github.com/amundsenjunior/helm@v2.8.0-rc.1.0.20180119233529-2b92431476e1+incompatible/cmd/helm/package.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "errors" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "os" 25 "path/filepath" 26 "syscall" 27 28 "github.com/Masterminds/semver" 29 "github.com/spf13/cobra" 30 "golang.org/x/crypto/ssh/terminal" 31 32 "k8s.io/helm/pkg/chartutil" 33 "k8s.io/helm/pkg/downloader" 34 "k8s.io/helm/pkg/getter" 35 "k8s.io/helm/pkg/helm/helmpath" 36 "k8s.io/helm/pkg/proto/hapi/chart" 37 "k8s.io/helm/pkg/provenance" 38 "k8s.io/helm/pkg/repo" 39 ) 40 41 const packageDesc = ` 42 This command packages a chart into a versioned chart archive file. If a path 43 is given, this will look at that path for a chart (which must contain a 44 Chart.yaml file) and then package that directory. 45 46 If no path is given, this will look in the present working directory for a 47 Chart.yaml file, and (if found) build the current directory into a chart. 48 49 Versioned chart archives are used by Helm package repositories. 50 ` 51 52 type packageCmd struct { 53 save bool 54 sign bool 55 path string 56 key string 57 keyring string 58 version string 59 appVersion string 60 destination string 61 dependencyUpdate bool 62 63 out io.Writer 64 home helmpath.Home 65 } 66 67 func newPackageCmd(out io.Writer) *cobra.Command { 68 pkg := &packageCmd{out: out} 69 70 cmd := &cobra.Command{ 71 Use: "package [flags] [CHART_PATH] [...]", 72 Short: "package a chart directory into a chart archive", 73 Long: packageDesc, 74 RunE: func(cmd *cobra.Command, args []string) error { 75 pkg.home = settings.Home 76 if len(args) == 0 { 77 return fmt.Errorf("need at least one argument, the path to the chart") 78 } 79 if pkg.sign { 80 if pkg.key == "" { 81 return errors.New("--key is required for signing a package") 82 } 83 if pkg.keyring == "" { 84 return errors.New("--keyring is required for signing a package") 85 } 86 } 87 for i := 0; i < len(args); i++ { 88 pkg.path = args[i] 89 if err := pkg.run(); err != nil { 90 return err 91 } 92 } 93 return nil 94 }, 95 } 96 97 f := cmd.Flags() 98 f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository") 99 f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package") 100 f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true") 101 f.StringVar(&pkg.keyring, "keyring", defaultKeyring(), "location of a public keyring") 102 f.StringVar(&pkg.version, "version", "", "set the version on the chart to this semver version") 103 f.StringVar(&pkg.appVersion, "app-version", "", "set the appVersion on the chart to this version") 104 f.StringVarP(&pkg.destination, "destination", "d", ".", "location to write the chart.") 105 f.BoolVarP(&pkg.dependencyUpdate, "dependency-update", "u", false, `update dependencies from "requirements.yaml" to dir "charts/" before packaging`) 106 107 return cmd 108 } 109 110 func (p *packageCmd) run() error { 111 path, err := filepath.Abs(p.path) 112 if err != nil { 113 return err 114 } 115 116 if p.dependencyUpdate { 117 downloadManager := &downloader.Manager{ 118 Out: p.out, 119 ChartPath: path, 120 HelmHome: settings.Home, 121 Keyring: p.keyring, 122 Getters: getter.All(settings), 123 Debug: settings.Debug, 124 } 125 126 if err := downloadManager.Update(); err != nil { 127 return err 128 } 129 } 130 131 ch, err := chartutil.LoadDir(path) 132 if err != nil { 133 return err 134 } 135 136 // If version is set, modify the version. 137 if len(p.version) != 0 { 138 if err := setVersion(ch, p.version); err != nil { 139 return err 140 } 141 debug("Setting version to %s", p.version) 142 } 143 144 if p.appVersion != "" { 145 ch.Metadata.AppVersion = p.appVersion 146 debug("Setting appVersion to %s", p.appVersion) 147 } 148 149 if filepath.Base(path) != ch.Metadata.Name { 150 return fmt.Errorf("directory name (%s) and Chart.yaml name (%s) must match", filepath.Base(path), ch.Metadata.Name) 151 } 152 153 if reqs, err := chartutil.LoadRequirements(ch); err == nil { 154 if err := checkDependencies(ch, reqs); err != nil { 155 return err 156 } 157 } else { 158 if err != chartutil.ErrRequirementsNotFound { 159 return err 160 } 161 } 162 163 var dest string 164 if p.destination == "." { 165 // Save to the current working directory. 166 dest, err = os.Getwd() 167 if err != nil { 168 return err 169 } 170 } else { 171 // Otherwise save to set destination 172 dest = p.destination 173 } 174 175 name, err := chartutil.Save(ch, dest) 176 if err == nil { 177 fmt.Fprintf(p.out, "Successfully packaged chart and saved it to: %s\n", name) 178 } else { 179 return fmt.Errorf("Failed to save: %s", err) 180 } 181 182 // Save to $HELM_HOME/local directory. This is second, because we don't want 183 // the case where we saved here, but didn't save to the default destination. 184 if p.save { 185 lr := p.home.LocalRepository() 186 if err := repo.AddChartToLocalRepo(ch, lr); err != nil { 187 return err 188 } 189 debug("Successfully saved %s to %s\n", name, lr) 190 } 191 192 if p.sign { 193 err = p.clearsign(name) 194 } 195 196 return err 197 } 198 199 func setVersion(ch *chart.Chart, ver string) error { 200 // Verify that version is a SemVer, and error out if it is not. 201 if _, err := semver.NewVersion(ver); err != nil { 202 return err 203 } 204 205 // Set the version field on the chart. 206 ch.Metadata.Version = ver 207 return nil 208 } 209 210 func (p *packageCmd) clearsign(filename string) error { 211 // Load keyring 212 signer, err := provenance.NewFromKeyring(p.keyring, p.key) 213 if err != nil { 214 return err 215 } 216 217 if err := signer.DecryptKey(promptUser); err != nil { 218 return err 219 } 220 221 sig, err := signer.ClearSign(filename) 222 if err != nil { 223 return err 224 } 225 226 debug(sig) 227 228 return ioutil.WriteFile(filename+".prov", []byte(sig), 0755) 229 } 230 231 // promptUser implements provenance.PassphraseFetcher 232 func promptUser(name string) ([]byte, error) { 233 fmt.Printf("Password for key %q > ", name) 234 pw, err := terminal.ReadPassword(int(syscall.Stdin)) 235 fmt.Println() 236 return pw, err 237 }