1package resource
2
3import (
4	"fmt"
5
6	"github.com/hashicorp/terraform/addrs"
7	"github.com/zclconf/go-cty/cty"
8
9	"github.com/hashicorp/terraform/config/hcl2shim"
10	"github.com/hashicorp/terraform/helper/schema"
11
12	"github.com/hashicorp/terraform/states"
13	"github.com/hashicorp/terraform/terraform"
14)
15
16// shimState takes a new *states.State and reverts it to a legacy state for the provider ACC tests
17func shimNewState(newState *states.State, providers map[string]terraform.ResourceProvider) (*terraform.State, error) {
18	state := terraform.NewState()
19
20	// in the odd case of a nil state, let the helper packages handle it
21	if newState == nil {
22		return nil, nil
23	}
24
25	for _, newMod := range newState.Modules {
26		mod := state.AddModule(newMod.Addr)
27
28		for name, out := range newMod.OutputValues {
29			outputType := ""
30			val := hcl2shim.ConfigValueFromHCL2(out.Value)
31			ty := out.Value.Type()
32			switch {
33			case ty == cty.String:
34				outputType = "string"
35			case ty.IsTupleType() || ty.IsListType():
36				outputType = "list"
37			case ty.IsMapType():
38				outputType = "map"
39			}
40
41			mod.Outputs[name] = &terraform.OutputState{
42				Type:      outputType,
43				Value:     val,
44				Sensitive: out.Sensitive,
45			}
46		}
47
48		for _, res := range newMod.Resources {
49			resType := res.Addr.Type
50			providerType := res.ProviderConfig.ProviderConfig.Type
51
52			resource := getResource(providers, providerType, res.Addr)
53
54			for key, i := range res.Instances {
55				flatmap, err := shimmedAttributes(i.Current, resource)
56				if err != nil {
57					return nil, fmt.Errorf("error decoding state for %q: %s", resType, err)
58				}
59
60				resState := &terraform.ResourceState{
61					Type: resType,
62					Primary: &terraform.InstanceState{
63						ID:         flatmap["id"],
64						Attributes: flatmap,
65						Tainted:    i.Current.Status == states.ObjectTainted,
66					},
67					Provider: res.ProviderConfig.String(),
68				}
69				if i.Current.SchemaVersion != 0 {
70					resState.Primary.Meta = map[string]interface{}{
71						"schema_version": i.Current.SchemaVersion,
72					}
73				}
74
75				for _, dep := range i.Current.Dependencies {
76					resState.Dependencies = append(resState.Dependencies, dep.String())
77				}
78
79				// convert the indexes to the old style flapmap indexes
80				idx := ""
81				switch key.(type) {
82				case addrs.IntKey:
83					// don't add numeric index values to resources with a count of 0
84					if len(res.Instances) > 1 {
85						idx = fmt.Sprintf(".%d", key)
86					}
87				case addrs.StringKey:
88					idx = "." + key.String()
89				}
90
91				mod.Resources[res.Addr.String()+idx] = resState
92
93				// add any deposed instances
94				for _, dep := range i.Deposed {
95					flatmap, err := shimmedAttributes(dep, resource)
96					if err != nil {
97						return nil, fmt.Errorf("error decoding deposed state for %q: %s", resType, err)
98					}
99
100					deposed := &terraform.InstanceState{
101						ID:         flatmap["id"],
102						Attributes: flatmap,
103						Tainted:    dep.Status == states.ObjectTainted,
104					}
105					if dep.SchemaVersion != 0 {
106						deposed.Meta = map[string]interface{}{
107							"schema_version": dep.SchemaVersion,
108						}
109					}
110
111					resState.Deposed = append(resState.Deposed, deposed)
112				}
113			}
114		}
115	}
116
117	return state, nil
118}
119
120func getResource(providers map[string]terraform.ResourceProvider, providerName string, addr addrs.Resource) *schema.Resource {
121	p := providers[providerName]
122	if p == nil {
123		panic(fmt.Sprintf("provider %q not found in test step", providerName))
124	}
125
126	// this is only for tests, so should only see schema.Providers
127	provider := p.(*schema.Provider)
128
129	switch addr.Mode {
130	case addrs.ManagedResourceMode:
131		resource := provider.ResourcesMap[addr.Type]
132		if resource != nil {
133			return resource
134		}
135	case addrs.DataResourceMode:
136		resource := provider.DataSourcesMap[addr.Type]
137		if resource != nil {
138			return resource
139		}
140	}
141
142	panic(fmt.Sprintf("resource %s not found in test step", addr.Type))
143}
144
145func shimmedAttributes(instance *states.ResourceInstanceObjectSrc, res *schema.Resource) (map[string]string, error) {
146	flatmap := instance.AttrsFlat
147	if flatmap != nil {
148		return flatmap, nil
149	}
150
151	// if we have json attrs, they need to be decoded
152	rio, err := instance.Decode(res.CoreConfigSchema().ImpliedType())
153	if err != nil {
154		return nil, err
155	}
156
157	instanceState, err := res.ShimInstanceStateFromValue(rio.Value)
158	if err != nil {
159		return nil, err
160	}
161
162	return instanceState.Attributes, nil
163}
164