istio.io/istio@v0.0.0-20240520182934-d79c90f27776/operator/pkg/util/merge_iop.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package util 16 17 import ( 18 "fmt" 19 "strings" 20 21 "google.golang.org/protobuf/types/known/durationpb" 22 wrappers "google.golang.org/protobuf/types/known/wrapperspb" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/apimachinery/pkg/util/strategicpatch" 25 yaml2 "sigs.k8s.io/yaml" 26 27 v1alpha13 "istio.io/api/mesh/v1alpha1" 28 "istio.io/api/networking/v1alpha3" 29 "istio.io/api/operator/v1alpha1" 30 v1alpha12 "istio.io/istio/operator/pkg/apis/istio/v1alpha1" 31 ) 32 33 // Partially mirrored from istio/api and operator/pkg/api (for values). 34 // Struct tags are required to use k8s strategic merge library. It would be possible 35 // to add these to source protos but because the values field is defined as 36 // map[string]interface{} here (and similar for MeshConfig in v1alpha1.Values) 37 // that alone would not be sufficient. 38 // Only non-scalar types require tags, therefore most fields are omitted here. 39 type iopMergeStructType struct { 40 metav1.ObjectMeta `json:"metadata" patchStrategy:"merge"` 41 Spec istioOperatorSpec `json:"spec" patchStrategy:"merge"` 42 } 43 44 type istioOperatorSpec struct { 45 MeshConfig *meshConfig `json:"meshConfig" patchStrategy:"merge"` 46 Components *istioComponentSetSpec `json:"components" patchStrategy:"merge"` 47 Values *values `json:"values" patchStrategy:"merge"` 48 } 49 50 type istioComponentSetSpec struct { 51 Base *baseComponentSpec `json:"base" patchStrategy:"merge"` 52 Pilot *componentSpec `json:"pilot" patchStrategy:"merge"` 53 Cni *componentSpec `json:"cni" patchStrategy:"merge"` 54 Ztunel *componentSpec `json:"ztunnel" patchStrategy:"merge"` 55 IstiodRemote *componentSpec `json:"istiodRemote" patchStrategy:"merge"` 56 IngressGateways []*gatewaySpec `json:"ingressGateways" patchStrategy:"merge" patchMergeKey:"name"` 57 EgressGateways []*gatewaySpec `json:"egressGateways" patchStrategy:"merge" patchMergeKey:"name"` 58 } 59 60 type baseComponentSpec struct { 61 K8S *v1alpha1.KubernetesResourcesSpec `json:"k8s" patchStrategy:"merge"` 62 } 63 64 type componentSpec struct { 65 K8S *v1alpha1.KubernetesResourcesSpec `json:"k8s" patchStrategy:"merge"` 66 } 67 68 type gatewaySpec struct { 69 Label map[string]string `json:"label" patchStrategy:"merge"` 70 K8S *v1alpha1.KubernetesResourcesSpec `json:"k8s" patchStrategy:"merge"` 71 } 72 73 type values struct { 74 Cni *v1alpha12.CNIConfig `json:"cni" patchStrategy:"merge"` 75 Gateways *gatewaysConfig `json:"gateways" patchStrategy:"merge"` 76 Global *v1alpha12.GlobalConfig `json:"global" patchStrategy:"merge"` 77 Pilot *v1alpha12.PilotConfig `json:"pilot" patchStrategy:"merge"` 78 Telemetry *telemetryConfig `json:"telemetry" patchStrategy:"merge"` 79 SidecarInjectorWebhook *v1alpha12.SidecarInjectorConfig `json:"sidecarInjectorWebhook" patchStrategy:"merge"` 80 IstioCni *v1alpha12.CNIConfig `json:"istio_cni" patchStrategy:"merge"` 81 MeshConfig *meshConfig `json:"meshConfig" patchStrategy:"merge"` 82 Base *v1alpha12.BaseConfig `json:"base" patchStrategy:"merge"` 83 IstiodRemote *v1alpha12.IstiodRemoteConfig `json:"istiodRemote" patchStrategy:"merge"` 84 Ztunnel map[string]any `json:"ztunnel" patchStrategy:"merge"` 85 } 86 87 type gatewaysConfig struct { 88 IstioEgressgateway *egressGatewayConfig `json:"istio-egressgateway" patchStrategy:"merge"` 89 IstioIngressgateway *ingressGatewayConfig `json:"istio-ingressgateway" patchStrategy:"merge"` 90 } 91 92 // Configuration for an ingress gateway. 93 type ingressGatewayConfig struct { 94 Env map[string]any `json:"env" patchStrategy:"merge"` 95 Labels map[string]string `json:"labels" patchStrategy:"merge"` 96 CPU *v1alpha12.TargetUtilizationConfig `json:"cpu" patchStrategy:"replace"` 97 Memory *v1alpha12.TargetUtilizationConfig `json:"memory" patchStrategy:"replace"` 98 LoadBalancerSourceRanges []string `json:"loadBalancerSourceRanges" patchStrategy:"replace"` 99 NodeSelector map[string]any `json:"nodeSelector" patchStrategy:"merge"` 100 PodAntiAffinityLabelSelector []map[string]any `json:"podAntiAffinityLabelSelector" patchStrategy:"replace"` 101 PodAntiAffinityTermLabelSelector []map[string]any `json:"podAntiAffinityTermLabelSelector" patchStrategy:"replace"` 102 PodAnnotations map[string]any `json:"podAnnotations" patchStrategy:"merge"` 103 MeshExpansionPorts []*v1alpha12.PortsConfig `json:"meshExpansionPorts" patchStrategy:"merge" patchMergeKey:"name"` 104 Ports []*v1alpha12.PortsConfig `json:"ports" patchStrategy:"merge" patchMergeKey:"name"` 105 Resources *resources `json:"resources" patchStrategy:"merge"` 106 SecretVolumes []*v1alpha12.SecretVolume `json:"secretVolumes" patchStrategy:"merge" patchMergeKey:"name"` 107 ServiceAnnotations map[string]any `json:"serviceAnnotations" patchStrategy:"merge"` 108 Tolerations []map[string]any `json:"tolerations" patchStrategy:"replace"` 109 IngressPorts []map[string]any `json:"ingressPorts" patchStrategy:"replace"` 110 AdditionalContainers []map[string]any `json:"additionalContainers" patchStrategy:"replace"` 111 ConfigVolumes []map[string]any `json:"configVolumes" patchStrategy:"replace"` 112 Zvpn *v1alpha12.IngressGatewayZvpnConfig `json:"zvpn" patchStrategy:"merge"` 113 } 114 115 type resources struct { 116 Limits map[string]string `json:"limits" patchStrategy:"merge"` 117 Requests map[string]string `json:"requests" patchStrategy:"merge"` 118 } 119 120 type egressGatewayConfig struct { 121 Env map[string]any `json:"env" patchStrategy:"merge"` 122 Labels map[string]string `json:"labels" patchStrategy:"merge"` 123 NodeSelector map[string]any `json:"nodeSelector" patchStrategy:"merge"` 124 PodAntiAffinityLabelSelector []map[string]any `json:"podAntiAffinityLabelSelector" patchStrategy:"replace"` 125 PodAntiAffinityTermLabelSelector []map[string]any `json:"podAntiAffinityTermLabelSelector" patchStrategy:"replace"` 126 PodAnnotations map[string]any `json:"podAnnotations" patchStrategy:"merge"` 127 Ports []*v1alpha12.PortsConfig `json:"ports" patchStrategy:"merge" patchMergeKey:"name"` 128 Resources *resources `json:"resources" patchStrategy:"merge"` 129 SecretVolumes []*v1alpha12.SecretVolume `json:"secretVolumes" patchStrategy:"merge" patchMergeKey:"name"` 130 Tolerations []map[string]any `json:"tolerations" patchStrategy:"replace"` 131 ConfigVolumes []map[string]any `json:"configVolumes" patchStrategy:"replace"` 132 AdditionalContainers []map[string]any `json:"additionalContainers" patchStrategy:"replace"` 133 Zvpn *v1alpha12.ZeroVPNConfig `json:"zvpn" patchStrategy:"replace"` 134 } 135 136 type meshConfig struct { 137 ConnectTimeout *durationpb.Duration `json:"connectTimeout" patchStrategy:"replace"` 138 ProtocolDetectionTimeout *durationpb.Duration `json:"protocolDetectionTimeout" patchStrategy:"replace"` 139 RdsRefreshDelay *durationpb.Duration `json:"rdsRefreshDelay" patchStrategy:"replace"` 140 EnableAutoMtls *wrappers.BoolValue `json:"enableAutoMtls" patchStrategy:"replace"` 141 EnablePrometheusMerge *wrappers.BoolValue `json:"enablePrometheusMerge" patchStrategy:"replace"` 142 OutboundTrafficPolicy *v1alpha13.MeshConfig_OutboundTrafficPolicy `json:"outboundTrafficPolicy" patchStrategy:"merge"` 143 InboundTrafficPolicy *v1alpha13.MeshConfig_InboundTrafficPolicy `json:"inboundTrafficPolicy" patchStrategy:"merge"` 144 TCPKeepalive *v1alpha3.ConnectionPoolSettings_TCPSettings_TcpKeepalive `json:"tcpKeepalive" patchStrategy:"merge"` 145 DefaultConfig *proxyConfig `json:"defaultConfig" patchStrategy:"merge"` 146 ConfigSources []*v1alpha13.ConfigSource `json:"configSources" patchStrategy:"merge" patchMergeKey:"address"` 147 TrustDomainAliases []string `json:"trustDomainAliases" patchStrategy:"merge"` 148 DefaultServiceExportTo []string `json:"defaultServiceExportTo" patchStrategy:"merge"` 149 DefaultVirtualServiceExportTo []string `json:"defaultVirtualServiceExportTo" patchStrategy:"merge"` 150 DefaultDestinationRuleExportTo []string `json:"defaultDestinationRuleExportTo" patchStrategy:"merge"` 151 LocalityLbSetting *v1alpha3.LocalityLoadBalancerSetting `json:"localityLbSetting" patchStrategy:"merge"` 152 DNSRefreshRate *durationpb.Duration `json:"dnsRefreshRate" patchStrategy:"replace"` 153 Certificates []*v1alpha13.Certificate `json:"certificates" patchStrategy:"merge" patchMergeKey:"secretName"` 154 ServiceSettings []*meshConfigServiceSettings `json:"serviceSettings" patchStrategy:"replace"` 155 DefaultProviders *meshConfigDefaultProviders `json:"defaultProviders" patchStrategy:"merge"` 156 ExtensionProviders []*meshConfigExtensionProvider `json:"extensionProviders" patchStrategy:"merge" patchMergeKey:"name"` 157 } 158 159 type ( 160 meshConfigDefaultProviders struct { 161 AccessLogging []struct{} `json:"accessLogging"` 162 Tracing []struct{} `json:"tracing"` 163 Metrics []struct{} `json:"metrics"` 164 } 165 meshConfigExtensionProvider struct { 166 Name string `json:"string"` 167 EnvoyOtelAls struct{} `json:"envoyOtelAls"` 168 Prometheus struct{} `json:"prometheus"` 169 EnvoyFileAccessLog struct{} `json:"envoyFileAccessLog"` 170 Stackdriver struct{} `json:"stackdriver"` 171 EnvoyExtAuthzHTTP struct{} `json:"envoyExtAuthzHttp"` 172 EnvoyExtAuthzGrpc struct{} `json:"envoyExtAuthzGrpc"` 173 Zipkin struct{} `json:"zipkin"` 174 Lightstep struct{} `json:"lightstep"` 175 Datadog struct{} `json:"datadog"` 176 Opencensus struct{} `json:"opencensus"` 177 Skywalking struct{} `json:"skywalking"` 178 EnvoyHTTPAls struct{} `json:"envoyHttpAls"` 179 EnvoyTCPAls struct{} `json:"envoyTcpAls"` 180 OpenTelemetry struct{} `json:"opentelemetry"` 181 } 182 clusterName struct { 183 ServiceCluster *v1alpha13.ProxyConfig_ServiceCluster `json:"serviceCluster,omitempty"` 184 TracingServiceName *v1alpha13.ProxyConfig_TracingServiceName_ `json:"tracingServiceName,omitempty"` 185 } 186 ) 187 188 type proxyConfig struct { 189 DrainDuration *durationpb.Duration `json:"drainDuration" patchStrategy:"replace"` 190 DiscoveryRefreshDelay *durationpb.Duration `json:"discoveryRefreshDelay" patchStrategy:"replace"` 191 TerminationDrainDuration *durationpb.Duration `json:"terminationDrainDuration" patchStrategy:"replace"` 192 Concurrency *wrappers.Int32Value `json:"concurrency" patchStrategy:"replace"` 193 ConfigSources []*v1alpha13.ConfigSource `json:"configSources" patchStrategy:"replace"` 194 ClusterName *clusterName `json:"clusterName" patchStrategy:"replace"` 195 TrustDomainAliases []string `json:"trustDomainAliases" patchStrategy:"replace"` 196 DefaultServiceExportTo []string `json:"defaultServiceExportTo" patchStrategy:"replace"` 197 DefaultVirtualServiceExportTo []string `json:"defaultVirtualServiceExportTo" patchStrategy:"replace"` 198 DefaultDestinationRuleExportTo []string `json:"defaultDestinationRuleExportTo" patchStrategy:"replace"` 199 LocalityLbSetting *v1alpha3.LocalityLoadBalancerSetting `json:"localityLbSetting" patchStrategy:"merge"` 200 DNSRefreshRate *durationpb.Duration `json:"dnsRefreshRate" patchStrategy:"replace"` 201 Certificates []*v1alpha13.Certificate `json:"certificates" patchStrategy:"replace"` 202 ServiceSettings []*v1alpha13.MeshConfig_ServiceSettings `json:"serviceSettings" patchStrategy:"replace"` 203 Tracing *tracing `json:"tracing" patchStrategy:"replace"` 204 Sds *v1alpha13.SDS `json:"sds" patchStrategy:"replace"` 205 EnvoyAccessLogService *v1alpha13.RemoteService `json:"envoyAccessLogService" patchStrategy:"merge" patchMergeKey:"address"` 206 EnvoyMetricsService *v1alpha13.RemoteService `json:"envoyMetricsService" patchStrategy:"merge" patchMergeKey:"address"` 207 ProxyMetadata map[string]string `json:"proxyMetadata" patchStrategy:"merge"` 208 ExtraStatTags []string `json:"extraStatTags" patchStrategy:"replace"` 209 GatewayTopology *v1alpha13.Topology `json:"gatewayTopology" patchStrategy:"replace"` 210 } 211 212 type tracing struct { 213 TlSSettings *v1alpha3.ClientTLSSettings `json:"tlsSettings" patchStrategy:"merge"` 214 } 215 216 type meshConfigServiceSettings struct { 217 Settings *v1alpha13.MeshConfig_ServiceSettings_Settings `json:"settings" patchStrategy:"merge"` 218 Hosts []string `json:"hosts" patchStrategy:"merge"` 219 } 220 221 type telemetryConfig struct { 222 V2 *telemetryV2Config `json:"v2" patchStrategy:"merge"` 223 } 224 225 type telemetryV2Config struct { 226 Prometheus *v1alpha12.TelemetryV2PrometheusConfig `json:"prometheus" patchStrategy:"merge"` 227 Stackdriver *v1alpha12.TelemetryV2StackDriverConfig `json:"stackdriver" patchStrategy:"merge"` 228 } 229 230 var iopMergeStruct iopMergeStructType 231 232 // OverlayIOP overlays over base using JSON strategic merge. 233 func OverlayIOP(base, overlay string) (string, error) { 234 if strings.TrimSpace(base) == "" { 235 return overlay, nil 236 } 237 if strings.TrimSpace(overlay) == "" { 238 return base, nil 239 } 240 bj, err := yaml2.YAMLToJSON([]byte(base)) 241 if err != nil { 242 return "", fmt.Errorf("yamlToJSON error in base: %s\n%s", err, bj) 243 } 244 oj, err := yaml2.YAMLToJSON([]byte(overlay)) 245 if err != nil { 246 return "", fmt.Errorf("yamlToJSON error in overlay: %s\n%s", err, oj) 247 } 248 if base == "" { 249 bj = []byte("{}") 250 } 251 if overlay == "" { 252 oj = []byte("{}") 253 } 254 255 merged, err := strategicpatch.StrategicMergePatch(bj, oj, &iopMergeStruct) 256 if err != nil { 257 return "", fmt.Errorf("json merge error (%s) for base object: \n%s\n override object: \n%s", err, bj, oj) 258 } 259 260 my, err := yaml2.JSONToYAML(merged) 261 if err != nil { 262 return "", fmt.Errorf("jsonToYAML error (%s) for merged object: \n%s", err, merged) 263 } 264 265 return string(my), nil 266 }