github.com/strongmonkey/helm@v2.7.2+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 destination string 60 dependencyUpdate bool 61 62 out io.Writer 63 home helmpath.Home 64 } 65 66 func newPackageCmd(out io.Writer) *cobra.Command { 67 pkg := &packageCmd{out: out} 68 69 cmd := &cobra.Command{ 70 Use: "package [flags] [CHART_PATH] [...]", 71 Short: "package a chart directory into a chart archive", 72 Long: packageDesc, 73 RunE: func(cmd *cobra.Command, args []string) error { 74 pkg.home = settings.Home 75 if len(args) == 0 { 76 return fmt.Errorf("need at least one argument, the path to the chart") 77 } 78 if pkg.sign { 79 if pkg.key == "" { 80 return errors.New("--key is required for signing a package") 81 } 82 if pkg.keyring == "" { 83 return errors.New("--keyring is required for signing a package") 84 } 85 } 86 for i := 0; i < len(args); i++ { 87 pkg.path = args[i] 88 if err := pkg.run(); err != nil { 89 return err 90 } 91 } 92 return nil 93 }, 94 } 95 96 f := cmd.Flags() 97 f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository") 98 f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package") 99 f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true") 100 f.StringVar(&pkg.keyring, "keyring", defaultKeyring(), "location of a public keyring") 101 f.StringVar(&pkg.version, "version", "", "set the version on the chart to this semver version") 102 f.StringVarP(&pkg.destination, "destination", "d", ".", "location to write the chart.") 103 f.BoolVarP(&pkg.dependencyUpdate, "dependency-update", "u", false, `update dependencies from "requirements.yaml" to dir "charts/" before packaging`) 104 105 return cmd 106 } 107 108 func (p *packageCmd) run() error { 109 path, err := filepath.Abs(p.path) 110 if err != nil { 111 return err 112 } 113 114 if p.dependencyUpdate { 115 downloadManager := &downloader.Manager{ 116 Out: p.out, 117 ChartPath: path, 118 HelmHome: settings.Home, 119 Keyring: p.keyring, 120 Getters: getter.All(settings), 121 Debug: settings.Debug, 122 } 123 124 if err := downloadManager.Update(); err != nil { 125 return err 126 } 127 } 128 129 ch, err := chartutil.LoadDir(path) 130 if err != nil { 131 return err 132 } 133 134 // If version is set, modify the version. 135 if len(p.version) != 0 { 136 if err := setVersion(ch, p.version); err != nil { 137 return err 138 } 139 debug("Setting version to %s", p.version) 140 } 141 142 if filepath.Base(path) != ch.Metadata.Name { 143 return fmt.Errorf("directory name (%s) and Chart.yaml name (%s) must match", filepath.Base(path), ch.Metadata.Name) 144 } 145 146 if reqs, err := chartutil.LoadRequirements(ch); err == nil { 147 if err := checkDependencies(ch, reqs); err != nil { 148 return err 149 } 150 } else { 151 if err != chartutil.ErrRequirementsNotFound { 152 return err 153 } 154 } 155 156 var dest string 157 if p.destination == "." { 158 // Save to the current working directory. 159 dest, err = os.Getwd() 160 if err != nil { 161 return err 162 } 163 } else { 164 // Otherwise save to set destination 165 dest = p.destination 166 } 167 168 name, err := chartutil.Save(ch, dest) 169 if err == nil { 170 fmt.Fprintf(p.out, "Successfully packaged chart and saved it to: %s\n", name) 171 } else { 172 return fmt.Errorf("Failed to save: %s", err) 173 } 174 175 // Save to $HELM_HOME/local directory. This is second, because we don't want 176 // the case where we saved here, but didn't save to the default destination. 177 if p.save { 178 lr := p.home.LocalRepository() 179 if err := repo.AddChartToLocalRepo(ch, lr); err != nil { 180 return err 181 } 182 debug("Successfully saved %s to %s\n", name, lr) 183 } 184 185 if p.sign { 186 err = p.clearsign(name) 187 } 188 189 return err 190 } 191 192 func setVersion(ch *chart.Chart, ver string) error { 193 // Verify that version is a SemVer, and error out if it is not. 194 if _, err := semver.NewVersion(ver); err != nil { 195 return err 196 } 197 198 // Set the version field on the chart. 199 ch.Metadata.Version = ver 200 return nil 201 } 202 203 func (p *packageCmd) clearsign(filename string) error { 204 // Load keyring 205 signer, err := provenance.NewFromKeyring(p.keyring, p.key) 206 if err != nil { 207 return err 208 } 209 210 if err := signer.DecryptKey(promptUser); err != nil { 211 return err 212 } 213 214 sig, err := signer.ClearSign(filename) 215 if err != nil { 216 return err 217 } 218 219 debug(sig) 220 221 return ioutil.WriteFile(filename+".prov", []byte(sig), 0755) 222 } 223 224 // promptUser implements provenance.PassphraseFetcher 225 func promptUser(name string) ([]byte, error) { 226 fmt.Printf("Password for key %q > ", name) 227 pw, err := terminal.ReadPassword(int(syscall.Stdin)) 228 fmt.Println() 229 return pw, err 230 }