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
}