Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: Improve resource preview and first-time experience #946

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Apr 11, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: Allow workspace resources to attach multiple agents
This enables a "kubernetes_pod" to attach multiple agents that
could be for multiple services. Each agent is required to have
a unique name, so SSH syntax is:

`coder ssh <workspace>.<agent>`

A resource can have zero agents too, they aren't required.
  • Loading branch information
kylecarbs committed Apr 9, 2022
commit e8258ea75359f16dce71c1c57a1ba5db4b39540b
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"emeraldwalk.runonsave": {
"commands": [
{
"match": "database/query.sql",
"match": "database/queries/*.sql",
"cmd": "make gen"
}
]
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ fmt/sql: $(wildcard coderd/database/queries/*.sql)

sed -i 's/@ /@/g' ./coderd/database/queries/*.sql

fmt: fmt/prettier fmt/sql
fmt/terraform: $(wildcard *.tf)
terraform fmt -recursive

fmt: fmt/prettier fmt/sql fmt/terraform
.PHONY: fmt

gen: coderd/database/generate peerbroker/proto provisionersdk/proto provisionerd/proto
Expand Down
16 changes: 8 additions & 8 deletions cli/cliui/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

type AgentOptions struct {
WorkspaceName string
Fetch func(context.Context) (codersdk.WorkspaceResource, error)
Fetch func(context.Context) (codersdk.WorkspaceAgent, error)
FetchInterval time.Duration
WarnInterval time.Duration
}
Expand All @@ -29,20 +29,20 @@ func Agent(ctx context.Context, writer io.Writer, opts AgentOptions) error {
opts.WarnInterval = 30 * time.Second
}
var resourceMutex sync.Mutex
resource, err := opts.Fetch(ctx)
agent, err := opts.Fetch(ctx)
if err != nil {
return xerrors.Errorf("fetch: %w", err)
}
if resource.Agent.Status == codersdk.WorkspaceAgentConnected {
if agent.Status == codersdk.WorkspaceAgentConnected {
return nil
}
if resource.Agent.Status == codersdk.WorkspaceAgentDisconnected {
if agent.Status == codersdk.WorkspaceAgentDisconnected {
opts.WarnInterval = 0
}
spin := spinner.New(spinner.CharSets[78], 100*time.Millisecond, spinner.WithColor("fgHiGreen"))
spin.Writer = writer
spin.ForceOutput = true
spin.Suffix = " Waiting for connection from " + Styles.Field.Render(resource.Type+"."+resource.Name) + "..."
spin.Suffix = " Waiting for connection from " + Styles.Field.Render(agent.Name) + "..."
spin.Start()
defer spin.Stop()

Expand All @@ -59,7 +59,7 @@ func Agent(ctx context.Context, writer io.Writer, opts AgentOptions) error {
resourceMutex.Lock()
defer resourceMutex.Unlock()
message := "Don't panic, your workspace is booting up!"
if resource.Agent.Status == codersdk.WorkspaceAgentDisconnected {
if 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
Expand All @@ -74,11 +74,11 @@ func Agent(ctx context.Context, writer io.Writer, opts AgentOptions) error {
case <-ticker.C:
}
resourceMutex.Lock()
resource, err = opts.Fetch(ctx)
agent, err = opts.Fetch(ctx)
if err != nil {
return xerrors.Errorf("fetch: %w", err)
}
if resource.Agent.Status != codersdk.WorkspaceAgentConnected {
if agent.Status != codersdk.WorkspaceAgentConnected {
resourceMutex.Unlock()
continue
}
Expand Down
12 changes: 5 additions & 7 deletions cli/cliui/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,14 @@ func TestAgent(t *testing.T) {
RunE: func(cmd *cobra.Command, args []string) error {
err := cliui.Agent(cmd.Context(), cmd.OutOrStdout(), cliui.AgentOptions{
WorkspaceName: "example",
Fetch: func(ctx context.Context) (codersdk.WorkspaceResource, error) {
resource := codersdk.WorkspaceResource{
Agent: &codersdk.WorkspaceAgent{
Status: codersdk.WorkspaceAgentDisconnected,
},
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
agent := codersdk.WorkspaceAgent{
Status: codersdk.WorkspaceAgentDisconnected,
}
if disconnected.Load() {
resource.Agent.Status = codersdk.WorkspaceAgentConnected
agent.Status = codersdk.WorkspaceAgentConnected
}
return resource, nil
return agent, nil
},
FetchInterval: time.Millisecond,
WarnInterval: 10 * time.Millisecond,
Expand Down
34 changes: 18 additions & 16 deletions cli/configssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/coder/coder/cli/cliflag"
"github.com/coder/coder/cli/cliui"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/codersdk"
)

Expand Down Expand Up @@ -70,29 +71,30 @@ func configSSH() *cobra.Command {
for _, workspace := range workspaces {
workspace := workspace
errGroup.Go(func() error {
resources, err := client.WorkspaceResourcesByBuild(cmd.Context(), workspace.LatestBuild.ID)
resources, err := client.TemplateVersionResources(cmd.Context(), workspace.LatestBuild.TemplateVersionID)
if err != nil {
return err
}
resourcesWithAgents := make([]codersdk.WorkspaceResource, 0)
for _, resource := range resources {
if resource.Agent == nil {
if resource.Transition != database.WorkspaceTransitionStart {
continue
}
resourcesWithAgents = append(resourcesWithAgents, resource)
}
sshConfigContentMutex.Lock()
defer sshConfigContentMutex.Unlock()
if len(resourcesWithAgents) == 1 {
sshConfigContent += strings.Join([]string{
"Host coder." + workspace.Name,
"\tHostName coder." + workspace.Name,
fmt.Sprintf("\tProxyCommand %q ssh --stdio %s", binPath, workspace.Name),
"\tConnectTimeout=0",
"\tStrictHostKeyChecking=no",
}, "\n") + "\n"
for _, agent := range resource.Agents {
sshConfigContentMutex.Lock()
hostname := workspace.Name
if len(resource.Agents) > 1 {
hostname += "." + agent.Name
}
sshConfigContent += strings.Join([]string{
"Host coder." + hostname,
"\tHostName coder." + hostname,
fmt.Sprintf("\tProxyCommand %q ssh --stdio %s", binPath, hostname),
"\tConnectTimeout=0",
"\tStrictHostKeyChecking=no",
}, "\n") + "\n"
sshConfigContentMutex.Unlock()
}
}

return nil
})
}
Expand Down
6 changes: 3 additions & 3 deletions cli/gitssh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ func TestGitSSH(t *testing.T) {
Resources: []*proto.Resource{{
Name: "somename",
Type: "someinstance",
Agent: &proto.Agent{
Agents: []*proto.Agent{{
Auth: &proto.Agent_InstanceId{
InstanceId: instanceID,
},
},
}},
}},
},
},
Expand Down Expand Up @@ -81,7 +81,7 @@ func TestGitSSH(t *testing.T) {
coderdtest.AwaitWorkspaceAgents(t, client, workspace.LatestBuild.ID)
resources, err := client.WorkspaceResourcesByBuild(ctx, workspace.LatestBuild.ID)
require.NoError(t, err)
dialer, err := client.DialWorkspaceAgent(ctx, resources[0].ID, nil, nil)
dialer, err := client.DialWorkspaceAgent(ctx, resources[0].Agents[0].ID, nil, nil)
require.NoError(t, err)
defer dialer.Close()
_, err = dialer.Ping()
Expand Down
50 changes: 23 additions & 27 deletions cli/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"time"

"github.com/google/uuid"
"github.com/mattn/go-isatty"
"github.com/pion/webrtc/v3"
"github.com/spf13/cobra"
Expand All @@ -25,7 +26,7 @@ func ssh() *cobra.Command {
stdio bool
)
cmd := &cobra.Command{
Use: "ssh <workspace> [resource]",
Use: "ssh <workspace> [agent]",
RunE: func(cmd *cobra.Command, args []string) error {
client, err := createClient(cmd)
if err != nil {
Expand Down Expand Up @@ -57,50 +58,45 @@ func ssh() *cobra.Command {
return err
}

resourceByAddress := make(map[string]codersdk.WorkspaceResource)
agents := make([]codersdk.WorkspaceAgent, 0)
for _, resource := range resources {
if resource.Agent == nil {
continue
}
resourceByAddress[resource.Address] = resource
agents = append(agents, resource.Agents...)
}

var resourceAddress string
if len(agents) == 0 {
return xerrors.New("workspace has no agents")
}
var agent codersdk.WorkspaceAgent
if len(args) >= 2 {
resourceAddress = args[1]
} else {
// No resource name was provided!
if len(resourceByAddress) > 1 {
// List available resources to connect into?
return xerrors.Errorf("multiple agents")
}
for _, resource := range resourceByAddress {
resourceAddress = resource.Address
for _, otherAgent := range agents {
if otherAgent.Name != args[1] {
continue
}
agent = otherAgent
break
}
if agent.ID == uuid.Nil {
return xerrors.Errorf("agent not found by name %q", args[1])
}
}

resource, exists := resourceByAddress[resourceAddress]
if !exists {
resourceKeys := make([]string, 0)
for resourceKey := range resourceByAddress {
resourceKeys = append(resourceKeys, resourceKey)
if agent.ID == uuid.Nil {
if len(agents) > 1 {
return xerrors.New("you must specify the name of an agent")
}
return xerrors.Errorf("no sshable agent with address %q: %+v", resourceAddress, resourceKeys)
agent = agents[0]
}
// OpenSSH passes stderr directly to the calling TTY.
// This is required in "stdio" mode so a connecting indicator can be displayed.
err = cliui.Agent(cmd.Context(), cmd.ErrOrStderr(), cliui.AgentOptions{
WorkspaceName: workspace.Name,
Fetch: func(ctx context.Context) (codersdk.WorkspaceResource, error) {
return client.WorkspaceResource(ctx, resource.ID)
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
return client.WorkspaceAgent(ctx, agent.ID)
},
})
if err != nil {
return xerrors.Errorf("await agent: %w", err)
}

conn, err := client.DialWorkspaceAgent(cmd.Context(), resource.ID, []webrtc.ICEServer{{
conn, err := client.DialWorkspaceAgent(cmd.Context(), agent.ID, []webrtc.ICEServer{{
URLs: []string{"stun:stun.l.google.com:19302"},
}}, nil)
if err != nil {
Expand Down
8 changes: 4 additions & 4 deletions cli/ssh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ func TestSSH(t *testing.T) {
Resources: []*proto.Resource{{
Name: "dev",
Type: "google_compute_instance",
Agent: &proto.Agent{
Agents: []*proto.Agent{{
Id: uuid.NewString(),
Auth: &proto.Agent_Token{
Token: agentToken,
},
},
}},
}},
},
},
Expand Down Expand Up @@ -98,12 +98,12 @@ func TestSSH(t *testing.T) {
Resources: []*proto.Resource{{
Name: "dev",
Type: "google_compute_instance",
Agent: &proto.Agent{
Agents: []*proto.Agent{{
Id: uuid.NewString(),
Auth: &proto.Agent_Token{
Token: agentToken,
},
},
}},
}},
},
},
Expand Down
2 changes: 1 addition & 1 deletion cli/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func displayTemplateVersionInfo(cmd *cobra.Command, resources []codersdk.Workspa
} else {
_, _ = fmt.Fprintln(cmd.OutOrStdout(), " "+cliui.Styles.Keyword.Render("+ start")+cliui.Styles.Placeholder.Render(" (deletes on stop)"))
}
if resource.Agent != nil {
if len(resource.Agents) > 0 {
_, _ = fmt.Fprintln(cmd.OutOrStdout(), " "+cliui.Styles.Fuschia.Render("▲ allows ssh"))
}
_, _ = fmt.Fprintln(cmd.OutOrStdout())
Expand Down
12 changes: 6 additions & 6 deletions cli/workspaceagent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ func TestWorkspaceAgent(t *testing.T) {
Resources: []*proto.Resource{{
Name: "somename",
Type: "someinstance",
Agent: &proto.Agent{
Agents: []*proto.Agent{{
Auth: &proto.Agent_InstanceId{
InstanceId: instanceID,
},
},
}},
}},
},
},
Expand All @@ -61,7 +61,7 @@ func TestWorkspaceAgent(t *testing.T) {
coderdtest.AwaitWorkspaceAgents(t, client, workspace.LatestBuild.ID)
resources, err := client.WorkspaceResourcesByBuild(ctx, workspace.LatestBuild.ID)
require.NoError(t, err)
dialer, err := client.DialWorkspaceAgent(ctx, resources[0].ID, nil, nil)
dialer, err := client.DialWorkspaceAgent(ctx, resources[0].Agents[0].ID, nil, nil)
require.NoError(t, err)
defer dialer.Close()
_, err = dialer.Ping()
Expand All @@ -86,11 +86,11 @@ func TestWorkspaceAgent(t *testing.T) {
Resources: []*proto.Resource{{
Name: "somename",
Type: "someinstance",
Agent: &proto.Agent{
Agents: []*proto.Agent{{
Auth: &proto.Agent_InstanceId{
InstanceId: instanceID,
},
},
}},
}},
},
},
Expand All @@ -115,7 +115,7 @@ func TestWorkspaceAgent(t *testing.T) {
coderdtest.AwaitWorkspaceAgents(t, client, workspace.LatestBuild.ID)
resources, err := client.WorkspaceResourcesByBuild(ctx, workspace.LatestBuild.ID)
require.NoError(t, err)
dialer, err := client.DialWorkspaceAgent(ctx, resources[0].ID, nil, nil)
dialer, err := client.DialWorkspaceAgent(ctx, resources[0].Agents[0].ID, nil, nil)
require.NoError(t, err)
defer dialer.Close()
_, err = dialer.Ping()
Expand Down
23 changes: 0 additions & 23 deletions cli/workspaceshow.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,13 @@
package cli

import (
"fmt"

"github.com/spf13/cobra"

"github.com/coder/coder/codersdk"
)

func workspaceShow() *cobra.Command {
return &cobra.Command{
Use: "show",
RunE: func(cmd *cobra.Command, args []string) error {
client, err := createClient(cmd)
if err != nil {
return err
}
workspace, err := client.WorkspaceByName(cmd.Context(), codersdk.Me, args[0])
if err != nil {
return err
}
resources, err := client.WorkspaceResourcesByBuild(cmd.Context(), workspace.LatestBuild.ID)
if err != nil {
return err
}
for _, resource := range resources {
if resource.Agent == nil {
continue
}

_, _ = fmt.Printf("Agent: %+v\n", resource.Agent)
}
return nil
},
}
Expand Down
Loading