/
workloadhelper.go
145 lines (121 loc) · 3.69 KB
/
workloadhelper.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
138
139
140
141
142
143
144
145
package dynamichelper
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"sort"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
func SetControllerReferences(resources []kruntime.Object, owner metav1.Object) error {
for _, resource := range resources {
r, err := meta.Accessor(resource)
if err != nil {
return err
}
err = controllerutil.SetControllerReference(owner, r, scheme.Scheme)
if err != nil {
return err
}
}
return nil
}
func Prepare(resources []kruntime.Object) error {
err := hashWorkloadConfigs(resources)
if err != nil {
return err
}
sort.SliceStable(resources, func(i, j int) bool {
return createOrder(resources[i], resources[j])
})
return nil
}
func addWorkloadHashes(o *metav1.ObjectMeta, t *corev1.PodTemplateSpec, configToHash map[string]string) {
for _, v := range t.Spec.Volumes {
if v.Secret != nil {
if hash, found := configToHash[keyFunc(schema.GroupKind{Kind: "Secret"}, o.Namespace, v.Secret.SecretName)]; found {
if t.Annotations == nil {
t.Annotations = map[string]string{}
}
t.Annotations["checksum/secret-"+v.Secret.SecretName] = hash
}
}
if v.ConfigMap != nil {
if hash, found := configToHash[keyFunc(schema.GroupKind{Kind: "ConfigMap"}, o.Namespace, v.ConfigMap.Name)]; found {
if t.Annotations == nil {
t.Annotations = map[string]string{}
}
t.Annotations["checksum/configmap-"+v.ConfigMap.Name] = hash
}
}
}
}
// hashWorkloadConfigs iterates daemonsets, walks their volumes, and updates
// their pod templates with annotations that include the hashes of the content
// for each configmap or secret.
func hashWorkloadConfigs(resources []kruntime.Object) error {
// map config resources to their hashed content
configToHash := map[string]string{}
for _, o := range resources {
switch o := o.(type) {
case *corev1.Secret:
configToHash[keyFunc(schema.GroupKind{Kind: "Secret"}, o.Namespace, o.Name)] = getHashSecret(o)
case *corev1.ConfigMap:
configToHash[keyFunc(schema.GroupKind{Kind: "ConfigMap"}, o.Namespace, o.Name)] = getHashConfigMap(o)
}
}
// iterate over workload controllers and add annotations with the hashes of
// every config map or secret appropriately to force redeployments on config
// updates.
for _, o := range resources {
switch o := o.(type) {
case *appsv1.DaemonSet:
addWorkloadHashes(&o.ObjectMeta, &o.Spec.Template, configToHash)
case *appsv1.Deployment:
addWorkloadHashes(&o.ObjectMeta, &o.Spec.Template, configToHash)
case *appsv1.StatefulSet:
addWorkloadHashes(&o.ObjectMeta, &o.Spec.Template, configToHash)
}
}
return nil
}
func getHashSecret(o *corev1.Secret) string {
keys := make([]string, 0, len(o.Data))
for key := range o.Data {
keys = append(keys, key)
}
sort.Strings(keys)
h := sha256.New()
for _, key := range keys {
fmt.Fprintf(h, "%s: %s\n", key, string(o.Data[key]))
}
return hex.EncodeToString(h.Sum(nil))
}
func getHashConfigMap(o *corev1.ConfigMap) string {
keys := make([]string, 0, len(o.Data))
for key := range o.Data {
keys = append(keys, key)
}
sort.Strings(keys)
h := sha256.New()
for _, key := range keys {
fmt.Fprintf(h, "%s: %s\n", key, o.Data[key])
}
return hex.EncodeToString(h.Sum(nil))
}
func keyFunc(gk schema.GroupKind, namespace, name string) string {
s := gk.String()
if namespace != "" {
s += "/" + namespace
}
s += "/" + name
return s
}