blob: 5534f9fa232574010b7f2cede8580ca37d650994 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package etcd
18
19import (
20 "strconv"
21
22 "k8s.io/apimachinery/pkg/api/meta"
23 "k8s.io/apimachinery/pkg/runtime"
24 "k8s.io/apimachinery/pkg/util/validation/field"
25 "k8s.io/apiserver/pkg/storage"
26)
27
28// APIObjectVersioner implements versioning and extracting etcd node information
29// for objects that have an embedded ObjectMeta or ListMeta field.
30type APIObjectVersioner struct{}
31
32// UpdateObject implements Versioner
33func (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
47func (a APIObjectVersioner) UpdateList(obj runtime.Object, resourceVersion uint64, nextKey string) error {
48 listAccessor, err := meta.ListAccessor(obj)
49 if err != nil || listAccessor == nil {
50 return err
51 }
52 versionString := ""
53 if resourceVersion != 0 {
54 versionString = strconv.FormatUint(resourceVersion, 10)
55 }
56 listAccessor.SetResourceVersion(versionString)
57 listAccessor.SetContinue(nextKey)
58 return nil
59}
60
61// PrepareObjectForStorage clears resource version and self link prior to writing to etcd.
62func (a APIObjectVersioner) PrepareObjectForStorage(obj runtime.Object) error {
63 accessor, err := meta.Accessor(obj)
64 if err != nil {
65 return err
66 }
67 accessor.SetResourceVersion("")
68 accessor.SetSelfLink("")
69 return nil
70}
71
72// ObjectResourceVersion implements Versioner
73func (a APIObjectVersioner) ObjectResourceVersion(obj runtime.Object) (uint64, error) {
74 accessor, err := meta.Accessor(obj)
75 if err != nil {
76 return 0, err
77 }
78 version := accessor.GetResourceVersion()
79 if len(version) == 0 {
80 return 0, nil
81 }
82 return strconv.ParseUint(version, 10, 64)
83}
84
85// ParseWatchResourceVersion takes a resource version argument and converts it to
86// the etcd version we should pass to helper.Watch(). Because resourceVersion is
87// an opaque value, the default watch behavior for non-zero watch is to watch
88// the next value (if you pass "1", you will see updates from "2" onwards).
89func (a APIObjectVersioner) ParseWatchResourceVersion(resourceVersion string) (uint64, error) {
90 if resourceVersion == "" || resourceVersion == "0" {
91 return 0, nil
92 }
93 version, err := strconv.ParseUint(resourceVersion, 10, 64)
94 if err != nil {
95 return 0, storage.NewInvalidError(field.ErrorList{
96 // Validation errors are supposed to return version-specific field
97 // paths, but this is probably close enough.
98 field.Invalid(field.NewPath("resourceVersion"), resourceVersion, err.Error()),
99 })
100 }
101 return version, nil
102}
103
104// ParseListResourceVersion takes a resource version argument and converts it to
105// the etcd version.
106// TODO: reevaluate whether it is really clearer to have both this and the
107// Watch version of this function, since they perform the same logic.
108func (a APIObjectVersioner) ParseListResourceVersion(resourceVersion string) (uint64, error) {
109 if resourceVersion == "" {
110 return 0, nil
111 }
112 version, err := strconv.ParseUint(resourceVersion, 10, 64)
113 if err != nil {
114 return 0, storage.NewInvalidError(field.ErrorList{
115 // Validation errors are supposed to return version-specific field
116 // paths, but this is probably close enough.
117 field.Invalid(field.NewPath("resourceVersion"), resourceVersion, err.Error()),
118 })
119 }
120 return version, nil
121}
122
123// APIObjectVersioner implements Versioner
124var Versioner storage.Versioner = APIObjectVersioner{}
125
126// CompareResourceVersion compares etcd resource versions. Outside this API they are all strings,
127// but etcd resource versions are special, they're actually ints, so we can easily compare them.
128func (a APIObjectVersioner) CompareResourceVersion(lhs, rhs runtime.Object) int {
129 lhsVersion, err := Versioner.ObjectResourceVersion(lhs)
130 if err != nil {
131 // coder error
132 panic(err)
133 }
134 rhsVersion, err := Versioner.ObjectResourceVersion(rhs)
135 if err != nil {
136 // coder error
137 panic(err)
138 }
139
140 if lhsVersion == rhsVersion {
141 return 0
142 }
143 if lhsVersion < rhsVersion {
144 return -1
145 }
146
147 return 1
148}