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 plans
|
|
|
|
|
|
|
|
import (
|
2018-08-28 01:45:07 +02:00
|
|
|
"fmt"
|
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
|
|
|
"sync"
|
2018-08-28 01:45:07 +02:00
|
|
|
|
2021-05-17 21:00:50 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
2021-05-17 21:43:35 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/states"
|
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
|
|
|
)
|
|
|
|
|
|
|
|
// ChangesSync is a wrapper around a Changes that provides a concurrency-safe
|
|
|
|
// interface to insert new changes and retrieve copies of existing changes.
|
|
|
|
//
|
|
|
|
// Each ChangesSync is independent of all others, so all concurrent writers
|
|
|
|
// to a particular Changes must share a single ChangesSync. Behavior is
|
|
|
|
// undefined if any other caller makes changes to the underlying Changes
|
|
|
|
// object or its nested objects concurrently with any of the methods of a
|
|
|
|
// particular ChangesSync.
|
|
|
|
type ChangesSync struct {
|
|
|
|
lock sync.Mutex
|
|
|
|
changes *Changes
|
|
|
|
}
|
2018-08-27 23:33:08 +02:00
|
|
|
|
2020-10-05 16:50:25 +02:00
|
|
|
// IsFullDestroy returns true if the set of changes indicates we are doing a
|
2020-10-01 23:08:25 +02:00
|
|
|
// destroy of all resources.
|
2020-10-05 16:50:25 +02:00
|
|
|
func (cs *ChangesSync) IsFullDestroy() bool {
|
2020-10-01 23:08:25 +02:00
|
|
|
if cs == nil {
|
|
|
|
panic("FullDestroy on nil ChangesSync")
|
|
|
|
}
|
|
|
|
cs.lock.Lock()
|
|
|
|
defer cs.lock.Unlock()
|
|
|
|
|
|
|
|
for _, c := range cs.changes.Resources {
|
|
|
|
if c.Action != Delete {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2018-08-27 23:33:08 +02:00
|
|
|
// AppendResourceInstanceChange records the given resource instance change in
|
|
|
|
// the set of planned resource changes.
|
|
|
|
//
|
|
|
|
// The caller must ensure that there are no concurrent writes to the given
|
|
|
|
// change while this method is running, but it is safe to resume mutating
|
|
|
|
// it after this method returns without affecting the saved change.
|
|
|
|
func (cs *ChangesSync) AppendResourceInstanceChange(changeSrc *ResourceInstanceChangeSrc) {
|
|
|
|
if cs == nil {
|
|
|
|
panic("AppendResourceInstanceChange on nil ChangesSync")
|
|
|
|
}
|
|
|
|
cs.lock.Lock()
|
|
|
|
defer cs.lock.Unlock()
|
|
|
|
|
|
|
|
s := changeSrc.DeepCopy()
|
|
|
|
cs.changes.Resources = append(cs.changes.Resources, s)
|
|
|
|
}
|
2018-08-28 01:45:07 +02:00
|
|
|
|
|
|
|
// GetResourceInstanceChange searches the set of resource instance changes for
|
|
|
|
// one matching the given address and generation, returning it if it exists.
|
|
|
|
//
|
|
|
|
// If no such change exists, nil is returned.
|
|
|
|
//
|
|
|
|
// The returned object is a deep copy of the change recorded in the plan, so
|
|
|
|
// callers may mutate it although it's generally better (less confusing) to
|
|
|
|
// treat planned changes as immutable after they've been initially constructed.
|
|
|
|
func (cs *ChangesSync) GetResourceInstanceChange(addr addrs.AbsResourceInstance, gen states.Generation) *ResourceInstanceChangeSrc {
|
|
|
|
if cs == nil {
|
|
|
|
panic("GetResourceInstanceChange on nil ChangesSync")
|
|
|
|
}
|
|
|
|
cs.lock.Lock()
|
|
|
|
defer cs.lock.Unlock()
|
|
|
|
|
|
|
|
if gen == states.CurrentGen {
|
2018-08-28 01:51:26 +02:00
|
|
|
return cs.changes.ResourceInstance(addr).DeepCopy()
|
2018-08-28 01:45:07 +02:00
|
|
|
}
|
|
|
|
if dk, ok := gen.(states.DeposedKey); ok {
|
2018-08-28 01:51:26 +02:00
|
|
|
return cs.changes.ResourceInstanceDeposed(addr, dk).DeepCopy()
|
2018-08-28 01:45:07 +02:00
|
|
|
}
|
|
|
|
panic(fmt.Sprintf("unsupported generation value %#v", gen))
|
|
|
|
}
|
2018-08-28 02:34:38 +02:00
|
|
|
|
2020-05-13 14:59:34 +02:00
|
|
|
// GetChangesForConfigResource searched the set of resource instance
|
2020-05-02 02:42:45 +02:00
|
|
|
// changes and returns all changes related to a given configuration address.
|
|
|
|
// This is be used to find possible changes related to a configuration
|
|
|
|
// reference.
|
|
|
|
//
|
|
|
|
// If no such changes exist, nil is returned.
|
|
|
|
//
|
|
|
|
// The returned objects are a deep copy of the change recorded in the plan, so
|
|
|
|
// callers may mutate them although it's generally better (less confusing) to
|
|
|
|
// treat planned changes as immutable after they've been initially constructed.
|
2020-05-13 14:59:34 +02:00
|
|
|
func (cs *ChangesSync) GetChangesForConfigResource(addr addrs.ConfigResource) []*ResourceInstanceChangeSrc {
|
2020-05-02 02:42:45 +02:00
|
|
|
if cs == nil {
|
2020-05-13 14:59:34 +02:00
|
|
|
panic("GetChangesForConfigResource on nil ChangesSync")
|
2020-05-02 02:42:45 +02:00
|
|
|
}
|
|
|
|
cs.lock.Lock()
|
|
|
|
defer cs.lock.Unlock()
|
|
|
|
var changes []*ResourceInstanceChangeSrc
|
2020-05-13 14:59:34 +02:00
|
|
|
for _, c := range cs.changes.InstancesForConfigResource(addr) {
|
2020-05-02 02:42:45 +02:00
|
|
|
changes = append(changes, c.DeepCopy())
|
|
|
|
}
|
|
|
|
return changes
|
|
|
|
}
|
|
|
|
|
2018-08-28 02:34:38 +02:00
|
|
|
// RemoveResourceInstanceChange searches the set of resource instance changes
|
|
|
|
// for one matching the given address and generation, and removes it from the
|
|
|
|
// set if it exists.
|
|
|
|
func (cs *ChangesSync) RemoveResourceInstanceChange(addr addrs.AbsResourceInstance, gen states.Generation) {
|
|
|
|
if cs == nil {
|
|
|
|
panic("RemoveResourceInstanceChange on nil ChangesSync")
|
|
|
|
}
|
|
|
|
cs.lock.Lock()
|
|
|
|
defer cs.lock.Unlock()
|
|
|
|
|
|
|
|
dk := states.NotDeposed
|
|
|
|
if realDK, ok := gen.(states.DeposedKey); ok {
|
|
|
|
dk = realDK
|
|
|
|
}
|
|
|
|
|
|
|
|
addrStr := addr.String()
|
|
|
|
for i, r := range cs.changes.Resources {
|
|
|
|
if r.Addr.String() != addrStr || r.DeposedKey != dk {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
copy(cs.changes.Resources[i:], cs.changes.Resources[i+1:])
|
|
|
|
cs.changes.Resources = cs.changes.Resources[:len(cs.changes.Resources)-1]
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2018-09-11 01:26:55 +02:00
|
|
|
|
|
|
|
// AppendOutputChange records the given output value change in the set of
|
|
|
|
// planned value changes.
|
|
|
|
//
|
|
|
|
// The caller must ensure that there are no concurrent writes to the given
|
|
|
|
// change while this method is running, but it is safe to resume mutating
|
|
|
|
// it after this method returns without affecting the saved change.
|
|
|
|
func (cs *ChangesSync) AppendOutputChange(changeSrc *OutputChangeSrc) {
|
|
|
|
if cs == nil {
|
|
|
|
panic("AppendOutputChange on nil ChangesSync")
|
|
|
|
}
|
|
|
|
cs.lock.Lock()
|
|
|
|
defer cs.lock.Unlock()
|
|
|
|
|
|
|
|
s := changeSrc.DeepCopy()
|
|
|
|
cs.changes.Outputs = append(cs.changes.Outputs, s)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetOutputChange searches the set of output value changes for one matching
|
|
|
|
// the given address, returning it if it exists.
|
|
|
|
//
|
|
|
|
// If no such change exists, nil is returned.
|
|
|
|
//
|
|
|
|
// The returned object is a deep copy of the change recorded in the plan, so
|
|
|
|
// callers may mutate it although it's generally better (less confusing) to
|
|
|
|
// treat planned changes as immutable after they've been initially constructed.
|
|
|
|
func (cs *ChangesSync) GetOutputChange(addr addrs.AbsOutputValue) *OutputChangeSrc {
|
|
|
|
if cs == nil {
|
|
|
|
panic("GetOutputChange on nil ChangesSync")
|
|
|
|
}
|
|
|
|
cs.lock.Lock()
|
|
|
|
defer cs.lock.Unlock()
|
|
|
|
|
|
|
|
return cs.changes.OutputValue(addr)
|
|
|
|
}
|
|
|
|
|
2020-04-12 17:29:21 +02:00
|
|
|
// GetOutputChanges searches the set of output changes for any that reside in
|
|
|
|
// module instances beneath the given module. If no changes exist, nil
|
|
|
|
// is returned.
|
|
|
|
//
|
|
|
|
// The returned objects are a deep copy of the change recorded in the plan, so
|
|
|
|
// callers may mutate them although it's generally better (less confusing) to
|
|
|
|
// treat planned changes as immutable after they've been initially constructed.
|
|
|
|
func (cs *ChangesSync) GetOutputChanges(parent addrs.ModuleInstance, module addrs.ModuleCall) []*OutputChangeSrc {
|
|
|
|
if cs == nil {
|
|
|
|
panic("GetOutputChange on nil ChangesSync")
|
|
|
|
}
|
|
|
|
cs.lock.Lock()
|
|
|
|
defer cs.lock.Unlock()
|
|
|
|
|
|
|
|
return cs.changes.OutputValues(parent, module)
|
|
|
|
}
|
|
|
|
|
2018-09-11 01:26:55 +02:00
|
|
|
// RemoveOutputChange searches the set of output value changes for one matching
|
|
|
|
// the given address, and removes it from the set if it exists.
|
|
|
|
func (cs *ChangesSync) RemoveOutputChange(addr addrs.AbsOutputValue) {
|
|
|
|
if cs == nil {
|
|
|
|
panic("RemoveOutputChange on nil ChangesSync")
|
|
|
|
}
|
|
|
|
cs.lock.Lock()
|
|
|
|
defer cs.lock.Unlock()
|
|
|
|
|
|
|
|
addrStr := addr.String()
|
2018-11-06 01:02:45 +01:00
|
|
|
for i, o := range cs.changes.Outputs {
|
|
|
|
if o.Addr.String() != addrStr {
|
2018-09-11 01:26:55 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
copy(cs.changes.Outputs[i:], cs.changes.Outputs[i+1:])
|
|
|
|
cs.changes.Outputs = cs.changes.Outputs[:len(cs.changes.Outputs)-1]
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|