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  }