diff --git a/.changelog/40019.txt b/.changelog/40019.txt new file mode 100644 index 00000000000..feab9cf71ba --- /dev/null +++ b/.changelog/40019.txt @@ -0,0 +1,9 @@ +```release-note:enhancement +resource/aws_batch_job_definition: Add `init_containers` and `share_process_namespace` arguments +``` +```release-note:enhancement +resource/aws_batch_job_definition: Increase maximum number of `containers` arguments to 10 +``` +```release-note:enhancement +data-source/aws_batch_job_definition: Add `init_containers`, `share_process_namespace`, and `image_pull_secrets` attributes +``` diff --git a/internal/service/batch/job_definition.go b/internal/service/batch/job_definition.go index da7ef0dd6e1..0823b3b0f76 100644 --- a/internal/service/batch/job_definition.go +++ b/internal/service/batch/job_definition.go @@ -107,7 +107,7 @@ func resourceJobDefinition() *schema.Resource { "containers": { Type: schema.TypeList, Required: true, - MaxItems: 1, + MaxItems: 10, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "args": { @@ -242,6 +242,122 @@ func resourceJobDefinition() *schema.Resource { }, }, }, + "init_containers": { + Type: schema.TypeList, + Optional: true, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "args": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "command": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "env": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + names.AttrName: { + Type: schema.TypeString, + Required: true, + }, + names.AttrValue: { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "image": { + Type: schema.TypeString, + Required: true, + }, + "image_pull_policy": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(imagePullPolicy_Values(), false), + }, + names.AttrName: { + Type: schema.TypeString, + Optional: true, + }, + names.AttrResources: { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "limits": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "requests": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + "security_context": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "privileged": { + Type: schema.TypeBool, + Optional: true, + }, + "read_only_root_file_system": { + Type: schema.TypeBool, + Optional: true, + }, + "run_as_group": { + Type: schema.TypeInt, + Optional: true, + }, + "run_as_non_root": { + Type: schema.TypeBool, + Optional: true, + }, + "run_as_user": { + Type: schema.TypeInt, + Optional: true, + }, + }, + }, + }, + "volume_mounts": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mount_path": { + Type: schema.TypeString, + Required: true, + }, + names.AttrName: { + Type: schema.TypeString, + Required: true, + }, + "read_only": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + }, + }, + }, "metadata": { Type: schema.TypeList, Optional: true, @@ -260,6 +376,10 @@ func resourceJobDefinition() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "share_process_namespace": { + Type: schema.TypeBool, + Optional: true, + }, "volumes": { Type: schema.TypeList, Optional: true, @@ -1186,6 +1306,10 @@ func expandEKSPodProperties(tfMap map[string]interface{}) *awstypes.EksPodProper apiObject.ImagePullSecrets = expandImagePullSecrets(v.([]interface{})) } + if v, ok := tfMap["init_containers"]; ok { + apiObject.InitContainers = expandContainers(v.([]interface{})) + } + if v, ok := tfMap["metadata"].([]interface{}); ok && len(v) > 0 { if v, ok := v[0].(map[string]interface{})["labels"]; ok { apiObject.Metadata = &awstypes.EksMetadata{ @@ -1198,6 +1322,10 @@ func expandEKSPodProperties(tfMap map[string]interface{}) *awstypes.EksPodProper apiObject.ServiceAccountName = aws.String(v) } + if v, ok := tfMap["share_process_namespace"]; ok { + apiObject.ShareProcessNamespace = aws.Bool(v.(bool)) + } + if v, ok := tfMap["volumes"]; ok { apiObject.Volumes = expandVolumes(v.([]interface{})) } @@ -1432,6 +1560,10 @@ func flattenEKSPodProperties(apiObject *awstypes.EksPodProperties) []interface{} tfMap["image_pull_secret"] = flattenImagePullSecrets(v) } + if v := apiObject.InitContainers; v != nil { + tfMap["init_containers"] = flattenEKSContainers(v) + } + if v := apiObject.Metadata; v != nil { metadata := make([]map[string]interface{}, 0) @@ -1448,6 +1580,10 @@ func flattenEKSPodProperties(apiObject *awstypes.EksPodProperties) []interface{} tfMap["service_account_name"] = aws.ToString(v) } + if v := apiObject.ShareProcessNamespace; v != nil { + tfMap["share_process_namespace"] = aws.ToBool(v) + } + if v := apiObject.Volumes; v != nil { tfMap["volumes"] = flattenEKSVolumes(v) } diff --git a/internal/service/batch/job_definition_data_source.go b/internal/service/batch/job_definition_data_source.go index 7d555b4378d..565bc306f9a 100644 --- a/internal/service/batch/job_definition_data_source.go +++ b/internal/service/batch/job_definition_data_source.go @@ -236,12 +236,19 @@ type eksPropertiesModel struct { } type eksPodPropertiesModel struct { - Containers fwtypes.ListNestedObjectValueOf[eksContainerModel] `tfsdk:"containers"` - DNSPolicy types.String `tfsdk:"dns_policy"` - HostNetwork types.Bool `tfsdk:"host_network"` - Metadata fwtypes.ListNestedObjectValueOf[eksMetadataModel] `tfsdk:"metadata"` - ServiceAccountName types.Bool `tfsdk:"service_account_name"` - Volumes fwtypes.ListNestedObjectValueOf[eksVolumeModel] `tfsdk:"volumes"` + Containers fwtypes.ListNestedObjectValueOf[eksContainerModel] `tfsdk:"containers"` + DNSPolicy types.String `tfsdk:"dns_policy"` + HostNetwork types.Bool `tfsdk:"host_network"` + ImagePullSecrets fwtypes.ListNestedObjectValueOf[eksImagePullSecrets] `tfsdk:"image_pull_secrets"` + InitContainers fwtypes.ListNestedObjectValueOf[eksContainerModel] `tfsdk:"init_containers"` + Metadata fwtypes.ListNestedObjectValueOf[eksMetadataModel] `tfsdk:"metadata"` + ServiceAccountName types.String `tfsdk:"service_account_name"` + ShareProcessNamespace types.Bool `tfsdk:"share_process_namespace"` + Volumes fwtypes.ListNestedObjectValueOf[eksVolumeModel] `tfsdk:"volumes"` +} + +type eksImagePullSecrets struct { + Name types.String `tfsdk:"name"` } type eksContainerModel struct { diff --git a/internal/service/batch/job_definition_data_source_test.go b/internal/service/batch/job_definition_data_source_test.go index 0d97e93e69d..e64f0795008 100644 --- a/internal/service/batch/job_definition_data_source_test.go +++ b/internal/service/batch/job_definition_data_source_test.go @@ -124,8 +124,11 @@ func TestAccBatchJobDefinitionDataSource_basicARN_EKSProperties(t *testing.T) { { Config: testAccJobDefinitionDataSourceConfig_basicARNEKS(rName), Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "eks_properties.0.pod_properties.0.init_containers.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "eks_properties.0.pod_properties.0.init_containers.0.image", "public.ecr.aws/amazonlinux/amazonlinux:1"), resource.TestCheckResourceAttr(dataSourceName, "eks_properties.0.pod_properties.0.containers.#", "1"), resource.TestCheckResourceAttr(dataSourceName, "eks_properties.0.pod_properties.0.containers.0.image", "public.ecr.aws/amazonlinux/amazonlinux:1"), + resource.TestCheckResourceAttr(dataSourceName, "eks_properties.0.pod_properties.0.share_process_namespace", acctest.CtFalse), resource.TestCheckResourceAttr(dataSourceName, names.AttrType, "container"), ), }, diff --git a/internal/service/batch/job_definition_test.go b/internal/service/batch/job_definition_test.go index b6f670934c7..07bada9a3b3 100644 --- a/internal/service/batch/job_definition_test.go +++ b/internal/service/batch/job_definition_test.go @@ -792,7 +792,9 @@ func TestAccBatchJobDefinition_EKSProperties_basic(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( testAccCheckJobDefinitionExists(ctx, resourceName, &jd), resource.TestCheckResourceAttr(resourceName, "eks_properties.0.pod_properties.0.containers.#", "1"), + resource.TestCheckResourceAttr(resourceName, "eks_properties.0.pod_properties.0.init_containers.#", "1"), resource.TestCheckResourceAttr(resourceName, "eks_properties.0.pod_properties.0.containers.0.image_pull_policy", ""), + resource.TestCheckResourceAttr(resourceName, "eks_properties.0.pod_properties.0.init_containers.0.image_pull_policy", ""), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), resource.TestCheckResourceAttr(resourceName, names.AttrType, "container"), ), @@ -891,6 +893,37 @@ func TestAccBatchJobDefinition_EKSProperties_imagePullSecrets(t *testing.T) { }) } +func TestAccBatchJobDefinition_EKSProperties_multiContainers(t *testing.T) { + ctx := acctest.Context(t) + var jd awstypes.JobDefinition + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_batch_job_definition.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.BatchServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckJobDefinitionDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccJobDefinitionConfig_EKSProperties_multiContainer(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckJobDefinitionExists(ctx, resourceName, &jd), + resource.TestCheckResourceAttr(resourceName, "eks_properties.0.pod_properties.0.containers.#", "2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "deregister_on_new_revision", + }, + }, + }, + }) +} + func TestAccBatchJobDefinition_createTypeContainerWithNodeProperties(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -1730,7 +1763,22 @@ resource "aws_batch_job_definition" "test" { type = "container" eks_properties { pod_properties { - host_network = true + host_network = true + share_process_namespace = false + init_containers { + name = "init0" + image = "public.ecr.aws/amazonlinux/amazonlinux:1" + command = [ + "sleep", + "60" + ] + resources { + limits = { + cpu = "1" + memory = "1024Mi" + } + } + } containers { image = "public.ecr.aws/amazonlinux/amazonlinux:1" command = [ @@ -1795,6 +1843,60 @@ resource "aws_batch_job_definition" "test" { `, rName) } +func testAccJobDefinitionConfig_EKSProperties_multiContainer(rName string) string { + return fmt.Sprintf(` +resource "aws_batch_job_definition" "test" { + name = %[1]q + type = "container" + eks_properties { + pod_properties { + host_network = true + containers { + name = "container1" + image = "public.ecr.aws/amazonlinux/amazonlinux:1" + command = [ + "sleep", + "60" + ] + resources { + limits = { + cpu = "1" + memory = "1024Mi" + } + } + } + containers { + name = "container2" + image = "public.ecr.aws/amazonlinux/amazonlinux:1" + command = [ + "sleep", + "60" + ] + resources { + limits = { + cpu = "1" + memory = "1024Mi" + } + } + } + image_pull_secret { + name = "chihiro" + } + image_pull_secret { + name = "haku" + } + metadata { + labels = { + environment = "test" + name = %[1]q + } + } + } + } +} +`, rName) +} + func testAccJobDefinitionConfig_EKSProperties_advancedUpdate(rName string) string { return fmt.Sprintf(` resource "aws_batch_job_definition" "test" { diff --git a/website/docs/d/batch_job_definition.html.markdown b/website/docs/d/batch_job_definition.html.markdown index b45a798013f..e4f0ef2c0ce 100644 --- a/website/docs/d/batch_job_definition.html.markdown +++ b/website/docs/d/batch_job_definition.html.markdown @@ -56,10 +56,12 @@ This data source exports the following attributes in addition to the arguments a ### pod_properties +* `containers` - The properties of the container that's used on the Amazon EKS pod. See [containers](#container) below. * `dns_policy` - The DNS policy for the pod. The default value is ClusterFirst. If the hostNetwork parameter is not specified, the default is ClusterFirstWithHostNet. ClusterFirst indicates that any DNS query that does not match the configured cluster domain suffix is forwarded to the upstream nameserver inherited from the node. * `host_network` - Indicates if the pod uses the hosts' network IP address. The default value is true. Setting this to false enables the Kubernetes pod networking model. Most AWS Batch workloads are egress-only and don't require the overhead of IP allocation for each pod for incoming connections. +* `init_containers` - Containers which run before application containers, always runs to completion, and must complete successfully before the next container starts. These containers are registered with the Amazon EKS Connector agent and persists the registration information in the Kubernetes backend data store. See [containers](#container) below. * `service_account_name` - The name of the service account that's used to run the pod. -* `containers` - The properties of the container that's used on the Amazon EKS pod. Array of [EksContainer](#container) objects. +* `share_process_namespace` - (Optional) Indicates if the processes in a container are shared, or visible, to other containers in the same pod. * `metadata` - [Metadata](#eks_metadata) about the Kubernetes pod. * `volumes` - Specifies the volumes for a job definition that uses Amazon EKS resources. Array of [EksVolume](#eks_volumes) objects. diff --git a/website/docs/r/batch_job_definition.html.markdown b/website/docs/r/batch_job_definition.html.markdown index 09936a3bcbd..b195ab46993 100644 --- a/website/docs/r/batch_job_definition.html.markdown +++ b/website/docs/r/batch_job_definition.html.markdown @@ -303,9 +303,12 @@ The following arguments are optional: * `containers` - (Optional) Properties of the container that's used on the Amazon EKS pod. See [containers](#containers) below. * `dns_policy` - (Optional) DNS policy for the pod. The default value is `ClusterFirst`. If the `host_network` argument is not specified, the default is `ClusterFirstWithHostNet`. `ClusterFirst` indicates that any DNS query that does not match the configured cluster domain suffix is forwarded to the upstream nameserver inherited from the node. For more information, see Pod's DNS policy in the Kubernetes documentation. * `host_network` - (Optional) Whether the pod uses the hosts' network IP address. The default value is `true`. Setting this to `false` enables the Kubernetes pod networking model. Most AWS Batch workloads are egress-only and don't require the overhead of IP allocation for each pod for incoming connections. +* `init_containers` - (Optional) Containers which run before application containers, always runs to completion, and must complete successfully before the next container starts. These containers are registered with the Amazon EKS Connector agent and persists the registration information in the Kubernetes backend data store. See [containers](#container) below. * `image_pull_secret` - (Optional) List of Kubernetes secret resources. See [`image_pull_secret`](#image_pull_secret) below. * `metadata` - (Optional) Metadata about the Kubernetes pod. * `service_account_name` - (Optional) Name of the service account that's used to run the pod. +* `share_process_namespace` - (Optional) Indicates if the processes in a container are shared, or visible, to other containers in the same pod. +* `metadata` - [Metadata](#eks_metadata) about the Kubernetes pod. * `volumes` - (Optional) Volumes for a job definition that uses Amazon EKS resources. AWS Batch supports [emptyDir](#eks_empty_dir), [hostPath](#eks_host_path), and [secret](#eks_secret) volume types. #### `containers` @@ -338,6 +341,10 @@ The following arguments are optional: * `path` - (Optional) Path of the file or directory on the host to mount into containers on the pod. +#### eks_metadata + +* `labels` - Key-value pairs used to identify, sort, and organize cube resources. + #### `eks_secret` * `secret_name` - (Required) Name of the secret. The name must be allowed as a DNS subdomain name.