mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-18 20:52:58 -06:00
36d0a50427
This is part of a general effort to move all of Terraform's non-library package surface under internal in order to reinforce that these are for internal use within Terraform only. If you were previously importing packages under this prefix into an external codebase, you could pin to an earlier release tag as an interim solution until you've make a plan to achieve the same functionality some other way.
450 lines
11 KiB
Go
450 lines
11 KiB
Go
package terraform
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/configs/configschema"
|
|
"github.com/hashicorp/terraform/internal/providers"
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
)
|
|
|
|
func TestContext2Input_provider(t *testing.T) {
|
|
m := testModule(t, "input-provider")
|
|
p := testProvider("aws")
|
|
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
|
|
Provider: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"foo": {
|
|
Type: cty.String,
|
|
Required: true,
|
|
Description: "something something",
|
|
},
|
|
},
|
|
},
|
|
ResourceTypes: map[string]*configschema.Block{
|
|
"aws_instance": {
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"id": {
|
|
Type: cty.String,
|
|
Computed: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
inp := &MockUIInput{
|
|
InputReturnMap: map[string]string{
|
|
"provider.aws.foo": "bar",
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Providers: map[addrs.Provider]providers.Factory{
|
|
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
|
},
|
|
UIInput: inp,
|
|
})
|
|
|
|
var actual interface{}
|
|
p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
|
actual = req.Config.GetAttr("foo").AsString()
|
|
return
|
|
}
|
|
|
|
if diags := ctx.Input(InputModeStd); diags.HasErrors() {
|
|
t.Fatalf("input errors: %s", diags.Err())
|
|
}
|
|
|
|
if !inp.InputCalled {
|
|
t.Fatal("no input prompt; want prompt for argument \"foo\"")
|
|
}
|
|
if got, want := inp.InputOpts.Description, "something something"; got != want {
|
|
t.Errorf("wrong description\ngot: %q\nwant: %q", got, want)
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
if !reflect.DeepEqual(actual, "bar") {
|
|
t.Fatalf("wrong result\ngot: %#v\nwant: %#v", actual, "bar")
|
|
}
|
|
}
|
|
|
|
func TestContext2Input_providerMulti(t *testing.T) {
|
|
m := testModule(t, "input-provider-multi")
|
|
|
|
p := testProvider("aws")
|
|
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
|
|
Provider: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"foo": {
|
|
Type: cty.String,
|
|
Required: true,
|
|
Description: "something something",
|
|
},
|
|
},
|
|
},
|
|
ResourceTypes: map[string]*configschema.Block{
|
|
"aws_instance": {
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"id": {
|
|
Type: cty.String,
|
|
Computed: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
inp := &MockUIInput{
|
|
InputReturnMap: map[string]string{
|
|
"provider.aws.foo": "bar",
|
|
"provider.aws.east.foo": "bar",
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Providers: map[addrs.Provider]providers.Factory{
|
|
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
|
},
|
|
UIInput: inp,
|
|
})
|
|
|
|
var actual []interface{}
|
|
var lock sync.Mutex
|
|
|
|
if diags := ctx.Input(InputModeStd); diags.HasErrors() {
|
|
t.Fatalf("input errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
actual = append(actual, req.Config.GetAttr("foo").AsString())
|
|
return
|
|
}
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
expected := []interface{}{"bar", "bar"}
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Fatalf("wrong result\ngot: %#v\nwant: %#v", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Input_providerOnce(t *testing.T) {
|
|
m := testModule(t, "input-provider-once")
|
|
p := testProvider("aws")
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Providers: map[addrs.Provider]providers.Factory{
|
|
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if diags := ctx.Input(InputModeStd); diags.HasErrors() {
|
|
t.Fatalf("input errors: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
func TestContext2Input_providerId(t *testing.T) {
|
|
input := new(MockUIInput)
|
|
|
|
m := testModule(t, "input-provider")
|
|
|
|
p := testProvider("aws")
|
|
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
|
|
Provider: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"foo": {
|
|
Type: cty.String,
|
|
Required: true,
|
|
Description: "something something",
|
|
},
|
|
},
|
|
},
|
|
ResourceTypes: map[string]*configschema.Block{
|
|
"aws_instance": {
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"id": {
|
|
Type: cty.String,
|
|
Computed: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Providers: map[addrs.Provider]providers.Factory{
|
|
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
|
},
|
|
UIInput: input,
|
|
})
|
|
|
|
var actual interface{}
|
|
p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
|
actual = req.Config.GetAttr("foo").AsString()
|
|
return
|
|
}
|
|
|
|
input.InputReturnMap = map[string]string{
|
|
"provider.aws.foo": "bar",
|
|
}
|
|
|
|
if diags := ctx.Input(InputModeStd); diags.HasErrors() {
|
|
t.Fatalf("input errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
if !reflect.DeepEqual(actual, "bar") {
|
|
t.Fatalf("wrong result\ngot: %#v\nwant: %#v", actual, "bar")
|
|
}
|
|
}
|
|
|
|
func TestContext2Input_providerOnly(t *testing.T) {
|
|
input := new(MockUIInput)
|
|
|
|
m := testModule(t, "input-provider-vars")
|
|
p := testProvider("aws")
|
|
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
|
|
Provider: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"foo": {
|
|
Type: cty.String,
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
ResourceTypes: map[string]*configschema.Block{
|
|
"aws_instance": {
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"foo": {Type: cty.String, Required: true},
|
|
"id": {Type: cty.String, Computed: true},
|
|
"type": {Type: cty.String, Computed: true},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Providers: map[addrs.Provider]providers.Factory{
|
|
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
|
},
|
|
Variables: InputValues{
|
|
"foo": &InputValue{
|
|
Value: cty.StringVal("us-west-2"),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
UIInput: input,
|
|
})
|
|
|
|
input.InputReturnMap = map[string]string{
|
|
"provider.aws.foo": "bar",
|
|
}
|
|
|
|
var actual interface{}
|
|
p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
|
actual = req.Config.GetAttr("foo").AsString()
|
|
return
|
|
}
|
|
|
|
if err := ctx.Input(InputModeProvider); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(actual, "bar") {
|
|
t.Fatalf("wrong result\ngot: %#v\nwant: %#v", actual, "bar")
|
|
}
|
|
|
|
actualStr := strings.TrimSpace(state.String())
|
|
expectedStr := strings.TrimSpace(testTerraformInputProviderOnlyStr)
|
|
if actualStr != expectedStr {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actualStr, expectedStr)
|
|
}
|
|
}
|
|
|
|
func TestContext2Input_providerVars(t *testing.T) {
|
|
input := new(MockUIInput)
|
|
m := testModule(t, "input-provider-with-vars")
|
|
p := testProvider("aws")
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Providers: map[addrs.Provider]providers.Factory{
|
|
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
|
},
|
|
Variables: InputValues{
|
|
"foo": &InputValue{
|
|
Value: cty.StringVal("bar"),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
UIInput: input,
|
|
})
|
|
|
|
input.InputReturnMap = map[string]string{
|
|
"var.foo": "bar",
|
|
}
|
|
|
|
var actual interface{}
|
|
p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
|
actual = req.Config.GetAttr("foo").AsString()
|
|
return
|
|
}
|
|
if diags := ctx.Input(InputModeStd); diags.HasErrors() {
|
|
t.Fatalf("input errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
if !reflect.DeepEqual(actual, "bar") {
|
|
t.Fatalf("bad: %#v", actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Input_providerVarsModuleInherit(t *testing.T) {
|
|
input := new(MockUIInput)
|
|
m := testModule(t, "input-provider-with-vars-and-module")
|
|
p := testProvider("aws")
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Providers: map[addrs.Provider]providers.Factory{
|
|
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
|
},
|
|
UIInput: input,
|
|
})
|
|
|
|
if diags := ctx.Input(InputModeStd); diags.HasErrors() {
|
|
t.Fatalf("input errors: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
// adding a list interpolation in fails to interpolate the count variable
|
|
func TestContext2Input_submoduleTriggersInvalidCount(t *testing.T) {
|
|
input := new(MockUIInput)
|
|
m := testModule(t, "input-submodule-count")
|
|
p := testProvider("aws")
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Providers: map[addrs.Provider]providers.Factory{
|
|
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
|
},
|
|
UIInput: input,
|
|
})
|
|
|
|
if diags := ctx.Input(InputModeStd); diags.HasErrors() {
|
|
t.Fatalf("input errors: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
// In this case, a module variable can't be resolved from a data source until
|
|
// it's refreshed, but it can't be refreshed during Input.
|
|
func TestContext2Input_dataSourceRequiresRefresh(t *testing.T) {
|
|
input := new(MockUIInput)
|
|
p := testProvider("null")
|
|
m := testModule(t, "input-module-data-vars")
|
|
|
|
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
|
|
DataSources: map[string]*configschema.Block{
|
|
"null_data_source": {
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"foo": {Type: cty.List(cty.String), Optional: true},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
|
|
return providers.ReadDataSourceResponse{
|
|
State: req.Config,
|
|
}
|
|
}
|
|
|
|
state := states.BuildState(func(s *states.SyncState) {
|
|
s.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.DataResourceMode,
|
|
Type: "null_data_source",
|
|
Name: "bar",
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
|
&states.ResourceInstanceObjectSrc{
|
|
AttrsFlat: map[string]string{
|
|
"id": "-",
|
|
"foo.#": "1",
|
|
"foo.0": "a",
|
|
// foo.1 exists in the data source, but needs to be refreshed.
|
|
},
|
|
Status: states.ObjectReady,
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("null"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
})
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Providers: map[addrs.Provider]providers.Factory{
|
|
addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
UIInput: input,
|
|
})
|
|
|
|
if diags := ctx.Input(InputModeStd); diags.HasErrors() {
|
|
t.Fatalf("input errors: %s", diags.Err())
|
|
}
|
|
|
|
// ensure that plan works after Refresh
|
|
if _, diags := ctx.Refresh(); diags.HasErrors() {
|
|
t.Fatalf("refresh errors: %s", diags.Err())
|
|
}
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
}
|