Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/operator/build_hierarchy.go
4094 views
1
package operator
2
3
import (
4
"context"
5
"fmt"
6
7
"github.com/go-kit/log"
8
"github.com/go-kit/log/level"
9
gragent "github.com/grafana/agent/pkg/operator/apis/monitoring/v1alpha1"
10
"github.com/grafana/agent/pkg/operator/assets"
11
"github.com/grafana/agent/pkg/operator/config"
12
"github.com/grafana/agent/pkg/operator/hierarchy"
13
prom "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
14
corev1 "k8s.io/api/core/v1"
15
"k8s.io/apimachinery/pkg/api/meta"
16
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17
"k8s.io/apimachinery/pkg/runtime"
18
"sigs.k8s.io/controller-runtime/pkg/client"
19
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
20
)
21
22
// buildHierarchy constructs a resource hierarchy starting from root.
23
func buildHierarchy(ctx context.Context, l log.Logger, cli client.Client, root *gragent.GrafanaAgent) (deployment gragent.Deployment, watchers []hierarchy.Watcher, err error) {
24
deployment.Agent = root
25
26
// search is used throughout BuildHierarchy, where it will perform a list for
27
// a set of objects in the hierarchy and populate the watchers return
28
// variable.
29
search := func(resources []hierarchyResource) error {
30
for _, res := range resources {
31
sel, err := res.Find(ctx, cli)
32
if err != nil {
33
gvk, _ := apiutil.GVKForObject(res.List, cli.Scheme())
34
return fmt.Errorf("failed to find %q resource: %w", gvk.String(), err)
35
}
36
37
watchers = append(watchers, hierarchy.Watcher{
38
Object: res.Selector.ObjectType,
39
Owner: client.ObjectKeyFromObject(root),
40
Selector: sel,
41
})
42
}
43
return nil
44
}
45
46
// Root resources
47
var (
48
metricInstances gragent.MetricsInstanceList
49
logsInstances gragent.LogsInstanceList
50
integrations gragent.IntegrationList
51
)
52
var roots = []hierarchyResource{
53
{List: &metricInstances, Selector: root.MetricsInstanceSelector()},
54
{List: &logsInstances, Selector: root.LogsInstanceSelector()},
55
{List: &integrations, Selector: root.IntegrationsSelector()},
56
}
57
if err := search(roots); err != nil {
58
return deployment, nil, err
59
}
60
61
// Metrics resources
62
for _, metricsInst := range metricInstances.Items {
63
var (
64
serviceMonitors prom.ServiceMonitorList
65
podMonitors prom.PodMonitorList
66
probes prom.ProbeList
67
)
68
var children = []hierarchyResource{
69
{List: &serviceMonitors, Selector: metricsInst.ServiceMonitorSelector()},
70
{List: &podMonitors, Selector: metricsInst.PodMonitorSelector()},
71
{List: &probes, Selector: metricsInst.ProbeSelector()},
72
}
73
if err := search(children); err != nil {
74
return deployment, nil, err
75
}
76
77
deployment.Metrics = append(deployment.Metrics, gragent.MetricsDeployment{
78
Instance: metricsInst,
79
ServiceMonitors: filterServiceMonitors(l, root, &serviceMonitors).Items,
80
PodMonitors: podMonitors.Items,
81
Probes: probes.Items,
82
})
83
}
84
85
// Logs resources
86
for _, logsInst := range logsInstances.Items {
87
var (
88
podLogs gragent.PodLogsList
89
)
90
var children = []hierarchyResource{
91
{List: &podLogs, Selector: logsInst.PodLogsSelector()},
92
}
93
if err := search(children); err != nil {
94
return deployment, nil, err
95
}
96
97
deployment.Logs = append(deployment.Logs, gragent.LogsDeployment{
98
Instance: logsInst,
99
PodLogs: podLogs.Items,
100
})
101
}
102
103
// Integration resources
104
for _, integration := range integrations.Items {
105
deployment.Integrations = append(deployment.Integrations, gragent.IntegrationsDeployment{
106
Instance: integration,
107
})
108
}
109
110
// Finally, find all referenced secrets
111
secrets, secretWatchers, err := buildSecrets(ctx, cli, deployment)
112
if err != nil {
113
return deployment, nil, fmt.Errorf("failed to discover secrets: %w", err)
114
}
115
deployment.Secrets = secrets
116
watchers = append(watchers, secretWatchers...)
117
118
return deployment, watchers, nil
119
}
120
121
type hierarchyResource struct {
122
List client.ObjectList // List to populate
123
Selector gragent.ObjectSelector // Raw selector to use for list
124
}
125
126
func (hr *hierarchyResource) Find(ctx context.Context, cli client.Client) (hierarchy.Selector, error) {
127
sel, err := toSelector(hr.Selector)
128
if err != nil {
129
return nil, fmt.Errorf("failed to build selector: %w", err)
130
}
131
err = hierarchy.List(ctx, cli, hr.List, sel)
132
if err != nil {
133
return nil, fmt.Errorf("failed to list resources: %w", err)
134
}
135
return sel, nil
136
}
137
138
func toSelector(os gragent.ObjectSelector) (hierarchy.Selector, error) {
139
var res hierarchy.LabelsSelector
140
res.NamespaceName = os.ParentNamespace
141
142
if os.NamespaceSelector != nil {
143
sel, err := metav1.LabelSelectorAsSelector(os.NamespaceSelector)
144
if err != nil {
145
return nil, fmt.Errorf("invalid namespace selector: %w", err)
146
}
147
res.NamespaceLabels = sel
148
}
149
150
sel, err := metav1.LabelSelectorAsSelector(os.Labels)
151
if err != nil {
152
return nil, fmt.Errorf("invalid label selector: %w", err)
153
}
154
res.Labels = sel
155
return &res, nil
156
}
157
158
func filterServiceMonitors(l log.Logger, root *gragent.GrafanaAgent, list *prom.ServiceMonitorList) *prom.ServiceMonitorList {
159
items := make([]*prom.ServiceMonitor, 0, len(list.Items))
160
161
Item:
162
for _, item := range list.Items {
163
if root.Spec.Metrics.ArbitraryFSAccessThroughSMs.Deny {
164
for _, ep := range item.Spec.Endpoints {
165
err := testForArbitraryFSAccess(ep)
166
if err == nil {
167
continue
168
}
169
170
level.Warn(l).Log(
171
"msg", "skipping service monitor",
172
"agent", client.ObjectKeyFromObject(root),
173
"servicemonitor", client.ObjectKeyFromObject(item),
174
"err", err,
175
)
176
continue Item
177
}
178
}
179
items = append(items, item)
180
}
181
182
return &prom.ServiceMonitorList{
183
TypeMeta: list.TypeMeta,
184
ListMeta: *list.ListMeta.DeepCopy(),
185
Items: items,
186
}
187
}
188
189
func testForArbitraryFSAccess(e prom.Endpoint) error {
190
if e.BearerTokenFile != "" {
191
return fmt.Errorf("it accesses file system via bearer token file which is disallowed via GrafanaAgent specification")
192
}
193
194
if e.TLSConfig == nil {
195
return nil
196
}
197
198
if e.TLSConfig.CAFile != "" || e.TLSConfig.CertFile != "" || e.TLSConfig.KeyFile != "" {
199
return fmt.Errorf("it accesses file system via TLS config which is disallowed via GrafanaAgent specification")
200
}
201
202
return nil
203
}
204
205
func buildSecrets(ctx context.Context, cli client.Client, deploy gragent.Deployment) (secrets assets.SecretStore, watchers []hierarchy.Watcher, err error) {
206
secrets = make(assets.SecretStore)
207
208
// KeySelector caches to make sure we don't create duplicate watchers.
209
var (
210
usedSecretSelectors = map[hierarchy.KeySelector]struct{}{}
211
usedConfigMapSelectors = map[hierarchy.KeySelector]struct{}{}
212
)
213
214
for _, ref := range config.AssetReferences(deploy) {
215
var (
216
objectList client.ObjectList
217
sel hierarchy.KeySelector
218
)
219
220
switch {
221
case ref.Reference.Secret != nil:
222
objectList = &corev1.SecretList{}
223
sel = hierarchy.KeySelector{
224
Namespace: ref.Namespace,
225
Name: ref.Reference.Secret.Name,
226
}
227
case ref.Reference.ConfigMap != nil:
228
objectList = &corev1.ConfigMapList{}
229
sel = hierarchy.KeySelector{
230
Namespace: ref.Namespace,
231
Name: ref.Reference.ConfigMap.Name,
232
}
233
}
234
235
gvk, _ := apiutil.GVKForObject(objectList, cli.Scheme())
236
if err := hierarchy.List(ctx, cli, objectList, &sel); err != nil {
237
return nil, nil, fmt.Errorf("failed to find %q resource: %w", gvk.String(), err)
238
}
239
240
err := meta.EachListItem(objectList, func(o runtime.Object) error {
241
var value string
242
243
switch o := o.(type) {
244
case *corev1.Secret:
245
rawValue, ok := o.Data[ref.Reference.Secret.Key]
246
if !ok {
247
return fmt.Errorf("no key %s in Secret %s", ref.Reference.Secret.Key, o.Name)
248
}
249
value = string(rawValue)
250
case *corev1.ConfigMap:
251
var (
252
dataValue, dataFound = o.Data[ref.Reference.ConfigMap.Key]
253
binaryValue, binaryFound = o.BinaryData[ref.Reference.ConfigMap.Key]
254
)
255
256
if dataFound {
257
value = dataValue
258
} else if binaryFound {
259
value = string(binaryValue)
260
} else {
261
return fmt.Errorf("no key %s in ConfigMap %s", ref.Reference.ConfigMap.Key, o.Name)
262
}
263
}
264
265
secrets[assets.KeyForSelector(ref.Namespace, &ref.Reference)] = value
266
return nil
267
})
268
if err != nil {
269
return nil, nil, fmt.Errorf("failed to iterate over %q list: %w", gvk.String(), err)
270
}
271
272
switch {
273
case ref.Reference.Secret != nil:
274
if _, used := usedSecretSelectors[sel]; used {
275
continue
276
}
277
watchers = append(watchers, hierarchy.Watcher{
278
Object: &corev1.Secret{},
279
Owner: client.ObjectKeyFromObject(deploy.Agent),
280
Selector: &sel,
281
})
282
usedSecretSelectors[sel] = struct{}{}
283
case ref.Reference.ConfigMap != nil:
284
if _, used := usedConfigMapSelectors[sel]; used {
285
continue
286
}
287
watchers = append(watchers, hierarchy.Watcher{
288
Object: &corev1.ConfigMap{},
289
Owner: client.ObjectKeyFromObject(deploy.Agent),
290
Selector: &sel,
291
})
292
usedConfigMapSelectors[sel] = struct{}{}
293
}
294
}
295
296
return secrets, watchers, nil
297
}
298
299