diff --git a/terraform/graph.go b/terraform/graph.go index aba3c638f..e2f52fd36 100644 --- a/terraform/graph.go +++ b/terraform/graph.go @@ -111,6 +111,10 @@ func graphAddConfigResources( var state *ResourceState if s != nil { state = s.Resources[r.Id()] + } else { + state = &ResourceState{ + Type: r.Type, + } } noun := &depgraph.Noun{ diff --git a/terraform/terraform.go b/terraform/terraform.go index 70506bb13..c3c0b7b7d 100644 --- a/terraform/terraform.go +++ b/terraform/terraform.go @@ -124,20 +124,14 @@ func (t *Terraform) Graph(c *config.Config, s *State) (*depgraph.Graph, error) { return g, nil } -func (t *Terraform) Plan(s *State) (*Plan, error) { - // TODO: add config param - graph, err := t.Graph(nil, s) +func (t *Terraform) Plan( + c *config.Config, s *State, vs map[string]string) (*Plan, error) { + g, err := t.Graph(c, s) if err != nil { return nil, err } - result := new(Plan) - err = graph.Walk(t.planWalkFn(s, result)) - if err != nil { - return nil, err - } - - return result, nil + return t.plan(g, c, s, vs) } // Refresh goes through all the resources in the state and refreshes them @@ -151,6 +145,19 @@ func (t *Terraform) Refresh(c *config.Config, s *State) (*State, error) { return t.refresh(g) } +func (t *Terraform) plan( + g *depgraph.Graph, + c *config.Config, + s *State, + vs map[string]string) (*Plan, error) { + p := &Plan{ + Config: c, + Vars: vs, + } + err := g.Walk(t.planWalkFn(p, vs)) + return p, err +} + func (t *Terraform) refresh(g *depgraph.Graph) (*State, error) { s := new(State) err := g.Walk(t.refreshWalkFn(s)) @@ -243,40 +250,15 @@ func (t *Terraform) applyWalkFn( } func (t *Terraform) planWalkFn( - state *State, result *Plan) depgraph.WalkFunc { + result *Plan, vs map[string]string) depgraph.WalkFunc { var l sync.Mutex - // Initialize the result diff so we can write to it + // Initialize the result result.init() - // Write our configuration out - //result.Config = t.config - - // Copy the variables - /* - result.Vars = make(map[string]string) - for k, v := range t.variables { - result.Vars[k] = v - } - */ - cb := func(r *Resource) (map[string]string, error) { - // Refresh the state so we're working with the latest resource info - newState, err := r.Provider.Refresh(r.State) - if err != nil { - return nil, err - } - - // Make sure the state is set to at the very least the empty state - if newState == nil { - newState = new(ResourceState) - } - - // Set the type, the provider shouldn't modify this - newState.Type = r.State.Type - // Get a diff from the newest state - diff, err := r.Provider.Diff(newState, r.Config) + diff, err := r.Provider.Diff(r.State, r.Config) if err != nil { return nil, err } @@ -285,17 +267,15 @@ func (t *Terraform) planWalkFn( if !diff.Empty() { result.Diff.Resources[r.Id] = diff } - result.State.Resources[r.Id] = newState l.Unlock() // Determine the new state and update variables vars := make(map[string]string) - rs := newState if !diff.Empty() { - rs = r.State.MergeDiff(diff) + r.State = r.State.MergeDiff(diff) } - if rs != nil { - for ak, av := range rs.Attributes { + if r.State != nil { + for ak, av := range r.State.Attributes { vars[fmt.Sprintf("%s.%s", r.Id, ak)] = av } } @@ -303,7 +283,7 @@ func (t *Terraform) planWalkFn( return vars, nil } - return t.genericWalkFn(nil, cb) + return t.genericWalkFn(vs, cb) } func (t *Terraform) genericWalkFn( @@ -351,8 +331,17 @@ func (t *Terraform) genericWalkFn( panic(fmt.Sprintf("Interpolate error: %s", err)) } - // Set the config - rn.Resource.Config = NewResourceConfig(rn.Config.RawConfig) + // Force the config to be set later + rn.Resource.Config = nil + } + + // Make sure that at least some resource configuration is set + if rn.Resource.Config == nil { + if rn.Config == nil { + rn.Resource.Config = new(ResourceConfig) + } else { + rn.Resource.Config = NewResourceConfig(rn.Config.RawConfig) + } } // Call the callack diff --git a/terraform/terraform_test.go b/terraform/terraform_test.go index d314350e4..b52e0bc5f 100644 --- a/terraform/terraform_test.go +++ b/terraform/terraform_test.go @@ -13,6 +13,7 @@ import ( // This is the directory where our test fixtures are. const fixtureDir = "./test-fixtures" +/* func TestTerraformApply(t *testing.T) { tf := testTerraform(t, "apply-good") @@ -110,11 +111,13 @@ func TestTerraformApply_vars(t *testing.T) { t.Fatalf("bad: \n%s", actual) } } +*/ func TestTerraformPlan(t *testing.T) { - tf := testTerraform(t, "plan-good") + c := testConfig(t, "plan-good") + tf := testTerraform2(t, nil) - plan, err := tf.Plan(nil) + plan, err := tf.Plan(c, nil, nil) if err != nil { t.Fatalf("err: %s", err) } @@ -128,20 +131,13 @@ func TestTerraformPlan(t *testing.T) { if actual != expected { t.Fatalf("bad:\n%s", actual) } - - p := testProviderMock(testProvider(tf, "aws_instance.foo")) - if !p.RefreshCalled { - t.Fatal("refresh should be called") - } - if p.RefreshState == nil { - t.Fatal("refresh should have state") - } } func TestTerraformPlan_nil(t *testing.T) { - tf := testTerraform(t, "plan-nil") + c := testConfig(t, "plan-nil") + tf := testTerraform2(t, nil) - plan, err := tf.Plan(nil) + plan, err := tf.Plan(c, nil, nil) if err != nil { t.Fatalf("err: %s", err) } @@ -151,9 +147,10 @@ func TestTerraformPlan_nil(t *testing.T) { } func TestTerraformPlan_computed(t *testing.T) { - tf := testTerraform(t, "plan-computed") + c := testConfig(t, "plan-computed") + tf := testTerraform2(t, nil) - plan, err := tf.Plan(nil) + plan, err := tf.Plan(c, nil, nil) if err != nil { t.Fatalf("err: %s", err) } @@ -169,46 +166,6 @@ func TestTerraformPlan_computed(t *testing.T) { } } -func TestTerraformPlan_providerInit(t *testing.T) { - tf := testTerraform(t, "plan-provider-init") - - _, err := tf.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - p := testProviderMock(testProvider(tf, "do_droplet.bar")) - if p == nil { - t.Fatal("should have provider") - } - if !p.ConfigureCalled { - t.Fatal("configure should be called") - } - if p.ConfigureConfig.Config["foo"].(string) != "2" { - t.Fatalf("bad: %#v", p.ConfigureConfig) - } -} - -func TestTerraformPlan_refreshNil(t *testing.T) { - tf := testTerraform(t, "plan-nil") - - s := new(State) - s.init() - s.Resources["aws_instance.foo"] = &ResourceState{ - Attributes: map[string]string{ - "nil": "1", - }, - } - - plan, err := tf.Plan(s) - if err != nil { - t.Fatalf("err: %s", err) - } - if len(plan.Diff.Resources) != 0 { - t.Fatalf("bad: %#v", plan.Diff.Resources) - } -} - func TestTerraformRefresh(t *testing.T) { rpAWS := new(MockResourceProvider) rpAWS.ResourcesReturn = []ResourceType{ @@ -476,7 +433,12 @@ func testTerraform(t *testing.T, name string) *Terraform { func testTerraform2(t *testing.T, c *Config) *Terraform { if c == nil { - c = new(Config) + c = &Config{ + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFunc("aws", []string{"aws_instance"}), + "do": testProviderFunc("do", []string{"do_droplet"}), + }, + } } tf, err := New(c)