terraform: provider input

This commit is contained in:
Mitchell Hashimoto 2015-02-13 17:59:54 -08:00
parent f3714f1efc
commit 7c78a3749e
7 changed files with 184 additions and 38 deletions

View File

@ -57,8 +57,9 @@ type Context2 struct {
uiInput UIInput
variables map[string]string
l sync.Mutex // Lock acquired during any task
runCh <-chan struct{}
l sync.Mutex // Lock acquired during any task
providerInputConfig map[string]map[string]interface{}
runCh <-chan struct{}
}
// NewContext creates a new Context structure.
@ -81,15 +82,16 @@ func NewContext2(opts *ContextOpts) *Context2 {
}
return &Context2{
diff: opts.Diff,
hooks: hooks,
module: opts.Module,
providers: opts.Providers,
provisioners: opts.Provisioners,
sh: sh,
state: state,
uiInput: opts.UIInput,
variables: opts.Variables,
diff: opts.Diff,
hooks: hooks,
module: opts.Module,
providers: opts.Providers,
providerInputConfig: make(map[string]map[string]interface{}),
provisioners: opts.Provisioners,
sh: sh,
state: state,
uiInput: opts.UIInput,
variables: opts.Variables,
}
}
@ -185,15 +187,12 @@ func (c *Context2) Input(mode InputMode) error {
}
}
/*
if mode&InputModeProvider != 0 {
// Create the walk context and walk the inputs, which will gather the
// inputs for any resource providers.
wc := c.walkContext(walkInput, rootModulePath)
wc.Meta = new(walkInputMeta)
return wc.Walk()
if mode&InputModeProvider != 0 {
// Do the walk
if _, err := c.walk(walkInput); err != nil {
return err
}
*/
}
return nil
}

View File

@ -2424,13 +2424,12 @@ func TestContext2Input(t *testing.T) {
}
}
/*
func TestContextInput_provider(t *testing.T) {
func TestContext2Input_provider(t *testing.T) {
m := testModule(t, "input-provider")
p := testProvider("aws")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
ctx := testContext(t, &ContextOpts{
ctx := testContext2(t, &ContextOpts{
Module: m,
Providers: map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
@ -2464,6 +2463,7 @@ func TestContextInput_provider(t *testing.T) {
}
}
/*
func TestContextInput_providerId(t *testing.T) {
input := new(MockUIInput)
m := testModule(t, "input-provider")

View File

@ -15,6 +15,9 @@ type EvalContext interface {
// hook and should return the hook action to take and the error.
Hook(func(Hook) (HookAction, error)) error
// Input is the UIInput object for interacting with the UI.
Input() UIInput
// InitProvider initializes the provider with the given name and
// returns the implementation of the resource provider or an error.
//
@ -32,6 +35,11 @@ type EvalContext interface {
ConfigureProvider(string, *ResourceConfig) error
ParentProviderConfig(string) *ResourceConfig
// ProviderInput and SetProviderInput are used to configure providers
// from user input.
ProviderInput(string) map[string]interface{}
SetProviderInput(string, map[string]interface{})
// InitProvisioner initializes the provisioner with the given name and
// returns the implementation of the resource provisioner or an error.
//
@ -69,6 +77,9 @@ type MockEvalContext struct {
HookCalled bool
HookError error
InputCalled bool
InputInput UIInput
InitProviderCalled bool
InitProviderName string
InitProviderProvider ResourceProvider
@ -78,6 +89,14 @@ type MockEvalContext struct {
ProviderName string
ProviderProvider ResourceProvider
ProviderInputCalled bool
ProviderInputName string
ProviderInputConfig map[string]interface{}
SetProviderInputCalled bool
SetProviderInputName string
SetProviderInputConfig map[string]interface{}
ConfigureProviderCalled bool
ConfigureProviderName string
ConfigureProviderConfig *ResourceConfig
@ -122,6 +141,11 @@ func (c *MockEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
return c.HookError
}
func (c *MockEvalContext) Input() UIInput {
c.InputCalled = true
return c.InputInput
}
func (c *MockEvalContext) InitProvider(n string) (ResourceProvider, error) {
c.InitProviderCalled = true
c.InitProviderName = n
@ -147,6 +171,18 @@ func (c *MockEvalContext) ParentProviderConfig(n string) *ResourceConfig {
return c.ParentProviderConfigConfig
}
func (c *MockEvalContext) ProviderInput(n string) map[string]interface{} {
c.ProviderInputCalled = true
c.ProviderInputName = n
return c.ProviderInputConfig
}
func (c *MockEvalContext) SetProviderInput(n string, cfg map[string]interface{}) {
c.SetProviderInputCalled = true
c.SetProviderInputName = n
c.SetProviderInputConfig = cfg
}
func (c *MockEvalContext) InitProvisioner(n string) (ResourceProvisioner, error) {
c.InitProvisionerCalled = true
c.InitProvisionerName = n

View File

@ -14,9 +14,11 @@ type BuiltinEvalContext struct {
PathValue []string
Interpolater *Interpolater
Hooks []Hook
InputValue UIInput
Providers map[string]ResourceProviderFactory
ProviderCache map[string]ResourceProvider
ProviderConfigCache map[string]*ResourceConfig
ProviderInputConfig map[string]map[string]interface{}
ProviderLock *sync.Mutex
Provisioners map[string]ResourceProvisionerFactory
ProvisionerCache map[string]ResourceProvisioner
@ -49,6 +51,10 @@ func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
return nil
}
func (ctx *BuiltinEvalContext) Input() UIInput {
return ctx.InputValue
}
func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) {
ctx.once.Do(ctx.init)
@ -100,6 +106,20 @@ func (ctx *BuiltinEvalContext) ConfigureProvider(
return p.Configure(cfg)
}
func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} {
ctx.ProviderLock.Lock()
defer ctx.ProviderLock.Unlock()
return ctx.ProviderInputConfig[n]
}
func (ctx *BuiltinEvalContext) SetProviderInput(n string, c map[string]interface{}) {
ctx.ProviderLock.Lock()
defer ctx.ProviderLock.Unlock()
ctx.ProviderInputConfig[n] = c
}
func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig {
ctx.ProviderLock.Lock()
defer ctx.ProviderLock.Unlock()

View File

@ -2,6 +2,8 @@ package terraform
import (
"fmt"
"github.com/hashicorp/terraform/config"
)
// EvalConfigProvider is an EvalNode implementation that configures
@ -17,15 +19,26 @@ func (n *EvalConfigProvider) Args() ([]EvalNode, []EvalType) {
func (n *EvalConfigProvider) Eval(
ctx EvalContext, args []interface{}) (interface{}, error) {
config := args[0].(*ResourceConfig)
cfg := args[0].(*ResourceConfig)
// If we have a configuration set, then use that
if input := ctx.ProviderInput(n.Provider); input != nil {
rc, err := config.NewRawConfig(input)
if err != nil {
return nil, err
}
merged := cfg.raw.Merge(rc)
cfg = NewResourceConfig(merged)
}
// Get the parent configuration if there is one
if parent := ctx.ParentProviderConfig(n.Provider); parent != nil {
merged := config.raw.Merge(parent.raw)
config = NewResourceConfig(merged)
merged := cfg.raw.Merge(parent.raw)
cfg = NewResourceConfig(merged)
}
return nil, ctx.ConfigureProvider(n.Provider, config)
return nil, ctx.ConfigureProvider(n.Provider, cfg)
}
func (n *EvalConfigProvider) Type() EvalType {
@ -80,3 +93,52 @@ func (n *EvalGetProvider) Eval(
func (n *EvalGetProvider) Type() EvalType {
return EvalTypeResourceProvider
}
// EvalInputProvider is an EvalNode implementation that asks for input
// for the given provider configurations.
type EvalInputProvider struct {
Name string
Provider *ResourceProvider
Config *config.RawConfig
}
func (n *EvalInputProvider) Args() ([]EvalNode, []EvalType) {
return nil, nil
}
func (n *EvalInputProvider) Eval(
ctx EvalContext, args []interface{}) (interface{}, error) {
// If we already configured this provider, then don't do this again
if v := ctx.ProviderInput(n.Name); v != nil {
return nil, nil
}
rc := NewResourceConfig(n.Config)
rc.Config = make(map[string]interface{})
// Wrap the input into a namespace
input := &PrefixUIInput{
IdPrefix: fmt.Sprintf("provider.%s", n.Name),
QueryPrefix: fmt.Sprintf("provider.%s.", n.Name),
UIInput: ctx.Input(),
}
// Go through each provider and capture the input necessary
// to satisfy it.
config, err := (*n.Provider).Input(input, rc)
if err != nil {
return nil, fmt.Errorf(
"Error configuring %s: %s", n.Name, err)
}
if config != nil && len(config.Config) > 0 {
// Set the configuration
ctx.SetProviderInput(n.Name, config.Config)
}
return nil, nil
}
func (n *EvalInputProvider) Type() EvalType {
return EvalTypeNull
}

View File

@ -7,18 +7,45 @@ import (
// ProviderEvalTree returns the evaluation tree for initializing and
// configuring providers.
func ProviderEvalTree(n string, config *config.RawConfig) EvalNode {
return &EvalSequence{
Nodes: []EvalNode{
&EvalInitProvider{Name: n},
&EvalValidateProvider{
ProviderName: n,
Provider: &EvalGetProvider{Name: n},
Config: &EvalInterpolate{Config: config},
},
&EvalConfigProvider{
Provider: n,
Config: &EvalInterpolate{Config: config},
seq := make([]EvalNode, 0, 5)
seq = append(seq, &EvalInitProvider{Name: n})
// Input stuff
var provider ResourceProvider
seq = append(seq, &EvalOpFilter{
Ops: []walkOperation{walkInput},
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Name: n,
Output: &provider,
},
&EvalInputProvider{
Name: n,
Provider: &provider,
Config: config,
},
},
},
}
})
// Apply stuff
seq = append(seq, &EvalOpFilter{
Ops: []walkOperation{walkValidate, walkRefresh, walkPlan, walkApply},
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalValidateProvider{
ProviderName: n,
Provider: &EvalGetProvider{Name: n},
Config: &EvalInterpolate{Config: config},
},
&EvalConfigProvider{
Provider: n,
Config: &EvalInterpolate{Config: config},
},
},
},
})
return &EvalSequence{Nodes: seq}
}

View File

@ -55,9 +55,11 @@ func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext {
ctx := &BuiltinEvalContext{
PathValue: g.Path,
Hooks: w.Context.hooks,
InputValue: w.Context.uiInput,
Providers: w.Context.providers,
ProviderCache: w.providerCache,
ProviderConfigCache: w.providerConfigCache,
ProviderInputConfig: w.Context.providerInputConfig,
ProviderLock: &w.providerLock,
Provisioners: w.Context.provisioners,
ProvisionerCache: w.provisionerCache,