master
go 150 lines 3.12 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package pipeline
4
5 import (
6 "bytes"
7 "errors"
8 "fmt"
9 "strings"
10 "text/template"
11
12 "github.com/netdata/netdata/go/plugins/logger"
13 "github.com/netdata/netdata/go/plugins/plugin/agent/discovery/sd/model"
14 "github.com/netdata/netdata/go/plugins/plugin/framework/confgroup"
15
16 "gopkg.in/yaml.v2"
17 )
18
19 func newServiceEngine(cfg []ServiceRuleConfig) (*serviceEngine, error) {
20 rules, err := newServiceRules(cfg)
21 if err != nil {
22 return nil, err
23 }
24 return &serviceEngine{rules: rules}, nil
25 }
26
27 type serviceEngine struct {
28 *logger.Logger
29 rules []*serviceRule
30 buf bytes.Buffer
31 }
32
33 type serviceRule struct {
34 id string
35 match *template.Template
36 tmpl *template.Template // optional
37 }
38
39 func newServiceRules(cfg []ServiceRuleConfig) ([]*serviceRule, error) {
40 fmap := newFuncMap()
41 var rules []*serviceRule
42
43 for i, rc := range cfg {
44 i++
45
46 m, err := parseTemplate(rc.Match, fmap)
47 if err != nil {
48 return nil, fmt.Errorf("service '%s'[%d]: match: %v", rc.ID, i, err)
49 }
50
51 var tmpl *template.Template
52 if strings.TrimSpace(rc.ConfigTemplate) != "" {
53 tmpl, err = parseTemplate(rc.ConfigTemplate, fmap)
54 if err != nil {
55 return nil, fmt.Errorf("service '%s'[%d]: config_template: %v", rc.ID, i, err)
56 }
57 }
58
59 rules = append(rules, &serviceRule{
60 id: rc.ID, match: m, tmpl: tmpl,
61 })
62 }
63 return rules, nil
64 }
65
66 func (s *serviceEngine) compose(tgt model.Target) []confgroup.Config {
67 var out []confgroup.Config
68
69 for i, r := range s.rules {
70 s.buf.Reset()
71
72 if err := r.match.Execute(&s.buf, tgt); err != nil {
73 s.Warningf("failed to execute services[%d]->match on target '%s'", i+1, tgt.TUID())
74 continue
75 }
76 if strings.TrimSpace(s.buf.String()) != "true" {
77 continue
78 }
79
80 // No config_template => drop
81 if r.tmpl == nil {
82 break
83 }
84
85 s.buf.Reset()
86 if err := r.tmpl.Execute(&s.buf, tgt); err != nil {
87 s.Warningf("failed to execute services[%d]->config_template on target '%s': %v", i+1, tgt.TUID(), err)
88 continue
89 }
90 if s.buf.Len() == 0 {
91 continue
92 }
93
94 cfgs, err := parseConfigTemplateData(s.buf.Bytes())
95 if err != nil {
96 s.Warningf("failed to parse services[%d] template data: %v", i+1, err)
97 continue
98 }
99
100 for _, cfg := range cfgs {
101 if cfg.Module() == "" {
102 cfg.SetModule(r.id)
103 }
104 }
105
106 out = append(out, cfgs...)
107 }
108
109 if len(out) > 0 {
110 s.Debugf("created %d config(s) for target '%s'", len(out), tgt.TUID())
111 }
112
113 return out
114 }
115
116 func parseConfigTemplateData(bs []byte) ([]confgroup.Config, error) {
117 var data any
118 if err := yaml.Unmarshal(bs, &data); err != nil {
119 return nil, err
120 }
121
122 type (
123 single = map[any]any
124 multi = []any
125 )
126
127 switch data.(type) {
128 case single:
129 var cfg confgroup.Config
130 if err := yaml.Unmarshal(bs, &cfg); err != nil {
131 return nil, err
132 }
133 return []confgroup.Config{cfg}, nil
134 case multi:
135 var cfgs []confgroup.Config
136 if err := yaml.Unmarshal(bs, &cfgs); err != nil {
137 return nil, err
138 }
139 return cfgs, nil
140 default:
141 return nil, errors.New("unknown config format")
142 }
143 }
144
145 func parseTemplate(s string, fmap template.FuncMap) (*template.Template, error) {
146 return template.New("root").
147 Option("missingkey=error").
148 Funcs(fmap).
149 Parse(s)
150 }