В 22:00 МСК будет объявлен перерыв - 10 минут. Вы отдыхаете - мы обновляем!

kubelatte-ce

Форк от sbertech/kubelatte-ce
Форк
2
233 строки · 6.4 Кб
1
package mutation
2

3
import (
4
	"context"
5
	"encoding/json"
6
	errors2 "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

25
type MutatorI interface {
26
	ApplyMutation(ctx context.Context, arfields *common.ARFields, raw []byte) ([]jsondiff.Operation, error)
27
}
28

29
var ErrJsonDiffCompare = errors.New("jsondiff.Compare error")
30

31
func NewMutator(renderer MutRendererI) MutatorI {
32
	return &Mutator{renderer: renderer}
33
}
34

35
type Mutator struct {
36
	renderer MutRendererI
37
}
38

39
func (m *Mutator) ApplyMutation(ctx context.Context, arfields *common.ARFields, raw []byte) ([]jsondiff.Operation, error) {
40
	var operations []jsondiff.Operation
41

42
	log := logger.FromContext(ctx)
43
	renders := m.renderer.GetRenders(ctx, arfields)
44
	for _, templateRender := range renders {
45
		newObj := m.getNewObject(ctx, templateRender, raw)
46
		patch, err := jsondiff.Compare(arfields.Object, newObj)
47
		if err != nil {
48

49
			log.Errorf("jsondiff.Compare error: %s", err.Error())
50
			return nil, errors2.Join(err, ErrJsonDiffCompare)
51
		}
52
		operations = append(operations, patch...)
53
	}
54
	return operations, nil
55
}
56

57
func (m *Mutator) getNewObject(ctx context.Context, templateRender util.RenderItem, raw []byte) map[string]interface{} {
58
	log := logger.FromContext(ctx)
59
	var newObj map[string]interface{}
60
	opts := conjungo.NewOptions()
61
	err := json.Unmarshal(raw, &newObj)
62
	if err != nil {
63
		log.Errorf("ApplyMutation: unmarshal error: %s", err.Error())
64
		return nil
65
	}
66

67
	var nodes *location.Node
68
	if templateRender.Template.Spec.Location != "" {
69
		nodes, err = location.CompilePathToLocation(templateRender.Template.Spec.Location)
70
		if err != nil {
71
			log.Errorf("Mutate: location path error: %s", err.Error())
72
			return nil
73
		}
74
	}
75

76
	var renderObj map[string]interface{}
77
	//ToYaml может обернуть "@" в '@'
78
	err = yaml.Unmarshal([]byte(html.UnescapeString(templateRender.Render)), &renderObj)
79
	if err != nil {
80
		if templateRender.Template.Spec.Location == "" {
81
			log.Errorf("yaml unmarshal error: %s", err.Error())
82
			return nil
83
		}
84
		lastPath := nodes.GetLastItem()
85
		news := util.WrapList(lastPath, html.UnescapeString(templateRender.Render))
86
		err = yaml.Unmarshal([]byte(news), &renderObj)
87
		if err != nil {
88
			news = util.WrapString(nodes.GetLastItem(), html.UnescapeString(templateRender.Render))
89
			err = yaml.Unmarshal([]byte(news), &renderObj)
90
			if err != nil {
91
				log.Errorf("yaml unmarshal error: %s", err.Error())
92
				return nil
93
			}
94
		}
95
	}
96

97
	if templateRender.Action != "replace" {
98
		if nodes != nil {
99
			renderObj, err = nodes.MergeByLocation(newObj, renderObj, nodes)
100
			if renderObj == nil {
101
				log.Error("merge location error: renderObj is nil")
102
				return nil
103
			}
104
			if err != nil {
105
				log.Errorf("merge location error: %s", err.Error())
106
				return nil
107
			}
108
		}
109
		opts.SetKindMergeFunc(reflect.Slice,
110
			func(t, s reflect.Value, o *conjungo.Options) (reflect.Value, error) {
111
				aT, _ := t.Interface().([]interface{})
112
				aS, _ := s.Interface().([]interface{})
113
				aT = util.MergeSliceCommon(aT, aS, true)
114
				return reflect.ValueOf(aT), nil
115
			})
116
		opts.SetKindMergeFunc(reflect.Map,
117
			func(t, s reflect.Value, o *conjungo.Options) (reflect.Value, error) {
118
				aT, _ := t.Interface().(map[string]interface{})
119
				aS, _ := s.Interface().(map[string]interface{})
120
				aT = util.MergeStruct(aT, aS, true)
121
				return reflect.ValueOf(aT), nil
122
			})
123
	} else {
124
		if nodes != nil {
125
			renderObj, err = nodes.ReplaceByLocation(deepcopy.DeepCopy(newObj).(map[string]interface{}), renderObj, nodes)
126
			if err != nil {
127
				log.Errorf("mutate location error: %s", err.Error())
128
				return nil
129
			}
130

131
			opts.SetKindMergeFunc(reflect.Map,
132
				func(_, s reflect.Value, _ *conjungo.Options) (reflect.Value, error) {
133
					aS, _ := s.Interface().(map[string]any)
134
					return reflect.ValueOf(aS), nil
135
				})
136
		}
137
		opts.SetKindMergeFunc(reflect.Slice,
138
			func(t, s reflect.Value, o *conjungo.Options) (reflect.Value, error) {
139
				aS, _ := s.Interface().([]interface{})
140
				return reflect.ValueOf(aS), nil
141
			})
142
	}
143

144
	opts.Overwrite = templateRender.Action == "replace"
145
	renderObj = addMutatingLabel(renderObj)
146
	err = conjungo.Merge(&newObj, renderObj, opts)
147
	if err != nil {
148
		log.Errorf("conjungo.Merge error: %s", err.Error())
149
		return nil
150
	}
151

152
	return newObj
153
}
154

155
type ObjInfo struct {
156
	Kind   string
157
	name   string
158
	annots map[string]string
159
	labels map[string]string
160
}
161

162
func getObjectInfoFromMap(obj map[string]interface{}) ObjInfo {
163
	objInf := ObjInfo{
164
		annots: map[string]string{},
165
		labels: map[string]string{},
166
	}
167

168
	kd, ok := obj["kind"]
169
	if ok {
170
		objInf.Kind = kd.(string)
171
	}
172

173
	metadata, ok := obj["metadata"]
174
	if !ok {
175
		return objInf
176
	}
177

178
	tmpAn, ok := metadata.(map[string]interface{})["annotations"]
179
	if ok {
180
		for k, v := range tmpAn.(map[string]interface{}) {
181
			objInf.annots[k] = v.(string)
182
		}
183
	}
184

185
	tmpLb, ok := metadata.(map[string]interface{})["labels"]
186
	if ok {
187
		for k, v := range tmpLb.(map[string]interface{}) {
188
			objInf.labels[k] = v.(string)
189
		}
190
	}
191

192
	tmpName, ok := metadata.(map[string]interface{})["name"]
193
	if ok {
194
		objInf.name = tmpName.(string)
195
	}
196

197
	return objInf
198
}
199

200
func addMutatingLabel(obj map[string]interface{}) map[string]interface{} {
201
	if obj == nil {
202
		return nil
203
	}
204
	if _, ok := obj["metadata"]; !ok {
205
		obj["metadata"] = make(map[string]interface{})
206
	}
207

208
	if _, ok := obj["metadata"].(map[string]interface{})["labels"]; !ok {
209
		obj["metadata"].(map[string]interface{})["labels"] = make(map[string]interface{})
210
	}
211

212
	obj["metadata"].(map[string]interface{})["labels"].(map[string]interface{})[env.ServiceAnnotationPrefix+"mutation.resource"] = "true"
213
	return obj
214
}
215

216
func getSelectorKey(key string, values []string) string {
217
	isLabelException := len(env.KbltLabelExceptionValues) > 0 && key == "app"
218
	isIstioApp := false
219
	if isLabelException {
220
		for _, v := range env.KbltLabelExceptionValues {
221
			if slices.Contains(values, v) {
222
				isIstioApp = true
223
			}
224
		}
225
	}
226
	if isLabelException && isIstioApp {
227
		return key
228
	}
229
	if env.KbltPrefixEnabled && !strings.HasPrefix(key, config.Prefix) {
230
		key = fmt.Sprintf("%s.%s", config.Prefix, key)
231
	}
232
	return key
233
}
234

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.