github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/states/statemgr/migrate.go (about)

     1  package statemgr
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform/states/statefile"
     7  )
     8  
     9  // Migrator is an optional interface implemented by state managers that
    10  // are capable of direct migration of state snapshots with their associated
    11  // metadata unchanged.
    12  //
    13  // This interface is used when available by function Migrate. See that
    14  // function for more information on how it is used.
    15  type Migrator interface {
    16  	PersistentMeta
    17  
    18  	// StateForMigration returns a full statefile representing the latest
    19  	// snapshot (as would be returned by Reader.State) and the associated
    20  	// snapshot metadata (as would be returned by
    21  	// PersistentMeta.StateSnapshotMeta).
    22  	//
    23  	// Just as with Reader.State, this must not fail.
    24  	StateForMigration() *statefile.File
    25  
    26  	// WriteStateForMigration accepts a full statefile including associated
    27  	// snapshot metadata, and atomically updates the stored file (as with
    28  	// Writer.WriteState) and the metadata.
    29  	//
    30  	// If "force" is not set, the manager must call CheckValidImport with
    31  	// the given file and the current file and complete the update only if
    32  	// that function returns nil. If force is set this may override such
    33  	// checks, but some backends do not support forcing and so will act
    34  	// as if force is always false.
    35  	WriteStateForMigration(f *statefile.File, force bool) error
    36  }
    37  
    38  // Migrate writes the latest transient state snapshot from src into dest,
    39  // preserving snapshot metadata (serial and lineage) where possible.
    40  //
    41  // If both managers implement the optional interface Migrator then it will
    42  // be used to copy the snapshot and its associated metadata. Otherwise,
    43  // the normal Reader and Writer interfaces will be used instead.
    44  //
    45  // If the destination manager refuses the new state or fails to write it then
    46  // its error is returned directly.
    47  //
    48  // For state managers that also implement Persistent, it is the caller's
    49  // responsibility to persist the newly-written state after a successful result,
    50  // just as with calls to Writer.WriteState.
    51  //
    52  // This function doesn't do any locking of its own, so if the state managers
    53  // also implement Locker the caller should hold a lock on both managers
    54  // for the duration of this call.
    55  func Migrate(dst, src Transient) error {
    56  	if dstM, ok := dst.(Migrator); ok {
    57  		if srcM, ok := src.(Migrator); ok {
    58  			// Full-fidelity migration, them.
    59  			s := srcM.StateForMigration()
    60  			return dstM.WriteStateForMigration(s, true)
    61  		}
    62  	}
    63  
    64  	// Managers to not support full-fidelity migration, so migration will not
    65  	// preserve serial/lineage.
    66  	s := src.State()
    67  	return dst.WriteState(s)
    68  }
    69  
    70  // Import loads the given state snapshot into the given manager, preserving
    71  // its metadata (serial and lineage) if the target manager supports metadata.
    72  //
    73  // A state manager must implement the optional interface Migrator to get
    74  // access to the full metadata.
    75  //
    76  // Unless "force" is true, Import will check first that the metadata given
    77  // in the file matches the current snapshot metadata for the manager, if the
    78  // manager supports metadata. Some managers do not support forcing, so a
    79  // write with an unsuitable lineage or serial may still be rejected even if
    80  // "force" is set. "force" has no effect for managers that do not support
    81  // snapshot metadata.
    82  //
    83  // For state managers that also implement Persistent, it is the caller's
    84  // responsibility to persist the newly-written state after a successful result,
    85  // just as with calls to Writer.WriteState.
    86  //
    87  // This function doesn't do any locking of its own, so if the state manager
    88  // also implements Locker the caller should hold a lock on it for the
    89  // duration of this call.
    90  func Import(f *statefile.File, mgr Transient, force bool) error {
    91  	if mgrM, ok := mgr.(Migrator); ok {
    92  		return mgrM.WriteStateForMigration(f, force)
    93  	}
    94  
    95  	// For managers that don't implement Migrator, this is just a normal write
    96  	// of the state contained in the given file.
    97  	return mgr.WriteState(f.State)
    98  }
    99  
   100  // Export retrieves the latest state snapshot from the given manager, including
   101  // its metadata (serial and lineage) where possible.
   102  //
   103  // A state manager must also implement either Migrator or PersistentMeta
   104  // for the metadata to be included. Otherwise, the relevant fields will have
   105  // zero value in the returned object.
   106  //
   107  // For state managers that also implement Persistent, it is the caller's
   108  // responsibility to refresh from persistent storage first if needed.
   109  //
   110  // This function doesn't do any locking of its own, so if the state manager
   111  // also implements Locker the caller should hold a lock on it for the
   112  // duration of this call.
   113  func Export(mgr Reader) *statefile.File {
   114  	switch mgrT := mgr.(type) {
   115  	case Migrator:
   116  		return mgrT.StateForMigration()
   117  	case PersistentMeta:
   118  		s := mgr.State()
   119  		meta := mgrT.StateSnapshotMeta()
   120  		return statefile.New(s, meta.Lineage, meta.Serial)
   121  	default:
   122  		s := mgr.State()
   123  		return statefile.New(s, "", 0)
   124  	}
   125  }
   126  
   127  // SnapshotMetaRel describes a relationship between two SnapshotMeta values,
   128  // returned from the SnapshotMeta.Compare method where the "first" value
   129  // is the receiver of that method and the "second" is the given argument.
   130  type SnapshotMetaRel rune
   131  
   132  //go:generate go run golang.org/x/tools/cmd/stringer -type=SnapshotMetaRel
   133  
   134  const (
   135  	// SnapshotOlder indicates that two snapshots have a common lineage and
   136  	// that the first has a lower serial value.
   137  	SnapshotOlder SnapshotMetaRel = '<'
   138  
   139  	// SnapshotNewer indicates that two snapshots have a common lineage and
   140  	// that the first has a higher serial value.
   141  	SnapshotNewer SnapshotMetaRel = '>'
   142  
   143  	// SnapshotEqual indicates that two snapshots have a common lineage and
   144  	// the same serial value.
   145  	SnapshotEqual SnapshotMetaRel = '='
   146  
   147  	// SnapshotUnrelated indicates that two snapshots have different lineage
   148  	// and thus cannot be meaningfully compared.
   149  	SnapshotUnrelated SnapshotMetaRel = '!'
   150  
   151  	// SnapshotLegacy indicates that one or both of the snapshots
   152  	// does not have a lineage at all, and thus no comparison is possible.
   153  	SnapshotLegacy SnapshotMetaRel = '?'
   154  )
   155  
   156  // Compare determines the relationship, if any, between the given existing
   157  // SnapshotMeta and the potential "new" SnapshotMeta that is the receiver.
   158  func (m SnapshotMeta) Compare(existing SnapshotMeta) SnapshotMetaRel {
   159  	switch {
   160  	case m.Lineage == "" || existing.Lineage == "":
   161  		return SnapshotLegacy
   162  	case m.Lineage != existing.Lineage:
   163  		return SnapshotUnrelated
   164  	case m.Serial > existing.Serial:
   165  		return SnapshotNewer
   166  	case m.Serial < existing.Serial:
   167  		return SnapshotOlder
   168  	default:
   169  		// both serials are equal, by elimination
   170  		return SnapshotEqual
   171  	}
   172  }
   173  
   174  // CheckValidImport returns nil if the "new" snapshot can be imported as a
   175  // successor of the "existing" snapshot without forcing.
   176  //
   177  // If not, an error is returned describing why.
   178  func CheckValidImport(newFile, existingFile *statefile.File) error {
   179  	if existingFile == nil || existingFile.State.Empty() {
   180  		// It's always okay to overwrite an empty state, regardless of
   181  		// its lineage/serial.
   182  		return nil
   183  	}
   184  	new := SnapshotMeta{
   185  		Lineage: newFile.Lineage,
   186  		Serial:  newFile.Serial,
   187  	}
   188  	existing := SnapshotMeta{
   189  		Lineage: existingFile.Lineage,
   190  		Serial:  existingFile.Serial,
   191  	}
   192  	rel := new.Compare(existing)
   193  	switch rel {
   194  	case SnapshotNewer:
   195  		return nil // a newer snapshot is fine
   196  	case SnapshotLegacy:
   197  		return nil // anything goes for a legacy state
   198  	case SnapshotUnrelated:
   199  		return fmt.Errorf("cannot import state with lineage %q over unrelated state with lineage %q", new.Lineage, existing.Lineage)
   200  	case SnapshotEqual:
   201  		if statefile.StatesMarshalEqual(newFile.State, existingFile.State) {
   202  			// If lineage, serial, and state all match then this is fine.
   203  			return nil
   204  		}
   205  		return fmt.Errorf("cannot overwrite existing state with serial %d with a different state that has the same serial", new.Serial)
   206  	case SnapshotOlder:
   207  		return fmt.Errorf("cannot import state with serial %d over newer state with serial %d", new.Serial, existing.Serial)
   208  	default:
   209  		// Should never happen, but we'll check to make sure for safety
   210  		return fmt.Errorf("unsupported state snapshot relationship %s", rel)
   211  	}
   212  }