github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/terraform/context_input_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"reflect"
     5  	"strings"
     6  	"sync"
     7  	"testing"
     8  
     9  	"github.com/zclconf/go-cty/cty"
    10  
    11  	"github.com/muratcelep/terraform/not-internal/addrs"
    12  	"github.com/muratcelep/terraform/not-internal/configs/configschema"
    13  	"github.com/muratcelep/terraform/not-internal/plans"
    14  	"github.com/muratcelep/terraform/not-internal/providers"
    15  	"github.com/muratcelep/terraform/not-internal/states"
    16  )
    17  
    18  func TestContext2Input_provider(t *testing.T) {
    19  	m := testModule(t, "input-provider")
    20  	p := testProvider("aws")
    21  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
    22  		Provider: &configschema.Block{
    23  			Attributes: map[string]*configschema.Attribute{
    24  				"foo": {
    25  					Type:        cty.String,
    26  					Required:    true,
    27  					Description: "something something",
    28  				},
    29  			},
    30  		},
    31  		ResourceTypes: map[string]*configschema.Block{
    32  			"aws_instance": {
    33  				Attributes: map[string]*configschema.Attribute{
    34  					"id": {
    35  						Type:     cty.String,
    36  						Computed: true,
    37  					},
    38  				},
    39  			},
    40  		},
    41  	})
    42  
    43  	inp := &MockUIInput{
    44  		InputReturnMap: map[string]string{
    45  			"provider.aws.foo": "bar",
    46  		},
    47  	}
    48  
    49  	ctx := testContext2(t, &ContextOpts{
    50  		Providers: map[addrs.Provider]providers.Factory{
    51  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
    52  		},
    53  		UIInput: inp,
    54  	})
    55  
    56  	var actual interface{}
    57  	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
    58  		actual = req.Config.GetAttr("foo").AsString()
    59  		return
    60  	}
    61  
    62  	if diags := ctx.Input(m, InputModeStd); diags.HasErrors() {
    63  		t.Fatalf("input errors: %s", diags.Err())
    64  	}
    65  
    66  	if !inp.InputCalled {
    67  		t.Fatal("no input prompt; want prompt for argument \"foo\"")
    68  	}
    69  	if got, want := inp.InputOpts.Description, "something something"; got != want {
    70  		t.Errorf("wrong description\ngot:  %q\nwant: %q", got, want)
    71  	}
    72  
    73  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
    74  	assertNoErrors(t, diags)
    75  
    76  	if _, diags := ctx.Apply(plan, m); diags.HasErrors() {
    77  		t.Fatalf("apply errors: %s", diags.Err())
    78  	}
    79  
    80  	if !reflect.DeepEqual(actual, "bar") {
    81  		t.Fatalf("wrong result\ngot:  %#v\nwant: %#v", actual, "bar")
    82  	}
    83  }
    84  
    85  func TestContext2Input_providerMulti(t *testing.T) {
    86  	m := testModule(t, "input-provider-multi")
    87  
    88  	getProviderSchemaResponse := getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
    89  		Provider: &configschema.Block{
    90  			Attributes: map[string]*configschema.Attribute{
    91  				"foo": {
    92  					Type:        cty.String,
    93  					Required:    true,
    94  					Description: "something something",
    95  				},
    96  			},
    97  		},
    98  		ResourceTypes: map[string]*configschema.Block{
    99  			"aws_instance": {
   100  				Attributes: map[string]*configschema.Attribute{
   101  					"id": {
   102  						Type:     cty.String,
   103  						Computed: true,
   104  					},
   105  				},
   106  			},
   107  		},
   108  	})
   109  
   110  	// In order to update the provider to check only the configure calls during
   111  	// apply, we will need to inject a new factory function after plan. We must
   112  	// use a closure around the factory, because in order for the inputs to
   113  	// work during apply we need to maintain the same context value, preventing
   114  	// us from assigning a new Providers map.
   115  	providerFactory := func() (providers.Interface, error) {
   116  		p := testProvider("aws")
   117  		p.GetProviderSchemaResponse = getProviderSchemaResponse
   118  		return p, nil
   119  	}
   120  
   121  	inp := &MockUIInput{
   122  		InputReturnMap: map[string]string{
   123  			"provider.aws.foo":      "bar",
   124  			"provider.aws.east.foo": "bar",
   125  		},
   126  	}
   127  
   128  	ctx := testContext2(t, &ContextOpts{
   129  		Providers: map[addrs.Provider]providers.Factory{
   130  			addrs.NewDefaultProvider("aws"): func() (providers.Interface, error) {
   131  				return providerFactory()
   132  			},
   133  		},
   134  		UIInput: inp,
   135  	})
   136  
   137  	var actual []interface{}
   138  	var lock sync.Mutex
   139  
   140  	if diags := ctx.Input(m, InputModeStd); diags.HasErrors() {
   141  		t.Fatalf("input errors: %s", diags.Err())
   142  	}
   143  
   144  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
   145  	assertNoErrors(t, diags)
   146  
   147  	providerFactory = func() (providers.Interface, error) {
   148  		p := testProvider("aws")
   149  		p.GetProviderSchemaResponse = getProviderSchemaResponse
   150  		p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
   151  			lock.Lock()
   152  			defer lock.Unlock()
   153  			actual = append(actual, req.Config.GetAttr("foo").AsString())
   154  			return
   155  		}
   156  		return p, nil
   157  	}
   158  
   159  	if _, diags := ctx.Apply(plan, m); diags.HasErrors() {
   160  		t.Fatalf("apply errors: %s", diags.Err())
   161  	}
   162  
   163  	expected := []interface{}{"bar", "bar"}
   164  	if !reflect.DeepEqual(actual, expected) {
   165  		t.Fatalf("wrong result\ngot:  %#v\nwant: %#v", actual, expected)
   166  	}
   167  }
   168  
   169  func TestContext2Input_providerOnce(t *testing.T) {
   170  	m := testModule(t, "input-provider-once")
   171  	p := testProvider("aws")
   172  	ctx := testContext2(t, &ContextOpts{
   173  		Providers: map[addrs.Provider]providers.Factory{
   174  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   175  		},
   176  	})
   177  
   178  	if diags := ctx.Input(m, InputModeStd); diags.HasErrors() {
   179  		t.Fatalf("input errors: %s", diags.Err())
   180  	}
   181  }
   182  
   183  func TestContext2Input_providerId(t *testing.T) {
   184  	input := new(MockUIInput)
   185  
   186  	m := testModule(t, "input-provider")
   187  
   188  	p := testProvider("aws")
   189  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
   190  		Provider: &configschema.Block{
   191  			Attributes: map[string]*configschema.Attribute{
   192  				"foo": {
   193  					Type:        cty.String,
   194  					Required:    true,
   195  					Description: "something something",
   196  				},
   197  			},
   198  		},
   199  		ResourceTypes: map[string]*configschema.Block{
   200  			"aws_instance": {
   201  				Attributes: map[string]*configschema.Attribute{
   202  					"id": {
   203  						Type:     cty.String,
   204  						Computed: true,
   205  					},
   206  				},
   207  			},
   208  		},
   209  	})
   210  
   211  	ctx := testContext2(t, &ContextOpts{
   212  		Providers: map[addrs.Provider]providers.Factory{
   213  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   214  		},
   215  		UIInput: input,
   216  	})
   217  
   218  	var actual interface{}
   219  	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
   220  		actual = req.Config.GetAttr("foo").AsString()
   221  		return
   222  	}
   223  
   224  	input.InputReturnMap = map[string]string{
   225  		"provider.aws.foo": "bar",
   226  	}
   227  
   228  	if diags := ctx.Input(m, InputModeStd); diags.HasErrors() {
   229  		t.Fatalf("input errors: %s", diags.Err())
   230  	}
   231  
   232  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
   233  	assertNoErrors(t, diags)
   234  
   235  	if _, diags := ctx.Apply(plan, m); diags.HasErrors() {
   236  		t.Fatalf("apply errors: %s", diags.Err())
   237  	}
   238  
   239  	if !reflect.DeepEqual(actual, "bar") {
   240  		t.Fatalf("wrong result\ngot:  %#v\nwant: %#v", actual, "bar")
   241  	}
   242  }
   243  
   244  func TestContext2Input_providerOnly(t *testing.T) {
   245  	input := new(MockUIInput)
   246  
   247  	m := testModule(t, "input-provider-vars")
   248  	p := testProvider("aws")
   249  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
   250  		Provider: &configschema.Block{
   251  			Attributes: map[string]*configschema.Attribute{
   252  				"foo": {
   253  					Type:     cty.String,
   254  					Required: true,
   255  				},
   256  			},
   257  		},
   258  		ResourceTypes: map[string]*configschema.Block{
   259  			"aws_instance": {
   260  				Attributes: map[string]*configschema.Attribute{
   261  					"foo":  {Type: cty.String, Required: true},
   262  					"id":   {Type: cty.String, Computed: true},
   263  					"type": {Type: cty.String, Computed: true},
   264  				},
   265  			},
   266  		},
   267  	})
   268  
   269  	ctx := testContext2(t, &ContextOpts{
   270  		Providers: map[addrs.Provider]providers.Factory{
   271  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   272  		},
   273  		UIInput: input,
   274  	})
   275  
   276  	input.InputReturnMap = map[string]string{
   277  		"provider.aws.foo": "bar",
   278  	}
   279  
   280  	var actual interface{}
   281  	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
   282  		actual = req.Config.GetAttr("foo").AsString()
   283  		return
   284  	}
   285  
   286  	if err := ctx.Input(m, InputModeProvider); err != nil {
   287  		t.Fatalf("err: %s", err)
   288  	}
   289  
   290  	// NOTE: This is a stale test case from an older version of Terraform
   291  	// where Input was responsible for prompting for both input variables _and_
   292  	// provider configuration arguments, where it was trying to test the case
   293  	// where we were turning off the mode of prompting for input variables.
   294  	// That's now always disabled, and so this is essentially the same as the
   295  	// normal Input test, but we're preserving it until we have time to review
   296  	// and make sure this isn't inadvertently providing unique test coverage
   297  	// other than what it set out to test.
   298  	plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
   299  		Mode: plans.NormalMode,
   300  		SetVariables: InputValues{
   301  			"foo": &InputValue{
   302  				Value:      cty.StringVal("us-west-2"),
   303  				SourceType: ValueFromCaller,
   304  			},
   305  		},
   306  	})
   307  	assertNoErrors(t, diags)
   308  
   309  	state, err := ctx.Apply(plan, m)
   310  	if err != nil {
   311  		t.Fatalf("err: %s", err)
   312  	}
   313  
   314  	if !reflect.DeepEqual(actual, "bar") {
   315  		t.Fatalf("wrong result\ngot:  %#v\nwant: %#v", actual, "bar")
   316  	}
   317  
   318  	actualStr := strings.TrimSpace(state.String())
   319  	expectedStr := strings.TrimSpace(testTerraformInputProviderOnlyStr)
   320  	if actualStr != expectedStr {
   321  		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actualStr, expectedStr)
   322  	}
   323  }
   324  
   325  func TestContext2Input_providerVars(t *testing.T) {
   326  	input := new(MockUIInput)
   327  	m := testModule(t, "input-provider-with-vars")
   328  	p := testProvider("aws")
   329  	ctx := testContext2(t, &ContextOpts{
   330  		Providers: map[addrs.Provider]providers.Factory{
   331  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   332  		},
   333  		UIInput: input,
   334  	})
   335  
   336  	input.InputReturnMap = map[string]string{
   337  		"var.foo": "bar",
   338  	}
   339  
   340  	var actual interface{}
   341  	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
   342  		actual = req.Config.GetAttr("foo").AsString()
   343  		return
   344  	}
   345  	if diags := ctx.Input(m, InputModeStd); diags.HasErrors() {
   346  		t.Fatalf("input errors: %s", diags.Err())
   347  	}
   348  
   349  	plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
   350  		Mode: plans.NormalMode,
   351  		SetVariables: InputValues{
   352  			"foo": &InputValue{
   353  				Value:      cty.StringVal("bar"),
   354  				SourceType: ValueFromCaller,
   355  			},
   356  		},
   357  	})
   358  	assertNoErrors(t, diags)
   359  
   360  	if _, diags := ctx.Apply(plan, m); diags.HasErrors() {
   361  		t.Fatalf("apply errors: %s", diags.Err())
   362  	}
   363  
   364  	if !reflect.DeepEqual(actual, "bar") {
   365  		t.Fatalf("bad: %#v", actual)
   366  	}
   367  }
   368  
   369  func TestContext2Input_providerVarsModuleInherit(t *testing.T) {
   370  	input := new(MockUIInput)
   371  	m := testModule(t, "input-provider-with-vars-and-module")
   372  	p := testProvider("aws")
   373  	ctx := testContext2(t, &ContextOpts{
   374  		Providers: map[addrs.Provider]providers.Factory{
   375  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   376  		},
   377  		UIInput: input,
   378  	})
   379  
   380  	if diags := ctx.Input(m, InputModeStd); diags.HasErrors() {
   381  		t.Fatalf("input errors: %s", diags.Err())
   382  	}
   383  }
   384  
   385  // adding a list interpolation in fails to interpolate the count variable
   386  func TestContext2Input_submoduleTriggersInvalidCount(t *testing.T) {
   387  	input := new(MockUIInput)
   388  	m := testModule(t, "input-submodule-count")
   389  	p := testProvider("aws")
   390  	ctx := testContext2(t, &ContextOpts{
   391  		Providers: map[addrs.Provider]providers.Factory{
   392  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   393  		},
   394  		UIInput: input,
   395  	})
   396  
   397  	if diags := ctx.Input(m, InputModeStd); diags.HasErrors() {
   398  		t.Fatalf("input errors: %s", diags.Err())
   399  	}
   400  }
   401  
   402  // In this case, a module variable can't be resolved from a data source until
   403  // it's refreshed, but it can't be refreshed during Input.
   404  func TestContext2Input_dataSourceRequiresRefresh(t *testing.T) {
   405  	input := new(MockUIInput)
   406  	p := testProvider("null")
   407  	m := testModule(t, "input-module-data-vars")
   408  
   409  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
   410  		DataSources: map[string]*configschema.Block{
   411  			"null_data_source": {
   412  				Attributes: map[string]*configschema.Attribute{
   413  					"foo": {Type: cty.List(cty.String), Optional: true},
   414  				},
   415  			},
   416  		},
   417  	})
   418  	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
   419  		return providers.ReadDataSourceResponse{
   420  			State: req.Config,
   421  		}
   422  	}
   423  
   424  	state := states.BuildState(func(s *states.SyncState) {
   425  		s.SetResourceInstanceCurrent(
   426  			addrs.Resource{
   427  				Mode: addrs.DataResourceMode,
   428  				Type: "null_data_source",
   429  				Name: "bar",
   430  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   431  			&states.ResourceInstanceObjectSrc{
   432  				AttrsFlat: map[string]string{
   433  					"id":    "-",
   434  					"foo.#": "1",
   435  					"foo.0": "a",
   436  					// foo.1 exists in the data source, but needs to be refreshed.
   437  				},
   438  				Status: states.ObjectReady,
   439  			},
   440  			addrs.AbsProviderConfig{
   441  				Provider: addrs.NewDefaultProvider("null"),
   442  				Module:   addrs.RootModule,
   443  			},
   444  		)
   445  	})
   446  
   447  	ctx := testContext2(t, &ContextOpts{
   448  		Providers: map[addrs.Provider]providers.Factory{
   449  			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
   450  		},
   451  		UIInput: input,
   452  	})
   453  
   454  	if diags := ctx.Input(m, InputModeStd); diags.HasErrors() {
   455  		t.Fatalf("input errors: %s", diags.Err())
   456  	}
   457  
   458  	// ensure that plan works after Refresh. This is a legacy test that
   459  	// doesn't really make sense anymore, because Refresh is really just
   460  	// a wrapper around plan anyway, but we're keeping it until we get a
   461  	// chance to review and check whether it's giving us any additional
   462  	// test coverage aside from what it's specifically intending to test.
   463  	if _, diags := ctx.Refresh(m, state, DefaultPlanOpts); diags.HasErrors() {
   464  		t.Fatalf("refresh errors: %s", diags.Err())
   465  	}
   466  	if _, diags := ctx.Plan(m, state, DefaultPlanOpts); diags.HasErrors() {
   467  		t.Fatalf("plan errors: %s", diags.Err())
   468  	}
   469  }