terraform: disable providers in new apply graph
This adds the proper logic for "disabling" providers to the new apply graph: interolating and storing the config for inheritance but not actually initializing and configuring the provider. This is important since parent modules will often contain incomplete provider configurations for the purpose of inheritance that would error if they were actually attempted to be configured (since they're incomplete). If the provider is not used, it should be "disabled".
This commit is contained in:
parent
13b9007474
commit
e4ef1fe553
|
@ -1915,6 +1915,43 @@ func TestContext2Apply_providerComputedVar(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContext2Apply_providerConfigureDisabled(t *testing.T) {
|
||||
m := testModule(t, "apply-provider-configure-disabled")
|
||||
p := testProvider("aws")
|
||||
p.ApplyFn = testApplyFn
|
||||
p.DiffFn = testDiffFn
|
||||
|
||||
called := false
|
||||
p.ConfigureFn = func(c *ResourceConfig) error {
|
||||
called = true
|
||||
|
||||
if _, ok := c.Get("value"); !ok {
|
||||
return fmt.Errorf("value is not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
if _, err := ctx.Plan(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if _, err := ctx.Apply(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if !called {
|
||||
t.Fatal("configure never called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Apply_Provisioner_compute(t *testing.T) {
|
||||
m := testModule(t, "apply-provisioner-compute")
|
||||
p := testProvider("aws")
|
||||
|
|
|
@ -115,7 +115,7 @@ func (b *BuiltinGraphBuilder) Steps(path []string) []GraphTransformer {
|
|||
// Provider-related transformations
|
||||
&MissingProviderTransformer{Providers: b.Providers},
|
||||
&ProviderTransformer{},
|
||||
&DisableProviderTransformer{},
|
||||
&DisableProviderTransformerOld{},
|
||||
|
||||
// Provisioner-related transformations
|
||||
&MissingProvisionerTransformer{Provisioners: b.Provisioners},
|
||||
|
|
|
@ -82,6 +82,7 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
|
|||
// Create all the providers
|
||||
&MissingProviderTransformer{Providers: b.Providers, Factory: providerFactory},
|
||||
&ProviderTransformer{},
|
||||
&DisableProviderTransformer{},
|
||||
&ParentProviderTransformer{},
|
||||
&AttachProviderConfigTransformer{Module: b.Module},
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
)
|
||||
|
||||
// NodeAbstractProvider represents a provider that has no associated operations.
|
||||
// It registers all the common interfaces across operations for providers.
|
||||
type NodeAbstractProvider struct {
|
||||
NameValue string
|
||||
PathValue []string
|
||||
|
||||
// The fields below will be automatically set using the Attach
|
||||
// interfaces if you're running those transforms, but also be explicitly
|
||||
// set if you already have that information.
|
||||
|
||||
Config *config.ProviderConfig
|
||||
}
|
||||
|
||||
func (n *NodeAbstractProvider) 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 *NodeAbstractProvider) Path() []string {
|
||||
return n.PathValue
|
||||
}
|
||||
|
||||
// GraphNodeReferencer
|
||||
func (n *NodeAbstractProvider) References() []string {
|
||||
if n.Config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ReferencesFromConfig(n.Config.RawConfig)
|
||||
}
|
||||
|
||||
// GraphNodeProvider
|
||||
func (n *NodeAbstractProvider) ProviderName() string {
|
||||
return n.NameValue
|
||||
}
|
||||
|
||||
// GraphNodeProvider
|
||||
func (n *NodeAbstractProvider) ProviderConfig() *config.RawConfig {
|
||||
if n.Config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return n.Config.RawConfig
|
||||
}
|
||||
|
||||
// GraphNodeAttachProvider
|
||||
func (n *NodeAbstractProvider) AttachProvider(c *config.ProviderConfig) {
|
||||
n.Config = c
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NodeDisabledProvider represents a provider that is disabled. A disabled
|
||||
// provider does nothing. It exists to properly set inheritance information
|
||||
// for child providers.
|
||||
type NodeDisabledProvider struct {
|
||||
*NodeAbstractProvider
|
||||
}
|
||||
|
||||
func (n *NodeDisabledProvider) Name() string {
|
||||
return fmt.Sprintf("%s (disabled)", n.NodeAbstractProvider.Name())
|
||||
}
|
||||
|
||||
// GraphNodeEvalable
|
||||
func (n *NodeDisabledProvider) EvalTree() EvalNode {
|
||||
var resourceConfig *ResourceConfig
|
||||
return &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalInterpolate{
|
||||
Config: n.ProviderConfig(),
|
||||
Output: &resourceConfig,
|
||||
},
|
||||
&EvalBuildProviderConfig{
|
||||
Provider: n.ProviderName(),
|
||||
Config: &resourceConfig,
|
||||
Output: &resourceConfig,
|
||||
},
|
||||
&EvalSetProviderConfig{
|
||||
Provider: n.ProviderName(),
|
||||
Config: &resourceConfig,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
provider "aws" {
|
||||
value = "foo"
|
||||
}
|
||||
|
||||
resource "aws_instance" "foo" {}
|
|
@ -0,0 +1,7 @@
|
|||
provider "aws" {
|
||||
foo = "bar"
|
||||
}
|
||||
|
||||
module "child" {
|
||||
source = "./child"
|
||||
}
|
|
@ -33,55 +33,6 @@ type GraphNodeProviderConsumer interface {
|
|||
ProvidedBy() []string
|
||||
}
|
||||
|
||||
// DisableProviderTransformer "disables" any providers that are only
|
||||
// depended on by modules.
|
||||
type DisableProviderTransformer struct{}
|
||||
|
||||
func (t *DisableProviderTransformer) Transform(g *Graph) error {
|
||||
// Since we're comparing against edges, we need to make sure we connect
|
||||
g.ConnectDependents()
|
||||
|
||||
for _, v := range g.Vertices() {
|
||||
// We only care about providers
|
||||
pn, ok := v.(GraphNodeProvider)
|
||||
if !ok || pn.ProviderName() == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Go through all the up-edges (things that depend on this
|
||||
// provider) and if any is not a module, then ignore this node.
|
||||
nonModule := false
|
||||
for _, sourceRaw := range g.UpEdges(v).List() {
|
||||
source := sourceRaw.(dag.Vertex)
|
||||
cn, ok := source.(graphNodeConfig)
|
||||
if !ok {
|
||||
nonModule = true
|
||||
break
|
||||
}
|
||||
|
||||
if cn.ConfigType() != GraphNodeConfigTypeModule {
|
||||
nonModule = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if nonModule {
|
||||
// We found something that depends on this provider that
|
||||
// isn't a module, so skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
// Disable the provider by replacing it with a "disabled" provider
|
||||
disabled := &graphNodeDisabledProvider{GraphNodeProvider: pn}
|
||||
if !g.Replace(v, disabled) {
|
||||
panic(fmt.Sprintf(
|
||||
"vertex disappeared from under us: %s",
|
||||
dag.VertexName(v)))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProviderTransformer is a GraphTransformer that maps resources to
|
||||
// providers within the graph. This will error if there are any resources
|
||||
// that don't map to proper resources.
|
||||
|
@ -381,118 +332,6 @@ func closeProviderVertexMap(g *Graph) map[string]dag.Vertex {
|
|||
return m
|
||||
}
|
||||
|
||||
type graphNodeDisabledProvider struct {
|
||||
GraphNodeProvider
|
||||
}
|
||||
|
||||
// GraphNodeEvalable impl.
|
||||
func (n *graphNodeDisabledProvider) EvalTree() EvalNode {
|
||||
var resourceConfig *ResourceConfig
|
||||
|
||||
return &EvalOpFilter{
|
||||
Ops: []walkOperation{walkInput, walkValidate, walkRefresh, walkPlan, walkApply, walkDestroy},
|
||||
Node: &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalInterpolate{
|
||||
Config: n.ProviderConfig(),
|
||||
Output: &resourceConfig,
|
||||
},
|
||||
&EvalBuildProviderConfig{
|
||||
Provider: n.ProviderName(),
|
||||
Config: &resourceConfig,
|
||||
Output: &resourceConfig,
|
||||
},
|
||||
&EvalSetProviderConfig{
|
||||
Provider: n.ProviderName(),
|
||||
Config: &resourceConfig,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GraphNodeFlattenable impl.
|
||||
func (n *graphNodeDisabledProvider) Flatten(p []string) (dag.Vertex, error) {
|
||||
return &graphNodeDisabledProviderFlat{
|
||||
graphNodeDisabledProvider: n,
|
||||
PathValue: p,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *graphNodeDisabledProvider) Name() string {
|
||||
return fmt.Sprintf("%s (disabled)", dag.VertexName(n.GraphNodeProvider))
|
||||
}
|
||||
|
||||
// GraphNodeDotter impl.
|
||||
func (n *graphNodeDisabledProvider) DotNode(name string, opts *GraphDotOpts) *dot.Node {
|
||||
return dot.NewNode(name, map[string]string{
|
||||
"label": n.Name(),
|
||||
"shape": "diamond",
|
||||
})
|
||||
}
|
||||
|
||||
// GraphNodeDotterOrigin impl.
|
||||
func (n *graphNodeDisabledProvider) DotOrigin() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GraphNodeDependable impl.
|
||||
func (n *graphNodeDisabledProvider) DependableName() []string {
|
||||
return []string{"provider." + n.ProviderName()}
|
||||
}
|
||||
|
||||
// GraphNodeProvider impl.
|
||||
func (n *graphNodeDisabledProvider) ProviderName() string {
|
||||
return n.GraphNodeProvider.ProviderName()
|
||||
}
|
||||
|
||||
// GraphNodeProvider impl.
|
||||
func (n *graphNodeDisabledProvider) ProviderConfig() *config.RawConfig {
|
||||
return n.GraphNodeProvider.ProviderConfig()
|
||||
}
|
||||
|
||||
// Same as graphNodeDisabledProvider, but for flattening
|
||||
type graphNodeDisabledProviderFlat struct {
|
||||
*graphNodeDisabledProvider
|
||||
|
||||
PathValue []string
|
||||
}
|
||||
|
||||
func (n *graphNodeDisabledProviderFlat) Name() string {
|
||||
return fmt.Sprintf(
|
||||
"%s.%s", modulePrefixStr(n.PathValue), n.graphNodeDisabledProvider.Name())
|
||||
}
|
||||
|
||||
func (n *graphNodeDisabledProviderFlat) Path() []string {
|
||||
return n.PathValue
|
||||
}
|
||||
|
||||
func (n *graphNodeDisabledProviderFlat) ProviderName() string {
|
||||
return fmt.Sprintf(
|
||||
"%s.%s", modulePrefixStr(n.PathValue),
|
||||
n.graphNodeDisabledProvider.ProviderName())
|
||||
}
|
||||
|
||||
// GraphNodeDependable impl.
|
||||
func (n *graphNodeDisabledProviderFlat) DependableName() []string {
|
||||
return modulePrefixList(
|
||||
n.graphNodeDisabledProvider.DependableName(),
|
||||
modulePrefixStr(n.PathValue))
|
||||
}
|
||||
|
||||
func (n *graphNodeDisabledProviderFlat) DependentOn() []string {
|
||||
var result []string
|
||||
|
||||
// If we're in a module, then depend on our parent's provider
|
||||
if len(n.PathValue) > 1 {
|
||||
prefix := modulePrefixStr(n.PathValue[:len(n.PathValue)-1])
|
||||
result = modulePrefixList(
|
||||
n.graphNodeDisabledProvider.DependableName(), prefix)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
type graphNodeCloseProvider struct {
|
||||
ProviderNameValue string
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
)
|
||||
|
||||
// DisableProviderTransformer "disables" any providers that are not actually
|
||||
// used by anything. This avoids the provider being initialized and configured.
|
||||
// This both saves resources but also avoids errors since configuration
|
||||
// may imply initialization which may require auth.
|
||||
type DisableProviderTransformer struct{}
|
||||
|
||||
func (t *DisableProviderTransformer) Transform(g *Graph) error {
|
||||
for _, v := range g.Vertices() {
|
||||
// We only care about providers
|
||||
pn, ok := v.(GraphNodeProvider)
|
||||
if !ok || pn.ProviderName() == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// If we have dependencies, then don't disable
|
||||
if g.UpEdges(v).Len() > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the path
|
||||
var path []string
|
||||
if pn, ok := v.(GraphNodeSubPath); ok {
|
||||
path = pn.Path()
|
||||
}
|
||||
|
||||
// Disable the provider by replacing it with a "disabled" provider
|
||||
disabled := &NodeDisabledProvider{
|
||||
NodeAbstractProvider: &NodeAbstractProvider{
|
||||
NameValue: pn.ProviderName(),
|
||||
PathValue: path,
|
||||
},
|
||||
}
|
||||
|
||||
if !g.Replace(v, disabled) {
|
||||
panic(fmt.Sprintf(
|
||||
"vertex disappeared from under us: %s",
|
||||
dag.VertexName(v)))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
"github.com/hashicorp/terraform/dot"
|
||||
)
|
||||
|
||||
// DisableProviderTransformer "disables" any providers that are only
|
||||
// depended on by modules.
|
||||
//
|
||||
// NOTE: "old" = used by old graph builders, will be removed one day
|
||||
type DisableProviderTransformerOld struct{}
|
||||
|
||||
func (t *DisableProviderTransformerOld) Transform(g *Graph) error {
|
||||
// Since we're comparing against edges, we need to make sure we connect
|
||||
g.ConnectDependents()
|
||||
|
||||
for _, v := range g.Vertices() {
|
||||
// We only care about providers
|
||||
pn, ok := v.(GraphNodeProvider)
|
||||
if !ok || pn.ProviderName() == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Go through all the up-edges (things that depend on this
|
||||
// provider) and if any is not a module, then ignore this node.
|
||||
nonModule := false
|
||||
for _, sourceRaw := range g.UpEdges(v).List() {
|
||||
source := sourceRaw.(dag.Vertex)
|
||||
cn, ok := source.(graphNodeConfig)
|
||||
if !ok {
|
||||
nonModule = true
|
||||
break
|
||||
}
|
||||
|
||||
if cn.ConfigType() != GraphNodeConfigTypeModule {
|
||||
nonModule = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if nonModule {
|
||||
// We found something that depends on this provider that
|
||||
// isn't a module, so skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
// Disable the provider by replacing it with a "disabled" provider
|
||||
disabled := &graphNodeDisabledProvider{GraphNodeProvider: pn}
|
||||
if !g.Replace(v, disabled) {
|
||||
panic(fmt.Sprintf(
|
||||
"vertex disappeared from under us: %s",
|
||||
dag.VertexName(v)))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type graphNodeDisabledProvider struct {
|
||||
GraphNodeProvider
|
||||
}
|
||||
|
||||
// GraphNodeEvalable impl.
|
||||
func (n *graphNodeDisabledProvider) EvalTree() EvalNode {
|
||||
var resourceConfig *ResourceConfig
|
||||
|
||||
return &EvalOpFilter{
|
||||
Ops: []walkOperation{walkInput, walkValidate, walkRefresh, walkPlan, walkApply, walkDestroy},
|
||||
Node: &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalInterpolate{
|
||||
Config: n.ProviderConfig(),
|
||||
Output: &resourceConfig,
|
||||
},
|
||||
&EvalBuildProviderConfig{
|
||||
Provider: n.ProviderName(),
|
||||
Config: &resourceConfig,
|
||||
Output: &resourceConfig,
|
||||
},
|
||||
&EvalSetProviderConfig{
|
||||
Provider: n.ProviderName(),
|
||||
Config: &resourceConfig,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GraphNodeFlattenable impl.
|
||||
func (n *graphNodeDisabledProvider) Flatten(p []string) (dag.Vertex, error) {
|
||||
return &graphNodeDisabledProviderFlat{
|
||||
graphNodeDisabledProvider: n,
|
||||
PathValue: p,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *graphNodeDisabledProvider) Name() string {
|
||||
return fmt.Sprintf("%s (disabled)", dag.VertexName(n.GraphNodeProvider))
|
||||
}
|
||||
|
||||
// GraphNodeDotter impl.
|
||||
func (n *graphNodeDisabledProvider) DotNode(name string, opts *GraphDotOpts) *dot.Node {
|
||||
return dot.NewNode(name, map[string]string{
|
||||
"label": n.Name(),
|
||||
"shape": "diamond",
|
||||
})
|
||||
}
|
||||
|
||||
// GraphNodeDotterOrigin impl.
|
||||
func (n *graphNodeDisabledProvider) DotOrigin() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GraphNodeDependable impl.
|
||||
func (n *graphNodeDisabledProvider) DependableName() []string {
|
||||
return []string{"provider." + n.ProviderName()}
|
||||
}
|
||||
|
||||
// GraphNodeProvider impl.
|
||||
func (n *graphNodeDisabledProvider) ProviderName() string {
|
||||
return n.GraphNodeProvider.ProviderName()
|
||||
}
|
||||
|
||||
// GraphNodeProvider impl.
|
||||
func (n *graphNodeDisabledProvider) ProviderConfig() *config.RawConfig {
|
||||
return n.GraphNodeProvider.ProviderConfig()
|
||||
}
|
||||
|
||||
// Same as graphNodeDisabledProvider, but for flattening
|
||||
type graphNodeDisabledProviderFlat struct {
|
||||
*graphNodeDisabledProvider
|
||||
|
||||
PathValue []string
|
||||
}
|
||||
|
||||
func (n *graphNodeDisabledProviderFlat) Name() string {
|
||||
return fmt.Sprintf(
|
||||
"%s.%s", modulePrefixStr(n.PathValue), n.graphNodeDisabledProvider.Name())
|
||||
}
|
||||
|
||||
func (n *graphNodeDisabledProviderFlat) Path() []string {
|
||||
return n.PathValue
|
||||
}
|
||||
|
||||
func (n *graphNodeDisabledProviderFlat) ProviderName() string {
|
||||
return fmt.Sprintf(
|
||||
"%s.%s", modulePrefixStr(n.PathValue),
|
||||
n.graphNodeDisabledProvider.ProviderName())
|
||||
}
|
||||
|
||||
// GraphNodeDependable impl.
|
||||
func (n *graphNodeDisabledProviderFlat) DependableName() []string {
|
||||
return modulePrefixList(
|
||||
n.graphNodeDisabledProvider.DependableName(),
|
||||
modulePrefixStr(n.PathValue))
|
||||
}
|
||||
|
||||
func (n *graphNodeDisabledProviderFlat) DependentOn() []string {
|
||||
var result []string
|
||||
|
||||
// If we're in a module, then depend on our parent's provider
|
||||
if len(n.PathValue) > 1 {
|
||||
prefix := modulePrefixStr(n.PathValue[:len(n.PathValue)-1])
|
||||
result = modulePrefixList(
|
||||
n.graphNodeDisabledProvider.DependableName(), prefix)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
Loading…
Reference in New Issue