terraform: add InputMode to determine what is asked for
This commit is contained in:
parent
014bde64c4
commit
8b5c120ecf
|
@ -58,6 +58,22 @@ type ContextOpts struct {
|
||||||
UIInput UIInput
|
UIInput UIInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InputMode defines what sort of input will be asked for when Input
|
||||||
|
// is called on Context.
|
||||||
|
type InputMode byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
// InputModeVar asks for variables
|
||||||
|
InputModeVar InputMode = 1 << iota
|
||||||
|
|
||||||
|
// InputModeProvider asks for provider variables
|
||||||
|
InputModeProvider
|
||||||
|
|
||||||
|
// InputModeStd is the standard operating mode and asks for both variables
|
||||||
|
// and providers.
|
||||||
|
InputModeStd = InputModeVar | InputModeProvider
|
||||||
|
)
|
||||||
|
|
||||||
// NewContext creates a new context.
|
// NewContext creates a new context.
|
||||||
//
|
//
|
||||||
// Once a context is created, the pointer values within ContextOpts should
|
// Once a context is created, the pointer values within ContextOpts should
|
||||||
|
@ -137,75 +153,81 @@ func (c *Context) Graph() (*depgraph.Graph, error) {
|
||||||
// Input asks for input to fill variables and provider configurations.
|
// Input asks for input to fill variables and provider configurations.
|
||||||
// This modifies the configuration in-place, so asking for Input twice
|
// This modifies the configuration in-place, so asking for Input twice
|
||||||
// may result in different UI output showing different current values.
|
// may result in different UI output showing different current values.
|
||||||
func (c *Context) Input() error {
|
func (c *Context) Input(mode InputMode) error {
|
||||||
v := c.acquireRun()
|
v := c.acquireRun()
|
||||||
defer c.releaseRun(v)
|
defer c.releaseRun(v)
|
||||||
|
|
||||||
// Walk the variables first for the root module. We walk them in
|
if mode&InputModeVar != 0 {
|
||||||
// alphabetical order for UX reasons.
|
// Walk the variables first for the root module. We walk them in
|
||||||
rootConf := c.module.Config()
|
// alphabetical order for UX reasons.
|
||||||
names := make([]string, len(rootConf.Variables))
|
rootConf := c.module.Config()
|
||||||
m := make(map[string]*config.Variable)
|
names := make([]string, len(rootConf.Variables))
|
||||||
for i, v := range rootConf.Variables {
|
m := make(map[string]*config.Variable)
|
||||||
names[i] = v.Name
|
for i, v := range rootConf.Variables {
|
||||||
m[v.Name] = v
|
names[i] = v.Name
|
||||||
}
|
m[v.Name] = v
|
||||||
sort.Strings(names)
|
|
||||||
for _, n := range names {
|
|
||||||
v := m[n]
|
|
||||||
switch v.Type() {
|
|
||||||
case config.VariableTypeMap:
|
|
||||||
continue
|
|
||||||
case config.VariableTypeString:
|
|
||||||
// Good!
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("Unknown variable type: %s", v.Type()))
|
|
||||||
}
|
}
|
||||||
|
sort.Strings(names)
|
||||||
var defaultString string
|
for _, n := range names {
|
||||||
if v.Default != nil {
|
v := m[n]
|
||||||
defaultString = v.Default.(string)
|
switch v.Type() {
|
||||||
}
|
case config.VariableTypeMap:
|
||||||
|
|
||||||
// Ask the user for a value for this variable
|
|
||||||
var value string
|
|
||||||
for {
|
|
||||||
var err error
|
|
||||||
value, err = c.uiInput.Input(&InputOpts{
|
|
||||||
Id: fmt.Sprintf("var.%s", n),
|
|
||||||
Query: fmt.Sprintf("var.%s", n),
|
|
||||||
Default: defaultString,
|
|
||||||
Description: v.Description,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"Error asking for %s: %s", n, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if value == "" && v.Required() {
|
|
||||||
// Redo if it is required.
|
|
||||||
continue
|
continue
|
||||||
|
case config.VariableTypeString:
|
||||||
|
// Good!
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unknown variable type: %s", v.Type()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if value == "" {
|
var defaultString string
|
||||||
// No value, just exit the loop. With no value, we just
|
if v.Default != nil {
|
||||||
// use whatever is currently set in variables.
|
defaultString = v.Default.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask the user for a value for this variable
|
||||||
|
var value string
|
||||||
|
for {
|
||||||
|
var err error
|
||||||
|
value, err = c.uiInput.Input(&InputOpts{
|
||||||
|
Id: fmt.Sprintf("var.%s", n),
|
||||||
|
Query: fmt.Sprintf("var.%s", n),
|
||||||
|
Default: defaultString,
|
||||||
|
Description: v.Description,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error asking for %s: %s", n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == "" && v.Required() {
|
||||||
|
// Redo if it is required.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == "" {
|
||||||
|
// No value, just exit the loop. With no value, we just
|
||||||
|
// use whatever is currently set in variables.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
if value != "" {
|
||||||
}
|
c.variables[n] = value
|
||||||
|
}
|
||||||
if value != "" {
|
|
||||||
c.variables[n] = value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the walk context and walk the inputs, which will gather the
|
if mode&InputModeProvider != 0 {
|
||||||
// inputs for any resource providers.
|
// Create the walk context and walk the inputs, which will gather the
|
||||||
wc := c.walkContext(walkInput, rootModulePath)
|
// inputs for any resource providers.
|
||||||
wc.Meta = new(walkInputMeta)
|
wc := c.walkContext(walkInput, rootModulePath)
|
||||||
return wc.Walk()
|
wc.Meta = new(walkInputMeta)
|
||||||
|
return wc.Walk()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plan generates an execution plan for the given context.
|
// Plan generates an execution plan for the given context.
|
||||||
|
|
|
@ -460,7 +460,7 @@ func TestContextInput(t *testing.T) {
|
||||||
"var.foo": "us-east-1",
|
"var.foo": "us-east-1",
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ctx.Input(); err != nil {
|
if err := ctx.Input(InputModeStd); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,7 +502,7 @@ func TestContextInput_provider(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ctx.Input(); err != nil {
|
if err := ctx.Input(InputModeStd); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,7 +552,7 @@ func TestContextInput_providerId(t *testing.T) {
|
||||||
"provider.aws.foo": "bar",
|
"provider.aws.foo": "bar",
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ctx.Input(); err != nil {
|
if err := ctx.Input(InputModeStd); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,6 +569,116 @@ func TestContextInput_providerId(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextInput_providerOnly(t *testing.T) {
|
||||||
|
input := new(MockUIInput)
|
||||||
|
m := testModule(t, "input-provider-vars")
|
||||||
|
p := testProvider("aws")
|
||||||
|
p.ApplyFn = testApplyFn
|
||||||
|
p.DiffFn = testDiffFn
|
||||||
|
ctx := testContext(t, &ContextOpts{
|
||||||
|
Module: m,
|
||||||
|
Providers: map[string]ResourceProviderFactory{
|
||||||
|
"aws": testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
Variables: map[string]string{
|
||||||
|
"foo": "us-west-2",
|
||||||
|
},
|
||||||
|
UIInput: input,
|
||||||
|
})
|
||||||
|
|
||||||
|
input.InputReturnMap = map[string]string{
|
||||||
|
"var.foo": "us-east-1",
|
||||||
|
}
|
||||||
|
|
||||||
|
var actual interface{}
|
||||||
|
p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) {
|
||||||
|
c.Raw["foo"] = "bar"
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
p.ConfigureFn = func(c *ResourceConfig) error {
|
||||||
|
actual = c.Raw["foo"]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.Input(InputModeProvider); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ctx.Plan(nil); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
state, err := ctx.Apply()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(actual, "bar") {
|
||||||
|
t.Fatalf("bad: %#v", actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
actualStr := strings.TrimSpace(state.String())
|
||||||
|
expectedStr := strings.TrimSpace(testTerraformInputProviderOnlyStr)
|
||||||
|
if actualStr != expectedStr {
|
||||||
|
t.Fatalf("bad: \n%s", actualStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextInput_varOnly(t *testing.T) {
|
||||||
|
input := new(MockUIInput)
|
||||||
|
m := testModule(t, "input-provider-vars")
|
||||||
|
p := testProvider("aws")
|
||||||
|
p.ApplyFn = testApplyFn
|
||||||
|
p.DiffFn = testDiffFn
|
||||||
|
ctx := testContext(t, &ContextOpts{
|
||||||
|
Module: m,
|
||||||
|
Providers: map[string]ResourceProviderFactory{
|
||||||
|
"aws": testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
Variables: map[string]string{
|
||||||
|
"foo": "us-west-2",
|
||||||
|
},
|
||||||
|
UIInput: input,
|
||||||
|
})
|
||||||
|
|
||||||
|
input.InputReturnMap = map[string]string{
|
||||||
|
"var.foo": "us-east-1",
|
||||||
|
}
|
||||||
|
|
||||||
|
var actual interface{}
|
||||||
|
p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) {
|
||||||
|
c.Raw["foo"] = "bar"
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
p.ConfigureFn = func(c *ResourceConfig) error {
|
||||||
|
actual = c.Raw["foo"]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.Input(InputModeVar); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ctx.Plan(nil); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
state, err := ctx.Apply()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.DeepEqual(actual, "bar") {
|
||||||
|
t.Fatalf("bad: %#v", actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
actualStr := strings.TrimSpace(state.String())
|
||||||
|
expectedStr := strings.TrimSpace(testTerraformInputVarOnlyStr)
|
||||||
|
if actualStr != expectedStr {
|
||||||
|
t.Fatalf("bad: \n%s", actualStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestContextApply(t *testing.T) {
|
func TestContextApply(t *testing.T) {
|
||||||
m := testModule(t, "apply-good")
|
m := testModule(t, "apply-good")
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
|
|
|
@ -126,6 +126,20 @@ aws_instance.foo:
|
||||||
type = aws_instance
|
type = aws_instance
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const testTerraformInputProviderOnlyStr = `
|
||||||
|
aws_instance.foo:
|
||||||
|
ID = foo
|
||||||
|
foo = us-west-2
|
||||||
|
type = aws_instance
|
||||||
|
`
|
||||||
|
|
||||||
|
const testTerraformInputVarOnlyStr = `
|
||||||
|
aws_instance.foo:
|
||||||
|
ID = foo
|
||||||
|
foo = us-east-1
|
||||||
|
type = aws_instance
|
||||||
|
`
|
||||||
|
|
||||||
const testTerraformInputVarsStr = `
|
const testTerraformInputVarsStr = `
|
||||||
aws_instance.bar:
|
aws_instance.bar:
|
||||||
ID = foo
|
ID = foo
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
variable "foo" {}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
foo = "${var.foo}"
|
||||||
|
}
|
Loading…
Reference in New Issue