terraform: diff hooks

This commit is contained in:
Mitchell Hashimoto 2014-06-26 17:17:10 -07:00
parent 01319e1dc9
commit ae6b85e11b
6 changed files with 79 additions and 13 deletions

View File

@ -62,6 +62,7 @@ func (c *PlanCommand) Run(args []string) int {
return 1 return 1
} }
c.TFConfig.Hooks = append(c.TFConfig.Hooks, &UiHook{Ui: c.Ui})
tf, err := terraform.New(c.TFConfig) tf, err := terraform.New(c.TFConfig)
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf("Error initializing Terraform: %s", err)) c.Ui.Error(fmt.Sprintf("Error initializing Terraform: %s", err))

View File

@ -7,9 +7,7 @@ import (
"log" "log"
"os" "os"
"github.com/hashicorp/terraform/command"
"github.com/hashicorp/terraform/plugin" "github.com/hashicorp/terraform/plugin"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli" "github.com/mitchellh/cli"
"github.com/mitchellh/panicwrap" "github.com/mitchellh/panicwrap"
"github.com/mitchellh/prefixedio" "github.com/mitchellh/prefixedio"
@ -86,7 +84,6 @@ func wrappedMain() int {
defer plugin.CleanupClients() defer plugin.CleanupClients()
// Initialize the TFConfig settings for the commands... // Initialize the TFConfig settings for the commands...
TFConfig.Hooks = []terraform.Hook{&command.UiHook{Ui: Ui}}
TFConfig.Providers = config.ProviderFactories() TFConfig.Providers = config.ProviderFactories()
// Get the command line args. We shortcut "--version" and "-v" to // Get the command line args. We shortcut "--version" and "-v" to

View File

@ -21,10 +21,14 @@ const (
// NilHook into your struct, which implements all of the interface but does // NilHook into your struct, which implements all of the interface but does
// nothing. Then, override only the functions you want to implement. // nothing. Then, override only the functions you want to implement.
type Hook interface { type Hook interface {
// PreRefresh is called before a resource is refreshed. // PreDiff and PostDiff are called before and after a single resource
PreRefresh(string, *ResourceState) (HookAction, error) // 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) PostRefresh(string, *ResourceState) (HookAction, error)
} }
@ -33,6 +37,14 @@ type Hook interface {
// and only implement the functions you are interested in. // and only implement the functions you are interested in.
type NilHook struct{} 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) { func (*NilHook) PreRefresh(string, *ResourceState) (HookAction, error) {
return HookActionContinue, nil return HookActionContinue, nil
} }

View File

@ -3,6 +3,18 @@ package terraform
// MockHook is an implementation of Hook that can be used for tests. // MockHook is an implementation of Hook that can be used for tests.
// It records all of its function calls. // It records all of its function calls.
type MockHook struct { 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 PostRefreshCalled bool
PostRefreshId string PostRefreshId string
PostRefreshState *ResourceState PostRefreshState *ResourceState
@ -16,6 +28,20 @@ type MockHook struct {
PreRefreshError error 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) { func (h *MockHook) PreRefresh(n string, s *ResourceState) (HookAction, error) {
h.PreRefreshCalled = true h.PreRefreshCalled = true
h.PreRefreshId = n h.PreRefreshId = n

View File

@ -243,6 +243,11 @@ func (t *Terraform) planWalkFn(
cb := func(r *Resource) (map[string]string, error) { cb := func(r *Resource) (map[string]string, error) {
var diff *ResourceDiff var diff *ResourceDiff
for _, h := range t.hooks {
// TODO: return value
h.PreDiff(r.Id, r.State)
}
if r.Config == nil { if r.Config == nil {
log.Printf("[DEBUG] %s: Orphan, marking for destroy", r.Id) log.Printf("[DEBUG] %s: Orphan, marking for destroy", r.Id)
@ -265,6 +270,11 @@ func (t *Terraform) planWalkFn(
} }
l.Unlock() l.Unlock()
for _, h := range t.hooks {
// TODO: return value
h.PostDiff(r.Id, diff)
}
// Determine the new state and update variables // Determine the new state and update variables
vars := make(map[string]string) vars := make(map[string]string)
if !diff.Empty() { if !diff.Empty() {
@ -311,7 +321,7 @@ func (t *Terraform) genericWalkFn(
} }
for k, p := range m.Providers { for k, p := range m.Providers {
log.Printf("Configuring provider: %s", k) log.Printf("[INFO] Configuring provider: %s", k)
err := p.Configure(rc) err := p.Configure(rc)
if err != nil { if err != nil {
return err return err
@ -345,7 +355,7 @@ func (t *Terraform) genericWalkFn(
} }
// Call the callack // Call the callack
log.Printf("Walking: %s", rn.Resource.Id) log.Printf("[INFO] Walking: %s", rn.Resource.Id)
newVars, err := cb(rn.Resource) newVars, err := cb(rn.Resource)
if err != nil { if err != nil {
return err return err

View File

@ -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) { func TestTerraformPlan_orphan(t *testing.T) {
c := testConfig(t, "plan-orphan") c := testConfig(t, "plan-orphan")
tf := testTerraform2(t, nil) tf := testTerraform2(t, nil)
@ -462,11 +480,13 @@ func testProviderMock(p ResourceProvider) *MockResourceProvider {
func testTerraform2(t *testing.T, c *Config) *Terraform { func testTerraform2(t *testing.T, c *Config) *Terraform {
if c == nil { if c == nil {
c = &Config{ c = new(Config)
Providers: map[string]ResourceProviderFactory{ }
if c.Providers == nil {
c.Providers = map[string]ResourceProviderFactory{
"aws": testProviderFunc("aws", []string{"aws_instance"}), "aws": testProviderFunc("aws", []string{"aws_instance"}),
"do": testProviderFunc("do", []string{"do_droplet"}), "do": testProviderFunc("do", []string{"do_droplet"}),
},
} }
} }