1
1
package cli
2
2
3
3
import (
4
+ "context"
4
5
"fmt"
5
6
"strings"
6
7
@@ -15,18 +16,19 @@ import (
15
16
)
16
17
17
18
type externalAgent struct {
18
- AgentName string `json:"-"`
19
- AuthType string `json:"auth_type"`
20
- AuthToken string `json:"auth_token"`
21
- InitScript string `json:"init_script"`
19
+ WorkspaceName string `json:"-"`
20
+ AgentName string `json:"-"`
21
+ AuthType string `json:"auth_type"`
22
+ AuthToken string `json:"auth_token"`
23
+ InitScript string `json:"init_script"`
22
24
}
23
25
24
26
func (r * RootCmd ) externalWorkspaces () * serpent.Command {
25
27
orgContext := NewOrganizationContext ()
26
28
27
29
cmd := & serpent.Command {
28
30
Use : "external-workspaces [subcommand]" ,
29
- Short : "External workspace related commands " ,
31
+ Short : "Create or manage external workspaces " ,
30
32
Handler : func (inv * serpent.Invocation ) error {
31
33
return inv .Command .HelpHandler (inv )
32
34
},
@@ -43,88 +45,61 @@ func (r *RootCmd) externalWorkspaces() *serpent.Command {
43
45
44
46
// externalWorkspaceCreate extends `coder create` to create an external workspace.
45
47
func (r * RootCmd ) externalWorkspaceCreate () * serpent.Command {
46
- var (
47
- orgContext = NewOrganizationContext ()
48
- client = new (codersdk.Client )
49
- )
50
-
51
- cmd := r .create ()
52
- cmd .Use = "create [workspace]"
53
- cmd .Short = "Create a new external workspace"
54
- cmd .Middleware = serpent .Chain (
55
- cmd .Middleware ,
56
- r .InitClient (client ),
57
- serpent .RequireNArgs (1 ),
58
- )
59
-
60
- createHandler := cmd .Handler
61
- cmd .Handler = func (inv * serpent.Invocation ) error {
62
- workspaceName := inv .Args [0 ]
63
- templateVersion := inv .ParsedFlags ().Lookup ("template-version" )
64
- templateName := inv .ParsedFlags ().Lookup ("template" )
65
- if templateName == nil || templateName .Value .String () == "" {
66
- return xerrors .Errorf ("template name is required for external workspace creation. Use --template=<template_name>" )
67
- }
48
+ opts := createOptions {
49
+ beforeCreate : func (ctx context.Context , client * codersdk.Client , _ codersdk.Template , templateVersionID uuid.UUID ) error {
50
+ resources , err := client .TemplateVersionResources (ctx , templateVersionID )
51
+ if err != nil {
52
+ return xerrors .Errorf ("get template version resources: %w" , err )
53
+ }
54
+ if len (resources ) == 0 {
55
+ return xerrors .Errorf ("no resources found for template version %q" , templateVersionID )
56
+ }
68
57
69
- organization , err := orgContext .Selected (inv , client )
70
- if err != nil {
71
- return xerrors .Errorf ("get current organization: %w" , err )
72
- }
58
+ var hasExternalAgent bool
59
+ for _ , resource := range resources {
60
+ if resource .Type == "coder_external_agent" {
61
+ hasExternalAgent = true
62
+ break
63
+ }
64
+ }
73
65
74
- template , err := client .TemplateByName (inv .Context (), organization .ID , templateName .Value .String ())
75
- if err != nil {
76
- return xerrors .Errorf ("get template by name: %w" , err )
77
- }
66
+ if ! hasExternalAgent {
67
+ return xerrors .Errorf ("template version %q does not have an external agent. Only templates with external agents can be used for external workspace creation" , templateVersionID )
68
+ }
78
69
79
- var resources []codersdk.WorkspaceResource
80
- var templateVersionID uuid.UUID
81
- if templateVersion == nil || templateVersion .Value .String () == "" {
82
- templateVersionID = template .ActiveVersionID
83
- } else {
84
- version , err := client .TemplateVersionByName (inv .Context (), template .ID , templateVersion .Value .String ())
70
+ return nil
71
+ },
72
+ afterCreate : func (ctx context.Context , inv * serpent.Invocation , client * codersdk.Client , workspace codersdk.Workspace ) error {
73
+ workspace , err := client .WorkspaceByOwnerAndName (ctx , codersdk .Me , workspace .Name , codersdk.WorkspaceOptions {})
85
74
if err != nil {
86
- return xerrors .Errorf ("get template version by name: %w" , err )
75
+ return xerrors .Errorf ("get workspace by name: %w" , err )
87
76
}
88
- templateVersionID = version .ID
89
- }
90
-
91
- resources , err = client .TemplateVersionResources (inv .Context (), templateVersionID )
92
- if err != nil {
93
- return xerrors .Errorf ("get template version resources: %w" , err )
94
- }
95
- if len (resources ) == 0 {
96
- return xerrors .Errorf ("no resources found for template version %q" , templateVersion .Value .String ())
97
- }
98
77
99
- var hasExternalAgent bool
100
- for _ , resource := range resources {
101
- if resource .Type == "coder_external_agent" {
102
- hasExternalAgent = true
103
- break
78
+ externalAgents , err := fetchExternalAgents (inv , client , workspace , workspace .LatestBuild .Resources )
79
+ if err != nil {
80
+ return xerrors .Errorf ("fetch external agents: %w" , err )
104
81
}
105
- }
106
82
107
- if ! hasExternalAgent {
108
- return xerrors .Errorf ("template version %q does not have an external agent. Only templates with external agents can be used for external workspace creation" , templateVersion .Value .String ())
109
- }
110
-
111
- err = createHandler (inv )
112
- if err != nil {
83
+ formatted := formatExternalAgent (workspace .Name , externalAgents )
84
+ _ , err = fmt .Fprintln (inv .Stdout , formatted )
113
85
return err
114
- }
86
+ },
87
+ }
115
88
116
- workspace , err := client .WorkspaceByOwnerAndName (inv .Context (), codersdk .Me , workspaceName , codersdk.WorkspaceOptions {})
117
- if err != nil {
118
- return xerrors .Errorf ("get workspace by name: %w" , err )
119
- }
89
+ cmd := r .create (opts )
90
+ cmd .Use = "create [workspace]"
91
+ cmd .Short = "Create a new external workspace"
92
+ cmd .Middleware = serpent .Chain (
93
+ cmd .Middleware ,
94
+ serpent .RequireNArgs (1 ),
95
+ )
120
96
121
- externalAgents , err := fetchExternalAgents ( inv , client , workspace , workspace . LatestBuild . Resources )
122
- if err != nil {
123
- return xerrors . Errorf ( "fetch external agents: %w" , err )
97
+ for i := range cmd . Options {
98
+ if cmd . Options [ i ]. Flag == "template" {
99
+ cmd . Options [ i ]. Required = true
124
100
}
125
-
126
- return printExternalAgents (inv , workspace .Name , externalAgents )
127
101
}
102
+
128
103
return cmd
129
104
}
130
105
@@ -138,57 +113,37 @@ func (r *RootCmd) externalWorkspaceAgentInstructions() *serpent.Command {
138
113
return "" , xerrors .Errorf ("expected externalAgent, got %T" , data )
139
114
}
140
115
141
- var output strings.Builder
142
- _ , _ = output .WriteString (fmt .Sprintf ("Please run the following commands to attach agent %s:\n " , cliui .Keyword (agent .AgentName )))
143
- _ , _ = output .WriteString (fmt .Sprintf ("%s\n " , pretty .Sprint (cliui .DefaultStyles .Code , fmt .Sprintf ("export CODER_AGENT_TOKEN=%s" , agent .AuthToken ))))
144
- _ , _ = output .WriteString (pretty .Sprint (cliui .DefaultStyles .Code , fmt .Sprintf ("curl -fsSL %s | sh" , agent .InitScript )))
145
-
146
- return output .String (), nil
116
+ return formatExternalAgent (agent .WorkspaceName , []externalAgent {agent }), nil
147
117
}),
148
118
cliui .JSONFormat (),
149
119
)
150
120
151
121
cmd := & serpent.Command {
152
- Use : "agent-instructions [workspace name] [ agent name ]" ,
122
+ Use : "agent-instructions [user/]workspace[. agent]" ,
153
123
Short : "Get the instructions for an external agent" ,
154
- Middleware : serpent .Chain (r .InitClient (client ), serpent .RequireNArgs (2 )),
124
+ Middleware : serpent .Chain (r .InitClient (client ), serpent .RequireNArgs (1 )),
155
125
Handler : func (inv * serpent.Invocation ) error {
156
- workspaceName := inv .Args [0 ]
157
- agentName := inv .Args [1 ]
158
-
159
- workspace , err := client .WorkspaceByOwnerAndName (inv .Context (), codersdk .Me , workspaceName , codersdk.WorkspaceOptions {})
126
+ workspace , workspaceAgent , _ , err := getWorkspaceAndAgent (inv .Context (), inv , client , false , inv .Args [0 ])
160
127
if err != nil {
161
- return xerrors .Errorf ("get workspace by name : %w" , err )
128
+ return xerrors .Errorf ("find workspace and agent : %w" , err )
162
129
}
163
130
164
- credential , err := client .WorkspaceExternalAgentCredential (inv .Context (), workspace .ID , agentName )
131
+ credential , err := client .WorkspaceExternalAgentCredential (inv .Context (), workspace .ID , workspaceAgent . Name )
165
132
if err != nil {
166
- return xerrors .Errorf ("get external agent token for agent %q: %w" , agentName , err )
167
- }
168
-
169
- var agent codersdk.WorkspaceAgent
170
- for _ , resource := range workspace .LatestBuild .Resources {
171
- for _ , a := range resource .Agents {
172
- if a .Name == agentName {
173
- agent = a
174
- break
175
- }
176
- }
177
- if agent .ID != uuid .Nil {
178
- break
179
- }
133
+ return xerrors .Errorf ("get external agent token for agent %q: %w" , workspaceAgent .Name , err )
180
134
}
181
135
182
136
initScriptURL := fmt .Sprintf ("%s/api/v2/init-script" , client .URL )
183
- if agent .OperatingSystem != "linux" || agent .Architecture != "amd64" {
184
- initScriptURL = fmt .Sprintf ("%s/api/v2/init-script?os=%s&arch=%s" , client .URL , agent .OperatingSystem , agent .Architecture )
137
+ if workspaceAgent .OperatingSystem != "linux" || workspaceAgent .Architecture != "amd64" {
138
+ initScriptURL = fmt .Sprintf ("%s/api/v2/init-script?os=%s&arch=%s" , client .URL , workspaceAgent .OperatingSystem , workspaceAgent .Architecture )
185
139
}
186
140
187
141
agentInfo := externalAgent {
188
- AgentName : agentName ,
189
- AuthType : "token" ,
190
- AuthToken : credential .AgentToken ,
191
- InitScript : initScriptURL ,
142
+ WorkspaceName : workspace .Name ,
143
+ AgentName : workspaceAgent .Name ,
144
+ AuthType : "token" ,
145
+ AuthToken : credential .AgentToken ,
146
+ InitScript : initScriptURL ,
192
147
}
193
148
194
149
out , err := formatter .Format (inv .Context (), agentInfo )
@@ -305,22 +260,23 @@ func fetchExternalAgents(inv *serpent.Invocation, client *codersdk.Client, works
305
260
return externalAgents , nil
306
261
}
307
262
308
- // printExternalAgents prints the instructions for an external agent.
309
- func printExternalAgents (inv * serpent.Invocation , workspaceName string , externalAgents []externalAgent ) error {
310
- _ , _ = fmt .Fprintf (inv .Stdout , "\n Please run the following commands to attach external agent to the workspace %s:\n \n " , cliui .Keyword (workspaceName ))
263
+ // formatExternalAgent formats the instructions for an external agent.
264
+ func formatExternalAgent (workspaceName string , externalAgents []externalAgent ) string {
265
+ var output strings.Builder
266
+ _ , _ = output .WriteString (fmt .Sprintf ("\n Please run the following commands to attach external agent to the workspace %s:\n \n " , cliui .Keyword (workspaceName )))
311
267
312
268
for i , agent := range externalAgents {
313
269
if len (externalAgents ) > 1 {
314
- _ , _ = fmt . Fprintf ( inv . Stdout , "For agent %s:\n " , cliui .Keyword (agent .AgentName ))
270
+ _ , _ = output . WriteString ( fmt . Sprintf ( "For agent %s:\n " , cliui .Keyword (agent .AgentName ) ))
315
271
}
316
272
317
- _ , _ = fmt . Fprintf ( inv . Stdout , "%s\n " , pretty .Sprint (cliui .DefaultStyles .Code , fmt .Sprintf ("export CODER_AGENT_TOKEN=%s" , agent .AuthToken )))
318
- _ , _ = fmt . Fprintf ( inv . Stdout , "%s\n " , pretty .Sprint (cliui .DefaultStyles .Code , fmt .Sprintf ("curl -fsSL %s | sh" , agent .InitScript )))
273
+ _ , _ = output . WriteString ( fmt . Sprintf ( "%s\n " , pretty .Sprint (cliui .DefaultStyles .Code , fmt .Sprintf ("export CODER_AGENT_TOKEN=%s" , agent .AuthToken ) )))
274
+ _ , _ = output . WriteString ( fmt . Sprintf ( "%s\n " , pretty .Sprint (cliui .DefaultStyles .Code , fmt .Sprintf ("curl -fsSL %s | sh" , agent .InitScript ) )))
319
275
320
276
if i < len (externalAgents )- 1 {
321
- _ , _ = fmt . Fprintf ( inv . Stdout , "\n " )
277
+ _ , _ = output . WriteString ( "\n " )
322
278
}
323
279
}
324
280
325
- return nil
281
+ return output . String ()
326
282
}
0 commit comments