k8s.io/apiserver@v0.31.1/pkg/storage/api_object_versioner.go (about)

     1  /*
     2  Copyright 2014 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  	"fmt"
    21  	"strconv"
    22  
    23  	"k8s.io/apimachinery/pkg/api/meta"
    24  	"k8s.io/apimachinery/pkg/runtime"
    25  	"k8s.io/apimachinery/pkg/util/validation/field"
    26  )
    27  
    28  // APIObjectVersioner implements versioning and extracting etcd node information
    29  // for objects that have an embedded ObjectMeta or ListMeta field.
    30  type APIObjectVersioner struct{}
    31  
    32  // UpdateObject implements Versioner
    33  func (a APIObjectVersioner) UpdateObject(obj runtime.Object, resourceVersion uint64) error {
    34  	accessor, err := meta.Accessor(obj)
    35  	if err != nil {
    36  		return err
    37  	}
    38  	versionString := ""
    39  	if resourceVersion != 0 {
    40  		versionString = strconv.FormatUint(resourceVersion, 10)
    41  	}
    42  	accessor.SetResourceVersion(versionString)
    43  	return nil
    44  }
    45  
    46  // UpdateList implements Versioner
    47  func (a APIObjectVersioner) UpdateList(obj runtime.Object, resourceVersion uint64, nextKey string, count *int64) error {
    48  	if resourceVersion == 0 {
    49  		return fmt.Errorf("illegal resource version from storage: %d", resourceVersion)
    50  	}
    51  	listAccessor, err := meta.ListAccessor(obj)
    52  	if err != nil || listAccessor == nil {
    53  		return err
    54  	}
    55  	versionString := strconv.FormatUint(resourceVersion, 10)
    56  	listAccessor.SetResourceVersion(versionString)
    57  	listAccessor.SetContinue(nextKey)
    58  	listAccessor.SetRemainingItemCount(count)
    59  	return nil
    60  }
    61  
    62  // PrepareObjectForStorage clears resourceVersion and selfLink prior to writing to etcd.
    63  func (a APIObjectVersioner) PrepareObjectForStorage(obj runtime.Object) error {
    64  	accessor, err := meta.Accessor(obj)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	accessor.SetResourceVersion("")
    69  	accessor.SetSelfLink("")
    70  	return nil
    71  }
    72  
    73  // ObjectResourceVersion implements Versioner
    74  func (a APIObjectVersioner) ObjectResourceVersion(obj runtime.Object) (uint64, error) {
    75  	accessor, err := meta.Accessor(obj)
    76  	if err != nil {
    77  		return 0, err
    78  	}
    79  	version := accessor.GetResourceVersion()
    80  	if len(version) == 0 {
    81  		return 0, nil
    82  	}
    83  	return strconv.ParseUint(version, 10, 64)
    84  }
    85  
    86  // ParseResourceVersion takes a resource version argument and converts it to
    87  // the etcd version. For watch we should pass to helper.Watch(). Because resourceVersion is
    88  // an opaque value, the default watch behavior for non-zero watch is to watch
    89  // the next value (if you pass "1", you will see updates from "2" onwards).
    90  func (a APIObjectVersioner) ParseResourceVersion(resourceVersion string) (uint64, error) {
    91  	if resourceVersion == "" || resourceVersion == "0" {
    92  		return 0, nil
    93  	}
    94  	version, err := strconv.ParseUint(resourceVersion, 10, 64)
    95  	if err != nil {
    96  		return 0, NewInvalidError(field.ErrorList{
    97  			// Validation errors are supposed to return version-specific field
    98  			// paths, but this is probably close enough.
    99  			field.Invalid(field.NewPath("resourceVersion"), resourceVersion, err.Error()),
   100  		})
   101  	}
   102  	return version, nil
   103  }
   104  
   105  // Versioner implements Versioner
   106  var _ Versioner = APIObjectVersioner{}
   107  
   108  // CompareResourceVersion compares etcd resource versions.  Outside this API they are all strings,
   109  // but etcd resource versions are special, they're actually ints, so we can easily compare them.
   110  func (a APIObjectVersioner) CompareResourceVersion(lhs, rhs runtime.Object) int {
   111  	lhsVersion, err := a.ObjectResourceVersion(lhs)
   112  	if err != nil {
   113  		// coder error
   114  		panic(err)
   115  	}
   116  	rhsVersion, err := a.ObjectResourceVersion(rhs)
   117  	if err != nil {
   118  		// coder error
   119  		panic(err)
   120  	}
   121  
   122  	if lhsVersion == rhsVersion {
   123  		return 0
   124  	}
   125  	if lhsVersion < rhsVersion {
   126  		return -1
   127  	}
   128  
   129  	return 1
   130  }