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  }