config

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 35 Imported by: 0

Documentation

Index

Constants

View Source
const GeneratedMarker = "# Generated by vizier from "

GeneratedMarker is the first thing every unit a stack writes says about itself.

It is load-bearing twice over: `stack generate` refuses to overwrite a vizier.hcl that does not carry it, and `stack clean` refuses to delete one. Without it both commands are a directory-shaped foot-gun, because a stack unit's `path` is an ordinary relative path that can name a unit somebody wrote by hand.

View Source
const StackFileName = "vizier.stack.hcl"

StackFileName is the composition file: a set of units generated from one declaration instead of a directory tree written out by hand.

Variables

View Source
var ErrNoDependencyOutputs = errors.New("dependency has no outputs yet")

ErrNoDependencyOutputs marks the one input-evaluation failure that is not a problem with the configuration: a dependency that has simply not been applied yet, on a tree where mocks are not available for this command.

It exists so an inspection command can tell "this config is wrong" apart from "this tree is new". Reporting a greenfield tree as broken would make `hcl validate` useless in exactly the place it is most wanted, which is CI before anything has ever been applied.

View Source
var ErrReadOnly = errors.New("read-only command declined to run this")

SetReadOnly makes side-effecting config functions refuse instead of running.

`vizier verify` is documented as checking proofs "without running tofu" and the site describes it as touching nothing - but Discover parses every config, which evaluates run_cmd. A unit containing `locals { x = run_cmd("sh", "-c", "...") }` therefore executed arbitrary commands during a command advertised as inert. Reading a config is not supposed to be a privileged operation. ErrReadOnly marks a refusal to DO something during an inspection command, as opposed to a problem with the configuration.

The distinction is the whole point: `hcl validate` on a config whose locals call run_cmd reported "config does not parse", which is false - the config is fine and a real run evaluates it happily. An inspection command declining to shell out is a fact about the command, not a defect in the file, and the two must not print the same way.

Functions

func CacheDirs added in v1.2.0

func CacheDirs() []string

CacheDirs names the directories that hold other people's files: modules this tool downloaded, providers Terraform downloaded, and git's own store. What is inside them is never part of the tree being described.

`.vizier` belongs here and was missing, which was not a listing problem. `vizier browse <repo>` caches the repository at <root>/.vizier/browse, so browsing somebody else's modules made every vizier.hcl in that repository a unit of YOUR tree. `run-all apply` then ran them, under the default verify-mode=enforce, because a unit carries its own verify block and the third party had written allow_any_source into theirs. Browsing is a read. It must not be able to add anything to what a later apply executes.

Exported so the pairing test can enumerate it: browse keeps its own list and the two cannot be merged (browse imports config), so they are checked against each other instead.

func CheckConstraint added in v1.2.0

func CheckConstraint(constraint, version string) (bool, error)

CheckConstraint reports whether version satisfies constraint.

An unparseable constraint is an ERROR, never a silent pass. A constraint nobody can read is a constraint nobody is enforcing, and this one exists to stop a run.

func DecodeForTest added in v1.2.0

func DecodeForTest(body hcl.Body, target any) error

DecodeForTest and ResolveRemoteStateForTest let a sibling package build a resolved remote_state the way the parser does.

Exported for tests only. The alternative is duplicating the decode-and-resolve pair in every package that needs one, which is how two code paths end up disagreeing about what a config means.

func DocumentedNames added in v1.2.0

func DocumentedNames() []string

DocumentedNames lists every name this file describes, for the test that keeps it paired with the reflected schema.

func EvalCtx

func EvalCtx(dir string) *hcl.EvalContext

EvalCtx returns an eval context whose functions are resolved relative to dir.

func EvalCtxWithLocals

func EvalCtxWithLocals(dir string, locals map[string]cty.Value) *hcl.EvalContext

func FindStack

func FindStack(dir string) string

FindStack returns the stack file in dir, or "" when there is none.

func IsGenerated added in v1.2.0

func IsGenerated(path string) (bool, error)

IsGenerated reports whether a path is safe for a stack to write or remove.

True for a file vizier generated AND for a file that does not exist, because both are cases where writing is harmless. False only for a file that exists and was not generated, which is the one case that must stop the caller.

It reads the first line rather than the whole file: the marker is the first thing renderUnit writes, and a unit config can be large.

func IsReadOnlyRefusal added in v1.2.0

func IsReadOnlyRefusal(err error) bool

IsReadOnlyRefusal reports whether an error is this package declining to act, rather than a problem with the file.

It checks the wrapped sentinel AND the rendered text, because an error raised inside an HCL function call is flattened into a diagnostic string by the time a caller sees it: hcl.Diagnostics carries a message, not a wrapped error, so errors.Is alone silently answers false for every real case.

func ResetAWSIdentity

func ResetAWSIdentity()

ResetAWSIdentity clears the cached identity, for tests.

func ResolveIncludes

func ResolveIncludes(u *Unit) error

ResolveIncludes loads each include's parent unit and merges parent settings into fields the child left unset. Phase-1 merge scope: Verify policy.

func ResolveRemoteStateForTest added in v1.2.0

func ResolveRemoteStateForTest(r *RemoteState) error

ResolveRemoteStateForTest evaluates the config expression, as parsing does.

func SetCommand

func SetCommand(c string)

SetCommand records the action being run.

func SetFeatures

func SetFeatures(m map[string]string)

SetFeatures installs the CLI's feature values.

func SetReadOnly

func SetReadOnly(v bool)

func SetStackFetcher

func SetStackFetcher(fn func(src, unitDir, into string) (string, error))

SetStackFetcher installs the remote-source resolver.

Types

type Compiled

type Compiled struct {
	Retries []CompiledRetry
	Ignores []CompiledIgnore
}

Compiled is the usable form: patterns are compiled once, at parse time, so a bad regex is a config error rather than a surprise during an apply.

func (*Compiled) MatchIgnore

func (c *Compiled) MatchIgnore(msg string) *CompiledIgnore

MatchIgnore reports the rule that swallows this error, if any.

func (*Compiled) MatchRetry

func (c *Compiled) MatchRetry(msg string) *CompiledRetry

MatchRetry reports the rule that retries this error, if any.

type CompiledIgnore

type CompiledIgnore struct {
	Name     string
	Patterns []*regexp.Regexp
	Message  string
}

type CompiledRetry

type CompiledRetry struct {
	Name        string
	Patterns    []*regexp.Regexp
	MaxAttempts int
	Sleep       time.Duration
}

type Dependency

type Dependency struct {
	Name       string `hcl:"name,label"`
	ConfigPath string `hcl:"config_path"`
	// MockOutputs stands in for a dependency that has not been applied yet, so
	// `run-all plan` works on a greenfield stack. Held as an expression because
	// it may reference locals.
	MockOutputs hcl.Expression `hcl:"mock_outputs,optional"`
	// MockOutputsAllowedCommands checks which commands may consume the mocks.
	// Empty means NONE: a mocked value must never reach an apply, where it
	// would be written into real infrastructure as though it were a real id.
	MockOutputsAllowedCommands []string `hcl:"mock_outputs_allowed_terraform_commands,optional"`
}

type DependsOnBlock

type DependsOnBlock struct {
	Paths []string `hcl:"paths"`
}

DependsOnBlock declares ordering-only dependencies: units that must run first but whose outputs are not consumed. Ordering without coupling.

type EngineBlock added in v1.2.0

type EngineBlock struct {
	// Source is a local path or an https URL to a BARE executable. Bare, not an
	// archive: an archive needs extracting, and extraction is a whole class of
	// path-traversal bug that simply cannot exist if there is nothing to unpack.
	Source string `hcl:"source"`
	// SHA256 is the digest of that executable. Required. An engine nobody pinned
	// is an engine nobody can prove.
	SHA256 string `hcl:"sha256"`
	// Version is recorded in the receipt. It never decides what gets downloaded.
	Version string `hcl:"version,optional"`
	// Meta is passed to the plugin on init, for whatever it needs to know.
	Meta map[string]string `hcl:"meta,optional"`
}

EngineBlock names an alternative executor for a unit.

Every field here is shaped by one question: can the thing that RUNS your proved module be proved too? A plugin replaces the process that executes the code verification just verified, so an unverifiable plugin would make verification a statement about a file nobody ran.

Hence SHA256 is REQUIRED. Terragrunt's equivalent block makes `version` optional and downloads whatever the host calls latest, verifies only GitHub sources against a single hard-coded vendor key, and documents "turn the check off" as the supported path for third-party engines. None of those choices is available here: there is no "latest", no unpinned source, and no flag that disables the check.

type ErrorsBlock

type ErrorsBlock struct {
	Retries []RetryRule  `hcl:"retry,block"`
	Ignores []IgnoreRule `hcl:"ignore,block"`
}

ErrorsBlock is Terragrunt's `errors { retry {...} ignore {...} }`.

The flat `retryable_errors` list is Terragrunt's older, deprecated shape and is still accepted, but it cannot express the two things people actually need: a per-class attempt count and backoff, and the ability to IGNORE a failure that is expected rather than retry it.

func (*ErrorsBlock) Compile

func (e *ErrorsBlock) Compile() (*Compiled, error)

Compile validates and prepares an errors block.

type Exclude

type Exclude struct {
	If      bool     `hcl:"if"`
	Actions []string `hcl:"actions,optional"`
}

Exclude leaves a unit out of a run when its condition holds.

Actions narrows it: excluding from `destroy` but not `plan` is the common case for something you want visible but never torn down - which is also Terragrunt's `prevent_destroy`, expressed once rather than twice.

func (*Exclude) AppliesTo

func (e *Exclude) AppliesTo(action string) bool

AppliesTo reports whether this exclusion covers the given action. No actions means all of them.

type ExtraArguments

type ExtraArguments struct {
	Name string `hcl:"name,label"`
	// Commands this applies to; empty means all.
	Commands  []string `hcl:"commands,optional"`
	Arguments []string `hcl:"arguments,optional"`
	// RequiredVarFiles are passed as -var-file and MUST exist.
	RequiredVarFiles []string `hcl:"required_var_files,optional"`
	// OptionalVarFiles are passed only when present, which is what lets one
	// parent config serve environments that do not all have the same files.
	OptionalVarFiles []string          `hcl:"optional_var_files,optional"`
	EnvVars          map[string]string `hcl:"env_vars,optional"`
}

ExtraArguments injects flags into specific engine commands, and is how the account.tfvars / region.tfvars / env.tfvars idiom is wired in real repos.

func (ExtraArguments) AppliesTo

func (e ExtraArguments) AppliesTo(action string) bool

AppliesTo reports whether these arguments apply to this engine action.

Same rule as Hook.AppliesTo, and the same reason: an extra_arguments block with required_var_files applied to `exec` and aborted a command that was never going to pass a var file to anything.

func (ExtraArguments) Resolve

func (e ExtraArguments) Resolve(unitDir string) ([]string, error)

Resolve returns the CLI arguments this block contributes, relative to unitDir. A missing REQUIRED var file is an error; a missing optional one is skipped.

type Feature

type Feature struct {
	Name    string `hcl:"name,label"`
	Default string `hcl:"default,optional"`
}

Feature is a named flag a config can branch on, settable from the CLI.

It is what makes `exclude` useful: "leave this unit out unless the release flag is on" is a thing pipelines need, and encoding it as commented-out HCL is how configs rot.

type Field added in v1.2.0

type Field struct {
	Name string `json:"name"`
	// Kind is "attribute" or "block".
	Kind string `json:"kind"`
	// Type is the Go type rendered readably, which is the closest thing HCL has
	// to a declared type for these.
	Type     string `json:"type"`
	Required bool   `json:"required"`
	// Label is the block label's name where a block takes one, as in
	// dependency "network" { }.
	Label string `json:"label,omitempty"`
	// Doc is one sentence on what it is for.
	Doc string `json:"doc"`
	// Fields are a block's own contents, one level down.
	Fields []Field `json:"fields,omitempty"`
}

Field is one attribute or block a config may contain.

func Schema added in v1.2.0

func Schema() []Field

Schema describes a unit configuration file.

func StackSchema added in v1.2.0

func StackSchema() []Field

StackSchema describes a vizier.stack.hcl.

type Generate

type Generate struct {
	Name     string `hcl:"name,label"`
	Path     string `hcl:"path"`
	Contents string `hcl:"contents"`
	// IfExists is accepted for Terragrunt compatibility. Vizier always
	// overwrites what it generates - the file is a build product - so this is
	// recorded rather than obeyed; rejecting the attribute outright just stopped
	// real configs from parsing.
	IfExists string `hcl:"if_exists,optional"`
}

type Hook

type Hook struct {
	Name string `hcl:"name,label"`
	// Commands are the engine actions this hook applies to (plan/apply/destroy).
	// Empty means all of them.
	Commands []string `hcl:"commands,optional"`
	// Execute is the command and its arguments.
	Execute []string `hcl:"execute"`
	// WorkingDir defaults to the unit directory.
	WorkingDir string `hcl:"working_dir,optional"`
	// RunOnError makes an after-hook run even when the engine failed - the
	// difference between a cleanup hook that works and one that only works on
	// the happy path.
	RunOnError bool `hcl:"run_on_error,optional"`
	// SuppressStdout hides output from a chatty hook.
	SuppressStdout bool `hcl:"suppress_stdout,optional"`
	// OnErrors (error hooks only) limits the hook to failures whose message
	// matches one of these regexes.
	OnErrors []string `hcl:"on_errors,optional"`
}

Hook is a command run around an engine invocation - the escape hatch mature repos rely on for auth refresh, secret fetch, extra linting, notifications and cleanup. Without it people wrap the CLI in shell and lose the DAG.

func (Hook) AppliesTo

func (h Hook) AppliesTo(action string) bool

AppliesTo reports whether the hook runs for this engine action.

An empty `commands` means the ENGINE verbs, not literally every action vizier will ever have. When `exec` was added, every such hook was silently enrolled into it: an after_hook that deletes .terraform started firing on `vizier exec -- ls`, in configs written years before exec existed. A hook runs under a non-engine verb only when it names that verb.

func (Hook) MatchesError

func (h Hook) MatchesError(err error) bool

MatchesError reports whether an error hook should fire for this failure. No on_errors means every failure matches.

func (Hook) Run

func (h Hook) Run(unitDir string, stdout, stderr io.Writer) error

Run executes the hook relative to unitDir.

A non-zero exit is an ERROR and aborts the run. That is the point of a hook: a policy check or an auth refresh that fails silently is worse than not having one, because the run proceeds looking healthy. Run executes the hook.

stdout and stderr are io.Writer rather than *os.File so a caller can attribute the output. Under --parallelism several units run at once and a hook's lines have to say which unit produced them; with *os.File the only thing a caller could pass was os.Stdout, so they never could. exec.Cmd copies through a pipe for a non-*os.File writer, which is exactly what is wanted here.

type IgnoreRule

type IgnoreRule struct {
	Name            string   `hcl:"name,label"`
	IgnorableErrors []string `hcl:"ignorable_errors"`
	Message         string   `hcl:"message"`
}

IgnoreRule swallows a named class of failure.

This is genuinely dangerous and therefore explicit: a signal has to say what it is ignoring and why, so `message` is required rather than optional. A rule that silently absorbs failures with no explanation is how a broken apply gets reported as a success.

type Include

type Include struct {
	Name string `hcl:"name,label"`
	Path string `hcl:"path"`
	// Expose makes the parent readable as include.<label>.locals.<name>.
	// Terragrunt does NOT inherit locals silently, and neither do we: a child
	// that reads a parent's local should say so, or renaming a local in a
	// parent breaks children that never mentioned it.
	Expose bool `hcl:"expose,optional"`
	// MergeStrategy is shallow (default) or deep. Deep merges maps recursively
	// and concatenates lists, for parents that build up structure.
	MergeStrategy string `hcl:"merge_strategy,optional"`
}

type RemoteState

type RemoteState struct {
	Backend string `hcl:"backend"`
	// Config is the backend's arguments. Held as an expression so it can use
	// locals and functions - deriving `key` from the path is the common case.
	Config   hcl.Expression       `hcl:"config"`
	Generate *RemoteStateGenerate `hcl:"generate,optional"`
	// contains filtered or unexported fields
}

RemoteState declares the state backend once and generates the backend .tf file for each unit before init.

This is the feature people actually adopt an orchestrator for: one declaration in a parent config, per-unit state keys derived from the unit's path. Written by hand it is the same block copy-pasted into every root module with one line different, which is precisely the duplication that goes stale.

func (*RemoteState) RenderHCL

func (r *RemoteState) RenderHCL() (string, error)

RenderHCL produces the `terraform { backend "..." {} }` file contents.

Keys are emitted in sorted order and values aligned, so the generated file is byte-stable across runs. Go randomises map iteration, and an unstable generated file shows up as a phantom diff on every run - which trains people to ignore diffs in exactly the file that decides where their state lives.

func (*RemoteState) ResolvedArgs added in v1.2.0

func (r *RemoteState) ResolvedArgs() map[string]cty.Value

ResolvedArgs returns a COPY of the resolved backend arguments.

A copy, because handing out the live map would let a caller change what RenderHCL writes into every unit's backend file.

func (*RemoteState) StringArg added in v1.2.0

func (r *RemoteState) StringArg(name string) (string, bool)

resolveRemoteState evaluates the config expression once, at parse time, while locals and the dir-relative functions are in scope. StringArg returns one resolved backend argument, when it is a plain string.

Exported so `vizier backend` can read the bucket, table and region a tree has already declared, instead of asking for them again on a command line. Values that are not strings are reported as absent rather than stringified: a bucket name that came out as a number or an object is a config problem, and guessing at it is how a command creates something nobody asked for.

func (*RemoteState) TargetPath

func (r *RemoteState) TargetPath() string

TargetPath is the file the backend block is written to, relative to the unit.

type RemoteStateGenerate

type RemoteStateGenerate struct {
	Path     string `cty:"path"`
	IfExists string `cty:"if_exists"`
}

RemoteStateGenerate controls where the backend file is written.

type RetryRule

type RetryRule struct {
	Name            string   `hcl:"name,label"`
	RetryableErrors []string `hcl:"retryable_errors"`
	MaxAttempts     int      `hcl:"max_attempts,optional"`
	SleepSeconds    int      `hcl:"sleep_interval_sec,optional"`
}

RetryRule retries a named class of failure.

type SourcesBlock added in v1.2.0

type SourcesBlock struct {
	URLs []string `hcl:"urls"`
}

SourcesBlock lists module repositories worth browsing.

Read only from the tree root, the way a root config declares anything that describes the whole repository. `urls` is required rather than optional, so a block with nothing in it is a parse error instead of a silent no-op.

Deliberately absent: Terragrunt's default_template, no_shell and no_hooks. Those exist to configure a boilerplate templating engine that runs shell hooks from a fetched repository during scaffolding. Scaffolding from a repository is already the moment vizier can prove least; running that repository's shell scripts at the same moment is not a capability this tool should offer.

type Stack

type Stack struct {
	Units  []StackUnit  `hcl:"unit,block"`
	Stacks []StackChild `hcl:"stack,block"`
	// locals is consumed by evalLocals before this decode runs; it is declared
	// here only so gohcl does not reject the block as unexpected. Without it,
	// `path = "${local.env}/vpc"` - the entire per-environment point of a stack -
	// cannot be written.
	Locals []struct {
		Remain hcl.Body `hcl:",remain"`
	} `hcl:"locals,block"`
}

Stack is `vizier.stack.hcl`.

The problem it solves is duplication ACROSS environments rather than within one: prod, staging and dev usually differ by three values and are otherwise the same twelve units. `include` makes each unit DRY; it does nothing about there being three near-identical copies of the whole tree. A stack declares the shape once and stamps it out per environment.

func ParseStack

func ParseStack(path string) (*Stack, error)

ParseStack reads a stack file.

func (*Stack) Generate

func (s *Stack) Generate(stackPath, root string) ([]string, error)

Generate writes each unit's vizier.hcl beneath root.

Generated files carry a header saying so and naming the stack that produced them, because the failure mode of any generator is someone editing the output and losing the edit on the next run.

Generation is two phases, plan then write, and the split is the safety property. The plan resolves EVERY unit the stack implies, nested stacks included, and refuses the whole thing on the first problem. Only once nothing can refuse does anything touch the disk: a stack that stops half way has already replaced part of a tree, which is a worse place to be than either finishing or not starting.

type StackChild

type StackChild struct {
	Name   string    `hcl:"name,label"`
	Source string    `hcl:"source"`
	Path   string    `hcl:"path"`
	Values cty.Value `hcl:"values,optional"`
}

StackChild composes another stack file, so a "region" stack can be reused across environments.

type StackUnit

type StackUnit struct {
	Name string `hcl:"name,label"`
	// Source is the module the generated unit will use.
	Source string `hcl:"source"`
	// Path is where the unit is written, relative to the stack file.
	Path string `hcl:"path"`
	// Values become the generated unit's `inputs`.
	Values cty.Value `hcl:"values,optional"`
	// DependsOn generates `dependencies { paths = [...] }`, so ordering survives
	// generation. Without it a stack could only produce independent units, which
	// is not what any real environment looks like.
	DependsOn []string `hcl:"depends_on,optional"`
	// Verify is copied verbatim into the generated unit, so a stack cannot
	// become a way to generate units that skip verification.
	Verify *VerifyPolicy `hcl:"verify,block"`
}

StackUnit is one unit to generate.

type TerraformBlock

type TerraformBlock struct {
	Source      string           `hcl:"source"`
	ExtraArgs   []ExtraArguments `hcl:"extra_arguments,block"`
	BeforeHooks []Hook           `hcl:"before_hook,block"`
	AfterHooks  []Hook           `hcl:"after_hook,block"`
	ErrorHooks  []Hook           `hcl:"error_hook,block"`
}

TerraformBlock is `terraform { ... }`.

Terragrunt nests extra_arguments and the hooks INSIDE this block, and that is how they appear in every config anyone would copy. Accepting only `source` made such a file fail to parse outright - `Blocks of type "extra_arguments" are not expected here` - which is a wall, not a degradation. They are merged into the unit's top-level collections after decode, so both spellings work and the rest of the code sees one list.

type Unit

type Unit struct {
	Dir          string
	Terraform    *TerraformBlock
	Includes     []Include
	Dependencies []Dependency
	Verify       *VerifyPolicy
	// Engine, when set, replaces the process that executes this unit.
	Engine *EngineBlock
	// Sources lists repositories `vizier browse` should list. Root config only.
	Sources     *SourcesBlock
	Generates   []Generate
	RemoteState *RemoteState
	BeforeHooks []Hook
	AfterHooks  []Hook
	ErrorHooks  []Hook
	ExtraArgs   []ExtraArguments
	DependsOn   []string             // ordering-only dependencies (dependencies { paths })
	Locals      map[string]cty.Value // resolved in pass 1 (locals.go)
	// Exposed carries each `expose = true` parent, keyed by include label, for
	// the `include.<label>.locals.<name>` lookup.
	Exposed map[string]map[string]cty.Value
	// Errors is the compiled `errors {}` block: per-class retry and ignore rules.
	Errors *Compiled
	// Excludes are `exclude {}` blocks, already evaluated to a boolean.
	Excludes []Exclude
	// IAMRole is a role to assume for this unit's engine calls, so one tree can
	// span accounts without a wrapper script per environment.
	IAMRole            string
	IAMRoleDuration    int
	IAMRoleSessionName string
	// DownloadDir is where modules are materialised. Empty means
	// <unit>/.vizier-module, matching Terragrunt's per-unit default.
	DownloadDir string
	// Skip excludes a unit from execution while leaving it in the DAG, so its
	// outputs still feed dependents. There was no way at all to leave one unit
	// out of a run, so the only options were running it or deleting its config.
	Skip bool
	// EngineVersionConstraint and VizierVersionConstraint refuse a run under a
	// toolchain the config was not written for.
	//
	// The failure they prevent is quiet: a module that needs tofu 1.6 running
	// under 1.5 does not say "wrong version", it says something obscure about a
	// syntax it does not recognise, three units into a run. Terragrunt spells
	// these terraform_version_constraint and terragrunt_version_constraint; both
	// spellings are accepted so an existing config parses unchanged.
	EngineVersionConstraint string
	VizierVersionConstraint string
	// TerragruntVersionConstraint is PARSED so an existing Terragrunt config
	// still reads, and deliberately NOT enforced. It pins a different product's
	// version namespace: Terragrunt is on 0.x and vizier is on 1.x, so honouring
	// a real pin like ">= 0.45.0, < 0.46.0" against our own version refuses
	// every tree that carries one. Reported by `hcl validate` so it is visible
	// rather than silently ignored.
	TerragruntVersionConstraint string
	// RetryableErrors extends the built-in transient-failure patterns.
	// runner.RetryPolicy.Patterns existed and was never assigned from anywhere,
	// so the hardcoded list was the only list and a provider with its own
	// distinctive throttling message could not be accommodated.
	RetryableErrors []string
	// External marks a unit pulled in because something inside the tree depends
	// on it, but which lies outside --dir. It is checked and its outputs are read;
	// it is never executed. Pointing at environments/prod must not apply
	// something in shared/.
	External bool
	// contains filtered or unexported fields
}

func Discover

func Discover(root string) ([]*Unit, error)

Discover walks root and returns every directory that is a unit: it contains a vizier.hcl with a `terraform` block. (The bare root config - includes only - is not a unit.) Includes are resolved for each unit.

A unit that cannot be parsed is an error. Use DiscoverReadOnly from an inspection command, where a config declining to shell out is an expected answer rather than a broken file.

func DiscoverReadOnly added in v1.2.0

func DiscoverReadOnly(root string) (units []*Unit, declined []string, err error)

DiscoverReadOnly is Discover for commands that promise not to act.

Under SetReadOnly a config using run_cmd, sops_decrypt_file or get_aws_* is declined rather than evaluated. Such a unit is returned in `declined` instead of aborting the walk, and instead of being dropped: an inspection command has to be able to say which part of the picture is missing, because a graph or an output listing that is quietly short of a unit is worse than one that says so.

func ParseUnit

func ParseUnit(dir string) (*Unit, error)

ParseUnit decodes <dir>/vizier.hcl into a Unit. The `inputs` attribute is captured but NOT evaluated (it may reference dependency outputs not yet known).

func ResolveExternalDependencies

func ResolveExternalDependencies(units []*Unit) ([]*Unit, error)

ResolveExternalDependencies parses units that live OUTSIDE the discovered tree but are named by a dependency's config_path, and returns them appended.

Any config_path that did not resolve inside `Discover(root)` used to be a hard error: "unit app depends on unknown path ../../../shared/dns". That rejects one of the most common Terragrunt layouts there is - shared/ beside environments/, with `--dir environments/prod` - and offered no flag to include or ignore it, so the only way forward was to widen --dir and run the whole repo.

External units are marked so they are never EXECUTED. They exist to be checked and to have their outputs read: running `--dir environments/prod` must not apply something in shared/ that the user did not point at.

func (*Unit) DepKey

func (u *Unit) DepKey(configPath string) string

DepKey resolves a dependency's config_path to the Key of the unit it names. Same resolution the graph uses for the same edge.

func (*Unit) EvalInputs

func (u *Unit) EvalInputs(depOutputs map[string]map[string]any) (map[string]cty.Value, error)

EvalInputs evaluates the unit's `inputs` map with dependency outputs in scope as dependency.<name>.outputs.<key>. Returns name -> cty value.

Equivalent to EvalInputsFor("", depOutputs): no command context, so mock outputs are never substituted.

func (*Unit) EvalInputsDetailed

func (u *Unit) EvalInputsDetailed(command string, depOutputs map[string]map[string]any) (map[string]cty.Value, []string, error)

EvalInputsDetailed is EvalInputsFor plus the names of the dependencies whose values came from mock_outputs rather than a real apply.

Callers need that list because a value's PROVENANCE changes what may be done with it: a saved plan bakes its variables in, so a plan built on mocks must never become a plan file that a later `apply <file>` materialises for real.

func (*Unit) EvalInputsFor

func (u *Unit) EvalInputsFor(command string, depOutputs map[string]map[string]any) (map[string]cty.Value, error)

EvalInputsFor evaluates inputs for a specific engine command.

Real outputs always win. Mocks are used only when a dependency has produced nothing yet AND the command is listed in that dependency's mock_outputs_allowed_terraform_commands. An empty list means mocks are never used, which is the safe default: a mocked value reaching `apply` would be written into live infrastructure as if it were real.

func (*Unit) Key

func (u *Unit) Key() string

Key is the unit's identity: its cleaned directory path.

Outputs, check decisions and DAG edges were previously keyed by three different things - basename, the dependency block's LABEL, and the resolved path - which agreed only when every directory happened to be uniquely named and every label happened to match its target's basename. When they did not, a tree with prod/vpc and dev/vpc threaded one environment's outputs into the other, silently and differently on each run.

Everything now keys on this, so the graph and the data flowing along it cannot disagree.

func (*Unit) Name

func (u *Unit) Name() string

Name is the unit's directory basename. It is a DISPLAY name only: two units in different environments routinely share one (prod/vpc and dev/vpc), so it must never be used to key anything.

func (*Unit) RawInputNames added in v1.1.0

func (u *Unit) RawInputNames() []string

RawInputNames is RawInputs' keys, sorted, for callers that only need the set.

func (*Unit) RawInputs added in v1.1.0

func (u *Unit) RawInputs() map[string]string

RawInputs returns each input as the EXPRESSION the operator wrote, keyed by input name, plus the ancestors' inputs that cascade into this unit.

Deliberately not evaluated. Evaluating needs dependency outputs, and those do not exist until the dependency has actually run - so a caller that wanted a "value" to display would be asking for a number nobody has computed yet. The expression is the honest answer: `dependency.vpc.outputs.id` is what the unit says, and what it resolves to is a fact about a run, not about the config.

Returns nil when the unit declares no inputs, which is different from an empty map only in intent and is treated the same by every caller here.

type VerifyPolicy

type VerifyPolicy struct {
	RequireSigned *bool  `hcl:"require_signed"`
	MinStatus     string `hcl:"min_status,optional"`
	// AllowUnverified lists sources admitted without catalog proof. Matched by
	// PREFIX, so one entry covers a whole repo rather than needing the exact
	// string of every module in it.
	AllowUnverified []string `hcl:"allow_unverified,optional"`
	// SourceSHA256 pins the expected digest of a NON-catalog module's tree.
	//
	// This is how a client brings their own modules without giving up the
	// guarantee: they do not need to publish to any catalog or hold a signing
	// key, they just declare what they expect the code to be. The digest is
	// computed over sorted relative paths plus contents, so a renamed file
	// changes it, and Vizier's own generated files are excluded so it describes
	// the module rather than the run.
	SourceSHA256 string `hcl:"source_sha256,optional"`
	// AllowAnySource admits non-catalog modules with no pin at all. It is the
	// explicit way to say "this tree runs code from anywhere", and the receipt
	// records it - a bypass must never be invisible.
	AllowAnySource *bool `hcl:"allow_any_source,optional"`
}

Jump to

Keyboard shortcuts

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