From 842eae96e05224248b0c09c808a4c9ee56f533eb Mon Sep 17 00:00:00 2001 From: alexknez Date: Mon, 8 Apr 2024 21:40:18 +0100 Subject: [PATCH 01/18] new resource codebuild fleet --- .changelog/36790.txt | 11 + internal/service/codebuild/exports_test.go | 1 + internal/service/codebuild/fleet.go | 577 ++++++++++++++++++ .../service/codebuild/fleet_data_source.go | 165 +++++ .../codebuild/fleet_data_source_test.go | 71 +++ internal/service/codebuild/fleet_test.go | 375 ++++++++++++ internal/service/codebuild/project.go | 39 ++ internal/service/codebuild/project_test.go | 69 +++ .../service/codebuild/service_package_gen.go | 16 +- website/docs/d/codebuild_fleet.html.markdown | 74 +++ website/docs/r/codebuild_fleet.html.markdown | 95 +++ .../docs/r/codebuild_project.html.markdown | 5 + 12 files changed, 1497 insertions(+), 1 deletion(-) create mode 100644 .changelog/36790.txt create mode 100644 internal/service/codebuild/fleet.go create mode 100644 internal/service/codebuild/fleet_data_source.go create mode 100644 internal/service/codebuild/fleet_data_source_test.go create mode 100644 internal/service/codebuild/fleet_test.go create mode 100644 website/docs/d/codebuild_fleet.html.markdown create mode 100644 website/docs/r/codebuild_fleet.html.markdown diff --git a/.changelog/36790.txt b/.changelog/36790.txt new file mode 100644 index 00000000000..a4b40563c2d --- /dev/null +++ b/.changelog/36790.txt @@ -0,0 +1,11 @@ +```release-note:new-resource +aws_codebuild_fleet +``` + +```release-note:new-data-source +aws_codebuild_fleet +``` + +```release-note:enhancement +resource/aws_codebuild_project: Add `fleet` attribute in `environment` configuration block +``` diff --git a/internal/service/codebuild/exports_test.go b/internal/service/codebuild/exports_test.go index 2b585b4c3c9..90231179d60 100644 --- a/internal/service/codebuild/exports_test.go +++ b/internal/service/codebuild/exports_test.go @@ -11,6 +11,7 @@ var ( ResourceSourceCredential = resourceSourceCredential ResourceWebhook = resourceWebhook + FindFleetByARNOrNames = findFleetByARNOrNames FindProjectByNameOrARN = findProjectByNameOrARN FindReportGroupByARN = findReportGroupByARN FindResourcePolicyByARN = findResourcePolicyByARN diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go new file mode 100644 index 00000000000..6abcd898f74 --- /dev/null +++ b/internal/service/codebuild/fleet.go @@ -0,0 +1,577 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package codebuild + +import ( + "context" + "errors" + "log" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/codebuild" + "github.com/aws/aws-sdk-go-v2/service/codebuild/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @SDKResource("aws_codebuild_fleet", name="Fleet") +// @Tags(identifierAttribute="arn") +func ResourceFleet() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceFleetCreate, + ReadWithoutTimeout: resourceFleetRead, + UpdateWithoutTimeout: resourceFleetUpdate, + DeleteWithoutTimeout: resourceFleetDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "base_capacity": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(1), + }, + "compute_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(computeTypeValues(types.ComputeType("").Values()), false), + }, + "created": { + Type: schema.TypeString, + Computed: true, + }, + "environment_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(environmentTypeValues(types.EnvironmentType("").Values()), false), + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "last_modified": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(2, 128), + }, + "overflow_behavior": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice(fleetOverflowBehaviorValues(types.FleetOverflowBehavior("").Values()), false), + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + if new == "" { + return true + } + return old == new + }, + }, + "scaling_configuration": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "desired_capacity": { + Type: schema.TypeInt, + Computed: true, + }, + "max_capacity": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(1), + }, + "scaling_type": { + Type: schema.TypeString, + Optional: true, + }, + "target_tracking_scaling_configs": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "metric_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(fleetScalingMetricTypeValues(types.FleetScalingMetricType("").Values()), false), + }, + "target_value": { + Type: schema.TypeFloat, + Optional: true, + ValidateFunc: validation.FloatAtLeast(0), + }, + }, + }, + }, + }, + }, + }, + "status": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "context": { + Type: schema.TypeString, + Computed: true, + }, + "message": { + Type: schema.TypeString, + Computed: true, + }, + "status_code": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + names.AttrTags: tftags.TagsSchema(), + names.AttrTagsAll: tftags.TagsSchemaComputed(), + }, + CustomizeDiff: verify.SetTagsDiff, + } +} + +const ( + ResNameFleet = "Fleet" +) + +func resourceFleetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + + conn := meta.(*conns.AWSClient).CodeBuildClient(ctx) + + input := &codebuild.CreateFleetInput{ + BaseCapacity: aws.Int32(int32(d.Get("base_capacity").(int))), + ComputeType: types.ComputeType(d.Get("compute_type").(string)), + EnvironmentType: types.EnvironmentType(d.Get("environment_type").(string)), + Name: aws.String(d.Get("name").(string)), + Tags: getTagsIn(ctx), + } + + if v, ok := d.GetOk("overflow_behavior"); ok { + input.OverflowBehavior = types.FleetOverflowBehavior(v.(string)) + } + + if v, ok := d.GetOk("scaling_configuration"); ok { + if len(v.([]interface{})) == 0 || v.([]interface{})[0] == nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, ResNameFleet, d.Get("name").(string), errors.New("scaling_configuration cannot be empty")) + } else { + input.ScalingConfiguration = expandScalingConfiguration(v.([]interface{})[0].(map[string]interface{})) + } + } + + out, err := conn.CreateFleet(ctx, input) + if err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, ResNameFleet, d.Get("name").(string), err) + } + + if out == nil || out.Fleet == nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, ResNameFleet, d.Get("name").(string), errors.New("empty output")) + } + + d.SetId(aws.ToString(out.Fleet.Arn)) + + if err := waitFleetActive(ctx, conn, d.Id()); err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionWaitingForCreation, ResNameFleet, d.Id(), err) + } + + return append(diags, resourceFleetRead(ctx, d, meta)...) +} + +func resourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + + conn := meta.(*conns.AWSClient).CodeBuildClient(ctx) + + out, err := findFleetByARNOrNames(ctx, conn, d.Id()) + + if out == nil || len(out.Fleets) == 0 { + log.Printf("[WARN] CodeBuild Fleet (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + + if err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionReading, ResNameFleet, d.Id(), err) + } + + d.Set("arn", out.Fleets[0].Arn) + d.Set("base_capacity", out.Fleets[0].BaseCapacity) + d.Set("compute_type", out.Fleets[0].ComputeType) + d.Set("created", out.Fleets[0].Created.String()) + d.Set("environment_type", out.Fleets[0].EnvironmentType) + d.Set("id", out.Fleets[0].Id) + d.Set("last_modified", out.Fleets[0].LastModified.String()) + d.Set("name", out.Fleets[0].Name) + d.Set("overflow_behavior", out.Fleets[0].OverflowBehavior) + if len(out.Fleets) > 0 && out.Fleets[0].ScalingConfiguration != nil { + empty_scaling_configuration := out.Fleets[0].ScalingConfiguration.DesiredCapacity == nil && + out.Fleets[0].ScalingConfiguration.MaxCapacity == nil && + out.Fleets[0].ScalingConfiguration.ScalingType == "" && + out.Fleets[0].ScalingConfiguration.TargetTrackingScalingConfigs == nil + + if !empty_scaling_configuration { + if err := d.Set("scaling_configuration", []interface{}{flattenScalingConfiguration(out.Fleets[0].ScalingConfiguration)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting overflow behavior: %s", err) + } + } else { + d.Set("scaling_configuration", nil) + } + } else { + d.Set("scaling_configuration", nil) + } + if out.Fleets[0].Status != nil { + if err := d.Set("status", []interface{}{flattenStatus(out.Fleets[0].Status)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting status: %s", err) + } + } else { + d.Set("status", nil) + } + setTagsOut(ctx, out.Fleets[0].Tags) + return diags +} + +func resourceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).CodeBuildClient(ctx) + + input := &codebuild.UpdateFleetInput{ + Arn: aws.String(d.Id()), + } + + if d.HasChange("base_capacity") { + input.BaseCapacity = aws.Int32(int32(d.Get("base_capacity").(int))) + } + + if d.HasChange("compute_type") { + input.ComputeType = types.ComputeType(d.Get("compute_type").(string)) + } + + if d.HasChange("environment_type") { + input.EnvironmentType = types.EnvironmentType(d.Get("environment_type").(string)) + } + + // Make sure that overflow_behavior is set (if defined) on update - api ommit it on updates + if v, ok := d.GetOk("overflow_behavior"); ok { + input.OverflowBehavior = types.FleetOverflowBehavior(v.(string)) + } + + if d.HasChange("scaling_configuration") { + if v, ok := d.GetOk("scaling_configuration"); ok { + if len(v.([]interface{})) == 0 || v.([]interface{})[0] == nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, ResNameFleet, d.Get("name").(string), errors.New("scaling_configuration cannot be empty")) + } else { + input.ScalingConfiguration = expandScalingConfiguration(v.([]interface{})[0].(map[string]interface{})) + } + } else { + input.ScalingConfiguration = &types.ScalingConfigurationInput{} + } + } + + input.Tags = getTagsIn(ctx) + + log.Printf("[DEBUG] Updating CodeBuild Fleet (%s): %#v", d.Id(), input) + _, err := conn.UpdateFleet(ctx, input) + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating CodeBuild Fleet (%s): %s", d.Id(), err) + } + + if err := waitFleetActive(ctx, conn, d.Id()); err != nil { + return sdkdiag.AppendErrorf(diags, "updating CodeBuild Fleet (%s): %s", d.Id(), err) + } + + return append(diags, resourceFleetRead(ctx, d, meta)...) +} + +func resourceFleetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).CodeBuildClient(ctx) + + log.Printf("[INFO] Deleting CodeBuild Fleet %s", d.Id()) + + _, err := conn.DeleteFleet(ctx, &codebuild.DeleteFleetInput{ + Arn: aws.String(d.Id()), + }) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return diags + } + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting CodeBuild Fleet (%s): %s", d.Id(), err) + } + + if err := waitFleetDeleted(ctx, conn, d.Id()); err != nil { + return sdkdiag.AppendErrorf(diags, "deleting CodeBuild Fleet (%s): %s", d.Id(), err) + } + + return diags +} + +func waitFleetActive(ctx context.Context, conn *codebuild.Client, id string) error { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(types.FleetStatusCodeCreating, types.FleetStatusCodeUpdating, types.FleetStatusCodeDeleting, types.FleetStatusCodeRotating), + Target: enum.Slice(types.FleetStatusCodeActive), + Refresh: statusFleet(ctx, conn, id, false), + Timeout: 20 * time.Minute, + MinTimeout: 15 * time.Second, + Delay: 15 * time.Second, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} + +func waitFleetDeleted(ctx context.Context, conn *codebuild.Client, id string) error { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(types.FleetStatusCodeDeleting, "PENDING_DELETION"), + Target: []string{}, + Refresh: statusFleet(ctx, conn, id, true), + Timeout: 90 * time.Minute, + MinTimeout: 15 * time.Second, + Delay: 15 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} + +func statusFleet(ctx context.Context, conn *codebuild.Client, id string, delete bool) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + out, err := findFleetByARNOrNames(ctx, conn, id) + if tfresource.NotFound(err) && delete { + return nil, "", nil + } + if err != nil { + return nil, "", err + } + + return out, aws.ToString((*string)(&out.Fleets[0].Status.StatusCode)), nil + } +} + +func findFleetByARNOrNames(ctx context.Context, conn *codebuild.Client, arn string) (*codebuild.BatchGetFleetsOutput, error) { + input := &codebuild.BatchGetFleetsInput{ + Names: []string{arn}, + } + output, err := conn.BatchGetFleets(ctx, input) + + if output == nil || len(output.Fleets) == 0 || len(output.FleetsNotFound) > 0 { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil +} + +func expandScalingConfiguration(tfMap map[string]interface{}) *types.ScalingConfigurationInput { + if len(tfMap) == 0 { + return nil + } + + apiObject := &types.ScalingConfigurationInput{} + if v, ok := tfMap["max_capacity"].(int); ok { + int32Value := int32(v) + apiObject.MaxCapacity = &int32Value + } + + if v, ok := tfMap["scaling_type"].(string); ok && v != "" { + apiObject.ScalingType = types.FleetScalingType(v) + } + + if v, ok := tfMap["target_tracking_scaling_configs"].([]interface{}); ok && len(v) > 0 { + apiObject.TargetTrackingScalingConfigs = expandTargetTrackingScalingConfigs(v) + } + + return apiObject +} + +func expandTargetTrackingScalingConfigs(tfList []interface{}) []types.TargetTrackingScalingConfiguration { + if len(tfList) == 0 { + return nil + } + + var apiObjects []types.TargetTrackingScalingConfiguration + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + apiObject := expandTargetTrackingScalingConfig(tfMap) + if apiObject == nil { + continue + } + + apiObjects = append(apiObjects, *apiObject) + } + return apiObjects +} + +func expandTargetTrackingScalingConfig(tfMap map[string]interface{}) *types.TargetTrackingScalingConfiguration { + if tfMap == nil { + return nil + } + + apiObject := &types.TargetTrackingScalingConfiguration{} + + if v, ok := tfMap["metric_type"].(string); ok { + apiObject.MetricType = types.FleetScalingMetricType(v) + } + + if v, ok := tfMap["target_value"].(float64); ok { + apiObject.TargetValue = &v + } + + return apiObject +} + +func flattenScalingConfiguration(apiObject *types.ScalingConfigurationOutput) map[string]interface{} { + tfMap := map[string]interface{}{} + + if apiObject != nil { + if v := apiObject.DesiredCapacity; v != nil { + tfMap["desired_capacity"] = aws.ToInt32(v) + } + + if v := apiObject.MaxCapacity; v != nil { + tfMap["max_capacity"] = aws.ToInt32(v) + } + + if v := apiObject.ScalingType; v != "" { + tfMap["scaling_type"] = v + } + + if v := apiObject.TargetTrackingScalingConfigs; v != nil { + tfMap["target_tracking_scaling_configs"] = flattenTargetTrackingScalingConfigs(v) + } + } + + return tfMap +} + +func flattenTargetTrackingScalingConfigs(apiObjects []types.TargetTrackingScalingConfiguration) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfMaps []interface{} + + for _, apiObject := range apiObjects { + tfMaps = append(tfMaps, flattenTargetTrackingScalingConfig(apiObject)) + } + + return tfMaps +} + +func flattenTargetTrackingScalingConfig(apiObject types.TargetTrackingScalingConfiguration) map[string]interface{} { + tfMap := map[string]interface{}{} + + if v := apiObject.MetricType; v != "" { + tfMap["metric_type"] = v + } + + if v := apiObject.TargetValue; v != nil { + tfMap["target_value"] = aws.ToFloat64(v) + } + + return tfMap +} + +func flattenStatus(apiObject *types.FleetStatus) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Context; v != "" { + tfMap["context"] = v + } + + if v := apiObject.Message; v != nil { + tfMap["message"] = aws.ToString(v) + } + + if v := apiObject.StatusCode; v != "" { + tfMap["status_code"] = v + } + + return tfMap +} + +func fleetOverflowBehaviorValues(in []types.FleetOverflowBehavior) []string { + var out []string + + for _, v := range in { + out = append(out, string(v)) + } + + return out +} + +func computeTypeValues(in []types.ComputeType) []string { + var out []string + + for _, v := range in { + out = append(out, string(v)) + } + + return out +} + +func environmentTypeValues(in []types.EnvironmentType) []string { + var out []string + + for _, v := range in { + out = append(out, string(v)) + } + + return out +} + +func fleetScalingMetricTypeValues(in []types.FleetScalingMetricType) []string { + var out []string + + for _, v := range in { + out = append(out, string(v)) + } + + return out +} diff --git a/internal/service/codebuild/fleet_data_source.go b/internal/service/codebuild/fleet_data_source.go new file mode 100644 index 00000000000..d68e1fe6028 --- /dev/null +++ b/internal/service/codebuild/fleet_data_source.go @@ -0,0 +1,165 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package codebuild + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @SDKDataSource("aws_codebuild_fleet", name="Fleet") +func DataSourceFleet() *schema.Resource { + return &schema.Resource{ + ReadWithoutTimeout: dataSourceFleetRead, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "base_capacity": { + Type: schema.TypeInt, + Computed: true, + }, + "compute_type": { + Type: schema.TypeString, + Computed: true, + }, + "created": { + Type: schema.TypeString, + Computed: true, + }, + "environment_type": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "last_modified": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(2, 128), + }, + "overflow_behavior": { + Type: schema.TypeString, + Computed: true, + }, + "scaling_configuration": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "desired_capacity": { + Type: schema.TypeInt, + Computed: true, + }, + "max_capacity": { + Type: schema.TypeInt, + Computed: true, + }, + "scaling_type": { + Type: schema.TypeString, + Computed: true, + }, + "target_tracking_scaling_configs": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "metric_type": { + Type: schema.TypeString, + Computed: true, + }, + "target_value": { + Type: schema.TypeFloat, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "status": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "context": { + Type: schema.TypeString, + Computed: true, + }, + "message": { + Type: schema.TypeString, + Computed: true, + }, + "status_code": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "tags": tftags.TagsSchemaComputed(), + }, + } +} + +const ( + DSNameFleet = "Fleet Data Source" +) + +func dataSourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + + conn := meta.(*conns.AWSClient).CodeBuildClient(ctx) + name := d.Get("name").(string) + + out, err := findFleetByARNOrNames(ctx, conn, name) + if err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionReading, DSNameFleet, name, err) + } + d.SetId(aws.StringValue(out.Fleets[0].Arn)) + + d.Set("arn", out.Fleets[0].Arn) + d.Set("base_capacity", out.Fleets[0].BaseCapacity) + d.Set("compute_type", out.Fleets[0].ComputeType) + d.Set("created", out.Fleets[0].Created.String()) + d.Set("environment_type", out.Fleets[0].EnvironmentType) + d.Set("last_modified", out.Fleets[0].LastModified.String()) + d.Set("overflow_behavior", out.Fleets[0].OverflowBehavior) + d.Set("name", out.Fleets[0].Name) + if out.Fleets[0].ScalingConfiguration != nil { + if err := d.Set("scaling_configuration", []interface{}{flattenScalingConfiguration(out.Fleets[0].ScalingConfiguration)}); err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) + } + } + if out.Fleets[0].Status != nil { + if err := d.Set("status", []interface{}{flattenStatus(out.Fleets[0].Status)}); err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) + } + } + + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + //lintignore:AWSR002 + if err := d.Set("tags", KeyValueTags(ctx, out.Fleets[0].Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) + } + + return diags +} diff --git a/internal/service/codebuild/fleet_data_source_test.go b/internal/service/codebuild/fleet_data_source_test.go new file mode 100644 index 00000000000..12db476f98b --- /dev/null +++ b/internal/service/codebuild/fleet_data_source_test.go @@ -0,0 +1,71 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package codebuild_test + +import ( + "fmt" + "testing" + + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccCodeBuildFleetDataSource_basic(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_codebuild_fleet.test" + datasourceName := "data.aws_codebuild_fleet.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccFleetDataSourceConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasourceName, "base_capacity", resourceName, "base_capacity"), + resource.TestCheckResourceAttrPair(datasourceName, "compute_type", resourceName, "compute_type"), + resource.TestCheckResourceAttrPair(datasourceName, "created", resourceName, "created"), + resource.TestCheckResourceAttrPair(datasourceName, "environment_type", resourceName, "environment_type"), + resource.TestCheckResourceAttrPair(datasourceName, "id", resourceName, "id"), + resource.TestCheckResourceAttrPair(datasourceName, "last_modified", resourceName, "last_modified"), + resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(datasourceName, "overflow_behavior", resourceName, "overflow_behavior"), + resource.TestCheckResourceAttrPair(datasourceName, "scaling_configuration.0.max_capacity", resourceName, "scaling_configuration.0.max_capacity"), + resource.TestCheckResourceAttrPair(datasourceName, "scaling_configuration.0.scaling_type", resourceName, "scaling_configuration.0.scaling_type"), + resource.TestCheckResourceAttrPair(datasourceName, "scaling_configuration.0.target_tracking_scaling_configs.0.metric_type", resourceName, "scaling_configuration.0.target_tracking_scaling_configs.0.metric_type"), + resource.TestCheckResourceAttrPair(datasourceName, "scaling_configuration.0.target_tracking_scaling_configs.0.target_value", resourceName, "scaling_configuration.0.target_tracking_scaling_configs.0.target_value"), + ), + }, + }, + }) +} + +func testAccFleetDataSourceConfig_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_codebuild_fleet" "test" { + base_capacity = 1 + compute_type = "BUILD_GENERAL1_SMALL" + environment_type = "ARM_CONTAINER" + name = %q + overflow_behavior = "QUEUE" + scaling_configuration { + max_capacity = 2 + scaling_type = "TARGET_TRACKING_SCALING" + target_tracking_scaling_configs { + metric_type = "FLEET_UTILIZATION_RATE" + target_value = 97.5 + } + } +} + +data "aws_codebuild_fleet" "test" { + name = aws_codebuild_fleet.test.name +} +`, rName) +} diff --git a/internal/service/codebuild/fleet_test.go b/internal/service/codebuild/fleet_test.go new file mode 100644 index 00000000000..b36a4c21c5c --- /dev/null +++ b/internal/service/codebuild/fleet_test.go @@ -0,0 +1,375 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package codebuild_test + +import ( + "context" + "fmt" + "testing" + + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfcodebuild "github.com/hashicorp/terraform-provider-aws/internal/service/codebuild" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccCodeBuildFleet_basic(t *testing.T) { + ctx := context.Background() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_codebuild_fleet.test" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFleetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFleetConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), + resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_SMALL"), + resource.TestCheckResourceAttr(resourceName, "environment_type", "LINUX_CONTAINER"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "ON_DEMAND"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccCodeBuildFleet_disappears(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resourceName := "aws_codebuild_fleet.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFleetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFleetConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfcodebuild.ResourceFleet(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccCodeBuildFleet_tags(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_codebuild_fleet.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFleetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFleetConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + Config: testAccFleetConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccFleetConfig_tags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func TestAccCodeBuildFleet_updateBasicParameters(t *testing.T) { + ctx := context.Background() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_codebuild_fleet.test" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFleetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFleetConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), + resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_SMALL"), + resource.TestCheckResourceAttr(resourceName, "environment_type", "LINUX_CONTAINER"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "ON_DEMAND"), + ), + }, + { + Config: testAccFleetConfig_updateBaseCapacity(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "base_capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_SMALL"), + resource.TestCheckResourceAttr(resourceName, "environment_type", "LINUX_CONTAINER"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "ON_DEMAND"), + ), + }, + { + Config: testAccFleetConfig_updateComputeType(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), + resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_LARGE"), + resource.TestCheckResourceAttr(resourceName, "environment_type", "LINUX_CONTAINER"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "ON_DEMAND"), + ), + }, + { + Config: testAccFleetConfig_updateEnvironmentType(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), + resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_LARGE"), + resource.TestCheckResourceAttr(resourceName, "environment_type", "ARM_CONTAINER"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "ON_DEMAND"), + ), + }, + }, + }) +} + +func TestAccCodeBuildFleet_updateScalingConfiguration(t *testing.T) { + ctx := context.Background() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_codebuild_fleet.test" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFleetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFleetConfig_scalingConfiguration(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), + resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_SMALL"), + resource.TestCheckResourceAttr(resourceName, "environment_type", "ARM_CONTAINER"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "QUEUE"), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.max_capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.scaling_type", "TARGET_TRACKING_SCALING"), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.target_tracking_scaling_configs.0.metric_type", "FLEET_UTILIZATION_RATE"), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.target_tracking_scaling_configs.0.target_value", "97.5"), + ), + }, + { + Config: testAccFleetConfig_noScalingConfiguration(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), + resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_SMALL"), + resource.TestCheckResourceAttr(resourceName, "environment_type", "ARM_CONTAINER"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "ON_DEMAND"), + ), + }, + }, + }) +} + +func testAccCheckFleetDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).CodeBuildClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_codebuild_fleet" { + continue + } + + _, err := tfcodebuild.FindFleetByARNOrNames(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("CodeBuild Fleet %s still exists", rs.Primary.ID) + } + + return nil + } +} + +func testAccCheckFleetExists(ctx context.Context, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).CodeBuildClient(ctx) + + fleet, err := tfcodebuild.FindFleetByARNOrNames(ctx, conn, rs.Primary.ID) + if err != nil { + return err + } + + if len(fleet.Fleets) == 0 { + return fmt.Errorf("Fleet not found: %s", rs.Primary.ID) + } + + expectedName := rs.Primary.Attributes["name"] + if *fleet.Fleets[0].Name != expectedName { + return fmt.Errorf("Fleet name mismatch, expected: %s, got: %s", expectedName, *fleet.Fleets[0].Name) + } + + return nil + } +} + +func testAccFleetConfig_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_codebuild_fleet" "test" { + base_capacity = 1 + compute_type = "BUILD_GENERAL1_SMALL" + environment_type = "LINUX_CONTAINER" + name = %q + overflow_behavior = "ON_DEMAND" +} +`, rName) +} + +func testAccFleetConfig_updateBaseCapacity(rName string) string { + return fmt.Sprintf(` +resource "aws_codebuild_fleet" "test" { + base_capacity = 2 + compute_type = "BUILD_GENERAL1_SMALL" + environment_type = "LINUX_CONTAINER" + name = %q + overflow_behavior = "ON_DEMAND" +} +`, rName) +} + +func testAccFleetConfig_updateComputeType(rName string) string { + return fmt.Sprintf(` +resource "aws_codebuild_fleet" "test" { + base_capacity = 1 + compute_type = "BUILD_GENERAL1_LARGE" + environment_type = "LINUX_CONTAINER" + name = %q + overflow_behavior = "ON_DEMAND" +} +`, rName) +} + +func testAccFleetConfig_updateEnvironmentType(rName string) string { + return fmt.Sprintf(` +resource "aws_codebuild_fleet" "test" { + base_capacity = 1 + compute_type = "BUILD_GENERAL1_LARGE" + environment_type = "ARM_CONTAINER" + name = %q + overflow_behavior = "ON_DEMAND" +} +`, rName) +} + +func testAccFleetConfig_scalingConfiguration(rName string) string { + return fmt.Sprintf(` +resource "aws_codebuild_fleet" "test" { + base_capacity = 1 + compute_type = "BUILD_GENERAL1_SMALL" + environment_type = "ARM_CONTAINER" + name = %q + overflow_behavior = "QUEUE" + scaling_configuration { + max_capacity = 2 + scaling_type = "TARGET_TRACKING_SCALING" + target_tracking_scaling_configs { + metric_type = "FLEET_UTILIZATION_RATE" + target_value = 97.5 + } + } +} +`, rName) +} + +func testAccFleetConfig_noScalingConfiguration(rName string) string { + return fmt.Sprintf(` +resource "aws_codebuild_fleet" "test" { + base_capacity = 1 + compute_type = "BUILD_GENERAL1_SMALL" + environment_type = "ARM_CONTAINER" + name = %q + overflow_behavior = "ON_DEMAND" +} +`, rName) +} + +func testAccFleetConfig_tags1(rName string, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_codebuild_fleet" "test" { + base_capacity = 1 + compute_type = "BUILD_GENERAL1_SMALL" + environment_type = "LINUX_CONTAINER" + name = %q + overflow_behavior = "ON_DEMAND" + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccFleetConfig_tags2(rName string, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_codebuild_fleet" "test" { + base_capacity = 1 + compute_type = "BUILD_GENERAL1_SMALL" + environment_type = "LINUX_CONTAINER" + name = %q + overflow_behavior = "ON_DEMAND" + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/internal/service/codebuild/project.go b/internal/service/codebuild/project.go index 893eb619d6f..64de3716d04 100644 --- a/internal/service/codebuild/project.go +++ b/internal/service/codebuild/project.go @@ -251,6 +251,20 @@ func resourceProject() *schema.Resource { Required: true, ValidateDiagFunc: enum.Validate[types.ComputeType](), }, + "fleet": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "fleet_arn": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: verify.ValidARN, + }, + }, + }, + }, "environment_variable": { Type: schema.TypeList, Optional: true, @@ -1338,6 +1352,18 @@ func expandProjectEnvironment(tfMap map[string]interface{}) *types.ProjectEnviro apiObject.ComputeType = types.ComputeType(v) } + if v, ok := tfMap["fleet"].([]interface{}); ok && len(v) > 0 && v[0] != nil { + tfMap := v[0].(map[string]interface{}) + + projectFleet := &types.ProjectFleet{} + + if v, ok := tfMap["fleet_arn"]; ok && v.(string) != "" { + projectFleet.FleetArn = aws.String(v.(string)) + } + + apiObject.Fleet = projectFleet + } + if v, ok := tfMap["image"].(string); ok && v != "" { apiObject.Image = aws.String(v) } @@ -1805,6 +1831,7 @@ func flattenProjectEnvironment(apiObject *types.ProjectEnvironment) []interface{ names.AttrType: apiObject.Type, } + tfMap["fleet"] = flattenFleet(apiObject.Fleet) tfMap["image"] = aws.ToString(apiObject.Image) tfMap[names.AttrCertificate] = aws.ToString(apiObject.Certificate) tfMap["privileged_mode"] = aws.ToBool(apiObject.PrivilegedMode) @@ -1817,6 +1844,18 @@ func flattenProjectEnvironment(apiObject *types.ProjectEnvironment) []interface{ return []interface{}{tfMap} } +func flattenFleet(apiObject *types.ProjectFleet) []interface{} { + if apiObject == nil { + return []interface{}{} + } + + tfMap := map[string]interface{}{ + "fleet_arn": aws.ToString(apiObject.FleetArn), + } + + return []interface{}{tfMap} +} + func flattenRegistryCredential(apiObject *types.RegistryCredential) []interface{} { if apiObject == nil { return []interface{}{} diff --git a/internal/service/codebuild/project_test.go b/internal/service/codebuild/project_test.go index 4013cd418b1..599abdac2a8 100644 --- a/internal/service/codebuild/project_test.go +++ b/internal/service/codebuild/project_test.go @@ -2860,6 +2860,40 @@ func TestAccCodeBuildProject_concurrentBuildLimit(t *testing.T) { }) } +func TestAccCodeBuildProject_fleet(t *testing.T) { + ctx := acctest.Context(t) + var project types.Project + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resourceName := "aws_codebuild_project.test" + roleResourceName := "aws_iam_role.test" + fleetResourceName := "aws_codebuild_fleet.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheck(ctx, t) + testAccPreCheckSourceCredentialsForServerType(ctx, t, types.ServerTypeGithub) + }, + ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProjectDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProjectConfig_fleet(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProjectExists(ctx, resourceName, &project), + acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "codebuild", fmt.Sprintf("project/%s", rName)), + resource.TestCheckResourceAttr(resourceName, "environment.0.compute_type", string(types.ComputeTypeBuildGeneral1Small)), + resource.TestCheckResourceAttr(resourceName, "environment.0.fleet.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "environment.0.fleet.0.fleet_arn", fleetResourceName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "service_role", roleResourceName, "arn"), + ), + }, + }, + }) +} + func testAccCheckProjectExists(ctx context.Context, n string, v *types.Project) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -5558,3 +5592,38 @@ resource "aws_codebuild_project" "test" { } `, concurrentBuildLimit, rName)) } + +func testAccProjectConfig_fleet(rName string) string { + return acctest.ConfigCompose(testAccProjectConfig_baseServiceRole(rName), fmt.Sprintf(` +resource "aws_codebuild_fleet" "test" { + base_capacity = 1 + compute_type = "BUILD_GENERAL1_SMALL" + environment_type = "LINUX_CONTAINER" + name = %[1]q + overflow_behavior = "ON_DEMAND" +} + +resource "aws_codebuild_project" "test" { + name = %[1]q + service_role = aws_iam_role.test.arn + + artifacts { + type = "NO_ARTIFACTS" + } + + environment { + compute_type = "BUILD_GENERAL1_SMALL" + image = "2" + type = "LINUX_CONTAINER" + fleet { + fleet_arn = aws_codebuild_fleet.test.arn + } + } + + source { + location = %[2]q + type = "GITHUB" + } +} +`, rName, testAccGitHubSourceLocationFromEnv())) +} diff --git a/internal/service/codebuild/service_package_gen.go b/internal/service/codebuild/service_package_gen.go index dfce895ba83..cde0622b48e 100644 --- a/internal/service/codebuild/service_package_gen.go +++ b/internal/service/codebuild/service_package_gen.go @@ -23,11 +23,25 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic } func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePackageSDKDataSource { - return []*types.ServicePackageSDKDataSource{} + return []*types.ServicePackageSDKDataSource{ + { + Factory: DataSourceFleet, + TypeName: "aws_codebuild_fleet", + Name: "Fleet", + }, + } } func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePackageSDKResource { return []*types.ServicePackageSDKResource{ + { + Factory: ResourceFleet, + TypeName: "aws_codebuild_fleet", + Name: "Fleet", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: "arn", + }, + }, { Factory: resourceProject, TypeName: "aws_codebuild_project", diff --git a/website/docs/d/codebuild_fleet.html.markdown b/website/docs/d/codebuild_fleet.html.markdown new file mode 100644 index 00000000000..aeafbdc44b8 --- /dev/null +++ b/website/docs/d/codebuild_fleet.html.markdown @@ -0,0 +1,74 @@ +--- +subcategory: "CodeBuild" +layout: "aws" +page_title: "AWS: aws_codebuild_fleet" +description: |- + Retrieve information about an CodeBuild Fleet +--- + +# Data Source: aws_codebuild_fleet + +Retrieve information about an CodeBuild Fleet. + +## Example Usage + +```terraform +data "aws_codebuild_fleet" "test" { + name = aws_codebuild_fleet.test.name +} + +resource "aws_codebuild_fleet" "test" { + base_capacity = 2 + compute_type = "BUILD_GENERAL1_SMALL" + environment_type = "LINUX_CONTAINER" + name = "full-example-codebuild-fleet" + overflow_behavior = "QUEUE" + scaling_configuration { + max_capacity = 5 + scaling_type = "TARGET_TRACKING_SCALING" + target_tracking_scaling_configs { + metric_type = "FLEET_UTILIZATION_RATE" + target_value = 97.5 + } + } +} +``` + +### Basic Usage + +```terraform +data "aws_codebuild_fleet" "example" { + name = "my-codebuild-fleet-name" +} +``` + +## Argument Reference + +The following arguments are required: + +* `name` - (Required) Fleet name. + +## Attribute Reference + +This data source exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Fleet. +* `base_capacity` - Number of machines allocated to the fleet. +* `compute_type` - Compute resources the compute fleet uses. +* `created` - Creation time of the fleet. +* `environment_type` - Environment type of the compute fleet. +* `id` - ARN of the Fleet. +* `last_modified` - Last modification time of the fleet. +* `overflow_behavior` - Overflow behavior for compute fleet. +* `scaling_configuration` - Nested attribute containing information about the scaling configuration. + * `desired_capacity` - The desired number of instances in the fleet when auto-scaling. + * `max_capacity` - The maximum number of instances in the fleet when auto-scaling. + * `scaling_type` - The scaling type for a compute fleet. + * `target_tracking_scaling_configs` - Nested attribute containing information about thresholds when new instance is auto-scaled into the compute fleet. + * `metric_type` - The metric type to determine auto-scaling. + * `target_value` - The value of metric_type when to start scaling. +* `status` - Nested attribute containing information about the current status of the fleet. + * `context` - Additional information about a compute fleet. + * `message` - Message associated with the status of a compute fleet. + * `status_code` - Status code of the compute fleet. +* `tags` - Mapping of Key-Value tags for the resource. diff --git a/website/docs/r/codebuild_fleet.html.markdown b/website/docs/r/codebuild_fleet.html.markdown new file mode 100644 index 00000000000..ad368c94fc2 --- /dev/null +++ b/website/docs/r/codebuild_fleet.html.markdown @@ -0,0 +1,95 @@ +--- +subcategory: "CodeBuild" +layout: "aws" +page_title: "AWS: aws_codebuild_fleet" +description: |- + Provides a CodeBuild Fleet Resource. +--- + +# Resource: aws_codebuild_fleet + +Provides a CodeBuild Fleet Resource. + +## Example Usage + +```terraform +resource "aws_codebuild_fleet" "test" { + base_capacity = 2 + compute_type = "BUILD_GENERAL1_SMALL" + environment_type = "LINUX_CONTAINER" + name = "full-example-codebuild-fleet" + overflow_behavior = "QUEUE" + scaling_configuration { + max_capacity = 5 + scaling_type = "TARGET_TRACKING_SCALING" + target_tracking_scaling_configs { + metric_type = "FLEET_UTILIZATION_RATE" + target_value = 97.5 + } + } +} +``` + +### Basic Usage + +```terraform +resource "aws_codebuild_fleet" "example" { + name = "example-codebuild-fleet" +} +``` + +## Argument Reference + +The following arguments are required: + +* `name` - (Required) Fleet name. + +The following arguments are optional: + +* `base_capacity` - (Optional) Number of machines allocated to the fleet. +* `compute_type` - (Optional) Compute resources the compute fleet uses. See [compute types](https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types) for more information and valid values. +* `environment_type` - (Optional) Environment type of the compute fleet. See [environment types](https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types) for more information and valid values. +* `overflow_behavior` - (Optional) Overflow behavior for compute fleet. Valid values: `ON_DEMAND`, `QUEUE`. +* `scaling_configuration` - (Optional) Configuration block. Detailed below. This option is only valid when your overflow behavior is `QUEUE`. +* `tags` - (Optional) Map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + +### scaling_configuration + +* `max_capacity` - (Optional) Maximum number of instances in the fleet when auto-scaling. +* `scaling_type` - (Optional) Scaling type for a compute fleet. Valid value: `TARGET_TRACKING_SCALING`. +* `target_tracking_scaling_configs` - (Optional) Configuration block. Detailed below. + +#### scaling_configuration:target_tracking_scaling_configs + +* `metric_type` - (Optional) Metric type to determine auto-scaling. Valid value: `FLEET_UTILIZATION_RATE`. +* `target_value` - (Optional) Value of metricType when to start scaling. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Fleet. +* `created` - Creation time of the fleet. +* `id` - ARN of the Fleet. +* `last_modified` - Last modification time of the fleet. +* `status` - Nested attribute containing information about the current status of the fleet. + * `context` - Additional information about a compute fleet. + * `message` - Message associated with the status of a compute fleet. + * `status_code` - Status code of the compute fleet. + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import CodeBuild Fleet using the `name` or the `arn`. For example: + +```terraform +import { + to = aws_codebuild_fleet.name + id = "fleet-name" +} +``` + +Using `terraform import`, import CodeBuild Fleet using the `name`. For example: + +```console +% terraform import aws_codebuild_fleet.name fleet-name +``` diff --git a/website/docs/r/codebuild_project.html.markdown b/website/docs/r/codebuild_project.html.markdown index d6d9b314e8b..afc307b2df7 100755 --- a/website/docs/r/codebuild_project.html.markdown +++ b/website/docs/r/codebuild_project.html.markdown @@ -289,6 +289,7 @@ The following arguments are optional: * `certificate` - (Optional) ARN of the S3 bucket, path prefix and object key that contains the PEM-encoded certificate. * `compute_type` - (Required) Information about the compute resources the build project will use. Valid values: `BUILD_GENERAL1_SMALL`, `BUILD_GENERAL1_MEDIUM`, `BUILD_GENERAL1_LARGE`, `BUILD_GENERAL1_2XLARGE`, `BUILD_LAMBDA_1GB`, `BUILD_LAMBDA_2GB`, `BUILD_LAMBDA_4GB`, `BUILD_LAMBDA_8GB`, `BUILD_LAMBDA_10GB`. `BUILD_GENERAL1_SMALL` is only valid if `type` is set to `LINUX_CONTAINER`. When `type` is set to `LINUX_GPU_CONTAINER`, `compute_type` must be `BUILD_GENERAL1_LARGE`. When `type` is set to `LINUX_LAMBDA_CONTAINER` or `ARM_LAMBDA_CONTAINER`, `compute_type` must be `BUILD_LAMBDA_XGB`.` +* `fleet` - (Optional) Configuration block. Detailed below. * `environment_variable` - (Optional) Configuration block. Detailed below. * `image_pull_credentials_type` - (Optional) Type of credentials AWS CodeBuild uses to pull images in your build. Valid values: `CODEBUILD`, `SERVICE_ROLE`. When you use a cross-account or private registry image, you must use SERVICE_ROLE credentials. When you use an AWS CodeBuild curated image, you must use CodeBuild credentials. Defaults to `CODEBUILD`. * `image` - (Required) Docker image to use for this build project. Valid values include [Docker images provided by CodeBuild](https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html) (e.g `aws/codebuild/amazonlinux2-x86_64-standard:4.0`), [Docker Hub images](https://hub.docker.com/) (e.g., `hashicorp/terraform:latest`), and full Docker repository URIs such as those for ECR (e.g., `137112412989.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latest`). @@ -296,6 +297,10 @@ The following arguments are optional: * `registry_credential` - (Optional) Configuration block. Detailed below. * `type` - (Required) Type of build environment to use for related builds. Valid values: `LINUX_CONTAINER`, `LINUX_GPU_CONTAINER`, `WINDOWS_CONTAINER` (deprecated), `WINDOWS_SERVER_2019_CONTAINER`, `ARM_CONTAINER`, `LINUX_LAMBDA_CONTAINER`, `ARM_LAMBDA_CONTAINER`. For additional information, see the [CodeBuild User Guide](https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html). +#### environment: fleet + +* `fleet_arn` - (Optional) Compute fleet ARN for the build project. + #### environment: environment_variable * `name` - (Required) Environment variable's name or key. From b10d3b4feafdb38f4353ab5ece4a26c42ecd82d9 Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Thu, 5 Sep 2024 17:00:42 +0200 Subject: [PATCH 02/18] Use enum.Validate --- internal/service/codebuild/fleet.go | 66 ++++++----------------------- 1 file changed, 13 insertions(+), 53 deletions(-) diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index 6abcd898f74..4f9c17e0edf 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -51,18 +51,18 @@ func ResourceFleet() *schema.Resource { ValidateFunc: validation.IntAtLeast(1), }, "compute_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(computeTypeValues(types.ComputeType("").Values()), false), + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: enum.Validate[types.ComputeType](), }, "created": { Type: schema.TypeString, Computed: true, }, "environment_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(environmentTypeValues(types.EnvironmentType("").Values()), false), + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: enum.Validate[types.EnvironmentType](), }, "id": { Type: schema.TypeString, @@ -79,10 +79,10 @@ func ResourceFleet() *schema.Resource { ValidateFunc: validation.StringLenBetween(2, 128), }, "overflow_behavior": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringInSlice(fleetOverflowBehaviorValues(types.FleetOverflowBehavior("").Values()), false), + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateDiagFunc: enum.Validate[types.FleetOverflowBehavior](), DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if new == "" { return true @@ -115,9 +115,9 @@ func ResourceFleet() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "metric_type": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice(fleetScalingMetricTypeValues(types.FleetScalingMetricType("").Values()), false), + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: enum.Validate[types.FleetScalingMetricType](), }, "target_value": { Type: schema.TypeFloat, @@ -535,43 +535,3 @@ func flattenStatus(apiObject *types.FleetStatus) map[string]interface{} { return tfMap } - -func fleetOverflowBehaviorValues(in []types.FleetOverflowBehavior) []string { - var out []string - - for _, v := range in { - out = append(out, string(v)) - } - - return out -} - -func computeTypeValues(in []types.ComputeType) []string { - var out []string - - for _, v := range in { - out = append(out, string(v)) - } - - return out -} - -func environmentTypeValues(in []types.EnvironmentType) []string { - var out []string - - for _, v := range in { - out = append(out, string(v)) - } - - return out -} - -func fleetScalingMetricTypeValues(in []types.FleetScalingMetricType) []string { - var out []string - - for _, v := range in { - out = append(out, string(v)) - } - - return out -} From 3ed711605581d4ccdac3e6b8bfed9bec3e4de827 Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Thu, 5 Sep 2024 17:02:42 +0200 Subject: [PATCH 03/18] Add `scaling_type` validation --- internal/service/codebuild/fleet.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index 4f9c17e0edf..31eaeaf96f7 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -106,8 +106,9 @@ func ResourceFleet() *schema.Resource { ValidateFunc: validation.IntAtLeast(1), }, "scaling_type": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: enum.Validate[types.FleetScalingType](), }, "target_tracking_scaling_configs": { Type: schema.TypeList, From a4fe22f8716694accaf9cba47bab2b9b3c6c2840 Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Mon, 9 Sep 2024 09:06:44 +0200 Subject: [PATCH 04/18] Introduce minor improvements to data handling and conversion --- internal/service/codebuild/fleet.go | 88 +++++++++++++---------------- 1 file changed, 39 insertions(+), 49 deletions(-) diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index 31eaeaf96f7..5bbc2c0b647 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -20,7 +20,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" - "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -179,15 +178,12 @@ func resourceFleetCreate(ctx context.Context, d *schema.ResourceData, meta inter input.OverflowBehavior = types.FleetOverflowBehavior(v.(string)) } - if v, ok := d.GetOk("scaling_configuration"); ok { - if len(v.([]interface{})) == 0 || v.([]interface{})[0] == nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, ResNameFleet, d.Get("name").(string), errors.New("scaling_configuration cannot be empty")) - } else { - input.ScalingConfiguration = expandScalingConfiguration(v.([]interface{})[0].(map[string]interface{})) - } + if v, ok := d.GetOk("scaling_configuration"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.ScalingConfiguration = expandScalingConfiguration(v.([]interface{})[0].(map[string]interface{})) } out, err := conn.CreateFleet(ctx, input) + if err != nil { return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, ResNameFleet, d.Get("name").(string), err) } @@ -222,39 +218,36 @@ func resourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interfa return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionReading, ResNameFleet, d.Id(), err) } - d.Set("arn", out.Fleets[0].Arn) - d.Set("base_capacity", out.Fleets[0].BaseCapacity) - d.Set("compute_type", out.Fleets[0].ComputeType) - d.Set("created", out.Fleets[0].Created.String()) - d.Set("environment_type", out.Fleets[0].EnvironmentType) - d.Set("id", out.Fleets[0].Id) - d.Set("last_modified", out.Fleets[0].LastModified.String()) - d.Set("name", out.Fleets[0].Name) - d.Set("overflow_behavior", out.Fleets[0].OverflowBehavior) - if len(out.Fleets) > 0 && out.Fleets[0].ScalingConfiguration != nil { - empty_scaling_configuration := out.Fleets[0].ScalingConfiguration.DesiredCapacity == nil && - out.Fleets[0].ScalingConfiguration.MaxCapacity == nil && - out.Fleets[0].ScalingConfiguration.ScalingType == "" && - out.Fleets[0].ScalingConfiguration.TargetTrackingScalingConfigs == nil - - if !empty_scaling_configuration { - if err := d.Set("scaling_configuration", []interface{}{flattenScalingConfiguration(out.Fleets[0].ScalingConfiguration)}); err != nil { - return sdkdiag.AppendErrorf(diags, "setting overflow behavior: %s", err) - } - } else { - d.Set("scaling_configuration", nil) + fleet := out.Fleets[0] + + d.Set("arn", fleet.Arn) + d.Set("base_capacity", fleet.BaseCapacity) + d.Set("compute_type", fleet.ComputeType) + d.Set("created", fleet.Created.String()) + d.Set("environment_type", fleet.EnvironmentType) + d.Set("id", fleet.Id) + d.Set("last_modified", fleet.LastModified.String()) + d.Set("name", fleet.Name) + d.Set("overflow_behavior", fleet.OverflowBehavior) + + if fleet.ScalingConfiguration != nil { + if err := d.Set("scaling_configuration", []interface{}{flattenScalingConfiguration(fleet.ScalingConfiguration)}); err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, ResNameFleet, d.Id(), err) } } else { d.Set("scaling_configuration", nil) } - if out.Fleets[0].Status != nil { - if err := d.Set("status", []interface{}{flattenStatus(out.Fleets[0].Status)}); err != nil { - return sdkdiag.AppendErrorf(diags, "setting status: %s", err) + + if fleet.Status != nil { + if err := d.Set("status", []interface{}{flattenStatus(fleet.Status)}); err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, ResNameFleet, d.Id(), err) } } else { d.Set("status", nil) } - setTagsOut(ctx, out.Fleets[0].Tags) + + setTagsOut(ctx, fleet.Tags) + return diags } @@ -278,20 +271,14 @@ func resourceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta inter input.EnvironmentType = types.EnvironmentType(d.Get("environment_type").(string)) } - // Make sure that overflow_behavior is set (if defined) on update - api ommit it on updates + // Make sure that overflow_behavior is set (if defined) on update - API omits it on updates. if v, ok := d.GetOk("overflow_behavior"); ok { input.OverflowBehavior = types.FleetOverflowBehavior(v.(string)) } if d.HasChange("scaling_configuration") { - if v, ok := d.GetOk("scaling_configuration"); ok { - if len(v.([]interface{})) == 0 || v.([]interface{})[0] == nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, ResNameFleet, d.Get("name").(string), errors.New("scaling_configuration cannot be empty")) - } else { - input.ScalingConfiguration = expandScalingConfiguration(v.([]interface{})[0].(map[string]interface{})) - } - } else { - input.ScalingConfiguration = &types.ScalingConfigurationInput{} + if v, ok := d.GetOk("scaling_configuration"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.ScalingConfiguration = expandScalingConfiguration(v.([]interface{})[0].(map[string]interface{})) } } @@ -299,12 +286,13 @@ func resourceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta inter log.Printf("[DEBUG] Updating CodeBuild Fleet (%s): %#v", d.Id(), input) _, err := conn.UpdateFleet(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "updating CodeBuild Fleet (%s): %s", d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionUpdating, ResNameFleet, d.Id(), err) } if err := waitFleetActive(ctx, conn, d.Id()); err != nil { - return sdkdiag.AppendErrorf(diags, "updating CodeBuild Fleet (%s): %s", d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionUpdating, ResNameFleet, d.Id(), err) } return append(diags, resourceFleetRead(ctx, d, meta)...) @@ -324,7 +312,7 @@ func resourceFleetDelete(ctx context.Context, d *schema.ResourceData, meta inter return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting CodeBuild Fleet (%s): %s", d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionDeleting, ResNameFleet, d.Id(), err) } if err := waitFleetDeleted(ctx, conn, d.Id()); err != nil { @@ -376,7 +364,7 @@ func statusFleet(ctx context.Context, conn *codebuild.Client, id string, delete return nil, "", err } - return out, aws.ToString((*string)(&out.Fleets[0].Status.StatusCode)), nil + return out, string(out.Fleets[0].Status.StatusCode), nil } } @@ -401,14 +389,14 @@ func findFleetByARNOrNames(ctx context.Context, conn *codebuild.Client, arn stri } func expandScalingConfiguration(tfMap map[string]interface{}) *types.ScalingConfigurationInput { - if len(tfMap) == 0 { + if tfMap == nil { return nil } apiObject := &types.ScalingConfigurationInput{} + if v, ok := tfMap["max_capacity"].(int); ok { - int32Value := int32(v) - apiObject.MaxCapacity = &int32Value + apiObject.MaxCapacity = aws.Int32(int32(v)) } if v, ok := tfMap["scaling_type"].(string); ok && v != "" { @@ -431,11 +419,13 @@ func expandTargetTrackingScalingConfigs(tfList []interface{}) []types.TargetTrac for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { continue } apiObject := expandTargetTrackingScalingConfig(tfMap) + if apiObject == nil { continue } @@ -457,7 +447,7 @@ func expandTargetTrackingScalingConfig(tfMap map[string]interface{}) *types.Targ } if v, ok := tfMap["target_value"].(float64); ok { - apiObject.TargetValue = &v + apiObject.TargetValue = aws.Float64(v) } return apiObject From b0437413403a98781b9b59dfbe8dc12e173af953 Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Mon, 9 Sep 2024 23:54:32 +0200 Subject: [PATCH 05/18] Add `fleet_service_role` argument --- internal/service/codebuild/fleet.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index 5bbc2c0b647..863386282d4 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -63,6 +63,11 @@ func ResourceFleet() *schema.Resource { Required: true, ValidateDiagFunc: enum.Validate[types.EnvironmentType](), }, + "fleet_service_role": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: verify.ValidARN, + }, "id": { Type: schema.TypeString, Computed: true, @@ -174,6 +179,10 @@ func resourceFleetCreate(ctx context.Context, d *schema.ResourceData, meta inter Tags: getTagsIn(ctx), } + if v, ok := d.GetOk("fleet_service_role"); ok { + input.FleetServiceRole = aws.String(v.(string)) + } + if v, ok := d.GetOk("overflow_behavior"); ok { input.OverflowBehavior = types.FleetOverflowBehavior(v.(string)) } @@ -225,6 +234,7 @@ func resourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interfa d.Set("compute_type", fleet.ComputeType) d.Set("created", fleet.Created.String()) d.Set("environment_type", fleet.EnvironmentType) + d.Set("fleet_service_role", fleet.FleetServiceRole) d.Set("id", fleet.Id) d.Set("last_modified", fleet.LastModified.String()) d.Set("name", fleet.Name) @@ -271,6 +281,10 @@ func resourceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta inter input.EnvironmentType = types.EnvironmentType(d.Get("environment_type").(string)) } + if d.HasChange("fleet_service_role") { + input.FleetServiceRole = aws.String(d.Get("fleet_service_role").(string)) + } + // Make sure that overflow_behavior is set (if defined) on update - API omits it on updates. if v, ok := d.GetOk("overflow_behavior"); ok { input.OverflowBehavior = types.FleetOverflowBehavior(v.(string)) From f67201af46443c4943f5c70654a166d581f1d11a Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Mon, 9 Sep 2024 23:54:39 +0200 Subject: [PATCH 06/18] Add `vpc_config` argument --- internal/service/codebuild/fleet.go | 108 +++++++++++++++++++++++++--- 1 file changed, 100 insertions(+), 8 deletions(-) diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index 863386282d4..1f76c5d0344 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -5,7 +5,6 @@ package codebuild import ( "context" - "errors" "log" "time" @@ -20,6 +19,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -157,6 +157,34 @@ func ResourceFleet() *schema.Resource { }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), + "vpc_config": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + RequiredWith: []string{"fleet_service_role"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "security_group_ids": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + MinItems: 1, + MaxItems: 5, + }, + "subnets": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + MinItems: 1, + MaxItems: 16, + }, + "vpc_id": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, }, CustomizeDiff: verify.SetTagsDiff, } @@ -191,17 +219,20 @@ func resourceFleetCreate(ctx context.Context, d *schema.ResourceData, meta inter input.ScalingConfiguration = expandScalingConfiguration(v.([]interface{})[0].(map[string]interface{})) } - out, err := conn.CreateFleet(ctx, input) + if v, ok := d.GetOk("vpc_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.VpcConfig = expandVpcConfig(v.([]interface{})[0].(map[string]interface{})) + } + + // InvalidInputException: CodeBuild is not authorized to perform + outputRaw, err := tfresource.RetryWhenIsAErrorMessageContains[*types.InvalidInputException](ctx, propagationTimeout, func() (interface{}, error) { + return conn.CreateFleet(ctx, input) + }, "ot authorized to perform") if err != nil { return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, ResNameFleet, d.Get("name").(string), err) } - if out == nil || out.Fleet == nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, ResNameFleet, d.Get("name").(string), errors.New("empty output")) - } - - d.SetId(aws.ToString(out.Fleet.Arn)) + d.SetId(aws.ToString(outputRaw.(*codebuild.CreateFleetOutput).Fleet.Arn)) if err := waitFleetActive(ctx, conn, d.Id()); err != nil { return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionWaitingForCreation, ResNameFleet, d.Id(), err) @@ -256,6 +287,14 @@ func resourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interfa d.Set("status", nil) } + if fleet.VpcConfig != nil { + if err := d.Set("vpc_config", []interface{}{flattenVpcConfig(fleet.VpcConfig)}); err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, ResNameFleet, d.Id(), err) + } + } else { + d.Set("vpc_config", nil) + } + setTagsOut(ctx, fleet.Tags) return diags @@ -296,10 +335,19 @@ func resourceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta inter } } + if d.HasChange("vpc_config") { + if v, ok := d.GetOk("vpc_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.VpcConfig = expandVpcConfig(v.([]interface{})[0].(map[string]interface{})) + } + } + input.Tags = getTagsIn(ctx) log.Printf("[DEBUG] Updating CodeBuild Fleet (%s): %#v", d.Id(), input) - _, err := conn.UpdateFleet(ctx, input) + + _, err := tfresource.RetryWhenIsAErrorMessageContains[*types.InvalidInputException](ctx, propagationTimeout, func() (interface{}, error) { + return conn.UpdateFleet(ctx, input) + }, "ot authorized to perform") if err != nil { return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionUpdating, ResNameFleet, d.Id(), err) @@ -467,6 +515,28 @@ func expandTargetTrackingScalingConfig(tfMap map[string]interface{}) *types.Targ return apiObject } +func expandVpcConfig(tfMap map[string]interface{}) *types.VpcConfig { + if tfMap == nil { + return nil + } + + apiObject := &types.VpcConfig{} + + if v, ok := tfMap["security_group_ids"].(*schema.Set); ok && v.Len() > 0 { + apiObject.SecurityGroupIds = flex.ExpandStringValueSet(v) + } + + if v, ok := tfMap["subnets"].(*schema.Set); ok && v.Len() > 0 { + apiObject.Subnets = flex.ExpandStringValueSet(v) + } + + if v, ok := tfMap["vpc_id"].(string); ok && v != "" { + apiObject.VpcId = aws.String(v) + } + + return apiObject +} + func flattenScalingConfiguration(apiObject *types.ScalingConfigurationOutput) map[string]interface{} { tfMap := map[string]interface{}{} @@ -540,3 +610,25 @@ func flattenStatus(apiObject *types.FleetStatus) map[string]interface{} { return tfMap } + +func flattenVpcConfig(apiObject *types.VpcConfig) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.SecurityGroupIds; v != nil { + tfMap["security_group_ids"] = v + } + + if v := apiObject.Subnets; v != nil { + tfMap["subnets"] = v + } + + if v := apiObject.VpcId; v != nil { + tfMap["vpc_id"] = aws.ToString(v) + } + + return tfMap +} From 32687f19ec4c7324d07eef8cd68dc1739e9ca651 Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Mon, 9 Sep 2024 23:54:40 +0200 Subject: [PATCH 07/18] Reuse existing attribute names and flex functions --- internal/service/codebuild/fleet.go | 73 +++++------------------------ 1 file changed, 13 insertions(+), 60 deletions(-) diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index 1f76c5d0344..11810ba11da 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -19,7 +19,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" - "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -157,28 +156,28 @@ func ResourceFleet() *schema.Resource { }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), - "vpc_config": { + names.AttrVPCConfig: { Type: schema.TypeList, Optional: true, MinItems: 1, RequiredWith: []string{"fleet_service_role"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "security_group_ids": { + names.AttrSecurityGroupIDs: { Type: schema.TypeSet, Required: true, Elem: &schema.Schema{Type: schema.TypeString}, MinItems: 1, MaxItems: 5, }, - "subnets": { + names.AttrSubnets: { Type: schema.TypeSet, Required: true, Elem: &schema.Schema{Type: schema.TypeString}, MinItems: 1, MaxItems: 16, }, - "vpc_id": { + names.AttrVPCID: { Type: schema.TypeString, Required: true, }, @@ -219,8 +218,8 @@ func resourceFleetCreate(ctx context.Context, d *schema.ResourceData, meta inter input.ScalingConfiguration = expandScalingConfiguration(v.([]interface{})[0].(map[string]interface{})) } - if v, ok := d.GetOk("vpc_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.VpcConfig = expandVpcConfig(v.([]interface{})[0].(map[string]interface{})) + if v, ok := d.GetOk(names.AttrVPCConfig); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.VpcConfig = expandVPCConfig(v.([]interface{})[0].(map[string]interface{})) } // InvalidInputException: CodeBuild is not authorized to perform @@ -287,12 +286,8 @@ func resourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interfa d.Set("status", nil) } - if fleet.VpcConfig != nil { - if err := d.Set("vpc_config", []interface{}{flattenVpcConfig(fleet.VpcConfig)}); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, ResNameFleet, d.Id(), err) - } - } else { - d.Set("vpc_config", nil) + if err := d.Set(names.AttrVPCConfig, flattenVPCConfig(fleet.VpcConfig)); err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, ResNameFleet, d.Id(), err) } setTagsOut(ctx, fleet.Tags) @@ -335,9 +330,11 @@ func resourceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta inter } } - if d.HasChange("vpc_config") { - if v, ok := d.GetOk("vpc_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.VpcConfig = expandVpcConfig(v.([]interface{})[0].(map[string]interface{})) + if d.HasChange(names.AttrVPCConfig) { + if v, ok := d.GetOk(names.AttrVPCConfig); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.VpcConfig = expandVPCConfig(v.([]interface{})[0].(map[string]interface{})) + } else { + input.VpcConfig = &types.VpcConfig{} } } @@ -515,28 +512,6 @@ func expandTargetTrackingScalingConfig(tfMap map[string]interface{}) *types.Targ return apiObject } -func expandVpcConfig(tfMap map[string]interface{}) *types.VpcConfig { - if tfMap == nil { - return nil - } - - apiObject := &types.VpcConfig{} - - if v, ok := tfMap["security_group_ids"].(*schema.Set); ok && v.Len() > 0 { - apiObject.SecurityGroupIds = flex.ExpandStringValueSet(v) - } - - if v, ok := tfMap["subnets"].(*schema.Set); ok && v.Len() > 0 { - apiObject.Subnets = flex.ExpandStringValueSet(v) - } - - if v, ok := tfMap["vpc_id"].(string); ok && v != "" { - apiObject.VpcId = aws.String(v) - } - - return apiObject -} - func flattenScalingConfiguration(apiObject *types.ScalingConfigurationOutput) map[string]interface{} { tfMap := map[string]interface{}{} @@ -610,25 +585,3 @@ func flattenStatus(apiObject *types.FleetStatus) map[string]interface{} { return tfMap } - -func flattenVpcConfig(apiObject *types.VpcConfig) map[string]interface{} { - if apiObject == nil { - return nil - } - - tfMap := map[string]interface{}{} - - if v := apiObject.SecurityGroupIds; v != nil { - tfMap["security_group_ids"] = v - } - - if v := apiObject.Subnets; v != nil { - tfMap["subnets"] = v - } - - if v := apiObject.VpcId; v != nil { - tfMap["vpc_id"] = aws.ToString(v) - } - - return tfMap -} From b54aa0cbe8a8f6563715fb40a116f48f1904f512 Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Mon, 9 Sep 2024 23:54:42 +0200 Subject: [PATCH 08/18] Revamp test and add VPC config tests --- internal/service/codebuild/fleet.go | 2 - internal/service/codebuild/fleet_test.go | 300 ++++++++++++++++++----- 2 files changed, 245 insertions(+), 57 deletions(-) diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index 11810ba11da..bd506869947 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -333,8 +333,6 @@ func resourceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta inter if d.HasChange(names.AttrVPCConfig) { if v, ok := d.GetOk(names.AttrVPCConfig); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { input.VpcConfig = expandVPCConfig(v.([]interface{})[0].(map[string]interface{})) - } else { - input.VpcConfig = &types.VpcConfig{} } } diff --git a/internal/service/codebuild/fleet_test.go b/internal/service/codebuild/fleet_test.go index b36a4c21c5c..5883aeb12eb 100644 --- a/internal/service/codebuild/fleet_test.go +++ b/internal/service/codebuild/fleet_test.go @@ -8,6 +8,8 @@ import ( "fmt" "testing" + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/service/codebuild/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -22,6 +24,7 @@ func TestAccCodeBuildFleet_basic(t *testing.T) { ctx := context.Background() rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_codebuild_fleet.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), @@ -51,7 +54,6 @@ func TestAccCodeBuildFleet_basic(t *testing.T) { func TestAccCodeBuildFleet_disappears(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_codebuild_fleet.test" resource.ParallelTest(t, resource.TestCase{ @@ -112,10 +114,11 @@ func TestAccCodeBuildFleet_tags(t *testing.T) { }) } -func TestAccCodeBuildFleet_updateBasicParameters(t *testing.T) { +func TestAccCodeBuildFleet_baseCapacity(t *testing.T) { ctx := context.Background() rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_codebuild_fleet.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), @@ -123,57 +126,86 @@ func TestAccCodeBuildFleet_updateBasicParameters(t *testing.T) { CheckDestroy: testAccCheckFleetDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccFleetConfig_basic(rName), + Config: testAccFleetConfig_baseCapacity(rName, 1), Check: resource.ComposeTestCheckFunc( testAccCheckFleetExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), - resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_SMALL"), - resource.TestCheckResourceAttr(resourceName, "environment_type", "LINUX_CONTAINER"), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "ON_DEMAND"), ), }, { - Config: testAccFleetConfig_updateBaseCapacity(rName), + Config: testAccFleetConfig_baseCapacity(rName, 2), Check: resource.ComposeTestCheckFunc( testAccCheckFleetExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "base_capacity", "2"), + ), + }, + }, + }) +} + +func TestAccCodeBuildFleet_computeType(t *testing.T) { + ctx := context.Background() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_codebuild_fleet.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFleetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFleetConfig_computeType(rName, types.ComputeTypeBuildGeneral1Small), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_SMALL"), - resource.TestCheckResourceAttr(resourceName, "environment_type", "LINUX_CONTAINER"), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "ON_DEMAND"), ), }, { - Config: testAccFleetConfig_updateComputeType(rName), + Config: testAccFleetConfig_computeType(rName, types.ComputeTypeBuildGeneral1Medium), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_MEDIUM"), + ), + }, + }, + }) +} + +func TestAccCodeBuildFleet_environmentType(t *testing.T) { + ctx := context.Background() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_codebuild_fleet.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFleetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFleetConfig_environmentType(rName, types.EnvironmentTypeLinuxContainer), Check: resource.ComposeTestCheckFunc( testAccCheckFleetExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), - resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_LARGE"), resource.TestCheckResourceAttr(resourceName, "environment_type", "LINUX_CONTAINER"), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "ON_DEMAND"), ), }, { - Config: testAccFleetConfig_updateEnvironmentType(rName), + Config: testAccFleetConfig_environmentType(rName, types.EnvironmentTypeArmContainer), Check: resource.ComposeTestCheckFunc( testAccCheckFleetExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), - resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_LARGE"), resource.TestCheckResourceAttr(resourceName, "environment_type", "ARM_CONTAINER"), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "ON_DEMAND"), ), }, }, }) } -func TestAccCodeBuildFleet_updateScalingConfiguration(t *testing.T) { +func TestAccCodeBuildFleet_scalingConfiguration(t *testing.T) { ctx := context.Background() rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_codebuild_fleet.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), @@ -181,29 +213,69 @@ func TestAccCodeBuildFleet_updateScalingConfiguration(t *testing.T) { CheckDestroy: testAccCheckFleetDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccFleetConfig_scalingConfiguration(rName), + Config: testAccFleetConfig_scalingConfiguration1(rName), Check: resource.ComposeTestCheckFunc( testAccCheckFleetExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), - resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_SMALL"), - resource.TestCheckResourceAttr(resourceName, "environment_type", "ARM_CONTAINER"), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "QUEUE"), - resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.max_capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.max_capacity", acctest.Ct2), resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.scaling_type", "TARGET_TRACKING_SCALING"), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.target_tracking_scaling_configs.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.target_tracking_scaling_configs.0.metric_type", "FLEET_UTILIZATION_RATE"), resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.target_tracking_scaling_configs.0.target_value", "97.5"), ), }, { - Config: testAccFleetConfig_noScalingConfiguration(rName), + Config: testAccFleetConfig_scalingConfiguration2(rName), Check: resource.ComposeTestCheckFunc( testAccCheckFleetExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), - resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_SMALL"), - resource.TestCheckResourceAttr(resourceName, "environment_type", "ARM_CONTAINER"), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "ON_DEMAND"), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.max_capacity", acctest.Ct3), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.scaling_type", "TARGET_TRACKING_SCALING"), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.target_tracking_scaling_configs.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.target_tracking_scaling_configs.0.metric_type", "FLEET_UTILIZATION_RATE"), + resource.TestCheckResourceAttr(resourceName, "scaling_configuration.0.target_tracking_scaling_configs.0.target_value", "90.5"), + ), + }, + }, + }) +} + +func TestAccCodeBuildFleet_vpcConfig(t *testing.T) { + ctx := context.Background() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_codebuild_fleet.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFleetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFleetConfig_vpcConfig2(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "vpc_config.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnets.#", acctest.Ct1), + resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.subnets.0", "aws_subnet.test.1", "id"), + resource.TestMatchResourceAttr(resourceName, "vpc_config.0.vpc_id", regexache.MustCompile(`^vpc-`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccFleetConfig_vpcConfig1(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "vpc_config.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnets.#", acctest.Ct1), + resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.subnets.0", "aws_subnet.test.0", "id"), + resource.TestMatchResourceAttr(resourceName, "vpc_config.0.vpc_id", regexache.MustCompile(`^vpc-`)), ), }, }, @@ -269,59 +341,61 @@ resource "aws_codebuild_fleet" "test" { base_capacity = 1 compute_type = "BUILD_GENERAL1_SMALL" environment_type = "LINUX_CONTAINER" - name = %q + name = %[1]q overflow_behavior = "ON_DEMAND" } `, rName) } -func testAccFleetConfig_updateBaseCapacity(rName string) string { +func testAccFleetConfig_baseCapacity(rName string, baseCapacity int) string { return fmt.Sprintf(` resource "aws_codebuild_fleet" "test" { - base_capacity = 2 + base_capacity = %[2]d compute_type = "BUILD_GENERAL1_SMALL" environment_type = "LINUX_CONTAINER" - name = %q + name = %[1]q overflow_behavior = "ON_DEMAND" } -`, rName) +`, rName, baseCapacity) } -func testAccFleetConfig_updateComputeType(rName string) string { +func testAccFleetConfig_computeType(rName string, computeType types.ComputeType) string { return fmt.Sprintf(` resource "aws_codebuild_fleet" "test" { base_capacity = 1 - compute_type = "BUILD_GENERAL1_LARGE" + compute_type = %[2]q environment_type = "LINUX_CONTAINER" - name = %q + name = %[1]q overflow_behavior = "ON_DEMAND" } -`, rName) +`, rName, string(computeType)) } -func testAccFleetConfig_updateEnvironmentType(rName string) string { +func testAccFleetConfig_environmentType(rName string, environmentType types.EnvironmentType) string { return fmt.Sprintf(` resource "aws_codebuild_fleet" "test" { base_capacity = 1 compute_type = "BUILD_GENERAL1_LARGE" - environment_type = "ARM_CONTAINER" - name = %q + environment_type = %[2]q + name = %[1]q overflow_behavior = "ON_DEMAND" } -`, rName) +`, rName, string(environmentType)) } -func testAccFleetConfig_scalingConfiguration(rName string) string { +func testAccFleetConfig_scalingConfiguration1(rName string) string { return fmt.Sprintf(` resource "aws_codebuild_fleet" "test" { base_capacity = 1 compute_type = "BUILD_GENERAL1_SMALL" environment_type = "ARM_CONTAINER" - name = %q + name = %[1]q overflow_behavior = "QUEUE" + scaling_configuration { max_capacity = 2 scaling_type = "TARGET_TRACKING_SCALING" + target_tracking_scaling_configs { metric_type = "FLEET_UTILIZATION_RATE" target_value = 97.5 @@ -331,25 +405,141 @@ resource "aws_codebuild_fleet" "test" { `, rName) } -func testAccFleetConfig_noScalingConfiguration(rName string) string { +func testAccFleetConfig_scalingConfiguration2(rName string) string { return fmt.Sprintf(` resource "aws_codebuild_fleet" "test" { base_capacity = 1 compute_type = "BUILD_GENERAL1_SMALL" environment_type = "ARM_CONTAINER" - name = %q - overflow_behavior = "ON_DEMAND" + name = %[1]q + overflow_behavior = "QUEUE" + + scaling_configuration { + max_capacity = 3 + scaling_type = "TARGET_TRACKING_SCALING" + + target_tracking_scaling_configs { + metric_type = "FLEET_UTILIZATION_RATE" + target_value = 90.5 + } + } } `, rName) } +func testAccFleetConfig_baseFleetServiceRole(rName string) string { + return fmt.Sprintf(` +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = < Date: Mon, 9 Sep 2024 23:54:43 +0200 Subject: [PATCH 09/18] Add `image_id` argument --- internal/service/codebuild/fleet.go | 13 ++++++++ internal/service/codebuild/fleet_test.go | 40 ++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index bd506869947..2b29ea5ece9 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -71,6 +71,10 @@ func ResourceFleet() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "image_id": { + Type: schema.TypeString, + Optional: true, + }, "last_modified": { Type: schema.TypeString, Computed: true, @@ -210,6 +214,10 @@ func resourceFleetCreate(ctx context.Context, d *schema.ResourceData, meta inter input.FleetServiceRole = aws.String(v.(string)) } + if v, ok := d.GetOk("image_id"); ok { + input.ImageId = aws.String(v.(string)) + } + if v, ok := d.GetOk("overflow_behavior"); ok { input.OverflowBehavior = types.FleetOverflowBehavior(v.(string)) } @@ -266,6 +274,7 @@ func resourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interfa d.Set("environment_type", fleet.EnvironmentType) d.Set("fleet_service_role", fleet.FleetServiceRole) d.Set("id", fleet.Id) + d.Set("image_id", fleet.ImageId) d.Set("last_modified", fleet.LastModified.String()) d.Set("name", fleet.Name) d.Set("overflow_behavior", fleet.OverflowBehavior) @@ -319,6 +328,10 @@ func resourceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta inter input.FleetServiceRole = aws.String(d.Get("fleet_service_role").(string)) } + if d.HasChange("image_id") { + input.ImageId = aws.String(d.Get("image_id").(string)) + } + // Make sure that overflow_behavior is set (if defined) on update - API omits it on updates. if v, ok := d.GetOk("overflow_behavior"); ok { input.OverflowBehavior = types.FleetOverflowBehavior(v.(string)) diff --git a/internal/service/codebuild/fleet_test.go b/internal/service/codebuild/fleet_test.go index 5883aeb12eb..9bb99709e36 100644 --- a/internal/service/codebuild/fleet_test.go +++ b/internal/service/codebuild/fleet_test.go @@ -201,6 +201,33 @@ func TestAccCodeBuildFleet_environmentType(t *testing.T) { }) } +func TestAccCodeBuildFleet_imageId(t *testing.T) { + ctx := context.Background() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_codebuild_fleet.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.CodeBuildServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFleetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFleetConfig_imageId(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFleetExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "image_id", "aws/codebuild/macos-arm-base:14"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccCodeBuildFleet_scalingConfiguration(t *testing.T) { ctx := context.Background() rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -383,6 +410,19 @@ resource "aws_codebuild_fleet" "test" { `, rName, string(environmentType)) } +func testAccFleetConfig_imageId(rName string) string { + return fmt.Sprintf(` +resource "aws_codebuild_fleet" "test" { + base_capacity = 1 + compute_type = "BUILD_GENERAL1_MEDIUM" + environment_type = "MAC_ARM" + name = %[1]q + overflow_behavior = "QUEUE" + image_id = "aws/codebuild/macos-arm-base:14" +} +`, rName) +} + func testAccFleetConfig_scalingConfiguration1(rName string) string { return fmt.Sprintf(` resource "aws_codebuild_fleet" "test" { From 833a1d195034b5a753a620e5736955f4659b37de Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Tue, 10 Sep 2024 00:00:15 +0200 Subject: [PATCH 10/18] Update resource docs --- website/docs/r/codebuild_fleet.html.markdown | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/website/docs/r/codebuild_fleet.html.markdown b/website/docs/r/codebuild_fleet.html.markdown index ad368c94fc2..c0a6de53a0d 100644 --- a/website/docs/r/codebuild_fleet.html.markdown +++ b/website/docs/r/codebuild_fleet.html.markdown @@ -19,9 +19,11 @@ resource "aws_codebuild_fleet" "test" { environment_type = "LINUX_CONTAINER" name = "full-example-codebuild-fleet" overflow_behavior = "QUEUE" + scaling_configuration { max_capacity = 5 scaling_type = "TARGET_TRACKING_SCALING" + target_tracking_scaling_configs { metric_type = "FLEET_UTILIZATION_RATE" target_value = 97.5 @@ -43,15 +45,17 @@ resource "aws_codebuild_fleet" "example" { The following arguments are required: * `name` - (Required) Fleet name. +* `base_capacity` - (Required) Number of machines allocated to the fleet. +* `compute_type` - (Required) Compute resources the compute fleet uses. See [compute types](https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types) for more information and valid values. +* `environment_type` - (Required) Environment type of the compute fleet. See [environment types](https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types) for more information and valid values. The following arguments are optional: - -* `base_capacity` - (Optional) Number of machines allocated to the fleet. -* `compute_type` - (Optional) Compute resources the compute fleet uses. See [compute types](https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types) for more information and valid values. -* `environment_type` - (Optional) Environment type of the compute fleet. See [environment types](https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types) for more information and valid values. +* `fleet_service_role` - (Optional) The service role associated with the compute fleet. +* `image_id` - (Optional) The Amazon Machine Image (AMI) of the compute fleet. * `overflow_behavior` - (Optional) Overflow behavior for compute fleet. Valid values: `ON_DEMAND`, `QUEUE`. * `scaling_configuration` - (Optional) Configuration block. Detailed below. This option is only valid when your overflow behavior is `QUEUE`. * `tags` - (Optional) Map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. +* `vpc_config` - (Optional) Configuration block. Detailed below. ### scaling_configuration @@ -59,11 +63,17 @@ The following arguments are optional: * `scaling_type` - (Optional) Scaling type for a compute fleet. Valid value: `TARGET_TRACKING_SCALING`. * `target_tracking_scaling_configs` - (Optional) Configuration block. Detailed below. -#### scaling_configuration:target_tracking_scaling_configs +#### scaling_configuration: target_tracking_scaling_configs * `metric_type` - (Optional) Metric type to determine auto-scaling. Valid value: `FLEET_UTILIZATION_RATE`. * `target_value` - (Optional) Value of metricType when to start scaling. +### vpc_config + +* `security_group_ids` - (Required) A list of one or more security groups IDs in your Amazon VPC. +* `subnets` - (Required) A list of one or more subnet IDs in your Amazon VPC. +* `vpc_id` - (Required) The ID of the Amazon VPC. + ## Attribute Reference This resource exports the following attributes in addition to the arguments above: From 6f3c8dd63f9ee9c84399141701c8ff2d7e8d8b67 Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Tue, 10 Sep 2024 00:07:13 +0200 Subject: [PATCH 11/18] Update data source --- internal/service/codebuild/fleet.go | 2 +- .../service/codebuild/fleet_data_source.go | 70 +++++++++++++++---- website/docs/d/codebuild_fleet.html.markdown | 8 +++ 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index 2b29ea5ece9..aaae168f27c 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -386,7 +386,7 @@ func resourceFleetDelete(ctx context.Context, d *schema.ResourceData, meta inter } if err := waitFleetDeleted(ctx, conn, d.Id()); err != nil { - return sdkdiag.AppendErrorf(diags, "deleting CodeBuild Fleet (%s): %s", d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionDeleting, ResNameFleet, d.Id(), err) } return diags diff --git a/internal/service/codebuild/fleet_data_source.go b/internal/service/codebuild/fleet_data_source.go index d68e1fe6028..7f952d85fc4 100644 --- a/internal/service/codebuild/fleet_data_source.go +++ b/internal/service/codebuild/fleet_data_source.go @@ -42,10 +42,18 @@ func DataSourceFleet() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "fleet_service_role": { + Type: schema.TypeString, + Computed: true, + }, "id": { Type: schema.TypeString, Computed: true, }, + "image_id": { + Type: schema.TypeString, + Computed: true, + }, "last_modified": { Type: schema.TypeString, Computed: true, @@ -116,6 +124,28 @@ func DataSourceFleet() *schema.Resource { }, }, "tags": tftags.TagsSchemaComputed(), + names.AttrVPCConfig: { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + names.AttrSecurityGroupIDs: { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + names.AttrSubnets: { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + names.AttrVPCID: { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, }, } } @@ -134,30 +164,40 @@ func dataSourceFleetRead(ctx context.Context, d *schema.ResourceData, meta inter if err != nil { return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionReading, DSNameFleet, name, err) } - d.SetId(aws.StringValue(out.Fleets[0].Arn)) - - d.Set("arn", out.Fleets[0].Arn) - d.Set("base_capacity", out.Fleets[0].BaseCapacity) - d.Set("compute_type", out.Fleets[0].ComputeType) - d.Set("created", out.Fleets[0].Created.String()) - d.Set("environment_type", out.Fleets[0].EnvironmentType) - d.Set("last_modified", out.Fleets[0].LastModified.String()) - d.Set("overflow_behavior", out.Fleets[0].OverflowBehavior) - d.Set("name", out.Fleets[0].Name) - if out.Fleets[0].ScalingConfiguration != nil { - if err := d.Set("scaling_configuration", []interface{}{flattenScalingConfiguration(out.Fleets[0].ScalingConfiguration)}); err != nil { + + fleet := out.Fleets[0] + + d.SetId(aws.StringValue(fleet.Arn)) + + d.Set("arn", fleet.Arn) + d.Set("base_capacity", fleet.BaseCapacity) + d.Set("compute_type", fleet.ComputeType) + d.Set("created", fleet.Created.String()) + d.Set("environment_type", fleet.EnvironmentType) + d.Set("fleet_service_role", fleet.FleetServiceRole) + d.Set("image_id", fleet.ImageId) + d.Set("last_modified", fleet.LastModified.String()) + d.Set("overflow_behavior", fleet.OverflowBehavior) + d.Set("name", fleet.Name) + + if fleet.ScalingConfiguration != nil { + if err := d.Set("scaling_configuration", []interface{}{flattenScalingConfiguration(fleet.ScalingConfiguration)}); err != nil { return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) } } - if out.Fleets[0].Status != nil { - if err := d.Set("status", []interface{}{flattenStatus(out.Fleets[0].Status)}); err != nil { + if fleet.Status != nil { + if err := d.Set("status", []interface{}{flattenStatus(fleet.Status)}); err != nil { return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) } } + if err := d.Set(names.AttrVPCConfig, flattenVPCConfig(fleet.VpcConfig)); err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) + } + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig //lintignore:AWSR002 - if err := d.Set("tags", KeyValueTags(ctx, out.Fleets[0].Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + if err := d.Set("tags", KeyValueTags(ctx, fleet.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) } diff --git a/website/docs/d/codebuild_fleet.html.markdown b/website/docs/d/codebuild_fleet.html.markdown index aeafbdc44b8..73e720a7060 100644 --- a/website/docs/d/codebuild_fleet.html.markdown +++ b/website/docs/d/codebuild_fleet.html.markdown @@ -23,9 +23,11 @@ resource "aws_codebuild_fleet" "test" { environment_type = "LINUX_CONTAINER" name = "full-example-codebuild-fleet" overflow_behavior = "QUEUE" + scaling_configuration { max_capacity = 5 scaling_type = "TARGET_TRACKING_SCALING" + target_tracking_scaling_configs { metric_type = "FLEET_UTILIZATION_RATE" target_value = 97.5 @@ -57,7 +59,9 @@ This data source exports the following attributes in addition to the arguments a * `compute_type` - Compute resources the compute fleet uses. * `created` - Creation time of the fleet. * `environment_type` - Environment type of the compute fleet. +* `fleet_service_role` - The service role associated with the compute fleet. * `id` - ARN of the Fleet. +* `image_id` - The Amazon Machine Image (AMI) of the compute fleet. * `last_modified` - Last modification time of the fleet. * `overflow_behavior` - Overflow behavior for compute fleet. * `scaling_configuration` - Nested attribute containing information about the scaling configuration. @@ -72,3 +76,7 @@ This data source exports the following attributes in addition to the arguments a * `message` - Message associated with the status of a compute fleet. * `status_code` - Status code of the compute fleet. * `tags` - Mapping of Key-Value tags for the resource. +* `vpc_config` - Nested attribute containing information about the VPC configuration. + * `security_group_ids` - A list of one or more security groups IDs in your Amazon VPC. + * `subnets` - A list of one or more subnet IDs in your Amazon VPC. + * `vpc_id` - The ID of the Amazon VPC. From 22ae63261d1e24a3a67b5ec7ff856905b5562164 Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Tue, 10 Sep 2024 00:10:26 +0200 Subject: [PATCH 12/18] Update changelog --- .changelog/{36790.txt => 39237.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changelog/{36790.txt => 39237.txt} (100%) diff --git a/.changelog/36790.txt b/.changelog/39237.txt similarity index 100% rename from .changelog/36790.txt rename to .changelog/39237.txt From afd8bc9792d887c5e7057c905d77a2f703e17b25 Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Tue, 10 Sep 2024 00:16:27 +0200 Subject: [PATCH 13/18] Fix terrafmt issues --- internal/service/codebuild/fleet_test.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/service/codebuild/fleet_test.go b/internal/service/codebuild/fleet_test.go index 9bb99709e36..4bb3b78b60b 100644 --- a/internal/service/codebuild/fleet_test.go +++ b/internal/service/codebuild/fleet_test.go @@ -418,7 +418,7 @@ resource "aws_codebuild_fleet" "test" { environment_type = "MAC_ARM" name = %[1]q overflow_behavior = "QUEUE" - image_id = "aws/codebuild/macos-arm-base:14" + image_id = "aws/codebuild/macos-arm-base:14" } `, rName) } @@ -535,11 +535,11 @@ func testAccFleetConfig_vpcConfig1(rName string) string { testAccFleetConfig_baseVPC(rName), fmt.Sprintf(` resource "aws_codebuild_fleet" "test" { - base_capacity = 1 - compute_type = "BUILD_GENERAL1_SMALL" - environment_type = "LINUX_CONTAINER" - name = %[1]q - overflow_behavior = "ON_DEMAND" + base_capacity = 1 + compute_type = "BUILD_GENERAL1_SMALL" + environment_type = "LINUX_CONTAINER" + name = %[1]q + overflow_behavior = "ON_DEMAND" fleet_service_role = aws_iam_role.test.arn vpc_config { @@ -557,11 +557,11 @@ func testAccFleetConfig_vpcConfig2(rName string) string { testAccFleetConfig_baseVPC(rName), fmt.Sprintf(` resource "aws_codebuild_fleet" "test" { - base_capacity = 1 - compute_type = "BUILD_GENERAL1_SMALL" - environment_type = "LINUX_CONTAINER" - name = %[1]q - overflow_behavior = "ON_DEMAND" + base_capacity = 1 + compute_type = "BUILD_GENERAL1_SMALL" + environment_type = "LINUX_CONTAINER" + name = %[1]q + overflow_behavior = "ON_DEMAND" fleet_service_role = aws_iam_role.test.arn vpc_config { From 7f511daebfc5fa57daa60ecc8a3a1f74fcea5ff9 Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Tue, 10 Sep 2024 00:17:26 +0200 Subject: [PATCH 14/18] Fix markdown issues --- website/docs/r/codebuild_fleet.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/codebuild_fleet.html.markdown b/website/docs/r/codebuild_fleet.html.markdown index c0a6de53a0d..a4bc345181b 100644 --- a/website/docs/r/codebuild_fleet.html.markdown +++ b/website/docs/r/codebuild_fleet.html.markdown @@ -50,6 +50,7 @@ The following arguments are required: * `environment_type` - (Required) Environment type of the compute fleet. See [environment types](https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types) for more information and valid values. The following arguments are optional: + * `fleet_service_role` - (Optional) The service role associated with the compute fleet. * `image_id` - (Optional) The Amazon Machine Image (AMI) of the compute fleet. * `overflow_behavior` - (Optional) Overflow behavior for compute fleet. Valid values: `ON_DEMAND`, `QUEUE`. From adb685f44d2eab9c3e84b825a597e8109b4e1714 Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Tue, 10 Sep 2024 00:27:58 +0200 Subject: [PATCH 15/18] Fix errors --- internal/service/codebuild/fleet.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index aaae168f27c..bfbef0cd416 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -362,7 +362,7 @@ func resourceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta inter } if err := waitFleetActive(ctx, conn, d.Id()); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionUpdating, ResNameFleet, d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionWaitingForUpdate, ResNameFleet, d.Id(), err) } return append(diags, resourceFleetRead(ctx, d, meta)...) @@ -386,7 +386,7 @@ func resourceFleetDelete(ctx context.Context, d *schema.ResourceData, meta inter } if err := waitFleetDeleted(ctx, conn, d.Id()); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionDeleting, ResNameFleet, d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionWaitingForDeletion, ResNameFleet, d.Id(), err) } return diags From 20da976819d1a48982fecfb28e2aee5530e491ec Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Tue, 10 Sep 2024 00:45:01 +0200 Subject: [PATCH 16/18] Fix constants --- internal/service/codebuild/fleet.go | 36 +++++++++---------- .../service/codebuild/fleet_data_source.go | 26 +++++++------- .../codebuild/fleet_data_source_test.go | 6 ++-- internal/service/codebuild/fleet_test.go | 34 +++++++++--------- internal/service/codebuild/project_test.go | 8 ++--- .../service/codebuild/service_package_gen.go | 2 +- 6 files changed, 56 insertions(+), 56 deletions(-) diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index bfbef0cd416..9d75e6ee20e 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -39,7 +39,7 @@ func ResourceFleet() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "arn": { + names.AttrARN: { Type: schema.TypeString, Computed: true, }, @@ -67,7 +67,7 @@ func ResourceFleet() *schema.Resource { Optional: true, ValidateFunc: verify.ValidARN, }, - "id": { + names.AttrID: { Type: schema.TypeString, Computed: true, }, @@ -79,7 +79,7 @@ func ResourceFleet() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "name": { + names.AttrName: { Type: schema.TypeString, Required: true, ForceNew: true, @@ -107,7 +107,7 @@ func ResourceFleet() *schema.Resource { Type: schema.TypeInt, Computed: true, }, - "max_capacity": { + names.AttrMaxCapacity: { Type: schema.TypeInt, Optional: true, ValidateFunc: validation.IntAtLeast(1), @@ -138,7 +138,7 @@ func ResourceFleet() *schema.Resource { }, }, }, - "status": { + names.AttrStatus: { Type: schema.TypeSet, Computed: true, Elem: &schema.Resource{ @@ -147,11 +147,11 @@ func ResourceFleet() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "message": { + names.AttrMessage: { Type: schema.TypeString, Computed: true, }, - "status_code": { + names.AttrStatusCode: { Type: schema.TypeString, Computed: true, }, @@ -206,7 +206,7 @@ func resourceFleetCreate(ctx context.Context, d *schema.ResourceData, meta inter BaseCapacity: aws.Int32(int32(d.Get("base_capacity").(int))), ComputeType: types.ComputeType(d.Get("compute_type").(string)), EnvironmentType: types.EnvironmentType(d.Get("environment_type").(string)), - Name: aws.String(d.Get("name").(string)), + Name: aws.String(d.Get(names.AttrName).(string)), Tags: getTagsIn(ctx), } @@ -236,7 +236,7 @@ func resourceFleetCreate(ctx context.Context, d *schema.ResourceData, meta inter }, "ot authorized to perform") if err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, ResNameFleet, d.Get("name").(string), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, ResNameFleet, d.Get(names.AttrName).(string), err) } d.SetId(aws.ToString(outputRaw.(*codebuild.CreateFleetOutput).Fleet.Arn)) @@ -267,16 +267,16 @@ func resourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interfa fleet := out.Fleets[0] - d.Set("arn", fleet.Arn) + d.Set(names.AttrARN, fleet.Arn) d.Set("base_capacity", fleet.BaseCapacity) d.Set("compute_type", fleet.ComputeType) d.Set("created", fleet.Created.String()) d.Set("environment_type", fleet.EnvironmentType) d.Set("fleet_service_role", fleet.FleetServiceRole) - d.Set("id", fleet.Id) + d.Set(names.AttrID, fleet.Id) d.Set("image_id", fleet.ImageId) d.Set("last_modified", fleet.LastModified.String()) - d.Set("name", fleet.Name) + d.Set(names.AttrName, fleet.Name) d.Set("overflow_behavior", fleet.OverflowBehavior) if fleet.ScalingConfiguration != nil { @@ -288,11 +288,11 @@ func resourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interfa } if fleet.Status != nil { - if err := d.Set("status", []interface{}{flattenStatus(fleet.Status)}); err != nil { + if err := d.Set(names.AttrStatus, []interface{}{flattenStatus(fleet.Status)}); err != nil { return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, ResNameFleet, d.Id(), err) } } else { - d.Set("status", nil) + d.Set(names.AttrStatus, nil) } if err := d.Set(names.AttrVPCConfig, flattenVPCConfig(fleet.VpcConfig)); err != nil { @@ -465,7 +465,7 @@ func expandScalingConfiguration(tfMap map[string]interface{}) *types.ScalingConf apiObject := &types.ScalingConfigurationInput{} - if v, ok := tfMap["max_capacity"].(int); ok { + if v, ok := tfMap[names.AttrMaxCapacity].(int); ok { apiObject.MaxCapacity = aws.Int32(int32(v)) } @@ -532,7 +532,7 @@ func flattenScalingConfiguration(apiObject *types.ScalingConfigurationOutput) ma } if v := apiObject.MaxCapacity; v != nil { - tfMap["max_capacity"] = aws.ToInt32(v) + tfMap[names.AttrMaxCapacity] = aws.ToInt32(v) } if v := apiObject.ScalingType; v != "" { @@ -587,11 +587,11 @@ func flattenStatus(apiObject *types.FleetStatus) map[string]interface{} { } if v := apiObject.Message; v != nil { - tfMap["message"] = aws.ToString(v) + tfMap[names.AttrMessage] = aws.ToString(v) } if v := apiObject.StatusCode; v != "" { - tfMap["status_code"] = v + tfMap[names.AttrStatusCode] = v } return tfMap diff --git a/internal/service/codebuild/fleet_data_source.go b/internal/service/codebuild/fleet_data_source.go index 7f952d85fc4..92ace4901bd 100644 --- a/internal/service/codebuild/fleet_data_source.go +++ b/internal/service/codebuild/fleet_data_source.go @@ -22,7 +22,7 @@ func DataSourceFleet() *schema.Resource { ReadWithoutTimeout: dataSourceFleetRead, Schema: map[string]*schema.Schema{ - "arn": { + names.AttrARN: { Type: schema.TypeString, Computed: true, }, @@ -46,7 +46,7 @@ func DataSourceFleet() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "id": { + names.AttrID: { Type: schema.TypeString, Computed: true, }, @@ -58,7 +58,7 @@ func DataSourceFleet() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "name": { + names.AttrName: { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringLenBetween(2, 128), @@ -76,7 +76,7 @@ func DataSourceFleet() *schema.Resource { Type: schema.TypeInt, Computed: true, }, - "max_capacity": { + names.AttrMaxCapacity: { Type: schema.TypeInt, Computed: true, }, @@ -103,7 +103,7 @@ func DataSourceFleet() *schema.Resource { }, }, }, - "status": { + names.AttrStatus: { Type: schema.TypeSet, Computed: true, Elem: &schema.Resource{ @@ -112,18 +112,18 @@ func DataSourceFleet() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "message": { + names.AttrMessage: { Type: schema.TypeString, Computed: true, }, - "status_code": { + names.AttrStatusCode: { Type: schema.TypeString, Computed: true, }, }, }, }, - "tags": tftags.TagsSchemaComputed(), + names.AttrTags: tftags.TagsSchemaComputed(), names.AttrVPCConfig: { Type: schema.TypeList, Computed: true, @@ -158,7 +158,7 @@ func dataSourceFleetRead(ctx context.Context, d *schema.ResourceData, meta inter var diags diag.Diagnostics conn := meta.(*conns.AWSClient).CodeBuildClient(ctx) - name := d.Get("name").(string) + name := d.Get(names.AttrName).(string) out, err := findFleetByARNOrNames(ctx, conn, name) if err != nil { @@ -169,7 +169,7 @@ func dataSourceFleetRead(ctx context.Context, d *schema.ResourceData, meta inter d.SetId(aws.StringValue(fleet.Arn)) - d.Set("arn", fleet.Arn) + d.Set(names.AttrARN, fleet.Arn) d.Set("base_capacity", fleet.BaseCapacity) d.Set("compute_type", fleet.ComputeType) d.Set("created", fleet.Created.String()) @@ -178,7 +178,7 @@ func dataSourceFleetRead(ctx context.Context, d *schema.ResourceData, meta inter d.Set("image_id", fleet.ImageId) d.Set("last_modified", fleet.LastModified.String()) d.Set("overflow_behavior", fleet.OverflowBehavior) - d.Set("name", fleet.Name) + d.Set(names.AttrName, fleet.Name) if fleet.ScalingConfiguration != nil { if err := d.Set("scaling_configuration", []interface{}{flattenScalingConfiguration(fleet.ScalingConfiguration)}); err != nil { @@ -186,7 +186,7 @@ func dataSourceFleetRead(ctx context.Context, d *schema.ResourceData, meta inter } } if fleet.Status != nil { - if err := d.Set("status", []interface{}{flattenStatus(fleet.Status)}); err != nil { + if err := d.Set(names.AttrStatus, []interface{}{flattenStatus(fleet.Status)}); err != nil { return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) } } @@ -197,7 +197,7 @@ func dataSourceFleetRead(ctx context.Context, d *schema.ResourceData, meta inter ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig //lintignore:AWSR002 - if err := d.Set("tags", KeyValueTags(ctx, fleet.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + if err := d.Set(names.AttrTags, KeyValueTags(ctx, fleet.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) } diff --git a/internal/service/codebuild/fleet_data_source_test.go b/internal/service/codebuild/fleet_data_source_test.go index 12db476f98b..7d0f3697a9b 100644 --- a/internal/service/codebuild/fleet_data_source_test.go +++ b/internal/service/codebuild/fleet_data_source_test.go @@ -27,14 +27,14 @@ func TestAccCodeBuildFleetDataSource_basic(t *testing.T) { { Config: testAccFleetDataSourceConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasourceName, names.AttrARN, resourceName, names.AttrARN), resource.TestCheckResourceAttrPair(datasourceName, "base_capacity", resourceName, "base_capacity"), resource.TestCheckResourceAttrPair(datasourceName, "compute_type", resourceName, "compute_type"), resource.TestCheckResourceAttrPair(datasourceName, "created", resourceName, "created"), resource.TestCheckResourceAttrPair(datasourceName, "environment_type", resourceName, "environment_type"), - resource.TestCheckResourceAttrPair(datasourceName, "id", resourceName, "id"), + resource.TestCheckResourceAttrPair(datasourceName, names.AttrID, resourceName, names.AttrID), resource.TestCheckResourceAttrPair(datasourceName, "last_modified", resourceName, "last_modified"), - resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(datasourceName, names.AttrName, resourceName, names.AttrName), resource.TestCheckResourceAttrPair(datasourceName, "overflow_behavior", resourceName, "overflow_behavior"), resource.TestCheckResourceAttrPair(datasourceName, "scaling_configuration.0.max_capacity", resourceName, "scaling_configuration.0.max_capacity"), resource.TestCheckResourceAttrPair(datasourceName, "scaling_configuration.0.scaling_type", resourceName, "scaling_configuration.0.scaling_type"), diff --git a/internal/service/codebuild/fleet_test.go b/internal/service/codebuild/fleet_test.go index 4bb3b78b60b..fcdb7d87d3a 100644 --- a/internal/service/codebuild/fleet_test.go +++ b/internal/service/codebuild/fleet_test.go @@ -35,10 +35,10 @@ func TestAccCodeBuildFleet_basic(t *testing.T) { Config: testAccFleetConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckFleetExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), + resource.TestCheckResourceAttr(resourceName, "base_capacity", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "compute_type", "BUILD_GENERAL1_SMALL"), resource.TestCheckResourceAttr(resourceName, "environment_type", "LINUX_CONTAINER"), - resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), resource.TestCheckResourceAttr(resourceName, "overflow_behavior", "ON_DEMAND"), ), }, @@ -86,28 +86,28 @@ func TestAccCodeBuildFleet_tags(t *testing.T) { CheckDestroy: testAccCheckFleetDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccFleetConfig_tags1(rName, "key1", "value1"), + Config: testAccFleetConfig_tags1(rName, acctest.CtKey1, acctest.CtValue1), Check: resource.ComposeTestCheckFunc( testAccCheckFleetExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1), ), }, { - Config: testAccFleetConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), + Config: testAccFleetConfig_tags2(rName, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, acctest.CtValue2), Check: resource.ComposeTestCheckFunc( testAccCheckFleetExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), - resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1Updated), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), ), }, { - Config: testAccFleetConfig_tags1(rName, "key2", "value2"), + Config: testAccFleetConfig_tags1(rName, acctest.CtKey2, acctest.CtValue2), Check: resource.ComposeTestCheckFunc( testAccCheckFleetExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), ), }, }, @@ -129,14 +129,14 @@ func TestAccCodeBuildFleet_baseCapacity(t *testing.T) { Config: testAccFleetConfig_baseCapacity(rName, 1), Check: resource.ComposeTestCheckFunc( testAccCheckFleetExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "base_capacity", "1"), + resource.TestCheckResourceAttr(resourceName, "base_capacity", acctest.Ct1), ), }, { Config: testAccFleetConfig_baseCapacity(rName, 2), Check: resource.ComposeTestCheckFunc( testAccCheckFleetExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "base_capacity", "2"), + resource.TestCheckResourceAttr(resourceName, "base_capacity", acctest.Ct2), ), }, }, @@ -285,7 +285,7 @@ func TestAccCodeBuildFleet_vpcConfig(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "vpc_config.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnets.#", acctest.Ct1), - resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.subnets.0", "aws_subnet.test.1", "id"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.subnets.0", "aws_subnet.test.1", names.AttrID), resource.TestMatchResourceAttr(resourceName, "vpc_config.0.vpc_id", regexache.MustCompile(`^vpc-`)), ), }, @@ -301,7 +301,7 @@ func TestAccCodeBuildFleet_vpcConfig(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "vpc_config.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnets.#", acctest.Ct1), - resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.subnets.0", "aws_subnet.test.0", "id"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.subnets.0", "aws_subnet.test.0", names.AttrID), resource.TestMatchResourceAttr(resourceName, "vpc_config.0.vpc_id", regexache.MustCompile(`^vpc-`)), ), }, @@ -353,7 +353,7 @@ func testAccCheckFleetExists(ctx context.Context, n string) resource.TestCheckFu return fmt.Errorf("Fleet not found: %s", rs.Primary.ID) } - expectedName := rs.Primary.Attributes["name"] + expectedName := rs.Primary.Attributes[names.AttrName] if *fleet.Fleets[0].Name != expectedName { return fmt.Errorf("Fleet name mismatch, expected: %s, got: %s", expectedName, *fleet.Fleets[0].Name) } diff --git a/internal/service/codebuild/project_test.go b/internal/service/codebuild/project_test.go index 599abdac2a8..ca76c1dc418 100644 --- a/internal/service/codebuild/project_test.go +++ b/internal/service/codebuild/project_test.go @@ -2883,11 +2883,11 @@ func TestAccCodeBuildProject_fleet(t *testing.T) { Config: testAccProjectConfig_fleet(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckProjectExists(ctx, resourceName, &project), - acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "codebuild", fmt.Sprintf("project/%s", rName)), + acctest.CheckResourceAttrRegionalARN(resourceName, names.AttrARN, "codebuild", fmt.Sprintf("project/%s", rName)), resource.TestCheckResourceAttr(resourceName, "environment.0.compute_type", string(types.ComputeTypeBuildGeneral1Small)), - resource.TestCheckResourceAttr(resourceName, "environment.0.fleet.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "environment.0.fleet.0.fleet_arn", fleetResourceName, "arn"), - resource.TestCheckResourceAttrPair(resourceName, "service_role", roleResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "environment.0.fleet.#", acctest.Ct1), + resource.TestCheckResourceAttrPair(resourceName, "environment.0.fleet.0.fleet_arn", fleetResourceName, names.AttrARN), + resource.TestCheckResourceAttrPair(resourceName, names.AttrServiceRole, roleResourceName, names.AttrARN), ), }, }, diff --git a/internal/service/codebuild/service_package_gen.go b/internal/service/codebuild/service_package_gen.go index cde0622b48e..afdb5574606 100644 --- a/internal/service/codebuild/service_package_gen.go +++ b/internal/service/codebuild/service_package_gen.go @@ -39,7 +39,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka TypeName: "aws_codebuild_fleet", Name: "Fleet", Tags: &types.ServicePackageResourceTags{ - IdentifierAttribute: "arn", + IdentifierAttribute: names.AttrARN, }, }, { From 3425319fad00580d5afd190141a3523f6cba669f Mon Sep 17 00:00:00 2001 From: Kamil Turek Date: Tue, 10 Sep 2024 21:59:46 +0200 Subject: [PATCH 17/18] Remove delete waiter and ignore fleet pending deletion --- internal/service/codebuild/fleet.go | 64 +++++++++---------- .../service/codebuild/fleet_data_source.go | 5 +- internal/service/codebuild/fleet_test.go | 11 ++-- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index 9d75e6ee20e..a5fc8780a04 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -253,19 +253,19 @@ func resourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interfa conn := meta.(*conns.AWSClient).CodeBuildClient(ctx) - out, err := findFleetByARNOrNames(ctx, conn, d.Id()) + fleets, err := findFleetByARNOrNames(ctx, conn, d.Id(), true) - if out == nil || len(out.Fleets) == 0 { + if err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionReading, ResNameFleet, d.Id(), err) + } + + if len(fleets) == 0 { log.Printf("[WARN] CodeBuild Fleet (%s) not found, removing from state", d.Id()) d.SetId("") return diags } - if err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionReading, ResNameFleet, d.Id(), err) - } - - fleet := out.Fleets[0] + fleet := fleets[0] d.Set(names.AttrARN, fleet.Arn) d.Set("base_capacity", fleet.BaseCapacity) @@ -385,10 +385,6 @@ func resourceFleetDelete(ctx context.Context, d *schema.ResourceData, meta inter return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionDeleting, ResNameFleet, d.Id(), err) } - if err := waitFleetDeleted(ctx, conn, d.Id()); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionWaitingForDeletion, ResNameFleet, d.Id(), err) - } - return diags } @@ -409,24 +405,9 @@ func waitFleetActive(ctx context.Context, conn *codebuild.Client, id string) err return err } -func waitFleetDeleted(ctx context.Context, conn *codebuild.Client, id string) error { - stateConf := &retry.StateChangeConf{ - Pending: enum.Slice(types.FleetStatusCodeDeleting, "PENDING_DELETION"), - Target: []string{}, - Refresh: statusFleet(ctx, conn, id, true), - Timeout: 90 * time.Minute, - MinTimeout: 15 * time.Second, - Delay: 15 * time.Second, - } - - _, err := stateConf.WaitForStateContext(ctx) - - return err -} - func statusFleet(ctx context.Context, conn *codebuild.Client, id string, delete bool) retry.StateRefreshFunc { return func() (interface{}, string, error) { - out, err := findFleetByARNOrNames(ctx, conn, id) + fleets, err := findFleetByARNOrNames(ctx, conn, id, true) if tfresource.NotFound(err) && delete { return nil, "", nil } @@ -434,28 +415,45 @@ func statusFleet(ctx context.Context, conn *codebuild.Client, id string, delete return nil, "", err } - return out, string(out.Fleets[0].Status.StatusCode), nil + return fleets, string(fleets[0].Status.StatusCode), nil } } -func findFleetByARNOrNames(ctx context.Context, conn *codebuild.Client, arn string) (*codebuild.BatchGetFleetsOutput, error) { +func findFleetByARNOrNames(ctx context.Context, conn *codebuild.Client, arn string, skipPendingDeletion bool) ([]types.Fleet, error) { input := &codebuild.BatchGetFleetsInput{ Names: []string{arn}, } output, err := conn.BatchGetFleets(ctx, input) - if output == nil || len(output.Fleets) == 0 || len(output.FleetsNotFound) > 0 { + if err != nil { + return nil, err + } + + if output == nil || len(output.FleetsNotFound) > 0 { return nil, &retry.NotFoundError{ LastError: err, LastRequest: input, } } - if err != nil { - return nil, err + fleets := []types.Fleet{} + + for _, fleet := range output.Fleets { + if skipPendingDeletion && fleet.Status.StatusCode == types.FleetStatusCodePendingDeletion { + continue + } + + fleets = append(fleets, fleet) + } + + if len(fleets) == 0 { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } } - return output, nil + return fleets, nil } func expandScalingConfiguration(tfMap map[string]interface{}) *types.ScalingConfigurationInput { diff --git a/internal/service/codebuild/fleet_data_source.go b/internal/service/codebuild/fleet_data_source.go index 92ace4901bd..1256c711925 100644 --- a/internal/service/codebuild/fleet_data_source.go +++ b/internal/service/codebuild/fleet_data_source.go @@ -160,12 +160,13 @@ func dataSourceFleetRead(ctx context.Context, d *schema.ResourceData, meta inter conn := meta.(*conns.AWSClient).CodeBuildClient(ctx) name := d.Get(names.AttrName).(string) - out, err := findFleetByARNOrNames(ctx, conn, name) + fleets, err := findFleetByARNOrNames(ctx, conn, name, true) + if err != nil { return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionReading, DSNameFleet, name, err) } - fleet := out.Fleets[0] + fleet := fleets[0] d.SetId(aws.StringValue(fleet.Arn)) diff --git a/internal/service/codebuild/fleet_test.go b/internal/service/codebuild/fleet_test.go index fcdb7d87d3a..13dad86c885 100644 --- a/internal/service/codebuild/fleet_test.go +++ b/internal/service/codebuild/fleet_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/codebuild/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -318,7 +319,7 @@ func testAccCheckFleetDestroy(ctx context.Context) resource.TestCheckFunc { continue } - _, err := tfcodebuild.FindFleetByARNOrNames(ctx, conn, rs.Primary.ID) + _, err := tfcodebuild.FindFleetByARNOrNames(ctx, conn, rs.Primary.ID, true) if tfresource.NotFound(err) { continue @@ -344,18 +345,18 @@ func testAccCheckFleetExists(ctx context.Context, n string) resource.TestCheckFu conn := acctest.Provider.Meta().(*conns.AWSClient).CodeBuildClient(ctx) - fleet, err := tfcodebuild.FindFleetByARNOrNames(ctx, conn, rs.Primary.ID) + fleets, err := tfcodebuild.FindFleetByARNOrNames(ctx, conn, rs.Primary.ID, true) if err != nil { return err } - if len(fleet.Fleets) == 0 { + if len(fleets) == 0 { return fmt.Errorf("Fleet not found: %s", rs.Primary.ID) } expectedName := rs.Primary.Attributes[names.AttrName] - if *fleet.Fleets[0].Name != expectedName { - return fmt.Errorf("Fleet name mismatch, expected: %s, got: %s", expectedName, *fleet.Fleets[0].Name) + if aws.ToString(fleets[0].Name) != expectedName { + return fmt.Errorf("Fleet name mismatch, expected: %s, got: %s", expectedName, aws.ToString(fleets[0].Name)) } return nil From cd78a3c1b216aebd6ee6254398489c1e96b8406a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 11 Sep 2024 11:28:48 -0400 Subject: [PATCH 18/18] r/aws_codebuild_fleet: Tidy up. --- internal/service/codebuild/exports_test.go | 3 +- internal/service/codebuild/fleet.go | 241 +++++++++++------- .../service/codebuild/fleet_data_source.go | 32 +-- internal/service/codebuild/fleet_test.go | 19 +- .../service/codebuild/service_package_gen.go | 4 +- 5 files changed, 176 insertions(+), 123 deletions(-) diff --git a/internal/service/codebuild/exports_test.go b/internal/service/codebuild/exports_test.go index 90231179d60..aeadfb7c054 100644 --- a/internal/service/codebuild/exports_test.go +++ b/internal/service/codebuild/exports_test.go @@ -5,13 +5,14 @@ package codebuild // Exports for use in tests only. var ( + ResourceFleet = resourceFleet ResourceProject = resourceProject ResourceReportGroup = resourceReportGroup ResourceResourcePolicy = resourceResourcePolicy ResourceSourceCredential = resourceSourceCredential ResourceWebhook = resourceWebhook - FindFleetByARNOrNames = findFleetByARNOrNames + FindFleetByARN = findFleetByARN FindProjectByNameOrARN = findProjectByNameOrARN FindReportGroupByARN = findReportGroupByARN FindResourcePolicyByARN = findResourcePolicyByARN diff --git a/internal/service/codebuild/fleet.go b/internal/service/codebuild/fleet.go index a5fc8780a04..6e9fe8e9207 100644 --- a/internal/service/codebuild/fleet.go +++ b/internal/service/codebuild/fleet.go @@ -5,6 +5,7 @@ package codebuild import ( "context" + "errors" "log" "time" @@ -27,7 +28,7 @@ import ( // @SDKResource("aws_codebuild_fleet", name="Fleet") // @Tags(identifierAttribute="arn") -func ResourceFleet() *schema.Resource { +func resourceFleet() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceFleetCreate, ReadWithoutTimeout: resourceFleetRead, @@ -194,7 +195,7 @@ func ResourceFleet() *schema.Resource { } const ( - ResNameFleet = "Fleet" + resNameFleet = "Fleet" ) func resourceFleetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -236,13 +237,16 @@ func resourceFleetCreate(ctx context.Context, d *schema.ResourceData, meta inter }, "ot authorized to perform") if err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, ResNameFleet, d.Get(names.AttrName).(string), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionCreating, resNameFleet, d.Get(names.AttrName).(string), err) } d.SetId(aws.ToString(outputRaw.(*codebuild.CreateFleetOutput).Fleet.Arn)) - if err := waitFleetActive(ctx, conn, d.Id()); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionWaitingForCreation, ResNameFleet, d.Id(), err) + const ( + timeout = 20 * time.Minute + ) + if _, err := waitFleetCreated(ctx, conn, d.Id(), timeout); err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionWaitingForCreation, resNameFleet, d.Id(), err) } return append(diags, resourceFleetRead(ctx, d, meta)...) @@ -253,50 +257,45 @@ func resourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interfa conn := meta.(*conns.AWSClient).CodeBuildClient(ctx) - fleets, err := findFleetByARNOrNames(ctx, conn, d.Id(), true) + fleet, err := findFleetByARN(ctx, conn, d.Id()) - if err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionReading, ResNameFleet, d.Id(), err) - } - - if len(fleets) == 0 { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] CodeBuild Fleet (%s) not found, removing from state", d.Id()) d.SetId("") return diags } - fleet := fleets[0] + if err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionReading, resNameFleet, d.Id(), err) + } d.Set(names.AttrARN, fleet.Arn) d.Set("base_capacity", fleet.BaseCapacity) d.Set("compute_type", fleet.ComputeType) - d.Set("created", fleet.Created.String()) + d.Set("created", aws.ToTime(fleet.Created).Format(time.RFC3339)) d.Set("environment_type", fleet.EnvironmentType) d.Set("fleet_service_role", fleet.FleetServiceRole) d.Set(names.AttrID, fleet.Id) d.Set("image_id", fleet.ImageId) - d.Set("last_modified", fleet.LastModified.String()) + d.Set("last_modified", aws.ToTime(fleet.LastModified).Format(time.RFC3339)) d.Set(names.AttrName, fleet.Name) d.Set("overflow_behavior", fleet.OverflowBehavior) - if fleet.ScalingConfiguration != nil { if err := d.Set("scaling_configuration", []interface{}{flattenScalingConfiguration(fleet.ScalingConfiguration)}); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, ResNameFleet, d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, resNameFleet, d.Id(), err) } } else { d.Set("scaling_configuration", nil) } - if fleet.Status != nil { if err := d.Set(names.AttrStatus, []interface{}{flattenStatus(fleet.Status)}); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, ResNameFleet, d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, resNameFleet, d.Id(), err) } } else { d.Set(names.AttrStatus, nil) } - if err := d.Set(names.AttrVPCConfig, flattenVPCConfig(fleet.VpcConfig)); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, ResNameFleet, d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, resNameFleet, d.Id(), err) } setTagsOut(ctx, fleet.Tags) @@ -351,18 +350,19 @@ func resourceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta inter input.Tags = getTagsIn(ctx) - log.Printf("[DEBUG] Updating CodeBuild Fleet (%s): %#v", d.Id(), input) - _, err := tfresource.RetryWhenIsAErrorMessageContains[*types.InvalidInputException](ctx, propagationTimeout, func() (interface{}, error) { return conn.UpdateFleet(ctx, input) }, "ot authorized to perform") if err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionUpdating, ResNameFleet, d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionUpdating, resNameFleet, d.Id(), err) } - if err := waitFleetActive(ctx, conn, d.Id()); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionWaitingForUpdate, ResNameFleet, d.Id(), err) + const ( + timeout = 20 * time.Minute + ) + if _, err := waitFleetUpdated(ctx, conn, d.Id(), timeout); err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionWaitingForUpdate, resNameFleet, d.Id(), err) } return append(diags, resourceFleetRead(ctx, d, meta)...) @@ -372,8 +372,7 @@ func resourceFleetDelete(ctx context.Context, d *schema.ResourceData, meta inter var diags diag.Diagnostics conn := meta.(*conns.AWSClient).CodeBuildClient(ctx) - log.Printf("[INFO] Deleting CodeBuild Fleet %s", d.Id()) - + log.Printf("[INFO] Deleting CodeBuild Fleet: %s", d.Id()) _, err := conn.DeleteFleet(ctx, &codebuild.DeleteFleetInput{ Arn: aws.String(d.Id()), }) @@ -381,79 +380,145 @@ func resourceFleetDelete(ctx context.Context, d *schema.ResourceData, meta inter if errs.IsA[*types.ResourceNotFoundException](err) { return diags } + if err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionDeleting, ResNameFleet, d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionDeleting, resNameFleet, d.Id(), err) + } + + const ( + timeout = 20 * time.Minute + ) + if _, err := waitFleetDeleted(ctx, conn, d.Id(), timeout); err != nil { + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionWaitingForDeletion, resNameFleet, d.Id(), err) } return diags } -func waitFleetActive(ctx context.Context, conn *codebuild.Client, id string) error { - stateConf := &retry.StateChangeConf{ - Pending: enum.Slice(types.FleetStatusCodeCreating, types.FleetStatusCodeUpdating, types.FleetStatusCodeDeleting, types.FleetStatusCodeRotating), - Target: enum.Slice(types.FleetStatusCodeActive), - Refresh: statusFleet(ctx, conn, id, false), - Timeout: 20 * time.Minute, - MinTimeout: 15 * time.Second, - Delay: 15 * time.Second, - NotFoundChecks: 20, - ContinuousTargetOccurence: 2, +func findFleetByARN(ctx context.Context, conn *codebuild.Client, arn string) (*types.Fleet, error) { + input := &codebuild.BatchGetFleetsInput{ + Names: []string{arn}, + } + + output, err := findFleet(ctx, conn, input) + + if err != nil { + return nil, err + } + + if statusCode := output.Status.StatusCode; statusCode == types.FleetStatusCodePendingDeletion { + return nil, &retry.NotFoundError{ + Message: string(statusCode), + LastRequest: input, + } + } + + return output, nil +} + +func findFleet(ctx context.Context, conn *codebuild.Client, input *codebuild.BatchGetFleetsInput) (*types.Fleet, error) { + output, err := findFleets(ctx, conn, input) + + if err != nil { + return nil, err } - _, err := stateConf.WaitForStateContext(ctx) + return tfresource.AssertSingleValueResult(output, func(v *types.Fleet) bool { + return v.Status != nil + }) +} + +func findFleets(ctx context.Context, conn *codebuild.Client, input *codebuild.BatchGetFleetsInput) ([]types.Fleet, error) { + output, err := conn.BatchGetFleets(ctx, input) - return err + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.Fleets, nil } -func statusFleet(ctx context.Context, conn *codebuild.Client, id string, delete bool) retry.StateRefreshFunc { +func statusFleet(ctx context.Context, conn *codebuild.Client, arn string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - fleets, err := findFleetByARNOrNames(ctx, conn, id, true) - if tfresource.NotFound(err) && delete { + output, err := findFleetByARN(ctx, conn, arn) + + if tfresource.NotFound(err) { return nil, "", nil } + if err != nil { return nil, "", err } - return fleets, string(fleets[0].Status.StatusCode), nil + return output, string(output.Status.StatusCode), nil } } -func findFleetByARNOrNames(ctx context.Context, conn *codebuild.Client, arn string, skipPendingDeletion bool) ([]types.Fleet, error) { - input := &codebuild.BatchGetFleetsInput{ - Names: []string{arn}, +func waitFleetCreated(ctx context.Context, conn *codebuild.Client, arn string, timeout time.Duration) (*types.Fleet, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(types.FleetStatusCodeCreating, types.FleetStatusCodeRotating), + Target: enum.Slice(types.FleetStatusCodeActive), + Refresh: statusFleet(ctx, conn, arn), + Timeout: timeout, + MinTimeout: 15 * time.Second, + Delay: 15 * time.Second, } - output, err := conn.BatchGetFleets(ctx, input) - if err != nil { - return nil, err + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*types.Fleet); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.Status.Message))) + + return output, err } - if output == nil || len(output.FleetsNotFound) > 0 { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } + return nil, err +} + +func waitFleetUpdated(ctx context.Context, conn *codebuild.Client, arn string, timeout time.Duration) (*types.Fleet, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(types.FleetStatusCodeUpdating, types.FleetStatusCodeRotating), + Target: enum.Slice(types.FleetStatusCodeActive), + Refresh: statusFleet(ctx, conn, arn), + Timeout: timeout, + MinTimeout: 15 * time.Second, + Delay: 15 * time.Second, } - fleets := []types.Fleet{} + outputRaw, err := stateConf.WaitForStateContext(ctx) - for _, fleet := range output.Fleets { - if skipPendingDeletion && fleet.Status.StatusCode == types.FleetStatusCodePendingDeletion { - continue - } + if output, ok := outputRaw.(*types.Fleet); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.Status.Message))) - fleets = append(fleets, fleet) + return output, err } - if len(fleets) == 0 { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } + return nil, err +} + +func waitFleetDeleted(ctx context.Context, conn *codebuild.Client, arn string, timeout time.Duration) (*types.Fleet, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(types.FleetStatusCodeDeleting), + Target: []string{}, + Refresh: statusFleet(ctx, conn, arn), + Timeout: timeout, + MinTimeout: 15 * time.Second, + Delay: 15 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*types.Fleet); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.Status.Message))) + + return output, err } - return fleets, nil + return nil, err } func expandScalingConfiguration(tfMap map[string]interface{}) *types.ScalingConfigurationInput { @@ -487,13 +552,11 @@ func expandTargetTrackingScalingConfigs(tfList []interface{}) []types.TargetTrac for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) - if !ok { continue } apiObject := expandTargetTrackingScalingConfig(tfMap) - if apiObject == nil { continue } @@ -522,24 +585,26 @@ func expandTargetTrackingScalingConfig(tfMap map[string]interface{}) *types.Targ } func flattenScalingConfiguration(apiObject *types.ScalingConfigurationOutput) map[string]interface{} { + if apiObject == nil { + return nil + } + tfMap := map[string]interface{}{} - if apiObject != nil { - if v := apiObject.DesiredCapacity; v != nil { - tfMap["desired_capacity"] = aws.ToInt32(v) - } + if v := apiObject.DesiredCapacity; v != nil { + tfMap["desired_capacity"] = aws.ToInt32(v) + } - if v := apiObject.MaxCapacity; v != nil { - tfMap[names.AttrMaxCapacity] = aws.ToInt32(v) - } + if v := apiObject.MaxCapacity; v != nil { + tfMap[names.AttrMaxCapacity] = aws.ToInt32(v) + } - if v := apiObject.ScalingType; v != "" { - tfMap["scaling_type"] = v - } + if v := apiObject.ScalingType; v != "" { + tfMap["scaling_type"] = v + } - if v := apiObject.TargetTrackingScalingConfigs; v != nil { - tfMap["target_tracking_scaling_configs"] = flattenTargetTrackingScalingConfigs(v) - } + if v := apiObject.TargetTrackingScalingConfigs; v != nil { + tfMap["target_tracking_scaling_configs"] = flattenTargetTrackingScalingConfigs(v) } return tfMap @@ -550,16 +615,20 @@ func flattenTargetTrackingScalingConfigs(apiObjects []types.TargetTrackingScalin return nil } - var tfMaps []interface{} + var tfList []interface{} for _, apiObject := range apiObjects { - tfMaps = append(tfMaps, flattenTargetTrackingScalingConfig(apiObject)) + tfList = append(tfList, flattenTargetTrackingScalingConfig(&apiObject)) } - return tfMaps + return tfList } -func flattenTargetTrackingScalingConfig(apiObject types.TargetTrackingScalingConfiguration) map[string]interface{} { +func flattenTargetTrackingScalingConfig(apiObject *types.TargetTrackingScalingConfiguration) map[string]interface{} { + if apiObject == nil { + return nil + } + tfMap := map[string]interface{}{} if v := apiObject.MetricType; v != "" { diff --git a/internal/service/codebuild/fleet_data_source.go b/internal/service/codebuild/fleet_data_source.go index 1256c711925..1506426c727 100644 --- a/internal/service/codebuild/fleet_data_source.go +++ b/internal/service/codebuild/fleet_data_source.go @@ -5,8 +5,9 @@ package codebuild import ( "context" + "time" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -17,7 +18,7 @@ import ( ) // @SDKDataSource("aws_codebuild_fleet", name="Fleet") -func DataSourceFleet() *schema.Resource { +func dataSourceFleet() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceFleetRead, @@ -151,7 +152,7 @@ func DataSourceFleet() *schema.Resource { } const ( - DSNameFleet = "Fleet Data Source" + dsNameFleet = "Fleet Data Source" ) func dataSourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -160,46 +161,41 @@ func dataSourceFleetRead(ctx context.Context, d *schema.ResourceData, meta inter conn := meta.(*conns.AWSClient).CodeBuildClient(ctx) name := d.Get(names.AttrName).(string) - fleets, err := findFleetByARNOrNames(ctx, conn, name, true) + fleet, err := findFleetByARN(ctx, conn, name) if err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionReading, DSNameFleet, name, err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionReading, dsNameFleet, name, err) } - fleet := fleets[0] - - d.SetId(aws.StringValue(fleet.Arn)) - + d.SetId(aws.ToString(fleet.Arn)) d.Set(names.AttrARN, fleet.Arn) d.Set("base_capacity", fleet.BaseCapacity) d.Set("compute_type", fleet.ComputeType) - d.Set("created", fleet.Created.String()) + d.Set("created", aws.ToTime(fleet.Created).Format(time.RFC3339)) d.Set("environment_type", fleet.EnvironmentType) d.Set("fleet_service_role", fleet.FleetServiceRole) d.Set("image_id", fleet.ImageId) - d.Set("last_modified", fleet.LastModified.String()) - d.Set("overflow_behavior", fleet.OverflowBehavior) + d.Set("last_modified", aws.ToTime(fleet.LastModified).Format(time.RFC3339)) d.Set(names.AttrName, fleet.Name) - + d.Set("overflow_behavior", fleet.OverflowBehavior) if fleet.ScalingConfiguration != nil { if err := d.Set("scaling_configuration", []interface{}{flattenScalingConfiguration(fleet.ScalingConfiguration)}); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, dsNameFleet, d.Id(), err) } } if fleet.Status != nil { if err := d.Set(names.AttrStatus, []interface{}{flattenStatus(fleet.Status)}); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, dsNameFleet, d.Id(), err) } } - if err := d.Set(names.AttrVPCConfig, flattenVPCConfig(fleet.VpcConfig)); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, dsNameFleet, d.Id(), err) } ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig //lintignore:AWSR002 if err := d.Set(names.AttrTags, KeyValueTags(ctx, fleet.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, DSNameFleet, d.Id(), err) + return create.AppendDiagError(diags, names.CodeBuild, create.ErrActionSetting, dsNameFleet, d.Id(), err) } return diags diff --git a/internal/service/codebuild/fleet_test.go b/internal/service/codebuild/fleet_test.go index 13dad86c885..f70627f808e 100644 --- a/internal/service/codebuild/fleet_test.go +++ b/internal/service/codebuild/fleet_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/codebuild/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -319,7 +318,7 @@ func testAccCheckFleetDestroy(ctx context.Context) resource.TestCheckFunc { continue } - _, err := tfcodebuild.FindFleetByARNOrNames(ctx, conn, rs.Primary.ID, true) + _, err := tfcodebuild.FindFleetByARN(ctx, conn, rs.Primary.ID) if tfresource.NotFound(err) { continue @@ -345,21 +344,9 @@ func testAccCheckFleetExists(ctx context.Context, n string) resource.TestCheckFu conn := acctest.Provider.Meta().(*conns.AWSClient).CodeBuildClient(ctx) - fleets, err := tfcodebuild.FindFleetByARNOrNames(ctx, conn, rs.Primary.ID, true) - if err != nil { - return err - } - - if len(fleets) == 0 { - return fmt.Errorf("Fleet not found: %s", rs.Primary.ID) - } + _, err := tfcodebuild.FindFleetByARN(ctx, conn, rs.Primary.ID) - expectedName := rs.Primary.Attributes[names.AttrName] - if aws.ToString(fleets[0].Name) != expectedName { - return fmt.Errorf("Fleet name mismatch, expected: %s, got: %s", expectedName, aws.ToString(fleets[0].Name)) - } - - return nil + return err } } diff --git a/internal/service/codebuild/service_package_gen.go b/internal/service/codebuild/service_package_gen.go index afdb5574606..ee483570cdb 100644 --- a/internal/service/codebuild/service_package_gen.go +++ b/internal/service/codebuild/service_package_gen.go @@ -25,7 +25,7 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePackageSDKDataSource { return []*types.ServicePackageSDKDataSource{ { - Factory: DataSourceFleet, + Factory: dataSourceFleet, TypeName: "aws_codebuild_fleet", Name: "Fleet", }, @@ -35,7 +35,7 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePackageSDKResource { return []*types.ServicePackageSDKResource{ { - Factory: ResourceFleet, + Factory: resourceFleet, TypeName: "aws_codebuild_fleet", Name: "Fleet", Tags: &types.ServicePackageResourceTags{