opentofu/helper/resource/state_shim.go
James Bardin 38163f2b37 use SimpleDiff and set "id" as RequiresReplace
Use the new SimpleDiff method of the provider so that the diff isn't
altered by ForceNew attributes.

Always set an "id" as RequiresReplace so core knows an instance will be
replaced, even if all ForceNew attributes are filtered out due to
ignore_changes.
2018-10-16 19:14:54 -07:00

143 lines
3.7 KiB
Go

package resource
import (
"fmt"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/config/hcl2shim"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/terraform"
)
func mustShimNewState(newState *states.State, schemas *terraform.Schemas) *terraform.State {
s, err := shimNewState(newState, schemas)
if err != nil {
panic(err)
}
return s
}
// shimState takes a new *states.State and reverts it to a legacy state for the provider ACC tests
func shimNewState(newState *states.State, schemas *terraform.Schemas) (*terraform.State, error) {
state := terraform.NewState()
// in the odd case of a nil state, let the helper packages handle it
if newState == nil {
return nil, nil
}
for _, newMod := range newState.Modules {
mod := state.AddModule(newMod.Addr)
for name, out := range newMod.OutputValues {
outputType := ""
val := hcl2shim.ConfigValueFromHCL2(out.Value)
ty := out.Value.Type()
switch {
case ty == cty.String:
outputType = "string"
case ty.IsTupleType() || ty.IsListType():
outputType = "list"
case ty.IsMapType():
outputType = "map"
}
mod.Outputs[name] = &terraform.OutputState{
Type: outputType,
Value: val,
Sensitive: out.Sensitive,
}
}
for _, res := range newMod.Resources {
resType := res.Addr.Type
providerType := res.ProviderConfig.ProviderConfig.Type
providerSchema := schemas.Providers[providerType]
if providerSchema == nil {
return nil, fmt.Errorf("missing schema for %q", providerType)
}
var resSchema *configschema.Block
switch res.Addr.Mode {
case addrs.ManagedResourceMode:
resSchema = providerSchema.ResourceTypes[resType]
case addrs.DataResourceMode:
resSchema = providerSchema.DataSources[resType]
}
if resSchema == nil {
return nil, fmt.Errorf("mising resource schema for %q in %q", resType, providerType)
}
for key, i := range res.Instances {
flatmap, err := shimmedAttributes(i.Current, resSchema.ImpliedType())
if err != nil {
return nil, fmt.Errorf("error decoding state for %q: %s", resType, err)
}
resState := &terraform.ResourceState{
Type: resType,
Primary: &terraform.InstanceState{
ID: flatmap["id"],
Attributes: flatmap,
Tainted: i.Current.Status == states.ObjectTainted,
},
}
for _, dep := range i.Current.Dependencies {
resState.Dependencies = append(resState.Dependencies, dep.String())
}
// convert the indexes to the old style flapmap indexes
idx := ""
switch key.(type) {
case addrs.IntKey:
idx = fmt.Sprintf(".%d", key)
case addrs.StringKey:
idx = "." + key.String()
}
mod.Resources[res.Addr.String()+idx] = resState
// ad any deposed instances
for _, dep := range i.Deposed {
flatmap, err := shimmedAttributes(dep, resSchema.ImpliedType())
if err != nil {
return nil, fmt.Errorf("error decoding deposed state for %q: %s", resType, err)
}
deposed := &terraform.InstanceState{
ID: flatmap["id"],
Attributes: flatmap,
Tainted: dep.Status == states.ObjectTainted,
}
resState.Deposed = append(resState.Deposed, deposed)
}
}
}
}
return state, nil
}
func shimmedAttributes(instance *states.ResourceInstanceObjectSrc, ty cty.Type) (map[string]string, error) {
flatmap := instance.AttrsFlat
// if we have json attrs, they need to be decoded
if flatmap == nil {
rio, err := instance.Decode(ty)
if err != nil {
return nil, err
}
flatmap = hcl2shim.FlatmapValueFromHCL2(rio.Value)
}
return flatmap, nil
}