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