From ce4ff06d25aab561a30014e8ab82fb889548ace1 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 4 Nov 2016 19:49:57 -0700 Subject: [PATCH] terraform: prepare Plan for shadowing --- terraform/context.go | 75 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/terraform/context.go b/terraform/context.go index 5f9c1e546..5b7fd7075 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -487,28 +487,75 @@ func (c *Context) Plan() (*Plan, error) { c.diffLock.Unlock() // Used throughout below + X_newApply := experiment.Enabled(experiment.X_newDestroy) X_newDestroy := experiment.Enabled(experiment.X_newDestroy) + newGraphEnabled := (c.destroy && X_newDestroy) || (!c.destroy && X_newApply) - // Build the graph. We have a branch here since for the pure-destroy - // plan (c.destroy) we use a much simpler graph builder that simply - // walks the state and reverses edges. - var graph *Graph - var err error - if c.destroy && X_newDestroy { - graph, err = (&DestroyPlanGraphBuilder{ - Module: c.module, - State: c.state, - Targets: c.targets, - }).Build(RootModulePath) - } else { - graph, err = c.Graph(&ContextGraphOpts{Validate: true}) + // Build the original graph. This is before the new graph builders + // coming in 0.8. We do this for shadow graphing. + oldGraph, err := c.Graph(&ContextGraphOpts{Validate: true}) + if err != nil && newGraphEnabled { + // If we had an error graphing but we're using the new graph, + // just set it to nil and let it go. There are some features that + // may work with the new graph that don't with the old. + oldGraph = nil + err = nil } if err != nil { return nil, err } + // Build the new graph. We do this no matter wht so we can shadow it. + var newGraph *Graph + err = nil + if c.destroy { + newGraph, err = (&DestroyPlanGraphBuilder{ + Module: c.module, + State: c.state, + Targets: c.targets, + }).Build(RootModulePath) + } else { + // TODO: new plan graph when its ready + newGraph = nil + } + if err != nil && !newGraphEnabled { + // If we had an error graphing but we're not using this graph, just + // set it to nil and record it as a shadow error. + c.shadowErr = multierror.Append(c.shadowErr, fmt.Errorf( + "Error building new graph: %s", err)) + + newGraph = nil + err = nil + } + if err != nil { + return nil, err + } + + // Determine what is the real and what is the shadow. The logic here + // is straightforward though the if statements are not: + // + // * If the new graph, shadow with experiment in both because the + // experiment has less nodes so the original can't shadow. + // * If not the new graph, shadow with the experiment + // + real := oldGraph + shadow := newGraph + if newGraphEnabled { + log.Printf("[WARN] terraform: real graph is experiment, shadow is experiment") + real = shadow + } else { + log.Printf("[WARN] terraform: real graph is original, shadow is experiment") + } + + // Special case here: if we're using destroy don't shadow it because + // the new destroy graph behaves a bit differently on purpose by not + // setting the module destroy flag. + if c.destroy && !newGraphEnabled { + shadow = nil + } + // Do the walk - walker, err := c.walk(graph, graph, operation) + walker, err := c.walk(real, shadow, operation) if err != nil { return nil, err }