diff --git a/pkg/kube/config_flags.go b/pkg/kube/config_flags.go index 7c0994c5d..3e4792edf 100644 --- a/pkg/kube/config_flags.go +++ b/pkg/kube/config_flags.go @@ -17,12 +17,24 @@ limitations under the License. package kube import ( + "path/filepath" + "regexp" + "strings" + "time" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/client-go/discovery" + diskcached "k8s.io/client-go/discovery/cached/disk" "k8s.io/client-go/rest" + "k8s.io/client-go/util/homedir" "helm.sh/helm/v3/internal/version" ) +var defaultCacheDir = filepath.Join(homedir.HomeDir(), ".kube", "http-cache") + +// ConfigFlags composes the set of values necessary +// for obtaining a REST client config type ConfigFlags struct { genericclioptions.ConfigFlags } @@ -39,3 +51,40 @@ func (f *ConfigFlags) ToRESTConfig() (*rest.Config, error) { config.UserAgent = version.GetUserAgent() return config, err } + +// ToDiscoveryClient implements RESTClientGetter. +// Expects the AddFlags method to have been called. +// Returns a CachedDiscoveryInterface using a computed RESTConfig. +func (f *ConfigFlags) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) { + config, err := f.ToRESTConfig() + if err != nil { + return nil, err + } + + // The more groups you have, the more discovery requests you need to make. + // given 25 groups (our groups + a few custom resources) with one-ish version each, discovery needs to make 50 requests + // double it just so we don't end up here again for a while. This config is only used for discovery. + config.Burst = 100 + + // retrieve a user-provided value for the "cache-dir" + // defaulting to ~/.kube/http-cache if no user-value is given. + httpCacheDir := defaultCacheDir + if f.CacheDir != nil { + httpCacheDir = *f.CacheDir + } + + discoveryCacheDir := computeDiscoverCacheDir(filepath.Join(homedir.HomeDir(), ".kube", "cache", "discovery"), config.Host) + return diskcached.NewCachedDiscoveryClientForConfig(config, discoveryCacheDir, httpCacheDir, time.Duration(10*time.Minute)) +} + +// overlyCautiousIllegalFileCharacters matches characters that *might* not be supported. Windows is really restrictive, so this is really restrictive +var overlyCautiousIllegalFileCharacters = regexp.MustCompile(`[^(\w/\.)]`) + +// computeDiscoverCacheDir takes the parentDir and the host and comes up with a "usually non-colliding" name. +func computeDiscoverCacheDir(parentDir, host string) string { + // strip the optional scheme from host if its there: + schemelessHost := strings.Replace(strings.Replace(host, "https://", "", 1), "http://", "", 1) + // now do a simple collapse of non-AZ09 characters. Collisions are possible but unlikely. Even if we do collide the problem is short lived + safeHost := overlyCautiousIllegalFileCharacters.ReplaceAllString(schemelessHost, "_") + return filepath.Join(parentDir, safeHost) +}