master
go 133 lines 3.32 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package jobruntime
4
5 import (
6 "fmt"
7 "maps"
8 "sort"
9
10 "github.com/netdata/netdata/go/plugins/pkg/metrix"
11 "github.com/netdata/netdata/go/plugins/pkg/netdataapi"
12 "github.com/netdata/netdata/go/plugins/plugin/framework/chartengine"
13 )
14
15 const defaultHostScopeKey = ""
16
17 func (j *JobV2) ensureScopeState(scope metrix.HostScope) (*jobV2ScopeState, error) {
18 if j == nil {
19 return nil, fmt.Errorf("nil job")
20 }
21 scopeKey := scope.ScopeKey
22 if scope.IsDefault() {
23 scope = metrix.HostScope{}
24 scopeKey = defaultHostScopeKey
25 }
26 if j.scopeStates == nil {
27 j.scopeStates = make(map[string]*jobV2ScopeState)
28 }
29 if state := j.scopeStates[scopeKey]; state != nil {
30 state.scope = scope
31 return state, nil
32 }
33
34 engine, err := j.newScopeEngine()
35 if err != nil {
36 return nil, err
37 }
38 state := &jobV2ScopeState{
39 scopeKey: scopeKey,
40 scope: scope,
41 engine: engine,
42 }
43 j.scopeStates[scopeKey] = state
44 return state, nil
45 }
46
47 func (j *JobV2) newScopeEngine() (*chartengine.Engine, error) {
48 opts := append([]chartengine.Option{}, j.engineOptions...)
49 opts = append(opts, chartengine.WithRuntimeStore(nil))
50 if j.runtimeAggregator != nil {
51 opts = append(opts, chartengine.WithRuntimeSampleObserver(j.runtimeAggregator.Observe))
52 }
53 engine, err := chartengine.New(opts...)
54 if err != nil {
55 return nil, err
56 }
57 if err := engine.LoadYAML(j.chartTemplateYAML, j.chartTemplateRevision); err != nil {
58 return nil, err
59 }
60 return engine, nil
61 }
62
63 func (j *JobV2) liveScopeSet() map[string]metrix.HostScope {
64 scopes := make(map[string]metrix.HostScope)
65 reader := j.store.Read(metrix.ReadRaw(), metrix.ReadFlatten())
66 for _, scope := range reader.HostScopes() {
67 if j.scopeHasVisibleSeries(scope.ScopeKey) {
68 scopes[scope.ScopeKey] = scope
69 }
70 }
71 return scopes
72 }
73
74 func (j *JobV2) scopeWorkSet(liveScopes map[string]metrix.HostScope) map[string]metrix.HostScope {
75 scopes := make(map[string]metrix.HostScope, len(liveScopes)+len(j.scopeStates))
76 maps.Copy(scopes, liveScopes)
77 // Retain previously emitted scopes until their engine emits lifecycle
78 // removals. This includes default scope when unscoped series disappear.
79 for key, state := range j.scopeStates {
80 if _, ok := scopes[key]; ok {
81 continue
82 }
83 scopes[key] = state.scope
84 }
85 return scopes
86 }
87
88 func (j *JobV2) scopeHasVisibleSeries(scopeKey string) bool {
89 reader := j.store.Read(metrix.ReadFlatten(), metrix.ReadHostScope(scopeKey))
90 found := false
91 reader.ForEachSeries(func(string, metrix.LabelView, metrix.SampleValue) {
92 found = true
93 })
94 return found
95 }
96
97 func sortedScopeKeys(scopes map[string]metrix.HostScope) []string {
98 keys := make([]string, 0, len(scopes))
99 for key := range scopes {
100 keys = append(keys, key)
101 }
102 sortHostScopeKeys(keys)
103 return keys
104 }
105
106 func sortedScopeStateKeys(scopes map[string]*jobV2ScopeState) []string {
107 keys := make([]string, 0, len(scopes))
108 for key := range scopes {
109 keys = append(keys, key)
110 }
111 sortHostScopeKeys(keys)
112 return keys
113 }
114
115 func sortHostScopeKeys(keys []string) {
116 sort.Slice(keys, func(i, j int) bool {
117 if keys[i] == defaultHostScopeKey {
118 return true
119 }
120 if keys[j] == defaultHostScopeKey {
121 return false
122 }
123 return keys[i] < keys[j]
124 })
125 }
126
127 func metrixHostScopeInfo(scope metrix.HostScope) netdataapi.HostInfo {
128 return netdataapi.HostInfo{
129 GUID: scope.GUID,
130 Hostname: scope.Hostname,
131 Labels: scope.Labels,
132 }
133 }