Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/river/vm/struct_decoder.go
4096 views
1
package vm
2
3
import (
4
"fmt"
5
"reflect"
6
"strings"
7
8
"github.com/grafana/agent/pkg/river/ast"
9
"github.com/grafana/agent/pkg/river/diag"
10
"github.com/grafana/agent/pkg/river/internal/reflectutil"
11
"github.com/grafana/agent/pkg/river/internal/rivertags"
12
"github.com/grafana/agent/pkg/river/internal/value"
13
)
14
15
// structDecoder decodes a series of AST statements into a Go value.
16
type structDecoder struct {
17
VM *Evaluator
18
Scope *Scope
19
Assoc map[value.Value]ast.Node
20
TagInfo *tagInfo
21
}
22
23
// Decode decodes the list of statements into the struct value specified by rv.
24
func (st *structDecoder) Decode(stmts ast.Body, rv reflect.Value) error {
25
// TODO(rfratto): potentially loosen this restriction and allow decoding into
26
// an interface{} or map[string]interface{}.
27
if rv.Kind() != reflect.Struct {
28
panic(fmt.Sprintf("river/vm: structDecoder expects struct, got %s", rv.Kind()))
29
}
30
31
state := decodeOptions{
32
Tags: st.TagInfo.TagLookup,
33
EnumBlocks: st.TagInfo.EnumLookup,
34
SeenAttrs: make(map[string]struct{}),
35
SeenBlocks: make(map[string]struct{}),
36
SeenEnums: make(map[string]struct{}),
37
38
BlockCount: make(map[string]int),
39
BlockIndex: make(map[*ast.BlockStmt]int),
40
41
EnumCount: make(map[string]int),
42
EnumIndex: make(map[*ast.BlockStmt]int),
43
}
44
45
// Iterate over the set of blocks to populate block count and block index.
46
// Block index is its index in the set of blocks with the *same name*.
47
//
48
// If the block belongs to an enum, we populate enum count and enum index
49
// instead. The enum index is the index on the set of blocks for the *same
50
// enum*.
51
for _, stmt := range stmts {
52
switch stmt := stmt.(type) {
53
case *ast.BlockStmt:
54
fullName := strings.Join(stmt.Name, ".")
55
56
if enumTf, isEnum := st.TagInfo.EnumLookup[fullName]; isEnum {
57
enumName := strings.Join(enumTf.EnumField.Name, ".")
58
state.EnumIndex[stmt] = state.EnumCount[enumName]
59
state.EnumCount[enumName]++
60
} else {
61
state.BlockIndex[stmt] = state.BlockCount[fullName]
62
state.BlockCount[fullName]++
63
}
64
}
65
}
66
67
for _, stmt := range stmts {
68
switch stmt := stmt.(type) {
69
case *ast.AttributeStmt:
70
// TODO(rfratto): append to list of diagnostics instead of aborting early.
71
if err := st.decodeAttr(stmt, rv, &state); err != nil {
72
return err
73
}
74
75
case *ast.BlockStmt:
76
// TODO(rfratto): append to list of diagnostics instead of aborting early.
77
if err := st.decodeBlock(stmt, rv, &state); err != nil {
78
return err
79
}
80
81
default:
82
panic(fmt.Sprintf("river/vm: unrecognized node type %T", stmt))
83
}
84
}
85
86
for _, tf := range st.TagInfo.Tags {
87
// Ignore any optional tags.
88
if tf.IsOptional() {
89
continue
90
}
91
92
fullName := strings.Join(tf.Name, ".")
93
94
switch {
95
case tf.IsAttr():
96
if _, consumed := state.SeenAttrs[fullName]; !consumed {
97
// TODO(rfratto): change to diagnostics.
98
return fmt.Errorf("missing required attribute %q", fullName)
99
}
100
101
case tf.IsBlock():
102
if _, consumed := state.SeenBlocks[fullName]; !consumed {
103
// TODO(rfratto): change to diagnostics.
104
return fmt.Errorf("missing required block %q", fullName)
105
}
106
}
107
}
108
109
return nil
110
}
111
112
type decodeOptions struct {
113
Tags map[string]rivertags.Field
114
EnumBlocks map[string]enumBlock
115
116
SeenAttrs, SeenBlocks, SeenEnums map[string]struct{}
117
118
// BlockCount and BlockIndex are used to determine:
119
//
120
// * How big a slice of blocks should be for a block of a given name (BlockCount)
121
// * Which element within that slice is a given block assigned to (BlockIndex)
122
//
123
// This is used for decoding a series of rule blocks for prometheus.relabel,
124
// where 5 rules would have a "rule" key in BlockCount with a value of 5, and
125
// where the first block would be index 0, the second block would be index 1,
126
// and so on.
127
//
128
// The index in BlockIndex is relative to a block name; the first block named
129
// "hello.world" and the first block named "fizz.buzz" both have index 0.
130
131
BlockCount map[string]int // Number of times a block by full name is seen.
132
BlockIndex map[*ast.BlockStmt]int // Index of a block within a set of blocks with the same name.
133
134
// EnumCount and EnumIndex are similar to BlockCount/BlockIndex, but instead
135
// reference the number of blocks assigned to the same enum (EnumCount) and
136
// the index of a block within that enum slice (EnumIndex).
137
138
EnumCount map[string]int // Number of times an enum group is seen by enum name.
139
EnumIndex map[*ast.BlockStmt]int // Index of a block within a set of enum blocks of the same enum.
140
}
141
142
func (st *structDecoder) decodeAttr(attr *ast.AttributeStmt, rv reflect.Value, state *decodeOptions) error {
143
fullName := attr.Name.Name
144
if _, seen := state.SeenAttrs[fullName]; seen {
145
return diag.Diagnostics{{
146
Severity: diag.SeverityLevelError,
147
StartPos: ast.StartPos(attr).Position(),
148
EndPos: ast.EndPos(attr).Position(),
149
Message: fmt.Sprintf("attribute %q may only be provided once", fullName),
150
}}
151
}
152
state.SeenAttrs[fullName] = struct{}{}
153
154
tf, ok := state.Tags[fullName]
155
if !ok {
156
return diag.Diagnostics{{
157
Severity: diag.SeverityLevelError,
158
StartPos: ast.StartPos(attr).Position(),
159
EndPos: ast.EndPos(attr).Position(),
160
Message: fmt.Sprintf("unrecognized attribute name %q", fullName),
161
}}
162
} else if tf.IsBlock() {
163
return diag.Diagnostics{{
164
Severity: diag.SeverityLevelError,
165
StartPos: ast.StartPos(attr).Position(),
166
EndPos: ast.EndPos(attr).Position(),
167
Message: fmt.Sprintf("%q must be a block, but is used as an attribute", fullName),
168
}}
169
}
170
171
// Decode the attribute.
172
val, err := st.VM.evaluateExpr(st.Scope, st.Assoc, attr.Value)
173
if err != nil {
174
// TODO(rfratto): get error as diagnostics.
175
return err
176
}
177
178
// We're reconverting our reflect.Value back into an interface{}, so we
179
// need to also turn it back into a pointer for decoding.
180
field := reflectutil.GetOrAlloc(rv, tf)
181
if err := value.Decode(val, field.Addr().Interface()); err != nil {
182
// TODO(rfratto): get error as diagnostics.
183
return err
184
}
185
186
return nil
187
}
188
189
func (st *structDecoder) decodeBlock(block *ast.BlockStmt, rv reflect.Value, state *decodeOptions) error {
190
fullName := block.GetBlockName()
191
192
if _, isEnum := state.EnumBlocks[fullName]; isEnum {
193
return st.decodeEnumBlock(fullName, block, rv, state)
194
}
195
return st.decodeNormalBlock(fullName, block, rv, state)
196
}
197
198
// decodeNormalBlock decodes a standard (non-enum) block.
199
func (st *structDecoder) decodeNormalBlock(fullName string, block *ast.BlockStmt, rv reflect.Value, state *decodeOptions) error {
200
tf, isBlock := state.Tags[fullName]
201
if !isBlock {
202
return diag.Diagnostics{{
203
Severity: diag.SeverityLevelError,
204
StartPos: ast.StartPos(block).Position(),
205
EndPos: ast.EndPos(block).Position(),
206
Message: fmt.Sprintf("unrecognized block name %q", fullName),
207
}}
208
} else if tf.IsAttr() {
209
return diag.Diagnostics{{
210
Severity: diag.SeverityLevelError,
211
StartPos: ast.StartPos(block).Position(),
212
EndPos: ast.EndPos(block).Position(),
213
Message: fmt.Sprintf("%q must be an attribute, but is used as a block", fullName),
214
}}
215
}
216
217
field := reflectutil.GetOrAlloc(rv, tf)
218
decodeField := prepareDecodeValue(field)
219
220
switch decodeField.Kind() {
221
case reflect.Slice:
222
// If this is the first time we've seen the block, reset its length to
223
// zero.
224
if _, seen := state.SeenBlocks[fullName]; !seen {
225
count := state.BlockCount[fullName]
226
decodeField.Set(reflect.MakeSlice(decodeField.Type(), count, count))
227
}
228
229
blockIndex, ok := state.BlockIndex[block]
230
if !ok {
231
panic("river/vm: block not found in index lookup table")
232
}
233
decodeElement := prepareDecodeValue(decodeField.Index(blockIndex))
234
err := st.VM.evaluateBlockOrBody(st.Scope, st.Assoc, block, decodeElement)
235
if err != nil {
236
// TODO(rfratto): get error as diagnostics.
237
return err
238
}
239
240
case reflect.Array:
241
if decodeField.Len() != state.BlockCount[fullName] {
242
return diag.Diagnostics{{
243
Severity: diag.SeverityLevelError,
244
StartPos: ast.StartPos(block).Position(),
245
EndPos: ast.EndPos(block).Position(),
246
Message: fmt.Sprintf(
247
"block %q must be specified exactly %d times, but was specified %d times",
248
fullName,
249
decodeField.Len(),
250
state.BlockCount[fullName],
251
),
252
}}
253
}
254
255
blockIndex, ok := state.BlockIndex[block]
256
if !ok {
257
panic("river/vm: block not found in index lookup table")
258
}
259
decodeElement := prepareDecodeValue(decodeField.Index(blockIndex))
260
err := st.VM.evaluateBlockOrBody(st.Scope, st.Assoc, block, decodeElement)
261
if err != nil {
262
// TODO(rfratto): get error as diagnostics.
263
return err
264
}
265
266
default:
267
if state.BlockCount[fullName] > 1 {
268
return diag.Diagnostics{{
269
Severity: diag.SeverityLevelError,
270
StartPos: ast.StartPos(block).Position(),
271
EndPos: ast.EndPos(block).Position(),
272
Message: fmt.Sprintf("block %q may only be specified once", fullName),
273
}}
274
}
275
276
err := st.VM.evaluateBlockOrBody(st.Scope, st.Assoc, block, decodeField)
277
if err != nil {
278
// TODO(rfratto): get error as diagnostics.
279
return err
280
}
281
}
282
283
state.SeenBlocks[fullName] = struct{}{}
284
return nil
285
}
286
287
func (st *structDecoder) decodeEnumBlock(fullName string, block *ast.BlockStmt, rv reflect.Value, state *decodeOptions) error {
288
tf, ok := state.EnumBlocks[fullName]
289
if !ok {
290
// decodeEnumBlock should only ever be called from decodeBlock, so this
291
// should never happen.
292
panic("decodeEnumBlock called with a non-enum block")
293
}
294
295
enumName := strings.Join(tf.EnumField.Name, ".")
296
enumField := reflectutil.GetOrAlloc(rv, tf.EnumField)
297
decodeField := prepareDecodeValue(enumField)
298
299
if decodeField.Kind() != reflect.Slice {
300
panic("river/vm: enum field must be a slice kind, got " + decodeField.Kind().String())
301
}
302
303
// If this is the first time we've seen the enum, reset its length to zero.
304
if _, seen := state.SeenEnums[enumName]; !seen {
305
count := state.EnumCount[enumName]
306
decodeField.Set(reflect.MakeSlice(decodeField.Type(), count, count))
307
}
308
state.SeenEnums[enumName] = struct{}{}
309
310
// Prepare the enum element to decode into.
311
enumIndex, ok := state.EnumIndex[block]
312
if !ok {
313
panic("river/vm: enum block not found in index lookup table")
314
}
315
enumElement := prepareDecodeValue(decodeField.Index(enumIndex))
316
317
// Prepare the block field to decode into.
318
enumBlock := reflectutil.GetOrAlloc(enumElement, tf.BlockField)
319
decodeBlock := prepareDecodeValue(enumBlock)
320
321
// Decode into the block field.
322
return st.VM.evaluateBlockOrBody(st.Scope, st.Assoc, block, decodeBlock)
323
}
324
325