terraform: initial hook impl

This commit is contained in:
Mitchell Hashimoto 2014-06-26 16:52:15 -07:00
parent d6fa7f2f6d
commit 501f926eba
5 changed files with 124 additions and 0 deletions

42
terraform/hook.go Normal file
View File

@ -0,0 +1,42 @@
package terraform
// HookAction is an enum of actions that can be taken as a result of a hook
// callback. This allows you to modify the behavior of Terraform at runtime.
type HookAction byte
const (
// HookActionContinue continues with processing as usual.
HookActionContinue HookAction = iota
// HookActionHalt halts immediately: no more hooks are processed
// and the action that Terraform was about to take is cancelled.
HookActionHalt
)
// Hook is the interface that must be implemented to hook into various
// parts of Terraform, allowing you to inspect or change behavior at runtime.
//
// There are MANY hook points into Terraform. If you only want to implement
// some hook points, but not all (which is the likely case), then embed the
// 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(*ResourceState) (HookAction, error)
// PostRefresh is called after a resource is refreshed.
PostRefresh(*ResourceState) (HookAction, error)
}
// NilHook is a Hook implementation that does nothing. It exists only to
// simplify implementing hooks. You can embed this into your Hook implementation
// and only implement the functions you are interested in.
type NilHook struct{}
func (*NilHook) PreRefresh(*ResourceState) (HookAction, error) {
return HookActionContinue, nil
}
func (*NilHook) PostRefresh(*ResourceState) (HookAction, error) {
return HookActionContinue, nil
}

27
terraform/hook_mock.go Normal file
View File

@ -0,0 +1,27 @@
package terraform
// MockHook is an implementation of Hook that can be used for tests.
// It records all of its function calls.
type MockHook struct {
PostRefreshCalled bool
PostRefreshState *ResourceState
PostRefreshReturn HookAction
PostRefreshError error
PreRefreshCalled bool
PreRefreshState *ResourceState
PreRefreshReturn HookAction
PreRefreshError error
}
func (h *MockHook) PreRefresh(s *ResourceState) (HookAction, error) {
h.PreRefreshCalled = true
h.PreRefreshState = s
return h.PreRefreshReturn, h.PreRefreshError
}
func (h *MockHook) PostRefresh(s *ResourceState) (HookAction, error) {
h.PostRefreshCalled = true
h.PostRefreshState = s
return h.PostRefreshReturn, h.PostRefreshError
}

9
terraform/hook_test.go Normal file
View File

@ -0,0 +1,9 @@
package terraform
import (
"testing"
)
func TestNilHook_impl(t *testing.T) {
var _ Hook = new(NilHook)
}

View File

@ -13,6 +13,7 @@ import (
// Terraform from code, and can perform operations such as returning
// all resources, a resource tree, a specific resource, etc.
type Terraform struct {
hooks []Hook
providers map[string]ResourceProviderFactory
}
@ -23,6 +24,7 @@ type genericWalkFunc func(*Resource) (map[string]string, error)
// Config is the configuration that must be given to instantiate
// a Terraform structure.
type Config struct {
Hooks []Hook
Providers map[string]ResourceProviderFactory
}
@ -34,6 +36,7 @@ type Config struct {
// can be properly initialized, can be configured, etc.
func New(c *Config) (*Terraform, error) {
return &Terraform{
hooks: c.Hooks,
providers: c.Providers,
}, nil
}
@ -128,6 +131,11 @@ func (t *Terraform) refreshWalkFn(result *State) depgraph.WalkFunc {
result.init()
cb := func(r *Resource) (map[string]string, error) {
for _, h := range t.hooks {
// TODO: return value
h.PreRefresh(r.State)
}
rs, err := r.Provider.Refresh(r.State)
if err != nil {
return nil, err
@ -143,6 +151,11 @@ func (t *Terraform) refreshWalkFn(result *State) depgraph.WalkFunc {
result.Resources[r.Id] = rs
l.Unlock()
for _, h := range t.hooks {
// TODO: return value
h.PostRefresh(rs)
}
return nil, nil
}

View File

@ -252,6 +252,39 @@ func TestTerraformRefresh(t *testing.T) {
}
}
func TestTerraformRefresh_hook(t *testing.T) {
rpAWS := new(MockResourceProvider)
rpAWS.ResourcesReturn = []ResourceType{
ResourceType{Name: "aws_instance"},
}
h := new(MockHook)
c := testConfig(t, "refresh-basic")
tf := testTerraform2(t, &Config{
Hooks: []Hook{h},
Providers: map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(rpAWS),
},
})
if _, err := tf.Refresh(c, nil); err != nil {
t.Fatalf("err: %s", err)
}
if !h.PreRefreshCalled {
t.Fatal("should be called")
}
if h.PreRefreshState.Type != "aws_instance" {
t.Fatalf("bad: %#v", h.PreRefreshState)
}
if !h.PostRefreshCalled {
t.Fatal("should be called")
}
if h.PostRefreshState.Type != "aws_instance" {
t.Fatalf("bad: %#v", h.PostRefreshState)
}
}
func TestTerraformRefresh_state(t *testing.T) {
rpAWS := new(MockResourceProvider)
rpAWS.ResourcesReturn = []ResourceType{