mirror of
https://github.com/hashicorp/terraform.git
synced 2026-02-03 20:50:59 -05:00
290 lines
9.7 KiB
Go
290 lines
9.7 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package plans
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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(change *ResourceInstanceChange) {
|
|
if cs == nil {
|
|
panic("AppendResourceInstanceChange on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
|
|
s := change.DeepCopy()
|
|
cs.changes.Resources = append(cs.changes.Resources, s)
|
|
}
|
|
|
|
func (cs *ChangesSync) AppendQueryInstance(query *QueryInstance) {
|
|
if cs == nil {
|
|
panic("AppendQueryInstance on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
|
|
s := query.DeepCopy() // TODO do we need to deep copy here?
|
|
cs.changes.Queries = append(cs.changes.Queries, s)
|
|
}
|
|
|
|
// GetResourceInstanceChange searches the set of resource instance changes for
|
|
// one matching the given address and deposed key, returning it if it exists.
|
|
// Use [addrs.NotDeposed] as the deposed key to represent the "current"
|
|
// object for the given resource instance.
|
|
//
|
|
// 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, dk addrs.DeposedKey) *ResourceInstanceChange {
|
|
if cs == nil {
|
|
panic("GetResourceInstanceChange on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
|
|
if dk == addrs.NotDeposed {
|
|
return cs.changes.ResourceInstance(addr).DeepCopy()
|
|
}
|
|
return cs.changes.ResourceInstanceDeposed(addr, dk).DeepCopy()
|
|
}
|
|
|
|
// GetChangesForConfigResource searches the set of resource instance
|
|
// 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.
|
|
func (cs *ChangesSync) GetChangesForConfigResource(addr addrs.ConfigResource) []*ResourceInstanceChange {
|
|
if cs == nil {
|
|
panic("GetChangesForConfigResource on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
var changes []*ResourceInstanceChange
|
|
for _, c := range cs.changes.InstancesForConfigResource(addr) {
|
|
changes = append(changes, c.DeepCopy())
|
|
}
|
|
return changes
|
|
}
|
|
|
|
// GetChangesForAbsResource searches the set of resource instance
|
|
// changes and returns all changes related to a given configuration address.
|
|
//
|
|
// 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.
|
|
func (cs *ChangesSync) GetChangesForAbsResource(addr addrs.AbsResource) []*ResourceInstanceChange {
|
|
if cs == nil {
|
|
panic("GetChangesForAbsResource on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
var changes []*ResourceInstanceChange
|
|
for _, c := range cs.changes.InstancesForAbsResource(addr) {
|
|
changes = append(changes, c.DeepCopy())
|
|
}
|
|
return changes
|
|
}
|
|
|
|
func (cs *ChangesSync) GetQueryInstancesForAbsResource(addr addrs.AbsResource) []*QueryInstance {
|
|
if cs == nil {
|
|
panic("GetQueryInstancesForAbsResource on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
var queries []*QueryInstance
|
|
for _, q := range cs.changes.QueriesForAbsResource(addr) {
|
|
queries = append(queries, q.DeepCopy())
|
|
}
|
|
return queries
|
|
}
|
|
|
|
// RemoveResourceInstanceChange searches the set of resource instance changes
|
|
// for one matching the given address and deposed key, and removes it from the
|
|
// set if it exists.
|
|
func (cs *ChangesSync) RemoveResourceInstanceChange(addr addrs.AbsResourceInstance, dk addrs.DeposedKey) {
|
|
if cs == nil {
|
|
panic("RemoveResourceInstanceChange on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
// 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 *OutputChange) {
|
|
if cs == nil {
|
|
panic("AppendOutputChange on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
|
|
cs.changes.Outputs = append(cs.changes.Outputs, changeSrc)
|
|
}
|
|
|
|
// 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) *OutputChange {
|
|
if cs == nil {
|
|
panic("GetOutputChange on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
|
|
return cs.changes.OutputValue(addr)
|
|
}
|
|
|
|
// GetRootOutputChanges searches the set of output changes for any that reside
|
|
// the root module. 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.
|
|
func (cs *ChangesSync) GetRootOutputChanges() []*OutputChange {
|
|
if cs == nil {
|
|
panic("GetRootOutputChanges on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
|
|
return cs.changes.RootOutputValues()
|
|
}
|
|
|
|
// 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) []*OutputChange {
|
|
if cs == nil {
|
|
panic("GetOutputChange on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
|
|
return cs.changes.OutputValues(parent, module)
|
|
}
|
|
|
|
// 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()
|
|
|
|
for i, o := range cs.changes.Outputs {
|
|
if o.Addr.String() != addrStr {
|
|
continue
|
|
}
|
|
copy(cs.changes.Outputs[i:], cs.changes.Outputs[i+1:])
|
|
cs.changes.Outputs = cs.changes.Outputs[:len(cs.changes.Outputs)-1]
|
|
return
|
|
}
|
|
}
|
|
|
|
// GetActionInvocation gets an action invocation based on the action address, the triggering
|
|
// resource address, the action trigger block index, and the action list index.
|
|
func (cs *ChangesSync) GetActionInvocation(addr addrs.AbsActionInstance, actionTrigger ActionTrigger) *ActionInvocationInstance {
|
|
if cs == nil {
|
|
panic("GetActionInvocation on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
|
|
for _, a := range cs.changes.ActionInvocations {
|
|
if a.Addr.Equal(addr) {
|
|
if a.ActionTrigger.Equals(actionTrigger) {
|
|
return a
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AppendActionInvocation
|
|
func (cs *ChangesSync) AppendActionInvocation(action *ActionInvocationInstance) {
|
|
if cs == nil {
|
|
panic("AppendActionInvocation on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
|
|
cs.changes.ActionInvocations = append(cs.changes.ActionInvocations, action)
|
|
}
|
|
|
|
// RemoveActionInvocation searches the set of action invocations for one
|
|
// matching the given address, and removes it from the set if it exists.
|
|
func (cs *ChangesSync) RemoveActionInvocation(addr addrs.AbsActionInstance) {
|
|
if cs == nil {
|
|
panic("RemoveActionInvocation on nil ChangesSync")
|
|
}
|
|
cs.lock.Lock()
|
|
defer cs.lock.Unlock()
|
|
|
|
addrStr := addr.String()
|
|
for i, a := range cs.changes.ActionInvocations {
|
|
if a.Addr.String() != addrStr {
|
|
continue
|
|
}
|
|
copy(cs.changes.ActionInvocations[i:], cs.changes.ActionInvocations[i+1:])
|
|
cs.changes.ActionInvocations = cs.changes.ActionInvocations[:len(cs.changes.ActionInvocations)-1]
|
|
return
|
|
}
|
|
}
|