github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/cli/client/getter.go (about)

     1  /*
     2  Adapted from
     3  https://github.com/kubernetes/cli-runtime/tree/master/pkg/genericclioptions
     4  */
     5  
     6  /*
     7  Copyright 2014 The Kubernetes Authors.
     8  
     9  Licensed under the Apache License, Version 2.0 (the "License");
    10  you may not use this file except in compliance with the License.
    11  You may obtain a copy of the License at
    12  
    13      http://www.apache.org/licenses/LICENSE-2.0
    14  
    15  Unless required by applicable law or agreed to in writing, software
    16  distributed under the License is distributed on an "AS IS" BASIS,
    17  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    18  See the License for the specific language governing permissions and
    19  limitations under the License.
    20  */
    21  
    22  package client
    23  
    24  import (
    25  	"path/filepath"
    26  	"regexp"
    27  	"strings"
    28  	"time"
    29  
    30  	"k8s.io/apimachinery/pkg/api/meta"
    31  	"k8s.io/cli-runtime/pkg/genericclioptions"
    32  	"k8s.io/client-go/discovery"
    33  	diskcached "k8s.io/client-go/discovery/cached/disk"
    34  	"k8s.io/client-go/rest"
    35  	"k8s.io/client-go/restmapper"
    36  	"k8s.io/client-go/tools/clientcmd"
    37  
    38  	"github.com/tilt-dev/wmclient/pkg/dirs"
    39  )
    40  
    41  // Composes the set of values necessary
    42  // for obtaining a REST client config
    43  type Getter struct {
    44  	dir    *dirs.TiltDevDir
    45  	config TiltClientConfig
    46  }
    47  
    48  func NewGetter(dir *dirs.TiltDevDir, config TiltClientConfig) *Getter {
    49  	return &Getter{dir: dir, config: config}
    50  }
    51  
    52  var _ genericclioptions.RESTClientGetter = &Getter{}
    53  
    54  // ToRESTConfig implements RESTClientGetter.
    55  // Returns a REST client configuration based on a provided path
    56  // to a .kubeconfig file, loading rules, and config flag overrides.
    57  // Expects the AddFlags method to have been called.
    58  func (f *Getter) ToRESTConfig() (*rest.Config, error) {
    59  	return f.ToRawKubeConfigLoader().ClientConfig()
    60  }
    61  
    62  // ToRawKubeConfigLoader binds config flag values to config overrides
    63  // Returns an interactive clientConfig if the password flag is enabled,
    64  // or a non-interactive clientConfig otherwise.
    65  func (f *Getter) ToRawKubeConfigLoader() clientcmd.ClientConfig {
    66  	return clientcmd.ClientConfig(f.config)
    67  }
    68  
    69  // ToDiscoveryClient implements RESTClientGetter.
    70  // Expects the AddFlags method to have been called.
    71  // Returns a CachedDiscoveryInterface using a computed RESTConfig.
    72  func (f *Getter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
    73  	config, err := f.ToRESTConfig()
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	// The more groups you have, the more discovery requests you need to make.
    79  	// given 25 groups (our groups + a few custom resources) with one-ish version each, discovery needs to make 50 requests
    80  	// double it just so we don't end up here again for a while.  This config is only used for discovery.
    81  	config.Burst = 100
    82  
    83  	cacheDir := filepath.Join(f.dir.Root(), "cache")
    84  	httpCacheDir := filepath.Join(cacheDir, "http")
    85  	discoveryCacheDir := computeDiscoverCacheDir(filepath.Join(cacheDir, "discovery"), config.Host)
    86  
    87  	return diskcached.NewCachedDiscoveryClientForConfig(config, discoveryCacheDir, httpCacheDir, 10*time.Minute)
    88  }
    89  
    90  func (f *Getter) ToRESTMapper() (meta.RESTMapper, error) {
    91  	discoveryClient, err := f.ToDiscoveryClient()
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	mapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient)
    97  
    98  	warningHandler := func(warning string) {
    99  		// no-op for now. historically we've found that these warnings
   100  		// are targeted at people writing kubernetes controllers, and won't
   101  		// make sense to tilt users.
   102  	}
   103  	expander := restmapper.NewShortcutExpander(mapper, discoveryClient, warningHandler)
   104  	return expander, nil
   105  }
   106  
   107  // overlyCautiousIllegalFileCharacters matches characters that *might* not be supported.  Windows is really restrictive, so this is really restrictive
   108  var overlyCautiousIllegalFileCharacters = regexp.MustCompile(`[^(\w/\.)]`)
   109  
   110  // computeDiscoverCacheDir takes the parentDir and the host and comes up with a "usually non-colliding" name.
   111  func computeDiscoverCacheDir(parentDir, host string) string {
   112  	// strip the optional scheme from host if its there:
   113  	schemelessHost := strings.Replace(strings.Replace(host, "https://", "", 1), "http://", "", 1)
   114  	// now do a simple collapse of non-AZ09 characters.  Collisions are possible but unlikely.  Even if we do collide the problem is short lived
   115  	safeHost := overlyCautiousIllegalFileCharacters.ReplaceAllString(schemelessHost, "_")
   116  	return filepath.Join(parentDir, safeHost)
   117  }