kubelatte-ce
Форк от sbertech/kubelatte-ce
233 строки · 6.4 Кб
1package mutation
2
3import (
4"context"
5"encoding/json"
6errors2 "errors"
7"fmt"
8"github.com/InVisionApp/conjungo"
9"github.com/gohobby/deepcopy"
10"github.com/pkg/errors"
11"github.com/wI2L/jsondiff"
12"gitverse.ru/synapse/kubelatte/pkg/api/common"
13"gitverse.ru/synapse/kubelatte/pkg/mutation/location"
14"gitverse.ru/synapse/kubelatte/pkg/observability/logger"
15"gitverse.ru/synapse/kubelatte/pkg/util"
16"gitverse.ru/synapse/kubelatte/pkg/util/env"
17"gitverse.ru/synapse/kubelatte/pkg/webhook/config"
18"html"
19"k8s.io/utils/strings/slices"
20"reflect"
21"sigs.k8s.io/yaml"
22"strings"
23)
24
25type MutatorI interface {
26ApplyMutation(ctx context.Context, arfields *common.ARFields, raw []byte) ([]jsondiff.Operation, error)
27}
28
29var ErrJsonDiffCompare = errors.New("jsondiff.Compare error")
30
31func NewMutator(renderer MutRendererI) MutatorI {
32return &Mutator{renderer: renderer}
33}
34
35type Mutator struct {
36renderer MutRendererI
37}
38
39func (m *Mutator) ApplyMutation(ctx context.Context, arfields *common.ARFields, raw []byte) ([]jsondiff.Operation, error) {
40var operations []jsondiff.Operation
41
42log := logger.FromContext(ctx)
43renders := m.renderer.GetRenders(ctx, arfields)
44for _, templateRender := range renders {
45newObj := m.getNewObject(ctx, templateRender, raw)
46patch, err := jsondiff.Compare(arfields.Object, newObj)
47if err != nil {
48
49log.Errorf("jsondiff.Compare error: %s", err.Error())
50return nil, errors2.Join(err, ErrJsonDiffCompare)
51}
52operations = append(operations, patch...)
53}
54return operations, nil
55}
56
57func (m *Mutator) getNewObject(ctx context.Context, templateRender util.RenderItem, raw []byte) map[string]interface{} {
58log := logger.FromContext(ctx)
59var newObj map[string]interface{}
60opts := conjungo.NewOptions()
61err := json.Unmarshal(raw, &newObj)
62if err != nil {
63log.Errorf("ApplyMutation: unmarshal error: %s", err.Error())
64return nil
65}
66
67var nodes *location.Node
68if templateRender.Template.Spec.Location != "" {
69nodes, err = location.CompilePathToLocation(templateRender.Template.Spec.Location)
70if err != nil {
71log.Errorf("Mutate: location path error: %s", err.Error())
72return nil
73}
74}
75
76var renderObj map[string]interface{}
77//ToYaml может обернуть "@" в '@'
78err = yaml.Unmarshal([]byte(html.UnescapeString(templateRender.Render)), &renderObj)
79if err != nil {
80if templateRender.Template.Spec.Location == "" {
81log.Errorf("yaml unmarshal error: %s", err.Error())
82return nil
83}
84lastPath := nodes.GetLastItem()
85news := util.WrapList(lastPath, html.UnescapeString(templateRender.Render))
86err = yaml.Unmarshal([]byte(news), &renderObj)
87if err != nil {
88news = util.WrapString(nodes.GetLastItem(), html.UnescapeString(templateRender.Render))
89err = yaml.Unmarshal([]byte(news), &renderObj)
90if err != nil {
91log.Errorf("yaml unmarshal error: %s", err.Error())
92return nil
93}
94}
95}
96
97if templateRender.Action != "replace" {
98if nodes != nil {
99renderObj, err = nodes.MergeByLocation(newObj, renderObj, nodes)
100if renderObj == nil {
101log.Error("merge location error: renderObj is nil")
102return nil
103}
104if err != nil {
105log.Errorf("merge location error: %s", err.Error())
106return nil
107}
108}
109opts.SetKindMergeFunc(reflect.Slice,
110func(t, s reflect.Value, o *conjungo.Options) (reflect.Value, error) {
111aT, _ := t.Interface().([]interface{})
112aS, _ := s.Interface().([]interface{})
113aT = util.MergeSliceCommon(aT, aS, true)
114return reflect.ValueOf(aT), nil
115})
116opts.SetKindMergeFunc(reflect.Map,
117func(t, s reflect.Value, o *conjungo.Options) (reflect.Value, error) {
118aT, _ := t.Interface().(map[string]interface{})
119aS, _ := s.Interface().(map[string]interface{})
120aT = util.MergeStruct(aT, aS, true)
121return reflect.ValueOf(aT), nil
122})
123} else {
124if nodes != nil {
125renderObj, err = nodes.ReplaceByLocation(deepcopy.DeepCopy(newObj).(map[string]interface{}), renderObj, nodes)
126if err != nil {
127log.Errorf("mutate location error: %s", err.Error())
128return nil
129}
130
131opts.SetKindMergeFunc(reflect.Map,
132func(_, s reflect.Value, _ *conjungo.Options) (reflect.Value, error) {
133aS, _ := s.Interface().(map[string]any)
134return reflect.ValueOf(aS), nil
135})
136}
137opts.SetKindMergeFunc(reflect.Slice,
138func(t, s reflect.Value, o *conjungo.Options) (reflect.Value, error) {
139aS, _ := s.Interface().([]interface{})
140return reflect.ValueOf(aS), nil
141})
142}
143
144opts.Overwrite = templateRender.Action == "replace"
145renderObj = addMutatingLabel(renderObj)
146err = conjungo.Merge(&newObj, renderObj, opts)
147if err != nil {
148log.Errorf("conjungo.Merge error: %s", err.Error())
149return nil
150}
151
152return newObj
153}
154
155type ObjInfo struct {
156Kind string
157name string
158annots map[string]string
159labels map[string]string
160}
161
162func getObjectInfoFromMap(obj map[string]interface{}) ObjInfo {
163objInf := ObjInfo{
164annots: map[string]string{},
165labels: map[string]string{},
166}
167
168kd, ok := obj["kind"]
169if ok {
170objInf.Kind = kd.(string)
171}
172
173metadata, ok := obj["metadata"]
174if !ok {
175return objInf
176}
177
178tmpAn, ok := metadata.(map[string]interface{})["annotations"]
179if ok {
180for k, v := range tmpAn.(map[string]interface{}) {
181objInf.annots[k] = v.(string)
182}
183}
184
185tmpLb, ok := metadata.(map[string]interface{})["labels"]
186if ok {
187for k, v := range tmpLb.(map[string]interface{}) {
188objInf.labels[k] = v.(string)
189}
190}
191
192tmpName, ok := metadata.(map[string]interface{})["name"]
193if ok {
194objInf.name = tmpName.(string)
195}
196
197return objInf
198}
199
200func addMutatingLabel(obj map[string]interface{}) map[string]interface{} {
201if obj == nil {
202return nil
203}
204if _, ok := obj["metadata"]; !ok {
205obj["metadata"] = make(map[string]interface{})
206}
207
208if _, ok := obj["metadata"].(map[string]interface{})["labels"]; !ok {
209obj["metadata"].(map[string]interface{})["labels"] = make(map[string]interface{})
210}
211
212obj["metadata"].(map[string]interface{})["labels"].(map[string]interface{})[env.ServiceAnnotationPrefix+"mutation.resource"] = "true"
213return obj
214}
215
216func getSelectorKey(key string, values []string) string {
217isLabelException := len(env.KbltLabelExceptionValues) > 0 && key == "app"
218isIstioApp := false
219if isLabelException {
220for _, v := range env.KbltLabelExceptionValues {
221if slices.Contains(values, v) {
222isIstioApp = true
223}
224}
225}
226if isLabelException && isIstioApp {
227return key
228}
229if env.KbltPrefixEnabled && !strings.HasPrefix(key, config.Prefix) {
230key = fmt.Sprintf("%s.%s", config.Prefix, key)
231}
232return key
233}
234