forked from kubernetes/kops
/
channel_version.go
137 lines (116 loc) · 3.48 KB
/
channel_version.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package channels
import (
"encoding/json"
"fmt"
"github.com/blang/semver"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_3"
"strings"
)
const AnnotationPrefix = "addons.k8s.io/"
type Channel struct {
Namespace string
Name string
}
type ChannelVersion struct {
Version *string `json:"version,omitempty"`
Channel *string `json:"channel,omitempty"`
}
func ParseChannelVersion(s string) (*ChannelVersion, error) {
v := &ChannelVersion{}
err := json.Unmarshal([]byte(s), v)
if err != nil {
return nil, fmt.Errorf("error parsing version spec %q", s)
}
return v, nil
}
func FindAddons(ns *v1.Namespace) map[string]*ChannelVersion {
addons := make(map[string]*ChannelVersion)
for k, v := range ns.Annotations {
if !strings.HasPrefix(k, AnnotationPrefix) {
continue
}
channelVersion, err := ParseChannelVersion(v)
if err != nil {
glog.Warningf("failed to parse annotation %q=%q", k, v)
continue
}
name := strings.TrimPrefix(k, AnnotationPrefix)
addons[name] = channelVersion
}
return addons
}
func (c *ChannelVersion) Encode() (string, error) {
data, err := json.Marshal(c)
if err != nil {
return "", fmt.Errorf("error encoding version spec: %v", err)
}
return string(data), nil
}
func (c *Channel) AnnotationName() string {
return AnnotationPrefix + c.Name
}
func (c *ChannelVersion) Replaces(existing *ChannelVersion) bool {
if existing.Version != nil {
if c.Version == nil {
return false
}
cVersion, err := semver.Parse(*c.Version)
if err != nil {
glog.Warningf("error parsing version %q; will ignore this version", *c.Version)
return false
}
existingVersion, err := semver.Parse(*existing.Version)
if err != nil {
glog.Warningf("error parsing existing version %q", *existing.Version)
return true
}
return cVersion.GT(existingVersion)
}
glog.Warningf("ChannelVersion did not have a version; can't perform real version check")
if c.Version == nil {
return false
}
return true
}
func (c *Channel) GetInstalledVersion(k8sClient *release_1_3.Clientset) (*ChannelVersion, error) {
ns, err := k8sClient.Namespaces().Get(c.Namespace)
if err != nil {
return nil, fmt.Errorf("error querying namespace %q: %v", c.Namespace, err)
}
annotationValue, ok := ns.Annotations[c.AnnotationName()]
if !ok {
return nil, nil
}
return ParseChannelVersion(annotationValue)
}
type annotationPatch struct {
Metadata annotationPatchMetadata `json:"metadata,omitempty"`
}
type annotationPatchMetadata struct {
Annotations map[string]string `json:"annotations,omitempty"`
}
func (c *Channel) SetInstalledVersion(k8sClient *release_1_3.Clientset, version *ChannelVersion) error {
// Primarily to check it exists
_, err := k8sClient.Namespaces().Get(c.Namespace)
if err != nil {
return fmt.Errorf("error querying namespace %q: %v", c.Namespace, err)
}
value, err := version.Encode()
if err != nil {
return err
}
annotationPatch := &annotationPatch{Metadata: annotationPatchMetadata{Annotations: map[string]string{c.AnnotationName(): value}}}
annotationPatchJson, err := json.Marshal(annotationPatch)
if err != nil {
return fmt.Errorf("error building annotation patch: %v", err)
}
glog.V(2).Infof("sending patch: %q", string(annotationPatchJson))
_, err = k8sClient.Namespaces().Patch(c.Namespace, api.StrategicMergePatchType, annotationPatchJson)
if err != nil {
return fmt.Errorf("error applying annotation to namespace: %v", err)
}
return nil
}