terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
|
|
|
package statemgr
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2021-05-17 21:43:35 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
|
|
"github.com/hashicorp/terraform/internal/states/statefile"
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// PlannedStateUpdate is a special helper to obtain a statefile representation
|
|
|
|
// of a not-yet-written state snapshot that can be written later by a call
|
|
|
|
// to the companion function WritePlannedStateUpdate.
|
|
|
|
//
|
|
|
|
// The statefile object returned here has an unusual interpretation of its
|
|
|
|
// metadata that is understood only by WritePlannedStateUpdate, and so the
|
|
|
|
// returned object should not be used for any other purpose.
|
|
|
|
//
|
|
|
|
// If the state manager implements Locker then it is the caller's
|
|
|
|
// responsibility to hold the lock at least for the duration of this call.
|
|
|
|
// It is not safe to modify the given state concurrently while
|
|
|
|
// PlannedStateUpdate is running.
|
|
|
|
func PlannedStateUpdate(mgr Transient, planned *states.State) *statefile.File {
|
|
|
|
ret := &statefile.File{
|
|
|
|
State: planned.DeepCopy(),
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the given manager uses snapshot metadata then we'll save that
|
|
|
|
// in our file so we can check it again during WritePlannedStateUpdate.
|
|
|
|
if mr, ok := mgr.(PersistentMeta); ok {
|
|
|
|
m := mr.StateSnapshotMeta()
|
|
|
|
ret.Lineage = m.Lineage
|
|
|
|
ret.Serial = m.Serial
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// WritePlannedStateUpdate is a companion to PlannedStateUpdate that attempts
|
|
|
|
// to apply a state update that was planned earlier to the given state
|
|
|
|
// manager.
|
|
|
|
//
|
|
|
|
// An error is returned if this function detects that a new state snapshot
|
|
|
|
// has been written to the backend since the update was planned, since that
|
|
|
|
// invalidates the plan. An error is returned also if the manager itself
|
|
|
|
// rejects the given state when asked to store it.
|
|
|
|
//
|
|
|
|
// If the returned error is nil, the given manager's transient state snapshot
|
|
|
|
// is updated to match what was planned. It is the caller's responsibility
|
|
|
|
// to then persist that state if the manager also implements Persistent and
|
|
|
|
// the snapshot should be written to the persistent store.
|
|
|
|
//
|
|
|
|
// If the state manager implements Locker then it is the caller's
|
|
|
|
// responsibility to hold the lock at least for the duration of this call.
|
|
|
|
func WritePlannedStateUpdate(mgr Transient, planned *statefile.File) error {
|
|
|
|
// If the given manager uses snapshot metadata then we'll check to make
|
|
|
|
// sure no new snapshots have been created since we planned to write
|
|
|
|
// the given state file.
|
|
|
|
if mr, ok := mgr.(PersistentMeta); ok {
|
|
|
|
m := mr.StateSnapshotMeta()
|
|
|
|
if planned.Lineage != "" {
|
|
|
|
if planned.Lineage != m.Lineage {
|
|
|
|
return fmt.Errorf("planned state update is from an unrelated state lineage than the current state")
|
|
|
|
}
|
|
|
|
if planned.Serial != m.Serial {
|
|
|
|
return fmt.Errorf("stored state has been changed by another operation since the given update was planned")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mgr.WriteState(planned.State)
|
|
|
|
}
|