github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/plans/changes_sync.go (about)

     1  package plans
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/hashicorp/terraform/internal/addrs"
     8  	"github.com/hashicorp/terraform/internal/states"
     9  )
    10  
    11  // ChangesSync is a wrapper around a Changes that provides a concurrency-safe
    12  // interface to insert new changes and retrieve copies of existing changes.
    13  //
    14  // Each ChangesSync is independent of all others, so all concurrent writers
    15  // to a particular Changes must share a single ChangesSync. Behavior is
    16  // undefined if any other caller makes changes to the underlying Changes
    17  // object or its nested objects concurrently with any of the methods of a
    18  // particular ChangesSync.
    19  type ChangesSync struct {
    20  	lock    sync.Mutex
    21  	changes *Changes
    22  }
    23  
    24  // IsFullDestroy returns true if the set of changes indicates we are doing a
    25  // destroy of all resources.
    26  func (cs *ChangesSync) IsFullDestroy() bool {
    27  	if cs == nil {
    28  		panic("FullDestroy on nil ChangesSync")
    29  	}
    30  	cs.lock.Lock()
    31  	defer cs.lock.Unlock()
    32  
    33  	for _, c := range cs.changes.Resources {
    34  		if c.Action != Delete {
    35  			return false
    36  		}
    37  	}
    38  
    39  	return true
    40  }
    41  
    42  // AppendResourceInstanceChange records the given resource instance change in
    43  // the set of planned resource changes.
    44  //
    45  // The caller must ensure that there are no concurrent writes to the given
    46  // change while this method is running, but it is safe to resume mutating
    47  // it after this method returns without affecting the saved change.
    48  func (cs *ChangesSync) AppendResourceInstanceChange(changeSrc *ResourceInstanceChangeSrc) {
    49  	if cs == nil {
    50  		panic("AppendResourceInstanceChange on nil ChangesSync")
    51  	}
    52  	cs.lock.Lock()
    53  	defer cs.lock.Unlock()
    54  
    55  	s := changeSrc.DeepCopy()
    56  	cs.changes.Resources = append(cs.changes.Resources, s)
    57  }
    58  
    59  // GetResourceInstanceChange searches the set of resource instance changes for
    60  // one matching the given address and generation, returning it if it exists.
    61  //
    62  // If no such change exists, nil is returned.
    63  //
    64  // The returned object is a deep copy of the change recorded in the plan, so
    65  // callers may mutate it although it's generally better (less confusing) to
    66  // treat planned changes as immutable after they've been initially constructed.
    67  func (cs *ChangesSync) GetResourceInstanceChange(addr addrs.AbsResourceInstance, gen states.Generation) *ResourceInstanceChangeSrc {
    68  	if cs == nil {
    69  		panic("GetResourceInstanceChange on nil ChangesSync")
    70  	}
    71  	cs.lock.Lock()
    72  	defer cs.lock.Unlock()
    73  
    74  	if gen == states.CurrentGen {
    75  		return cs.changes.ResourceInstance(addr).DeepCopy()
    76  	}
    77  	if dk, ok := gen.(states.DeposedKey); ok {
    78  		return cs.changes.ResourceInstanceDeposed(addr, dk).DeepCopy()
    79  	}
    80  	panic(fmt.Sprintf("unsupported generation value %#v", gen))
    81  }
    82  
    83  // GetChangesForConfigResource searches the set of resource instance
    84  // changes and returns all changes related to a given configuration address.
    85  // This is be used to find possible changes related to a configuration
    86  // reference.
    87  //
    88  // If no such changes exist, nil is returned.
    89  //
    90  // The returned objects are a deep copy of the change recorded in the plan, so
    91  // callers may mutate them although it's generally better (less confusing) to
    92  // treat planned changes as immutable after they've been initially constructed.
    93  func (cs *ChangesSync) GetChangesForConfigResource(addr addrs.ConfigResource) []*ResourceInstanceChangeSrc {
    94  	if cs == nil {
    95  		panic("GetChangesForConfigResource on nil ChangesSync")
    96  	}
    97  	cs.lock.Lock()
    98  	defer cs.lock.Unlock()
    99  	var changes []*ResourceInstanceChangeSrc
   100  	for _, c := range cs.changes.InstancesForConfigResource(addr) {
   101  		changes = append(changes, c.DeepCopy())
   102  	}
   103  	return changes
   104  }
   105  
   106  // GetChangesForAbsResource searches the set of resource instance
   107  // changes and returns all changes related to a given configuration address.
   108  //
   109  // If no such changes exist, nil is returned.
   110  //
   111  // The returned objects are a deep copy of the change recorded in the plan, so
   112  // callers may mutate them although it's generally better (less confusing) to
   113  // treat planned changes as immutable after they've been initially constructed.
   114  func (cs *ChangesSync) GetChangesForAbsResource(addr addrs.AbsResource) []*ResourceInstanceChangeSrc {
   115  	if cs == nil {
   116  		panic("GetChangesForAbsResource on nil ChangesSync")
   117  	}
   118  	cs.lock.Lock()
   119  	defer cs.lock.Unlock()
   120  	var changes []*ResourceInstanceChangeSrc
   121  	for _, c := range cs.changes.InstancesForAbsResource(addr) {
   122  		changes = append(changes, c.DeepCopy())
   123  	}
   124  	return changes
   125  }
   126  
   127  // RemoveResourceInstanceChange searches the set of resource instance changes
   128  // for one matching the given address and generation, and removes it from the
   129  // set if it exists.
   130  func (cs *ChangesSync) RemoveResourceInstanceChange(addr addrs.AbsResourceInstance, gen states.Generation) {
   131  	if cs == nil {
   132  		panic("RemoveResourceInstanceChange on nil ChangesSync")
   133  	}
   134  	cs.lock.Lock()
   135  	defer cs.lock.Unlock()
   136  
   137  	dk := states.NotDeposed
   138  	if realDK, ok := gen.(states.DeposedKey); ok {
   139  		dk = realDK
   140  	}
   141  
   142  	addrStr := addr.String()
   143  	for i, r := range cs.changes.Resources {
   144  		if r.Addr.String() != addrStr || r.DeposedKey != dk {
   145  			continue
   146  		}
   147  		copy(cs.changes.Resources[i:], cs.changes.Resources[i+1:])
   148  		cs.changes.Resources = cs.changes.Resources[:len(cs.changes.Resources)-1]
   149  		return
   150  	}
   151  }
   152  
   153  // AppendOutputChange records the given output value change in the set of
   154  // planned value changes.
   155  //
   156  // The caller must ensure that there are no concurrent writes to the given
   157  // change while this method is running, but it is safe to resume mutating
   158  // it after this method returns without affecting the saved change.
   159  func (cs *ChangesSync) AppendOutputChange(changeSrc *OutputChangeSrc) {
   160  	if cs == nil {
   161  		panic("AppendOutputChange on nil ChangesSync")
   162  	}
   163  	cs.lock.Lock()
   164  	defer cs.lock.Unlock()
   165  
   166  	s := changeSrc.DeepCopy()
   167  	cs.changes.Outputs = append(cs.changes.Outputs, s)
   168  }
   169  
   170  // GetOutputChange searches the set of output value changes for one matching
   171  // the given address, returning it if it exists.
   172  //
   173  // If no such change exists, nil is returned.
   174  //
   175  // The returned object is a deep copy of the change recorded in the plan, so
   176  // callers may mutate it although it's generally better (less confusing) to
   177  // treat planned changes as immutable after they've been initially constructed.
   178  func (cs *ChangesSync) GetOutputChange(addr addrs.AbsOutputValue) *OutputChangeSrc {
   179  	if cs == nil {
   180  		panic("GetOutputChange on nil ChangesSync")
   181  	}
   182  	cs.lock.Lock()
   183  	defer cs.lock.Unlock()
   184  
   185  	return cs.changes.OutputValue(addr)
   186  }
   187  
   188  // GetRootOutputChanges searches the set of output changes for any that reside
   189  // the root module. If no such changes exist, nil is returned.
   190  //
   191  // The returned objects are a deep copy of the change recorded in the plan, so
   192  // callers may mutate them although it's generally better (less confusing) to
   193  // treat planned changes as immutable after they've been initially constructed.
   194  func (cs *ChangesSync) GetRootOutputChanges() []*OutputChangeSrc {
   195  	if cs == nil {
   196  		panic("GetRootOutputChanges on nil ChangesSync")
   197  	}
   198  	cs.lock.Lock()
   199  	defer cs.lock.Unlock()
   200  
   201  	return cs.changes.RootOutputValues()
   202  }
   203  
   204  // GetOutputChanges searches the set of output changes for any that reside in
   205  // module instances beneath the given module. If no changes exist, nil
   206  // is returned.
   207  //
   208  // The returned objects are a deep copy of the change recorded in the plan, so
   209  // callers may mutate them although it's generally better (less confusing) to
   210  // treat planned changes as immutable after they've been initially constructed.
   211  func (cs *ChangesSync) GetOutputChanges(parent addrs.ModuleInstance, module addrs.ModuleCall) []*OutputChangeSrc {
   212  	if cs == nil {
   213  		panic("GetOutputChange on nil ChangesSync")
   214  	}
   215  	cs.lock.Lock()
   216  	defer cs.lock.Unlock()
   217  
   218  	return cs.changes.OutputValues(parent, module)
   219  }
   220  
   221  // RemoveOutputChange searches the set of output value changes for one matching
   222  // the given address, and removes it from the set if it exists.
   223  func (cs *ChangesSync) RemoveOutputChange(addr addrs.AbsOutputValue) {
   224  	if cs == nil {
   225  		panic("RemoveOutputChange on nil ChangesSync")
   226  	}
   227  	cs.lock.Lock()
   228  	defer cs.lock.Unlock()
   229  
   230  	addrStr := addr.String()
   231  
   232  	for i, o := range cs.changes.Outputs {
   233  		if o.Addr.String() != addrStr {
   234  			continue
   235  		}
   236  		copy(cs.changes.Outputs[i:], cs.changes.Outputs[i+1:])
   237  		cs.changes.Outputs = cs.changes.Outputs[:len(cs.changes.Outputs)-1]
   238  		return
   239  	}
   240  }