kubelatte-ce
Форк от sbertech/kubelatte-ce
244 строки · 5.9 Кб
1package mutation
2
3import (
4"github.com/jmespath/go-jmespath"
5"sigs.k8s.io/yaml"
6"strings"
7)
8
9type UpdateStrategy string
10
11const (
12ReplaceStrategy UpdateStrategy = "replace"
13MergeStrategy UpdateStrategy = "merge"
14)
15
16type detailedRefTemplateObjectsData struct {
17containers map[string][]string
18annotations map[string][]string
19volumes map[string][]string
20}
21
22func prepareDataForUniversalMutation(containers []string, annotations []string, volumes []string) (attitude detailedRefTemplateObjectsData) {
23attitude = detailedRefTemplateObjectsData{
24containers: make(map[string][]string),
25annotations: make(map[string][]string),
26volumes: make(map[string][]string),
27}
28
29attitude.containers = appendCutElem(attitude.containers, containers)
30attitude.annotations = appendCutElem(attitude.annotations, annotations)
31attitude.volumes = appendCutElem(attitude.volumes, volumes)
32
33return attitude
34}
35
36func appendCutElem(attitude map[string][]string, elems []string) map[string][]string {
37for _, elem := range elems {
38ref := strings.Split(elem, "/")
39cutInfo := ref[0] + "/" + ref[1]
40
41attitude[cutInfo] = append(attitude[cutInfo], strings.Join(ref[2:], "/"))
42}
43
44return attitude
45}
46
47func getPatchForContainer(specData interface{}, contName string, obj map[string]interface{}, strategy UpdateStrategy) []string {
48spec, ok := specData.(map[string]interface{})["spec"]
49if !ok {
50return nil
51}
52containers, ok := spec.(map[string]interface{})["containers"]
53if !ok {
54return nil
55}
56
57var patches []string
58
59for _, contToInsert := range containers.([]interface{}) {
60res, ok := contToInsert.(map[string]interface{})["name"]
61if !ok || res != contName {
62continue
63}
64
65if strategy == MergeStrategy {
66patches = append(patches, getPatchForContainerMergeStrategy(contToInsert.(map[string]interface{})))
67}
68
69if strategy == ReplaceStrategy {
70patches = append(patches, getPatchForContainerReplaceStrategy(contName, obj, contToInsert.(map[string]interface{})))
71}
72}
73
74return patches
75}
76
77func getPatchForContainerMergeStrategy(contToInsert map[string]interface{}) string {
78withFantic := map[string]map[string][]map[string]interface{}{"spec": {"containers": {contToInsert}}}
79yamlPatch, err := yaml.Marshal(withFantic)
80if err != nil {
81return ""
82}
83return string(yamlPatch)
84}
85
86func getPatchForContainerReplaceStrategy(contName string, obj, contToInsert map[string]interface{}) string {
87objConts, err := jmespath.Search("spec.containers", obj)
88if err != nil || objConts == nil {
89return ""
90}
91
92objContainers, ok := objConts.([]interface{})
93if !ok {
94return ""
95}
96
97var yamlPatch []byte
98
99for i := range objContainers {
100item := objContainers[i].(map[string]interface{})
101if item["name"] != contName {
102continue
103}
104
105objContainers[i] = contToInsert
106withFantic := map[string]map[string][]interface{}{"spec": {"containers": objContainers}}
107yamlPatch, err = yaml.Marshal(withFantic)
108if err != nil {
109return ""
110}
111}
112
113if len(yamlPatch) != 0 {
114return string(yamlPatch)
115}
116
117objContainers = append(objContainers, contToInsert)
118
119withFantic := map[string]map[string][]interface{}{"spec": {"containers": objContainers}}
120yamlPatch, err = yaml.Marshal(withFantic)
121if err != nil {
122return ""
123}
124
125return string(yamlPatch)
126}
127
128func getPatchForVolume(specData interface{}, volName string, obj map[string]interface{}, strategy UpdateStrategy) []string {
129spec, ok := specData.(map[string]interface{})["spec"]
130if !ok {
131return nil
132}
133volumes, ok := spec.(map[string]interface{})["volumes"]
134if !ok {
135return nil
136}
137
138var patches []string
139
140for _, volToInsert := range volumes.([]interface{}) {
141res, ok := volToInsert.(map[string]interface{})["name"]
142if !ok || res != volName {
143continue
144}
145
146if strategy == MergeStrategy {
147patches = append(patches, getPatchForVolumeMergeStrategy(volToInsert.(map[string]interface{})))
148}
149if strategy == ReplaceStrategy {
150patches = append(patches, getPatchForVolumeReplaceStrategy(volName, obj, volToInsert.(map[string]interface{})))
151}
152}
153
154return patches
155}
156
157func getPatchForVolumeMergeStrategy(volToInsert map[string]interface{}) string {
158withFantic := map[string]map[string][]map[string]interface{}{"spec": {"volumes": {volToInsert}}}
159yamlPatch, err := yaml.Marshal(withFantic)
160if err != nil {
161return ""
162}
163return string(yamlPatch)
164}
165
166func getPatchForVolumeReplaceStrategy(volName string, obj, volToInsert map[string]interface{}) string {
167objVols, err := jmespath.Search("spec.volumes", obj)
168if err != nil || objVols == nil {
169return ""
170}
171
172objVolumes, ok := objVols.([]interface{})
173if !ok {
174return ""
175}
176
177var yamlPatch []byte
178
179for i := range objVolumes {
180if objVolumes[i].(map[string]interface{})["name"] != volName {
181continue
182}
183objVolumes[i] = volToInsert
184withFantic := map[string]map[string][]interface{}{"spec": {"volumes": objVolumes}}
185yamlPatch, err = yaml.Marshal(withFantic)
186if err != nil {
187return ""
188}
189
190}
191
192if len(yamlPatch) != 0 {
193return string(yamlPatch)
194}
195
196objVolumes = append(objVolumes, volToInsert)
197
198withFantic := map[string]map[string][]interface{}{"spec": {"volumes": objVolumes}}
199yamlPatch, err = yaml.Marshal(withFantic)
200if err != nil {
201return ""
202}
203return string(yamlPatch)
204}
205
206func getPatchForAnnotation(specData interface{}, annName string) []string {
207metadata, ok := specData.(map[string]interface{})["metadata"]
208if !ok {
209return nil
210}
211annots, ok := metadata.(map[string]interface{})["annotations"]
212if !ok {
213return nil
214}
215
216val, ok := annots.(map[string]interface{})[annName]
217if !ok {
218return nil
219}
220
221withFantic := map[string]map[string]map[string]interface{}{"metadata": {"annotations": {annName: val}}}
222yamlPatch, err := yaml.Marshal(withFantic)
223if err != nil {
224return nil
225}
226
227return []string{string(yamlPatch)}
228}
229
230func convert(i interface{}) interface{} {
231switch x := i.(type) {
232case map[interface{}]interface{}:
233m2 := map[string]interface{}{}
234for k, v := range x {
235m2[k.(string)] = convert(v)
236}
237return m2
238case []interface{}:
239for i, v := range x {
240x[i] = convert(v)
241}
242}
243return i
244}
245