github.com/opentofu/opentofu@v1.7.1/internal/cloud/migration.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package cloud
     7  
     8  import (
     9  	"github.com/opentofu/opentofu/internal/configs"
    10  	legacy "github.com/opentofu/opentofu/internal/legacy/tofu"
    11  )
    12  
    13  // Most of the logic for migrating into and out of "cloud mode" actually lives
    14  // in the "command" package as part of the general backend init mechanisms,
    15  // but we have some cloud-specific helper functionality here.
    16  
    17  // ConfigChangeMode is a rough way to think about different situations that
    18  // our backend change and state migration codepaths need to distinguish in
    19  // the context of Cloud integration mode.
    20  type ConfigChangeMode rune
    21  
    22  //go:generate go run golang.org/x/tools/cmd/stringer -type ConfigChangeMode
    23  
    24  const (
    25  	// ConfigMigrationIn represents when the configuration calls for using
    26  	// Cloud mode but the working directory state disagrees.
    27  	ConfigMigrationIn ConfigChangeMode = '↘'
    28  
    29  	// ConfigMigrationOut represents when the working directory state calls
    30  	// for using Cloud mode but the working directory state disagrees.
    31  	ConfigMigrationOut ConfigChangeMode = '↖'
    32  
    33  	// ConfigChangeInPlace represents when both the working directory state
    34  	// and the config call for using Cloud mode, and so there might be
    35  	// (but won't necessarily be) cloud settings changing, but we don't
    36  	// need to do any actual migration.
    37  	ConfigChangeInPlace ConfigChangeMode = '↻'
    38  
    39  	// ConfigChangeIrrelevant represents when the config and working directory
    40  	// state disagree but neither calls for using Cloud mode, and so the
    41  	// Cloud integration is not involved in dealing with this.
    42  	ConfigChangeIrrelevant ConfigChangeMode = '🤷'
    43  )
    44  
    45  // DetectConfigChangeType encapsulates the fiddly logic for deciding what kind
    46  // of Cloud configuration change we seem to be making, based on the existing
    47  // working directory state (if any) and the current configuration.
    48  //
    49  // This is a pretty specialized sort of thing focused on finicky details of
    50  // the way we currently model working directory settings and config, so its
    51  // signature probably won't survive any non-trivial refactoring of how
    52  // the CLI layer thinks about backends/state storage.
    53  func DetectConfigChangeType(wdState *legacy.BackendState, config *configs.Backend, haveLocalStates bool) ConfigChangeMode {
    54  	// Although externally the cloud integration isn't really a "backend",
    55  	// internally we treat it a bit like one just to preserve all of our
    56  	// existing interfaces that assume backends. "cloud" is the placeholder
    57  	// name we use for it, even though that isn't a backend that's actually
    58  	// available for selection in the usual way.
    59  	wdIsCloud := wdState != nil && wdState.Type == "cloud"
    60  	configIsCloud := config != nil && config.Type == "cloud"
    61  
    62  	// "uninit" here means that the working directory is totally uninitialized,
    63  	// even taking into account the possibility of implied local state that
    64  	// therefore doesn't typically require explicit "tofu init".
    65  	wdIsUninit := wdState == nil && !haveLocalStates
    66  
    67  	switch {
    68  	case configIsCloud:
    69  		switch {
    70  		case wdIsCloud || wdIsUninit:
    71  			// If config has cloud and the working directory is completely
    72  			// uninitialized then we assume we're doing the initial activation
    73  			// of this working directory for an already-migrated-to-cloud
    74  			// remote state.
    75  			return ConfigChangeInPlace
    76  		default:
    77  			// Otherwise, we seem to be migrating into cloud mode from a backend.
    78  			return ConfigMigrationIn
    79  		}
    80  	default:
    81  		switch {
    82  		case wdIsCloud:
    83  			// If working directory is already cloud but config isn't, we're
    84  			// migrating away from cloud to a backend.
    85  			return ConfigMigrationOut
    86  		default:
    87  			// Otherwise, this situation seems to be something unrelated to
    88  			// cloud mode and so outside of our scope here.
    89  			return ConfigChangeIrrelevant
    90  		}
    91  	}
    92  
    93  }
    94  
    95  func (m ConfigChangeMode) InvolvesCloud() bool {
    96  	switch m {
    97  	case ConfigMigrationIn, ConfigMigrationOut, ConfigChangeInPlace:
    98  		return true
    99  	default:
   100  		return false
   101  	}
   102  }
   103  
   104  func (m ConfigChangeMode) IsCloudMigration() bool {
   105  	switch m {
   106  	case ConfigMigrationIn, ConfigMigrationOut:
   107  		return true
   108  	default:
   109  		return false
   110  	}
   111  }