github.com/rakanixu/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  }