Skip to content

Commit

Permalink
Merge pull request #16972 from juozasget/eks-cluster-addons
Browse files Browse the repository at this point in the history
  • Loading branch information
gdavison committed Apr 13, 2021
2 parents ed8ee5e + a0668ef commit d03950a
Show file tree
Hide file tree
Showing 11 changed files with 1,379 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .changelog/16972.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:new-data-source
aws_eks_addon
```

```release-note:new-resource
aws_eks_addon
```
89 changes: 89 additions & 0 deletions aws/data_source_aws_eks_addon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package aws

import (
"context"
"fmt"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/eks"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
)

func dataSourceAwsEksAddon() *schema.Resource {
return &schema.Resource{
ReadWithoutTimeout: dataSourceAwsEksAddonRead,
Schema: map[string]*schema.Schema{
"addon_name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.NoZeroValues,
},
"cluster_name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.NoZeroValues,
},
"arn": {
Type: schema.TypeString,
Computed: true,
},
"addon_version": {
Type: schema.TypeString,
Computed: true,
},
"service_account_role_arn": {
Type: schema.TypeString,
Computed: true,
},
"created_at": {
Type: schema.TypeString,
Computed: true,
},
"modified_at": {
Type: schema.TypeString,
Computed: true,
},
"tags": tagsSchemaComputed(),
},
}
}

func dataSourceAwsEksAddonRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*AWSClient).eksconn
ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

addonName := d.Get("addon_name").(string)
clusterName := d.Get("cluster_name").(string)

input := &eks.DescribeAddonInput{
AddonName: aws.String(addonName),
ClusterName: aws.String(clusterName),
}

output, err := conn.DescribeAddonWithContext(ctx, input)
if err != nil {
return diag.FromErr(fmt.Errorf("error reading EKS Addon (%s): %w", addonName, err))
}

addon := output.Addon
if addon == nil {
return diag.FromErr(fmt.Errorf("EKS Addon (%s) not found", addonName))
}

d.SetId(fmt.Sprintf("%s:%s", clusterName, addonName))
d.Set("arn", addon.AddonArn)
d.Set("addon_version", addon.AddonVersion)
d.Set("service_account_role_arn", addon.ServiceAccountRoleArn)
d.Set("created_at", aws.TimeValue(addon.CreatedAt).Format(time.RFC3339))
d.Set("modified_at", aws.TimeValue(addon.ModifiedAt).Format(time.RFC3339))

if err := d.Set("tags", keyvaluetags.EksKeyValueTags(addon.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
return diag.FromErr(fmt.Errorf("error setting tags attribute: %w", err))
}

return nil
}
62 changes: 62 additions & 0 deletions aws/data_source_aws_eks_addon_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package aws

import (
"context"
"fmt"
"regexp"
"testing"

"github.com/aws/aws-sdk-go/service/eks"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccAWSEksAddonDataSource_basic(t *testing.T) {
var addon eks.Addon
rName := acctest.RandomWithPrefix("tf-acc-test")
dataSourceResourceName := "data.aws_eks_addon.test"
resourceName := "aws_eks_addon.test"
addonName := "vpc-cni"
ctx := context.TODO()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t); testAccPreCheckAWSEksAddon(t) },
ErrorCheck: testAccErrorCheck(t, eks.EndpointsID),
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckAWSEksAddonDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSEksAddonDataSourceConfig_Basic(rName, addonName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEksAddonExists(ctx, dataSourceResourceName, &addon),
testAccMatchResourceAttrRegionalARN(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, "service_account_role_arn", dataSourceResourceName, "service_account_role_arn"),
resource.TestCheckResourceAttrPair(resourceName, "created_at", dataSourceResourceName, "created_at"),
resource.TestCheckResourceAttrPair(resourceName, "modified_at", dataSourceResourceName, "modified_at"),
resource.TestCheckResourceAttrPair(resourceName, "tags.%", dataSourceResourceName, "tags.%"),
),
},
},
})
}

func testAccAWSEksAddonDataSourceConfig_Basic(rName, addonName string) string {
return composeConfig(testAccAWSEksAddonConfig_Base(rName), fmt.Sprintf(`
resource "aws_eks_addon" "test" {
addon_name = %[2]q
cluster_name = aws_eks_cluster.test.name
}
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))
}
47 changes: 47 additions & 0 deletions aws/internal/service/eks/waiter/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package waiter

import (
"context"
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/eks"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func EksAddonStatus(ctx context.Context, conn *eks.EKS, addonName, clusterName string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := conn.DescribeAddonWithContext(ctx, &eks.DescribeAddonInput{
ClusterName: aws.String(clusterName),
AddonName: aws.String(addonName),
})
if tfawserr.ErrCodeEquals(err, eks.ErrCodeResourceNotFoundException) {
return nil, "", nil
}
if err != nil {
return output, "", err
}
if output == nil || output.Addon == nil {
return nil, "", fmt.Errorf("EKS Cluster (%s) add-on (%s) missing", clusterName, addonName)
}
return output.Addon, aws.StringValue(output.Addon.Status), nil
}
}

func EksAddonUpdateStatus(ctx context.Context, conn *eks.EKS, clusterName, addonName, updateID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := conn.DescribeUpdateWithContext(ctx, &eks.DescribeUpdateInput{
Name: aws.String(clusterName),
AddonName: aws.String(addonName),
UpdateId: aws.String(updateID),
})
if err != nil {
return output, "", err
}
if output == nil || output.Update == nil {
return nil, "", fmt.Errorf("EKS Cluster (%s) add-on (%s) update (%s) missing", clusterName, addonName, updateID)
}
return output.Update, aws.StringValue(output.Update.Status), nil
}
}
116 changes: 116 additions & 0 deletions aws/internal/service/eks/waiter/waiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package waiter

import (
"context"
"fmt"
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/eks"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

const (
EksAddonCreatedTimeout = 20 * time.Minute
EksAddonUpdatedTimeout = 20 * time.Minute
EksAddonDeletedTimeout = 40 * time.Minute
)

// EksAddonCreated waits for a EKS add-on to return status "ACTIVE" or "CREATE_FAILED"
func EksAddonCreated(ctx context.Context, conn *eks.EKS, clusterName, addonName string) (*eks.Addon, error) {
stateConf := resource.StateChangeConf{
Pending: []string{eks.AddonStatusCreating},
Target: []string{
eks.AddonStatusActive,
eks.AddonStatusCreateFailed,
},
Refresh: EksAddonStatus(ctx, conn, addonName, clusterName),
Timeout: EksAddonCreatedTimeout,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)

if addon, ok := outputRaw.(*eks.Addon); ok {
// If "CREATE_FAILED" status was returned, gather add-on health issues and return error
if aws.StringValue(addon.Status) == eks.AddonStatusCreateFailed {
var detailedErrors []string
for i, addonIssue := range addon.Health.Issues {
detailedErrors = append(detailedErrors, fmt.Sprintf("Error %d: Code: %s / Message: %s",
i+1, aws.StringValue(addonIssue.Code), aws.StringValue(addonIssue.Message)))
}

return addon, fmt.Errorf("creation not successful (%s): Errors:\n%s",
aws.StringValue(addon.Status), strings.Join(detailedErrors, "\n"))
}

return addon, err
}

return nil, err
}

// EksAddonDeleted waits for a EKS add-on to be deleted
func EksAddonDeleted(ctx context.Context, conn *eks.EKS, clusterName, addonName string) (*eks.Addon, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{
eks.AddonStatusActive,
eks.AddonStatusDeleting,
},
Target: []string{},
Refresh: EksAddonStatus(ctx, conn, addonName, clusterName),
Timeout: EksAddonDeletedTimeout,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)
if err != nil {
// EKS API returns the ResourceNotFound error in this form:
// ResourceNotFoundException: No addon: vpc-cni found in cluster: tf-acc-test-533189557170672934
if tfawserr.ErrCodeEquals(err, eks.ErrCodeResourceNotFoundException) {
return nil, nil
}
}
if v, ok := outputRaw.(*eks.Addon); ok {
return v, err
}

return nil, err
}

// EksAddonUpdateSuccessful waits for a EKS add-on update to return "Successful"
func EksAddonUpdateSuccessful(ctx context.Context, conn *eks.EKS, clusterName, addonName, updateID string) (*eks.Update, error) {
stateConf := resource.StateChangeConf{
Pending: []string{eks.UpdateStatusInProgress},
Target: []string{
eks.UpdateStatusCancelled,
eks.UpdateStatusFailed,
eks.UpdateStatusSuccessful,
},
Refresh: EksAddonUpdateStatus(ctx, conn, clusterName, addonName, updateID),
Timeout: EksAddonUpdatedTimeout,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)
if err != nil {
return nil, err
}

update, ok := outputRaw.(*eks.Update)
if !ok {
return nil, err
}

if aws.StringValue(update.Status) == eks.UpdateStatusSuccessful {
return nil, nil
}

var detailedErrors []string
for i, updateError := range update.Errors {
detailedErrors = append(detailedErrors, fmt.Sprintf("Error %d: Code: %s / Message: %s",
i+1, aws.StringValue(updateError.ErrorCode), aws.StringValue(updateError.ErrorMessage)))
}

return update, fmt.Errorf("EKS add-on (%s:%s) update (%s) not successful (%s): Errors:\n%s",
clusterName, addonName, updateID, aws.StringValue(update.Status), strings.Join(detailedErrors, "\n"))
}
2 changes: 2 additions & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ func Provider() *schema.Provider {
"aws_efs_file_system": dataSourceAwsEfsFileSystem(),
"aws_efs_mount_target": dataSourceAwsEfsMountTarget(),
"aws_eip": dataSourceAwsEip(),
"aws_eks_addon": dataSourceAwsEksAddon(),
"aws_eks_cluster": dataSourceAwsEksCluster(),
"aws_eks_cluster_auth": dataSourceAwsEksClusterAuth(),
"aws_elastic_beanstalk_application": dataSourceAwsElasticBeanstalkApplication(),
Expand Down Expand Up @@ -684,6 +685,7 @@ func Provider() *schema.Provider {
"aws_eip": resourceAwsEip(),
"aws_eip_association": resourceAwsEipAssociation(),
"aws_eks_cluster": resourceAwsEksCluster(),
"aws_eks_addon": resourceAwsEksAddon(),
"aws_eks_fargate_profile": resourceAwsEksFargateProfile(),
"aws_eks_node_group": resourceAwsEksNodeGroup(),
"aws_elasticache_cluster": resourceAwsElasticacheCluster(),
Expand Down
Loading

0 comments on commit d03950a

Please sign in to comment.