| package json |
| |
| import ( |
| "fmt" |
| "time" |
| |
| "github.com/hashicorp/terraform/internal/addrs" |
| "github.com/hashicorp/terraform/internal/plans" |
| ) |
| |
| type Hook interface { |
| HookType() MessageType |
| String() string |
| } |
| |
| // ApplyStart: triggered by PreApply hook |
| type applyStart struct { |
| Resource ResourceAddr `json:"resource"` |
| Action ChangeAction `json:"action"` |
| IDKey string `json:"id_key,omitempty"` |
| IDValue string `json:"id_value,omitempty"` |
| actionVerb string |
| } |
| |
| var _ Hook = (*applyStart)(nil) |
| |
| func (h *applyStart) HookType() MessageType { |
| return MessageApplyStart |
| } |
| |
| func (h *applyStart) String() string { |
| var id string |
| if h.IDKey != "" && h.IDValue != "" { |
| id = fmt.Sprintf(" [%s=%s]", h.IDKey, h.IDValue) |
| } |
| return fmt.Sprintf("%s: %s...%s", h.Resource.Addr, h.actionVerb, id) |
| } |
| |
| func NewApplyStart(addr addrs.AbsResourceInstance, action plans.Action, idKey string, idValue string) Hook { |
| hook := &applyStart{ |
| Resource: newResourceAddr(addr), |
| Action: changeAction(action), |
| IDKey: idKey, |
| IDValue: idValue, |
| actionVerb: startActionVerb(action), |
| } |
| |
| return hook |
| } |
| |
| // ApplyProgress: currently triggered by a timer started on PreApply. In |
| // future, this might also be triggered by provider progress reporting. |
| type applyProgress struct { |
| Resource ResourceAddr `json:"resource"` |
| Action ChangeAction `json:"action"` |
| Elapsed float64 `json:"elapsed_seconds"` |
| actionVerb string |
| elapsed time.Duration |
| } |
| |
| var _ Hook = (*applyProgress)(nil) |
| |
| func (h *applyProgress) HookType() MessageType { |
| return MessageApplyProgress |
| } |
| |
| func (h *applyProgress) String() string { |
| return fmt.Sprintf("%s: Still %s... [%s elapsed]", h.Resource.Addr, h.actionVerb, h.elapsed) |
| } |
| |
| func NewApplyProgress(addr addrs.AbsResourceInstance, action plans.Action, elapsed time.Duration) Hook { |
| return &applyProgress{ |
| Resource: newResourceAddr(addr), |
| Action: changeAction(action), |
| Elapsed: elapsed.Seconds(), |
| actionVerb: progressActionVerb(action), |
| elapsed: elapsed, |
| } |
| } |
| |
| // ApplyComplete: triggered by PostApply hook |
| type applyComplete struct { |
| Resource ResourceAddr `json:"resource"` |
| Action ChangeAction `json:"action"` |
| IDKey string `json:"id_key,omitempty"` |
| IDValue string `json:"id_value,omitempty"` |
| Elapsed float64 `json:"elapsed_seconds"` |
| actionNoun string |
| elapsed time.Duration |
| } |
| |
| var _ Hook = (*applyComplete)(nil) |
| |
| func (h *applyComplete) HookType() MessageType { |
| return MessageApplyComplete |
| } |
| |
| func (h *applyComplete) String() string { |
| var id string |
| if h.IDKey != "" && h.IDValue != "" { |
| id = fmt.Sprintf(" [%s=%s]", h.IDKey, h.IDValue) |
| } |
| return fmt.Sprintf("%s: %s complete after %s%s", h.Resource.Addr, h.actionNoun, h.elapsed, id) |
| } |
| |
| func NewApplyComplete(addr addrs.AbsResourceInstance, action plans.Action, idKey, idValue string, elapsed time.Duration) Hook { |
| return &applyComplete{ |
| Resource: newResourceAddr(addr), |
| Action: changeAction(action), |
| IDKey: idKey, |
| IDValue: idValue, |
| Elapsed: elapsed.Seconds(), |
| actionNoun: actionNoun(action), |
| elapsed: elapsed, |
| } |
| } |
| |
| // ApplyErrored: triggered by PostApply hook on failure. This will be followed |
| // by diagnostics when the apply finishes. |
| type applyErrored struct { |
| Resource ResourceAddr `json:"resource"` |
| Action ChangeAction `json:"action"` |
| Elapsed float64 `json:"elapsed_seconds"` |
| actionNoun string |
| elapsed time.Duration |
| } |
| |
| var _ Hook = (*applyErrored)(nil) |
| |
| func (h *applyErrored) HookType() MessageType { |
| return MessageApplyErrored |
| } |
| |
| func (h *applyErrored) String() string { |
| return fmt.Sprintf("%s: %s errored after %s", h.Resource.Addr, h.actionNoun, h.elapsed) |
| } |
| |
| func NewApplyErrored(addr addrs.AbsResourceInstance, action plans.Action, elapsed time.Duration) Hook { |
| return &applyErrored{ |
| Resource: newResourceAddr(addr), |
| Action: changeAction(action), |
| Elapsed: elapsed.Seconds(), |
| actionNoun: actionNoun(action), |
| elapsed: elapsed, |
| } |
| } |
| |
| // ProvisionStart: triggered by PreProvisionInstanceStep hook |
| type provisionStart struct { |
| Resource ResourceAddr `json:"resource"` |
| Provisioner string `json:"provisioner"` |
| } |
| |
| var _ Hook = (*provisionStart)(nil) |
| |
| func (h *provisionStart) HookType() MessageType { |
| return MessageProvisionStart |
| } |
| |
| func (h *provisionStart) String() string { |
| return fmt.Sprintf("%s: Provisioning with '%s'...", h.Resource.Addr, h.Provisioner) |
| } |
| |
| func NewProvisionStart(addr addrs.AbsResourceInstance, provisioner string) Hook { |
| return &provisionStart{ |
| Resource: newResourceAddr(addr), |
| Provisioner: provisioner, |
| } |
| } |
| |
| // ProvisionProgress: triggered by ProvisionOutput hook |
| type provisionProgress struct { |
| Resource ResourceAddr `json:"resource"` |
| Provisioner string `json:"provisioner"` |
| Output string `json:"output"` |
| } |
| |
| var _ Hook = (*provisionProgress)(nil) |
| |
| func (h *provisionProgress) HookType() MessageType { |
| return MessageProvisionProgress |
| } |
| |
| func (h *provisionProgress) String() string { |
| return fmt.Sprintf("%s: (%s): %s", h.Resource.Addr, h.Provisioner, h.Output) |
| } |
| |
| func NewProvisionProgress(addr addrs.AbsResourceInstance, provisioner string, output string) Hook { |
| return &provisionProgress{ |
| Resource: newResourceAddr(addr), |
| Provisioner: provisioner, |
| Output: output, |
| } |
| } |
| |
| // ProvisionComplete: triggered by PostProvisionInstanceStep hook |
| type provisionComplete struct { |
| Resource ResourceAddr `json:"resource"` |
| Provisioner string `json:"provisioner"` |
| } |
| |
| var _ Hook = (*provisionComplete)(nil) |
| |
| func (h *provisionComplete) HookType() MessageType { |
| return MessageProvisionComplete |
| } |
| |
| func (h *provisionComplete) String() string { |
| return fmt.Sprintf("%s: (%s) Provisioning complete", h.Resource.Addr, h.Provisioner) |
| } |
| |
| func NewProvisionComplete(addr addrs.AbsResourceInstance, provisioner string) Hook { |
| return &provisionComplete{ |
| Resource: newResourceAddr(addr), |
| Provisioner: provisioner, |
| } |
| } |
| |
| // ProvisionErrored: triggered by PostProvisionInstanceStep hook on failure. |
| // This will be followed by diagnostics when the apply finishes. |
| type provisionErrored struct { |
| Resource ResourceAddr `json:"resource"` |
| Provisioner string `json:"provisioner"` |
| } |
| |
| var _ Hook = (*provisionErrored)(nil) |
| |
| func (h *provisionErrored) HookType() MessageType { |
| return MessageProvisionErrored |
| } |
| |
| func (h *provisionErrored) String() string { |
| return fmt.Sprintf("%s: (%s) Provisioning errored", h.Resource.Addr, h.Provisioner) |
| } |
| |
| func NewProvisionErrored(addr addrs.AbsResourceInstance, provisioner string) Hook { |
| return &provisionErrored{ |
| Resource: newResourceAddr(addr), |
| Provisioner: provisioner, |
| } |
| } |
| |
| // RefreshStart: triggered by PreRefresh hook |
| type refreshStart struct { |
| Resource ResourceAddr `json:"resource"` |
| IDKey string `json:"id_key,omitempty"` |
| IDValue string `json:"id_value,omitempty"` |
| } |
| |
| var _ Hook = (*refreshStart)(nil) |
| |
| func (h *refreshStart) HookType() MessageType { |
| return MessageRefreshStart |
| } |
| |
| func (h *refreshStart) String() string { |
| var id string |
| if h.IDKey != "" && h.IDValue != "" { |
| id = fmt.Sprintf(" [%s=%s]", h.IDKey, h.IDValue) |
| } |
| return fmt.Sprintf("%s: Refreshing state...%s", h.Resource.Addr, id) |
| } |
| |
| func NewRefreshStart(addr addrs.AbsResourceInstance, idKey, idValue string) Hook { |
| return &refreshStart{ |
| Resource: newResourceAddr(addr), |
| IDKey: idKey, |
| IDValue: idValue, |
| } |
| } |
| |
| // RefreshComplete: triggered by PostRefresh hook |
| type refreshComplete struct { |
| Resource ResourceAddr `json:"resource"` |
| IDKey string `json:"id_key,omitempty"` |
| IDValue string `json:"id_value,omitempty"` |
| } |
| |
| var _ Hook = (*refreshComplete)(nil) |
| |
| func (h *refreshComplete) HookType() MessageType { |
| return MessageRefreshComplete |
| } |
| |
| func (h *refreshComplete) String() string { |
| var id string |
| if h.IDKey != "" && h.IDValue != "" { |
| id = fmt.Sprintf(" [%s=%s]", h.IDKey, h.IDValue) |
| } |
| return fmt.Sprintf("%s: Refresh complete%s", h.Resource.Addr, id) |
| } |
| |
| func NewRefreshComplete(addr addrs.AbsResourceInstance, idKey, idValue string) Hook { |
| return &refreshComplete{ |
| Resource: newResourceAddr(addr), |
| IDKey: idKey, |
| IDValue: idValue, |
| } |
| } |
| |
| // Convert the subset of plans.Action values we expect to receive into a |
| // present-tense verb for the applyStart hook message. |
| func startActionVerb(action plans.Action) string { |
| switch action { |
| case plans.Create: |
| return "Creating" |
| case plans.Update: |
| return "Modifying" |
| case plans.Delete: |
| return "Destroying" |
| case plans.Read: |
| return "Refreshing" |
| case plans.CreateThenDelete, plans.DeleteThenCreate: |
| // This is not currently possible to reach, as we receive separate |
| // passes for create and delete |
| return "Replacing" |
| case plans.NoOp: |
| // This should never be possible: a no-op planned change should not |
| // be applied. We'll fall back to "Applying". |
| fallthrough |
| default: |
| return "Applying" |
| } |
| } |
| |
| // Convert the subset of plans.Action values we expect to receive into a |
| // present-tense verb for the applyProgress hook message. This will be |
| // prefixed with "Still ", so it is lower-case. |
| func progressActionVerb(action plans.Action) string { |
| switch action { |
| case plans.Create: |
| return "creating" |
| case plans.Update: |
| return "modifying" |
| case plans.Delete: |
| return "destroying" |
| case plans.Read: |
| return "refreshing" |
| case plans.CreateThenDelete, plans.DeleteThenCreate: |
| // This is not currently possible to reach, as we receive separate |
| // passes for create and delete |
| return "replacing" |
| case plans.NoOp: |
| // This should never be possible: a no-op planned change should not |
| // be applied. We'll fall back to "applying". |
| fallthrough |
| default: |
| return "applying" |
| } |
| } |
| |
| // Convert the subset of plans.Action values we expect to receive into a |
| // noun for the applyComplete and applyErrored hook messages. This will be |
| // combined into a phrase like "Creation complete after 1m4s". |
| func actionNoun(action plans.Action) string { |
| switch action { |
| case plans.Create: |
| return "Creation" |
| case plans.Update: |
| return "Modifications" |
| case plans.Delete: |
| return "Destruction" |
| case plans.Read: |
| return "Refresh" |
| case plans.CreateThenDelete, plans.DeleteThenCreate: |
| // This is not currently possible to reach, as we receive separate |
| // passes for create and delete |
| return "Replacement" |
| case plans.NoOp: |
| // This should never be possible: a no-op planned change should not |
| // be applied. We'll fall back to "Apply". |
| fallthrough |
| default: |
| return "Apply" |
| } |
| } |