terraform

package
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2026 License: MPL-2.0 Imports: 13 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Action added in v0.9.0

type Action int

Action enumerates the kinds of changes a plan can produce for a single resource. ActionUnknown is reserved for inputs that don't correspond to any operator-visible change (terraform "noop"/"read" or unrecognised action strings) and is dropped from rendered resource lists.

const (
	ActionUnknown Action = iota
	ActionCreate
	ActionUpdate
	ActionDelete
	ActionReplace
)

type ComponentFailure added in v0.9.0

type ComponentFailure struct {
	ID  string
	Err error
}

ComponentFailure is a per-component error captured during continue-on-error destroy. ID is the terraform component's blueprint identifier; Err is the terraform destroy error that the operator can use to diagnose.

type DestroyOutcome added in v0.9.0

type DestroyOutcome struct {
	Destroyed []string
	Skipped   []string
	Failed    []ComponentFailure
}

DestroyOutcome tallies what a single terraform destroy pass produced. Destroyed lists component IDs whose terraform destroy ran to completion; Skipped lists component IDs whose state was empty (no-op); Failed lists per-component failures collected when continue-on-error is true. Aggregate cross-layer concerns (backend-tier deferral, kustomize counts) live on the provisioner-layer result type, not here.

type MockStack

type MockStack struct {
	UpFunc                          func(blueprint *blueprintv1alpha1.Blueprint, onApply ...func(id string) (bool, error)) (bool, error)
	MigrateStateFunc                func(blueprint *blueprintv1alpha1.Blueprint) ([]string, error)
	MigrateComponentStateFunc       func(blueprint *blueprintv1alpha1.Blueprint, componentID string) error
	HasRemoteStateFunc              func(blueprint *blueprintv1alpha1.Blueprint, componentID string) (bool, error)
	HasLocalStateWithResourcesFunc  func(componentID string) (bool, error)
	InitComponentFunc               func(blueprint *blueprintv1alpha1.Blueprint, componentID string) error
	RemoveLocalStateFunc            func(componentID string) error
	PostApplyFunc                   func(fns ...func(id string) error)
	DestroyAllFunc                  func(blueprint *blueprintv1alpha1.Blueprint, continueOnError bool, excludeIDs ...string) (DestroyOutcome, error)
	PlanFunc                        func(blueprint *blueprintv1alpha1.Blueprint, componentID string) error
	PlanAllFunc                     func(blueprint *blueprintv1alpha1.Blueprint) error
	PlanJSONFunc                    func(blueprint *blueprintv1alpha1.Blueprint, componentID string) error
	PlanAllJSONFunc                 func(blueprint *blueprintv1alpha1.Blueprint) error
	ApplyFunc                       func(blueprint *blueprintv1alpha1.Blueprint, componentID string) error
	DestroyFunc                     func(blueprint *blueprintv1alpha1.Blueprint, componentID string) (bool, error)
	PlanSummaryFunc                 func(blueprint *blueprintv1alpha1.Blueprint) []TerraformComponentPlan
	PlanComponentSummaryFunc        func(blueprint *blueprintv1alpha1.Blueprint, componentID string) TerraformComponentPlan
	PlanDestroySummaryFunc          func(blueprint *blueprintv1alpha1.Blueprint) []TerraformComponentPlan
	PlanDestroyComponentSummaryFunc func(blueprint *blueprintv1alpha1.Blueprint, componentID string) TerraformComponentPlan
}

MockStack is a mock implementation of the Stack interface for testing.

func NewMockStack

func NewMockStack() *MockStack

NewMockStack creates a new mock stack.

func (*MockStack) Apply added in v0.9.0

func (m *MockStack) Apply(blueprint *blueprintv1alpha1.Blueprint, componentID string) error

Apply is a mock implementation of the Apply method.

func (*MockStack) Destroy added in v0.9.0

func (m *MockStack) Destroy(blueprint *blueprintv1alpha1.Blueprint, componentID string) (bool, error)

Destroy is a mock implementation of the Destroy method.

func (*MockStack) DestroyAll added in v0.9.0

func (m *MockStack) DestroyAll(blueprint *blueprintv1alpha1.Blueprint, continueOnError bool, excludeIDs ...string) (DestroyOutcome, error)

DestroyAll is a mock implementation of the DestroyAll method.

func (*MockStack) HasLocalStateWithResources added in v0.9.0

func (m *MockStack) HasLocalStateWithResources(componentID string) (bool, error)

HasLocalStateWithResources is a mock implementation of the HasLocalStateWithResources method.

func (*MockStack) HasRemoteState added in v0.9.0

func (m *MockStack) HasRemoteState(blueprint *blueprintv1alpha1.Blueprint, componentID string) (bool, error)

HasRemoteState is a mock implementation of the HasRemoteState method.

func (*MockStack) InitComponent added in v0.9.0

func (m *MockStack) InitComponent(blueprint *blueprintv1alpha1.Blueprint, componentID string) error

InitComponent is a mock implementation of the InitComponent method.

func (*MockStack) MigrateComponentState added in v0.9.0

func (m *MockStack) MigrateComponentState(blueprint *blueprintv1alpha1.Blueprint, componentID string) error

MigrateComponentState is a mock implementation of the MigrateComponentState method.

func (*MockStack) MigrateState added in v0.9.0

func (m *MockStack) MigrateState(blueprint *blueprintv1alpha1.Blueprint) ([]string, error)

MigrateState is a mock implementation of the MigrateState method.

func (*MockStack) Plan added in v0.9.0

func (m *MockStack) Plan(blueprint *blueprintv1alpha1.Blueprint, componentID string) error

Plan is a mock implementation of the Plan method.

func (*MockStack) PlanAll added in v0.9.0

func (m *MockStack) PlanAll(blueprint *blueprintv1alpha1.Blueprint) error

PlanAll is a mock implementation of the PlanAll method.

func (*MockStack) PlanAllJSON added in v0.9.0

func (m *MockStack) PlanAllJSON(blueprint *blueprintv1alpha1.Blueprint) error

PlanAllJSON is a mock implementation of the PlanAllJSON method.

func (*MockStack) PlanComponentSummary added in v0.9.0

func (m *MockStack) PlanComponentSummary(blueprint *blueprintv1alpha1.Blueprint, componentID string) TerraformComponentPlan

PlanComponentSummary is a mock implementation of the PlanComponentSummary method.

func (*MockStack) PlanDestroyComponentSummary added in v0.9.0

func (m *MockStack) PlanDestroyComponentSummary(blueprint *blueprintv1alpha1.Blueprint, componentID string) TerraformComponentPlan

PlanDestroyComponentSummary is a mock implementation of the PlanDestroyComponentSummary method.

func (*MockStack) PlanDestroySummary added in v0.9.0

func (m *MockStack) PlanDestroySummary(blueprint *blueprintv1alpha1.Blueprint) []TerraformComponentPlan

PlanDestroySummary is a mock implementation of the PlanDestroySummary method.

func (*MockStack) PlanJSON added in v0.9.0

func (m *MockStack) PlanJSON(blueprint *blueprintv1alpha1.Blueprint, componentID string) error

PlanJSON is a mock implementation of the PlanJSON method.

func (*MockStack) PlanSummary added in v0.9.0

func (m *MockStack) PlanSummary(blueprint *blueprintv1alpha1.Blueprint) []TerraformComponentPlan

PlanSummary is a mock implementation of the PlanSummary method.

func (*MockStack) PostApply added in v0.9.0

func (m *MockStack) PostApply(fns ...func(id string) error)

PostApply is a mock implementation of the PostApply method.

func (*MockStack) RemoveLocalState added in v0.9.0

func (m *MockStack) RemoveLocalState(componentID string) error

RemoveLocalState is a mock implementation of the RemoveLocalState method.

func (*MockStack) Up

func (m *MockStack) Up(blueprint *blueprintv1alpha1.Blueprint, onApply ...func(id string) (bool, error)) (bool, error)

Up is a mock implementation of the Up method.

type ResourceChange added in v0.9.0

type ResourceChange struct {
	Address string
	Action  Action
}

ResourceChange identifies one resource changed by a plan along with its action. Address is the terraform resource address with the leading "module.main." wrapper stripped, since every Windsor component wraps its resources in a single module and the prefix would otherwise be repeated noise on every line.

type Shims

type Shims struct {
	Stat      func(string) (os.FileInfo, error)
	Chdir     func(string) error
	Getwd     func() (string, error)
	Setenv    func(string, string) error
	Unsetenv  func(string) error
	Remove    func(string) error
	MkdirTemp func(string, string) (string, error)
	RemoveAll func(string) error
	WriteFile func(string, []byte, os.FileMode) error
	ReadFile  func(string) ([]byte, error)
}

Shims provides mockable wrappers around system and runtime functions

func NewShims

func NewShims() *Shims

NewShims creates a new Shims instance with default implementations

type Stack

type Stack interface {
	Up(blueprint *blueprintv1alpha1.Blueprint, onApply ...func(id string) (bool, error)) (bool, error)
	MigrateState(blueprint *blueprintv1alpha1.Blueprint) ([]string, error)
	MigrateComponentState(blueprint *blueprintv1alpha1.Blueprint, componentID string) error
	HasRemoteState(blueprint *blueprintv1alpha1.Blueprint, componentID string) (bool, error)
	HasLocalStateWithResources(componentID string) (bool, error)
	InitComponent(blueprint *blueprintv1alpha1.Blueprint, componentID string) error
	RemoveLocalState(componentID string) error
	PostApply(fns ...func(id string) error)
	DestroyAll(blueprint *blueprintv1alpha1.Blueprint, continueOnError bool, excludeIDs ...string) (DestroyOutcome, error)
	Plan(blueprint *blueprintv1alpha1.Blueprint, componentID string) error
	PlanAll(blueprint *blueprintv1alpha1.Blueprint) error
	PlanJSON(blueprint *blueprintv1alpha1.Blueprint, componentID string) error
	PlanAllJSON(blueprint *blueprintv1alpha1.Blueprint) error
	Apply(blueprint *blueprintv1alpha1.Blueprint, componentID string) error
	Destroy(blueprint *blueprintv1alpha1.Blueprint, componentID string) (bool, error)
	PlanSummary(blueprint *blueprintv1alpha1.Blueprint) []TerraformComponentPlan
	PlanComponentSummary(blueprint *blueprintv1alpha1.Blueprint, componentID string) TerraformComponentPlan
	PlanDestroySummary(blueprint *blueprintv1alpha1.Blueprint) []TerraformComponentPlan
	PlanDestroyComponentSummary(blueprint *blueprintv1alpha1.Blueprint, componentID string) TerraformComponentPlan
}

Stack defines the interface for Terraform stack operations. Both the Stack struct and MockStack implement this interface.

onApply hooks return (haltAfter, err). A non-nil err is a real failure and aborts the loop the same as before. haltAfter=true means the component apply succeeded but subsequent components should NOT be applied — used to defer privileged host configuration to an explicit 'windsor configure network' invocation by the operator. Stack.Up returns (halted bool, err error); halted=true indicates a clean stop, not a failure.

func NewStack

func NewStack(rt *runtime.Runtime, opts ...*TerraformStack) Stack

NewStack creates a new stack of components.

type TerraformComponentPlan added in v0.9.0

type TerraformComponentPlan struct {
	ComponentID string
	Path        string
	Add         int
	Change      int
	Destroy     int
	NoChanges   bool
	IsNew       bool
	Resources   []ResourceChange
	// Protected lists resource addresses that terraform reports cannot be
	// destroyed because their config sets `lifecycle { prevent_destroy = true }`.
	// Populated only on destroy-plan paths; nil on apply-plan paths.
	// Informational — cmd/destroy.go surfaces these as a stderr warning so the
	// operator knows terraform will halt the destroy partway. The wrapper does
	// not refuse: half-completed destroys orphan more state than letting the
	// operator see the addresses and decide. Module authors who want a hard
	// gate should consume TF_VAR_operation / TF_VAR_ephemeral and design the
	// destroy contract in HCL.
	Protected []string
	Err       error
}

TerraformComponentPlan holds the plan result for a single Terraform component. ComponentID is the unique identifier (Name when set, else Path). Path carries the component's blueprint Path field (e.g., "cluster/aws-eks") so renderers can show the underlying module location alongside or instead of the short ID. Add, Change, and Destroy reflect terraform's "to add / to change / to destroy" counts. NoChanges is true when terraform reports no changes. IsNew is true when no state exists for the component in the configured backend — the component has never been applied. Resources is the per-resource change list extracted from the plan output; it is empty when IsNew is true, when the plan errored, or when terraform reports no changes. Err is non-nil when the component's init or plan step failed; subsequent layers may still be attempted.

IsNew supersedes the count fields: when IsNew is true, terraform plan is not executed (it would either misreport "all creates" or fail reading dependent upstream state), so Add/Change/Destroy are zero and NoChanges is false. JSON consumers detecting "pending work" must check IsNew alongside the counts — `IsNew || Add+Change+Destroy > 0` rather than counts alone.

IsNew has two operator-facing meanings depending on which producer populated the struct, and the struct itself does not carry a mode flag — callers route this disambiguation. Apply-side producers (PlanSummary, PlanComponentSummary) set IsNew when the component has never been applied, rendered as "(new)". Destroy-side producers (PlanDestroySummary, PlanDestroyComponentSummary) set IsNew when there is no state to destroy, rendered as "(no state)". Renderers must therefore route apply and destroy results through distinct formatters — calling the apply-side formatter on a destroy-side result would emit "(new)" for a component that has nothing to tear down, which is misleading.

type TerraformStack

type TerraformStack struct {
	// contains filtered or unexported fields
}

TerraformStack manages Terraform infrastructure components by initializing and applying Terraform configurations. It processes components in order, generating terraform arguments, running Terraform init, plan, and apply operations.

warningWriter is the destination for non-blocking operator-facing warnings (e.g. the refresh-fallback notice in destroy paths). Defaults to os.Stderr; tests inject a buffer. Routing warnings through this field rather than os.Stderr directly keeps tests off the fragile os.Stderr-redirect-with-pipe pattern, which deadlocks on Windows when the TUI spinner shares the redirected stream.

func (*TerraformStack) Apply added in v0.9.0

func (s *TerraformStack) Apply(blueprint *blueprintv1alpha1.Blueprint, componentID string) error

Apply runs terraform init, plan, and apply for a single component identified by componentID. It resolves the component from the blueprint, sets up the environment, and executes init, plan, then apply in sequence. Returns an error if the component is not found, the directory does not exist, or any terraform operation fails.

func (*TerraformStack) Destroy added in v0.9.0

func (s *TerraformStack) Destroy(blueprint *blueprintv1alpha1.Blueprint, componentID string) (bool, error)

Destroy tears down a single component idempotently: init → pre-refresh state check → refresh → post-refresh state check → destroy, skipping the rest when state is empty at either check. Returns (true, nil) when skipped, (false, nil) on success, (false, err) on failure. Destroy-mode config (S3 force_destroy, etc.) is honored via TF_VAR_operation; no prep-apply is run because that could recreate resources refresh just dropped.

The whole sequence is wrapped in tui.WithProgress("Destroying <path>") so the visible label matches the bulk DestroyAll loop body — mixing this method into the per-component destroy dance (which calls bulk DestroyAll for non-backend components and this method for backend) would otherwise produce inconsistent "Destroying X" / "Destroying terraform for X" lines side by side. The terraform destroy exec runs silently inside the spinner for the same reason: the bulk loop is silent, so single-component Destroy must be too. Bounded by constants.DefaultTerraformDestroyTimeout.

func (*TerraformStack) DestroyAll added in v0.9.0

func (s *TerraformStack) DestroyAll(blueprint *blueprintv1alpha1.Blueprint, continueOnError bool, excludeIDs ...string) (DestroyOutcome, error)

DestroyAll destroys components in reverse dependency order using the idempotent flow: init → pre-refresh state check → refresh → post-refresh state check → destroy, skipping the rest when state is empty at either check. Components with Destroy=false are skipped. excludeIDs are skipped entirely (used by symmetric-destroy flow at the cmd layer to peel off the backend component from the bulk pass — it gets destroyed last, after its state is migrated to local). When continueOnError is true, per-component destroy errors are collected in DestroyOutcome.Failed and the loop proceeds to the next component; when false, the first error aborts and is returned alongside a partial DestroyOutcome. Each destroy is bounded by constants.DefaultTerraformDestroyTimeout.

func (*TerraformStack) HasLocalStateWithResources added in v0.9.0

func (s *TerraformStack) HasLocalStateWithResources(componentID string) (bool, error)

HasLocalStateWithResources reports whether the per-component local state file exists and contains at least one resource entry. Reads the file as JSON; no terraform invocation. Missing files report (false, nil).

func (*TerraformStack) HasRemoteState added in v0.9.0

func (s *TerraformStack) HasRemoteState(blueprint *blueprintv1alpha1.Blueprint, componentID string) (bool, error)

HasRemoteState reports whether the named component has non-empty terraform state in the currently-configured backend. Runs init + `terraform show -json`; callers must invoke before any in-memory backend override.

func (*TerraformStack) InitComponent added in v0.9.0

func (s *TerraformStack) InitComponent(blueprint *blueprintv1alpha1.Blueprint, componentID string) error

InitComponent runs `terraform init` for one component using the currently- configured backend; no -migrate-state, no plan, no apply.

func (*TerraformStack) MigrateComponentState added in v0.9.0

func (s *TerraformStack) MigrateComponentState(blueprint *blueprintv1alpha1.Blueprint, componentID string) error

MigrateComponentState runs `terraform init -migrate-state -force-copy` for a single component identified by componentID, moving its state to the currently configured backend. Used by `windsor bootstrap` after applying just the backend component with a local backend, so only that component's state is moved to remote (the rest haven't been applied yet and will init directly against the configured backend on the next Up). Returns an error if the blueprint is nil, the component is not found, the component's directory does not exist, or any terraform operation fails.

func (*TerraformStack) MigrateState added in v0.9.0

func (s *TerraformStack) MigrateState(blueprint *blueprintv1alpha1.Blueprint) ([]string, error)

MigrateState runs `terraform init -migrate-state -force-copy` per component to move state to the currently configured backend. Components whose directories don't exist on disk are skipped and their IDs returned in the skipped slice, paired with any error so callers see both what migrated and what didn't. Stops on the first failure; safe to retry.

func (*TerraformStack) Plan added in v0.9.0

func (s *TerraformStack) Plan(blueprint *blueprintv1alpha1.Blueprint, componentID string) error

Plan runs terraform init and plan for a single component identified by componentID. It resolves the component from the blueprint, sets up the environment, and executes init then plan without applying any changes. Returns an error if the component is not found, the directory does not exist, or any terraform operation fails.

func (*TerraformStack) PlanAll added in v0.9.0

func (s *TerraformStack) PlanAll(blueprint *blueprintv1alpha1.Blueprint) error

PlanAll runs terraform init and plan for every enabled component in the blueprint, streaming output directly to stdout. Stops on the first error. Returns an error if blueprint is nil or any component's init or plan step fails.

func (*TerraformStack) PlanAllJSON added in v0.9.0

func (s *TerraformStack) PlanAllJSON(blueprint *blueprintv1alpha1.Blueprint) error

PlanAllJSON runs terraform init and plan -json for every enabled component in the blueprint, streaming machine-readable JSON lines to stdout. Stops on the first error. Returns an error if blueprint is nil or any component's init or plan step fails.

func (*TerraformStack) PlanComponentSummary added in v0.9.0

func (s *TerraformStack) PlanComponentSummary(blueprint *blueprintv1alpha1.Blueprint, componentID string) TerraformComponentPlan

PlanComponentSummary runs terraform init and plan for a single component and returns its structured plan result. It resolves only the requested component from the blueprint, so no other components are initialised or planned. If the component is not found, a result with a non-nil Err is returned rather than an error, consistent with PlanSummary.

func (*TerraformStack) PlanDestroyComponentSummary added in v0.9.0

func (s *TerraformStack) PlanDestroyComponentSummary(blueprint *blueprintv1alpha1.Blueprint, componentID string) TerraformComponentPlan

PlanDestroyComponentSummary runs terraform init and `plan -destroy` for a single component and returns its structured plan result. If the component is not found, a result with a non-nil Err is returned rather than an error, to match PlanComponentSummary. A component pinned with Destroy=false also returns Err — Teardown would skip it, so producing a destroy plan for it would mislead the operator.

func (*TerraformStack) PlanDestroySummary added in v0.9.0

func (s *TerraformStack) PlanDestroySummary(blueprint *blueprintv1alpha1.Blueprint) []TerraformComponentPlan

PlanDestroySummary runs terraform init and `plan -destroy` for every component in the blueprint that is not pinned with Destroy=false, capturing the destroy counts and the per-resource list rather than printing them. Components with Destroy=false are filtered out so the rendered plan matches what Teardown would actually destroy — including them would lie. As with PlanSummary, errors are recorded per-component and the summary continues so callers receive partial results for independent layers. Returns nil if blueprint is nil or projectRoot is unset.

func (*TerraformStack) PlanJSON added in v0.9.0

func (s *TerraformStack) PlanJSON(blueprint *blueprintv1alpha1.Blueprint, componentID string) error

PlanJSON runs terraform init and plan -json for a single component identified by componentID, streaming machine-readable JSON lines to stdout. Returns an error if the component is not found, the directory does not exist, or any terraform operation fails.

func (*TerraformStack) PlanSummary added in v0.9.0

PlanSummary runs terraform init and plan for every enabled component in the blueprint, capturing output to parse add/change/destroy counts rather than printing them. Errors are recorded per-component; the summary continues even if a component fails, so callers receive partial results for independent layers. Returns nil if blueprint is nil.

func (*TerraformStack) PostApply added in v0.9.0

func (s *TerraformStack) PostApply(fns ...func(id string) error)

PostApply registers hooks to run after each component's WithProgress block completes (i.e. after Done is printed). Hooks are consumed and cleared at the start of the next Up call so they are not retained.

func (*TerraformStack) RemoveLocalState added in v0.9.0

func (s *TerraformStack) RemoveLocalState(componentID string) error

RemoveLocalState removes the per-component local state file (and its .backup sibling) under the windsor scratch path. Missing files are tolerated.

func (*TerraformStack) Up

func (s *TerraformStack) Up(blueprint *blueprintv1alpha1.Blueprint, onApply ...func(id string) (bool, error)) (bool, error)

Up runs init/plan/apply for each component in order. Backend override files are cleaned up after all components complete so terraform_output() calls between components keep working. onApply hooks run inside each spinner; PostApply hooks run after each Done line and are consumed (not retained across calls).

Returns (halted bool, err error). halted=true means a hook signaled that the component apply succeeded but subsequent components should not be applied (e.g. cluster reachability needs a host-side configuration step the operator hasn't done yet). halted is reported as a clean stop — the TUI marks the component Done, no error is returned, and the caller can surface a "deferred work" summary at the cmd layer. A non-nil err is a real failure as before.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL