mirror of
https://github.com/kubernetes/client-go.git
synced 2025-09-29 00:16:25 +00:00
Automatic merge from submit-queue (batch tested with PRs 51480, 49616, 50123, 50846, 50404) Kubectl to use http caching to cache openapi responses from the server **What this PR does / why we need it**: This PR is trying to address the problems raised in #50254 > * uses a disk-based cache that is not safe between processes (does not use atomic fs operations) > * writes get/list responses to disk that should not be cached (like kubectl get secrets) > * is vulnerable to partially written cache responses being used as responses to future requests > * breaks uses of the client transport that make use of websockets > * defaults to enabling the cache for any client builder using RecommendedConfigOverrideFlags or DefaultClientConfig which affects more components than just kubectl All of these points are addressed by this pull-request: 1. It now uses atomic fs operations 2. Doesn't cache by default, only if requested by the client (and it's only done by openapi client) 3. Fixed because of atomic fs operations 4. Found the reason for the bug: Cache wrapper couldn't be unwrapped. I implemented the `WrappedRoundTripper` interface. 5. Since 2. is fixed, I think that should be fine @smarterclayton @liggitt **Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #50254 **Special notes for your reviewer**: **Release note**: ```release-note Allows kubectl to use http caching mechanism for the OpenAPI schema. The cache directory can be configured through `--cache-dir` command line flag to kubectl. If set to empty string, caching will be disabled. ``` Kubernetes-commit: 0f2c2bd847ca7a66035b2289e83359ef4c923617
116 lines
3.0 KiB
Go
116 lines
3.0 KiB
Go
package diskv
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/google/btree"
|
|
)
|
|
|
|
// Index is a generic interface for things that can
|
|
// provide an ordered list of keys.
|
|
type Index interface {
|
|
Initialize(less LessFunction, keys <-chan string)
|
|
Insert(key string)
|
|
Delete(key string)
|
|
Keys(from string, n int) []string
|
|
}
|
|
|
|
// LessFunction is used to initialize an Index of keys in a specific order.
|
|
type LessFunction func(string, string) bool
|
|
|
|
// btreeString is a custom data type that satisfies the BTree Less interface,
|
|
// making the strings it wraps sortable by the BTree package.
|
|
type btreeString struct {
|
|
s string
|
|
l LessFunction
|
|
}
|
|
|
|
// Less satisfies the BTree.Less interface using the btreeString's LessFunction.
|
|
func (s btreeString) Less(i btree.Item) bool {
|
|
return s.l(s.s, i.(btreeString).s)
|
|
}
|
|
|
|
// BTreeIndex is an implementation of the Index interface using google/btree.
|
|
type BTreeIndex struct {
|
|
sync.RWMutex
|
|
LessFunction
|
|
*btree.BTree
|
|
}
|
|
|
|
// Initialize populates the BTree tree with data from the keys channel,
|
|
// according to the passed less function. It's destructive to the BTreeIndex.
|
|
func (i *BTreeIndex) Initialize(less LessFunction, keys <-chan string) {
|
|
i.Lock()
|
|
defer i.Unlock()
|
|
i.LessFunction = less
|
|
i.BTree = rebuild(less, keys)
|
|
}
|
|
|
|
// Insert inserts the given key (only) into the BTree tree.
|
|
func (i *BTreeIndex) Insert(key string) {
|
|
i.Lock()
|
|
defer i.Unlock()
|
|
if i.BTree == nil || i.LessFunction == nil {
|
|
panic("uninitialized index")
|
|
}
|
|
i.BTree.ReplaceOrInsert(btreeString{s: key, l: i.LessFunction})
|
|
}
|
|
|
|
// Delete removes the given key (only) from the BTree tree.
|
|
func (i *BTreeIndex) Delete(key string) {
|
|
i.Lock()
|
|
defer i.Unlock()
|
|
if i.BTree == nil || i.LessFunction == nil {
|
|
panic("uninitialized index")
|
|
}
|
|
i.BTree.Delete(btreeString{s: key, l: i.LessFunction})
|
|
}
|
|
|
|
// Keys yields a maximum of n keys in order. If the passed 'from' key is empty,
|
|
// Keys will return the first n keys. If the passed 'from' key is non-empty, the
|
|
// first key in the returned slice will be the key that immediately follows the
|
|
// passed key, in key order.
|
|
func (i *BTreeIndex) Keys(from string, n int) []string {
|
|
i.RLock()
|
|
defer i.RUnlock()
|
|
|
|
if i.BTree == nil || i.LessFunction == nil {
|
|
panic("uninitialized index")
|
|
}
|
|
|
|
if i.BTree.Len() <= 0 {
|
|
return []string{}
|
|
}
|
|
|
|
btreeFrom := btreeString{s: from, l: i.LessFunction}
|
|
skipFirst := true
|
|
if len(from) <= 0 || !i.BTree.Has(btreeFrom) {
|
|
// no such key, so fabricate an always-smallest item
|
|
btreeFrom = btreeString{s: "", l: func(string, string) bool { return true }}
|
|
skipFirst = false
|
|
}
|
|
|
|
keys := []string{}
|
|
iterator := func(i btree.Item) bool {
|
|
keys = append(keys, i.(btreeString).s)
|
|
return len(keys) < n
|
|
}
|
|
i.BTree.AscendGreaterOrEqual(btreeFrom, iterator)
|
|
|
|
if skipFirst && len(keys) > 0 {
|
|
keys = keys[1:]
|
|
}
|
|
|
|
return keys
|
|
}
|
|
|
|
// rebuildIndex does the work of regenerating the index
|
|
// with the given keys.
|
|
func rebuild(less LessFunction, keys <-chan string) *btree.BTree {
|
|
tree := btree.New(2)
|
|
for key := range keys {
|
|
tree.ReplaceOrInsert(btreeString{s: key, l: less})
|
|
}
|
|
return tree
|
|
}
|