Skip to content

Commit fab400f

Browse files
authored
Merge pull request #44491 from hashicorp/b-datazone-project-sweeper
`datazone` fixes
2 parents 9f3a8e0 + f69fc3b commit fab400f

File tree

18 files changed

+981
-325
lines changed

18 files changed

+981
-325
lines changed

.changelog/44491.txt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
```release-note:bug
2+
resource/aws_datazone_environment: Prevents `unknown value` error when optional `account_identifier` is not specified.
3+
```
4+
5+
```release-note:bug
6+
resource/aws_datazone_environment: Prevents `unknown value` error when optional `account_region` is not specified.
7+
```
8+
9+
```release-note:bug
10+
resource/aws_datazone_environment: Properly passes `blueprint_identifier` on creation.
11+
```
12+
13+
```release-note:bug
14+
resource/aws_datazone_environment: Prevents error when updating.
15+
```
16+
17+
```release-note:bug
18+
resource/aws_datazone_environment: Prevents occasional `unexpected state` error when deleting.
19+
```
20+
21+
```release-note:bug
22+
resource/aws_datazone_environment: Sets values for `user_parameters` when importing.
23+
```
24+
25+
```release-note:bug
26+
resource/aws_datazone_environment: Values in `user_parameters` should not be updateable.
27+
```
28+
29+
```release-note:bug
30+
resource/aws_datazone_environment: Correctly updates `glossary_terms`.
31+
```
32+
33+
```release-note:bug
34+
resource/aws_datazone_project: No longer ignores errors when deleting.
35+
```
36+
37+
```release-note:bug
38+
resource/aws_datazone_project: No longer returns error when already deleting.
39+
```

internal/service/datazone/asset_type_test.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ func TestAccDataZoneAssetType_basic(t *testing.T) {
3131

3232
var assettype datazone.GetAssetTypeOutput
3333
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
34-
pName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
35-
dName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
3634
resourceName := "aws_datazone_asset_type.test"
3735
projectName := "aws_datazone_project.test"
3836
domainName := "aws_datazone_domain.test"
@@ -48,7 +46,7 @@ func TestAccDataZoneAssetType_basic(t *testing.T) {
4846
CheckDestroy: testAccCheckAssetTypeDestroy(ctx),
4947
Steps: []resource.TestStep{
5048
{
51-
Config: testAccAssetTypeConfig_basic(rName, pName, dName),
49+
Config: testAccAssetTypeConfig_basic(rName),
5250
Check: resource.ComposeTestCheckFunc(
5351
testAccCheckAssetTypeExists(ctx, resourceName, &assettype),
5452
resource.TestCheckResourceAttrSet(resourceName, names.AttrCreatedAt),
@@ -79,8 +77,6 @@ func TestAccDataZoneAssetType_disappears(t *testing.T) {
7977

8078
var assettype datazone.GetAssetTypeOutput
8179
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
82-
pName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
83-
dName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
8480
resourceName := "aws_datazone_asset_type.test"
8581

8682
resource.ParallelTest(t, resource.TestCase{
@@ -94,7 +90,7 @@ func TestAccDataZoneAssetType_disappears(t *testing.T) {
9490
CheckDestroy: testAccCheckAssetTypeDestroy(ctx),
9591
Steps: []resource.TestStep{
9692
{
97-
Config: testAccAssetTypeConfig_basic(rName, pName, dName),
93+
Config: testAccAssetTypeConfig_basic(rName),
9894
Check: resource.ComposeTestCheckFunc(
9995
testAccCheckAssetTypeExists(ctx, resourceName, &assettype),
10096
acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfdatazone.ResourceAssetType, resourceName),
@@ -164,8 +160,8 @@ func testAccAssetTypeImportStateIdFunc(resourceName string) resource.ImportState
164160
}
165161
}
166162

167-
func testAccAssetTypeConfig_basic(rName, pName, dName string) string {
168-
return acctest.ConfigCompose(testAccProjectConfig_basic(pName, dName), fmt.Sprintf(`
163+
func testAccAssetTypeConfig_basic(rName string) string {
164+
return acctest.ConfigCompose(testAccProjectConfig_basic(rName), fmt.Sprintf(`
169165
resource "aws_datazone_asset_type" "test" {
170166
description = %[1]q
171167
domain_identifier = aws_datazone_domain.test.id

internal/service/datazone/datazone_test.go

Lines changed: 0 additions & 24 deletions
This file was deleted.

internal/service/datazone/environment.go

Lines changed: 100 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
awstypes "github.com/aws/aws-sdk-go-v2/service/datazone/types"
1616
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
1717
"github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes"
18+
"github.com/hashicorp/terraform-plugin-framework/diag"
1819
"github.com/hashicorp/terraform-plugin-framework/path"
1920
"github.com/hashicorp/terraform-plugin-framework/resource"
2021
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
@@ -142,6 +143,7 @@ func (r *environmentResource) Schema(ctx context.Context, req resource.SchemaReq
142143
CustomType: fwtypes.NewListNestedObjectTypeOf[resourceUserParametersData](ctx),
143144
PlanModifiers: []planmodifier.List{
144145
listplanmodifier.UseStateForUnknown(),
146+
listplanmodifier.RequiresReplace(),
145147
},
146148
NestedObject: schema.NestedBlockObject{
147149
Attributes: map[string]schema.Attribute{
@@ -219,7 +221,7 @@ func (r *environmentResource) Create(ctx context.Context, req resource.CreateReq
219221
return
220222
}
221223

222-
resp.Diagnostics.Append(fwflex.Flatten(ctx, output, &state, fwflex.WithIgnoredFieldNames([]string{"UserParameters"}))...)
224+
flattenEnvironment(ctx, output, &state, &resp.Diagnostics)
223225
if resp.Diagnostics.HasError() {
224226
return
225227
}
@@ -252,21 +254,14 @@ func (r *environmentResource) Read(ctx context.Context, req resource.ReadRequest
252254
return
253255
}
254256

255-
resp.Diagnostics.Append(fwflex.Flatten(ctx, out, &state, fwflex.WithIgnoredFieldNamesAppend("UserParameters"),
256-
fwflex.WithFieldNamePrefix("Environment"),
257-
)...)
258-
257+
flattenEnvironment(ctx, out, &state, &resp.Diagnostics)
259258
if resp.Diagnostics.HasError() {
260259
return
261260
}
262261

263-
state.AccountIdentifier = fwflex.StringToFramework(ctx, out.AwsAccountId)
264-
state.AccountRegion = fwflex.StringToFramework(ctx, out.AwsAccountRegion)
265-
state.ProjectIdentifier = fwflex.StringToFramework(ctx, out.ProjectId)
266-
state.ProfileIdentifier = fwflex.StringToFramework(ctx, out.EnvironmentProfileId)
267-
268262
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
269263
}
264+
270265
func (r *environmentResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
271266
conn := r.Meta().DataZoneClient(ctx)
272267

@@ -277,18 +272,35 @@ func (r *environmentResource) Update(ctx context.Context, req resource.UpdateReq
277272
return
278273
}
279274

280-
if !plan.Name.Equal(state.Name) ||
281-
!plan.Description.Equal(state.Description) ||
282-
!plan.GlossaryTerms.Equal(state.GlossaryTerms) {
283-
in := &datazone.UpdateEnvironmentInput{}
284-
resp.Diagnostics.Append(fwflex.Expand(ctx, plan, in)...)
275+
var (
276+
needsUpdate bool
277+
)
278+
input := datazone.UpdateEnvironmentInput{
279+
DomainIdentifier: plan.DomainIdentifier.ValueStringPointer(),
280+
Identifier: plan.Id.ValueStringPointer(),
281+
}
282+
283+
if !plan.Name.Equal(state.Name) {
284+
needsUpdate = true
285+
input.Name = plan.Name.ValueStringPointer()
286+
}
285287

286-
if resp.Diagnostics.HasError() {
288+
if !plan.Description.Equal(state.Description) {
289+
needsUpdate = true
290+
input.Description = plan.Description.ValueStringPointer()
291+
}
292+
293+
if !plan.GlossaryTerms.Equal(state.GlossaryTerms) {
294+
needsUpdate = true
295+
d := fwflex.Expand(ctx, &plan.GlossaryTerms, &input.GlossaryTerms)
296+
resp.Diagnostics.Append(d...)
297+
if d.HasError() {
287298
return
288299
}
289-
in.Identifier = state.Id.ValueStringPointer()
300+
}
290301

291-
out, err := conn.UpdateEnvironment(ctx, in)
302+
if needsUpdate {
303+
out, err := conn.UpdateEnvironment(ctx, &input)
292304
if err != nil {
293305
resp.Diagnostics.AddError(
294306
create.ProblemStandardMessage(names.DataZone, create.ErrActionUpdating, ResNameEnvironment, plan.Id.String(), err),
@@ -306,7 +318,7 @@ func (r *environmentResource) Update(ctx context.Context, req resource.UpdateReq
306318
}
307319

308320
updateTimeout := r.UpdateTimeout(ctx, plan.Timeouts)
309-
_, err = waitEnvironmentUpdated(ctx, conn, plan.DomainIdentifier.ValueString(), plan.Id.ValueString(), updateTimeout)
321+
output, err := waitEnvironmentUpdated(ctx, conn, plan.DomainIdentifier.ValueString(), plan.Id.ValueString(), updateTimeout)
310322

311323
if err != nil {
312324
resp.Diagnostics.AddError(
@@ -315,6 +327,11 @@ func (r *environmentResource) Update(ctx context.Context, req resource.UpdateReq
315327
)
316328
return
317329
}
330+
331+
flattenEnvironment(ctx, output, &plan, &resp.Diagnostics)
332+
if resp.Diagnostics.HasError() {
333+
return
334+
}
318335
}
319336

320337
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
@@ -375,8 +392,8 @@ func (r *environmentResource) ImportState(ctx context.Context, req resource.Impo
375392

376393
func waitEnvironmentCreated(ctx context.Context, conn *datazone.Client, domainId string, id string, timeout time.Duration) (*datazone.GetEnvironmentOutput, error) {
377394
stateConf := &retry.StateChangeConf{
378-
Pending: enum.Slice[awstypes.EnvironmentStatus](awstypes.EnvironmentStatusCreating),
379-
Target: enum.Slice[awstypes.EnvironmentStatus](awstypes.EnvironmentStatusActive),
395+
Pending: enum.Slice(awstypes.EnvironmentStatusCreating),
396+
Target: enum.Slice(awstypes.EnvironmentStatusActive),
380397
Refresh: statusEnvironment(ctx, conn, domainId, id),
381398
Timeout: timeout,
382399
NotFoundChecks: 20,
@@ -385,7 +402,7 @@ func waitEnvironmentCreated(ctx context.Context, conn *datazone.Client, domainId
385402

386403
outputRaw, err := stateConf.WaitForStateContext(ctx)
387404
if out, ok := outputRaw.(*datazone.GetEnvironmentOutput); ok {
388-
if status, deployment := out.Status, out.LastDeployment; status == awstypes.EnvironmentStatusCreateFailed && deployment != nil {
405+
if status, deployment := out.Status, out.LastDeployment; (status == awstypes.EnvironmentStatusCreateFailed || status == awstypes.EnvironmentStatusValidationFailed) && deployment != nil {
389406
tfresource.SetLastError(err, fmt.Errorf("%s: %s", status, aws.ToString(deployment.FailureReason.Message)))
390407
}
391408
return out, err
@@ -396,8 +413,8 @@ func waitEnvironmentCreated(ctx context.Context, conn *datazone.Client, domainId
396413

397414
func waitEnvironmentUpdated(ctx context.Context, conn *datazone.Client, domainId string, id string, timeout time.Duration) (*datazone.GetEnvironmentOutput, error) {
398415
stateConf := &retry.StateChangeConf{
399-
Pending: enum.Slice[awstypes.EnvironmentStatus](awstypes.EnvironmentStatusUpdating),
400-
Target: enum.Slice[awstypes.EnvironmentStatus](awstypes.EnvironmentStatusActive),
416+
Pending: enum.Slice(awstypes.EnvironmentStatusUpdating),
417+
Target: enum.Slice(awstypes.EnvironmentStatusActive),
401418
Refresh: statusEnvironment(ctx, conn, domainId, id),
402419
Timeout: timeout,
403420
NotFoundChecks: 20,
@@ -417,10 +434,12 @@ func waitEnvironmentUpdated(ctx context.Context, conn *datazone.Client, domainId
417434

418435
func waitEnvironmentDeleted(ctx context.Context, conn *datazone.Client, domainId string, id string, timeout time.Duration) (*datazone.GetEnvironmentOutput, error) {
419436
stateConf := &retry.StateChangeConf{
420-
Pending: enum.Slice(awstypes.EnvironmentStatusDeleting, awstypes.EnvironmentStatusActive),
421-
Target: []string{},
422-
Refresh: statusEnvironment(ctx, conn, domainId, id),
423-
Timeout: timeout,
437+
Pending: enum.Slice(awstypes.EnvironmentStatusActive, awstypes.EnvironmentStatusDeleting, awstypes.EnvironmentStatusDeleted),
438+
Target: []string{},
439+
Refresh: statusEnvironment(ctx, conn, domainId, id),
440+
Timeout: timeout,
441+
Delay: 10 * time.Second,
442+
PollInterval: 5 * time.Second,
424443
}
425444

426445
outputRaw, err := stateConf.WaitForStateContext(ctx)
@@ -479,7 +498,7 @@ type environmentResourceModel struct {
479498
framework.WithRegionModel
480499
AccountIdentifier types.String `tfsdk:"account_identifier"`
481500
AccountRegion types.String `tfsdk:"account_region"`
482-
BlueprintId types.String `tfsdk:"blueprint_identifier"`
501+
BlueprintIdentifier types.String `tfsdk:"blueprint_identifier"`
483502
CreatedAt timetypes.RFC3339 `tfsdk:"created_at"`
484503
CreatedBy types.String `tfsdk:"created_by"`
485504
Description types.String `tfsdk:"description"`
@@ -496,6 +515,23 @@ type environmentResourceModel struct {
496515
UserParameters fwtypes.ListNestedObjectValueOf[resourceUserParametersData] `tfsdk:"user_parameters"`
497516
}
498517

518+
// nosemgrep:ci.semgrep.framework.manual-flattener-functions
519+
func flattenEnvironment(ctx context.Context, apiObject *datazone.GetEnvironmentOutput, model *environmentResourceModel, diags *diag.Diagnostics) {
520+
diags.Append(fwflex.Flatten(ctx, apiObject, model, fwflex.WithIgnoredFieldNamesAppend("UserParameters"))...)
521+
522+
model.AccountIdentifier = fwflex.StringToFramework(ctx, apiObject.AwsAccountId)
523+
model.AccountRegion = fwflex.StringToFramework(ctx, apiObject.AwsAccountRegion)
524+
model.BlueprintIdentifier = fwflex.StringToFramework(ctx, apiObject.EnvironmentBlueprintId)
525+
model.ProfileIdentifier = fwflex.StringToFramework(ctx, apiObject.EnvironmentProfileId)
526+
model.ProjectIdentifier = fwflex.StringToFramework(ctx, apiObject.ProjectId)
527+
528+
if model.UserParameters.IsNull() { // Import
529+
importUserParameters(ctx, &model.UserParameters, apiObject.UserParameters, diags)
530+
} else {
531+
populateUserParameters(ctx, &model.UserParameters, apiObject.UserParameters, diags)
532+
}
533+
}
534+
499535
type resourceLastDeployment struct {
500536
DeploymentId types.String `tfsdk:"deployment_id"`
501537
DeploymentStatus types.String `tfsdk:"deployment_status"`
@@ -521,3 +557,38 @@ type resourceUserParametersData struct {
521557
Name types.String `tfsdk:"name"`
522558
Value types.String `tfsdk:"value"`
523559
}
560+
561+
func importUserParameters(ctx context.Context, stateUserParams *fwtypes.ListNestedObjectValueOf[resourceUserParametersData], userParameters []awstypes.CustomParameter, diags *diag.Diagnostics) {
562+
params := make([]resourceUserParametersData, 0, len(userParameters))
563+
for _, param := range userParameters {
564+
// If `DefaultValue` is nil, no value has been set
565+
if param.DefaultValue != nil {
566+
params = append(params, resourceUserParametersData{
567+
Name: fwflex.StringToFramework(ctx, param.KeyName),
568+
Value: fwflex.StringToFramework(ctx, param.DefaultValue),
569+
})
570+
}
571+
}
572+
s, d := fwtypes.NewListNestedObjectValueOfValueSlice(ctx, params)
573+
diags.Append(d...)
574+
if d.HasError() {
575+
return
576+
}
577+
*stateUserParams = s
578+
}
579+
580+
func populateUserParameters(ctx context.Context, stateUserParams *fwtypes.ListNestedObjectValueOf[resourceUserParametersData], userParameters []awstypes.CustomParameter, diags *diag.Diagnostics) {
581+
params, d := stateUserParams.ToSlice(ctx)
582+
diags.Append(d...)
583+
if d.HasError() {
584+
return
585+
}
586+
for _, p := range params {
587+
for _, up := range userParameters {
588+
if p.Name.ValueString() == aws.ToString(up.KeyName) {
589+
p.Value = fwflex.StringToFramework(ctx, up.DefaultValue)
590+
break
591+
}
592+
}
593+
}
594+
}

0 commit comments

Comments
 (0)