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