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
}
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))

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

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) {
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"}),
}
}