github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/internal/util/attribution/attribution.go (about) 1 // Copyright 2021 Google LLC 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 attribution 16 17 import ( 18 "fmt" 19 "os" 20 "strings" 21 22 "sigs.k8s.io/kustomize/kyaml/kio" 23 kyaml "sigs.k8s.io/kustomize/kyaml/yaml" 24 ) 25 26 const ( 27 CNRMMetricsAnnotation = "cnrm.cloud.google.com/blueprint" 28 DisableKptAttributionEnvVariable = "KPT_DISABLE_ATTRIBUTION" 29 ) 30 31 // Attributor is used to attribute the kpt action on resources 32 type Attributor struct { 33 // PackagePaths is the package paths to add the attribution annotation 34 PackagePaths []string 35 36 // Resources to add the attribution annotation 37 Resources []*kyaml.RNode 38 39 // CmdGroup is the command groups in kpt, e.g., pkg, fn, live 40 CmdGroup string 41 } 42 43 // Process invokes Attribution kyaml filter on the resources in input packages paths 44 func (a *Attributor) Process() { 45 // users can opt-out by setting the "KPT_DISABLE_ATTRIBUTION" environment variable 46 if os.Getenv(DisableKptAttributionEnvVariable) != "" { 47 return 48 } 49 50 if a.CmdGroup == "" { 51 return 52 } 53 54 for _, path := range a.PackagePaths { 55 inout := &kio.LocalPackageReadWriter{PackagePath: path, PreserveSeqIndent: true, WrapBareSeqNode: true} 56 err := kio.Pipeline{ 57 Inputs: []kio.Reader{inout}, 58 Filters: []kio.Filter{kio.FilterAll(a)}, 59 Outputs: []kio.Writer{inout}, 60 }.Execute() 61 if err != nil { 62 // this should be a best effort, do not error if this step fails 63 // https://github.com/GoogleContainerTools/kpt/issues/2559 64 return 65 } 66 } 67 68 for _, resource := range a.Resources { 69 _, _ = a.Filter(resource) 70 } 71 } 72 73 // Filter implements kyaml.Filter 74 // this filter adds "cnrm.cloud.google.com/blueprint" annotation to the resource 75 // if the annotation is already present, it appends kpt-<cmdGroup> suffix 76 // it uses "default" namespace 77 func (a *Attributor) Filter(object *kyaml.RNode) (*kyaml.RNode, error) { 78 // users can opt-out by setting the "KPT_DISABLE_ATTRIBUTION" environment variable 79 if os.Getenv(DisableKptAttributionEnvVariable) != "" { 80 return object, nil 81 } 82 83 // add this annotation to only KCC resource types 84 if !strings.Contains(object.GetApiVersion(), ".cnrm.") { 85 return object, nil 86 } 87 88 curAnnoVal := object.GetAnnotations()[CNRMMetricsAnnotation] 89 mf := object.Field(kyaml.MetadataField) 90 if mf.IsNilOrEmpty() { 91 // skip adding merge comment if empty metadata 92 return object, nil 93 } 94 if _, err := object.Pipe(kyaml.SetAnnotation(CNRMMetricsAnnotation, recordAction(curAnnoVal, a.CmdGroup))); err != nil { 95 return object, nil 96 } 97 return object, nil 98 } 99 100 // recordAction appends the input cmdGroup to the annotation to attribute the usage 101 // if the cmdGroup is already present, then it is no-op 102 func recordAction(curAnnoVal, cmdGroup string) string { 103 if curAnnoVal == "" { 104 return fmt.Sprintf("kpt-%s", cmdGroup) 105 } 106 if !strings.Contains(curAnnoVal, "kpt-") { 107 // just append the value 108 return fmt.Sprintf("%s,kpt-%s", curAnnoVal, cmdGroup) 109 } 110 // we want to extract the current kpt part from the annotation 111 // value and make sure that the input cmdGroup is added 112 // e.g. curAnnoVal: cnrm/landing-zone:networking/v0.4.0,kpt-pkg,blueprints_controller 113 curAnnoParts := strings.Split(curAnnoVal, ",") 114 115 // form the new kpt part value 116 newKptPart := []string{"kpt"} 117 118 for i, curAnnoPart := range curAnnoParts { 119 if strings.Contains(curAnnoPart, "kpt") { 120 if strings.Contains(curAnnoPart, "pkg") || cmdGroup == "pkg" { 121 newKptPart = append(newKptPart, "pkg") 122 } 123 if strings.Contains(curAnnoPart, "fn") || cmdGroup == "fn" { 124 newKptPart = append(newKptPart, "fn") 125 } 126 if strings.Contains(curAnnoPart, "live") || cmdGroup == "live" { 127 newKptPart = append(newKptPart, "live") 128 } 129 // replace the kpt part with the newly formed part 130 curAnnoParts[i] = strings.Join(newKptPart, "-") 131 } 132 } 133 return strings.Join(curAnnoParts, ",") 134 }