Skip to content

Commit

Permalink
Modifying attribute plan modifier and adding tests for integer resour…
Browse files Browse the repository at this point in the history
…ce (#303)
  • Loading branch information
bendbennett committed Sep 1, 2022
1 parent 5fbbb26 commit 16d2e85
Show file tree
Hide file tree
Showing 3 changed files with 912 additions and 29 deletions.
96 changes: 69 additions & 27 deletions internal/planmodifiers/attribute.go
Expand Up @@ -168,46 +168,88 @@ func (r requiresReplaceIfValuesNotNullModifier) Modify(ctx context.Context, req
return
}

configMap, ok := req.AttributeConfig.(types.Map)
if !ok {
// If there are no differences, do not mark the resource for replacement
// and ensure the plan matches the configuration.
if req.AttributeConfig.Equal(req.AttributeState) {
return
}

stateMap, ok := req.AttributeState.(types.Map)
if !ok {
return
}
if req.AttributeState.IsNull() {
// terraform-plugin-sdk would store maps as null if all keys had null
// values. To prevent unintentional replacement plans when migrating
// to terraform-plugin-framework, only trigger replacement when the
// prior state (map) is null and when there are not null map values.
allNullValues := true

configMap, ok := req.AttributeConfig.(types.Map)

replace := false
if !ok {
return
}

for _, configValue := range configMap.Elems {
if !configValue.IsNull() {
allNullValues = false
}
}

if allNullValues {
return
}
} else {
// terraform-plugin-sdk would completely omit storing map keys with
// null values, so this also must prevent unintentional replacement
// in that case as well.
allNewNullValues := true

configMap, ok := req.AttributeConfig.(types.Map)

if !ok {
return
}

for k, configValue := range configMap.Elems {
stateValue, ok := stateMap.Elems[k]
stateMap, ok := req.AttributeState.(types.Map)

if !ok && configValue.IsNull() {
continue
if !ok {
return
}

if !configValue.Equal(stateValue) {
replace = true
for configKey, configValue := range configMap.Elems {
stateValue, ok := stateMap.Elems[configKey]

// If the key doesn't exist in state and the config value is
// null, do not trigger replacement.
if !ok && configValue.IsNull() {
continue
}

// If the state value exists and it is equal to the config value,
// do not trigger replacement.
if configValue.Equal(stateValue) {
continue
}

allNewNullValues = false
break
}
}

if replace {
resp.RequiresReplace = true
return
for stateKey, _ := range stateMap.Elems {
_, ok := configMap.Elems[stateKey]

// If the key doesn't exist in the config, but there is a state
// value, trigger replacement.
if !ok {
allNewNullValues = false
break
}
}

if allNewNullValues {
return
}
}

//respPlan := resp.AttributePlan
//
//pm, ok := respPlan.(types.Map)
//if ok {
// for k, v := range additionalElems {
// pm.Elems[k] = v
// }
//}
//
//resp.AttributePlan = pm
resp.RequiresReplace = true
}

// Description returns a human-readable description of the plan modifier.
Expand Down
11 changes: 9 additions & 2 deletions internal/provider/resource_integer.go
Expand Up @@ -36,7 +36,6 @@ func (r *integerResourceType) GetSchema(context.Context) (tfsdk.Schema, diag.Dia
ElemType: types.StringType,
},
Optional: true,
Computed: true,
PlanModifiers: []tfsdk.AttributePlanModifier{
planmodifiers.RequiresReplaceIfValuesNotNull(),
},
Expand Down Expand Up @@ -142,7 +141,15 @@ func (r *integerResource) Read(ctx context.Context, req resource.ReadRequest, re
// Update is intentionally left blank as all required and optional attributes force replacement of the resource
// through the RequiresReplace AttributePlanModifier.
func (r *integerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
resp.State = tfsdk.State(req.Plan)
var model integerModelV0

resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...)

if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &model)...)
}

// Delete does not need to explicitly call resp.State.RemoveResource() as this is automatically handled by the
Expand Down

0 comments on commit 16d2e85

Please sign in to comment.