2024-02-08 03:48:59 -06:00
// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
2023-05-02 10:33:06 -05:00
// SPDX-License-Identifier: MPL-2.0
2017-01-18 22:50:04 -06:00
package command
// This file contains all the Backend-related function calls on Meta,
// exported and private.
import (
command: Early error message for missing cache entries of locked providers
In the original incarnation of Meta.providerFactories we were returning
into a Meta.contextOpts whose signature didn't allow it to return an
error directly, and so we had compromised by making the provider factory
functions themselves return errors once called.
Subsequent work made Meta.contextOpts need to return an error anyway, but
at the time we neglected to update our handling of the providerFactories
result, having it still defer the error handling until we finally
instantiate a provider.
Although that did ultimately get the expected result anyway, the error
ended up being reported from deep in the guts of a Terraform Core graph
walk, in whichever concurrently-visited graph node happened to try to
instantiate the plugin first. This meant that the exact phrasing of the
error message would vary between runs and the reporting codepath didn't
have enough context to given an actionable suggestion on how to proceed.
In this commit we make Meta.contextOpts pass through directly any error
that Meta.providerFactories produces, and then make Meta.providerFactories
produce a special error type so that Meta.Backend can ultimately return
a user-friendly diagnostic message containing a specific suggestion to
run "terraform init", along with a short explanation of what a provider
plugin is.
The reliance here on an implied contract between two functions that are
not directly connected in the callstack is non-ideal, and so hopefully
we'll revisit this further in future work on the overall architecture of
the CLI layer. To try to make this robust in the meantime though, I wrote
it to use the errors.As function to potentially unwrap a wrapped version
of our special error type, in case one of the intervening layers is
changed at some point to wrap the downstream error before returning it.
2021-10-01 16:51:06 -05:00
"bytes"
2017-04-01 14:42:13 -05:00
"context"
2018-03-27 17:31:05 -05:00
"encoding/json"
command: Early error message for missing cache entries of locked providers
In the original incarnation of Meta.providerFactories we were returning
into a Meta.contextOpts whose signature didn't allow it to return an
error directly, and so we had compromised by making the provider factory
functions themselves return errors once called.
Subsequent work made Meta.contextOpts need to return an error anyway, but
at the time we neglected to update our handling of the providerFactories
result, having it still defer the error handling until we finally
instantiate a provider.
Although that did ultimately get the expected result anyway, the error
ended up being reported from deep in the guts of a Terraform Core graph
walk, in whichever concurrently-visited graph node happened to try to
instantiate the plugin first. This meant that the exact phrasing of the
error message would vary between runs and the reporting codepath didn't
have enough context to given an actionable suggestion on how to proceed.
In this commit we make Meta.contextOpts pass through directly any error
that Meta.providerFactories produces, and then make Meta.providerFactories
produce a special error type so that Meta.Backend can ultimately return
a user-friendly diagnostic message containing a specific suggestion to
run "terraform init", along with a short explanation of what a provider
plugin is.
The reliance here on an implied contract between two functions that are
not directly connected in the callstack is non-ideal, and so hopefully
we'll revisit this further in future work on the overall architecture of
the CLI layer. To try to make this robust in the meantime though, I wrote
it to use the errors.As function to potentially unwrap a wrapped version
of our special error type, in case one of the intervening layers is
changed at some point to wrap the downstream error before returning it.
2021-10-01 16:51:06 -05:00
"errors"
2017-01-18 22:50:04 -06:00
"fmt"
"log"
"path/filepath"
2018-10-31 10:45:03 -05:00
"strconv"
2017-01-18 22:50:04 -06:00
"strings"
2019-09-09 17:58:44 -05:00
"github.com/hashicorp/hcl/v2"
2023-08-09 04:19:00 -05:00
"github.com/zclconf/go-cty/cty"
ctyjson "github.com/zclconf/go-cty/cty/json"
2023-09-20 06:35:35 -05:00
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/cloud"
"github.com/opentofu/opentofu/internal/command/arguments"
"github.com/opentofu/opentofu/internal/command/clistate"
"github.com/opentofu/opentofu/internal/command/views"
"github.com/opentofu/opentofu/internal/configs"
2024-03-04 08:25:14 -06:00
"github.com/opentofu/opentofu/internal/encryption"
2023-09-20 06:35:35 -05:00
"github.com/opentofu/opentofu/internal/plans"
"github.com/opentofu/opentofu/internal/states/statemgr"
"github.com/opentofu/opentofu/internal/tfdiags"
2023-09-20 07:16:53 -05:00
"github.com/opentofu/opentofu/internal/tofu"
2023-09-20 06:35:35 -05:00
backendInit "github.com/opentofu/opentofu/internal/backend/init"
backendLocal "github.com/opentofu/opentofu/internal/backend/local"
2023-09-20 08:10:32 -05:00
legacy "github.com/opentofu/opentofu/internal/legacy/tofu"
2017-01-18 22:50:04 -06:00
)
// BackendOpts are the options used to initialize a backend.Backend.
type BackendOpts struct {
2018-03-20 20:43:02 -05:00
// Config is a representation of the backend configuration block given in
// the root module, or nil if no such block is present.
Config * configs . Backend
2017-01-18 22:50:04 -06:00
2018-03-27 17:31:05 -05:00
// ConfigOverride is an hcl.Body that, if non-nil, will be used with
// configs.MergeBodies to override the type-specific backend configuration
// arguments in Config.
ConfigOverride hcl . Body
2017-03-17 01:27:05 -05:00
2017-01-18 22:50:04 -06:00
// Init should be set to true if initialization is allowed. If this is
// false, then any configuration that requires configuration will show
// an error asking the user to reinitialize.
Init bool
// ForceLocal will force a purely local backend, including state.
// You probably don't want to set this.
ForceLocal bool
2023-02-07 02:06:12 -06:00
// ViewType will set console output format for the
// initialization operation (JSON or human-readable).
ViewType arguments . ViewType
2017-01-18 22:50:04 -06:00
}
2021-08-24 14:28:12 -05:00
// BackendWithRemoteTerraformVersion is a shared interface between the 'remote' and 'cloud' backends
// for simplified type checking when calling functions common to those particular backends.
type BackendWithRemoteTerraformVersion interface {
IgnoreVersionConflict ( )
VerifyWorkspaceTerraformVersion ( workspace string ) tfdiags . Diagnostics
2021-10-29 21:23:28 -05:00
IsLocalOperations ( ) bool
2021-08-24 14:28:12 -05:00
}
2017-01-18 22:50:04 -06:00
// Backend initializes and returns the backend for this CLI session.
//
2023-09-26 12:09:27 -05:00
// The backend is used to perform the actual OpenTofu operations. This
// abstraction enables easily sliding in new OpenTofu behavior such as
2017-01-18 22:50:04 -06:00
// remote state storage, remote operations, etc. while allowing the CLI
// to remain mostly identical.
//
// This will initialize a new backend for each call, which can carry some
// overhead with it. Please reuse the returned value for optimal behavior.
//
// Only one backend should be used per Meta. This function is stateful
// and is unsafe to create multiple backends used at once. This function
// can be called multiple times with each backend being "live" (usable)
// one at a time.
2018-10-09 16:53:24 -05:00
//
// A side-effect of this method is the population of m.backendState, recording
// the final resolved backend configuration after dealing with overrides from
2023-09-26 12:09:27 -05:00
// the "tofu init" command line, etc.
2024-03-07 07:55:57 -06:00
func ( m * Meta ) Backend ( opts * BackendOpts , enc encryption . StateEncryption ) ( backend . Enhanced , tfdiags . Diagnostics ) {
2018-03-27 17:31:05 -05:00
var diags tfdiags . Diagnostics
2017-01-18 22:50:04 -06:00
// If no opts are set, then initialize
if opts == nil {
opts = & BackendOpts { }
}
// Initialize a backend from the config unless we're forcing a purely
// local operation.
var b backend . Backend
if ! opts . ForceLocal {
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
var backendDiags tfdiags . Diagnostics
2024-03-07 07:55:57 -06:00
b , backendDiags = m . backendFromConfig ( opts , enc )
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
diags = diags . Append ( backendDiags )
2018-03-27 17:31:05 -05:00
if diags . HasErrors ( ) {
return nil , diags
2017-01-18 22:50:04 -06:00
}
2018-11-12 20:27:39 -06:00
log . Printf ( "[TRACE] Meta.Backend: instantiated backend of type %T" , b )
2017-01-18 22:50:04 -06:00
}
2021-01-26 13:39:11 -06:00
// Set up the CLI opts we pass into backends that support it.
2020-06-16 11:23:15 -05:00
cliOpts , err := m . backendCLIOpts ( )
if err != nil {
command: Early error message for missing cache entries of locked providers
In the original incarnation of Meta.providerFactories we were returning
into a Meta.contextOpts whose signature didn't allow it to return an
error directly, and so we had compromised by making the provider factory
functions themselves return errors once called.
Subsequent work made Meta.contextOpts need to return an error anyway, but
at the time we neglected to update our handling of the providerFactories
result, having it still defer the error handling until we finally
instantiate a provider.
Although that did ultimately get the expected result anyway, the error
ended up being reported from deep in the guts of a Terraform Core graph
walk, in whichever concurrently-visited graph node happened to try to
instantiate the plugin first. This meant that the exact phrasing of the
error message would vary between runs and the reporting codepath didn't
have enough context to given an actionable suggestion on how to proceed.
In this commit we make Meta.contextOpts pass through directly any error
that Meta.providerFactories produces, and then make Meta.providerFactories
produce a special error type so that Meta.Backend can ultimately return
a user-friendly diagnostic message containing a specific suggestion to
run "terraform init", along with a short explanation of what a provider
plugin is.
The reliance here on an implied contract between two functions that are
not directly connected in the callstack is non-ideal, and so hopefully
we'll revisit this further in future work on the overall architecture of
the CLI layer. To try to make this robust in the meantime though, I wrote
it to use the errors.As function to potentially unwrap a wrapped version
of our special error type, in case one of the intervening layers is
changed at some point to wrap the downstream error before returning it.
2021-10-01 16:51:06 -05:00
if errs := providerPluginErrors ( nil ) ; errors . As ( err , & errs ) {
// This is a special type returned by m.providerFactories, which
// indicates one or more inconsistencies between the dependency
// lock file and the provider plugins actually available in the
// local cache directory.
2021-10-21 07:44:26 -05:00
//
// If initialization is allowed, we ignore this error, as it may
// be resolved by the later step where providers are fetched.
if ! opts . Init {
var buf bytes . Buffer
for addr , err := range errs {
fmt . Fprintf ( & buf , "\n - %s: %s" , addr , err )
}
2023-09-21 07:38:46 -05:00
suggestion := "To download the plugins required for this configuration, run:\n tofu init"
2021-10-21 07:44:26 -05:00
if m . RunningInAutomation {
2023-09-26 12:09:27 -05:00
// Don't mention "tofu init" specifically if we're running in an automation wrapper
2023-09-21 07:38:46 -05:00
suggestion = "You must install the required plugins before running OpenTofu operations."
2021-10-21 07:44:26 -05:00
}
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
"Required plugins are not installed" ,
fmt . Sprintf (
2023-09-21 07:38:46 -05:00
"The installed provider plugins are not consistent with the packages selected in the dependency lock file:%s\n\nOpenTofu uses external plugins to integrate with a variety of different infrastructure services. %s" ,
2021-10-21 07:44:26 -05:00
buf . String ( ) , suggestion ,
) ,
) )
return nil , diags
command: Early error message for missing cache entries of locked providers
In the original incarnation of Meta.providerFactories we were returning
into a Meta.contextOpts whose signature didn't allow it to return an
error directly, and so we had compromised by making the provider factory
functions themselves return errors once called.
Subsequent work made Meta.contextOpts need to return an error anyway, but
at the time we neglected to update our handling of the providerFactories
result, having it still defer the error handling until we finally
instantiate a provider.
Although that did ultimately get the expected result anyway, the error
ended up being reported from deep in the guts of a Terraform Core graph
walk, in whichever concurrently-visited graph node happened to try to
instantiate the plugin first. This meant that the exact phrasing of the
error message would vary between runs and the reporting codepath didn't
have enough context to given an actionable suggestion on how to proceed.
In this commit we make Meta.contextOpts pass through directly any error
that Meta.providerFactories produces, and then make Meta.providerFactories
produce a special error type so that Meta.Backend can ultimately return
a user-friendly diagnostic message containing a specific suggestion to
run "terraform init", along with a short explanation of what a provider
plugin is.
The reliance here on an implied contract between two functions that are
not directly connected in the callstack is non-ideal, and so hopefully
we'll revisit this further in future work on the overall architecture of
the CLI layer. To try to make this robust in the meantime though, I wrote
it to use the errors.As function to potentially unwrap a wrapped version
of our special error type, in case one of the intervening layers is
changed at some point to wrap the downstream error before returning it.
2021-10-01 16:51:06 -05:00
}
} else {
// All other errors just get generic handling.
diags = diags . Append ( err )
2021-10-21 07:44:26 -05:00
return nil , diags
command: Early error message for missing cache entries of locked providers
In the original incarnation of Meta.providerFactories we were returning
into a Meta.contextOpts whose signature didn't allow it to return an
error directly, and so we had compromised by making the provider factory
functions themselves return errors once called.
Subsequent work made Meta.contextOpts need to return an error anyway, but
at the time we neglected to update our handling of the providerFactories
result, having it still defer the error handling until we finally
instantiate a provider.
Although that did ultimately get the expected result anyway, the error
ended up being reported from deep in the guts of a Terraform Core graph
walk, in whichever concurrently-visited graph node happened to try to
instantiate the plugin first. This meant that the exact phrasing of the
error message would vary between runs and the reporting codepath didn't
have enough context to given an actionable suggestion on how to proceed.
In this commit we make Meta.contextOpts pass through directly any error
that Meta.providerFactories produces, and then make Meta.providerFactories
produce a special error type so that Meta.Backend can ultimately return
a user-friendly diagnostic message containing a specific suggestion to
run "terraform init", along with a short explanation of what a provider
plugin is.
The reliance here on an implied contract between two functions that are
not directly connected in the callstack is non-ideal, and so hopefully
we'll revisit this further in future work on the overall architecture of
the CLI layer. To try to make this robust in the meantime though, I wrote
it to use the errors.As function to potentially unwrap a wrapped version
of our special error type, in case one of the intervening layers is
changed at some point to wrap the downstream error before returning it.
2021-10-01 16:51:06 -05:00
}
2020-06-16 11:23:15 -05:00
}
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
cliOpts . Validation = true
2017-03-27 16:11:26 -05:00
2017-02-28 12:58:29 -06:00
// If the backend supports CLI initialization, do it.
if cli , ok := b . ( backend . CLI ) ; ok {
if err := cli . CLIInit ( cliOpts ) ; err != nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( fmt . Errorf (
2023-09-18 07:16:17 -05:00
"Error initializing backend %T: %w\n\n" +
2018-03-27 17:31:05 -05:00
"This is a bug; please report it to the backend developer" ,
b , err ,
) )
return nil , diags
2017-02-28 12:58:29 -06:00
}
}
2017-01-18 22:50:04 -06:00
// If the result of loading the backend is an enhanced backend,
// then return that as-is. This works even if b == nil (it will be !ok).
if enhanced , ok := b . ( backend . Enhanced ) ; ok {
2018-11-12 20:27:39 -06:00
log . Printf ( "[TRACE] Meta.Backend: backend %T supports operations" , b )
2017-01-18 22:50:04 -06:00
return enhanced , nil
}
// We either have a non-enhanced backend or no backend configured at
// all. In either case, we use local as our enhanced backend and the
// non-enhanced (if any) as the state backend.
if ! opts . ForceLocal {
2018-11-12 20:27:39 -06:00
log . Printf ( "[TRACE] Meta.Backend: backend %T does not support operations, so wrapping it in a local backend" , b )
2017-01-18 22:50:04 -06:00
}
// Build the local backend
2024-03-07 07:55:57 -06:00
local := backendLocal . NewWithBackend ( b , enc )
2017-02-28 12:58:29 -06:00
if err := local . CLIInit ( cliOpts ) ; err != nil {
// Local backend isn't allowed to fail. It would be a bug.
panic ( err )
}
2018-10-09 16:53:24 -05:00
// If we got here from backendFromConfig returning nil then m.backendState
// won't be set, since that codepath considers that to be no backend at all,
// but our caller considers that to be the local backend with no config
// and so we'll synthesize a backend state so other code doesn't need to
// care about this special case.
//
// FIXME: We should refactor this so that we more directly and explicitly
// treat the local backend as the default, including in the UI shown to
// the user, since the local backend should only be used when learning or
// in exceptional cases and so it's better to help the user learn that
// by introducing it as a concept.
if m . backendState == nil {
// NOTE: This synthetic object is intentionally _not_ retained in the
// on-disk record of the backend configuration, which was already dealt
// with inside backendFromConfig, because we still need that codepath
// to be able to recognize the lack of a config as distinct from
// explicitly setting local until we do some more refactoring here.
2020-11-18 12:41:33 -06:00
m . backendState = & legacy . BackendState {
2018-10-09 16:53:24 -05:00
Type : "local" ,
ConfigRaw : json . RawMessage ( "{}" ) ,
}
}
2017-02-28 12:58:29 -06:00
return local , nil
2017-01-18 22:50:04 -06:00
}
2019-05-07 08:23:26 -05:00
// selectWorkspace gets a list of existing workspaces and then checks
// if the currently selected workspace is valid. If not, it will ask
// the user to select a workspace from the list.
func ( m * Meta ) selectWorkspace ( b backend . Backend ) error {
2023-11-08 15:09:14 -06:00
workspaces , err := b . Workspaces ( )
2019-05-07 08:23:26 -05:00
if err == backend . ErrWorkspacesNotSupported {
return nil
}
if err != nil {
2023-09-18 07:16:17 -05:00
return fmt . Errorf ( "Failed to get existing workspaces: %w" , err )
2019-05-07 08:23:26 -05:00
}
if len ( workspaces ) == 0 {
2021-11-01 12:20:15 -05:00
if c , ok := b . ( * cloud . Cloud ) ; ok && m . input {
// len is always 1 if using Name; 0 means we're using Tags and there
// aren't any matching workspaces. Which might be normal and fine, so
// let's just ask:
2023-11-08 15:09:14 -06:00
name , err := m . UIInput ( ) . Input ( context . Background ( ) , & tofu . InputOpts {
2021-11-01 12:20:15 -05:00
Id : "create-workspace" ,
Query : "\n[reset][bold][yellow]No workspaces found.[reset]" ,
Description : fmt . Sprintf ( inputCloudInitCreateWorkspace , strings . Join ( c . WorkspaceMapping . Tags , ", " ) ) ,
} )
2021-12-01 10:53:47 -06:00
if err != nil {
return fmt . Errorf ( "Couldn't create initial workspace: %w" , err )
}
2021-11-01 12:20:15 -05:00
name = strings . TrimSpace ( name )
2021-12-01 10:53:47 -06:00
if name == "" {
2021-11-01 12:20:15 -05:00
return fmt . Errorf ( "Couldn't create initial workspace: no name provided" )
}
log . Printf ( "[TRACE] Meta.selectWorkspace: selecting the new TFC workspace requested by the user (%s)" , name )
return m . SetWorkspace ( name )
} else {
return fmt . Errorf ( strings . TrimSpace ( errBackendNoExistingWorkspaces ) )
}
2019-05-07 08:23:26 -05:00
}
// Get the currently selected workspace.
2020-06-16 11:23:15 -05:00
workspace , err := m . Workspace ( )
if err != nil {
return err
}
2019-05-07 08:23:26 -05:00
// Check if any of the existing workspaces matches the selected
// workspace and create a numbered list of existing workspaces.
var list strings . Builder
for i , w := range workspaces {
if w == workspace {
2021-09-21 23:29:02 -05:00
log . Printf ( "[TRACE] Meta.selectWorkspace: the currently selected workspace is present in the configured backend (%s)" , workspace )
2019-05-07 08:23:26 -05:00
return nil
}
fmt . Fprintf ( & list , "%d. %s\n" , i + 1 , w )
}
2021-09-21 23:29:02 -05:00
// If the backend only has a single workspace, select that as the current workspace
if len ( workspaces ) == 1 {
log . Printf ( "[TRACE] Meta.selectWorkspace: automatically selecting the single workspace provided by the backend (%s)" , workspaces [ 0 ] )
return m . SetWorkspace ( workspaces [ 0 ] )
}
2021-10-25 14:08:10 -05:00
if ! m . input {
return fmt . Errorf ( "Currently selected workspace %q does not exist" , workspace )
}
2021-09-21 23:29:02 -05:00
// Otherwise, ask the user to select a workspace from the list of existing workspaces.
2023-11-08 15:09:14 -06:00
v , err := m . UIInput ( ) . Input ( context . Background ( ) , & tofu . InputOpts {
2019-05-07 08:23:26 -05:00
Id : "select-workspace" ,
Query : fmt . Sprintf (
"\n[reset][bold][yellow]The currently selected workspace (%s) does not exist.[reset]" ,
workspace ) ,
Description : fmt . Sprintf (
strings . TrimSpace ( inputBackendSelectWorkspace ) , list . String ( ) ) ,
} )
if err != nil {
2023-09-18 07:16:17 -05:00
return fmt . Errorf ( "Failed to select workspace: %w" , err )
2019-05-07 08:23:26 -05:00
}
idx , err := strconv . Atoi ( v )
if err != nil || ( idx < 1 || idx > len ( workspaces ) ) {
return fmt . Errorf ( "Failed to select workspace: input not a valid number" )
}
2021-09-21 23:29:02 -05:00
workspace = workspaces [ idx - 1 ]
2023-06-26 12:02:55 -05:00
log . Printf ( "[TRACE] Meta.selectWorkspace: setting the current workspace according to user selection (%s)" , workspace )
2021-09-21 23:29:02 -05:00
return m . SetWorkspace ( workspace )
2019-05-07 08:23:26 -05:00
}
2023-06-20 21:06:18 -05:00
// BackendForLocalPlan is similar to Backend, but uses backend settings that were
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
// stored in a plan.
//
// The current workspace name is also stored as part of the plan, and so this
// method will check that it matches the currently-selected workspace name
// and produce error diagnostics if not.
2024-03-07 07:55:57 -06:00
func ( m * Meta ) BackendForLocalPlan ( settings plans . Backend , enc encryption . StateEncryption ) ( backend . Enhanced , tfdiags . Diagnostics ) {
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
var diags tfdiags . Diagnostics
2018-10-31 10:45:03 -05:00
f := backendInit . Backend ( settings . Type )
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
if f == nil {
diags = diags . Append ( fmt . Errorf ( strings . TrimSpace ( errBackendSavedUnknown ) , settings . Type ) )
return nil , diags
}
2024-03-07 07:55:57 -06:00
b := f ( enc )
2023-06-20 21:06:18 -05:00
log . Printf ( "[TRACE] Meta.BackendForLocalPlan: instantiated backend of type %T" , b )
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
2023-11-08 15:09:14 -06:00
schema := b . ConfigSchema ( )
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
configVal , err := settings . Config . Decode ( schema . ImpliedType ( ) )
if err != nil {
2021-08-31 16:33:26 -05:00
diags = diags . Append ( fmt . Errorf ( "saved backend configuration is invalid: %w" , err ) )
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
return nil , diags
}
2023-11-08 15:09:14 -06:00
newVal , validateDiags := b . PrepareConfig ( configVal )
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
diags = diags . Append ( validateDiags )
if validateDiags . HasErrors ( ) {
return nil , diags
}
2023-11-08 15:09:14 -06:00
configureDiags := b . Configure ( newVal )
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
diags = diags . Append ( configureDiags )
2022-03-30 16:50:08 -05:00
if configureDiags . HasErrors ( ) {
return nil , diags
}
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
2018-10-09 16:46:11 -05:00
// If the backend supports CLI initialization, do it.
if cli , ok := b . ( backend . CLI ) ; ok {
2020-06-16 11:23:15 -05:00
cliOpts , err := m . backendCLIOpts ( )
if err != nil {
diags = diags . Append ( err )
return nil , diags
}
2018-10-09 16:46:11 -05:00
if err := cli . CLIInit ( cliOpts ) ; err != nil {
diags = diags . Append ( fmt . Errorf (
2023-09-18 07:16:17 -05:00
"Error initializing backend %T: %w\n\n" +
2018-10-09 16:46:11 -05:00
"This is a bug; please report it to the backend developer" ,
b , err ,
) )
return nil , diags
}
}
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
// If the result of loading the backend is an enhanced backend,
// then return that as-is. This works even if b == nil (it will be !ok).
if enhanced , ok := b . ( backend . Enhanced ) ; ok {
2018-11-14 18:31:56 -06:00
log . Printf ( "[TRACE] Meta.BackendForPlan: backend %T supports operations" , b )
2023-06-26 12:02:55 -05:00
if err := m . setupEnhancedBackendAliases ( enhanced ) ; err != nil {
diags = diags . Append ( err )
return nil , diags
}
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
return enhanced , nil
}
// Otherwise, we'll wrap our state-only remote backend in the local backend
// to cause any operations to be run locally.
2023-06-20 21:06:18 -05:00
log . Printf ( "[TRACE] Meta.BackendForLocalPlan: backend %T does not support operations, so wrapping it in a local backend" , b )
2020-06-16 11:23:15 -05:00
cliOpts , err := m . backendCLIOpts ( )
if err != nil {
diags = diags . Append ( err )
return nil , diags
}
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
cliOpts . Validation = false // don't validate here in case config contains file(...) calls where the file doesn't exist
2024-03-07 07:55:57 -06:00
local := backendLocal . NewWithBackend ( b , enc )
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
if err := local . CLIInit ( cliOpts ) ; err != nil {
// Local backend should never fail, so this is always a bug.
panic ( err )
}
return local , diags
}
// backendCLIOpts returns a backend.CLIOpts object that should be passed to
// a backend that supports local CLI operations.
2020-06-16 11:23:15 -05:00
func ( m * Meta ) backendCLIOpts ( ) ( * backend . CLIOpts , error ) {
contextOpts , err := m . contextOpts ( )
2021-10-21 07:44:26 -05:00
if contextOpts == nil && err != nil {
2020-06-16 11:23:15 -05:00
return nil , err
}
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
return & backend . CLIOpts {
CLI : m . Ui ,
CLIColor : m . Colorize ( ) ,
2021-01-11 20:20:58 -06:00
Streams : m . Streams ,
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
StatePath : m . statePath ,
StateOutPath : m . stateOutPath ,
StateBackupPath : m . backupPath ,
2020-06-16 11:23:15 -05:00
ContextOpts : contextOpts ,
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
Input : m . Input ( ) ,
RunningInAutomation : m . RunningInAutomation ,
2021-10-21 07:44:26 -05:00
} , err
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
}
2017-01-18 22:50:04 -06:00
// Operation initializes a new backend.Operation struct.
//
// This prepares the operation. After calling this, the caller is expected
// to modify fields of the operation such as Sequence to specify what will
// be called.
2024-03-07 07:55:57 -06:00
func ( m * Meta ) Operation ( b backend . Backend , vt arguments . ViewType , enc encryption . Encryption ) * backend . Operation {
2023-11-08 15:09:14 -06:00
schema := b . ConfigSchema ( )
2020-06-16 11:23:15 -05:00
workspace , err := m . Workspace ( )
if err != nil {
// An invalid workspace error would have been raised when creating the
// backend, and the caller should have already exited. Seeing the error
// here first is a bug, so panic.
panic ( fmt . Sprintf ( "invalid workspace: %s" , err ) )
}
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
planOutBackend , err := m . backendState . ForPlan ( schema , workspace )
if err != nil {
// Always indicates an implementation error in practice, because
// errors here indicate invalid encoding of the backend configuration
// in memory, and we should always have validated that by the time
// we get here.
panic ( fmt . Sprintf ( "failed to encode backend configuration for plan: %s" , err ) )
}
2021-02-16 06:19:22 -06:00
stateLocker := clistate . NewNoopLocker ( )
if m . stateLock {
2023-02-07 02:06:12 -06:00
view := views . NewStateLocker ( vt , m . View )
2021-02-16 06:19:22 -06:00
stateLocker = clistate . NewLocker ( m . stateLockTimeout , view )
}
backend/local: Check dependency lock consistency before any operations
In historical versions of Terraform the responsibility to check this was
inside the terraform.NewContext function, along with various other
assorted concerns that made that function particularly complicated.
More recently, we reduced the responsibility of the "terraform" package
only to instantiating particular named plugins, assuming that its caller
is responsible for selecting appropriate versions of any providers that
_are_ external. However, until this commit we were just assuming that
"terraform init" had correctly selected appropriate plugins and recorded
them in the lock file, and so nothing was dealing with the problem of
ensuring that there haven't been any changes to the lock file or config
since the most recent "terraform init" which would cause us to need to
re-evaluate those decisions.
Part of the game here is to slightly extend the role of the dependency
locks object to also carry information about a subset of provider
addresses whose lock entries we're intentionally disregarding as part of
the various little edge-case features we have for overridding providers:
dev_overrides, "unmanaged providers", and the testing overrides in our
own unit tests. This is an in-memory-only annotation, never included in
the serialized plan files on disk.
I had originally intended to create a new package to encapsulate all of
this plugin-selection logic, including both the version constraint
checking here and also the handling of the provider factory functions, but
as an interim step I've just made version constraint consistency checks
the responsibility of the backend/local package, which means that we'll
always catch problems as part of preparing for local operations, while
not imposing these additional checks on commands that _don't_ run local
operations, such as "terraform apply" when in remote operations mode.
2021-09-29 19:31:43 -05:00
depLocks , diags := m . lockedDependencies ( )
if diags . HasErrors ( ) {
// We can't actually report errors from here, but m.lockedDependencies
// should always have been called earlier to prepare the "ContextOpts"
// for the backend anyway, so we should never actually get here in
// a real situation. If we do get here then the backend will inevitably
2024-08-29 12:20:33 -05:00
// fail downstream somewhere if it tries to use the empty depLocks.
backend/local: Check dependency lock consistency before any operations
In historical versions of Terraform the responsibility to check this was
inside the terraform.NewContext function, along with various other
assorted concerns that made that function particularly complicated.
More recently, we reduced the responsibility of the "terraform" package
only to instantiating particular named plugins, assuming that its caller
is responsible for selecting appropriate versions of any providers that
_are_ external. However, until this commit we were just assuming that
"terraform init" had correctly selected appropriate plugins and recorded
them in the lock file, and so nothing was dealing with the problem of
ensuring that there haven't been any changes to the lock file or config
since the most recent "terraform init" which would cause us to need to
re-evaluate those decisions.
Part of the game here is to slightly extend the role of the dependency
locks object to also carry information about a subset of provider
addresses whose lock entries we're intentionally disregarding as part of
the various little edge-case features we have for overridding providers:
dev_overrides, "unmanaged providers", and the testing overrides in our
own unit tests. This is an in-memory-only annotation, never included in
the serialized plan files on disk.
I had originally intended to create a new package to encapsulate all of
this plugin-selection logic, including both the version constraint
checking here and also the handling of the provider factory functions, but
as an interim step I've just made version constraint consistency checks
the responsibility of the backend/local package, which means that we'll
always catch problems as part of preparing for local operations, while
not imposing these additional checks on commands that _don't_ run local
operations, such as "terraform apply" when in remote operations mode.
2021-09-29 19:31:43 -05:00
log . Printf ( "[WARN] Failed to load dependency locks while preparing backend operation (ignored): %s" , diags . Err ( ) . Error ( ) )
}
2017-01-18 22:50:04 -06:00
return & backend . Operation {
2024-03-07 07:55:57 -06:00
Encryption : enc ,
backend/local: Check dependency lock consistency before any operations
In historical versions of Terraform the responsibility to check this was
inside the terraform.NewContext function, along with various other
assorted concerns that made that function particularly complicated.
More recently, we reduced the responsibility of the "terraform" package
only to instantiating particular named plugins, assuming that its caller
is responsible for selecting appropriate versions of any providers that
_are_ external. However, until this commit we were just assuming that
"terraform init" had correctly selected appropriate plugins and recorded
them in the lock file, and so nothing was dealing with the problem of
ensuring that there haven't been any changes to the lock file or config
since the most recent "terraform init" which would cause us to need to
re-evaluate those decisions.
Part of the game here is to slightly extend the role of the dependency
locks object to also carry information about a subset of provider
addresses whose lock entries we're intentionally disregarding as part of
the various little edge-case features we have for overridding providers:
dev_overrides, "unmanaged providers", and the testing overrides in our
own unit tests. This is an in-memory-only annotation, never included in
the serialized plan files on disk.
I had originally intended to create a new package to encapsulate all of
this plugin-selection logic, including both the version constraint
checking here and also the handling of the provider factory functions, but
as an interim step I've just made version constraint consistency checks
the responsibility of the backend/local package, which means that we'll
always catch problems as part of preparing for local operations, while
not imposing these additional checks on commands that _don't_ run local
operations, such as "terraform apply" when in remote operations mode.
2021-09-29 19:31:43 -05:00
PlanOutBackend : planOutBackend ,
Targets : m . targets ,
2024-11-05 09:16:00 -06:00
Excludes : m . excludes ,
backend/local: Check dependency lock consistency before any operations
In historical versions of Terraform the responsibility to check this was
inside the terraform.NewContext function, along with various other
assorted concerns that made that function particularly complicated.
More recently, we reduced the responsibility of the "terraform" package
only to instantiating particular named plugins, assuming that its caller
is responsible for selecting appropriate versions of any providers that
_are_ external. However, until this commit we were just assuming that
"terraform init" had correctly selected appropriate plugins and recorded
them in the lock file, and so nothing was dealing with the problem of
ensuring that there haven't been any changes to the lock file or config
since the most recent "terraform init" which would cause us to need to
re-evaluate those decisions.
Part of the game here is to slightly extend the role of the dependency
locks object to also carry information about a subset of provider
addresses whose lock entries we're intentionally disregarding as part of
the various little edge-case features we have for overridding providers:
dev_overrides, "unmanaged providers", and the testing overrides in our
own unit tests. This is an in-memory-only annotation, never included in
the serialized plan files on disk.
I had originally intended to create a new package to encapsulate all of
this plugin-selection logic, including both the version constraint
checking here and also the handling of the provider factory functions, but
as an interim step I've just made version constraint consistency checks
the responsibility of the backend/local package, which means that we'll
always catch problems as part of preparing for local operations, while
not imposing these additional checks on commands that _don't_ run local
operations, such as "terraform apply" when in remote operations mode.
2021-09-29 19:31:43 -05:00
UIIn : m . UIInput ( ) ,
UIOut : m . Ui ,
Workspace : workspace ,
StateLocker : stateLocker ,
DependencyLocks : depLocks ,
2017-01-18 22:50:04 -06:00
}
}
// backendConfig returns the local configuration for the backend
2018-03-27 17:31:05 -05:00
func ( m * Meta ) backendConfig ( opts * BackendOpts ) ( * configs . Backend , int , tfdiags . Diagnostics ) {
var diags tfdiags . Diagnostics
2017-05-01 16:47:53 -05:00
if opts . Config == nil {
// check if the config was missing, or just not required
2019-11-14 16:33:43 -06:00
conf , moreDiags := m . loadBackendConfig ( "." )
diags = diags . Append ( moreDiags )
if moreDiags . HasErrors ( ) {
return nil , 0 , diags
2017-01-18 22:50:04 -06:00
}
2017-05-01 16:47:53 -05:00
if conf == nil {
2018-11-12 20:27:39 -06:00
log . Println ( "[TRACE] Meta.Backend: no config given or present on disk, so returning nil config" )
2018-03-27 17:31:05 -05:00
return nil , 0 , nil
2017-01-18 22:50:04 -06:00
}
2018-11-12 20:27:39 -06:00
log . Printf ( "[TRACE] Meta.Backend: BackendOpts.Config not set, so using settings loaded from %s" , conf . DeclRange )
2017-05-01 16:47:53 -05:00
opts . Config = conf
2017-01-18 22:50:04 -06:00
}
2017-05-01 16:47:53 -05:00
c := opts . Config
2017-01-18 22:50:04 -06:00
2018-03-27 17:31:05 -05:00
if c == nil {
2018-11-12 20:27:39 -06:00
log . Println ( "[TRACE] Meta.Backend: no explicit backend config, so returning nil config" )
2018-03-27 17:31:05 -05:00
return nil , 0 , nil
2017-03-17 01:27:05 -05:00
}
2018-10-31 10:45:03 -05:00
bf := backendInit . Backend ( c . Type )
2018-03-27 17:31:05 -05:00
if bf == nil {
2022-06-28 12:13:20 -05:00
detail := fmt . Sprintf ( "There is no backend type named %q." , c . Type )
if msg , removed := backendInit . RemovedBackends [ c . Type ] ; removed {
detail = msg
}
2018-03-27 17:31:05 -05:00
diags = diags . Append ( & hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Invalid backend type" ,
2022-06-28 12:13:20 -05:00
Detail : detail ,
2018-03-27 17:31:05 -05:00
Subject : & c . TypeRange ,
} )
return nil , 0 , diags
2017-01-18 22:50:04 -06:00
}
2024-03-07 07:55:57 -06:00
b := bf ( nil ) // Just using this for config/schema, don't need encryption here
2017-01-18 22:50:04 -06:00
2023-11-08 15:09:14 -06:00
configSchema := b . ConfigSchema ( )
2018-03-27 17:31:05 -05:00
configBody := c . Config
2024-06-24 08:13:07 -05:00
configHash , cfgDiags := c . Hash ( configSchema )
diags = diags . Append ( cfgDiags )
if diags . HasErrors ( ) {
return nil , 0 , diags
}
2017-01-18 22:50:04 -06:00
2018-03-27 17:31:05 -05:00
// If we have an override configuration body then we must apply it now.
if opts . ConfigOverride != nil {
2018-11-12 20:27:39 -06:00
log . Println ( "[TRACE] Meta.Backend: merging -backend-config=... CLI overrides into backend configuration" )
2018-03-27 17:31:05 -05:00
configBody = configs . MergeBodies ( configBody , opts . ConfigOverride )
2017-01-18 22:50:04 -06:00
}
2018-11-12 20:27:39 -06:00
log . Printf ( "[TRACE] Meta.Backend: built configuration for %q backend with hash value %d" , c . Type , configHash )
2018-03-27 17:31:05 -05:00
// We'll shallow-copy configs.Backend here so that we can replace the
// body without affecting others that hold this reference.
configCopy := * c
2018-11-08 19:08:32 -06:00
configCopy . Config = configBody
2018-03-27 17:31:05 -05:00
return & configCopy , configHash , diags
2017-01-18 22:50:04 -06:00
}
// backendFromConfig returns the initialized (not configured) backend
// directly from the config/state..
//
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
// This function handles various edge cases around backend config loading. For
// example: new config changes, backend type changes, etc.
//
// As of the 0.12 release it can no longer migrate from legacy remote state
// to backends, and will instead instruct users to use 0.11 or earlier as
// a stepping-stone to do that migration.
2017-01-18 22:50:04 -06:00
//
// This function may query the user for input unless input is disabled, in
// which case this function will error.
2024-03-07 07:55:57 -06:00
func ( m * Meta ) backendFromConfig ( opts * BackendOpts , enc encryption . StateEncryption ) ( backend . Backend , tfdiags . Diagnostics ) {
2017-01-18 22:50:04 -06:00
// Get the local backend configuration.
2018-03-27 17:31:05 -05:00
c , cHash , diags := m . backendConfig ( opts )
if diags . HasErrors ( ) {
return nil , diags
2017-03-16 13:47:59 -05:00
}
2018-10-09 16:53:24 -05:00
// ------------------------------------------------------------------------
// For historical reasons, current backend configuration for a working
// directory is kept in a *state-like* file, using the legacy state
2023-09-26 12:09:27 -05:00
// structures in the OpenTofu package. It is not actually a OpenTofu
2018-10-09 16:53:24 -05:00
// state, and so only the "backend" portion of it is actually used.
//
// The remainder of this code often confusingly refers to this as a "state",
// so it's unfortunately important to remember that this is not actually
// what we _usually_ think of as "state", and is instead a local working
// directory "backend configuration state" that is never persisted anywhere.
//
// Since the "real" state has since moved on to be represented by
// states.State, we can recognize the special meaning of state that applies
// to this function and its callees by their continued use of the
2023-09-26 12:09:27 -05:00
// otherwise-obsolete tofu.State.
2018-10-09 16:53:24 -05:00
// ------------------------------------------------------------------------
2017-01-18 22:50:04 -06:00
// Get the path to where we store a local cache of backend configuration
// if we're using a remote backend. This may not yet exist which means
// we haven't used a non-local backend before. That is okay.
statePath := filepath . Join ( m . DataDir ( ) , DefaultStateFilename )
2020-08-11 10:43:01 -05:00
sMgr := & clistate . LocalState { Path : statePath }
2017-01-18 22:50:04 -06:00
if err := sMgr . RefreshState ( ) ; err != nil {
2023-09-18 07:16:17 -05:00
diags = diags . Append ( fmt . Errorf ( "Failed to load state: %w" , err ) )
2018-03-27 17:31:05 -05:00
return nil , diags
2017-01-18 22:50:04 -06:00
}
// Load the state, it must be non-nil for the tests below but can be empty
s := sMgr . State ( )
if s == nil {
2018-11-12 20:27:39 -06:00
log . Printf ( "[TRACE] Meta.Backend: backend has not previously been initialized in this working directory" )
2020-11-18 12:41:33 -06:00
s = legacy . NewState ( )
2018-11-12 20:27:39 -06:00
} else if s . Backend != nil {
log . Printf ( "[TRACE] Meta.Backend: working directory was previously initialized for %q backend" , s . Backend . Type )
} else {
log . Printf ( "[TRACE] Meta.Backend: working directory was previously initialized but has no backend (is using legacy remote state?)" )
2017-01-18 22:50:04 -06:00
}
2017-04-20 16:26:50 -05:00
// if we want to force reconfiguration of the backend, we set the backend
// state to nil on this copy. This will direct us through the correct
// configuration path in the switch statement below.
if m . reconfigure {
s . Backend = nil
}
2017-01-18 22:50:04 -06:00
// Upon return, we want to set the state we're using in-memory so that
// we can access it for commands.
m . backendState = nil
defer func ( ) {
if s := sMgr . State ( ) ; s != nil && ! s . Backend . Empty ( ) {
m . backendState = s . Backend
}
} ( )
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
if ! s . Remote . Empty ( ) {
// Legacy remote state is no longer supported. User must first
// migrate with Terraform 0.11 or earlier.
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
"Legacy remote state not supported" ,
2023-09-21 07:38:46 -05:00
"This working directory is configured for legacy remote state, which is no longer supported from Terraform v0.12 onwards, and thus not supported by OpenTofu, either. To migrate this environment, first run \"terraform init\" under a Terraform 0.11 release, and then upgrade to OpenTofu." ,
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
) )
return nil , diags
}
// This switch statement covers all the different combinations of
// configuring new backends, updating previously-configured backends, etc.
2017-01-18 22:50:04 -06:00
switch {
// No configuration set at all. Pure local state.
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
case c == nil && s . Backend . Empty ( ) :
2018-11-12 20:27:39 -06:00
log . Printf ( "[TRACE] Meta.Backend: using default local state only (no backend configuration, and no existing initialized backend)" )
2017-01-18 22:50:04 -06:00
return nil , nil
// We're unsetting a backend (moving from backend => local)
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
case c == nil && ! s . Backend . Empty ( ) :
2018-11-12 20:27:39 -06:00
log . Printf ( "[TRACE] Meta.Backend: previously-initialized %q backend is no longer present in config" , s . Backend . Type )
2021-05-14 16:36:54 -05:00
initReason := fmt . Sprintf ( "Unsetting the previously set backend %q" , s . Backend . Type )
2017-01-18 22:50:04 -06:00
if ! opts . Init {
2021-05-14 16:36:54 -05:00
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
2023-09-21 07:38:46 -05:00
"Backend initialization required, please run \"tofu init\"" ,
2021-05-14 16:36:54 -05:00
fmt . Sprintf ( strings . TrimSpace ( errBackendInit ) , initReason ) ,
) )
return nil , diags
}
2021-11-12 19:07:10 -06:00
if s . Backend . Type != "cloud" && ! m . migrateState {
2021-05-14 16:36:54 -05:00
diags = diags . Append ( migrateOrReconfigDiag )
2018-03-27 17:31:05 -05:00
return nil , diags
2017-01-18 22:50:04 -06:00
}
2024-03-07 07:55:57 -06:00
return m . backend_c_r_S ( c , cHash , sMgr , true , opts , enc )
2017-01-18 22:50:04 -06:00
2021-12-01 10:53:47 -06:00
// Configuring a backend for the first time or -reconfigure flag was used
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
case c != nil && s . Backend . Empty ( ) :
2018-11-12 20:27:39 -06:00
log . Printf ( "[TRACE] Meta.Backend: moving from default local state only to %q backend" , c . Type )
2017-01-18 22:50:04 -06:00
if ! opts . Init {
2021-10-21 14:29:12 -05:00
if c . Type == "cloud" {
2023-10-12 06:07:16 -05:00
initReason := "Initial configuration of cloud backend"
2021-10-21 14:29:12 -05:00
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
2023-10-12 06:07:16 -05:00
"Cloud backend initialization required: please run \"tofu init\"" ,
2021-10-27 17:05:36 -05:00
fmt . Sprintf ( strings . TrimSpace ( errBackendInitCloud ) , initReason ) ,
2021-10-21 14:29:12 -05:00
) )
} else {
initReason := fmt . Sprintf ( "Initial configuration of the requested backend %q" , c . Type )
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
2023-09-21 07:38:46 -05:00
"Backend initialization required, please run \"tofu init\"" ,
2021-10-21 14:29:12 -05:00
fmt . Sprintf ( strings . TrimSpace ( errBackendInit ) , initReason ) ,
) )
}
2018-03-27 17:31:05 -05:00
return nil , diags
2017-01-18 22:50:04 -06:00
}
2024-03-07 07:55:57 -06:00
return m . backend_C_r_s ( c , cHash , sMgr , opts , enc )
2017-01-18 22:50:04 -06:00
// Potentially changing a backend configuration
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
case c != nil && ! s . Backend . Empty ( ) :
2021-11-02 13:26:00 -05:00
// We are not going to migrate if...
//
// We're not initializing
// AND the backend cache hash values match, indicating that the stored config is valid and completely unchanged.
// AND we're not providing any overrides. An override can mean a change overriding an unchanged backend block (indicated by the hash value).
2019-05-24 10:31:04 -05:00
if ( uint64 ( cHash ) == s . Backend . Hash ) && ( ! opts . Init || opts . ConfigOverride == nil ) {
2018-11-15 16:45:10 -06:00
log . Printf ( "[TRACE] Meta.Backend: using already-initialized, unchanged %q backend configuration" , c . Type )
2024-03-07 07:55:57 -06:00
savedBackend , diags := m . savedBackend ( sMgr , enc )
2021-12-01 10:53:47 -06:00
// Verify that selected workspace exist. Otherwise prompt user to create one
if opts . Init && savedBackend != nil {
if err := m . selectWorkspace ( savedBackend ) ; err != nil {
diags = diags . Append ( err )
return nil , diags
}
}
return savedBackend , diags
2018-11-15 16:45:10 -06:00
}
2021-11-02 13:26:00 -05:00
// If our configuration (the result of both the literal configuration and given
// -backend-config options) is the same, then we're just initializing a previously
// configured backend. The literal configuration may differ, however, so while we
// don't need to migrate, we update the backend cache hash value.
2018-11-09 14:13:30 -06:00
if ! m . backendConfigNeedsMigration ( c , s . Backend ) {
2018-11-12 20:27:39 -06:00
log . Printf ( "[TRACE] Meta.Backend: using already-initialized %q backend configuration" , c . Type )
2024-03-07 07:55:57 -06:00
savedBackend , moreDiags := m . savedBackend ( sMgr , enc )
2021-11-02 13:26:00 -05:00
diags = diags . Append ( moreDiags )
if moreDiags . HasErrors ( ) {
return nil , diags
}
// It's possible for a backend to be unchanged, and the config itself to
// have changed by moving a parameter from the config to `-backend-config`
// In this case, we update the Hash.
moreDiags = m . updateSavedBackendHash ( cHash , sMgr )
if moreDiags . HasErrors ( ) {
return nil , diags
}
2021-12-01 10:53:47 -06:00
// Verify that selected workspace exist. Otherwise prompt user to create one
if opts . Init && savedBackend != nil {
if err := m . selectWorkspace ( savedBackend ) ; err != nil {
diags = diags . Append ( err )
return nil , diags
}
}
2021-11-02 13:26:00 -05:00
return savedBackend , diags
2017-01-18 22:50:04 -06:00
}
2018-11-12 20:27:39 -06:00
log . Printf ( "[TRACE] Meta.Backend: backend configuration has changed (from type %q to type %q)" , s . Backend . Type , c . Type )
2017-01-18 22:50:04 -06:00
2021-11-12 19:07:10 -06:00
cloudMode := cloud . DetectConfigChangeType ( s . Backend , c , false )
2017-01-18 22:50:04 -06:00
if ! opts . Init {
2021-11-22 15:32:34 -06:00
//user ran another cmd that is not init but they are required to initialize because of a potential relevant change to their backend configuration
initDiag := m . determineInitReason ( s . Backend . Type , c . Type , cloudMode )
diags = diags . Append ( initDiag )
2021-05-14 16:36:54 -05:00
return nil , diags
}
2021-11-12 19:07:10 -06:00
if ! cloudMode . InvolvesCloud ( ) && ! m . migrateState {
diags = diags . Append ( migrateOrReconfigDiag )
2018-03-27 17:31:05 -05:00
return nil , diags
2017-01-18 22:50:04 -06:00
}
2018-11-09 14:13:30 -06:00
log . Printf ( "[WARN] backend config has changed since last init" )
2024-03-07 07:55:57 -06:00
return m . backend_C_r_S_changed ( c , cHash , sMgr , true , opts , enc )
2017-01-18 22:50:04 -06:00
default :
2018-03-27 17:31:05 -05:00
diags = diags . Append ( fmt . Errorf (
2017-01-18 22:50:04 -06:00
"Unhandled backend configuration state. This is a bug. Please\n" +
"report this error with the following information.\n\n" +
"Config Nil: %v\n" +
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
"Saved Backend Empty: %v\n" ,
c == nil , s . Backend . Empty ( ) ,
2018-03-27 17:31:05 -05:00
) )
return nil , diags
2017-01-18 22:50:04 -06:00
}
}
2021-11-22 15:32:34 -06:00
func ( m * Meta ) determineInitReason ( previousBackendType string , currentBackendType string , cloudMode cloud . ConfigChangeMode ) tfdiags . Diagnostics {
initReason := ""
switch cloudMode {
case cloud . ConfigMigrationIn :
2023-10-12 06:07:16 -05:00
initReason = fmt . Sprintf ( "Changed from backend %q to cloud backend" , previousBackendType )
2021-11-22 15:32:34 -06:00
case cloud . ConfigMigrationOut :
2023-10-12 06:07:16 -05:00
initReason = fmt . Sprintf ( "Changed from cloud backend to backend %q" , previousBackendType )
2021-11-22 15:32:34 -06:00
case cloud . ConfigChangeInPlace :
2023-10-12 06:07:16 -05:00
initReason = "Cloud backend configuration block has changed"
2021-11-22 15:32:34 -06:00
default :
switch {
case previousBackendType != currentBackendType :
initReason = fmt . Sprintf ( "Backend type changed from %q to %q" , previousBackendType , currentBackendType )
default :
initReason = "Backend configuration block has changed"
}
}
var diags tfdiags . Diagnostics
switch cloudMode {
case cloud . ConfigChangeInPlace :
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
2023-10-12 06:07:16 -05:00
"Cloud backend initialization required: please run \"tofu init\"" ,
2021-11-22 15:32:34 -06:00
fmt . Sprintf ( strings . TrimSpace ( errBackendInitCloud ) , initReason ) ,
) )
case cloud . ConfigMigrationIn :
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
2023-10-12 06:07:16 -05:00
"Cloud backend initialization required: please run \"tofu init\"" ,
2021-12-01 11:09:46 -06:00
fmt . Sprintf ( strings . TrimSpace ( errBackendInitCloud ) , initReason ) ,
2021-11-22 15:32:34 -06:00
) )
default :
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
2023-09-21 07:38:46 -05:00
"Backend initialization required: please run \"tofu init\"" ,
2021-11-22 15:32:34 -06:00
fmt . Sprintf ( strings . TrimSpace ( errBackendInit ) , initReason ) ,
) )
}
return diags
}
2020-01-07 14:07:06 -06:00
// backendFromState returns the initialized (not configured) backend directly
command: Start of propagating OpenTelemetry context
Several times over the years we've considered adding tracing
instrumentation to Terraform, since even when running in isolation as a
CLI program it has a "distributed system-like" structure, with lots of
concurrent internal work and also some work delegated to provider plugins
that are essentially temporarily-running microservices.
However, it's always felt a bit overwhelming to do it because much of
Terraform predates the Go context.Context idiom and so it's tough to get
a clean chain of context.Context values all the way down the stack without
disturbing a lot of existing APIs.
This commit aims to just get that process started by establishing how a
context can propagate from "package main" into the command package,
focusing initially on "terraform init" and some other commands that share
some underlying functions with that command.
OpenTelemetry has emerged as a de-facto industry standard and so this uses
its API directly, without any attempt to hide it behind an abstraction.
The OpenTelemetry API is itself already an adapter layer, so we should be
able to swap in any backend that uses comparable concepts. For now we just
discard the tracing reports by default, and allow users to opt in to
delivering traces over OTLP by setting an environment variable when
running Terraform (the environment variable was established in an earlier
commit, so this commit builds on that.)
When tracing collection is enabled, every Terraform CLI run will generate
at least one overall span representing the command that was run. Some
commands might also create child spans, but most currently do not.
2023-07-10 13:29:57 -05:00
// from the backend state. This should be used only when a user runs
2023-09-26 12:09:27 -05:00
// `tofu init -backend=false`. This function returns a local backend if
command: Start of propagating OpenTelemetry context
Several times over the years we've considered adding tracing
instrumentation to Terraform, since even when running in isolation as a
CLI program it has a "distributed system-like" structure, with lots of
concurrent internal work and also some work delegated to provider plugins
that are essentially temporarily-running microservices.
However, it's always felt a bit overwhelming to do it because much of
Terraform predates the Go context.Context idiom and so it's tough to get
a clean chain of context.Context values all the way down the stack without
disturbing a lot of existing APIs.
This commit aims to just get that process started by establishing how a
context can propagate from "package main" into the command package,
focusing initially on "terraform init" and some other commands that share
some underlying functions with that command.
OpenTelemetry has emerged as a de-facto industry standard and so this uses
its API directly, without any attempt to hide it behind an abstraction.
The OpenTelemetry API is itself already an adapter layer, so we should be
able to swap in any backend that uses comparable concepts. For now we just
discard the tracing reports by default, and allow users to opt in to
delivering traces over OTLP by setting an environment variable when
running Terraform (the environment variable was established in an earlier
commit, so this commit builds on that.)
When tracing collection is enabled, every Terraform CLI run will generate
at least one overall span representing the command that was run. Some
commands might also create child spans, but most currently do not.
2023-07-10 13:29:57 -05:00
// there is no backend state or no backend configured.
2024-03-07 07:55:57 -06:00
func ( m * Meta ) backendFromState ( ctx context . Context , enc encryption . StateEncryption ) ( backend . Backend , tfdiags . Diagnostics ) {
2020-01-07 14:07:06 -06:00
var diags tfdiags . Diagnostics
// Get the path to where we store a local cache of backend configuration
// if we're using a remote backend. This may not yet exist which means
// we haven't used a non-local backend before. That is okay.
statePath := filepath . Join ( m . DataDir ( ) , DefaultStateFilename )
2020-08-11 10:43:01 -05:00
sMgr := & clistate . LocalState { Path : statePath }
2020-01-07 14:07:06 -06:00
if err := sMgr . RefreshState ( ) ; err != nil {
2023-09-18 07:16:17 -05:00
diags = diags . Append ( fmt . Errorf ( "Failed to load state: %w" , err ) )
2020-01-07 14:07:06 -06:00
return nil , diags
}
s := sMgr . State ( )
if s == nil {
// no state, so return a local backend
log . Printf ( "[TRACE] Meta.Backend: backend has not previously been initialized in this working directory" )
2024-03-07 07:55:57 -06:00
return backendLocal . New ( enc ) , diags
2020-01-07 14:07:06 -06:00
}
if s . Backend == nil {
// s.Backend is nil, so return a local backend
log . Printf ( "[TRACE] Meta.Backend: working directory was previously initialized but has no backend (is using legacy remote state?)" )
2024-03-07 07:55:57 -06:00
return backendLocal . New ( enc ) , diags
2020-01-07 14:07:06 -06:00
}
log . Printf ( "[TRACE] Meta.Backend: working directory was previously initialized for %q backend" , s . Backend . Type )
//backend init function
if s . Backend . Type == "" {
2024-03-07 07:55:57 -06:00
return backendLocal . New ( enc ) , diags
2020-01-07 14:07:06 -06:00
}
f := backendInit . Backend ( s . Backend . Type )
if f == nil {
diags = diags . Append ( fmt . Errorf ( strings . TrimSpace ( errBackendSavedUnknown ) , s . Backend . Type ) )
return nil , diags
}
2024-03-07 07:55:57 -06:00
b := f ( enc )
2020-01-07 14:07:06 -06:00
// The configuration saved in the working directory state file is used
// in this case, since it will contain any additional values that
2023-09-26 12:09:27 -05:00
// were provided via -backend-config arguments on tofu init.
2023-11-08 15:09:14 -06:00
schema := b . ConfigSchema ( )
2020-01-07 14:07:06 -06:00
configVal , err := s . Backend . Config ( schema )
if err != nil {
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
"Failed to decode current backend config" ,
2023-09-21 07:38:46 -05:00
fmt . Sprintf ( "The backend configuration created by the most recent run of \"tofu init\" could not be decoded: %s. The configuration may have been initialized by an earlier version that used an incompatible configuration structure. Run \"tofu init -reconfigure\" to force re-initialization of the backend." , err ) ,
2020-01-07 14:07:06 -06:00
) )
return nil , diags
}
// Validate the config and then configure the backend
2023-11-08 15:09:14 -06:00
newVal , validDiags := b . PrepareConfig ( configVal )
2020-01-07 14:07:06 -06:00
diags = diags . Append ( validDiags )
if validDiags . HasErrors ( ) {
return nil , diags
}
2023-11-08 15:09:14 -06:00
configDiags := b . Configure ( newVal )
2020-01-07 14:07:06 -06:00
diags = diags . Append ( configDiags )
if configDiags . HasErrors ( ) {
return nil , diags
}
2023-06-26 12:02:55 -05:00
// If the result of loading the backend is an enhanced backend,
// then set up enhanced backend service aliases.
if enhanced , ok := b . ( backend . Enhanced ) ; ok {
log . Printf ( "[TRACE] Meta.BackendForPlan: backend %T supports operations" , b )
if err := m . setupEnhancedBackendAliases ( enhanced ) ; err != nil {
diags = diags . Append ( err )
return nil , diags
}
}
2020-01-07 14:07:06 -06:00
return b , diags
}
2017-01-18 22:50:04 -06:00
//-------------------------------------------------------------------
// Backend Config Scenarios
//
// The functions below cover handling all the various scenarios that
// can exist when loading a backend. They are named in the format of
// "backend_C_R_S" where C, R, S may be upper or lowercase. Lowercase
// means it is false, uppercase means it is true. The full set of eight
// possible cases is handled.
//
// The fields are:
//
// * C - Backend configuration is set and changed in TF files
// * R - Legacy remote state is set
// * S - Backend configuration is set in the state
//
//-------------------------------------------------------------------
// Unconfiguring a backend (moving from backend => local).
2023-02-07 02:06:12 -06:00
func ( m * Meta ) backend_c_r_S (
2024-03-07 07:55:57 -06:00
c * configs . Backend , cHash int , sMgr * clistate . LocalState , output bool , opts * BackendOpts , enc encryption . StateEncryption ) ( backend . Backend , tfdiags . Diagnostics ) {
2023-02-07 02:06:12 -06:00
2021-11-12 19:07:10 -06:00
var diags tfdiags . Diagnostics
2023-02-07 02:06:12 -06:00
vt := arguments . ViewJSON
// Set default viewtype if none was set as the StateLocker needs to know exactly
// what viewType we want to have.
if opts == nil || opts . ViewType != vt {
vt = arguments . ViewHuman
}
2017-01-18 22:50:04 -06:00
s := sMgr . State ( )
2021-11-12 19:07:10 -06:00
cloudMode := cloud . DetectConfigChangeType ( s . Backend , c , false )
diags = diags . Append ( m . assertSupportedCloudInitOptions ( cloudMode ) )
if diags . HasErrors ( ) {
return nil , diags
}
2017-01-18 22:50:04 -06:00
// Get the backend type for output
backendType := s . Backend . Type
2021-11-12 19:07:10 -06:00
if cloudMode == cloud . ConfigMigrationOut {
2023-10-12 06:07:16 -05:00
m . Ui . Output ( "Migrating from cloud backend to local state." )
2021-10-27 16:27:12 -05:00
} else {
m . Ui . Output ( fmt . Sprintf ( strings . TrimSpace ( outputBackendMigrateLocal ) , s . Backend . Type ) )
}
2017-01-18 22:50:04 -06:00
2017-12-18 10:11:09 -06:00
// Grab a purely local backend to get the local state if it exists
2024-03-07 07:55:57 -06:00
localB , moreDiags := m . Backend ( & BackendOpts { ForceLocal : true , Init : true } , enc )
2021-11-12 19:07:10 -06:00
diags = diags . Append ( moreDiags )
if moreDiags . HasErrors ( ) {
2018-03-27 17:31:05 -05:00
return nil , diags
2017-12-18 10:11:09 -06:00
}
2017-02-28 12:13:03 -06:00
2017-12-18 10:11:09 -06:00
// Initialize the configured backend
2024-03-07 07:55:57 -06:00
b , moreDiags := m . savedBackend ( sMgr , enc )
2018-03-27 17:31:05 -05:00
diags = diags . Append ( moreDiags )
if moreDiags . HasErrors ( ) {
return nil , diags
2017-12-18 10:11:09 -06:00
}
2017-01-18 22:50:04 -06:00
2017-12-18 10:11:09 -06:00
// Perform the migration
2018-03-27 17:31:05 -05:00
err := m . backendMigrateState ( & backendMigrateOpts {
2021-10-05 15:36:50 -05:00
SourceType : s . Backend . Type ,
DestinationType : "local" ,
Source : b ,
Destination : localB ,
2023-02-07 02:06:12 -06:00
ViewType : vt ,
2017-12-18 10:11:09 -06:00
} )
if err != nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( err )
return nil , diags
2017-01-18 22:50:04 -06:00
}
// Remove the stored metadata
s . Backend = nil
if err := sMgr . WriteState ( s ) ; err != nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( fmt . Errorf ( strings . TrimSpace ( errBackendClearSaved ) , err ) )
return nil , diags
2017-01-18 22:50:04 -06:00
}
if err := sMgr . PersistState ( ) ; err != nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( fmt . Errorf ( strings . TrimSpace ( errBackendClearSaved ) , err ) )
return nil , diags
2017-01-18 22:50:04 -06:00
}
if output {
m . Ui . Output ( m . Colorize ( ) . Color ( fmt . Sprintf (
"[reset][green]\n\n" +
strings . TrimSpace ( successBackendUnset ) , backendType ) ) )
}
// Return no backend
2018-03-27 17:31:05 -05:00
return nil , diags
2017-01-18 22:50:04 -06:00
}
// Configuring a backend for the first time.
2024-03-07 07:55:57 -06:00
func ( m * Meta ) backend_C_r_s ( c * configs . Backend , cHash int , sMgr * clistate . LocalState , opts * BackendOpts , enc encryption . StateEncryption ) ( backend . Backend , tfdiags . Diagnostics ) {
2021-11-12 19:07:10 -06:00
var diags tfdiags . Diagnostics
2017-01-18 22:50:04 -06:00
2023-02-07 02:06:12 -06:00
vt := arguments . ViewJSON
// Set default viewtype if none was set as the StateLocker needs to know exactly
// what viewType we want to have.
if opts == nil || opts . ViewType != vt {
vt = arguments . ViewHuman
}
2017-01-18 22:50:04 -06:00
// Grab a purely local backend to get the local state if it exists
2024-03-07 07:55:57 -06:00
localB , localBDiags := m . Backend ( & BackendOpts { ForceLocal : true , Init : true } , enc )
2018-03-27 17:31:05 -05:00
if localBDiags . HasErrors ( ) {
diags = diags . Append ( localBDiags )
return nil , diags
2017-01-18 22:50:04 -06:00
}
2017-02-28 12:13:03 -06:00
2023-11-08 15:09:14 -06:00
workspaces , err := localB . Workspaces ( )
2017-01-18 22:50:04 -06:00
if err != nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( fmt . Errorf ( errBackendLocalRead , err ) )
return nil , diags
2017-01-18 22:50:04 -06:00
}
2018-10-31 10:45:03 -05:00
2020-08-11 10:43:01 -05:00
var localStates [ ] statemgr . Full
2018-10-31 10:45:03 -05:00
for _ , workspace := range workspaces {
2023-11-08 15:09:14 -06:00
localState , err := localB . StateMgr ( workspace )
2018-10-31 10:45:03 -05:00
if err != nil {
diags = diags . Append ( fmt . Errorf ( errBackendLocalRead , err ) )
return nil , diags
}
2023-11-08 15:09:14 -06:00
if err := localState . RefreshState ( ) ; err != nil {
2018-10-31 10:45:03 -05:00
diags = diags . Append ( fmt . Errorf ( errBackendLocalRead , err ) )
return nil , diags
}
// We only care about non-empty states.
if localS := localState . State ( ) ; ! localS . Empty ( ) {
2018-11-12 20:27:39 -06:00
log . Printf ( "[TRACE] Meta.Backend: will need to migrate workspace states because of existing %q workspace" , workspace )
2018-10-31 10:45:03 -05:00
localStates = append ( localStates , localState )
2018-11-12 20:27:39 -06:00
} else {
log . Printf ( "[TRACE] Meta.Backend: ignoring local %q workspace because its state is empty" , workspace )
2018-10-31 10:45:03 -05:00
}
2017-01-18 22:50:04 -06:00
}
2021-11-12 19:07:10 -06:00
cloudMode := cloud . DetectConfigChangeType ( nil , c , len ( localStates ) > 0 )
diags = diags . Append ( m . assertSupportedCloudInitOptions ( cloudMode ) )
if diags . HasErrors ( ) {
return nil , diags
}
// Get the backend
2024-03-07 07:55:57 -06:00
b , configVal , moreDiags := m . backendInitFromConfig ( c , enc )
2021-11-12 19:07:10 -06:00
diags = diags . Append ( moreDiags )
if diags . HasErrors ( ) {
return nil , diags
}
2018-10-31 10:45:03 -05:00
if len ( localStates ) > 0 {
2017-01-18 22:50:04 -06:00
// Perform the migration
err = m . backendMigrateState ( & backendMigrateOpts {
2021-10-05 15:36:50 -05:00
SourceType : "local" ,
DestinationType : c . Type ,
Source : localB ,
Destination : b ,
2023-02-07 02:06:12 -06:00
ViewType : vt ,
2017-01-18 22:50:04 -06:00
} )
if err != nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( err )
return nil , diags
2017-01-18 22:50:04 -06:00
}
2017-03-31 14:21:32 -05:00
// we usually remove the local state after migration to prevent
// confusion, but adding a default local backend block to the config
// can get us here too. Don't delete our state if the old and new paths
// are the same.
erase := true
2018-10-31 10:45:03 -05:00
if newLocalB , ok := b . ( * backendLocal . Local ) ; ok {
if localB , ok := localB . ( * backendLocal . Local ) ; ok {
2023-11-08 15:09:14 -06:00
if newLocalB . PathsConflictWith ( localB ) {
2017-03-31 14:21:32 -05:00
erase = false
2018-11-15 18:01:56 -06:00
log . Printf ( "[TRACE] Meta.Backend: both old and new backends share the same local state paths, so not erasing old state" )
2017-03-31 14:21:32 -05:00
}
}
2017-01-18 22:50:04 -06:00
}
2017-03-31 14:21:32 -05:00
if erase {
2018-11-15 18:01:56 -06:00
log . Printf ( "[TRACE] Meta.Backend: removing old state snapshots from old backend" )
2018-10-31 10:45:03 -05:00
for _ , localState := range localStates {
// We always delete the local state, unless that was our new state too.
if err := localState . WriteState ( nil ) ; err != nil {
diags = diags . Append ( fmt . Errorf ( errBackendMigrateLocalDelete , err ) )
return nil , diags
}
2023-11-08 15:09:14 -06:00
if err := localState . PersistState ( nil ) ; err != nil {
2018-10-31 10:45:03 -05:00
diags = diags . Append ( fmt . Errorf ( errBackendMigrateLocalDelete , err ) )
return nil , diags
}
2017-03-31 14:21:32 -05:00
}
2017-01-18 22:50:04 -06:00
}
}
2017-04-01 14:42:13 -05:00
if m . stateLock {
2023-02-07 02:06:12 -06:00
view := views . NewStateLocker ( vt , m . View )
2021-02-16 06:19:22 -06:00
stateLocker := clistate . NewLocker ( m . stateLockTimeout , view )
2023-09-18 07:16:17 -05:00
if d := stateLocker . Lock ( sMgr , "backend from plan" ) ; d != nil {
diags = diags . Append ( fmt . Errorf ( "Error locking state: %s" , d ) )
2018-03-27 17:31:05 -05:00
return nil , diags
2017-04-01 14:42:13 -05:00
}
2021-02-16 06:19:22 -06:00
defer stateLocker . Unlock ( )
2017-02-09 14:35:49 -06:00
}
2023-11-08 15:09:14 -06:00
configJSON , err := ctyjson . Marshal ( configVal , b . ConfigSchema ( ) . ImpliedType ( ) )
2018-03-27 17:31:05 -05:00
if err != nil {
2023-09-18 07:16:17 -05:00
diags = diags . Append ( fmt . Errorf ( "Can't serialize backend configuration as JSON: %w" , err ) )
2018-03-27 17:31:05 -05:00
return nil , diags
}
2017-01-18 22:50:04 -06:00
// Store the metadata in our saved state location
s := sMgr . State ( )
if s == nil {
2020-11-18 12:41:33 -06:00
s = legacy . NewState ( )
2017-01-18 22:50:04 -06:00
}
2020-11-18 12:41:33 -06:00
s . Backend = & legacy . BackendState {
2018-03-27 17:31:05 -05:00
Type : c . Type ,
ConfigRaw : json . RawMessage ( configJSON ) ,
2018-12-18 16:50:42 -06:00
Hash : uint64 ( cHash ) ,
2017-01-18 22:50:04 -06:00
}
2017-02-09 14:35:49 -06:00
2021-12-10 13:43:17 -06:00
// Verify that selected workspace exists in the backend.
2021-12-01 10:53:47 -06:00
if opts . Init && b != nil {
2021-12-10 13:43:17 -06:00
err := m . selectWorkspace ( b )
if err != nil {
2021-12-01 10:53:47 -06:00
diags = diags . Append ( err )
2021-12-10 13:43:17 -06:00
// FIXME: A compatibility oddity with the 'remote' backend.
// As an awkward legacy UX, when the remote backend is configured and there
// are no workspaces, the output to the user saying that there are none and
// the user should create one with 'workspace new' takes the form of an
// error message - even though it's happy path, expected behavior.
//
// Therefore, only return nil with errored diags for everything else, and
// allow the remote backend to continue and write its configuration to state
// even though no workspace is selected.
if c . Type != "remote" {
return nil , diags
}
2021-12-01 10:53:47 -06:00
}
}
2017-01-18 22:50:04 -06:00
if err := sMgr . WriteState ( s ) ; err != nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( fmt . Errorf ( errBackendWriteSaved , err ) )
return nil , diags
2017-01-18 22:50:04 -06:00
}
if err := sMgr . PersistState ( ) ; err != nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( fmt . Errorf ( errBackendWriteSaved , err ) )
return nil , diags
2017-01-18 22:50:04 -06:00
}
2021-10-13 10:22:12 -05:00
// By now the backend is successfully configured. If using Terraform Cloud, the success
// message is handled as part of the final init message
if _ , ok := b . ( * cloud . Cloud ) ; ! ok {
m . Ui . Output ( m . Colorize ( ) . Color ( fmt . Sprintf (
"[reset][green]\n" + strings . TrimSpace ( successBackendSet ) , s . Backend . Type ) ) )
}
2018-11-12 09:51:32 -06:00
2018-03-27 17:31:05 -05:00
return b , diags
2017-01-18 22:50:04 -06:00
}
// Changing a previously saved backend.
2024-03-07 07:55:57 -06:00
func ( m * Meta ) backend_C_r_S_changed ( c * configs . Backend , cHash int , sMgr * clistate . LocalState , output bool , opts * BackendOpts , enc encryption . StateEncryption ) ( backend . Backend , tfdiags . Diagnostics ) {
2021-11-12 19:07:10 -06:00
var diags tfdiags . Diagnostics
2017-01-18 22:50:04 -06:00
2023-02-07 02:06:12 -06:00
vt := arguments . ViewJSON
// Set default viewtype if none was set as the StateLocker needs to know exactly
// what viewType we want to have.
if opts == nil || opts . ViewType != vt {
vt = arguments . ViewHuman
}
2017-02-09 14:35:49 -06:00
// Get the old state
s := sMgr . State ( )
2021-11-12 19:07:10 -06:00
cloudMode := cloud . DetectConfigChangeType ( s . Backend , c , false )
diags = diags . Append ( m . assertSupportedCloudInitOptions ( cloudMode ) )
2018-03-27 17:31:05 -05:00
if diags . HasErrors ( ) {
return nil , diags
2017-01-18 22:50:04 -06:00
}
2021-11-12 19:07:10 -06:00
if output {
// Notify the user
switch cloudMode {
case cloud . ConfigChangeInPlace :
2023-10-12 06:07:16 -05:00
m . Ui . Output ( "Cloud backend configuration has changed." )
2021-11-12 19:07:10 -06:00
case cloud . ConfigMigrationIn :
2023-10-12 06:07:16 -05:00
m . Ui . Output ( fmt . Sprintf ( "Migrating from backend %q to cloud backend." , s . Backend . Type ) )
2021-11-12 19:07:10 -06:00
case cloud . ConfigMigrationOut :
2023-10-12 06:07:16 -05:00
m . Ui . Output ( fmt . Sprintf ( "Migrating from cloud backend to backend %q." , c . Type ) )
2021-11-12 19:07:10 -06:00
default :
if s . Backend . Type != c . Type {
output := fmt . Sprintf ( outputBackendMigrateChange , s . Backend . Type , c . Type )
m . Ui . Output ( m . Colorize ( ) . Color ( fmt . Sprintf (
"[reset]%s\n" ,
strings . TrimSpace ( output ) ) ) )
} else {
m . Ui . Output ( m . Colorize ( ) . Color ( fmt . Sprintf (
"[reset]%s\n" ,
strings . TrimSpace ( outputBackendReconfigure ) ) ) )
}
2021-10-18 16:02:45 -05:00
}
2017-12-20 16:50:37 -06:00
}
2017-01-18 22:50:04 -06:00
2021-11-12 19:07:10 -06:00
// Get the backend
2024-03-07 07:55:57 -06:00
b , configVal , moreDiags := m . backendInitFromConfig ( c , enc )
2021-11-12 19:07:10 -06:00
diags = diags . Append ( moreDiags )
if moreDiags . HasErrors ( ) {
2018-03-27 17:31:05 -05:00
return nil , diags
2017-12-18 10:11:09 -06:00
}
2017-01-18 22:50:04 -06:00
2021-11-12 19:07:10 -06:00
// If this is a migration into, out of, or irrelevant to Terraform Cloud
// mode then we will do state migration here. Otherwise, we just update
// the working directory initialization directly, because Terraform Cloud
// doesn't have configurable state storage anyway -- we're only changing
// which workspaces are relevant to this configuration, not where their
// state lives.
if cloudMode != cloud . ConfigChangeInPlace {
// Grab the existing backend
2024-03-07 07:55:57 -06:00
oldB , oldBDiags := m . savedBackend ( sMgr , enc )
2021-11-12 19:07:10 -06:00
diags = diags . Append ( oldBDiags )
if oldBDiags . HasErrors ( ) {
return nil , diags
}
2017-01-18 22:50:04 -06:00
2021-11-12 19:07:10 -06:00
// Perform the migration
err := m . backendMigrateState ( & backendMigrateOpts {
SourceType : s . Backend . Type ,
DestinationType : c . Type ,
Source : oldB ,
Destination : b ,
2023-02-07 02:06:12 -06:00
ViewType : vt ,
2021-11-12 19:07:10 -06:00
} )
if err != nil {
diags = diags . Append ( err )
2018-03-27 17:31:05 -05:00
return nil , diags
2017-04-01 14:42:13 -05:00
}
2021-11-12 19:07:10 -06:00
if m . stateLock {
2023-02-07 02:06:12 -06:00
view := views . NewStateLocker ( vt , m . View )
2021-11-12 19:07:10 -06:00
stateLocker := clistate . NewLocker ( m . stateLockTimeout , view )
2023-09-18 07:16:17 -05:00
if d := stateLocker . Lock ( sMgr , "backend from plan" ) ; d != nil {
diags = diags . Append ( fmt . Errorf ( "Error locking state: %s" , d ) )
2021-11-12 19:07:10 -06:00
return nil , diags
}
defer stateLocker . Unlock ( )
}
2017-02-09 14:35:49 -06:00
}
2023-11-08 15:09:14 -06:00
configJSON , err := ctyjson . Marshal ( configVal , b . ConfigSchema ( ) . ImpliedType ( ) )
2018-03-27 17:31:05 -05:00
if err != nil {
2023-09-18 07:16:17 -05:00
diags = diags . Append ( fmt . Errorf ( "Can't serialize backend configuration as JSON: %w" , err ) )
2018-03-27 17:31:05 -05:00
return nil , diags
}
2017-01-18 22:50:04 -06:00
// Update the backend state
2017-02-09 14:35:49 -06:00
s = sMgr . State ( )
2017-01-18 22:50:04 -06:00
if s == nil {
2020-11-18 12:41:33 -06:00
s = legacy . NewState ( )
2017-01-18 22:50:04 -06:00
}
2020-11-18 12:41:33 -06:00
s . Backend = & legacy . BackendState {
2018-03-27 17:31:05 -05:00
Type : c . Type ,
ConfigRaw : json . RawMessage ( configJSON ) ,
2018-12-18 16:50:42 -06:00
Hash : uint64 ( cHash ) ,
2017-01-18 22:50:04 -06:00
}
2017-02-09 14:35:49 -06:00
2021-12-01 10:53:47 -06:00
// Verify that selected workspace exist. Otherwise prompt user to create one
if opts . Init && b != nil {
if err := m . selectWorkspace ( b ) ; err != nil {
diags = diags . Append ( err )
return b , diags
}
}
2017-01-18 22:50:04 -06:00
if err := sMgr . WriteState ( s ) ; err != nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( fmt . Errorf ( errBackendWriteSaved , err ) )
return nil , diags
2017-01-18 22:50:04 -06:00
}
if err := sMgr . PersistState ( ) ; err != nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( fmt . Errorf ( errBackendWriteSaved , err ) )
return nil , diags
2017-01-18 22:50:04 -06:00
}
if output {
2021-10-18 16:02:45 -05:00
// By now the backend is successfully configured. If using Terraform Cloud, the success
// message is handled as part of the final init message
if _ , ok := b . ( * cloud . Cloud ) ; ! ok {
m . Ui . Output ( m . Colorize ( ) . Color ( fmt . Sprintf (
"[reset][green]\n" + strings . TrimSpace ( successBackendSet ) , s . Backend . Type ) ) )
}
2017-01-18 22:50:04 -06:00
}
2018-03-27 17:31:05 -05:00
return b , diags
2017-01-18 22:50:04 -06:00
}
2021-11-02 13:26:00 -05:00
// Initializing a saved backend from the cache file (legacy state file)
//
// TODO: This is extremely similar to Meta.backendFromState() but for legacy reasons this is the
// function used by the migration APIs within this file. The other handles 'init -backend=false',
// specifically.
2024-03-07 07:55:57 -06:00
func ( m * Meta ) savedBackend ( sMgr * clistate . LocalState , enc encryption . StateEncryption ) ( backend . Backend , tfdiags . Diagnostics ) {
2018-03-27 17:31:05 -05:00
var diags tfdiags . Diagnostics
2017-01-18 22:50:04 -06:00
s := sMgr . State ( )
// Get the backend
2018-10-31 10:45:03 -05:00
f := backendInit . Backend ( s . Backend . Type )
2017-02-22 13:17:27 -06:00
if f == nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( fmt . Errorf ( strings . TrimSpace ( errBackendSavedUnknown ) , s . Backend . Type ) )
return nil , diags
2017-01-18 22:50:04 -06:00
}
2024-03-07 07:55:57 -06:00
b := f ( enc )
2017-01-18 22:50:04 -06:00
2018-03-27 17:31:05 -05:00
// The configuration saved in the working directory state file is used
// in this case, since it will contain any additional values that
2023-09-26 12:09:27 -05:00
// were provided via -backend-config arguments on tofu init.
2023-11-08 15:09:14 -06:00
schema := b . ConfigSchema ( )
2018-03-27 17:31:05 -05:00
configVal , err := s . Backend . Config ( schema )
2017-01-18 22:50:04 -06:00
if err != nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
"Failed to decode current backend config" ,
2023-09-21 07:38:46 -05:00
fmt . Sprintf ( "The backend configuration created by the most recent run of \"tofu init\" could not be decoded: %s. The configuration may have been initialized by an earlier version that used an incompatible configuration structure. Run \"tofu init -reconfigure\" to force re-initialization of the backend." , err ) ,
2018-03-27 17:31:05 -05:00
) )
return nil , diags
2017-01-18 22:50:04 -06:00
}
2018-03-27 17:31:05 -05:00
// Validate the config and then configure the backend
2023-11-08 15:09:14 -06:00
newVal , validDiags := b . PrepareConfig ( configVal )
2018-03-27 17:31:05 -05:00
diags = diags . Append ( validDiags )
if validDiags . HasErrors ( ) {
return nil , diags
2017-01-18 22:50:04 -06:00
}
2019-02-25 17:37:20 -06:00
2023-11-08 15:09:14 -06:00
configDiags := b . Configure ( newVal )
2018-03-27 17:31:05 -05:00
diags = diags . Append ( configDiags )
if configDiags . HasErrors ( ) {
return nil , diags
2017-01-18 22:50:04 -06:00
}
2023-06-26 12:02:55 -05:00
// If the result of loading the backend is an enhanced backend,
// then set up enhanced backend service aliases.
if enhanced , ok := b . ( backend . Enhanced ) ; ok {
log . Printf ( "[TRACE] Meta.BackendForPlan: backend %T supports operations" , b )
if err := m . setupEnhancedBackendAliases ( enhanced ) ; err != nil {
diags = diags . Append ( err )
return nil , diags
}
}
2018-03-27 17:31:05 -05:00
return b , diags
}
2017-02-28 12:13:03 -06:00
2021-11-02 13:26:00 -05:00
func ( m * Meta ) updateSavedBackendHash ( cHash int , sMgr * clistate . LocalState ) tfdiags . Diagnostics {
var diags tfdiags . Diagnostics
s := sMgr . State ( )
if s . Backend . Hash != uint64 ( cHash ) {
s . Backend . Hash = uint64 ( cHash )
if err := sMgr . WriteState ( s ) ; err != nil {
diags = diags . Append ( err )
}
}
return diags
}
2017-01-18 22:50:04 -06:00
//-------------------------------------------------------------------
// Reusable helper functions for backend management
//-------------------------------------------------------------------
2018-11-09 14:13:30 -06:00
// backendConfigNeedsMigration returns true if migration might be required to
// move from the configured backend to the given cached backend config.
//
// This must be called with the synthetic *configs.Backend that results from
// merging in any command-line options for correct behavior.
//
// If either the given configuration or cached configuration are invalid then
// this function will conservatively assume that migration is required,
// expecting that the migration code will subsequently deal with the same
// errors.
2020-11-18 12:41:33 -06:00
func ( m * Meta ) backendConfigNeedsMigration ( c * configs . Backend , s * legacy . BackendState ) bool {
2018-11-09 14:13:30 -06:00
if s == nil || s . Empty ( ) {
log . Print ( "[TRACE] backendConfigNeedsMigration: no cached config, so migration is required" )
return true
}
if c . Type != s . Type {
log . Printf ( "[TRACE] backendConfigNeedsMigration: type changed from %q to %q, so migration is required" , s . Type , c . Type )
return true
}
// We need the backend's schema to do our comparison here.
f := backendInit . Backend ( c . Type )
if f == nil {
log . Printf ( "[TRACE] backendConfigNeedsMigration: no backend of type %q, which migration codepath must handle" , c . Type )
return true // let the migration codepath deal with the missing backend
}
2024-03-07 07:55:57 -06:00
b := f ( nil ) // We don't need encryption here as it's only used for config/schema
2018-11-09 14:13:30 -06:00
2024-11-04 13:23:32 -06:00
// We use "NoneRequired" here because we're only evaluating the body written directly
// in the root module configuration, and we're intentionally not including any
// additional arguments passed on the command line (using -backend-config), so
// some of the required arguments might be satisfied from outside of the body we're
// evaluating here.
schema := b . ConfigSchema ( ) . NoneRequired ( )
2024-10-08 18:38:56 -05:00
givenVal , diags := c . Decode ( schema )
2018-11-09 14:13:30 -06:00
if diags . HasErrors ( ) {
log . Printf ( "[TRACE] backendConfigNeedsMigration: failed to decode given config; migration codepath must handle problem: %s" , diags . Error ( ) )
return true // let the migration codepath deal with these errors
}
cachedVal , err := s . Config ( schema )
if err != nil {
log . Printf ( "[TRACE] backendConfigNeedsMigration: failed to decode cached config; migration codepath must handle problem: %s" , err )
return true // let the migration codepath deal with the error
}
// If we get all the way down here then it's the exact equality of the
// two decoded values that decides our outcome. It's safe to use RawEquals
// here (rather than Equals) because we know that unknown values can
// never appear in backend configurations.
if cachedVal . RawEquals ( givenVal ) {
log . Print ( "[TRACE] backendConfigNeedsMigration: given configuration matches cached configuration, so no migration is required" )
return false
}
log . Print ( "[TRACE] backendConfigNeedsMigration: configuration values have changed, so migration is required" )
return true
}
2024-03-07 07:55:57 -06:00
func ( m * Meta ) backendInitFromConfig ( c * configs . Backend , enc encryption . StateEncryption ) ( backend . Backend , cty . Value , tfdiags . Diagnostics ) {
2018-03-27 17:31:05 -05:00
var diags tfdiags . Diagnostics
2017-01-18 22:50:04 -06:00
// Get the backend
2018-10-31 10:45:03 -05:00
f := backendInit . Backend ( c . Type )
2017-02-22 13:17:27 -06:00
if f == nil {
2018-03-27 17:31:05 -05:00
diags = diags . Append ( fmt . Errorf ( strings . TrimSpace ( errBackendNewUnknown ) , c . Type ) )
return nil , cty . NilVal , diags
2017-01-18 22:50:04 -06:00
}
2024-03-07 07:55:57 -06:00
b := f ( enc )
2017-01-18 22:50:04 -06:00
2023-11-08 15:09:14 -06:00
schema := b . ConfigSchema ( )
2024-06-24 08:13:07 -05:00
configVal , hclDiags := c . Decode ( schema . NoneRequired ( ) )
2018-03-27 17:31:05 -05:00
diags = diags . Append ( hclDiags )
if hclDiags . HasErrors ( ) {
return nil , cty . NilVal , diags
}
2023-08-09 04:19:00 -05:00
if ! configVal . IsWhollyKnown ( ) {
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
"Unknown values within backend definition" ,
2023-09-21 07:38:46 -05:00
"The `tofu` configuration block should contain only concrete and static values. Another diagnostic should contain more information about which part of the configuration is problematic." ) )
2023-08-09 04:19:00 -05:00
return nil , cty . NilVal , diags
}
2017-01-18 22:50:04 -06:00
// TODO: test
if m . Input ( ) {
var err error
2018-03-27 17:31:05 -05:00
configVal , err = m . inputForSchema ( configVal , schema )
2017-01-18 22:50:04 -06:00
if err != nil {
2023-09-18 07:16:17 -05:00
diags = diags . Append ( fmt . Errorf ( "Error asking for input to configure backend %q: %w" , c . Type , err ) )
2017-01-18 22:50:04 -06:00
}
2019-03-01 17:45:06 -06:00
// We get an unknown here if the if the user aborted input, but we can't
// turn that into a config value, so set it to null and let the provider
// handle it in PrepareConfig.
if ! configVal . IsKnown ( ) {
configVal = cty . NullVal ( configVal . Type ( ) )
}
2017-01-18 22:50:04 -06:00
}
2023-11-08 15:09:14 -06:00
newVal , validateDiags := b . PrepareConfig ( configVal )
2021-04-02 16:11:20 -05:00
diags = diags . Append ( validateDiags . InConfigBody ( c . Config , "" ) )
2018-03-27 17:31:05 -05:00
if validateDiags . HasErrors ( ) {
return nil , cty . NilVal , diags
2017-01-18 22:50:04 -06:00
}
2023-11-08 15:09:14 -06:00
configureDiags := b . Configure ( newVal )
2021-04-02 16:11:20 -05:00
diags = diags . Append ( configureDiags . InConfigBody ( c . Config , "" ) )
2017-01-18 22:50:04 -06:00
2023-06-26 12:02:55 -05:00
// If the result of loading the backend is an enhanced backend,
// then set up enhanced backend service aliases.
if enhanced , ok := b . ( backend . Enhanced ) ; ok {
log . Printf ( "[TRACE] Meta.BackendForPlan: backend %T supports operations" , b )
if err := m . setupEnhancedBackendAliases ( enhanced ) ; err != nil {
diags = diags . Append ( err )
return nil , cty . NilVal , diags
}
}
2018-03-27 17:31:05 -05:00
return b , configVal , diags
2017-01-18 22:50:04 -06:00
}
2023-06-26 12:02:55 -05:00
// Helper method to get aliases from the enhanced backend and alias them
// in the Meta service discovery. It's unfortunate that the Meta backend
// is modifying the service discovery at this level, but the owner
// of the service discovery pointer does not have easy access to the backend.
func ( m * Meta ) setupEnhancedBackendAliases ( b backend . Enhanced ) error {
// Set up the service discovery aliases specified by the enhanced backend.
serviceAliases , err := b . ServiceDiscoveryAliases ( )
if err != nil {
return err
}
for _ , alias := range serviceAliases {
m . Services . Alias ( alias . From , alias . To )
}
return nil
}
2021-08-24 14:28:12 -05:00
// Helper method to ignore remote/cloud backend version conflicts. Only call this
backend: Validate remote backend Terraform version
When using the enhanced remote backend, a subset of all Terraform
operations are supported. Of these, only plan and apply can be executed
on the remote infrastructure (e.g. Terraform Cloud). Other operations
run locally and use the remote backend for state storage.
This causes problems when the local version of Terraform does not match
the configured version from the remote workspace. If the two versions
are incompatible, an `import` or `state mv` operation can cause the
remote workspace to be unusable until a manual fix is applied.
To prevent this from happening accidentally, this commit introduces a
check that the local Terraform version and the configured remote
workspace Terraform version are compatible. This check is skipped for
commands which do not write state, and can also be disabled by the use
of a new command-line flag, `-ignore-remote-version`.
Terraform version compatibility is defined as:
- For all releases before 0.14.0, local must exactly equal remote, as
two different versions cannot share state;
- 0.14.0 to 1.0.x are compatible, as we will not change the state
version number until at least Terraform 1.1.0;
- Versions after 1.1.0 must have the same major and minor versions, as
we will not change the state version number in a patch release.
If the two versions are incompatible, a diagnostic is displayed,
advising that the error can be suppressed with `-ignore-remote-version`.
When this flag is used, the diagnostic is still displayed, but as a
warning instead of an error.
Commands which will not write state can assert this fact by calling the
helper `meta.ignoreRemoteBackendVersionConflict`, which will disable the
checks. Those which can write state should instead call the helper
`meta.remoteBackendVersionCheck`, which will return diagnostics for
display.
In addition to these explicit paths for managing the version check, we
have an implicit check in the remote backend's state manager
initialization method. Both of the above helpers will disable this
check. This fallback is in place to ensure that future code paths which
access state cannot accidentally skip the remote version check.
2020-11-13 15:43:56 -06:00
// for commands which cannot accidentally upgrade remote state files.
2021-08-24 14:28:12 -05:00
func ( m * Meta ) ignoreRemoteVersionConflict ( b backend . Backend ) {
if back , ok := b . ( BackendWithRemoteTerraformVersion ) ; ok {
back . IgnoreVersionConflict ( )
backend: Validate remote backend Terraform version
When using the enhanced remote backend, a subset of all Terraform
operations are supported. Of these, only plan and apply can be executed
on the remote infrastructure (e.g. Terraform Cloud). Other operations
run locally and use the remote backend for state storage.
This causes problems when the local version of Terraform does not match
the configured version from the remote workspace. If the two versions
are incompatible, an `import` or `state mv` operation can cause the
remote workspace to be unusable until a manual fix is applied.
To prevent this from happening accidentally, this commit introduces a
check that the local Terraform version and the configured remote
workspace Terraform version are compatible. This check is skipped for
commands which do not write state, and can also be disabled by the use
of a new command-line flag, `-ignore-remote-version`.
Terraform version compatibility is defined as:
- For all releases before 0.14.0, local must exactly equal remote, as
two different versions cannot share state;
- 0.14.0 to 1.0.x are compatible, as we will not change the state
version number until at least Terraform 1.1.0;
- Versions after 1.1.0 must have the same major and minor versions, as
we will not change the state version number in a patch release.
If the two versions are incompatible, a diagnostic is displayed,
advising that the error can be suppressed with `-ignore-remote-version`.
When this flag is used, the diagnostic is still displayed, but as a
warning instead of an error.
Commands which will not write state can assert this fact by calling the
helper `meta.ignoreRemoteBackendVersionConflict`, which will disable the
checks. Those which can write state should instead call the helper
`meta.remoteBackendVersionCheck`, which will return diagnostics for
display.
In addition to these explicit paths for managing the version check, we
have an implicit check in the remote backend's state manager
initialization method. Both of the above helpers will disable this
check. This fallback is in place to ensure that future code paths which
access state cannot accidentally skip the remote version check.
2020-11-13 15:43:56 -06:00
}
}
2023-09-26 12:09:27 -05:00
// Helper method to check the local OpenTofu version against the configured
backend: Validate remote backend Terraform version
When using the enhanced remote backend, a subset of all Terraform
operations are supported. Of these, only plan and apply can be executed
on the remote infrastructure (e.g. Terraform Cloud). Other operations
run locally and use the remote backend for state storage.
This causes problems when the local version of Terraform does not match
the configured version from the remote workspace. If the two versions
are incompatible, an `import` or `state mv` operation can cause the
remote workspace to be unusable until a manual fix is applied.
To prevent this from happening accidentally, this commit introduces a
check that the local Terraform version and the configured remote
workspace Terraform version are compatible. This check is skipped for
commands which do not write state, and can also be disabled by the use
of a new command-line flag, `-ignore-remote-version`.
Terraform version compatibility is defined as:
- For all releases before 0.14.0, local must exactly equal remote, as
two different versions cannot share state;
- 0.14.0 to 1.0.x are compatible, as we will not change the state
version number until at least Terraform 1.1.0;
- Versions after 1.1.0 must have the same major and minor versions, as
we will not change the state version number in a patch release.
If the two versions are incompatible, a diagnostic is displayed,
advising that the error can be suppressed with `-ignore-remote-version`.
When this flag is used, the diagnostic is still displayed, but as a
warning instead of an error.
Commands which will not write state can assert this fact by calling the
helper `meta.ignoreRemoteBackendVersionConflict`, which will disable the
checks. Those which can write state should instead call the helper
`meta.remoteBackendVersionCheck`, which will return diagnostics for
display.
In addition to these explicit paths for managing the version check, we
have an implicit check in the remote backend's state manager
initialization method. Both of the above helpers will disable this
check. This fallback is in place to ensure that future code paths which
access state cannot accidentally skip the remote version check.
2020-11-13 15:43:56 -06:00
// version in the remote workspace, returning diagnostics if they conflict.
2021-08-24 14:28:12 -05:00
func ( m * Meta ) remoteVersionCheck ( b backend . Backend , workspace string ) tfdiags . Diagnostics {
backend: Validate remote backend Terraform version
When using the enhanced remote backend, a subset of all Terraform
operations are supported. Of these, only plan and apply can be executed
on the remote infrastructure (e.g. Terraform Cloud). Other operations
run locally and use the remote backend for state storage.
This causes problems when the local version of Terraform does not match
the configured version from the remote workspace. If the two versions
are incompatible, an `import` or `state mv` operation can cause the
remote workspace to be unusable until a manual fix is applied.
To prevent this from happening accidentally, this commit introduces a
check that the local Terraform version and the configured remote
workspace Terraform version are compatible. This check is skipped for
commands which do not write state, and can also be disabled by the use
of a new command-line flag, `-ignore-remote-version`.
Terraform version compatibility is defined as:
- For all releases before 0.14.0, local must exactly equal remote, as
two different versions cannot share state;
- 0.14.0 to 1.0.x are compatible, as we will not change the state
version number until at least Terraform 1.1.0;
- Versions after 1.1.0 must have the same major and minor versions, as
we will not change the state version number in a patch release.
If the two versions are incompatible, a diagnostic is displayed,
advising that the error can be suppressed with `-ignore-remote-version`.
When this flag is used, the diagnostic is still displayed, but as a
warning instead of an error.
Commands which will not write state can assert this fact by calling the
helper `meta.ignoreRemoteBackendVersionConflict`, which will disable the
checks. Those which can write state should instead call the helper
`meta.remoteBackendVersionCheck`, which will return diagnostics for
display.
In addition to these explicit paths for managing the version check, we
have an implicit check in the remote backend's state manager
initialization method. Both of the above helpers will disable this
check. This fallback is in place to ensure that future code paths which
access state cannot accidentally skip the remote version check.
2020-11-13 15:43:56 -06:00
var diags tfdiags . Diagnostics
2021-08-24 14:28:12 -05:00
if back , ok := b . ( BackendWithRemoteTerraformVersion ) ; ok {
backend: Validate remote backend Terraform version
When using the enhanced remote backend, a subset of all Terraform
operations are supported. Of these, only plan and apply can be executed
on the remote infrastructure (e.g. Terraform Cloud). Other operations
run locally and use the remote backend for state storage.
This causes problems when the local version of Terraform does not match
the configured version from the remote workspace. If the two versions
are incompatible, an `import` or `state mv` operation can cause the
remote workspace to be unusable until a manual fix is applied.
To prevent this from happening accidentally, this commit introduces a
check that the local Terraform version and the configured remote
workspace Terraform version are compatible. This check is skipped for
commands which do not write state, and can also be disabled by the use
of a new command-line flag, `-ignore-remote-version`.
Terraform version compatibility is defined as:
- For all releases before 0.14.0, local must exactly equal remote, as
two different versions cannot share state;
- 0.14.0 to 1.0.x are compatible, as we will not change the state
version number until at least Terraform 1.1.0;
- Versions after 1.1.0 must have the same major and minor versions, as
we will not change the state version number in a patch release.
If the two versions are incompatible, a diagnostic is displayed,
advising that the error can be suppressed with `-ignore-remote-version`.
When this flag is used, the diagnostic is still displayed, but as a
warning instead of an error.
Commands which will not write state can assert this fact by calling the
helper `meta.ignoreRemoteBackendVersionConflict`, which will disable the
checks. Those which can write state should instead call the helper
`meta.remoteBackendVersionCheck`, which will return diagnostics for
display.
In addition to these explicit paths for managing the version check, we
have an implicit check in the remote backend's state manager
initialization method. Both of the above helpers will disable this
check. This fallback is in place to ensure that future code paths which
access state cannot accidentally skip the remote version check.
2020-11-13 15:43:56 -06:00
// Allow user override based on command-line flag
if m . ignoreRemoteVersion {
2021-08-24 14:28:12 -05:00
back . IgnoreVersionConflict ( )
backend: Validate remote backend Terraform version
When using the enhanced remote backend, a subset of all Terraform
operations are supported. Of these, only plan and apply can be executed
on the remote infrastructure (e.g. Terraform Cloud). Other operations
run locally and use the remote backend for state storage.
This causes problems when the local version of Terraform does not match
the configured version from the remote workspace. If the two versions
are incompatible, an `import` or `state mv` operation can cause the
remote workspace to be unusable until a manual fix is applied.
To prevent this from happening accidentally, this commit introduces a
check that the local Terraform version and the configured remote
workspace Terraform version are compatible. This check is skipped for
commands which do not write state, and can also be disabled by the use
of a new command-line flag, `-ignore-remote-version`.
Terraform version compatibility is defined as:
- For all releases before 0.14.0, local must exactly equal remote, as
two different versions cannot share state;
- 0.14.0 to 1.0.x are compatible, as we will not change the state
version number until at least Terraform 1.1.0;
- Versions after 1.1.0 must have the same major and minor versions, as
we will not change the state version number in a patch release.
If the two versions are incompatible, a diagnostic is displayed,
advising that the error can be suppressed with `-ignore-remote-version`.
When this flag is used, the diagnostic is still displayed, but as a
warning instead of an error.
Commands which will not write state can assert this fact by calling the
helper `meta.ignoreRemoteBackendVersionConflict`, which will disable the
checks. Those which can write state should instead call the helper
`meta.remoteBackendVersionCheck`, which will return diagnostics for
display.
In addition to these explicit paths for managing the version check, we
have an implicit check in the remote backend's state manager
initialization method. Both of the above helpers will disable this
check. This fallback is in place to ensure that future code paths which
access state cannot accidentally skip the remote version check.
2020-11-13 15:43:56 -06:00
}
// If the override is set, this check will return a warning instead of
// an error
2021-08-24 14:28:12 -05:00
versionDiags := back . VerifyWorkspaceTerraformVersion ( workspace )
backend: Validate remote backend Terraform version
When using the enhanced remote backend, a subset of all Terraform
operations are supported. Of these, only plan and apply can be executed
on the remote infrastructure (e.g. Terraform Cloud). Other operations
run locally and use the remote backend for state storage.
This causes problems when the local version of Terraform does not match
the configured version from the remote workspace. If the two versions
are incompatible, an `import` or `state mv` operation can cause the
remote workspace to be unusable until a manual fix is applied.
To prevent this from happening accidentally, this commit introduces a
check that the local Terraform version and the configured remote
workspace Terraform version are compatible. This check is skipped for
commands which do not write state, and can also be disabled by the use
of a new command-line flag, `-ignore-remote-version`.
Terraform version compatibility is defined as:
- For all releases before 0.14.0, local must exactly equal remote, as
two different versions cannot share state;
- 0.14.0 to 1.0.x are compatible, as we will not change the state
version number until at least Terraform 1.1.0;
- Versions after 1.1.0 must have the same major and minor versions, as
we will not change the state version number in a patch release.
If the two versions are incompatible, a diagnostic is displayed,
advising that the error can be suppressed with `-ignore-remote-version`.
When this flag is used, the diagnostic is still displayed, but as a
warning instead of an error.
Commands which will not write state can assert this fact by calling the
helper `meta.ignoreRemoteBackendVersionConflict`, which will disable the
checks. Those which can write state should instead call the helper
`meta.remoteBackendVersionCheck`, which will return diagnostics for
display.
In addition to these explicit paths for managing the version check, we
have an implicit check in the remote backend's state manager
initialization method. Both of the above helpers will disable this
check. This fallback is in place to ensure that future code paths which
access state cannot accidentally skip the remote version check.
2020-11-13 15:43:56 -06:00
diags = diags . Append ( versionDiags )
2021-01-13 09:43:35 -06:00
// If there are no errors resulting from this check, we do not need to
// check again
if ! diags . HasErrors ( ) {
2021-08-24 14:28:12 -05:00
back . IgnoreVersionConflict ( )
2021-01-13 09:43:35 -06:00
}
backend: Validate remote backend Terraform version
When using the enhanced remote backend, a subset of all Terraform
operations are supported. Of these, only plan and apply can be executed
on the remote infrastructure (e.g. Terraform Cloud). Other operations
run locally and use the remote backend for state storage.
This causes problems when the local version of Terraform does not match
the configured version from the remote workspace. If the two versions
are incompatible, an `import` or `state mv` operation can cause the
remote workspace to be unusable until a manual fix is applied.
To prevent this from happening accidentally, this commit introduces a
check that the local Terraform version and the configured remote
workspace Terraform version are compatible. This check is skipped for
commands which do not write state, and can also be disabled by the use
of a new command-line flag, `-ignore-remote-version`.
Terraform version compatibility is defined as:
- For all releases before 0.14.0, local must exactly equal remote, as
two different versions cannot share state;
- 0.14.0 to 1.0.x are compatible, as we will not change the state
version number until at least Terraform 1.1.0;
- Versions after 1.1.0 must have the same major and minor versions, as
we will not change the state version number in a patch release.
If the two versions are incompatible, a diagnostic is displayed,
advising that the error can be suppressed with `-ignore-remote-version`.
When this flag is used, the diagnostic is still displayed, but as a
warning instead of an error.
Commands which will not write state can assert this fact by calling the
helper `meta.ignoreRemoteBackendVersionConflict`, which will disable the
checks. Those which can write state should instead call the helper
`meta.remoteBackendVersionCheck`, which will return diagnostics for
display.
In addition to these explicit paths for managing the version check, we
have an implicit check in the remote backend's state manager
initialization method. Both of the above helpers will disable this
check. This fallback is in place to ensure that future code paths which
access state cannot accidentally skip the remote version check.
2020-11-13 15:43:56 -06:00
}
return diags
}
2021-11-12 19:07:10 -06:00
// assertSupportedCloudInitOptions returns diagnostics with errors if the
// init-related command line options (implied inside the Meta receiver)
// are incompatible with the given cloud configuration change mode.
func ( m * Meta ) assertSupportedCloudInitOptions ( mode cloud . ConfigChangeMode ) tfdiags . Diagnostics {
var diags tfdiags . Diagnostics
if mode . InvolvesCloud ( ) {
2023-10-12 06:07:16 -05:00
log . Printf ( "[TRACE] Meta.Backend: Cloud backend mode initialization type: %s" , mode )
2021-11-12 19:07:10 -06:00
if m . reconfigure {
if mode . IsCloudMigration ( ) {
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
"Invalid command-line option" ,
2023-10-12 06:07:16 -05:00
"The -reconfigure option is unsupported when migrating to cloud backend, because activating cloud backend involves some additional steps." ,
2021-11-12 19:07:10 -06:00
) )
} else {
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
"Invalid command-line option" ,
2023-10-12 06:07:16 -05:00
"The -reconfigure option is for in-place reconfiguration of state backends only, and is not needed when changing cloud backend settings.\n\nWhen using cloud backend, initialization automatically activates any new Cloud configuration settings." ,
2021-11-12 19:07:10 -06:00
) )
}
}
if m . migrateState {
name := "-migrate-state"
if m . forceInitCopy {
2023-09-26 12:09:27 -05:00
// -force copy implies -migrate-state in "tofu init",
2021-11-12 19:07:10 -06:00
// so m.migrateState is forced to true in this case even if
// the user didn't actually specify it. We'll use the other
// name here to avoid being confusing, then.
name = "-force-copy"
}
if mode . IsCloudMigration ( ) {
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
"Invalid command-line option" ,
2023-10-12 06:07:16 -05:00
fmt . Sprintf ( "The %s option is for migration between state backends only, and is not applicable when using cloud backend.\n\nCloud backend migration has additional steps, configured by interactive prompts." , name ) ,
2021-11-12 19:07:10 -06:00
) )
} else {
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
"Invalid command-line option" ,
2023-10-12 06:07:16 -05:00
fmt . Sprintf ( "The %s option is for migration between state backends only, and is not applicable when using cloud backend.\n\nState storage is handled automatically by cloud backend and so the state storage location is not configurable." , name ) ,
2021-11-12 19:07:10 -06:00
) )
}
}
}
return diags
}
2017-01-18 22:50:04 -06:00
//-------------------------------------------------------------------
// Output constants and initialization code
//-------------------------------------------------------------------
const errBackendLocalRead = `
2023-09-18 07:16:17 -05:00
Error reading local state : % w
2017-01-18 22:50:04 -06:00
2023-09-21 07:38:46 -05:00
OpenTofu is trying to read your local state to determine if there is
state to migrate to your newly configured backend . OpenTofu can ' t continue
2017-01-18 22:50:04 -06:00
without this check because that would risk losing state . Please resolve the
error above and try again .
`
const errBackendMigrateLocalDelete = `
2023-09-18 07:16:17 -05:00
Error deleting local state after migration : % w
2017-01-18 22:50:04 -06:00
Your local state is deleted after successfully migrating it to the newly
configured backend . As part of the deletion process , a backup is made at
the standard backup path unless explicitly asked not to . To cleanly operate
with a backend , we must delete the local state file . Please resolve the
issue above and retry the command .
`
const errBackendNewUnknown = `
The backend % q could not be found .
2023-09-21 07:38:46 -05:00
This is the backend specified in your OpenTofu configuration file .
2017-01-18 22:50:04 -06:00
This error could be a simple typo in your configuration , but it can also
2023-09-21 07:38:46 -05:00
be caused by using a OpenTofu version that doesn ' t support the specified
backend type . Please check your configuration and your OpenTofu version .
2017-01-18 22:50:04 -06:00
2023-09-21 07:38:46 -05:00
If you ' d like to run OpenTofu and store state locally , you can fix this
2017-01-18 22:50:04 -06:00
error by removing the backend configuration from your configuration .
`
2019-05-07 08:23:26 -05:00
const errBackendNoExistingWorkspaces = `
No existing workspaces .
2018-11-12 09:51:32 -06:00
2023-09-21 07:38:46 -05:00
Use the "tofu workspace" command to create and select a new workspace .
2018-11-12 09:51:32 -06:00
If the backend already contains existing workspaces , you may need to update
the backend configuration .
`
2017-01-18 22:50:04 -06:00
const errBackendSavedUnknown = `
The backend % q could not be found .
2023-09-21 07:38:46 -05:00
This is the backend that this OpenTofu environment is configured to use
2017-01-18 22:50:04 -06:00
both in your configuration and saved locally as your last - used backend .
2023-09-21 07:38:46 -05:00
If it isn ' t found , it could mean an alternate version of OpenTofu was
used with this configuration . Please use the proper version of OpenTofu that
2017-01-18 22:50:04 -06:00
contains support for this backend .
If you ' d like to force remove this backend , you must update your configuration
2023-09-21 07:38:46 -05:00
to not use the backend and run "tofu init" ( or any other command ) again .
2017-01-18 22:50:04 -06:00
`
const errBackendClearSaved = `
2023-09-18 07:16:17 -05:00
Error clearing the backend configuration : % w
2017-01-18 22:50:04 -06:00
2023-09-21 07:38:46 -05:00
OpenTofu removes the saved backend configuration when you ' re removing a
configured backend . This must be done so future OpenTofu runs know to not
2017-01-18 22:50:04 -06:00
use the backend configuration . Please look at the error above , resolve it ,
and try again .
`
const errBackendInit = `
2021-05-14 16:36:54 -05:00
Reason : % s
2017-01-18 22:50:04 -06:00
2023-09-21 07:38:46 -05:00
The "backend" is the interface that OpenTofu uses to store state ,
2017-01-18 22:50:04 -06:00
perform operations , etc . If this message is showing up , it means that the
2023-09-21 07:38:46 -05:00
OpenTofu configuration you ' re using is using a custom configuration for
the OpenTofu backend .
2017-01-18 22:50:04 -06:00
Changes to backend configurations require reinitialization . This allows
2023-09-21 07:38:46 -05:00
OpenTofu to set up the new configuration , copy existing state , etc . Please run
"tofu init" with either the "-reconfigure" or "-migrate-state" flags to
2021-05-14 16:36:54 -05:00
use the current configuration .
2017-01-18 22:50:04 -06:00
If the change reason above is incorrect , please verify your configuration
hasn ' t changed and try again . At this point , no changes to your existing
configuration or state have been made .
`
2021-10-21 06:19:33 -05:00
const errBackendInitCloud = `
2021-11-12 19:07:10 -06:00
Reason : % s .
2021-10-21 06:19:33 -05:00
2023-10-12 06:07:16 -05:00
Changes to the cloud backend configuration block require reinitialization , to discover any changes to the available workspaces .
2021-10-21 06:19:33 -05:00
2021-11-12 19:07:10 -06:00
To re - initialize , run :
2023-09-21 07:38:46 -05:00
tofu init
2021-11-12 19:07:10 -06:00
2023-09-21 07:38:46 -05:00
OpenTofu has not yet made changes to your existing configuration or state .
2021-10-21 06:19:33 -05:00
`
2017-01-18 22:50:04 -06:00
const errBackendWriteSaved = `
2023-09-18 07:16:17 -05:00
Error saving the backend configuration : % w
2017-01-18 22:50:04 -06:00
2023-09-21 07:38:46 -05:00
OpenTofu saves the complete backend configuration in a local file for
2017-01-18 22:50:04 -06:00
configuring the backend on future operations . This cannot be disabled . Errors
are usually due to simple file permission errors . Please look at the error
above , resolve it , and try again .
`
2017-12-18 10:11:09 -06:00
const outputBackendMigrateChange = `
2023-09-21 07:38:46 -05:00
OpenTofu detected that the backend type changed from % q to % q .
2017-01-18 22:50:04 -06:00
`
2017-12-18 10:11:09 -06:00
const outputBackendMigrateLocal = `
2023-09-21 07:38:46 -05:00
OpenTofu has detected you ' re unconfiguring your previously set % q backend .
2017-01-18 22:50:04 -06:00
`
const outputBackendReconfigure = `
[ reset ] [ bold ] Backend configuration changed ! [ reset ]
2023-09-21 07:38:46 -05:00
OpenTofu has detected that the configuration specified for the backend
has changed . OpenTofu will now check for existing state in the backends .
2017-01-18 22:50:04 -06:00
`
2021-11-01 12:20:15 -05:00
const inputCloudInitCreateWorkspace = `
There are no workspaces with the configured tags ( % s )
2023-10-12 06:07:16 -05:00
in your cloud backend organization . To finish initializing , OpenTofu needs at
2021-11-01 12:20:15 -05:00
least one workspace available .
2023-09-21 07:38:46 -05:00
OpenTofu can create a properly tagged workspace for you now . Please enter a
2023-10-12 06:07:16 -05:00
name to create a new cloud backend workspace .
2021-11-01 12:20:15 -05:00
`
2017-01-18 22:50:04 -06:00
const successBackendUnset = `
2023-09-21 07:38:46 -05:00
Successfully unset the backend % q . OpenTofu will now operate locally .
2017-01-18 22:50:04 -06:00
`
const successBackendSet = `
2023-09-21 07:38:46 -05:00
Successfully configured the backend % q ! OpenTofu will automatically
2017-01-18 22:50:04 -06:00
use this backend unless the backend configuration changes .
`
2021-05-14 16:36:54 -05:00
var migrateOrReconfigDiag = tfdiags . Sourceless (
tfdiags . Error ,
"Backend configuration changed" ,
"A change in the backend configuration has been detected, which may require migrating existing state.\n\n" +
2023-09-21 07:38:46 -05:00
"If you wish to attempt automatic migration of the state, use \"tofu init -migrate-state\".\n" +
` If you wish to store the current configuration with no changes to the state, use "tofu init -reconfigure". ` )