github.com/canthefason/helm@v2.2.1-0.20170221172616-16b043b8d505+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"
    23  	"io/ioutil"
    24  	"log"
    25  	"os"
    26  	"path/filepath"
    27  	"strings"
    28  
    29  	"github.com/spf13/cobra"
    30  	"google.golang.org/grpc"
    31  	"google.golang.org/grpc/grpclog"
    32  	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
    33  	"k8s.io/kubernetes/pkg/client/restclient"
    34  
    35  	"k8s.io/helm/cmd/helm/helmpath"
    36  	"k8s.io/helm/pkg/helm/portforwarder"
    37  	"k8s.io/helm/pkg/kube"
    38  	"k8s.io/helm/pkg/tiller/environment"
    39  )
    40  
    41  const (
    42  	localRepoIndexFilePath = "index.yaml"
    43  	homeEnvVar             = "HELM_HOME"
    44  	hostEnvVar             = "HELM_HOST"
    45  	tillerNamespaceEnvVar  = "TILLER_NAMESPACE"
    46  )
    47  
    48  var (
    49  	helmHome        string
    50  	tillerHost      string
    51  	tillerNamespace string
    52  	kubeContext     string
    53  	// TODO refactor out this global var
    54  	tillerTunnel *kube.Tunnel
    55  )
    56  
    57  // flagDebug is a signal that the user wants additional output.
    58  var flagDebug bool
    59  
    60  var globalUsage = `The Kubernetes package manager
    61  
    62  To begin working with Helm, run the 'helm init' command:
    63  
    64  	$ helm init
    65  
    66  This will install Tiller to your running Kubernetes cluster.
    67  It will also set up any necessary local configuration.
    68  
    69  Common actions from this point include:
    70  
    71  - helm search:    search for charts
    72  - helm fetch:     download a chart to your local directory to view
    73  - helm install:   upload the chart to Kubernetes
    74  - helm list:      list releases of charts
    75  
    76  Environment:
    77    $HELM_HOME          set an alternative location for Helm files. By default, these are stored in ~/.helm
    78    $HELM_HOST          set an alternative Tiller host. The format is host:port
    79    $TILLER_NAMESPACE   set an alternative Tiller namespace (default "kube-namespace")
    80    $KUBECONFIG         set an alternative Kubernetes configuration file (default "~/.kube/config")
    81  `
    82  
    83  func newRootCmd(out io.Writer) *cobra.Command {
    84  	cmd := &cobra.Command{
    85  		Use:          "helm",
    86  		Short:        "The Helm package manager for Kubernetes.",
    87  		Long:         globalUsage,
    88  		SilenceUsage: true,
    89  		PersistentPostRun: func(cmd *cobra.Command, args []string) {
    90  			teardown()
    91  		},
    92  	}
    93  	p := cmd.PersistentFlags()
    94  	p.StringVar(&helmHome, "home", defaultHelmHome(), "location of your Helm config. Overrides $HELM_HOME")
    95  	p.StringVar(&tillerHost, "host", defaultHelmHost(), "address of tiller. Overrides $HELM_HOST")
    96  	p.StringVar(&kubeContext, "kube-context", "", "name of the kubeconfig context to use")
    97  	p.BoolVar(&flagDebug, "debug", false, "enable verbose output")
    98  	p.StringVar(&tillerNamespace, "tiller-namespace", defaultTillerNamespace(), "namespace of tiller")
    99  
   100  	cmd.AddCommand(
   101  		// chart commands
   102  		newCreateCmd(out),
   103  		newDependencyCmd(out),
   104  		newFetchCmd(out),
   105  		newInspectCmd(out),
   106  		newLintCmd(out),
   107  		newPackageCmd(out),
   108  		newRepoCmd(out),
   109  		newSearchCmd(out),
   110  		newServeCmd(out),
   111  		newVerifyCmd(out),
   112  
   113  		// release commands
   114  		newDeleteCmd(nil, out),
   115  		newGetCmd(nil, out),
   116  		newHistoryCmd(nil, out),
   117  		newInstallCmd(nil, out),
   118  		newListCmd(nil, out),
   119  		newRollbackCmd(nil, out),
   120  		newStatusCmd(nil, out),
   121  		newUpgradeCmd(nil, out),
   122  
   123  		newCompletionCmd(out),
   124  		newHomeCmd(out),
   125  		newInitCmd(out),
   126  		newResetCmd(nil, out),
   127  		newVersionCmd(nil, out),
   128  		newReleaseTestCmd(nil, out),
   129  
   130  		// Hidden documentation generator command: 'helm docs'
   131  		newDocsCmd(out),
   132  
   133  		// Deprecated
   134  		markDeprecated(newRepoUpdateCmd(out), "use 'helm repo update'\n"),
   135  	)
   136  
   137  	// Find and add plugins
   138  	loadPlugins(cmd, helmpath.Home(homePath()), out)
   139  
   140  	return cmd
   141  }
   142  
   143  func init() {
   144  	// Tell gRPC not to log to console.
   145  	grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags))
   146  }
   147  
   148  func main() {
   149  	cmd := newRootCmd(os.Stdout)
   150  	if err := cmd.Execute(); err != nil {
   151  		os.Exit(1)
   152  	}
   153  }
   154  
   155  func markDeprecated(cmd *cobra.Command, notice string) *cobra.Command {
   156  	cmd.Deprecated = notice
   157  	return cmd
   158  }
   159  
   160  func setupConnection(c *cobra.Command, args []string) error {
   161  	if tillerHost == "" {
   162  		config, client, err := getKubeClient(kubeContext)
   163  		if err != nil {
   164  			return err
   165  		}
   166  
   167  		tunnel, err := portforwarder.New(tillerNamespace, client, config)
   168  		if err != nil {
   169  			return err
   170  		}
   171  
   172  		tillerHost = fmt.Sprintf("localhost:%d", tunnel.Local)
   173  		if flagDebug {
   174  			fmt.Printf("Created tunnel using local port: '%d'\n", tunnel.Local)
   175  		}
   176  	}
   177  
   178  	// Set up the gRPC config.
   179  	if flagDebug {
   180  		fmt.Printf("SERVER: %q\n", tillerHost)
   181  	}
   182  	// Plugin support.
   183  	return nil
   184  }
   185  
   186  func teardown() {
   187  	if tillerTunnel != nil {
   188  		tillerTunnel.Close()
   189  	}
   190  }
   191  
   192  func checkArgsLength(argsReceived int, requiredArgs ...string) error {
   193  	expectedNum := len(requiredArgs)
   194  	if argsReceived != expectedNum {
   195  		arg := "arguments"
   196  		if expectedNum == 1 {
   197  			arg = "argument"
   198  		}
   199  		return fmt.Errorf("This command needs %v %s: %s", expectedNum, arg, strings.Join(requiredArgs, ", "))
   200  	}
   201  	return nil
   202  }
   203  
   204  // prettyError unwraps or rewrites certain errors to make them more user-friendly.
   205  func prettyError(err error) error {
   206  	if err == nil {
   207  		return nil
   208  	}
   209  	// This is ridiculous. Why is 'grpc.rpcError' not exported? The least they
   210  	// could do is throw an interface on the lib that would let us get back
   211  	// the desc. Instead, we have to pass ALL errors through this.
   212  	return errors.New(grpc.ErrorDesc(err))
   213  }
   214  
   215  func defaultHelmHome() string {
   216  	if home := os.Getenv(homeEnvVar); home != "" {
   217  		return home
   218  	}
   219  	return filepath.Join(os.Getenv("HOME"), ".helm")
   220  }
   221  
   222  func homePath() string {
   223  	return os.ExpandEnv(helmHome)
   224  }
   225  
   226  func defaultHelmHost() string {
   227  	return os.Getenv(hostEnvVar)
   228  }
   229  
   230  func defaultTillerNamespace() string {
   231  	if ns := os.Getenv(tillerNamespaceEnvVar); ns != "" {
   232  		return ns
   233  	}
   234  	return environment.DefaultTillerNamespace
   235  }
   236  
   237  // getKubeClient is a convenience method for creating kubernetes config and client
   238  // for a given kubeconfig context
   239  func getKubeClient(context string) (*restclient.Config, *internalclientset.Clientset, error) {
   240  	config, err := kube.GetConfig(context).ClientConfig()
   241  	if err != nil {
   242  		return nil, nil, fmt.Errorf("could not get kubernetes config for context '%s': %s", context, err)
   243  	}
   244  	client, err := internalclientset.NewForConfig(config)
   245  	if err != nil {
   246  		return nil, nil, fmt.Errorf("could not get kubernetes client: %s", err)
   247  	}
   248  	return config, client, nil
   249  }
   250  
   251  // getKubeCmd is a convenience method for creating kubernetes cmd client
   252  // for a given kubeconfig context
   253  func getKubeCmd(context string) *kube.Client {
   254  	return kube.New(kube.GetConfig(context))
   255  }