From 429711b938400fad42f89d0df76a53ebeb76b41c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 23 Feb 2015 19:09:48 -0800 Subject: [PATCH] terraform: PostStateUpdate hook and EvalUpdateStateHook --- terraform/eval_context.go | 152 ------------------------------ terraform/eval_context_mock.go | 166 +++++++++++++++++++++++++++++++++ terraform/eval_state.go | 23 ++++- terraform/eval_state_test.go | 27 ++++++ terraform/hook.go | 7 ++ terraform/hook_mock.go | 11 +++ terraform/hook_stop.go | 4 + 7 files changed, 237 insertions(+), 153 deletions(-) create mode 100644 terraform/eval_context_mock.go create mode 100644 terraform/eval_state_test.go diff --git a/terraform/eval_context.go b/terraform/eval_context.go index e1d4eef516..120cf71e77 100644 --- a/terraform/eval_context.go +++ b/terraform/eval_context.go @@ -70,155 +70,3 @@ type EvalContext interface { // be used to modify that state. State() (*State, *sync.RWMutex) } - -// MockEvalContext is a mock version of EvalContext that can be used -// for tests. -type MockEvalContext struct { - HookCalled bool - HookError error - - InputCalled bool - InputInput UIInput - - InitProviderCalled bool - InitProviderName string - InitProviderProvider ResourceProvider - InitProviderError error - - ProviderCalled bool - ProviderName string - ProviderProvider ResourceProvider - - ProviderInputCalled bool - ProviderInputName string - ProviderInputConfig map[string]interface{} - - SetProviderInputCalled bool - SetProviderInputName string - SetProviderInputConfig map[string]interface{} - - ConfigureProviderCalled bool - ConfigureProviderName string - ConfigureProviderConfig *ResourceConfig - ConfigureProviderError error - - ParentProviderConfigCalled bool - ParentProviderConfigName string - ParentProviderConfigConfig *ResourceConfig - - InitProvisionerCalled bool - InitProvisionerName string - InitProvisionerProvisioner ResourceProvisioner - InitProvisionerError error - - ProvisionerCalled bool - ProvisionerName string - ProvisionerProvisioner ResourceProvisioner - - InterpolateCalled bool - InterpolateConfig *config.RawConfig - InterpolateResource *Resource - InterpolateConfigResult *ResourceConfig - InterpolateError error - - PathCalled bool - PathPath []string - - SetVariablesCalled bool - SetVariablesVariables map[string]string - - DiffCalled bool - DiffDiff *Diff - DiffLock *sync.RWMutex - - StateCalled bool - StateState *State - StateLock *sync.RWMutex -} - -func (c *MockEvalContext) Hook(fn func(Hook) (HookAction, error)) error { - c.HookCalled = true - return c.HookError -} - -func (c *MockEvalContext) Input() UIInput { - c.InputCalled = true - return c.InputInput -} - -func (c *MockEvalContext) InitProvider(n string) (ResourceProvider, error) { - c.InitProviderCalled = true - c.InitProviderName = n - return c.InitProviderProvider, c.InitProviderError -} - -func (c *MockEvalContext) Provider(n string) ResourceProvider { - c.ProviderCalled = true - c.ProviderName = n - return c.ProviderProvider -} - -func (c *MockEvalContext) ConfigureProvider(n string, cfg *ResourceConfig) error { - c.ConfigureProviderCalled = true - c.ConfigureProviderName = n - c.ConfigureProviderConfig = cfg - return c.ConfigureProviderError -} - -func (c *MockEvalContext) ParentProviderConfig(n string) *ResourceConfig { - c.ParentProviderConfigCalled = true - c.ParentProviderConfigName = n - return c.ParentProviderConfigConfig -} - -func (c *MockEvalContext) ProviderInput(n string) map[string]interface{} { - c.ProviderInputCalled = true - c.ProviderInputName = n - return c.ProviderInputConfig -} - -func (c *MockEvalContext) SetProviderInput(n string, cfg map[string]interface{}) { - c.SetProviderInputCalled = true - c.SetProviderInputName = n - c.SetProviderInputConfig = cfg -} - -func (c *MockEvalContext) InitProvisioner(n string) (ResourceProvisioner, error) { - c.InitProvisionerCalled = true - c.InitProvisionerName = n - return c.InitProvisionerProvisioner, c.InitProvisionerError -} - -func (c *MockEvalContext) Provisioner(n string) ResourceProvisioner { - c.ProvisionerCalled = true - c.ProvisionerName = n - return c.ProvisionerProvisioner -} - -func (c *MockEvalContext) Interpolate( - config *config.RawConfig, resource *Resource) (*ResourceConfig, error) { - c.InterpolateCalled = true - c.InterpolateConfig = config - c.InterpolateResource = resource - return c.InterpolateConfigResult, c.InterpolateError -} - -func (c *MockEvalContext) Path() []string { - c.PathCalled = true - return c.PathPath -} - -func (c *MockEvalContext) SetVariables(vs map[string]string) { - c.SetVariablesCalled = true - c.SetVariablesVariables = vs -} - -func (c *MockEvalContext) Diff() (*Diff, *sync.RWMutex) { - c.DiffCalled = true - return c.DiffDiff, c.DiffLock -} - -func (c *MockEvalContext) State() (*State, *sync.RWMutex) { - c.StateCalled = true - return c.StateState, c.StateLock -} diff --git a/terraform/eval_context_mock.go b/terraform/eval_context_mock.go new file mode 100644 index 0000000000..3190f680ac --- /dev/null +++ b/terraform/eval_context_mock.go @@ -0,0 +1,166 @@ +package terraform + +import ( + "sync" + + "github.com/hashicorp/terraform/config" +) + +// MockEvalContext is a mock version of EvalContext that can be used +// for tests. +type MockEvalContext struct { + HookCalled bool + HookHook Hook + HookError error + + InputCalled bool + InputInput UIInput + + InitProviderCalled bool + InitProviderName string + InitProviderProvider ResourceProvider + InitProviderError error + + ProviderCalled bool + ProviderName string + ProviderProvider ResourceProvider + + ProviderInputCalled bool + ProviderInputName string + ProviderInputConfig map[string]interface{} + + SetProviderInputCalled bool + SetProviderInputName string + SetProviderInputConfig map[string]interface{} + + ConfigureProviderCalled bool + ConfigureProviderName string + ConfigureProviderConfig *ResourceConfig + ConfigureProviderError error + + ParentProviderConfigCalled bool + ParentProviderConfigName string + ParentProviderConfigConfig *ResourceConfig + + InitProvisionerCalled bool + InitProvisionerName string + InitProvisionerProvisioner ResourceProvisioner + InitProvisionerError error + + ProvisionerCalled bool + ProvisionerName string + ProvisionerProvisioner ResourceProvisioner + + InterpolateCalled bool + InterpolateConfig *config.RawConfig + InterpolateResource *Resource + InterpolateConfigResult *ResourceConfig + InterpolateError error + + PathCalled bool + PathPath []string + + SetVariablesCalled bool + SetVariablesVariables map[string]string + + DiffCalled bool + DiffDiff *Diff + DiffLock *sync.RWMutex + + StateCalled bool + StateState *State + StateLock *sync.RWMutex +} + +func (c *MockEvalContext) Hook(fn func(Hook) (HookAction, error)) error { + c.HookCalled = true + if c.HookHook != nil { + if _, err := fn(c.HookHook); err != nil { + return err + } + } + + return c.HookError +} + +func (c *MockEvalContext) Input() UIInput { + c.InputCalled = true + return c.InputInput +} + +func (c *MockEvalContext) InitProvider(n string) (ResourceProvider, error) { + c.InitProviderCalled = true + c.InitProviderName = n + return c.InitProviderProvider, c.InitProviderError +} + +func (c *MockEvalContext) Provider(n string) ResourceProvider { + c.ProviderCalled = true + c.ProviderName = n + return c.ProviderProvider +} + +func (c *MockEvalContext) ConfigureProvider(n string, cfg *ResourceConfig) error { + c.ConfigureProviderCalled = true + c.ConfigureProviderName = n + c.ConfigureProviderConfig = cfg + return c.ConfigureProviderError +} + +func (c *MockEvalContext) ParentProviderConfig(n string) *ResourceConfig { + c.ParentProviderConfigCalled = true + c.ParentProviderConfigName = n + return c.ParentProviderConfigConfig +} + +func (c *MockEvalContext) ProviderInput(n string) map[string]interface{} { + c.ProviderInputCalled = true + c.ProviderInputName = n + return c.ProviderInputConfig +} + +func (c *MockEvalContext) SetProviderInput(n string, cfg map[string]interface{}) { + c.SetProviderInputCalled = true + c.SetProviderInputName = n + c.SetProviderInputConfig = cfg +} + +func (c *MockEvalContext) InitProvisioner(n string) (ResourceProvisioner, error) { + c.InitProvisionerCalled = true + c.InitProvisionerName = n + return c.InitProvisionerProvisioner, c.InitProvisionerError +} + +func (c *MockEvalContext) Provisioner(n string) ResourceProvisioner { + c.ProvisionerCalled = true + c.ProvisionerName = n + return c.ProvisionerProvisioner +} + +func (c *MockEvalContext) Interpolate( + config *config.RawConfig, resource *Resource) (*ResourceConfig, error) { + c.InterpolateCalled = true + c.InterpolateConfig = config + c.InterpolateResource = resource + return c.InterpolateConfigResult, c.InterpolateError +} + +func (c *MockEvalContext) Path() []string { + c.PathCalled = true + return c.PathPath +} + +func (c *MockEvalContext) SetVariables(vs map[string]string) { + c.SetVariablesCalled = true + c.SetVariablesVariables = vs +} + +func (c *MockEvalContext) Diff() (*Diff, *sync.RWMutex) { + c.DiffCalled = true + return c.DiffDiff, c.DiffLock +} + +func (c *MockEvalContext) State() (*State, *sync.RWMutex) { + c.StateCalled = true + return c.StateState, c.StateLock +} diff --git a/terraform/eval_state.go b/terraform/eval_state.go index bf8fec0801..d584c0363b 100644 --- a/terraform/eval_state.go +++ b/terraform/eval_state.go @@ -58,6 +58,28 @@ func (n *EvalReadState) Eval(ctx EvalContext) (interface{}, error) { return result, nil } +// EvalUpdateStateHook is an EvalNode implementation that calls the +// PostStateUpdate hook with the current state. +type EvalUpdateStateHook struct{} + +func (n *EvalUpdateStateHook) Eval(ctx EvalContext) (interface{}, error) { + state, lock := ctx.State() + + // Get a read lock so it doesn't change while we're calling this + lock.RLock() + defer lock.RUnlock() + + // Call the hook + err := ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostStateUpdate(state) + }) + if err != nil { + return nil, err + } + + return nil, nil +} + // EvalWriteState is an EvalNode implementation that reads the // InstanceState for a specific resource out of the state. type EvalWriteState struct { @@ -111,7 +133,6 @@ func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) { // Set the primary state rs.Primary = *n.State } - println(fmt.Sprintf("%#v", rs)) return nil, nil } diff --git a/terraform/eval_state_test.go b/terraform/eval_state_test.go new file mode 100644 index 0000000000..0e9e988082 --- /dev/null +++ b/terraform/eval_state_test.go @@ -0,0 +1,27 @@ +package terraform + +import ( + "sync" + "testing" +) + +func TestEvalUpdateStateHook(t *testing.T) { + mockHook := new(MockHook) + + ctx := new(MockEvalContext) + ctx.HookHook = mockHook + ctx.StateState = &State{Serial: 42} + ctx.StateLock = new(sync.RWMutex) + + node := &EvalUpdateStateHook{} + if _, err := node.Eval(ctx); err != nil { + t.Fatalf("err: %s", err) + } + + if !mockHook.PostStateUpdateCalled { + t.Fatal("should call PostStateUpdate") + } + if mockHook.PostStateUpdateState.Serial != 42 { + t.Fatalf("bad: %#v", mockHook.PostStateUpdateState) + } +} diff --git a/terraform/hook.go b/terraform/hook.go index e4ad420165..79e69c6353 100644 --- a/terraform/hook.go +++ b/terraform/hook.go @@ -49,6 +49,9 @@ type Hook interface { // resource state is refreshed, respectively. PreRefresh(*InstanceInfo, *InstanceState) (HookAction, error) PostRefresh(*InstanceInfo, *InstanceState) (HookAction, error) + + // PostStateUpdate is called after the state is updated. + PostStateUpdate(*State) (HookAction, error) } // NilHook is a Hook implementation that does nothing. It exists only to @@ -100,6 +103,10 @@ func (*NilHook) PostRefresh(*InstanceInfo, *InstanceState) (HookAction, error) { return HookActionContinue, nil } +func (*NilHook) PostStateUpdate(*State) (HookAction, error) { + return HookActionContinue, nil +} + // handleHook turns hook actions into panics. This lets you use the // panic/recover mechanism in Go as a flow control mechanism for hook // actions. diff --git a/terraform/hook_mock.go b/terraform/hook_mock.go index b2b6a6e6f5..d30ab8f068 100644 --- a/terraform/hook_mock.go +++ b/terraform/hook_mock.go @@ -69,6 +69,11 @@ type MockHook struct { PreRefreshState *InstanceState PreRefreshReturn HookAction PreRefreshError error + + PostStateUpdateCalled bool + PostStateUpdateState *State + PostStateUpdateReturn HookAction + PostStateUpdateError error } func (h *MockHook) PreApply(n *InstanceInfo, s *InstanceState, d *InstanceDiff) (HookAction, error) { @@ -152,3 +157,9 @@ func (h *MockHook) PostRefresh(n *InstanceInfo, s *InstanceState) (HookAction, e h.PostRefreshState = s return h.PostRefreshReturn, h.PostRefreshError } + +func (h *MockHook) PostStateUpdate(s *State) (HookAction, error) { + h.PostStateUpdateCalled = true + h.PostStateUpdateState = s + return h.PostStateUpdateReturn, h.PostStateUpdateError +} diff --git a/terraform/hook_stop.go b/terraform/hook_stop.go index 0dc1ad7b40..34713221ed 100644 --- a/terraform/hook_stop.go +++ b/terraform/hook_stop.go @@ -53,6 +53,10 @@ func (h *stopHook) PostRefresh(*InstanceInfo, *InstanceState) (HookAction, error return h.hook() } +func (h *stopHook) PostStateUpdate(*State) (HookAction, error) { + return h.hook() +} + func (h *stopHook) hook() (HookAction, error) { if h.Stopped() { return HookActionHalt, nil