github.com/latiif/helm@v2.15.0+incompatible/cmd/helm/helm.go (about)

     1  /*
     2  Copyright The Helm Authors.
     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  	"fmt"
    21  	"io/ioutil"
    22  	"log"
    23  	"os"
    24  	"strings"
    25  
    26  	"github.com/spf13/cobra"
    27  	"google.golang.org/grpc/grpclog"
    28  	"google.golang.org/grpc/status"
    29  	"k8s.io/client-go/kubernetes"
    30  	"k8s.io/client-go/rest"
    31  
    32  	// Import to initialize client auth plugins.
    33  	_ "k8s.io/client-go/plugin/pkg/client/auth"
    34  
    35  	"k8s.io/helm/pkg/helm"
    36  	helm_env "k8s.io/helm/pkg/helm/environment"
    37  	"k8s.io/helm/pkg/helm/portforwarder"
    38  	"k8s.io/helm/pkg/kube"
    39  	"k8s.io/helm/pkg/tlsutil"
    40  )
    41  
    42  const (
    43  	bashCompletionFunc = `
    44  __helm_override_flag_list=(--kubeconfig --kube-context --host --tiller-namespace --home)
    45  __helm_override_flags()
    46  {
    47      local ${__helm_override_flag_list[*]##*-} two_word_of of var
    48      for w in "${words[@]}"; do
    49          if [ -n "${two_word_of}" ]; then
    50              eval "${two_word_of##*-}=\"${two_word_of}=\${w}\""
    51              two_word_of=
    52              continue
    53          fi
    54          for of in "${__helm_override_flag_list[@]}"; do
    55              case "${w}" in
    56                  ${of}=*)
    57                      eval "${of##*-}=\"${w}\""
    58                      ;;
    59                  ${of})
    60                      two_word_of="${of}"
    61                      ;;
    62              esac
    63          done
    64      done
    65      for var in "${__helm_override_flag_list[@]##*-}"; do
    66          if eval "test -n \"\$${var}\""; then
    67              eval "echo \${${var}}"
    68          fi
    69      done
    70  }
    71  
    72  __helm_binary_name()
    73  {
    74      local helm_binary
    75      helm_binary="${words[0]}"
    76      __helm_debug "${FUNCNAME[0]}: helm_binary is ${helm_binary}"
    77      echo ${helm_binary}
    78  }
    79  
    80  __helm_list_releases()
    81  {
    82      __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
    83      local out filter
    84      # Use ^ to map from the start of the release name
    85      filter="^${words[c]}"
    86      # Use eval in case helm_binary_name or __helm_override_flags contains a variable (e.g., $HOME/bin/h2)
    87      if out=$(eval $(__helm_binary_name) list $(__helm_override_flags) -a -q -m 1000 ${filter} 2>/dev/null); then
    88          COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
    89      fi
    90  }
    91  
    92  __helm_list_repos()
    93  {
    94      __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
    95      local out oflags
    96      oflags=$(__helm_override_flags)
    97      __helm_debug "${FUNCNAME[0]}: __helm_override_flags are ${oflags}"
    98      # Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h2)
    99      if out=$(eval $(__helm_binary_name) repo list ${oflags} 2>/dev/null | tail +2 | cut -f1); then
   100          COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
   101      fi
   102  }
   103  
   104  __helm_list_plugins()
   105  {
   106      __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
   107      local out oflags
   108      oflags=$(__helm_override_flags)
   109      __helm_debug "${FUNCNAME[0]}: __helm_override_flags are ${oflags}"
   110      # Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h2)
   111      if out=$(eval $(__helm_binary_name) plugin list ${oflags} 2>/dev/null | tail +2 | cut -f1); then
   112          COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
   113      fi
   114  }
   115  
   116  __helm_custom_func()
   117  {
   118      __helm_debug "${FUNCNAME[0]}: c is $c words[@] is ${words[@]}"
   119      case ${last_command} in
   120          helm_delete | helm_history | helm_status | helm_test |\
   121          helm_upgrade | helm_rollback | helm_get_*)
   122              __helm_list_releases
   123              return
   124              ;;
   125          helm_repo_remove | helm_repo_update)
   126              __helm_list_repos
   127              return
   128              ;;
   129          helm_plugin_remove | helm_plugin_update)
   130              __helm_list_plugins
   131              return
   132              ;;
   133          *)
   134              ;;
   135      esac
   136  }
   137  `
   138  )
   139  
   140  var (
   141  	tillerTunnel *kube.Tunnel
   142  	settings     helm_env.EnvSettings
   143  )
   144  
   145  var globalUsage = `The Kubernetes package manager
   146  
   147  To begin working with Helm, run the 'helm init' command:
   148  
   149  	$ helm init
   150  
   151  This will install Tiller to your running Kubernetes cluster.
   152  It will also set up any necessary local configuration.
   153  
   154  Common actions from this point include:
   155  
   156  - helm search:    Search for charts
   157  - helm fetch:     Download a chart to your local directory to view
   158  - helm install:   Upload the chart to Kubernetes
   159  - helm list:      List releases of charts
   160  
   161  Environment:
   162  
   163  - $HELM_HOME:           Set an alternative location for Helm files. By default, these are stored in ~/.helm
   164  - $HELM_HOST:           Set an alternative Tiller host. The format is host:port
   165  - $HELM_NO_PLUGINS:     Disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins.
   166  - $TILLER_NAMESPACE:    Set an alternative Tiller namespace (default "kube-system")
   167  - $KUBECONFIG:          Set an alternative Kubernetes configuration file (default "~/.kube/config")
   168  - $HELM_TLS_CA_CERT:    Path to TLS CA certificate used to verify the Helm client and Tiller server certificates (default "$HELM_HOME/ca.pem")
   169  - $HELM_TLS_CERT:       Path to TLS client certificate file for authenticating to Tiller (default "$HELM_HOME/cert.pem")
   170  - $HELM_TLS_KEY:        Path to TLS client key file for authenticating to Tiller (default "$HELM_HOME/key.pem")
   171  - $HELM_TLS_ENABLE:     Enable TLS connection between Helm and Tiller (default "false")
   172  - $HELM_TLS_VERIFY:     Enable TLS connection between Helm and Tiller and verify Tiller server certificate (default "false")
   173  - $HELM_TLS_HOSTNAME:   The hostname or IP address used to verify the Tiller server certificate (default "127.0.0.1")
   174  - $HELM_KEY_PASSPHRASE: Set HELM_KEY_PASSPHRASE to the passphrase of your PGP private key. If set, you will not be prompted for the passphrase while signing helm charts
   175  
   176  `
   177  
   178  func newRootCmd(args []string) *cobra.Command {
   179  	cmd := &cobra.Command{
   180  		Use:          "helm",
   181  		Short:        "The Helm package manager for Kubernetes.",
   182  		Long:         globalUsage,
   183  		SilenceUsage: true,
   184  		PersistentPreRun: func(*cobra.Command, []string) {
   185  			if settings.TLSCaCertFile == helm_env.DefaultTLSCaCert || settings.TLSCaCertFile == "" {
   186  				settings.TLSCaCertFile = settings.Home.TLSCaCert()
   187  			} else {
   188  				settings.TLSCaCertFile = os.ExpandEnv(settings.TLSCaCertFile)
   189  			}
   190  			if settings.TLSCertFile == helm_env.DefaultTLSCert || settings.TLSCertFile == "" {
   191  				settings.TLSCertFile = settings.Home.TLSCert()
   192  			} else {
   193  				settings.TLSCertFile = os.ExpandEnv(settings.TLSCertFile)
   194  			}
   195  			if settings.TLSKeyFile == helm_env.DefaultTLSKeyFile || settings.TLSKeyFile == "" {
   196  				settings.TLSKeyFile = settings.Home.TLSKey()
   197  			} else {
   198  				settings.TLSKeyFile = os.ExpandEnv(settings.TLSKeyFile)
   199  			}
   200  		},
   201  		PersistentPostRun: func(*cobra.Command, []string) {
   202  			teardown()
   203  		},
   204  		BashCompletionFunction: bashCompletionFunc,
   205  	}
   206  	flags := cmd.PersistentFlags()
   207  
   208  	settings.AddFlags(flags)
   209  
   210  	out := cmd.OutOrStdout()
   211  
   212  	cmd.AddCommand(
   213  		// chart commands
   214  		newCreateCmd(out),
   215  		newDependencyCmd(out),
   216  		newFetchCmd(out),
   217  		newInspectCmd(out),
   218  		newLintCmd(out),
   219  		newPackageCmd(out),
   220  		newRepoCmd(out),
   221  		newSearchCmd(out),
   222  		newServeCmd(out),
   223  		newVerifyCmd(out),
   224  
   225  		// release commands
   226  		newDeleteCmd(nil, out),
   227  		newGetCmd(nil, out),
   228  		newHistoryCmd(nil, out),
   229  		newInstallCmd(nil, out),
   230  		newListCmd(nil, out),
   231  		newRollbackCmd(nil, out),
   232  		newStatusCmd(nil, out),
   233  		newUpgradeCmd(nil, out),
   234  
   235  		newReleaseTestCmd(nil, out),
   236  		newResetCmd(nil, out),
   237  		newVersionCmd(nil, out),
   238  
   239  		newCompletionCmd(out),
   240  		newHomeCmd(out),
   241  		newInitCmd(out),
   242  		newPluginCmd(out),
   243  		newTemplateCmd(out),
   244  
   245  		// Hidden documentation generator command: 'helm docs'
   246  		newDocsCmd(out),
   247  
   248  		// Deprecated
   249  		markDeprecated(newRepoUpdateCmd(out), "Use 'helm repo update'\n"),
   250  	)
   251  
   252  	flags.Parse(args)
   253  
   254  	// set defaults from environment
   255  	settings.Init(flags)
   256  
   257  	// Find and add plugins
   258  	loadPlugins(cmd, out)
   259  
   260  	return cmd
   261  }
   262  
   263  func init() {
   264  	// Tell gRPC not to log to console.
   265  	grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags))
   266  }
   267  
   268  func main() {
   269  	cmd := newRootCmd(os.Args[1:])
   270  	if err := cmd.Execute(); err != nil {
   271  		switch e := err.(type) {
   272  		case pluginError:
   273  			os.Exit(e.code)
   274  		default:
   275  			os.Exit(1)
   276  		}
   277  	}
   278  }
   279  
   280  func markDeprecated(cmd *cobra.Command, notice string) *cobra.Command {
   281  	cmd.Deprecated = notice
   282  	return cmd
   283  }
   284  
   285  func setupConnection() error {
   286  	if settings.TillerHost == "" {
   287  		config, client, err := getKubeClient(settings.KubeContext, settings.KubeConfig)
   288  		if err != nil {
   289  			return err
   290  		}
   291  
   292  		tillerTunnel, err = portforwarder.New(settings.TillerNamespace, client, config)
   293  		if err != nil {
   294  			return err
   295  		}
   296  
   297  		settings.TillerHost = fmt.Sprintf("127.0.0.1:%d", tillerTunnel.Local)
   298  		debug("Created tunnel using local port: '%d'\n", tillerTunnel.Local)
   299  	}
   300  
   301  	// Set up the gRPC config.
   302  	debug("SERVER: %q\n", settings.TillerHost)
   303  
   304  	// Plugin support.
   305  	return nil
   306  }
   307  
   308  func teardown() {
   309  	if tillerTunnel != nil {
   310  		tillerTunnel.Close()
   311  	}
   312  }
   313  
   314  func checkArgsLength(argsReceived int, requiredArgs ...string) error {
   315  	expectedNum := len(requiredArgs)
   316  	if argsReceived != expectedNum {
   317  		arg := "arguments"
   318  		if expectedNum == 1 {
   319  			arg = "argument"
   320  		}
   321  		return fmt.Errorf("This command needs %v %s: %s", expectedNum, arg, strings.Join(requiredArgs, ", "))
   322  	}
   323  	return nil
   324  }
   325  
   326  // prettyError unwraps or rewrites certain errors to make them more user-friendly.
   327  func prettyError(err error) error {
   328  	// Add this check can prevent the object creation if err is nil.
   329  	if err == nil {
   330  		return nil
   331  	}
   332  	// If it's grpc's error, make it more user-friendly.
   333  	if s, ok := status.FromError(err); ok {
   334  		return fmt.Errorf(s.Message())
   335  	}
   336  	// Else return the original error.
   337  	return err
   338  }
   339  
   340  // configForContext creates a Kubernetes REST client configuration for a given kubeconfig context.
   341  func configForContext(context string, kubeconfig string) (*rest.Config, error) {
   342  	config, err := kube.GetConfig(context, kubeconfig).ClientConfig()
   343  	if err != nil {
   344  		return nil, fmt.Errorf("could not get Kubernetes config for context %q: %s", context, err)
   345  	}
   346  	return config, nil
   347  }
   348  
   349  // getKubeClient creates a Kubernetes config and client for a given kubeconfig context.
   350  func getKubeClient(context string, kubeconfig string) (*rest.Config, kubernetes.Interface, error) {
   351  	config, err := configForContext(context, kubeconfig)
   352  	if err != nil {
   353  		return nil, nil, err
   354  	}
   355  	client, err := kubernetes.NewForConfig(config)
   356  	if err != nil {
   357  		return nil, nil, fmt.Errorf("could not get Kubernetes client: %s", err)
   358  	}
   359  	return config, client, nil
   360  }
   361  
   362  // ensureHelmClient returns a new helm client impl. if h is not nil.
   363  func ensureHelmClient(h helm.Interface) helm.Interface {
   364  	if h != nil {
   365  		return h
   366  	}
   367  	return newClient()
   368  }
   369  
   370  func newClient() helm.Interface {
   371  	options := []helm.Option{helm.Host(settings.TillerHost), helm.ConnectTimeout(settings.TillerConnectionTimeout)}
   372  
   373  	if settings.TLSVerify || settings.TLSEnable {
   374  		debug("Host=%q, Key=%q, Cert=%q, CA=%q\n", settings.TLSServerName, settings.TLSKeyFile, settings.TLSCertFile, settings.TLSCaCertFile)
   375  		tlsopts := tlsutil.Options{
   376  			ServerName:         settings.TLSServerName,
   377  			KeyFile:            settings.TLSKeyFile,
   378  			CertFile:           settings.TLSCertFile,
   379  			InsecureSkipVerify: true,
   380  		}
   381  		if settings.TLSVerify {
   382  			tlsopts.CaCertFile = settings.TLSCaCertFile
   383  			tlsopts.InsecureSkipVerify = false
   384  		}
   385  		tlscfg, err := tlsutil.ClientConfig(tlsopts)
   386  		if err != nil {
   387  			fmt.Fprintln(os.Stderr, err)
   388  			os.Exit(2)
   389  		}
   390  		options = append(options, helm.WithTLS(tlscfg))
   391  	}
   392  	return helm.NewClient(options...)
   393  }