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 }