Merge pull request #27885 from hashicorp/jbardin/show-json

jsonstate: indicate schema version mismatch during encoding
This commit is contained in:
James Bardin 2021-02-23 12:57:06 -05:00 committed by GitHub
commit c103242bef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 76 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, r.ProviderConfig.Provider,
resAddr.Mode, resAddr.Mode,
resAddr.Type, 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 // It is possible that the only instance is deposed
if ri.Current != nil { 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 current.SchemaVersion = ri.Current.SchemaVersion
if schema == nil { if schema == nil {

View File

@ -167,7 +167,6 @@ func TestMarshalResources(t *testing.T) {
Instances: map[addrs.InstanceKey]*states.ResourceInstance{ Instances: map[addrs.InstanceKey]*states.ResourceInstance{
addrs.NoKey: { addrs.NoKey: {
Current: &states.ResourceInstanceObjectSrc{ Current: &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`), AttrsJSON: []byte(`{"woozles":"confuzles"}`),
}, },
@ -188,7 +187,6 @@ func TestMarshalResources(t *testing.T) {
Name: "bar", Name: "bar",
Index: addrs.InstanceKey(nil), Index: addrs.InstanceKey(nil),
ProviderName: "registry.terraform.io/hashicorp/test", ProviderName: "registry.terraform.io/hashicorp/test",
SchemaVersion: 1,
AttributeValues: attributeValues{ AttributeValues: attributeValues{
"foozles": json.RawMessage(`null`), "foozles": json.RawMessage(`null`),
"woozles": json.RawMessage(`"confuzles"`), "woozles": json.RawMessage(`"confuzles"`),
@ -197,6 +195,35 @@ func TestMarshalResources(t *testing.T) {
}, },
false, 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": { "resource with count": {
map[string]*states.Resource{ map[string]*states.Resource{
"test_thing.bar": { "test_thing.bar": {
@ -210,7 +237,6 @@ func TestMarshalResources(t *testing.T) {
Instances: map[addrs.InstanceKey]*states.ResourceInstance{ Instances: map[addrs.InstanceKey]*states.ResourceInstance{
addrs.IntKey(0): { addrs.IntKey(0): {
Current: &states.ResourceInstanceObjectSrc{ Current: &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`), AttrsJSON: []byte(`{"woozles":"confuzles"}`),
}, },
@ -231,7 +257,6 @@ func TestMarshalResources(t *testing.T) {
Name: "bar", Name: "bar",
Index: addrs.IntKey(0), Index: addrs.IntKey(0),
ProviderName: "registry.terraform.io/hashicorp/test", ProviderName: "registry.terraform.io/hashicorp/test",
SchemaVersion: 1,
AttributeValues: attributeValues{ AttributeValues: attributeValues{
"foozles": json.RawMessage(`null`), "foozles": json.RawMessage(`null`),
"woozles": json.RawMessage(`"confuzles"`), "woozles": json.RawMessage(`"confuzles"`),
@ -253,7 +278,6 @@ func TestMarshalResources(t *testing.T) {
Instances: map[addrs.InstanceKey]*states.ResourceInstance{ Instances: map[addrs.InstanceKey]*states.ResourceInstance{
addrs.StringKey("rockhopper"): { addrs.StringKey("rockhopper"): {
Current: &states.ResourceInstanceObjectSrc{ Current: &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`), AttrsJSON: []byte(`{"woozles":"confuzles"}`),
}, },
@ -274,7 +298,6 @@ func TestMarshalResources(t *testing.T) {
Name: "bar", Name: "bar",
Index: addrs.StringKey("rockhopper"), Index: addrs.StringKey("rockhopper"),
ProviderName: "registry.terraform.io/hashicorp/test", ProviderName: "registry.terraform.io/hashicorp/test",
SchemaVersion: 1,
AttributeValues: attributeValues{ AttributeValues: attributeValues{
"foozles": json.RawMessage(`null`), "foozles": json.RawMessage(`null`),
"woozles": json.RawMessage(`"confuzles"`), "woozles": json.RawMessage(`"confuzles"`),
@ -297,7 +320,6 @@ func TestMarshalResources(t *testing.T) {
addrs.NoKey: { addrs.NoKey: {
Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{ Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{
states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{ states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`), AttrsJSON: []byte(`{"woozles":"confuzles"}`),
}, },
@ -342,13 +364,11 @@ func TestMarshalResources(t *testing.T) {
addrs.NoKey: { addrs.NoKey: {
Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{ Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{
states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{ states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`), AttrsJSON: []byte(`{"woozles":"confuzles"}`),
}, },
}, },
Current: &states.ResourceInstanceObjectSrc{ Current: &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`), AttrsJSON: []byte(`{"woozles":"confuzles"}`),
}, },
@ -369,7 +389,6 @@ func TestMarshalResources(t *testing.T) {
Name: "bar", Name: "bar",
Index: addrs.InstanceKey(nil), Index: addrs.InstanceKey(nil),
ProviderName: "registry.terraform.io/hashicorp/test", ProviderName: "registry.terraform.io/hashicorp/test",
SchemaVersion: 1,
AttributeValues: attributeValues{ AttributeValues: attributeValues{
"foozles": json.RawMessage(`null`), "foozles": json.RawMessage(`null`),
"woozles": json.RawMessage(`"confuzles"`), "woozles": json.RawMessage(`"confuzles"`),

View File

@ -28,6 +28,12 @@ For Terraform state files (including when no path is provided),
For Terraform plan files, `terraform show -json` will show a JSON representation For Terraform plan files, `terraform show -json` will show a JSON representation
of the plan, configuration, and current state. of the plan, configuration, and current state.
If you've updated providers which contain new schema versions since the state
was written, the state needs to be upgraded before it can be displayed with
`show -json`. If you are viewing a plan, it must be created without
`-refresh=false`. If you are viewing a state file, run `terraform refresh`
first.
The output format is covered in detail in [JSON Output Format](/docs/internals/json-format.html). The output format is covered in detail in [JSON Output Format](/docs/internals/json-format.html).
## Usage ## Usage