github.com/opentofu/opentofu@v1.7.1/internal/states/state_deepcopy.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 states
     7  
     8  import (
     9  	"github.com/opentofu/opentofu/internal/addrs"
    10  	"github.com/zclconf/go-cty/cty"
    11  )
    12  
    13  // Taking deep copies of states is an important operation because state is
    14  // otherwise a mutable data structure that is challenging to share across
    15  // many separate callers. It is important that the DeepCopy implementations
    16  // in this file comprehensively copy all parts of the state data structure
    17  // that could be mutated via pointers.
    18  
    19  // DeepCopy returns a new state that contains equivalent data to the reciever
    20  // but shares no backing memory in common.
    21  //
    22  // As with all methods on State, this method is not safe to use concurrently
    23  // with writing to any portion of the recieving data structure. It is the
    24  // caller's responsibility to ensure mutual exclusion for the duration of the
    25  // operation, but may then freely modify the receiver and the returned copy
    26  // independently once this method returns.
    27  func (s *State) DeepCopy() *State {
    28  	if s == nil {
    29  		return nil
    30  	}
    31  
    32  	modules := make(map[string]*Module, len(s.Modules))
    33  	for k, m := range s.Modules {
    34  		modules[k] = m.DeepCopy()
    35  	}
    36  	return &State{
    37  		Modules:      modules,
    38  		CheckResults: s.CheckResults.DeepCopy(),
    39  	}
    40  }
    41  
    42  // DeepCopy returns a new module state that contains equivalent data to the
    43  // receiver but shares no backing memory in common.
    44  //
    45  // As with all methods on Module, this method is not safe to use concurrently
    46  // with writing to any portion of the recieving data structure. It is the
    47  // caller's responsibility to ensure mutual exclusion for the duration of the
    48  // operation, but may then freely modify the receiver and the returned copy
    49  // independently once this method returns.
    50  func (ms *Module) DeepCopy() *Module {
    51  	if ms == nil {
    52  		return nil
    53  	}
    54  
    55  	resources := make(map[string]*Resource, len(ms.Resources))
    56  	for k, r := range ms.Resources {
    57  		resources[k] = r.DeepCopy()
    58  	}
    59  	outputValues := make(map[string]*OutputValue, len(ms.OutputValues))
    60  	for k, v := range ms.OutputValues {
    61  		outputValues[k] = v.DeepCopy()
    62  	}
    63  	localValues := make(map[string]cty.Value, len(ms.LocalValues))
    64  	for k, v := range ms.LocalValues {
    65  		// cty.Value is immutable, so we don't need to copy these.
    66  		localValues[k] = v
    67  	}
    68  
    69  	return &Module{
    70  		Addr:         ms.Addr, // technically mutable, but immutable by convention
    71  		Resources:    resources,
    72  		OutputValues: outputValues,
    73  		LocalValues:  localValues,
    74  	}
    75  }
    76  
    77  // DeepCopy returns a new resource state that contains equivalent data to the
    78  // receiver but shares no backing memory in common.
    79  //
    80  // As with all methods on Resource, this method is not safe to use concurrently
    81  // with writing to any portion of the recieving data structure. It is the
    82  // caller's responsibility to ensure mutual exclusion for the duration of the
    83  // operation, but may then freely modify the receiver and the returned copy
    84  // independently once this method returns.
    85  func (rs *Resource) DeepCopy() *Resource {
    86  	if rs == nil {
    87  		return nil
    88  	}
    89  
    90  	instances := make(map[addrs.InstanceKey]*ResourceInstance, len(rs.Instances))
    91  	for k, i := range rs.Instances {
    92  		instances[k] = i.DeepCopy()
    93  	}
    94  
    95  	return &Resource{
    96  		Addr:           rs.Addr,
    97  		Instances:      instances,
    98  		ProviderConfig: rs.ProviderConfig, // technically mutable, but immutable by convention
    99  	}
   100  }
   101  
   102  // DeepCopy returns a new resource instance state that contains equivalent data
   103  // to the receiver but shares no backing memory in common.
   104  //
   105  // As with all methods on ResourceInstance, this method is not safe to use
   106  // concurrently with writing to any portion of the recieving data structure. It
   107  // is the caller's responsibility to ensure mutual exclusion for the duration
   108  // of the operation, but may then freely modify the receiver and the returned
   109  // copy independently once this method returns.
   110  func (i *ResourceInstance) DeepCopy() *ResourceInstance {
   111  	if i == nil {
   112  		return nil
   113  	}
   114  
   115  	deposed := make(map[DeposedKey]*ResourceInstanceObjectSrc, len(i.Deposed))
   116  	for k, obj := range i.Deposed {
   117  		deposed[k] = obj.DeepCopy()
   118  	}
   119  
   120  	return &ResourceInstance{
   121  		Current: i.Current.DeepCopy(),
   122  		Deposed: deposed,
   123  	}
   124  }
   125  
   126  // DeepCopy returns a new resource instance object that contains equivalent data
   127  // to the receiver but shares no backing memory in common.
   128  //
   129  // As with all methods on ResourceInstanceObjectSrc, this method is not safe to
   130  // use concurrently with writing to any portion of the recieving data structure.
   131  // It is the caller's responsibility to ensure mutual exclusion for the duration
   132  // of the operation, but may then freely modify the receiver and the returned
   133  // copy independently once this method returns.
   134  func (os *ResourceInstanceObjectSrc) DeepCopy() *ResourceInstanceObjectSrc {
   135  	if os == nil {
   136  		return nil
   137  	}
   138  
   139  	var attrsFlat map[string]string
   140  	if os.AttrsFlat != nil {
   141  		attrsFlat = make(map[string]string, len(os.AttrsFlat))
   142  		for k, v := range os.AttrsFlat {
   143  			attrsFlat[k] = v
   144  		}
   145  	}
   146  
   147  	var attrsJSON []byte
   148  	if os.AttrsJSON != nil {
   149  		attrsJSON = make([]byte, len(os.AttrsJSON))
   150  		copy(attrsJSON, os.AttrsJSON)
   151  	}
   152  
   153  	var attrPaths []cty.PathValueMarks
   154  	if os.AttrSensitivePaths != nil {
   155  		attrPaths = make([]cty.PathValueMarks, len(os.AttrSensitivePaths))
   156  		copy(attrPaths, os.AttrSensitivePaths)
   157  	}
   158  
   159  	var private []byte
   160  	if os.Private != nil {
   161  		private = make([]byte, len(os.Private))
   162  		copy(private, os.Private)
   163  	}
   164  
   165  	// Some addrs.Referencable implementations are technically mutable, but
   166  	// we treat them as immutable by convention and so we don't deep-copy here.
   167  	var dependencies []addrs.ConfigResource
   168  	if os.Dependencies != nil {
   169  		dependencies = make([]addrs.ConfigResource, len(os.Dependencies))
   170  		copy(dependencies, os.Dependencies)
   171  	}
   172  
   173  	return &ResourceInstanceObjectSrc{
   174  		Status:              os.Status,
   175  		SchemaVersion:       os.SchemaVersion,
   176  		Private:             private,
   177  		AttrsFlat:           attrsFlat,
   178  		AttrsJSON:           attrsJSON,
   179  		AttrSensitivePaths:  attrPaths,
   180  		Dependencies:        dependencies,
   181  		CreateBeforeDestroy: os.CreateBeforeDestroy,
   182  	}
   183  }
   184  
   185  // DeepCopy returns a new resource instance object that contains equivalent data
   186  // to the receiver but shares no backing memory in common.
   187  //
   188  // As with all methods on ResourceInstanceObject, this method is not safe to use
   189  // concurrently with writing to any portion of the recieving data structure. It
   190  // is the caller's responsibility to ensure mutual exclusion for the duration
   191  // of the operation, but may then freely modify the receiver and the returned
   192  // copy independently once this method returns.
   193  func (o *ResourceInstanceObject) DeepCopy() *ResourceInstanceObject {
   194  	if o == nil {
   195  		return nil
   196  	}
   197  
   198  	var private []byte
   199  	if o.Private != nil {
   200  		private = make([]byte, len(o.Private))
   201  		copy(private, o.Private)
   202  	}
   203  
   204  	// Some addrs.Referenceable implementations are technically mutable, but
   205  	// we treat them as immutable by convention and so we don't deep-copy here.
   206  	var dependencies []addrs.ConfigResource
   207  	if o.Dependencies != nil {
   208  		dependencies = make([]addrs.ConfigResource, len(o.Dependencies))
   209  		copy(dependencies, o.Dependencies)
   210  	}
   211  
   212  	return &ResourceInstanceObject{
   213  		Value:               o.Value,
   214  		Status:              o.Status,
   215  		Private:             private,
   216  		Dependencies:        dependencies,
   217  		CreateBeforeDestroy: o.CreateBeforeDestroy,
   218  	}
   219  }
   220  
   221  // DeepCopy returns a new output value state that contains equivalent data
   222  // to the receiver but shares no backing memory in common.
   223  //
   224  // As with all methods on OutputValue, this method is not safe to use
   225  // concurrently with writing to any portion of the recieving data structure. It
   226  // is the caller's responsibility to ensure mutual exclusion for the duration
   227  // of the operation, but may then freely modify the receiver and the returned
   228  // copy independently once this method returns.
   229  func (os *OutputValue) DeepCopy() *OutputValue {
   230  	if os == nil {
   231  		return nil
   232  	}
   233  
   234  	return &OutputValue{
   235  		Addr:      os.Addr,
   236  		Value:     os.Value,
   237  		Sensitive: os.Sensitive,
   238  	}
   239  }