terraform: ask for input for providers
This commit is contained in:
parent
caf8e372f2
commit
2791badf01
|
@ -25,14 +25,15 @@ type genericWalkFunc func(*walkContext, *Resource) error
|
|||
//
|
||||
// Additionally, a context can be created from a Plan using Plan.Context.
|
||||
type Context struct {
|
||||
module *module.Tree
|
||||
diff *Diff
|
||||
hooks []Hook
|
||||
state *State
|
||||
providers map[string]ResourceProviderFactory
|
||||
provisioners map[string]ResourceProvisionerFactory
|
||||
variables map[string]string
|
||||
uiInput UIInput
|
||||
module *module.Tree
|
||||
diff *Diff
|
||||
hooks []Hook
|
||||
state *State
|
||||
providerConfig map[string]map[string]map[string]interface{}
|
||||
providers map[string]ResourceProviderFactory
|
||||
provisioners map[string]ResourceProvisionerFactory
|
||||
variables map[string]string
|
||||
uiInput UIInput
|
||||
|
||||
l sync.Mutex // Lock acquired during any task
|
||||
parCh chan struct{} // Semaphore used to limit parallelism
|
||||
|
@ -78,14 +79,15 @@ func NewContext(opts *ContextOpts) *Context {
|
|||
parCh := make(chan struct{}, par)
|
||||
|
||||
return &Context{
|
||||
diff: opts.Diff,
|
||||
hooks: hooks,
|
||||
module: opts.Module,
|
||||
state: opts.State,
|
||||
providers: opts.Providers,
|
||||
provisioners: opts.Provisioners,
|
||||
variables: opts.Variables,
|
||||
uiInput: opts.UIInput,
|
||||
diff: opts.Diff,
|
||||
hooks: hooks,
|
||||
module: opts.Module,
|
||||
state: opts.State,
|
||||
providerConfig: make(map[string]map[string]map[string]interface{}),
|
||||
providers: opts.Providers,
|
||||
provisioners: opts.Provisioners,
|
||||
variables: opts.Variables,
|
||||
uiInput: opts.UIInput,
|
||||
|
||||
parCh: parCh,
|
||||
sh: sh,
|
||||
|
@ -548,31 +550,44 @@ func (c *walkContext) inputWalkFn() depgraph.WalkFunc {
|
|||
// Resources don't matter for input. Continue.
|
||||
return nil
|
||||
case *GraphNodeResourceProvider:
|
||||
return nil
|
||||
/*
|
||||
// If we already did this provider, then we're done.
|
||||
meta.Lock()
|
||||
_, ok := meta.Done[rn.ID]
|
||||
meta.Unlock()
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
// Acquire the lock the whole time so we only ask for input
|
||||
// one at a time.
|
||||
meta.Lock()
|
||||
defer meta.Unlock()
|
||||
|
||||
// Get the raw configuration because this is what we
|
||||
// pass into the API.
|
||||
var raw *config.RawConfig
|
||||
sharedProvider := rn.Provider
|
||||
if sharedProvider.Config != nil {
|
||||
raw = sharedProvider.Config.RawConfig
|
||||
}
|
||||
rc := NewResourceConfig(raw)
|
||||
// If we already did this provider, then we're done.
|
||||
if _, ok := meta.Done[rn.ID]; ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Go through each provider and capture the input necessary
|
||||
// to satisfy it.
|
||||
for k, p := range sharedProvider.Providers {
|
||||
ws, es := p.Validate(rc)
|
||||
// Get the raw configuration because this is what we
|
||||
// pass into the API.
|
||||
var raw *config.RawConfig
|
||||
sharedProvider := rn.Provider
|
||||
if sharedProvider.Config != nil {
|
||||
raw = sharedProvider.Config.RawConfig
|
||||
}
|
||||
rc := NewResourceConfig(raw)
|
||||
|
||||
// Go through each provider and capture the input necessary
|
||||
// to satisfy it.
|
||||
configs := make(map[string]map[string]interface{})
|
||||
for k, p := range sharedProvider.Providers {
|
||||
newc, err := p.Input(c.Context.uiInput, rc)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Error configuring %s: %s", k, err)
|
||||
}
|
||||
*/
|
||||
if newc != nil {
|
||||
configs[k] = newc.Raw
|
||||
}
|
||||
}
|
||||
|
||||
// Mark this provider as done
|
||||
meta.Done[rn.ID] = struct{}{}
|
||||
|
||||
// Set the configuration
|
||||
c.Context.providerConfig[rn.ID] = configs
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -1116,47 +1131,17 @@ func (c *walkContext) genericWalkFn(cb genericWalkFunc) depgraph.WalkFunc {
|
|||
case *GraphNodeResourceProvider:
|
||||
sharedProvider := m.Provider
|
||||
|
||||
// Interpolate in the variables and configure all the providers
|
||||
var raw *config.RawConfig
|
||||
if sharedProvider.Config != nil {
|
||||
raw = sharedProvider.Config.RawConfig
|
||||
// Check if we have an override
|
||||
cs, ok := c.Context.providerConfig[m.ID]
|
||||
if !ok {
|
||||
cs = make(map[string]map[string]interface{})
|
||||
}
|
||||
|
||||
// If we have a parent, then merge in the parent configurations
|
||||
// properly so we "inherit" the configurations.
|
||||
if sharedProvider.Parent != nil {
|
||||
var rawMap map[string]interface{}
|
||||
if raw != nil {
|
||||
rawMap = raw.Raw
|
||||
}
|
||||
|
||||
parent := sharedProvider.Parent
|
||||
for parent != nil {
|
||||
if parent.Config != nil {
|
||||
if rawMap == nil {
|
||||
rawMap = parent.Config.RawConfig.Raw
|
||||
}
|
||||
|
||||
for k, v := range parent.Config.RawConfig.Config() {
|
||||
rawMap[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
parent = parent.Parent
|
||||
}
|
||||
|
||||
// Update our configuration to be the merged result
|
||||
var err error
|
||||
raw, err = config.NewRawConfig(rawMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error merging configurations: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
rc := NewResourceConfig(raw)
|
||||
rc.interpolate(c)
|
||||
|
||||
for k, p := range sharedProvider.Providers {
|
||||
// Merge the configurations to get what we use to configure with
|
||||
rc := sharedProvider.MergeConfig(false, cs[k])
|
||||
rc.interpolate(c)
|
||||
|
||||
log.Printf("[INFO] Configuring provider: %s", k)
|
||||
err := p.Configure(rc)
|
||||
if err != nil {
|
||||
|
|
|
@ -460,6 +460,45 @@ func TestContextInput(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContextInput_provider(t *testing.T) {
|
||||
m := testModule(t, "input-provider")
|
||||
p := testProvider("aws")
|
||||
p.ApplyFn = testApplyFn
|
||||
p.DiffFn = testDiffFn
|
||||
ctx := testContext(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
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(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if _, err := ctx.Plan(nil); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if _, err := ctx.Apply(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, "bar") {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextApply(t *testing.T) {
|
||||
m := testModule(t, "apply-good")
|
||||
p := testProvider("aws")
|
||||
|
|
|
@ -119,7 +119,8 @@ type graphSharedProvider struct {
|
|||
ProviderKeys []string
|
||||
Parent *graphSharedProvider
|
||||
|
||||
parentNoun *depgraph.Noun
|
||||
overrideConfig map[string]map[string]interface{}
|
||||
parentNoun *depgraph.Noun
|
||||
}
|
||||
|
||||
// Graph builds a dependency graph of all the resources for infrastructure
|
||||
|
@ -1461,6 +1462,50 @@ func graphMapResourceProvisioners(g *depgraph.Graph,
|
|||
return nil
|
||||
}
|
||||
|
||||
// MergeConfig merges all the configurations in the proper order
|
||||
// to result in the final configuration to use to configure this
|
||||
// provider.
|
||||
func (p *graphSharedProvider) MergeConfig(
|
||||
raw bool, override map[string]interface{}) *ResourceConfig {
|
||||
var rawMap map[string]interface{}
|
||||
if override != nil {
|
||||
rawMap = override
|
||||
} else if p.Config != nil {
|
||||
rawMap = p.Config.RawConfig.Raw
|
||||
}
|
||||
if rawMap == nil {
|
||||
rawMap = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// Merge in all the parent configurations
|
||||
if p.Parent != nil {
|
||||
parent := p.Parent
|
||||
for parent != nil {
|
||||
if parent.Config != nil {
|
||||
var merge map[string]interface{}
|
||||
if raw {
|
||||
merge = parent.Config.RawConfig.Raw
|
||||
} else {
|
||||
merge = parent.Config.RawConfig.Config()
|
||||
}
|
||||
|
||||
for k, v := range merge {
|
||||
rawMap[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
parent = parent.Parent
|
||||
}
|
||||
}
|
||||
|
||||
rc, err := config.NewRawConfig(rawMap)
|
||||
if err != nil {
|
||||
panic("error building config: " + err.Error())
|
||||
}
|
||||
|
||||
return NewResourceConfig(rc)
|
||||
}
|
||||
|
||||
// matchingPrefixes takes a resource type and a set of resource
|
||||
// providers we know about by prefix and returns a list of prefixes
|
||||
// that might be valid for that resource.
|
||||
|
|
|
@ -199,11 +199,16 @@ func (c *ResourceConfig) interpolate(ctx *walkContext) error {
|
|||
}
|
||||
}
|
||||
|
||||
if c.raw != nil {
|
||||
c.ComputedKeys = c.raw.UnknownKeys()
|
||||
c.Raw = c.raw.Raw
|
||||
c.Config = c.raw.Config()
|
||||
if c.raw == nil {
|
||||
var err error
|
||||
c.raw, err = config.NewRawConfig(make(map[string]interface{}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.ComputedKeys = c.raw.UnknownKeys()
|
||||
c.Raw = c.raw.Raw
|
||||
c.Config = c.raw.Config()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ type MockResourceProvider struct {
|
|||
InputConfig *ResourceConfig
|
||||
InputReturnConfig *ResourceConfig
|
||||
InputReturnError error
|
||||
InputFn func(UIInput, *ResourceConfig) (*ResourceConfig, error)
|
||||
ApplyCalled bool
|
||||
ApplyInfo *InstanceInfo
|
||||
ApplyState *InstanceState
|
||||
|
@ -61,6 +62,9 @@ func (p *MockResourceProvider) Input(
|
|||
p.InputCalled = true
|
||||
p.InputInput = input
|
||||
p.InputConfig = c
|
||||
if p.InputFn != nil {
|
||||
return p.InputFn(input, c)
|
||||
}
|
||||
return p.InputReturnConfig, p.InputReturnError
|
||||
}
|
||||
|
||||
|
|
|
@ -113,6 +113,19 @@ func (h *HookRecordApplyOrder) PreApply(
|
|||
// Below are all the constant strings that are the expected output for
|
||||
// various tests.
|
||||
|
||||
const testTerraformInputProviderStr = `
|
||||
aws_instance.bar:
|
||||
ID = foo
|
||||
bar = override
|
||||
foo = us-east-1
|
||||
type = aws_instance
|
||||
aws_instance.foo:
|
||||
ID = foo
|
||||
bar = baz
|
||||
num = 2
|
||||
type = aws_instance
|
||||
`
|
||||
|
||||
const testTerraformInputVarsStr = `
|
||||
aws_instance.bar:
|
||||
ID = foo
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
resource "aws_instance" "foo" {}
|
Loading…
Reference in New Issue