kubelatte-ce
Форк от sbertech/kubelatte-ce
435 строк · 13.1 Кб
1package templates2
3import (4"context"5"fmt"6"github.com/Masterminds/sprig/v3"7"github.com/gohobby/deepcopy"8"gitverse.ru/ktrntrsv/kubelatte-ce/pkg/api/v1alpha1"9"gitverse.ru/ktrntrsv/kubelatte-ce/pkg/observability/logger"10"sigs.k8s.io/yaml"11"strings"12"text/template"13)
14
15func LoadTemplate(ctx context.Context, ts ...v1alpha1.Template) *template.Template {16log := logger.FromContext(ctx)17templateEngine := template.New("common").18Delims("{{%", "%}}").19Funcs(GomplateFuncs).20Funcs(sprig.TxtFuncMap())21for _, t := range ts {22_, err := templateEngine.New(t.Name).Parse(t.Spec.Data)23if err != nil {24log.Errorf("LoadTemplate failed %s", err.Error())25return nil26}27}28return templateEngine29}
30
31var GomplateFuncs = template.FuncMap{32"del": Del,33"toYaml": ToYAML,34"indent": Indent,35"nindent": Nindent,36"fromYaml": FromYAML,37"replaceKey": ReplaceKey,38"OTTconfigPatchesToV3": OTTconfigPatchesToV3,39"RBACconfigPatchesToV3": ConfigPatchesRBACToV3,40"RateLimiterPatchesToV3": RateLimiterPatchesToV3,41}
42
43// FromYAML decodes a YAML string into an interface{}. This allows injection templates to access
44// configs defined as YAML strings.
45func FromYAML(in string) (interface{}, error) {46var out interface{}47
48if err := yaml.Unmarshal([]byte(in), &out); err != nil {49return nil, fmt.Errorf("failed to unmarshal yaml with error: %v. source: %q", err, in)50}51return out, nil52}
53
54// ToYAML encodes an interface{} into a YAML string.
55func ToYAML(in interface{}) (string, error) {56out, err := yaml.Marshal(in)57if err != nil {58return "", fmt.Errorf("failed to unmarshal yaml with error: %v. source: %q", err, out)59}60if in == nil {61out = []byte{}62}63
64return string(out), nil65}
66
67func Indent(in string, spacesNum int) string {68spaces := strings.Repeat(" ", spacesNum)69out := strings.Replace(in, "\n", "\n"+spaces, -1) // nolint:gocritic70return out + "\n"71}
72
73func Nindent(in string, spacesNum int) string {74out := "\n" + in75return Indent(out, spacesNum)76}
77
78func ReplaceKey(oldK, newKey string, in any) any {79log := logger.FromContext(context.Background())80oldKeyPath := strings.Split(oldK, ".")81tmp := in82
83for i, key := range oldKeyPath {84tmpMap, ok := tmp.(map[string]any)85if !ok {86log.Debugf("ReplaceKey: can not assign to map %v", in)87return in88}89
90tmp = tmpMap[key]91
92if i == len(oldKeyPath)-1 {93v, ok := tmpMap[key]94if !ok {95return in96}97
98delete(tmpMap, key)99tmpMap[newKey] = v100log.Debugf("ReplaceKey executed: %v -> %v", key, newKey)101}102}103
104return in105}
106
107// Del go template func. Deleting key from map
108func Del(path string, obj any) any {109log := logger.FromContext(context.Background())110pathToDelete := strings.Split(path, ".")111tmp := obj112
113for i, key := range pathToDelete {114tmpMap, ok := tmp.(map[string]any)115if !ok {116log.Debugf("Del: can not assign to map %v", obj)117return obj118}119
120tmp = tmpMap[key]121
122if i == len(pathToDelete)-1 {123_, ok := tmpMap[key]124if !ok {125log.Debug("Del: key was not deleted")126return obj127}128delete(tmpMap, key)129log.Debugf("Del: key %v was deleted", key)130}131}132
133return obj134}
135
136func RateLimiterPatchesToV3(in interface{}) (interface{}, error) {137copy := deepcopy.DeepCopy(in)138copy, err := replaceFilterChainName(copy)139if err != nil {140return ToYAML(in)141}142copy, err = replaceLimiterTypedConfig(copy)143if err != nil {144return ToYAML(in)145}146return ToYAML(copy)147}
148func OTTconfigPatchesToV3(in interface{}) (interface{}, error) {149copy := deepcopy.DeepCopy(in)150copy, err := replaceFilterChainName(copy)151if err != nil {152return ToYAML(in)153}154copy, err = replaceOTTTypedConfig(copy)155if err != nil {156return ToYAML(in)157}158
159return ToYAML(copy)160}
161
162func replaceLimiterTypedConfig(in interface{}) (interface{}, error) {163_, ok := in.(map[string]interface{})164if !ok {165return nil, fmt.Errorf("cant get value from yaml, check syntax")166}167patch, ok := in.(map[string]interface{})["patch"]168if !ok {169return nil, fmt.Errorf("cant get patch field")170}171_, ok = patch.(map[string]interface{})172if !ok {173return nil, fmt.Errorf("patch is not a map check syntax")174}175value, ok := patch.(map[string]interface{})["value"]176if !ok {177return nil, fmt.Errorf("cant get patch.value field")178}179_, ok = value.(map[string]interface{})180if !ok {181return nil, fmt.Errorf("value is not a map check syntax")182}183config, ok := value.(map[string]interface{})["config"]184if !ok {185return nil, fmt.Errorf("cant get patch.value.config field")186}187_, ok = config.(map[string]interface{})188if !ok {189return nil, fmt.Errorf("value is not a map check syntax")190}191rate_limit_service, ok := config.(map[string]interface{})["rate_limit_service"]192if !ok {193return nil, fmt.Errorf("cant get patch.value.config.rate_limit_service field")194}195_, ok = rate_limit_service.(map[string]interface{})196if !ok {197return nil, fmt.Errorf("patch.value.config.rate_limit_service field is not a map")198}199rate_limit_service.(map[string]interface{})["transport_api_version"] = "V3"200
201value.(map[string]interface{})["typed_config"] = config202value.(map[string]interface{})["typed_config"].(map[string]interface{})["@type"] = "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit"203delete(value.(map[string]interface{}), "config")204
205return in, nil206
207}
208func replaceOTTTypedConfig(in interface{}) (interface{}, error) {209_, ok := in.(map[string]interface{})210if !ok {211return nil, fmt.Errorf("cant get value from yaml, check syntax")212}213patch, ok := in.(map[string]interface{})["patch"]214if !ok {215return nil, fmt.Errorf("cant get patch field")216}217_, ok = patch.(map[string]interface{})218if !ok {219return nil, fmt.Errorf("patch is not a map check syntax")220}221value, ok := patch.(map[string]interface{})["value"]222if !ok {223return nil, fmt.Errorf("cant get patch.value field")224}225_, ok = value.(map[string]interface{})226if !ok {227return nil, fmt.Errorf("value is not a map check syntax")228}229config, ok := value.(map[string]interface{})["config"]230if !ok {231return nil, fmt.Errorf("cant get patch.value.config field")232}233_, ok = config.(map[string]interface{})234if !ok {235return nil, fmt.Errorf("value is not a map check syntax")236}237config.(map[string]interface{})["transport_api_version"] = "V3"238grpcService, ok := config.(map[string]interface{})["grpc_service"]239if !ok {240return nil, fmt.Errorf("cant get patch.value.config.grpc_service field")241}242_, ok = grpcService.(map[string]interface{})243if !ok {244return nil, fmt.Errorf("value is not a map check syntax")245}246googleGrpc, ok := grpcService.(map[string]interface{})["google_grpc"]247if !ok {248return nil, fmt.Errorf("cant get patch.value.config.grpc_service.google_grpc field")249}250_, ok = googleGrpc.(map[string]interface{})251if !ok {252return nil, fmt.Errorf("value is not a map check syntax")253}254statPrefix, ok := googleGrpc.(map[string]interface{})["stat_prefix"]255if !ok {256return nil, fmt.Errorf("cant get patch.value.config.grpc_service.google_grpc.stat_prefix field")257}258switch statPrefix {259case "ext_authz":260value.(map[string]interface{})["typed_config"] = config261value.(map[string]interface{})["typed_config"].(map[string]interface{})["@type"] = "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz"262delete(value.(map[string]interface{}), "config")263case "sbt_authz":264value.(map[string]interface{})["typed_config"] = make(map[string]interface{})265value.(map[string]interface{})["typed_config"].(map[string]interface{})["value"] = config266typedValue := value.(map[string]interface{})["typed_config"].(map[string]interface{})["value"]267typedValue.(map[string]interface{})["api"] = "STANDART"268value.(map[string]interface{})["typed_config"].(map[string]interface{})["@type"] = "type.googleapis.com/udpa.type.v1.TypedStruct"269value.(map[string]interface{})["typed_config"].(map[string]interface{})["typeUrl"] = "type.googleapis.com/envoy.extensions.filters.http.sbt_authz.v3.SbtAuthz"270delete(value.(map[string]interface{}), "config")271default:272return nil, fmt.Errorf("stat_prefix is not supported")273}274
275return in, nil276}
277
278func ConfigPatchesRBACToV3(in interface{}) (interface{}, error) {279copy := deepcopy.DeepCopy(in)280copy, err := replaceFilterChainName(copy)281if err != nil {282return ToYAML(in)283}284copy, err = replaceRBACTypedConfig(copy, "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC")285if err != nil {286return ToYAML(in)287}288
289return ToYAML(copy)290}
291
292func replaceRBACTypedConfig(in interface{}, filterType string) (interface{}, error) {293_, ok := in.(map[string]interface{})294if !ok {295return nil, fmt.Errorf("cant get value from yaml, check syntax")296}297patch, ok := in.(map[string]interface{})["patch"]298if !ok {299return nil, fmt.Errorf("cant get patch field")300}301_, ok = patch.(map[string]interface{})302if !ok {303return nil, fmt.Errorf("patch is not a map check syntax")304}305value, ok := patch.(map[string]interface{})["value"]306if !ok {307return nil, fmt.Errorf("cant get patch.value field")308}309_, ok = value.(map[string]interface{})310if !ok {311return nil, fmt.Errorf("value is not a map check syntax")312}313config, ok := value.(map[string]interface{})["config"]314if !ok {315return nil, fmt.Errorf("cant get patch.value.config field")316}317value.(map[string]interface{})["typed_config"] = config318value.(map[string]interface{})["typed_config"].(map[string]interface{})["@type"] = filterType319delete(value.(map[string]interface{}), "config")320
321typedConfig := value.(map[string]interface{})["typed_config"]322
323rules, ok := typedConfig.(map[string]interface{})["rules"]324if !ok {325return nil, fmt.Errorf("cant get patch.value.config.rules field")326}327if _, ok = rules.(map[string]interface{}); !ok {328return nil, fmt.Errorf("rules is not a map, check syntax")329}330policies, ok := rules.(map[string]interface{})["policies"]331if !ok {332return nil, fmt.Errorf("cant get patch.value.config.rules.policies field")333}334if _, ok = policies.(map[string]interface{}); !ok {335return nil, fmt.Errorf("policies is not a map, check syntax")336}337for k, v := range policies.(map[string]interface{}) {338if _, ok = v.(map[string]interface{}); !ok {339return nil, fmt.Errorf("%v is not a map, check syntax", k)340}341permissions, ok := v.(map[string]interface{})["permissions"]342if !ok {343return nil, fmt.Errorf("cant get patch.value.config.rules.policies.%v.permissions field", k)344}345if _, ok = permissions.([]interface{}); !ok {346if _, ok = permissions.(map[string]interface{}); !ok {347return nil, fmt.Errorf("permissions is not a map, check syntax")348}349var perms []interface{}350for perm_key, perm_value := range permissions.(map[string]interface{}) {351permMap := make(map[string]interface{})352permMap[perm_key] = perm_value353perms = append(perms, permMap)354}355policies.(map[string]interface{})[k].(map[string]interface{})["permissions"] = perms356}357principals, ok := v.(map[string]interface{})["principals"]358if !ok {359return nil, fmt.Errorf("cant get patch.value.config.rules.policies.%v.principals field", k)360}361if _, ok = principals.([]interface{}); !ok {362if _, ok = principals.(map[string]interface{}); !ok {363return nil, fmt.Errorf("principals is not a map, check syntax")364}365var princ []interface{}366for pr_key, pr_value := range principals.(map[string]interface{}) {367permMap := make(map[string]interface{})368permMap[pr_key] = pr_value369princ = append(princ, permMap)370}371policies.(map[string]interface{})[k].(map[string]interface{})["principals"] = princ372}373}374return in, nil375
376}
377
378func replaceFilterChainName(out interface{}) (interface{}, error) {379_, ok := out.(map[string]interface{})380if !ok {381return out, fmt.Errorf("cant get value from yaml, check syntax")382}383match, ok := out.(map[string]interface{})["match"]384if !ok {385return out, fmt.Errorf("cant get match field")386}387_, ok = match.(map[string]interface{})388if !ok {389return out, fmt.Errorf("match is not a map check syntax")390}391listener, ok := match.(map[string]interface{})["listener"]392if !ok {393return out, fmt.Errorf("cant get match.listener field")394}395_, ok = listener.(map[string]interface{})396if !ok {397return out, fmt.Errorf("listener is not a map, check syntax")398}399filterChain, ok := listener.(map[string]interface{})["filterChain"]400if !ok {401return out, fmt.Errorf("cant get match.listener.filterChain field")402}403_, ok = filterChain.(map[string]interface{})404if !ok {405return out, fmt.Errorf("filterChain is not a map, check syntax")406}407filter, ok := filterChain.(map[string]interface{})["filter"]408if !ok {409return out, fmt.Errorf("cant get match.listener.filterChain.filter field")410}411_, ok = filter.(map[string]interface{})412if !ok {413return out, fmt.Errorf("filter is not a map, check syntax")414}415_, ok = filter.(map[string]interface{})["name"]416if !ok {417return out, fmt.Errorf("cant get match.listener.filterChain.filter.name field")418}419filter.(map[string]interface{})["name"] = "envoy.filters.network.http_connection_manager"420if subfilter, ok := filter.(map[string]interface{})["subFilter"]; ok {421if _, ok = subfilter.(map[string]interface{}); !ok {422return out, nil423}424name, ok := subfilter.(map[string]interface{})["name"]425if !ok {426return out, nil427}428if name == "envoy.router" {429subfilter.(map[string]interface{})["name"] = "envoy.filters.http.router"430}431
432return out, nil433}434return out, nil435}
436