From afacc47f00752b0056210ba2192842c3b8c0a9d1 Mon Sep 17 00:00:00 2001 From: Aidan Mundy Date: Tue, 12 Dec 2023 12:19:05 -0500 Subject: [PATCH] HPR-1513: Introduce `version_fingerprint` alias for `hcp_packer_channel_assignment.iteration_fingerprint` (#136) * Add `version_fingerprint` attribute * Add migration/compatibility tests * Run `make gencheck` * Deprecate `iteration_fingerprint` * Update related changelogs --- .changelog/679.txt | 6 +- .changelog/tbd_HPR-1513.txt | 9 + docs/resources/packer_channel_assignment.md | 9 +- .../data_source_packer_image_test.go | 6 +- .../data_source_packer_iteration_test.go | 4 +- .../resource_packer_channel_assignment.go | 96 +++++--- ...resource_packer_channel_assignment_test.go | 220 +++++++++++++----- .../providersdkv2/test_helpers_packer_test.go | 2 +- 8 files changed, 249 insertions(+), 103 deletions(-) create mode 100644 .changelog/tbd_HPR-1513.txt diff --git a/.changelog/679.txt b/.changelog/679.txt index c784a845c..22dfd4511 100644 --- a/.changelog/679.txt +++ b/.changelog/679.txt @@ -1,11 +1,11 @@ ```release-note:breaking-change -Removed the `data.hcp_packer_iteration.incremental_version` attribute. Use the `fingerprint`, `id` or `uuid` attributes to reference iterations instead. +`data.hcp_packer_iteration`: Removed the `incremental_version` attribute. Use the `fingerprint`, `id` or `uuid` attributes instead. ``` ```release-note:breaking-change -Removed the `hcp_packer_channel_assignment.iteration_version` attribute. Use the `iteration_fingerprint` attribute to reference iterations instead. +`hcp_packer_channel_assignment`: Removed the `iteration_version` attribute. Use the `version_fingerprint` attribute instead. ``` ```release-note:breaking-change -Removed the `hcp_packer_channel_assignment.iteration_id` attribute. Use the `iteration_fingerprint` attribute to reference iterations instead. +`hcp_packer_channel_assignment`: Removed the `iteration_id` attribute. Use the `version_fingerprint` attribute instead. ``` diff --git a/.changelog/tbd_HPR-1513.txt b/.changelog/tbd_HPR-1513.txt new file mode 100644 index 000000000..47fa6a467 --- /dev/null +++ b/.changelog/tbd_HPR-1513.txt @@ -0,0 +1,9 @@ +```release-note:deprecation +`hcp_packer_channel_assignment`: The `iteration_fingerprint` attribute is now deprecated and will be removed in a future release. +Refer to the `version_fingerprint` attribute release notes for more information. +``` + +```release-note:improvement +`hcp_packer_channel_assignment`: The `iteration_fingerprint` attribute has been renamed to `version_fingerprint`. The attribute functionality has not changed. +During the deprecation period for `iteration_fingerprint`, either attribute may be used. +``` diff --git a/docs/resources/packer_channel_assignment.md b/docs/resources/packer_channel_assignment.md index 6047b7db3..496347184 100644 --- a/docs/resources/packer_channel_assignment.md +++ b/docs/resources/packer_channel_assignment.md @@ -2,12 +2,12 @@ page_title: "hcp_packer_channel_assignment Resource - terraform-provider-hcp" subcategory: "HCP Packer" description: |- - The Packer Channel Assignment resource allows you to manage the iteration assigned to a bucket channel in an active HCP Packer Registry. + The Packer Channel Assignment resource allows you to manage the version assigned to a channel in an active HCP Packer Registry. --- # hcp_packer_channel_assignment (Resource) -The Packer Channel Assignment resource allows you to manage the iteration assigned to a bucket channel in an active HCP Packer Registry. +The Packer Channel Assignment resource allows you to manage the version assigned to a channel in an active HCP Packer Registry. ## Example Usage @@ -39,16 +39,17 @@ resource "hcp_packer_channel_assignment" "staging" { ### Required -- `bucket_name` (String) The slug of the HCP Packer Registry bucket where the channel is located. +- `bucket_name` (String) The slug of the HCP Packer bucket where the channel is located. - `channel_name` (String) The name of the HCP Packer channel being managed. -- `iteration_fingerprint` (String) The fingerprint of the iteration assigned to the channel. ### Optional +- `iteration_fingerprint` (String, Deprecated) The fingerprint of the version assigned to the channel. - `project_id` (String) The ID of the HCP project where the channel is located. If not specified, the project specified in the HCP Provider config block will be used, if configured. If a project is not configured in the HCP Provider config block, the oldest project in the organization will be used. - `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) +- `version_fingerprint` (String) The fingerprint of the version assigned to the channel. ### Read-Only diff --git a/internal/providersdkv2/data_source_packer_image_test.go b/internal/providersdkv2/data_source_packer_image_test.go index 51cd40e37..58b0ef70e 100644 --- a/internal/providersdkv2/data_source_packer_image_test.go +++ b/internal/providersdkv2/data_source_packer_image_test.go @@ -60,7 +60,7 @@ func TestAcc_dataSourcePackerImage_Simple(t *testing.T) { testAccPreCheck(t, map[string]bool{"aws": false, "azure": false}) upsertRegistry(t) upsertBucket(t, bucketSlug) - iteration, build = upsertCompleteIteration(t, bucketSlug, "1234", &buildOptions) + iteration, build = upsertCompleteVersion(t, bucketSlug, "1234", &buildOptions) upsertChannel(t, bucketSlug, channelSlug, iteration.ID) }, ProtoV6ProviderFactories: testProtoV6ProviderFactories, @@ -121,7 +121,7 @@ func TestAcc_dataSourcePackerImage_IterationID(t *testing.T) { testAccPreCheck(t, map[string]bool{"aws": false, "azure": false}) upsertRegistry(t) upsertBucket(t, bucketSlug) - iteration, build = upsertCompleteIteration(t, bucketSlug, "1234", &buildOptions) + iteration, build = upsertCompleteVersion(t, bucketSlug, "1234", &buildOptions) upsertChannel(t, bucketSlug, channelSlug, iteration.ID) }, ProtoV6ProviderFactories: testProtoV6ProviderFactories, @@ -166,7 +166,7 @@ func TestAcc_dataSourcePackerImage_revokedIteration(t *testing.T) { testAccPreCheck(t, map[string]bool{"aws": false, "azure": false}) upsertRegistry(t) upsertBucket(t, bucketSlug) - iteration, build = upsertCompleteIteration(t, bucketSlug, "1234", &buildOptions) + iteration, build = upsertCompleteVersion(t, bucketSlug, "1234", &buildOptions) upsertChannel(t, bucketSlug, channelSlug, iteration.ID) revokeIteration(t, iteration.ID, bucketSlug, revokeAt) }, diff --git a/internal/providersdkv2/data_source_packer_iteration_test.go b/internal/providersdkv2/data_source_packer_iteration_test.go index 49338da35..cc275450b 100644 --- a/internal/providersdkv2/data_source_packer_iteration_test.go +++ b/internal/providersdkv2/data_source_packer_iteration_test.go @@ -27,7 +27,7 @@ func TestAcc_dataSourcePackerIteration_Simple(t *testing.T) { testAccPreCheck(t, map[string]bool{"aws": false, "azure": false}) upsertRegistry(t) upsertBucket(t, bucketSlug) - iteration, _ = upsertCompleteIteration(t, bucketSlug, "1234", nil) + iteration, _ = upsertCompleteVersion(t, bucketSlug, "1234", nil) upsertChannel(t, bucketSlug, channelSlug, iteration.ID) }, ProtoV6ProviderFactories: testProtoV6ProviderFactories, @@ -72,7 +72,7 @@ func TestAcc_dataSourcePackerIteration_revokedIteration(t *testing.T) { testAccPreCheck(t, map[string]bool{"aws": false, "azure": false}) upsertRegistry(t) upsertBucket(t, bucketSlug) - unrevokedIteration, _ := upsertCompleteIteration(t, bucketSlug, "1234", nil) + unrevokedIteration, _ := upsertCompleteVersion(t, bucketSlug, "1234", nil) upsertChannel(t, bucketSlug, channelSlug, unrevokedIteration.ID) revokedIteration = revokeIteration(t, unrevokedIteration.ID, bucketSlug, revokeAt) }, diff --git a/internal/providersdkv2/resource_packer_channel_assignment.go b/internal/providersdkv2/resource_packer_channel_assignment.go index 581cafbd6..24b0a1460 100644 --- a/internal/providersdkv2/resource_packer_channel_assignment.go +++ b/internal/providersdkv2/resource_packer_channel_assignment.go @@ -17,17 +17,18 @@ import ( "github.com/hashicorp/terraform-provider-hcp/internal/clients" ) -// This string is used to represent an unassigned (or "null") channel -// assignment for iteration identifiers of the String type +// This string is used as the version fingerprint to represent an unassigned +// (or "null") channel assignment const unassignString string = "none" func resourcePackerChannelAssignment() *schema.Resource { return &schema.Resource{ - Description: "The Packer Channel Assignment resource allows you to manage the iteration assigned to a bucket channel in an active HCP Packer Registry.", + Description: "The Packer Channel Assignment resource allows you to manage the version assigned to a channel in an active HCP Packer Registry.", CreateContext: resourcePackerChannelAssignmentCreate, DeleteContext: resourcePackerChannelAssignmentDelete, ReadContext: resourcePackerChannelAssignmentRead, UpdateContext: resourcePackerChannelAssignmentUpdate, + CustomizeDiff: resourcePackerChannelAssignmentCustomizeDiff, Timeouts: &schema.ResourceTimeout{ Create: &defaultPackerTimeout, Default: &defaultPackerTimeout, @@ -47,27 +48,39 @@ func resourcePackerChannelAssignment() *schema.Resource { ValidateDiagFunc: validateSlugID, }, "bucket_name": { - Description: "The slug of the HCP Packer Registry bucket where the channel is located.", + Description: "The slug of the HCP Packer bucket where the channel is located.", Type: schema.TypeString, Required: true, ForceNew: true, ValidateDiagFunc: validateSlugID, }, + // Optional inputs "iteration_fingerprint": { - Description: "The fingerprint of the iteration assigned to the channel.", + Description: "The fingerprint of the version assigned to the channel.", Type: schema.TypeString, - Required: true, + Optional: true, + Computed: true, + ExactlyOneOf: []string{"iteration_fingerprint", "version_fingerprint"}, + ValidateFunc: validation.StringIsNotEmpty, + Deprecated: "This attribute has been renamed to 'version_fingerprint`. The `iteration_fingerprint` attribute is deprecated and will be removed in a future release. " + + "The `iteration_fingerprint` attribute will act as an alias for `version_fingerprint` during the deprecation period.", + }, + "version_fingerprint": { + Description: "The fingerprint of the version assigned to the channel.", + Type: schema.TypeString, + Optional: true, + Computed: true, + ExactlyOneOf: []string{"iteration_fingerprint", "version_fingerprint"}, ValidateFunc: validation.StringIsNotEmpty, }, - // Optional inputs "project_id": { Description: ` The ID of the HCP project where the channel is located. If not specified, the project specified in the HCP Provider config block will be used, if configured. If a project is not configured in the HCP Provider config block, the oldest project in the organization will be used.`, Type: schema.TypeString, - Computed: true, Optional: true, + Computed: true, ForceNew: true, ValidateFunc: validation.IsUUID, }, @@ -104,7 +117,7 @@ func resourcePackerChannelAssignmentRead(ctx context.Context, d *schema.Resource }} } - if err := setPackerChannelAssignmentIterationData(d, channel.Iteration); err != nil { + if err := setPackerChannelAssignmentVersionData(d, channel.Iteration); err != nil { return diag.FromErr(err) } @@ -132,28 +145,31 @@ func resourcePackerChannelAssignmentCreate(ctx context.Context, d *schema.Resour } else if channel.Managed { return diag.Diagnostics{diag.Diagnostic{ Severity: diag.Error, - Summary: fmt.Sprintf("HCP Packer channel with (channel_name %q) (bucket_name %q) (project_id %q) is managed by HCP Packer and cannot have an iteration assigned by Terraform.", channelName, bucketName, loc.ProjectID), + Summary: fmt.Sprintf("HCP Packer channel with (channel_name %q) (bucket_name %q) (project_id %q) is managed by HCP Packer and cannot have a version assigned by Terraform.", channelName, bucketName, loc.ProjectID), }} - } else if iteration := channel.Iteration; iteration != nil && (iteration.IncrementalVersion > 0 || iteration.ID != "" || iteration.Fingerprint != "") { + } else if version := channel.Iteration; version != nil && (version.IncrementalVersion > 0 || version.ID != "" || version.Fingerprint != "") { return diag.Diagnostics{diag.Diagnostic{ Severity: diag.Error, - Summary: fmt.Sprintf("HCP Packer channel with (channel_name %q) (bucket_name %q) (project_id %q) already has an assigned iteration.", channelName, bucketName, loc.ProjectID), - Detail: "To adopt this resource into Terraform, use `terraform import`, or remove the channel's assigned iteration using the HCP Packer GUI/API.", + Summary: fmt.Sprintf("HCP Packer channel with (channel_name %q) (bucket_name %q) (project_id %q) already has an assigned version.", channelName, bucketName, loc.ProjectID), + Detail: "To adopt this resource into Terraform, use `terraform import`, or remove the channel's assigned version using the HCP Packer GUI/API.", }} } - iterFingerprint := d.Get("iteration_fingerprint").(string) - if iterFingerprint == unassignString { - iterFingerprint = "" + versionFingerprint := d.Get("version_fingerprint").(string) + if versionFingerprint == "" { + versionFingerprint = d.Get("iteration_fingerprint").(string) + } + if versionFingerprint == unassignString { + versionFingerprint = "" } - updatedChannel, err := clients.UpdatePackerChannelAssignment(ctx, client, loc, bucketName, channelName, iterFingerprint) + updatedChannel, err := clients.UpdatePackerChannelAssignment(ctx, client, loc, bucketName, channelName, versionFingerprint) if err != nil { return diag.FromErr(err) } d.SetId(updatedChannel.ID) - if err := setPackerChannelAssignmentIterationData(d, updatedChannel.Iteration); err != nil { + if err := setPackerChannelAssignmentVersionData(d, updatedChannel.Iteration); err != nil { return diag.FromErr(err) } @@ -170,17 +186,20 @@ func resourcePackerChannelAssignmentUpdate(ctx context.Context, d *schema.Resour bucketName := d.Get("bucket_name").(string) channelName := d.Get("channel_name").(string) - iterFingerpint := d.Get("iteration_fingerprint").(string) - if iterFingerpint == unassignString { - iterFingerpint = "" + versionFingerprint := d.Get("version_fingerprint").(string) + if versionFingerprint == "" { + versionFingerprint = d.Get("iteration_fingerprint").(string) + } + if versionFingerprint == unassignString { + versionFingerprint = "" } - updatedChannel, err := clients.UpdatePackerChannelAssignment(ctx, client, loc, bucketName, channelName, iterFingerpint) + updatedChannel, err := clients.UpdatePackerChannelAssignment(ctx, client, loc, bucketName, channelName, versionFingerprint) if err != nil { return diag.FromErr(err) } - if err := setPackerChannelAssignmentIterationData(d, updatedChannel.Iteration); err != nil { + if err := setPackerChannelAssignmentVersionData(d, updatedChannel.Iteration); err != nil { return diag.FromErr(err) } @@ -266,36 +285,53 @@ func resourcePackerChannelAssignmentImport(ctx context.Context, d *schema.Resour } else if channel == nil || channel.Slug == "" { return nil, fmt.Errorf("HCP Packer channel with (channel_name %q) (bucket_name %q) (project_id %q) not found", channelName, bucketName, loc.ProjectID) } else if channel.Managed { - return nil, fmt.Errorf("HCP Packer channel with (channel_name %q) (bucket_name %q) (project_id %q) is managed by HCP Packer and cannot have an iteration assigned by Terraform", channelName, bucketName, loc.ProjectID) + return nil, fmt.Errorf("HCP Packer channel with (channel_name %q) (bucket_name %q) (project_id %q) is managed by HCP Packer and cannot have a version assigned by Terraform", channelName, bucketName, loc.ProjectID) } d.SetId(channel.ID) - if err := setPackerChannelAssignmentIterationData(d, channel.Iteration); err != nil { + if err := setPackerChannelAssignmentVersionData(d, channel.Iteration); err != nil { return nil, err } return []*schema.ResourceData{d}, nil } -func setPackerChannelAssignmentIterationData(d *schema.ResourceData, i *packermodels.HashicorpCloudPackerIteration) error { - var iteration packermodels.HashicorpCloudPackerIteration +func setPackerChannelAssignmentVersionData(d *schema.ResourceData, i *packermodels.HashicorpCloudPackerIteration) error { + var version packermodels.HashicorpCloudPackerIteration if i == nil { - iteration = packermodels.HashicorpCloudPackerIteration{ + version = packermodels.HashicorpCloudPackerIteration{ Fingerprint: "", } } else { - iteration = *i + version = *i } - fingerprint := iteration.Fingerprint + fingerprint := version.Fingerprint if fingerprint == "" { fingerprint = unassignString } + if err := d.Set("version_fingerprint", fingerprint); err != nil { + return err + } if err := d.Set("iteration_fingerprint", fingerprint); err != nil { return err } return nil } + +func resourcePackerChannelAssignmentCustomizeDiff(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + if rawFingerprint, ok := d.GetOk("version_fingerprint"); ok && d.HasChange("version_fingerprint") && d.NewValueKnown("version_fingerprint") { + if err := d.SetNew("iteration_fingerprint", rawFingerprint.(string)); err != nil { + return err + } + } else if rawFingerprint, ok := d.GetOk("iteration_fingerprint"); ok && d.HasChange("iteration_fingerprint") && d.NewValueKnown("iteration_fingerprint") { + if err := d.SetNew("version_fingerprint", rawFingerprint.(string)); err != nil { + return err + } + } + + return nil +} diff --git a/internal/providersdkv2/resource_packer_channel_assignment_test.go b/internal/providersdkv2/resource_packer_channel_assignment_test.go index d9d3b7134..b85b316be 100644 --- a/internal/providersdkv2/resource_packer_channel_assignment_test.go +++ b/internal/providersdkv2/resource_packer_channel_assignment_test.go @@ -18,9 +18,9 @@ import ( func TestAccPackerChannelAssignment_SimpleSetUnset(t *testing.T) { bucketSlug := testAccCreateSlug("AssignmentSimpleSetUnset") channelSlug := bucketSlug // No need for a different slug - iterationFingerprint := "1" + versionFingerprint := "1" - var iteration *models.HashicorpCloudPackerIteration + var version *models.HashicorpCloudPackerIteration baseAssignment := testAccPackerAssignmentBuilderBase("SimpleSetUnset", fmt.Sprintf("%q", bucketSlug), fmt.Sprintf("%q", channelSlug)) @@ -30,7 +30,7 @@ func TestAccPackerChannelAssignment_SimpleSetUnset(t *testing.T) { upsertRegistry(t) upsertBucket(t, bucketSlug) upsertChannel(t, bucketSlug, channelSlug, "") - iteration, _ = upsertCompleteIteration(t, bucketSlug, iterationFingerprint, nil) + version, _ = upsertCompleteVersion(t, bucketSlug, versionFingerprint, nil) }, ProtoV6ProviderFactories: testProtoV6ProviderFactories, CheckDestroy: func(state *terraform.State) error { @@ -41,14 +41,14 @@ func TestAccPackerChannelAssignment_SimpleSetUnset(t *testing.T) { return nil }, Steps: []resource.TestStep{ - { // Set channel assignment to the iteration + { // Set channel assignment to the version Config: testConfig(testAccConfigBuildersToString(testAccPackerAssignmentBuilderFromAssignment( baseAssignment, - fmt.Sprintf("%q", iterationFingerprint), + fmt.Sprintf("%q", versionFingerprint), ))), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAssignmentStateBucketAndChannelName(baseAssignment.BlockName(), bucketSlug, channelSlug), - testAccCheckAssignmentStateMatchesIteration(baseAssignment.BlockName(), &iteration), + testAccCheckAssignmentStateMatchesVersion(baseAssignment.BlockName(), &version), testAccCheckAssignmentStateMatchesAPI(baseAssignment.BlockName()), ), }, @@ -65,7 +65,7 @@ func TestAccPackerChannelAssignment_SimpleSetUnset(t *testing.T) { ))), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAssignmentStateBucketAndChannelName(baseAssignment.BlockName(), bucketSlug, channelSlug), - testAccCheckAssignmentStateMatchesIteration(baseAssignment.BlockName(), nil), + testAccCheckAssignmentStateMatchesVersion(baseAssignment.BlockName(), nil), testAccCheckAssignmentStateMatchesAPI(baseAssignment.BlockName()), ), }, @@ -85,7 +85,7 @@ func TestAccPackerChannelAssignment_AssignLatest(t *testing.T) { uniqueName := "AssignLatest" // This config creates a data source that is read before apply time - beforeIteration := testAccPackerDataIterationBuilder( + beforeVersion := testAccPackerDataIterationBuilder( uniqueName, fmt.Sprintf("%q", bucketSlug), `"latest"`, @@ -93,12 +93,12 @@ func TestAccPackerChannelAssignment_AssignLatest(t *testing.T) { beforeChannel := testAccPackerChannelBuilderBase( uniqueName, fmt.Sprintf("%q", channelSlug), - beforeIteration.AttributeRef("bucket_name"), + beforeVersion.AttributeRef("bucket_name"), ) beforeAssignment := testAccPackerAssignmentBuilderWithChannelReference( uniqueName, beforeChannel, - beforeIteration.AttributeRef("fingerprint"), + beforeVersion.AttributeRef("fingerprint"), ) // This config creates a data source that is read after apply time, @@ -108,7 +108,7 @@ func TestAccPackerChannelAssignment_AssignLatest(t *testing.T) { fmt.Sprintf("%q", channelSlug), fmt.Sprintf("%q", bucketSlug), ) - afterIteration := testAccPackerDataIterationBuilder( + afterVersion := testAccPackerDataIterationBuilder( uniqueName, afterChannel.AttributeRef("bucket_name"), `"latest"`, @@ -116,17 +116,17 @@ func TestAccPackerChannelAssignment_AssignLatest(t *testing.T) { afterAssignment := testAccPackerAssignmentBuilderWithChannelReference( uniqueName, afterChannel, - afterIteration.AttributeRef("fingerprint"), + afterVersion.AttributeRef("fingerprint"), ) - var iteration *models.HashicorpCloudPackerIteration + var version *models.HashicorpCloudPackerIteration - generateStep := func(iterationData, channelResource, assignmentResource testAccConfigBuilderInterface) resource.TestStep { + generateStep := func(versionData, channelResource, assignmentResource testAccConfigBuilderInterface) resource.TestStep { return resource.TestStep{ - Config: testConfig(testAccConfigBuildersToString(iterationData, channelResource, assignmentResource)), + Config: testConfig(testAccConfigBuildersToString(versionData, channelResource, assignmentResource)), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAssignmentStateBucketAndChannelName(assignmentResource.BlockName(), bucketSlug, channelSlug), - testAccCheckAssignmentStateMatchesIteration(assignmentResource.BlockName(), &iteration), + testAccCheckAssignmentStateMatchesVersion(assignmentResource.BlockName(), &version), testAccCheckAssignmentStateMatchesChannelState(assignmentResource.BlockName(), channelResource.BlockName()), testAccCheckAssignmentStateMatchesAPI(assignmentResource.BlockName()), ), @@ -138,7 +138,7 @@ func TestAccPackerChannelAssignment_AssignLatest(t *testing.T) { testAccPreCheck(t, map[string]bool{"aws": false, "azure": false}) upsertRegistry(t) upsertBucket(t, bucketSlug) - iteration, _ = upsertCompleteIteration(t, bucketSlug, "abc", nil) + version, _ = upsertCompleteVersion(t, bucketSlug, "abc", nil) }, ProtoV6ProviderFactories: testProtoV6ProviderFactories, CheckDestroy: func(state *terraform.State) error { @@ -146,11 +146,11 @@ func TestAccPackerChannelAssignment_AssignLatest(t *testing.T) { return nil }, Steps: []resource.TestStep{ - generateStep(beforeIteration, beforeChannel, beforeAssignment), + generateStep(beforeVersion, beforeChannel, beforeAssignment), { // Remove any resources and data sources completely Config: testConfig(""), }, - generateStep(afterIteration, afterChannel, afterAssignment), + generateStep(afterVersion, afterChannel, afterAssignment), }, }) } @@ -159,13 +159,13 @@ func TestAccPackerChannelAssignment_InvalidInputs(t *testing.T) { bucketSlug := testAccCreateSlug("AssignmentInvalidInputs") channelSlug := bucketSlug // No need for a different slug - generateStep := func(iterFingerprint string, errorRegex string) resource.TestStep { + generateStep := func(fingerprint string, errorRegex string) resource.TestStep { return resource.TestStep{ Config: testConfig(testAccConfigBuildersToString(testAccPackerAssignmentBuilder( "InvalidInputs", fmt.Sprintf("%q", bucketSlug), fmt.Sprintf("%q", channelSlug), - iterFingerprint, + fingerprint, ))), ExpectError: regexp.MustCompile(errorRegex), } @@ -186,12 +186,25 @@ func TestAccPackerChannelAssignment_InvalidInputs(t *testing.T) { Steps: []resource.TestStep{ generateStep( `""`, - `.*expected "iteration_fingerprint" to not be an empty string.*`, + `.*expected "version_fingerprint" to not be an empty string.*`, ), generateStep( `"doesNotExist"`, + //TODO: Update to "version" once the new API is implemented for this resource. `.*iteration with attributes \(fingerprint: doesNotExist\) does not exist.*`, ), + // TODO: Remove once the iteration_fingerprint attribute is removed + { + Config: fmt.Sprintf(` +resource "hcp_packer_channel_assignment" "InvalidInputs" { + bucket_name = %q + channel_name = %q + iteration_fingerprint = "someVersion" + version_fingerprint = "someVersion" +} + `, bucketSlug, channelSlug), + ExpectError: regexp.MustCompile(`.*only one of.*\n.*can be specified.*`), + }, { // Create a dummy non-empty state so that `CheckDestroy` will run. Config: testAccConfigDummyNonemptyState, }, @@ -202,7 +215,7 @@ func TestAccPackerChannelAssignment_InvalidInputs(t *testing.T) { func TestAccPackerChannelAssignment_CreateFailsWhenPreassigned(t *testing.T) { bucketSlug := testAccCreateSlug("AssignmentCreateFailPreassign") channelSlug := bucketSlug // No need for a different slug - iterationFingerprint := "1" + versionFingerprint := "1" channel := testAccPackerChannelBuilderBase( channelSlug, @@ -221,7 +234,7 @@ func TestAccPackerChannelAssignment_CreateFailsWhenPreassigned(t *testing.T) { testAccPreCheck(t, map[string]bool{"aws": false, "azure": false}) upsertRegistry(t) upsertBucket(t, bucketSlug) - upsertCompleteIteration(t, bucketSlug, iterationFingerprint, nil) + upsertCompleteVersion(t, bucketSlug, versionFingerprint, nil) }, ProtoV6ProviderFactories: testProtoV6ProviderFactories, CheckDestroy: func(state *terraform.State) error { @@ -237,11 +250,11 @@ func TestAccPackerChannelAssignment_CreateFailsWhenPreassigned(t *testing.T) { updateChannelAssignment(t, bucketSlug, channelSlug, - &models.HashicorpCloudPackerIteration{Fingerprint: iterationFingerprint}, + &models.HashicorpCloudPackerIteration{Fingerprint: versionFingerprint}, ) }, Config: testConfig(testAccConfigBuildersToString(channel, assignment)), - ExpectError: regexp.MustCompile(".*channel with.*already has an assigned iteration.*"), + ExpectError: regexp.MustCompile(".*channel with.*already has an assigned version.*"), }, }, }) @@ -288,7 +301,7 @@ func TestAccPackerChannelAssignment_HCPManagedChannelErrors(t *testing.T) { } // Test that all attributes generate and successfully apply plans to fix -// the assignment when it is changed OOB from null to a non-null iteration +// the assignment when it is changed OOB from null to a non-null version func TestAccPackerChannelAssignment_EnforceNull(t *testing.T) { bucketSlug := testAccCreateSlug("AssignmentEnforceNull") channelSlug := bucketSlug // No need for a different slug @@ -298,8 +311,8 @@ func TestAccPackerChannelAssignment_EnforceNull(t *testing.T) { fmt.Sprintf("%q", bucketSlug), ) - var iteration1 *models.HashicorpCloudPackerIteration - var iteration2 *models.HashicorpCloudPackerIteration + var version1 *models.HashicorpCloudPackerIteration + var version2 *models.HashicorpCloudPackerIteration baseAssignment := testAccPackerAssignmentBuilderBaseWithChannelReference("EnforceNull", channel) @@ -312,7 +325,7 @@ func TestAccPackerChannelAssignment_EnforceNull(t *testing.T) { checks := resource.ComposeAggregateTestCheckFunc( testAccCheckAssignmentStateBucketAndChannelName(assignment.BlockName(), bucketSlug, channelSlug), - testAccCheckAssignmentStateMatchesIteration(assignment.BlockName(), nil), + testAccCheckAssignmentStateMatchesVersion(assignment.BlockName(), nil), testAccCheckAssignmentStateMatchesChannelState(assignment.BlockName(), channel.BlockName()), testAccCheckAssignmentStateMatchesAPI(assignment.BlockName()), ) @@ -322,10 +335,10 @@ func TestAccPackerChannelAssignment_EnforceNull(t *testing.T) { testAccPreCheck(t, map[string]bool{"aws": false, "azure": false}) upsertRegistry(t) upsertBucket(t, bucketSlug) - // Pushing two iterations so that we can also implicitly verify that - // nullifying the assignment doesn't actually result in a rollback to iteration1 - iteration1, _ = upsertCompleteIteration(t, bucketSlug, "1", nil) - iteration2, _ = upsertCompleteIteration(t, bucketSlug, "2", nil) + // Pushing two versions so that we can also implicitly verify that + // nullifying the assignment doesn't actually result in a rollback to version1 + version1, _ = upsertCompleteVersion(t, bucketSlug, "1", nil) + version2, _ = upsertCompleteVersion(t, bucketSlug, "2", nil) }, ProtoV6ProviderFactories: testProtoV6ProviderFactories, CheckDestroy: func(state *terraform.State) error { @@ -342,8 +355,8 @@ func TestAccPackerChannelAssignment_EnforceNull(t *testing.T) { }, { // Change assignment OOB, then test with assignment set by Terraform PreConfig: func() { - updateChannelAssignment(t, bucketSlug, channelSlug, &models.HashicorpCloudPackerIteration{ID: iteration1.ID}) - updateChannelAssignment(t, bucketSlug, channelSlug, &models.HashicorpCloudPackerIteration{ID: iteration2.ID}) + updateChannelAssignment(t, bucketSlug, channelSlug, &models.HashicorpCloudPackerIteration{ID: version1.ID}) + updateChannelAssignment(t, bucketSlug, channelSlug, &models.HashicorpCloudPackerIteration{ID: version2.ID}) }, Config: config, Check: checks, @@ -352,7 +365,80 @@ func TestAccPackerChannelAssignment_EnforceNull(t *testing.T) { }) } -// An AssignmentBuilder without any iteration fields set. +// TODO: Remove once the iteration_fingerprint attribute is removed +// Test that migration from `iteration_fingerprint` to `version_fingerprint` works properly +func TestAccPackerChannelAssignment_AliasMigration(t *testing.T) { + bucketSlug := testAccCreateSlug("AssignmentAliasMigration") + channelSlug := bucketSlug // No need for a different slug + versionFingerprint := "1" + + var version *models.HashicorpCloudPackerIteration + + baseAssignment := testAccPackerAssignmentBuilderBase("AliasMigration", fmt.Sprintf("%q", bucketSlug), fmt.Sprintf("%q", channelSlug)) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t, map[string]bool{"aws": false, "azure": false}) + upsertRegistry(t) + upsertBucket(t, bucketSlug) + upsertChannel(t, bucketSlug, channelSlug, "") + version, _ = upsertCompleteVersion(t, bucketSlug, versionFingerprint, nil) + }, + ProtoV6ProviderFactories: testProtoV6ProviderFactories, + CheckDestroy: func(state *terraform.State) error { + deleteBucket(t, bucketSlug, true) + return nil + }, + Steps: []resource.TestStep{ + { // Set channel assignment to the version using `iteration_fingerprint` + Config: testConfig(testAccConfigBuildersToString(testAccPackerAssignmentBuilderFromAssignmentWithIterationFingerprint( + baseAssignment, + fmt.Sprintf("%q", versionFingerprint), + ))), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAssignmentStateBucketAndChannelName(baseAssignment.BlockName(), bucketSlug, channelSlug), + testAccCheckAssignmentStateMatchesVersion(baseAssignment.BlockName(), &version), + testAccCheckAssignmentStateMatchesAPI(baseAssignment.BlockName()), + ), + }, + { // Set channel assignment to the version using `version_fingerprint` + Config: testConfig(testAccConfigBuildersToString(testAccPackerAssignmentBuilderFromAssignment( + baseAssignment, + fmt.Sprintf("%q", versionFingerprint), + ))), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAssignmentStateBucketAndChannelName(baseAssignment.BlockName(), bucketSlug, channelSlug), + testAccCheckAssignmentStateMatchesVersion(baseAssignment.BlockName(), &version), + testAccCheckAssignmentStateMatchesAPI(baseAssignment.BlockName()), + ), + }, + { // Set channel assignment to null with `iteration_fingerprint` + Config: testConfig(testAccConfigBuildersToString(testAccPackerAssignmentBuilderFromAssignmentWithIterationFingerprint( + baseAssignment, + fmt.Sprintf("%q", unassignString), + ))), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAssignmentStateBucketAndChannelName(baseAssignment.BlockName(), bucketSlug, channelSlug), + testAccCheckAssignmentStateMatchesVersion(baseAssignment.BlockName(), nil), + testAccCheckAssignmentStateMatchesAPI(baseAssignment.BlockName()), + ), + }, + { // Set channel assignment to null with `version_fingerprint` + Config: testConfig(testAccConfigBuildersToString(testAccPackerAssignmentBuilderFromAssignment( + baseAssignment, + fmt.Sprintf("%q", unassignString), + ))), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAssignmentStateBucketAndChannelName(baseAssignment.BlockName(), bucketSlug, channelSlug), + testAccCheckAssignmentStateMatchesVersion(baseAssignment.BlockName(), nil), + testAccCheckAssignmentStateMatchesAPI(baseAssignment.BlockName()), + ), + }, + }, + }) +} + +// An AssignmentBuilder without any version fields set. // To be used downstream by other assignments to ensure core settings aren't changed. func testAccPackerAssignmentBuilderBase(uniqueName string, bucketName string, channelName string) testAccConfigBuilderInterface { return testAccPackerAssignmentBuilder( @@ -371,33 +457,46 @@ func testAccPackerAssignmentBuilderBaseWithChannelReference(uniqueName string, c ) } -func testAccPackerAssignmentBuilder(uniqueName string, bucketName string, channelName string, iterFingerprint string) testAccConfigBuilderInterface { +func testAccPackerAssignmentBuilder(uniqueName string, bucketName string, channelName string, fingerprint string) testAccConfigBuilderInterface { return &testAccResourceConfigBuilder{ resourceType: "hcp_packer_channel_assignment", uniqueName: uniqueName, attributes: map[string]string{ - "bucket_name": bucketName, - "channel_name": channelName, - "iteration_fingerprint": iterFingerprint, + "bucket_name": bucketName, + "channel_name": channelName, + "version_fingerprint": fingerprint, }, } } -func testAccPackerAssignmentBuilderFromAssignment(oldAssignment testAccConfigBuilderInterface, iterFingerprint string) testAccConfigBuilderInterface { +func testAccPackerAssignmentBuilderFromAssignment(oldAssignment testAccConfigBuilderInterface, fingerprint string) testAccConfigBuilderInterface { return testAccPackerAssignmentBuilder( oldAssignment.UniqueName(), oldAssignment.Attributes()["bucket_name"], oldAssignment.Attributes()["channel_name"], - iterFingerprint, + fingerprint, ) } -func testAccPackerAssignmentBuilderWithChannelReference(uniqueName string, channel testAccConfigBuilderInterface, iterFingerprint string) testAccConfigBuilderInterface { +// TODO: Remove once the iteration_fingerprint attribute is removed +func testAccPackerAssignmentBuilderFromAssignmentWithIterationFingerprint(oldAssignment testAccConfigBuilderInterface, fingerprint string) testAccConfigBuilderInterface { + return &testAccResourceConfigBuilder{ + resourceType: "hcp_packer_channel_assignment", + uniqueName: oldAssignment.UniqueName(), + attributes: map[string]string{ + "bucket_name": oldAssignment.Attributes()["bucket_name"], + "channel_name": oldAssignment.Attributes()["channel_name"], + "iteration_fingerprint": fingerprint, + }, + } +} + +func testAccPackerAssignmentBuilderWithChannelReference(uniqueName string, channel testAccConfigBuilderInterface, fingerprint string) testAccConfigBuilderInterface { return testAccPackerAssignmentBuilder( uniqueName, channel.AttributeRef("bucket_name"), channel.AttributeRef("name"), - iterFingerprint, + fingerprint, ) } @@ -417,29 +516,30 @@ func testAccCheckAssignmentStateMatchesChannelState(assignmentResourceName strin ) } -func testAccCheckAssignmentStateMatchesIteration(resourceName string, iterationPtr **models.HashicorpCloudPackerIteration) resource.TestCheckFunc { +func testAccCheckAssignmentStateMatchesVersion(resourceName string, versionPtr **models.HashicorpCloudPackerIteration) resource.TestCheckFunc { return func(state *terraform.State) error { - var iteration *models.HashicorpCloudPackerIteration - if iterationPtr != nil { - iteration = *iterationPtr + var version *models.HashicorpCloudPackerIteration + if versionPtr != nil { + version = *versionPtr } - if iteration == nil { - iteration = &models.HashicorpCloudPackerIteration{} + if version == nil { + version = &models.HashicorpCloudPackerIteration{} } - iterFingerprint := iteration.Fingerprint - if iterFingerprint == "" { - iterFingerprint = unassignString + versionFingerprint := version.Fingerprint + if versionFingerprint == "" { + versionFingerprint = unassignString } return resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "iteration_fingerprint", iterFingerprint), + resource.TestCheckResourceAttr(resourceName, "version_fingerprint", versionFingerprint), + resource.TestCheckResourceAttr(resourceName, "iteration_fingerprint", versionFingerprint), )(state) } } -func testAccPullIterationFromAPIWithAssignmentState(resourceName string, state *terraform.State) (*models.HashicorpCloudPackerIteration, error) { +func testAccPullVersionFromAPIWithAssignmentState(resourceName string, state *terraform.State) (*models.HashicorpCloudPackerIteration, error) { client := testAccProvider.Meta().(*clients.Client) loc, _ := testAccGetLocationFromState(resourceName, state) @@ -463,20 +563,20 @@ func testAccPullIterationFromAPIWithAssignmentState(resourceName string, state * func testAccCheckAssignmentStateMatchesAPI(resourceName string) resource.TestCheckFunc { return func(state *terraform.State) error { - iteration, err := testAccPullIterationFromAPIWithAssignmentState(resourceName, state) + version, err := testAccPullVersionFromAPIWithAssignmentState(resourceName, state) if err != nil { return err } - return testAccCheckAssignmentStateMatchesIteration(resourceName, &iteration)(state) + return testAccCheckAssignmentStateMatchesVersion(resourceName, &version)(state) } } func testAccCheckAssignmentDestroyed(resourceName string) resource.TestCheckFunc { return func(state *terraform.State) error { - iteration, err := testAccPullIterationFromAPIWithAssignmentState(resourceName, state) + version, err := testAccPullVersionFromAPIWithAssignmentState(resourceName, state) if err != nil { return fmt.Errorf("Unexpected error while validating channel assignment destruction. Got %v", err) - } else if iteration != nil && (iteration.ID != "" || iteration.Fingerprint != "" || iteration.IncrementalVersion != 0) { + } else if version != nil && (version.ID != "" || version.Fingerprint != "" || version.IncrementalVersion != 0) { return fmt.Errorf("Resource %q not properly destroyed", resourceName) } diff --git a/internal/providersdkv2/test_helpers_packer_test.go b/internal/providersdkv2/test_helpers_packer_test.go index ad41d87e0..0ba96b4f9 100644 --- a/internal/providersdkv2/test_helpers_packer_test.go +++ b/internal/providersdkv2/test_helpers_packer_test.go @@ -203,7 +203,7 @@ func upsertIteration(t *testing.T, bucketSlug, fingerprint string) *models.Hashi return nil } -func upsertCompleteIteration(t *testing.T, bucketSlug, fingerprint string, options *buildOptions) (*models.HashicorpCloudPackerIteration, *models.HashicorpCloudPackerBuild) { +func upsertCompleteVersion(t *testing.T, bucketSlug, fingerprint string, options *buildOptions) (*models.HashicorpCloudPackerIteration, *models.HashicorpCloudPackerBuild) { iteration := upsertIteration(t, bucketSlug, fingerprint) if t.Failed() || iteration == nil { return nil, nil