Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/operator/config/config.go
4096 views
1
// Package config generates Grafana Agent configuration based on Kubernetes
2
// resources.
3
package config
4
5
import (
6
"embed"
7
"encoding/json"
8
"fmt"
9
"io/fs"
10
"path"
11
12
"github.com/fatih/structs"
13
jsonnet "github.com/google/go-jsonnet"
14
"github.com/google/go-jsonnet/ast"
15
gragent "github.com/grafana/agent/pkg/operator/apis/monitoring/v1alpha1"
16
"github.com/grafana/agent/pkg/operator/assets"
17
"gopkg.in/yaml.v3"
18
)
19
20
// Type is the type of Agent deployment that a config is being generated
21
// for.
22
type Type int
23
24
const (
25
// MetricsType generates a configuration for metrics.
26
MetricsType Type = iota + 1
27
// LogsType generates a configuration for logs.
28
LogsType
29
// IntegrationsType generates a configuration for integrations.
30
IntegrationsType
31
)
32
33
// String returns the string form of Type.
34
func (t Type) String() string {
35
switch t {
36
case MetricsType:
37
return "metrics"
38
case LogsType:
39
return "logs"
40
case IntegrationsType:
41
return "integrations"
42
default:
43
return fmt.Sprintf("unknown (%d)", int(t))
44
}
45
}
46
47
//go:embed templates/*
48
var templates embed.FS
49
50
// TODO(rfratto): the "Optional" field of secrets is currently ignored.
51
52
// BuildConfig builds an Agent configuration file.
53
func BuildConfig(d *gragent.Deployment, ty Type) (string, error) {
54
vm, err := createVM(d.Secrets)
55
if err != nil {
56
return "", err
57
}
58
59
bb, err := jsonnetMarshal(d)
60
if err != nil {
61
return "", err
62
}
63
64
vm.TLACode("ctx", string(bb))
65
66
switch ty {
67
case MetricsType:
68
return vm.EvaluateFile("./agent-metrics.libsonnet")
69
case LogsType:
70
return vm.EvaluateFile("./agent-logs.libsonnet")
71
case IntegrationsType:
72
return vm.EvaluateFile("./agent-integrations.libsonnet")
73
default:
74
panic(fmt.Sprintf("unexpected config type %v", ty))
75
}
76
}
77
78
func createVM(secrets assets.SecretStore) (*jsonnet.VM, error) {
79
vm := jsonnet.MakeVM()
80
vm.StringOutput = true
81
82
templatesContents, err := fs.Sub(templates, "templates")
83
if err != nil {
84
return nil, err
85
}
86
87
vm.Importer(NewFSImporter(templatesContents, []string{"./"}))
88
89
vm.NativeFunction(&jsonnet.NativeFunction{
90
Name: "marshalYAML",
91
Params: ast.Identifiers{"object"},
92
Func: func(i []interface{}) (interface{}, error) {
93
bb, err := yaml.Marshal(i[0])
94
if err != nil {
95
return nil, jsonnet.RuntimeError{Msg: err.Error()}
96
}
97
return string(bb), nil
98
},
99
})
100
vm.NativeFunction(&jsonnet.NativeFunction{
101
Name: "unmarshalYAML",
102
Params: ast.Identifiers{"text"},
103
Func: unmarshalYAML,
104
})
105
vm.NativeFunction(&jsonnet.NativeFunction{
106
Name: "intoStages",
107
Params: ast.Identifiers{"text"},
108
Func: intoStages,
109
})
110
111
vm.NativeFunction(&jsonnet.NativeFunction{
112
Name: "trimOptional",
113
Params: ast.Identifiers{"value"},
114
Func: func(i []interface{}) (interface{}, error) {
115
m := i[0].(map[string]interface{})
116
trimMap(m)
117
return m, nil
118
},
119
})
120
vm.NativeFunction(&jsonnet.NativeFunction{
121
Name: "secretLookup",
122
Params: ast.Identifiers{"key"},
123
Func: func(i []interface{}) (interface{}, error) {
124
if i[0] == nil {
125
return nil, nil
126
}
127
128
k := assets.Key(i[0].(string))
129
val, ok := secrets[k]
130
if !ok {
131
return nil, jsonnet.RuntimeError{Msg: fmt.Sprintf("key not provided: %s", k)}
132
}
133
return val, nil
134
},
135
})
136
vm.NativeFunction(&jsonnet.NativeFunction{
137
Name: "secretPath",
138
Params: ast.Identifiers{"key"},
139
Func: func(i []interface{}) (interface{}, error) {
140
if i[0] == nil {
141
return nil, nil
142
}
143
144
key := SanitizeLabelName(i[0].(string))
145
return path.Join("/var/lib/grafana-agent/secrets", key), nil
146
},
147
})
148
149
vm.NativeFunction(&jsonnet.NativeFunction{
150
Name: "sanitize",
151
Params: ast.Identifiers{"text"},
152
Func: func(i []interface{}) (interface{}, error) {
153
if len(i) != 1 {
154
return nil, jsonnet.RuntimeError{Msg: "inappropriate number of arguments"}
155
}
156
s, ok := i[0].(string)
157
if !ok {
158
return nil, jsonnet.RuntimeError{Msg: "text must be a string"}
159
}
160
return SanitizeLabelName(s), nil
161
},
162
})
163
164
return vm, nil
165
}
166
167
// jsonnetMarshal marshals a value for passing to Jsonnet. This marshals to a
168
// JSON representation of the Go value, ignoring all json struct tags. Fields
169
// must be access as they would from Go, with the exception of embedded fields,
170
// which must be accessed through the embedded type name (a.Embedded.Field).
171
func jsonnetMarshal(v interface{}) ([]byte, error) {
172
if structs.IsStruct(v) {
173
return json.Marshal(structs.Map(v))
174
}
175
return json.Marshal(v)
176
}
177
178