Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

terraform: new apply graph builder based on the "diff" #9388

Merged
merged 82 commits into from
Oct 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
50afee2
terraform: Diff.Empty should be true for nil Diff
mitchellh Sep 13, 2016
361bc5a
terraform: parse internal resource addresses used in state/diff
mitchellh Sep 13, 2016
3f090df
terraform: start apply-based graph builder, basic diff transform
mitchellh Sep 13, 2016
77b9177
terraform: an incredible number of failing tests!
mitchellh Sep 13, 2016
dc9b9ee
terraform: connect providers in the apply graph
mitchellh Sep 14, 2016
5828a0a
terraform: minimal applies work!
mitchellh Sep 14, 2016
dcc3eb3
terraform: test for ResourceAddress.stateId()
mitchellh Sep 14, 2016
b0bae6d
Adding my test helper while I work on this branch
mitchellh Sep 14, 2016
9ea9e52
terraform: rename Config to Module, tests for diff transform
mitchellh Sep 14, 2016
e784e4a
terraform: remove more nil panics (doesn't change test logic)
mitchellh Sep 14, 2016
ba751c4
terraform: comment to avoid panic
mitchellh Sep 14, 2016
55ef966
config/module: tree.Child on a nil tree works
mitchellh Sep 14, 2016
87bff93
terraform: ParentProviderTransform to connect parent providers
mitchellh Sep 14, 2016
11578f0
terraform: tests for ParentProviderTransformer
mitchellh Sep 14, 2016
b2ef4e9
terraform: add way to toggle the graphs to use for apply
mitchellh Sep 15, 2016
79a742c
terraform: new provider graph node for flattened world
mitchellh Sep 15, 2016
39abec4
terraform: NodeApplyableProvider evals with config
mitchellh Sep 15, 2016
4033e90
terraform: clarify commment
mitchellh Sep 15, 2016
0f0eecf
terraform: add provisioner nodes to the apply graph
mitchellh Sep 15, 2016
5220cba
terraform: enable provisioners to execute
mitchellh Sep 15, 2016
7dd4813
terraform: rename test to be more easily targetable
mitchellh Sep 15, 2016
e9e8304
terraform: new output transform that isn't used yet
mitchellh Sep 15, 2016
994f5ce
terraform: ReferenceTransform to connect references
mitchellh Sep 16, 2016
21888b1
terraform: test for referencetransform for modules
mitchellh Sep 16, 2016
ba51295
terraform: ReferenceTransform test
mitchellh Sep 16, 2016
0d7674b
terraform: apply builder adds outputs to graphs
mitchellh Sep 16, 2016
7d07f20
terraform: fix references to module outputs
mitchellh Sep 16, 2016
f2aa880
terraform: proper prefix for output connects
mitchellh Sep 16, 2016
6376c4c
terraform: update comment
mitchellh Sep 16, 2016
4dfdc52
terraform: first stap at module variables, going to redo some things
mitchellh Sep 16, 2016
ad03a21
terraform: rename to ModuleVariable
mitchellh Sep 16, 2016
3fb83f0
terraform: depend on parent items
mitchellh Sep 16, 2016
0d81587
terraform: tests for module variable node
mitchellh Sep 16, 2016
993c29f
terraform: move ModuleVariableTransformer to its own file
mitchellh Sep 16, 2016
0463ad7
terraform: RootVariableTransform
mitchellh Sep 16, 2016
0e666aa
terraform: get tests to not panic on failures
mitchellh Sep 16, 2016
dfa02e4
terraform: rename attach config to only attach provider config
mitchellh Sep 16, 2016
2e8a419
terraform: starting work on destroy
mitchellh Sep 17, 2016
9e8cd48
terraform: add destroy nodes, destroys kind of work
mitchellh Sep 17, 2016
80ef7f1
terraform: properly compare bad diffs
mitchellh Sep 17, 2016
cd04ccf
terraform: update a test to be easier to target
mitchellh Sep 18, 2016
5018617
terraform: change node name so that it shows up properly
mitchellh Sep 18, 2016
38b9f77
terraform: reference transformer shouldn't make loop to self
mitchellh Sep 19, 2016
2e8cb94
terraform: orphan outputs are deleted from the state
mitchellh Sep 19, 2016
ebc7d20
terraform: new graph fixes ".0" and "" boundaries on counts
mitchellh Sep 19, 2016
56b4521
terraform: provider depends on config references
mitchellh Sep 19, 2016
ceb5c53
terraform: destroy nodes should call post state update hook
mitchellh Sep 19, 2016
bd5d97f
terraform: transform to attach resource configs
mitchellh Sep 20, 2016
7b2bd93
terraform: test the destroy edge transform
mitchellh Sep 20, 2016
08dade5
terraform: more destroy edge tests
mitchellh Sep 20, 2016
311d271
terraform: Enable DestroyEdgeTransformer
mitchellh Sep 20, 2016
4988378
terraform: remove diff transformer test that no longer happens
mitchellh Sep 20, 2016
7baf64f
terraform: starting CBD, destroy edge for the destroy relationship
mitchellh Sep 21, 2016
b9b23e8
terraform: improved logging
mitchellh Sep 21, 2016
4e8e6cd
dag: add EdgesFrom, EdgesTo, needs tests
mitchellh Sep 21, 2016
6e632ec
dag: test for EdgesFrom, EdgesTo
mitchellh Sep 21, 2016
046faf2
terraform: cleanup and failing test for CBD
mitchellh Sep 21, 2016
6622ca0
terraform: abstract resource nodes
mitchellh Sep 21, 2016
3d4937b
terraform: FlatConfigTransformer
mitchellh Sep 21, 2016
4aa84a2
terraform: CBD makes the proper edge connections for dependent resources
mitchellh Sep 21, 2016
aaee4df
terraform: working on enabling CBD, some cycles
mitchellh Sep 22, 2016
2366579
terraform: destroy resource should have no references
mitchellh Sep 22, 2016
c1664d2
terraform: cbd works!
mitchellh Sep 22, 2016
924f7a4
terraform: module variable transform must do children later (tested)
mitchellh Sep 23, 2016
9ac4ee4
terraform: transform module variables does parent first
mitchellh Sep 24, 2016
7c2c9b8
terraform: interpolation for multi-var checks both ".0" and "" suffix
mitchellh Oct 13, 2016
eb9ecea
terraform: don't set Provider on destroy nodes
mitchellh Oct 14, 2016
ec15783
-Xnew-apply to enable the new apply graph
mitchellh Oct 15, 2016
e8516f2
command/apply: Xnew-apply
mitchellh Oct 15, 2016
5cd6898
config: fix a conflicting test name
mitchellh Oct 15, 2016
e59efa0
terraform: fix merge issues with master
mitchellh Oct 19, 2016
c9c1912
terraform: missing fields from ApplyGraphBuilder after master rebase
mitchellh Oct 19, 2016
d87bdc2
terraform: update destroy resource with proper unique-ifier for shadow
mitchellh Oct 19, 2016
5d598ad
terraform: if components is closed, initialize closed components
mitchellh Oct 19, 2016
7d36e99
terraform: resource address internal can parse data resource addrs
mitchellh Oct 17, 2016
13b9007
terraform: logic for shadowing the original graph
mitchellh Oct 19, 2016
e4ef1fe
terraform: disable providers in new apply graph
mitchellh Oct 19, 2016
d27c8fb
terraform: compared states from shadow graph must be pruned
mitchellh Oct 19, 2016
fa25a30
terraform: orphan resources in old graph need unique ID
mitchellh Oct 19, 2016
51e90cd
terraform: move references for disable provider transform to old
mitchellh Oct 19, 2016
a89dcfd
terraform: re-enable shadow tests
mitchellh Oct 19, 2016
fee0351
config: RawConfig merge should only set unknown keys non-nil if
mitchellh Oct 19, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ install:
- bash scripts/gogetcookie.sh
script:
- make test vet
- make test TEST=./terraform TESTARGS=-Xnew-apply
branches:
only:
- master
Expand Down
20 changes: 20 additions & 0 deletions command/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,26 @@ func (c *ApplyCommand) Run(args []string) int {
}
}

// Check for the new apply
if terraform.X_newApply {
desc := "Experimental new apply 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-apply",
Query: "Experimental feature enabled: new apply 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
}
}

// Build the context based on the arguments given
ctx, planned, err := c.Context(contextOpts{
Destroy: c.Destroy,
Expand Down
3 changes: 3 additions & 0 deletions command/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ func (m *Meta) flagSet(n string) *flag.FlagSet {
f.Var((*FlagKVFile)(&m.autoVariables), m.autoKey, "variable file")
}

// Experimental features
f.BoolVar(&terraform.X_newApply, "Xnew-apply", false, "experiment: new apply")

// Create an io.Writer that writes to our Ui properly for errors.
// This is kind of a hack, but it does the job. Basically: create
// a pipe, use a scanner to break it into lines, and output each line
Expand Down
4 changes: 4 additions & 0 deletions config/module/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ func (t *Tree) Config() *config.Config {

// Child returns the child with the given path (by name).
func (t *Tree) Child(path []string) *Tree {
if t == nil {
return nil
}

if len(path) == 0 {
return t
}
Expand Down
5 changes: 5 additions & 0 deletions config/module/tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import (
)

func TestTreeChild(t *testing.T) {
var nilTree *Tree
if nilTree.Child(nil) != nil {
t.Fatal("child should be nil")
}

storage := testStorage(t)
tree := NewTree("", testConfig(t, "child"))
if err := tree.Load(storage, GetModeGet); err != nil {
Expand Down
22 changes: 12 additions & 10 deletions config/raw_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,17 +191,19 @@ func (r *RawConfig) Merge(other *RawConfig) *RawConfig {
}

// Build the unknown keys
unknownKeys := make(map[string]struct{})
for _, k := range r.unknownKeys {
unknownKeys[k] = struct{}{}
}
for _, k := range other.unknownKeys {
unknownKeys[k] = struct{}{}
}
if len(r.unknownKeys) > 0 || len(other.unknownKeys) > 0 {
unknownKeys := make(map[string]struct{})
for _, k := range r.unknownKeys {
unknownKeys[k] = struct{}{}
}
for _, k := range other.unknownKeys {
unknownKeys[k] = struct{}{}
}

result.unknownKeys = make([]string, 0, len(unknownKeys))
for k, _ := range unknownKeys {
result.unknownKeys = append(result.unknownKeys, k)
result.unknownKeys = make([]string, 0, len(unknownKeys))
for k, _ := range unknownKeys {
result.unknownKeys = append(result.unknownKeys, k)
}
}

return result
Expand Down
2 changes: 1 addition & 1 deletion config/raw_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestNewRawConfig(t *testing.T) {
}
}

func TestRawConfig(t *testing.T) {
func TestRawConfig_basic(t *testing.T) {
raw := map[string]interface{}{
"foo": "${var.bar}",
}
Expand Down
15 changes: 15 additions & 0 deletions config/testing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package config

import (
"testing"
)

// TestRawConfig is used to create a RawConfig for testing.
func TestRawConfig(t *testing.T, c map[string]interface{}) *RawConfig {
cfg, err := NewRawConfig(c)
if err != nil {
t.Fatalf("err: %s", err)
}

return cfg
}
26 changes: 26 additions & 0 deletions dag/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,32 @@ func (g *Graph) Edges() []Edge {
return result
}

// EdgesFrom returns the list of edges from the given source.
func (g *Graph) EdgesFrom(v Vertex) []Edge {
var result []Edge
from := hashcode(v)
for _, e := range g.Edges() {
if hashcode(e.Source()) == from {
result = append(result, e)
}
}

return result
}

// EdgesTo returns the list of edges to the given target.
func (g *Graph) EdgesTo(v Vertex) []Edge {
var result []Edge
search := hashcode(v)
for _, e := range g.Edges() {
if hashcode(e.Target()) == search {
result = append(result, e)
}
}

return result
}

// HasVertex checks if the given Vertex is present in the graph.
func (g *Graph) HasVertex(v Vertex) bool {
return g.vertices.Include(v)
Expand Down
46 changes: 46 additions & 0 deletions dag/graph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,52 @@ func TestGraphHasEdge(t *testing.T) {
}
}

func TestGraphEdgesFrom(t *testing.T) {
var g Graph
g.Add(1)
g.Add(2)
g.Add(3)
g.Connect(BasicEdge(1, 3))
g.Connect(BasicEdge(2, 3))

edges := g.EdgesFrom(1)

var expected Set
expected.Add(BasicEdge(1, 3))

var s Set
for _, e := range edges {
s.Add(e)
}

if s.Intersection(&expected).Len() != expected.Len() {
t.Fatalf("bad: %#v", edges)
}
}

func TestGraphEdgesTo(t *testing.T) {
var g Graph
g.Add(1)
g.Add(2)
g.Add(3)
g.Connect(BasicEdge(1, 3))
g.Connect(BasicEdge(1, 2))

edges := g.EdgesTo(3)

var expected Set
expected.Add(BasicEdge(1, 3))

var s Set
for _, e := range edges {
s.Add(e)
}

if s.Intersection(&expected).Len() != expected.Len() {
t.Fatalf("bad: %#v", edges)
}
}

type hashVertex struct {
code interface{}
}
Expand Down
1 change: 1 addition & 0 deletions go.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go test ./terraform | grep -E '(FAIL|panic)' | tee /dev/tty | wc -l
82 changes: 75 additions & 7 deletions terraform/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ import (
"github.com/hashicorp/terraform/config/module"
)

// Variables prefixed with X_ are experimental features. They can be enabled
// by setting them to true. This should be done before any API is called.
// These should be expected to be removed at some point in the future; each
// option should mention a schedule.
var (
// X_newApply will enable the new apply graph. This will be removed
// and be on by default in 0.8.0.
X_newApply = false
)

// InputMode defines what sort of input will be asked for when Input
// is called on Context.
type InputMode byte
Expand Down Expand Up @@ -353,21 +363,79 @@ func (c *Context) Apply() (*State, error) {
// Copy our own state
c.state = c.state.DeepCopy()

// Build the graph
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 && X_newApply {
// 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
}

// Do the walk
var walker *ContextGraphWalker
// 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(),
}).Build(RootModulePath)
if err != nil && !X_newApply {
// 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 apply 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 c.destroy {
walker, err = c.walk(graph, graph, walkDestroy)
log.Printf("[WARN] terraform: real graph is original, shadow is nil")
shadow = nil
} else {
//walker, err = c.walk(graph, nil, walkApply)
walker, err = c.walk(graph, graph, walkApply)
if X_newApply {
log.Printf("[WARN] terraform: real graph is Xnew-apply, shadow is Xnew-apply")
real = shadow
} else {
log.Printf("[WARN] terraform: real graph is original, shadow is Xnew-apply")
}
}

// Determine the operation
operation := walkApply
if c.destroy {
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
walker, err := c.walk(real, shadow, operation)
if len(walker.ValidationErrors) > 0 {
err = multierror.Append(err, walker.ValidationErrors...)
}
Expand Down
Loading