blob: 1628c10e319f1c7e31f04b0acb094638a6e22607 [file] [log] [blame]
package utils
import (
"compress/gzip"
"encoding/base64"
"encoding/json"
"fmt"
"regexp"
"strconv"
"strings"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery"
)
// Format v0.0.0(-master+$Format:%h$)
var gitVersionRe = regexp.MustCompile("v([0-9])+.([0-9])+.[0-9]+.*")
// ServerVersion captures k8s major.minor version in a parsed form
type ServerVersion struct {
Major int
Minor int
}
func parseGitVersion(gitVersion string) (ServerVersion, error) {
parsedVersion := gitVersionRe.FindStringSubmatch(gitVersion)
if len(parsedVersion) != 3 {
return ServerVersion{}, fmt.Errorf("Unable to parse git version %s", gitVersion)
}
var ret ServerVersion
var err error
ret.Major, err = strconv.Atoi(parsedVersion[1])
if err != nil {
return ServerVersion{}, err
}
ret.Minor, err = strconv.Atoi(parsedVersion[2])
if err != nil {
return ServerVersion{}, err
}
return ret, nil
}
// ParseVersion parses version.Info into a ServerVersion struct
func ParseVersion(v *version.Info) (ServerVersion, error) {
var ret ServerVersion
var err error
ret.Major, err = strconv.Atoi(v.Major)
if err != nil {
// Try to parse using GitVersion
return parseGitVersion(v.GitVersion)
}
// trim "+" in minor version (happened on GKE)
v.Minor = strings.TrimSuffix(v.Minor, "+")
ret.Minor, err = strconv.Atoi(v.Minor)
if err != nil {
// Try to parse using GitVersion
return parseGitVersion(v.GitVersion)
}
return ret, err
}
// FetchVersion fetches version information from discovery client, and parses
func FetchVersion(v discovery.ServerVersionInterface) (ret ServerVersion, err error) {
version, err := v.ServerVersion()
if err != nil {
return ServerVersion{}, err
}
return ParseVersion(version)
}
// GetDefaultVersion returns a default server version. This value will be updated
// periodically to match a current/popular version corresponding to the age of this code
// Current default version: 1.8
func GetDefaultVersion() ServerVersion {
return ServerVersion{Major: 1, Minor: 8}
}
// Compare returns -1/0/+1 iff v is less than / equal / greater than major.minor
func (v ServerVersion) Compare(major, minor int) int {
a := v.Major
b := major
if a == b {
a = v.Minor
b = minor
}
var res int
if a > b {
res = 1
} else if a == b {
res = 0
} else {
res = -1
}
return res
}
func (v ServerVersion) String() string {
return fmt.Sprintf("%d.%d", v.Major, v.Minor)
}
// SetMetaDataAnnotation sets an annotation value
func SetMetaDataAnnotation(obj metav1.Object, key, value string) {
a := obj.GetAnnotations()
if a == nil {
a = make(map[string]string)
}
a[key] = value
obj.SetAnnotations(a)
}
// DeleteMetaDataAnnotation removes an annotation value
func DeleteMetaDataAnnotation(obj metav1.Object, key string) {
a := obj.GetAnnotations()
if a != nil {
delete(a, key)
obj.SetAnnotations(a)
}
}
// SetMetaDataLabel sets an annotation value
func SetMetaDataLabel(obj metav1.Object, key, value string) {
l := obj.GetLabels()
if l == nil {
l = make(map[string]string)
}
l[key] = value
obj.SetLabels(l)
}
// DeleteMetaDataLabel removes a label value
func DeleteMetaDataLabel(obj metav1.Object, key string) {
l := obj.GetLabels()
if l != nil {
delete(l, key)
obj.SetLabels(l)
}
}
// ResourceNameFor returns a lowercase plural form of a type, for
// human messages. Returns lowercased kind if discovery lookup fails.
func ResourceNameFor(mapper meta.RESTMapper, o runtime.Object) string {
gvk := o.GetObjectKind().GroupVersionKind()
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
log.Debugf("RESTMapper failed for %s (%s), falling back to kind", gvk, err)
return strings.ToLower(gvk.Kind)
}
return mapping.Resource.Resource
}
// FqName returns "namespace.name"
func FqName(o metav1.Object) string {
if o.GetNamespace() == "" {
return o.GetName()
}
return fmt.Sprintf("%s.%s", o.GetNamespace(), o.GetName())
}
// CompactEncodeObject returns a compact string representation
// (json->gzip->base64) of an object, intended for use in
// last-applied-configuration annotation.
func CompactEncodeObject(o runtime.Object) (string, error) {
var buf strings.Builder
b64enc := base64.NewEncoder(base64.StdEncoding, &buf)
zw := gzip.NewWriter(b64enc)
jsenc := json.NewEncoder(zw)
jsenc.SetEscapeHTML(false)
jsenc.SetIndent("", "")
if err := jsenc.Encode(o); err != nil {
return "", err
}
zw.Close()
b64enc.Close()
return buf.String(), nil
}
// CompactDecodeObject does the reverse of CompactEncodeObject.
func CompactDecodeObject(data string, into runtime.Object) error {
zr, err := gzip.NewReader(
base64.NewDecoder(base64.StdEncoding,
strings.NewReader(data)))
if err != nil {
return err
}
jsdec := json.NewDecoder(zr)
return jsdec.Decode(into)
}