github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/pkg/kptpkg/init.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 kptpkg 16 17 import ( 18 "bytes" 19 "context" 20 "html/template" 21 "path/filepath" 22 "strings" 23 24 "github.com/GoogleContainerTools/kpt/internal/builtins" 25 "github.com/GoogleContainerTools/kpt/internal/pkg" 26 "github.com/GoogleContainerTools/kpt/internal/printer" 27 "github.com/GoogleContainerTools/kpt/internal/util/man" 28 kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1" 29 "sigs.k8s.io/kustomize/kyaml/errors" 30 "sigs.k8s.io/kustomize/kyaml/filesys" 31 "sigs.k8s.io/kustomize/kyaml/kio/filters" 32 "sigs.k8s.io/kustomize/kyaml/yaml" 33 ) 34 35 // Initializer defines capability to initialize a kpt package. 36 type Initializer interface { 37 Initialize(ctx context.Context, pkg filesys.FileSystem, opts InitOptions) error 38 } 39 40 // InitOptions contains customization options for package initialization. 41 type InitOptions struct { 42 PkgName string 43 PkgPath string 44 // RelPath is used purely for printing info relative to current working dir of user. 45 // It may or may not be same as PkgPath. 46 RelPath string 47 Desc string 48 Keywords []string 49 Site string 50 } 51 52 // DefaultInitilizer implements Initializer interface. 53 type DefaultInitializer struct{} 54 55 func (i *DefaultInitializer) Initialize( 56 ctx context.Context, 57 fsys filesys.FileSystem, 58 opts InitOptions, 59 ) error { 60 p, err := pkg.New(fsys, opts.PkgPath) 61 if err != nil { 62 return err 63 } 64 65 var pkgName string 66 if opts.PkgName != "" { 67 pkgName = opts.PkgName 68 } else { 69 pkgName = string(p.DisplayPath) 70 } 71 72 up := string(p.UniquePath) 73 if !fsys.Exists(string(p.UniquePath)) { 74 return errors.Errorf("%s does not exist", p.UniquePath) 75 } 76 77 pr := printer.FromContextOrDie(ctx) 78 79 if !fsys.Exists(filepath.Join(up, kptfilev1.KptFileName)) { 80 pr.Printf("writing %s\n", filepath.Join(opts.RelPath, "Kptfile")) 81 k := kptfilev1.KptFile{ 82 ResourceMeta: yaml.ResourceMeta{ 83 ObjectMeta: yaml.ObjectMeta{ 84 NameMeta: yaml.NameMeta{ 85 Name: pkgName, 86 }, 87 // mark Kptfile as local-config 88 Annotations: map[string]string{ 89 filters.LocalConfigAnnotation: "true", 90 }, 91 }, 92 }, 93 Info: &kptfilev1.PackageInfo{ 94 Description: opts.Desc, 95 Site: opts.Site, 96 Keywords: opts.Keywords, 97 }, 98 } 99 100 // serialize the gvk when writing the Kptfile 101 k.Kind = kptfilev1.TypeMeta.Kind 102 k.APIVersion = kptfilev1.TypeMeta.APIVersion 103 104 err = func() error { 105 f, err := fsys.Create(filepath.Join(up, kptfilev1.KptFileName)) 106 if err != nil { 107 return err 108 } 109 defer f.Close() 110 e := yaml.NewEncoder(f) 111 112 defer e.Close() 113 return e.Encode(k) 114 }() 115 if err != nil { 116 return err 117 } 118 } 119 120 if !fsys.Exists(filepath.Join(up, man.ManFilename)) { 121 pr.Printf("writing %s\n", filepath.Join(opts.RelPath, man.ManFilename)) 122 buff := &bytes.Buffer{} 123 t, err := template.New("man").Parse(manTemplate) 124 if err != nil { 125 return err 126 } 127 templateData := map[string]string{ 128 "Name": pkgName, 129 "Description": opts.Desc, 130 } 131 132 err = t.Execute(buff, templateData) 133 if err != nil { 134 return err 135 } 136 137 // Replace single quotes with backticks. 138 content := strings.ReplaceAll(buff.String(), "'", "`") 139 140 err = fsys.WriteFile(filepath.Join(up, man.ManFilename), []byte(content)) 141 if err != nil { 142 return err 143 } 144 } 145 146 pkgContextPath := filepath.Join(up, builtins.PkgContextFile) 147 if !fsys.Exists(pkgContextPath) { 148 pr.Printf("writing %s\n", filepath.Join(opts.RelPath, builtins.PkgContextFile)) 149 if err := fsys.WriteFile(pkgContextPath, []byte(builtins.AbstractPkgContext())); err != nil { 150 return err 151 } 152 } 153 return nil 154 } 155 156 // manTemplate is the content for the automatically generated README.md file. 157 // It uses ' instead of ` since golang doesn't allow using ` in a raw string 158 // literal. We do a replace on the content before printing. 159 var manTemplate = `# {{.Name}} 160 161 ## Description 162 {{.Description}} 163 164 ## Usage 165 166 ### Fetch the package 167 'kpt pkg get REPO_URI[.git]/PKG_PATH[@VERSION] {{.Name}}' 168 Details: https://kpt.dev/reference/cli/pkg/get/ 169 170 ### View package content 171 'kpt pkg tree {{.Name}}' 172 Details: https://kpt.dev/reference/cli/pkg/tree/ 173 174 ### Apply the package 175 ''' 176 kpt live init {{.Name}} 177 kpt live apply {{.Name}} --reconcile-timeout=2m --output=table 178 ''' 179 Details: https://kpt.dev/reference/cli/live/ 180 `