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

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