Documentation
¶
Overview ¶
Package offlineplan runs live-plan's second identity pass without a cloud, for the callers that measure a configuration offline: the live-check command and the two corpus tools (GitHub issue #2297).
live-plan's liveResolve (internal/command/live_plan.go) resolves identities once, and when that pass refuses for a value only the provider knows - the ACM/Route53 validation records reading aws_acm_certificate.domain_validation_options are the shape it was built for - it asks each provider to plan a create (projection.PlanInstancesIn) and resolves again with those values in hand. internal/live/check stays provider-free on purpose ([check.Context.ManagedResults]), so before #2297 the offline instruments never ran that second pass and refused sites that live-plan resolves (choudoufu#2292's research: complete-alb, complete-nlb and mutual-auth-alb).
This package is the caller-side half. ManagedResults makes the same decision liveResolve makes, over the same two passes, and returns the planned values only when liveResolve would keep the second pass. The caller hands them to [check.Analyze] as [check.Context.ManagedResults].
Offline means no cloud call ¶
A plan for a create needs a configured provider, not a credentialed one (see pluginschema.Session). Providers configures each provider from ConfigValue, never from the configuration's own provider block: the block may name real credentials or read data sources, and this pass must not reach a cloud on anyone's behalf. For hashicorp/aws that value carries placeholder keys, every skip_* switch that stops the provider calling STS or the instance metadata service while it configures, and a proxy at a closed local port, so a resource whose plan would make an API call fails that call on this machine instead of reaching AWS. Every other provider is configured with an empty configuration; one that requires arguments fails to configure, and its resources are simply absent from the planned values, which is projection.PlanInstancesIn's own rule for anything it cannot plan.
Index ¶
Constants ¶
const DeadProxy = "http://127.0.0.1:9"
DeadProxy is the proxy ConfigValue gives hashicorp/aws: a closed port on the loopback interface, so any request the provider makes fails locally.
Variables ¶
This section is empty.
Functions ¶
func ConfigValue ¶
ConfigValue is the provider configuration this package configures provider with: the schema's empty value, plus [awsOffline] for hashicorp/aws. See the package doc for why the configuration's own provider block is never read.
Types ¶
type Outcome ¶
type Outcome string
Outcome says how ManagedResults decided, for a caller that reports it.
const ( // OutcomeNotNeeded: the first pass refused nothing a managed value // could settle, so no provider was started. OutcomeNotNeeded Outcome = "not-needed" // OutcomeNoValues: the providers planned nothing. OutcomeNoValues Outcome = "no-values" // OutcomeKeptFirst: the second pass refused as many sites or more, or // demoted an instance to needs-discovery, so live-plan would keep the // first pass and so does this. OutcomeKeptFirst Outcome = "kept-first-pass" // OutcomeSecondPass: the planned values are returned. OutcomeSecondPass Outcome = "second-pass" )
func ManagedResults ¶
func ManagedResults(ctx context.Context, cfg *configs.Config, ictx identity.Context, provs projection.Providers) (map[string]cty.Value, Outcome)
ManagedResults is liveResolve's second pass, offline. ictx is the identity context the caller's [check.Analyze] call will resolve with (Cloud and Schemas; ManagedResults is ignored, and PendingDataReads is set as [check.Analyze] sets it, so both passes are compared the way the check will resolve). It returns the planned values when liveResolve would keep the second pass, and nil otherwise.
The decision is liveResolve's, rule for rule: no second pass when the first resolved cleanly or demanded no managed read (identity.DemandedManagedReads); and the second pass is kept only when it raises strictly fewer errors and demotes no instance to identity.ClassNeedsDiscovery. See liveResolve's doc comment for why each rule exists.
type Providers ¶
type Providers struct {
// contains filtered or unexported fields
}
Providers is a projection.Providers that configures every provider with ConfigValue. One configured instance serves every configuration of a provider type (aliases and module-level blocks included), because the offline value does not depend on which block asked. Close stops what it started.
func (*Providers) ConfiguredProvider ¶
func (p *Providers) ConfiguredProvider(ctx context.Context, addr addrs.AbsProviderConfig) (providers.Interface, error)
ConfiguredProvider implements projection.Providers.
type Sessions ¶
type Sessions struct {
// contains filtered or unexported fields
}
Sessions is the corpus tools' provider source for the second pass: one installed provider plugin per (provider, version), started on first use and kept until Sessions.Close, so a run over many configurations installs each release once.
The tools read schemas with pluginschema.Acquire, which closes its plugin and calls go-plugin's CleanupClients, ending every plugin process the program started. A Sessions survives that because it keeps the install, not a process: each Providers built over Sessions.For starts its own configured processes, and the caller closes that Providers before the next configuration.
func NewSessions ¶
NewSessions returns an empty Sessions that installs providers with initBin. Work directories are made with os.MkdirTemp, so TMPDIR decides where they go; a long TMPDIR can break go-plugin's socket handshake.
type Source ¶
type Source interface {
GetProviderSchema(ctx context.Context, addr addrs.Provider) (providers.ProviderSchema, tfdiags.Diagnostics)
NewConfiguredProvider(ctx context.Context, addr addrs.Provider, cfgVal cty.Value) (providers.Configured, tfdiags.Diagnostics)
}
Source starts providers. [plugins.ProviderManager] satisfies it, which is what the live-check command passes; the corpus tools pass a Sessions view.