k8s.io/apiserver@v0.29.3/pkg/storage/util.go (about)

     1  /*
     2  Copyright 2015 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 storage
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strconv"
    23  	"sync/atomic"
    24  
    25  	"k8s.io/apimachinery/pkg/api/meta"
    26  	"k8s.io/apimachinery/pkg/api/validation/path"
    27  	"k8s.io/apimachinery/pkg/fields"
    28  	"k8s.io/apimachinery/pkg/labels"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  )
    31  
    32  const (
    33  	// initialEventsAnnotationKey the name of the key
    34  	// under which an annotation marking the end of list stream
    35  	// is kept.
    36  	initialEventsAnnotationKey = "k8s.io/initial-events-end"
    37  )
    38  
    39  type SimpleUpdateFunc func(runtime.Object) (runtime.Object, error)
    40  
    41  // SimpleUpdateFunc converts SimpleUpdateFunc into UpdateFunc
    42  func SimpleUpdate(fn SimpleUpdateFunc) UpdateFunc {
    43  	return func(input runtime.Object, _ ResponseMeta) (runtime.Object, *uint64, error) {
    44  		out, err := fn(input)
    45  		return out, nil, err
    46  	}
    47  }
    48  
    49  func EverythingFunc(runtime.Object) bool {
    50  	return true
    51  }
    52  
    53  func NamespaceKeyFunc(prefix string, obj runtime.Object) (string, error) {
    54  	meta, err := meta.Accessor(obj)
    55  	if err != nil {
    56  		return "", err
    57  	}
    58  	name := meta.GetName()
    59  	if msgs := path.IsValidPathSegmentName(name); len(msgs) != 0 {
    60  		return "", fmt.Errorf("invalid name: %v", msgs)
    61  	}
    62  	return prefix + "/" + meta.GetNamespace() + "/" + name, nil
    63  }
    64  
    65  func NoNamespaceKeyFunc(prefix string, obj runtime.Object) (string, error) {
    66  	meta, err := meta.Accessor(obj)
    67  	if err != nil {
    68  		return "", err
    69  	}
    70  	name := meta.GetName()
    71  	if msgs := path.IsValidPathSegmentName(name); len(msgs) != 0 {
    72  		return "", fmt.Errorf("invalid name: %v", msgs)
    73  	}
    74  	return prefix + "/" + name, nil
    75  }
    76  
    77  // HighWaterMark is a thread-safe object for tracking the maximum value seen
    78  // for some quantity.
    79  type HighWaterMark int64
    80  
    81  // Update returns true if and only if 'current' is the highest value ever seen.
    82  func (hwm *HighWaterMark) Update(current int64) bool {
    83  	for {
    84  		old := atomic.LoadInt64((*int64)(hwm))
    85  		if current <= old {
    86  			return false
    87  		}
    88  		if atomic.CompareAndSwapInt64((*int64)(hwm), old, current) {
    89  			return true
    90  		}
    91  	}
    92  }
    93  
    94  // GetCurrentResourceVersionFromStorage gets the current resource version from the underlying storage engine.
    95  // This method issues an empty list request and reads only the ResourceVersion from the object metadata
    96  func GetCurrentResourceVersionFromStorage(ctx context.Context, storage Interface, newListFunc func() runtime.Object, resourcePrefix, objectType string) (uint64, error) {
    97  	if storage == nil {
    98  		return 0, fmt.Errorf("storage wasn't provided for %s", objectType)
    99  	}
   100  	if newListFunc == nil {
   101  		return 0, fmt.Errorf("newListFunction wasn't provided for %s", objectType)
   102  	}
   103  	emptyList := newListFunc()
   104  	pred := SelectionPredicate{
   105  		Label: labels.Everything(),
   106  		Field: fields.Everything(),
   107  		Limit: 1, // just in case we actually hit something
   108  	}
   109  
   110  	err := storage.GetList(ctx, resourcePrefix, ListOptions{Predicate: pred}, emptyList)
   111  	if err != nil {
   112  		return 0, err
   113  	}
   114  	emptyListAccessor, err := meta.ListAccessor(emptyList)
   115  	if err != nil {
   116  		return 0, err
   117  	}
   118  	if emptyListAccessor == nil {
   119  		return 0, fmt.Errorf("unable to extract a list accessor from %T", emptyList)
   120  	}
   121  
   122  	currentResourceVersion, err := strconv.Atoi(emptyListAccessor.GetResourceVersion())
   123  	if err != nil {
   124  		return 0, err
   125  	}
   126  
   127  	if currentResourceVersion == 0 {
   128  		return 0, fmt.Errorf("the current resource version must be greater than 0")
   129  	}
   130  	return uint64(currentResourceVersion), nil
   131  }
   132  
   133  // AnnotateInitialEventsEndBookmark adds a special annotation to the given object
   134  // which indicates that the initial events have been sent.
   135  //
   136  // Note that this function assumes that the obj's annotation
   137  // field is a reference type (i.e. a map).
   138  func AnnotateInitialEventsEndBookmark(obj runtime.Object) error {
   139  	objMeta, err := meta.Accessor(obj)
   140  	if err != nil {
   141  		return err
   142  	}
   143  	objAnnotations := objMeta.GetAnnotations()
   144  	if objAnnotations == nil {
   145  		objAnnotations = map[string]string{}
   146  	}
   147  	objAnnotations[initialEventsAnnotationKey] = "true"
   148  	objMeta.SetAnnotations(objAnnotations)
   149  	return nil
   150  }
   151  
   152  // HasInitialEventsEndBookmarkAnnotation checks the presence of the
   153  // special annotation which marks that the initial events have been sent.
   154  func HasInitialEventsEndBookmarkAnnotation(obj runtime.Object) (bool, error) {
   155  	objMeta, err := meta.Accessor(obj)
   156  	if err != nil {
   157  		return false, err
   158  	}
   159  	objAnnotations := objMeta.GetAnnotations()
   160  	return objAnnotations[initialEventsAnnotationKey] == "true", nil
   161  }