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