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