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  }