istio.io/istio@v0.0.0-20240520182934-d79c90f27776/operator/pkg/translate/translate_value.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 translate 16 17 import ( 18 "fmt" 19 "sort" 20 "strings" 21 22 "sigs.k8s.io/yaml" 23 24 "istio.io/api/operator/v1alpha1" 25 "istio.io/istio/operator/pkg/metrics" 26 "istio.io/istio/operator/pkg/name" 27 "istio.io/istio/operator/pkg/tpath" 28 "istio.io/istio/operator/pkg/util" 29 "istio.io/istio/operator/pkg/version" 30 oversion "istio.io/istio/operator/version" 31 ) 32 33 // ReverseTranslator is a set of mappings to translate between values.yaml and API paths, charts, k8s paths. 34 type ReverseTranslator struct { 35 Version version.MinorVersion 36 // APIMapping is Values.yaml path to API path mapping using longest prefix match. If the path is a non-leaf node, 37 // the output path is the matching portion of the path, plus any remaining output path. 38 APIMapping map[string]*Translation `yaml:"apiMapping,omitempty"` 39 // KubernetesPatternMapping defines mapping patterns from k8s resource paths to IstioOperator API paths. 40 KubernetesPatternMapping map[string]string `yaml:"kubernetesPatternMapping,omitempty"` 41 // KubernetesMapping defines actual k8s mappings generated from KubernetesPatternMapping before each translation. 42 KubernetesMapping map[string]*Translation `yaml:"kubernetesMapping,omitempty"` 43 // GatewayKubernetesMapping defines actual k8s mappings for gateway components generated from KubernetesPatternMapping before each translation. 44 GatewayKubernetesMapping gatewayKubernetesMapping `yaml:"GatewayKubernetesMapping,omitempty"` 45 // ValuesToComponentName defines mapping from value path to component name in API paths. 46 ValuesToComponentName map[string]name.ComponentName `yaml:"valuesToComponentName,omitempty"` 47 } 48 49 type gatewayKubernetesMapping struct { 50 IngressMapping map[string]*Translation 51 EgressMapping map[string]*Translation 52 } 53 54 var ( 55 // Component enablement mapping. Ex "{{.ValueComponent}}.enabled": Components.{{.ComponentName}}.enabled}", nil}, 56 componentEnablementPattern = "Components.{{.ComponentName}}.Enabled" 57 // specialComponentPath lists cases of component path of values.yaml we need to have special treatment. 58 specialComponentPath = map[string]bool{ 59 "gateways": true, 60 "gateways.istio-ingressgateway": true, 61 "gateways.istio-egressgateway": true, 62 } 63 64 skipTranslate = map[name.ComponentName]bool{ 65 name.IstioBaseComponentName: true, 66 name.IstioOperatorComponentName: true, 67 name.IstioOperatorCustomResourceName: true, 68 name.CNIComponentName: true, 69 name.IstiodRemoteComponentName: true, 70 name.ZtunnelComponentName: true, 71 } 72 73 gatewayPathMapping = map[string]name.ComponentName{ 74 "gateways.istio-ingressgateway": name.IngressComponentName, 75 "gateways.istio-egressgateway": name.EgressComponentName, 76 } 77 ) 78 79 // initAPIMapping generate the reverse mapping from original translator apiMapping. 80 func (t *ReverseTranslator) initAPIAndComponentMapping() { 81 ts := NewTranslator() 82 t.APIMapping = make(map[string]*Translation) 83 t.KubernetesMapping = make(map[string]*Translation) 84 t.ValuesToComponentName = make(map[string]name.ComponentName) 85 for valKey, outVal := range ts.APIMapping { 86 t.APIMapping[outVal.OutPath] = &Translation{valKey, nil} 87 } 88 for cn, cm := range ts.ComponentMaps { 89 // we use dedicated translateGateway for gateway instead 90 if !skipTranslate[cn] && !cm.SkipReverseTranslate && !cn.IsGateway() { 91 t.ValuesToComponentName[cm.ToHelmValuesTreeRoot] = cn 92 } 93 } 94 } 95 96 // initK8SMapping generates the k8s settings mapping for components that are enabled based on templates. 97 func (t *ReverseTranslator) initK8SMapping() error { 98 outputMapping := make(map[string]*Translation) 99 for valKey, componentName := range t.ValuesToComponentName { 100 for K8SValKey, outPathTmpl := range t.KubernetesPatternMapping { 101 newKey, err := renderComponentName(K8SValKey, valKey) 102 if err != nil { 103 return err 104 } 105 newVal, err := renderFeatureComponentPathTemplate(outPathTmpl, componentName) 106 if err != nil { 107 return err 108 } 109 outputMapping[newKey] = &Translation{newVal, nil} 110 } 111 } 112 113 t.KubernetesMapping = outputMapping 114 115 igwOutputMapping := make(map[string]*Translation) 116 egwOutputMapping := make(map[string]*Translation) 117 for valKey, componentName := range gatewayPathMapping { 118 mapping := igwOutputMapping 119 if componentName == name.EgressComponentName { 120 mapping = egwOutputMapping 121 } 122 for K8SValKey, outPathTmpl := range t.KubernetesPatternMapping { 123 newKey, err := renderComponentName(K8SValKey, valKey) 124 if err != nil { 125 return err 126 } 127 newP := util.PathFromString(outPathTmpl) 128 mapping[newKey] = &Translation{newP[len(newP)-2:].String(), nil} 129 } 130 } 131 t.GatewayKubernetesMapping = gatewayKubernetesMapping{IngressMapping: igwOutputMapping, EgressMapping: egwOutputMapping} 132 return nil 133 } 134 135 // NewReverseTranslator creates a new ReverseTranslator for minorVersion and returns a ptr to it. 136 func NewReverseTranslator() *ReverseTranslator { 137 rt := &ReverseTranslator{ 138 KubernetesPatternMapping: map[string]string{ 139 "{{.ValueComponentName}}.env": "Components.{{.ComponentName}}.K8s.Env", 140 "{{.ValueComponentName}}.autoscaleEnabled": "Components.{{.ComponentName}}.K8s.HpaSpec", 141 "{{.ValueComponentName}}.imagePullPolicy": "Components.{{.ComponentName}}.K8s.ImagePullPolicy", 142 "{{.ValueComponentName}}.nodeSelector": "Components.{{.ComponentName}}.K8s.NodeSelector", 143 "{{.ValueComponentName}}.tolerations": "Components.{{.ComponentName}}.K8s.Tolerations", 144 "{{.ValueComponentName}}.podDisruptionBudget": "Components.{{.ComponentName}}.K8s.PodDisruptionBudget", 145 "{{.ValueComponentName}}.podAnnotations": "Components.{{.ComponentName}}.K8s.PodAnnotations", 146 "{{.ValueComponentName}}.priorityClassName": "Components.{{.ComponentName}}.K8s.PriorityClassName", 147 "{{.ValueComponentName}}.readinessProbe": "Components.{{.ComponentName}}.K8s.ReadinessProbe", 148 "{{.ValueComponentName}}.replicaCount": "Components.{{.ComponentName}}.K8s.ReplicaCount", 149 "{{.ValueComponentName}}.resources": "Components.{{.ComponentName}}.K8s.Resources", 150 "{{.ValueComponentName}}.rollingMaxSurge": "Components.{{.ComponentName}}.K8s.Strategy", 151 "{{.ValueComponentName}}.rollingMaxUnavailable": "Components.{{.ComponentName}}.K8s.Strategy", 152 "{{.ValueComponentName}}.serviceAnnotations": "Components.{{.ComponentName}}.K8s.ServiceAnnotations", 153 }, 154 } 155 rt.initAPIAndComponentMapping() 156 rt.Version = oversion.OperatorBinaryVersion.MinorVersion 157 return rt 158 } 159 160 // TranslateFromValueToSpec translates from values.yaml value to IstioOperatorSpec. 161 func (t *ReverseTranslator) TranslateFromValueToSpec(values []byte, force bool) (controlPlaneSpec *v1alpha1.IstioOperatorSpec, err error) { 162 yamlTree := make(map[string]any) 163 err = yaml.Unmarshal(values, &yamlTree) 164 if err != nil { 165 return nil, fmt.Errorf("error when unmarshalling into untype tree %v", err) 166 } 167 168 outputTree := make(map[string]any) 169 err = t.TranslateTree(yamlTree, outputTree, nil) 170 if err != nil { 171 return nil, err 172 } 173 outputVal, err := yaml.Marshal(outputTree) 174 if err != nil { 175 return nil, err 176 } 177 178 cpSpec := &v1alpha1.IstioOperatorSpec{} 179 err = util.UnmarshalWithJSONPB(string(outputVal), cpSpec, force) 180 if err != nil { 181 return nil, fmt.Errorf("error when unmarshalling into control plane spec %v, \nyaml:\n %s", err, outputVal) 182 } 183 184 return cpSpec, nil 185 } 186 187 // TranslateTree translates input value.yaml Tree to ControlPlaneSpec Tree. 188 func (t *ReverseTranslator) TranslateTree(valueTree map[string]any, cpSpecTree map[string]any, path util.Path) error { 189 // translate enablement and namespace 190 err := t.setEnablementFromValue(valueTree, cpSpecTree) 191 if err != nil { 192 return fmt.Errorf("error when translating enablement and namespace from value.yaml tree: %v", err) 193 } 194 // translate with api mapping 195 err = t.translateAPI(valueTree, cpSpecTree) 196 if err != nil { 197 return fmt.Errorf("error when translating value.yaml tree with global mapping: %v", err) 198 } 199 200 // translate with k8s mapping 201 if err := t.TranslateK8S(valueTree, cpSpecTree); err != nil { 202 return err 203 } 204 205 if err := t.translateGateway(valueTree, cpSpecTree); err != nil { 206 return fmt.Errorf("error when translating gateway with kubernetes mapping: %v", err.Error()) 207 } 208 // translate remaining untranslated paths into component values 209 err = t.translateRemainingPaths(valueTree, cpSpecTree, nil) 210 if err != nil { 211 return fmt.Errorf("error when translating remaining path: %v", err) 212 } 213 return nil 214 } 215 216 // TranslateK8S is a helper function to translate k8s settings from values.yaml to IstioOperator, except for gateways. 217 func (t *ReverseTranslator) TranslateK8S(valueTree map[string]any, cpSpecTree map[string]any) error { 218 // translate with k8s mapping 219 if err := t.initK8SMapping(); err != nil { 220 return fmt.Errorf("error when initiating k8s mapping: %v", err) 221 } 222 if err := t.translateK8sTree(valueTree, cpSpecTree, t.KubernetesMapping); err != nil { 223 return fmt.Errorf("error when translating value.yaml tree with kubernetes mapping: %v", err) 224 } 225 return nil 226 } 227 228 // setEnablementFromValue translates the enablement value of components in the values.yaml 229 // tree, based on feature/component inheritance relationship. 230 func (t *ReverseTranslator) setEnablementFromValue(valueSpec map[string]any, root map[string]any) error { 231 for _, cni := range t.ValuesToComponentName { 232 enabled, pathExist, err := IsComponentEnabledFromValue(cni, valueSpec) 233 if err != nil { 234 return err 235 } 236 if !pathExist { 237 continue 238 } 239 tmpl := componentEnablementPattern 240 ceVal, err := renderFeatureComponentPathTemplate(tmpl, cni) 241 if err != nil { 242 return err 243 } 244 outCP := util.ToYAMLPath(ceVal) 245 // set component enablement 246 if err := tpath.WriteNode(root, outCP, enabled); err != nil { 247 return err 248 } 249 } 250 251 return nil 252 } 253 254 // WarningForGatewayK8SSettings creates deprecated warning messages 255 // when user try to set kubernetes settings for gateways via values api. 256 func (t *ReverseTranslator) WarningForGatewayK8SSettings(valuesOverlay string) (string, error) { 257 gwOverlay, err := tpath.GetConfigSubtree(valuesOverlay, "gateways") 258 if err != nil { 259 return "", fmt.Errorf("error getting gateways overlay from valuesOverlayYaml %v", err) 260 } 261 if gwOverlay == "" { 262 return "", nil 263 } 264 var deprecatedFields []string 265 for inPath := range t.GatewayKubernetesMapping.IngressMapping { 266 _, found, err := tpath.GetPathContext(valuesOverlay, util.ToYAMLPath(inPath), false) 267 if err != nil { 268 scope.Debug(err.Error()) 269 continue 270 } 271 if found { 272 deprecatedFields = append(deprecatedFields, inPath) 273 } 274 } 275 for inPath := range t.GatewayKubernetesMapping.EgressMapping { 276 _, found, err := tpath.GetPathContext(valuesOverlay, util.ToYAMLPath(inPath), false) 277 if err != nil { 278 scope.Debug(err.Error()) 279 continue 280 } 281 if found { 282 deprecatedFields = append(deprecatedFields, inPath) 283 } 284 } 285 if len(deprecatedFields) == 0 { 286 return "", nil 287 } 288 warningMessage := fmt.Sprintf("using deprecated values api paths: %s.\n"+ 289 " please use k8s spec of gateway components instead\n", strings.Join(deprecatedFields, ",")) 290 return warningMessage, nil 291 } 292 293 // translateGateway handles translation for gateways specific configuration 294 func (t *ReverseTranslator) translateGateway(valueSpec map[string]any, root map[string]any) error { 295 for inPath, outPath := range gatewayPathMapping { 296 enabled, pathExist, err := IsComponentEnabledFromValue(outPath, valueSpec) 297 if err != nil { 298 return err 299 } 300 if !pathExist && !enabled { 301 continue 302 } 303 gwSpecs := make([]map[string]any, 1) 304 gwSpec := make(map[string]any) 305 gwSpecs[0] = gwSpec 306 gwSpec["enabled"] = enabled 307 gwSpec["name"] = util.ToYAMLPath(inPath)[1] 308 outCP := util.ToYAMLPath("Components." + string(outPath)) 309 310 if enabled { 311 mapping := t.GatewayKubernetesMapping.IngressMapping 312 if outPath == name.EgressComponentName { 313 mapping = t.GatewayKubernetesMapping.EgressMapping 314 } 315 err = t.translateK8sTree(valueSpec, gwSpec, mapping) 316 if err != nil { 317 return err 318 } 319 } 320 err = tpath.WriteNode(root, outCP, gwSpecs) 321 if err != nil { 322 return err 323 } 324 } 325 return nil 326 } 327 328 // TranslateK8SfromValueToIOP use reverse translation to convert k8s settings defined in values API to IOP API. 329 // this ensures that user overlays that set k8s through spec.values 330 // are not overridden by spec.components.X.k8s settings in the base profiles 331 func (t *ReverseTranslator) TranslateK8SfromValueToIOP(userOverlayYaml string) (string, error) { 332 valuesOverlay, err := tpath.GetConfigSubtree(userOverlayYaml, "spec.values") 333 if err != nil { 334 scope.Debugf("no spec.values section from userOverlayYaml %v", err) 335 return "", nil 336 } 337 valuesOverlayTree := make(map[string]any) 338 err = yaml.Unmarshal([]byte(valuesOverlay), &valuesOverlayTree) 339 if err != nil { 340 return "", fmt.Errorf("error unmarshalling values overlay yaml into untype tree %v", err) 341 } 342 iopSpecTree := make(map[string]any) 343 iopSpecOverlay, err := tpath.GetConfigSubtree(userOverlayYaml, "spec") 344 if err != nil { 345 return "", fmt.Errorf("error getting iop spec subtree from overlay yaml %v", err) 346 } 347 err = yaml.Unmarshal([]byte(iopSpecOverlay), &iopSpecTree) 348 if err != nil { 349 return "", fmt.Errorf("error unmarshalling spec overlay yaml into tree %v", err) 350 } 351 if err = t.TranslateK8S(valuesOverlayTree, iopSpecTree); err != nil { 352 return "", err 353 } 354 warning, err := t.WarningForGatewayK8SSettings(valuesOverlay) 355 if err != nil { 356 return "", fmt.Errorf("error handling values gateway k8s settings: %v", err) 357 } 358 if warning != "" { 359 return "", fmt.Errorf(warning) 360 } 361 iopSpecTreeYAML, err := yaml.Marshal(iopSpecTree) 362 if err != nil { 363 return "", fmt.Errorf("error marshaling reverse translated tree %v", err) 364 } 365 iopTreeYAML, err := tpath.AddSpecRoot(string(iopSpecTreeYAML)) 366 if err != nil { 367 return "", fmt.Errorf("error adding spec root: %v", err) 368 } 369 // overlay the reverse translated iopTreeYAML back to userOverlayYaml 370 finalYAML, err := util.OverlayYAML(userOverlayYaml, iopTreeYAML) 371 if err != nil { 372 return "", fmt.Errorf("failed to overlay the reverse translated iopTreeYAML: %v", err) 373 } 374 return finalYAML, err 375 } 376 377 // translateStrategy translates Deployment Strategy related configurations from helm values.yaml tree. 378 func translateStrategy(fieldName string, outPath string, value any, cpSpecTree map[string]any) error { 379 fieldMap := map[string]string{ 380 "rollingMaxSurge": "maxSurge", 381 "rollingMaxUnavailable": "maxUnavailable", 382 } 383 newFieldName, ok := fieldMap[fieldName] 384 if !ok { 385 return fmt.Errorf("expected field name found in values.yaml: %s", fieldName) 386 } 387 outPath += ".rollingUpdate." + newFieldName 388 389 scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", outPath) 390 if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath(outPath), value); err != nil { 391 return err 392 } 393 return nil 394 } 395 396 // translateEnv translates env value from helm values.yaml tree. 397 func translateEnv(outPath string, value any, cpSpecTree map[string]any) error { 398 envMap, ok := value.(map[string]any) 399 if !ok { 400 return fmt.Errorf("expect env node type to be map[string]interface{} but got: %T", value) 401 } 402 if len(envMap) == 0 { 403 return nil 404 } 405 scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", outPath) 406 nc, found, _ := tpath.GetPathContext(cpSpecTree, util.ToYAMLPath(outPath), false) 407 var envValStr []byte 408 if nc != nil { 409 envValStr, _ = yaml.Marshal(nc.Node) 410 } 411 if !found || strings.TrimSpace(string(envValStr)) == "{}" { 412 scope.Debugf("path doesn't have value in k8s setting with output path %s, override with helm Value.yaml tree", outPath) 413 outEnv := make([]map[string]any, len(envMap)) 414 keys := make([]string, 0, len(envMap)) 415 for k := range envMap { 416 keys = append(keys, k) 417 } 418 sort.Strings(keys) 419 for i, k := range keys { 420 outEnv[i] = make(map[string]any) 421 outEnv[i]["name"] = k 422 outEnv[i]["value"] = fmt.Sprintf("%v", envMap[k]) 423 } 424 if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath(outPath), outEnv); err != nil { 425 return err 426 } 427 } else { 428 scope.Debugf("path has value in k8s setting with output path %s, merge it with helm Value.yaml tree", outPath) 429 keys := make([]string, 0, len(envMap)) 430 for k := range envMap { 431 keys = append(keys, k) 432 } 433 sort.Strings(keys) 434 for _, k := range keys { 435 outEnv := make(map[string]any) 436 outEnv["name"] = k 437 outEnv["value"] = fmt.Sprintf("%v", envMap[k]) 438 if err := tpath.MergeNode(cpSpecTree, util.ToYAMLPath(outPath), outEnv); err != nil { 439 return err 440 } 441 } 442 } 443 return nil 444 } 445 446 // translateK8sTree is internal method for translating K8s configurations from value.yaml tree. 447 func (t *ReverseTranslator) translateK8sTree(valueTree map[string]any, 448 cpSpecTree map[string]any, mapping map[string]*Translation, 449 ) error { 450 for inPath, v := range mapping { 451 scope.Debugf("Checking for k8s path %s in helm Value.yaml tree", inPath) 452 path := util.PathFromString(inPath) 453 k8sSettingName := "" 454 if len(path) != 0 { 455 k8sSettingName = path[len(path)-1] 456 } 457 if k8sSettingName == "autoscaleEnabled" { 458 continue 459 } 460 m, found, err := tpath.Find(valueTree, util.ToYAMLPath(inPath)) 461 if err != nil { 462 return err 463 } 464 if !found { 465 scope.Debugf("path %s not found in helm Value.yaml tree, skip mapping.", inPath) 466 continue 467 } 468 469 if mstr, ok := m.(string); ok && mstr == "" { 470 scope.Debugf("path %s is empty string, skip mapping.", inPath) 471 continue 472 } 473 // Zero int values are due to proto3 compiling to scalars rather than ptrs. Skip these because values of 0 are 474 // the default in destination fields and need not be set explicitly. 475 if mint, ok := util.ToIntValue(m); ok && mint == 0 { 476 scope.Debugf("path %s is int 0, skip mapping.", inPath) 477 continue 478 } 479 480 switch k8sSettingName { 481 case "env": 482 err := translateEnv(v.OutPath, m, cpSpecTree) 483 if err != nil { 484 return fmt.Errorf("error in translating k8s Env: %s", err) 485 } 486 487 case "rollingMaxSurge", "rollingMaxUnavailable": 488 err := translateStrategy(k8sSettingName, v.OutPath, m, cpSpecTree) 489 if err != nil { 490 return fmt.Errorf("error in translating k8s Strategy: %s", err) 491 } 492 493 default: 494 if util.IsValueNilOrDefault(m) { 495 continue 496 } 497 output := util.ToYAMLPath(v.OutPath) 498 scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", output) 499 500 if err := tpath.WriteNode(cpSpecTree, output, m); err != nil { 501 return err 502 } 503 } 504 metrics.LegacyPathTranslationTotal.Increment() 505 506 if _, err := tpath.Delete(valueTree, util.ToYAMLPath(inPath)); err != nil { 507 return err 508 } 509 } 510 return nil 511 } 512 513 // translateRemainingPaths translates remaining paths that are not available in existing mappings. 514 func (t *ReverseTranslator) translateRemainingPaths(valueTree map[string]any, 515 cpSpecTree map[string]any, path util.Path, 516 ) error { 517 for key, val := range valueTree { 518 newPath := append(path, key) 519 // value set to nil means no translation needed or being translated already. 520 if val == nil { 521 continue 522 } 523 switch node := val.(type) { 524 case map[string]any: 525 err := t.translateRemainingPaths(node, cpSpecTree, newPath) 526 if err != nil { 527 return err 528 } 529 case []any: 530 if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath("Values."+newPath.String()), node); err != nil { 531 return err 532 } 533 // remaining leaf need to be put into root.values 534 default: 535 if t.isEnablementPath(newPath) { 536 continue 537 } 538 if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath("Values."+newPath.String()), val); err != nil { 539 return err 540 } 541 } 542 } 543 return nil 544 } 545 546 // translateAPI is internal method for translating value.yaml tree based on API mapping. 547 func (t *ReverseTranslator) translateAPI(valueTree map[string]any, 548 cpSpecTree map[string]any, 549 ) error { 550 for inPath, v := range t.APIMapping { 551 scope.Debugf("Checking for path %s in helm Value.yaml tree", inPath) 552 m, found, err := tpath.Find(valueTree, util.ToYAMLPath(inPath)) 553 if err != nil { 554 return err 555 } 556 if !found { 557 scope.Debugf("path %s not found in helm Value.yaml tree, skip mapping.", inPath) 558 continue 559 } 560 if mstr, ok := m.(string); ok && mstr == "" { 561 scope.Debugf("path %s is empty string, skip mapping.", inPath) 562 continue 563 } 564 // Zero int values are due to proto3 compiling to scalars rather than ptrs. Skip these because values of 0 are 565 // the default in destination fields and need not be set explicitly. 566 if mint, ok := util.ToIntValue(m); ok && mint == 0 { 567 scope.Debugf("path %s is int 0, skip mapping.", inPath) 568 continue 569 } 570 571 path := util.ToYAMLPath(v.OutPath) 572 scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", path) 573 metrics.LegacyPathTranslationTotal. 574 With(metrics.ResourceKindLabel.Value(inPath)).Increment() 575 576 if err := tpath.WriteNode(cpSpecTree, path, m); err != nil { 577 return err 578 } 579 580 if _, err := tpath.Delete(valueTree, util.ToYAMLPath(inPath)); err != nil { 581 return err 582 } 583 } 584 return nil 585 } 586 587 // isEnablementPath is helper function to check whether paths represent enablement of components in values.yaml 588 func (t *ReverseTranslator) isEnablementPath(path util.Path) bool { 589 if len(path) < 2 || path[len(path)-1] != "enabled" { 590 return false 591 } 592 593 pf := path[:len(path)-1].String() 594 if specialComponentPath[pf] { 595 return true 596 } 597 598 _, exist := t.ValuesToComponentName[pf] 599 return exist 600 } 601 602 // renderComponentName renders a template of the form <path>{{.ComponentName}}<path> with 603 // the supplied parameters. 604 func renderComponentName(tmpl string, componentName string) (string, error) { 605 type temp struct { 606 ValueComponentName string 607 } 608 return util.RenderTemplate(tmpl, temp{componentName}) 609 }