From fb8f2e2753886c4f2074a57984556855ca423db2 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 2 Dec 2016 22:38:49 -0500 Subject: [PATCH] terraform: new Graph API that can return the graph for each op --- terraform/context.go | 92 +++++++++++++++---------- terraform/context_graph_type.go | 26 +++++++ terraform/graph_builder_apply.go | 5 +- terraform/graph_builder_destroy_plan.go | 5 +- terraform/graph_builder_plan.go | 5 +- terraform/graphtype_string.go | 16 +++++ 6 files changed, 111 insertions(+), 38 deletions(-) create mode 100644 terraform/context_graph_type.go create mode 100644 terraform/graphtype_string.go diff --git a/terraform/context.go b/terraform/context.go index fdd65cf6e..2ebeed097 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -189,13 +189,55 @@ func NewContext(opts *ContextOpts) (*Context, error) { } type ContextGraphOpts struct { + // If true, validates the graph structure (checks for cycles). Validate bool - Verbose bool + + // Legacy graphs only: won't prune the graph + Verbose bool } -// Graph returns the graph for this config. -func (c *Context) Graph(g *ContextGraphOpts) (*Graph, error) { - return c.graphBuilder(g).Build(RootModulePath) +// Graph returns the graph used for the given operation type. +// +// The most extensive or complex graph type is GraphTypePlan. +func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, error) { + if opts == nil { + opts = &ContextGraphOpts{Validate: true} + } + + switch typ { + case GraphTypeApply: + return (&ApplyGraphBuilder{ + Module: c.module, + Diff: c.diff, + State: c.state, + Providers: c.components.ResourceProviders(), + Provisioners: c.components.ResourceProvisioners(), + Destroy: c.destroy, + Validate: opts.Validate, + }).Build(RootModulePath) + + case GraphTypePlan: + return (&PlanGraphBuilder{ + Module: c.module, + State: c.state, + Providers: c.components.ResourceProviders(), + Targets: c.targets, + Validate: opts.Validate, + }).Build(RootModulePath) + + case GraphTypePlanDestroy: + return (&DestroyPlanGraphBuilder{ + Module: c.module, + State: c.state, + Targets: c.targets, + Validate: opts.Validate, + }).Build(RootModulePath) + + case GraphTypeLegacy: + return c.graphBuilder(opts).Build(RootModulePath) + } + + return nil, fmt.Errorf("unknown graph type: %s", typ) } // GraphBuilder returns the GraphBuilder that will be used to create @@ -360,7 +402,7 @@ func (c *Context) Input(mode InputMode) error { if mode&InputModeProvider != 0 { // Build the graph - graph, err := c.Graph(&ContextGraphOpts{Validate: true}) + graph, err := c.Graph(GraphTypeLegacy, nil) if err != nil { return err } @@ -390,20 +432,11 @@ func (c *Context) Apply() (*State, error) { X_legacyGraph := experiment.Enabled(experiment.X_legacyGraph) // Build the graph. - var graph *Graph - var err error + graphType := GraphTypeLegacy if !X_legacyGraph { - graph, err = (&ApplyGraphBuilder{ - Module: c.module, - Diff: c.diff, - State: c.state, - Providers: c.components.ResourceProviders(), - Provisioners: c.components.ResourceProvisioners(), - Destroy: c.destroy, - }).Build(RootModulePath) - } else { - graph, err = c.Graph(&ContextGraphOpts{Validate: true}) + graphType = GraphTypeApply } + graph, err := c.Graph(graphType, nil) if err != nil { return nil, err } @@ -475,26 +508,15 @@ func (c *Context) Plan() (*Plan, error) { X_legacyGraph := experiment.Enabled(experiment.X_legacyGraph) // Build the graph. - var graph *Graph - var err error + graphType := GraphTypeLegacy if !X_legacyGraph { if c.destroy { - graph, err = (&DestroyPlanGraphBuilder{ - Module: c.module, - State: c.state, - Targets: c.targets, - }).Build(RootModulePath) + graphType = GraphTypePlanDestroy } else { - graph, err = (&PlanGraphBuilder{ - Module: c.module, - State: c.state, - Providers: c.components.ResourceProviders(), - Targets: c.targets, - }).Build(RootModulePath) + graphType = GraphTypePlan } - } else { - graph, err = c.Graph(&ContextGraphOpts{Validate: true}) } + graph, err := c.Graph(graphType, nil) if err != nil { return nil, err } @@ -522,7 +544,7 @@ func (c *Context) Plan() (*Plan, error) { if X_legacyGraph { // Now that we have a diff, we can build the exact graph that Apply will use // and catch any possible cycles during the Plan phase. - if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil { + if _, err := c.Graph(GraphTypeLegacy, nil); err != nil { return nil, err } } @@ -548,7 +570,7 @@ func (c *Context) Refresh() (*State, error) { c.state = c.state.DeepCopy() // Build the graph - graph, err := c.Graph(&ContextGraphOpts{Validate: true}) + graph, err := c.Graph(GraphTypeLegacy, nil) if err != nil { return nil, err } @@ -619,7 +641,7 @@ func (c *Context) Validate() ([]string, []error) { // 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 // graph again later after Planning. - graph, err := c.Graph(&ContextGraphOpts{Validate: true}) + graph, err := c.Graph(GraphTypeLegacy, nil) if err != nil { return nil, []error{err} } diff --git a/terraform/context_graph_type.go b/terraform/context_graph_type.go new file mode 100644 index 000000000..a204969ff --- /dev/null +++ b/terraform/context_graph_type.go @@ -0,0 +1,26 @@ +package terraform + +//go:generate stringer -type=GraphType context_graph_type.go + +// GraphType is an enum of the type of graph to create with a Context. +// The values of the constants may change so they shouldn't be depended on; +// always use the constant name. +type GraphType byte + +const ( + GraphTypeInvalid GraphType = 0 + GraphTypeLegacy GraphType = iota + GraphTypePlan + GraphTypePlanDestroy + GraphTypeApply +) + +// GraphTypeMap is a mapping of human-readable string to GraphType. This +// is useful to use as the mechanism for human input for configurable +// graph types. +var GraphTypeMap = map[string]GraphType{ + "apply": GraphTypeApply, + "plan": GraphTypePlan, + "plan-destroy": GraphTypePlanDestroy, + "legacy": GraphTypeLegacy, +} diff --git a/terraform/graph_builder_apply.go b/terraform/graph_builder_apply.go index 1f8794c25..6837c6769 100644 --- a/terraform/graph_builder_apply.go +++ b/terraform/graph_builder_apply.go @@ -33,13 +33,16 @@ type ApplyGraphBuilder struct { // Destroy, if true, represents a pure destroy operation Destroy bool + + // Validate will do structural validation of the graph. + Validate bool } // See GraphBuilder func (b *ApplyGraphBuilder) Build(path []string) (*Graph, error) { return (&BasicGraphBuilder{ Steps: b.Steps(), - Validate: true, + Validate: b.Validate, Name: "ApplyGraphBuilder", }).Build(path) } diff --git a/terraform/graph_builder_destroy_plan.go b/terraform/graph_builder_destroy_plan.go index 5a85f9a63..014b348e5 100644 --- a/terraform/graph_builder_destroy_plan.go +++ b/terraform/graph_builder_destroy_plan.go @@ -19,13 +19,16 @@ type DestroyPlanGraphBuilder struct { // Targets are resources to target Targets []string + + // Validate will do structural validation of the graph. + Validate bool } // See GraphBuilder func (b *DestroyPlanGraphBuilder) Build(path []string) (*Graph, error) { return (&BasicGraphBuilder{ Steps: b.Steps(), - Validate: true, + Validate: b.Validate, Name: "DestroyPlanGraphBuilder", }).Build(path) } diff --git a/terraform/graph_builder_plan.go b/terraform/graph_builder_plan.go index b7d6c8aa1..85259e702 100644 --- a/terraform/graph_builder_plan.go +++ b/terraform/graph_builder_plan.go @@ -31,13 +31,16 @@ type PlanGraphBuilder struct { // DisableReduce, if true, will not reduce the graph. Great for testing. DisableReduce bool + + // Validate will do structural validation of the graph. + Validate bool } // See GraphBuilder func (b *PlanGraphBuilder) Build(path []string) (*Graph, error) { return (&BasicGraphBuilder{ Steps: b.Steps(), - Validate: true, + Validate: b.Validate, Name: "PlanGraphBuilder", }).Build(path) } diff --git a/terraform/graphtype_string.go b/terraform/graphtype_string.go new file mode 100644 index 000000000..8e644abb9 --- /dev/null +++ b/terraform/graphtype_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=GraphType context_graph_type.go"; DO NOT EDIT + +package terraform + +import "fmt" + +const _GraphType_name = "GraphTypeInvalidGraphTypeLegacyGraphTypePlanGraphTypePlanDestroyGraphTypeApply" + +var _GraphType_index = [...]uint8{0, 16, 31, 44, 64, 78} + +func (i GraphType) String() string { + if i >= GraphType(len(_GraphType_index)-1) { + return fmt.Sprintf("GraphType(%d)", i) + } + return _GraphType_name[_GraphType_index[i]:_GraphType_index[i+1]] +}