mirror of https://github.com/hashicorp/terraform
Expand the existing hooks to emit events throughout the planning process, providing enough information for the Terraform Cloud UI to render a live-updating representation of the plan. We also sketch out the equivalent hooks for the apply operation.pull/34738/head
parent
bdeca87f7a
commit
54062a52cb
@ -0,0 +1,43 @@
|
||||
package hooks
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/internal/rpcapi/terraform1"
|
||||
)
|
||||
|
||||
// ComponentInstanceStatus is a UI-focused description of the overall status
|
||||
// for a given component instance undergoing a Terraform plan or apply
|
||||
// operation. The "pending" and "errored" status are used for both operation
|
||||
// types, and the others will be used only for one of plan or apply.
|
||||
type ComponentInstanceStatus rune
|
||||
|
||||
//go:generate go run golang.org/x/tools/cmd/stringer -type=ComponentInstanceStatus component_instance.go
|
||||
|
||||
const (
|
||||
ComponentInstanceStatusInvalid ComponentInstanceStatus = 0
|
||||
ComponentInstancePending ComponentInstanceStatus = '.'
|
||||
ComponentInstancePlanning ComponentInstanceStatus = 'p'
|
||||
ComponentInstancePlanned ComponentInstanceStatus = 'P'
|
||||
ComponentInstanceApplying ComponentInstanceStatus = 'a'
|
||||
ComponentInstanceApplied ComponentInstanceStatus = 'A'
|
||||
ComponentInstanceErrored ComponentInstanceStatus = 'E'
|
||||
)
|
||||
|
||||
// TODO: move this into the rpcapi package somewhere
|
||||
func (s ComponentInstanceStatus) ForProtobuf() terraform1.ComponentInstanceStatus_Status {
|
||||
switch s {
|
||||
case ComponentInstancePending:
|
||||
return terraform1.ComponentInstanceStatus_PENDING
|
||||
case ComponentInstancePlanning:
|
||||
return terraform1.ComponentInstanceStatus_PLANNING
|
||||
case ComponentInstancePlanned:
|
||||
return terraform1.ComponentInstanceStatus_PLANNED
|
||||
case ComponentInstanceApplying:
|
||||
return terraform1.ComponentInstanceStatus_APPLYING
|
||||
case ComponentInstanceApplied:
|
||||
return terraform1.ComponentInstanceStatus_APPLIED
|
||||
case ComponentInstanceErrored:
|
||||
return terraform1.ComponentInstanceStatus_ERRORED
|
||||
default:
|
||||
return terraform1.ComponentInstanceStatus_INVALID
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
// Code generated by "stringer -type=ComponentInstanceStatus component_instance.go"; DO NOT EDIT.
|
||||
|
||||
package hooks
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ComponentInstanceStatusInvalid-0]
|
||||
_ = x[ComponentInstancePending-46]
|
||||
_ = x[ComponentInstancePlanning-112]
|
||||
_ = x[ComponentInstancePlanned-80]
|
||||
_ = x[ComponentInstanceApplying-97]
|
||||
_ = x[ComponentInstanceApplied-65]
|
||||
_ = x[ComponentInstanceErrored-69]
|
||||
}
|
||||
|
||||
const (
|
||||
_ComponentInstanceStatus_name_0 = "ComponentInstanceStatusInvalid"
|
||||
_ComponentInstanceStatus_name_1 = "ComponentInstancePending"
|
||||
_ComponentInstanceStatus_name_2 = "ComponentInstanceApplied"
|
||||
_ComponentInstanceStatus_name_3 = "ComponentInstanceErrored"
|
||||
_ComponentInstanceStatus_name_4 = "ComponentInstancePlanned"
|
||||
_ComponentInstanceStatus_name_5 = "ComponentInstanceApplying"
|
||||
_ComponentInstanceStatus_name_6 = "ComponentInstancePlanning"
|
||||
)
|
||||
|
||||
func (i ComponentInstanceStatus) String() string {
|
||||
switch {
|
||||
case i == 0:
|
||||
return _ComponentInstanceStatus_name_0
|
||||
case i == 46:
|
||||
return _ComponentInstanceStatus_name_1
|
||||
case i == 65:
|
||||
return _ComponentInstanceStatus_name_2
|
||||
case i == 69:
|
||||
return _ComponentInstanceStatus_name_3
|
||||
case i == 80:
|
||||
return _ComponentInstanceStatus_name_4
|
||||
case i == 97:
|
||||
return _ComponentInstanceStatus_name_5
|
||||
case i == 112:
|
||||
return _ComponentInstanceStatus_name_6
|
||||
default:
|
||||
return "ComponentInstanceStatus(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
// Code generated by "stringer -type=ProvisionerStatus resource_instance.go"; DO NOT EDIT.
|
||||
|
||||
package hooks
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ProvisionerStatusInvalid-0]
|
||||
_ = x[ProvisionerProvisioning-112]
|
||||
_ = x[ProvisionerProvisioned-80]
|
||||
_ = x[ProvisionerErrored-69]
|
||||
}
|
||||
|
||||
const (
|
||||
_ProvisionerStatus_name_0 = "ProvisionerStatusInvalid"
|
||||
_ProvisionerStatus_name_1 = "ProvisionerErrored"
|
||||
_ProvisionerStatus_name_2 = "ProvisionerProvisioned"
|
||||
_ProvisionerStatus_name_3 = "ProvisionerProvisioning"
|
||||
)
|
||||
|
||||
func (i ProvisionerStatus) String() string {
|
||||
switch {
|
||||
case i == 0:
|
||||
return _ProvisionerStatus_name_0
|
||||
case i == 69:
|
||||
return _ProvisionerStatus_name_1
|
||||
case i == 80:
|
||||
return _ProvisionerStatus_name_2
|
||||
case i == 112:
|
||||
return _ProvisionerStatus_name_3
|
||||
default:
|
||||
return "ProvisionerStatus(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package hooks
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/rpcapi/terraform1"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
|
||||
)
|
||||
|
||||
// ResourceInstanceStatus is a UI-focused description of the overall status
|
||||
// for a given resource instance undergoing a Terraform plan or apply
|
||||
// operation. The "pending" and "errored" status are used for both operation
|
||||
// types, and the others will be used only for one of plan or apply.
|
||||
type ResourceInstanceStatus rune
|
||||
|
||||
//go:generate go run golang.org/x/tools/cmd/stringer -type=ResourceInstanceStatus resource_instance.go
|
||||
|
||||
const (
|
||||
ResourceInstanceStatusInvalid ResourceInstanceStatus = 0
|
||||
ResourceInstancePending ResourceInstanceStatus = '.'
|
||||
ResourceInstanceRefreshing ResourceInstanceStatus = 'r'
|
||||
ResourceInstanceRefreshed ResourceInstanceStatus = 'R'
|
||||
ResourceInstancePlanning ResourceInstanceStatus = 'p'
|
||||
ResourceInstancePlanned ResourceInstanceStatus = 'P'
|
||||
ResourceInstanceApplying ResourceInstanceStatus = 'a'
|
||||
ResourceInstanceApplied ResourceInstanceStatus = 'A'
|
||||
ResourceInstanceErrored ResourceInstanceStatus = 'E'
|
||||
)
|
||||
|
||||
// TODO: move this into the rpcapi package somewhere
|
||||
func (s ResourceInstanceStatus) ForProtobuf() terraform1.ResourceInstanceStatus_Status {
|
||||
switch s {
|
||||
case ResourceInstancePending:
|
||||
return terraform1.ResourceInstanceStatus_PENDING
|
||||
case ResourceInstanceRefreshing:
|
||||
return terraform1.ResourceInstanceStatus_REFRESHING
|
||||
case ResourceInstanceRefreshed:
|
||||
return terraform1.ResourceInstanceStatus_REFRESHED
|
||||
case ResourceInstancePlanning:
|
||||
return terraform1.ResourceInstanceStatus_PLANNING
|
||||
case ResourceInstancePlanned:
|
||||
return terraform1.ResourceInstanceStatus_PLANNED
|
||||
case ResourceInstanceApplying:
|
||||
return terraform1.ResourceInstanceStatus_APPLYING
|
||||
case ResourceInstanceApplied:
|
||||
return terraform1.ResourceInstanceStatus_APPLIED
|
||||
case ResourceInstanceErrored:
|
||||
return terraform1.ResourceInstanceStatus_ERRORED
|
||||
default:
|
||||
return terraform1.ResourceInstanceStatus_INVALID
|
||||
}
|
||||
}
|
||||
|
||||
// ProvisionerStatus is a UI-focused description of the progress of a given
|
||||
// resource instance's provisioner during a Terraform apply operation. Each
|
||||
// specified provisioner will start in "provisioning" state, and progress to
|
||||
// either "provisioned" or "errored".
|
||||
type ProvisionerStatus rune
|
||||
|
||||
//go:generate go run golang.org/x/tools/cmd/stringer -type=ProvisionerStatus resource_instance.go
|
||||
|
||||
const (
|
||||
ProvisionerStatusInvalid ProvisionerStatus = 0
|
||||
ProvisionerProvisioning ProvisionerStatus = 'p'
|
||||
ProvisionerProvisioned ProvisionerStatus = 'P'
|
||||
ProvisionerErrored ProvisionerStatus = 'E'
|
||||
)
|
||||
|
||||
// TODO: move this into the rpcapi package somewhere
|
||||
func (s ProvisionerStatus) ForProtobuf() terraform1.ProvisionerStatus_Status {
|
||||
switch s {
|
||||
case ProvisionerProvisioning:
|
||||
return terraform1.ProvisionerStatus_PROVISIONING
|
||||
case ProvisionerProvisioned:
|
||||
return terraform1.ProvisionerStatus_PROVISIONING
|
||||
case ProvisionerErrored:
|
||||
return terraform1.ProvisionerStatus_ERRORED
|
||||
default:
|
||||
return terraform1.ProvisionerStatus_INVALID
|
||||
}
|
||||
}
|
||||
|
||||
// ResourceInstanceStatusHookData is the argument type for hook callbacks which
|
||||
// signal a resource instance's status updates.
|
||||
type ResourceInstanceStatusHookData struct {
|
||||
Addr stackaddrs.AbsResourceInstance
|
||||
Status ResourceInstanceStatus
|
||||
}
|
||||
|
||||
// ResourceInstanceProvisionerHookData is the argument type for hook callbacks
|
||||
// which signal a resource instance's provisioner progress, including both
|
||||
// status updates and optional provisioner output data.
|
||||
type ResourceInstanceProvisionerHookData struct {
|
||||
Addr stackaddrs.AbsResourceInstance
|
||||
Name string
|
||||
Status ProvisionerStatus
|
||||
Output *string
|
||||
}
|
||||
|
||||
// ResourceInstanceChangeSrc is the argument type for hook callbacks which
|
||||
// signal a detected or planned change for a resource instance resulting from a
|
||||
// plan operation.
|
||||
type ResourceInstanceChange struct {
|
||||
Addr stackaddrs.AbsResourceInstance
|
||||
Change *plans.ResourceInstanceChangeSrc
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
// Code generated by "stringer -type=ResourceInstanceStatus resource_instance.go"; DO NOT EDIT.
|
||||
|
||||
package hooks
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ResourceInstanceStatusInvalid-0]
|
||||
_ = x[ResourceInstancePending-46]
|
||||
_ = x[ResourceInstanceRefreshing-114]
|
||||
_ = x[ResourceInstanceRefreshed-82]
|
||||
_ = x[ResourceInstancePlanning-112]
|
||||
_ = x[ResourceInstancePlanned-80]
|
||||
_ = x[ResourceInstanceApplying-97]
|
||||
_ = x[ResourceInstanceApplied-65]
|
||||
_ = x[ResourceInstanceErrored-69]
|
||||
}
|
||||
|
||||
const (
|
||||
_ResourceInstanceStatus_name_0 = "ResourceInstanceStatusInvalid"
|
||||
_ResourceInstanceStatus_name_1 = "ResourceInstancePending"
|
||||
_ResourceInstanceStatus_name_2 = "ResourceInstanceApplied"
|
||||
_ResourceInstanceStatus_name_3 = "ResourceInstanceErrored"
|
||||
_ResourceInstanceStatus_name_4 = "ResourceInstancePlanned"
|
||||
_ResourceInstanceStatus_name_5 = "ResourceInstanceRefreshed"
|
||||
_ResourceInstanceStatus_name_6 = "ResourceInstanceApplying"
|
||||
_ResourceInstanceStatus_name_7 = "ResourceInstancePlanning"
|
||||
_ResourceInstanceStatus_name_8 = "ResourceInstanceRefreshing"
|
||||
)
|
||||
|
||||
func (i ResourceInstanceStatus) String() string {
|
||||
switch {
|
||||
case i == 0:
|
||||
return _ResourceInstanceStatus_name_0
|
||||
case i == 46:
|
||||
return _ResourceInstanceStatus_name_1
|
||||
case i == 65:
|
||||
return _ResourceInstanceStatus_name_2
|
||||
case i == 69:
|
||||
return _ResourceInstanceStatus_name_3
|
||||
case i == 80:
|
||||
return _ResourceInstanceStatus_name_4
|
||||
case i == 82:
|
||||
return _ResourceInstanceStatus_name_5
|
||||
case i == 97:
|
||||
return _ResourceInstanceStatus_name_6
|
||||
case i == 112:
|
||||
return _ResourceInstanceStatus_name_7
|
||||
case i == 114:
|
||||
return _ResourceInstanceStatus_name_8
|
||||
default:
|
||||
return "ResourceInstanceStatus(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
package stackeval
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackruntime/hooks"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
"github.com/hashicorp/terraform/internal/terraform"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// componentInstanceTerraformHook implements terraform.Hook for plan and apply
|
||||
// operations on a specified component instance. It connects the standard
|
||||
// terraform.Hook callbacks to the given stackruntime.Hooks callbacks.
|
||||
//
|
||||
// We unfortunately must embed a context.Context in this type, as the existing
|
||||
// Terraform core hook interface does not support threading a context through.
|
||||
// The lifetime of this hook instance is strictly smaller than its surrounding
|
||||
// context, but we should migrate away from this for clarity when possible.
|
||||
type componentInstanceTerraformHook struct {
|
||||
terraform.NilHook
|
||||
|
||||
ctx context.Context
|
||||
seq *hookSeq
|
||||
hooks *Hooks
|
||||
addr stackaddrs.AbsComponentInstance
|
||||
}
|
||||
|
||||
func (h *componentInstanceTerraformHook) resourceInstanceAddr(addr addrs.AbsResourceInstance) stackaddrs.AbsResourceInstance {
|
||||
return stackaddrs.AbsResourceInstance{
|
||||
Component: h.addr,
|
||||
Item: addr,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *componentInstanceTerraformHook) PreDiff(addr addrs.AbsResourceInstance, gen states.Generation, priorState, proposedNewState cty.Value) (terraform.HookAction, error) {
|
||||
hookMore(h.ctx, h.seq, h.hooks.ReportResourceInstanceStatus, &hooks.ResourceInstanceStatusHookData{
|
||||
Addr: h.resourceInstanceAddr(addr),
|
||||
Status: hooks.ResourceInstancePlanning,
|
||||
})
|
||||
return terraform.HookActionContinue, nil
|
||||
}
|
||||
|
||||
func (h *componentInstanceTerraformHook) PostDiff(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) {
|
||||
hookMore(h.ctx, h.seq, h.hooks.ReportResourceInstanceStatus, &hooks.ResourceInstanceStatusHookData{
|
||||
Addr: h.resourceInstanceAddr(addr),
|
||||
Status: hooks.ResourceInstancePlanned,
|
||||
})
|
||||
return terraform.HookActionContinue, nil
|
||||
}
|
||||
|
||||
func (h *componentInstanceTerraformHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) {
|
||||
if action != plans.NoOp {
|
||||
hookMore(h.ctx, h.seq, h.hooks.ReportResourceInstanceStatus, &hooks.ResourceInstanceStatusHookData{
|
||||
Addr: h.resourceInstanceAddr(addr),
|
||||
Status: hooks.ResourceInstanceApplying,
|
||||
})
|
||||
}
|
||||
return terraform.HookActionContinue, nil
|
||||
}
|
||||
|
||||
func (h *componentInstanceTerraformHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, err error) (terraform.HookAction, error) {
|
||||
// FIXME: need to emit nothing if this was a no-op, which means tracking
|
||||
// the `action` argument to `PreApply`. See `jsonHook` for more on this.
|
||||
status := hooks.ResourceInstanceApplied
|
||||
if err != nil {
|
||||
status = hooks.ResourceInstanceErrored
|
||||
}
|
||||
|
||||
hookMore(h.ctx, h.seq, h.hooks.ReportResourceInstanceStatus, &hooks.ResourceInstanceStatusHookData{
|
||||
Addr: h.resourceInstanceAddr(addr),
|
||||
Status: status,
|
||||
})
|
||||
return terraform.HookActionContinue, nil
|
||||
}
|
||||
|
||||
func (h *componentInstanceTerraformHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (terraform.HookAction, error) {
|
||||
hookMore(h.ctx, h.seq, h.hooks.ReportResourceInstanceProvisionerStatus, &hooks.ResourceInstanceProvisionerHookData{
|
||||
Addr: h.resourceInstanceAddr(addr),
|
||||
Name: typeName,
|
||||
Status: hooks.ProvisionerProvisioning,
|
||||
})
|
||||
return terraform.HookActionContinue, nil
|
||||
}
|
||||
|
||||
func (h *componentInstanceTerraformHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, msg string) {
|
||||
// TODO: determine whether we should continue line splitting as we do with jsonHook
|
||||
output := msg
|
||||
hookMore(h.ctx, h.seq, h.hooks.ReportResourceInstanceProvisionerStatus, &hooks.ResourceInstanceProvisionerHookData{
|
||||
Addr: h.resourceInstanceAddr(addr),
|
||||
Name: typeName,
|
||||
Status: hooks.ProvisionerProvisioning,
|
||||
Output: &output,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *componentInstanceTerraformHook) PostProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string, err error) (terraform.HookAction, error) {
|
||||
status := hooks.ProvisionerProvisioned
|
||||
if err != nil {
|
||||
status = hooks.ProvisionerErrored
|
||||
}
|
||||
hookMore(h.ctx, h.seq, h.hooks.ReportResourceInstanceProvisionerStatus, &hooks.ResourceInstanceProvisionerHookData{
|
||||
Addr: h.resourceInstanceAddr(addr),
|
||||
Name: typeName,
|
||||
Status: status,
|
||||
})
|
||||
return terraform.HookActionContinue, nil
|
||||
}
|
||||
Loading…
Reference in new issue