From 78c32ac196a345a40937ccde624c948fa472b24f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 27 Jul 2014 09:00:34 -0700 Subject: [PATCH] terraform: hooks around provisioners --- terraform/context.go | 28 ++++++++++++++++++++--- terraform/graph.go | 1 + terraform/hook.go | 22 ++++++++++++++++++ terraform/hook_mock.go | 52 ++++++++++++++++++++++++++++++++++++++++++ terraform/hook_stop.go | 16 +++++++++++++ terraform/resource.go | 1 + 6 files changed, 117 insertions(+), 3 deletions(-) diff --git a/terraform/context.go b/terraform/context.go index 05f1231f5..3b837c6d8 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -574,6 +574,11 @@ func (c *Context) applyWalkFn() depgraph.WalkFunc { delete(c.state.Tainted, r.Id) } else { c.state.Resources[r.Id] = rs + + // We always mark the resource as tainted here in case a + // hook below during provisioning does HookActionStop. This + // way, we keep the resource tainted. + c.state.Tainted[r.Id] = struct{}{} } c.sl.Unlock() @@ -585,19 +590,28 @@ func (c *Context) applyWalkFn() depgraph.WalkFunc { // was an error during the provider apply. tainted := false if applyerr == nil && r.State.ID == "" && len(r.Provisioners) > 0 { + for _, h := range c.hooks { + handleHook(h.PreProvisionResource(r.Id, r.State)) + } + if err := c.applyProvisioners(r, rs); err != nil { errs = append(errs, err) tainted = true } + + for _, h := range c.hooks { + handleHook(h.PostProvisionResource(r.Id, r.State)) + } } + c.sl.Lock() if tainted { log.Printf("[DEBUG] %s: Marking as tainted", r.Id) - - c.sl.Lock() c.state.Tainted[r.Id] = struct{}{} - c.sl.Unlock() + } else { + delete(c.state.Tainted, r.Id) } + c.sl.Unlock() // Update the state for the resource itself r.State = rs @@ -671,9 +685,17 @@ func (c *Context) applyProvisioners(r *Resource, rs *ResourceState) error { rs.ConnInfo = overlay // Invoke the Provisioner + for _, h := range c.hooks { + handleHook(h.PreProvision(r.Id, prov.Type)) + } + if err := prov.Provisioner.Apply(rs, prov.Config); err != nil { return err } + + for _, h := range c.hooks { + handleHook(h.PostProvision(r.Id, prov.Type)) + } } return nil diff --git a/terraform/graph.go b/terraform/graph.go index a3f2ca96a..ea23648fb 100644 --- a/terraform/graph.go +++ b/terraform/graph.go @@ -864,6 +864,7 @@ func graphMapResourceProvisioners(g *depgraph.Graph, // Save the provisioner rn.Resource.Provisioners = append(rn.Resource.Provisioners, &ResourceProvisionerConfig{ + Type: p.Type, Provisioner: provisioner, Config: NewResourceConfig(p.RawConfig), RawConfig: p.RawConfig, diff --git a/terraform/hook.go b/terraform/hook.go index 1e7475951..83b541422 100644 --- a/terraform/hook.go +++ b/terraform/hook.go @@ -32,6 +32,12 @@ type Hook interface { PreDiff(string, *ResourceState) (HookAction, error) PostDiff(string, *ResourceDiff) (HookAction, error) + // Provisioning hooks + PreProvisionResource(string, *ResourceState) (HookAction, error) + PostProvisionResource(string, *ResourceState) (HookAction, error) + PreProvision(string, string) (HookAction, error) + PostProvision(string, string) (HookAction, error) + // PreRefresh and PostRefresh are called before and after a single // resource state is refreshed, respectively. PreRefresh(string, *ResourceState) (HookAction, error) @@ -59,6 +65,22 @@ func (*NilHook) PostDiff(string, *ResourceDiff) (HookAction, error) { return HookActionContinue, nil } +func (*NilHook) PreProvisionResource(string, *ResourceState) (HookAction, error) { + return HookActionContinue, nil +} + +func (*NilHook) PostProvisionResource(string, *ResourceState) (HookAction, error) { + return HookActionContinue, nil +} + +func (*NilHook) PreProvision(string, string) (HookAction, error) { + return HookActionContinue, nil +} + +func (*NilHook) PostProvision(string, string) (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 e4ff675e4..344d35b6b 100644 --- a/terraform/hook_mock.go +++ b/terraform/hook_mock.go @@ -29,6 +29,30 @@ type MockHook struct { PostDiffReturn HookAction PostDiffError error + PreProvisionResourceCalled bool + PreProvisionResourceId string + PreProvisionResourceState *ResourceState + PreProvisionResourceReturn HookAction + PreProvisionResourceError error + + PostProvisionResourceCalled bool + PostProvisionResourceId string + PostProvisionResourceState *ResourceState + PostProvisionResourceReturn HookAction + PostProvisionResourceError error + + PreProvisionCalled bool + PreProvisionId string + PreProvisionProvisionerId string + PreProvisionReturn HookAction + PreProvisionError error + + PostProvisionCalled bool + PostProvisionId string + PostProvisionProvisionerId string + PostProvisionReturn HookAction + PostProvisionError error + PostRefreshCalled bool PostRefreshId string PostRefreshState *ResourceState @@ -72,6 +96,34 @@ func (h *MockHook) PostDiff(n string, d *ResourceDiff) (HookAction, error) { return h.PostDiffReturn, h.PostDiffError } +func (h *MockHook) PreProvisionResource(id string, s *ResourceState) (HookAction, error) { + h.PreProvisionResourceCalled = true + h.PreProvisionResourceId = id + h.PreProvisionResourceState = s + return h.PreProvisionResourceReturn, h.PreProvisionResourceError +} + +func (h *MockHook) PostProvisionResource(id string, s *ResourceState) (HookAction, error) { + h.PostProvisionResourceCalled = true + h.PostProvisionResourceId = id + h.PostProvisionResourceState = s + return h.PostProvisionResourceReturn, h.PostProvisionResourceError +} + +func (h *MockHook) PreProvision(id, provId string) (HookAction, error) { + h.PreProvisionCalled = true + h.PreProvisionId = id + h.PreProvisionProvisionerId = provId + return h.PreProvisionReturn, h.PreProvisionError +} + +func (h *MockHook) PostProvision(id, provId string) (HookAction, error) { + h.PostProvisionCalled = true + h.PostProvisionId = id + h.PostProvisionProvisionerId = provId + return h.PostProvisionReturn, h.PostProvisionError +} + func (h *MockHook) PreRefresh(n string, s *ResourceState) (HookAction, error) { h.PreRefreshCalled = true h.PreRefreshId = n diff --git a/terraform/hook_stop.go b/terraform/hook_stop.go index fc36c38f5..5807f0cd3 100644 --- a/terraform/hook_stop.go +++ b/terraform/hook_stop.go @@ -26,6 +26,22 @@ func (h *stopHook) PostDiff(string, *ResourceDiff) (HookAction, error) { return h.hook() } +func (h *stopHook) PreProvisionResource(string, *ResourceState) (HookAction, error) { + return h.hook() +} + +func (h *stopHook) PostProvisionResource(string, *ResourceState) (HookAction, error) { + return h.hook() +} + +func (h *stopHook) PreProvision(string, string) (HookAction, error) { + return h.hook() +} + +func (h *stopHook) PostProvision(string, string) (HookAction, error) { + return h.hook() +} + func (h *stopHook) PreRefresh(string, *ResourceState) (HookAction, error) { return h.hook() } diff --git a/terraform/resource.go b/terraform/resource.go index 8eba49db4..e634d6d91 100644 --- a/terraform/resource.go +++ b/terraform/resource.go @@ -15,6 +15,7 @@ import ( // configuration instead of instantiating a new Provisioner for each // resource. type ResourceProvisionerConfig struct { + Type string Provisioner ResourceProvisioner Config *ResourceConfig RawConfig *config.RawConfig