github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/internal/builtins/pkg_context.go (about) 1 // Copyright 2022 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 builtins 16 17 import ( 18 "fmt" 19 "io" 20 "path" 21 22 "sigs.k8s.io/kustomize/kyaml/fn/framework" 23 "sigs.k8s.io/kustomize/kyaml/kio" 24 "sigs.k8s.io/kustomize/kyaml/kio/kioutil" 25 "sigs.k8s.io/kustomize/kyaml/resid" 26 "sigs.k8s.io/kustomize/kyaml/yaml" 27 28 kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1" 29 ) 30 31 const ( 32 PkgContextFile = "package-context.yaml" 33 PkgContextName = "kptfile.kpt.dev" 34 35 ConfigKeyPackagePath = "package-path" 36 ) 37 38 var ( 39 configMapGVK = resid.NewGvk("", "v1", "ConfigMap") 40 kptfileGVK = resid.NewGvk(kptfilev1.KptFileGVK().Group, kptfilev1.KptFileGVK().Version, kptfilev1.KptFileGVK().Kind) 41 ) 42 43 // PackageContextGenerator is a built-in KRM function that generates 44 // a KRM object that contains package context information that can be 45 // used by functions such as `set-namespace` to customize package with 46 // minimal configuration. 47 type PackageContextGenerator struct { 48 // PackageConfig contains the package configuration to set. 49 PackageConfig *PackageConfig 50 } 51 52 // PackageConfig holds package automatic configuration 53 type PackageConfig struct { 54 // PackagePath is the path to the package, as determined by the names of the parent packages. 55 // The path to a package is the parent package path joined with the package name. 56 PackagePath string 57 } 58 59 // Run function reads the function input `resourceList` from a given reader `r` 60 // and writes the function output to the provided writer `w`. 61 // Run implements the function signature defined in 62 // sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/FunctionFilter.Run. 63 func (pc *PackageContextGenerator) Run(r io.Reader, w io.Writer) error { 64 rw := &kio.ByteReadWriter{ 65 Reader: r, 66 Writer: w, 67 KeepReaderAnnotations: true, 68 } 69 return framework.Execute(pc, rw) 70 } 71 72 // Process implements framework.ResourceListProcessor interface. 73 func (pc *PackageContextGenerator) Process(resourceList *framework.ResourceList) error { 74 var contextResources, updatedResources []*yaml.RNode 75 76 // This loop does the following: 77 // - Filters out package context resources from the input resources 78 // - Generates a package context resource for each kpt package (i.e Kptfile) 79 for _, resource := range resourceList.Items { 80 gvk := resid.GvkFromNode(resource) 81 if gvk.Equals(configMapGVK) && resource.GetName() == PkgContextName { 82 // drop existing package context resources 83 continue 84 } 85 updatedResources = append(updatedResources, resource) 86 if gvk.Equals(kptfileGVK) { 87 // it's a Kptfile, generate a corresponding package context 88 pkgContext, err := pkgContextResource(resource, pc.PackageConfig) 89 if err != nil { 90 resourceList.Results = framework.Results{ 91 &framework.Result{ 92 Message: err.Error(), 93 Severity: framework.Error, 94 }, 95 } 96 return resourceList.Results 97 } 98 contextResources = append(contextResources, pkgContext) 99 } 100 } 101 102 for _, resource := range contextResources { 103 updatedResources = append(updatedResources, resource) 104 resourcePath, _, _ := kioutil.GetFileAnnotations(resource) 105 resourceList.Results = append(resourceList.Results, &framework.Result{ 106 Message: "generated package context", 107 Severity: framework.Info, 108 File: &framework.File{Path: resourcePath, Index: 0}, 109 }) 110 } 111 resourceList.Items = updatedResources 112 return nil 113 } 114 115 // pkgContextResource generates package context resource from a given 116 // Kptfile. The resource is generated adjacent to the Kptfile of the package. 117 func pkgContextResource(kptfile *yaml.RNode, packageConfig *PackageConfig) (*yaml.RNode, error) { 118 cm := yaml.MustParse(AbstractPkgContext()) 119 120 kptfilePath, _, err := kioutil.GetFileAnnotations(kptfile) 121 if err != nil { 122 return nil, err 123 } 124 annotations := map[string]string{ 125 kioutil.PathAnnotation: path.Join(path.Dir(kptfilePath), PkgContextFile), 126 } 127 128 for k, v := range annotations { 129 if _, err := cm.Pipe(yaml.SetAnnotation(k, v)); err != nil { 130 return nil, err 131 } 132 } 133 data := map[string]string{ 134 "name": kptfile.GetName(), 135 } 136 if packageConfig != nil { 137 if packageConfig.PackagePath != "" { 138 data[ConfigKeyPackagePath] = packageConfig.PackagePath 139 } 140 } 141 142 cm.SetDataMap(data) 143 return cm, nil 144 } 145 146 // AbstractPkgContext returns content for package context that contains 147 // placeholder value for package name. This will be used to create 148 // abstract blueprints. 149 func AbstractPkgContext() string { 150 return fmt.Sprintf(`apiVersion: v1 151 kind: ConfigMap 152 metadata: 153 name: %s 154 annotations: 155 config.kubernetes.io/local-config: "true" 156 data: 157 name: example 158 `, PkgContextName) 159 }