github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/backend/backend.go (about)

     1  // Package backend provides interfaces that the CLI uses to interact with
     2  // Terraform. A backend provides the abstraction that allows the same CLI
     3  // to simultaneously support both local and remote operations for seamlessly
     4  // using Terraform in a team environment.
     5  package backend
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"time"
    11  
    12  	"github.com/hashicorp/terraform/addrs"
    13  	"github.com/hashicorp/terraform/command/clistate"
    14  	"github.com/hashicorp/terraform/configs"
    15  	"github.com/hashicorp/terraform/configs/configload"
    16  	"github.com/hashicorp/terraform/configs/configschema"
    17  	"github.com/hashicorp/terraform/plans"
    18  	"github.com/hashicorp/terraform/plans/planfile"
    19  	"github.com/hashicorp/terraform/states"
    20  	"github.com/hashicorp/terraform/states/statemgr"
    21  	"github.com/hashicorp/terraform/terraform"
    22  	"github.com/hashicorp/terraform/tfdiags"
    23  	"github.com/zclconf/go-cty/cty"
    24  )
    25  
    26  // DefaultStateName is the name of the default, initial state that every
    27  // backend must have. This state cannot be deleted.
    28  const DefaultStateName = "default"
    29  
    30  var (
    31  	// ErrDefaultWorkspaceNotSupported is returned when an operation does not
    32  	// support using the default workspace, but requires a named workspace to
    33  	// be selected.
    34  	ErrDefaultWorkspaceNotSupported = errors.New("default workspace not supported\n" +
    35  		"You can create a new workspace with the \"workspace new\" command.")
    36  
    37  	// ErrOperationNotSupported is returned when an unsupported operation
    38  	// is detected by the configured backend.
    39  	ErrOperationNotSupported = errors.New("operation not supported")
    40  
    41  	// ErrWorkspacesNotSupported is an error returned when a caller attempts
    42  	// to perform an operation on a workspace other than "default" for a
    43  	// backend that doesn't support multiple workspaces.
    44  	//
    45  	// The caller can detect this to do special fallback behavior or produce
    46  	// a specific, helpful error message.
    47  	ErrWorkspacesNotSupported = errors.New("workspaces not supported")
    48  )
    49  
    50  // InitFn is used to initialize a new backend.
    51  type InitFn func() Backend
    52  
    53  // Backend is the minimal interface that must be implemented to enable Terraform.
    54  type Backend interface {
    55  	// ConfigSchema returns a description of the expected configuration
    56  	// structure for the receiving backend.
    57  	//
    58  	// This method does not have any side-effects for the backend and can
    59  	// be safely used before configuring.
    60  	ConfigSchema() *configschema.Block
    61  
    62  	// PrepareConfig checks the validity of the values in the given
    63  	// configuration, and inserts any missing defaults, assuming that its
    64  	// structure has already been validated per the schema returned by
    65  	// ConfigSchema.
    66  	//
    67  	// This method does not have any side-effects for the backend and can
    68  	// be safely used before configuring. It also does not consult any
    69  	// external data such as environment variables, disk files, etc. Validation
    70  	// that requires such external data should be deferred until the
    71  	// Configure call.
    72  	//
    73  	// If error diagnostics are returned then the configuration is not valid
    74  	// and must not subsequently be passed to the Configure method.
    75  	//
    76  	// This method may return configuration-contextual diagnostics such
    77  	// as tfdiags.AttributeValue, and so the caller should provide the
    78  	// necessary context via the diags.InConfigBody method before returning
    79  	// diagnostics to the user.
    80  	PrepareConfig(cty.Value) (cty.Value, tfdiags.Diagnostics)
    81  
    82  	// Configure uses the provided configuration to set configuration fields
    83  	// within the backend.
    84  	//
    85  	// The given configuration is assumed to have already been validated
    86  	// against the schema returned by ConfigSchema and passed validation
    87  	// via PrepareConfig.
    88  	//
    89  	// This method may be called only once per backend instance, and must be
    90  	// called before all other methods except where otherwise stated.
    91  	//
    92  	// If error diagnostics are returned, the internal state of the instance
    93  	// is undefined and no other methods may be called.
    94  	Configure(cty.Value) tfdiags.Diagnostics
    95  
    96  	// StateMgr returns the state manager for the given workspace name.
    97  	//
    98  	// If the returned state manager also implements statemgr.Locker then
    99  	// it's the caller's responsibility to call Lock and Unlock as appropriate.
   100  	//
   101  	// If the named workspace doesn't exist, or if it has no state, it will
   102  	// be created either immediately on this call or the first time
   103  	// PersistState is called, depending on the state manager implementation.
   104  	StateMgr(workspace string) (statemgr.Full, error)
   105  
   106  	// StateMgrWithoutCheckVersion returns the state manager for the given
   107  	// workspace name, while ensuring that Terraform version checks are not
   108  	// performed if the backend needs to read a state file in order to
   109  	// initialize the state manager.
   110  	//
   111  	// For backends which do not need to read a state file at this point, this
   112  	// is identical to StateMgr.
   113  	//
   114  	// This is used to facilitate reading compatible state files from newer
   115  	// versions of Terraform.
   116  	StateMgrWithoutCheckVersion(workspace string) (statemgr.Full, error)
   117  
   118  	// DeleteWorkspace removes the workspace with the given name if it exists.
   119  	//
   120  	// DeleteWorkspace cannot prevent deleting a state that is in use. It is
   121  	// the responsibility of the caller to hold a Lock for the state manager
   122  	// belonging to this workspace before calling this method.
   123  	DeleteWorkspace(name string) error
   124  
   125  	// States returns a list of the names of all of the workspaces that exist
   126  	// in this backend.
   127  	Workspaces() ([]string, error)
   128  }
   129  
   130  // Enhanced implements additional behavior on top of a normal backend.
   131  //
   132  // Enhanced backends allow customizing the behavior of Terraform operations.
   133  // This allows Terraform to potentially run operations remotely, load
   134  // configurations from external sources, etc.
   135  type Enhanced interface {
   136  	Backend
   137  
   138  	// Operation performs a Terraform operation such as refresh, plan, apply.
   139  	// It is up to the implementation to determine what "performing" means.
   140  	// This DOES NOT BLOCK. The context returned as part of RunningOperation
   141  	// should be used to block for completion.
   142  	// If the state used in the operation can be locked, it is the
   143  	// responsibility of the Backend to lock the state for the duration of the
   144  	// running operation.
   145  	Operation(context.Context, *Operation) (*RunningOperation, error)
   146  }
   147  
   148  // Local implements additional behavior on a Backend that allows local
   149  // operations in addition to remote operations.
   150  //
   151  // This enables more behaviors of Terraform that require more data such
   152  // as `console`, `import`, `graph`. These require direct access to
   153  // configurations, variables, and more. Not all backends may support this
   154  // so we separate it out into its own optional interface.
   155  type Local interface {
   156  	// Context returns a runnable terraform Context. The operation parameter
   157  	// doesn't need a Type set but it needs other options set such as Module.
   158  	Context(*Operation) (*terraform.Context, statemgr.Full, tfdiags.Diagnostics)
   159  }
   160  
   161  // An operation represents an operation for Terraform to execute.
   162  //
   163  // Note that not all fields are supported by all backends and can result
   164  // in an error if set. All backend implementations should show user-friendly
   165  // errors explaining any incorrectly set values. For example, the local
   166  // backend doesn't support a PlanId being set.
   167  //
   168  // The operation options are purposely designed to have maximal compatibility
   169  // between Terraform and Terraform Servers (a commercial product offered by
   170  // HashiCorp). Therefore, it isn't expected that other implementation support
   171  // every possible option. The struct here is generalized in order to allow
   172  // even partial implementations to exist in the open, without walling off
   173  // remote functionality 100% behind a commercial wall. Anyone can implement
   174  // against this interface and have Terraform interact with it just as it
   175  // would with HashiCorp-provided Terraform Servers.
   176  type Operation struct {
   177  	// Type is the operation to perform.
   178  	Type OperationType
   179  
   180  	// PlanId is an opaque value that backends can use to execute a specific
   181  	// plan for an apply operation.
   182  	//
   183  	// PlanOutBackend is the backend to store with the plan. This is the
   184  	// backend that will be used when applying the plan.
   185  	PlanId         string
   186  	PlanRefresh    bool   // PlanRefresh will do a refresh before a plan
   187  	PlanOutPath    string // PlanOutPath is the path to save the plan
   188  	PlanOutBackend *plans.Backend
   189  
   190  	// ConfigDir is the path to the directory containing the configuration's
   191  	// root module.
   192  	ConfigDir string
   193  
   194  	// ConfigLoader is a configuration loader that can be used to load
   195  	// configuration from ConfigDir.
   196  	ConfigLoader *configload.Loader
   197  
   198  	// Plan is a plan that was passed as an argument. This is valid for
   199  	// plan and apply arguments but may not work for all backends.
   200  	PlanFile *planfile.Reader
   201  
   202  	// The options below are more self-explanatory and affect the runtime
   203  	// behavior of the operation.
   204  	AutoApprove  bool
   205  	Destroy      bool
   206  	DestroyForce bool
   207  	Parallelism  int
   208  	Targets      []addrs.Targetable
   209  	Variables    map[string]UnparsedVariableValue
   210  
   211  	// Some operations use root module variables only opportunistically or
   212  	// don't need them at all. If this flag is set, the backend must treat
   213  	// all variables as optional and provide an unknown value for any required
   214  	// variables that aren't set in order to allow partial evaluation against
   215  	// the resulting incomplete context.
   216  	//
   217  	// This flag is honored only if PlanFile isn't set. If PlanFile is set then
   218  	// the variables set in the plan are used instead, and they must be valid.
   219  	AllowUnsetVariables bool
   220  
   221  	// Input/output/control options.
   222  	UIIn  terraform.UIInput
   223  	UIOut terraform.UIOutput
   224  
   225  	// If LockState is true, the Operation must Lock any
   226  	// state.Lockers for its duration, and Unlock when complete.
   227  	LockState bool
   228  
   229  	// StateLocker is used to lock the state while providing UI feedback to the
   230  	// user. This will be supplied by the Backend itself.
   231  	StateLocker clistate.Locker
   232  
   233  	// The duration to retry obtaining a State lock.
   234  	StateLockTimeout time.Duration
   235  
   236  	// Workspace is the name of the workspace that this operation should run
   237  	// in, which controls which named state is used.
   238  	Workspace string
   239  }
   240  
   241  // HasConfig returns true if and only if the operation has a ConfigDir value
   242  // that refers to a directory containing at least one Terraform configuration
   243  // file.
   244  func (o *Operation) HasConfig() bool {
   245  	return o.ConfigLoader.IsConfigDir(o.ConfigDir)
   246  }
   247  
   248  // Config loads the configuration that the operation applies to, using the
   249  // ConfigDir and ConfigLoader fields within the receiving operation.
   250  func (o *Operation) Config() (*configs.Config, tfdiags.Diagnostics) {
   251  	var diags tfdiags.Diagnostics
   252  	config, hclDiags := o.ConfigLoader.LoadConfig(o.ConfigDir)
   253  	diags = diags.Append(hclDiags)
   254  	return config, diags
   255  }
   256  
   257  // RunningOperation is the result of starting an operation.
   258  type RunningOperation struct {
   259  	// For implementers of a backend, this context should not wrap the
   260  	// passed in context. Otherwise, cancelling the parent context will
   261  	// immediately mark this context as "done" but those aren't the semantics
   262  	// we want: we want this context to be done only when the operation itself
   263  	// is fully done.
   264  	context.Context
   265  
   266  	// Stop requests the operation to complete early, by calling Stop on all
   267  	// the plugins. If the process needs to terminate immediately, call Cancel.
   268  	Stop context.CancelFunc
   269  
   270  	// Cancel is the context.CancelFunc associated with the embedded context,
   271  	// and can be called to terminate the operation early.
   272  	// Once Cancel is called, the operation should return as soon as possible
   273  	// to avoid running operations during process exit.
   274  	Cancel context.CancelFunc
   275  
   276  	// Result is the exit status of the operation, populated only after the
   277  	// operation has completed.
   278  	Result OperationResult
   279  
   280  	// PlanEmpty is populated after a Plan operation completes without error
   281  	// to note whether a plan is empty or has changes.
   282  	PlanEmpty bool
   283  
   284  	// State is the final state after the operation completed. Persisting
   285  	// this state is managed by the backend. This should only be read
   286  	// after the operation completes to avoid read/write races.
   287  	State *states.State
   288  }
   289  
   290  // OperationResult describes the result status of an operation.
   291  type OperationResult int
   292  
   293  const (
   294  	// OperationSuccess indicates that the operation completed as expected.
   295  	OperationSuccess OperationResult = 0
   296  
   297  	// OperationFailure indicates that the operation encountered some sort
   298  	// of error, and thus may have been only partially performed or not
   299  	// performed at all.
   300  	OperationFailure OperationResult = 1
   301  )
   302  
   303  func (r OperationResult) ExitStatus() int {
   304  	return int(r)
   305  }