Skip to content

Commit 382a471

Browse files
Mia-Crossremyleone
andauthored
feat(serverless): add command and args to container resource (#3151)
Co-authored-by: Rémy Léone <[email protected]>
1 parent 75f2024 commit 382a471

File tree

7 files changed

+3102
-119
lines changed

7 files changed

+3102
-119
lines changed

docs/resources/container.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ resource "scaleway_container" "main" {
3636
protocol = "http1"
3737
deploy = true
3838
39+
command = [ "bash", "-c", "script.sh" ]
40+
args = [ "some", "args" ]
41+
3942
environment_variables = {
4043
"foo" = "var"
4144
}
@@ -104,6 +107,10 @@ The following arguments are supported:
104107

105108
- `local_storage_limit` - (Optional) Local storage limit of the container (in MB)
106109

110+
- `command` - (Optional) Command executed when the container starts. This overrides the default command defined in the container image. This is usually the main executable, or entry point script to run.
111+
112+
- `args` - (Optional) Arguments passed to the command specified in the "command" field. These override the default arguments from the container image, and behave like command-line parameters.
113+
107114
Note that if you want to use your own configuration, you must consult our configuration [restrictions](https://www.scaleway.com/en/docs/serverless-containers/reference-content/containers-limitations/#configuration-restrictions) section.
108115

109116
## Attributes Reference

go.mod

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,13 @@ require (
154154
go.opentelemetry.io/otel/metric v1.35.0 // indirect
155155
go.opentelemetry.io/otel/trace v1.35.0 // indirect
156156
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
157-
golang.org/x/mod v0.24.0 // indirect
158-
golang.org/x/net v0.39.0 // indirect
159-
golang.org/x/sync v0.14.0 // indirect
157+
golang.org/x/mod v0.25.0 // indirect
158+
golang.org/x/net v0.40.0 // indirect
159+
golang.org/x/sync v0.15.0 // indirect
160160
golang.org/x/sys v0.33.0 // indirect
161-
golang.org/x/text v0.25.0 // indirect
161+
golang.org/x/text v0.26.0 // indirect
162162
golang.org/x/time v0.3.0 // indirect
163-
golang.org/x/tools v0.30.0 // indirect
163+
golang.org/x/tools v0.33.0 // indirect
164164
google.golang.org/appengine v1.6.8 // indirect
165165
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect
166166
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 // indirect

go.sum

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
587587
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
588588
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
589589
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
590-
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
591-
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
590+
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
591+
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
592592
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
593593
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
594594
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -624,8 +624,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
624624
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
625625
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
626626
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
627-
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
628-
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
627+
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
628+
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
629629
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
630630
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
631631
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -647,8 +647,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
647647
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
648648
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
649649
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
650-
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
651-
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
650+
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
651+
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
652652
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
653653
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
654654
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -720,8 +720,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
720720
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
721721
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
722722
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
723-
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
724-
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
723+
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
724+
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
725725
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
726726
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
727727
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -780,8 +780,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
780780
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
781781
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
782782
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
783-
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
784-
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
783+
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
784+
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
785785
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
786786
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
787787
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

internal/services/container/container.go

Lines changed: 17 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,18 @@ func ResourceContainer() *schema.Resource {
251251
Optional: true,
252252
Computed: true,
253253
},
254+
"command": {
255+
Type: schema.TypeList,
256+
Elem: &schema.Schema{Type: schema.TypeString},
257+
Optional: true,
258+
Description: "Command executed when the container starts. Overrides the command from the container image.",
259+
},
260+
"args": {
261+
Type: schema.TypeList,
262+
Elem: &schema.Schema{Type: schema.TypeString},
263+
Optional: true,
264+
Description: "Arguments passed to the command from the command \"field\". Overrides the arguments from the container image.",
265+
},
254266
// computed
255267
"status": {
256268
Type: schema.TypeString,
@@ -370,6 +382,8 @@ func ResourceContainerRead(ctx context.Context, d *schema.ResourceData, m interf
370382
_ = d.Set("local_storage_limit", int(co.LocalStorageLimit))
371383
_ = d.Set("secret_environment_variables", flattenContainerSecrets(co.SecretEnvironmentVariables))
372384
_ = d.Set("tags", types.FlattenSliceString(co.Tags))
385+
_ = d.Set("command", types.FlattenSliceString(co.Command))
386+
_ = d.Set("args", types.FlattenSliceString(co.Args))
373387

374388
return nil
375389
}
@@ -394,110 +408,9 @@ func ResourceContainerUpdate(ctx context.Context, d *schema.ResourceData, m inte
394408
}
395409

396410
// update container
397-
req := &container.UpdateContainerRequest{
398-
Region: region,
399-
ContainerID: containerID,
400-
}
401-
402-
if d.HasChanges("environment_variables") {
403-
envVariablesRaw := d.Get("environment_variables")
404-
req.EnvironmentVariables = types.ExpandMapPtrStringString(envVariablesRaw)
405-
}
406-
407-
if d.HasChanges("secret_environment_variables") {
408-
oldEnv, newEnv := d.GetChange("secret_environment_variables")
409-
req.SecretEnvironmentVariables = filterSecretEnvsToPatch(expandContainerSecrets(oldEnv), expandContainerSecrets(newEnv))
410-
}
411-
412-
if d.HasChange("tags") {
413-
req.Tags = types.ExpandUpdatedStringsPtr(d.Get("tags"))
414-
}
415-
416-
if d.HasChanges("min_scale") {
417-
req.MinScale = scw.Uint32Ptr(uint32(d.Get("min_scale").(int)))
418-
}
419-
420-
if d.HasChanges("max_scale") {
421-
req.MaxScale = scw.Uint32Ptr(uint32(d.Get("max_scale").(int)))
422-
}
423-
424-
if d.HasChanges("memory_limit") {
425-
req.MemoryLimit = scw.Uint32Ptr(uint32(d.Get("memory_limit").(int)))
426-
}
427-
428-
if d.HasChanges("cpu_limit") {
429-
req.CPULimit = scw.Uint32Ptr(uint32(d.Get("cpu_limit").(int)))
430-
}
431-
432-
if d.HasChanges("timeout") {
433-
req.Timeout = &scw.Duration{Seconds: int64(d.Get("timeout").(int))}
434-
}
435-
436-
if d.HasChanges("privacy") {
437-
req.Privacy = container.ContainerPrivacy(*types.ExpandStringPtr(d.Get("privacy")))
438-
}
439-
440-
if d.HasChanges("description") {
441-
req.Description = types.ExpandUpdatedStringPtr(d.Get("description"))
442-
}
443-
444-
if d.HasChanges("registry_image") {
445-
req.RegistryImage = types.ExpandStringPtr(d.Get("registry_image"))
446-
}
447-
448-
if d.HasChanges("max_concurrency") {
449-
req.MaxConcurrency = scw.Uint32Ptr(uint32(d.Get("max_concurrency").(int))) //nolint:staticcheck
450-
}
451-
452-
if d.HasChanges("protocol") {
453-
req.Protocol = container.ContainerProtocol(*types.ExpandStringPtr(d.Get("protocol")))
454-
}
455-
456-
if d.HasChanges("port") {
457-
req.Port = scw.Uint32Ptr(uint32(d.Get("port").(int)))
458-
}
459-
460-
if d.HasChanges("http_option") {
461-
req.HTTPOption = container.ContainerHTTPOption(d.Get("http_option").(string))
462-
}
463-
464-
if d.HasChanges("deploy") {
465-
req.Redeploy = types.ExpandBoolPtr(d.Get("deploy"))
466-
}
467-
468-
if d.HasChanges("sandbox") {
469-
req.Sandbox = container.ContainerSandbox(d.Get("sandbox").(string))
470-
}
471-
472-
if d.HasChanges("health_check") {
473-
healthCheck := d.Get("health_check")
474-
475-
healthCheckReq, errExpandHealthCheck := expandHealthCheck(healthCheck)
476-
if errExpandHealthCheck != nil {
477-
return diag.FromErr(errExpandHealthCheck)
478-
}
479-
480-
req.HealthCheck = healthCheckReq
481-
}
482-
483-
if d.HasChanges("scaling_option") {
484-
scalingOption := d.Get("scaling_option")
485-
486-
scalingOptionReq, err := expandScalingOptions(scalingOption)
487-
if err != nil {
488-
return diag.FromErr(err)
489-
}
490-
491-
req.ScalingOption = scalingOptionReq
492-
}
493-
494-
imageHasChanged := d.HasChanges("registry_sha256")
495-
if imageHasChanged {
496-
req.Redeploy = &imageHasChanged
497-
}
498-
499-
if d.HasChanges("local_storage_limit") {
500-
req.LocalStorageLimit = scw.Uint32Ptr(uint32(d.Get("local_storage_limit").(int)))
411+
req, err := setUpdateContainerRequest(d, region, containerID)
412+
if err != nil {
413+
return diag.FromErr(err)
501414
}
502415

503416
con, err := api.UpdateContainer(req, scw.WithContext(ctx))

internal/services/container/container_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,88 @@ func TestAccContainer_ScalingOption(t *testing.T) {
563563
})
564564
}
565565

566+
func TestAccContainer_CommandAndArgs(t *testing.T) {
567+
tt := acctest.NewTestTools(t)
568+
defer tt.Cleanup()
569+
resource.ParallelTest(t, resource.TestCase{
570+
PreCheck: func() { acctest.PreCheck(t) },
571+
ProviderFactories: tt.ProviderFactories,
572+
CheckDestroy: isContainerDestroyed(tt),
573+
Steps: []resource.TestStep{
574+
{
575+
Config: `
576+
resource scaleway_container_namespace main {}
577+
578+
resource scaleway_container main {
579+
namespace_id = scaleway_container_namespace.main.id
580+
command = [ "bash", "-c", "my-script.sh" ]
581+
}
582+
`,
583+
Check: resource.ComposeTestCheckFunc(
584+
isContainerPresent(tt, "scaleway_container.main"),
585+
resource.TestCheckResourceAttr("scaleway_container.main", "command.#", "3"),
586+
resource.TestCheckResourceAttr("scaleway_container.main", "command.0", "bash"),
587+
resource.TestCheckResourceAttr("scaleway_container.main", "command.1", "-c"),
588+
resource.TestCheckResourceAttr("scaleway_container.main", "command.2", "my-script.sh"),
589+
resource.TestCheckResourceAttr("scaleway_container.main", "args.#", "0"),
590+
),
591+
},
592+
{
593+
Config: `
594+
resource scaleway_container_namespace main {}
595+
596+
resource scaleway_container main {
597+
namespace_id = scaleway_container_namespace.main.id
598+
command = [ "bash", "-c", "my-script.sh" ]
599+
args = [ "some", "args" ]
600+
}
601+
`,
602+
Check: resource.ComposeTestCheckFunc(
603+
isContainerPresent(tt, "scaleway_container.main"),
604+
resource.TestCheckResourceAttr("scaleway_container.main", "command.#", "3"),
605+
resource.TestCheckResourceAttr("scaleway_container.main", "command.0", "bash"),
606+
resource.TestCheckResourceAttr("scaleway_container.main", "command.1", "-c"),
607+
resource.TestCheckResourceAttr("scaleway_container.main", "command.2", "my-script.sh"),
608+
resource.TestCheckResourceAttr("scaleway_container.main", "args.#", "2"),
609+
resource.TestCheckResourceAttr("scaleway_container.main", "args.0", "some"),
610+
resource.TestCheckResourceAttr("scaleway_container.main", "args.1", "args"),
611+
),
612+
},
613+
{
614+
Config: `
615+
resource scaleway_container_namespace main {}
616+
617+
resource scaleway_container main {
618+
namespace_id = scaleway_container_namespace.main.id
619+
args = [ "some", "args" ]
620+
}
621+
`,
622+
Check: resource.ComposeTestCheckFunc(
623+
isContainerPresent(tt, "scaleway_container.main"),
624+
resource.TestCheckResourceAttr("scaleway_container.main", "command.#", "0"),
625+
resource.TestCheckResourceAttr("scaleway_container.main", "args.#", "2"),
626+
resource.TestCheckResourceAttr("scaleway_container.main", "args.0", "some"),
627+
resource.TestCheckResourceAttr("scaleway_container.main", "args.1", "args"),
628+
),
629+
},
630+
{
631+
Config: `
632+
resource scaleway_container_namespace main {}
633+
634+
resource scaleway_container main {
635+
namespace_id = scaleway_container_namespace.main.id
636+
}
637+
`,
638+
Check: resource.ComposeTestCheckFunc(
639+
isContainerPresent(tt, "scaleway_container.main"),
640+
resource.TestCheckResourceAttr("scaleway_container.main", "command.#", "0"),
641+
resource.TestCheckResourceAttr("scaleway_container.main", "args.#", "0"),
642+
),
643+
},
644+
},
645+
})
646+
}
647+
566648
func isContainerPresent(tt *acctest.TestTools, n string) resource.TestCheckFunc {
567649
return func(state *terraform.State) error {
568650
rs, ok := state.RootModule().Resources[n]

0 commit comments

Comments
 (0)