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