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 }