From 9dc5c3acb6b1d34122d6976571169589522acad5 Mon Sep 17 00:00:00 2001 From: Kumarappan-A Date: Mon, 20 May 2019 23:36:26 -0700 Subject: [PATCH 1/6] resource/aws_ecs_task_set: Support ECS task set --- aws/provider.go | 1 + aws/resource_aws_ecs_task_set.go | 737 +++++ aws/resource_aws_ecs_task_set_test.go | 1069 ++++++ website/aws.erb | 3650 +++++++++++++++++++++ website/docs/r/ecs_task_set.html.markdown | 121 + 5 files changed, 5578 insertions(+) create mode 100644 aws/resource_aws_ecs_task_set.go create mode 100644 aws/resource_aws_ecs_task_set_test.go create mode 100644 website/aws.erb create mode 100644 website/docs/r/ecs_task_set.html.markdown diff --git a/aws/provider.go b/aws/provider.go index 1beb5e9c100..93d8fe696c1 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -728,6 +728,7 @@ func Provider() *schema.Provider { "aws_ecs_cluster": resourceAwsEcsCluster(), "aws_ecs_service": resourceAwsEcsService(), "aws_ecs_task_definition": resourceAwsEcsTaskDefinition(), + "aws_ecs_task_set": resourceAwsEcsTaskSet(), "aws_efs_access_point": resourceAwsEfsAccessPoint(), "aws_efs_backup_policy": resourceAwsEfsBackupPolicy(), "aws_efs_file_system": resourceAwsEfsFileSystem(), diff --git a/aws/resource_aws_ecs_task_set.go b/aws/resource_aws_ecs_task_set.go new file mode 100644 index 00000000000..f77d59d8b1c --- /dev/null +++ b/aws/resource_aws_ecs_task_set.go @@ -0,0 +1,737 @@ +package aws + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ecs" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "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 resourceAwsEcsTaskSet() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsEcsTaskSetCreate, + Read: resourceAwsEcsTaskSetRead, + Update: resourceAwsEcsTaskSetUpdate, + Delete: resourceAwsEcsTaskSetDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Read: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "service": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "cluster": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "external_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "task_definition": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "network_configuration": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "security_groups": { + Type: schema.TypeSet, + MaxItems: 5, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "subnets": { + Type: schema.TypeSet, + MaxItems: 16, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "assign_public_ip": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, + + // If you are using the CodeDeploy or an external deployment controller, + // multiple target groups are not supported. + // https://docs.aws.amazon.com/AmazonECS/latest/developerguide/register-multiple-targetgroups.html + "load_balancers": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "elb_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "target_group_arn": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + "container_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "container_port": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IsPortNumber, + }, + }, + }, + }, + + "service_registries": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "container_name": { + Type: schema.TypeString, + Optional: true, + }, + "container_port": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IsPortNumber, + }, + "port": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IsPortNumber, + }, + "registry_arn": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateArn, + }, + }, + }, + }, + + "launch_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + ecs.LaunchTypeEc2, + ecs.LaunchTypeFargate, + }, false), + }, + + "capacity_provider_strategy": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "base": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 100000), + ForceNew: true, + }, + + "capacity_provider": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "weight": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 1000), + ForceNew: true, + }, + }, + }, + }, + + "platform_version": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "scale": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "unit": { + Type: schema.TypeString, + Optional: true, + Default: ecs.ScaleUnitPercent, + ValidateFunc: validation.StringInSlice([]string{ + ecs.ScaleUnitPercent, + }, false), + }, + "value": { + Type: schema.TypeFloat, + Optional: true, + ValidateFunc: validation.FloatBetween(0.0, 100.0), + }, + }, + }, + }, + + "force_delete": { + Type: schema.TypeBool, + Optional: true, + }, + + "wait_until_stable": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "wait_until_stable_timeout": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + duration, err := time.ParseDuration(value) + if err != nil { + errors = append(errors, fmt.Errorf( + "%q cannot be parsed as a duration: %s", k, err)) + } + if duration < 0 { + errors = append(errors, fmt.Errorf( + "%q must be greater than zero", k)) + } + return + }, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceAwsEcsTaskSetCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ecsconn + + cluster := d.Get("cluster").(string) + service := d.Get("service").(string) + input := ecs.CreateTaskSetInput{ + ClientToken: aws.String(resource.UniqueId()), + Cluster: aws.String(cluster), + Service: aws.String(service), + TaskDefinition: aws.String(d.Get("task_definition").(string)), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().EcsTags(), + } + + if v, ok := d.GetOk("external_id"); ok { + input.ExternalId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("launch_type"); ok { + input.LaunchType = aws.String(v.(string)) + } + + input.CapacityProviderStrategy = expandEcsCapacityProviderStrategy(d.Get("capacity_provider_strategy").(*schema.Set)) + + loadBalancers := expandEcsLoadBalancers(d.Get("load_balancers").([]interface{})) + if len(loadBalancers) > 0 { + log.Printf("[DEBUG] Adding ECS load balancers: %s", loadBalancers) + input.LoadBalancers = loadBalancers + } + + input.NetworkConfiguration = expandEcsNetworkConfiguration(d.Get("network_configuration").([]interface{})) + + if v, ok := d.GetOk("platform_version"); ok { + input.PlatformVersion = aws.String(v.(string)) + } + + scale := d.Get("scale").([]interface{}) + if len(scale) > 0 { + input.Scale = expandAwsEcsScale(scale[0].(map[string]interface{})) + } + + serviceRegistries := d.Get("service_registries").([]interface{}) + if len(serviceRegistries) > 0 { + input.ServiceRegistries = expandAwsEcsServiceRegistries(serviceRegistries) + } + + log.Printf("[DEBUG] Creating ECS Task set: %s", input) + + // Retry due to AWS IAM & ECS eventual consistency + var out *ecs.CreateTaskSetOutput + var err error + err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError { + out, err = conn.CreateTaskSet(&input) + + if err != nil { + if isAWSErr(err, ecs.ErrCodeClusterNotFoundException, "") || + isAWSErr(err, ecs.ErrCodeServiceNotFoundException, "") || + isAWSErr(err, ecs.ErrCodeTaskSetNotFoundException, "") || + isAWSErr(err, ecs.ErrCodeInvalidParameterException, "does not have an associated load balancer") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + + return nil + }) + + if isResourceTimeoutError(err) { + out, err = conn.CreateTaskSet(&input) + } + + if err != nil { + return fmt.Errorf("Error creating ECS TaskSet: %s", err) + } + + taskSet := *out.TaskSet + + log.Printf("[DEBUG] ECS Task set created: %s", aws.StringValue(taskSet.Id)) + d.SetId(aws.StringValue(taskSet.Id)) + + if d.Get("wait_until_stable").(bool) { + waitUntilStableTimeOut := d.Timeout(schema.TimeoutCreate) + if v, ok := d.GetOk("wait_until_stable_timeout"); ok && v.(string) != "" { + timeout, err := time.ParseDuration(v.(string)) + if err != nil { + return err + } + waitUntilStableTimeOut = timeout + } + + // Wait until it's stable + wait := resource.StateChangeConf{ + Pending: []string{ecs.StabilityStatusStabilizing}, + Target: []string{ecs.StabilityStatusSteadyState}, + Timeout: waitUntilStableTimeOut, + Delay: 10 * time.Second, + Refresh: func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking if ECS task set %s is set to %s", d.Id(), ecs.StabilityStatusSteadyState) + resp, err := conn.DescribeTaskSets(&ecs.DescribeTaskSetsInput{ + TaskSets: []*string{aws.String(d.Id())}, + Cluster: aws.String(d.Get("cluster").(string)), + Service: aws.String(d.Get("service").(string)), + }) + if err != nil { + return resp, "FAILED", err + } + + log.Printf("[DEBUG] ECS task set (%s) is currently %s", d.Id(), aws.StringValue(resp.TaskSets[0].StabilityStatus)) + return resp, aws.StringValue(resp.TaskSets[0].StabilityStatus), nil + }, + } + + _, err = wait.WaitForState() + if err != nil { + return err + } + } + + return resourceAwsEcsTaskSetRead(d, meta) +} + +func resourceAwsEcsTaskSetRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ecsconn + + log.Printf("[DEBUG] Reading ECS task set %s", d.Id()) + + cluster := d.Get("cluster").(string) + service := d.Get("service").(string) + input := ecs.DescribeTaskSetsInput{ + Cluster: aws.String(cluster), + Service: aws.String(service), + TaskSets: []*string{aws.String(d.Id())}, + } + + var out *ecs.DescribeTaskSetsOutput + err := resource.Retry(d.Timeout(schema.TimeoutRead), func() *resource.RetryError { + var err error + out, err = conn.DescribeTaskSets(&input) + if err != nil { + if d.IsNewResource() && + isAWSErr(err, ecs.ErrCodeServiceNotFoundException, "") || + isAWSErr(err, ecs.ErrCodeClusterNotFoundException, "") || + isAWSErr(err, ecs.ErrCodeTaskSetNotFoundException, "") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + + if len(out.TaskSets) < 1 { + if d.IsNewResource() { + return resource.RetryableError(fmt.Errorf("ECS task set not created yet: %q", d.Id())) + } + log.Printf("[WARN] ECS Task Set %s not found, removing from state.", d.Id()) + d.SetId("") + return nil + } + + return nil + }) + + if isResourceTimeoutError(err) { + out, err = conn.DescribeTaskSets(&input) + } + + // after retrying + if err != nil { + if isAWSErr(err, ecs.ErrCodeClusterNotFoundException, "") || + isAWSErr(err, ecs.ErrCodeServiceNotFoundException, "") || + isAWSErr(err, ecs.ErrCodeTaskSetNotFoundException, "") { + log.Printf("[WARN] ECS TaskSet (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + return err + } + + if len(out.TaskSets) < 1 { + if d.IsNewResource() { + return fmt.Errorf("ECS TaskSet not created: %q", d.Id()) + } + log.Printf("[WARN] Removing ECS task set %s because it's gone", d.Id()) + d.SetId("") + return nil + } + + if len(out.TaskSets) != 1 { + return fmt.Errorf("Error reading # of ECS TaskSet (%s) expected 1, got %d", d.Id(), len(out.TaskSets)) + } + + taskSet := out.TaskSets[0] + + log.Printf("[DEBUG] Received ECS task set %s", taskSet) + + d.SetId(aws.StringValue(taskSet.Id)) + d.Set("arn", taskSet.TaskSetArn) + d.Set("launch_type", taskSet.LaunchType) + d.Set("platform_version", taskSet.PlatformVersion) + d.Set("external_id", taskSet.ExternalId) + + // Save cluster in the same format + if strings.HasPrefix(d.Get("cluster").(string), "arn:"+meta.(*AWSClient).partition+":ecs:") { + d.Set("cluster", taskSet.ClusterArn) + } else { + clusterARN := getNameFromARN(*taskSet.ClusterArn) + d.Set("cluster", clusterARN) + } + + // Save task definition in the same format + if strings.HasPrefix(d.Get("task_definition").(string), "arn:"+meta.(*AWSClient).partition+":ecs:") { + d.Set("task_definition", taskSet.TaskDefinition) + } else { + taskDefinition := buildFamilyAndRevisionFromARN(*taskSet.TaskDefinition) + d.Set("task_definition", taskDefinition) + } + + if taskSet.LoadBalancers != nil { + d.Set("load_balancers", flattenEcsLoadBalancers(taskSet.LoadBalancers)) + } + + if err := d.Set("scale", flattenAwsEcsScale(taskSet.Scale)); err != nil { + return fmt.Errorf("Error setting scale for (%s): %s", d.Id(), err) + } + + if err := d.Set("capacity_provider_strategy", flattenEcsCapacityProviderStrategy(taskSet.CapacityProviderStrategy)); err != nil { + return fmt.Errorf("error setting capacity_provider_strategy: %s", err) + } + + if err := d.Set("network_configuration", flattenEcsNetworkConfiguration(taskSet.NetworkConfiguration)); err != nil { + return fmt.Errorf("Error setting network_configuration for (%s): %s", d.Id(), err) + } + + if err := d.Set("service_registries", flattenServiceRegistries(taskSet.ServiceRegistries)); err != nil { + return fmt.Errorf("Error setting service_registries for (%s): %s", d.Id(), err) + } + + return nil +} + +func resourceAwsEcsTaskSetUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ecsconn + updateTaskset := false + + input := ecs.UpdateTaskSetInput{ + Cluster: aws.String(d.Get("cluster").(string)), + Service: aws.String(d.Get("service").(string)), + TaskSet: aws.String(d.Id()), + } + + if d.HasChange("scale") { + scale := d.Get("scale").([]interface{}) + if len(scale) > 0 { + updateTaskset = true + input.Scale = expandAwsEcsScale(scale[0].(map[string]interface{})) + } + } + + if updateTaskset { + log.Printf("[DEBUG] Updating ECS Task Set (%s): %s", d.Id(), input) + // Retry due to IAM eventual consistency + err := resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError { + _, err := conn.UpdateTaskSet(&input) + if err != nil { + if isAWSErr(err, ecs.ErrCodeClusterNotFoundException, "") || + isAWSErr(err, ecs.ErrCodeServiceNotFoundException, "") || + isAWSErr(err, ecs.ErrCodeTaskSetNotFoundException, "") || + isAWSErr(err, ecs.ErrCodeInvalidParameterException, "does not have an associated load balancer") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + + if isResourceTimeoutError(err) { + _, err = conn.UpdateTaskSet(&input) + } + if err != nil { + return fmt.Errorf("Error updating ECS Task set (%s): %s", d.Id(), err) + } + + if d.Get("wait_until_stable").(bool) { + waitUntilStableTimeOut := d.Timeout(schema.TimeoutUpdate) + if v, ok := d.GetOk("wait_until_stable_timeout"); ok && v.(string) != "" { + timeout, err := time.ParseDuration(v.(string)) + if err != nil { + return err + } + waitUntilStableTimeOut = timeout + } + + // Wait until it's stable + wait := resource.StateChangeConf{ + Pending: []string{ecs.StabilityStatusStabilizing}, + Target: []string{ecs.StabilityStatusSteadyState}, + Timeout: waitUntilStableTimeOut, + Delay: 10 * time.Second, + Refresh: func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking if ECS task set %s is set to %s", d.Id(), ecs.StabilityStatusSteadyState) + resp, err := conn.DescribeTaskSets(&ecs.DescribeTaskSetsInput{ + TaskSets: []*string{aws.String(d.Id())}, + Cluster: aws.String(d.Get("cluster").(string)), + Service: aws.String(d.Get("service").(string)), + }) + if err != nil { + return resp, "FAILED", err + } + + log.Printf("[DEBUG] ECS task set (%s) is currently %q", d.Id(), *resp.TaskSets[0].StabilityStatus) + return resp, *resp.TaskSets[0].StabilityStatus, nil + }, + } + + _, err = wait.WaitForState() + if err != nil { + return err + } + } + + } + + return resourceAwsEcsTaskSetRead(d, meta) +} + +func resourceAwsEcsTaskSetDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ecsconn + + // Check if it's not already gone + resp, err := conn.DescribeTaskSets(&ecs.DescribeTaskSetsInput{ + TaskSets: []*string{aws.String(d.Id())}, + Service: aws.String(d.Get("service").(string)), + Cluster: aws.String(d.Get("cluster").(string)), + }) + + if err != nil { + if isAWSErr(err, ecs.ErrCodeTaskSetNotFoundException, "") { + log.Printf("[DEBUG] Removing ECS Task set from state, %q is already gone", d.Id()) + return nil + } + return err + } + + if len(resp.TaskSets) == 0 { + log.Printf("[DEBUG] Removing ECS Task set from state, %q is already gone", d.Id()) + return nil + } + + log.Printf("[DEBUG] ECS TaskSet %s is currently %s", d.Id(), aws.StringValue(resp.TaskSets[0].Status)) + + input := ecs.DeleteTaskSetInput{ + Cluster: aws.String(d.Get("cluster").(string)), + Service: aws.String(d.Get("service").(string)), + TaskSet: aws.String(d.Id()), + } + + if v, ok := d.GetOk("force_delete"); ok && v.(bool) { + input.Force = aws.Bool(v.(bool)) + } + + // Wait until the ECS task set is drained + err = resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError { + log.Printf("[DEBUG] Trying to delete ECS task set %s", input) + _, err := conn.DeleteTaskSet(&input) + if err != nil { + if isAWSErr(err, ecs.ErrCodeTaskSetNotFoundException, "") { + return nil + } + if isAWSErr(err, ecs.ErrCodeInvalidParameterException, "The service cannot be stopped while deployments are active.") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + + if isResourceTimeoutError(err) { + _, err = conn.DeleteTaskSet(&input) + } + + if err != nil { + if isAWSErr(err, ecs.ErrCodeTaskSetNotFoundException, "") { + return nil + } + return fmt.Errorf("Error deleting ECS task set: %s", err) + } + + // Wait until it's deleted + wait := resource.StateChangeConf{ + Pending: []string{"ACTIVE", "PRIMARY", "DRAINING"}, + Target: []string{"INACTIVE"}, + Timeout: d.Timeout(schema.TimeoutDelete), + Refresh: func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking if ECS task set %s is INACTIVE", d.Id()) + resp, err := conn.DescribeTaskSets(&ecs.DescribeTaskSetsInput{ + TaskSets: []*string{aws.String(d.Id())}, + Cluster: aws.String(d.Get("cluster").(string)), + Service: aws.String(d.Get("service").(string)), + }) + + if err != nil { + return resp, "FAILED", err + } + + // task set is already gone + if len(resp.TaskSets) == 0 { + return resp, "INACTIVE", nil + } + + log.Printf("[DEBUG] ECS task set (%s) is currently %s", d.Id(), aws.StringValue(resp.TaskSets[0].Status)) + return resp, aws.StringValue(resp.TaskSets[0].Status), nil + }, + } + + _, err = wait.WaitForState() + if err != nil { + return err + } + + log.Printf("[DEBUG] ECS TaskSet %s deleted.", d.Id()) + return nil +} + +func expandAwsEcsServiceRegistries(d []interface{}) []*ecs.ServiceRegistry { + if len(d) == 0 { + return nil + } + + result := make([]*ecs.ServiceRegistry, 0, len(d)) + for _, v := range d { + m := v.(map[string]interface{}) + sr := &ecs.ServiceRegistry{ + RegistryArn: aws.String(m["registry_arn"].(string)), + } + if raw, ok := m["container_name"].(string); ok && raw != "" { + sr.ContainerName = aws.String(raw) + } + if raw, ok := m["container_port"].(int); ok && raw != 0 { + sr.ContainerPort = aws.Int64(int64(raw)) + } + if raw, ok := m["port"].(int); ok && raw != 0 { + sr.Port = aws.Int64(int64(raw)) + } + result = append(result, sr) + } + + return result +} + +func expandAwsEcsScale(d map[string]interface{}) *ecs.Scale { + if len(d) == 0 { + return nil + } + + result := &ecs.Scale{} + if v, ok := d["unit"]; ok && v.(string) != "" { + result.Unit = aws.String(v.(string)) + } + if v, ok := d["value"]; ok { + result.Value = aws.Float64(v.(float64)) + } + + return result +} + +func flattenAwsEcsScale(scale *ecs.Scale) []map[string]interface{} { + if scale == nil { + return nil + } + + m := make(map[string]interface{}) + m["unit"] = aws.StringValue(scale.Unit) + m["value"] = aws.Float64Value(scale.Value) + + return []map[string]interface{}{m} +} diff --git a/aws/resource_aws_ecs_task_set_test.go b/aws/resource_aws_ecs_task_set_test.go new file mode 100644 index 00000000000..09326f61354 --- /dev/null +++ b/aws/resource_aws_ecs_task_set_test.go @@ -0,0 +1,1069 @@ +package aws + +import ( + "fmt" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ecs" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAwsEcsTaskSet_basic(t *testing.T) { + var taskSet ecs.TaskSet + + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + tdName := acctest.RandomWithPrefix("tf-acc-td") + svcName := acctest.RandomWithPrefix("tf-acc-svc") + resourceName := "aws_ecs_task_set.mongo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsTaskSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcsTaskSet(clusterName, tdName, svcName, 0.0), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + testAccCheckAwsEcsTaskSetArn(resourceName, clusterName, svcName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "service_registries.#", "0"), + resource.TestCheckResourceAttr(resourceName, "load_balancers.#", "0"), + ), + }, + }, + }) +} + +func TestAccAWSEcsTaskSet_withARN(t *testing.T) { + var taskSet ecs.TaskSet + + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + tdName := acctest.RandomWithPrefix("tf-acc-td") + svcName := acctest.RandomWithPrefix("tf-acc-svc") + resourceName := "aws_ecs_task_set.mongo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsTaskSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcsTaskSet(clusterName, tdName, svcName, 0.0), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "service_registries.#", "0"), + resource.TestCheckResourceAttr(resourceName, "load_balancers.#", "0"), + ), + }, + + { + Config: testAccAWSEcsTaskSetModified(clusterName, tdName, svcName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "service_registries.#", "0"), + resource.TestCheckResourceAttr(resourceName, "load_balancers.#", "0"), + resource.TestCheckResourceAttr(resourceName, "external_id", "TEST_ID"), + ), + }, + }, + }) +} + +func TestAccAWSEcsTaskSet_disappears(t *testing.T) { + var taskSet ecs.TaskSet + + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + tdName := acctest.RandomWithPrefix("tf-acc-td") + svcName := acctest.RandomWithPrefix("tf-acc-svc") + resourceName := "aws_ecs_task_set.mongo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsTaskSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcsTaskSet(clusterName, tdName, svcName, 0.0), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + testAccCheckResourceDisappears(testAccProvider, resourceAwsEcsTaskSet(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSEcsTaskSet_scale(t *testing.T) { + var taskSet ecs.TaskSet + + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + tdName := acctest.RandomWithPrefix("tf-acc-td") + svcName := acctest.RandomWithPrefix("tf-acc-svc") + resourceName := "aws_ecs_task_set.mongo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsTaskSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcsTaskSet(clusterName, tdName, svcName, 0.0), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "scale.0.unit", "PERCENT"), + resource.TestCheckResourceAttr(resourceName, "scale.0.value", "0"), + ), + }, + { + Config: testAccAWSEcsTaskSet(clusterName, tdName, svcName, 100.0), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "scale.0.unit", "PERCENT"), + resource.TestCheckResourceAttr(resourceName, "scale.0.value", "100"), + ), + }, + }, + }) +} + +func TestAccAWSEcsTaskSet_withCapacityProviderStrategy(t *testing.T) { + var taskSet ecs.TaskSet + + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + tdName := acctest.RandomWithPrefix("tf-acc-td") + svcName := acctest.RandomWithPrefix("tf-acc-svc") + providerName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_ecs_task_set.mongo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsTaskSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcsTaskSetWithCapacityProviderStrategy(providerName, clusterName, tdName, svcName, 1, 0), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + ), + }, + { + Config: testAccAWSEcsTaskSetWithCapacityProviderStrategy(providerName, clusterName, tdName, svcName, 10, 1), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + ), + }, + }, + }) +} + +func TestAccAWSEcsTaskSet_withMultipleCapacityProviderStrategies(t *testing.T) { + var taskSet ecs.TaskSet + + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + tdName := acctest.RandomWithPrefix("tf-acc-td") + svcName := acctest.RandomWithPrefix("tf-acc-svc") + sgName := acctest.RandomWithPrefix("tf-acc-sg") + resourceName := "aws_ecs_task_set.mongo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsTaskSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcsTaskSetWithMultipleCapacityProviderStrategies(clusterName, tdName, svcName, sgName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "capacity_provider_strategy.#", "2"), + ), + }, + }, + }) +} + +func TestAccAWSEcsTaskSet_withAlb(t *testing.T) { + var taskSet ecs.TaskSet + + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + tdName := acctest.RandomWithPrefix("tf-acc-td") + svcName := acctest.RandomWithPrefix("tf-acc-svc") + lbName := acctest.RandomWithPrefix("tf-acc-lb") + resourceName := "aws_ecs_task_set.with_alb" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsTaskSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcsTaskSetWithAlb(clusterName, tdName, lbName, svcName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "load_balancers.#", "1"), + ), + }, + }, + }) +} + +func TestAccAWSEcsTaskSet_withLaunchTypeFargate(t *testing.T) { + var taskSet ecs.TaskSet + + sg1Name := acctest.RandomWithPrefix("tf-acc-sg-1") + sg2Name := acctest.RandomWithPrefix("tf-acc-sg-2") + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + tdName := acctest.RandomWithPrefix("tf-acc-td") + svcName := acctest.RandomWithPrefix("tf-acc-svc") + resourceName := "aws_ecs_task_set.main" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsTaskSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcsTaskSetWithLaunchTypeFargate(sg1Name, sg2Name, clusterName, tdName, svcName, "false"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "launch_type", "FARGATE"), + resource.TestCheckResourceAttr(resourceName, "network_configuration.0.assign_public_ip", "false"), + resource.TestCheckResourceAttr(resourceName, "network_configuration.0.security_groups.#", "2"), + resource.TestCheckResourceAttr(resourceName, "network_configuration.0.subnets.#", "2"), + resource.TestCheckResourceAttr(resourceName, "platform_version", "1.3.0"), + ), + }, + }, + }) +} + +func TestAccAWSEcsTaskSet_withLaunchTypeFargateAndPlatformVersion(t *testing.T) { + var taskSet ecs.TaskSet + + sg1Name := acctest.RandomWithPrefix("tf-acc-sg-1") + sg2Name := acctest.RandomWithPrefix("tf-acc-sg-2") + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + tdName := acctest.RandomWithPrefix("tf-acc-td") + svcName := acctest.RandomWithPrefix("tf-acc-svc") + resourceName := "aws_ecs_task_set.main" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsTaskSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcsTaskSetWithLaunchTypeFargateAndPlatformVersion(sg1Name, sg2Name, clusterName, tdName, svcName, "1.2.0"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "platform_version", "1.2.0"), + ), + }, + { + Config: testAccAWSEcsTaskSetWithLaunchTypeFargateAndPlatformVersion(sg1Name, sg2Name, clusterName, tdName, svcName, "1.3.0"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "platform_version", "1.3.0"), + ), + }, + }, + }) +} + +func TestAccAWSEcsTaskSet_withServiceRegistries(t *testing.T) { + var taskSet ecs.TaskSet + rString := acctest.RandString(8) + + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + tdName := acctest.RandomWithPrefix("tf-acc-td") + svcName := acctest.RandomWithPrefix("tf-acc-svc") + resourceName := "aws_ecs_task_set.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsTaskSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcsTaskSet_withServiceRegistries(rString, clusterName, tdName, svcName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "service_registries.#", "1"), + ), + }, + }, + }) +} + +func TestAccAWSEcsTaskSet_Tags(t *testing.T) { + var taskSet ecs.TaskSet + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_ecs_task_set.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsTaskSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcsTaskSetConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + Config: testAccAWSEcsTaskSetConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSEcsTaskSetConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskSetExists(resourceName, &taskSet), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +////////////// +// Fixtures // +////////////// + +func testAccAWSEcsTaskSet(clusterName, tdName, svcName string, scale float64) string { + return fmt.Sprintf(` +resource "aws_ecs_cluster" "default" { + name = "%s" +} +resource "aws_ecs_task_definition" "mongo" { + family = "%s" + container_definitions = < 0 { + var activeTaskSets []*ecs.TaskSet + for _, ts := range out.TaskSets { + if *ts.Status != "INACTIVE" { + activeTaskSets = append(activeTaskSets, ts) + } + } + if len(activeTaskSets) == 0 { + return nil + } + + return fmt.Errorf("ECS task set still exists:\n%#v", activeTaskSets) + } + return nil + } + + return err + } + + return nil +} diff --git a/website/aws.erb b/website/aws.erb new file mode 100644 index 00000000000..2a9f45c1703 --- /dev/null +++ b/website/aws.erb @@ -0,0 +1,3650 @@ +<% wrap_layout :inner do %> + <% content_for :sidebar do %> + + <% end %> + <%= yield %> +<% end %> diff --git a/website/docs/r/ecs_task_set.html.markdown b/website/docs/r/ecs_task_set.html.markdown new file mode 100644 index 00000000000..a02cfeff5e4 --- /dev/null +++ b/website/docs/r/ecs_task_set.html.markdown @@ -0,0 +1,121 @@ +--- +subcategory: "ECS" +layout: "aws" +page_title: "AWS: aws_ecs_task_set" +description: |- + Provides an ECS task set. +--- + +# Resource: aws_ecs_task_set + +Provides an ECS task set - effectively a task that is expected to run until an error occurs or a user terminates it (typically a webserver or a database). + +See [ECS Task Set section in AWS developer guide](https://docs.amazonaws.cn/en_us/AmazonECS/latest/userguide/deployment-type-external.html). + +## Example Usage + +```hcl +resource "aws_ecs_task_set" "mongo" { + service = aws_ecs_service.foo.id + cluster = aws_ecs_cluster.foo.id + task_definition = aws_ecs_task_definition.mongo.arn + + load_balancer { + target_group_arn = aws_lb_target_group.foo.arn + container_name = "mongo" + container_port = 8080 + } +} +``` + +### Ignoring Changes to Scale + +You can utilize the generic Terraform resource [lifecycle configuration block](/docs/configuration/resources.html#lifecycle) with `ignore_changes` to create an ECS service with an initial count of running instances, then ignore any changes to that count caused externally (e.g. Application Autoscaling). + +```hcl +resource "aws_ecs_task_set" "example" { + # ... other configurations ... + + # Example: Run 50% of the servcie's desired count + scale { + value = 50.0 + } + + # Optional: Allow external changes without Terraform plan difference + lifecycle { + ignore_changes = ["scale"] + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `service` - (Required) The name or ARN of the ECS service. +* `cluster` - (Required) The name or ARN of an ECS cluster. +* `external_id` - (Optional) The external ID associated with the task set. +* `task_definition` - (Required) The family and revision (`family:revision`) or full ARN of the task definition that you want to run in your service. +* `network_configuration` - (Optional) The network configuration for the service. This parameter is required for task definitions that use the `awsvpc` network mode to receive their own Elastic Network Interface, and it is not supported for other network modes. +* `load_balancer` - (Optional) A load balancer block. Load balancers documented below. +* `service_registries` - (Optional) The service discovery registries for the service. The maximum number of `service_registries` blocks is `1`. +* `launch_type` - (Optional) The launch type on which to run your service. The valid values are `EC2` and `FARGATE`. Defaults to `EC2`. +* `capacity_provider_strategy` - (Optional) The capacity provider strategy to use for the service. Can be one or more. Defined below. +* `platform_version` - (Optional) The platform version on which to run your service. Only applicable for `launch_type` set to `FARGATE`. Defaults to `LATEST`. More information about Fargate platform versions can be found in the [AWS ECS User Guide](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/platform_versions.html). +* `scale` - (Optional) A floating-point percentage of the desired number of tasks to place and keep running in the task set. `unit` determines the interpretation of this number (a percentage of the service's desired count). Accepted values are numbers between 0 and 100. +* `force_delete` - (Optional) Allows deleting the task set without waiting for scaling down to 0. You can force a task set to delete even if it's in the process of scaling a resource. Normally, Terraform drains all the tasks before deleting the task set. This bypasses that behavior and potentially leaves resources dangling. +* `wait_until_stable` - (Optional) Apply will wait until the task set has reached `STEADY_STATE` +* `wait_until_stable_timeout` - (Optional) Wait timeout for task set to reach `STEADY_STATE`. Default `10m` +* `tags` - (Optional) Key-value map of resource tags + +## capacity_provider_strategy + +The `capacity_provider_strategy` configuration block supports the following: + +* `capacity_provider` - (Required) The short name or full Amazon Resource Name (ARN) of the capacity provider. +* `weight` - (Required) The relative percentage of the total number of launched tasks that should use the specified capacity provider. +* `base` - (Optional) The number of tasks, at a minimum, to run on the specified capacity provider. Only one capacity provider in a capacity provider strategy can have a base defined. + +## scale + +The `scale` configuration block supports the following: + +* `unit` - (Optional) The unit of measure for the scale value. Default: `PERCENT` +* `value` - (Required) The value, specified as a percent total of a service's `desiredCount`, to scale the task set. Accepted values are numbers between 0.0 and 100.0. + +## load_balancer + +`load_balancer` supports the following: + +* `elb_name` - (Required for ELB Classic) The name of the ELB (Classic) to associate with the service. +* `target_group_arn` - (Required for ALB/NLB) The ARN of the Load Balancer target group to associate with the service. +* `container_name` - (Required) The name of the container to associate with the load balancer (as it appears in a container definition). +* `container_port` - (Required) The port on the container to associate with the load balancer. + +-> **Note:** Multiple `load_balancer` configuration is still not supported by AWS for ECS task set. + +## network_configuration + +`network_configuration` support the following: + +* `subnets` - (Required) The subnets associated with the task or service. +* `security_groups` - (Optional) The security groups associated with the task or service. If you do not specify a security group, the default security group for the VPC is used. +* `assign_public_ip` - (Optional) Assign a public IP address to the ENI (Fargate launch type only). Valid values are `true` or `false`. Default `false`. + +For more information, see [Task Networking](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-networking.html) + +## service_registries + +`service_registries` support the following: + +* `registry_arn` - (Required) The ARN of the Service Registry. The currently supported service registry is Amazon Route 53 Auto Naming Service(`aws_service_discovery_service`). For more information, see [Service](https://docs.aws.amazon.com/Route53/latest/APIReference/API_autonaming_Service.html) +* `port` - (Optional) The port value used if your Service Discovery service specified an SRV record. +* `container_port` - (Optional) The port value, already specified in the task definition, to be used for your service discovery service. +* `container_name` - (Optional) The container name value, already specified in the task definition, to be used for your service discovery service. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The ID of the task set +* `arn` - The Amazon Resource Name (ARN) that identifies the task set From f10752ef019d19f3a899bdc49440eea0d53a4b0f Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Wed, 23 Sep 2020 11:18:00 -0700 Subject: [PATCH 2/6] Don't set network_configuration property on services with EXTERNAL deployment controller --- aws/resource_aws_ecs_service.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_ecs_service.go b/aws/resource_aws_ecs_service.go index a0d96724812..db84aebf9a8 100644 --- a/aws/resource_aws_ecs_service.go +++ b/aws/resource_aws_ecs_service.go @@ -719,8 +719,10 @@ func resourceAwsEcsServiceRead(d *schema.ResourceData, meta interface{}) error { log.Printf("[ERR] Error setting placement_constraints for (%s): %s", d.Id(), err) } - if err := d.Set("network_configuration", flattenEcsNetworkConfiguration(service.NetworkConfiguration)); err != nil { - return fmt.Errorf("error setting network_configuration for (%s): %w", d.Id(), err) + if service.DeploymentController == nil || aws.StringValue(service.DeploymentController.Type) != ecs.DeploymentControllerTypeExternal { + if err := d.Set("network_configuration", flattenEcsNetworkConfiguration(service.NetworkConfiguration)); err != nil { + return fmt.Errorf("error setting network_configuration for (%s): %w", d.Id(), err) + } } if err := d.Set("service_registries", flattenServiceRegistries(service.ServiceRegistries)); err != nil { From ef37806ab632402b021cf347e3aaeb6885d92806 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 19 Jul 2021 16:40:13 -0700 Subject: [PATCH 3/6] Automate releases to figma/aws on terraform registry --- .github/workflows/release.yml | 93 +++++++++++++++++------------------ .goreleaser.yml | 69 ++++++++++++++------------ 2 files changed, 83 insertions(+), 79 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e86bad8f7b2..190d274978f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,54 +1,51 @@ -name: Post Publish +# This GitHub action can publish assets for release when a tag is created. +# Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0). +# +# This uses an action (hashicorp/ghaction-import-gpg) that assumes you set your +# private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE` +# secret. If you would rather own your own GPG handling, please fork this action +# or use an alternative one for key handling. +# +# You will need to pass the `--batch` flag to `gpg` in your signing step +# in `goreleaser` to indicate this is being used in a non-interactive mode. +# +name: release on: - release: - types: [published] + push: + tags: + - 'v*' +permissions: + contents: write jobs: - tidy: - name: Tidy Asana + goreleaser: runs-on: ubuntu-latest steps: - - uses: breathingdust/github-asana-tidy@v1 + - + name: Checkout + uses: actions/checkout@v2.3.4 + - + name: Unshallow + run: git fetch --prune --unshallow + - + name: Set up Go + uses: actions/setup-go@v2 with: - asana_pat: ${{ secrets.asana_pat }} - asana_target_section_gid: '1141945723817371' - asana_workspace_gid: '90955849329269' - asana_project_gid: '632425409545160' - asana_github_url_field_gid: '1134594824474912' - github_release_name: ${{ github.event.release.tag_name }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - project-archive: - name: Archive Released Cards - runs-on: ubuntu-latest - steps: - - uses: breathingdust/github-project-archive@v1 + go-version: 1.16 + - + name: Import GPG key + id: import_gpg + uses: hashicorp/ghaction-import-gpg@v2.1.0 + env: + # These secrets will need to be configured for the repository: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + PASSPHRASE: ${{ secrets.PASSPHRASE }} + - + name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2.5.0 with: - github_done_column_id: 11513756 - github_release_name: ${{ github.event.release.tag_name }} - github_token: ${{ secrets.ORGSCOPED_GITHUB_TOKEN }} - changelog-newversion: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - ref: main - - run: | - CHANGELOG_FILE_NAME="CHANGELOG.md" - PREVIOUS_RELEASE_TAG=$(git describe --abbrev=0 --match='v*.*.*' --tags) - echo Previous release is: $PREVIOUS_RELEASE_TAG - - NEW_RELEASE_LINE=$(echo $PREVIOUS_RELEASE_TAG | awk -F. '{ - $1 = substr($1,2) - $2 += 1 - printf("%s.%02d.%s\n\n", $1, $2, $3); - }') - - echo New minor version is: v$NEW_RELEASE_LINE - - echo -e "## $NEW_RELEASE_LINE (Unreleased)\n$(cat $CHANGELOG_FILE_NAME)" > $CHANGELOG_FILE_NAME - - run: | - git config --local user.email changelogbot@hashicorp.com - git config --local user.name changelogbot - git add CHANGELOG.md - git commit -m "Update CHANGELOG.md after ${{ github.event.release.tag_name }}" - git push + version: latest + args: release --rm-dist --timeout 60m + env: + GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} + # GitHub sets this automatically + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yml b/.goreleaser.yml index fe1eb97fe8c..68390d523ed 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,43 +1,45 @@ -archives: - - files: - - none* - format: zip - name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' +# Visit https://goreleaser.com for documentation on how to customize this +# behavior. before: hooks: - - go mod download + # this is just an example and not a requirement for provider building/publishing + - go mod tidy builds: - - binary: '{{ .ProjectName }}_{{ .Version }}' - flags: - - -trimpath - goarch: - - '386' - - amd64 - - arm - - arm64 - goos: - - darwin - - freebsd - - linux - - windows - ignore: - - goarch: '386' - goos: darwin - ldflags: - - -s -w -X version.ProviderVersion={{.Version}} - mod_timestamp: '{{ .CommitTimestamp }}' -changelog: - skip: true +- env: + # goreleaser does not work with CGO, it could also complicate + # usage by users in CI/CD systems like Terraform Cloud where + # they are unable to install libraries. + - CGO_ENABLED=0 + mod_timestamp: '{{ .CommitTimestamp }}' + flags: + - -trimpath + ldflags: + - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' + goos: + - freebsd + - windows + - linux + - darwin + goarch: + - amd64 + - '386' + - arm + - arm64 + ignore: + - goos: darwin + goarch: '386' + binary: '{{ .ProjectName }}_v{{ .Version }}' +archives: +- format: zip + name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' checksum: name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' algorithm: sha256 -env: - - CGO_ENABLED=0 -release: - disable: true signs: - artifacts: checksum args: + # if you are using this in a GitHub action or some other automated pipeline, you + # need to pass the batch flag to indicate its not interactive. - "--batch" - "--local-user" - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key @@ -45,3 +47,8 @@ signs: - "${signature}" - "--detach-sign" - "${artifact}" +release: + # If you want to manually examine the release before its live, uncomment this line: + # draft: true +changelog: + skip: true From 35bb30f0fa9c89a9a74ac711232bf96c021d7290 Mon Sep 17 00:00:00 2001 From: David Mah Date: Tue, 27 Jul 2021 17:00:48 -0700 Subject: [PATCH 4/6] ForceNew for task set network configuration --- aws/resource_aws_ecs_task_set.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws/resource_aws_ecs_task_set.go b/aws/resource_aws_ecs_task_set.go index f77d59d8b1c..4ec47d7c38c 100644 --- a/aws/resource_aws_ecs_task_set.go +++ b/aws/resource_aws_ecs_task_set.go @@ -72,6 +72,7 @@ func resourceAwsEcsTaskSet() *schema.Resource { Type: schema.TypeSet, MaxItems: 5, Optional: true, + ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, @@ -79,6 +80,7 @@ func resourceAwsEcsTaskSet() *schema.Resource { Type: schema.TypeSet, MaxItems: 16, Required: true, + ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, @@ -86,6 +88,7 @@ func resourceAwsEcsTaskSet() *schema.Resource { Type: schema.TypeBool, Optional: true, Default: false, + ForceNew: true, }, }, }, From d517c589a4d0afc74c561bfdbb28dc9e88671ae3 Mon Sep 17 00:00:00 2001 From: David Mah Date: Wed, 28 Jul 2021 10:48:32 -0700 Subject: [PATCH 5/6] ListenerRule: Do not replace for priority tweaks (#296) --- aws/resource_aws_lb_listener_rule.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_lb_listener_rule.go b/aws/resource_aws_lb_listener_rule.go index 4945fa860e0..5d0d2a61885 100644 --- a/aws/resource_aws_lb_listener_rule.go +++ b/aws/resource_aws_lb_listener_rule.go @@ -44,10 +44,11 @@ func resourceAwsLbbListenerRule() *schema.Resource { ValidateFunc: validateArn, }, "priority": { - Type: schema.TypeInt, - Optional: true, - Computed: true, - ForceNew: true, + Type: schema.TypeInt, + Optional: true, + Computed: true, + // ForceNew: true is not needed as this provider does use SetRulePriorities for updates + // https://github.com/hashicorp/terraform-provider-aws/pull/9848 ValidateFunc: validateAwsLbListenerRulePriority, }, "action": { From 259abf5a9586eb323c730ba32a9d3b45afe09fa4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Sep 2021 08:17:10 +0000 Subject: [PATCH 6/6] build(deps): bump github.com/hashicorp/terraform-plugin-sdk/v2 Bumps [github.com/hashicorp/terraform-plugin-sdk/v2](https://github.com/hashicorp/terraform-plugin-sdk) from 2.7.0 to 2.8.0. - [Release notes](https://github.com/hashicorp/terraform-plugin-sdk/releases) - [Changelog](https://github.com/hashicorp/terraform-plugin-sdk/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/terraform-plugin-sdk/compare/v2.7.0...v2.8.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/terraform-plugin-sdk/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- awsproviderlint/go.mod | 2 +- awsproviderlint/go.sum | 8 +- .../terraform-plugin-go/tfprotov5/schema.go | 2 +- .../tfprotov5/{server => tf5server}/doc.go | 0 .../tfprotov5/{server => tf5server}/plugin.go | 0 .../tfprotov5/{server => tf5server}/server.go | 0 .../terraform-plugin-go/tfprotov6/schema.go | 4 +- .../tfprotov6/{server => tf6server}/doc.go | 0 .../tfprotov6/{server => tf6server}/plugin.go | 0 .../tfprotov6/{server => tf6server}/server.go | 0 .../tftypes/attribute_path.go | 2 +- .../tftypes/attribute_path_error.go | 22 +++- .../terraform-plugin-go/tftypes/diff.go | 16 ++- .../terraform-plugin-go/tftypes/list.go | 62 +++++----- .../terraform-plugin-go/tftypes/map.go | 77 ++++++------ .../terraform-plugin-go/tftypes/object.go | 93 ++++++++------- .../terraform-plugin-go/tftypes/primitive.go | 110 +++--------------- .../terraform-plugin-go/tftypes/set.go | 62 +++++----- .../terraform-plugin-go/tftypes/tuple.go | 76 ++++++------ .../terraform-plugin-go/tftypes/type.go | 87 +++----------- .../terraform-plugin-go/tftypes/value.go | 47 ++++++-- .../terraform-plugin-go/tftypes/value_json.go | 30 +++-- .../tftypes/value_msgpack.go | 57 +++++---- .../terraform-plugin-go/tftypes/walk.go | 24 +++- .../v2/helper/schema/grpc_provider.go | 26 ++++- .../v2/helper/schema/provider.go | 10 +- .../v2/helper/schema/resource_data.go | 48 ++++++++ .../v2/helper/schema/resource_diff.go | 49 ++++++++ .../v2/helper/schema/schema.go | 3 + .../v2/helper/schema/shims.go | 10 +- .../v2/internal/plugin/convert/diagnostics.go | 2 +- .../v2/internal/plugin/convert/schema.go | 4 +- .../terraform-plugin-sdk/v2/meta/meta.go | 2 +- .../terraform-plugin-sdk/v2/plugin/serve.go | 4 +- .../terraform-plugin-sdk/v2/terraform/diff.go | 4 + .../v2/terraform/state.go | 4 + awsproviderlint/vendor/modules.txt | 8 +- 37 files changed, 543 insertions(+), 412 deletions(-) rename awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/{server => tf5server}/doc.go (100%) rename awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/{server => tf5server}/plugin.go (100%) rename awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/{server => tf5server}/server.go (100%) rename awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/{server => tf6server}/doc.go (100%) rename awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/{server => tf6server}/plugin.go (100%) rename awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/{server => tf6server}/server.go (100%) diff --git a/awsproviderlint/go.mod b/awsproviderlint/go.mod index 9eab7b3440f..7252f3d0620 100644 --- a/awsproviderlint/go.mod +++ b/awsproviderlint/go.mod @@ -5,6 +5,6 @@ go 1.16 require ( github.com/aws/aws-sdk-go v1.40.20 github.com/bflad/tfproviderlint v0.27.1 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.8.0 golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb ) diff --git a/awsproviderlint/go.sum b/awsproviderlint/go.sum index 4328e8439e2..11f65e2555a 100644 --- a/awsproviderlint/go.sum +++ b/awsproviderlint/go.sum @@ -229,13 +229,13 @@ github.com/hashicorp/terraform-json v0.8.0/go.mod h1:3defM4kkMfttwiE7VakJDwCd4R+ github.com/hashicorp/terraform-json v0.12.0 h1:8czPgEEWWPROStjkWPUnTQDXmpmZPlkQAwYYLETaTvw= github.com/hashicorp/terraform-json v0.12.0/go.mod h1:pmbq9o4EuL43db5+0ogX10Yofv1nozM+wskr/bGFJpI= github.com/hashicorp/terraform-plugin-go v0.2.1/go.mod h1:10V6F3taeDWVAoLlkmArKttR3IULlRWFAGtQIQTIDr4= -github.com/hashicorp/terraform-plugin-go v0.3.0 h1:AJqYzP52JFYl9NABRI7smXI1pNjgR5Q/y2WyVJ/BOZA= -github.com/hashicorp/terraform-plugin-go v0.3.0/go.mod h1:dFHsQMaTLpON2gWhVWT96fvtlc/MF1vSy3OdMhWBzdM= +github.com/hashicorp/terraform-plugin-go v0.4.0 h1:LFbXNeLDo0J/wR0kUzSPq0RpdmFh2gNedzU0n/gzPAo= +github.com/hashicorp/terraform-plugin-go v0.4.0/go.mod h1:7u/6nt6vaiwcWE2GuJKbJwNlDFnf5n95xKw4hqIVr58= github.com/hashicorp/terraform-plugin-sdk v1.16.1 h1:G2iK7MBT4LuNcVASPXWS1ciBUuIm8oIY0zRfCmi3xy4= github.com/hashicorp/terraform-plugin-sdk v1.16.1/go.mod h1:KSsGcuZ1JRqnmYzz+sWIiUwNvJkzXbGRIdefwFfOdyY= github.com/hashicorp/terraform-plugin-sdk/v2 v2.5.0/go.mod h1:z+cMZ0iswzZOahBJ3XmNWgWkVnAd2bl8g+FhyyuPDH4= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0 h1:SuI59MqNjYDrL7EfqHX9V6P/24isgqYx/FdglwVs9bg= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0/go.mod h1:grseeRo9g3yNkYW09iFlV8LG78jTa1ssBgouogQg/RU= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.8.0 h1:GSumgrL6GGcRYU37YuF1CC59hRPR7Yzy6tpoFlo8wr4= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.8.0/go.mod h1:6KbP09YzlB++S6XSUKYl83WyoHVN4MgeoCbPRsdfCtA= github.com/hashicorp/terraform-plugin-test/v2 v2.1.3/go.mod h1:pmaUHiUtDL/8Mz3FuyZ/vRDb0LpaOWQjVRW9ORF7FHs= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/schema.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/schema.go index ffcb96577c0..5f1acc34d04 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/schema.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/schema.go @@ -29,7 +29,7 @@ const ( // SchemaNestedBlockNestingModeMap indicates that multiple instances of // the nested block should be permitted, each with a single label, and // that they should be represented in state and config values as a - // tftypes.Map, with an AttributeType of tftypes.Object. The labels on + // tftypes.Map, with an ElementType of tftypes.Object. The labels on // the blocks will be used as the map keys. It is an error, therefore, // to use the same label value on multiple block instances. SchemaNestedBlockNestingModeMap SchemaNestedBlockNestingMode = 4 diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/server/doc.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server/doc.go similarity index 100% rename from awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/server/doc.go rename to awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server/doc.go diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/server/plugin.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server/plugin.go similarity index 100% rename from awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/server/plugin.go rename to awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server/plugin.go diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/server/server.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server/server.go similarity index 100% rename from awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/server/server.go rename to awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server/server.go diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/schema.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/schema.go index 17ead5b71e1..a7e139fed25 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/schema.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/schema.go @@ -29,7 +29,7 @@ const ( // SchemaNestedBlockNestingModeMap indicates that multiple instances of // the nested block should be permitted, each with a single label, and // that they should be represented in state and config values as a - // tftypes.Map, with an AttributeType of tftypes.Object. The labels on + // tftypes.Map, with an ElementType of tftypes.Object. The labels on // the blocks will be used as the map keys. It is an error, therefore, // to use the same label value on multiple block instances. SchemaNestedBlockNestingModeMap SchemaNestedBlockNestingMode = 4 @@ -73,7 +73,7 @@ const ( // SchemaObjectNestingModeMap indicates that multiple instances of the // nested type should be permitted, and that they should be appear in state - // and config values as a tftypes.Map, with an AttributeType of + // and config values as a tftypes.Map, with an ElementType of // tftypes.Object. SchemaObjectNestingModeMap SchemaObjectNestingMode = 4 ) diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/server/doc.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server/doc.go similarity index 100% rename from awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/server/doc.go rename to awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server/doc.go diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/server/plugin.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server/plugin.go similarity index 100% rename from awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/server/plugin.go rename to awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server/plugin.go diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/server/server.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server/server.go similarity index 100% rename from awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/server/server.go rename to awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server/server.go diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/attribute_path.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/attribute_path.go index 84eabe1453a..256756fe4fc 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/attribute_path.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/attribute_path.go @@ -149,7 +149,7 @@ func (a *AttributePath) WithElementKeyString(key string) *AttributePath { // WithElementKeyInt adds an ElementKeyInt step to `a`, using `key` as the // element's key. `a` is copied, not modified. -func (a *AttributePath) WithElementKeyInt(key int64) *AttributePath { +func (a *AttributePath) WithElementKeyInt(key int) *AttributePath { steps := a.Steps() return &AttributePath{ steps: append(steps, ElementKeyInt(key)), diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/attribute_path_error.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/attribute_path_error.go index f2276fc05da..8456650bcdb 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/attribute_path_error.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/attribute_path_error.go @@ -11,8 +11,28 @@ type AttributePathError struct { err error } +func (a AttributePathError) Equal(o AttributePathError) bool { + if !a.Path.Equal(o.Path) { + return false + } + + if (a.err == nil && o.err != nil) || (a.err != nil && o.err == nil) { + return false + } + + if a.err == nil { + return true + } + + return a.err.Error() == o.err.Error() +} + func (a AttributePathError) Error() string { - return fmt.Sprintf("%s: %s", a.Path, a.err) + var path string + if len(a.Path.Steps()) > 0 { + path = a.Path.String() + ": " + } + return fmt.Sprintf("%s%s", path, a.err) } func (a AttributePathError) Unwrap() error { diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/diff.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/diff.go index afd302de528..dc89026c53b 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/diff.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/diff.go @@ -71,14 +71,22 @@ func (v ValueDiff) Equal(o ValueDiff) bool { // a slice of ValueDiffs. The ValueDiffs in the struct will use `val1`'s values // as Value1 and `val2`'s values as Value2. An empty or nil slice means the two // Values can be considered equal. Values must be the same type when passed to -// Diff; passing in Values of two different types will result in an error. -// val1.Type().Is(val2.Type()) is a safe way to check that Values can be -// compared with Diff. +// Diff; passing in Values of two different types will result in an error. If +// both Values are empty, they are considered equal. If one Value is missing +// type, it will result in an error. val1.Type().Is(val2.Type()) is a safe way +// to check that Values can be compared with Diff. func (val1 Value) Diff(val2 Value) ([]ValueDiff, error) { + var diffs []ValueDiff + + if val1.Type() == nil && val2.Type() == nil && val1.value == nil && val2.value == nil { + return diffs, nil + } + if (val1.Type() == nil && val2.Type() != nil) || (val1.Type() != nil && val2.Type() == nil) { + return nil, errors.New("cannot diff value missing type") + } if !val1.Type().Is(val2.Type()) { return nil, errors.New("Can't diff values of different types") } - var diffs []ValueDiff // make sure everything in val2 is also in val1 err := Walk(val2, func(path *AttributePath, value2 Value) (bool, error) { diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/list.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/list.go index 52abc7c369f..a633ed1e048 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/list.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/list.go @@ -17,20 +17,8 @@ type List struct { // Equal returns true if the two Lists are exactly equal. Unlike Is, passing in // a List with no ElementType will always return false. -func (l List) Equal(o List) bool { - return l.equals(o, true) -} - -// Is returns whether `t` is a List type or not. If `t` is an instance of the -// List type and its ElementType property is nil, it will return true. If `t`'s -// ElementType property is not nil, it will only return true if its ElementType -// is considered the same type as `l`'s ElementType. -func (l List) Is(t Type) bool { - return l.equals(t, false) -} - -func (l List) equals(t Type, exact bool) bool { - v, ok := t.(List) +func (l List) Equal(o Type) bool { + v, ok := o.(List) if !ok { return false } @@ -38,19 +26,33 @@ func (l List) equals(t Type, exact bool) bool { // when doing exact comparisons, we can't compare types that // don't have element types set, so we just consider them not // equal - // - // when doing inexact comparisons, the absence of an element - // type just means "is this a List?" We know it is, so return - // true if and only if l has an ElementType and t doesn't. This - // behavior only makes sense if the user is trying to see if a - // proper type is a list, so we want to ensure that the method - // receiver always has an element type. - if exact { - return false - } - return l.ElementType != nil + return false } - return l.ElementType.equals(v.ElementType, exact) + return l.ElementType.Equal(v.ElementType) +} + +// UsableAs returns whether the two Lists are type compatible. +// +// If the other type is DynamicPseudoType, it will return true. +// If the other type is not a List, it will return false. +// If the other List does not have a type compatible ElementType, it will +// return false. +func (l List) UsableAs(o Type) bool { + if o.Is(DynamicPseudoType) { + return true + } + v, ok := o.(List) + if !ok { + return false + } + return l.ElementType.UsableAs(v.ElementType) +} + +// Is returns whether `t` is a List type or not. It does not perform any +// ElementType checks. +func (l List) Is(t Type) bool { + _, ok := t.(List) + return ok } func (l List) String() string { @@ -68,13 +70,15 @@ func valueFromList(typ Type, in interface{}) (Value, error) { case []Value: var valType Type for pos, v := range value { - if err := useTypeAs(v.Type(), typ, NewAttributePath().WithElementKeyInt(int64(pos))); err != nil { - return Value{}, err + if v.Type().Is(DynamicPseudoType) && v.IsKnown() { + return Value{}, NewAttributePath().WithElementKeyInt(pos).NewErrorf("invalid value %s for %s", v, v.Type()) + } else if !v.Type().Is(DynamicPseudoType) && !v.Type().UsableAs(typ) { + return Value{}, NewAttributePath().WithElementKeyInt(pos).NewErrorf("can't use %s as %s", v.Type(), typ) } if valType == nil { valType = v.Type() } - if !v.Type().equals(valType, true) { + if !v.Type().Equal(valType) { return Value{}, fmt.Errorf("lists must only contain one type of element, saw %s and %s", valType, v.Type()) } } diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/map.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/map.go index fe903897950..9188df88f4d 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/map.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/map.go @@ -8,7 +8,7 @@ import ( // Map is a Terraform type representing an unordered collection of elements, // all of the same type, each identifiable with a unique string key. type Map struct { - AttributeType Type + ElementType Type // used to make this type uncomparable // see https://golang.org/ref/spec#Comparison_operators @@ -17,44 +17,47 @@ type Map struct { } // Equal returns true if the two Maps are exactly equal. Unlike Is, passing in -// a Map with no AttributeType will always return false. -func (m Map) Equal(o Map) bool { - return m.equals(o, true) -} - -// Is returns whether `t` is a Map type or not. If `t` is an instance of the -// Map type and its AttributeType property is not nil, it will only return true -// if its AttributeType is considered the same type as `m`'s AttributeType. -func (m Map) Is(t Type) bool { - return m.equals(t, false) -} - -func (m Map) equals(t Type, exact bool) bool { - v, ok := t.(Map) +// a Map with no ElementType will always return false. +func (m Map) Equal(o Type) bool { + v, ok := o.(Map) if !ok { return false } - if v.AttributeType == nil || m.AttributeType == nil { + if v.ElementType == nil || m.ElementType == nil { // when doing exact comparisons, we can't compare types that // don't have element types set, so we just consider them not // equal - // - // when doing inexact comparisons, the absence of an element - // type just means "is this a Map?" We know it is, so return - // true if and only if m has an ElementType and t doesn't. This - // behavior only makes sense if the user is trying to see if a - // proper type is a map, so we want to ensure that the method - // receiver always has an element type. - if exact { - return false - } - return m.AttributeType != nil + return false } - return m.AttributeType.equals(v.AttributeType, exact) + return m.ElementType.Equal(v.ElementType) +} + +// UsableAs returns whether the two Maps are type compatible. +// +// If the other type is DynamicPseudoType, it will return true. +// If the other type is not a Map, it will return false. +// If the other Map does not have a type compatible ElementType, it will +// return false. +func (m Map) UsableAs(o Type) bool { + if o.Is(DynamicPseudoType) { + return true + } + v, ok := o.(Map) + if !ok { + return false + } + return m.ElementType.UsableAs(v.ElementType) +} + +// Is returns whether `t` is a Map type or not. It does not perform any +// ElementType checks. +func (m Map) Is(t Type) bool { + _, ok := t.(Map) + return ok } func (m Map) String() string { - return "tftypes.Map[" + m.AttributeType.String() + "]" + return "tftypes.Map[" + m.ElementType.String() + "]" } func (m Map) private() {} @@ -64,13 +67,13 @@ func (m Map) supportedGoTypes() []string { } // MarshalJSON returns a JSON representation of the full type signature of `m`, -// including its AttributeType. +// including its ElementType. // // Deprecated: this is not meant to be called by third-party code. func (m Map) MarshalJSON() ([]byte, error) { - attributeType, err := m.AttributeType.MarshalJSON() + attributeType, err := m.ElementType.MarshalJSON() if err != nil { - return nil, fmt.Errorf("error marshaling tftypes.Map's attribute type %T to JSON: %w", m.AttributeType, err) + return nil, fmt.Errorf("error marshaling tftypes.Map's attribute type %T to JSON: %w", m.ElementType, err) } return []byte(`["map",` + string(attributeType) + `]`), nil } @@ -86,18 +89,20 @@ func valueFromMap(typ Type, in interface{}) (Value, error) { var elType Type for _, k := range keys { v := value[k] - if err := useTypeAs(v.Type(), typ, NewAttributePath().WithElementKeyString(k)); err != nil { - return Value{}, err + if v.Type().Is(DynamicPseudoType) && v.IsKnown() { + return Value{}, NewAttributePath().WithElementKeyString(k).NewErrorf("invalid value %s for %s", v, v.Type()) + } else if !v.Type().Is(DynamicPseudoType) && !v.Type().UsableAs(typ) { + return Value{}, NewAttributePath().WithElementKeyString(k).NewErrorf("can't use %s as %s", v.Type(), typ) } if elType == nil { elType = v.Type() } - if !elType.equals(v.Type(), true) { + if !elType.Equal(v.Type()) { return Value{}, fmt.Errorf("maps must only contain one type of element, saw %s and %s", elType, v.Type()) } } return Value{ - typ: Map{AttributeType: typ}, + typ: Map{ElementType: typ}, value: value, }, nil default: diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/object.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/object.go index 2da1631f495..eedfbf98011 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/object.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/object.go @@ -40,21 +40,8 @@ type Object struct { // Equal returns true if the two Objects are exactly equal. Unlike Is, passing // in an Object with no AttributeTypes will always return false. -func (o Object) Equal(other Object) bool { - return o.equals(other, true) -} - -// Is returns whether `t` is an Object type or not. If `t` is an instance of -// the Object type and its AttributeTypes property is not nil, it will only -// return true the AttributeTypes are considered the same. To be considered -// equal, the same set of keys must be present in each, and each key's value -// needs to be considered the same type between the two Objects. -func (o Object) Is(t Type) bool { - return o.equals(t, false) -} - -func (o Object) equals(t Type, exact bool) bool { - v, ok := t.(Object) +func (o Object) Equal(other Type) bool { + v, ok := other.(Object) if !ok { return false } @@ -62,17 +49,7 @@ func (o Object) equals(t Type, exact bool) bool { // when doing exact comparisons, we can't compare types that // don't have attribute types set, so we just consider them not // equal - // - // when doing inexact comparisons, the absence of an attribute - // type just means "is this a Object?" We know it is, so return - // true if and only if o has AttributeTypes and t doesn't. This - // behavior only makes sense if the user is trying to see if a - // proper type is a object, so we want to ensure that the - // method receiver always has attribute types. - if exact { - return false - } - return o.AttributeTypes != nil + return false } // if the don't have the exact same optional attributes, they're not @@ -95,13 +72,56 @@ func (o Object) equals(t Type, exact bool) bool { if _, ok := v.AttributeTypes[k]; !ok { return false } - if !typ.equals(v.AttributeTypes[k], exact) { + if !typ.Equal(v.AttributeTypes[k]) { return false } } return true } +// UsableAs returns whether the two Objects are type compatible. +// +// If the other type is DynamicPseudoType, it will return true. +// If the other type is not a Object, it will return false. +// If the other Object does not have matching AttributeTypes length, it will +// return false. +// If the other Object does not have a type compatible ElementType for every +// nested attribute, it will return false. +// +// If the current type contains OptionalAttributes, it will panic. +func (o Object) UsableAs(other Type) bool { + if other.Is(DynamicPseudoType) { + return true + } + v, ok := other.(Object) + if !ok { + return false + } + if len(o.OptionalAttributes) > 0 { + panic("Objects with OptionalAttributes cannot be used.") + } + if len(v.AttributeTypes) != len(o.AttributeTypes) { + return false + } + for k, typ := range o.AttributeTypes { + otherTyp, ok := v.AttributeTypes[k] + if !ok { + return false + } + if !typ.UsableAs(otherTyp) { + return false + } + } + return true +} + +// Is returns whether `t` is an Object type or not. It does not perform any +// AttributeTypes checks. +func (o Object) Is(t Type) bool { + _, ok := t.(Object) + return ok +} + func (o Object) attrIsOptional(attr string) bool { if o.OptionalAttributes == nil { return false @@ -138,15 +158,6 @@ func (o Object) supportedGoTypes() []string { return []string{"map[string]tftypes.Value"} } -func valueCanBeObject(val interface{}) bool { - switch val.(type) { - case map[string]Value: - return true - default: - return false - } -} - func valueFromObject(types map[string]Type, optionalAttrs map[string]struct{}, in interface{}) (Value, error) { switch value := in.(type) { case map[string]Value: @@ -169,9 +180,13 @@ func valueFromObject(types map[string]Type, optionalAttrs map[string]struct{}, i if !ok { return Value{}, fmt.Errorf("can't set a value on %q in tftypes.NewValue, key not part of the object type %s", k, Object{AttributeTypes: types}) } - err := useTypeAs(v.Type(), typ, NewAttributePath().WithAttributeName(k)) - if err != nil { - return Value{}, err + if v.Type() == nil { + return Value{}, NewAttributePath().WithAttributeName(k).NewErrorf("missing value type") + } + if v.Type().Is(DynamicPseudoType) && v.IsKnown() { + return Value{}, NewAttributePath().WithAttributeName(k).NewErrorf("invalid value %s for %s", v, v.Type()) + } else if !v.Type().Is(DynamicPseudoType) && !v.Type().UsableAs(typ) { + return Value{}, NewAttributePath().WithAttributeName(k).NewErrorf("can't use %s as %s", v.Type(), typ) } } } diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/primitive.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/primitive.go index 905056ed52c..b0a3c8fc3ef 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/primitive.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/primitive.go @@ -37,20 +37,27 @@ type primitive struct { _ []struct{} } -func (p primitive) Equal(o primitive) bool { - return p.equals(o, true) +func (p primitive) Equal(o Type) bool { + v, ok := o.(primitive) + if !ok { + return false + } + return p.name == v.name } func (p primitive) Is(t Type) bool { - return p.equals(t, false) + return p.Equal(t) } -func (p primitive) equals(t Type, exact bool) bool { +func (p primitive) UsableAs(t Type) bool { v, ok := t.(primitive) if !ok { return false } - return p.name == v.name + if v.name == DynamicPseudoType.name { + return true + } + return v.name == p.name } func (p primitive) String() string { @@ -95,30 +102,11 @@ func (p primitive) supportedGoTypes() []string { case Bool.name: return []string{"bool", "*bool"} case DynamicPseudoType.name: - possibleTypes := []Type{ - String, Bool, Number, - Tuple{}, Object{}, - } - results := []string{} - for _, t := range possibleTypes { - results = append(results, t.supportedGoTypes()...) - } - return results + return []string{"nil", "UnknownValue"} } panic(fmt.Sprintf("unknown primitive type %q", p.name)) } -func valueCanBeString(val interface{}) bool { - switch val.(type) { - case string: - return true - case *string: - return true - default: - return false - } -} - func valueFromString(in interface{}) (Value, error) { switch value := in.(type) { case *string: @@ -142,17 +130,6 @@ func valueFromString(in interface{}) (Value, error) { } } -func valueCanBeBool(val interface{}) bool { - switch val.(type) { - case bool: - return true - case *bool: - return true - default: - return false - } -} - func valueFromBool(in interface{}) (Value, error) { switch value := in.(type) { case *bool: @@ -176,25 +153,6 @@ func valueFromBool(in interface{}) (Value, error) { } } -func valueCanBeNumber(val interface{}) bool { - switch val.(type) { - case uint, uint8, uint16, uint32, uint64: - return true - case *uint, *uint8, *uint16, *uint32, *uint64: - return true - case int, int8, int16, int32, int64: - return true - case *int, *int8, *int16, *int32, *int64: - return true - case float64, *float64: - return true - case *big.Float: - return true - default: - return false - } -} - func valueFromNumber(in interface{}) (Value, error) { switch value := in.(type) { case *big.Float: @@ -382,45 +340,3 @@ func valueFromNumber(in interface{}) (Value, error) { return Value{}, fmt.Errorf("tftypes.NewValue can't use %T as a tftypes.Number; expected types are: %s", in, formattedSupportedGoTypes(Number)) } } - -func valueFromDynamicPseudoType(val interface{}) (Value, error) { - switch { - case valueCanBeString(val): - v, err := valueFromString(val) - if err != nil { - return Value{}, err - } - v.typ = DynamicPseudoType - return v, nil - case valueCanBeNumber(val): - v, err := valueFromNumber(val) - if err != nil { - return Value{}, err - } - v.typ = DynamicPseudoType - return v, nil - case valueCanBeBool(val): - v, err := valueFromBool(val) - if err != nil { - return Value{}, err - } - v.typ = DynamicPseudoType - return v, nil - case valueCanBeObject(val): - v, err := valueFromObject(nil, nil, val) - if err != nil { - return Value{}, err - } - v.typ = DynamicPseudoType - return v, nil - case valueCanBeTuple(val): - v, err := valueFromTuple(nil, val) - if err != nil { - return Value{}, err - } - v.typ = DynamicPseudoType - return v, nil - default: - return Value{}, fmt.Errorf("tftypes.NewValue can't use %T as a tftypes.DynamicPseudoType; expected types are: %s", val, formattedSupportedGoTypes(DynamicPseudoType)) - } -} diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/set.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/set.go index 8f456cbee9f..3bd3e8a67e4 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/set.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/set.go @@ -17,20 +17,8 @@ type Set struct { // Equal returns true if the two Sets are exactly equal. Unlike Is, passing in // a Set with no ElementType will always return false. -func (s Set) Equal(o Set) bool { - return s.equals(o, true) -} - -// Is returns whether `t` is a Set type or not. If `t` is an instance of the -// Set type and its ElementType property is nil, it will return true. If `t`'s -// ElementType property is not nil, it will only return true if its ElementType -// is considered the same type as `s`'s ElementType. -func (s Set) Is(t Type) bool { - return s.equals(t, false) -} - -func (s Set) equals(t Type, exact bool) bool { - v, ok := t.(Set) +func (s Set) Equal(o Type) bool { + v, ok := o.(Set) if !ok { return false } @@ -38,19 +26,33 @@ func (s Set) equals(t Type, exact bool) bool { // when doing exact comparisons, we can't compare types that // don't have element types set, so we just consider them not // equal - // - // when doing inexact comparisons, the absence of an element - // type just means "is this a Set?" We know it is, so return - // true if and only if s has an ElementType and t doesn't. This - // behavior only makes sense if the user is trying to see if a - // proper type is a set, so we want to ensure that the method - // receiver always has an element type. - if exact { - return false - } - return s.ElementType != nil + return false } - return s.ElementType.equals(v.ElementType, exact) + return s.ElementType.Equal(v.ElementType) +} + +// UsableAs returns whether the two Sets are type compatible. +// +// If the other type is DynamicPseudoType, it will return true. +// If the other type is not a Set, it will return false. +// If the other Set does not have a type compatible ElementType, it will +// return false. +func (s Set) UsableAs(o Type) bool { + if o.Is(DynamicPseudoType) { + return true + } + v, ok := o.(Set) + if !ok { + return false + } + return s.ElementType.UsableAs(v.ElementType) +} + +// Is returns whether `t` is a Set type or not. It does not perform any +// ElementType checks. +func (s Set) Is(t Type) bool { + _, ok := t.(Set) + return ok } func (s Set) String() string { @@ -68,13 +70,15 @@ func valueFromSet(typ Type, in interface{}) (Value, error) { case []Value: var elType Type for _, v := range value { - if err := useTypeAs(v.Type(), typ, NewAttributePath().WithElementKeyValue(v)); err != nil { - return Value{}, err + if v.Type().Is(DynamicPseudoType) && v.IsKnown() { + return Value{}, NewAttributePath().WithElementKeyValue(v).NewErrorf("invalid value %s for %s", v, v.Type()) + } else if !v.Type().Is(DynamicPseudoType) && !v.Type().UsableAs(typ) { + return Value{}, NewAttributePath().WithElementKeyValue(v).NewErrorf("can't use %s as %s", v.Type(), typ) } if elType == nil { elType = v.Type() } - if !elType.equals(v.Type(), true) { + if !elType.Equal(v.Type()) { return Value{}, fmt.Errorf("sets must only contain one type of element, saw %s and %s", elType, v.Type()) } } diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/tuple.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/tuple.go index 8defef0cf72..088243bbd56 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/tuple.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/tuple.go @@ -21,22 +21,8 @@ type Tuple struct { // Equal returns true if the two Tuples are exactly equal. Unlike Is, passing // in a Tuple with no ElementTypes will always return false. -func (tu Tuple) Equal(o Tuple) bool { - return tu.equals(o, true) -} - -// Is returns whether `t` is a Tuple type or not. If `t` is an instance of the -// Tuple type and its ElementTypes property is not nil, it will only return -// true if the ElementTypes are considered the same. To be considered the same, -// there must be the same number of ElementTypes, arranged in the same order, -// and the types in each position must be considered the same as the type in -// the same position in the other Tuple. -func (tu Tuple) Is(t Type) bool { - return tu.equals(t, false) -} - -func (tu Tuple) equals(t Type, exact bool) bool { - v, ok := t.(Tuple) +func (tu Tuple) Equal(o Type) bool { + v, ok := o.(Tuple) if !ok { return false } @@ -44,23 +30,53 @@ func (tu Tuple) equals(t Type, exact bool) bool { // when doing exact comparisons, we can't compare types that // don't have element types set, so we just consider them not // equal - // - // when doing inexact comparisons, the absence of an element - // type just means "is this a Tuple?" We know it is, so return - // true - return !exact + return false + } + if len(v.ElementTypes) != len(tu.ElementTypes) { + return false + } + for pos, typ := range tu.ElementTypes { + if !typ.Equal(v.ElementTypes[pos]) { + return false + } + } + return true +} + +// UsableAs returns whether the two Tuples are type compatible. +// +// If the other type is DynamicPseudoType, it will return true. +// If the other type is not a Tuple, it will return false. +// If the other Tuple does not have matching ElementTypes length, it will +// return false. +// If the other Tuple does not have type compatible ElementTypes in each +// position, it will return false. +func (tu Tuple) UsableAs(o Type) bool { + if o.Is(DynamicPseudoType) { + return true + } + v, ok := o.(Tuple) + if !ok { + return false } if len(v.ElementTypes) != len(tu.ElementTypes) { return false } for pos, typ := range tu.ElementTypes { - if !typ.equals(v.ElementTypes[pos], exact) { + if !typ.UsableAs(v.ElementTypes[pos]) { return false } } return true } +// Is returns whether `t` is a Tuple type or not. It does not perform any +// ElementTypes checks. +func (tu Tuple) Is(t Type) bool { + _, ok := t.(Tuple) + return ok +} + func (tu Tuple) String() string { var res strings.Builder res.WriteString("tftypes.Tuple[") @@ -80,15 +96,6 @@ func (tu Tuple) supportedGoTypes() []string { return []string{"[]tftypes.Value"} } -func valueCanBeTuple(val interface{}) bool { - switch val.(type) { - case []Value: - return true - default: - return false - } -} - func valueFromTuple(types []Type, in interface{}) (Value, error) { switch value := in.(type) { case []Value: @@ -102,9 +109,10 @@ func valueFromTuple(types []Type, in interface{}) (Value, error) { } for pos, v := range value { typ := types[pos] - err := useTypeAs(v.Type(), typ, NewAttributePath().WithElementKeyInt(int64(pos))) - if err != nil { - return Value{}, err + if v.Type().Is(DynamicPseudoType) && v.IsKnown() { + return Value{}, NewAttributePath().WithElementKeyInt(pos).NewErrorf("invalid value %s for %s", v, v.Type()) + } else if !v.Type().Is(DynamicPseudoType) && !v.Type().UsableAs(typ) { + return Value{}, NewAttributePath().WithElementKeyInt(pos).NewErrorf("can't use %s as %s", v.Type(), typ) } } } diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/type.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/type.go index 4c2bc67d55b..955e322fcff 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/type.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/type.go @@ -15,8 +15,19 @@ type Type interface { // Is is used to determine what type a Type implementation is. It is // the recommended method for determining whether two types are // equivalent or not. + + // Is performs shallow type equality checks, in that the root type is + // compared, but underlying attribute/element types are not. Is(Type) bool + // Equal performs deep type equality checks, including attribute/element + // types and whether attributes are optional or not. + Equal(Type) bool + + // UsableAs performs type conformance checks. This primarily checks if the + // target implements DynamicPsuedoType in a compatible manner. + UsableAs(Type) bool + // String returns a string representation of the Type's name. String() string @@ -35,9 +46,6 @@ type Type interface { // supportedGoTypes returns a list of string representations of the Go // types that the Type supports for its values. supportedGoTypes() []string - - // equals allows for exact or inexact type comparisons. - equals(Type, bool) bool } // TypeFromElements returns the common type that the passed elements all have @@ -50,7 +58,7 @@ func TypeFromElements(elements []Value) (Type, error) { typ = el.Type() continue } - if !typ.Is(el.Type()) { + if !typ.Equal(el.Type()) { return nil, errors.New("elements do not all have the same types") } } @@ -60,75 +68,6 @@ func TypeFromElements(elements []Value) (Type, error) { return typ, nil } -func useTypeAs(candidate, usedAs Type, path *AttributePath) error { - switch { - case usedAs.Is(DynamicPseudoType): - return nil - case usedAs.Is(String), usedAs.Is(Bool), usedAs.Is(Number): - if candidate.Is(usedAs) { - return nil - } - return path.NewErrorf("can't use %s as %s", candidate, usedAs) - case usedAs.Is(List{}): - if !candidate.Is(List{}) { - return path.NewErrorf("can't use %s as %s", candidate, usedAs) - } - return useTypeAs(candidate.(List).ElementType, usedAs.(List).ElementType, path.WithElementKeyInt(0)) - case usedAs.Is(Set{}): - if !candidate.Is(Set{}) { - return path.NewErrorf("can't use %s as %s", candidate, usedAs) - } - return useTypeAs(candidate.(Set).ElementType, usedAs.(Set).ElementType, path.WithElementKeyValue(NewValue(DynamicPseudoType, UnknownValue))) - case usedAs.Is(Map{}): - if !candidate.Is(Map{}) { - return path.NewErrorf("can't use %s as %s", candidate, usedAs) - } - return useTypeAs(candidate.(Map).AttributeType, usedAs.(Map).AttributeType, path.WithElementKeyString("")) - case usedAs.Is(Tuple{}): - if !candidate.Is(Tuple{}) { - return path.NewErrorf("can't use %s as %s", candidate, usedAs) - } - cElems := candidate.(Tuple).ElementTypes - uElems := usedAs.(Tuple).ElementTypes - if len(cElems) != len(uElems) { - return path.NewErrorf("can't use %s as %s", candidate, usedAs) - } - for pos, cElem := range cElems { - uElem := uElems[pos] - err := useTypeAs(cElem, uElem, path.WithElementKeyInt(int64(pos))) - if err != nil { - return err - } - } - case usedAs.Is(Object{}): - if !candidate.Is(Object{}) { - return path.NewErrorf("can't use %s as %s", candidate, usedAs) - } - if len(candidate.(Object).OptionalAttributes) != len(usedAs.(Object).OptionalAttributes) { - return path.NewErrorf("can't use %s as %s", candidate, usedAs) - } - for attr := range usedAs.(Object).OptionalAttributes { - if !candidate.(Object).attrIsOptional(attr) { - return path.NewErrorf("can't use %s as %s", candidate, usedAs) - } - } - if len(candidate.(Object).AttributeTypes) != len(usedAs.(Object).AttributeTypes) { - return path.NewErrorf("can't use %s as %s", candidate, usedAs) - } - for attr, uAttr := range usedAs.(Object).AttributeTypes { - cAttr, ok := candidate.(Object).AttributeTypes[attr] - if !ok { - return path.NewErrorf("can't use %s as %s", candidate, usedAs) - } - err := useTypeAs(cAttr, uAttr, path.WithAttributeName(attr)) - if err != nil { - return err - } - } - } - return nil -} - type jsonType struct { t Type } @@ -204,7 +143,7 @@ func (t *jsonType) UnmarshalJSON(buf []byte) error { return err } t.t = Map{ - AttributeType: ety.t, + ElementType: ety.t, } case "set": var ety jsonType diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/value.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/value.go index 48a24ee4142..6166cb01bb9 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/value.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/value.go @@ -2,6 +2,7 @@ package tftypes import ( "bytes" + "errors" "fmt" "math/big" "sort" @@ -46,6 +47,10 @@ type Value struct { func (val Value) String() string { typ := val.Type() + if typ == nil { + return "invalid typeless tftypes.Value<>" + } + // null and unknown values we use static strings for if val.IsNull() { return typ.String() + "" @@ -200,10 +205,16 @@ func (val Value) ApplyTerraform5AttributePathStep(step AttributePathStep) (inter // considered equal if their types are considered equal and if they represent // data that is considered equal. func (val Value) Equal(o Value) bool { - if val.typ == nil && o.typ == nil && val.value == nil && o.value == nil { + if val.Type() == nil && o.Type() == nil && val.value == nil && o.value == nil { return true } - if !val.Type().Is(o.Type()) { + if val.Type() == nil { + return false + } + if o.Type() == nil { + return false + } + if !val.Type().Equal(o.Type()) { return false } diff, err := val.Diff(o) @@ -284,6 +295,11 @@ func newValue(t Type, val interface{}) (Value, error) { value: val, }, nil } + + if t.Is(DynamicPseudoType) { + return Value{}, errors.New("cannot have DynamicPseudoType with known value, DynamicPseudoType can only contain null or unknown values") + } + if creator, ok := val.(ValueCreator); ok { var err error val, err = creator.ToTerraform5Value() @@ -293,12 +309,6 @@ func newValue(t Type, val interface{}) (Value, error) { } switch { - case t.Is(DynamicPseudoType): - v, err := valueFromDynamicPseudoType(val) - if err != nil { - return Value{}, err - } - return v, nil case t.Is(String): v, err := valueFromString(val) if err != nil { @@ -318,7 +328,7 @@ func newValue(t Type, val interface{}) (Value, error) { } return v, nil case t.Is(Map{}): - v, err := valueFromMap(t.(Map).AttributeType, val) + v, err := valueFromMap(t.(Map).ElementType, val) if err != nil { return Value{}, err } @@ -415,6 +425,10 @@ func (val Value) As(dst interface{}) error { *target = nil return nil } + if *target == nil { + var s string + *target = &s + } return val.As(*target) case *big.Float: if val.IsNull() { @@ -432,6 +446,9 @@ func (val Value) As(dst interface{}) error { *target = nil return nil } + if *target == nil { + *target = big.NewFloat(0) + } return val.As(*target) case *bool: if val.IsNull() { @@ -449,6 +466,10 @@ func (val Value) As(dst interface{}) error { *target = nil return nil } + if *target == nil { + var b bool + *target = &b + } return val.As(*target) case *map[string]Value: if val.IsNull() { @@ -466,6 +487,10 @@ func (val Value) As(dst interface{}) error { *target = nil return nil } + if *target == nil { + m := map[string]Value{} + *target = &m + } return val.As(*target) case *[]Value: if val.IsNull() { @@ -483,6 +508,10 @@ func (val Value) As(dst interface{}) error { *target = nil return nil } + if *target == nil { + l := []Value{} + *target = &l + } return val.As(*target) } return fmt.Errorf("can't unmarshal into %T, needs FromTerraform5Value method", dst) diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/value_json.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/value_json.go index 247556fdeed..c96401adf19 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/value_json.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/value_json.go @@ -53,7 +53,7 @@ func jsonUnmarshal(buf []byte, typ Type, p *AttributePath) (Value, error) { return jsonUnmarshalSet(buf, typ.(Set).ElementType, p) case typ.Is(Map{}): - return jsonUnmarshalMap(buf, typ.(Map).AttributeType, p) + return jsonUnmarshalMap(buf, typ.(Map).ElementType, p) case typ.Is(Tuple{}): return jsonUnmarshalTuple(buf, typ.(Tuple).ElementTypes, p) case typ.Is(Object{}): @@ -216,7 +216,7 @@ func jsonUnmarshalList(buf []byte, elementType Type, p *AttributePath) (Value, e // distinction, so we'll allow it. vals := []Value{} - var idx int64 + var idx int for dec.More() { innerPath := p.WithElementKeyInt(idx) // update the index @@ -355,8 +355,20 @@ func jsonUnmarshalMap(buf []byte, attrType Type, p *AttributePath) (Value, error return Value{}, p.NewErrorf("invalid JSON, expected %q, got %q", json.Delim('}'), tok) } + elTyp := attrType + if attrType.Is(DynamicPseudoType) { + var elements []Value + for _, val := range vals { + elements = append(elements, val) + } + elTyp, err = TypeFromElements(elements) + if err != nil { + return Value{}, p.NewErrorf("invalid elements for map: %w", err) + } + } + return NewValue(Map{ - AttributeType: attrType, + ElementType: elTyp, }, vals), nil } @@ -381,11 +393,12 @@ func jsonUnmarshalTuple(buf []byte, elementTypes []Type, p *AttributePath) (Valu // while generally in Go it's undesirable to treat empty and nil slices // separately, in this case we're surfacing a non-Go-in-origin // distinction, so we'll allow it. + types := []Type{} vals := []Value{} - var idx int64 + var idx int for dec.More() { - if idx >= int64(len(elementTypes)) { + if idx >= len(elementTypes) { return Value{}, p.NewErrorf("too many tuple elements (only have types for %d)", len(elementTypes)) } @@ -402,6 +415,7 @@ func jsonUnmarshalTuple(buf []byte, elementTypes []Type, p *AttributePath) (Valu if err != nil { return Value{}, err } + types = append(types, val.Type()) vals = append(vals, val) } @@ -418,7 +432,7 @@ func jsonUnmarshalTuple(buf []byte, elementTypes []Type, p *AttributePath) (Valu } return NewValue(Tuple{ - ElementTypes: elementTypes, + ElementTypes: types, }, vals), nil } @@ -433,6 +447,7 @@ func jsonUnmarshalObject(buf []byte, attrTypes map[string]Type, p *AttributePath return Value{}, p.NewErrorf("invalid JSON, expected %q, got %q", json.Delim('{'), tok) } + types := map[string]Type{} vals := map[string]Value{} for dec.More() { innerPath := p.WithElementKeyValue(NewValue(String, UnknownValue)) @@ -459,6 +474,7 @@ func jsonUnmarshalObject(buf []byte, attrTypes map[string]Type, p *AttributePath if err != nil { return Value{}, err } + types[key] = val.Type() vals[key] = val } @@ -478,6 +494,6 @@ func jsonUnmarshalObject(buf []byte, attrTypes map[string]Type, p *AttributePath } return NewValue(Object{ - AttributeTypes: attrTypes, + AttributeTypes: types, }, vals), nil } diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/value_msgpack.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/value_msgpack.go index d2fa8a362c6..3f3db12ef89 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/value_msgpack.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/value_msgpack.go @@ -122,7 +122,7 @@ func msgpackUnmarshal(dec *msgpack.Decoder, typ Type, path *AttributePath) (Valu case typ.Is(Set{}): return msgpackUnmarshalSet(dec, typ.(Set).ElementType, path) case typ.Is(Map{}): - return msgpackUnmarshalMap(dec, typ.(Map).AttributeType, path) + return msgpackUnmarshalMap(dec, typ.(Map).ElementType, path) case typ.Is(Tuple{}): return msgpackUnmarshalTuple(dec, typ.(Tuple).ElementTypes, path) case typ.Is(Object{}): @@ -150,7 +150,7 @@ func msgpackUnmarshalList(dec *msgpack.Decoder, typ Type, path *AttributePath) ( vals := make([]Value, 0, length) for i := 0; i < length; i++ { - innerPath := path.WithElementKeyInt(int64(i)) + innerPath := path.WithElementKeyInt(i) val, err := msgpackUnmarshal(dec, typ, innerPath) if err != nil { return Value{}, err @@ -190,7 +190,7 @@ func msgpackUnmarshalSet(dec *msgpack.Decoder, typ Type, path *AttributePath) (V vals := make([]Value, 0, length) for i := 0; i < length; i++ { - innerPath := path.WithElementKeyInt(int64(i)) + innerPath := path.WithElementKeyInt(i) val, err := msgpackUnmarshal(dec, typ, innerPath) if err != nil { return Value{}, err @@ -217,11 +217,11 @@ func msgpackUnmarshalMap(dec *msgpack.Decoder, typ Type, path *AttributePath) (V switch { case length < 0: return NewValue(Map{ - AttributeType: typ, + ElementType: typ, }, nil), nil case length == 0: return NewValue(Map{ - AttributeType: typ, + ElementType: typ, }, map[string]Value{}), nil } @@ -238,8 +238,25 @@ func msgpackUnmarshalMap(dec *msgpack.Decoder, typ Type, path *AttributePath) (V } vals[key] = val } + + elTyp := typ + + if typ.Is(DynamicPseudoType) { + var elements []Value + + for _, val := range vals { + elements = append(elements, val) + } + + elTyp, err = TypeFromElements(elements) + + if err != nil { + return Value{}, path.NewErrorf("invalid elements for map: %w", err) + } + } + return NewValue(Map{ - AttributeType: typ, + ElementType: elTyp, }, vals), nil } @@ -254,28 +271,25 @@ func msgpackUnmarshalTuple(dec *msgpack.Decoder, types []Type, path *AttributePa return NewValue(Tuple{ ElementTypes: types, }, nil), nil - case length == 0: - return NewValue(Tuple{ - // no elements means no types - ElementTypes: nil, - }, []Value{}), nil case length != len(types): return Value{}, path.NewErrorf("error decoding tuple; expected %d items, got %d", len(types), length) } + elTypes := make([]Type, 0, length) vals := make([]Value, 0, length) for i := 0; i < length; i++ { - innerPath := path.WithElementKeyInt(int64(i)) + innerPath := path.WithElementKeyInt(i) typ := types[i] val, err := msgpackUnmarshal(dec, typ, innerPath) if err != nil { return Value{}, err } + elTypes = append(elTypes, val.Type()) vals = append(vals, val) } return NewValue(Tuple{ - ElementTypes: types, + ElementTypes: elTypes, }, vals), nil } @@ -290,15 +304,11 @@ func msgpackUnmarshalObject(dec *msgpack.Decoder, types map[string]Type, path *A return NewValue(Object{ AttributeTypes: types, }, nil), nil - case length == 0: - return NewValue(Object{ - // no attributes means no types - AttributeTypes: map[string]Type{}, - }, map[string]Value{}), nil case length != len(types): return Value{}, path.NewErrorf("error decoding object; expected %d attributes, got %d", len(types), length) } + attrTypes := make(map[string]Type, length) vals := make(map[string]Value, length) for i := 0; i < length; i++ { key, err := dec.DecodeString() @@ -314,11 +324,12 @@ func msgpackUnmarshalObject(dec *msgpack.Decoder, types map[string]Type, path *A if err != nil { return Value{}, err } + attrTypes[key] = val.Type() vals[key] = val } return NewValue(Object{ - AttributeTypes: types, + AttributeTypes: attrTypes, }, vals), nil } @@ -330,7 +341,7 @@ func msgpackUnmarshalDynamic(dec *msgpack.Decoder, path *AttributePath) (Value, switch { case length == -1: - return NewValue(DynamicPseudoType, nil), nil + return newValue(DynamicPseudoType, nil) case length != 2: return Value{}, path.NewErrorf("expected %d elements in DynamicPseudoType array, got %d", 2, length) } @@ -478,7 +489,7 @@ func marshalMsgPackList(val Value, typ Type, p *AttributePath, enc *msgpack.Enco return p.NewErrorf("error encoding list length: %w", err) } for pos, i := range l { - err := marshalMsgPack(i, typ.(List).ElementType, p.WithElementKeyInt(int64(pos)), enc) + err := marshalMsgPack(i, typ.(List).ElementType, p.WithElementKeyInt(pos), enc) if err != nil { return err } @@ -518,7 +529,7 @@ func marshalMsgPackMap(val Value, typ Type, p *AttributePath, enc *msgpack.Encod if err != nil { return p.NewErrorf("error encoding map key: %w", err) } - err = marshalMsgPack(v, typ.(Map).AttributeType, p, enc) + err = marshalMsgPack(v, typ.(Map).ElementType, p, enc) if err != nil { return err } @@ -538,7 +549,7 @@ func marshalMsgPackTuple(val Value, typ Type, p *AttributePath, enc *msgpack.Enc } for pos, v := range t { ty := types[pos] - err := marshalMsgPack(v, ty, p.WithElementKeyInt(int64(pos)), enc) + err := marshalMsgPack(v, ty, p.WithElementKeyInt(pos), enc) if err != nil { return err } diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/walk.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/walk.go index d72b430ff5a..6b26f8ff3ed 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/walk.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/walk.go @@ -67,7 +67,7 @@ func walk(path *AttributePath, val Value, cb func(*AttributePath, Value) (bool, if ty.Is(Set{}) { path = path.WithElementKeyValue(el) } else { - path = path.WithElementKeyInt(int64(pos)) + path = path.WithElementKeyInt(pos) } err = walk(path, el, cb) if err != nil { @@ -116,6 +116,10 @@ func transform(path *AttributePath, val Value, cb func(*AttributePath, Value) (V var newVal Value ty := val.Type() + if ty == nil { + return val, path.NewError(errors.New("invalid transform: value missing type")) + } + switch { case val.IsNull() || !val.IsKnown(): newVal = val @@ -133,7 +137,7 @@ func transform(path *AttributePath, val Value, cb func(*AttributePath, Value) (V if ty.Is(Set{}) { path = path.WithElementKeyValue(el) } else { - path = path.WithElementKeyInt(int64(pos)) + path = path.WithElementKeyInt(pos) } newEl, err := transform(path, el, cb) if err != nil { @@ -142,7 +146,10 @@ func transform(path *AttributePath, val Value, cb func(*AttributePath, Value) (V elems = append(elems, newEl) path = path.WithoutLastStep() } - newVal = NewValue(ty, elems) + newVal, err = newValue(ty, elems) + if err != nil { + return val, path.NewError(err) + } } case ty.Is(Map{}), ty.Is(Object{}): v := map[string]Value{} @@ -167,7 +174,10 @@ func transform(path *AttributePath, val Value, cb func(*AttributePath, Value) (V elems[k] = newEl path = path.WithoutLastStep() } - newVal = NewValue(ty, elems) + newVal, err = newValue(ty, elems) + if err != nil { + return val, path.NewError(err) + } } default: newVal = val @@ -176,7 +186,11 @@ func transform(path *AttributePath, val Value, cb func(*AttributePath, Value) (V if err != nil { return res, path.NewError(err) } - if !newVal.Type().Is(ty) { + newTy := newVal.Type() + if newTy == nil { + return val, path.NewError(errors.New("invalid transform: new value missing type")) + } + if !newTy.UsableAs(ty) { return val, path.NewError(errors.New("invalid transform: value changed type")) } return res, err diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/grpc_provider.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/grpc_provider.go index 49a541b4d58..0695fc648d6 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/grpc_provider.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/grpc_provider.go @@ -552,6 +552,7 @@ func (s *GRPCProviderServer) ReadResource(ctx context.Context, req *tfprotov5.Re resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) return resp, nil } + instanceState.RawState = stateVal private := make(map[string]interface{}) if len(req.Private) > 0 { @@ -656,11 +657,20 @@ func (s *GRPCProviderServer) PlanResourceChange(ctx context.Context, req *tfprot return resp, nil } + configVal, err := msgpack.Unmarshal(req.Config.MsgPack, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + priorState, err := res.ShimInstanceStateFromValue(priorStateVal) if err != nil { resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) return resp, nil } + priorState.RawState = priorStateVal + priorState.RawPlan = proposedNewStateVal + priorState.RawConfig = configVal priorPrivate := make(map[string]interface{}) if len(req.PriorPrivate) > 0 { if err := json.Unmarshal(req.PriorPrivate, &priorPrivate); err != nil { @@ -869,6 +879,12 @@ func (s *GRPCProviderServer) ApplyResourceChange(ctx context.Context, req *tfpro return resp, nil } + configVal, err := msgpack.Unmarshal(req.Config.MsgPack, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + priorState, err := res.ShimInstanceStateFromValue(priorStateVal) if err != nil { resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) @@ -893,9 +909,12 @@ func (s *GRPCProviderServer) ApplyResourceChange(ctx context.Context, req *tfpro Attributes: make(map[string]*terraform.ResourceAttrDiff), Meta: make(map[string]interface{}), Destroy: true, + RawPlan: plannedStateVal, + RawState: priorStateVal, + RawConfig: configVal, } } else { - diff, err = DiffFromValues(ctx, priorStateVal, plannedStateVal, stripResourceModifiers(res)) + diff, err = DiffFromValues(ctx, priorStateVal, plannedStateVal, configVal, stripResourceModifiers(res)) if err != nil { resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) return resp, nil @@ -906,6 +925,9 @@ func (s *GRPCProviderServer) ApplyResourceChange(ctx context.Context, req *tfpro diff = &terraform.InstanceDiff{ Attributes: make(map[string]*terraform.ResourceAttrDiff), Meta: make(map[string]interface{}), + RawPlan: plannedStateVal, + RawState: priorStateVal, + RawConfig: configVal, } } @@ -1101,6 +1123,8 @@ func (s *GRPCProviderServer) ReadDataSource(ctx context.Context, req *tfprotov5. return resp, nil } + diff.RawConfig = configVal + // now we can get the new complete data source newInstanceState, diags := res.ReadDataApply(ctx, diff, s.provider.Meta()) resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, diags) diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/provider.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/provider.go index 25148e17c10..e8a8a8809b1 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/provider.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/provider.go @@ -282,15 +282,21 @@ func (p *Provider) Configure(ctx context.Context, c *terraform.ResourceConfig) d } p.meta = meta } + + var diags diag.Diagnostics + if p.ConfigureContextFunc != nil { - meta, diags := p.ConfigureContextFunc(ctx, data) + meta, configureDiags := p.ConfigureContextFunc(ctx, data) + diags = append(diags, configureDiags...) + if diags.HasError() { return diags } + p.meta = meta } - return nil + return diags } // Resources returns all the available resource types that this provider diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/resource_data.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/resource_data.go index d12e7546046..ce1d35f9ffe 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/resource_data.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/resource_data.go @@ -580,3 +580,51 @@ func (d *ResourceData) GetProviderMeta(dst interface{}) error { } return gocty.FromCtyValue(d.providerMeta, &dst) } + +// GetRawConfig returns the cty.Value that Terraform sent the SDK for the +// config. If no value was sent, or if a null value was sent, the value will be +// a null value of the resource's type. +// +// GetRawConfig is considered experimental and advanced functionality, and +// familiarity with the Terraform protocol is suggested when using it. +func (d *ResourceData) GetRawConfig() cty.Value { + if d.diff != nil && !d.diff.RawConfig.IsNull() { + return d.diff.RawConfig + } + if d.state != nil && !d.state.RawConfig.IsNull() { + return d.state.RawConfig + } + return cty.NullVal(schemaMap(d.schema).CoreConfigSchema().ImpliedType()) +} + +// GetRawState returns the cty.Value that Terraform sent the SDK for the state. +// If no value was sent, or if a null value was sent, the value will be a null +// value of the resource's type. +// +// GetRawState is considered experimental and advanced functionality, and +// familiarity with the Terraform protocol is suggested when using it. +func (d *ResourceData) GetRawState() cty.Value { + if d.diff != nil && !d.diff.RawState.IsNull() { + return d.diff.RawState + } + if d.state != nil && !d.state.RawState.IsNull() { + return d.state.RawState + } + return cty.NullVal(schemaMap(d.schema).CoreConfigSchema().ImpliedType()) +} + +// GetRawPlan returns the cty.Value that Terraform sent the SDK for the plan. +// If no value was sent, or if a null value was sent, the value will be a null +// value of the resource's type. +// +// GetRawPlan is considered experimental and advanced functionality, and +// familiarity with the Terraform protocol is suggested when using it. +func (d *ResourceData) GetRawPlan() cty.Value { + if d.diff != nil && !d.diff.RawPlan.IsNull() { + return d.diff.RawPlan + } + if d.state != nil && !d.state.RawPlan.IsNull() { + return d.state.RawPlan + } + return cty.NullVal(schemaMap(d.schema).CoreConfigSchema().ImpliedType()) +} diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/resource_diff.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/resource_diff.go index 984929df7b5..73e1e3a1d7c 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/resource_diff.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/resource_diff.go @@ -7,6 +7,7 @@ import ( "strings" "sync" + "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) @@ -450,6 +451,54 @@ func (d *ResourceDiff) Id() string { return result } +// GetRawConfig returns the cty.Value that Terraform sent the SDK for the +// config. If no value was sent, or if a null value was sent, the value will be +// a null value of the resource's type. +// +// GetRawConfig is considered experimental and advanced functionality, and +// familiarity with the Terraform protocol is suggested when using it. +func (d *ResourceDiff) GetRawConfig() cty.Value { + if d.diff != nil { + return d.diff.RawConfig + } + if d.state != nil { + return d.state.RawConfig + } + return cty.NullVal(schemaMap(d.schema).CoreConfigSchema().ImpliedType()) +} + +// GetRawState returns the cty.Value that Terraform sent the SDK for the state. +// If no value was sent, or if a null value was sent, the value will be a null +// value of the resource's type. +// +// GetRawState is considered experimental and advanced functionality, and +// familiarity with the Terraform protocol is suggested when using it. +func (d *ResourceDiff) GetRawState() cty.Value { + if d.diff != nil { + return d.diff.RawState + } + if d.state != nil { + return d.state.RawState + } + return cty.NullVal(schemaMap(d.schema).CoreConfigSchema().ImpliedType()) +} + +// GetRawPlan returns the cty.Value that Terraform sent the SDK for the plan. +// If no value was sent, or if a null value was sent, the value will be a null +// value of the resource's type. +// +// GetRawPlan is considered experimental and advanced functionality, and +// familiarity with the Terraform protocol is suggested when using it. +func (d *ResourceDiff) GetRawPlan() cty.Value { + if d.diff != nil { + return d.diff.RawPlan + } + if d.state != nil { + return d.state.RawPlan + } + return cty.NullVal(schemaMap(d.schema).CoreConfigSchema().ImpliedType()) +} + // getChange gets values from two different levels, designed for use in // diffChange, HasChange, and GetChange. // diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/schema.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/schema.go index 10a9743c717..b032c2839b9 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/schema.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/schema.go @@ -509,6 +509,9 @@ func (m schemaMap) Diff( // Make sure to mark if the resource is tainted if s != nil { result.DestroyTainted = s.Tainted + result.RawConfig = s.RawConfig + result.RawState = s.RawState + result.RawPlan = s.RawPlan } d := &ResourceData{ diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/shims.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/shims.go index e1575a0f891..9c7f0906c64 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/shims.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema/shims.go @@ -16,20 +16,24 @@ import ( // derives a terraform.InstanceDiff to give to the legacy providers. This is // used to take the states provided by the new ApplyResourceChange method and // convert them to a state+diff required for the legacy Apply method. -func DiffFromValues(ctx context.Context, prior, planned cty.Value, res *Resource) (*terraform.InstanceDiff, error) { - return diffFromValues(ctx, prior, planned, res, nil) +func DiffFromValues(ctx context.Context, prior, planned, config cty.Value, res *Resource) (*terraform.InstanceDiff, error) { + return diffFromValues(ctx, prior, planned, config, res, nil) } // diffFromValues takes an additional CustomizeDiffFunc, so we can generate our // test fixtures from the legacy tests. In the new provider protocol the diff // only needs to be created for the apply operation, and any customizations // have already been done. -func diffFromValues(ctx context.Context, prior, planned cty.Value, res *Resource, cust CustomizeDiffFunc) (*terraform.InstanceDiff, error) { +func diffFromValues(ctx context.Context, prior, planned, config cty.Value, res *Resource, cust CustomizeDiffFunc) (*terraform.InstanceDiff, error) { instanceState, err := res.ShimInstanceStateFromValue(prior) if err != nil { return nil, err } + instanceState.RawConfig = config + instanceState.RawPlan = planned + instanceState.RawState = prior + configSchema := res.CoreConfigSchema() cfg := terraform.NewResourceConfigShimmed(planned, configSchema) diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugin/convert/diagnostics.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugin/convert/diagnostics.go index babd0ecfb8a..252456747de 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugin/convert/diagnostics.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugin/convert/diagnostics.go @@ -123,7 +123,7 @@ func PathToAttributePath(p cty.Path) *tftypes.AttributePath { ap = ap.WithElementKeyString(key.AsString()) case cty.Number: v, _ := key.AsBigFloat().Int64() - ap = ap.WithElementKeyInt(v) + ap = ap.WithElementKeyInt(int(v)) default: // We'll bail early if we encounter anything else, and just // return the valid prefix. diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugin/convert/schema.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugin/convert/schema.go index 310dc63878a..c5dbf5b8fde 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugin/convert/schema.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugin/convert/schema.go @@ -56,7 +56,7 @@ func tftypeFromCtyType(in cty.Type) (tftypes.Type, error) { return nil, err } return tftypes.Map{ - AttributeType: elemType, + ElementType: elemType, }, nil case in.IsObjectType(): attrTypes := make(map[string]tftypes.Type) @@ -97,7 +97,7 @@ func ctyTypeFromTFType(in tftypes.Type) (cty.Type, error) { } return cty.Set(elemType), nil case in.Is(tftypes.Map{}): - elemType, err := ctyTypeFromTFType(in.(tftypes.Map).AttributeType) + elemType, err := ctyTypeFromTFType(in.(tftypes.Map).ElementType) if err != nil { return cty.Type{}, err } diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/meta/meta.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/meta/meta.go index ab6c9e2aba8..b8eba713e95 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/meta/meta.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/meta/meta.go @@ -11,7 +11,7 @@ import ( ) // The main version number that is being run at the moment. -var SDKVersion = "2.7.0" +var SDKVersion = "2.8.0" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/plugin/serve.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/plugin/serve.go index 127889986ea..f7278a87efd 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/plugin/serve.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/plugin/serve.go @@ -8,9 +8,9 @@ import ( "google.golang.org/grpc" "github.com/hashicorp/terraform-plugin-go/tfprotov5" - tf5server "github.com/hashicorp/terraform-plugin-go/tfprotov5/server" + tf5server "github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server" "github.com/hashicorp/terraform-plugin-go/tfprotov6" - tf6server "github.com/hashicorp/terraform-plugin-go/tfprotov6/server" + tf6server "github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/terraform/diff.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/terraform/diff.go index c7d82cf3d0f..0511a1fc5e8 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/terraform/diff.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/terraform/diff.go @@ -39,6 +39,10 @@ type InstanceDiff struct { DestroyDeposed bool DestroyTainted bool + RawConfig cty.Value + RawState cty.Value + RawPlan cty.Value + // Meta is a simple K/V map that is stored in a diff and persisted to // plans but otherwise is completely ignored by Terraform core. It is // meant to be used for additional data a resource may want to pass through. diff --git a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/terraform/state.go b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/terraform/state.go index 87f7610a93d..7f8655ee7d8 100644 --- a/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/terraform/state.go +++ b/awsproviderlint/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/terraform/state.go @@ -1343,6 +1343,10 @@ type InstanceState struct { ProviderMeta cty.Value + RawConfig cty.Value + RawState cty.Value + RawPlan cty.Value + // Tainted is used to mark a resource for recreation. Tainted bool `json:"tainted"` diff --git a/awsproviderlint/vendor/modules.txt b/awsproviderlint/vendor/modules.txt index 56c2e6c8c86..2673bd78dcc 100644 --- a/awsproviderlint/vendor/modules.txt +++ b/awsproviderlint/vendor/modules.txt @@ -280,19 +280,19 @@ github.com/hashicorp/terraform-exec/tfexec github.com/hashicorp/terraform-exec/tfinstall # github.com/hashicorp/terraform-json v0.12.0 github.com/hashicorp/terraform-json -# github.com/hashicorp/terraform-plugin-go v0.3.0 +# github.com/hashicorp/terraform-plugin-go v0.4.0 github.com/hashicorp/terraform-plugin-go/tfprotov5 github.com/hashicorp/terraform-plugin-go/tfprotov5/internal/fromproto github.com/hashicorp/terraform-plugin-go/tfprotov5/internal/tfplugin5 github.com/hashicorp/terraform-plugin-go/tfprotov5/internal/toproto -github.com/hashicorp/terraform-plugin-go/tfprotov5/server +github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server github.com/hashicorp/terraform-plugin-go/tfprotov6 github.com/hashicorp/terraform-plugin-go/tfprotov6/internal/fromproto github.com/hashicorp/terraform-plugin-go/tfprotov6/internal/tfplugin6 github.com/hashicorp/terraform-plugin-go/tfprotov6/internal/toproto -github.com/hashicorp/terraform-plugin-go/tfprotov6/server +github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server github.com/hashicorp/terraform-plugin-go/tftypes -# github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0 +# github.com/hashicorp/terraform-plugin-sdk/v2 v2.8.0 ## explicit github.com/hashicorp/terraform-plugin-sdk/v2/diag github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging