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