terraform: wip moving validation to new graph
This commit is contained in:
parent
66f6f70cdb
commit
1427075005
|
@ -219,15 +219,32 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, error) {
|
||||||
case GraphTypeInput:
|
case GraphTypeInput:
|
||||||
// The input graph is just a slightly modified plan graph
|
// The input graph is just a slightly modified plan graph
|
||||||
fallthrough
|
fallthrough
|
||||||
|
case GraphTypeValidate:
|
||||||
|
// The validate graph is just a slightly modified plan graph
|
||||||
|
fallthrough
|
||||||
case GraphTypePlan:
|
case GraphTypePlan:
|
||||||
return (&PlanGraphBuilder{
|
// Create the plan graph builder
|
||||||
|
p := &PlanGraphBuilder{
|
||||||
Module: c.module,
|
Module: c.module,
|
||||||
State: c.state,
|
State: c.state,
|
||||||
Providers: c.components.ResourceProviders(),
|
Providers: c.components.ResourceProviders(),
|
||||||
Targets: c.targets,
|
Targets: c.targets,
|
||||||
Validate: opts.Validate,
|
Validate: opts.Validate,
|
||||||
Input: typ == GraphTypeInput,
|
}
|
||||||
}).Build(RootModulePath)
|
|
||||||
|
// Some special cases for other graph types shared with plan currently
|
||||||
|
var b GraphBuilder = p
|
||||||
|
switch typ {
|
||||||
|
case GraphTypeInput:
|
||||||
|
b = InputGraphBuilder(p)
|
||||||
|
case GraphTypeValidate:
|
||||||
|
// We need to set the provisioners so those can be validated
|
||||||
|
p.Provisioners = c.components.ResourceProvisioners()
|
||||||
|
|
||||||
|
b = ValidateGraphBuilder(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.Build(RootModulePath)
|
||||||
|
|
||||||
case GraphTypePlanDestroy:
|
case GraphTypePlanDestroy:
|
||||||
return (&DestroyPlanGraphBuilder{
|
return (&DestroyPlanGraphBuilder{
|
||||||
|
@ -661,7 +678,7 @@ func (c *Context) Validate() ([]string, []error) {
|
||||||
// We also validate the graph generated here, but this graph doesn't
|
// We also validate the graph generated here, but this graph doesn't
|
||||||
// necessarily match the graph that Plan will generate, so we'll validate the
|
// necessarily match the graph that Plan will generate, so we'll validate the
|
||||||
// graph again later after Planning.
|
// graph again later after Planning.
|
||||||
graph, err := c.Graph(GraphTypeLegacy, nil)
|
graph, err := c.Graph(GraphTypeValidate, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, []error{err}
|
return nil, []error{err}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ const (
|
||||||
GraphTypePlanDestroy
|
GraphTypePlanDestroy
|
||||||
GraphTypeApply
|
GraphTypeApply
|
||||||
GraphTypeInput
|
GraphTypeInput
|
||||||
|
GraphTypeValidate
|
||||||
)
|
)
|
||||||
|
|
||||||
// GraphTypeMap is a mapping of human-readable string to GraphType. This
|
// GraphTypeMap is a mapping of human-readable string to GraphType. This
|
||||||
|
@ -27,4 +28,5 @@ var GraphTypeMap = map[string]GraphType{
|
||||||
"plan-destroy": GraphTypePlanDestroy,
|
"plan-destroy": GraphTypePlanDestroy,
|
||||||
"refresh": GraphTypeRefresh,
|
"refresh": GraphTypeRefresh,
|
||||||
"legacy": GraphTypeLegacy,
|
"legacy": GraphTypeLegacy,
|
||||||
|
"validate": GraphTypeValidate,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/dag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InputGraphBuilder creates the graph for the input operation.
|
||||||
|
//
|
||||||
|
// Unlike other graph builders, this is a function since it currently modifies
|
||||||
|
// and is based on the PlanGraphBuilder. The PlanGraphBuilder passed in will be
|
||||||
|
// modified and should not be used for any other operations.
|
||||||
|
func InputGraphBuilder(p *PlanGraphBuilder) GraphBuilder {
|
||||||
|
// We're going to customize the concrete functions
|
||||||
|
p.CustomConcrete = true
|
||||||
|
|
||||||
|
// Set the provider to the normal provider. This will ask for input.
|
||||||
|
p.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
|
||||||
|
return &NodeApplyableProvider{
|
||||||
|
NodeAbstractProvider: a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We purposely don't set any more concrete fields since the remainder
|
||||||
|
// should be no-ops.
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config/module"
|
"github.com/hashicorp/terraform/config/module"
|
||||||
"github.com/hashicorp/terraform/dag"
|
"github.com/hashicorp/terraform/dag"
|
||||||
)
|
)
|
||||||
|
@ -26,6 +28,9 @@ type PlanGraphBuilder struct {
|
||||||
// Providers is the list of providers supported.
|
// Providers is the list of providers supported.
|
||||||
Providers []string
|
Providers []string
|
||||||
|
|
||||||
|
// Provisioners is the list of provisioners supported.
|
||||||
|
Provisioners []string
|
||||||
|
|
||||||
// Targets are resources to target
|
// Targets are resources to target
|
||||||
Targets []string
|
Targets []string
|
||||||
|
|
||||||
|
@ -35,13 +40,15 @@ type PlanGraphBuilder struct {
|
||||||
// Validate will do structural validation of the graph.
|
// Validate will do structural validation of the graph.
|
||||||
Validate bool
|
Validate bool
|
||||||
|
|
||||||
// Input, if true, modifies this graph for inputs. There isn't a
|
// CustomConcrete can be set to customize the node types created
|
||||||
// dedicated input graph because asking for input is identical to
|
// for various parts of the plan. This is useful in order to customize
|
||||||
// planning except for the operations done. You still need to know WHAT
|
// the plan behavior.
|
||||||
// you're going to plan since you only need to ask for input for things
|
CustomConcrete bool
|
||||||
// that are necessary for planning. This requirement makes the graphs
|
ConcreteProvider ConcreteProviderNodeFunc
|
||||||
// very similar.
|
ConcreteResource ConcreteResourceNodeFunc
|
||||||
Input bool
|
ConcreteResourceOrphan ConcreteResourceNodeFunc
|
||||||
|
|
||||||
|
once sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// See GraphBuilder
|
// See GraphBuilder
|
||||||
|
@ -55,32 +62,12 @@ func (b *PlanGraphBuilder) Build(path []string) (*Graph, error) {
|
||||||
|
|
||||||
// See GraphBuilder
|
// See GraphBuilder
|
||||||
func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
||||||
// Custom factory for creating providers.
|
b.once.Do(b.init)
|
||||||
concreteProvider := func(a *NodeAbstractProvider) dag.Vertex {
|
|
||||||
return &NodeApplyableProvider{
|
|
||||||
NodeAbstractProvider: a,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var concreteResource, concreteResourceOrphan ConcreteResourceNodeFunc
|
|
||||||
if !b.Input {
|
|
||||||
concreteResource = func(a *NodeAbstractResource) dag.Vertex {
|
|
||||||
return &NodePlannableResource{
|
|
||||||
NodeAbstractResource: a,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
concreteResourceOrphan = func(a *NodeAbstractResource) dag.Vertex {
|
|
||||||
return &NodePlannableResourceOrphan{
|
|
||||||
NodeAbstractResource: a,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
steps := []GraphTransformer{
|
steps := []GraphTransformer{
|
||||||
// Creates all the resources represented in the config
|
// Creates all the resources represented in the config
|
||||||
&ConfigTransformer{
|
&ConfigTransformer{
|
||||||
Concrete: concreteResource,
|
Concrete: b.ConcreteResource,
|
||||||
Module: b.Module,
|
Module: b.Module,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -89,7 +76,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
||||||
|
|
||||||
// Add orphan resources
|
// Add orphan resources
|
||||||
&OrphanResourceTransformer{
|
&OrphanResourceTransformer{
|
||||||
Concrete: concreteResourceOrphan,
|
Concrete: b.ConcreteResourceOrphan,
|
||||||
State: b.State,
|
State: b.State,
|
||||||
Module: b.Module,
|
Module: b.Module,
|
||||||
},
|
},
|
||||||
|
@ -104,12 +91,21 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
||||||
&RootVariableTransformer{Module: b.Module},
|
&RootVariableTransformer{Module: b.Module},
|
||||||
|
|
||||||
// Create all the providers
|
// Create all the providers
|
||||||
&MissingProviderTransformer{Providers: b.Providers, Concrete: concreteProvider},
|
&MissingProviderTransformer{Providers: b.Providers, Concrete: b.ConcreteProvider},
|
||||||
&ProviderTransformer{},
|
&ProviderTransformer{},
|
||||||
&DisableProviderTransformer{},
|
&DisableProviderTransformer{},
|
||||||
&ParentProviderTransformer{},
|
&ParentProviderTransformer{},
|
||||||
&AttachProviderConfigTransformer{Module: b.Module},
|
&AttachProviderConfigTransformer{Module: b.Module},
|
||||||
|
|
||||||
|
// Provisioner-related transformations. Only add these if requested.
|
||||||
|
GraphTransformIf(
|
||||||
|
func() bool { return b.Provisioners != nil },
|
||||||
|
GraphTransformMulti(
|
||||||
|
&MissingProvisionerTransformer{Provisioners: b.Provisioners},
|
||||||
|
&ProvisionerTransformer{},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
// Add module variables
|
// Add module variables
|
||||||
&ModuleVariableTransformer{Module: b.Module},
|
&ModuleVariableTransformer{Module: b.Module},
|
||||||
|
|
||||||
|
@ -132,3 +128,28 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
||||||
|
|
||||||
return steps
|
return steps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *PlanGraphBuilder) init() {
|
||||||
|
// Do nothing if the user requests customizing the fields
|
||||||
|
if b.CustomConcrete {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
|
||||||
|
return &NodeApplyableProvider{
|
||||||
|
NodeAbstractProvider: a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
|
||||||
|
return &NodePlannableResource{
|
||||||
|
NodeAbstractResource: a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ConcreteResourceOrphan = func(a *NodeAbstractResource) dag.Vertex {
|
||||||
|
return &NodePlannableResourceOrphan{
|
||||||
|
NodeAbstractResource: a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/dag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidateGraphBuilder creates the graph for the validate operation.
|
||||||
|
//
|
||||||
|
// ValidateGraphBuilder is based on the PlanGraphBuilder. We do this so that
|
||||||
|
// we only have to validate what we'd normally plan anyways. The
|
||||||
|
// PlanGraphBuilder given will be modified so it shouldn't be used for anything
|
||||||
|
// else after calling this function.
|
||||||
|
func ValidateGraphBuilder(p *PlanGraphBuilder) GraphBuilder {
|
||||||
|
// We're going to customize the concrete functions
|
||||||
|
p.CustomConcrete = true
|
||||||
|
|
||||||
|
// Set the provider to the normal provider. This will ask for input.
|
||||||
|
p.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
|
||||||
|
return &NodeApplyableProvider{
|
||||||
|
NodeAbstractProvider: a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
|
||||||
|
return &NodeValidatableResource{
|
||||||
|
NodeAbstractResource: a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We purposely don't set any other concrete types since they don't
|
||||||
|
// require validation.
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
// NodeValidatableResource represents a resource that is used for validation
|
||||||
|
// only.
|
||||||
|
type NodeValidatableResource struct {
|
||||||
|
*NodeAbstractResource
|
||||||
|
}
|
||||||
|
|
||||||
|
// GraphNodeEvalable
|
||||||
|
func (n *NodeValidatableResource) EvalTree() EvalNode {
|
||||||
|
addr := n.NodeAbstractResource.Addr
|
||||||
|
|
||||||
|
// Build the resource for eval
|
||||||
|
resource := &Resource{
|
||||||
|
Name: addr.Name,
|
||||||
|
Type: addr.Type,
|
||||||
|
CountIndex: addr.Index,
|
||||||
|
}
|
||||||
|
if resource.CountIndex < 0 {
|
||||||
|
resource.CountIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare a bunch of variables that are used for state during
|
||||||
|
// evaluation. Most of this are written to by-address below.
|
||||||
|
var config *ResourceConfig
|
||||||
|
var provider ResourceProvider
|
||||||
|
|
||||||
|
seq := &EvalSequence{
|
||||||
|
Nodes: []EvalNode{
|
||||||
|
&EvalGetProvider{
|
||||||
|
Name: n.ProvidedBy()[0],
|
||||||
|
Output: &provider,
|
||||||
|
},
|
||||||
|
&EvalInterpolate{
|
||||||
|
Config: n.Config.RawConfig.Copy(),
|
||||||
|
Resource: resource,
|
||||||
|
Output: &config,
|
||||||
|
},
|
||||||
|
&EvalValidateResource{
|
||||||
|
Provider: &provider,
|
||||||
|
Config: &config,
|
||||||
|
ResourceName: n.Config.Name,
|
||||||
|
ResourceType: n.Config.Type,
|
||||||
|
ResourceMode: n.Config.Mode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate all the provisioners
|
||||||
|
for _, p := range n.Config.Provisioners {
|
||||||
|
var provisioner ResourceProvisioner
|
||||||
|
seq.Nodes = append(seq.Nodes, &EvalGetProvisioner{
|
||||||
|
Name: p.Type,
|
||||||
|
Output: &provisioner,
|
||||||
|
}, &EvalInterpolate{
|
||||||
|
Config: p.RawConfig.Copy(),
|
||||||
|
Resource: resource,
|
||||||
|
Output: &config,
|
||||||
|
}, &EvalValidateProvisioner{
|
||||||
|
Provisioner: &provisioner,
|
||||||
|
Config: &config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return seq
|
||||||
|
}
|
Loading…
Reference in New Issue