Merge pull request #10037 from hashicorp/f-default-graph
terraform: default new graphs on, old graphs behind -Xlegacy-graph
This commit is contained in:
commit
26490f71a7
|
@ -10,7 +10,6 @@ install:
|
||||||
- bash scripts/gogetcookie.sh
|
- bash scripts/gogetcookie.sh
|
||||||
script:
|
script:
|
||||||
- make test vet
|
- make test vet
|
||||||
- make test TEST=./terraform TESTARGS=-Xnew-apply
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
|
|
@ -98,44 +98,14 @@ func (c *ApplyCommand) Run(args []string) int {
|
||||||
|
|
||||||
terraform.SetDebugInfo(DefaultDataDir)
|
terraform.SetDebugInfo(DefaultDataDir)
|
||||||
|
|
||||||
// Check for the new apply
|
// Check for the legacy graph
|
||||||
if experiment.Enabled(experiment.X_newApply) && !experiment.Force() {
|
if experiment.Enabled(experiment.X_legacyGraph) {
|
||||||
desc := "Experimental new apply graph has been enabled. This may still\n" +
|
c.Ui.Output(c.Colorize().Color(
|
||||||
"have bugs, and should be used with care. If you'd like to continue,\n" +
|
"[reset][bold][yellow]" +
|
||||||
"you must enter exactly 'yes' as a response."
|
"Legacy graph enabled! This will use the graph from Terraform 0.7.x\n" +
|
||||||
v, err := c.UIInput().Input(&terraform.InputOpts{
|
"to execute this operation. This will be removed in the future so\n" +
|
||||||
Id: "Xnew-apply",
|
"please report any issues causing you to use this to the Terraform\n" +
|
||||||
Query: "Experimental feature enabled: new apply graph. Continue?",
|
"project.\n\n"))
|
||||||
Description: desc,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
c.Ui.Error(fmt.Sprintf("Error asking for confirmation: %s", err))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if v != "yes" {
|
|
||||||
c.Ui.Output("Apply cancelled.")
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for the new destroy
|
|
||||||
if experiment.Enabled(experiment.X_newDestroy) && !experiment.Force() {
|
|
||||||
desc := "Experimental new destroy graph has been enabled. This may still\n" +
|
|
||||||
"have bugs, and should be used with care. If you'd like to continue,\n" +
|
|
||||||
"you must enter exactly 'yes' as a response."
|
|
||||||
v, err := c.UIInput().Input(&terraform.InputOpts{
|
|
||||||
Id: "Xnew-destroy",
|
|
||||||
Query: "Experimental feature enabled: new destroy graph. Continue?",
|
|
||||||
Description: desc,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
c.Ui.Error(fmt.Sprintf("Error asking for confirmation: %s", err))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if v != "yes" {
|
|
||||||
c.Ui.Output("Apply cancelled.")
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is going to keep track of shadow errors
|
// This is going to keep track of shadow errors
|
||||||
|
|
|
@ -184,7 +184,7 @@ func TestApply_destroyTargeted(t *testing.T) {
|
||||||
actualStr := strings.TrimSpace(state.String())
|
actualStr := strings.TrimSpace(state.String())
|
||||||
expectedStr := strings.TrimSpace(testApplyDestroyStr)
|
expectedStr := strings.TrimSpace(testApplyDestroyStr)
|
||||||
if actualStr != expectedStr {
|
if actualStr != expectedStr {
|
||||||
t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
|
t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actualStr, expectedStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should have a backup file
|
// Should have a backup file
|
||||||
|
|
|
@ -50,11 +50,8 @@ import (
|
||||||
// of definition and use. This allows the compiler to enforce references
|
// of definition and use. This allows the compiler to enforce references
|
||||||
// so it becomes easy to remove the features.
|
// so it becomes easy to remove the features.
|
||||||
var (
|
var (
|
||||||
// New apply graph. This will be removed and be the default in 0.8.0.
|
// Reuse the old graphs from TF 0.7.x. These will be removed at some point.
|
||||||
X_newApply = newBasicID("new-apply", "NEW_APPLY", false)
|
X_legacyGraph = newBasicID("legacy-graph", "LEGACY_GRAPH", false)
|
||||||
|
|
||||||
// New destroy graph. This will be reomved and be the default in 0.8.0.
|
|
||||||
X_newDestroy = newBasicID("new-destroy", "NEW_DESTROY", false)
|
|
||||||
|
|
||||||
// Shadow graph. This is already on by default. Disabling it will be
|
// Shadow graph. This is already on by default. Disabling it will be
|
||||||
// allowed for awhile in order for it to not block operations.
|
// allowed for awhile in order for it to not block operations.
|
||||||
|
@ -78,8 +75,7 @@ var (
|
||||||
func init() {
|
func init() {
|
||||||
// The list of all experiments, update this when an experiment is added.
|
// The list of all experiments, update this when an experiment is added.
|
||||||
All = []ID{
|
All = []ID{
|
||||||
X_newApply,
|
X_legacyGraph,
|
||||||
X_newDestroy,
|
|
||||||
X_shadow,
|
X_shadow,
|
||||||
x_force,
|
x_force,
|
||||||
}
|
}
|
||||||
|
|
|
@ -359,63 +359,26 @@ func (c *Context) Apply() (*State, error) {
|
||||||
// Copy our own state
|
// Copy our own state
|
||||||
c.state = c.state.DeepCopy()
|
c.state = c.state.DeepCopy()
|
||||||
|
|
||||||
X_newApply := experiment.Enabled(experiment.X_newApply)
|
// Enable the new graph by default
|
||||||
X_newDestroy := experiment.Enabled(experiment.X_newDestroy)
|
X_legacyGraph := experiment.Enabled(experiment.X_legacyGraph)
|
||||||
newGraphEnabled := (c.destroy && X_newDestroy) || (!c.destroy && X_newApply)
|
|
||||||
|
|
||||||
// Build the original graph. This is before the new graph builders
|
// Build the graph.
|
||||||
// coming in 0.8. We do this for shadow graphing.
|
var graph *Graph
|
||||||
oldGraph, err := c.Graph(&ContextGraphOpts{Validate: true})
|
var err error
|
||||||
if err != nil && X_newApply {
|
if !X_legacyGraph {
|
||||||
// If we had an error graphing but we're using the new graph,
|
graph, err = (&ApplyGraphBuilder{
|
||||||
// just set it to nil and let it go. There are some features that
|
Module: c.module,
|
||||||
// may work with the new graph that don't with the old.
|
Diff: c.diff,
|
||||||
oldGraph = nil
|
State: c.state,
|
||||||
err = nil
|
Providers: c.components.ResourceProviders(),
|
||||||
}
|
Provisioners: c.components.ResourceProvisioners(),
|
||||||
if err != nil {
|
Destroy: c.destroy,
|
||||||
return nil, err
|
}).Build(RootModulePath)
|
||||||
}
|
|
||||||
|
|
||||||
// Build the new graph. We do this no matter what so we can shadow it.
|
|
||||||
newGraph, err := (&ApplyGraphBuilder{
|
|
||||||
Module: c.module,
|
|
||||||
Diff: c.diff,
|
|
||||||
State: c.state,
|
|
||||||
Providers: c.components.ResourceProviders(),
|
|
||||||
Provisioners: c.components.ResourceProvisioners(),
|
|
||||||
Destroy: c.destroy,
|
|
||||||
}).Build(RootModulePath)
|
|
||||||
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:
|
|
||||||
//
|
|
||||||
// * Destroy mode - always use original, shadow with nothing because
|
|
||||||
// we're only testing the new APPLY graph.
|
|
||||||
// * Apply with new apply - use new graph, shadow is new graph. We can't
|
|
||||||
// shadow with the old graph because the old graph does a lot more
|
|
||||||
// that it shouldn't.
|
|
||||||
// * Apply with old apply - use old graph, shadow with new graph.
|
|
||||||
//
|
|
||||||
real := oldGraph
|
|
||||||
shadow := newGraph
|
|
||||||
if newGraphEnabled {
|
|
||||||
log.Printf("[WARN] terraform: real graph is experiment, shadow is experiment")
|
|
||||||
real = shadow
|
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[WARN] terraform: real graph is original, shadow is experiment")
|
graph, err = c.Graph(&ContextGraphOpts{Validate: true})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the operation
|
// Determine the operation
|
||||||
|
@ -424,14 +387,8 @@ func (c *Context) Apply() (*State, error) {
|
||||||
operation = walkDestroy
|
operation = walkDestroy
|
||||||
}
|
}
|
||||||
|
|
||||||
// This shouldn't happen, so assert it. This is before any state changes
|
|
||||||
// so it is safe to crash here.
|
|
||||||
if real == nil {
|
|
||||||
panic("nil real graph")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk the graph
|
// Walk the graph
|
||||||
walker, err := c.walk(real, shadow, operation)
|
walker, err := c.walk(graph, graph, operation)
|
||||||
if len(walker.ValidationErrors) > 0 {
|
if len(walker.ValidationErrors) > 0 {
|
||||||
err = multierror.Append(err, walker.ValidationErrors...)
|
err = multierror.Append(err, walker.ValidationErrors...)
|
||||||
}
|
}
|
||||||
|
@ -488,79 +445,35 @@ func (c *Context) Plan() (*Plan, error) {
|
||||||
c.diffLock.Unlock()
|
c.diffLock.Unlock()
|
||||||
|
|
||||||
// Used throughout below
|
// Used throughout below
|
||||||
X_newApply := experiment.Enabled(experiment.X_newApply)
|
X_legacyGraph := experiment.Enabled(experiment.X_legacyGraph)
|
||||||
X_newDestroy := experiment.Enabled(experiment.X_newDestroy)
|
|
||||||
newGraphEnabled := (c.destroy && X_newDestroy) || (!c.destroy && X_newApply)
|
|
||||||
|
|
||||||
// Build the original graph. This is before the new graph builders
|
// Build the graph.
|
||||||
// coming in 0.8. We do this for shadow graphing.
|
var graph *Graph
|
||||||
oldGraph, err := c.Graph(&ContextGraphOpts{Validate: true})
|
var err error
|
||||||
if err != nil && newGraphEnabled {
|
if !X_legacyGraph {
|
||||||
// If we had an error graphing but we're using the new graph,
|
if c.destroy {
|
||||||
// just set it to nil and let it go. There are some features that
|
graph, err = (&DestroyPlanGraphBuilder{
|
||||||
// may work with the new graph that don't with the old.
|
Module: c.module,
|
||||||
oldGraph = nil
|
State: c.state,
|
||||||
err = nil
|
Targets: c.targets,
|
||||||
|
}).Build(RootModulePath)
|
||||||
|
} else {
|
||||||
|
graph, err = (&PlanGraphBuilder{
|
||||||
|
Module: c.module,
|
||||||
|
State: c.state,
|
||||||
|
Providers: c.components.ResourceProviders(),
|
||||||
|
Targets: c.targets,
|
||||||
|
}).Build(RootModulePath)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
graph, err = c.Graph(&ContextGraphOpts{Validate: true})
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
|
||||||
newGraph, err = (&PlanGraphBuilder{
|
|
||||||
Module: c.module,
|
|
||||||
State: c.state,
|
|
||||||
Providers: c.components.ResourceProviders(),
|
|
||||||
Targets: c.targets,
|
|
||||||
}).Build(RootModulePath)
|
|
||||||
}
|
|
||||||
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
|
// Do the walk
|
||||||
walker, err := c.walk(real, shadow, operation)
|
walker, err := c.walk(graph, graph, operation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -579,7 +492,7 @@ func (c *Context) Plan() (*Plan, error) {
|
||||||
|
|
||||||
// We don't do the reverification during the new destroy plan because
|
// We don't do the reverification during the new destroy plan because
|
||||||
// it will use a different apply process.
|
// it will use a different apply process.
|
||||||
if !newGraphEnabled {
|
if X_legacyGraph {
|
||||||
// Now that we have a diff, we can build the exact graph that Apply will use
|
// 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.
|
// and catch any possible cycles during the Plan phase.
|
||||||
if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil {
|
if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil {
|
||||||
|
|
|
@ -146,9 +146,13 @@ func TestDebug_plan(t *testing.T) {
|
||||||
t.Fatal("no files with data found")
|
t.Fatal("no files with data found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if graphs == 0 {
|
/*
|
||||||
t.Fatal("no no-empty graphs found")
|
TODO: once @jbardin finishes the dot refactor, uncomment this. This
|
||||||
}
|
won't pass since the new graph doesn't implement the dot nodes.
|
||||||
|
if graphs == 0 {
|
||||||
|
t.Fatal("no no-empty graphs found")
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify that no hooks panic on nil input
|
// verify that no hooks panic on nil input
|
||||||
|
|
Loading…
Reference in New Issue