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