backend

package
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2024 License: MPL-2.0 Imports: 33 Imported by: 0

Documentation

Overview

Package backend provides interfaces that the CLI uses to interact with OpenTofu. A backend provides the abstraction that allows the same CLI to simultaneously support both local and remote operations for seamlessly using OpenTofu in a team environment.

Index

Constants

View Source
const DefaultStateName = "default"

DefaultStateName is the name of the default, initial state that every backend must have. This state cannot be deleted.

Variables

View Source
var (
	// ErrDefaultWorkspaceNotSupported is returned when an operation does not
	// support using the default workspace, but requires a named workspace to
	// be selected.
	ErrDefaultWorkspaceNotSupported = errors.New("default workspace not supported\n" +
		"You can create a new workspace with the \"workspace new\" command.")

	// ErrWorkspacesNotSupported is an error returned when a caller attempts
	// to perform an operation on a workspace other than "default" for a
	// backend that doesn't support multiple workspaces.
	//
	// The caller can detect this to do special fallback behavior or produce
	// a specific, helpful error message.
	ErrWorkspacesNotSupported = errors.New("workspaces not supported")
)

Functions

func ParseDeclaredVariableValues

func ParseDeclaredVariableValues(vv map[string]UnparsedVariableValue, decls map[string]*configs.Variable) (tofu.InputValues, tfdiags.Diagnostics)

ParseDeclaredVariableValues processes a map of unparsed variable values and returns an input values map of the ones declared in the specified variable declaration mapping. Diagnostics will be populating with any variable parsing errors encountered within this collection.

func ParseUndeclaredVariableValues

func ParseUndeclaredVariableValues(vv map[string]UnparsedVariableValue, decls map[string]*configs.Variable) (tofu.InputValues, tfdiags.Diagnostics)

ParseUndeclaredVariableValues processes a map of unparsed variable values and returns an input values map of the ones not declared in the specified declaration map along with detailed diagnostics about values of undeclared variables being present, depending on the source of these values. If more than two undeclared values are present in file form (config, auto, -var-file) the remaining errors are summarized to avoid a massive list of errors.

func ParseVariableValues

func ParseVariableValues(vv map[string]UnparsedVariableValue, decls map[string]*configs.Variable) (tofu.InputValues, tfdiags.Diagnostics)

ParseVariableValues processes a map of unparsed variable values by correlating each one with the given variable declarations which should be from a root module.

The map of unparsed variable values should include variables from all possible root module declarations sources such that it is as complete as it can possibly be for the current operation. If any declared variables are not included in the map, ParseVariableValues will either substitute a configured default value or produce an error.

If this function returns without any errors in the diagnostics, the resulting input values map is guaranteed to be valid and ready to pass to tofu.NewContext. If the diagnostics contains errors, the returned InputValues may be incomplete but will include the subset of variables that were successfully processed, allowing for careful analysis of the partial result.

func ReadPathOrContents

func ReadPathOrContents(poc string) (string, error)

If the argument is a path, ReadPathOrContents loads it and returns the contents, otherwise the argument is assumed to be the desired contents and is simply returned.

func TestBackendStateForceUnlock

func TestBackendStateForceUnlock(t *testing.T, b1, b2 Backend)

TestBackendStateForceUnlock verifies that the lock error is the expected type, and the lock can be unlocked using the ID reported in the error. Remote state backends that support -force-unlock should call this in at least one of the acceptance tests.

func TestBackendStateForceUnlockInWS

func TestBackendStateForceUnlockInWS(t *testing.T, b1, b2 Backend, ws string)

TestBackendStateForceUnlockInWS verifies that the lock error is the expected type, and the lock can be unlocked using the ID reported in the error. Remote state backends that support -force-unlock should call this in at least one of the acceptance tests.

func TestBackendStateLocks

func TestBackendStateLocks(t *testing.T, b1, b2 Backend)

TestBackendStateLocks will test the locking functionality of the remote state backend.

func TestBackendStateLocksInWS

func TestBackendStateLocksInWS(t *testing.T, b1, b2 Backend, ws string)

TestBackendStateLocksInWS will test the locking functionality of the remote state backend.

func TestBackendStates

func TestBackendStates(t *testing.T, b Backend)

TestBackend will test the functionality of a Backend. The backend is assumed to already be configured. This will test state functionality. If the backend reports it doesn't support multi-state by returning the error ErrWorkspacesNotSupported, then it will not test that.

func TestWrapConfig

func TestWrapConfig(raw map[string]interface{}) hcl.Body

TestWrapConfig takes a raw data structure and converts it into a synthetic hcl.Body to use for testing.

The given structure should only include values that can be accepted by hcl2shim.HCL2ValueFromConfigValue. If incompatible values are given, this function will panic.

Types

type Backend

type Backend interface {
	// ConfigSchema returns a description of the expected configuration
	// structure for the receiving backend.
	//
	// This method does not have any side-effects for the backend and can
	// be safely used before configuring.
	ConfigSchema() *configschema.Block

	// PrepareConfig checks the validity of the values in the given
	// configuration, and inserts any missing defaults, assuming that its
	// structure has already been validated per the schema returned by
	// ConfigSchema.
	//
	// This method does not have any side-effects for the backend and can
	// be safely used before configuring. It also does not consult any
	// external data such as environment variables, disk files, etc. Validation
	// that requires such external data should be deferred until the
	// Configure call.
	//
	// If error diagnostics are returned then the configuration is not valid
	// and must not subsequently be passed to the Configure method.
	//
	// This method may return configuration-contextual diagnostics such
	// as tfdiags.AttributeValue, and so the caller should provide the
	// necessary context via the diags.InConfigBody method before returning
	// diagnostics to the user.
	PrepareConfig(cty.Value) (cty.Value, tfdiags.Diagnostics)

	// Configure uses the provided configuration to set configuration fields
	// within the backend.
	//
	// The given configuration is assumed to have already been validated
	// against the schema returned by ConfigSchema and passed validation
	// via PrepareConfig.
	//
	// This method may be called only once per backend instance, and must be
	// called before all other methods except where otherwise stated.
	//
	// If error diagnostics are returned, the internal state of the instance
	// is undefined and no other methods may be called.
	Configure(cty.Value) tfdiags.Diagnostics

	// StateMgr returns the state manager for the given workspace name.
	//
	// If the returned state manager also implements statemgr.Locker then
	// it's the caller's responsibility to call Lock and Unlock as appropriate.
	//
	// If the named workspace doesn't exist, or if it has no state, it will
	// be created either immediately on this call or the first time
	// PersistState is called, depending on the state manager implementation.
	StateMgr(workspace string) (statemgr.Full, error)

	// DeleteWorkspace removes the workspace with the given name if it exists.
	//
	// DeleteWorkspace cannot prevent deleting a state that is in use. It is
	// the responsibility of the caller to hold a Lock for the state manager
	// belonging to this workspace before calling this method.
	DeleteWorkspace(name string, force bool) error

	// States returns a list of the names of all of the workspaces that exist
	// in this backend.
	Workspaces() ([]string, error)
}

Backend is the minimal interface that must be implemented to enable OpenTofu.

func TestBackendConfig

func TestBackendConfig(t *testing.T, b Backend, c hcl.Body) Backend

TestBackendConfig validates and configures the backend with the given configuration.

type CLI

type CLI interface {
	Backend

	// CLIInit is called once with options. The options passed to this
	// function may not be modified after calling this since they can be
	// read/written at any time by the Backend implementation.
	//
	// This may be called before or after Configure is called, so if settings
	// here affect configurable settings, care should be taken to handle
	// whether they should be overwritten or not.
	CLIInit(*CLIOpts) error
}

CLI is an optional interface that can be implemented to be initialized with information from the OpenTofu CLI. If this is implemented, this initialization function will be called with data to help interact better with a CLI.

This interface was created to improve backend interaction with the official OpenTofu CLI while making it optional for API users to have to provide full CLI interaction to every backend.

If you're implementing a Backend, it is acceptable to require CLI initialization. In this case, your backend should be coded to error on other methods (such as State, Operation) if CLI initialization was not done with all required fields.

type CLIOpts

type CLIOpts struct {
	// CLI and Colorize control the CLI output. If CLI is nil then no CLI
	// output will be done. If CLIColor is nil then no coloring will be done.
	CLI      cli.Ui
	CLIColor *colorstring.Colorize

	// Streams describes the low-level streams for Stdout, Stderr and Stdin,
	// including some metadata about whether they are terminals. Most output
	// should go via the object in field CLI above, but Streams can be useful
	// for tailoring the output to fit the attached terminal, for example.
	Streams *terminal.Streams

	// StatePath is the local path where state is read from.
	//
	// StateOutPath is the local path where the state will be written.
	// If this is empty, it will default to StatePath.
	//
	// StateBackupPath is the local path where a backup file will be written.
	// If this is empty, no backup will be taken.
	StatePath       string
	StateOutPath    string
	StateBackupPath string

	// ContextOpts are the base context options to set when initializing a
	// OpenTofu context. Many of these will be overridden or merged by
	// Operation. See Operation for more details.
	ContextOpts *tofu.ContextOpts

	// Input will ask for necessary input prior to performing any operations.
	//
	// Validation will perform validation prior to running an operation. The
	// variable naming doesn't match the style of others since we have a func
	// Validate.
	Input      bool
	Validation bool

	// RunningInAutomation indicates that commands are being run by an
	// automated system rather than directly at a command prompt.
	//
	// This is a hint not to produce messages that expect that a user can
	// run a follow-up command, perhaps because OpenTofu is running in
	// some sort of workflow automation tool that abstracts away the
	// exact commands that are being run.
	RunningInAutomation bool
}

CLIOpts are the options passed into CLIInit for the CLI interface.

These options represent the functionality the CLI exposes and often maps to meta-flags available on every CLI (such as -input).

When implementing a backend, it isn't expected that every option applies. Your backend should be documented clearly to explain to end users what options have an affect and what won't. In some cases, it may even make sense to error in your backend when an option is set so that users don't make a critically incorrect assumption about behavior.

type Enhanced

type Enhanced interface {
	Backend

	// Operation performs a OpenTofu operation such as refresh, plan, apply.
	// It is up to the implementation to determine what "performing" means.
	// This DOES NOT BLOCK. The context returned as part of RunningOperation
	// should be used to block for completion.
	// If the state used in the operation can be locked, it is the
	// responsibility of the Backend to lock the state for the duration of the
	// running operation.
	Operation(context.Context, *Operation) (*RunningOperation, error)

	// ServiceDiscoveryAliases returns a mapping of Alias -> Target hosts to
	// configure.
	ServiceDiscoveryAliases() ([]HostAlias, error)
}

Enhanced implements additional behavior on top of a normal backend.

'Enhanced' backends are an implementation detail only, and are no longer reflected as an external 'feature' of backends. In other words, backends refer to plugins for remote state snapshot storage only, and the Enhanced interface here is a necessary vestige of the 'local' and remote/cloud backends only.

type HostAlias

type HostAlias struct {
	From svchost.Hostname
	To   svchost.Hostname
}

HostAlias describes a list of aliases that should be used when initializing an Enhanced Backend

type InitFn

type InitFn func(encryption.StateEncryption) Backend

InitFn is used to initialize a new backend.

type Local

type Local interface {
	// LocalRun uses information in the Operation to prepare a set of objects
	// needed to start running that operation.
	//
	// The operation doesn't need a Type set, but it needs various other
	// options set. This is a rather odd API that tries to treat all
	// operations as the same when they really aren't; see the local and remote
	// backend's implementations of this to understand what this actually
	// does, because this operation has no well-defined contract aside from
	// "whatever it already does".
	LocalRun(*Operation) (*LocalRun, statemgr.Full, tfdiags.Diagnostics)
}

Local implements additional behavior on a Backend that allows local operations in addition to remote operations.

This enables more behaviors of OpenTofu that require more data such as `console`, `import`, `graph`. These require direct access to configurations, variables, and more. Not all backends may support this so we separate it out into its own optional interface.

type LocalRun

type LocalRun struct {
	// Core is an already-initialized OpenTofu Core context, ready to be
	// used to run operations such as Plan and Apply.
	Core *tofu.Context

	// Config is the configuration we're working with, which typically comes
	// from either config files directly on local disk (when we're creating
	// a plan, or similar) or from a snapshot embedded in a plan file
	// (when we're applying a saved plan).
	Config *configs.Config

	// InputState is the state that should be used for whatever is the first
	// method call to a context created with CoreOpts. When creating a plan
	// this will be the previous run state, but when applying a saved plan
	// this will be the prior state recorded in that plan.
	InputState *states.State

	// PlanOpts are options to pass to a Plan or Plan-like operation.
	//
	// This is nil when we're applying a saved plan, because the plan itself
	// contains enough information about its options to apply it.
	PlanOpts *tofu.PlanOpts

	// Plan is a plan loaded from a saved plan file, if our operation is to
	// apply that saved plan.
	//
	// This is nil when we're not applying a saved plan.
	Plan *plans.Plan
}

LocalRun represents the assortment of objects that we can collect or calculate from an Operation object, which we can then use for local operations.

The operation methods on tofu.Context (Plan, Apply, Import, etc) each generate new artifacts which supersede parts of the LocalRun object that started the operation, so callers should be careful to use those subsequent artifacts instead of the fields of LocalRun where appropriate. The LocalRun data intentionally doesn't update as a result of calling methods on Context, in order to make data flow explicit.

This type is a weird architectural wart resulting from the overly-general way our backend API models operations, whereby we behave as if all OpenTofu operations have the same inputs and outputs even though they are actually all rather different. The exact meaning of the fields in this type therefore vary depending on which OperationType was passed to Local.Context in order to create an object of this type.

type Operation

type Operation struct {
	// Type is the operation to perform.
	Type OperationType

	// Encryption is used by enhanced backends for planning and tofu.Context initialization
	Encryption encryption.Encryption

	// PlanId is an opaque value that backends can use to execute a specific
	// plan for an apply operation.
	//
	// PlanOutBackend is the backend to store with the plan. This is the
	// backend that will be used when applying the plan.
	PlanId         string
	PlanRefresh    bool   // PlanRefresh will do a refresh before a plan
	PlanOutPath    string // PlanOutPath is the path to save the plan
	PlanOutBackend *plans.Backend

	// ConfigDir is the path to the directory containing the configuration's
	// root module.
	ConfigDir string

	// ConfigLoader is a configuration loader that can be used to load
	// configuration from ConfigDir.
	ConfigLoader *configload.Loader

	// DependencyLocks represents the locked dependencies associated with
	// the configuration directory given in ConfigDir.
	//
	// Note that if field PlanFile is set then the plan file should contain
	// its own dependency locks. The backend is responsible for correctly
	// selecting between these two sets of locks depending on whether it
	// will be using ConfigDir or PlanFile to get the configuration for
	// this operation.
	DependencyLocks *depsfile.Locks

	// Hooks can be used to perform actions triggered by various events during
	// the operation's lifecycle.
	Hooks []tofu.Hook

	// Plan is a plan that was passed as an argument. This is valid for
	// plan and apply arguments but may not work for all backends.
	PlanFile *planfile.WrappedPlanFile

	// The options below are more self-explanatory and affect the runtime
	// behavior of the operation.
	PlanMode     plans.Mode
	AutoApprove  bool
	Targets      []addrs.Targetable
	ForceReplace []addrs.AbsResourceInstance
	// Injected by the command creating the operation (plan/apply/refresh/etc...)
	Variables map[string]UnparsedVariableValue
	RootCall  configs.StaticModuleCall

	// Some operations use root module variables only opportunistically or
	// don't need them at all. If this flag is set, the backend must treat
	// all variables as optional and provide an unknown value for any required
	// variables that aren't set in order to allow partial evaluation against
	// the resulting incomplete context.
	//
	// This flag is honored only if PlanFile isn't set. If PlanFile is set then
	// the variables set in the plan are used instead, and they must be valid.
	AllowUnsetVariables bool

	// View implements the logic for all UI interactions.
	View views.Operation

	// Input/output/control options.
	UIIn  tofu.UIInput
	UIOut tofu.UIOutput

	// StateLocker is used to lock the state while providing UI feedback to the
	// user. This will be replaced by the Backend to update the context.
	//
	// If state locking is not necessary, this should be set to a no-op
	// implementation of clistate.Locker.
	StateLocker clistate.Locker

	// Workspace is the name of the workspace that this operation should run
	// in, which controls which named state is used.
	Workspace string

	// GenerateConfigOut tells the operation both that it should generate config
	// for unmatched import targets and where any generated config should be
	// written to.
	GenerateConfigOut string
}

An operation represents an operation for OpenTofu to execute.

Note that not all fields are supported by all backends and can result in an error if set. All backend implementations should show user-friendly errors explaining any incorrectly set values. For example, the local backend doesn't support a PlanId being set.

The operation options are purposely designed to have maximal compatibility between OpenTofu and Terraform Servers (a commercial product offered by HashiCorp). Therefore, it isn't expected that other implementation support every possible option. The struct here is generalized in order to allow even partial implementations to exist in the open, without walling off remote functionality 100% behind a commercial wall. Anyone can implement against this interface and have OpenTofu interact with it just as it would with HashiCorp-provided Terraform Servers.

func (*Operation) HasConfig

func (o *Operation) HasConfig() bool

HasConfig returns true if and only if the operation has a ConfigDir value that refers to a directory containing at least one OpenTofu configuration file.

func (*Operation) ReportResult

func (o *Operation) ReportResult(op *RunningOperation, diags tfdiags.Diagnostics)

ReportResult is a helper for the common chore of setting the status of a running operation and showing any diagnostics produced during that operation.

If the given diagnostics contains errors then the operation's result will be set to backend.OperationFailure. It will be set to backend.OperationSuccess otherwise. It will then use o.View.Diagnostics to show the given diagnostics before returning.

Callers should feel free to do each of these operations separately in more complex cases where e.g. diagnostics are interleaved with other output, but terminating immediately after reporting error diagnostics is common and can be expressed concisely via this method.

type OperationResult

type OperationResult int

OperationResult describes the result status of an operation.

const (
	// OperationSuccess indicates that the operation completed as expected.
	OperationSuccess OperationResult = 0

	// OperationFailure indicates that the operation encountered some sort
	// of error, and thus may have been only partially performed or not
	// performed at all.
	OperationFailure OperationResult = 1
)

func (OperationResult) ExitStatus

func (r OperationResult) ExitStatus() int

type OperationType

type OperationType uint

OperationType is an enum used with Operation to specify the operation type to perform for OpenTofu.

const (
	OperationTypeInvalid OperationType = iota
	OperationTypeRefresh
	OperationTypePlan
	OperationTypeApply
)

func (OperationType) String

func (i OperationType) String() string

type RunningOperation

type RunningOperation struct {
	// For implementers of a backend, this context should not wrap the
	// passed in context. Otherwise, cancelling the parent context will
	// immediately mark this context as "done" but those aren't the semantics
	// we want: we want this context to be done only when the operation itself
	// is fully done.
	context.Context

	// Stop requests the operation to complete early, by calling Stop on all
	// the plugins. If the process needs to terminate immediately, call Cancel.
	Stop context.CancelFunc

	// Cancel is the context.CancelFunc associated with the embedded context,
	// and can be called to terminate the operation early.
	// Once Cancel is called, the operation should return as soon as possible
	// to avoid running operations during process exit.
	Cancel context.CancelFunc

	// Result is the exit status of the operation, populated only after the
	// operation has completed.
	Result OperationResult

	// PlanEmpty is populated after a Plan operation completes to note whether
	// a plan is empty or has changes. This is only used in the CLI to determine
	// the exit status because the plan value is not available at that point.
	PlanEmpty bool

	// State is the final state after the operation completed. Persisting
	// this state is managed by the backend. This should only be read
	// after the operation completes to avoid read/write races.
	State *states.State
}

RunningOperation is the result of starting an operation.

type UnparsedVariableValue

type UnparsedVariableValue interface {
	// ParseVariableValue information in the provided variable configuration
	// to parse (if necessary) and return the variable value encapsulated in
	// the receiver.
	//
	// If error diagnostics are returned, the resulting value may be invalid
	// or incomplete.
	ParseVariableValue(mode configs.VariableParsingMode) (*tofu.InputValue, tfdiags.Diagnostics)
}

UnparsedVariableValue represents a variable value provided by the caller whose parsing must be deferred until configuration is available.

This exists to allow processing of variable-setting arguments (e.g. in the command package) to be separated from parsing (in the backend package).

Directories

Path Synopsis
Package init contains the list of backends that can be initialized and basic helper functions for initializing those backends.
Package init contains the list of backends that can be initialized and basic helper functions for initializing those backends.
remote-state
cos
gcs
Package gcs implements remote storage of state on Google Cloud Storage (GCS).
Package gcs implements remote storage of state on Google Cloud Storage (GCS).
oss
pg
s3

Jump to

Keyboard shortcuts

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