kubelatte-ce
Форк от sbertech/kubelatte-ce
233 строки · 6.4 Кб
1package mutation2
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.Operation41
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, nil55}
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 nil65}66
67var nodes *location.Node68if 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 nil73}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 nil83}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 nil93}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 nil103}104if err != nil {105log.Errorf("merge location error: %s", err.Error())106return nil107}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), nil115})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), nil122})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 nil129}130
131opts.SetKindMergeFunc(reflect.Map,132func(_, s reflect.Value, _ *conjungo.Options) (reflect.Value, error) {133aS, _ := s.Interface().(map[string]any)134return reflect.ValueOf(aS), nil135})136}137opts.SetKindMergeFunc(reflect.Slice,138func(t, s reflect.Value, o *conjungo.Options) (reflect.Value, error) {139aS, _ := s.Interface().([]interface{})140return reflect.ValueOf(aS), nil141})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 nil150}151
152return newObj153}
154
155type ObjInfo struct {156Kind string157name string158annots map[string]string159labels map[string]string160}
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 objInf176}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 objInf198}
199
200func addMutatingLabel(obj map[string]interface{}) map[string]interface{} {201if obj == nil {202return nil203}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 obj214}
215
216func getSelectorKey(key string, values []string) string {217isLabelException := len(env.KbltLabelExceptionValues) > 0 && key == "app"218isIstioApp := false219if isLabelException {220for _, v := range env.KbltLabelExceptionValues {221if slices.Contains(values, v) {222isIstioApp = true223}224}225}226if isLabelException && isIstioApp {227return key228}229if env.KbltPrefixEnabled && !strings.HasPrefix(key, config.Prefix) {230key = fmt.Sprintf("%s.%s", config.Prefix, key)231}232return key233}
234