github.com/opentofu/opentofu@v1.7.1/internal/encryption/config/config_merge.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 config
     7  
     8  import (
     9  	"github.com/hashicorp/hcl/v2"
    10  	"github.com/opentofu/opentofu/internal/configs/hcl2shim"
    11  )
    12  
    13  // MergeConfigs merges two Configs together, with the override taking precedence.
    14  func MergeConfigs(cfg *EncryptionConfig, override *EncryptionConfig) *EncryptionConfig {
    15  	if cfg == nil {
    16  		return override
    17  	}
    18  	if override == nil {
    19  		return cfg
    20  	}
    21  	return &EncryptionConfig{
    22  		KeyProviderConfigs: mergeKeyProviderConfigs(cfg.KeyProviderConfigs, override.KeyProviderConfigs),
    23  		MethodConfigs:      mergeMethodConfigs(cfg.MethodConfigs, override.MethodConfigs),
    24  
    25  		State:  mergeEnforcableTargetConfigs(cfg.State, override.State),
    26  		Plan:   mergeEnforcableTargetConfigs(cfg.Plan, override.Plan),
    27  		Remote: mergeRemoteConfigs(cfg.Remote, override.Remote),
    28  	}
    29  }
    30  
    31  func mergeMethodConfigs(configs []MethodConfig, overrides []MethodConfig) []MethodConfig {
    32  	// Initialize a copy of configs to preserve the original entries.
    33  	merged := make([]MethodConfig, len(configs))
    34  	copy(merged, configs)
    35  
    36  	for _, override := range overrides {
    37  		wasOverridden := false
    38  
    39  		// Attempt to find a match based on type/name
    40  		for i, method := range merged {
    41  			if method.Type == override.Type && method.Name == override.Name {
    42  				// Override the existing method.
    43  				merged[i].Body = mergeBody(method.Body, override.Body)
    44  				wasOverridden = true
    45  				break
    46  			}
    47  		}
    48  
    49  		// If no existing method was overridden, append the new override.
    50  		if !wasOverridden {
    51  			merged = append(merged, override)
    52  		}
    53  	}
    54  	return merged
    55  }
    56  
    57  func mergeKeyProviderConfigs(configs []KeyProviderConfig, overrides []KeyProviderConfig) []KeyProviderConfig {
    58  	// Initialize a copy of configs to preserve the original entries.
    59  	merged := make([]KeyProviderConfig, len(configs))
    60  	copy(merged, configs)
    61  
    62  	for _, override := range overrides {
    63  		wasOverridden := false
    64  
    65  		// Attempt to find a match based on type/name
    66  		for i, keyProvider := range merged {
    67  			if keyProvider.Type == override.Type && keyProvider.Name == override.Name {
    68  				// Override the existing key provider.
    69  				merged[i].Body = mergeBody(keyProvider.Body, override.Body)
    70  				wasOverridden = true
    71  				break
    72  			}
    73  		}
    74  
    75  		// If no existing key provider was overridden, append the new override.
    76  		if !wasOverridden {
    77  			merged = append(merged, override)
    78  		}
    79  	}
    80  	return merged
    81  }
    82  
    83  func mergeTargetConfigs(cfg *TargetConfig, override *TargetConfig) *TargetConfig {
    84  	if cfg == nil {
    85  		return override
    86  	}
    87  	if override == nil {
    88  		return cfg
    89  	}
    90  
    91  	merged := &TargetConfig{}
    92  
    93  	if override.Method != nil {
    94  		merged.Method = override.Method
    95  	} else {
    96  		merged.Method = cfg.Method
    97  	}
    98  
    99  	if override.Fallback != nil {
   100  		merged.Fallback = override.Fallback
   101  	} else {
   102  		merged.Fallback = cfg.Fallback
   103  	}
   104  
   105  	return merged
   106  }
   107  
   108  func mergeEnforcableTargetConfigs(cfg *EnforcableTargetConfig, override *EnforcableTargetConfig) *EnforcableTargetConfig {
   109  	if cfg == nil {
   110  		return override
   111  	}
   112  	if override == nil {
   113  		return cfg
   114  	}
   115  
   116  	mergeTarget := mergeTargetConfigs(cfg.AsTargetConfig(), override.AsTargetConfig())
   117  	return &EnforcableTargetConfig{
   118  		Enforced: cfg.Enforced || override.Enforced,
   119  		Method:   mergeTarget.Method,
   120  		Fallback: mergeTarget.Fallback,
   121  	}
   122  }
   123  
   124  func mergeRemoteConfigs(cfg *RemoteConfig, override *RemoteConfig) *RemoteConfig {
   125  	if cfg == nil {
   126  		return override
   127  	}
   128  	if override == nil {
   129  		return cfg
   130  	}
   131  
   132  	merged := &RemoteConfig{
   133  		Default: mergeTargetConfigs(cfg.Default, override.Default),
   134  		Targets: make([]NamedTargetConfig, len(cfg.Targets)),
   135  	}
   136  
   137  	copy(merged.Targets, cfg.Targets)
   138  	for _, overrideTarget := range override.Targets {
   139  		found := false
   140  		for i, t := range merged.Targets {
   141  			found = t.Name == overrideTarget.Name
   142  			if found {
   143  				// gohcl does not support struct embedding
   144  				mergeTarget := mergeTargetConfigs(t.AsTargetConfig(), overrideTarget.AsTargetConfig())
   145  				merged.Targets[i] = NamedTargetConfig{
   146  					Name:     t.Name,
   147  					Method:   mergeTarget.Method,
   148  					Fallback: mergeTarget.Fallback,
   149  				}
   150  				break
   151  			}
   152  		}
   153  		if !found {
   154  			merged.Targets = append(merged.Targets, overrideTarget)
   155  		}
   156  	}
   157  
   158  	return merged
   159  }
   160  
   161  func mergeBody(base hcl.Body, override hcl.Body) hcl.Body {
   162  	if base == nil {
   163  		return override
   164  	}
   165  
   166  	if override == nil {
   167  		return base
   168  	}
   169  
   170  	return hcl2shim.MergeBodies(base, override)
   171  }