github.com/Racer159/helm-experiment@v0.0.0-20230822001441-1eb31183f614/src/root.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 cmd // import "helm.sh/helm/v3/cmd/helm"
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"log"
    24  	"os"
    25  	"strings"
    26  
    27  	"github.com/spf13/cobra"
    28  
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/client-go/tools/clientcmd"
    31  
    32  	"helm.sh/helm/v3/pkg/action"
    33  	"helm.sh/helm/v3/pkg/registry"
    34  	"helm.sh/helm/v3/pkg/repo"
    35  )
    36  
    37  var globalUsage = `The Kubernetes package manager
    38  
    39  Common actions for Helm:
    40  
    41  - helm search:    search for charts
    42  - helm pull:      download a chart to your local directory to view
    43  - helm install:   upload the chart to Kubernetes
    44  - helm list:      list releases of charts
    45  
    46  Environment variables:
    47  
    48  | Name                               | Description                                                                                       |
    49  |------------------------------------|---------------------------------------------------------------------------------------------------|
    50  | $HELM_CACHE_HOME                   | set an alternative location for storing cached files.                                             |
    51  | $HELM_CONFIG_HOME                  | set an alternative location for storing Helm configuration.                                       |
    52  | $HELM_DATA_HOME                    | set an alternative location for storing Helm data.                                                |
    53  | $HELM_DEBUG                        | indicate whether or not Helm is running in Debug mode                                             |
    54  | $HELM_DRIVER                       | set the backend storage driver. Values are: configmap, secret, memory, sql.                       |
    55  | $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use.                                      |
    56  | $HELM_MAX_HISTORY                  | set the maximum number of helm release history.                                                   |
    57  | $HELM_NAMESPACE                    | set the namespace used for the helm operations.                                                   |
    58  | $HELM_NO_PLUGINS                   | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins.                                        |
    59  | $HELM_PLUGINS                      | set the path to the plugins directory                                                             |
    60  | $HELM_REGISTRY_CONFIG              | set the path to the registry config file.                                                         |
    61  | $HELM_REPOSITORY_CACHE             | set the path to the repository cache directory                                                    |
    62  | $HELM_REPOSITORY_CONFIG            | set the path to the repositories file.                                                            |
    63  | $KUBECONFIG                        | set an alternative Kubernetes configuration file (default "~/.kube/config")                       |
    64  | $HELM_KUBEAPISERVER                | set the Kubernetes API Server Endpoint for authentication                                         |
    65  | $HELM_KUBECAFILE                   | set the Kubernetes certificate authority file.                                                    |
    66  | $HELM_KUBEASGROUPS                 | set the Groups to use for impersonation using a comma-separated list.                             |
    67  | $HELM_KUBEASUSER                   | set the Username to impersonate for the operation.                                                |
    68  | $HELM_KUBECONTEXT                  | set the name of the kubeconfig context.                                                           |
    69  | $HELM_KUBETOKEN                    | set the Bearer KubeToken used for authentication.                                                 |
    70  | $HELM_KUBEINSECURE_SKIP_TLS_VERIFY | indicate if the Kubernetes API server's certificate validation should be skipped (insecure)       |
    71  | $HELM_KUBETLS_SERVER_NAME          | set the server name used to validate the Kubernetes API server certificate                        |
    72  | $HELM_BURST_LIMIT                  | set the default burst limit in the case the server contains many CRDs (default 100, -1 to disable)|
    73  
    74  Helm stores cache, configuration, and data based on the following configuration order:
    75  
    76  - If a HELM_*_HOME environment variable is set, it will be used
    77  - Otherwise, on systems supporting the XDG base directory specification, the XDG variables will be used
    78  - When no other location is set a default location will be used based on the operating system
    79  
    80  By default, the default directories depend on the Operating System. The defaults are listed below:
    81  
    82  | Operating System | Cache Path                | Configuration Path             | Data Path               |
    83  |------------------|---------------------------|--------------------------------|-------------------------|
    84  | Linux            | $HOME/.cache/helm         | $HOME/.config/helm             | $HOME/.local/share/helm |
    85  | macOS            | $HOME/Library/Caches/helm | $HOME/Library/Preferences/helm | $HOME/Library/helm      |
    86  | Windows          | %TEMP%\helm               | %APPDATA%\helm                 | %APPDATA%\helm          |
    87  `
    88  
    89  func NewRootCmd(actionConfig *action.Configuration, out io.Writer, args []string) (*cobra.Command, error) {
    90  	cmd := &cobra.Command{
    91  		Use:          "helm",
    92  		Short:        "The Helm package manager for Kubernetes.",
    93  		Long:         globalUsage,
    94  		SilenceUsage: true,
    95  	}
    96  	flags := cmd.PersistentFlags()
    97  
    98  	settings.AddFlags(flags)
    99  	addKlogFlags(flags)
   100  
   101  	// Setup shell completion for the namespace flag
   102  	err := cmd.RegisterFlagCompletionFunc("namespace", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   103  		if client, err := actionConfig.KubernetesClientSet(); err == nil {
   104  			// Choose a long enough timeout that the user notices something is not working
   105  			// but short enough that the user is not made to wait very long
   106  			to := int64(3)
   107  			cobra.CompDebugln(fmt.Sprintf("About to call kube client for namespaces with timeout of: %d", to), settings.Debug)
   108  
   109  			nsNames := []string{}
   110  			if namespaces, err := client.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{TimeoutSeconds: &to}); err == nil {
   111  				for _, ns := range namespaces.Items {
   112  					nsNames = append(nsNames, ns.Name)
   113  				}
   114  				return nsNames, cobra.ShellCompDirectiveNoFileComp
   115  			}
   116  		}
   117  		return nil, cobra.ShellCompDirectiveDefault
   118  	})
   119  
   120  	if err != nil {
   121  		log.Fatal(err)
   122  	}
   123  
   124  	// Setup shell completion for the kube-context flag
   125  	err = cmd.RegisterFlagCompletionFunc("kube-context", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   126  		cobra.CompDebugln("About to get the different kube-contexts", settings.Debug)
   127  
   128  		loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
   129  		if len(settings.KubeConfig) > 0 {
   130  			loadingRules = &clientcmd.ClientConfigLoadingRules{ExplicitPath: settings.KubeConfig}
   131  		}
   132  		if config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
   133  			loadingRules,
   134  			&clientcmd.ConfigOverrides{}).RawConfig(); err == nil {
   135  			comps := []string{}
   136  			for name, context := range config.Contexts {
   137  				comps = append(comps, fmt.Sprintf("%s\t%s", name, context.Cluster))
   138  			}
   139  			return comps, cobra.ShellCompDirectiveNoFileComp
   140  		}
   141  		return nil, cobra.ShellCompDirectiveNoFileComp
   142  	})
   143  
   144  	if err != nil {
   145  		log.Fatal(err)
   146  	}
   147  
   148  	// We can safely ignore any errors that flags.Parse encounters since
   149  	// those errors will be caught later during the call to cmd.Execution.
   150  	// This call is required to gather configuration information prior to
   151  	// execution.
   152  	flags.ParseErrorsWhitelist.UnknownFlags = true
   153  	flags.Parse(args)
   154  
   155  	registryClient, err := newDefaultRegistryClient()
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	actionConfig.RegistryClient = registryClient
   160  
   161  	// Add subcommands
   162  	cmd.AddCommand(
   163  		// chart commands
   164  		newCreateCmd(out),
   165  		newDependencyCmd(actionConfig, out),
   166  		newPullCmd(actionConfig, out),
   167  		newShowCmd(actionConfig, out),
   168  		newLintCmd(out),
   169  		newPackageCmd(actionConfig, out),
   170  		newRepoCmd(out),
   171  		newSearchCmd(out),
   172  		newVerifyCmd(out),
   173  
   174  		// release commands
   175  		newGetCmd(actionConfig, out),
   176  		newHistoryCmd(actionConfig, out),
   177  		newInstallCmd(actionConfig, out),
   178  		newListCmd(actionConfig, out),
   179  		newReleaseTestCmd(actionConfig, out),
   180  		newRollbackCmd(actionConfig, out),
   181  		newStatusCmd(actionConfig, out),
   182  		newTemplateCmd(actionConfig, out),
   183  		newUninstallCmd(actionConfig, out),
   184  		newUpgradeCmd(actionConfig, out),
   185  
   186  		newCompletionCmd(out),
   187  		newEnvCmd(out),
   188  		newPluginCmd(out),
   189  		newVersionCmd(out),
   190  
   191  		// Hidden documentation generator command: 'helm docs'
   192  		newDocsCmd(out),
   193  	)
   194  
   195  	cmd.AddCommand(
   196  		newRegistryCmd(actionConfig, out),
   197  		newPushCmd(actionConfig, out),
   198  	)
   199  
   200  	// Find and add plugins
   201  	loadPlugins(cmd, out)
   202  
   203  	// Check permissions on critical files
   204  	checkPerms()
   205  
   206  	// Check for expired repositories
   207  	checkForExpiredRepos(settings.RepositoryConfig)
   208  
   209  	return cmd, nil
   210  }
   211  
   212  func checkForExpiredRepos(repofile string) {
   213  
   214  	expiredRepos := []struct {
   215  		name string
   216  		old  string
   217  		new  string
   218  	}{
   219  		{
   220  			name: "stable",
   221  			old:  "kubernetes-charts.storage.googleapis.com",
   222  			new:  "https://charts.helm.sh/stable",
   223  		},
   224  		{
   225  			name: "incubator",
   226  			old:  "kubernetes-charts-incubator.storage.googleapis.com",
   227  			new:  "https://charts.helm.sh/incubator",
   228  		},
   229  	}
   230  
   231  	// parse repo file.
   232  	// Ignore the error because it is okay for a repo file to be unparseable at this
   233  	// stage. Later checks will trap the error and respond accordingly.
   234  	repoFile, err := repo.LoadFile(repofile)
   235  	if err != nil {
   236  		return
   237  	}
   238  
   239  	for _, exp := range expiredRepos {
   240  		r := repoFile.Get(exp.name)
   241  		if r == nil {
   242  			return
   243  		}
   244  
   245  		if url := r.URL; strings.Contains(url, exp.old) {
   246  			fmt.Fprintf(
   247  				os.Stderr,
   248  				"WARNING: %q is deprecated for %q and will be deleted Nov. 13, 2020.\nWARNING: You should switch to %q via:\nWARNING: helm repo add %q %q --force-update\n",
   249  				exp.old,
   250  				exp.name,
   251  				exp.new,
   252  				exp.name,
   253  				exp.new,
   254  			)
   255  		}
   256  	}
   257  
   258  }
   259  
   260  func newRegistryClient(certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*registry.Client, error) {
   261  	if certFile != "" && keyFile != "" || caFile != "" || insecureSkipTLSverify {
   262  		registryClient, err := newRegistryClientWithTLS(certFile, keyFile, caFile, insecureSkipTLSverify)
   263  		if err != nil {
   264  			return nil, err
   265  		}
   266  		return registryClient, nil
   267  	}
   268  	registryClient, err := newDefaultRegistryClient()
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  	return registryClient, nil
   273  }
   274  
   275  func newDefaultRegistryClient() (*registry.Client, error) {
   276  	// Create a new registry client
   277  	registryClient, err := registry.NewClient(
   278  		registry.ClientOptDebug(settings.Debug),
   279  		registry.ClientOptEnableCache(true),
   280  		registry.ClientOptWriter(os.Stderr),
   281  		registry.ClientOptCredentialsFile(settings.RegistryConfig),
   282  	)
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  	return registryClient, nil
   287  }
   288  
   289  func newRegistryClientWithTLS(certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*registry.Client, error) {
   290  	// Create a new registry client
   291  	registryClient, err := registry.NewRegistryClientWithTLS(os.Stderr, certFile, keyFile, caFile, insecureSkipTLSverify,
   292  		settings.RegistryConfig, settings.Debug,
   293  	)
   294  	if err != nil {
   295  		return nil, err
   296  	}
   297  	return registryClient, nil
   298  }