mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: Allow workspace resources to attach multiple agents (#942)
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.
This commit is contained in:
+24
-28
@@ -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"
|
||||
@@ -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 {
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
resource, exists := resourceByAddress[resourceAddress]
|
||||
if !exists {
|
||||
resourceKeys := make([]string, 0)
|
||||
for resourceKey := range resourceByAddress {
|
||||
resourceKeys = append(resourceKeys, resourceKey)
|
||||
if agent.ID == uuid.Nil {
|
||||
return xerrors.Errorf("agent not found by name %q", args[1])
|
||||
}
|
||||
return xerrors.Errorf("no sshable agent with address %q: %+v", resourceAddress, resourceKeys)
|
||||
}
|
||||
if agent.ID == uuid.Nil {
|
||||
if len(agents) > 1 {
|
||||
return xerrors.New("you must specify the name of an agent")
|
||||
}
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user