github.com/Azure/draft-classic@v0.16.0/cmd/draft/draft.go (about)

     1  // Copyright (c) Microsoft Corporation. All rights reserved.
     2  //
     3  // Licensed under the MIT license.
     4  
     5  package main
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"path/filepath"
    12  	"runtime"
    13  
    14  	log "github.com/sirupsen/logrus"
    15  	"github.com/spf13/cobra"
    16  	"k8s.io/client-go/kubernetes"
    17  	_ "k8s.io/client-go/plugin/pkg/client/auth"
    18  	"k8s.io/client-go/rest"
    19  	"k8s.io/client-go/tools/clientcmd"
    20  	"k8s.io/helm/pkg/helm"
    21  	hpf "k8s.io/helm/pkg/helm/portforwarder"
    22  	"k8s.io/helm/pkg/kube"
    23  	"k8s.io/helm/pkg/tiller/environment"
    24  
    25  	"github.com/Azure/draft/pkg/draft/draftpath"
    26  )
    27  
    28  const (
    29  	homeEnvVar      = "DRAFT_HOME"
    30  	hostEnvVar      = "HELM_HOST"
    31  	namespaceEnvVar = "TILLER_NAMESPACE"
    32  )
    33  
    34  var (
    35  	// flagDebug is a signal that the user wants additional output.
    36  	flagDebug   bool
    37  	kubeContext string
    38  	// draftHome depicts the home directory where all Draft config is stored.
    39  	draftHome string
    40  	// tillerHost depicts where Tiller is hosted. This is used when the port forwarding logic by Kubernetes is unavailable.
    41  	tillerHost string
    42  	// tillerNamespace depicts which namespace Tiller is running in. This is used when Tiller was installed in a different namespace than kube-system.
    43  	tillerNamespace string
    44  	//rootCmd is the root command handling `draft`. It's used in other parts of package cmd to add/search the command tree.
    45  	rootCmd *cobra.Command
    46  	// globalConfig is the configuration stored in $DRAFT_HOME/config.toml
    47  	globalConfig DraftConfig
    48  )
    49  
    50  var globalUsage = `The application deployment tool for Kubernetes.
    51  `
    52  
    53  func init() {
    54  	rootCmd = newRootCmd(os.Stdout, os.Stdin)
    55  }
    56  
    57  func newRootCmd(out io.Writer, in io.Reader) *cobra.Command {
    58  	cmd := &cobra.Command{
    59  		Use:          "draft",
    60  		Short:        globalUsage,
    61  		Long:         globalUsage,
    62  		SilenceUsage: true,
    63  		PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) {
    64  			if flagDebug {
    65  				log.SetLevel(log.DebugLevel)
    66  			}
    67  			os.Setenv(homeEnvVar, draftHome)
    68  			globalConfig, err = ReadConfig()
    69  			return
    70  		},
    71  	}
    72  	p := cmd.PersistentFlags()
    73  	p.StringVar(&draftHome, "home", defaultDraftHome(), "location of your Draft config. Overrides $DRAFT_HOME")
    74  	p.BoolVar(&flagDebug, "debug", false, "enable verbose output")
    75  	p.StringVar(&kubeContext, "kube-context", "", "name of the kubeconfig context to use when talking to Tiller")
    76  	p.StringVar(&tillerNamespace, "tiller-namespace", defaultTillerNamespace(), "namespace where Tiller is running. This is used when Tiller was installed in a different namespace than kube-system. Overrides $TILLER_NAMESPACE")
    77  
    78  	cmd.AddCommand(
    79  		newConfigCmd(out),
    80  		newCreateCmd(out),
    81  		newHomeCmd(out),
    82  		newInitCmd(out, in),
    83  		newUpCmd(out),
    84  		newVersionCmd(out),
    85  		newPluginCmd(out),
    86  		newConnectCmd(out),
    87  		newDeleteCmd(out),
    88  		newLogsCmd(out),
    89  		newHistoryCmd(out),
    90  		newPackCmd(out),
    91  	)
    92  
    93  	// Find and add plugins
    94  	loadPlugins(cmd, draftpath.Home(homePath()), out, in)
    95  
    96  	return cmd
    97  }
    98  
    99  func defaultDraftHome() string {
   100  	if home := os.Getenv(homeEnvVar); home != "" {
   101  		return home
   102  	}
   103  
   104  	homeEnvPath := os.Getenv("HOME")
   105  	if homeEnvPath == "" && runtime.GOOS == "windows" {
   106  		homeEnvPath = os.Getenv("USERPROFILE")
   107  	}
   108  
   109  	return filepath.Join(homeEnvPath, ".draft")
   110  }
   111  
   112  func defaultTillerNamespace() string {
   113  	if namespace := os.Getenv(namespaceEnvVar); namespace != "" {
   114  		return namespace
   115  	}
   116  	return environment.DefaultTillerNamespace
   117  }
   118  
   119  func homePath() string {
   120  	return os.ExpandEnv(draftHome)
   121  }
   122  
   123  // configForContext creates a Kubernetes REST client configuration for a given kubeconfig context.
   124  func configForContext(context string) (clientcmd.ClientConfig, *rest.Config, error) {
   125  	clientConfig := kube.GetConfig(context)
   126  	config, err := clientConfig.ClientConfig()
   127  	if err != nil {
   128  		return nil, nil, fmt.Errorf("could not get Kubernetes config for context %q: %s", context, err)
   129  	}
   130  	return clientConfig, config, nil
   131  }
   132  
   133  // getKubeClient creates a Kubernetes config and client for a given kubeconfig context.
   134  func getKubeClient(context string) (kubernetes.Interface, *rest.Config, error) {
   135  	_, config, err := configForContext(context)
   136  	if err != nil {
   137  		return nil, nil, err
   138  	}
   139  	client, err := kubernetes.NewForConfig(config)
   140  	if err != nil {
   141  		return nil, nil, fmt.Errorf("could not get Kubernetes client: %s", err)
   142  	}
   143  	return client, config, nil
   144  }
   145  
   146  func debug(format string, args ...interface{}) {
   147  	if flagDebug {
   148  		format = fmt.Sprintf("[debug] %s\n", format)
   149  		fmt.Printf(format, args...)
   150  	}
   151  }
   152  
   153  func validateArgs(args, expectedArgs []string) error {
   154  	if len(args) != len(expectedArgs) {
   155  		return fmt.Errorf("This command needs %v argument(s): %v", len(expectedArgs), expectedArgs)
   156  	}
   157  	return nil
   158  }
   159  
   160  func setupHelm(kubeClient kubernetes.Interface, config *rest.Config, namespace string) (helm.Interface, error) {
   161  	tunnel, err := setupTillerConnection(kubeClient, config, namespace)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	return helm.NewClient(helm.Host(fmt.Sprintf("127.0.0.1:%d", tunnel.Local))), nil
   167  }
   168  
   169  func setupTillerConnection(client kubernetes.Interface, config *rest.Config, namespace string) (*kube.Tunnel, error) {
   170  	tunnel, err := hpf.New(namespace, client, config)
   171  	if err != nil {
   172  		return nil, fmt.Errorf("Could not get a connection to tiller: %s\nPlease ensure you have run `helm init`", err)
   173  	}
   174  
   175  	return tunnel, err
   176  }
   177  
   178  func main() {
   179  	if err := rootCmd.Execute(); err != nil {
   180  		os.Exit(1)
   181  	}
   182  }