| /* |
| Copyright 2014 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package etcd |
| |
| import ( |
| "strconv" |
| |
| "k8s.io/apimachinery/pkg/api/meta" |
| "k8s.io/apimachinery/pkg/runtime" |
| "k8s.io/apimachinery/pkg/util/validation/field" |
| "k8s.io/apiserver/pkg/storage" |
| ) |
| |
| // APIObjectVersioner implements versioning and extracting etcd node information |
| // for objects that have an embedded ObjectMeta or ListMeta field. |
| type APIObjectVersioner struct{} |
| |
| // UpdateObject implements Versioner |
| func (a APIObjectVersioner) UpdateObject(obj runtime.Object, resourceVersion uint64) error { |
| accessor, err := meta.Accessor(obj) |
| if err != nil { |
| return err |
| } |
| versionString := "" |
| if resourceVersion != 0 { |
| versionString = strconv.FormatUint(resourceVersion, 10) |
| } |
| accessor.SetResourceVersion(versionString) |
| return nil |
| } |
| |
| // UpdateList implements Versioner |
| func (a APIObjectVersioner) UpdateList(obj runtime.Object, resourceVersion uint64, nextKey string) error { |
| listAccessor, err := meta.ListAccessor(obj) |
| if err != nil || listAccessor == nil { |
| return err |
| } |
| versionString := "" |
| if resourceVersion != 0 { |
| versionString = strconv.FormatUint(resourceVersion, 10) |
| } |
| listAccessor.SetResourceVersion(versionString) |
| listAccessor.SetContinue(nextKey) |
| return nil |
| } |
| |
| // PrepareObjectForStorage clears resource version and self link prior to writing to etcd. |
| func (a APIObjectVersioner) PrepareObjectForStorage(obj runtime.Object) error { |
| accessor, err := meta.Accessor(obj) |
| if err != nil { |
| return err |
| } |
| accessor.SetResourceVersion("") |
| accessor.SetSelfLink("") |
| return nil |
| } |
| |
| // ObjectResourceVersion implements Versioner |
| func (a APIObjectVersioner) ObjectResourceVersion(obj runtime.Object) (uint64, error) { |
| accessor, err := meta.Accessor(obj) |
| if err != nil { |
| return 0, err |
| } |
| version := accessor.GetResourceVersion() |
| if len(version) == 0 { |
| return 0, nil |
| } |
| return strconv.ParseUint(version, 10, 64) |
| } |
| |
| // ParseWatchResourceVersion takes a resource version argument and converts it to |
| // the etcd version we should pass to helper.Watch(). Because resourceVersion is |
| // an opaque value, the default watch behavior for non-zero watch is to watch |
| // the next value (if you pass "1", you will see updates from "2" onwards). |
| func (a APIObjectVersioner) ParseWatchResourceVersion(resourceVersion string) (uint64, error) { |
| if resourceVersion == "" || resourceVersion == "0" { |
| return 0, nil |
| } |
| version, err := strconv.ParseUint(resourceVersion, 10, 64) |
| if err != nil { |
| return 0, storage.NewInvalidError(field.ErrorList{ |
| // Validation errors are supposed to return version-specific field |
| // paths, but this is probably close enough. |
| field.Invalid(field.NewPath("resourceVersion"), resourceVersion, err.Error()), |
| }) |
| } |
| return version, nil |
| } |
| |
| // ParseListResourceVersion takes a resource version argument and converts it to |
| // the etcd version. |
| // TODO: reevaluate whether it is really clearer to have both this and the |
| // Watch version of this function, since they perform the same logic. |
| func (a APIObjectVersioner) ParseListResourceVersion(resourceVersion string) (uint64, error) { |
| if resourceVersion == "" { |
| return 0, nil |
| } |
| version, err := strconv.ParseUint(resourceVersion, 10, 64) |
| if err != nil { |
| return 0, storage.NewInvalidError(field.ErrorList{ |
| // Validation errors are supposed to return version-specific field |
| // paths, but this is probably close enough. |
| field.Invalid(field.NewPath("resourceVersion"), resourceVersion, err.Error()), |
| }) |
| } |
| return version, nil |
| } |
| |
| // APIObjectVersioner implements Versioner |
| var Versioner storage.Versioner = APIObjectVersioner{} |
| |
| // CompareResourceVersion compares etcd resource versions. Outside this API they are all strings, |
| // but etcd resource versions are special, they're actually ints, so we can easily compare them. |
| func (a APIObjectVersioner) CompareResourceVersion(lhs, rhs runtime.Object) int { |
| lhsVersion, err := Versioner.ObjectResourceVersion(lhs) |
| if err != nil { |
| // coder error |
| panic(err) |
| } |
| rhsVersion, err := Versioner.ObjectResourceVersion(rhs) |
| if err != nil { |
| // coder error |
| panic(err) |
| } |
| |
| if lhsVersion == rhsVersion { |
| return 0 |
| } |
| if lhsVersion < rhsVersion { |
| return -1 |
| } |
| |
| return 1 |
| } |