blob: 1628c10e319f1c7e31f04b0acb094638a6e22607 [file] [log] [blame]
Serge Bazanskibe538db2020-11-12 00:22:42 +01001package utils
2
3import (
4 "compress/gzip"
5 "encoding/base64"
6 "encoding/json"
7 "fmt"
8 "regexp"
9 "strconv"
10 "strings"
11
12 log "github.com/sirupsen/logrus"
13 "k8s.io/apimachinery/pkg/api/meta"
14 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15 "k8s.io/apimachinery/pkg/runtime"
16 "k8s.io/apimachinery/pkg/version"
17 "k8s.io/client-go/discovery"
18)
19
20// Format v0.0.0(-master+$Format:%h$)
21var gitVersionRe = regexp.MustCompile("v([0-9])+.([0-9])+.[0-9]+.*")
22
23// ServerVersion captures k8s major.minor version in a parsed form
24type ServerVersion struct {
25 Major int
26 Minor int
27}
28
29func parseGitVersion(gitVersion string) (ServerVersion, error) {
30 parsedVersion := gitVersionRe.FindStringSubmatch(gitVersion)
31 if len(parsedVersion) != 3 {
32 return ServerVersion{}, fmt.Errorf("Unable to parse git version %s", gitVersion)
33 }
34 var ret ServerVersion
35 var err error
36 ret.Major, err = strconv.Atoi(parsedVersion[1])
37 if err != nil {
38 return ServerVersion{}, err
39 }
40 ret.Minor, err = strconv.Atoi(parsedVersion[2])
41 if err != nil {
42 return ServerVersion{}, err
43 }
44 return ret, nil
45}
46
47// ParseVersion parses version.Info into a ServerVersion struct
48func ParseVersion(v *version.Info) (ServerVersion, error) {
49 var ret ServerVersion
50 var err error
51 ret.Major, err = strconv.Atoi(v.Major)
52 if err != nil {
53 // Try to parse using GitVersion
54 return parseGitVersion(v.GitVersion)
55 }
56
57 // trim "+" in minor version (happened on GKE)
58 v.Minor = strings.TrimSuffix(v.Minor, "+")
59 ret.Minor, err = strconv.Atoi(v.Minor)
60 if err != nil {
61 // Try to parse using GitVersion
62 return parseGitVersion(v.GitVersion)
63 }
64 return ret, err
65}
66
67// FetchVersion fetches version information from discovery client, and parses
68func FetchVersion(v discovery.ServerVersionInterface) (ret ServerVersion, err error) {
69 version, err := v.ServerVersion()
70 if err != nil {
71 return ServerVersion{}, err
72 }
73 return ParseVersion(version)
74}
75
76// GetDefaultVersion returns a default server version. This value will be updated
77// periodically to match a current/popular version corresponding to the age of this code
78// Current default version: 1.8
79func GetDefaultVersion() ServerVersion {
80 return ServerVersion{Major: 1, Minor: 8}
81}
82
83// Compare returns -1/0/+1 iff v is less than / equal / greater than major.minor
84func (v ServerVersion) Compare(major, minor int) int {
85 a := v.Major
86 b := major
87
88 if a == b {
89 a = v.Minor
90 b = minor
91 }
92
93 var res int
94 if a > b {
95 res = 1
96 } else if a == b {
97 res = 0
98 } else {
99 res = -1
100 }
101 return res
102}
103
104func (v ServerVersion) String() string {
105 return fmt.Sprintf("%d.%d", v.Major, v.Minor)
106}
107
108// SetMetaDataAnnotation sets an annotation value
109func SetMetaDataAnnotation(obj metav1.Object, key, value string) {
110 a := obj.GetAnnotations()
111 if a == nil {
112 a = make(map[string]string)
113 }
114 a[key] = value
115 obj.SetAnnotations(a)
116}
117
118// DeleteMetaDataAnnotation removes an annotation value
119func DeleteMetaDataAnnotation(obj metav1.Object, key string) {
120 a := obj.GetAnnotations()
121 if a != nil {
122 delete(a, key)
123 obj.SetAnnotations(a)
124 }
125}
126
127// SetMetaDataLabel sets an annotation value
128func SetMetaDataLabel(obj metav1.Object, key, value string) {
129 l := obj.GetLabels()
130 if l == nil {
131 l = make(map[string]string)
132 }
133 l[key] = value
134 obj.SetLabels(l)
135}
136
137// DeleteMetaDataLabel removes a label value
138func DeleteMetaDataLabel(obj metav1.Object, key string) {
139 l := obj.GetLabels()
140 if l != nil {
141 delete(l, key)
142 obj.SetLabels(l)
143 }
144}
145
146// ResourceNameFor returns a lowercase plural form of a type, for
147// human messages. Returns lowercased kind if discovery lookup fails.
148func ResourceNameFor(mapper meta.RESTMapper, o runtime.Object) string {
149 gvk := o.GetObjectKind().GroupVersionKind()
150 mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
151 if err != nil {
152 log.Debugf("RESTMapper failed for %s (%s), falling back to kind", gvk, err)
153 return strings.ToLower(gvk.Kind)
154 }
155
156 return mapping.Resource.Resource
157}
158
159// FqName returns "namespace.name"
160func FqName(o metav1.Object) string {
161 if o.GetNamespace() == "" {
162 return o.GetName()
163 }
164 return fmt.Sprintf("%s.%s", o.GetNamespace(), o.GetName())
165}
166
167// CompactEncodeObject returns a compact string representation
168// (json->gzip->base64) of an object, intended for use in
169// last-applied-configuration annotation.
170func CompactEncodeObject(o runtime.Object) (string, error) {
171 var buf strings.Builder
172 b64enc := base64.NewEncoder(base64.StdEncoding, &buf)
173 zw := gzip.NewWriter(b64enc)
174 jsenc := json.NewEncoder(zw)
175 jsenc.SetEscapeHTML(false)
176 jsenc.SetIndent("", "")
177
178 if err := jsenc.Encode(o); err != nil {
179 return "", err
180 }
181
182 zw.Close()
183 b64enc.Close()
184
185 return buf.String(), nil
186}
187
188// CompactDecodeObject does the reverse of CompactEncodeObject.
189func CompactDecodeObject(data string, into runtime.Object) error {
190 zr, err := gzip.NewReader(
191 base64.NewDecoder(base64.StdEncoding,
192 strings.NewReader(data)))
193 if err != nil {
194 return err
195 }
196
197 jsdec := json.NewDecoder(zr)
198 return jsdec.Decode(into)
199}