github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/cmd/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 package main // import "github.com/stefanmcshane/helm/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 "github.com/stefanmcshane/helm/pkg/action" 33 "github.com/stefanmcshane/helm/pkg/registry" 34 "github.com/stefanmcshane/helm/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 := registry.NewClient( 156 registry.ClientOptDebug(settings.Debug), 157 registry.ClientOptEnableCache(true), 158 registry.ClientOptWriter(os.Stderr), 159 registry.ClientOptCredentialsFile(settings.RegistryConfig), 160 ) 161 if err != nil { 162 return nil, err 163 } 164 actionConfig.RegistryClient = registryClient 165 166 // Add subcommands 167 cmd.AddCommand( 168 // chart commands 169 newCreateCmd(out), 170 newDependencyCmd(actionConfig, out), 171 newPullCmd(actionConfig, out), 172 newShowCmd(actionConfig, out), 173 newLintCmd(out), 174 newPackageCmd(actionConfig, out), 175 newRepoCmd(out), 176 newSearchCmd(out), 177 newVerifyCmd(out), 178 179 // release commands 180 newGetCmd(actionConfig, out), 181 newHistoryCmd(actionConfig, out), 182 newInstallCmd(actionConfig, out), 183 newListCmd(actionConfig, out), 184 newReleaseTestCmd(actionConfig, out), 185 newRollbackCmd(actionConfig, out), 186 newStatusCmd(actionConfig, out), 187 newTemplateCmd(actionConfig, out), 188 newUninstallCmd(actionConfig, out), 189 newUpgradeCmd(actionConfig, out), 190 191 newCompletionCmd(out), 192 newEnvCmd(out), 193 newPluginCmd(out), 194 newVersionCmd(out), 195 196 // Hidden documentation generator command: 'helm docs' 197 newDocsCmd(out), 198 ) 199 200 cmd.AddCommand( 201 newRegistryCmd(actionConfig, out), 202 newPushCmd(actionConfig, out), 203 ) 204 205 // Find and add plugins 206 loadPlugins(cmd, out) 207 208 // Check permissions on critical files 209 checkPerms() 210 211 // Check for expired repositories 212 checkForExpiredRepos(settings.RepositoryConfig) 213 214 return cmd, nil 215 } 216 217 func checkForExpiredRepos(repofile string) { 218 219 expiredRepos := []struct { 220 name string 221 old string 222 new string 223 }{ 224 { 225 name: "stable", 226 old: "kubernetes-charts.storage.googleapis.com", 227 new: "https://charts.helm.sh/stable", 228 }, 229 { 230 name: "incubator", 231 old: "kubernetes-charts-incubator.storage.googleapis.com", 232 new: "https://charts.helm.sh/incubator", 233 }, 234 } 235 236 // parse repo file. 237 // Ignore the error because it is okay for a repo file to be unparseable at this 238 // stage. Later checks will trap the error and respond accordingly. 239 repoFile, err := repo.LoadFile(repofile) 240 if err != nil { 241 return 242 } 243 244 for _, exp := range expiredRepos { 245 r := repoFile.Get(exp.name) 246 if r == nil { 247 return 248 } 249 250 if url := r.URL; strings.Contains(url, exp.old) { 251 fmt.Fprintf( 252 os.Stderr, 253 "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", 254 exp.old, 255 exp.name, 256 exp.new, 257 exp.name, 258 exp.new, 259 ) 260 } 261 } 262 263 }