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 }