From ccf1d0cde84e3f911b1feb98adc58b388cca3d88 Mon Sep 17 00:00:00 2001 From: sakazuki Date: Sat, 18 Nov 2023 15:48:23 +0900 Subject: [PATCH 1/7] Add init_container support in Azure Container Apps refs: #23824 --- .../containerapps/helpers/container_apps.go | 231 +++++++++++++++++- website/docs/d/container_app.html.markdown | 24 ++ website/docs/r/container_app.html.markdown | 30 +++ 3 files changed, 280 insertions(+), 5 deletions(-) diff --git a/internal/services/containerapps/helpers/container_apps.go b/internal/services/containerapps/helpers/container_apps.go index 50a1e8745b90..0d640ba24972 100644 --- a/internal/services/containerapps/helpers/container_apps.go +++ b/internal/services/containerapps/helpers/container_apps.go @@ -686,6 +686,7 @@ func ContainerAppEnvironmentDaprMetadataSchema() *pluginsdk.Schema { type ContainerTemplate struct { Containers []Container `tfschema:"container"` + InitContainers []BaseContainer `tfschema:"init_container"` Suffix string `tfschema:"revision_suffix"` MinReplicas int `tfschema:"min_replicas"` MaxReplicas int `tfschema:"max_replicas"` @@ -705,6 +706,8 @@ func ContainerTemplateSchema() *pluginsdk.Schema { Schema: map[string]*pluginsdk.Schema{ "container": ContainerAppContainerSchema(), + "init_container": InitContainerAppContainerSchema(), + "min_replicas": { Type: pluginsdk.TypeInt, Optional: true, @@ -750,6 +753,8 @@ func ContainerTemplateSchemaComputed() *pluginsdk.Schema { Schema: map[string]*pluginsdk.Schema{ "container": ContainerAppContainerSchemaComputed(), + "init_container": InitContainerAppContainerSchemaComputed(), + "min_replicas": { Type: pluginsdk.TypeInt, Computed: true, @@ -788,8 +793,9 @@ func ExpandContainerAppTemplate(input []ContainerTemplate, metadata sdk.Resource config := input[0] template := &containerapps.Template{ - Containers: expandContainerAppContainers(config.Containers), - Volumes: expandContainerAppVolumes(config.Volumes), + Containers: expandContainerAppContainers(config.Containers), + InitContainers: expandInitContainerAppContainers(config.InitContainers), + Volumes: expandContainerAppVolumes(config.Volumes), } if config.MaxReplicas != 0 { @@ -828,9 +834,10 @@ func FlattenContainerAppTemplate(input *containerapps.Template) []ContainerTempl return []ContainerTemplate{} } result := ContainerTemplate{ - Containers: flattenContainerAppContainers(input.Containers), - Suffix: pointer.From(input.RevisionSuffix), - Volumes: flattenContainerAppVolumes(input.Volumes), + Containers: flattenContainerAppContainers(input.Containers), + InitContainers: flattenInitContainerAppContainers(input.InitContainers), + Suffix: pointer.From(input.RevisionSuffix), + Volumes: flattenContainerAppVolumes(input.Volumes), } if scale := input.Scale; scale != nil { @@ -1007,6 +1014,213 @@ func ContainerAppContainerSchemaComputed() *pluginsdk.Schema { } } +type BaseContainer struct { + Name string `tfschema:"name"` + Image string `tfschema:"image"` + CPU float64 `tfschema:"cpu"` + Memory string `tfschema:"memory"` + EphemeralStorage string `tfschema:"ephemeral_storage"` + Env []ContainerEnvVar `tfschema:"env"` + Args []string `tfschema:"args"` + Command []string `tfschema:"command"` + VolumeMounts []ContainerVolumeMount `tfschema:"volume_mounts"` +} + +func InitContainerAppContainerSchema() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.ContainerAppContainerName, + Description: "The name of the container.", + }, + + "image": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + Description: "The image to use to create the container.", + }, + + "cpu": { + Type: pluginsdk.TypeFloat, + Optional: true, + ValidateFunc: validate.ContainerCpu, + Description: "The amount of vCPU to allocate to the container. Possible values include `0.25`, `0.5`, `0.75`, `1.0`, `1.25`, `1.5`, `1.75`, and `2.0`. **NOTE:** `cpu` and `memory` must be specified in `0.25'/'0.5Gi` combination increments. e.g. `1.0` / `2.0` or `0.5` / `1.0`", + }, + + "memory": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "0.5Gi", + "1Gi", + "1.5Gi", + "2Gi", + "2.5Gi", + "3Gi", + "3.5Gi", + "4Gi", + }, false), + Description: "The amount of memory to allocate to the container. Possible values include `0.5Gi`, `1.0Gi`, `1.5Gi`, `2.0Gi`, `2.5Gi`, `3.0Gi`, `3.5Gi`, and `4.0Gi`. **NOTE:** `cpu` and `memory` must be specified in `0.25'/'0.5Gi` combination increments. e.g. `1.25` / `2.5Gi` or `0.75` / `1.5Gi`", + }, + + "ephemeral_storage": { + Type: pluginsdk.TypeString, + Computed: true, + Description: "The amount of ephemeral storage available to the Container App.", + }, + + "env": ContainerEnvVarSchema(), + + "args": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + Description: "A list of args to pass to the container.", + }, + + "command": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + Description: "A command to pass to the container to override the default. This is provided as a list of command line elements without spaces.", + }, + + "volume_mounts": ContainerVolumeMountSchema(), + }, + }, + } +} + +func InitContainerAppContainerSchemaComputed() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + Description: "The name of the container.", + }, + + "image": { + Type: pluginsdk.TypeString, + Computed: true, + Description: "The image to use to create the container.", + }, + + "cpu": { + Type: pluginsdk.TypeFloat, + Computed: true, + Description: "The amount of vCPU to allocate to the container. Possible values include `0.25`, `0.5`, `0.75`, `1.0`, `1.25`, `1.5`, `1.75`, and `2.0`. **NOTE:** `cpu` and `memory` must be specified in `0.25'/'0.5Gi` combination increments. e.g. `1.0` / `2.0` or `0.5` / `1.0`", + }, + + "memory": { + Type: pluginsdk.TypeString, + Computed: true, + Description: "The amount of memory to allocate to the container. Possible values include `0.5Gi`, `1.0Gi`, `1.5Gi`, `2.0Gi`, `2.5Gi`, `3.0Gi`, `3.5Gi`, and `4.0Gi`. **NOTE:** `cpu` and `memory` must be specified in `0.25'/'0.5Gi` combination increments. e.g. `1.25` / `2.5Gi` or `0.75` / `1.5Gi`", + }, + + "ephemeral_storage": { + Type: pluginsdk.TypeString, + Computed: true, + Description: "The amount of ephemeral storage available to the Container App.", + }, + + "env": ContainerEnvVarSchemaComputed(), + + "args": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + Description: "A list of args to pass to the container.", + }, + + "command": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + Description: "A command to pass to the container to override the default. This is provided as a list of command line elements without spaces.", + }, + + "volume_mounts": ContainerVolumeMountSchemaComputed(), + }, + }, + } +} + +func expandInitContainerAppContainers(input []BaseContainer) *[]containerapps.BaseContainer { + if input == nil { + return nil + } + + result := make([]containerapps.BaseContainer, 0) + for _, v := range input { + container := containerapps.BaseContainer{ + Env: expandInitContainerEnvVar(v), + Image: pointer.To(v.Image), + Name: pointer.To(v.Name), + Resources: &containerapps.ContainerResources{ + Cpu: pointer.To(v.CPU), + EphemeralStorage: pointer.To(v.EphemeralStorage), + Memory: pointer.To(v.Memory), + }, + VolumeMounts: expandContainerVolumeMounts(v.VolumeMounts), + } + if len(v.Args) != 0 { + container.Args = pointer.To(v.Args) + } + if len(v.Command) != 0 { + container.Command = pointer.To(v.Command) + } + + result = append(result, container) + } + + return &result +} + +func flattenInitContainerAppContainers(input *[]containerapps.BaseContainer) []BaseContainer { + if input == nil || len(*input) == 0 { + return []BaseContainer{} + } + result := make([]BaseContainer, 0) + for _, v := range *input { + container := BaseContainer{ + Name: pointer.From(v.Name), + Image: pointer.From(v.Image), + Args: pointer.From(v.Args), + Command: pointer.From(v.Command), + Env: flattenContainerEnvVar(v.Env), + VolumeMounts: flattenContainerVolumeMounts(v.VolumeMounts), + } + + if resources := v.Resources; resources != nil { + container.CPU = pointer.From(resources.Cpu) + container.Memory = pointer.From(resources.Memory) + container.EphemeralStorage = pointer.From(resources.EphemeralStorage) + } + + result = append(result, container) + } + return result +} + func expandContainerAppContainers(input []Container) *[]containerapps.Container { if input == nil { return nil @@ -1335,6 +1549,13 @@ func ContainerEnvVarSchemaComputed() *pluginsdk.Schema { } } +func expandInitContainerEnvVar(input BaseContainer) *[]containerapps.EnvironmentVar { + container := Container{ + Env: input.Env, + } + return expandContainerEnvVar(container) +} + func expandContainerEnvVar(input Container) *[]containerapps.EnvironmentVar { envs := make([]containerapps.EnvironmentVar, 0) if input.Env == nil || len(input.Env) == 0 { diff --git a/website/docs/d/container_app.html.markdown b/website/docs/d/container_app.html.markdown index d3816280f2bd..d448ba5a7d2e 100644 --- a/website/docs/d/container_app.html.markdown +++ b/website/docs/d/container_app.html.markdown @@ -63,6 +63,8 @@ A `secret` block supports the following: A `template` block supports the following: +* `init_container` - One or more `init_container` blocks as detailed below. + * `container` - One or more `container` blocks as detailed below. * `max_replicas` - The maximum number of replicas for this container. @@ -85,6 +87,28 @@ A `volume` block supports the following: --- +A `init_container` block supports the following: + +* `args` - A list of extra arguments to pass to the container. + +* `command` - A command to pass to the container to override the default. This is provided as a list of command line elements without spaces. + +* `cpu` - The amount of vCPU to allocate to the container. Possible values include `0.25`, `0.5`, `0.75`, `1.0`, `1.25`, `1.5`, `1.75`, and `2.0`. + +* `env` - One or more `env` blocks as detailed below. + +* `ephemeral_storage` - The amount of ephemeral storage available to the Container App. + +* `image` - The image to use to create the container. + +* `memory` - The amount of memory to allocate to the container. Possible values include `0.5Gi`, `1Gi`, `1.5Gi`, `2Gi`, `2.5Gi`, `3Gi`, `3.5Gi`, and `4Gi`. + +* `name` - The name of the container + +* `volume_mounts` - A `volume_mounts` block as detailed below. + +--- + A `container` block supports the following: * `args` - A list of extra arguments to pass to the container. diff --git a/website/docs/r/container_app.html.markdown b/website/docs/r/container_app.html.markdown index 2419e71b7e3e..a26bf5a1d006 100644 --- a/website/docs/r/container_app.html.markdown +++ b/website/docs/r/container_app.html.markdown @@ -91,6 +91,8 @@ A `secret` block supports the following: A `template` block supports the following: +* `init_container` - (Optional) The definition of an init container that is part of the group as documented in the `init_container` block below. Changing this forces a new resource to be created. + * `container` - (Required) One or more `container` blocks as detailed below. * `max_replicas` - (Optional) The maximum number of replicas for this container. @@ -173,6 +175,34 @@ A `volume` block supports the following: --- +An `init_container` block supports: + +* `args` - (Optional) A list of extra arguments to pass to the container. + +* `command` - (Optional) A command to pass to the container to override the default. This is provided as a list of command line elements without spaces. + +* `cpu` - (Required) The amount of vCPU to allocate to the container. Possible values include `0.25`, `0.5`, `0.75`, `1.0`, `1.25`, `1.5`, `1.75`, and `2.0`. + +~> **NOTE:** `cpu` and `memory` must be specified in `0.25'/'0.5Gi` combination increments. e.g. `1.0` / `2.0` or `0.5` / `1.0` + +* `env` - (Optional) One or more `env` blocks as detailed below. + +* `ephemeral_storage` - The amount of ephemeral storage available to the Container App. + +~> **NOTE:** `ephemeral_storage` is currently in preview and not configurable at this time. + +* `image` - (Required) The image to use to create the container. + +* `memory` - (Required) The amount of memory to allocate to the container. Possible values are `0.5Gi`, `1Gi`, `1.5Gi`, `2Gi`, `2.5Gi`, `3Gi`, `3.5Gi` and `4Gi`. + +~> **NOTE:** `cpu` and `memory` must be specified in `0.25'/'0.5Gi` combination increments. e.g. `1.25` / `2.5Gi` or `0.75` / `1.5Gi` + +* `name` - (Required) The name of the container + +* `volume_mounts` - (Optional) A `volume_mounts` block as detailed below. + +--- + A `container` block supports the following: * `args` - (Optional) A list of extra arguments to pass to the container. From e7a05725c4407e1c451a7b82addbe399d31f2b64 Mon Sep 17 00:00:00 2001 From: sakazuki Date: Thu, 7 Dec 2023 11:54:57 +0900 Subject: [PATCH 2/7] add test --- .../containerapps/container_app_resource_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/internal/services/containerapps/container_app_resource_test.go b/internal/services/containerapps/container_app_resource_test.go index 03ba2e6ee6d6..7c7c94a45935 100644 --- a/internal/services/containerapps/container_app_resource_test.go +++ b/internal/services/containerapps/container_app_resource_test.go @@ -641,6 +641,17 @@ resource "azurerm_container_app" "test" { } } + init_container { + name = "init-cont-%[2]d" + image = "jackofallops/azure-containerapps-python-acctest:v0.0.1" + cpu = 0.25 + memory = "0.5Gi" + volume_mounts { + name = azurerm_container_app_environment_storage.test.name + path = "/tmp/testdata" + } + } + volume { name = azurerm_container_app_environment_storage.test.name storage_type = "AzureFile" From 5c684a013afb893449d44bdd5e131d543a7b3a2b Mon Sep 17 00:00:00 2001 From: sakazuki Date: Fri, 8 Dec 2023 07:49:44 +0900 Subject: [PATCH 3/7] Update internal/services/containerapps/helpers/container_apps.go LGTM Co-authored-by: jackofallops <11830746+jackofallops@users.noreply.github.com> --- .../containerapps/helpers/container_apps.go | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/internal/services/containerapps/helpers/container_apps.go b/internal/services/containerapps/helpers/container_apps.go index 0d640ba24972..a8676cf3b18b 100644 --- a/internal/services/containerapps/helpers/container_apps.go +++ b/internal/services/containerapps/helpers/container_apps.go @@ -1550,10 +1550,25 @@ func ContainerEnvVarSchemaComputed() *pluginsdk.Schema { } func expandInitContainerEnvVar(input BaseContainer) *[]containerapps.EnvironmentVar { - container := Container{ - Env: input.Env, + envs := make([]containerapps.EnvironmentVar, 0) + if input.Env == nil || len(input.Env) == 0 { + return &envs } - return expandContainerEnvVar(container) + + for _, v := range input.Env { + env := containerapps.EnvironmentVar{ + Name: pointer.To(v.Name), + } + if v.SecretReference != "" { + env.SecretRef = pointer.To(v.SecretReference) + } else { + env.Value = pointer.To(v.Value) + } + + envs = append(envs, env) + } + + return &envs } func expandContainerEnvVar(input Container) *[]containerapps.EnvironmentVar { From ea0b0fa7ddb319d3c875dba37acb43f1ee63709c Mon Sep 17 00:00:00 2001 From: sakazuki Date: Fri, 8 Dec 2023 08:00:04 +0900 Subject: [PATCH 4/7] Update internal/services/containerapps/helpers/container_apps.go Yes, it is correct. Co-authored-by: jackofallops <11830746+jackofallops@users.noreply.github.com> --- internal/services/containerapps/helpers/container_apps.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/services/containerapps/helpers/container_apps.go b/internal/services/containerapps/helpers/container_apps.go index a8676cf3b18b..fe0ccdb6dd45 100644 --- a/internal/services/containerapps/helpers/container_apps.go +++ b/internal/services/containerapps/helpers/container_apps.go @@ -1030,6 +1030,7 @@ func InitContainerAppContainerSchema() *pluginsdk.Schema { return &pluginsdk.Schema{ Type: pluginsdk.TypeList, Optional: true, + ForceNew: true, MinItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ From 56660d80bacc76534987b7a9877a634ab3162c6b Mon Sep 17 00:00:00 2001 From: sakazuki Date: Fri, 8 Dec 2023 08:48:56 +0900 Subject: [PATCH 5/7] Revert "Update internal/services/containerapps/helpers/container_apps.go" This reverts commit ea0b0fa7ddb319d3c875dba37acb43f1ee63709c. --- internal/services/containerapps/helpers/container_apps.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/services/containerapps/helpers/container_apps.go b/internal/services/containerapps/helpers/container_apps.go index fe0ccdb6dd45..a8676cf3b18b 100644 --- a/internal/services/containerapps/helpers/container_apps.go +++ b/internal/services/containerapps/helpers/container_apps.go @@ -1030,7 +1030,6 @@ func InitContainerAppContainerSchema() *pluginsdk.Schema { return &pluginsdk.Schema{ Type: pluginsdk.TypeList, Optional: true, - ForceNew: true, MinItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ From 9122e03f7b5dfad60b57d89efcd4e602f90b4408 Mon Sep 17 00:00:00 2001 From: sakazuki Date: Fri, 8 Dec 2023 08:50:11 +0900 Subject: [PATCH 6/7] fix the init_container resource description. The behavior of `init_container` should be the same as the behavior of `container` in `azurerm_container_app`. --- website/docs/r/container_app.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/container_app.html.markdown b/website/docs/r/container_app.html.markdown index a26bf5a1d006..f841b976ede5 100644 --- a/website/docs/r/container_app.html.markdown +++ b/website/docs/r/container_app.html.markdown @@ -91,7 +91,7 @@ A `secret` block supports the following: A `template` block supports the following: -* `init_container` - (Optional) The definition of an init container that is part of the group as documented in the `init_container` block below. Changing this forces a new resource to be created. +* `init_container` - (Optional) The definition of an init container that is part of the group as documented in the `init_container` block below. * `container` - (Required) One or more `container` blocks as detailed below. From 9a6b5cc2dc16cfe3549c8fc052b9793cb0551f7b Mon Sep 17 00:00:00 2001 From: sakazuki Date: Fri, 8 Dec 2023 09:01:49 +0900 Subject: [PATCH 7/7] formatted --- internal/services/containerapps/helpers/container_apps.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/containerapps/helpers/container_apps.go b/internal/services/containerapps/helpers/container_apps.go index a8676cf3b18b..b53b89177e7e 100644 --- a/internal/services/containerapps/helpers/container_apps.go +++ b/internal/services/containerapps/helpers/container_apps.go @@ -1567,7 +1567,7 @@ func expandInitContainerEnvVar(input BaseContainer) *[]containerapps.Environment envs = append(envs, env) } - + return &envs }