mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 18:01:01 -06:00
1987a92386
The previous commit added a new flag to schema.Schema which is documented to make a list with MaxItems: 1 be presented to Terraform Core as a single value instead, giving a way to switch to non-list nested resources without it being a breaking change for Terraform v0.11 users as long as it's done prior to a provider's first v0.12-compatible release. This is the implementation of that mechanism. It's intentionally implemented as a suite of extra fixups rather than direct modifications to existing shim code because we want to ensure that this has no effect whatsoever on the result of a resource type that _isn't_ using AsSingle. Although there is some small unit test coverage of the fixup steps here, the primary testing for this is in the test provider since the integration of all of these fixup steps in the correct order is the more important result than any of the intermediate fixup steps.
97 lines
3.2 KiB
Go
97 lines
3.2 KiB
Go
package schema
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
ctyjson "github.com/zclconf/go-cty/cty/json"
|
|
|
|
"github.com/hashicorp/terraform/configs/configschema"
|
|
"github.com/hashicorp/terraform/terraform"
|
|
)
|
|
|
|
// DiffFromValues takes the current state and desired state as cty.Values and
|
|
// derives a terraform.InstanceDiff to give to the legacy providers. This is
|
|
// used to take the states provided by the new ApplyResourceChange method and
|
|
// convert them to a state+diff required for the legacy Apply method.
|
|
//
|
|
// If the fixup function is non-nil, it will be called with the constructed
|
|
// shimmed InstanceState and ResourceConfig values to do any necessary in-place
|
|
// mutations before producing the diff.
|
|
func DiffFromValues(prior, planned cty.Value, res *Resource) (*terraform.InstanceDiff, error) {
|
|
return diffFromValues(prior, planned, res, nil)
|
|
}
|
|
|
|
// diffFromValues takes an additional CustomizeDiffFunc, so we can generate our
|
|
// test fixtures from the legacy tests. In the new provider protocol the diff
|
|
// only needs to be created for the apply operation, and any customizations
|
|
// have already been done.
|
|
func diffFromValues(prior, planned cty.Value, res *Resource, cust CustomizeDiffFunc) (*terraform.InstanceDiff, error) {
|
|
instanceState, err := res.ShimInstanceStateFromValue(prior)
|
|
// The result of ShimInstanceStateFromValue already has FixupAsSingleInstanceStateIn applied
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
configSchema := res.CoreConfigSchema()
|
|
|
|
cfg := terraform.NewResourceConfigShimmed(planned, configSchema)
|
|
FixupAsSingleResourceConfigIn(cfg, schemaMap(res.Schema))
|
|
|
|
diff, err := schemaMap(res.Schema).Diff(instanceState, cfg, cust, nil, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return diff, err
|
|
}
|
|
|
|
// ApplyDiff takes a cty.Value state and applies a terraform.InstanceDiff to
|
|
// get a new cty.Value state. This is used to convert the diff returned from
|
|
// the legacy provider Diff method to the state required for the new
|
|
// PlanResourceChange method.
|
|
func ApplyDiff(base cty.Value, d *terraform.InstanceDiff, schema *configschema.Block) (cty.Value, error) {
|
|
return d.ApplyToValue(base, schema)
|
|
}
|
|
|
|
// StateValueToJSONMap converts a cty.Value to generic JSON map via the cty JSON
|
|
// encoding.
|
|
func StateValueToJSONMap(val cty.Value, ty cty.Type) (map[string]interface{}, error) {
|
|
js, err := ctyjson.Marshal(val, ty)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var m map[string]interface{}
|
|
if err := json.Unmarshal(js, &m); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
// JSONMapToStateValue takes a generic json map[string]interface{} and converts it
|
|
// to the specific type, ensuring that the values conform to the schema.
|
|
func JSONMapToStateValue(m map[string]interface{}, block *configschema.Block) (cty.Value, error) {
|
|
var val cty.Value
|
|
|
|
js, err := json.Marshal(m)
|
|
if err != nil {
|
|
return val, err
|
|
}
|
|
|
|
val, err = ctyjson.Unmarshal(js, block.ImpliedType())
|
|
if err != nil {
|
|
return val, err
|
|
}
|
|
|
|
return block.CoerceValue(val)
|
|
}
|
|
|
|
// StateValueFromInstanceState converts a terraform.InstanceState to a
|
|
// cty.Value as described by the provided cty.Type, and maintains the resource
|
|
// ID as the "id" attribute.
|
|
func StateValueFromInstanceState(is *terraform.InstanceState, ty cty.Type) (cty.Value, error) {
|
|
return is.AttrsAsObjectValue(ty)
|
|
}
|