k8s.io/apiserver@v0.31.1/pkg/storage/value/encrypt/aes/cache.go (about)

     1  /*
     2  Copyright 2023 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 aes
    18  
    19  import (
    20  	"bytes"
    21  	"time"
    22  	"unsafe"
    23  
    24  	utilcache "k8s.io/apimachinery/pkg/util/cache"
    25  	"k8s.io/apiserver/pkg/storage/value"
    26  	"k8s.io/utils/clock"
    27  )
    28  
    29  type simpleCache struct {
    30  	cache *utilcache.Expiring
    31  	ttl   time.Duration
    32  }
    33  
    34  func newSimpleCache(clock clock.Clock, ttl time.Duration) *simpleCache {
    35  	cache := utilcache.NewExpiringWithClock(clock)
    36  	// "Stale" entries are always valid for us because the TTL is just used to prevent
    37  	// unbounded growth on the cache - for a given info the transformer is always the same.
    38  	// The key always corresponds to the exact same value, with the caveat that
    39  	// since we use the value.Context.AuthenticatedData to overwrite old keys,
    40  	// we always have to check that the info matches (to validate the transformer is correct).
    41  	cache.AllowExpiredGet = true
    42  	return &simpleCache{
    43  		cache: cache,
    44  		ttl:   ttl,
    45  	}
    46  }
    47  
    48  // given a key, return the transformer, or nil if it does not exist in the cache
    49  func (c *simpleCache) get(info []byte, dataCtx value.Context) *transformerWithInfo {
    50  	val, ok := c.cache.Get(keyFunc(dataCtx))
    51  	if !ok {
    52  		return nil
    53  	}
    54  
    55  	transformer := val.(*transformerWithInfo)
    56  
    57  	if !bytes.Equal(transformer.info, info) {
    58  		return nil
    59  	}
    60  
    61  	return transformer
    62  }
    63  
    64  // set caches the record for the key
    65  func (c *simpleCache) set(dataCtx value.Context, transformer *transformerWithInfo) {
    66  	if dataCtx == nil || len(dataCtx.AuthenticatedData()) == 0 {
    67  		panic("authenticated data must not be empty")
    68  	}
    69  	if transformer == nil {
    70  		panic("transformer must not be nil")
    71  	}
    72  	if len(transformer.info) == 0 {
    73  		panic("info must not be empty")
    74  	}
    75  	c.cache.Set(keyFunc(dataCtx), transformer, c.ttl)
    76  }
    77  
    78  func keyFunc(dataCtx value.Context) string {
    79  	return toString(dataCtx.AuthenticatedData())
    80  }
    81  
    82  // toString performs unholy acts to avoid allocations
    83  func toString(b []byte) string {
    84  	// unsafe.SliceData relies on cap whereas we want to rely on len
    85  	if len(b) == 0 {
    86  		return ""
    87  	}
    88  	// Copied from go 1.20.1 strings.Builder.String
    89  	// https://github.com/golang/go/blob/202a1a57064127c3f19d96df57b9f9586145e21c/src/strings/builder.go#L48
    90  	return unsafe.String(unsafe.SliceData(b), len(b))
    91  }