github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/backend/local/backend_refresh_test.go (about)

     1  package local
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/hashicorp/terraform/providers"
     9  
    10  	"github.com/hashicorp/terraform/backend"
    11  	"github.com/hashicorp/terraform/configs/configschema"
    12  	"github.com/hashicorp/terraform/internal/initwd"
    13  	"github.com/hashicorp/terraform/terraform"
    14  	"github.com/zclconf/go-cty/cty"
    15  )
    16  
    17  func TestLocal_refresh(t *testing.T) {
    18  	b, cleanup := TestLocal(t)
    19  	defer cleanup()
    20  
    21  	p := TestLocalProvider(t, b, "test", refreshFixtureSchema())
    22  	terraform.TestStateFile(t, b.StatePath, testRefreshState())
    23  
    24  	p.ReadResourceFn = nil
    25  	p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{
    26  		"id": cty.StringVal("yes"),
    27  	})}
    28  
    29  	op, configCleanup := testOperationRefresh(t, "./testdata/refresh")
    30  	defer configCleanup()
    31  
    32  	run, err := b.Operation(context.Background(), op)
    33  	if err != nil {
    34  		t.Fatalf("bad: %s", err)
    35  	}
    36  	<-run.Done()
    37  
    38  	if !p.ReadResourceCalled {
    39  		t.Fatal("ReadResource should be called")
    40  	}
    41  
    42  	checkState(t, b.StateOutPath, `
    43  test_instance.foo:
    44    ID = yes
    45    provider = provider.test
    46  	`)
    47  }
    48  
    49  func TestLocal_refreshNoConfig(t *testing.T) {
    50  	b, cleanup := TestLocal(t)
    51  	defer cleanup()
    52  	p := TestLocalProvider(t, b, "test", refreshFixtureSchema())
    53  	terraform.TestStateFile(t, b.StatePath, testRefreshState())
    54  	p.ReadResourceFn = nil
    55  	p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{
    56  		"id": cty.StringVal("yes"),
    57  	})}
    58  
    59  	op, configCleanup := testOperationRefresh(t, "./testdata/empty")
    60  	defer configCleanup()
    61  
    62  	run, err := b.Operation(context.Background(), op)
    63  	if err != nil {
    64  		t.Fatalf("bad: %s", err)
    65  	}
    66  	<-run.Done()
    67  
    68  	if !p.ReadResourceCalled {
    69  		t.Fatal("ReadResource should be called")
    70  	}
    71  
    72  	checkState(t, b.StateOutPath, `
    73  test_instance.foo:
    74    ID = yes
    75    provider = provider.test
    76  	`)
    77  }
    78  
    79  // GH-12174
    80  func TestLocal_refreshNilModuleWithInput(t *testing.T) {
    81  	b, cleanup := TestLocal(t)
    82  	defer cleanup()
    83  	p := TestLocalProvider(t, b, "test", refreshFixtureSchema())
    84  	terraform.TestStateFile(t, b.StatePath, testRefreshState())
    85  	p.ReadResourceFn = nil
    86  	p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{
    87  		"id": cty.StringVal("yes"),
    88  	})}
    89  
    90  	b.OpInput = true
    91  
    92  	op, configCleanup := testOperationRefresh(t, "./testdata/empty")
    93  	defer configCleanup()
    94  
    95  	run, err := b.Operation(context.Background(), op)
    96  	if err != nil {
    97  		t.Fatalf("bad: %s", err)
    98  	}
    99  	<-run.Done()
   100  
   101  	if !p.ReadResourceCalled {
   102  		t.Fatal("ReadResource should be called")
   103  	}
   104  
   105  	checkState(t, b.StateOutPath, `
   106  test_instance.foo:
   107    ID = yes
   108    provider = provider.test
   109  	`)
   110  }
   111  
   112  func TestLocal_refreshInput(t *testing.T) {
   113  	b, cleanup := TestLocal(t)
   114  	defer cleanup()
   115  	p := TestLocalProvider(t, b, "test", refreshFixtureSchema())
   116  	terraform.TestStateFile(t, b.StatePath, testRefreshState())
   117  
   118  	p.GetSchemaReturn = &terraform.ProviderSchema{
   119  		Provider: &configschema.Block{
   120  			Attributes: map[string]*configschema.Attribute{
   121  				"value": {Type: cty.String, Optional: true},
   122  			},
   123  		},
   124  		ResourceTypes: map[string]*configschema.Block{
   125  			"test_instance": {
   126  				Attributes: map[string]*configschema.Attribute{
   127  					"foo": {Type: cty.String, Optional: true},
   128  					"id":  {Type: cty.String, Optional: true},
   129  				},
   130  			},
   131  		},
   132  	}
   133  	p.ReadResourceFn = nil
   134  	p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{
   135  		"id": cty.StringVal("yes"),
   136  	})}
   137  	p.ConfigureFn = func(c *terraform.ResourceConfig) error {
   138  		if v, ok := c.Get("value"); !ok || v != "bar" {
   139  			return fmt.Errorf("no value set")
   140  		}
   141  
   142  		return nil
   143  	}
   144  
   145  	// Enable input asking since it is normally disabled by default
   146  	b.OpInput = true
   147  	b.ContextOpts.UIInput = &terraform.MockUIInput{InputReturnString: "bar"}
   148  
   149  	op, configCleanup := testOperationRefresh(t, "./testdata/refresh-var-unset")
   150  	defer configCleanup()
   151  	op.UIIn = b.ContextOpts.UIInput
   152  
   153  	run, err := b.Operation(context.Background(), op)
   154  	if err != nil {
   155  		t.Fatalf("bad: %s", err)
   156  	}
   157  	<-run.Done()
   158  
   159  	if !p.ReadResourceCalled {
   160  		t.Fatal("ReadResource should be called")
   161  	}
   162  
   163  	checkState(t, b.StateOutPath, `
   164  test_instance.foo:
   165    ID = yes
   166    provider = provider.test
   167  	`)
   168  }
   169  
   170  func TestLocal_refreshValidate(t *testing.T) {
   171  	b, cleanup := TestLocal(t)
   172  	defer cleanup()
   173  	p := TestLocalProvider(t, b, "test", refreshFixtureSchema())
   174  	terraform.TestStateFile(t, b.StatePath, testRefreshState())
   175  	p.ReadResourceFn = nil
   176  	p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{
   177  		"id": cty.StringVal("yes"),
   178  	})}
   179  
   180  	// Enable validation
   181  	b.OpValidation = true
   182  
   183  	op, configCleanup := testOperationRefresh(t, "./testdata/refresh")
   184  	defer configCleanup()
   185  
   186  	run, err := b.Operation(context.Background(), op)
   187  	if err != nil {
   188  		t.Fatalf("bad: %s", err)
   189  	}
   190  	<-run.Done()
   191  
   192  	if !p.PrepareProviderConfigCalled {
   193  		t.Fatal("Prepare provider config should be called")
   194  	}
   195  
   196  	checkState(t, b.StateOutPath, `
   197  test_instance.foo:
   198    ID = yes
   199    provider = provider.test
   200  	`)
   201  }
   202  
   203  func testOperationRefresh(t *testing.T, configDir string) (*backend.Operation, func()) {
   204  	t.Helper()
   205  
   206  	_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir)
   207  
   208  	return &backend.Operation{
   209  		Type:         backend.OperationTypeRefresh,
   210  		ConfigDir:    configDir,
   211  		ConfigLoader: configLoader,
   212  	}, configCleanup
   213  }
   214  
   215  // testRefreshState is just a common state that we use for testing refresh.
   216  func testRefreshState() *terraform.State {
   217  	return &terraform.State{
   218  		Version: 2,
   219  		Modules: []*terraform.ModuleState{
   220  			&terraform.ModuleState{
   221  				Path: []string{"root"},
   222  				Resources: map[string]*terraform.ResourceState{
   223  					"test_instance.foo": &terraform.ResourceState{
   224  						Type: "test_instance",
   225  						Primary: &terraform.InstanceState{
   226  							ID: "bar",
   227  						},
   228  					},
   229  				},
   230  				Outputs: map[string]*terraform.OutputState{},
   231  			},
   232  		},
   233  	}
   234  }
   235  
   236  // refreshFixtureSchema returns a schema suitable for processing the
   237  // configuration in testdata/refresh . This schema should be
   238  // assigned to a mock provider named "test".
   239  func refreshFixtureSchema() *terraform.ProviderSchema {
   240  	return &terraform.ProviderSchema{
   241  		ResourceTypes: map[string]*configschema.Block{
   242  			"test_instance": {
   243  				Attributes: map[string]*configschema.Attribute{
   244  					"ami": {Type: cty.String, Optional: true},
   245  					"id":  {Type: cty.String, Computed: true},
   246  				},
   247  			},
   248  		},
   249  	}
   250  }