@@ -7,6 +7,7 @@ package cli
7
7
import (
8
8
"fmt"
9
9
10
+ "github.com/aws/aws-sdk-go/aws"
10
11
"github.com/aws/copilot-cli/cmd/copilot/template"
11
12
"github.com/aws/copilot-cli/internal/pkg/aws/identity"
12
13
"github.com/aws/copilot-cli/internal/pkg/aws/sessions"
@@ -41,33 +42,45 @@ type initVars struct {
41
42
// Flags unique to "init" that's not provided by other sub-commands.
42
43
shouldDeploy bool
43
44
appName string
44
- svcType string
45
+ wkldType string
45
46
svcName string
46
47
dockerfilePath string
48
+ image string
47
49
imageTag string
48
- port uint16
50
+
51
+ // Service specific flags
52
+ port uint16
53
+
54
+ // Scheduled Job specific flags
55
+ schedule string
56
+ retries int
57
+ timeout string
49
58
}
50
59
51
60
type initOpts struct {
61
+ initVars
62
+
52
63
ShouldDeploy bool // true means we should create a test environment and deploy the service to it. Defaults to false.
53
64
promptForShouldDeploy bool // true means that the user set the ShouldDeploy flag explicitly.
54
65
55
66
// Sub-commands to execute.
56
67
initAppCmd actionCommand
57
- initSvcCmd actionCommand
68
+ initWlCmd actionCommand
58
69
initEnvCmd actionCommand
59
70
deploySvcCmd actionCommand
71
+ deployJobCmd actionCommand
60
72
61
73
// Pointers to flag values part of sub-commands.
62
74
// Since the sub-commands implement the actionCommand interface, without pointers to their internal fields
63
75
// we have to resort to type-casting the interface. These pointers simplify data access.
64
- appName * string
65
- svcType * string
66
- svcName * string
67
- svcPort * uint16
68
- dockerfilePath * string
76
+ appName * string
77
+ port * uint16
78
+ schedule * string
79
+ initWkldVars * initWkldVars
69
80
70
81
prompt prompter
82
+
83
+ setupWorkoadInit func (* initOpts , string ) error
71
84
}
72
85
73
86
func newInitOpts (vars initVars ) (* initOpts , error ) {
@@ -92,7 +105,6 @@ func newInitOpts(vars initVars) (*initOpts, error) {
92
105
if err != nil {
93
106
return nil , err
94
107
}
95
-
96
108
initAppCmd := & initAppOpts {
97
109
initAppVars : initAppVars {
98
110
name : vars .appName ,
@@ -104,24 +116,6 @@ func newInitOpts(vars initVars) (*initOpts, error) {
104
116
cfn : deployer ,
105
117
prog : spin ,
106
118
}
107
- wkldInitter := & initialize.WorkloadInitializer {Store : ssm , Ws : ws , Prog : spin , Deployer : deployer }
108
- initSvcCmd := & initSvcOpts {
109
- initWkldVars : initWkldVars {
110
- wkldType : vars .svcType ,
111
- name : vars .svcName ,
112
- dockerfilePath : vars .dockerfilePath ,
113
- port : vars .port ,
114
- appName : vars .appName ,
115
- },
116
- fs : & afero.Afero {Fs : afero .NewOsFs ()},
117
-
118
- init : wkldInitter ,
119
- sel : sel ,
120
- prompt : prompt ,
121
- setupParser : func (o * initSvcOpts ) {
122
- o .df = dockerfile .New (o .fs , o .dockerfilePath )
123
- },
124
- }
125
119
initEnvCmd := & initEnvOpts {
126
120
initEnvVars : initEnvVars {
127
121
appName : vars .appName ,
@@ -153,22 +147,88 @@ func newInitOpts(vars initVars) (*initOpts, error) {
153
147
cmd : command .New (),
154
148
sessProvider : sessProvider ,
155
149
}
150
+ deployJobCmd := & deployJobOpts {
151
+ deployJobVars : deployJobVars {
152
+ envName : defaultEnvironmentName ,
153
+ imageTag : vars .imageTag ,
154
+ appName : vars .appName ,
155
+ },
156
+ store : ssm ,
157
+ prompt : prompt ,
158
+ ws : ws ,
159
+ unmarshal : manifest .UnmarshalWorkload ,
160
+ sel : sel ,
161
+ spinner : spin ,
162
+ cmd : command .New (),
163
+ sessProvider : sessProvider ,
164
+ }
156
165
157
166
return & initOpts {
167
+ initVars : vars ,
158
168
ShouldDeploy : vars .shouldDeploy ,
159
169
160
170
initAppCmd : initAppCmd ,
161
- initSvcCmd : initSvcCmd ,
162
171
initEnvCmd : initEnvCmd ,
163
172
deploySvcCmd : deploySvcCmd ,
173
+ deployJobCmd : deployJobCmd ,
164
174
165
- appName : & initAppCmd .name ,
166
- svcType : & initSvcCmd .wkldType ,
167
- svcName : & initSvcCmd .name ,
168
- svcPort : & initSvcCmd .port ,
169
- dockerfilePath : & initSvcCmd .dockerfilePath ,
175
+ appName : & initAppCmd .name ,
170
176
171
177
prompt : prompt ,
178
+
179
+ setupWorkoadInit : func (o * initOpts , wkldType string ) error {
180
+ wlInitializer := & initialize.WorkloadInitializer {Store : ssm , Ws : ws , Prog : spin , Deployer : deployer }
181
+ wkldVars := initWkldVars {
182
+ appName : * o .appName ,
183
+ wkldType : wkldType ,
184
+ name : vars .svcName ,
185
+ dockerfilePath : vars .dockerfilePath ,
186
+ image : vars .image ,
187
+ }
188
+ switch t := wkldType ; {
189
+ case t == manifest .ScheduledJobType :
190
+ jobVars := initJobVars {
191
+ initWkldVars : wkldVars ,
192
+ schedule : vars .schedule ,
193
+ retries : vars .retries ,
194
+ timeout : vars .timeout ,
195
+ }
196
+
197
+ opts := initJobOpts {
198
+ initJobVars : jobVars ,
199
+
200
+ fs : & afero.Afero {Fs : afero .NewOsFs ()},
201
+ init : wlInitializer ,
202
+ sel : sel ,
203
+ prompt : prompt ,
204
+ }
205
+ o .initWlCmd = & opts
206
+ o .schedule = & opts .schedule // Surfaced via pointer for logging
207
+ o .initWkldVars = & opts .initWkldVars
208
+ case t == manifest .LoadBalancedWebServiceType || t == manifest .BackendServiceType :
209
+ svcVars := initSvcVars {
210
+ initWkldVars : wkldVars ,
211
+ port : vars .port ,
212
+ }
213
+ opts := initSvcOpts {
214
+ initSvcVars : svcVars ,
215
+
216
+ fs : & afero.Afero {Fs : afero .NewOsFs ()},
217
+ init : wlInitializer ,
218
+ sel : sel ,
219
+ prompt : prompt ,
220
+ setupParser : func (o * initSvcOpts ) {
221
+ o .df = dockerfile .New (o .fs , o .dockerfilePath )
222
+ },
223
+ }
224
+ o .initWlCmd = & opts
225
+ o .port = & opts .port // Surfaced via pointer for logging.
226
+ o .initWkldVars = & opts .initWkldVars
227
+ default :
228
+ return fmt .Errorf ("unrecognized workload type" )
229
+ }
230
+ return nil
231
+ },
172
232
}, nil
173
233
}
174
234
@@ -185,27 +245,46 @@ containerized services that operate together.`))
185
245
if err := o .loadApp (); err != nil {
186
246
return err
187
247
}
188
- if err := o .loadSvc (); err != nil {
248
+
249
+ if err := o .loadWkld (); err != nil {
189
250
return err
190
251
}
191
252
192
- log . Infof ( "Ok great, we'll set up a %s named %s in application %s listening on port %s. \n " ,
193
- color . HighlightUserInput ( * o . svcType ), color . HighlightUserInput ( * o . svcName ), color . HighlightUserInput ( * o . appName ), color . HighlightUserInput ( fmt . Sprintf ( "%d" , * o . svcPort )))
253
+ o . logWorkloadTypeAck ()
254
+
194
255
log .Infoln ()
195
256
if err := o .initAppCmd .Execute (); err != nil {
196
257
return fmt .Errorf ("execute app init: %w" , err )
197
258
}
198
- if err := o .initSvcCmd .Execute (); err != nil {
199
- return fmt .Errorf ("execute svc init: %w" , err )
259
+ if err := o .initWlCmd .Execute (); err != nil {
260
+ return fmt .Errorf ("execute %s init: %w" , o . wkldType , err )
200
261
}
201
262
202
263
if err := o .deployEnv (); err != nil {
203
264
return err
204
265
}
266
+ return o .deploy ()
267
+ }
268
+
269
+ func (o * initOpts ) logWorkloadTypeAck () {
270
+ if o .initWkldVars .wkldType == manifest .ScheduledJobType {
271
+ log .Infof ("Ok great, we'll set up a %s named %s in application %s running on the schedule %s.\n " ,
272
+ color .HighlightUserInput (o .initWkldVars .wkldType ), color .HighlightUserInput (o .initWkldVars .name ), color .HighlightUserInput (o .initWkldVars .appName ), color .HighlightUserInput (* o .schedule ))
273
+ return
274
+ }
275
+ if aws .Uint16Value (o .port ) != 0 {
276
+ log .Infof ("Ok great, we'll set up a %s named %s in application %s listening on port %s.\n " , color .HighlightUserInput (o .initWkldVars .wkldType ), color .HighlightUserInput (o .initWkldVars .name ), color .HighlightUserInput (o .initWkldVars .appName ), color .HighlightUserInput (fmt .Sprintf ("%d" , * o .port )))
277
+ } else {
278
+ log .Infof ("Ok great, we'll set up a %s named %s in application %s.\n " , color .HighlightUserInput (o .initWkldVars .wkldType ), color .HighlightUserInput (o .initWkldVars .name ), color .HighlightUserInput (o .initWkldVars .appName ))
279
+ }
280
+ }
205
281
282
+ func (o * initOpts ) deploy () error {
283
+ if o .initWkldVars .wkldType == manifest .ScheduledJobType {
284
+ return o .deployJob ()
285
+ }
206
286
return o .deploySvc ()
207
287
}
208
-
209
288
func (o * initOpts ) loadApp () error {
210
289
if err := o .initAppCmd .Ask (); err != nil {
211
290
return fmt .Errorf ("ask app init: %w" , err )
@@ -216,16 +295,52 @@ func (o *initOpts) loadApp() error {
216
295
return nil
217
296
}
218
297
219
- func (o * initOpts ) loadSvc () error {
220
- if initSvcOpts , ok := o .initSvcCmd .(* initSvcOpts ); ok {
221
- // Set the application name from app init to the service init command.
222
- initSvcOpts .appName = * o .appName
298
+ func (o * initOpts ) loadWkld () error {
299
+ err := o .loadWkldCmd ()
300
+ if err != nil {
301
+ return err
302
+ }
303
+ if err := o .initWlCmd .Ask (); err != nil {
304
+ return fmt .Errorf ("ask %s: %w" , o .wkldType , err )
305
+ }
306
+ if err := o .initWlCmd .Validate (); err != nil {
307
+ return fmt .Errorf ("validate %s: %w" , o .wkldType , err )
223
308
}
224
309
225
- if err := o .initSvcCmd .Ask (); err != nil {
226
- return fmt .Errorf ("ask svc init: %w" , err )
310
+ return nil
311
+ }
312
+
313
+ func (o * initOpts ) loadWkldCmd () error {
314
+ wkldType , err := o .askWorkload ()
315
+ if err != nil {
316
+ return err
227
317
}
228
- return o .initSvcCmd .Validate ()
318
+ if err := o .setupWorkoadInit (o , wkldType ); err != nil {
319
+ return err
320
+ }
321
+
322
+ return nil
323
+ }
324
+
325
+ func (o * initOpts ) askWorkload () (string , error ) {
326
+ if o .wkldType != "" {
327
+ return o .wkldType , nil
328
+ }
329
+ wkldInitTypePrompt := "Which " + color .Emphasize ("workload type" ) + " best represents your architecture?"
330
+ // Build the workload help prompt from existing helps text.
331
+ wkldHelp := fmt .Sprintf (fmtSvcInitSvcTypeHelpPrompt ,
332
+ manifest .LoadBalancedWebServiceType ,
333
+ manifest .BackendServiceType ,
334
+ ) + `
335
+
336
+ ` + fmt .Sprintf (fmtJobInitTypeHelp , manifest .ScheduledJobType )
337
+
338
+ t , err := o .prompt .SelectOne (wkldInitTypePrompt , wkldHelp , manifest .WorkloadTypes , prompt .WithFinalMessage ("Workload type:" ))
339
+ if err != nil {
340
+ return "" , fmt .Errorf ("select service type: %w" , err )
341
+ }
342
+ o .wkldType = t
343
+ return t , nil
229
344
}
230
345
231
346
// deployEnv prompts the user to deploy a test environment if the application doesn't already have one.
@@ -255,7 +370,7 @@ func (o *initOpts) deploySvc() error {
255
370
}
256
371
if deployOpts , ok := o .deploySvcCmd .(* deploySvcOpts ); ok {
257
372
// Set the service's name and app name to the deploy sub-command.
258
- deployOpts .name = * o . svcName
373
+ deployOpts .name = o . initWkldVars . name
259
374
deployOpts .appName = * o .appName
260
375
}
261
376
@@ -265,6 +380,22 @@ func (o *initOpts) deploySvc() error {
265
380
return o .deploySvcCmd .Execute ()
266
381
}
267
382
383
+ func (o * initOpts ) deployJob () error {
384
+ if ! o .ShouldDeploy {
385
+ return nil
386
+ }
387
+ if deployOpts , ok := o .deployJobCmd .(* deployJobOpts ); ok {
388
+ // Set the service's name and app name to the deploy sub-command.
389
+ deployOpts .name = o .initWkldVars .name
390
+ deployOpts .appName = * o .appName
391
+ }
392
+
393
+ if err := o .deployJobCmd .Ask (); err != nil {
394
+ return err
395
+ }
396
+ return o .deployJobCmd .Execute ()
397
+ }
398
+
268
399
func (o * initOpts ) askShouldDeploy () error {
269
400
v , err := o .prompt .Confirm (initShouldDeployPrompt , initShouldDeployHelpPrompt , prompt .WithFinalMessage ("Deploy:" ))
270
401
if err != nil {
@@ -294,20 +425,24 @@ func BuildInitCmd() *cobra.Command {
294
425
log .Info ("\n No problem, you can deploy your service later:\n " )
295
426
log .Infof ("- Run %s to create your staging environment.\n " ,
296
427
color .HighlightCode (fmt .Sprintf ("copilot env init --name %s --profile %s --app %s" , defaultEnvironmentName , defaultEnvironmentProfile , * opts .appName )))
297
- for _ , followup := range opts .initSvcCmd .RecommendedActions () {
428
+ for _ , followup := range opts .initWlCmd .RecommendedActions () {
298
429
log .Infof ("- %s\n " , followup )
299
430
}
300
431
}
301
432
return nil
302
433
}),
303
434
}
304
435
cmd .Flags ().StringVarP (& vars .appName , appFlag , appFlagShort , tryReadingAppName (), appFlagDescription )
305
- cmd .Flags ().StringVarP (& vars .svcName , svcFlag , svcFlagShort , "" , svcFlagDescription )
306
- cmd .Flags ().StringVarP (& vars .svcType , svcTypeFlag , svcTypeFlagShort , "" , svcTypeFlagDescription )
436
+ cmd .Flags ().StringVarP (& vars .svcName , nameFlag , svcFlagShort , "" , svcFlagDescription )
437
+ cmd .Flags ().StringVarP (& vars .wkldType , typeFlag , svcTypeFlagShort , "" , wkldTypeFlagDescription )
307
438
cmd .Flags ().StringVarP (& vars .dockerfilePath , dockerFileFlag , dockerFileFlagShort , "" , dockerFileFlagDescription )
439
+ cmd .Flags ().StringVarP (& vars .image , imageFlag , imageFlagShort , "" , imageFlagDescription )
308
440
cmd .Flags ().BoolVar (& vars .shouldDeploy , deployFlag , false , deployTestFlagDescription )
309
441
cmd .Flags ().StringVar (& vars .imageTag , imageTagFlag , "" , imageTagFlagDescription )
310
442
cmd .Flags ().Uint16Var (& vars .port , svcPortFlag , 0 , svcPortFlagDescription )
443
+ cmd .Flags ().StringVar (& vars .schedule , scheduleFlag , "" , scheduleFlagDescription )
444
+ cmd .Flags ().StringVar (& vars .timeout , timeoutFlag , "" , timeoutFlagDescription )
445
+ cmd .Flags ().IntVar (& vars .retries , retriesFlag , 0 , retriesFlagDescription )
311
446
cmd .SetUsageTemplate (template .Usage )
312
447
cmd .Annotations = map [string ]string {
313
448
"group" : group .GettingStarted ,
0 commit comments