kubelatte-ce
Форк от sbertech/kubelatte-ce
246 строк · 8.5 Кб
1package mutation
2
3import (
4"context"
5"errors"
6"fmt"
7"github.com/gohobby/deepcopy"
8"gitverse.ru/ktrntrsv/kubelatte-ce/pkg/api/common"
9"gitverse.ru/ktrntrsv/kubelatte-ce/pkg/api/v1alpha1"
10"gitverse.ru/ktrntrsv/kubelatte-ce/pkg/observability/logger"
11"gitverse.ru/ktrntrsv/kubelatte-ce/pkg/storage"
12"gitverse.ru/ktrntrsv/kubelatte-ce/pkg/util"
13"gitverse.ru/ktrntrsv/kubelatte-ce/pkg/util/match"
14"gitverse.ru/ktrntrsv/kubelatte-ce/pkg/util/templates"
15"k8s.io/api/admission/v1beta1"
16"k8s.io/apimachinery/pkg/apis/meta/v1"
17"k8s.io/apimachinery/pkg/labels"
18"regexp"
19"sigs.k8s.io/yaml"
20"strings"
21)
22
23var ErrCheckMatchSelector = errors.New("err check match selector")
24
25type MutRendererI interface {
26GetRenders(ctx context.Context, arfields *common.ARFields) []util.RenderItem
27GetRendersForMutatingConfig(ctx context.Context, arfields common.ARFields, config v1alpha1.MutationConfig, object ObjInfo, obj map[string]interface{}, templs map[string]v1alpha1.Template, renders []util.RenderItem) ([]util.RenderItem, error)
28ApplyTemplate(ctx context.Context, template v1alpha1.Template, obj map[string]interface{}, strategy string) []util.RenderItem
29}
30
31type MutRenderer struct {
32matcher match.MatcherI
33act MutRendererI
34}
35
36func NewMutRenderer(matcher match.MatcherI, act MutRendererI) MutRendererI {
37renderer := &MutRenderer{matcher: matcher}
38if act == nil {
39act = renderer
40}
41renderer.act = act
42return renderer
43}
44
45func (r *MutRenderer) GetRenders(ctx context.Context, arfields *common.ARFields) []util.RenderItem {
46var renders []util.RenderItem
47triggers := storage.Storage.GetTriggers()
48
49for _, trigger := range triggers {
50renders = append(renders, r.getRendersForTrigger(ctx, trigger, *arfields)...)
51}
52return renders
53}
54
55func (r *MutRenderer) getRendersForTrigger(ctx context.Context, trigger v1alpha1.Trigger, arfields common.ARFields) []util.RenderItem {
56var renders []util.RenderItem
57var err error
58log := logger.FromContext(ctx)
59templs := storage.Storage.GetTemplates()
60obj := deepcopy.DeepCopy(arfields.Object).(map[string]interface{})
61object := getObjectInfoFromMap(obj)
62
63for _, config := range trigger.Spec.MutationConfigs {
64renders, err = r.act.GetRendersForMutatingConfig(ctx, arfields, config, object, obj, templs, renders)
65if err != nil {
66log.Debugf(err.Error())
67}
68}
69
70return renders
71}
72
73func (r *MutRenderer) GetRendersForMutatingConfig(ctx context.Context, arfields common.ARFields, config v1alpha1.MutationConfig, object ObjInfo, obj map[string]interface{}, templs map[string]v1alpha1.Template, renders []util.RenderItem,
74) ([]util.RenderItem, error) {
75log := logger.FromContext(ctx)
76
77if matches, _ := r.matcher.Match(ctx, arfields, config.Match); !matches || arfields.Operation == v1beta1.Delete {
78return renders, fmt.Errorf("check mutation trigger %s match failed, %w", config.Name, match.ErrMatchFailed)
79}
80
81if config.Match.IsEmpty() && !r.checkMatchSelector(ctx, &config, object) {
82return renders, fmt.Errorf("check trigger %s checkMatchSelector failed, %w", config.Name, ErrCheckMatchSelector)
83}
84
85log.Debugf("Check trigger %s match selector ok", config.Name)
86
87if len(config.TemplateRefs) == 0 && object.Kind == "Pod" {
88renders = append(renders, r.getRendersForPod(ctx, templs, obj, config)...)
89}
90
91for _, ref := range config.TemplateRefs {
92if t, ok := templs[ref]; ok {
93renders = append(renders, r.act.ApplyTemplate(ctx, t, obj, config.UpdateStrategy)...)
94}
95}
96return renders, nil
97}
98
99// checkMatchSelector Need replaced to universal Match allow function
100func (r *MutRenderer) checkMatchSelector(ctx context.Context, config *v1alpha1.MutationConfig, obj ObjInfo) bool {
101log := logger.FromContext(ctx)
102log.Debugf("checkMatchSelector got annotations %s, labels %s ", obj.annots, obj.labels)
103log.Debugf("checkMatchSelector ObjectSelector Kind %s, Name %s", obj.Kind, obj.name)
104if config.ObjectSelector.Kind != "" && config.ObjectSelector.Name != "" {
105if obj.Kind == config.ObjectSelector.Kind {
106matches, err := regexp.Match(config.ObjectSelector.Name, []byte(obj.name))
107if err != nil {
108log.Errorf("Mutator: checkMatchSelector failed %s", err.Error())
109}
110if matches {
111log.Debugf("checkMatchSelector ObjectSelector Kind %s, Name %s checked ok", obj.Kind, obj.name)
112return true
113}
114}
115}
116
117if obj.labels != nil && len(config.LabelSelector.MatchExpressions) != 0 {
118labelSet := labels.Set(obj.labels)
119labelSelectors := v1.LabelSelector{}
120
121for _, expression := range config.LabelSelector.MatchExpressions {
122
123labelSelectors.MatchExpressions = append(labelSelectors.MatchExpressions,
124v1.LabelSelectorRequirement{
125Key: getSelectorKey(expression.Key, expression.Values),
126Operator: expression.Operator,
127Values: expression.Values,
128},
129)
130
131sel, err := v1.LabelSelectorAsSelector(&labelSelectors)
132if err != nil {
133log.Errorf("Mutator: checkMatchSelector failed %s", err)
134} else {
135return sel.Matches(labelSet)
136}
137}
138}
139
140if obj.annots != nil {
141triggerKey := getSelectorKey(fmt.Sprintf("%s/%s", config.AnnotationNamespace, config.AnnotationTrigger), nil)
142triggerValue, ok := obj.annots[triggerKey]
143if ok {
144log.Debugf("checkMatchSelector Annotations annotation %s %s ok ", triggerKey, triggerValue)
145if triggerValue != "true" && triggerValue != "enabled" {
146log.Error("Mutator: checkMatchSelector failed skip creation cause annotation value is not valid")
147} else {
148return true
149}
150}
151}
152
153return false
154}
155
156func (r *MutRenderer) getRendersForPod(ctx context.Context, templs map[string]v1alpha1.Template,
157ob map[string]interface{}, config v1alpha1.MutationConfig) []util.RenderItem {
158log := logger.FromContext(ctx)
159obj := deepcopy.DeepCopy(ob).(map[string]interface{})
160
161var detailedData detailedRefTemplateObjectsData
162var renders []util.RenderItem
163strategy := UpdateStrategy(config.UpdateStrategy)
164
165log.Debugf("[getRendersForPod] config.Containers: %v, config.Annotations: %v, config.Volumes: %v",
166config.Containers, config.Annotations, config.Volumes)
167
168detailedData = prepareDataForUniversalMutation(config.Containers, config.Annotations, config.Volumes)
169
170renders = append(renders, r.cutAndApplyTemplate(ctx, detailedData.containers, templs, obj, "container", strategy)...)
171renders = append(renders, r.cutAndApplyTemplate(ctx, detailedData.annotations, templs, obj, "annotation", strategy)...)
172renders = append(renders, r.cutAndApplyTemplate(ctx, detailedData.volumes, templs, obj, "volume", strategy)...)
173
174for i, render := range renders {
175log.Debugf("render %v: %v\n", i, render)
176}
177
178return renders
179}
180
181func (r *MutRenderer) cutAndApplyTemplate(ctx context.Context, resource map[string][]string,
182templs map[string]v1alpha1.Template, obj map[string]interface{}, resourceType string, strategy UpdateStrategy) []util.RenderItem {
183log := logger.FromContext(ctx)
184var renders []util.RenderItem
185var patches []string
186
187for ref, names := range resource {
188templ, ok := templs[ref]
189if !ok {
190log.Debugf("There is no refs to %s, continue", ref)
191continue
192}
193
194rs := r.act.ApplyTemplate(ctx, templ, obj, string(strategy))
195if len(rs) == 0 {
196log.Error("Render is empty, nothing for mutate")
197continue
198}
199templ.Spec.Data = rs[0].Render
200
201var specData interface{}
202if err := yaml.Unmarshal([]byte(templ.Spec.Data), &specData); err != nil {
203log.Errorf("Converting templ.spec.data to yaml error: %s", err.Error())
204continue
205}
206
207specData = convert(specData)
208
209for _, name := range names {
210switch resourceType {
211case "container":
212patches = append(patches, getPatchForContainer(specData, name, obj, strategy)...)
213case "volume":
214patches = append(patches, getPatchForVolume(specData, name, obj, strategy)...)
215case "annotation":
216patches = append(patches, getPatchForAnnotation(specData, name)...)
217}
218}
219
220for _, patch := range patches {
221templ.Spec.Data = patch
222renders = append(renders, r.act.ApplyTemplate(ctx, templ, obj, string(strategy))...)
223}
224}
225
226return renders
227}
228
229func (r *MutRenderer) ApplyTemplate(ctx context.Context, template v1alpha1.Template, obj map[string]interface{}, strategy string) []util.RenderItem {
230log := logger.FromContext(ctx)
231var renders []util.RenderItem
232
233engine := templates.LoadTemplate(ctx, template)
234log.Debugf("loading tmpl %v, %v", template.Name, template.Spec.Data)
235for _, engineTemplate := range engine.Templates() {
236log.Debugf("template %v", engineTemplate.Name())
237var tempBuffer strings.Builder
238err := engineTemplate.Execute(&tempBuffer, obj)
239if err != nil {
240log.Errorf("Mutator: getRenders failed %s", err.Error())
241continue
242}
243renders = append(renders, util.RenderItem{Template: template, Render: tempBuffer.String(), Action: strategy})
244}
245return renders
246}
247