// backendFromConfig returns the initialized (not configured) backend
// directly from the config/state..
//
// This function handles various edge cases around backend config loading. For
// example: new config changes, backend type changes, etc.
//
// As of the 0.12 release it can no longer migrate from legacy remote state
// to backends, and will instead instruct users to use 0.11 or earlier as
// a stepping-stone to do that migration.
//
// This function may query the user for input unless input is disabled, in
// which case this function will error.
backendFromConfig returns the initialized (not configured) backend
directly from the config/state

This function handles various edge cases around backend config loading. For
example: new config changes, backend type changes, etc.

As of the 0.12 release it can no longer migrate from legacy remote state
to backends, and will instead instruct users to use 0.11 or earlier as
a stepping-stone to do that migration.

This function may query the user for input unless input is disabled, in
which case this function will error.
// backendFromConfig returns the initialized ( not configured ) backend // directly from the config / state .. // // This function handles various edge cases around backend config loading . For // example : new config changes backend type changes etc . // // As of the 0 . 12 release it can no longer migrate from legacy remote state // to backends and will instead instruct users to use 0 . 11 or earlier as // a stepping - stone to do that migration . // // This function may query the user for input unless input is disabled in // which case this function will error .
func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Diagnostics) {
	c, cHash, diags := m.backendConfig(opts)
	if diags.HasErrors() {
		return nil, diags
	}
	 The remainder of this code often confusingly refers to this as a "state",
	 so it's unfortunately important to remember that this is not actually
	 what we _usually_ think of as "state", and is instead a local working
	 directory "backend configuration state" that is never persisted anywhere.
	 Since the "real" state has since moved on to be represented by
	 states.State, we can recognize the special meaning of state that applies
	 to this function and its callees by their continued use of the
	 otherwise-obsolete terraform.State.
	 Get the path to where we store a local cache of backend configuration
	 if we're using a remote backend. This may not yet exist which means
	 we haven't used a non-local backend before. That is okay.
	statePath := filepath.Join(m.DataDir(), DefaultStateFilename)
	sMgr := &clistate.LocalState{Path: statePath}
	if err := sMgr.RefreshState(); err != nil {
		diags = diags.Append(fmt.Errorf("Failed to load state: %s", err))
		return nil, diags
	}
	 Load the state, it must be non-nil for the tests below but can be empty
	s := sMgr.State()
	if s == nil {
		log.Printf("[TRACE] Meta.Backend: backend has not previously been initialized in this working directory")
		s = terraform.NewState()
	} else if s.Backend != nil {
		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized for %q backend", s.Backend.Type)
	} else {
		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized but has no backend (is using legacy remote state?)")
	}
	 if we want to force reconfiguration of the backend, we set the backend
	 state to nil on this copy. This will direct us through the correct
	 configuration path in the switch statement below.
	if m.reconfigure {
		s.Backend = nil
	}
	 Upon return, we want to set the state we're using in-memory so that
	 we can access it for commands.
	m.backendState = nil
	defer func() {
		if s := sMgr.State(); s != nil && !s.Backend.Empty() {
			m.backendState = s.Backend
		}
	}()
	if !s.Remote.Empty() {
		 Legacy remote state is no longer supported. User must first
		 migrate with Terraform 0.11 or earlier.
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Legacy remote state not supported",
			"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.",
		))
		return nil, diags
	}
	 This switch statement covers all the different combinations of
	 configuring new backends, updating previously-configured backends, etc.
	switch {
	 No configuration set at all. Pure local state.
	case c == nil && s.Backend.Empty():
		log.Printf("[TRACE] Meta.Backend: using default local state only (no backend configuration, and no existing initialized backend)")
		return nil, nil
	 We're unsetting a backend (moving from backend => local)
	case c == nil && !s.Backend.Empty():
		log.Printf("[TRACE] Meta.Backend: previously-initialized %q backend is no longer present in config", s.Backend.Type)
		if !opts.Init {
			initReason := fmt.Sprintf(
				"Unsetting the previously set backend %q",
				s.Backend.Type)
			m.backendInitRequired(initReason)
			diags = diags.Append(errBackendInitRequired)
			return nil, diags
		}
		return m.backend_c_r_S(c, cHash, sMgr, true)
	 Configuring a backend for the first time.
	case c != nil && s.Backend.Empty():
		log.Printf("[TRACE] Meta.Backend: moving from default local state only to %q backend", c.Type)
		if !opts.Init {
			initReason := fmt.Sprintf(
				"Initial configuration of the requested backend %q",
				c.Type)
			m.backendInitRequired(initReason)
			diags = diags.Append(errBackendInitRequired)
			return nil, diags
		}
		return m.backend_C_r_s(c, cHash, sMgr)
	 Potentially changing a backend configuration
	case c != nil && !s.Backend.Empty():
		 We are not going to migrate if were not initializing and the hashes
		 match indicating that the stored config is valid. If we are
		 initializing, then we also assume the the backend config is OK if
		 the hashes match, as long as we're not providing any new overrides.
		if (uint64(cHash) == s.Backend.Hash) && (!opts.Init || opts.ConfigOverride == nil) {
			log.Printf("[TRACE] Meta.Backend: using already-initialized, unchanged %q backend configuration", c.Type)
			return m.backend_C_r_S_unchanged(c, cHash, sMgr)
		}
		 If our configuration is the same, then we're just initializing
		 a previously configured remote backend.
		if !m.backendConfigNeedsMigration(c, s.Backend) {
			log.Printf("[TRACE] Meta.Backend: using already-initialized %q backend configuration", c.Type)
			return m.backend_C_r_S_unchanged(c, cHash, sMgr)
		}
		log.Printf("[TRACE] Meta.Backend: backend configuration has changed (from type %q to type %q)", s.Backend.Type, c.Type)
		if !opts.Init {
			initReason := fmt.Sprintf(
				"Backend configuration changed for %q",
				c.Type)
			m.backendInitRequired(initReason)
			diags = diags.Append(errBackendInitRequired)
			return nil, diags
		}
		log.Printf("[WARN] backend config has changed since last init")
		return m.backend_C_r_S_changed(c, cHash, sMgr, true)
	default:
		diags = diags.Append(fmt.Errorf(
			"Unhandled backend configuration state. This is a bug. Please\n"+
				"report this error with the following information.\n\n"+
				"Config Nil: %v\n"+
				"Saved Backend Empty: %v\n",
			c == nil, s.Backend.Empty(),
		))
		return nil, diags
	}
}
func ( m * Meta ) backendFromConfig ( opts * BackendOpts ) ( backend . Backend , tfdiags . Diagnostics ) { c , cHash , diags := m . backendConfig ( opts ) if diags . HasErrors ( ) { return nil , diags } statePath := filepath . Join ( m . DataDir ( ) , DefaultStateFilename ) sMgr := & clistate . LocalState { Path : statePath } if err := sMgr . RefreshState ( ) ; err != nil { diags = diags . Append ( fmt . Errorf ( " " , err ) ) return nil , diags } s := sMgr . State ( ) if s == nil { log . Printf ( " " ) s = terraform . NewState ( ) } else if s . Backend != nil { log . Printf ( " " , s . Backend . Type ) } else { log . Printf ( " " ) } if m . reconfigure { s . Backend = nil } m . backendState = nil defer func ( ) { if s := sMgr . State ( ) ; s != nil && ! s . Backend . Empty ( ) { m . backendState = s . Backend } } ( ) if ! s . Remote . Empty ( ) { diags = diags . Append ( tfdiags . Sourceless ( tfdiags . Error , " " , " \" \" " , ) ) return nil , diags } switch { case c == nil && s . Backend . Empty ( ) : log . Printf ( " " ) return nil , nil case c == nil && ! s . Backend . Empty ( ) : log . Printf ( " " , s . Backend . Type ) if ! opts . Init { initReason := fmt . Sprintf ( " " , s . Backend . Type ) m . backendInitRequired ( initReason ) diags = diags . Append ( errBackendInitRequired ) return nil , diags } return m . backend_c_r_S ( c , cHash , sMgr , true ) case c != nil && s . Backend . Empty ( ) : log . Printf ( " " , c . Type ) if ! opts . Init { initReason := fmt . Sprintf ( " " , c . Type ) m . backendInitRequired ( initReason ) diags = diags . Append ( errBackendInitRequired ) return nil , diags } return m . backend_C_r_s ( c , cHash , sMgr ) case c != nil && ! s . Backend . Empty ( ) : if ( uint64 ( cHash ) == s . Backend . Hash ) && ( ! opts . Init || opts . ConfigOverride == nil ) { log . Printf ( " " , c . Type ) return m . backend_C_r_S_unchanged ( c , cHash , sMgr ) } if ! m . backendConfigNeedsMigration ( c , s . Backend ) { log . Printf ( " " , c . Type ) return m . backend_C_r_S_unchanged ( c , cHash , sMgr ) } log . Printf ( " " , s . Backend . Type , c . Type ) if ! opts . Init { initReason := fmt . Sprintf ( " " , c . Type ) m . backendInitRequired ( initReason ) diags = diags . Append ( errBackendInitRequired ) return nil , diags } log . Printf ( " " ) return m . backend_C_r_S_changed ( c , cHash , sMgr , true ) default : diags = diags . Append ( fmt . Errorf ( " \n " + " \n \n " + " \n " + " \n " , c == nil , s . Backend . Empty ( ) , ) ) return nil , diags } }
backendFromConfig returns the initialized (not configured) backend
directly from the config/state
backendFromConfig returns the initialized ( not configured ) backend directly from the config / state
// Get the local backend configuration. // ------------------------------------------------------------------------ // For historical reasons, current backend configuration for a working // directory is kept in a *state-like* file, using the legacy state // structures in the Terraform package. It is not actually a Terraform // state, and so only the "backend" portion of it is actually used. // // The remainder of this code often confusingly refers to this as a "state", // so it's unfortunately important to remember that this is not actually // what we _usually_ think of as "state", and is instead a local working // directory "backend configuration state" that is never persisted anywhere. // // Since the "real" state has since moved on to be represented by // states.State, we can recognize the special meaning of state that applies // to this function and its callees by their continued use of the // otherwise-obsolete terraform.State. // ------------------------------------------------------------------------ // Get the path to where we store a local cache of backend configuration // if we're using a remote backend. This may not yet exist which means // we haven't used a non-local backend before. That is okay. // Load the state, it must be non-nil for the tests below but can be empty // if we want to force reconfiguration of the backend, we set the backend // state to nil on this copy. This will direct us through the correct // configuration path in the switch statement below. // Upon return, we want to set the state we're using in-memory so that // we can access it for commands. // Legacy remote state is no longer supported. User must first // migrate with Terraform 0.11 or earlier. // This switch statement covers all the different combinations of // configuring new backends, updating previously-configured backends, etc. // No configuration set at all. Pure local state. // We're unsetting a backend (moving from backend => local) // Configuring a backend for the first time. // Potentially changing a backend configuration // We are not going to migrate if were not initializing and the hashes // match indicating that the stored config is valid. If we are // initializing, then we also assume the the backend config is OK if // the hashes match, as long as we're not providing any new overrides. // If our configuration is the same, then we're just initializing // a previously configured remote backend.