master
go 148 lines 4.13 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package program
4
5 import (
6 "errors"
7 "fmt"
8 )
9
10 // Algorithm defines how Netdata interprets values on wire.
11 type Algorithm string
12
13 const (
14 // AlgorithmAbsolute sends direct values.
15 AlgorithmAbsolute Algorithm = "absolute"
16 // AlgorithmIncremental sends monotonic totals (Netdata computes rates/deltas).
17 AlgorithmIncremental Algorithm = "incremental"
18 )
19
20 // ChartType controls chart visualization.
21 type ChartType string
22
23 const (
24 ChartTypeLine ChartType = "line"
25 ChartTypeArea ChartType = "area"
26 ChartTypeStacked ChartType = "stacked"
27 ChartTypeHeatmap ChartType = "heatmap"
28 )
29
30 // ReduceOp defines aggregation for series collisions mapped to one dimension.
31 type ReduceOp string
32
33 const (
34 // ReduceSum is the phase-1 collision reduction rule.
35 ReduceSum ReduceOp = "sum"
36 )
37
38 // Chart is one compiled chart template in immutable program IR.
39 type Chart struct {
40 // TemplateID is compiler-assigned stable ID inside one Program revision.
41 TemplateID string
42
43 Meta ChartMeta
44 Identity ChartIdentity
45 Labels LabelPolicy
46 Lifecycle LifecyclePolicy
47
48 // Dimensions are declaration-ordered templates.
49 Dimensions []Dimension
50
51 // CollisionReduce is currently fixed to ReduceSum in phase-1.
52 CollisionReduce ReduceOp
53 }
54
55 // ChartMeta carries user-facing chart metadata after normalization/defaulting.
56 type ChartMeta struct {
57 Title string
58 Family string
59 Context string
60 Units string
61 Algorithm Algorithm
62 Type ChartType
63 Priority int
64 }
65
66 // ChartIdentity describes how chart instances are derived.
67 //
68 // Phase-1 uses literal chart IDs and optional instance suffix derivation from
69 // configured labels.
70 type ChartIdentity struct {
71 // IDTemplate is a normalized literal chart ID.
72 IDTemplate Template
73
74 // InstanceByLabels contains resolved explicit identity selectors (if used).
75 InstanceByLabels []InstanceLabelSelector
76
77 // ContextNamespace holds normalized namespace fragments that participate in
78 // derived context/id building in namespace-based authoring mode.
79 ContextNamespace []string
80
81 // Static is true when identity renders one aggregated chart instance.
82 Static bool
83 }
84
85 func validateChart(chart Chart) error {
86 var errs []error
87 if chart.TemplateID == "" {
88 errs = append(errs, fmt.Errorf("template_id is required"))
89 }
90 if chart.Meta.Context == "" {
91 errs = append(errs, fmt.Errorf("context is required"))
92 }
93 if chart.Meta.Units == "" {
94 errs = append(errs, fmt.Errorf("units is required"))
95 }
96 if chart.Meta.Algorithm != AlgorithmAbsolute && chart.Meta.Algorithm != AlgorithmIncremental {
97 errs = append(errs, fmt.Errorf("invalid algorithm %q", chart.Meta.Algorithm))
98 }
99 switch chart.Meta.Type {
100 case ChartTypeLine, ChartTypeArea, ChartTypeStacked, ChartTypeHeatmap:
101 default:
102 errs = append(errs, fmt.Errorf("invalid chart type %q", chart.Meta.Type))
103 }
104 if err := validateInstanceLabelSelectors(chart.Identity.InstanceByLabels); err != nil {
105 errs = append(errs, fmt.Errorf("identity: %w", err))
106 }
107 if err := validateLabelPolicy(chart.Labels); err != nil {
108 errs = append(errs, fmt.Errorf("labels: %w", err))
109 }
110 if chart.CollisionReduce == "" {
111 errs = append(errs, fmt.Errorf("collision reduce op is required"))
112 }
113 if len(chart.Dimensions) == 0 {
114 errs = append(errs, fmt.Errorf("at least one dimension is required"))
115 }
116 for i, dim := range chart.Dimensions {
117 if err := validateDimension(dim); err != nil {
118 errs = append(errs, fmt.Errorf("dimension[%d]: %w", i, err))
119 }
120 }
121 return errors.Join(errs...)
122 }
123
124 func (c Chart) clone() Chart {
125 out := c
126 out.Meta = c.Meta
127 out.Identity = c.Identity.clone()
128 out.Labels = c.Labels.clone()
129 out.Lifecycle = c.Lifecycle.clone()
130
131 out.Dimensions = make([]Dimension, 0, len(c.Dimensions))
132 for _, dim := range c.Dimensions {
133 out.Dimensions = append(out.Dimensions, dim.clone())
134 }
135 return out
136 }
137
138 func (i ChartIdentity) clone() ChartIdentity {
139 out := i
140 out.IDTemplate = i.IDTemplate.clone()
141
142 out.InstanceByLabels = make([]InstanceLabelSelector, 0, len(i.InstanceByLabels))
143 for _, selector := range i.InstanceByLabels {
144 out.InstanceByLabels = append(out.InstanceByLabels, selector.clone())
145 }
146 out.ContextNamespace = append([]string(nil), i.ContextNamespace...)
147 return out
148 }