kubelatte-ce
Форк от sbertech/kubelatte-ce
435 строк · 13.1 Кб
1package templates
2
3import (
4"context"
5"fmt"
6"github.com/Masterminds/sprig/v3"
7"github.com/gohobby/deepcopy"
8"gitverse.ru/synapse/kubelatte/pkg/api/v1alpha1"
9"gitverse.ru/synapse/kubelatte/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 nil
26}
27}
28return templateEngine
29}
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, nil
52}
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), nil
65}
66
67func Indent(in string, spacesNum int) string {
68spaces := strings.Repeat(" ", spacesNum)
69out := strings.Replace(in, "\n", "\n"+spaces, -1) // nolint:gocritic
70return out + "\n"
71}
72
73func Nindent(in string, spacesNum int) string {
74out := "\n" + in
75return Indent(out, spacesNum)
76}
77
78func ReplaceKey(oldK, newKey string, in any) any {
79log := logger.FromContext(context.Background())
80oldKeyPath := strings.Split(oldK, ".")
81tmp := in
82
83for i, key := range oldKeyPath {
84tmpMap, ok := tmp.(map[string]any)
85if !ok {
86log.Debugf("ReplaceKey: can not assign to map %v", in)
87return in
88}
89
90tmp = tmpMap[key]
91
92if i == len(oldKeyPath)-1 {
93v, ok := tmpMap[key]
94if !ok {
95return in
96}
97
98delete(tmpMap, key)
99tmpMap[newKey] = v
100log.Debugf("ReplaceKey executed: %v -> %v", key, newKey)
101}
102}
103
104return in
105}
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 := obj
112
113for i, key := range pathToDelete {
114tmpMap, ok := tmp.(map[string]any)
115if !ok {
116log.Debugf("Del: can not assign to map %v", obj)
117return obj
118}
119
120tmp = tmpMap[key]
121
122if i == len(pathToDelete)-1 {
123_, ok := tmpMap[key]
124if !ok {
125log.Debug("Del: key was not deleted")
126return obj
127}
128delete(tmpMap, key)
129log.Debugf("Del: key %v was deleted", key)
130}
131}
132
133return obj
134}
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"] = config
202value.(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, nil
206
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"] = config
261value.(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"] = config
266typedValue := 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, nil
276}
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"] = config
318value.(map[string]interface{})["typed_config"].(map[string]interface{})["@type"] = filterType
319delete(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_value
353perms = append(perms, permMap)
354}
355policies.(map[string]interface{})[k].(map[string]interface{})["permissions"] = perms
356}
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_value
369princ = append(princ, permMap)
370}
371policies.(map[string]interface{})[k].(map[string]interface{})["principals"] = princ
372}
373}
374return in, nil
375
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, nil
423}
424name, ok := subfilter.(map[string]interface{})["name"]
425if !ok {
426return out, nil
427}
428if name == "envoy.router" {
429subfilter.(map[string]interface{})["name"] = "envoy.filters.http.router"
430}
431
432return out, nil
433}
434return out, nil
435}
436