github.com/opentofu/opentofu@v1.7.1/internal/states/remote/state.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 remote
     7  
     8  import (
     9  	"bytes"
    10  	"fmt"
    11  	"log"
    12  	"sync"
    13  
    14  	uuid "github.com/hashicorp/go-uuid"
    15  
    16  	"github.com/opentofu/opentofu/internal/backend/local"
    17  	"github.com/opentofu/opentofu/internal/encryption"
    18  	"github.com/opentofu/opentofu/internal/states"
    19  	"github.com/opentofu/opentofu/internal/states/statefile"
    20  	"github.com/opentofu/opentofu/internal/states/statemgr"
    21  	"github.com/opentofu/opentofu/internal/tofu"
    22  )
    23  
    24  // State implements the State interfaces in the state package to handle
    25  // reading and writing the remote state. This State on its own does no
    26  // local caching so every persist will go to the remote storage and local
    27  // writes will go to memory.
    28  type State struct {
    29  	mu sync.Mutex
    30  
    31  	Client Client
    32  
    33  	encryption encryption.StateEncryption
    34  
    35  	// We track two pieces of meta data in addition to the state itself:
    36  	//
    37  	// lineage - the state's unique ID
    38  	// serial  - the monotonic counter of "versions" of the state
    39  	//
    40  	// Both of these (along with state) have a sister field
    41  	// that represents the values read in from an existing source.
    42  	// All three of these values are used to determine if the new
    43  	// state has changed from an existing state we read in.
    44  	lineage, readLineage string
    45  	serial, readSerial   uint64
    46  	state, readState     *states.State
    47  	disableLocks         bool
    48  
    49  	// If this is set then the state manager will decline to store intermediate
    50  	// state snapshots created while a OpenTofu Core apply operation is in
    51  	// progress. Otherwise (by default) it will accept persistent snapshots
    52  	// using the default rules defined in the local backend.
    53  	disableIntermediateSnapshots bool
    54  }
    55  
    56  var _ statemgr.Full = (*State)(nil)
    57  var _ statemgr.Migrator = (*State)(nil)
    58  var _ local.IntermediateStateConditionalPersister = (*State)(nil)
    59  
    60  func NewState(client Client, enc encryption.StateEncryption) *State {
    61  	return &State{
    62  		Client:     client,
    63  		encryption: enc,
    64  	}
    65  }
    66  
    67  func (s *State) DisableIntermediateSnapshots() {
    68  	s.disableIntermediateSnapshots = true
    69  }
    70  
    71  // statemgr.Reader impl.
    72  func (s *State) State() *states.State {
    73  	s.mu.Lock()
    74  	defer s.mu.Unlock()
    75  
    76  	return s.state.DeepCopy()
    77  }
    78  
    79  func (s *State) GetRootOutputValues() (map[string]*states.OutputValue, error) {
    80  	if err := s.RefreshState(); err != nil {
    81  		return nil, fmt.Errorf("Failed to load state: %w", err)
    82  	}
    83  
    84  	state := s.State()
    85  	if state == nil {
    86  		state = states.NewState()
    87  	}
    88  
    89  	return state.RootModule().OutputValues, nil
    90  }
    91  
    92  // StateForMigration is part of our implementation of statemgr.Migrator.
    93  func (s *State) StateForMigration() *statefile.File {
    94  	s.mu.Lock()
    95  	defer s.mu.Unlock()
    96  
    97  	return statefile.New(s.state.DeepCopy(), s.lineage, s.serial)
    98  }
    99  
   100  // statemgr.Writer impl.
   101  func (s *State) WriteState(state *states.State) error {
   102  	s.mu.Lock()
   103  	defer s.mu.Unlock()
   104  
   105  	// We create a deep copy of the state here, because the caller also has
   106  	// a reference to the given object and can potentially go on to mutate
   107  	// it after we return, but we want the snapshot at this point in time.
   108  	s.state = state.DeepCopy()
   109  
   110  	return nil
   111  }
   112  
   113  // WriteStateForMigration is part of our implementation of statemgr.Migrator.
   114  func (s *State) WriteStateForMigration(f *statefile.File, force bool) error {
   115  	s.mu.Lock()
   116  	defer s.mu.Unlock()
   117  
   118  	if !force {
   119  		checkFile := statefile.New(s.state, s.lineage, s.serial)
   120  		if err := statemgr.CheckValidImport(f, checkFile); err != nil {
   121  			return err
   122  		}
   123  	}
   124  
   125  	// The remote backend needs to pass the `force` flag through to its client.
   126  	// For backends that support such operations, inform the client
   127  	// that a force push has been requested
   128  	c, isForcePusher := s.Client.(ClientForcePusher)
   129  	if force && isForcePusher {
   130  		c.EnableForcePush()
   131  	}
   132  
   133  	// We create a deep copy of the state here, because the caller also has
   134  	// a reference to the given object and can potentially go on to mutate
   135  	// it after we return, but we want the snapshot at this point in time.
   136  	s.state = f.State.DeepCopy()
   137  	s.lineage = f.Lineage
   138  	s.serial = f.Serial
   139  
   140  	return nil
   141  }
   142  
   143  // statemgr.Refresher impl.
   144  func (s *State) RefreshState() error {
   145  	s.mu.Lock()
   146  	defer s.mu.Unlock()
   147  	return s.refreshState()
   148  }
   149  
   150  // refreshState is the main implementation of RefreshState, but split out so
   151  // that we can make internal calls to it from methods that are already holding
   152  // the s.mu lock.
   153  func (s *State) refreshState() error {
   154  	payload, err := s.Client.Get()
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	// no remote state is OK
   160  	if payload == nil {
   161  		s.readState = nil
   162  		s.lineage = ""
   163  		s.serial = 0
   164  		return nil
   165  	}
   166  
   167  	stateFile, err := statefile.Read(bytes.NewReader(payload.Data), s.encryption)
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	s.lineage = stateFile.Lineage
   173  	s.serial = stateFile.Serial
   174  	s.state = stateFile.State
   175  
   176  	// Properties from the remote must be separate so we can
   177  	// track changes as lineage, serial and/or state are mutated
   178  	s.readLineage = stateFile.Lineage
   179  	s.readSerial = stateFile.Serial
   180  	s.readState = s.state.DeepCopy()
   181  	return nil
   182  }
   183  
   184  // statemgr.Persister impl.
   185  func (s *State) PersistState(schemas *tofu.Schemas) error {
   186  	s.mu.Lock()
   187  	defer s.mu.Unlock()
   188  
   189  	log.Printf("[DEBUG] states/remote: state read serial is: %d; serial is: %d", s.readSerial, s.serial)
   190  	log.Printf("[DEBUG] states/remote: state read lineage is: %s; lineage is: %s", s.readLineage, s.lineage)
   191  
   192  	if s.readState != nil {
   193  		lineageUnchanged := s.readLineage != "" && s.lineage == s.readLineage
   194  		serialUnchanged := s.readSerial != 0 && s.serial == s.readSerial
   195  		stateUnchanged := statefile.StatesMarshalEqual(s.state, s.readState)
   196  		if stateUnchanged && lineageUnchanged && serialUnchanged {
   197  			// If the state, lineage or serial haven't changed at all then we have nothing to do.
   198  			return nil
   199  		}
   200  		s.serial++
   201  	} else {
   202  		// We might be writing a new state altogether, but before we do that
   203  		// we'll check to make sure there isn't already a snapshot present
   204  		// that we ought to be updating.
   205  		err := s.refreshState()
   206  		if err != nil {
   207  			return fmt.Errorf("failed checking for existing remote state: %w", err)
   208  		}
   209  		log.Printf("[DEBUG] states/remote: after refresh, state read serial is: %d; serial is: %d", s.readSerial, s.serial)
   210  		log.Printf("[DEBUG] states/remote: after refresh, state read lineage is: %s; lineage is: %s", s.readLineage, s.lineage)
   211  		if s.lineage == "" { // indicates that no state snapshot is present yet
   212  			lineage, err := uuid.GenerateUUID()
   213  			if err != nil {
   214  				return fmt.Errorf("failed to generate initial lineage: %w", err)
   215  			}
   216  			s.lineage = lineage
   217  			s.serial++
   218  		}
   219  	}
   220  
   221  	f := statefile.New(s.state, s.lineage, s.serial)
   222  
   223  	var buf bytes.Buffer
   224  	err := statefile.Write(f, &buf, s.encryption)
   225  	if err != nil {
   226  		return err
   227  	}
   228  
   229  	err = s.Client.Put(buf.Bytes())
   230  	if err != nil {
   231  		return err
   232  	}
   233  
   234  	// After we've successfully persisted, what we just wrote is our new
   235  	// reference state until someone calls RefreshState again.
   236  	// We've potentially overwritten (via force) the state, lineage
   237  	// and / or serial (and serial was incremented) so we copy over all
   238  	// three fields so everything matches the new state and a subsequent
   239  	// operation would correctly detect no changes to the lineage, serial or state.
   240  	s.readState = s.state.DeepCopy()
   241  	s.readLineage = s.lineage
   242  	s.readSerial = s.serial
   243  	return nil
   244  }
   245  
   246  // ShouldPersistIntermediateState implements local.IntermediateStateConditionalPersister
   247  func (s *State) ShouldPersistIntermediateState(info *local.IntermediateStatePersistInfo) bool {
   248  	if s.disableIntermediateSnapshots {
   249  		return false
   250  	}
   251  	return local.DefaultIntermediateStatePersistRule(info)
   252  }
   253  
   254  // Lock calls the Client's Lock method if it's implemented.
   255  func (s *State) Lock(info *statemgr.LockInfo) (string, error) {
   256  	s.mu.Lock()
   257  	defer s.mu.Unlock()
   258  
   259  	if s.disableLocks {
   260  		return "", nil
   261  	}
   262  
   263  	if c, ok := s.Client.(ClientLocker); ok {
   264  		return c.Lock(info)
   265  	}
   266  	return "", nil
   267  }
   268  
   269  // Unlock calls the Client's Unlock method if it's implemented.
   270  func (s *State) Unlock(id string) error {
   271  	s.mu.Lock()
   272  	defer s.mu.Unlock()
   273  
   274  	if s.disableLocks {
   275  		return nil
   276  	}
   277  
   278  	if c, ok := s.Client.(ClientLocker); ok {
   279  		return c.Unlock(id)
   280  	}
   281  	return nil
   282  }
   283  
   284  // DisableLocks turns the Lock and Unlock methods into no-ops. This is intended
   285  // to be called during initialization of a state manager and should not be
   286  // called after any of the statemgr.Full interface methods have been called.
   287  func (s *State) DisableLocks() {
   288  	s.disableLocks = true
   289  }
   290  
   291  // StateSnapshotMeta returns the metadata from the most recently persisted
   292  // or refreshed persistent state snapshot.
   293  //
   294  // This is an implementation of statemgr.PersistentMeta.
   295  func (s *State) StateSnapshotMeta() statemgr.SnapshotMeta {
   296  	return statemgr.SnapshotMeta{
   297  		Lineage: s.lineage,
   298  		Serial:  s.serial,
   299  	}
   300  }