func()

in operator/pkg/translate/translate.go [188:300]


func (t *Translator) OverlayK8sSettings(yml string, iop *v1alpha1.IstioOperatorSpec, componentName name.ComponentName,
	resourceName string, index int) (string, error,
) {
	// om is a map of kind:name string to Object ptr.
	// This is lazy loaded to avoid parsing when there are no overlays
	var om map[string]*object.K8sObject
	var objects object.K8sObjects

	for inPath, v := range t.KubernetesMapping {
		inPath, err := renderFeatureComponentPathTemplate(inPath, componentName)
		if err != nil {
			return "", err
		}
		renderedInPath := strings.Replace(inPath, "gressGateways.", "gressGateways."+fmt.Sprint(index)+".", 1)
		scope.Debugf("Checking for path %s in IstioOperatorSpec", renderedInPath)

		m, found, err := tpath.GetFromStructPath(iop, renderedInPath)
		if err != nil {
			return "", err
		}
		if !found {
			scope.Debugf("path %s not found in IstioOperatorSpec, skip mapping.", renderedInPath)
			continue
		}
		if mstr, ok := m.(string); ok && mstr == "" {
			scope.Debugf("path %s is empty string, skip mapping.", renderedInPath)
			continue
		}
		// Zero int values are due to proto3 compiling to scalars rather than ptrs. Skip these because values of 0 are
		// the default in destination fields and need not be set explicitly.
		if mint, ok := util.ToIntValue(m); ok && mint == 0 {
			scope.Debugf("path %s is int 0, skip mapping.", renderedInPath)
			continue
		}
		if componentName == name.IstioBaseComponentName {
			return "", fmt.Errorf("base component can only have k8s.overlays, not other K8s settings")
		}
		inPathParts := strings.Split(inPath, ".")
		outPath, err := t.renderResourceComponentPathTemplate(v.OutPath, componentName, resourceName, iop.Revision)
		if err != nil {
			return "", err
		}
		scope.Debugf("path has value in IstioOperatorSpec, mapping to output path %s", outPath)
		path := util.PathFromString(outPath)
		pe := path[0]
		// Output path must start with [kind:name], which is used to map to the object to overlay.
		if !util.IsKVPathElement(pe) {
			return "", fmt.Errorf("path %s has an unexpected first element %s in OverlayK8sSettings", path, pe)
		}

		// We need to apply overlay, lazy load om
		if om == nil {
			objects, err = object.ParseK8sObjectsFromYAMLManifest(yml)
			if err != nil {
				return "", err
			}
			if scope.DebugEnabled() {
				scope.Debugf("Manifest contains the following objects:")
				for _, o := range objects {
					scope.Debugf("%s", o.HashNameKind())
				}
			}
			om = objects.ToNameKindMap()
		}

		// After brackets are removed, the remaining "kind:name" is the same format as the keys in om.
		pe, _ = util.RemoveBrackets(pe)
		oo, ok := om[pe]
		if !ok {
			// skip to overlay the K8s settings if the corresponding resource doesn't exist.
			scope.Infof("resource Kind:name %s doesn't exist in the output manifest, skip overlay.", pe)
			continue
		}

		// When autoscale is enabled we should not overwrite replica count, consider following scenario:
		// 0. Set values.pilot.autoscaleEnabled=true, components.pilot.k8s.replicaCount=1
		// 1. In istio operator it "caches" the generated manifests (with istiod.replicas=1)
		// 2. HPA autoscales our pilot replicas to 3
		// 3. Set values.pilot.autoscaleEnabled=false
		// 4. The generated manifests (with istiod.replicas=1) is same as istio operator "cache",
		//    the deployment will not get updated unless istio operator is restarted.
		if inPathParts[len(inPathParts)-1] == "ReplicaCount" {
			if skipReplicaCountWithAutoscaleEnabled(iop, componentName) {
				continue
			}
		}

		// strategic merge overlay m to the base object oo
		mergedObj, err := MergeK8sObject(oo, m, path[1:])
		if err != nil {
			return "", err
		}

		// Apply the workaround for merging service ports with (port,protocol) composite
		// keys instead of just the merging by port.
		if inPathParts[len(inPathParts)-1] == "Service" {
			if msvc, ok := m.(*v1alpha1.ServiceSpec); ok {
				mergedObj, err = t.fixMergedObjectWithCustomServicePortOverlay(oo, msvc, mergedObj)
				if err != nil {
					return "", err
				}
			}
		}

		// Update the original object in objects slice, since the output should be ordered.
		*(om[pe]) = *mergedObj
	}

	if objects != nil {
		return objects.YAMLManifest()
	}
	return yml, nil
}