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.
|
// Additionally, a context can be created from a Plan using Plan.Context.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
module *module.Tree
|
module *module.Tree
|
||||||
diff *Diff
|
diff *Diff
|
||||||
hooks []Hook
|
hooks []Hook
|
||||||
state *State
|
state *State
|
||||||
providers map[string]ResourceProviderFactory
|
providerConfig map[string]map[string]map[string]interface{}
|
||||||
provisioners map[string]ResourceProvisionerFactory
|
providers map[string]ResourceProviderFactory
|
||||||
variables map[string]string
|
provisioners map[string]ResourceProvisionerFactory
|
||||||
uiInput UIInput
|
variables map[string]string
|
||||||
|
uiInput UIInput
|
||||||
|
|
||||||
l sync.Mutex // Lock acquired during any task
|
l sync.Mutex // Lock acquired during any task
|
||||||
parCh chan struct{} // Semaphore used to limit parallelism
|
parCh chan struct{} // Semaphore used to limit parallelism
|
||||||
|
@ -78,14 +79,15 @@ func NewContext(opts *ContextOpts) *Context {
|
||||||
parCh := make(chan struct{}, par)
|
parCh := make(chan struct{}, par)
|
||||||
|
|
||||||
return &Context{
|
return &Context{
|
||||||
diff: opts.Diff,
|
diff: opts.Diff,
|
||||||
hooks: hooks,
|
hooks: hooks,
|
||||||
module: opts.Module,
|
module: opts.Module,
|
||||||
state: opts.State,
|
state: opts.State,
|
||||||
providers: opts.Providers,
|
providerConfig: make(map[string]map[string]map[string]interface{}),
|
||||||
provisioners: opts.Provisioners,
|
providers: opts.Providers,
|
||||||
variables: opts.Variables,
|
provisioners: opts.Provisioners,
|
||||||
uiInput: opts.UIInput,
|
variables: opts.Variables,
|
||||||
|
uiInput: opts.UIInput,
|
||||||
|
|
||||||
parCh: parCh,
|
parCh: parCh,
|
||||||
sh: sh,
|
sh: sh,
|
||||||
|
@ -548,31 +550,44 @@ func (c *walkContext) inputWalkFn() depgraph.WalkFunc {
|
||||||
// Resources don't matter for input. Continue.
|
// Resources don't matter for input. Continue.
|
||||||
return nil
|
return nil
|
||||||
case *GraphNodeResourceProvider:
|
case *GraphNodeResourceProvider:
|
||||||
return nil
|
// Acquire the lock the whole time so we only ask for input
|
||||||
/*
|
// one at a time.
|
||||||
// If we already did this provider, then we're done.
|
meta.Lock()
|
||||||
meta.Lock()
|
defer meta.Unlock()
|
||||||
_, ok := meta.Done[rn.ID]
|
|
||||||
meta.Unlock()
|
|
||||||
if ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the raw configuration because this is what we
|
// If we already did this provider, then we're done.
|
||||||
// pass into the API.
|
if _, ok := meta.Done[rn.ID]; ok {
|
||||||
var raw *config.RawConfig
|
return nil
|
||||||
sharedProvider := rn.Provider
|
}
|
||||||
if sharedProvider.Config != nil {
|
|
||||||
raw = sharedProvider.Config.RawConfig
|
|
||||||
}
|
|
||||||
rc := NewResourceConfig(raw)
|
|
||||||
|
|
||||||
// Go through each provider and capture the input necessary
|
// Get the raw configuration because this is what we
|
||||||
// to satisfy it.
|
// pass into the API.
|
||||||
for k, p := range sharedProvider.Providers {
|
var raw *config.RawConfig
|
||||||
ws, es := p.Validate(rc)
|
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
|
return nil
|
||||||
|
@ -1116,47 +1131,17 @@ func (c *walkContext) genericWalkFn(cb genericWalkFunc) depgraph.WalkFunc {
|
||||||
case *GraphNodeResourceProvider:
|
case *GraphNodeResourceProvider:
|
||||||
sharedProvider := m.Provider
|
sharedProvider := m.Provider
|
||||||
|
|
||||||
// Interpolate in the variables and configure all the providers
|
// Check if we have an override
|
||||||
var raw *config.RawConfig
|
cs, ok := c.Context.providerConfig[m.ID]
|
||||||
if sharedProvider.Config != nil {
|
if !ok {
|
||||||
raw = sharedProvider.Config.RawConfig
|
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 {
|
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)
|
log.Printf("[INFO] Configuring provider: %s", k)
|
||||||
err := p.Configure(rc)
|
err := p.Configure(rc)
|
||||||
if err != nil {
|
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) {
|
func TestContextApply(t *testing.T) {
|
||||||
m := testModule(t, "apply-good")
|
m := testModule(t, "apply-good")
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
|
|
|
@ -119,7 +119,8 @@ type graphSharedProvider struct {
|
||||||
ProviderKeys []string
|
ProviderKeys []string
|
||||||
Parent *graphSharedProvider
|
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
|
// Graph builds a dependency graph of all the resources for infrastructure
|
||||||
|
@ -1461,6 +1462,50 @@ func graphMapResourceProvisioners(g *depgraph.Graph,
|
||||||
return nil
|
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
|
// matchingPrefixes takes a resource type and a set of resource
|
||||||
// providers we know about by prefix and returns a list of prefixes
|
// providers we know about by prefix and returns a list of prefixes
|
||||||
// that might be valid for that resource.
|
// that might be valid for that resource.
|
||||||
|
|
|
@ -199,11 +199,16 @@ func (c *ResourceConfig) interpolate(ctx *walkContext) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.raw != nil {
|
if c.raw == nil {
|
||||||
c.ComputedKeys = c.raw.UnknownKeys()
|
var err error
|
||||||
c.Raw = c.raw.Raw
|
c.raw, err = config.NewRawConfig(make(map[string]interface{}))
|
||||||
c.Config = c.raw.Config()
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.ComputedKeys = c.raw.UnknownKeys()
|
||||||
|
c.Raw = c.raw.Raw
|
||||||
|
c.Config = c.raw.Config()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ type MockResourceProvider struct {
|
||||||
InputConfig *ResourceConfig
|
InputConfig *ResourceConfig
|
||||||
InputReturnConfig *ResourceConfig
|
InputReturnConfig *ResourceConfig
|
||||||
InputReturnError error
|
InputReturnError error
|
||||||
|
InputFn func(UIInput, *ResourceConfig) (*ResourceConfig, error)
|
||||||
ApplyCalled bool
|
ApplyCalled bool
|
||||||
ApplyInfo *InstanceInfo
|
ApplyInfo *InstanceInfo
|
||||||
ApplyState *InstanceState
|
ApplyState *InstanceState
|
||||||
|
@ -61,6 +62,9 @@ func (p *MockResourceProvider) Input(
|
||||||
p.InputCalled = true
|
p.InputCalled = true
|
||||||
p.InputInput = input
|
p.InputInput = input
|
||||||
p.InputConfig = c
|
p.InputConfig = c
|
||||||
|
if p.InputFn != nil {
|
||||||
|
return p.InputFn(input, c)
|
||||||
|
}
|
||||||
return p.InputReturnConfig, p.InputReturnError
|
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
|
// Below are all the constant strings that are the expected output for
|
||||||
// various tests.
|
// 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 = `
|
const testTerraformInputVarsStr = `
|
||||||
aws_instance.bar:
|
aws_instance.bar:
|
||||||
ID = foo
|
ID = foo
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
resource "aws_instance" "foo" {}
|
Loading…
Reference in New Issue