Skip to content

Commit

Permalink
command/jsonstate: properly marshal deposed resources
Browse files Browse the repository at this point in the history
This PR addresses 2 issues: `show -json` would crash if there was not a
`Current` `states.ResourceInstance` for a given resource, and `deposed`
resource instances were not shown at all.

Fixes #22642
  • Loading branch information
mildwonkey committed Oct 8, 2019
1 parent 1ee851f commit f72b942
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 37 deletions.
84 changes: 63 additions & 21 deletions command/jsonstate/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ type resource struct {

// Tainted is true if the resource is tainted in terraform state.
Tainted bool `json:"tainted,omitempty"`

// Deposed is set if the resource is deposed in terraform state.
DeposedKey string `json:"deposed,omitempty"`
}

// attributeValues is the JSON representation of the attribute values of the
Expand Down Expand Up @@ -246,7 +249,7 @@ func marshalResources(resources map[string]*states.Resource, schemas *terraform.
for _, r := range resources {
for k, ri := range r.Instances {

resource := resource{
current := resource{
Address: r.Addr.String(),
Type: r.Addr.Type,
Name: r.Addr.Name,
Expand All @@ -255,9 +258,9 @@ func marshalResources(resources map[string]*states.Resource, schemas *terraform.

switch r.Addr.Mode {
case addrs.ManagedResourceMode:
resource.Mode = "managed"
current.Mode = "managed"
case addrs.DataResourceMode:
resource.Mode = "data"
current.Mode = "data"
default:
return ret, fmt.Errorf("resource %s has an unsupported mode %s",
r.Addr.String(),
Expand All @@ -266,39 +269,78 @@ func marshalResources(resources map[string]*states.Resource, schemas *terraform.
}

if r.EachMode != states.NoEach {
resource.Index = k
current.Index = k
}

schema, _ := schemas.ResourceTypeConfig(
r.ProviderConfig.ProviderConfig.Type,
r.Addr.Mode,
r.Addr.Type,
)
resource.SchemaVersion = ri.Current.SchemaVersion

if schema == nil {
return nil, fmt.Errorf("no schema found for %s", r.Addr.String())
}
riObj, err := ri.Current.Decode(schema.ImpliedType())
if err != nil {
return nil, err
}
// It is possible that the only instance is deposed
if ri.Current != nil {
current.SchemaVersion = ri.Current.SchemaVersion

resource.AttributeValues = marshalAttributeValues(riObj.Value, schema)
if schema == nil {
return nil, fmt.Errorf("no schema found for %s", r.Addr.String())
}
riObj, err := ri.Current.Decode(schema.ImpliedType())
if err != nil {
return nil, err
}

current.AttributeValues = marshalAttributeValues(riObj.Value, schema)

if len(riObj.Dependencies) > 0 {
dependencies := make([]string, len(riObj.Dependencies))
for i, v := range riObj.Dependencies {
dependencies[i] = v.String()
if len(riObj.Dependencies) > 0 {
dependencies := make([]string, len(riObj.Dependencies))
for i, v := range riObj.Dependencies {
dependencies[i] = v.String()
}
current.DependsOn = dependencies
}
resource.DependsOn = dependencies

if riObj.Status == states.ObjectTainted {
current.Tainted = true
}
ret = append(ret, current)
}

if riObj.Status == states.ObjectTainted {
resource.Tainted = true
if ri.Deposed != nil {
for deposedKey, rios := range ri.Deposed {
// copy the base fields from the current instance
deposed := resource{
Address: current.Address,
Type: current.Type,
Name: current.Name,
ProviderName: current.ProviderName,
Mode: current.Mode,
Index: current.Index,
}

riObj, err := rios.Decode(schema.ImpliedType())
if err != nil {
return nil, err
}

deposed.AttributeValues = marshalAttributeValues(riObj.Value, schema)

if len(riObj.Dependencies) > 0 {
dependencies := make([]string, len(riObj.Dependencies))
for i, v := range riObj.Dependencies {
dependencies[i] = v.String()
}
deposed.DependsOn = dependencies
}

if riObj.Status == states.ObjectTainted {
deposed.Tainted = true
}
deposed.DeposedKey = deposedKey.String()
ret = append(ret, deposed)
}
}

ret = append(ret, resource)
}

}
Expand Down
139 changes: 123 additions & 16 deletions command/jsonstate/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,104 @@ func TestMarshalAttributeValues(t *testing.T) {
}

func TestMarshalResources(t *testing.T) {
tests := []struct {
deposedKey := states.NewDeposedKey()
tests := map[string]struct {
Resources map[string]*states.Resource
Schemas *terraform.Schemas
Want []resource
Err bool
}{
{
"nil": {
nil,
nil,
nil,
false,
},
{
"single resource": {
map[string]*states.Resource{
"test_thing.baz": {
Addr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_thing",
Name: "bar",
},
EachMode: states.EachList,
Instances: map[addrs.InstanceKey]*states.ResourceInstance{
addrs.IntKey(0): {
Current: &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
},
},
},
ProviderConfig: addrs.ProviderConfig{
Type: "test",
}.Absolute(addrs.RootModuleInstance),
},
},
testSchemas(),
[]resource{
resource{
Address: "test_thing.bar",
Mode: "managed",
Type: "test_thing",
Name: "bar",
Index: addrs.IntKey(0),
ProviderName: "test",
SchemaVersion: 1,
AttributeValues: attributeValues{
"foozles": json.RawMessage(`null`),
"woozles": json.RawMessage(`"confuzles"`),
},
},
},
false,
},
"deposed resource": {
map[string]*states.Resource{
"test_thing.baz": {
Addr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_thing",
Name: "bar",
},
EachMode: states.EachList,
Instances: map[addrs.InstanceKey]*states.ResourceInstance{
addrs.IntKey(0): {
Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{
states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
},
},
},
},
ProviderConfig: addrs.ProviderConfig{
Type: "test",
}.Absolute(addrs.RootModuleInstance),
},
},
testSchemas(),
[]resource{
resource{
Address: "test_thing.bar",
Mode: "managed",
Type: "test_thing",
Name: "bar",
Index: addrs.IntKey(0),
ProviderName: "test",
DeposedKey: deposedKey.String(),
AttributeValues: attributeValues{
"foozles": json.RawMessage(`null`),
"woozles": json.RawMessage(`"confuzles"`),
},
},
},
false,
},
"deposed and current resource": {
map[string]*states.Resource{
"test_thing.baz": {
Addr: addrs.Resource{
Expand All @@ -181,6 +266,13 @@ func TestMarshalResources(t *testing.T) {
EachMode: states.EachList,
Instances: map[addrs.InstanceKey]*states.ResourceInstance{
addrs.IntKey(0): {
Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{
states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
},
},
Current: &states.ResourceInstanceObjectSrc{
SchemaVersion: 1,
Status: states.ObjectReady,
Expand Down Expand Up @@ -208,25 +300,40 @@ func TestMarshalResources(t *testing.T) {
"woozles": json.RawMessage(`"confuzles"`),
},
},
resource{
Address: "test_thing.bar",
Mode: "managed",
Type: "test_thing",
Name: "bar",
Index: addrs.IntKey(0),
ProviderName: "test",
DeposedKey: deposedKey.String(),
AttributeValues: attributeValues{
"foozles": json.RawMessage(`null`),
"woozles": json.RawMessage(`"confuzles"`),
},
},
},
false,
},
}

for _, test := range tests {
got, err := marshalResources(test.Resources, test.Schemas)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
for name, test := range tests {
t.Run(name, func(t *testing.T) {
got, err := marshalResources(test.Resources, test.Schemas)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
eq := reflect.DeepEqual(got, test.Want)
if !eq {
t.Fatalf("wrong result:\nGot: %#v\nWant: %#v\n", got, test.Want)
}
eq := reflect.DeepEqual(got, test.Want)
if !eq {
t.Fatalf("wrong result:\nGot: %#v\nWant: %#v\n", got, test.Want)
}
})
}
}

Expand Down

0 comments on commit f72b942

Please sign in to comment.