Documentation
¶
Overview ¶
Package configs contains types that represent OpenTofu configurations and the different elements thereof.
The functionality in this package can be used for some static analyses of OpenTofu configurations, but this package generally exposes representations of the configuration source code rather than the result of evaluating these objects. The sibling package "lang" deals with evaluation of structures and expressions in the configuration.
Due to its close relationship with HCL, this package makes frequent use of types from the HCL API, including raw HCL diagnostic messages. Such diagnostics can be converted into OpenTofu-flavored diagnostics, if needed, using functions in the sibling package tfdiags.
The Parser type is the main entry-point into this package. The LoadConfigDir method can be used to load a single module directory, and then a full configuration (including any descendent modules) can be produced using the top-level BuildConfig method.
Index ¶
- Constants
- Variables
- func IsCrossStackDataSource(typeName string) bool
- func IsEmptyDir(path string) (bool, error)
- func IsIgnoredFile(name string) bool
- func IsRemoteState(typeName string) bool
- func IsTfeOutputs(typeName string) bool
- func MergeBodies(base, override hcl.Body) hcl.Body
- func ModulesFromStringsForTesting(t testing.TB, srcs map[string]string) map[addrs.ModuleSourceLocal]*Module
- func ParseProviderConfigCompact(traversal hcl.Traversal) (addrs.LocalProviderConfig, tfdiags.Diagnostics)
- func ParseProviderConfigCompactStr(str string) (addrs.LocalProviderConfig, tfdiags.Diagnostics)
- func SynthBody(filename string, values map[string]cty.Value) hcl.Body
- type Backend
- type Check
- type CheckRule
- type CloudConfig
- type Config
- func (c *Config) AllModules() []*Config
- func (c *Config) DeepEach(cb func(c *Config))
- func (c *Config) Depth() int
- func (c *Config) Descendent(path addrs.Module) *Config
- func (c *Config) DescendentForInstance(path addrs.ModuleInstance) *Config
- func (c *Config) EntersNewPackage() bool
- func (c *Config) ProviderForConfigAddr(addr addrs.LocalProviderConfig) addrs.Provider
- func (c *Config) ProviderRequirements() (getproviders.Requirements, *getproviders.ProvidersQualification, ...)
- func (c *Config) ProviderRequirementsByModule() (*ModuleRequirements, hcl.Diagnostics)
- func (c *Config) ProviderRequirementsShallow() (getproviders.Requirements, hcl.Diagnostics)
- func (c *Config) ProviderTypes() []addrs.Provider
- func (c *Config) ResolveAbsProviderAddr(addr addrs.ProviderConfig, inModule addrs.Module) addrs.AbsProviderConfig
- func (c *Config) TransformForTest(run *TestRun, file *TestFile, evalCtx *hcl.EvalContext) (func(), hcl.Diagnostics)
- func (c *Config) VerifyDependencySelections(depLocks *depsfile.Locks) []error
- type Connection
- type Container
- type File
- type Import
- type Live
- type LivePolicy
- type LivePolicyScope
- type LiveRecordStore
- type LiveStrict
- type LiveStrictMarkers
- type Local
- type ManagedResource
- type MockProvider
- type MockResource
- type Module
- func ModuleFromStringForTesting(t testing.TB, src string) *Module
- func NewModule(primaryFiles, overrideFiles []*File, call StaticModuleCall, sourceDir string, ...) (*Module, hcl.Diagnostics)
- func NewModuleUneval(primaryFiles, overrideFiles []*File, sourceDir string, load SelectiveLoader) (*Module, hcl.Diagnostics)
- func NewModuleWithTests(primaryFiles, overrideFiles []*File, testFiles map[string]*TestFile, ...) (*Module, hcl.Diagnostics)
- func (m *Module) EphemeralVariablesHints() map[string]bool
- func (m *Module) GetProviderConfig(name, alias string) (*Provider, bool)
- func (m *Module) ImpliedProviderForUnqualifiedType(pType string) addrs.Provider
- func (m *Module) LocalNameForProvider(p addrs.Provider) string
- func (m *Module) ProviderForLocalConfig(pc addrs.LocalProviderConfig) addrs.Provider
- func (m *Module) ResourceByAddr(addr addrs.Resource) *Resource
- type ModuleCall
- type ModuleRequest
- type ModuleRequirements
- type ModuleWalker
- type ModuleWalkerFunc
- type Moved
- type Output
- type OverrideModule
- type OverrideResource
- type Parser
- func (p Parser) ConfigDirFiles(dir string) (primary, override []string, diags hcl.Diagnostics)
- func (p Parser) ConfigDirFilesWithTests(dir string, testDirectory string) (primary, override, tests []string, diags hcl.Diagnostics)
- func (p *Parser) ConfigFiles(dir string) (primary, override []string, diags hcl.Diagnostics)
- func (p *Parser) ForceFileSource(filename string, src []byte)
- func (p *Parser) IsConfigDir(path string) bool
- func (p *Parser) LoadConfigDir(path string, call StaticModuleCall) (*Module, hcl.Diagnostics)
- func (p *Parser) LoadConfigDirSelective(path string, call StaticModuleCall, load SelectiveLoader) (*Module, hcl.Diagnostics)
- func (p *Parser) LoadConfigDirUneval(path string, load SelectiveLoader) (*Module, hcl.Diagnostics)
- func (p *Parser) LoadConfigDirWithTests(path string, testDirectory string, call StaticModuleCall) (*Module, hcl.Diagnostics)
- func (p *Parser) LoadConfigFile(path string) (*File, hcl.Diagnostics)
- func (p *Parser) LoadConfigFileOverride(path string) (*File, hcl.Diagnostics)
- func (p *Parser) LoadHCLFile(path string) (hcl.Body, hcl.Diagnostics)
- func (p *Parser) LoadTestFile(path string) (*TestFile, hcl.Diagnostics)
- func (p *Parser) Sources() map[string]*hcl.File
- type PassedProviderConfig
- type Provider
- type ProviderConfigRef
- type ProviderMeta
- type Provisioner
- type ProvisionerOnFailure
- type ProvisionerWhen
- type ReferenceCategory
- type RefusedReference
- type Removed
- type RequiredProvider
- type RequiredProviders
- type Resource
- type SelectiveLoader
- type StaticDataLookup
- type StaticEvaluator
- func (s StaticEvaluator) DecodeBlock(ctx context.Context, body hcl.Body, spec hcldec.Spec, ident StaticIdentifier) (cty.Value, hcl.Diagnostics)
- func (s StaticEvaluator) DecodeExpression(ctx context.Context, expr hcl.Expression, ident StaticIdentifier, val any) hcl.Diagnostics
- func (s StaticEvaluator) EvalContext(ctx context.Context, ident StaticIdentifier, refs []*addrs.Reference) (*hcl.EvalContext, hcl.Diagnostics)
- func (s StaticEvaluator) EvalContextWithParent(ctx context.Context, parent *hcl.EvalContext, ident StaticIdentifier, ...) (*hcl.EvalContext, hcl.Diagnostics)
- func (s StaticEvaluator) Evaluate(ctx context.Context, expr hcl.Expression, ident StaticIdentifier) (cty.Value, hcl.Diagnostics)
- func (s StaticEvaluator) EvaluateStructural(ctx context.Context, expr hcl.Expression, ident StaticIdentifier) (cty.Value, hcl.Diagnostics)
- func (s *StaticEvaluator) Pure() *StaticEvaluator
- func (s *StaticEvaluator) WithDataResults(lookup StaticDataLookup) *StaticEvaluator
- func (s *StaticEvaluator) WithFunctionOverrides(overrides map[string]function.Function) *StaticEvaluator
- func (s *StaticEvaluator) WithModuleInstance(modInst addrs.ModuleInstance) *StaticEvaluator
- func (s *StaticEvaluator) WithModuleOutputResults(lookup StaticModuleOutputLookup) *StaticEvaluator
- func (s *StaticEvaluator) WithRepetitionData(rd instances.RepetitionData) *StaticEvaluator
- func (s *StaticEvaluator) WithUnknownForRefusedReferences(outputs StaticModuleOutputLookup) *StaticEvaluator
- func (s *StaticEvaluator) WithVariables(vars StaticModuleVariables) *StaticEvaluator
- type StaticIdentifier
- type StaticModuleCall
- type StaticModuleOutputLookup
- type StaticModuleVariables
- type TestCommand
- type TestFile
- type TestFileModuleRequirements
- type TestMode
- type TestRun
- type TestRunModuleCall
- type TestRunOptions
- type Variable
- type VariableParsingMode
- type VariableTypeHint
- type VersionConstraint
Constants ¶
const ( // ApplyTestCommand causes the run block to execute a OpenTofu apply // operation. ApplyTestCommand TestCommand = 0 // PlanTestCommand causes the run block to execute a OpenTofu plan // operation. PlanTestCommand TestCommand = 'P' // NormalTestMode causes the run block to execute in plans.NormalMode. NormalTestMode TestMode = 0 // RefreshOnlyTestMode causes the run block to execute in // plans.RefreshOnlyMode. RefreshOnlyTestMode TestMode = 'R' )
const (
DefaultTestDirectory = "tests"
)
const LiveSidecarFilename = "estate.chdf.hcl"
LiveSidecarFilename is the name of the sidecar configuration file that can carry a module's live configuration instead of a "live" block inside a "terraform" block. The file's whole body is the live block's content - "estate", "policy", "record_store" - with no wrapper block around it.
The sidecar exists for ecosystem compatibility (GitHub issue #72): strict HCL parsers - stock tofu and terraform validate, tflint, editors - are right to reject an unknown block inside terraform{}, so the in-.tf form taxes any repository whose CI runs stock tooling over the same files. The sidecar is a file those tools never read (the extension is deliberately not .tf or .tofu), so adopting live markers adds one file and changes zero existing lines. It is still a checked-in, reviewed file and not a flag, which is the property Live's doc comment explains the mode depends on.
A module may use either form; both at once is refused in Module.appendFile, because a live configuration must have one source of truth.
Variables ¶
var ResourceBlockSchema = &hcl.BodySchema{ Attributes: commonResourceAttributes, Blocks: []hcl.BlockHeaderSchema{ {Type: "locals"}, {Type: "lifecycle"}, {Type: "connection"}, {Type: "provisioner", LabelNames: []string{"type"}}, {Type: "_"}, }, }
ResourceBlockSchema is the schema for a resource or data resource type within OpenTofu.
This schema is public as it is required elsewhere in order to validate and use generated config.
Functions ¶
func IsCrossStackDataSource ¶ added in v0.3.0
IsCrossStackDataSource reports whether typeName is one of the data source types that read another stack's recorded outputs rather than a live cloud resource - the union of IsTfeOutputs and IsRemoteState. Exported because the data-read phase draws its coverage line exactly here: a same-stack data source is an ordering problem the phase solves, a cross-stack one carries its own auth surface and failure modes and stays refused until its own stage ships (issue #179).
func IsEmptyDir ¶
IsEmptyDir returns true if the given filesystem path contains no OpenTofu configuration files.
Unlike the methods of the Parser type, this function always consults the real filesystem, and thus it isn't appropriate to use when working with configuration loaded from a plan file.
func IsIgnoredFile ¶
IsIgnoredFile returns true if the given filename (which must not have a directory path ahead of it) should be ignored as e.g. an editor swap file.
func IsRemoteState ¶ added in v0.3.0
IsRemoteState reports whether typeName is the builtin terraform_remote_state data source. It stays refused byte-for-byte until #179's stage 3 gives it its own read pipeline.
func IsTfeOutputs ¶ added in v0.3.0
IsTfeOutputs reports whether typeName is the tfe provider's cross-stack output reader. Stage 2 of #179 gives it its own read pipeline; see IsRemoteState for the flavor that stays refused until stage 3.
func MergeBodies ¶
These were all moved to the hcl2shim package, but still have uses referenced from this package TODO Call sites through opentofu to these functions should be migrated to hcl2shim eventually and this file removed
func ModulesFromStringsForTesting ¶
func ModulesFromStringsForTesting(t testing.TB, srcs map[string]string) map[addrs.ModuleSourceLocal]*Module
ModulesFromStringsForTesting calls ModuleFromStringForTesting for each element of the given map and then treats the map keys as local module source addresses to construct a map from source address to module.
As with ModuleFromStringForTesting, if any of the given configuration strings are invalid then this halts testing by calling testing.TB.FailNow, and experiments are always allowed. The map keys must also be valid local module source addresses.
func ParseProviderConfigCompact ¶
func ParseProviderConfigCompact(traversal hcl.Traversal) (addrs.LocalProviderConfig, tfdiags.Diagnostics)
ParseProviderConfigCompact parses the given absolute traversal as a relative provider address in compact form. The following are examples of traversals that can be successfully parsed as compact relative provider configuration addresses:
- aws
- aws.foo
This function will panic if given a relative traversal.
If the returned diagnostics contains errors then the result value is invalid and must not be used.
func ParseProviderConfigCompactStr ¶
func ParseProviderConfigCompactStr(str string) (addrs.LocalProviderConfig, tfdiags.Diagnostics)
ParseProviderConfigCompactStr is a helper wrapper around ParseProviderConfigCompact that takes a string and parses it with the HCL native syntax traversal parser before interpreting it.
This should be used only in specialized situations since it will cause the created references to not have any meaningful source location information. If a reference string is coming from a source that should be identified in error messages then the caller should instead parse it directly using a suitable function from the HCL API and pass the traversal itself to ParseProviderConfigCompact.
Error diagnostics are returned if either the parsing fails or the analysis of the traversal fails. There is no way for the caller to distinguish the two kinds of diagnostics programmatically. If error diagnostics are returned then the returned address is invalid.
Types ¶
type Backend ¶
type Backend struct {
Type string
Config hcl.Body
Eval *StaticEvaluator
TypeRange hcl.Range
DeclRange hcl.Range
}
Backend represents a "backend" block inside a "terraform" block in a module or file.
func (*Backend) Decode ¶
func (b *Backend) Decode(ctx context.Context, schema *configschema.Block) (cty.Value, hcl.Diagnostics)
func (*Backend) Hash ¶
func (b *Backend) Hash(ctx context.Context, schema *configschema.Block) (int, hcl.Diagnostics)
Hash produces a hash value for the receiver that covers the type and the portions of the config that conform to the given schema.
If the config does not conform to the schema then the result is not meaningful for comparison since it will be based on an incomplete result.
As an exception, required attributes in the schema are treated as optional for the purpose of hashing, so that an incomplete configuration can still be hashed. Other errors, such as extraneous attributes, have no such special case.
type Check ¶
Check represents a configuration defined check block.
A check block contains 0-1 data blocks, and 0-n assert blocks. The check block will load the data block, and execute the assert blocks as check rules during the plan and apply OpenTofu operations.
func (Check) Accessible ¶
func (c Check) Accessible(addr addrs.Referenceable) bool
type CheckRule ¶
type CheckRule struct {
// Condition is an expression that must evaluate to true if the condition
// holds or false if it does not. If the expression produces an error then
// that's considered to be a bug in the module defining the check.
//
// The available variables in a condition expression vary depending on what
// a check is attached to. For example, validation rules attached to
// input variables can only refer to the variable that is being validated.
Condition hcl.Expression
// ErrorMessage should be one or more full sentences, which should be in
// English for consistency with the rest of the error message output but
// can in practice be in any language. The message should describe what is
// required for the condition to return true in a way that would make sense
// to a caller of the module.
//
// The error message expression has the same variables available for
// interpolation as the corresponding condition.
ErrorMessage hcl.Expression
DeclRange hcl.Range
}
CheckRule represents a configuration-defined validation rule, precondition, or postcondition. Blocks of this sort can appear in a few different places in configuration, including "validation" blocks for variables, and "precondition" and "postcondition" blocks for resources.
type CloudConfig ¶
type CloudConfig struct {
Config hcl.Body
DeclRange hcl.Range
// contains filtered or unexported fields
}
Cloud represents a "cloud" block inside a "terraform" block in a module or file.
func (*CloudConfig) ToBackendConfig ¶
func (c *CloudConfig) ToBackendConfig() Backend
type Config ¶
type Config struct {
// RootModule points to the Config for the root module within the same
// module tree as this module. If this module _is_ the root module then
// this is self-referential.
Root *Config
// ParentModule points to the Config for the module that directly calls
// this module. If this is the root module then this field is nil.
Parent *Config
// Path is a sequence of module logical names that traverse from the root
// module to this config. Path is empty for the root module.
//
// This should only be used to display paths to the end-user in rare cases
// where we are talking about the static module tree, before module calls
// have been resolved. In most cases, an addrs.ModuleInstance describing
// a node in the dynamic module tree is better, since it will then include
// any keys resulting from evaluating "count" and "for_each" arguments.
Path addrs.Module
// ChildModules points to the Config for each of the direct child modules
// called from this module. The keys in this map match the keys in
// Module.ModuleCalls.
Children map[string]*Config
// Module points to the object describing the configuration for the
// various elements (variables, resources, etc) defined by this module.
Module *Module
// CallRange is the source range for the header of the module block that
// requested this module.
//
// This field is meaningless for the root module, where its contents are undefined.
CallRange hcl.Range
// SourceAddr is the source address that the referenced module was requested
// from, as specified in configuration. SourceAddrRaw is the same
// information, but as the raw string the user originally entered.
//
// These fields are meaningless for the root module, where their contents are undefined.
SourceAddr addrs.ModuleSource
SourceAddrRaw string
// SourceAddrRange is the location in the configuration source where the
// SourceAddr value was set, for use in diagnostic messages.
//
// This field is meaningless for the root module, where its contents are undefined.
SourceAddrRange hcl.Range
// Version is the specific version that was selected for this module,
// based on version constraints given in configuration.
//
// This field is nil if the module was loaded from a non-registry source,
// since versions are not supported for other sources.
//
// This field is meaningless for the root module, where it will always
// be nil.
Version *version.Version
}
A Config is a node in the tree of modules within a configuration.
The module tree is constructed by following ModuleCall instances recursively through the root module transitively into descendent modules.
A module tree described in *this* package represents the static tree represented by configuration. During evaluation a static ModuleNode may expand into zero or more module instances depending on the use of count and for_each configuration attributes within each call.
func BuildConfig ¶
func BuildConfig(ctx context.Context, root *Module, walker ModuleWalker) (*Config, hcl.Diagnostics)
BuildConfig constructs a Config from a root module by loading all of its descendent modules via the given ModuleWalker.
The result is a module tree that has so far only had basic module- and file-level invariants validated. If the returned diagnostics contains errors, the returned module tree may be incomplete but can still be used carefully for static analysis.
func NewEmptyConfig ¶
func NewEmptyConfig() *Config
NewEmptyConfig constructs a single-node configuration tree with an empty root module. This is generally a pretty useless thing to do, so most callers should instead use BuildConfig.
func (*Config) AllModules ¶
AllModules returns a slice of all the receiver and all of its descendent nodes in the module tree, in the same order they would be visited by DeepEach.
func (*Config) DeepEach ¶
DeepEach calls the given function once for each module in the tree, starting with the receiver.
A parent is always called before its children and children of a particular node are visited in lexicographic order by their names.
func (*Config) Depth ¶
Depth returns the number of "hops" the receiver is from the root of its module tree, with the root module having a depth of zero.
func (*Config) Descendent ¶
Descendent returns the descendent config that has the given path beneath the receiver, or nil if there is no such module.
The path traverses the static module tree, prior to any expansion to handle count and for_each arguments.
An empty path will just return the receiver, and is therefore pointless.
func (*Config) DescendentForInstance ¶
func (c *Config) DescendentForInstance(path addrs.ModuleInstance) *Config
DescendentForInstance is like Descendent except that it accepts a path to a particular module instance in the dynamic module graph, returning the node from the static module graph that corresponds to it.
All instances created by a particular module call share the same configuration, so the keys within the given path are disregarded.
func (*Config) EntersNewPackage ¶
EntersNewPackage returns true if this call is to an external module, either directly via a remote source address or indirectly via a registry source address.
Other behaviors in OpenTofu may treat package crossings as a special situation, because that indicates that the caller and callee can change independently of one another and thus we should disallow using any features where the caller assumes anything about the callee other than its input variables, required provider configurations, and output values.
It's not meaningful to ask if the Config representing the root module enters a new package because the root module is always outside of all module packages, and so this function will arbitrarily return false in that case.
func (*Config) ProviderForConfigAddr ¶
func (c *Config) ProviderForConfigAddr(addr addrs.LocalProviderConfig) addrs.Provider
ProviderForConfigAddr returns the FQN for a given addrs.ProviderConfig, first by checking for the provider in module.ProviderRequirements and falling back to addrs.NewDefaultProvider if it is not found.
func (*Config) ProviderRequirements ¶
func (c *Config) ProviderRequirements() (getproviders.Requirements, *getproviders.ProvidersQualification, hcl.Diagnostics)
ProviderRequirements searches the full tree of modules under the receiver for both explicit and implicit dependencies on providers.
The result is a full manifest of all of the providers that must be available in order to work with the receiving configuration.
If the returned diagnostics includes errors then the resulting Requirements may be incomplete.
func (*Config) ProviderRequirementsByModule ¶
func (c *Config) ProviderRequirementsByModule() (*ModuleRequirements, hcl.Diagnostics)
ProviderRequirementsByModule searches the full tree of modules under the receiver for both explicit and implicit dependencies on providers, constructing a tree where the requirements are broken out by module.
If the returned diagnostics includes errors then the resulting Requirements may be incomplete.
func (*Config) ProviderRequirementsShallow ¶
func (c *Config) ProviderRequirementsShallow() (getproviders.Requirements, hcl.Diagnostics)
ProviderRequirementsShallow searches only the direct receiver for explicit and implicit dependencies on providers. Descendant modules are ignored.
If the returned diagnostics includes errors then the resulting Requirements may be incomplete.
func (*Config) ProviderTypes ¶
ProviderTypes returns the FQNs of each distinct provider type referenced in the receiving configuration.
This is a helper for easily determining which provider types are required to fully interpret the configuration, though it does not include version information and so callers are expected to have already dealt with provider version selection in an earlier step and have identified suitable versions for each provider.
func (*Config) ResolveAbsProviderAddr ¶
func (c *Config) ResolveAbsProviderAddr(addr addrs.ProviderConfig, inModule addrs.Module) addrs.AbsProviderConfig
ResolveAbsProviderAddr returns the AbsProviderConfig represented by the given ProviderConfig address, which must not be nil or this method will panic.
If the given address is already an AbsProviderConfig then this method returns it verbatim, and will always succeed. If it's a LocalProviderConfig then it will consult the local-to-FQN mapping table for the given module to find the absolute address corresponding to the given local one.
The module address to resolve local addresses in must be given in the second argument, and must refer to a module that exists under the receiver or else this method will panic.
func (*Config) TransformForTest ¶
func (c *Config) TransformForTest(run *TestRun, file *TestFile, evalCtx *hcl.EvalContext) (func(), hcl.Diagnostics)
TransformForTest prepares the config to execute the given test.
This function directly edits the config that is to be tested, and returns a function that will reset the config back to its original state.
Tests will call this before they execute, and then call the deferred function to reset the config before the next test.
func (*Config) VerifyDependencySelections ¶
VerifyDependencySelections checks whether the given locked dependencies are acceptable for all of the version constraints reported in the configuration tree represented by the receiver.
This function will errors only if any of the locked dependencies are out of range for corresponding constraints in the configuration. If there are multiple inconsistencies then it will attempt to describe as many of them as possible, rather than stopping at the first problem.
It's typically the responsibility of "tofu init" to change the locked dependencies to conform with the configuration, and so VerifyDependencySelections is intended for other commands to check whether it did so correctly and to catch if anything has changed in configuration since the last "tofu init" which requires re-initialization. However, it's up to the caller to decide how to advise users recover from these errors, because the advise can vary depending on what operation the user is attempting.
type Connection ¶
Connection represents a "connection" block when used within either a "resource" or "provisioner" block in a module or file.
type Container ¶
type Container interface {
// Accessible should return true if the resource specified by addr can
// reference other items within this Container.
//
// Typically, that means that addr will either be the container itself or
// something within the container.
Accessible(addr addrs.Referenceable) bool
}
Container provides an interface for scoped resources.
Any resources contained within a Container should not be accessible from outside the container.
type File ¶
type File struct {
Backends []*Backend
CloudConfigs []*CloudConfig
Lives []*Live
ProviderConfigs []*Provider
ProviderMetas []*ProviderMeta
RequiredProviders []*RequiredProviders
Encryptions []*config.EncryptionConfig
Variables []*Variable
Locals []*Local
Outputs []*Output
ModuleCalls []*ModuleCall
ManagedResources []*Resource
DataResources []*Resource
EphemeralResources []*Resource
Moved []*Moved
Import []*Import
Removed []*Removed
Checks []*Check
}
File describes the contents of a single configuration file.
Individual files are not usually used alone, but rather combined together with other files (conventionally, those in the same directory) to produce a *Module, using NewModule.
At the level of an individual file we represent directly the structural elements present in the file, without any attempt to detect conflicting declarations. A File object can therefore be used for some basic static analysis of individual elements, but must be built into a Module to detect duplicate declarations.
type Import ¶
type Import struct {
ID hcl.Expression
// Identity is an alternative to ID, which is used to identify the resource instance
// by its 'Resource Identity'.
Identity hcl.Expression
// To is the address HCL expression given in the `import` block configuration.
// It supports the following address formats:
// - aws_s3_bucket.my_bucket
// - module.my_module.aws_s3_bucket.my_bucket
// - aws_s3_bucket.my_bucket["static_key"]
// - module.my_module[0].aws_s3_bucket.my_buckets["static_key"]
// - aws_s3_bucket.my_bucket[expression]
// - module.my_module[expression].aws_s3_bucket.my_buckets[expression]
// A dynamic instance key supports a dynamic expression like - a variable, a local, a condition (for example,
// ternary), a resource block attribute, a data block attribute, etc.
To hcl.Expression
// StaticTo is the corresponding resource and module that the address is referring to. When decoding, as long
// as the `to` field is in the accepted format, we could determine the actual modules and resource that the
// address represents. However, we do not yet know for certain what module instance and resource instance this
// address refers to. So, Static import is mainly used to figure out the Module and Resource, and Provider of the
// import target resource
// If we could not determine the StaticTo when decoding the block, then the address is in an unacceptable format
StaticTo addrs.ConfigResource
// ResolvedTo will be a reference to the resource instance of the import target, if it can be resolved when decoding
// the `import` block. If the `to` field does not represent a static address
// (for example: module.my_module[var.var1].aws_s3_bucket.bucket), then this will be nil.
// However, if the address is static and can be fully resolved at decode time
// (for example: module.my_module[2].aws_s3_bucket.bucket), then this will be a reference to the resource instance's
// address
// Mainly used for early validations on the import block address, for example making sure there are no duplicate
// import blocks targeting the same resource
ResolvedTo *addrs.AbsResourceInstance
ForEach hcl.Expression
ProviderConfigRef *ProviderConfigRef
Provider addrs.Provider
DeclRange hcl.Range
ProviderDeclRange hcl.Range
}
type Live ¶
type Live struct {
// Estate is the name of the estate this configuration owns, as it appears
// in the tofu-estate marker described by live/MARKERS.md. It is
// optional: when it is not set, the estate name is derived from the
// tofu-estate tags the configuration already stamps, and a configuration
// that stamps none (or several) is told to name one here.
//
// The value must be a literal string. An estate name assembled from a
// variable would mean the identity of the thing that owns live resources
// depends on how a run was invoked, and ownership records that move with
// the invocation are not ownership records.
Estate string
// EstateSet distinguishes an absent estate argument from one set to the
// empty string, so that the second can be an error rather than silently
// meaning "derive it".
EstateSet bool
// EstateRange is where the "estate" argument was written, the zero value
// when the block does not set it. It is recorded so that a diagnostic
// about the estate can point at the argument that named it rather than at
// the whole block, which is what DeclRange gives.
EstateRange hcl.Range
// DeclRange is the "live" block's own header, which is what a diagnostic
// about the block as a whole points at - a backend beside it, or a
// stateless refusal that has no more specific argument to name. For a
// live configuration read from the sidecar file it is the start of that
// file, so the same diagnostics point at the file instead.
DeclRange hcl.Range
// Sidecar records that this live configuration was read from the
// [LiveSidecarFilename] sidecar file rather than from a live block inside
// a terraform block. The two sources are equivalent everywhere else; the
// field exists so that a module carrying both can be told exactly which
// two places disagree, rather than getting the duplicate-block error
// meant for two live blocks in .tf files.
Sidecar bool
// Policy is the optional nested "policy" block: one verb per ownership
// quadrant (declared-in-source x carries-the-tag), plus the tag those
// quadrants read and the delete quadrant's safety rails. Nil when the
// live block sets no policy block at all, which must mean today's fixed
// behavior (issue #67's "existing estates change nothing").
//
// This struct is the raw decode only - the literal string an author
// wrote for each attribute, or its zero value with the matching *Set
// flag false when they left it out. It deliberately knows nothing about
// which verbs are valid for which quadrant: that is
// internal/live/policy's ValidVerbs matrix, enforced at lint time by
// internal/live/lint, the same layering every other semantic rule about
// this fork's configuration follows (compare Estate above, whose own
// grammar is checked by internal/live/discovery.ValidEstateName rather
// than here).
Policy *LivePolicy
// RecordStore is the nested "record_store" block: where GitHub issue
// #73's record-backed logical types (null_resource, terraform_data,
// time_*, non-sensitive random_*) persist their micro-state, and where
// issue #364's per-instance record will live.
//
// It is NEVER nil for a decoded live configuration. A live block that
// declares no record_store block gets the implied local one
// ([impliedRecordStore]): HANDOFF.md's "compatible out of the box" says
// "a local record store is implied when none is declared, the way stock
// implies local state", so declaring an estate is the whole setup step
// and the record rung is available to every estate from its first run.
// [LiveRecordStore.Implied] tells the two apart for a diagnostic that
// needs to point at something the author actually wrote.
//
// A configuration with no live block at all still has no record store,
// because a nil *Live has no fields: there is no estate, so there is
// nothing to imply a store for. That is the case every reader must keep
// treating as "no record store", and it is the only one left.
//
// See [LiveRecordStore].
RecordStore *LiveRecordStore
// Strict is the optional nested "strict" block: GitHub issue #365's
// profile toggles, each one of the principles this fork exists for
// turned into a setting whose default is today's behavior. Nil when the
// live block sets no strict block at all, which must mean exactly what
// a configuration written before the block existed got - the same
// "absent means absent" contract Policy and RecordStore already follow,
// and the thing that makes HANDOFF.md's "compatible out of the box"
// true by construction rather than by review.
//
// Like [Live.Policy], this is the raw decode only: the literal string
// an author wrote, with no opinion on whether it means anything. That
// judgement needs internal/live/strict's vocabulary and belongs to
// internal/live/lint.
Strict *LiveStrict
}
Live represents a module's live configuration: a "live" block inside a "terraform" block, or the LiveSidecarFilename sidecar file, whose whole body is the same content the block would carry. Its presence is what puts a run into stateless mode: no state file, no backend, no lock, with prior state rebuilt from the live system on every operation.
It is deliberately a configuration block and not a command-line flag. Whether a team's infrastructure has an authoritative state file is a property of the configuration, checked in and reviewed with it; a flag would mean a run that forgot it silently fell back to writing a state file, which is exactly the failure this mode exists to remove.
The block is also why a stateless module cannot have a backend: a "backend" or "cloud" block alongside it is refused here, in the configuration decoder, rather than only by the stateless subset lint. The decoder is the earlier wall, and it is the one every command passes through - including the ones that would otherwise reach for a state manager before any stateless code ran.
type LivePolicy ¶ added in v0.3.0
type LivePolicy struct {
// DeclaredTagged, DeclaredUntagged, UndeclaredTagged and
// UndeclaredUntagged are the four quadrant verbs, named for their
// policy-block attribute: "declared_tagged" and so on. Each is the
// literal string an author wrote - "converge", "delete", or whatever
// they typed, valid or not; a typo is caught by
// internal/live/lint.checkLivePolicy, not here, because deciding
// validity needs the per-quadrant matrix and this package does not
// depend on it. The matching *Set field distinguishes an omitted
// attribute (which resolves to today's fixed behavior for that
// quadrant) from one written out.
DeclaredTagged string
DeclaredTaggedSet bool
DeclaredTaggedRange hcl.Range
DeclaredUntagged string
DeclaredUntaggedSet bool
DeclaredUntaggedRange hcl.Range
UndeclaredTagged string
UndeclaredTaggedSet bool
UndeclaredTaggedRange hcl.Range
UndeclaredUntagged string
UndeclaredUntaggedSet bool
UndeclaredUntaggedRange hcl.Range
// TagKey and TagValue name the tag every quadrant's "tagged" half is
// read against. Both are optional; omitted, they default to the estate
// marker (internal/live/policy.Build fills in markers.TagEstate and
// this estate's name), which is the reading every quadrant had before
// this block existed. A configuration sets them to use a preservation
// tag distinct from the estate marker instead - issue #67's "one
// semantic question for the maintainer to confirm".
TagKey string
TagKeySet bool
TagKeyRange hcl.Range
TagValue string
TagValueSet bool
TagValueRange hcl.Range
// Scope narrows a delete-quadrant verb's reach. Nil when the policy
// block sets no scope block. internal/live/lint refuses a quadrant
// explicitly assigned "delete" with no scope block at all - "an
// unscoped account-wide purge is a lint refusal, not a default" per
// issue #67 - so a Policy that later carries a delete verb and a nil
// Scope can only happen if that check was skipped.
Scope *LivePolicyScope
// Threshold is the delete quadrant's first-run guard: a policy whose
// delete quadrant would touch more resources than this refuses to
// apply. Parsed and validated as a non-negative literal integer here;
// enforcing it against an actual roster is the behavioral half's job
// (issue #67, "First-run protection"), not this package's.
Threshold int
ThresholdSet bool
ThresholdRange hcl.Range
// DeclRange is the "policy" block's own header.
DeclRange hcl.Range
}
LivePolicy is the "policy" block nested inside a live block. See Live.Policy.
type LivePolicyScope ¶ added in v0.3.0
type LivePolicyScope struct {
// Services, Types and Regions each narrow what a delete-quadrant verb
// may reach: provider service namespaces, resource type names, and
// regions, respectively. Every one is optional and any combination may
// be set; an empty list is indistinguishable from an absent attribute,
// because in both cases nothing was named to narrow by, so
// internal/live/lint requires at least one of the three to be
// non-empty in a scope block that accompanies a delete quadrant.
Services []string
Types []string
Regions []string
// DeclRange is the "scope" block's own header.
DeclRange hcl.Range
}
LivePolicyScope is the "scope" block nested inside a policy block. See LivePolicy.Scope.
type LiveRecordStore ¶ added in v0.3.0
type LiveRecordStore struct {
// Type is the block's label: "local", "ssm", or "s3". Validated against
// exactly those three spellings in decodeRecordStoreBlock; nothing else
// reaches this field.
Type string
TypeRange hcl.Range
// Path is the "local" backend's directory, relative to the module
// directory. Optional; empty means the caller's own default (a
// ".tofu-records" directory beside the module, mirroring plain local
// state's default filename). Literal string, relative, and confined to
// the module directory: see validateRecordStorePath.
Path string
PathSet bool
PathRange hcl.Range
// Bucket is the "s3" backend's bucket name. Required for that backend;
// unused by the other two.
Bucket string
BucketSet bool
BucketRange hcl.Range
// KeyPrefix overrides the "ssm" and "s3" backends' default key
// namespace, which the caller derives from the estate name. Optional.
// When set, it must stay disjoint from the receipts namespace
// (live/RECEIPTS.md's "/tofu-receipts/<estate>/<effect>"): a key_prefix
// whose first "/"-delimited segment is literally "tofu-receipts" is a
// decode error, so a record can never be written where a receipt's own
// namespace lives. See validateRecordStoreKeyPrefix.
KeyPrefix string
KeyPrefixSet bool
KeyPrefixRange hcl.Range
// Region overrides the "ssm" and "s3" backends' AWS region. Optional;
// empty defers to the ordinary AWS SDK default-config chain (environment,
// shared config, IMDS), the same as every other AWS client this fork
// builds when a caller names no region.
Region string
RegionSet bool
RegionRange hcl.Range
// DeclRange is the "record_store" block's own header, or - for the
// implied store - the live block's own header, since that is the
// nearest thing the author wrote.
DeclRange hcl.Range
// Implied is true for the store [impliedRecordStore] fills in when the
// live block declares no record_store block of its own.
//
// Nothing about where a record GOES may branch on this. An implied
// local store and one written out as `record_store "local" {}` are the
// same store, holding the same records under the same keys in the same
// directory, and internal/live/lint's implied_record_store_test.go
// asserts that by value. That is the whole point of implying it: a
// reader deciding differently would make the default a third behavior
// rather than the default one.
//
// What it is for is the question "did the author ASK for a store", which
// is a different question and has exactly two readers today:
//
// - internal/live/lint's strict-markers check, which refuses
// `markers "record"` without a declared store. That block is an
// author giving UP an available marker for a record, and naming
// where the record goes stays part of turning it on.
// - internal/command's statelessApplyGuidedDiscovery, which leaves
// guided discovery - an opt-in cost optimization that was reached
// by declaring a store - opt-in.
//
// It is also what a diagnostic reads to say "your live block has the
// implied local record store" rather than pointing at a block the author
// never wrote, and what a test reads to assert which of the two it got.
Implied bool
}
LiveRecordStore is the "record_store" block nested inside a live block. Its label picks the backend ("local", "ssm", or "s3"), the same labeled-block-names-the-implementation shape a stock "backend" block uses, per issue #73's "phrased in familiar backend-like terms" ruling. See Live.RecordStore.
type LiveStrict ¶ added in v0.3.0
type LiveStrict struct {
// MarkerRepair is the literal string an author wrote for the
// "marker_repair" argument - "repair", "never", or whatever they typed,
// valid or not. Validity is internal/live/strict's vocabulary, checked
// at lint time by internal/live/lint.checkLiveStrict, for the same
// reason [LivePolicy]'s quadrant verbs are checked there: this package
// does not depend on that one.
//
// MarkerRepairSet distinguishes an omitted argument, which resolves to
// internal/live/strict.DefaultMarkerRepair and therefore to today's
// behavior, from one written out.
MarkerRepair string
MarkerRepairSet bool
MarkerRepairRange hcl.Range
// Secrets is the literal string an author wrote for the "secrets"
// argument - "store", "refuse", or whatever they typed, valid or not.
// Read the same way [LiveStrict.MarkerRepair] is, by the same decoder,
// and judged the same place: internal/live/strict says what the
// spellings mean and internal/live/lint refuses the ones that mean
// nothing.
//
// SecretsSet distinguishes an omitted argument, which resolves to
// internal/live/strict.DefaultSecrets, from one written out. The two
// must behave identically for the default spelling - GitHub issue #101's
// standing lesson - and the flag exists so that a reader can tell the
// difference for a diagnostic's Subject range without changing the
// verdict.
Secrets string
SecretsSet bool
SecretsRange hcl.Range
// NoSourceCreate is the literal string an author wrote for the
// "no_source_create" argument - "refuse", "create", or whatever they
// typed, valid or not. GitHub issue #365's ruling-4 toggle
// (rfc/20260823-foundation-order-ruling.md): a no-source instance (no
// record, no marker, and an identity nothing - neither the static
// evaluator nor #388's plan-node seam - can derive) refuses by default;
// this selects stock's own behavior of planning a create instead. Read
// the same way [LiveStrict.Secrets] is, by the same decoder;
// internal/live/strict says what the spellings mean.
//
// NoSourceCreateSet distinguishes an omitted argument, which resolves
// to internal/live/strict.DefaultNoSourceCreate, from one written out,
// the same reason [LiveStrict.SecretsSet] exists.
NoSourceCreate string
NoSourceCreateSet bool
NoSourceCreateRange hcl.Range
// MarkersRecord is the optional nested `markers "record"` block: which
// resources hold their identity in the estate's record store instead of
// in an ownership marker tag, HANDOFF.md's "per-type or per-address
// markers = record, for tag budgets and tag policies, trading IAM
// governability for a record-held identity". Nil when the strict block
// declares no such block, which must mean today's behavior - every
// taggable resource is marked.
//
// It is a LABELED block rather than an attribute because HANDOFF's
// phrasing is shorthand for what is really a selection with two lists,
// and because the label leaves room for the inverse selection
// (`markers "tag"`) without a grammar change.
//
// Like every other field here this is the raw decode: two literal lists
// of strings, with no opinion on whether the type names exist or the
// addresses parse. That judgement needs internal/addrs' target grammar
// and the provider's schemas, and belongs to internal/live/lint - the
// same layering [Live.Estate] already has, whose grammar is checked by
// internal/live/discovery.ValidEstateName rather than here.
MarkersRecord *LiveStrictMarkers
// DeclRange is the "strict" block's own header.
DeclRange hcl.Range
}
LiveStrict is the "strict" block nested inside a live block. See Live.Strict.
type LiveStrictMarkers ¶ added in v0.3.0
type LiveStrictMarkers struct {
// Kind is the block's label. "record" is the only one this fork knows
// today; decodeStrictBlock refuses anything else, so nothing else
// reaches this field.
Kind string
KindRange hcl.Range
// Types names resource types whose every instance this selection
// covers, in the same literal-list-of-strings shape
// [LivePolicyScope.Types] uses and read by the same decode helper.
Types []string
TypesSet bool
TypesRange hcl.Range
// Addresses names individual resources this selection covers, in the
// `-target` grammar (internal/addrs' ParseTargetStr): module-qualified
// or not, whole-resource, with no wildcards. Parsed by
// internal/live/lint rather than here.
Addresses []string
AddressesSet bool
AddressesRange hcl.Range
// DeclRange is the "markers" block's own header.
DeclRange hcl.Range
}
LiveStrictMarkers is a `markers "<kind>"` block nested inside a strict block. See LiveStrict.MarkersRecord.
type Local ¶
type Local struct {
Name string
Expr hcl.Expression
DeclRange hcl.Range
}
Local represents a single entry from a "locals" block in a module or file. The "locals" block itself is not represented, because it serves only to provide context for us to interpret its contents.
func (*Local) Addr ¶
func (l *Local) Addr() addrs.LocalValue
Addr returns the address of the local value declared by the receiver, relative to its containing module.
type ManagedResource ¶
type ManagedResource struct {
Connection *Connection
Provisioners []*Provisioner
CreateBeforeDestroy bool
PreventDestroy hcl.Expression
// Destroy attribute indicates if the resource should be destroy once it is planned for destruction. This attribute corresponds to the `lifecycle.destroy` attribute.
// The default behavior is to destroy the resource when it is planned for destruction, so the value of false will skip destroying the resource.
// Note that the resource will still be removed from the state file even if Destroy is set to false but won't call the underlying provider for destruction.
// This field will accept only constant boolean expressions. This is of type hcl.Expression to make future extensions of dynamic evaluation easier.
Destroy hcl.Expression
IgnoreChanges []hcl.Traversal
IgnoreAllChanges bool
CreateBeforeDestroySet bool
}
ManagedResource represents a "resource" block in a module or file.
type MockProvider ¶
type MockProvider struct {
Name string
NameRange hcl.Range
Alias string
AliasRange *hcl.Range // nil if no alias set
DeclRange hcl.Range
ForEach hcl.Expression
Instances map[addrs.InstanceKey]instances.RepetitionData
MockResources []*MockResource
OverrideResources []*OverrideResource
}
MockProvider represents mocked provider block. It partially matches the Provider configuration block (name, alias) and includes additional mocking data (mock resources).
type MockResource ¶
MockResource represents mocked resource. It is similar to OverrideResource, except all the resources with the same type should be overridden (mocked).
type Module ¶
type Module struct {
// Any other caller that constructs a module directly with NewModule may
// assign a suitable value to this attribute before using it for other
// purposes. It should be treated as immutable by all consumers of Module
// values.
SourceDir string
Backend *Backend
CloudConfig *CloudConfig
Live *Live
ProviderConfigs map[string]*Provider
ProviderRequirements *RequiredProviders
ProviderLocalNames map[addrs.Provider]string
ProviderMetas map[addrs.Provider]*ProviderMeta
Encryption *config.EncryptionConfig
Variables map[string]*Variable
Locals map[string]*Local
Outputs map[string]*Output
ModuleCalls map[string]*ModuleCall
ManagedResources map[string]*Resource
DataResources map[string]*Resource
EphemeralResources map[string]*Resource
Moved []*Moved
Import []*Import
Removed []*Removed
Checks map[string]*Check
Tests map[string]*TestFile
// IsOverridden indicates if the module is being overridden. It's used in
// testing framework to not call the underlying module.
IsOverridden bool
// StaticEvaluator is used to evaluate static expressions in the scope of the Module.
StaticEvaluator *StaticEvaluator
// ActiveExperiments is not currently used and so is always nil, but is
// reserved to be a place to capture a module's active experiments if we
// begin using language experiments in a later release.
ActiveExperiments experiments.Set
}
Module is a container for a set of configuration constructs that are evaluated within a common namespace.
func ModuleFromStringForTesting ¶
ModuleFromStringForTesting interprets the given string as if it were the content of a ".tofu" file in a module directory, parsing and decoding it as a single-file module.
Note that THIS FUNCTION DOES NOT PERFORM EARLY EVALUATION. This is intended mainly for an experimental new config evaluation strategy where early evaluation and config tree assembly are handled outside of this package.
If the configuration is not valid then this halts testing by calling testing.TB.FailNow.
Language experiments are always allowed in the "modules" loaded by this function.
func NewModule ¶
func NewModule(primaryFiles, overrideFiles []*File, call StaticModuleCall, sourceDir string, load SelectiveLoader) (*Module, hcl.Diagnostics)
NewModule takes a list of primary files and a list of override files and produces a *Module by combining the files together.
If there are any conflicting declarations in the given files -- for example, if the same variable name is defined twice -- then the resulting module will be incomplete and error diagnostics will be returned. Careful static analysis of the returned Module is still possible in this case, but the module will probably not be semantically valid.
func NewModuleUneval ¶
func NewModuleUneval(primaryFiles, overrideFiles []*File, sourceDir string, load SelectiveLoader) (*Module, hcl.Diagnostics)
NewModuleUneval is a variation of NewModule which performs only the static decoding steps and stops before performing any of the "early eval" steps, instead just returning with the results of early eval unpopulated.
This is currently here only in support of the experiment in internal/lang/eval, which wants to handle the situations where we currently rely on early eval in a different way. Outside of that experiment we should keep using NewModule in its entirety for now.
func NewModuleWithTests ¶
func NewModuleWithTests(primaryFiles, overrideFiles []*File, testFiles map[string]*TestFile, call StaticModuleCall, sourceDir string) (*Module, hcl.Diagnostics)
NewModuleWithTests matches NewModule except it will also load in the provided test files.
func (*Module) EphemeralVariablesHints ¶
EphemeralVariablesHints builds a map that indicates what variable name of the module is ephemeral and which isn't. This is used by the plan to know what variables are meant to be stored and which ones should be skipped.
func (*Module) GetProviderConfig ¶
GetProviderConfig uses name and alias to find the respective Provider configuration.
func (*Module) ImpliedProviderForUnqualifiedType ¶
ImpliedProviderForUnqualifiedType returns the provider FQN for a given type, first by looking up the type in the provider requirements map, and falling back to an implied default provider.
The intended behaviour is that configuring a provider with local name "foo" in a required_providers block will result in resources with type "foo" using that provider.
func (*Module) LocalNameForProvider ¶
LocalNameForProvider returns the module-specific user-supplied local name for a given provider FQN, or the default local name if none was supplied.
func (*Module) ProviderForLocalConfig ¶
func (m *Module) ProviderForLocalConfig(pc addrs.LocalProviderConfig) addrs.Provider
ProviderForLocalConfig returns the provider FQN for a given LocalProviderConfig, based on its local name.
type ModuleCall ¶
type ModuleCall struct {
Name string
Source hcl.Expression
SourceAddrRaw string
SourceAddr addrs.ModuleSource
SourceSet bool
// Used when building the corresponding StaticModuleCall
Variables StaticModuleVariables
Workspace string
Config hcl.Body
VersionAttr *hcl.Attribute
Version VersionConstraint
Count hcl.Expression
ForEach hcl.Expression
Enabled hcl.Expression
Providers []PassedProviderConfig
DependsOn []hcl.Traversal
DeclRange hcl.Range
}
ModuleCall represents a "module" block in a module or file.
func (*ModuleCall) EntersNewPackage ¶
func (mc *ModuleCall) EntersNewPackage() bool
EntersNewPackage returns true if this call is to an external module, either directly via a remote source address or indirectly via a registry source address.
Other behaviors in OpenTofu may treat package crossings as a special situation, because that indicates that the caller and callee can change independently of one another and thus we should disallow using any features where the caller assumes anything about the callee other than its input variables, required provider configurations, and output values.
func (*ModuleCall) VariablesUsing ¶ added in v0.3.0
func (mc *ModuleCall) VariablesUsing(ctx context.Context, eval *StaticEvaluator) StaticModuleVariables
VariablesUsing returns the StaticModuleVariables closure that evaluates this module call's own variable-assignment expressions - the `X = <expr>` arguments inside `module "name" { ... }` - through eval, the SAME construction [ModuleCall.decodeStaticVariables] uses to freeze mc.Variables once, when the module tree is first built.
Exposed so a caller resolving a reference that reaches back into this call's own module can rebuild the closure against an evaluator that actually carries that module's own per-request coverage - a data lookup, most concretely (issue #212) - rather than the frozen evaluator decodeStaticVariables captured at load time, before any caller had a chance to attach one. See StaticEvaluator.WithVariables, the seam that installs the result this method returns.
type ModuleRequest ¶
type ModuleRequest struct {
// Name is the "logical name" of the module call within configuration.
// This is provided in case the name is used as part of a storage key
// for the module, but implementations must otherwise treat it as an
// opaque string. It is guaranteed to have already been validated as an
// HCL identifier and UTF-8 encoded.
Name string
// Path is a list of logical names that traverse from the root module to
// this module. This can be used, for example, to form a lookup key for
// each distinct module call in a configuration, allowing for multiple
// calls with the same name at different points in the tree.
Path addrs.Module
// SourceAddr is the source address string provided by the user in
// configuration.
SourceAddr addrs.ModuleSource
// SourceAddrRange is the source range for the SourceAddr value as it
// was provided in configuration. This can and should be used to generate
// diagnostics about the source address having invalid syntax, referring
// to a non-existent object, etc.
SourceAddrRange hcl.Range
// VersionConstraint is the version constraint applied to the module in
// configuration. This data structure includes the source range for
// the constraint, which can and should be used to generate diagnostics
// about constraint-related issues, such as constraints that eliminate all
// available versions of a module whose source is otherwise valid.
VersionConstraint VersionConstraint
// Parent is the partially-constructed module tree node that the loaded
// module will be added to. Callers may refer to any field of this
// structure except Children, which is still under construction when
// ModuleRequest objects are created and thus has undefined content.
// The main reason this is provided is so that full module paths can
// be constructed for uniqueness.
Parent *Config
// CallRange is the source range for the header of the "module" block
// in configuration that prompted this request. This can be used as the
// subject of an error diagnostic that relates to the module call itself,
// rather than to either its source address or its version number.
CallRange hcl.Range
// This is where variables and other information from the calling module
// are propagated to the child module for use in the static evaluator
Call StaticModuleCall
}
ModuleRequest is used with the ModuleWalker interface to describe a child module that must be loaded.
type ModuleRequirements ¶
type ModuleRequirements struct {
Name string
SourceAddr addrs.ModuleSource
SourceDir string
Requirements getproviders.Requirements
Children map[string]*ModuleRequirements
Tests map[string]*TestFileModuleRequirements
}
ModuleRequirements represents the provider requirements for an individual module, along with references to any child modules. This is used to determine which modules require which providers.
type ModuleWalker ¶
type ModuleWalker interface {
// LoadModule finds and loads a requested child module.
//
// If errors are detected during loading, implementations should return them
// in the diagnostics object. If the diagnostics object contains any errors
// then the caller will tolerate the returned module being nil or incomplete.
// If no errors are returned, it should be non-nil and complete.
//
// Full validation need not have been performed but an implementation should
// ensure that the basic file- and module-validations performed by the
// LoadConfigDir function (valid syntax, no namespace collisions, etc) have
// been performed before returning a module.
LoadModule(ctx context.Context, req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics)
}
A ModuleWalker knows how to find and load a child module given details about the module to be loaded and a reference to its partially-loaded parent Config.
var DisabledModuleWalker ModuleWalker
DisabledModuleWalker is a ModuleWalker that doesn't support child modules at all, and so will return an error if asked to load one.
This is provided primarily for testing. There is no good reason to use this in the main application.
type ModuleWalkerFunc ¶
type ModuleWalkerFunc func(ctx context.Context, req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics)
ModuleWalkerFunc is an implementation of ModuleWalker that directly wraps a callback function, for more convenient use of that interface.
func (ModuleWalkerFunc) LoadModule ¶
func (f ModuleWalkerFunc) LoadModule(ctx context.Context, req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics)
LoadModule implements ModuleWalker.
type Moved ¶
type Moved struct {
From *addrs.MoveEndpoint
To *addrs.MoveEndpoint
DeclRange hcl.Range
}
type Output ¶
type Output struct {
Name string
Description string
Expr hcl.Expression
DependsOn []hcl.Traversal
Sensitive bool
Deprecated string
Ephemeral bool
Preconditions []*CheckRule
DescriptionSet bool
SensitiveSet bool
EphemeralSet bool
DeclRange hcl.Range
// IsOverridden indicates if the output is being overridden. It's used in
// testing framework to not evaluate expression and use OverrideValue instead.
IsOverridden bool
// OverrideValue is only valid if IsOverridden is set to true. The value
// should be used instead of evaluated expression. It's possible to have no
// OverrideValue even with IsOverridden is set to true.
OverrideValue *cty.Value
}
Output represents an "output" block in a module or file.
func (*Output) Addr ¶
func (o *Output) Addr() addrs.OutputValue
func (*Output) UsageRange ¶
UsageRange returns the location where the output value is configured, but if the expression is not configured then it returns the output definition location. Useful for generating diagnostics.
type OverrideModule ¶
type OverrideModule struct {
// Target references module call to override.
Target hcl.Traversal
TargetParsed addrs.Module
// Outputs represents fields to use instead
// of the real module call output.
Outputs map[string]cty.Value
}
OverrideModule contains information about a module to be overridden.
type OverrideResource ¶
type OverrideResource struct {
// Target references resource or data block to override.
Target hcl.Traversal
TargetParsed *addrs.ConfigResource
// Mode indicates if the Target is resource or data block.
Mode addrs.ResourceMode
// Values represents fields to use as defaults
// if they are not present in configuration.
Values map[string]cty.Value
}
OverrideResource contains information about a resource or data block to be overridden.
type Parser ¶
type Parser struct {
// contains filtered or unexported fields
}
Parser is the main interface to read configuration files and other related files from disk.
It retains a cache of all files that are loaded so that they can be used to create source code snippets in diagnostics, etc.
func NewParser ¶
NewParser creates and returns a new Parser that reads files from the given filesystem. If a nil filesystem is passed then the system's "real" filesystem will be used, via afero.OsFs.
func (Parser) ConfigDirFiles ¶
func (p Parser) ConfigDirFiles(dir string) (primary, override []string, diags hcl.Diagnostics)
ConfigDirFiles returns lists of the primary and override files configuration files in the given directory.
If the given directory does not exist or cannot be read, error diagnostics are returned. If errors are returned, the resulting lists may be incomplete.
func (Parser) ConfigDirFilesWithTests ¶
func (p Parser) ConfigDirFilesWithTests(dir string, testDirectory string) (primary, override, tests []string, diags hcl.Diagnostics)
ConfigDirFilesWithTests matches ConfigDirFiles except it also returns the paths to any test files within the module.
func (*Parser) ConfigFiles ¶ added in v0.3.0
func (p *Parser) ConfigFiles(dir string) (primary, override []string, diags hcl.Diagnostics)
ConfigFiles returns the configuration files Parser.LoadConfigDir would read in dir: the primary set and the override set, in the same order, with the same extension rules, the same ignored-file rules and the same .tf-shadowed-by-.tofu filtering.
It exists so that a caller which must REWRITE a module's source text - internal/live/onboard, computing the edit that turns a state-backed module into a live one - reads exactly the file set the loader will read back. Duplicating that selection has failed in this repository before: an ownership check filtered on ".tf" while the loader also accepted ".tf.json" and ".tofu", so the guard was narrower than the thing it guarded and said nothing about the files it missed. A rewriter with that bug is worse than a narrow guard: it would delete a backend block from one spelling of a file and leave the module carrying the backend that spelling of the loader still finds, then report the module as edited.
Test files are deliberately not returned. They are not part of the module and cannot carry a terraform block's backend.
func (*Parser) ForceFileSource ¶
ForceFileSource artificially adds source code to the cache of file sources, as if it had been loaded from the given filename.
This should be used only in special situations where configuration is loaded some other way. Most callers should load configuration via methods of Parser, which will update the sources cache automatically.
func (*Parser) IsConfigDir ¶
IsConfigDir determines whether the given path refers to a directory that exists and contains at least one OpenTofu config file (with a .tf or .tf.json extension.). Note, we explicitly exclude checking for tests here as tests must live alongside actual .tf config files.
func (*Parser) LoadConfigDir ¶
func (p *Parser) LoadConfigDir(path string, call StaticModuleCall) (*Module, hcl.Diagnostics)
LoadConfigDir reads the .tf and .tf.json files in the given directory as config files (using LoadConfigFile) and then combines these files into a single Module.
If this method returns nil, that indicates that the given directory does not exist at all or could not be opened for some reason. Callers may wish to detect this case and ignore the returned diagnostics so that they can produce a more context-aware error message in that case.
If this method returns a non-nil module while error diagnostics are returned then the module may be incomplete but can be used carefully for static analysis.
This file does not consider a directory with no files to be an error, and will simply return an empty module in that case. Callers should first call Parser.IsConfigDir if they wish to recognize that situation.
.tf files are parsed using the HCL native syntax while .tf.json files are parsed using the HCL JSON syntax.
func (*Parser) LoadConfigDirSelective ¶
func (p *Parser) LoadConfigDirSelective(path string, call StaticModuleCall, load SelectiveLoader) (*Module, hcl.Diagnostics)
func (*Parser) LoadConfigDirUneval ¶
func (p *Parser) LoadConfigDirUneval(path string, load SelectiveLoader) (*Module, hcl.Diagnostics)
LoadConfigDirUneval is a variant of Parser.LoadConfigDir that only performs the static decoding step and does not perform early evaluation.
This is currently intended only for the experiment in internal/lang/eval, which wants to use a different strategy to meet the "early evaluation" use-cases. We should continue to use Parser.LoadConfigDir for all other callers for now.
func (*Parser) LoadConfigDirWithTests ¶
func (p *Parser) LoadConfigDirWithTests(path string, testDirectory string, call StaticModuleCall) (*Module, hcl.Diagnostics)
LoadConfigDirWithTests matches LoadConfigDir, but the return Module also contains any relevant .tftest.hcl files.
func (*Parser) LoadConfigFile ¶
func (p *Parser) LoadConfigFile(path string) (*File, hcl.Diagnostics)
LoadConfigFile reads the file at the given path and parses it as a config file.
If the file cannot be read -- for example, if it does not exist -- then a nil *File will be returned along with error diagnostics. Callers may wish to disregard the returned diagnostics in this case and instead generate their own error message(s) with additional context.
If the returned diagnostics has errors when a non-nil map is returned then the map may be incomplete but should be valid enough for careful static analysis.
This method wraps LoadHCLFile, and so it inherits the syntax selection behaviors documented for that method.
func (*Parser) LoadConfigFileOverride ¶
func (p *Parser) LoadConfigFileOverride(path string) (*File, hcl.Diagnostics)
LoadConfigFileOverride is the same as LoadConfigFile except that it relaxes certain required attribute constraints in order to interpret the given file as an overrides file.
func (*Parser) LoadHCLFile ¶
LoadHCLFile is a low-level method that reads the file at the given path, parses it, and returns the hcl.Body representing its root. In many cases it is better to use one of the other Load*File methods on this type, which additionally decode the root body in some way and return a higher-level construct.
If the file cannot be read at all -- e.g. because it does not exist -- then this method will return a nil body and error diagnostics. In this case callers may wish to ignore the provided error diagnostics and produce a more context-sensitive error instead.
The file will be parsed using the HCL native syntax unless the filename ends with ".json", in which case the HCL JSON syntax will be used.
func (*Parser) LoadTestFile ¶
func (p *Parser) LoadTestFile(path string) (*TestFile, hcl.Diagnostics)
LoadTestFile reads the file at the given path and parses it as a OpenTofu test file.
It references the same LoadHCLFile as LoadConfigFile, so inherits the same syntax selection behaviours.
type PassedProviderConfig ¶
type PassedProviderConfig struct {
InChild *ProviderConfigRef
InParent *ProviderConfigRef
}
PassedProviderConfig represents a provider config explicitly passed down to a child module, possibly giving it a new local address in the process.
type Provider ¶
type Provider struct {
Name string
NameRange hcl.Range
Alias string
AliasRange *hcl.Range // nil if no alias set
Version VersionConstraint
Config hcl.Body
DeclRange hcl.Range
// IsMocked indicates if this provider has been mocked. It is used in
// testing framework to instantiate test provider wrapper.
IsMocked bool
MockResources []*MockResource
OverrideResources []*OverrideResource
ForEach hcl.Expression
Instances map[addrs.InstanceKey]instances.RepetitionData
// contains filtered or unexported fields
}
Provider represents a "provider" block in a module or file. A provider block is a provider configuration, and there can be zero or more configurations for each actual provider.
func (*Provider) Addr ¶
func (p *Provider) Addr() addrs.LocalProviderConfig
Addr returns the address of the receiving provider configuration, relative to its containing module.
type ProviderConfigRef ¶
type ProviderConfigRef struct {
Name string
NameRange hcl.Range
Alias string
AliasRange *hcl.Range // nil if alias not set
KeyExpression hcl.Expression
// contains filtered or unexported fields
}
func (*ProviderConfigRef) Addr ¶
func (r *ProviderConfigRef) Addr() addrs.LocalProviderConfig
Addr returns the provider config address corresponding to the receiving config reference.
This is a trivial conversion, essentially just discarding the source location information and keeping just the addressing information.
func (*ProviderConfigRef) InstanceValidation ¶
func (r *ProviderConfigRef) InstanceValidation(blockType string, isInstanced bool, hasConfig bool) hcl.Diagnostics
func (*ProviderConfigRef) String ¶
func (r *ProviderConfigRef) String() string
type ProviderMeta ¶
type ProviderMeta struct {
Provider string
Config hcl.Body
ProviderRange hcl.Range
DeclRange hcl.Range
}
ProviderMeta represents a "provider_meta" block inside a "terraform" block in a module or file.
type Provisioner ¶
type Provisioner struct {
Type string
Config hcl.Body
Connection *Connection
When ProvisionerWhen
OnFailure ProvisionerOnFailure
DeclRange hcl.Range
TypeRange hcl.Range
}
Provisioner represents a "provisioner" block when used within a "resource" block in a module or file.
type ProvisionerOnFailure ¶
type ProvisionerOnFailure int
ProvisionerOnFailure is an enum for valid values for on_failure options for provisioners.
const ( ProvisionerOnFailureInvalid ProvisionerOnFailure = iota ProvisionerOnFailureContinue ProvisionerOnFailureFail )
func (ProvisionerOnFailure) String ¶
func (i ProvisionerOnFailure) String() string
type ProvisionerWhen ¶
type ProvisionerWhen int
ProvisionerWhen is an enum for valid values for when to run provisioners.
const ( ProvisionerWhenInvalid ProvisionerWhen = iota ProvisionerWhenCreate ProvisionerWhenDestroy )
func (ProvisionerWhen) String ¶
func (i ProvisionerWhen) String() string
type ReferenceCategory ¶ added in v0.3.0
type ReferenceCategory string
ReferenceCategory classifies the kind of object a reference site named, derived structurally from ref.Subject's Go type at the point StaticValidateReferences refused it - never by parsing a diagnostic's rendered text. It rides in the diagnostic's Extra field (the same mechanism tfdiags.ExtraInfo already exposes for other diagnostic metadata), so a caller can recover it with tfdiags.ExtraInfo[ReferenceCategory](diag) instead of re-deriving it.
#178's scoping pass found the split between a same-stack data source and a cross-stack one load-bearing for its design question: a same-stack data source is an ordering problem this repo could plausibly solve with a pre-resolution read phase against the live provider, while a cross-stack one (terraform_remote_state, tfe_outputs) depends on a second state backend this fork does not open, and needs its own design call regardless of what the first gets.
const ( CategoryManagedResource ReferenceCategory = "managed_resource" CategoryDataSource ReferenceCategory = "data_source" CategoryTfeOutputs ReferenceCategory = "tfe_outputs" CategoryRemoteState ReferenceCategory = "remote_state" CategoryModuleOutput ReferenceCategory = "module_output" CategoryProviderFunction ReferenceCategory = "provider_function" CategoryOther ReferenceCategory = "other" )
type RefusedReference ¶ added in v0.3.0
type RefusedReference struct {
// Category classifies the referenced object. See [ReferenceCategory].
Category ReferenceCategory
// Subject is the referenced object itself, module-relative, exactly as
// the reference parser produced it.
Subject addrs.Referenceable
// Module is the module the referencing expression belongs to: the
// evaluating module's call path, with no instance keys, because a static
// evaluator is shared by every instance of its module.
Module addrs.Module
// NeededBy names the static identifier whose evaluation needed the
// refused reference - the top of the evaluation stack, rendered the way
// the diagnostic's own text renders it.
NeededBy string
}
RefusedReference is the structured account of one reference [staticScopeData.StaticValidateReferences] refused, carried in the diagnostic's Extra field. It wraps the ReferenceCategory that has ridden there since #178 - tfdiags.ExtraInfo[ReferenceCategory] still finds it, through the unwrap chain - and adds the two facts a consumer needs to act on the refusal rather than merely count it: which object was referenced and in which module. The pre-resolution data-read phase (internal/live/dataread) is the consumer: it derives which data sources identity resolution demands from exactly these refusals.
func (RefusedReference) UnwrapDiagnosticExtra ¶ added in v0.3.0
func (r RefusedReference) UnwrapDiagnosticExtra() interface{}
UnwrapDiagnosticExtra keeps tfdiags.ExtraInfo[ReferenceCategory] working: the category used to BE the Extra value, and every consumer of it reads through the standard unwrap chain.
type Removed ¶
type Removed struct {
From *addrs.RemoveEndpoint
Destroy bool
DestroySet bool
Provisioners []*Provisioner
DeclRange hcl.Range
}
Removed represents a removed block in the configuration.
type RequiredProvider ¶
type RequiredProvider struct {
Name string
Source string
Type addrs.Provider
Requirement VersionConstraint
DeclRange hcl.Range
Aliases []addrs.LocalProviderConfig
}
RequiredProvider represents a declaration of a dependency on a particular provider version or source without actually configuring that provider. This is used in child modules that expect a provider to be passed in from their parent.
type RequiredProviders ¶
type RequiredProviders struct {
RequiredProviders map[string]*RequiredProvider
DeclRange hcl.Range
}
type Resource ¶
type Resource struct {
Mode addrs.ResourceMode
Name string
Type string
Config hcl.Body
Count hcl.Expression
Enabled hcl.Expression
ForEach hcl.Expression
ProviderConfigRef *ProviderConfigRef
Provider addrs.Provider
Preconditions []*CheckRule
Postconditions []*CheckRule
DependsOn []hcl.Traversal
TriggersReplacement []hcl.Expression
// Managed is populated only for Mode = addrs.ManagedResourceMode,
// containing the additional fields that apply to managed resources.
// For all other resource modes, this field is nil.
Managed *ManagedResource
// Container links a scoped resource back up to the resources that contains
// it. This field is referenced during static analysis to check whether any
// references are also made from within the same container.
//
// If this is nil, then this resource is essentially public.
Container Container
// IsOverridden indicates if the resource is being overridden. It's used in
// testing framework to not call the underlying provider.
IsOverridden bool
// OverrideValues are only valid if IsOverridden is set to true. The values
// should be used to compose mock provider response. It is possible to have
// zero-length OverrideValues even if IsOverridden is set to true.
OverrideValues map[string]cty.Value
DeclRange hcl.Range
TypeRange hcl.Range
}
Resource represents a "resource" or "data" block in a module or file.
func (*Resource) Addr ¶
Addr returns a resource address for the receiver that is relative to the resource's containing module.
func (*Resource) HasCustomConditions ¶
HasCustomConditions returns true if and only if the resource has at least one author-specified custom condition.
func (*Resource) ProviderConfigAddr ¶
func (r *Resource) ProviderConfigAddr() addrs.LocalProviderConfig
ProviderConfigAddr returns the address for the provider configuration that should be used for this resource. This function returns a default provider config addr if an explicit "provider" argument was not provided.
type SelectiveLoader ¶
type SelectiveLoader int
SelectiveLoader allows the consumer to only load and validate the portions of files needed for the given operations/contexts
const ( SelectiveLoadAll SelectiveLoader = 0 SelectiveLoadBackend SelectiveLoader = 1 SelectiveLoadEncryption SelectiveLoader = 2 )
type StaticDataLookup ¶ added in v0.3.0
StaticDataLookup answers a data-resource reference with a value read ahead of static evaluation, or reports that it has none. The address is the module-relative resource (addrs.Resource with addrs.DataResourceMode); the value is the whole resource's: the single instance's object for an unexpanded block, a tuple for count, an object keyed by string for for_each, matching how the plan-time evaluator shapes a resource reference so that instance indexing works unchanged.
type StaticEvaluator ¶
type StaticEvaluator struct {
// contains filtered or unexported fields
}
A static evaluator contains the information required to build a EvalContext which only understands "static" (non-state) data. Internally, it relies on staticData
func NewStaticEvaluator ¶
func NewStaticEvaluator(mod *Module, call StaticModuleCall) *StaticEvaluator
Creates a static evaluator based from the given module and module call
func (StaticEvaluator) DecodeBlock ¶
func (s StaticEvaluator) DecodeBlock(ctx context.Context, body hcl.Body, spec hcldec.Spec, ident StaticIdentifier) (cty.Value, hcl.Diagnostics)
DecodeBlock decodes body against spec, expanding any "dynamic" block it contains first - the same two-phase split stock OpenTofu's own plan-time evaluator uses (internal/lang/eval.go's Scope.ExpandBlock, then Scope.EvalBlock): phase one resolves whatever a "dynamic" block's own for_each/iterator/labels arguments reference and expands the block; phase two resolves the expanded body's own references and decodes it.
Before this, DecodeBlock ran phase two alone, directly against the raw body. hcldec rejects a "dynamic" block outright ("Blocks of type \"dynamic\" are not expected here") because it is not a block type any schema ever declares, so a data source's arguments using one - an IAM policy document's `dynamic "statement" { ... }`, ordinary and common in real configurations - refused every time, never silently: the caller always got an error diagnostic, not a wrong or partial value. See internal/live/dataread's #212 fix, which is the concrete case that first exercised this.
func (StaticEvaluator) DecodeExpression ¶
func (s StaticEvaluator) DecodeExpression(ctx context.Context, expr hcl.Expression, ident StaticIdentifier, val any) hcl.Diagnostics
func (StaticEvaluator) EvalContext ¶
func (s StaticEvaluator) EvalContext(ctx context.Context, ident StaticIdentifier, refs []*addrs.Reference) (*hcl.EvalContext, hcl.Diagnostics)
func (StaticEvaluator) EvalContextWithParent ¶
func (s StaticEvaluator) EvalContextWithParent(ctx context.Context, parent *hcl.EvalContext, ident StaticIdentifier, refs []*addrs.Reference) (*hcl.EvalContext, hcl.Diagnostics)
func (StaticEvaluator) Evaluate ¶
func (s StaticEvaluator) Evaluate(ctx context.Context, expr hcl.Expression, ident StaticIdentifier) (cty.Value, hcl.Diagnostics)
func (StaticEvaluator) EvaluateStructural ¶ added in v0.3.0
func (s StaticEvaluator) EvaluateStructural(ctx context.Context, expr hcl.Expression, ident StaticIdentifier) (cty.Value, hcl.Diagnostics)
EvaluateStructural evaluates expr the same way StaticEvaluator.Evaluate does, except a reference this evaluator refuses (a managed resource, a data source, a module output - see [staticScopeData.StaticValidateReferences]) does not automatically veto expr's own rendered VALUE the way Evaluate's EvalExpr does. Ordinary evaluation treats "some reference this expression transitively depends on was refused" and "the value this expression renders is unusable" as the same fact; they are not. A tuple literal's LENGTH is a property of its type, independent of any one element's contents, so length(var.x) for a var.x whose declared value is [{a=1,b=SOMETHING_DYNAMIC}] still has a real answer even though b does not - GetInputVariable (static_scope.go) already keeps that shape's TYPE on a refused reference rather than collapsing it to cty.DynamicVal's type-erased unknown, so this method's only job is to stop discarding the value the instant ANY refusal fired anywhere in the reference graph, and ask instead whether THIS expression's own rendered result came back usable regardless.
The gate is deliberately narrower than "ignore every error": every diagnostic has to carry a RefusedReference extra (attached only by StaticValidateReferences, meaning exactly "a reference named here is outside static scope" - never a parse error, a type mismatch, or a wrong argument count, none of which carry that extra), AND the rendered value has to come back non-null, wholly known and unmarked. Either condition failing returns the value and diagnostics exactly as computed, unfiltered - the same conservative default every refusal in this codebase falls back to.
Nothing else calls this yet: it is additive, so every existing caller of Evaluate/EvalExpr keeps its current behavior unchanged.
func (*StaticEvaluator) Pure ¶
func (s *StaticEvaluator) Pure() *StaticEvaluator
Pure returns a copy of the evaluator whose scopes evaluate uuid(), timestamp() and bcrypt() to unknown values instead of calling them, the same way the plan walk does outside of apply (see lang.Scope.PureOnly and internal/tofu/evaluate.go).
The default is impure because the original callers of the static evaluator - module source, backend configuration, module call variables - all want whatever the function returns and consume it once, immediately.
A caller that is deriving a stable identity from configuration wants the opposite. An impure function evaluated here produces a value that is real, known, and different on the next run: for stateless mode's identity resolution that means a fabricated import ID, a plan that proposes to create something that already exists, and a leaked resource per run, with no diagnostic anywhere because nothing about the value looks wrong. Such a caller asks for a pure evaluator, and gets an unknown value it can refuse on rather than a plausible one it cannot tell from a real answer.
func (*StaticEvaluator) WithDataResults ¶ added in v0.3.0
func (s *StaticEvaluator) WithDataResults(lookup StaticDataLookup) *StaticEvaluator
WithDataResults returns a copy of the evaluator whose scopes answer data-resource references through the given lookup instead of refusing them as dynamic values.
The base evaluator refuses every resource reference, because static evaluation runs before anything has been read and inventing a value here would be a guess a later phase treats as fact. A caller that HAS performed reads - the pre-resolution data-read phase - hands the results in through this seam, and only references the lookup actually covers are permitted; everything else keeps refusing exactly as before.
A managed-mode reference is answerable only from the resource block's own configuration - never from a pre-plan read, because there is nothing such a read could honestly say about an object the plan may be about to change. internal/live/dataread's lookup answers one when the block's own body sets an argument of that name and the expression evaluates statically; the answer is partial by construction, so [lookupCoversTraversal] refuses a reference that does not take it down to one of those arguments.
func (*StaticEvaluator) WithFunctionOverrides ¶ added in v0.3.0
func (s *StaticEvaluator) WithFunctionOverrides(overrides map[string]function.Function) *StaticEvaluator
WithFunctionOverrides returns a copy of the evaluator whose scopes use overrides in place of the named entries of the base function table (lang.Scope.FuncOverrides), for every reference this evaluator resolves at any depth - the same "carries through nested scopes" property StaticEvaluator.WithRepetitionData documents, because [newStaticScope] builds every nested scope from this same *StaticEvaluator.
internal/live/dataread is the first and, as of this writing, only caller: issue #193's length()/keys() guard needs to see whether the OBJECT a data source's argument passes to either function is an unexpanded managed resource's own projection, a fact [configs.lookupCoversTraversal] and plain attribute access cannot be made to draw without over-refusing a legitimate for_each-expanded reference (see managedproj.go's doc). A function table override is the one seam that can see the argument VALUE at the exact place length()/keys() are invoked without changing what an ordinary `.subnet_id`-style reference resolves to, because it replaces only those two function-table entries and never touches attribute access at all.
func (*StaticEvaluator) WithModuleInstance ¶ added in v0.3.0
func (s *StaticEvaluator) WithModuleInstance(modInst addrs.ModuleInstance) *StaticEvaluator
WithModuleInstance returns a copy of the evaluator that knows WHICH instance of its module it is evaluating for, and therefore can answer this fork's one added evaluator symbol, markers.ModulePrefixAttr - see [staticScopeData.GetTerraformAttr].
It carries through nested scopes exactly as StaticEvaluator.WithRepetitionData does, and for the same mechanical reason: [newStaticScope] builds every nested scope from this same *StaticEvaluator, so a local value or module-call variable reached from the expression a caller hands to Evaluate sees the same instance.
The base evaluator does NOT know its module instance, and must not guess one. A StaticEvaluator belongs to a *configs.Module, and a module with a for_each'd or count'd call above it has one static evaluator shared by every instance of it - the exact fact issue #378 is about. Answering the symbol from an evaluator nobody threaded an instance into would answer it for whichever instance happened to be nearest, or for none, and a marker built on that answer is a WRONG marker on a real object rather than a missing one. So the refusal in GetTerraformAttr is the load-bearing half of this seam, not an edge case of it: this method exists to turn that refusal off for exactly the caller that has established the instance, and for nobody else.
A caller that genuinely evaluates for the root module passes addrs.RootModuleInstance, which is answered as a refusal of its own (the root has no module prefix; see markers.ModulePrefix) rather than as "not threaded" - the distinction the moduleInstanceSet flag exists for.
func (*StaticEvaluator) WithModuleOutputResults ¶ added in v0.3.0
func (s *StaticEvaluator) WithModuleOutputResults(lookup StaticModuleOutputLookup) *StaticEvaluator
WithModuleOutputResults returns a copy of the evaluator whose scopes answer a module-call reference through the given lookup instead of refusing it as unsupported in a static context, when the lookup covers that call.
This is [WithDataResults]'s exact counterpart for the one referenceable kind that seam does not cover: a data source's own argument, or a provider block's, can equally well read a child module's output (`name = module.eks.cluster_id`), and that reference is refused by a dedicated case in [staticScopeData.StaticValidateReferences], never reaching [staticScopeData.GetResource] or [StaticEvaluator.dataLookup] at all. A caller that can evaluate the child module's own output expression - internal/live/dataread, through the same live evaluator it already builds for that module's data sources and managed-resource projections - hands the result in through this seam.
Deliberately independent of [WithUnknownForRefusedReferences]: that seam turns EVERY uncovered managed, data or module-output reference into an unknown, which would make an uncovered managed reference stop raising the RefusedReference-tagged diagnostic [identity.DemandedManagedReads] reads - silently discarding the one signal a caller needs to know a live read would settle it. This seam keeps that discipline: only a call the lookup actually covers passes, in the same "covered or still refused" shape [dataLookup] already uses, and it changes nothing about how any other reference kind is handled.
func (*StaticEvaluator) WithRepetitionData ¶ added in v0.3.0
func (s *StaticEvaluator) WithRepetitionData(rd instances.RepetitionData) *StaticEvaluator
WithRepetitionData returns a copy of the evaluator whose scopes answer each.key, each.value and count.index from rd, for every reference this evaluator resolves at any depth - not only the expression a caller hands straight to StaticEvaluator.Evaluate, but also any local value or module-call variable that expression reaches through StaticEvaluator's own reference resolution (a local's own defining expression evaluates through a freshly built child scope, and that scope carries the same *StaticEvaluator, so it sees the same rd).
rd is a value bag, not a source of truth: whatever the caller passes is what a reference gets back, with no independent check that it matches the resource instance actually being evaluated. The caller - identity resolution's own per-instance expansion - is the party responsible for that, because only it knows which instance's arguments it is asking this evaluator to answer. A field of rd left at cty.NilVal (the zero value) means "not known for this instance", which behaves exactly as an evaluator built with no repetition data at all: an unset each.value under a for_each whose values are not statically known must still refuse a reference to it, not answer with something invented.
The base evaluator refuses every each/count reference (see [staticScopeData.GetCountAttr] and [staticScopeData.GetForEachAttr]), because plain static evaluation - module source, backend configuration, module call variables - has no notion of a resource instance at all. A caller resolving one instance's identity is the first caller of this package that does.
func (*StaticEvaluator) WithUnknownForRefusedReferences ¶ added in v0.3.0
func (s *StaticEvaluator) WithUnknownForRefusedReferences(outputs StaticModuleOutputLookup) *StaticEvaluator
WithUnknownForRefusedReferences returns a copy of the evaluator whose scopes substitute an UNKNOWN value for a managed-resource, data-source or module-output reference instead of refusing it, and which answer a module call by value through outputs where outputs can.
What this is for ¶
Stock OpenTofu's plan-time evaluator has no strict/loose distinction to make. A reference it cannot answer yet becomes an unknown and evaluation continues, so an object with one apply-time leaf is a KNOWN object with an unknown attribute, and everything the configuration derives from the parts it did write down - a map's key set, a literal sibling, a conditional on a literal flag - still has an answer. This fork's static evaluator refuses the whole enclosing value instead, and that difference is choudoufu refusing where stock proceeds rather than a fact about the configuration.
[identity.resolver] already brought the two together for one shape, a module-call ARGUMENT whose skeleton is literal and one of whose leaves is not (internal/live/identity/partialargs.go). That rebuild works one constructor element at a time and therefore reaches only what the caller wrote out AT the call. The same poisoning happens one layer in - inside a local value, and inside a module output's own expression - where there is no constructor for a caller to rebuild, and this is the seam for it.
Why an unknown, and why that cannot widen what becomes a marker ¶
The substitution is an unknown, never a guess, so any value that comes back KNOWN is independent of every reference that was substituted: it is the configuration's own literals, run through the configuration's own functions, exactly as stock would run them. A value that DID depend on a substituted leaf comes back unknown, and every path in this fork that turns a value into an identity demands a known one ([identity.staticSubValue] requires IsWhollyKnown, [identity.collectionKeyNames] rejects an unknown key, the resolver refuses an unknown identity argument), so a substituted leaf cannot become a marker. What can newly succeed is a count, a for_each key set, or an identity component that reads only what the configuration states outright.
What it deliberately does not tolerate ¶
count.index, each.key, each.value and provider functions keep refusing exactly as they did. Repetition is answered by StaticEvaluator.WithRepetitionData from a caller that has established WHICH instance it is asking about; substituting an unknown for it instead would make an expansion that reads count.index appear answerable when nothing has established the instance at all. A provider function's answer comes from a provider that has not been started.
This is opt-in and additive: an evaluator nobody calls this on refuses every one of these references exactly as it always has.
func (*StaticEvaluator) WithVariables ¶ added in v0.3.0
func (s *StaticEvaluator) WithVariables(vars StaticModuleVariables) *StaticEvaluator
WithVariables returns a copy of the evaluator whose module call answers var.* references (see [staticScopeData.GetInputVariable]) through vars instead of the one captured when the module tree was first built.
The base evaluator's own vars closure (ModuleCall.Variables, built by [ModuleCall.decodeStaticVariables]) is frozen once, at load time, against this module's OWN *StaticEvaluator as it existed then - before any caller has ever had a chance to call StaticEvaluator.WithDataResults or StaticEvaluator.WithRepetitionData on anything, because no read or instance resolution has happened yet. That closure is a real *StaticEvaluator pointer captured by a Go closure, not re-derived per call, so every module-call variable it ever answers is evaluated with NO data-source coverage and NO repetition data, forever, for the lifetime of that *Config tree - regardless of what coverage a later caller attaches to ITS OWN evaluator for a descendant module.
That staleness is invisible for an ordinary var.X - source, count, for_each, module version - because those rarely depend on a data source or a resource instance's repetition. It stops being invisible the moment a descendant module's data source references var.X, and var.X's value, in the ANCESTOR, itself reads that ancestor's OWN data source (issue #212): the ancestor's frozen evaluator has never seen a data-source lookup at all, so the reference refuses as unreadable even when #179 stage 3 says this exact shape should be readable.
A caller that has built a "live" evaluator for the ancestor module - carrying that module's own StaticEvaluator.WithDataResults coverage, scoped to that module and no other (see internal/live/dataread's per-module lookup, never a lookup shared across two different modules) - uses WithVariables to re-point THIS module's own var.* resolution at that live ancestor evaluator instead of the frozen one, via ModuleCall.VariablesUsing. Every other field this evaluator carries (its own module, its own data lookup, its own repetition) is untouched: only how ITS OWN var.* references reach their ancestor's expression changes.
type StaticIdentifier ¶
StaticIdentifier holds a Referenceable item and where it was declared
func (StaticIdentifier) String ¶
func (ref StaticIdentifier) String() string
type StaticModuleCall ¶
type StaticModuleCall struct {
// contains filtered or unexported fields
}
StaticModuleCall contains the information required to call a given module
func NewStaticModuleCall ¶
func NewStaticModuleCall(addr addrs.Module, declRange hcl.Range, vars StaticModuleVariables, rootPath string, workspace string) StaticModuleCall
func RootModuleCallForTesting ¶
func RootModuleCallForTesting() StaticModuleCall
only used in testing
func (StaticModuleCall) Variables ¶
func (s StaticModuleCall) Variables() StaticModuleVariables
func (StaticModuleCall) WithVariables ¶
func (s StaticModuleCall) WithVariables(vars StaticModuleVariables) StaticModuleCall
type StaticModuleOutputLookup ¶ added in v0.3.0
type StaticModuleOutputLookup func(call addrs.ModuleCall) (cty.Value, bool)
StaticModuleOutputLookup answers a module-call reference with the object of that call's own output values, or reports that it has none. The address is the call as written in the module doing the referring (`module.network` is addrs.ModuleCall{Name: "network"}); the value is the whole call's, an object with one attribute per output, matching how the plan-time evaluator shapes a module reference so that `module.network.configuration` resolves by ordinary attribute access.
It is consulted only by an evaluator built through StaticEvaluator.WithUnknownForRefusedReferences, and a false there is not a refusal: the reference becomes unknown like any other the tolerant scope cannot answer.
type StaticModuleVariables ¶
type StaticModuleVariables func(v *Variable) (cty.Value, hcl.Diagnostics)
type TestCommand ¶
type TestCommand rune
TestCommand represents the OpenTofu a given run block will execute, plan or apply. Defaults to apply.
type TestFile ¶
type TestFile struct {
// Variables defines a set of global variable definitions that should be set
// for every run block within the test file.
Variables map[string]hcl.Expression
// Providers defines a set of providers that are available to run blocks
// within this test file.
//
// If empty, tests should use the default providers for the module under
// test.
Providers map[string]*Provider
// Runs defines the sequential list of run blocks that should be executed in
// order.
Runs []*TestRun
// OverrideResources is a list of resources to be overridden with static values.
// Underlying providers shouldn't be called for overridden resources.
OverrideResources []*OverrideResource
// OverrideModules is a list of modules to be overridden with static values.
// Underlying modules shouldn't be called.
OverrideModules []*OverrideModule
// MockProviders is a map of providers that should be mocked. It is merged
// with Providers map to use later when instantiating provider instance.
MockProviders map[string]*MockProvider
VariablesDeclRange hcl.Range
}
TestFile represents a single test file within a `tofu test` execution.
A test file is made up of a sequential list of run blocks, each designating a command to execute and a series of validations to check after the command.
func (*TestFile) Validate ¶
func (file *TestFile) Validate() tfdiags.Diagnostics
Validate does a very simple and cursory check across the file blocks to look for simple issues we can highlight early on. It doesn't validate nested run blocks.
type TestFileModuleRequirements ¶
type TestFileModuleRequirements struct {
Requirements getproviders.Requirements
Runs map[string]*ModuleRequirements
}
TestFileModuleRequirements maps the runs for a given test file to the module requirements for that run block.
type TestMode ¶
type TestMode rune
TestMode represents the plan mode that OpenTofu will use for a given run block, normal or refresh-only. Defaults to normal.
type TestRun ¶
type TestRun struct {
Name string
// Command is the OpenTofu command to execute.
//
// One of ['apply', 'plan'].
Command TestCommand
// Options contains the embedded plan options that will affect the given
// Command. These should map to the options documented here:
// - https://opentofu.org/docs/cli/commands/plan/#planning-options
//
// Note, that the Variables are a top level concept and not embedded within
// the options despite being listed as plan options in the documentation.
Options *TestRunOptions
// Variables defines a set of variable definitions for this command.
//
// Any variables specified locally that clash with the global variables will
// take precedence over the global definition.
Variables map[string]hcl.Expression
// Providers specifies the set of providers that should be loaded into the
// module for this run block.
//
// Providers specified here must be configured in one of the provider blocks
// for this file. If empty, the run block will load the default providers
// for the module under test.
Providers []PassedProviderConfig
// CheckRules defines the list of assertions/validations that should be
// checked by this run block.
CheckRules []*CheckRule
// Module defines an address of another module that should be loaded and
// executed as part of this run block instead of the module under test.
//
// We support loading from all the module sources, like local directories, registry,
// generic git repos, github, bitbucket, s3 and gcs repositories.
Module *TestRunModuleCall
// ConfigUnderTest describes the configuration this run block should execute
// against.
//
// In typical cases, this will be null and the config under test is the
// configuration within the directory the tofu test command is
// executing within. However, when Module is set the config under test is
// whichever config is defined by Module. This field is then set during the
// configuration load process and should be used when the test is executed.
ConfigUnderTest *Config
// ExpectFailures should be a list of checkable objects that are expected
// to report a failure from their custom conditions as part of this test
// run.
ExpectFailures []hcl.Traversal
// OverrideResources is a list of resources to be overridden with static values.
// Underlying providers shouldn't be called for overridden resources.
OverrideResources []*OverrideResource
// OverrideModules is a list of modules to be overridden with static values.
// Underlying modules shouldn't be called.
OverrideModules []*OverrideModule
NameDeclRange hcl.Range
VariablesDeclRange hcl.Range
DeclRange hcl.Range
}
TestRun represents a single run block within a test file.
Each run block represents a single OpenTofu command to be executed and a set of validations to run after the command.
func (*TestRun) Validate ¶
func (run *TestRun) Validate() tfdiags.Diagnostics
Validate does a very simple and cursory check across the run block to look for simple issues we can highlight early on.
type TestRunModuleCall ¶
type TestRunModuleCall struct {
// Source is the source of the module to test.
Source addrs.ModuleSource
// Version is the version of the module to load from the registry.
Version VersionConstraint
DeclRange hcl.Range
SourceDeclRange hcl.Range
}
TestRunModuleCall specifies which module should be executed by a given run block.
type TestRunOptions ¶
type TestRunOptions struct {
// Mode is the planning mode to run in. One of ['normal', 'refresh-only'].
Mode TestMode
// Refresh is analogous to the -refresh=false OpenTofu plan option.
Refresh bool
// Replace is analogous to the -refresh=ADDRESS OpenTofu plan option.
Replace []hcl.Traversal
// Target is analogous to the -target=ADDRESS OpenTofu plan option.
Target []hcl.Traversal
DeclRange hcl.Range
}
TestRunOptions contains the plan options for a given run block.
type Variable ¶
type Variable struct {
Name string
Description string
Default cty.Value
// Only used inside modules that have *some* variable with ConstSet.
// This allows us to match terraform's validation in their imitation
// of our static eval concept.
Const bool
// Type is the concrete type of the variable value.
Type cty.Type
// ConstraintType is used for decoding and type conversions, and may
// contain nested ObjectWithOptionalAttr types.
ConstraintType cty.Type
TypeDefaults *typeexpr.Defaults
ParsingMode VariableParsingMode
Validations []*CheckRule
Sensitive bool
Deprecated string
Ephemeral bool
DescriptionSet bool
SensitiveSet bool
EphemeralSet bool
ConstSet bool
// Nullable indicates that null is a valid value for this variable. Setting
// Nullable to false means that the module can expect this variable to
// never be null.
Nullable bool
NullableSet bool
DeclRange hcl.Range
}
Variable represents a "variable" block in a module or file.
func (*Variable) Addr ¶
func (v *Variable) Addr() addrs.InputVariable
func (*Variable) InputPrompt ¶
InputPrompt returns the text that will be shown during prompting for the variable input when required but no value given. This method is meant to return also the deprecated message together with the description when both exists or only the deprecated info when the description is missing. Other than these 2 cases, the method is keeping the default behavior in case of both, deprecated and description, are missing by returning the description. This will keep the previous behavior where during prompting will be shown only the variable name.
type VariableParsingMode ¶
type VariableParsingMode rune
VariableParsingMode defines how values of a particular variable given by text-only mechanisms (command line arguments and environment variables) should be parsed to produce the final value.
const VariableParseHCL VariableParsingMode = 'H'
VariableParseHCL is a variable parsing mode that attempts to parse the given string as an HCL expression and returns the result.
const VariableParseLiteral VariableParsingMode = 'L'
VariableParseLiteral is a variable parsing mode that just takes the given string directly as a cty.String value.
func (VariableParsingMode) Parse ¶
func (m VariableParsingMode) Parse(name, value string) (cty.Value, hcl.Diagnostics)
Parse uses the receiving parsing mode to process the given variable value string, returning the result along with any diagnostics.
A VariableParsingMode does not know the expected type of the corresponding variable, so it's the caller's responsibility to attempt to convert the result to the appropriate type and return to the user any diagnostics that conversion may produce.
The given name is used to create a synthetic filename in case any diagnostics must be generated about the given string value. This should be the name of the root module variable whose value will be populated from the given string.
If the returned diagnostics has errors, the returned value may not be valid.
type VariableTypeHint ¶
type VariableTypeHint rune
VariableTypeHint is an enumeration used for the Variable.TypeHint field, which is an incompletely-specified type for the variable which is used as a hint for whether a value provided in an ambiguous context (on the command line or in an environment variable) should be taken literally as a string or parsed as an HCL expression to produce a data structure.
The type hint is applied to runtime values as well, but since it does not accurately describe a precise type it is not fully-sufficient to infer the dynamic type of a value passed through a variable.
These hints use inaccurate terminology for historical reasons. Full details are in the documentation for each constant in this enumeration, but in summary:
- TypeHintString requires a primitive type
- TypeHintList requires a type that could be converted to a tuple
- TypeHintMap requires a type that could be converted to an object
const TypeHintList VariableTypeHint = 'L'
TypeHintList indicates that a value provided in an ambiguous context should be treated as an HCL expression, and additionally requires that the runtime value for the variable is of an tuple, list, or set type.
const TypeHintMap VariableTypeHint = 'M'
TypeHintMap indicates that a value provided in an ambiguous context should be treated as an HCL expression, and additionally requires that the runtime value for the variable is of an object or map type.
const TypeHintNone VariableTypeHint = 0
TypeHintNone indicates the absence of a type hint. Values specified in ambiguous contexts will be treated as literal strings, as if TypeHintString were selected, but no runtime value checks will be applied. This is reasonable type hint for a module that is never intended to be used at the top-level of a configuration, since descendent modules never receive values from ambiguous contexts.
const TypeHintString VariableTypeHint = 'S'
TypeHintString spec indicates that a value provided in an ambiguous context should be treated as a literal string, and additionally requires that the runtime value for the variable is of a primitive type (string, number, bool).
func (VariableTypeHint) String ¶
func (i VariableTypeHint) String() string
type VersionConstraint ¶
type VersionConstraint struct {
Required version.Constraints
RequiredSet versions.Set // New style
DeclRange hcl.Range
}
VersionConstraint represents a version constraint on some resource (e.g. OpenTofu Core, a provider, a module, ...) that carries with it a source range so that a helpful diagnostic can be printed in the event that a particular constraint does not match.
func (VersionConstraint) HasRequirements ¶
func (v VersionConstraint) HasRequirements() bool
func (VersionConstraint) String ¶
func (v VersionConstraint) String() string
Source Files
¶
- backend.go
- checks.go
- cloud.go
- compat_shim.go
- config.go
- config_build.go
- config_files.go
- container.go
- depends_on.go
- diagnostics.go
- doc.go
- import.go
- language.go
- live.go
- module.go
- module_call.go
- module_merge.go
- moved.go
- named_values.go
- parser.go
- parser_config.go
- parser_config_dir.go
- parser_live_sidecar.go
- provider.go
- provider_meta.go
- provider_requirements.go
- provider_validation.go
- provisioner.go
- provisioneronfailure_string.go
- provisionerwhen_string.go
- removed.go
- resource.go
- shim.go
- static_evaluator.go
- static_scope.go
- test_file.go
- test_provider_body.go
- testing_helpers.go
- validation.go
- variable_type_hint.go
- variabletypehint_string.go
- version_constraint.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package configload knows how to install modules into the .terraform/modules directory and to load modules from those installed locations.
|
Package configload knows how to install modules into the .terraform/modules directory and to load modules from those installed locations. |
|
Package configschema contains types for describing the expected structure of a configuration block whose shape is not known until runtime.
|
Package configschema contains types for describing the expected structure of a configuration block whose shape is not known until runtime. |