From 79a742c1aea9fafbc8f7dee2ba7beca5024c3f6a Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 14 Sep 2016 23:58:16 -0700 Subject: [PATCH] terraform: new provider graph node for flattened world --- terraform/graph_builder_apply.go | 13 ++++- terraform/node_provider.go | 55 +++++++++++++++++++++ terraform/transform_attach_config.go | 74 ++++++++++++++++++++++++++++ terraform/transform_provider.go | 36 +++++++++----- 4 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 terraform/node_provider.go create mode 100644 terraform/transform_attach_config.go diff --git a/terraform/graph_builder_apply.go b/terraform/graph_builder_apply.go index a853ed33c..eeda2a857 100644 --- a/terraform/graph_builder_apply.go +++ b/terraform/graph_builder_apply.go @@ -38,6 +38,14 @@ func (b *ApplyGraphBuilder) Build(path []string) (*Graph, error) { // See GraphBuilder func (b *ApplyGraphBuilder) Steps() []GraphTransformer { + // Custom factory for creating providers. + providerFactory := func(name string, path []string) GraphNodeProvider { + return &NodeApplyableProvider{ + NameValue: name, + PathValue: path, + } + } + steps := []GraphTransformer{ // Creates all the nodes represented in the diff. &DiffTransformer{ @@ -47,10 +55,13 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { }, // Create all the providers - &MissingProviderTransformer{Providers: b.Providers}, + &MissingProviderTransformer{Providers: b.Providers, Factory: providerFactory}, &ProviderTransformer{}, &ParentProviderTransformer{}, + // Attach the configurations + &AttachConfigTransformer{Module: b.Module}, + // Single root &RootTransformer{}, } diff --git a/terraform/node_provider.go b/terraform/node_provider.go new file mode 100644 index 000000000..476be64b1 --- /dev/null +++ b/terraform/node_provider.go @@ -0,0 +1,55 @@ +package terraform + +import ( + "fmt" + + "github.com/hashicorp/terraform/config" +) + +// NodeApplyableProvider represents a provider during an apply. +// +// NOTE: There is a lot of logic here that will be shared with non-Apply. +// The plan is to abstract that eventually into an embedded abstract struct. +type NodeApplyableProvider struct { + NameValue string + PathValue []string + Config *config.ProviderConfig +} + +func (n *NodeApplyableProvider) Name() string { + result := fmt.Sprintf("provider.%s", n.NameValue) + if len(n.PathValue) > 1 { + result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result) + } + + return result +} + +// GraphNodeSubPath +func (n *NodeApplyableProvider) Path() []string { + return n.PathValue +} + +// GraphNodeProvider +func (n *NodeApplyableProvider) ProviderName() string { + return n.NameValue +} + +// GraphNodeProvider +func (n *NodeApplyableProvider) ProviderConfig() *config.RawConfig { + if n.Config == nil { + return nil + } + + return n.Config.RawConfig +} + +// GraphNodeAttachProvider +func (n *NodeApplyableProvider) AttachProvider(c *config.ProviderConfig) { + n.Config = c +} + +// GraphNodeEvalable +func (n *NodeApplyableProvider) EvalTree() EvalNode { + return ProviderEvalTree(n.NameValue, nil) +} diff --git a/terraform/transform_attach_config.go b/terraform/transform_attach_config.go new file mode 100644 index 000000000..1802ac5d9 --- /dev/null +++ b/terraform/transform_attach_config.go @@ -0,0 +1,74 @@ +package terraform + +import ( + "log" + + "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/config/module" +) + +// GraphNodeAttachProvider is an interface that must be implemented by nodes +// that want provider configurations attached. +type GraphNodeAttachProvider interface { + // Must be implemented to determine the path for the configuration + GraphNodeSubPath + + // ProviderName with no module prefix. Example: "aws". + ProviderName() string + + // Sets the configuration + AttachProvider(*config.ProviderConfig) +} + +// AttachConfigTransformer goes through the graph and attaches configuration +// structures to nodes that implement the interfaces above. +// +// The attached configuration structures are directly from the configuration. +// If they're going to be modified, a copy should be made. +type AttachConfigTransformer struct { + Module *module.Tree // Module is the root module for the config +} + +func (t *AttachConfigTransformer) Transform(g *Graph) error { + if err := t.attachProviders(g); err != nil { + return err + } + + return nil +} + +func (t *AttachConfigTransformer) attachProviders(g *Graph) error { + // Go through and find GraphNodeAttachProvider + for _, v := range g.Vertices() { + // Only care about GraphNodeAttachProvider implementations + apn, ok := v.(GraphNodeAttachProvider) + if !ok { + continue + } + + // TODO: aliases? + + // Determine what we're looking for + path := normalizeModulePath(apn.Path()) + path = path[1:] + name := apn.ProviderName() + log.Printf("[TRACE] Attach provider request: %#v %s", path, name) + + // Get the configuration. + tree := t.Module.Child(path) + if tree == nil { + continue + } + + // Go through the provider configs to find the matching config + for _, p := range tree.Config().ProviderConfigs { + if p.Name == name { + log.Printf("[TRACE] Attaching provider config: %#v", p) + apn.AttachProvider(p) + break + } + } + } + + return nil +} diff --git a/terraform/transform_provider.go b/terraform/transform_provider.go index 80d51f526..0a762245a 100644 --- a/terraform/transform_provider.go +++ b/terraform/transform_provider.go @@ -163,9 +163,19 @@ func (t *CloseProviderTransformer) Transform(g *Graph) error { type MissingProviderTransformer struct { // Providers is the list of providers we support. Providers []string + + // Factory, if set, overrides how the providers are made. + Factory func(name string, path []string) GraphNodeProvider } func (t *MissingProviderTransformer) Transform(g *Graph) error { + // Initialize factory + if t.Factory == nil { + t.Factory = func(name string, path []string) GraphNodeProvider { + return &graphNodeProvider{ProviderNameValue: name} + } + } + // Create a set of our supported providers supported := make(map[string]struct{}, len(t.Providers)) for _, v := range t.Providers { @@ -217,17 +227,14 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error { } // Add the missing provider node to the graph - raw := &graphNodeProvider{ - ProviderNameValue: p, - PathValue: path, - } - - var v dag.Vertex = raw + v := t.Factory(p, path).(dag.Vertex) if len(path) > 0 { - var err error - v, err = raw.Flatten(path) - if err != nil { - return err + if fn, ok := v.(GraphNodeFlattenable); ok { + var err error + v, err = fn.Flatten(path) + if err != nil { + return err + } } // We'll need the parent provider as well, so let's @@ -347,7 +354,12 @@ func providerVertexMap(g *Graph) map[string]dag.Vertex { m := make(map[string]dag.Vertex) for _, v := range g.Vertices() { if pv, ok := v.(GraphNodeProvider); ok { - m[pv.ProviderName()] = v + key := pv.ProviderName() + if _, ok := v.(*NodeApplyableProvider); ok { + key = providerMapKey(pv.ProviderName(), v) + } + + m[key] = v } } @@ -512,7 +524,6 @@ func (n *graphNodeCloseProvider) DotNode(name string, opts *GraphDotOpts) *dot.N type graphNodeProvider struct { ProviderNameValue string - PathValue []string } func (n *graphNodeProvider) Name() string { @@ -529,6 +540,7 @@ func (n *graphNodeProvider) DependableName() []string { return []string{n.Name()} } +// GraphNodeProvider func (n *graphNodeProvider) ProviderName() string { return n.ProviderNameValue }