github.com/sgoings/helm@v2.0.0-alpha.2.0.20170406211108-734e92851ac3+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/helm/helmpath" 34 "k8s.io/helm/pkg/proto/hapi/chart" 35 "k8s.io/helm/pkg/provenance" 36 "k8s.io/helm/pkg/repo" 37 ) 38 39 const packageDesc = ` 40 This command packages a chart into a versioned chart archive file. If a path 41 is given, this will look at that path for a chart (which must contain a 42 Chart.yaml file) and then package that directory. 43 44 If no path is given, this will look in the present working directory for a 45 Chart.yaml file, and (if found) build the current directory into a chart. 46 47 Versioned chart archives are used by Helm package repositories. 48 ` 49 50 type packageCmd struct { 51 save bool 52 sign bool 53 path string 54 key string 55 keyring string 56 version string 57 destination string 58 59 out io.Writer 60 home helmpath.Home 61 } 62 63 func newPackageCmd(out io.Writer) *cobra.Command { 64 pkg := &packageCmd{ 65 out: out, 66 } 67 68 cmd := &cobra.Command{ 69 Use: "package [flags] [CHART_PATH] [...]", 70 Short: "package a chart directory into a chart archive", 71 Long: packageDesc, 72 RunE: func(cmd *cobra.Command, args []string) error { 73 pkg.home = helmpath.Home(homePath()) 74 if len(args) == 0 { 75 return fmt.Errorf("This command needs at least one argument, the path to the chart.") 76 } 77 if pkg.sign { 78 if pkg.key == "" { 79 return errors.New("--key is required for signing a package") 80 } 81 if pkg.keyring == "" { 82 return errors.New("--keyring is required for signing a package") 83 } 84 } 85 for i := 0; i < len(args); i++ { 86 pkg.path = args[i] 87 if err := pkg.run(cmd, args); err != nil { 88 return err 89 } 90 } 91 return nil 92 }, 93 } 94 95 f := cmd.Flags() 96 f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository") 97 f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package") 98 f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true") 99 f.StringVar(&pkg.keyring, "keyring", defaultKeyring(), "location of a public keyring") 100 f.StringVar(&pkg.version, "version", "", "set the version on the chart to this semver version") 101 f.StringVarP(&pkg.destination, "destination", "d", ".", "location to write the chart.") 102 103 return cmd 104 } 105 106 func (p *packageCmd) run(cmd *cobra.Command, args []string) error { 107 path, err := filepath.Abs(p.path) 108 if err != nil { 109 return err 110 } 111 112 ch, err := chartutil.LoadDir(path) 113 if err != nil { 114 return err 115 } 116 117 // If version is set, modify the version. 118 if len(p.version) != 0 { 119 if err := setVersion(ch, p.version); err != nil { 120 return err 121 } 122 if flagDebug { 123 fmt.Fprintf(p.out, "Setting version to %s", p.version) 124 } 125 } 126 127 if filepath.Base(path) != ch.Metadata.Name { 128 return fmt.Errorf("directory name (%s) and Chart.yaml name (%s) must match", filepath.Base(path), ch.Metadata.Name) 129 } 130 131 if reqs, err := chartutil.LoadRequirements(ch); err == nil { 132 checkDependencies(ch, reqs, p.out) 133 } 134 135 var dest string 136 if p.destination == "." { 137 // Save to the current working directory. 138 dest, err = os.Getwd() 139 if err != nil { 140 return err 141 } 142 } else { 143 // Otherwise save to set destination 144 dest = p.destination 145 } 146 147 name, err := chartutil.Save(ch, dest) 148 if err == nil && flagDebug { 149 fmt.Fprintf(p.out, "Saved %s to current directory\n", name) 150 } 151 152 // Save to $HELM_HOME/local directory. This is second, because we don't want 153 // the case where we saved here, but didn't save to the default destination. 154 if p.save { 155 lr := p.home.LocalRepository() 156 if err := repo.AddChartToLocalRepo(ch, lr); err != nil { 157 return err 158 } else if flagDebug { 159 fmt.Fprintf(p.out, "Saved %s to %s\n", name, lr) 160 } 161 } 162 163 if p.sign { 164 err = p.clearsign(name) 165 } 166 167 return err 168 } 169 170 func setVersion(ch *chart.Chart, ver string) error { 171 // Verify that version is a SemVer, and error out if it is not. 172 if _, err := semver.NewVersion(ver); err != nil { 173 return err 174 } 175 176 // Set the version field on the chart. 177 ch.Metadata.Version = ver 178 return nil 179 } 180 181 func (p *packageCmd) clearsign(filename string) error { 182 // Load keyring 183 signer, err := provenance.NewFromKeyring(p.keyring, p.key) 184 if err != nil { 185 return err 186 } 187 188 if err := signer.DecryptKey(promptUser); err != nil { 189 return err 190 } 191 192 sig, err := signer.ClearSign(filename) 193 if err != nil { 194 return err 195 } 196 197 if flagDebug { 198 fmt.Fprintln(p.out, sig) 199 } 200 201 return ioutil.WriteFile(filename+".prov", []byte(sig), 0755) 202 } 203 204 // promptUser implements provenance.PassphraseFetcher 205 func promptUser(name string) ([]byte, error) { 206 fmt.Printf("Password for key %q > ", name) 207 pw, err := terminal.ReadPassword(int(syscall.Stdin)) 208 fmt.Println() 209 return pw, err 210 }