github.com/opentofu/opentofu@v1.7.1/internal/states/statemgr/plan.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 statemgr
     7  
     8  import (
     9  	"fmt"
    10  
    11  	"github.com/opentofu/opentofu/internal/states"
    12  	"github.com/opentofu/opentofu/internal/states/statefile"
    13  )
    14  
    15  // PlannedStateUpdate is a special helper to obtain a statefile representation
    16  // of a not-yet-written state snapshot that can be written later by a call
    17  // to the companion function WritePlannedStateUpdate.
    18  //
    19  // The statefile object returned here has an unusual interpretation of its
    20  // metadata that is understood only by WritePlannedStateUpdate, and so the
    21  // returned object should not be used for any other purpose.
    22  //
    23  // If the state manager implements Locker then it is the caller's
    24  // responsibility to hold the lock at least for the duration of this call.
    25  // It is not safe to modify the given state concurrently while
    26  // PlannedStateUpdate is running.
    27  func PlannedStateUpdate(mgr Transient, planned *states.State) *statefile.File {
    28  	ret := &statefile.File{
    29  		State: planned.DeepCopy(),
    30  	}
    31  
    32  	// If the given manager uses snapshot metadata then we'll save that
    33  	// in our file so we can check it again during WritePlannedStateUpdate.
    34  	if mr, ok := mgr.(PersistentMeta); ok {
    35  		m := mr.StateSnapshotMeta()
    36  		ret.Lineage = m.Lineage
    37  		ret.Serial = m.Serial
    38  	}
    39  
    40  	return ret
    41  }
    42  
    43  // WritePlannedStateUpdate is a companion to PlannedStateUpdate that attempts
    44  // to apply a state update that was planned earlier to the given state
    45  // manager.
    46  //
    47  // An error is returned if this function detects that a new state snapshot
    48  // has been written to the backend since the update was planned, since that
    49  // invalidates the plan. An error is returned also if the manager itself
    50  // rejects the given state when asked to store it.
    51  //
    52  // If the returned error is nil, the given manager's transient state snapshot
    53  // is updated to match what was planned. It is the caller's responsibility
    54  // to then persist that state if the manager also implements Persistent and
    55  // the snapshot should be written to the persistent store.
    56  //
    57  // If the state manager implements Locker then it is the caller's
    58  // responsibility to hold the lock at least for the duration of this call.
    59  func WritePlannedStateUpdate(mgr Transient, planned *statefile.File) error {
    60  	// If the given manager uses snapshot metadata then we'll check to make
    61  	// sure no new snapshots have been created since we planned to write
    62  	// the given state file.
    63  	if mr, ok := mgr.(PersistentMeta); ok {
    64  		m := mr.StateSnapshotMeta()
    65  		if planned.Lineage != "" {
    66  			if planned.Lineage != m.Lineage {
    67  				return fmt.Errorf("planned state update is from an unrelated state lineage than the current state")
    68  			}
    69  			if planned.Serial != m.Serial {
    70  				return fmt.Errorf("stored state has been changed by another operation since the given update was planned")
    71  			}
    72  		}
    73  	}
    74  
    75  	return mgr.WriteState(planned.State)
    76  }