-
Notifications
You must be signed in to change notification settings - Fork 582
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add UI for awaiting agent connections (#578)
* feat: Add stage to build logs This adds a stage property to logs, and refactors the job logs cliui. It also adds tests to the cliui for build logs! * feat: Add stage to build logs This adds a stage property to logs, and refactors the job logs cliui. It also adds tests to the cliui for build logs! * feat: Add config-ssh and tests for resiliency * Rename "Echo" test to "ImmediateExit" * Fix Terraform resource agent association * Fix logs post-cancel * Fix select on Windows * Remove terraform init logs * Move timer into it's own loop * Fix race condition in provisioner jobs * Fix requested changes
- Loading branch information
Showing
26 changed files
with
536 additions
and
228 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package cliui | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"sync" | ||
"time" | ||
|
||
"github.com/briandowns/spinner" | ||
"github.com/spf13/cobra" | ||
"golang.org/x/xerrors" | ||
|
||
"github.com/coder/coder/codersdk" | ||
) | ||
|
||
type AgentOptions struct { | ||
WorkspaceName string | ||
Fetch func(context.Context) (codersdk.WorkspaceResource, error) | ||
FetchInterval time.Duration | ||
WarnInterval time.Duration | ||
} | ||
|
||
// Agent displays a spinning indicator that waits for a workspace agent to connect. | ||
func Agent(cmd *cobra.Command, opts AgentOptions) error { | ||
if opts.FetchInterval == 0 { | ||
opts.FetchInterval = 500 * time.Millisecond | ||
} | ||
if opts.WarnInterval == 0 { | ||
opts.WarnInterval = 30 * time.Second | ||
} | ||
var resourceMutex sync.Mutex | ||
resource, err := opts.Fetch(cmd.Context()) | ||
if err != nil { | ||
return xerrors.Errorf("fetch: %w", err) | ||
} | ||
if resource.Agent.Status == codersdk.WorkspaceAgentConnected { | ||
return nil | ||
} | ||
if resource.Agent.Status == codersdk.WorkspaceAgentDisconnected { | ||
opts.WarnInterval = 0 | ||
} | ||
spin := spinner.New(spinner.CharSets[78], 100*time.Millisecond, spinner.WithColor("fgHiGreen")) | ||
spin.Writer = cmd.OutOrStdout() | ||
spin.Suffix = " Waiting for connection from " + Styles.Field.Render(resource.Type+"."+resource.Name) + "..." | ||
spin.Start() | ||
defer spin.Stop() | ||
|
||
ticker := time.NewTicker(opts.FetchInterval) | ||
defer ticker.Stop() | ||
timer := time.NewTimer(opts.WarnInterval) | ||
defer timer.Stop() | ||
go func() { | ||
select { | ||
case <-cmd.Context().Done(): | ||
return | ||
case <-timer.C: | ||
} | ||
resourceMutex.Lock() | ||
defer resourceMutex.Unlock() | ||
message := "Don't panic, your workspace is booting up!" | ||
if resource.Agent.Status == codersdk.WorkspaceAgentDisconnected { | ||
message = "The workspace agent lost connection! Wait for it to reconnect or run: " + Styles.Code.Render("coder workspaces rebuild "+opts.WorkspaceName) | ||
} | ||
// This saves the cursor position, then defers clearing from the cursor | ||
// position to the end of the screen. | ||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\033[s\r\033[2K%s\n\n", Styles.Paragraph.Render(Styles.Prompt.String()+message)) | ||
defer fmt.Fprintf(cmd.OutOrStdout(), "\033[u\033[J") | ||
}() | ||
for { | ||
select { | ||
case <-cmd.Context().Done(): | ||
return cmd.Context().Err() | ||
case <-ticker.C: | ||
} | ||
resourceMutex.Lock() | ||
resource, err = opts.Fetch(cmd.Context()) | ||
if err != nil { | ||
return xerrors.Errorf("fetch: %w", err) | ||
} | ||
if resource.Agent.Status != codersdk.WorkspaceAgentConnected { | ||
resourceMutex.Unlock() | ||
continue | ||
} | ||
resourceMutex.Unlock() | ||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package cliui_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/stretchr/testify/require" | ||
"go.uber.org/atomic" | ||
|
||
"github.com/coder/coder/cli/cliui" | ||
"github.com/coder/coder/codersdk" | ||
"github.com/coder/coder/pty/ptytest" | ||
) | ||
|
||
func TestAgent(t *testing.T) { | ||
t.Parallel() | ||
var disconnected atomic.Bool | ||
ptty := ptytest.New(t) | ||
cmd := &cobra.Command{ | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
err := cliui.Agent(cmd, cliui.AgentOptions{ | ||
WorkspaceName: "example", | ||
Fetch: func(ctx context.Context) (codersdk.WorkspaceResource, error) { | ||
resource := codersdk.WorkspaceResource{ | ||
Agent: &codersdk.WorkspaceAgent{ | ||
Status: codersdk.WorkspaceAgentDisconnected, | ||
}, | ||
} | ||
if disconnected.Load() { | ||
resource.Agent.Status = codersdk.WorkspaceAgentConnected | ||
} | ||
return resource, nil | ||
}, | ||
FetchInterval: time.Millisecond, | ||
WarnInterval: 10 * time.Millisecond, | ||
}) | ||
return err | ||
}, | ||
} | ||
cmd.SetOutput(ptty.Output()) | ||
cmd.SetIn(ptty.Input()) | ||
done := make(chan struct{}) | ||
go func() { | ||
defer close(done) | ||
err := cmd.Execute() | ||
require.NoError(t, err) | ||
}() | ||
ptty.ExpectMatch("lost connection") | ||
disconnected.Store(true) | ||
<-done | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.