Skip to content

Commit

Permalink
support using modules inside of foreach
Browse files Browse the repository at this point in the history
  • Loading branch information
wildum committed Jan 8, 2025
1 parent e4bd6a3 commit b041704
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 18 deletions.
20 changes: 15 additions & 5 deletions internal/runtime/internal/controller/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ func (l *Loader) populateConfigBlockNodes(args map[string]any, g *dag.Graph, con
node = exist.(BlockNode)
node.UpdateBlock(block)
} else {
node, newConfigNodeDiags = NewConfigNode(block, l.globals)
node, newConfigNodeDiags = NewConfigNode(block, l.globals, l.componentNodeManager.customComponentReg)
diags = append(diags, newConfigNodeDiags...)
if diags.HasErrors() {
continue
Expand Down Expand Up @@ -625,6 +625,8 @@ func (l *Loader) wireGraphEdges(g *dag.Graph) diag.Diagnostics {
continue
case *CustomComponentNode:
l.wireCustomComponentNode(g, n)
case *ForeachConfigNode:
l.wireForEachNode(g, n)
}

// Finally, wire component references.
Expand Down Expand Up @@ -656,6 +658,14 @@ func (l *Loader) wireCustomComponentNode(g *dag.Graph, cc *CustomComponentNode)
}
}

func (l *Loader) wireForEachNode(g *dag.Graph, fn *ForeachConfigNode) {
refs := l.findCustomComponentReferences(fn.Block())
for ref := range refs {
// add edges between the foreach node and declare/import nodes.
g.AddEdge(dag.Edge{From: fn, To: ref})
}
}

// Variables returns the Variables the Loader exposes for other components to
// reference.
func (l *Loader) Variables() map[string]interface{} {
Expand Down Expand Up @@ -915,10 +925,10 @@ func (l *Loader) isRootController() bool {
return l.globals.ControllerID == ""
}

// findCustomComponentReferences returns references to import/declare nodes in a declare block.
func (l *Loader) findCustomComponentReferences(declare *ast.BlockStmt) map[BlockNode]struct{} {
// findCustomComponentReferences returns references to import/declare nodes in a block.
func (l *Loader) findCustomComponentReferences(block *ast.BlockStmt) map[BlockNode]struct{} {
uniqueReferences := make(map[BlockNode]struct{})
l.collectCustomComponentReferences(declare.Body, uniqueReferences)
l.collectCustomComponentReferences(block.Body, uniqueReferences)
return uniqueReferences
}

Expand All @@ -938,7 +948,7 @@ func (l *Loader) collectCustomComponentReferences(stmts ast.Body, uniqueReferenc
)

switch {
case componentName == declareType:
case componentName == declareType || componentName == templateType:
l.collectCustomComponentReferences(blockStmt.Body, uniqueReferences)
case foundDeclare:
uniqueReferences[declareNode] = struct{}{}
Expand Down
4 changes: 2 additions & 2 deletions internal/runtime/internal/controller/node_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const (

// NewConfigNode creates a new ConfigNode from an initial ast.BlockStmt.
// The underlying config isn't applied until Evaluate is called.
func NewConfigNode(block *ast.BlockStmt, globals ComponentGlobals) (BlockNode, diag.Diagnostics) {
func NewConfigNode(block *ast.BlockStmt, globals ComponentGlobals, customReg *CustomComponentRegistry) (BlockNode, diag.Diagnostics) {
switch block.GetBlockName() {
case argumentBlockID:
return NewArgumentConfigNode(block, globals), nil
Expand All @@ -31,7 +31,7 @@ func NewConfigNode(block *ast.BlockStmt, globals ComponentGlobals) (BlockNode, d
case importsource.BlockImportFile, importsource.BlockImportString, importsource.BlockImportHTTP, importsource.BlockImportGit:
return NewImportConfigNode(block, globals, importsource.GetSourceType(block.GetBlockName())), nil
case foreachID:
return NewForeachConfigNode(block, globals), nil
return NewForeachConfigNode(block, globals, customReg), nil
default:
var diags diag.Diagnostics
diags.Add(diag.Diagnostic{
Expand Down
20 changes: 15 additions & 5 deletions internal/runtime/internal/controller/node_config_foreach.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ import (
"github.com/grafana/alloy/syntax/vm"
)

const templateType = "template"

type ForeachConfigNode struct {
nodeID string
label string
moduleController ModuleController

// customReg is the customComponentRegistry of the current loader.
// We pass it so that the foreach children have access to modules.
customReg *CustomComponentRegistry

customComponents map[string]CustomComponent
customComponentHashCounts map[string]int

Expand All @@ -39,7 +45,7 @@ type ForeachArguments struct {
Collection []string `alloy:"collection,attr"`
}

func NewForeachConfigNode(block *ast.BlockStmt, globals ComponentGlobals) *ForeachConfigNode {
func NewForeachConfigNode(block *ast.BlockStmt, globals ComponentGlobals, customReg *CustomComponentRegistry) *ForeachConfigNode {
nodeID := BlockComponentID(block).String()
globalID := nodeID
if globals.ControllerID != "" {
Expand All @@ -51,6 +57,7 @@ func NewForeachConfigNode(block *ast.BlockStmt, globals ComponentGlobals) *Forea
label: block.Label,
block: block,
moduleController: globals.NewModuleController(globalID),
customReg: customReg,
forEachChildrenUpdateChan: make(chan struct{}, 1),
customComponents: make(map[string]CustomComponent, 0),
customComponentHashCounts: make(map[string]int, 0),
Expand All @@ -61,7 +68,11 @@ func (fn *ForeachConfigNode) Label() string { return fn.label }

func (fn *ForeachConfigNode) NodeID() string { return fn.nodeID }

func (fn *ForeachConfigNode) Block() *ast.BlockStmt { return fn.block }
func (fn *ForeachConfigNode) Block() *ast.BlockStmt {
fn.mut.RLock()
defer fn.mut.RUnlock()
return fn.block
}

type Arguments struct {
Collection []any `alloy:"collection,attr"`
Expand All @@ -75,7 +86,7 @@ func (fn *ForeachConfigNode) Evaluate(scope *vm.Scope) error {
var argsBody ast.Body
var template *ast.BlockStmt
for _, stmt := range fn.block.Body {
if blockStmt, ok := stmt.(*ast.BlockStmt); ok && blockStmt.GetBlockName() == "template" {
if blockStmt, ok := stmt.(*ast.BlockStmt); ok && blockStmt.GetBlockName() == templateType {
template = blockStmt
continue // we don't add the template to the argsBody
}
Expand Down Expand Up @@ -115,8 +126,7 @@ func (fn *ForeachConfigNode) Evaluate(scope *vm.Scope) error {
vars := deepCopyMap(scope.Variables)
vars[args.Var] = args.Collection[i]

// TODO: use the registry from the loader to access the modules
customComponentRegistry := NewCustomComponentRegistry(nil, vm.NewScope(vars))
customComponentRegistry := NewCustomComponentRegistry(fn.customReg, vm.NewScope(vars))
if err := cc.LoadBody(template.Body, map[string]any{}, customComponentRegistry); err != nil {
return fmt.Errorf("updating custom component in foreach: %w", err)
}
Expand Down
12 changes: 6 additions & 6 deletions internal/runtime/internal/controller/node_config_foreach_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestCreateCustomComponents(t *testing.T) {
template {
}
}`
foreachConfigNode := NewForeachConfigNode(getBlockFromConfig(t, config), getComponentGlobals(t))
foreachConfigNode := NewForeachConfigNode(getBlockFromConfig(t, config), getComponentGlobals(t), nil)
require.NoError(t, foreachConfigNode.Evaluate(vm.NewScope(make(map[string]interface{}))))
customComponentIds := foreachConfigNode.moduleController.(*ModuleControllerMock).CustomComponents
require.ElementsMatch(t, customComponentIds, []string{"foreach_1_1", "foreach_2_1", "foreach_3_1"})
Expand All @@ -44,7 +44,7 @@ func TestCreateCustomComponentsDuplicatedIds(t *testing.T) {
template {
}
}`
foreachConfigNode := NewForeachConfigNode(getBlockFromConfig(t, config), getComponentGlobals(t))
foreachConfigNode := NewForeachConfigNode(getBlockFromConfig(t, config), getComponentGlobals(t), nil)
require.NoError(t, foreachConfigNode.Evaluate(vm.NewScope(make(map[string]interface{}))))
customComponentIds := foreachConfigNode.moduleController.(*ModuleControllerMock).CustomComponents
require.ElementsMatch(t, customComponentIds, []string{"foreach_1_1", "foreach_2_1", "foreach_1_2"})
Expand All @@ -62,7 +62,7 @@ func TestCreateCustomComponentsWithUpdate(t *testing.T) {
template {
}
}`
foreachConfigNode := NewForeachConfigNode(getBlockFromConfig(t, config), getComponentGlobals(t))
foreachConfigNode := NewForeachConfigNode(getBlockFromConfig(t, config), getComponentGlobals(t), nil)
require.NoError(t, foreachConfigNode.Evaluate(vm.NewScope(make(map[string]interface{}))))
customComponentIds := foreachConfigNode.moduleController.(*ModuleControllerMock).CustomComponents
require.ElementsMatch(t, customComponentIds, []string{"foreach_1_1", "foreach_2_1", "foreach_3_1"})
Expand Down Expand Up @@ -101,7 +101,7 @@ func TestRunCustomComponents(t *testing.T) {
template {
}
}`
foreachConfigNode := NewForeachConfigNode(getBlockFromConfig(t, config), getComponentGlobals(t))
foreachConfigNode := NewForeachConfigNode(getBlockFromConfig(t, config), getComponentGlobals(t), nil)
require.NoError(t, foreachConfigNode.Evaluate(vm.NewScope(make(map[string]interface{}))))
ctx, cancel := context.WithCancel(context.Background())
go foreachConfigNode.Run(ctx)
Expand Down Expand Up @@ -129,7 +129,7 @@ func TestRunCustomComponentsAfterUpdate(t *testing.T) {
template {
}
}`
foreachConfigNode := NewForeachConfigNode(getBlockFromConfig(t, config), getComponentGlobals(t))
foreachConfigNode := NewForeachConfigNode(getBlockFromConfig(t, config), getComponentGlobals(t), nil)
require.NoError(t, foreachConfigNode.Evaluate(vm.NewScope(make(map[string]interface{}))))
ctx, cancel := context.WithCancel(context.Background())
go foreachConfigNode.Run(ctx)
Expand Down Expand Up @@ -176,7 +176,7 @@ func TestCreateCustomComponentsCollectionObjectsWithUpdate(t *testing.T) {
template {
}
}`
foreachConfigNode := NewForeachConfigNode(getBlockFromConfig(t, config), getComponentGlobals(t))
foreachConfigNode := NewForeachConfigNode(getBlockFromConfig(t, config), getComponentGlobals(t), nil)
vars := map[string]interface{}{
"obj1": map[string]string{
"label1": "a",
Expand Down
32 changes: 32 additions & 0 deletions internal/runtime/testdata/foreach/foreach_7.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Module used inside of a foreach.

-- main.alloy --
import.file "testImport" {
filename = "module.alloy"
}

foreach "testForeach" {
collection = [5, 5]
var = "num"

template {
testImport.a "cc" {
max = num
receiver = testcomponents.summation_receiver.sum.receiver
}
}
}

testcomponents.summation_receiver "sum" {
}

-- module.alloy --
declare "a" {
argument "max" {}
argument "receiver" {}
testcomponents.pulse "pt" {
max = argument.max.value
frequency = "10ms"
forward_to = [argument.receiver.value]
}
}

0 comments on commit b041704

Please sign in to comment.