github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/command/meta_backend.go (about)

     1  package command
     2  
     3  // This file contains all the Backend-related function calls on Meta,
     4  // exported and private.
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  	"log"
    11  	"path/filepath"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"github.com/hashicorp/errwrap"
    16  	"github.com/hashicorp/hcl/v2"
    17  	"github.com/hashicorp/hcl/v2/hcldec"
    18  	"github.com/iaas-resource-provision/iaas-rpc/internal/backend"
    19  	remoteBackend "github.com/iaas-resource-provision/iaas-rpc/internal/backend/remote"
    20  	"github.com/iaas-resource-provision/iaas-rpc/internal/command/arguments"
    21  	"github.com/iaas-resource-provision/iaas-rpc/internal/command/clistate"
    22  	"github.com/iaas-resource-provision/iaas-rpc/internal/command/views"
    23  	"github.com/iaas-resource-provision/iaas-rpc/internal/configs"
    24  	"github.com/iaas-resource-provision/iaas-rpc/internal/plans"
    25  	"github.com/iaas-resource-provision/iaas-rpc/internal/states/statemgr"
    26  	"github.com/iaas-resource-provision/iaas-rpc/internal/terraform"
    27  	"github.com/iaas-resource-provision/iaas-rpc/internal/tfdiags"
    28  	"github.com/zclconf/go-cty/cty"
    29  	ctyjson "github.com/zclconf/go-cty/cty/json"
    30  
    31  	backendInit "github.com/iaas-resource-provision/iaas-rpc/internal/backend/init"
    32  	backendLocal "github.com/iaas-resource-provision/iaas-rpc/internal/backend/local"
    33  	legacy "github.com/iaas-resource-provision/iaas-rpc/internal/legacy/terraform"
    34  )
    35  
    36  // BackendOpts are the options used to initialize a backend.Backend.
    37  type BackendOpts struct {
    38  	// Config is a representation of the backend configuration block given in
    39  	// the root module, or nil if no such block is present.
    40  	Config *configs.Backend
    41  
    42  	// ConfigOverride is an hcl.Body that, if non-nil, will be used with
    43  	// configs.MergeBodies to override the type-specific backend configuration
    44  	// arguments in Config.
    45  	ConfigOverride hcl.Body
    46  
    47  	// Init should be set to true if initialization is allowed. If this is
    48  	// false, then any configuration that requires configuration will show
    49  	// an error asking the user to reinitialize.
    50  	Init bool
    51  
    52  	// ForceLocal will force a purely local backend, including state.
    53  	// You probably don't want to set this.
    54  	ForceLocal bool
    55  }
    56  
    57  // Backend initializes and returns the backend for this CLI session.
    58  //
    59  // The backend is used to perform the actual Terraform operations. This
    60  // abstraction enables easily sliding in new Terraform behavior such as
    61  // remote state storage, remote operations, etc. while allowing the CLI
    62  // to remain mostly identical.
    63  //
    64  // This will initialize a new backend for each call, which can carry some
    65  // overhead with it. Please reuse the returned value for optimal behavior.
    66  //
    67  // Only one backend should be used per Meta. This function is stateful
    68  // and is unsafe to create multiple backends used at once. This function
    69  // can be called multiple times with each backend being "live" (usable)
    70  // one at a time.
    71  //
    72  // A side-effect of this method is the population of m.backendState, recording
    73  // the final resolved backend configuration after dealing with overrides from
    74  // the "terraform init" command line, etc.
    75  func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, tfdiags.Diagnostics) {
    76  	var diags tfdiags.Diagnostics
    77  
    78  	// If no opts are set, then initialize
    79  	if opts == nil {
    80  		opts = &BackendOpts{}
    81  	}
    82  
    83  	// Initialize a backend from the config unless we're forcing a purely
    84  	// local operation.
    85  	var b backend.Backend
    86  	if !opts.ForceLocal {
    87  		var backendDiags tfdiags.Diagnostics
    88  		b, backendDiags = m.backendFromConfig(opts)
    89  		diags = diags.Append(backendDiags)
    90  
    91  		if opts.Init && b != nil && !diags.HasErrors() {
    92  			// Its possible that the currently selected workspace doesn't exist, so
    93  			// we call selectWorkspace to ensure an existing workspace is selected.
    94  			if err := m.selectWorkspace(b); err != nil {
    95  				diags = diags.Append(err)
    96  			}
    97  		}
    98  
    99  		if diags.HasErrors() {
   100  			return nil, diags
   101  		}
   102  
   103  		log.Printf("[TRACE] Meta.Backend: instantiated backend of type %T", b)
   104  	}
   105  
   106  	// Set up the CLI opts we pass into backends that support it.
   107  	cliOpts, err := m.backendCLIOpts()
   108  	if err != nil {
   109  		diags = diags.Append(err)
   110  		return nil, diags
   111  	}
   112  	cliOpts.Validation = true
   113  
   114  	// If the backend supports CLI initialization, do it.
   115  	if cli, ok := b.(backend.CLI); ok {
   116  		if err := cli.CLIInit(cliOpts); err != nil {
   117  			diags = diags.Append(fmt.Errorf(
   118  				"Error initializing backend %T: %s\n\n"+
   119  					"This is a bug; please report it to the backend developer",
   120  				b, err,
   121  			))
   122  			return nil, diags
   123  		}
   124  	}
   125  
   126  	// If the result of loading the backend is an enhanced backend,
   127  	// then return that as-is. This works even if b == nil (it will be !ok).
   128  	if enhanced, ok := b.(backend.Enhanced); ok {
   129  		log.Printf("[TRACE] Meta.Backend: backend %T supports operations", b)
   130  		return enhanced, nil
   131  	}
   132  
   133  	// We either have a non-enhanced backend or no backend configured at
   134  	// all. In either case, we use local as our enhanced backend and the
   135  	// non-enhanced (if any) as the state backend.
   136  
   137  	if !opts.ForceLocal {
   138  		log.Printf("[TRACE] Meta.Backend: backend %T does not support operations, so wrapping it in a local backend", b)
   139  	}
   140  
   141  	// Build the local backend
   142  	local := backendLocal.NewWithBackend(b)
   143  	if err := local.CLIInit(cliOpts); err != nil {
   144  		// Local backend isn't allowed to fail. It would be a bug.
   145  		panic(err)
   146  	}
   147  
   148  	// If we got here from backendFromConfig returning nil then m.backendState
   149  	// won't be set, since that codepath considers that to be no backend at all,
   150  	// but our caller considers that to be the local backend with no config
   151  	// and so we'll synthesize a backend state so other code doesn't need to
   152  	// care about this special case.
   153  	//
   154  	// FIXME: We should refactor this so that we more directly and explicitly
   155  	// treat the local backend as the default, including in the UI shown to
   156  	// the user, since the local backend should only be used when learning or
   157  	// in exceptional cases and so it's better to help the user learn that
   158  	// by introducing it as a concept.
   159  	if m.backendState == nil {
   160  		// NOTE: This synthetic object is intentionally _not_ retained in the
   161  		// on-disk record of the backend configuration, which was already dealt
   162  		// with inside backendFromConfig, because we still need that codepath
   163  		// to be able to recognize the lack of a config as distinct from
   164  		// explicitly setting local until we do some more refactoring here.
   165  		m.backendState = &legacy.BackendState{
   166  			Type:      "local",
   167  			ConfigRaw: json.RawMessage("{}"),
   168  		}
   169  	}
   170  
   171  	return local, nil
   172  }
   173  
   174  // selectWorkspace gets a list of existing workspaces and then checks
   175  // if the currently selected workspace is valid. If not, it will ask
   176  // the user to select a workspace from the list.
   177  func (m *Meta) selectWorkspace(b backend.Backend) error {
   178  	workspaces, err := b.Workspaces()
   179  	if err == backend.ErrWorkspacesNotSupported {
   180  		return nil
   181  	}
   182  	if err != nil {
   183  		return fmt.Errorf("Failed to get existing workspaces: %s", err)
   184  	}
   185  	if len(workspaces) == 0 {
   186  		return fmt.Errorf(strings.TrimSpace(errBackendNoExistingWorkspaces))
   187  	}
   188  
   189  	// Get the currently selected workspace.
   190  	workspace, err := m.Workspace()
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	// Check if any of the existing workspaces matches the selected
   196  	// workspace and create a numbered list of existing workspaces.
   197  	var list strings.Builder
   198  	for i, w := range workspaces {
   199  		if w == workspace {
   200  			return nil
   201  		}
   202  		fmt.Fprintf(&list, "%d. %s\n", i+1, w)
   203  	}
   204  
   205  	// If the selected workspace doesn't exist, ask the user to select
   206  	// a workspace from the list of existing workspaces.
   207  	v, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{
   208  		Id: "select-workspace",
   209  		Query: fmt.Sprintf(
   210  			"\n[reset][bold][yellow]The currently selected workspace (%s) does not exist.[reset]",
   211  			workspace),
   212  		Description: fmt.Sprintf(
   213  			strings.TrimSpace(inputBackendSelectWorkspace), list.String()),
   214  	})
   215  	if err != nil {
   216  		return fmt.Errorf("Failed to select workspace: %s", err)
   217  	}
   218  
   219  	idx, err := strconv.Atoi(v)
   220  	if err != nil || (idx < 1 || idx > len(workspaces)) {
   221  		return fmt.Errorf("Failed to select workspace: input not a valid number")
   222  	}
   223  
   224  	return m.SetWorkspace(workspaces[idx-1])
   225  }
   226  
   227  // BackendForPlan is similar to Backend, but uses backend settings that were
   228  // stored in a plan.
   229  //
   230  // The current workspace name is also stored as part of the plan, and so this
   231  // method will check that it matches the currently-selected workspace name
   232  // and produce error diagnostics if not.
   233  func (m *Meta) BackendForPlan(settings plans.Backend) (backend.Enhanced, tfdiags.Diagnostics) {
   234  	var diags tfdiags.Diagnostics
   235  
   236  	f := backendInit.Backend(settings.Type)
   237  	if f == nil {
   238  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), settings.Type))
   239  		return nil, diags
   240  	}
   241  	b := f()
   242  	log.Printf("[TRACE] Meta.BackendForPlan: instantiated backend of type %T", b)
   243  
   244  	schema := b.ConfigSchema()
   245  	configVal, err := settings.Config.Decode(schema.ImpliedType())
   246  	if err != nil {
   247  		diags = diags.Append(errwrap.Wrapf("saved backend configuration is invalid: {{err}}", err))
   248  		return nil, diags
   249  	}
   250  
   251  	newVal, validateDiags := b.PrepareConfig(configVal)
   252  	diags = diags.Append(validateDiags)
   253  	if validateDiags.HasErrors() {
   254  		return nil, diags
   255  	}
   256  
   257  	configureDiags := b.Configure(newVal)
   258  	diags = diags.Append(configureDiags)
   259  
   260  	// If the backend supports CLI initialization, do it.
   261  	if cli, ok := b.(backend.CLI); ok {
   262  		cliOpts, err := m.backendCLIOpts()
   263  		if err != nil {
   264  			diags = diags.Append(err)
   265  			return nil, diags
   266  		}
   267  		if err := cli.CLIInit(cliOpts); err != nil {
   268  			diags = diags.Append(fmt.Errorf(
   269  				"Error initializing backend %T: %s\n\n"+
   270  					"This is a bug; please report it to the backend developer",
   271  				b, err,
   272  			))
   273  			return nil, diags
   274  		}
   275  	}
   276  
   277  	// If the result of loading the backend is an enhanced backend,
   278  	// then return that as-is. This works even if b == nil (it will be !ok).
   279  	if enhanced, ok := b.(backend.Enhanced); ok {
   280  		log.Printf("[TRACE] Meta.BackendForPlan: backend %T supports operations", b)
   281  		return enhanced, nil
   282  	}
   283  
   284  	// Otherwise, we'll wrap our state-only remote backend in the local backend
   285  	// to cause any operations to be run locally.
   286  	log.Printf("[TRACE] Meta.Backend: backend %T does not support operations, so wrapping it in a local backend", b)
   287  	cliOpts, err := m.backendCLIOpts()
   288  	if err != nil {
   289  		diags = diags.Append(err)
   290  		return nil, diags
   291  	}
   292  	cliOpts.Validation = false // don't validate here in case config contains file(...) calls where the file doesn't exist
   293  	local := backendLocal.NewWithBackend(b)
   294  	if err := local.CLIInit(cliOpts); err != nil {
   295  		// Local backend should never fail, so this is always a bug.
   296  		panic(err)
   297  	}
   298  
   299  	return local, diags
   300  }
   301  
   302  // backendCLIOpts returns a backend.CLIOpts object that should be passed to
   303  // a backend that supports local CLI operations.
   304  func (m *Meta) backendCLIOpts() (*backend.CLIOpts, error) {
   305  	contextOpts, err := m.contextOpts()
   306  	if err != nil {
   307  		return nil, err
   308  	}
   309  	return &backend.CLIOpts{
   310  		CLI:                 m.Ui,
   311  		CLIColor:            m.Colorize(),
   312  		Streams:             m.Streams,
   313  		StatePath:           m.statePath,
   314  		StateOutPath:        m.stateOutPath,
   315  		StateBackupPath:     m.backupPath,
   316  		ContextOpts:         contextOpts,
   317  		Input:               m.Input(),
   318  		RunningInAutomation: m.RunningInAutomation,
   319  	}, nil
   320  }
   321  
   322  // Operation initializes a new backend.Operation struct.
   323  //
   324  // This prepares the operation. After calling this, the caller is expected
   325  // to modify fields of the operation such as Sequence to specify what will
   326  // be called.
   327  func (m *Meta) Operation(b backend.Backend) *backend.Operation {
   328  	schema := b.ConfigSchema()
   329  	workspace, err := m.Workspace()
   330  	if err != nil {
   331  		// An invalid workspace error would have been raised when creating the
   332  		// backend, and the caller should have already exited. Seeing the error
   333  		// here first is a bug, so panic.
   334  		panic(fmt.Sprintf("invalid workspace: %s", err))
   335  	}
   336  	planOutBackend, err := m.backendState.ForPlan(schema, workspace)
   337  	if err != nil {
   338  		// Always indicates an implementation error in practice, because
   339  		// errors here indicate invalid encoding of the backend configuration
   340  		// in memory, and we should always have validated that by the time
   341  		// we get here.
   342  		panic(fmt.Sprintf("failed to encode backend configuration for plan: %s", err))
   343  	}
   344  
   345  	stateLocker := clistate.NewNoopLocker()
   346  	if m.stateLock {
   347  		view := views.NewStateLocker(arguments.ViewHuman, m.View)
   348  		stateLocker = clistate.NewLocker(m.stateLockTimeout, view)
   349  	}
   350  
   351  	return &backend.Operation{
   352  		PlanOutBackend: planOutBackend,
   353  		Parallelism:    m.parallelism,
   354  		Targets:        m.targets,
   355  		UIIn:           m.UIInput(),
   356  		UIOut:          m.Ui,
   357  		Workspace:      workspace,
   358  		StateLocker:    stateLocker,
   359  	}
   360  }
   361  
   362  // backendConfig returns the local configuration for the backend
   363  func (m *Meta) backendConfig(opts *BackendOpts) (*configs.Backend, int, tfdiags.Diagnostics) {
   364  	var diags tfdiags.Diagnostics
   365  
   366  	if opts.Config == nil {
   367  		// check if the config was missing, or just not required
   368  		conf, moreDiags := m.loadBackendConfig(".")
   369  		diags = diags.Append(moreDiags)
   370  		if moreDiags.HasErrors() {
   371  			return nil, 0, diags
   372  		}
   373  
   374  		if conf == nil {
   375  			log.Println("[TRACE] Meta.Backend: no config given or present on disk, so returning nil config")
   376  			return nil, 0, nil
   377  		}
   378  
   379  		log.Printf("[TRACE] Meta.Backend: BackendOpts.Config not set, so using settings loaded from %s", conf.DeclRange)
   380  		opts.Config = conf
   381  	}
   382  
   383  	c := opts.Config
   384  
   385  	if c == nil {
   386  		log.Println("[TRACE] Meta.Backend: no explicit backend config, so returning nil config")
   387  		return nil, 0, nil
   388  	}
   389  
   390  	bf := backendInit.Backend(c.Type)
   391  	if bf == nil {
   392  		diags = diags.Append(&hcl.Diagnostic{
   393  			Severity: hcl.DiagError,
   394  			Summary:  "Invalid backend type",
   395  			Detail:   fmt.Sprintf("There is no backend type named %q.", c.Type),
   396  			Subject:  &c.TypeRange,
   397  		})
   398  		return nil, 0, diags
   399  	}
   400  	b := bf()
   401  
   402  	configSchema := b.ConfigSchema()
   403  	configBody := c.Config
   404  	configHash := c.Hash(configSchema)
   405  
   406  	// If we have an override configuration body then we must apply it now.
   407  	if opts.ConfigOverride != nil {
   408  		log.Println("[TRACE] Meta.Backend: merging -backend-config=... CLI overrides into backend configuration")
   409  		configBody = configs.MergeBodies(configBody, opts.ConfigOverride)
   410  	}
   411  
   412  	log.Printf("[TRACE] Meta.Backend: built configuration for %q backend with hash value %d", c.Type, configHash)
   413  
   414  	// We'll shallow-copy configs.Backend here so that we can replace the
   415  	// body without affecting others that hold this reference.
   416  	configCopy := *c
   417  	configCopy.Config = configBody
   418  	return &configCopy, configHash, diags
   419  }
   420  
   421  // backendFromConfig returns the initialized (not configured) backend
   422  // directly from the config/state..
   423  //
   424  // This function handles various edge cases around backend config loading. For
   425  // example: new config changes, backend type changes, etc.
   426  //
   427  // As of the 0.12 release it can no longer migrate from legacy remote state
   428  // to backends, and will instead instruct users to use 0.11 or earlier as
   429  // a stepping-stone to do that migration.
   430  //
   431  // This function may query the user for input unless input is disabled, in
   432  // which case this function will error.
   433  func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Diagnostics) {
   434  	// Get the local backend configuration.
   435  	c, cHash, diags := m.backendConfig(opts)
   436  	if diags.HasErrors() {
   437  		return nil, diags
   438  	}
   439  
   440  	// ------------------------------------------------------------------------
   441  	// For historical reasons, current backend configuration for a working
   442  	// directory is kept in a *state-like* file, using the legacy state
   443  	// structures in the Terraform package. It is not actually a Terraform
   444  	// state, and so only the "backend" portion of it is actually used.
   445  	//
   446  	// The remainder of this code often confusingly refers to this as a "state",
   447  	// so it's unfortunately important to remember that this is not actually
   448  	// what we _usually_ think of as "state", and is instead a local working
   449  	// directory "backend configuration state" that is never persisted anywhere.
   450  	//
   451  	// Since the "real" state has since moved on to be represented by
   452  	// states.State, we can recognize the special meaning of state that applies
   453  	// to this function and its callees by their continued use of the
   454  	// otherwise-obsolete terraform.State.
   455  	// ------------------------------------------------------------------------
   456  
   457  	// Get the path to where we store a local cache of backend configuration
   458  	// if we're using a remote backend. This may not yet exist which means
   459  	// we haven't used a non-local backend before. That is okay.
   460  	statePath := filepath.Join(m.DataDir(), DefaultStateFilename)
   461  	sMgr := &clistate.LocalState{Path: statePath}
   462  	if err := sMgr.RefreshState(); err != nil {
   463  		diags = diags.Append(fmt.Errorf("Failed to load state: %s", err))
   464  		return nil, diags
   465  	}
   466  
   467  	// Load the state, it must be non-nil for the tests below but can be empty
   468  	s := sMgr.State()
   469  	if s == nil {
   470  		log.Printf("[TRACE] Meta.Backend: backend has not previously been initialized in this working directory")
   471  		s = legacy.NewState()
   472  	} else if s.Backend != nil {
   473  		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized for %q backend", s.Backend.Type)
   474  	} else {
   475  		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized but has no backend (is using legacy remote state?)")
   476  	}
   477  
   478  	// if we want to force reconfiguration of the backend, we set the backend
   479  	// state to nil on this copy. This will direct us through the correct
   480  	// configuration path in the switch statement below.
   481  	if m.reconfigure {
   482  		s.Backend = nil
   483  	}
   484  
   485  	// Upon return, we want to set the state we're using in-memory so that
   486  	// we can access it for commands.
   487  	m.backendState = nil
   488  	defer func() {
   489  		if s := sMgr.State(); s != nil && !s.Backend.Empty() {
   490  			m.backendState = s.Backend
   491  		}
   492  	}()
   493  
   494  	if !s.Remote.Empty() {
   495  		// Legacy remote state is no longer supported. User must first
   496  		// migrate with Terraform 0.11 or earlier.
   497  		diags = diags.Append(tfdiags.Sourceless(
   498  			tfdiags.Error,
   499  			"Legacy remote state not supported",
   500  			"This working directory is configured for legacy remote state, which is no longer supported from Terraform v0.12 onwards. To migrate this environment, first run \"terraform init\" under a Terraform 0.11 release, and then upgrade Terraform again.",
   501  		))
   502  		return nil, diags
   503  	}
   504  
   505  	// This switch statement covers all the different combinations of
   506  	// configuring new backends, updating previously-configured backends, etc.
   507  	switch {
   508  	// No configuration set at all. Pure local state.
   509  	case c == nil && s.Backend.Empty():
   510  		log.Printf("[TRACE] Meta.Backend: using default local state only (no backend configuration, and no existing initialized backend)")
   511  		return nil, nil
   512  
   513  	// We're unsetting a backend (moving from backend => local)
   514  	case c == nil && !s.Backend.Empty():
   515  		log.Printf("[TRACE] Meta.Backend: previously-initialized %q backend is no longer present in config", s.Backend.Type)
   516  
   517  		initReason := fmt.Sprintf("Unsetting the previously set backend %q", s.Backend.Type)
   518  		if !opts.Init {
   519  			diags = diags.Append(tfdiags.Sourceless(
   520  				tfdiags.Error,
   521  				"Backend initialization required, please run \"terraform init\"",
   522  				fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
   523  			))
   524  			return nil, diags
   525  		}
   526  
   527  		if !m.migrateState {
   528  			diags = diags.Append(migrateOrReconfigDiag)
   529  			return nil, diags
   530  		}
   531  
   532  		return m.backend_c_r_S(c, cHash, sMgr, true)
   533  
   534  	// Configuring a backend for the first time.
   535  	case c != nil && s.Backend.Empty():
   536  		log.Printf("[TRACE] Meta.Backend: moving from default local state only to %q backend", c.Type)
   537  		if !opts.Init {
   538  			initReason := fmt.Sprintf("Initial configuration of the requested backend %q", c.Type)
   539  			diags = diags.Append(tfdiags.Sourceless(
   540  				tfdiags.Error,
   541  				"Backend initialization required, please run \"terraform init\"",
   542  				fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
   543  			))
   544  			return nil, diags
   545  		}
   546  
   547  		return m.backend_C_r_s(c, cHash, sMgr)
   548  
   549  	// Potentially changing a backend configuration
   550  	case c != nil && !s.Backend.Empty():
   551  		// We are not going to migrate if were not initializing and the hashes
   552  		// match indicating that the stored config is valid. If we are
   553  		// initializing, then we also assume the the backend config is OK if
   554  		// the hashes match, as long as we're not providing any new overrides.
   555  		if (uint64(cHash) == s.Backend.Hash) && (!opts.Init || opts.ConfigOverride == nil) {
   556  			log.Printf("[TRACE] Meta.Backend: using already-initialized, unchanged %q backend configuration", c.Type)
   557  			return m.backend_C_r_S_unchanged(c, cHash, sMgr)
   558  		}
   559  
   560  		// If our configuration is the same, then we're just initializing
   561  		// a previously configured remote backend.
   562  		if !m.backendConfigNeedsMigration(c, s.Backend) {
   563  			log.Printf("[TRACE] Meta.Backend: using already-initialized %q backend configuration", c.Type)
   564  			return m.backend_C_r_S_unchanged(c, cHash, sMgr)
   565  		}
   566  		log.Printf("[TRACE] Meta.Backend: backend configuration has changed (from type %q to type %q)", s.Backend.Type, c.Type)
   567  
   568  		initReason := fmt.Sprintf("Backend configuration changed for %q", c.Type)
   569  		if s.Backend.Type != c.Type {
   570  			initReason = fmt.Sprintf("Backend configuration changed from %q to %q", s.Backend.Type, c.Type)
   571  		}
   572  
   573  		if !opts.Init {
   574  			diags = diags.Append(tfdiags.Sourceless(
   575  				tfdiags.Error,
   576  				"Backend initialization required, please run \"terraform init\"",
   577  				fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
   578  			))
   579  			return nil, diags
   580  		}
   581  
   582  		if !m.migrateState {
   583  			diags = diags.Append(migrateOrReconfigDiag)
   584  			return nil, diags
   585  		}
   586  
   587  		log.Printf("[WARN] backend config has changed since last init")
   588  		return m.backend_C_r_S_changed(c, cHash, sMgr, true)
   589  
   590  	default:
   591  		diags = diags.Append(fmt.Errorf(
   592  			"Unhandled backend configuration state. This is a bug. Please\n"+
   593  				"report this error with the following information.\n\n"+
   594  				"Config Nil: %v\n"+
   595  				"Saved Backend Empty: %v\n",
   596  			c == nil, s.Backend.Empty(),
   597  		))
   598  		return nil, diags
   599  	}
   600  }
   601  
   602  // backendFromState returns the initialized (not configured) backend directly
   603  // from the state. This should be used only when a user runs `terraform init
   604  // -backend=false`. This function returns a local backend if there is no state
   605  // or no backend configured.
   606  func (m *Meta) backendFromState() (backend.Backend, tfdiags.Diagnostics) {
   607  	var diags tfdiags.Diagnostics
   608  	// Get the path to where we store a local cache of backend configuration
   609  	// if we're using a remote backend. This may not yet exist which means
   610  	// we haven't used a non-local backend before. That is okay.
   611  	statePath := filepath.Join(m.DataDir(), DefaultStateFilename)
   612  	sMgr := &clistate.LocalState{Path: statePath}
   613  	if err := sMgr.RefreshState(); err != nil {
   614  		diags = diags.Append(fmt.Errorf("Failed to load state: %s", err))
   615  		return nil, diags
   616  	}
   617  	s := sMgr.State()
   618  	if s == nil {
   619  		// no state, so return a local backend
   620  		log.Printf("[TRACE] Meta.Backend: backend has not previously been initialized in this working directory")
   621  		return backendLocal.New(), diags
   622  	}
   623  	if s.Backend == nil {
   624  		// s.Backend is nil, so return a local backend
   625  		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized but has no backend (is using legacy remote state?)")
   626  		return backendLocal.New(), diags
   627  	}
   628  	log.Printf("[TRACE] Meta.Backend: working directory was previously initialized for %q backend", s.Backend.Type)
   629  
   630  	//backend init function
   631  	if s.Backend.Type == "" {
   632  		return backendLocal.New(), diags
   633  	}
   634  	f := backendInit.Backend(s.Backend.Type)
   635  	if f == nil {
   636  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type))
   637  		return nil, diags
   638  	}
   639  	b := f()
   640  
   641  	// The configuration saved in the working directory state file is used
   642  	// in this case, since it will contain any additional values that
   643  	// were provided via -backend-config arguments on terraform init.
   644  	schema := b.ConfigSchema()
   645  	configVal, err := s.Backend.Config(schema)
   646  	if err != nil {
   647  		diags = diags.Append(tfdiags.Sourceless(
   648  			tfdiags.Error,
   649  			"Failed to decode current backend config",
   650  			fmt.Sprintf("The backend configuration created by the most recent run of \"terraform init\" could not be decoded: %s. The configuration may have been initialized by an earlier version that used an incompatible configuration structure. Run \"terraform init -reconfigure\" to force re-initialization of the backend.", err),
   651  		))
   652  		return nil, diags
   653  	}
   654  
   655  	// Validate the config and then configure the backend
   656  	newVal, validDiags := b.PrepareConfig(configVal)
   657  	diags = diags.Append(validDiags)
   658  	if validDiags.HasErrors() {
   659  		return nil, diags
   660  	}
   661  
   662  	configDiags := b.Configure(newVal)
   663  	diags = diags.Append(configDiags)
   664  	if configDiags.HasErrors() {
   665  		return nil, diags
   666  	}
   667  
   668  	return b, diags
   669  }
   670  
   671  //-------------------------------------------------------------------
   672  // Backend Config Scenarios
   673  //
   674  // The functions below cover handling all the various scenarios that
   675  // can exist when loading a backend. They are named in the format of
   676  // "backend_C_R_S" where C, R, S may be upper or lowercase. Lowercase
   677  // means it is false, uppercase means it is true. The full set of eight
   678  // possible cases is handled.
   679  //
   680  // The fields are:
   681  //
   682  //   * C - Backend configuration is set and changed in TF files
   683  //   * R - Legacy remote state is set
   684  //   * S - Backend configuration is set in the state
   685  //
   686  //-------------------------------------------------------------------
   687  
   688  // Unconfiguring a backend (moving from backend => local).
   689  func (m *Meta) backend_c_r_S(c *configs.Backend, cHash int, sMgr *clistate.LocalState, output bool) (backend.Backend, tfdiags.Diagnostics) {
   690  	s := sMgr.State()
   691  
   692  	// Get the backend type for output
   693  	backendType := s.Backend.Type
   694  
   695  	m.Ui.Output(fmt.Sprintf(strings.TrimSpace(outputBackendMigrateLocal), s.Backend.Type))
   696  
   697  	// Grab a purely local backend to get the local state if it exists
   698  	localB, diags := m.Backend(&BackendOpts{ForceLocal: true})
   699  	if diags.HasErrors() {
   700  		return nil, diags
   701  	}
   702  
   703  	// Initialize the configured backend
   704  	b, moreDiags := m.backend_C_r_S_unchanged(c, cHash, sMgr)
   705  	diags = diags.Append(moreDiags)
   706  	if moreDiags.HasErrors() {
   707  		return nil, diags
   708  	}
   709  
   710  	// Perform the migration
   711  	err := m.backendMigrateState(&backendMigrateOpts{
   712  		OneType: s.Backend.Type,
   713  		TwoType: "local",
   714  		One:     b,
   715  		Two:     localB,
   716  	})
   717  	if err != nil {
   718  		diags = diags.Append(err)
   719  		return nil, diags
   720  	}
   721  
   722  	// Remove the stored metadata
   723  	s.Backend = nil
   724  	if err := sMgr.WriteState(s); err != nil {
   725  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendClearSaved), err))
   726  		return nil, diags
   727  	}
   728  	if err := sMgr.PersistState(); err != nil {
   729  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendClearSaved), err))
   730  		return nil, diags
   731  	}
   732  
   733  	if output {
   734  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   735  			"[reset][green]\n\n"+
   736  				strings.TrimSpace(successBackendUnset), backendType)))
   737  	}
   738  
   739  	// Return no backend
   740  	return nil, diags
   741  }
   742  
   743  // Configuring a backend for the first time.
   744  func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
   745  	// Get the backend
   746  	b, configVal, diags := m.backendInitFromConfig(c)
   747  	if diags.HasErrors() {
   748  		return nil, diags
   749  	}
   750  
   751  	// Grab a purely local backend to get the local state if it exists
   752  	localB, localBDiags := m.Backend(&BackendOpts{ForceLocal: true})
   753  	if localBDiags.HasErrors() {
   754  		diags = diags.Append(localBDiags)
   755  		return nil, diags
   756  	}
   757  
   758  	workspaces, err := localB.Workspaces()
   759  	if err != nil {
   760  		diags = diags.Append(fmt.Errorf(errBackendLocalRead, err))
   761  		return nil, diags
   762  	}
   763  
   764  	var localStates []statemgr.Full
   765  	for _, workspace := range workspaces {
   766  		localState, err := localB.StateMgr(workspace)
   767  		if err != nil {
   768  			diags = diags.Append(fmt.Errorf(errBackendLocalRead, err))
   769  			return nil, diags
   770  		}
   771  		if err := localState.RefreshState(); err != nil {
   772  			diags = diags.Append(fmt.Errorf(errBackendLocalRead, err))
   773  			return nil, diags
   774  		}
   775  
   776  		// We only care about non-empty states.
   777  		if localS := localState.State(); !localS.Empty() {
   778  			log.Printf("[TRACE] Meta.Backend: will need to migrate workspace states because of existing %q workspace", workspace)
   779  			localStates = append(localStates, localState)
   780  		} else {
   781  			log.Printf("[TRACE] Meta.Backend: ignoring local %q workspace because its state is empty", workspace)
   782  		}
   783  	}
   784  
   785  	if len(localStates) > 0 {
   786  		// Perform the migration
   787  		err = m.backendMigrateState(&backendMigrateOpts{
   788  			OneType: "local",
   789  			TwoType: c.Type,
   790  			One:     localB,
   791  			Two:     b,
   792  		})
   793  		if err != nil {
   794  			diags = diags.Append(err)
   795  			return nil, diags
   796  		}
   797  
   798  		// we usually remove the local state after migration to prevent
   799  		// confusion, but adding a default local backend block to the config
   800  		// can get us here too. Don't delete our state if the old and new paths
   801  		// are the same.
   802  		erase := true
   803  		if newLocalB, ok := b.(*backendLocal.Local); ok {
   804  			if localB, ok := localB.(*backendLocal.Local); ok {
   805  				if newLocalB.PathsConflictWith(localB) {
   806  					erase = false
   807  					log.Printf("[TRACE] Meta.Backend: both old and new backends share the same local state paths, so not erasing old state")
   808  				}
   809  			}
   810  		}
   811  
   812  		if erase {
   813  			log.Printf("[TRACE] Meta.Backend: removing old state snapshots from old backend")
   814  			for _, localState := range localStates {
   815  				// We always delete the local state, unless that was our new state too.
   816  				if err := localState.WriteState(nil); err != nil {
   817  					diags = diags.Append(fmt.Errorf(errBackendMigrateLocalDelete, err))
   818  					return nil, diags
   819  				}
   820  				if err := localState.PersistState(); err != nil {
   821  					diags = diags.Append(fmt.Errorf(errBackendMigrateLocalDelete, err))
   822  					return nil, diags
   823  				}
   824  			}
   825  		}
   826  	}
   827  
   828  	if m.stateLock {
   829  		view := views.NewStateLocker(arguments.ViewHuman, m.View)
   830  		stateLocker := clistate.NewLocker(m.stateLockTimeout, view)
   831  		if err := stateLocker.Lock(sMgr, "backend from plan"); err != nil {
   832  			diags = diags.Append(fmt.Errorf("Error locking state: %s", err))
   833  			return nil, diags
   834  		}
   835  		defer stateLocker.Unlock()
   836  	}
   837  
   838  	configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema().ImpliedType())
   839  	if err != nil {
   840  		diags = diags.Append(fmt.Errorf("Can't serialize backend configuration as JSON: %s", err))
   841  		return nil, diags
   842  	}
   843  
   844  	// Store the metadata in our saved state location
   845  	s := sMgr.State()
   846  	if s == nil {
   847  		s = legacy.NewState()
   848  	}
   849  	s.Backend = &legacy.BackendState{
   850  		Type:      c.Type,
   851  		ConfigRaw: json.RawMessage(configJSON),
   852  		Hash:      uint64(cHash),
   853  	}
   854  
   855  	if err := sMgr.WriteState(s); err != nil {
   856  		diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
   857  		return nil, diags
   858  	}
   859  	if err := sMgr.PersistState(); err != nil {
   860  		diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
   861  		return nil, diags
   862  	}
   863  
   864  	// By now the backend is successfully configured.
   865  	m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   866  		"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
   867  
   868  	return b, diags
   869  }
   870  
   871  // Changing a previously saved backend.
   872  func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clistate.LocalState, output bool) (backend.Backend, tfdiags.Diagnostics) {
   873  	if output {
   874  		// Notify the user
   875  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   876  			"[reset]%s\n\n",
   877  			strings.TrimSpace(outputBackendReconfigure))))
   878  	}
   879  
   880  	// Get the old state
   881  	s := sMgr.State()
   882  
   883  	// Get the backend
   884  	b, configVal, diags := m.backendInitFromConfig(c)
   885  	if diags.HasErrors() {
   886  		return nil, diags
   887  	}
   888  
   889  	// no need to confuse the user if the backend types are the same
   890  	if s.Backend.Type != c.Type {
   891  		m.Ui.Output(strings.TrimSpace(fmt.Sprintf(outputBackendMigrateChange, s.Backend.Type, c.Type)))
   892  	}
   893  
   894  	// Grab the existing backend
   895  	oldB, oldBDiags := m.backend_C_r_S_unchanged(c, cHash, sMgr)
   896  	diags = diags.Append(oldBDiags)
   897  	if oldBDiags.HasErrors() {
   898  		return nil, diags
   899  	}
   900  
   901  	// Perform the migration
   902  	err := m.backendMigrateState(&backendMigrateOpts{
   903  		OneType: s.Backend.Type,
   904  		TwoType: c.Type,
   905  		One:     oldB,
   906  		Two:     b,
   907  	})
   908  	if err != nil {
   909  		diags = diags.Append(err)
   910  		return nil, diags
   911  	}
   912  
   913  	if m.stateLock {
   914  		view := views.NewStateLocker(arguments.ViewHuman, m.View)
   915  		stateLocker := clistate.NewLocker(m.stateLockTimeout, view)
   916  		if err := stateLocker.Lock(sMgr, "backend from plan"); err != nil {
   917  			diags = diags.Append(fmt.Errorf("Error locking state: %s", err))
   918  			return nil, diags
   919  		}
   920  		defer stateLocker.Unlock()
   921  	}
   922  
   923  	configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema().ImpliedType())
   924  	if err != nil {
   925  		diags = diags.Append(fmt.Errorf("Can't serialize backend configuration as JSON: %s", err))
   926  		return nil, diags
   927  	}
   928  
   929  	// Update the backend state
   930  	s = sMgr.State()
   931  	if s == nil {
   932  		s = legacy.NewState()
   933  	}
   934  	s.Backend = &legacy.BackendState{
   935  		Type:      c.Type,
   936  		ConfigRaw: json.RawMessage(configJSON),
   937  		Hash:      uint64(cHash),
   938  	}
   939  
   940  	if err := sMgr.WriteState(s); err != nil {
   941  		diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
   942  		return nil, diags
   943  	}
   944  	if err := sMgr.PersistState(); err != nil {
   945  		diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
   946  		return nil, diags
   947  	}
   948  
   949  	if output {
   950  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   951  			"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
   952  	}
   953  
   954  	return b, diags
   955  }
   956  
   957  // Initiailizing an unchanged saved backend
   958  func (m *Meta) backend_C_r_S_unchanged(c *configs.Backend, cHash int, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
   959  	var diags tfdiags.Diagnostics
   960  
   961  	s := sMgr.State()
   962  
   963  	// it's possible for a backend to be unchanged, and the config itself to
   964  	// have changed by moving a parameter from the config to `-backend-config`
   965  	// In this case we only need to update the Hash.
   966  	if c != nil && s.Backend.Hash != uint64(cHash) {
   967  		s.Backend.Hash = uint64(cHash)
   968  		if err := sMgr.WriteState(s); err != nil {
   969  			diags = diags.Append(err)
   970  			return nil, diags
   971  		}
   972  	}
   973  
   974  	// Get the backend
   975  	f := backendInit.Backend(s.Backend.Type)
   976  	if f == nil {
   977  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type))
   978  		return nil, diags
   979  	}
   980  	b := f()
   981  
   982  	// The configuration saved in the working directory state file is used
   983  	// in this case, since it will contain any additional values that
   984  	// were provided via -backend-config arguments on terraform init.
   985  	schema := b.ConfigSchema()
   986  	configVal, err := s.Backend.Config(schema)
   987  	if err != nil {
   988  		diags = diags.Append(tfdiags.Sourceless(
   989  			tfdiags.Error,
   990  			"Failed to decode current backend config",
   991  			fmt.Sprintf("The backend configuration created by the most recent run of \"terraform init\" could not be decoded: %s. The configuration may have been initialized by an earlier version that used an incompatible configuration structure. Run \"terraform init -reconfigure\" to force re-initialization of the backend.", err),
   992  		))
   993  		return nil, diags
   994  	}
   995  
   996  	// Validate the config and then configure the backend
   997  	newVal, validDiags := b.PrepareConfig(configVal)
   998  	diags = diags.Append(validDiags)
   999  	if validDiags.HasErrors() {
  1000  		return nil, diags
  1001  	}
  1002  
  1003  	configDiags := b.Configure(newVal)
  1004  	diags = diags.Append(configDiags)
  1005  	if configDiags.HasErrors() {
  1006  		return nil, diags
  1007  	}
  1008  
  1009  	return b, diags
  1010  }
  1011  
  1012  //-------------------------------------------------------------------
  1013  // Reusable helper functions for backend management
  1014  //-------------------------------------------------------------------
  1015  
  1016  // backendConfigNeedsMigration returns true if migration might be required to
  1017  // move from the configured backend to the given cached backend config.
  1018  //
  1019  // This must be called with the synthetic *configs.Backend that results from
  1020  // merging in any command-line options for correct behavior.
  1021  //
  1022  // If either the given configuration or cached configuration are invalid then
  1023  // this function will conservatively assume that migration is required,
  1024  // expecting that the migration code will subsequently deal with the same
  1025  // errors.
  1026  func (m *Meta) backendConfigNeedsMigration(c *configs.Backend, s *legacy.BackendState) bool {
  1027  	if s == nil || s.Empty() {
  1028  		log.Print("[TRACE] backendConfigNeedsMigration: no cached config, so migration is required")
  1029  		return true
  1030  	}
  1031  	if c.Type != s.Type {
  1032  		log.Printf("[TRACE] backendConfigNeedsMigration: type changed from %q to %q, so migration is required", s.Type, c.Type)
  1033  		return true
  1034  	}
  1035  
  1036  	// We need the backend's schema to do our comparison here.
  1037  	f := backendInit.Backend(c.Type)
  1038  	if f == nil {
  1039  		log.Printf("[TRACE] backendConfigNeedsMigration: no backend of type %q, which migration codepath must handle", c.Type)
  1040  		return true // let the migration codepath deal with the missing backend
  1041  	}
  1042  	b := f()
  1043  
  1044  	schema := b.ConfigSchema()
  1045  	decSpec := schema.NoneRequired().DecoderSpec()
  1046  	givenVal, diags := hcldec.Decode(c.Config, decSpec, nil)
  1047  	if diags.HasErrors() {
  1048  		log.Printf("[TRACE] backendConfigNeedsMigration: failed to decode given config; migration codepath must handle problem: %s", diags.Error())
  1049  		return true // let the migration codepath deal with these errors
  1050  	}
  1051  
  1052  	cachedVal, err := s.Config(schema)
  1053  	if err != nil {
  1054  		log.Printf("[TRACE] backendConfigNeedsMigration: failed to decode cached config; migration codepath must handle problem: %s", err)
  1055  		return true // let the migration codepath deal with the error
  1056  	}
  1057  
  1058  	// If we get all the way down here then it's the exact equality of the
  1059  	// two decoded values that decides our outcome. It's safe to use RawEquals
  1060  	// here (rather than Equals) because we know that unknown values can
  1061  	// never appear in backend configurations.
  1062  	if cachedVal.RawEquals(givenVal) {
  1063  		log.Print("[TRACE] backendConfigNeedsMigration: given configuration matches cached configuration, so no migration is required")
  1064  		return false
  1065  	}
  1066  	log.Print("[TRACE] backendConfigNeedsMigration: configuration values have changed, so migration is required")
  1067  	return true
  1068  }
  1069  
  1070  func (m *Meta) backendInitFromConfig(c *configs.Backend) (backend.Backend, cty.Value, tfdiags.Diagnostics) {
  1071  	var diags tfdiags.Diagnostics
  1072  
  1073  	// Get the backend
  1074  	f := backendInit.Backend(c.Type)
  1075  	if f == nil {
  1076  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendNewUnknown), c.Type))
  1077  		return nil, cty.NilVal, diags
  1078  	}
  1079  	b := f()
  1080  
  1081  	schema := b.ConfigSchema()
  1082  	decSpec := schema.NoneRequired().DecoderSpec()
  1083  	configVal, hclDiags := hcldec.Decode(c.Config, decSpec, nil)
  1084  	diags = diags.Append(hclDiags)
  1085  	if hclDiags.HasErrors() {
  1086  		return nil, cty.NilVal, diags
  1087  	}
  1088  
  1089  	// TODO: test
  1090  	if m.Input() {
  1091  		var err error
  1092  		configVal, err = m.inputForSchema(configVal, schema)
  1093  		if err != nil {
  1094  			diags = diags.Append(fmt.Errorf("Error asking for input to configure backend %q: %s", c.Type, err))
  1095  		}
  1096  
  1097  		// We get an unknown here if the if the user aborted input, but we can't
  1098  		// turn that into a config value, so set it to null and let the provider
  1099  		// handle it in PrepareConfig.
  1100  		if !configVal.IsKnown() {
  1101  			configVal = cty.NullVal(configVal.Type())
  1102  		}
  1103  	}
  1104  
  1105  	newVal, validateDiags := b.PrepareConfig(configVal)
  1106  	diags = diags.Append(validateDiags.InConfigBody(c.Config, ""))
  1107  	if validateDiags.HasErrors() {
  1108  		return nil, cty.NilVal, diags
  1109  	}
  1110  
  1111  	configureDiags := b.Configure(newVal)
  1112  	diags = diags.Append(configureDiags.InConfigBody(c.Config, ""))
  1113  
  1114  	return b, configVal, diags
  1115  }
  1116  
  1117  // Helper method to ignore remote backend version conflicts. Only call this
  1118  // for commands which cannot accidentally upgrade remote state files.
  1119  func (m *Meta) ignoreRemoteBackendVersionConflict(b backend.Backend) {
  1120  	if rb, ok := b.(*remoteBackend.Remote); ok {
  1121  		rb.IgnoreVersionConflict()
  1122  	}
  1123  }
  1124  
  1125  // Helper method to check the local Terraform version against the configured
  1126  // version in the remote workspace, returning diagnostics if they conflict.
  1127  func (m *Meta) remoteBackendVersionCheck(b backend.Backend, workspace string) tfdiags.Diagnostics {
  1128  	var diags tfdiags.Diagnostics
  1129  
  1130  	if rb, ok := b.(*remoteBackend.Remote); ok {
  1131  		// Allow user override based on command-line flag
  1132  		if m.ignoreRemoteVersion {
  1133  			rb.IgnoreVersionConflict()
  1134  		}
  1135  		// If the override is set, this check will return a warning instead of
  1136  		// an error
  1137  		versionDiags := rb.VerifyWorkspaceTerraformVersion(workspace)
  1138  		diags = diags.Append(versionDiags)
  1139  		// If there are no errors resulting from this check, we do not need to
  1140  		// check again
  1141  		if !diags.HasErrors() {
  1142  			rb.IgnoreVersionConflict()
  1143  		}
  1144  	}
  1145  
  1146  	return diags
  1147  }
  1148  
  1149  //-------------------------------------------------------------------
  1150  // Output constants and initialization code
  1151  //-------------------------------------------------------------------
  1152  
  1153  const errBackendLocalRead = `
  1154  Error reading local state: %s
  1155  
  1156  Terraform is trying to read your local state to determine if there is
  1157  state to migrate to your newly configured backend. Terraform can't continue
  1158  without this check because that would risk losing state. Please resolve the
  1159  error above and try again.
  1160  `
  1161  
  1162  const errBackendMigrateLocalDelete = `
  1163  Error deleting local state after migration: %s
  1164  
  1165  Your local state is deleted after successfully migrating it to the newly
  1166  configured backend. As part of the deletion process, a backup is made at
  1167  the standard backup path unless explicitly asked not to. To cleanly operate
  1168  with a backend, we must delete the local state file. Please resolve the
  1169  issue above and retry the command.
  1170  `
  1171  
  1172  const errBackendNewUnknown = `
  1173  The backend %q could not be found.
  1174  
  1175  This is the backend specified in your Terraform configuration file.
  1176  This error could be a simple typo in your configuration, but it can also
  1177  be caused by using a Terraform version that doesn't support the specified
  1178  backend type. Please check your configuration and your Terraform version.
  1179  
  1180  If you'd like to run Terraform and store state locally, you can fix this
  1181  error by removing the backend configuration from your configuration.
  1182  `
  1183  
  1184  const errBackendNoExistingWorkspaces = `
  1185  No existing workspaces.
  1186  
  1187  Use the "terraform workspace" command to create and select a new workspace.
  1188  If the backend already contains existing workspaces, you may need to update
  1189  the backend configuration.
  1190  `
  1191  
  1192  const errBackendSavedUnknown = `
  1193  The backend %q could not be found.
  1194  
  1195  This is the backend that this Terraform environment is configured to use
  1196  both in your configuration and saved locally as your last-used backend.
  1197  If it isn't found, it could mean an alternate version of Terraform was
  1198  used with this configuration. Please use the proper version of Terraform that
  1199  contains support for this backend.
  1200  
  1201  If you'd like to force remove this backend, you must update your configuration
  1202  to not use the backend and run "terraform init" (or any other command) again.
  1203  `
  1204  
  1205  const errBackendClearSaved = `
  1206  Error clearing the backend configuration: %s
  1207  
  1208  Terraform removes the saved backend configuration when you're removing a
  1209  configured backend. This must be done so future Terraform runs know to not
  1210  use the backend configuration. Please look at the error above, resolve it,
  1211  and try again.
  1212  `
  1213  
  1214  const errBackendInit = `
  1215  Reason: %s
  1216  
  1217  The "backend" is the interface that Terraform uses to store state,
  1218  perform operations, etc. If this message is showing up, it means that the
  1219  Terraform configuration you're using is using a custom configuration for
  1220  the Terraform backend.
  1221  
  1222  Changes to backend configurations require reinitialization. This allows
  1223  Terraform to set up the new configuration, copy existing state, etc. Please run
  1224  "terraform init" with either the "-reconfigure" or "-migrate-state" flags to
  1225  use the current configuration.
  1226  
  1227  If the change reason above is incorrect, please verify your configuration
  1228  hasn't changed and try again. At this point, no changes to your existing
  1229  configuration or state have been made.
  1230  `
  1231  
  1232  const errBackendWriteSaved = `
  1233  Error saving the backend configuration: %s
  1234  
  1235  Terraform saves the complete backend configuration in a local file for
  1236  configuring the backend on future operations. This cannot be disabled. Errors
  1237  are usually due to simple file permission errors. Please look at the error
  1238  above, resolve it, and try again.
  1239  `
  1240  
  1241  const outputBackendMigrateChange = `
  1242  Terraform detected that the backend type changed from %q to %q.
  1243  `
  1244  
  1245  const outputBackendMigrateLocal = `
  1246  Terraform has detected you're unconfiguring your previously set %q backend.
  1247  `
  1248  
  1249  const outputBackendReconfigure = `
  1250  [reset][bold]Backend configuration changed![reset]
  1251  
  1252  Terraform has detected that the configuration specified for the backend
  1253  has changed. Terraform will now check for existing state in the backends.
  1254  `
  1255  
  1256  const successBackendUnset = `
  1257  Successfully unset the backend %q. Terraform will now operate locally.
  1258  `
  1259  
  1260  const successBackendSet = `
  1261  Successfully configured the backend %q! Terraform will automatically
  1262  use this backend unless the backend configuration changes.
  1263  `
  1264  
  1265  var migrateOrReconfigDiag = tfdiags.Sourceless(
  1266  	tfdiags.Error,
  1267  	"Backend configuration changed",
  1268  	"A change in the backend configuration has been detected, which may require migrating existing state.\n\n"+
  1269  		"If you wish to attempt automatic migration of the state, use \"terraform init -migrate-state\".\n"+
  1270  		`If you wish to store the current configuration with no changes to the state, use "terraform init -reconfigure".`)