don't marshal state with the wrong schema

Instead of returning an error with no context about unexpected
attributes or incorrect types, notify users that the schema stored in
the state does not match the current provider.

User can only encounter this error if the providers have updated their
schemas since the state was stored. This would appears when running
`terraform show -json` to display the current state, or
`terraform show -json planfile` if that plan was created with
`-refresh=false`. In either case, the state must be refreshed in order
to properly json encoded.
This commit is contained in:
James Bardin 2021-02-23 10:19:24 -05:00
parent 56b756cfd9
commit 3449a8aa35
2 changed files with 70 additions and 47 deletions

View File

@ -300,7 +300,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
)
}
schema, _ := schemas.ResourceTypeConfig(
schema, version := schemas.ResourceTypeConfig(
r.ProviderConfig.Provider,
resAddr.Mode,
resAddr.Type,
@ -308,6 +308,10 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
// It is possible that the only instance is deposed
if ri.Current != nil {
if version != ri.Current.SchemaVersion {
return nil, fmt.Errorf("schema version %d for %s in state does not match version %d from the provider", ri.Current.SchemaVersion, resAddr, version)
}
current.SchemaVersion = ri.Current.SchemaVersion
if schema == nil {

View File

@ -167,9 +167,8 @@ func TestMarshalResources(t *testing.T) {
Instances: map[addrs.InstanceKey]*states.ResourceInstance{
addrs.NoKey: {
Current: &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
},
},
},
@ -182,13 +181,12 @@ func TestMarshalResources(t *testing.T) {
testSchemas(),
[]resource{
resource{
Address: "test_thing.bar",
Mode: "managed",
Type: "test_thing",
Name: "bar",
Index: addrs.InstanceKey(nil),
ProviderName: "registry.terraform.io/hashicorp/test",
SchemaVersion: 1,
Address: "test_thing.bar",
Mode: "managed",
Type: "test_thing",
Name: "bar",
Index: addrs.InstanceKey(nil),
ProviderName: "registry.terraform.io/hashicorp/test",
AttributeValues: attributeValues{
"foozles": json.RawMessage(`null`),
"woozles": json.RawMessage(`"confuzles"`),
@ -197,6 +195,35 @@ func TestMarshalResources(t *testing.T) {
},
false,
},
"single resource wrong schema": {
map[string]*states.Resource{
"test_thing.baz": {
Addr: addrs.AbsResource{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_thing",
Name: "bar",
},
},
Instances: map[addrs.InstanceKey]*states.ResourceInstance{
addrs.NoKey: {
Current: &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":["confuzles"]}`),
},
},
},
ProviderConfig: addrs.AbsProviderConfig{
Provider: addrs.NewDefaultProvider("test"),
Module: addrs.RootModule,
},
},
},
testSchemas(),
nil,
true,
},
"resource with count": {
map[string]*states.Resource{
"test_thing.bar": {
@ -210,9 +237,8 @@ func TestMarshalResources(t *testing.T) {
Instances: map[addrs.InstanceKey]*states.ResourceInstance{
addrs.IntKey(0): {
Current: &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
},
},
},
@ -225,13 +251,12 @@ func TestMarshalResources(t *testing.T) {
testSchemas(),
[]resource{
resource{
Address: "test_thing.bar[0]",
Mode: "managed",
Type: "test_thing",
Name: "bar",
Index: addrs.IntKey(0),
ProviderName: "registry.terraform.io/hashicorp/test",
SchemaVersion: 1,
Address: "test_thing.bar[0]",
Mode: "managed",
Type: "test_thing",
Name: "bar",
Index: addrs.IntKey(0),
ProviderName: "registry.terraform.io/hashicorp/test",
AttributeValues: attributeValues{
"foozles": json.RawMessage(`null`),
"woozles": json.RawMessage(`"confuzles"`),
@ -253,9 +278,8 @@ func TestMarshalResources(t *testing.T) {
Instances: map[addrs.InstanceKey]*states.ResourceInstance{
addrs.StringKey("rockhopper"): {
Current: &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
},
},
},
@ -268,13 +292,12 @@ func TestMarshalResources(t *testing.T) {
testSchemas(),
[]resource{
resource{
Address: "test_thing.bar[\"rockhopper\"]",
Mode: "managed",
Type: "test_thing",
Name: "bar",
Index: addrs.StringKey("rockhopper"),
ProviderName: "registry.terraform.io/hashicorp/test",
SchemaVersion: 1,
Address: "test_thing.bar[\"rockhopper\"]",
Mode: "managed",
Type: "test_thing",
Name: "bar",
Index: addrs.StringKey("rockhopper"),
ProviderName: "registry.terraform.io/hashicorp/test",
AttributeValues: attributeValues{
"foozles": json.RawMessage(`null`),
"woozles": json.RawMessage(`"confuzles"`),
@ -297,9 +320,8 @@ func TestMarshalResources(t *testing.T) {
addrs.NoKey: {
Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{
states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
},
},
},
@ -342,15 +364,13 @@ func TestMarshalResources(t *testing.T) {
addrs.NoKey: {
Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{
states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
},
},
Current: &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
},
},
},
@ -363,13 +383,12 @@ func TestMarshalResources(t *testing.T) {
testSchemas(),
[]resource{
resource{
Address: "test_thing.bar",
Mode: "managed",
Type: "test_thing",
Name: "bar",
Index: addrs.InstanceKey(nil),
ProviderName: "registry.terraform.io/hashicorp/test",
SchemaVersion: 1,
Address: "test_thing.bar",
Mode: "managed",
Type: "test_thing",
Name: "bar",
Index: addrs.InstanceKey(nil),
ProviderName: "registry.terraform.io/hashicorp/test",
AttributeValues: attributeValues{
"foozles": json.RawMessage(`null`),
"woozles": json.RawMessage(`"confuzles"`),