github.com/sgoings/helm@v2.0.0-alpha.2.0.20170406211108-734e92851ac3+incompatible/cmd/helm/helm.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 // import "k8s.io/helm/cmd/helm" 18 19 import ( 20 "errors" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "log" 25 "os" 26 "path/filepath" 27 "strings" 28 29 "github.com/spf13/cobra" 30 "google.golang.org/grpc" 31 "google.golang.org/grpc/grpclog" 32 "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" 33 "k8s.io/kubernetes/pkg/client/restclient" 34 35 "k8s.io/helm/pkg/helm" 36 "k8s.io/helm/pkg/helm/helmpath" 37 "k8s.io/helm/pkg/helm/portforwarder" 38 "k8s.io/helm/pkg/kube" 39 "k8s.io/helm/pkg/tiller/environment" 40 "k8s.io/helm/pkg/tlsutil" 41 ) 42 43 const ( 44 localRepoIndexFilePath = "index.yaml" 45 homeEnvVar = "HELM_HOME" 46 hostEnvVar = "HELM_HOST" 47 tillerNamespaceEnvVar = "TILLER_NAMESPACE" 48 ) 49 50 var ( 51 tlsCaCertFile string // path to TLS CA certificate file 52 tlsCertFile string // path to TLS certificate file 53 tlsKeyFile string // path to TLS key file 54 tlsVerify bool // enable TLS and verify remote certificates 55 tlsEnable bool // enable TLS 56 ) 57 58 var ( 59 helmHome string 60 tillerHost string 61 tillerNamespace string 62 kubeContext string 63 // TODO refactor out this global var 64 tillerTunnel *kube.Tunnel 65 ) 66 67 // flagDebug is a signal that the user wants additional output. 68 var flagDebug bool 69 70 var globalUsage = `The Kubernetes package manager 71 72 To begin working with Helm, run the 'helm init' command: 73 74 $ helm init 75 76 This will install Tiller to your running Kubernetes cluster. 77 It will also set up any necessary local configuration. 78 79 Common actions from this point include: 80 81 - helm search: search for charts 82 - helm fetch: download a chart to your local directory to view 83 - helm install: upload the chart to Kubernetes 84 - helm list: list releases of charts 85 86 Environment: 87 $HELM_HOME set an alternative location for Helm files. By default, these are stored in ~/.helm 88 $HELM_HOST set an alternative Tiller host. The format is host:port 89 $HELM_NO_PLUGINS disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. 90 $TILLER_NAMESPACE set an alternative Tiller namespace (default "kube-namespace") 91 $KUBECONFIG set an alternative Kubernetes configuration file (default "~/.kube/config") 92 ` 93 94 func newRootCmd(out io.Writer) *cobra.Command { 95 cmd := &cobra.Command{ 96 Use: "helm", 97 Short: "The Helm package manager for Kubernetes.", 98 Long: globalUsage, 99 SilenceUsage: true, 100 PersistentPreRun: func(cmd *cobra.Command, args []string) { 101 tlsCaCertFile = os.ExpandEnv(tlsCaCertFile) 102 tlsCertFile = os.ExpandEnv(tlsCertFile) 103 tlsKeyFile = os.ExpandEnv(tlsKeyFile) 104 }, 105 PersistentPostRun: func(cmd *cobra.Command, args []string) { 106 teardown() 107 }, 108 } 109 p := cmd.PersistentFlags() 110 p.StringVar(&helmHome, "home", defaultHelmHome(), "location of your Helm config. Overrides $HELM_HOME") 111 p.StringVar(&tillerHost, "host", defaultHelmHost(), "address of tiller. Overrides $HELM_HOST") 112 p.StringVar(&kubeContext, "kube-context", "", "name of the kubeconfig context to use") 113 p.BoolVar(&flagDebug, "debug", false, "enable verbose output") 114 p.StringVar(&tillerNamespace, "tiller-namespace", defaultTillerNamespace(), "namespace of tiller") 115 116 cmd.AddCommand( 117 // chart commands 118 newCreateCmd(out), 119 newDependencyCmd(out), 120 newFetchCmd(out), 121 newInspectCmd(out), 122 newLintCmd(out), 123 newPackageCmd(out), 124 newRepoCmd(out), 125 newSearchCmd(out), 126 newServeCmd(out), 127 newVerifyCmd(out), 128 129 // release commands 130 addFlagsTLS(newDeleteCmd(nil, out)), 131 addFlagsTLS(newGetCmd(nil, out)), 132 addFlagsTLS(newHistoryCmd(nil, out)), 133 addFlagsTLS(newInstallCmd(nil, out)), 134 addFlagsTLS(newListCmd(nil, out)), 135 addFlagsTLS(newRollbackCmd(nil, out)), 136 addFlagsTLS(newStatusCmd(nil, out)), 137 addFlagsTLS(newUpgradeCmd(nil, out)), 138 139 addFlagsTLS(newReleaseTestCmd(nil, out)), 140 addFlagsTLS(newResetCmd(nil, out)), 141 addFlagsTLS(newVersionCmd(nil, out)), 142 newCompletionCmd(out), 143 newHomeCmd(out), 144 newInitCmd(out), 145 newResetCmd(nil, out), 146 newVersionCmd(nil, out), 147 newReleaseTestCmd(nil, out), 148 newPluginCmd(out), 149 150 // Hidden documentation generator command: 'helm docs' 151 newDocsCmd(out), 152 153 // Deprecated 154 markDeprecated(newRepoUpdateCmd(out), "use 'helm repo update'\n"), 155 ) 156 157 // Find and add plugins 158 loadPlugins(cmd, helmpath.Home(homePath()), out) 159 160 return cmd 161 } 162 163 func init() { 164 // Tell gRPC not to log to console. 165 grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags)) 166 } 167 168 func main() { 169 cmd := newRootCmd(os.Stdout) 170 if err := cmd.Execute(); err != nil { 171 os.Exit(1) 172 } 173 } 174 175 func markDeprecated(cmd *cobra.Command, notice string) *cobra.Command { 176 cmd.Deprecated = notice 177 return cmd 178 } 179 180 func setupConnection(c *cobra.Command, args []string) error { 181 if tillerHost == "" { 182 config, client, err := getKubeClient(kubeContext) 183 if err != nil { 184 return err 185 } 186 187 tunnel, err := portforwarder.New(tillerNamespace, client, config) 188 if err != nil { 189 return err 190 } 191 192 tillerHost = fmt.Sprintf("localhost:%d", tunnel.Local) 193 if flagDebug { 194 fmt.Printf("Created tunnel using local port: '%d'\n", tunnel.Local) 195 } 196 } 197 198 // Set up the gRPC config. 199 if flagDebug { 200 fmt.Printf("SERVER: %q\n", tillerHost) 201 } 202 // Plugin support. 203 return nil 204 } 205 206 func teardown() { 207 if tillerTunnel != nil { 208 tillerTunnel.Close() 209 } 210 } 211 212 func checkArgsLength(argsReceived int, requiredArgs ...string) error { 213 expectedNum := len(requiredArgs) 214 if argsReceived != expectedNum { 215 arg := "arguments" 216 if expectedNum == 1 { 217 arg = "argument" 218 } 219 return fmt.Errorf("This command needs %v %s: %s", expectedNum, arg, strings.Join(requiredArgs, ", ")) 220 } 221 return nil 222 } 223 224 // prettyError unwraps or rewrites certain errors to make them more user-friendly. 225 func prettyError(err error) error { 226 if err == nil { 227 return nil 228 } 229 // This is ridiculous. Why is 'grpc.rpcError' not exported? The least they 230 // could do is throw an interface on the lib that would let us get back 231 // the desc. Instead, we have to pass ALL errors through this. 232 return errors.New(grpc.ErrorDesc(err)) 233 } 234 235 func defaultHelmHome() string { 236 if home := os.Getenv(homeEnvVar); home != "" { 237 return home 238 } 239 return filepath.Join(os.Getenv("HOME"), ".helm") 240 } 241 242 func homePath() string { 243 s := os.ExpandEnv(helmHome) 244 os.Setenv(homeEnvVar, s) 245 return s 246 } 247 248 func defaultHelmHost() string { 249 return os.Getenv(hostEnvVar) 250 } 251 252 func defaultTillerNamespace() string { 253 if ns := os.Getenv(tillerNamespaceEnvVar); ns != "" { 254 return ns 255 } 256 return environment.DefaultTillerNamespace 257 } 258 259 // getKubeClient is a convenience method for creating kubernetes config and client 260 // for a given kubeconfig context 261 func getKubeClient(context string) (*restclient.Config, *internalclientset.Clientset, error) { 262 config, err := kube.GetConfig(context).ClientConfig() 263 if err != nil { 264 return nil, nil, fmt.Errorf("could not get kubernetes config for context '%s': %s", context, err) 265 } 266 client, err := internalclientset.NewForConfig(config) 267 if err != nil { 268 return nil, nil, fmt.Errorf("could not get kubernetes client: %s", err) 269 } 270 return config, client, nil 271 } 272 273 // getKubeCmd is a convenience method for creating kubernetes cmd client 274 // for a given kubeconfig context 275 func getKubeCmd(context string) *kube.Client { 276 return kube.New(kube.GetConfig(context)) 277 } 278 279 // ensureHelmClient returns a new helm client impl. if h is not nil. 280 func ensureHelmClient(h helm.Interface) helm.Interface { 281 if h != nil { 282 return h 283 } 284 return newClient() 285 } 286 287 func newClient() helm.Interface { 288 options := []helm.Option{helm.Host(tillerHost)} 289 290 if tlsVerify || tlsEnable { 291 tlsopts := tlsutil.Options{KeyFile: tlsKeyFile, CertFile: tlsCertFile, InsecureSkipVerify: true} 292 if tlsVerify { 293 tlsopts.CaCertFile = tlsCaCertFile 294 tlsopts.InsecureSkipVerify = false 295 } 296 tlscfg, err := tlsutil.ClientConfig(tlsopts) 297 if err != nil { 298 fmt.Fprintln(os.Stderr, err) 299 os.Exit(2) 300 } 301 options = append(options, helm.WithTLS(tlscfg)) 302 } 303 return helm.NewClient(options...) 304 } 305 306 // addFlagsTLS adds the flags for supporting client side TLS to the 307 // helm command (only those that invoke communicate to Tiller.) 308 func addFlagsTLS(cmd *cobra.Command) *cobra.Command { 309 // defaults 310 var ( 311 tlsCaCertDefault = "$HELM_HOME/ca.pem" 312 tlsCertDefault = "$HELM_HOME/cert.pem" 313 tlsKeyDefault = "$HELM_HOME/key.pem" 314 ) 315 316 // add flags 317 cmd.Flags().StringVar(&tlsCaCertFile, "tls-ca-cert", tlsCaCertDefault, "path to TLS CA certificate file") 318 cmd.Flags().StringVar(&tlsCertFile, "tls-cert", tlsCertDefault, "path to TLS certificate file") 319 cmd.Flags().StringVar(&tlsKeyFile, "tls-key", tlsKeyDefault, "path to TLS key file") 320 cmd.Flags().BoolVar(&tlsVerify, "tls-verify", false, "enable TLS for request and verify remote") 321 cmd.Flags().BoolVar(&tlsEnable, "tls", false, "enable TLS for request") 322 return cmd 323 }