Documentation
¶
Index ¶
- Constants
- Variables
- func CacheDirs() []string
- func CheckConstraint(constraint, version string) (bool, error)
- func DecodeForTest(body hcl.Body, target any) error
- func DocumentedNames() []string
- func EvalCtx(dir string) *hcl.EvalContext
- func EvalCtxWithLocals(dir string, locals map[string]cty.Value) *hcl.EvalContext
- func FindStack(dir string) string
- func IsGenerated(path string) (bool, error)
- func IsReadOnlyRefusal(err error) bool
- func ResetAWSIdentity()
- func ResolveIncludes(u *Unit) error
- func ResolveRemoteStateForTest(r *RemoteState) error
- func SetCommand(c string)
- func SetFeatures(m map[string]string)
- func SetReadOnly(v bool)
- func SetStackFetcher(fn func(src, unitDir, into string) (string, error))
- type Compiled
- type CompiledIgnore
- type CompiledRetry
- type Dependency
- type DependsOnBlock
- type EngineBlock
- type ErrorsBlock
- type Exclude
- type ExtraArguments
- type Feature
- type Field
- type Generate
- type Hook
- type IgnoreRule
- type Include
- type RemoteState
- type RemoteStateGenerate
- type RetryRule
- type SourcesBlock
- type Stack
- type StackChild
- type StackUnit
- type TerraformBlock
- type Unit
- func (u *Unit) DepKey(configPath string) string
- func (u *Unit) EvalInputs(depOutputs map[string]map[string]any) (map[string]cty.Value, error)
- func (u *Unit) EvalInputsDetailed(command string, depOutputs map[string]map[string]any) (map[string]cty.Value, []string, error)
- func (u *Unit) EvalInputsFor(command string, depOutputs map[string]map[string]any) (map[string]cty.Value, error)
- func (u *Unit) Key() string
- func (u *Unit) Name() string
- func (u *Unit) RawInputNames() []string
- func (u *Unit) RawInputs() map[string]string
- type VerifyPolicy
Constants ¶
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.
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 ¶
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.
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
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
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 IsGenerated ¶ added in v1.2.0
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
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 ¶
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 SetFeatures ¶
SetFeatures installs the CLI's feature values.
func SetReadOnly ¶
func SetReadOnly(v bool)
func SetStackFetcher ¶
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 CompiledRetry ¶
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 ¶
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.
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.
type Feature ¶
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 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 ¶
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 ¶
MatchesError reports whether an error hook should fire for this failure. No on_errors means every failure matches.
func (Hook) Run ¶
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 ¶
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 (*Stack) Generate ¶
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 ¶
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
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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
RawInputNames is RawInputs' keys, sorted, for callers that only need the set.
func (*Unit) RawInputs ¶ added in v1.1.0
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"`
}