Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 7 additions & 0 deletions docs/resources/container.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ resource "scaleway_container" "main" {
protocol = "http1"
deploy = true

command = [ "bash", "-c", "script.sh" ]
args = [ "some", "args" ]

environment_variables = {
"foo" = "var"
}
Expand Down Expand Up @@ -104,6 +107,10 @@ The following arguments are supported:

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

- `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.

- `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.

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.

## Attributes Reference
Expand Down
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,13 @@ require (
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.30.0 // indirect
golang.org/x/tools v0.33.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 // indirect
Expand Down
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -587,8 +587,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -624,8 +624,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand All @@ -647,8 +647,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down Expand Up @@ -720,8 +720,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down Expand Up @@ -780,8 +780,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
121 changes: 17 additions & 104 deletions internal/services/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,18 @@ func ResourceContainer() *schema.Resource {
Optional: true,
Computed: true,
},
"command": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Description: "Command executed when the container starts. Overrides the command from the container image.",
},
"args": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Description: "Arguments passed to the command from the command \"field\". Overrides the arguments from the container image.",
},
// computed
"status": {
Type: schema.TypeString,
Expand Down Expand Up @@ -370,6 +382,8 @@ func ResourceContainerRead(ctx context.Context, d *schema.ResourceData, m interf
_ = d.Set("local_storage_limit", int(co.LocalStorageLimit))
_ = d.Set("secret_environment_variables", flattenContainerSecrets(co.SecretEnvironmentVariables))
_ = d.Set("tags", types.FlattenSliceString(co.Tags))
_ = d.Set("command", types.FlattenSliceString(co.Command))
_ = d.Set("args", types.FlattenSliceString(co.Args))

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

// update container
req := &container.UpdateContainerRequest{
Region: region,
ContainerID: containerID,
}

if d.HasChanges("environment_variables") {
envVariablesRaw := d.Get("environment_variables")
req.EnvironmentVariables = types.ExpandMapPtrStringString(envVariablesRaw)
}

if d.HasChanges("secret_environment_variables") {
oldEnv, newEnv := d.GetChange("secret_environment_variables")
req.SecretEnvironmentVariables = filterSecretEnvsToPatch(expandContainerSecrets(oldEnv), expandContainerSecrets(newEnv))
}

if d.HasChange("tags") {
req.Tags = types.ExpandUpdatedStringsPtr(d.Get("tags"))
}

if d.HasChanges("min_scale") {
req.MinScale = scw.Uint32Ptr(uint32(d.Get("min_scale").(int)))
}

if d.HasChanges("max_scale") {
req.MaxScale = scw.Uint32Ptr(uint32(d.Get("max_scale").(int)))
}

if d.HasChanges("memory_limit") {
req.MemoryLimit = scw.Uint32Ptr(uint32(d.Get("memory_limit").(int)))
}

if d.HasChanges("cpu_limit") {
req.CPULimit = scw.Uint32Ptr(uint32(d.Get("cpu_limit").(int)))
}

if d.HasChanges("timeout") {
req.Timeout = &scw.Duration{Seconds: int64(d.Get("timeout").(int))}
}

if d.HasChanges("privacy") {
req.Privacy = container.ContainerPrivacy(*types.ExpandStringPtr(d.Get("privacy")))
}

if d.HasChanges("description") {
req.Description = types.ExpandUpdatedStringPtr(d.Get("description"))
}

if d.HasChanges("registry_image") {
req.RegistryImage = types.ExpandStringPtr(d.Get("registry_image"))
}

if d.HasChanges("max_concurrency") {
req.MaxConcurrency = scw.Uint32Ptr(uint32(d.Get("max_concurrency").(int))) //nolint:staticcheck
}

if d.HasChanges("protocol") {
req.Protocol = container.ContainerProtocol(*types.ExpandStringPtr(d.Get("protocol")))
}

if d.HasChanges("port") {
req.Port = scw.Uint32Ptr(uint32(d.Get("port").(int)))
}

if d.HasChanges("http_option") {
req.HTTPOption = container.ContainerHTTPOption(d.Get("http_option").(string))
}

if d.HasChanges("deploy") {
req.Redeploy = types.ExpandBoolPtr(d.Get("deploy"))
}

if d.HasChanges("sandbox") {
req.Sandbox = container.ContainerSandbox(d.Get("sandbox").(string))
}

if d.HasChanges("health_check") {
healthCheck := d.Get("health_check")

healthCheckReq, errExpandHealthCheck := expandHealthCheck(healthCheck)
if errExpandHealthCheck != nil {
return diag.FromErr(errExpandHealthCheck)
}

req.HealthCheck = healthCheckReq
}

if d.HasChanges("scaling_option") {
scalingOption := d.Get("scaling_option")

scalingOptionReq, err := expandScalingOptions(scalingOption)
if err != nil {
return diag.FromErr(err)
}

req.ScalingOption = scalingOptionReq
}

imageHasChanged := d.HasChanges("registry_sha256")
if imageHasChanged {
req.Redeploy = &imageHasChanged
}

if d.HasChanges("local_storage_limit") {
req.LocalStorageLimit = scw.Uint32Ptr(uint32(d.Get("local_storage_limit").(int)))
req, err := setUpdateContainerRequest(d, region, containerID)
if err != nil {
return diag.FromErr(err)
}

con, err := api.UpdateContainer(req, scw.WithContext(ctx))
Expand Down
82 changes: 82 additions & 0 deletions internal/services/container/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,88 @@ func TestAccContainer_ScalingOption(t *testing.T) {
})
}

func TestAccContainer_CommandAndArgs(t *testing.T) {
tt := acctest.NewTestTools(t)
defer tt.Cleanup()
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProviderFactories: tt.ProviderFactories,
CheckDestroy: isContainerDestroyed(tt),
Steps: []resource.TestStep{
{
Config: `
resource scaleway_container_namespace main {}

resource scaleway_container main {
namespace_id = scaleway_container_namespace.main.id
command = [ "bash", "-c", "my-script.sh" ]
}
`,
Check: resource.ComposeTestCheckFunc(
isContainerPresent(tt, "scaleway_container.main"),
resource.TestCheckResourceAttr("scaleway_container.main", "command.#", "3"),
resource.TestCheckResourceAttr("scaleway_container.main", "command.0", "bash"),
resource.TestCheckResourceAttr("scaleway_container.main", "command.1", "-c"),
resource.TestCheckResourceAttr("scaleway_container.main", "command.2", "my-script.sh"),
resource.TestCheckResourceAttr("scaleway_container.main", "args.#", "0"),
),
},
{
Config: `
resource scaleway_container_namespace main {}

resource scaleway_container main {
namespace_id = scaleway_container_namespace.main.id
command = [ "bash", "-c", "my-script.sh" ]
args = [ "some", "args" ]
}
`,
Check: resource.ComposeTestCheckFunc(
isContainerPresent(tt, "scaleway_container.main"),
resource.TestCheckResourceAttr("scaleway_container.main", "command.#", "3"),
resource.TestCheckResourceAttr("scaleway_container.main", "command.0", "bash"),
resource.TestCheckResourceAttr("scaleway_container.main", "command.1", "-c"),
resource.TestCheckResourceAttr("scaleway_container.main", "command.2", "my-script.sh"),
resource.TestCheckResourceAttr("scaleway_container.main", "args.#", "2"),
resource.TestCheckResourceAttr("scaleway_container.main", "args.0", "some"),
resource.TestCheckResourceAttr("scaleway_container.main", "args.1", "args"),
),
},
{
Config: `
resource scaleway_container_namespace main {}

resource scaleway_container main {
namespace_id = scaleway_container_namespace.main.id
args = [ "some", "args" ]
}
`,
Check: resource.ComposeTestCheckFunc(
isContainerPresent(tt, "scaleway_container.main"),
resource.TestCheckResourceAttr("scaleway_container.main", "command.#", "0"),
resource.TestCheckResourceAttr("scaleway_container.main", "args.#", "2"),
resource.TestCheckResourceAttr("scaleway_container.main", "args.0", "some"),
resource.TestCheckResourceAttr("scaleway_container.main", "args.1", "args"),
),
},
{
Config: `
resource scaleway_container_namespace main {}

resource scaleway_container main {
namespace_id = scaleway_container_namespace.main.id
}
`,
Check: resource.ComposeTestCheckFunc(
isContainerPresent(tt, "scaleway_container.main"),
resource.TestCheckResourceAttr("scaleway_container.main", "command.#", "0"),
resource.TestCheckResourceAttr("scaleway_container.main", "args.#", "0"),
),
},
},
})
}

func isContainerPresent(tt *acctest.TestTools, n string) resource.TestCheckFunc {
return func(state *terraform.State) error {
rs, ok := state.RootModule().Resources[n]
Expand Down
Loading
Loading