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  }