diff --git a/.changelog/28295.txt b/.changelog/28295.txt new file mode 100644 index 000000000000..beb6c11f754b --- /dev/null +++ b/.changelog/28295.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_eks_addon: Add `configuration_values` argument +``` + +```release-note:enhancement +data-source/aws_eks_addon: Add `configuration_values` attribute +``` \ No newline at end of file diff --git a/internal/service/eks/addon.go b/internal/service/eks/addon.go index 34c6f9187254..0c2a972784f0 100644 --- a/internal/service/eks/addon.go +++ b/internal/service/eks/addon.go @@ -65,6 +65,11 @@ func ResourceAddon() *schema.Resource { ForceNew: true, ValidateFunc: validClusterName, }, + "configuration_values": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, "created_at": { Type: schema.TypeString, Computed: true, @@ -120,6 +125,10 @@ func resourceAddonCreate(ctx context.Context, d *schema.ResourceData, meta inter input.ServiceAccountRoleArn = aws.String(v.(string)) } + if v, ok := d.GetOk("configuration_values"); ok { + input.ConfigurationValues = aws.String(v.(string)) + } + if len(tags) > 0 { input.Tags = Tags(tags.IgnoreAWS()) } @@ -198,6 +207,7 @@ func resourceAddonRead(ctx context.Context, d *schema.ResourceData, meta interfa d.Set("addon_version", addon.AddonVersion) d.Set("arn", addon.AddonArn) d.Set("cluster_name", addon.ClusterName) + d.Set("configuration_values", addon.ConfigurationValues) d.Set("created_at", aws.TimeValue(addon.CreatedAt).Format(time.RFC3339)) d.Set("modified_at", aws.TimeValue(addon.ModifiedAt).Format(time.RFC3339)) d.Set("service_account_role_arn", addon.ServiceAccountRoleArn) @@ -225,7 +235,7 @@ func resourceAddonUpdate(ctx context.Context, d *schema.ResourceData, meta inter return diag.FromErr(err) } - if d.HasChanges("addon_version", "service_account_role_arn") { + if d.HasChanges("addon_version", "service_account_role_arn", "configuration_values") { input := &eks.UpdateAddonInput{ AddonName: aws.String(addonName), ClientRequestToken: aws.String(resource.UniqueId()), @@ -236,6 +246,10 @@ func resourceAddonUpdate(ctx context.Context, d *schema.ResourceData, meta inter input.AddonVersion = aws.String(d.Get("addon_version").(string)) } + if d.HasChange("configuration_values") { + input.ConfigurationValues = aws.String(d.Get("configuration_values").(string)) + } + if v, ok := d.GetOk("resolve_conflicts"); ok { input.ResolveConflicts = aws.String(v.(string)) } diff --git a/internal/service/eks/addon_data_source.go b/internal/service/eks/addon_data_source.go index 53b84318ab46..ab09bddc7d8c 100644 --- a/internal/service/eks/addon_data_source.go +++ b/internal/service/eks/addon_data_source.go @@ -35,6 +35,10 @@ func DataSourceAddon() *schema.Resource { Required: true, ValidateFunc: validClusterName, }, + "configuration_values": { + Type: schema.TypeString, + Computed: true, + }, "created_at": { Type: schema.TypeString, Computed: true, @@ -69,6 +73,7 @@ func dataSourceAddonRead(ctx context.Context, d *schema.ResourceData, meta inter d.SetId(id) d.Set("addon_version", addon.AddonVersion) d.Set("arn", addon.AddonArn) + d.Set("configuration_values", addon.ConfigurationValues) d.Set("created_at", aws.TimeValue(addon.CreatedAt).Format(time.RFC3339)) d.Set("modified_at", aws.TimeValue(addon.ModifiedAt).Format(time.RFC3339)) d.Set("service_account_role_arn", addon.ServiceAccountRoleArn) diff --git a/internal/service/eks/addon_data_source_test.go b/internal/service/eks/addon_data_source_test.go index 39e54e9ea2dc..1735ab290e73 100644 --- a/internal/service/eks/addon_data_source_test.go +++ b/internal/service/eks/addon_data_source_test.go @@ -1,9 +1,7 @@ package eks_test import ( - "context" "fmt" - "regexp" "testing" "github.com/aws/aws-sdk-go/service/eks" @@ -13,12 +11,10 @@ import ( ) func TestAccEKSAddonDataSource_basic(t *testing.T) { - var addon eks.Addon rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dataSourceResourceName := "data.aws_eks_addon.test" resourceName := "aws_eks_addon.test" addonName := "vpc-cni" - ctx := context.Background() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t); testAccPreCheckAddon(t) }, @@ -29,13 +25,42 @@ func TestAccEKSAddonDataSource_basic(t *testing.T) { { Config: testAccAddonDataSourceConfig_basic(rName, addonName), Check: resource.ComposeTestCheckFunc( - testAccCheckAddonExists(ctx, dataSourceResourceName, &addon), - acctest.MatchResourceAttrRegionalARN(dataSourceResourceName, "arn", "eks", regexp.MustCompile(fmt.Sprintf("addon/%s/%s/.+$", rName, addonName))), - resource.TestCheckResourceAttrPair(resourceName, "arn", dataSourceResourceName, "arn"), resource.TestCheckResourceAttrPair(resourceName, "addon_version", dataSourceResourceName, "addon_version"), + resource.TestCheckResourceAttrPair(resourceName, "arn", dataSourceResourceName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "configuration_values", dataSourceResourceName, "configuration_values"), + resource.TestCheckResourceAttrPair(resourceName, "created_at", dataSourceResourceName, "created_at"), + resource.TestCheckResourceAttrPair(resourceName, "modified_at", dataSourceResourceName, "modified_at"), resource.TestCheckResourceAttrPair(resourceName, "service_account_role_arn", dataSourceResourceName, "service_account_role_arn"), + resource.TestCheckResourceAttrPair(resourceName, "tags.%", dataSourceResourceName, "tags.%"), + ), + }, + }, + }) +} + +func TestAccEKSAddonDataSource_configurationValues(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceResourceName := "data.aws_eks_addon.test" + resourceName := "aws_eks_addon.test" + addonName := "vpc-cni" + addonVersion := "v1.10.4-eksbuild.1" + configurationValues := "{\"env\": {\"WARM_ENI_TARGET\":\"2\",\"ENABLE_POD_ENI\":\"true\"},\"resources\": {\"limits\":{\"cpu\":\"100m\",\"memory\":\"100Mi\"},\"requests\":{\"cpu\":\"100m\",\"memory\":\"100Mi\"}}}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t); testAccPreCheckAddon(t) }, + ErrorCheck: acctest.ErrorCheck(t, eks.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAddonDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAddonDataSourceConfig_configurationValues(rName, addonName, addonVersion, configurationValues, eks.ResolveConflictsOverwrite), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "addon_version", dataSourceResourceName, "addon_version"), + resource.TestCheckResourceAttrPair(resourceName, "arn", dataSourceResourceName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "configuration_values", dataSourceResourceName, "configuration_values"), resource.TestCheckResourceAttrPair(resourceName, "created_at", dataSourceResourceName, "created_at"), resource.TestCheckResourceAttrPair(resourceName, "modified_at", dataSourceResourceName, "modified_at"), + resource.TestCheckResourceAttrPair(resourceName, "service_account_role_arn", dataSourceResourceName, "service_account_role_arn"), resource.TestCheckResourceAttrPair(resourceName, "tags.%", dataSourceResourceName, "tags.%"), ), }, @@ -44,7 +69,7 @@ func TestAccEKSAddonDataSource_basic(t *testing.T) { } func testAccAddonDataSourceConfig_basic(rName, addonName string) string { - return acctest.ConfigCompose(testAccAddonBaseConfig(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccAddonConfig_base(rName), fmt.Sprintf(` resource "aws_eks_addon" "test" { addon_name = %[2]q cluster_name = aws_eks_cluster.test.name @@ -61,3 +86,25 @@ data "aws_eks_addon" "test" { } `, rName, addonName)) } + +func testAccAddonDataSourceConfig_configurationValues(rName, addonName, addonVersion, configurationValues, resolveConflicts string) string { + return acctest.ConfigCompose(testAccAddonConfig_base(rName), fmt.Sprintf(` +resource "aws_eks_addon" "test" { + cluster_name = aws_eks_cluster.test.name + addon_name = %[2]q + addon_version = %[3]q + configuration_values = %[4]q + resolve_conflicts = %[5]q +} + +data "aws_eks_addon" "test" { + addon_name = %[2]q + cluster_name = aws_eks_cluster.test.name + + depends_on = [ + aws_eks_addon.test, + aws_eks_cluster.test, + ] +} +`, rName, addonName, addonVersion, configurationValues, resolveConflicts)) +} diff --git a/internal/service/eks/addon_test.go b/internal/service/eks/addon_test.go index 8de65c8d5b32..4a97a260f43b 100644 --- a/internal/service/eks/addon_test.go +++ b/internal/service/eks/addon_test.go @@ -33,12 +33,13 @@ func TestAccEKSAddon_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAddonConfig_basic(rName, addonName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAddonExists(ctx, addonResourceName, &addon), resource.TestCheckResourceAttr(addonResourceName, "addon_name", addonName), resource.TestCheckResourceAttrSet(addonResourceName, "addon_version"), acctest.MatchResourceAttrRegionalARN(addonResourceName, "arn", "eks", regexp.MustCompile(fmt.Sprintf("addon/%s/%s/.+$", rName, addonName))), resource.TestCheckResourceAttrPair(addonResourceName, "cluster_name", clusterResourceName, "name"), + resource.TestCheckResourceAttr(addonResourceName, "configuration_values", ""), resource.TestCheckNoResourceAttr(addonResourceName, "preserve"), resource.TestCheckResourceAttr(addonResourceName, "tags.%", "0"), ), @@ -246,6 +247,59 @@ func TestAccEKSAddon_serviceAccountRoleARN(t *testing.T) { }) } +func TestAccEKSAddon_configurationValues(t *testing.T) { + var addon eks.Addon + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_addon.test" + configurationValues := "{\"env\": {\"WARM_ENI_TARGET\":\"2\",\"ENABLE_POD_ENI\":\"true\"},\"resources\": {\"limits\":{\"cpu\":\"100m\",\"memory\":\"100Mi\"},\"requests\":{\"cpu\":\"100m\",\"memory\":\"100Mi\"}}}" + updateConfigurationValues := "{\"env\": {\"WARM_ENI_TARGET\":\"2\",\"ENABLE_POD_ENI\":\"true\"},\"resources\": {\"limits\":{\"cpu\":\"200m\",\"memory\":\"150Mi\"},\"requests\":{\"cpu\":\"200m\",\"memory\":\"150Mi\"}}}" + emptyConfigurationValues := "{}" + invalidConfigurationValues := "{\"env\": {\"INVALID_FIELD\":\"2\"}}" + addonName := "vpc-cni" + addonVersion := "v1.10.4-eksbuild.1" + ctx := context.Background() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t); testAccPreCheckAddon(t) }, + ErrorCheck: acctest.ErrorCheck(t, eks.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAddonDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAddonConfig_configurationValues(rName, addonName, addonVersion, configurationValues, eks.ResolveConflictsOverwrite), + Check: resource.ComposeTestCheckFunc( + testAccCheckAddonExists(ctx, resourceName, &addon), + resource.TestCheckResourceAttr(resourceName, "configuration_values", configurationValues), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"resolve_conflicts"}, + }, + { + Config: testAccAddonConfig_configurationValues(rName, addonName, addonVersion, updateConfigurationValues, eks.ResolveConflictsOverwrite), + Check: resource.ComposeTestCheckFunc( + testAccCheckAddonExists(ctx, resourceName, &addon), + resource.TestCheckResourceAttr(resourceName, "configuration_values", updateConfigurationValues), + ), + }, + { + Config: testAccAddonConfig_configurationValues(rName, addonName, addonVersion, emptyConfigurationValues, eks.ResolveConflictsOverwrite), + Check: resource.ComposeTestCheckFunc( + testAccCheckAddonExists(ctx, resourceName, &addon), + resource.TestCheckResourceAttr(resourceName, "configuration_values", emptyConfigurationValues), + ), + }, + { + Config: testAccAddonConfig_configurationValues(rName, addonName, addonVersion, invalidConfigurationValues, eks.ResolveConflictsOverwrite), + ExpectError: regexp.MustCompile(`InvalidParameterException: ConfigurationValue provided in request is not supported`), + }, + }, + }) +} + func TestAccEKSAddon_tags(t *testing.T) { var addon1, addon2, addon3 eks.Addon rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -754,7 +808,7 @@ func testAccCheckAddonUpdateTags(addon *eks.Addon, oldTags, newTags map[string]s } } -func testAccAddonBaseConfig(rName string) string { +func testAccAddonConfig_base(rName string) string { return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` data "aws_partition" "current" {} @@ -818,7 +872,7 @@ resource "aws_eks_cluster" "test" { } func testAccAddonConfig_basic(rName, addonName string) string { - return acctest.ConfigCompose(testAccAddonBaseConfig(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccAddonConfig_base(rName), fmt.Sprintf(` resource "aws_eks_addon" "test" { cluster_name = aws_eks_cluster.test.name addon_name = %[2]q @@ -827,7 +881,7 @@ resource "aws_eks_addon" "test" { } func testAccAddonConfig_version(rName, addonName, addonVersion string) string { - return acctest.ConfigCompose(testAccAddonBaseConfig(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccAddonConfig_base(rName), fmt.Sprintf(` resource "aws_eks_addon" "test" { cluster_name = aws_eks_cluster.test.name addon_name = %[2]q @@ -838,7 +892,7 @@ resource "aws_eks_addon" "test" { } func testAccAddonConfig_preserve(rName, addonName string) string { - return acctest.ConfigCompose(testAccAddonBaseConfig(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccAddonConfig_base(rName), fmt.Sprintf(` resource "aws_eks_addon" "test" { cluster_name = aws_eks_cluster.test.name addon_name = %[2]q @@ -848,7 +902,7 @@ resource "aws_eks_addon" "test" { } func testAccAddonConfig_resolveConflicts(rName, addonName, resolveConflicts string) string { - return acctest.ConfigCompose(testAccAddonBaseConfig(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccAddonConfig_base(rName), fmt.Sprintf(` resource "aws_eks_addon" "test" { cluster_name = aws_eks_cluster.test.name addon_name = %[2]q @@ -858,7 +912,7 @@ resource "aws_eks_addon" "test" { } func testAccAddonConfig_serviceAccountRoleARN(rName, addonName string) string { - return acctest.ConfigCompose(testAccAddonBaseConfig(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccAddonConfig_base(rName), fmt.Sprintf(` resource "aws_iam_role" "test-service-role" { name = "test-service-role" assume_role_policy = < **Note:** `configuration_values` is a single JSON string should match the valid JSON schema for each add-on with specific version. + +To find the correct JSON schema for each add-on can be extracted using [describe-addon-configuration](https://docs.aws.amazon.com/cli/latest/reference/eks/describe-addon-configuration.html) call. +This below is an example for extracting the `configuration_values` schema for `coredns`. + +```bash + aws eks describe-addon-configuration \ + --addon-name coredns \ + --addon-version v1.8.7-eksbuild.2 +``` + +Example to create a `coredns` managed addon with custom `configuration_values`. + +```terraform +resource "aws_eks_addon" "example" { + cluster_name = "mycluster" + addon_name = "coredns" + addon_version = "v1.8.7-eksbuild.3" + resolve_conflicts = "OVERWRITE" + configuration_values = "{\"replicaCount\":4,\"resources\":{\"limits\":{\"cpu\":\"100m\",\"memory\":\"150Mi\"},\"requests\":{\"cpu\":\"100m\",\"memory\":\"150Mi\"}}}" +} +``` + ### Example IAM Role for EKS Addon "vpc-cni" with AWS managed policy ```terraform @@ -96,6 +122,7 @@ The following arguments are optional: * `addon_version` – (Optional) The version of the EKS add-on. The version must match one of the versions returned by [describe-addon-versions](https://docs.aws.amazon.com/cli/latest/reference/eks/describe-addon-versions.html). +* `configuration_values` - (Optional) custom configuration values for addons with single JSON string. This JSON string value must match the JSON schema derived from [describe-addon-configuration](https://docs.aws.amazon.com/cli/latest/reference/eks/describe-addon-configuration.html). * `resolve_conflicts` - (Optional) Define how to resolve parameter value conflicts when migrating an existing add-on to an Amazon EKS add-on or when applying version updates to the add-on. Valid values are `NONE`, `OVERWRITE` and `PRESERVE`. For more details check [UpdateAddon](https://docs.aws.amazon.com/eks/latest/APIReference/API_UpdateAddon.html) API Docs.