github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/eval_state_upgrade.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 8 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" 9 "github.com/hashicorp/terraform-plugin-sdk/internal/providers" 10 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 11 "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" 12 ) 13 14 // UpgradeResourceState will, if necessary, run the provider-defined upgrade 15 // logic against the given state object to make it compliant with the 16 // current schema version. This is a no-op if the given state object is 17 // already at the latest version. 18 // 19 // If any errors occur during upgrade, error diagnostics are returned. In that 20 // case it is not safe to proceed with using the original state object. 21 func UpgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Interface, src *states.ResourceInstanceObjectSrc, currentSchema *configschema.Block, currentVersion uint64) (*states.ResourceInstanceObjectSrc, tfdiags.Diagnostics) { 22 if addr.Resource.Resource.Mode != addrs.ManagedResourceMode { 23 // We only do state upgrading for managed resources. 24 return src, nil 25 } 26 27 stateIsFlatmap := len(src.AttrsJSON) == 0 28 29 providerType := addr.Resource.Resource.DefaultProviderConfig().Type 30 if src.SchemaVersion > currentVersion { 31 log.Printf("[TRACE] UpgradeResourceState: can't downgrade state for %s from version %d to %d", addr, src.SchemaVersion, currentVersion) 32 var diags tfdiags.Diagnostics 33 diags = diags.Append(tfdiags.Sourceless( 34 tfdiags.Error, 35 "Resource instance managed by newer provider version", 36 // This is not a very good error message, but we don't retain enough 37 // information in state to give good feedback on what provider 38 // version might be required here. :( 39 fmt.Sprintf("The current state of %s was created by a newer provider version than is currently selected. Upgrade the %s provider to work with this state.", addr, providerType), 40 )) 41 return nil, diags 42 } 43 44 // If we get down here then we need to upgrade the state, with the 45 // provider's help. 46 // If this state was originally created by a version of Terraform prior to 47 // v0.12, this also includes translating from legacy flatmap to new-style 48 // representation, since only the provider has enough information to 49 // understand a flatmap built against an older schema. 50 if src.SchemaVersion != currentVersion { 51 log.Printf("[TRACE] UpgradeResourceState: upgrading state for %s from version %d to %d using provider %q", addr, src.SchemaVersion, currentVersion, providerType) 52 } else { 53 log.Printf("[TRACE] UpgradeResourceState: schema version of %s is still %d; calling provider %q for any other minor fixups", addr, currentVersion, providerType) 54 } 55 56 req := providers.UpgradeResourceStateRequest{ 57 TypeName: addr.Resource.Resource.Type, 58 59 // TODO: The internal schema version representations are all using 60 // uint64 instead of int64, but unsigned integers aren't friendly 61 // to all protobuf target languages so in practice we use int64 62 // on the wire. In future we will change all of our internal 63 // representations to int64 too. 64 Version: int64(src.SchemaVersion), 65 } 66 67 if stateIsFlatmap { 68 req.RawStateFlatmap = src.AttrsFlat 69 } else { 70 req.RawStateJSON = src.AttrsJSON 71 } 72 73 resp := provider.UpgradeResourceState(req) 74 diags := resp.Diagnostics 75 if diags.HasErrors() { 76 return nil, diags 77 } 78 79 // After upgrading, the new value must conform to the current schema. When 80 // going over RPC this is actually already ensured by the 81 // marshaling/unmarshaling of the new value, but we'll check it here 82 // anyway for robustness, e.g. for in-process providers. 83 newValue := resp.UpgradedState 84 if errs := newValue.Type().TestConformance(currentSchema.ImpliedType()); len(errs) > 0 { 85 for _, err := range errs { 86 diags = diags.Append(tfdiags.Sourceless( 87 tfdiags.Error, 88 "Invalid resource state upgrade", 89 fmt.Sprintf("The %s provider upgraded the state for %s from a previous version, but produced an invalid result: %s.", providerType, addr, tfdiags.FormatError(err)), 90 )) 91 } 92 return nil, diags 93 } 94 95 new, err := src.CompleteUpgrade(newValue, currentSchema.ImpliedType(), uint64(currentVersion)) 96 if err != nil { 97 // We already checked for type conformance above, so getting into this 98 // codepath should be rare and is probably a bug somewhere under CompleteUpgrade. 99 diags = diags.Append(tfdiags.Sourceless( 100 tfdiags.Error, 101 "Failed to encode result of resource state upgrade", 102 fmt.Sprintf("Failed to encode state for %s after resource schema upgrade: %s.", addr, tfdiags.FormatError(err)), 103 )) 104 } 105 return new, diags 106 }