github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/eval_state_test.go (about) 1 package terraform 2 3 import ( 4 "testing" 5 6 "github.com/davecgh/go-spew/spew" 7 "github.com/zclconf/go-cty/cty" 8 9 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 10 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" 11 "github.com/hashicorp/terraform-plugin-sdk/internal/providers" 12 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 13 ) 14 15 func TestEvalRequireState(t *testing.T) { 16 ctx := new(MockEvalContext) 17 18 cases := []struct { 19 State *states.ResourceInstanceObject 20 Exit bool 21 }{ 22 { 23 nil, 24 true, 25 }, 26 { 27 &states.ResourceInstanceObject{ 28 Value: cty.NullVal(cty.Object(map[string]cty.Type{ 29 "id": cty.String, 30 })), 31 Status: states.ObjectReady, 32 }, 33 true, 34 }, 35 { 36 &states.ResourceInstanceObject{ 37 Value: cty.ObjectVal(map[string]cty.Value{ 38 "id": cty.StringVal("foo"), 39 }), 40 Status: states.ObjectReady, 41 }, 42 false, 43 }, 44 } 45 46 var exitVal EvalEarlyExitError 47 for _, tc := range cases { 48 node := &EvalRequireState{State: &tc.State} 49 _, err := node.Eval(ctx) 50 if tc.Exit { 51 if err != exitVal { 52 t.Fatalf("should've exited: %#v", tc.State) 53 } 54 55 continue 56 } 57 if !tc.Exit && err != nil { 58 t.Fatalf("shouldn't exit: %#v", tc.State) 59 } 60 if err != nil { 61 t.Fatalf("err: %s", err) 62 } 63 } 64 } 65 66 func TestEvalUpdateStateHook(t *testing.T) { 67 mockHook := new(MockHook) 68 69 state := states.NewState() 70 state.Module(addrs.RootModuleInstance).SetLocalValue("foo", cty.StringVal("hello")) 71 72 ctx := new(MockEvalContext) 73 ctx.HookHook = mockHook 74 ctx.StateState = state.SyncWrapper() 75 76 node := &EvalUpdateStateHook{} 77 if _, err := node.Eval(ctx); err != nil { 78 t.Fatalf("err: %s", err) 79 } 80 81 if !mockHook.PostStateUpdateCalled { 82 t.Fatal("should call PostStateUpdate") 83 } 84 if mockHook.PostStateUpdateState.LocalValue(addrs.LocalValue{Name: "foo"}.Absolute(addrs.RootModuleInstance)) != cty.StringVal("hello") { 85 t.Fatalf("wrong state passed to hook: %s", spew.Sdump(mockHook.PostStateUpdateState)) 86 } 87 } 88 89 func TestEvalReadState(t *testing.T) { 90 var output *states.ResourceInstanceObject 91 mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ 92 Attributes: map[string]*configschema.Attribute{ 93 "id": { 94 Type: cty.String, 95 Optional: true, 96 }, 97 }, 98 }) 99 providerSchema := mockProvider.GetSchemaReturn 100 provider := providers.Interface(mockProvider) 101 102 cases := map[string]struct { 103 Resources map[string]*ResourceState 104 Node EvalNode 105 ExpectedInstanceId string 106 }{ 107 "ReadState gets primary instance state": { 108 Resources: map[string]*ResourceState{ 109 "aws_instance.bar": { 110 Primary: &InstanceState{ 111 ID: "i-abc123", 112 }, 113 }, 114 }, 115 Node: &EvalReadState{ 116 Addr: addrs.Resource{ 117 Mode: addrs.ManagedResourceMode, 118 Type: "aws_instance", 119 Name: "bar", 120 }.Instance(addrs.NoKey), 121 Provider: &provider, 122 ProviderSchema: &providerSchema, 123 124 Output: &output, 125 }, 126 ExpectedInstanceId: "i-abc123", 127 }, 128 "ReadStateDeposed gets deposed instance": { 129 Resources: map[string]*ResourceState{ 130 "aws_instance.bar": { 131 Deposed: []*InstanceState{ 132 {ID: "i-abc123"}, 133 }, 134 }, 135 }, 136 Node: &EvalReadStateDeposed{ 137 Addr: addrs.Resource{ 138 Mode: addrs.ManagedResourceMode, 139 Type: "aws_instance", 140 Name: "bar", 141 }.Instance(addrs.NoKey), 142 Key: states.DeposedKey("00000001"), // shim from legacy state assigns 0th deposed index this key 143 Provider: &provider, 144 ProviderSchema: &providerSchema, 145 146 Output: &output, 147 }, 148 ExpectedInstanceId: "i-abc123", 149 }, 150 } 151 152 for k, c := range cases { 153 t.Run(k, func(t *testing.T) { 154 ctx := new(MockEvalContext) 155 state := MustShimLegacyState(&State{ 156 Modules: []*ModuleState{ 157 { 158 Path: rootModulePath, 159 Resources: c.Resources, 160 }, 161 }, 162 }) 163 ctx.StateState = state.SyncWrapper() 164 ctx.PathPath = addrs.RootModuleInstance 165 166 result, err := c.Node.Eval(ctx) 167 if err != nil { 168 t.Fatalf("[%s] Got err: %#v", k, err) 169 } 170 171 expected := c.ExpectedInstanceId 172 if !(result != nil && instanceObjectIdForTests(result.(*states.ResourceInstanceObject)) == expected) { 173 t.Fatalf("[%s] Expected return with ID %#v, got: %#v", k, expected, result) 174 } 175 176 if !(output != nil && output.Value.GetAttr("id") == cty.StringVal(expected)) { 177 t.Fatalf("[%s] Expected output with ID %#v, got: %#v", k, expected, output) 178 } 179 180 output = nil 181 }) 182 } 183 } 184 185 func TestEvalWriteState(t *testing.T) { 186 state := states.NewState() 187 ctx := new(MockEvalContext) 188 ctx.StateState = state.SyncWrapper() 189 ctx.PathPath = addrs.RootModuleInstance 190 191 mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ 192 Attributes: map[string]*configschema.Attribute{ 193 "id": { 194 Type: cty.String, 195 Optional: true, 196 }, 197 }, 198 }) 199 providerSchema := mockProvider.GetSchemaReturn 200 201 obj := &states.ResourceInstanceObject{ 202 Value: cty.ObjectVal(map[string]cty.Value{ 203 "id": cty.StringVal("i-abc123"), 204 }), 205 Status: states.ObjectReady, 206 } 207 node := &EvalWriteState{ 208 Addr: addrs.Resource{ 209 Mode: addrs.ManagedResourceMode, 210 Type: "aws_instance", 211 Name: "foo", 212 }.Instance(addrs.NoKey), 213 214 State: &obj, 215 216 ProviderSchema: &providerSchema, 217 ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault("aws"), 218 } 219 _, err := node.Eval(ctx) 220 if err != nil { 221 t.Fatalf("Got err: %#v", err) 222 } 223 224 checkStateString(t, state, ` 225 aws_instance.foo: 226 ID = i-abc123 227 provider = provider.aws 228 `) 229 } 230 231 func TestEvalWriteStateDeposed(t *testing.T) { 232 state := states.NewState() 233 ctx := new(MockEvalContext) 234 ctx.StateState = state.SyncWrapper() 235 ctx.PathPath = addrs.RootModuleInstance 236 237 mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ 238 Attributes: map[string]*configschema.Attribute{ 239 "id": { 240 Type: cty.String, 241 Optional: true, 242 }, 243 }, 244 }) 245 providerSchema := mockProvider.GetSchemaReturn 246 247 obj := &states.ResourceInstanceObject{ 248 Value: cty.ObjectVal(map[string]cty.Value{ 249 "id": cty.StringVal("i-abc123"), 250 }), 251 Status: states.ObjectReady, 252 } 253 node := &EvalWriteStateDeposed{ 254 Addr: addrs.Resource{ 255 Mode: addrs.ManagedResourceMode, 256 Type: "aws_instance", 257 Name: "foo", 258 }.Instance(addrs.NoKey), 259 Key: states.DeposedKey("deadbeef"), 260 261 State: &obj, 262 263 ProviderSchema: &providerSchema, 264 ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault("aws"), 265 } 266 _, err := node.Eval(ctx) 267 if err != nil { 268 t.Fatalf("Got err: %#v", err) 269 } 270 271 checkStateString(t, state, ` 272 aws_instance.foo: (1 deposed) 273 ID = <not created> 274 provider = provider.aws 275 Deposed ID 1 = i-abc123 276 `) 277 }