From ae6b85e11bdbc2fb4f729b8a18d52848c2093da2 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 26 Jun 2014 17:17:10 -0700 Subject: [PATCH] terraform: diff hooks --- command/plan.go | 1 + main.go | 3 --- terraform/hook.go | 18 +++++++++++++++--- terraform/hook_mock.go | 26 ++++++++++++++++++++++++++ terraform/terraform.go | 14 ++++++++++++-- terraform/terraform_test.go | 30 +++++++++++++++++++++++++----- 6 files changed, 79 insertions(+), 13 deletions(-) diff --git a/command/plan.go b/command/plan.go index bd26a5d510..0c28753632 100644 --- a/command/plan.go +++ b/command/plan.go @@ -62,6 +62,7 @@ func (c *PlanCommand) Run(args []string) int { return 1 } + c.TFConfig.Hooks = append(c.TFConfig.Hooks, &UiHook{Ui: c.Ui}) tf, err := terraform.New(c.TFConfig) if err != nil { c.Ui.Error(fmt.Sprintf("Error initializing Terraform: %s", err)) diff --git a/main.go b/main.go index fbe6b99487..31c0d5aed0 100644 --- a/main.go +++ b/main.go @@ -7,9 +7,7 @@ import ( "log" "os" - "github.com/hashicorp/terraform/command" "github.com/hashicorp/terraform/plugin" - "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/cli" "github.com/mitchellh/panicwrap" "github.com/mitchellh/prefixedio" @@ -86,7 +84,6 @@ func wrappedMain() int { defer plugin.CleanupClients() // Initialize the TFConfig settings for the commands... - TFConfig.Hooks = []terraform.Hook{&command.UiHook{Ui: Ui}} TFConfig.Providers = config.ProviderFactories() // Get the command line args. We shortcut "--version" and "-v" to diff --git a/terraform/hook.go b/terraform/hook.go index dc1a16fbd6..33411c84a0 100644 --- a/terraform/hook.go +++ b/terraform/hook.go @@ -21,10 +21,14 @@ const ( // NilHook into your struct, which implements all of the interface but does // nothing. Then, override only the functions you want to implement. type Hook interface { - // PreRefresh is called before a resource is refreshed. - PreRefresh(string, *ResourceState) (HookAction, error) + // PreDiff and PostDiff are called before and after a single resource + // resource is diffed. + PreDiff(string, *ResourceState) (HookAction, error) + PostDiff(string, *ResourceDiff) (HookAction, error) - // PostRefresh is called after a resource is refreshed. + // PreRefresh and PostRefresh are called before and after a single + // resource state is refreshed, respectively. + PreRefresh(string, *ResourceState) (HookAction, error) PostRefresh(string, *ResourceState) (HookAction, error) } @@ -33,6 +37,14 @@ type Hook interface { // and only implement the functions you are interested in. type NilHook struct{} +func (*NilHook) PreDiff(string, *ResourceState) (HookAction, error) { + return HookActionContinue, nil +} + +func (*NilHook) PostDiff(string, *ResourceDiff) (HookAction, error) { + return HookActionContinue, nil +} + func (*NilHook) PreRefresh(string, *ResourceState) (HookAction, error) { return HookActionContinue, nil } diff --git a/terraform/hook_mock.go b/terraform/hook_mock.go index 90b1852e94..f78ebe05aa 100644 --- a/terraform/hook_mock.go +++ b/terraform/hook_mock.go @@ -3,6 +3,18 @@ package terraform // MockHook is an implementation of Hook that can be used for tests. // It records all of its function calls. type MockHook struct { + PreDiffCalled bool + PreDiffId string + PreDiffState *ResourceState + PreDiffReturn HookAction + PreDiffError error + + PostDiffCalled bool + PostDiffId string + PostDiffDiff *ResourceDiff + PostDiffReturn HookAction + PostDiffError error + PostRefreshCalled bool PostRefreshId string PostRefreshState *ResourceState @@ -16,6 +28,20 @@ type MockHook struct { PreRefreshError error } +func (h *MockHook) PreDiff(n string, s *ResourceState) (HookAction, error) { + h.PreDiffCalled = true + h.PreDiffId = n + h.PreDiffState = s + return h.PreDiffReturn, h.PreDiffError +} + +func (h *MockHook) PostDiff(n string, d *ResourceDiff) (HookAction, error) { + h.PostDiffCalled = true + h.PostDiffId = n + h.PostDiffDiff = d + return h.PostDiffReturn, h.PostDiffError +} + func (h *MockHook) PreRefresh(n string, s *ResourceState) (HookAction, error) { h.PreRefreshCalled = true h.PreRefreshId = n diff --git a/terraform/terraform.go b/terraform/terraform.go index b3266f59fc..9be29dec83 100644 --- a/terraform/terraform.go +++ b/terraform/terraform.go @@ -243,6 +243,11 @@ func (t *Terraform) planWalkFn( cb := func(r *Resource) (map[string]string, error) { var diff *ResourceDiff + for _, h := range t.hooks { + // TODO: return value + h.PreDiff(r.Id, r.State) + } + if r.Config == nil { log.Printf("[DEBUG] %s: Orphan, marking for destroy", r.Id) @@ -265,6 +270,11 @@ func (t *Terraform) planWalkFn( } l.Unlock() + for _, h := range t.hooks { + // TODO: return value + h.PostDiff(r.Id, diff) + } + // Determine the new state and update variables vars := make(map[string]string) if !diff.Empty() { @@ -311,7 +321,7 @@ func (t *Terraform) genericWalkFn( } for k, p := range m.Providers { - log.Printf("Configuring provider: %s", k) + log.Printf("[INFO] Configuring provider: %s", k) err := p.Configure(rc) if err != nil { return err @@ -345,7 +355,7 @@ func (t *Terraform) genericWalkFn( } // Call the callack - log.Printf("Walking: %s", rn.Resource.Id) + log.Printf("[INFO] Walking: %s", rn.Resource.Id) newVars, err := cb(rn.Resource) if err != nil { return err diff --git a/terraform/terraform_test.go b/terraform/terraform_test.go index c462e12f7c..d067f1d66a 100644 --- a/terraform/terraform_test.go +++ b/terraform/terraform_test.go @@ -161,6 +161,24 @@ func TestTerraformPlan_computed(t *testing.T) { } } +func TestTerraformPlan_hook(t *testing.T) { + c := testConfig(t, "plan-good") + h := new(MockHook) + tf := testTerraform2(t, &Config{ + Hooks: []Hook{h}, + }) + + if _, err := tf.Plan(c, nil, nil); err != nil { + t.Fatalf("err: %s", err) + } + if !h.PreDiffCalled { + t.Fatal("should be called") + } + if !h.PostDiffCalled { + t.Fatal("should be called") + } +} + func TestTerraformPlan_orphan(t *testing.T) { c := testConfig(t, "plan-orphan") tf := testTerraform2(t, nil) @@ -462,11 +480,13 @@ func testProviderMock(p ResourceProvider) *MockResourceProvider { func testTerraform2(t *testing.T, c *Config) *Terraform { if c == nil { - c = &Config{ - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFunc("aws", []string{"aws_instance"}), - "do": testProviderFunc("do", []string{"do_droplet"}), - }, + c = new(Config) + } + + if c.Providers == nil { + c.Providers = map[string]ResourceProviderFactory{ + "aws": testProviderFunc("aws", []string{"aws_instance"}), + "do": testProviderFunc("do", []string{"do_droplet"}), } }