master
go 172 lines 3.91 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package chartengine
4
5 import (
6 "sort"
7 "strings"
8
9 "github.com/netdata/netdata/go/plugins/pkg/metrix"
10 "github.com/netdata/netdata/go/plugins/plugin/framework/chartengine/internal/program"
11 )
12
13 type instanceLabelValue struct {
14 Key string
15 Value string
16 }
17
18 type labelAccessor interface {
19 Get(key string) (string, bool)
20 Range(fn func(key, value string) bool)
21 }
22
23 type mapLabelAccessor map[string]string
24
25 func (m mapLabelAccessor) Get(key string) (string, bool) {
26 value, ok := m[key]
27 return value, ok
28 }
29
30 func (m mapLabelAccessor) Range(fn func(key, value string) bool) {
31 for key, value := range m {
32 if !fn(key, value) {
33 return
34 }
35 }
36 }
37
38 type labelViewAccessor struct {
39 view metrix.LabelView
40 }
41
42 func (l labelViewAccessor) Get(key string) (string, bool) {
43 return l.view.Get(key)
44 }
45
46 func (l labelViewAccessor) Range(fn func(key, value string) bool) {
47 l.view.Range(fn)
48 }
49
50 func renderChartInstanceID(identity program.ChartIdentity, labels map[string]string) (string, bool, error) {
51 return renderChartInstanceIDWithAccessor(identity, mapLabelAccessor(labels))
52 }
53
54 func renderChartInstanceIDFromView(identity program.ChartIdentity, labels metrix.LabelView) (string, bool, error) {
55 return renderChartInstanceIDWithAccessor(identity, labelViewAccessor{view: labels})
56 }
57
58 func renderChartInstanceIDWithAccessor(identity program.ChartIdentity, labels labelAccessor) (string, bool, error) {
59 baseID, ok, err := renderTemplate(identity.IDTemplate, labels)
60 if err != nil {
61 return "", false, err
62 }
63 if !ok {
64 return "", false, nil
65 }
66
67 suffix, ok, err := renderInstanceSuffix(identity, labels)
68 if err != nil {
69 return "", false, err
70 }
71 if !ok {
72 return "", false, nil
73 }
74 if suffix == "" {
75 return baseID, true, nil
76 }
77 return baseID + suffix, true, nil
78 }
79
80 func renderTemplate(tpl program.Template, _ labelAccessor) (string, bool, error) {
81 return tpl.Raw, true, nil
82 }
83
84 func renderInstanceSuffix(identity program.ChartIdentity, labels labelAccessor) (string, bool, error) {
85 values, ok, err := resolveInstanceLabelValues(identity, labels)
86 if err != nil {
87 return "", false, err
88 }
89 if !ok {
90 return "", false, nil
91 }
92 if len(values) == 0 {
93 return "", true, nil
94 }
95
96 parts := make([]string, 0, len(values))
97 hasNonEmpty := false
98 for _, item := range values {
99 part := sanitizeChartIDLabelValue(item.Value)
100 if strings.TrimSpace(part) != "" {
101 hasNonEmpty = true
102 }
103 parts = append(parts, part)
104 }
105 if !hasNonEmpty {
106 // Keep base chart ID when every instance label value is empty.
107 return "", true, nil
108 }
109 for i := range parts {
110 if strings.TrimSpace(parts[i]) == "" {
111 parts[i] = "empty"
112 }
113 }
114 return "_" + strings.Join(parts, "_"), true, nil
115 }
116
117 func resolveInstanceLabelValues(identity program.ChartIdentity, labels labelAccessor) ([]instanceLabelValue, bool, error) {
118 if len(identity.InstanceByLabels) == 0 {
119 return nil, true, nil
120 }
121
122 plan := compileInstanceLabelPlan(identity)
123 out := make([]instanceLabelValue, 0, len(plan.explicitKeys))
124 for _, key := range plan.explicitKeys {
125 value, ok := labels.Get(key)
126 if !ok {
127 // Explicit instance key is required to materialize one instance.
128 return nil, false, nil
129 }
130 out = append(out, instanceLabelValue{
131 Key: key,
132 Value: value,
133 })
134 }
135
136 if plan.includeAll {
137 all := make([]string, 0)
138 labels.Range(func(key, _ string) bool {
139 if _, excluded := plan.excludeSet[key]; excluded {
140 return true
141 }
142 if _, exists := plan.explicitSet[key]; exists {
143 return true
144 }
145 all = append(all, key)
146 return true
147 })
148 sort.Strings(all)
149 for _, key := range all {
150 value, ok := labels.Get(key)
151 if !ok {
152 return nil, false, nil
153 }
154 out = append(out, instanceLabelValue{
155 Key: key,
156 Value: value,
157 })
158 }
159 }
160 return out, true, nil
161 }
162
163 var chartIDLabelValueSanitizer = strings.NewReplacer(
164 "\\", "_",
165 "'", "",
166 " ", "_",
167 ".", "_",
168 )
169
170 func sanitizeChartIDLabelValue(value string) string {
171 return chartIDLabelValueSanitizer.Replace(value)
172 }