Skip to content

Commit e06b063

Browse files
committed
apiserver: add warnings for deprecated APIs
1 parent e5e557e commit e06b063

File tree

3 files changed

+470
-2
lines changed

3 files changed

+470
-2
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
Copyright 2020 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 deprecation
18+
19+
import (
20+
"fmt"
21+
"regexp"
22+
"strconv"
23+
24+
"k8s.io/apimachinery/pkg/runtime"
25+
"k8s.io/apimachinery/pkg/runtime/schema"
26+
"k8s.io/apimachinery/pkg/version"
27+
)
28+
29+
type apiLifecycleDeprecated interface {
30+
APILifecycleDeprecated() (major, minor int)
31+
}
32+
33+
type apiLifecycleRemoved interface {
34+
APILifecycleRemoved() (major, minor int)
35+
}
36+
37+
type apiLifecycleReplacement interface {
38+
APILifecycleReplacement() schema.GroupVersionKind
39+
}
40+
41+
// extract all digits at the beginning of the string
42+
var leadingDigits = regexp.MustCompile(`^(\d+)`)
43+
44+
// MajorMinor parses a numeric major/minor version from the provided version info.
45+
// The minor version drops all characters after the first non-digit character:
46+
// version.Info{Major:"1", Minor:"2+"} -> 1,2
47+
// version.Info{Major:"1", Minor:"2.3-build4"} -> 1,2
48+
func MajorMinor(v version.Info) (int, int, error) {
49+
major, err := strconv.Atoi(v.Major)
50+
if err != nil {
51+
return 0, 0, err
52+
}
53+
minor, err := strconv.Atoi(leadingDigits.FindString(v.Minor))
54+
if err != nil {
55+
return 0, 0, err
56+
}
57+
return major, minor, nil
58+
}
59+
60+
// IsDeprecated returns true if obj implements APILifecycleDeprecated() and returns
61+
// a major/minor version that is non-zero and is <= the specified current major/minor version.
62+
func IsDeprecated(obj runtime.Object, currentMajor, currentMinor int) bool {
63+
deprecated, isDeprecated := obj.(apiLifecycleDeprecated)
64+
if !isDeprecated {
65+
return false
66+
}
67+
68+
deprecatedMajor, deprecatedMinor := deprecated.APILifecycleDeprecated()
69+
// no deprecation version expressed
70+
if deprecatedMajor == 0 && deprecatedMinor == 0 {
71+
return false
72+
}
73+
// no current version info available
74+
if currentMajor == 0 && currentMinor == 0 {
75+
return true
76+
}
77+
// compare deprecation version to current version
78+
if deprecatedMajor > currentMajor {
79+
return false
80+
}
81+
if deprecatedMajor == currentMajor && deprecatedMinor > currentMinor {
82+
return false
83+
}
84+
return true
85+
}
86+
87+
// RemovedRelease returns the major/minor version in which the given object is unavailable (in the form "<major>.<minor>")
88+
// if the object implements APILifecycleRemoved() to indicate a non-zero removal version, and returns an empty string otherwise.
89+
func RemovedRelease(obj runtime.Object) string {
90+
if removed, hasRemovalInfo := obj.(apiLifecycleRemoved); hasRemovalInfo {
91+
removedMajor, removedMinor := removed.APILifecycleRemoved()
92+
if removedMajor != 0 || removedMinor != 0 {
93+
return fmt.Sprintf("%d.%d", removedMajor, removedMinor)
94+
}
95+
}
96+
return ""
97+
}
98+
99+
// WarningMessage returns a human-readable deprecation warning if the object implements APILifecycleDeprecated()
100+
// to indicate a non-zero deprecated major/minor version and has a populated GetObjectKind().GroupVersionKind().
101+
func WarningMessage(obj runtime.Object) string {
102+
deprecated, isDeprecated := obj.(apiLifecycleDeprecated)
103+
if !isDeprecated {
104+
return ""
105+
}
106+
107+
deprecatedMajor, deprecatedMinor := deprecated.APILifecycleDeprecated()
108+
if deprecatedMajor == 0 && deprecatedMinor == 0 {
109+
return ""
110+
}
111+
112+
gvk := obj.GetObjectKind().GroupVersionKind()
113+
if gvk.Empty() {
114+
return ""
115+
}
116+
deprecationWarning := fmt.Sprintf("%s %s is deprecated in v%d.%d+", gvk.GroupVersion().String(), gvk.Kind, deprecatedMajor, deprecatedMinor)
117+
118+
if removed, hasRemovalInfo := obj.(apiLifecycleRemoved); hasRemovalInfo {
119+
removedMajor, removedMinor := removed.APILifecycleRemoved()
120+
if removedMajor != 0 || removedMinor != 0 {
121+
deprecationWarning = deprecationWarning + fmt.Sprintf(", unavailable in v%d.%d+", removedMajor, removedMinor)
122+
}
123+
}
124+
125+
if replaced, hasReplacement := obj.(apiLifecycleReplacement); hasReplacement {
126+
replacement := replaced.APILifecycleReplacement()
127+
if !replacement.Empty() {
128+
deprecationWarning = deprecationWarning + fmt.Sprintf("; use %s %s", replacement.GroupVersion().String(), replacement.Kind)
129+
}
130+
}
131+
132+
return deprecationWarning
133+
}

0 commit comments

Comments
 (0)