Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/metrics/instance/modal_manager.go
4094 views
1
package instance
2
3
import (
4
"fmt"
5
"sync"
6
7
"github.com/go-kit/log"
8
"github.com/go-kit/log/level"
9
"github.com/prometheus/client_golang/prometheus"
10
"github.com/prometheus/client_golang/prometheus/promauto"
11
)
12
13
// Mode controls how instances are created.
14
type Mode string
15
16
// Types of instance modes
17
var (
18
ModeDistinct Mode = "distinct"
19
ModeShared Mode = "shared"
20
21
DefaultMode = ModeShared
22
)
23
24
// UnmarshalYAML unmarshals a string to a Mode. Fails if the string is
25
// unrecognized.
26
func (m *Mode) UnmarshalYAML(unmarshal func(interface{}) error) error {
27
*m = DefaultMode
28
29
var plain string
30
if err := unmarshal(&plain); err != nil {
31
return err
32
}
33
34
switch plain {
35
case string(ModeDistinct):
36
*m = ModeDistinct
37
return nil
38
case string(ModeShared):
39
*m = ModeShared
40
return nil
41
default:
42
return fmt.Errorf("unsupported instance_mode '%s'. supported values 'shared', 'distinct'", plain)
43
}
44
}
45
46
// ModalManager runs instances by either grouping them or running them fully
47
// separately.
48
type ModalManager struct {
49
mut sync.RWMutex
50
mode Mode
51
configs map[string]Config
52
53
changedConfigs *prometheus.CounterVec
54
currentActiveConfigs prometheus.Gauge
55
56
log log.Logger
57
58
// The ModalManager wraps around a "final" Manager that is intended to
59
// launch and manage instances based on Configs. This is specified here by the
60
// "wrapped" Manager.
61
//
62
// However, there may be another manager performing formations on the configs
63
// before they are passed through to wrapped. This is specified by the "active"
64
// Manager.
65
//
66
// If no transformations on Configs are needed, active will be identical to
67
// wrapped.
68
wrapped, active Manager
69
}
70
71
// NewModalManager creates a new ModalManager.
72
func NewModalManager(reg prometheus.Registerer, l log.Logger, next Manager, mode Mode) (*ModalManager, error) {
73
changedConfigs := promauto.With(reg).NewCounterVec(prometheus.CounterOpts{
74
Name: "agent_metrics_configs_changed_total",
75
Help: "Total number of dynamically updated configs",
76
}, []string{"event"})
77
currentActiveConfigs := promauto.With(reg).NewGauge(prometheus.GaugeOpts{
78
Name: "agent_metrics_active_configs",
79
Help: "Current number of active configs being used by the agent.",
80
})
81
82
mm := ModalManager{
83
wrapped: next,
84
log: l,
85
changedConfigs: changedConfigs,
86
currentActiveConfigs: currentActiveConfigs,
87
configs: make(map[string]Config),
88
}
89
if err := mm.SetMode(mode); err != nil {
90
return nil, err
91
}
92
return &mm, nil
93
}
94
95
// SetMode updates the mode ModalManager is running in. Changing the mode is
96
// an expensive operation; all underlying configs must be stopped and then
97
// reapplied.
98
func (m *ModalManager) SetMode(newMode Mode) error {
99
if newMode == "" {
100
newMode = DefaultMode
101
}
102
103
m.mut.Lock()
104
defer m.mut.Unlock()
105
106
var (
107
prevMode = m.mode
108
prevActive = m.active
109
)
110
111
if prevMode == newMode {
112
return nil
113
}
114
115
// Set the active Manager based on the new mode. "distinct" means no transformations
116
// need to be applied and we can use the wrapped Manager directly. Otherwise, we need
117
// to create a new Manager to apply transformations.
118
switch newMode {
119
case ModeDistinct:
120
m.active = m.wrapped
121
case ModeShared:
122
m.active = NewGroupManager(m.wrapped)
123
default:
124
panic("unknown mode " + m.mode)
125
}
126
m.mode = newMode
127
128
// Remove all configs from the previous active Manager.
129
if prevActive != nil {
130
prevActive.Stop()
131
}
132
133
// Re-apply configs to the new active Manager.
134
var firstError error
135
for name, cfg := range m.configs {
136
err := m.active.ApplyConfig(cfg)
137
if err != nil {
138
level.Error(m.log).Log("msg", "failed to apply config when changing modes", "name", name, "prev_mode", prevMode, "new_mode", newMode, "err", err)
139
}
140
if firstError == nil && err != nil {
141
firstError = err
142
}
143
}
144
145
return firstError
146
}
147
148
// GetInstance implements Manager.
149
func (m *ModalManager) GetInstance(name string) (ManagedInstance, error) {
150
m.mut.RLock()
151
defer m.mut.RUnlock()
152
return m.active.GetInstance(name)
153
}
154
155
// ListInstances implements Manager.
156
func (m *ModalManager) ListInstances() map[string]ManagedInstance {
157
m.mut.RLock()
158
defer m.mut.RUnlock()
159
return m.active.ListInstances()
160
}
161
162
// ListConfigs implements Manager.
163
func (m *ModalManager) ListConfigs() map[string]Config {
164
m.mut.RLock()
165
defer m.mut.RUnlock()
166
return m.active.ListConfigs()
167
}
168
169
// ApplyConfig implements Manager.
170
func (m *ModalManager) ApplyConfig(c Config) error {
171
m.mut.Lock()
172
defer m.mut.Unlock()
173
174
if err := m.active.ApplyConfig(c); err != nil {
175
return err
176
}
177
178
if _, existingConfig := m.configs[c.Name]; !existingConfig {
179
m.currentActiveConfigs.Inc()
180
m.changedConfigs.WithLabelValues("created").Inc()
181
} else {
182
m.changedConfigs.WithLabelValues("updated").Inc()
183
}
184
185
m.configs[c.Name] = c
186
187
return nil
188
}
189
190
// DeleteConfig implements Manager.
191
func (m *ModalManager) DeleteConfig(name string) error {
192
m.mut.Lock()
193
defer m.mut.Unlock()
194
195
if err := m.active.DeleteConfig(name); err != nil {
196
return err
197
}
198
199
if _, existingConfig := m.configs[name]; existingConfig {
200
m.currentActiveConfigs.Dec()
201
delete(m.configs, name)
202
}
203
204
m.changedConfigs.WithLabelValues("deleted").Inc()
205
return nil
206
}
207
208
// Stop implements Manager.
209
func (m *ModalManager) Stop() {
210
m.mut.Lock()
211
defer m.mut.Unlock()
212
213
m.active.Stop()
214
m.currentActiveConfigs.Set(0)
215
m.configs = make(map[string]Config)
216
}
217
218