...

Source file src/k8s.io/client-go/discovery/cached/disk/round_tripper.go

Documentation: k8s.io/client-go/discovery/cached/disk

     1  /*
     2  Copyright 2017 The Kubernetes 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 disk
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/sha256"
    22  	"fmt"
    23  	"net/http"
    24  	"os"
    25  	"path/filepath"
    26  
    27  	"github.com/gregjones/httpcache"
    28  	"github.com/peterbourgon/diskv"
    29  	"k8s.io/klog/v2"
    30  )
    31  
    32  type cacheRoundTripper struct {
    33  	rt *httpcache.Transport
    34  }
    35  
    36  // newCacheRoundTripper creates a roundtripper that reads the ETag on
    37  // response headers and send the If-None-Match header on subsequent
    38  // corresponding requests.
    39  func newCacheRoundTripper(cacheDir string, rt http.RoundTripper) http.RoundTripper {
    40  	d := diskv.New(diskv.Options{
    41  		PathPerm: os.FileMode(0750),
    42  		FilePerm: os.FileMode(0660),
    43  		BasePath: cacheDir,
    44  		TempDir:  filepath.Join(cacheDir, ".diskv-temp"),
    45  	})
    46  	t := httpcache.NewTransport(&sumDiskCache{disk: d})
    47  	t.Transport = rt
    48  
    49  	return &cacheRoundTripper{rt: t}
    50  }
    51  
    52  func (rt *cacheRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
    53  	return rt.rt.RoundTrip(req)
    54  }
    55  
    56  func (rt *cacheRoundTripper) CancelRequest(req *http.Request) {
    57  	type canceler interface {
    58  		CancelRequest(*http.Request)
    59  	}
    60  	if cr, ok := rt.rt.Transport.(canceler); ok {
    61  		cr.CancelRequest(req)
    62  	} else {
    63  		klog.Errorf("CancelRequest not implemented by %T", rt.rt.Transport)
    64  	}
    65  }
    66  
    67  func (rt *cacheRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt.Transport }
    68  
    69  // A sumDiskCache is a cache backend for github.com/gregjones/httpcache. It is
    70  // similar to httpcache's diskcache package, but uses SHA256 sums to ensure
    71  // cache integrity at read time rather than fsyncing each cache entry to
    72  // increase the likelihood they will be persisted at write time. This avoids
    73  // significant performance degradation on MacOS.
    74  //
    75  // See https://github.com/kubernetes/kubernetes/issues/110753 for more.
    76  type sumDiskCache struct {
    77  	disk *diskv.Diskv
    78  }
    79  
    80  // Get the requested key from the cache on disk. If Get encounters an error, or
    81  // the returned value is not a SHA256 sum followed by bytes with a matching
    82  // checksum it will return false to indicate a cache miss.
    83  func (c *sumDiskCache) Get(key string) ([]byte, bool) {
    84  	b, err := c.disk.Read(sanitize(key))
    85  	if err != nil || len(b) < sha256.Size {
    86  		return []byte{}, false
    87  	}
    88  
    89  	response := b[sha256.Size:]
    90  	want := b[:sha256.Size] // The first 32 bytes of the file should be the SHA256 sum.
    91  	got := sha256.Sum256(response)
    92  	if !bytes.Equal(want, got[:]) {
    93  		return []byte{}, false
    94  	}
    95  
    96  	return response, true
    97  }
    98  
    99  // Set writes the response to a file on disk. The filename will be the SHA256
   100  // sum of the key. The file will contain a SHA256 sum of the response bytes,
   101  // followed by said response bytes.
   102  func (c *sumDiskCache) Set(key string, response []byte) {
   103  	s := sha256.Sum256(response)
   104  	_ = c.disk.Write(sanitize(key), append(s[:], response...)) // Nothing we can do with this error.
   105  }
   106  
   107  func (c *sumDiskCache) Delete(key string) {
   108  	_ = c.disk.Erase(sanitize(key)) // Nothing we can do with this error.
   109  }
   110  
   111  // Sanitize an httpcache key such that it can be used as a diskv key, which must
   112  // be a valid filename. The httpcache key will either be the requested URL (if
   113  // the request method was GET) or "<method> <url>" for other methods, per the
   114  // httpcache.cacheKey function.
   115  func sanitize(key string) string {
   116  	// These keys are not sensitive. We use sha256 to avoid a (potentially
   117  	// malicious) collision causing the wrong cache data to be written or
   118  	// accessed.
   119  	return fmt.Sprintf("%x", sha256.Sum256([]byte(key)))
   120  }
   121  

View as plain text