github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/action/package.go (about) 1 /* 2 Copyright The Helm Authors. 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 action 18 19 import ( 20 "bufio" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "syscall" 25 26 "github.com/Masterminds/semver/v3" 27 "github.com/pkg/errors" 28 "golang.org/x/term" 29 30 "github.com/stefanmcshane/helm/pkg/chart/loader" 31 "github.com/stefanmcshane/helm/pkg/chartutil" 32 "github.com/stefanmcshane/helm/pkg/provenance" 33 ) 34 35 // Package is the action for packaging a chart. 36 // 37 // It provides the implementation of 'helm package'. 38 type Package struct { 39 Sign bool 40 Key string 41 Keyring string 42 PassphraseFile string 43 Version string 44 AppVersion string 45 Destination string 46 DependencyUpdate bool 47 48 RepositoryConfig string 49 RepositoryCache string 50 } 51 52 // NewPackage creates a new Package object with the given configuration. 53 func NewPackage() *Package { 54 return &Package{} 55 } 56 57 // Run executes 'helm package' against the given chart and returns the path to the packaged chart. 58 func (p *Package) Run(path string, vals map[string]interface{}) (string, error) { 59 ch, err := loader.LoadDir(path) 60 if err != nil { 61 return "", err 62 } 63 64 // If version is set, modify the version. 65 if p.Version != "" { 66 ch.Metadata.Version = p.Version 67 } 68 69 if err := validateVersion(ch.Metadata.Version); err != nil { 70 return "", err 71 } 72 73 if p.AppVersion != "" { 74 ch.Metadata.AppVersion = p.AppVersion 75 } 76 77 if reqs := ch.Metadata.Dependencies; reqs != nil { 78 if err := CheckDependencies(ch, reqs); err != nil { 79 return "", err 80 } 81 } 82 83 var dest string 84 if p.Destination == "." { 85 // Save to the current working directory. 86 dest, err = os.Getwd() 87 if err != nil { 88 return "", err 89 } 90 } else { 91 // Otherwise save to set destination 92 dest = p.Destination 93 } 94 95 name, err := chartutil.Save(ch, dest) 96 if err != nil { 97 return "", errors.Wrap(err, "failed to save") 98 } 99 100 if p.Sign { 101 err = p.Clearsign(name) 102 } 103 104 return name, err 105 } 106 107 // validateVersion Verify that version is a Version, and error out if it is not. 108 func validateVersion(ver string) error { 109 if _, err := semver.NewVersion(ver); err != nil { 110 return err 111 } 112 return nil 113 } 114 115 // Clearsign signs a chart 116 func (p *Package) Clearsign(filename string) error { 117 // Load keyring 118 signer, err := provenance.NewFromKeyring(p.Keyring, p.Key) 119 if err != nil { 120 return err 121 } 122 123 passphraseFetcher := promptUser 124 if p.PassphraseFile != "" { 125 passphraseFetcher, err = passphraseFileFetcher(p.PassphraseFile, os.Stdin) 126 if err != nil { 127 return err 128 } 129 } 130 131 if err := signer.DecryptKey(passphraseFetcher); err != nil { 132 return err 133 } 134 135 sig, err := signer.ClearSign(filename) 136 if err != nil { 137 return err 138 } 139 140 return ioutil.WriteFile(filename+".prov", []byte(sig), 0644) 141 } 142 143 // promptUser implements provenance.PassphraseFetcher 144 func promptUser(name string) ([]byte, error) { 145 fmt.Printf("Password for key %q > ", name) 146 // syscall.Stdin is not an int in all environments and needs to be coerced 147 // into one there (e.g., Windows) 148 pw, err := term.ReadPassword(int(syscall.Stdin)) 149 fmt.Println() 150 return pw, err 151 } 152 153 func passphraseFileFetcher(passphraseFile string, stdin *os.File) (provenance.PassphraseFetcher, error) { 154 file, err := openPassphraseFile(passphraseFile, stdin) 155 if err != nil { 156 return nil, err 157 } 158 defer file.Close() 159 160 reader := bufio.NewReader(file) 161 passphrase, _, err := reader.ReadLine() 162 if err != nil { 163 return nil, err 164 } 165 return func(name string) ([]byte, error) { 166 return passphrase, nil 167 }, nil 168 } 169 170 func openPassphraseFile(passphraseFile string, stdin *os.File) (*os.File, error) { 171 if passphraseFile == "-" { 172 stat, err := stdin.Stat() 173 if err != nil { 174 return nil, err 175 } 176 if (stat.Mode() & os.ModeNamedPipe) == 0 { 177 return nil, errors.New("specified reading passphrase from stdin, without input on stdin") 178 } 179 return stdin, nil 180 } 181 return os.Open(passphraseFile) 182 }