PollPostCreateStates watches for state changes in a codespace, and calls the supplied poller for each batch of state changes. It runs until it encounters an error, including cancellation of the context.
(ctx context.Context, progress progressIndicator, apiClient apiClient, codespace *api.Codespace, poller func([]PostCreateState))
| 38 | // and calls the supplied poller for each batch of state changes. |
| 39 | // It runs until it encounters an error, including cancellation of the context. |
| 40 | func PollPostCreateStates(ctx context.Context, progress progressIndicator, apiClient apiClient, codespace *api.Codespace, poller func([]PostCreateState)) (err error) { |
| 41 | codespaceConnection, err := GetCodespaceConnection(ctx, progress, apiClient, codespace) |
| 42 | if err != nil { |
| 43 | return fmt.Errorf("error connecting to codespace: %w", err) |
| 44 | } |
| 45 | |
| 46 | fwd, err := portforwarder.NewPortForwarder(ctx, codespaceConnection) |
| 47 | if err != nil { |
| 48 | return fmt.Errorf("failed to create port forwarder: %w", err) |
| 49 | } |
| 50 | defer safeClose(fwd, &err) |
| 51 | |
| 52 | // Ensure local port is listening before client (getPostCreateOutput) connects. |
| 53 | listen, localPort, err := ListenTCP(0, false) |
| 54 | if err != nil { |
| 55 | return err |
| 56 | } |
| 57 | |
| 58 | progress.StartProgressIndicatorWithLabel("Fetching SSH Details") |
| 59 | invoker, err := rpc.CreateInvoker(ctx, fwd) |
| 60 | if err != nil { |
| 61 | return err |
| 62 | } |
| 63 | defer safeClose(invoker, &err) |
| 64 | |
| 65 | remoteSSHServerPort, sshUser, err := invoker.StartSSHServer(ctx) |
| 66 | if err != nil { |
| 67 | return fmt.Errorf("error getting ssh server details: %w", err) |
| 68 | } |
| 69 | progress.StopProgressIndicator() |
| 70 | |
| 71 | progress.StartProgressIndicatorWithLabel("Fetching status") |
| 72 | tunnelClosed := make(chan error, 1) // buffered to avoid sender stuckness |
| 73 | go func() { |
| 74 | opts := portforwarder.ForwardPortOpts{ |
| 75 | Port: remoteSSHServerPort, |
| 76 | Internal: true, |
| 77 | } |
| 78 | tunnelClosed <- fwd.ForwardPortToListener(ctx, opts, listen) |
| 79 | }() |
| 80 | |
| 81 | t := time.NewTicker(1 * time.Second) |
| 82 | defer t.Stop() |
| 83 | |
| 84 | for ticks := 0; ; ticks++ { |
| 85 | select { |
| 86 | case <-ctx.Done(): |
| 87 | return ctx.Err() |
| 88 | |
| 89 | case err := <-tunnelClosed: |
| 90 | return fmt.Errorf("connection failed: %w", err) |
| 91 | |
| 92 | case <-t.C: |
| 93 | states, err := getPostCreateOutput(ctx, localPort, sshUser) |
| 94 | // There is an active progress indicator before the first tick |
| 95 | // to show that we are fetching statuses. |
| 96 | // Once the first tick happens, we stop the indicator and let |
| 97 | // the subsequent post create states manage their own progress. |
no test coverage detected