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 }