-
Notifications
You must be signed in to change notification settings - Fork 24
/
nested-circuit.go
206 lines (178 loc) · 6.3 KB
/
nested-circuit.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package circuitfactory
import (
"encoding/json"
"fmt"
"github.com/mitchellh/mapstructure"
"go.uber.org/fx"
policylangv1 "github.com/fluxninja/aperture/v2/api/gen/proto/go/aperture/policy/language/v1"
"github.com/fluxninja/aperture/v2/pkg/config"
"github.com/fluxninja/aperture/v2/pkg/log"
"github.com/fluxninja/aperture/v2/pkg/policies/controlplane/components"
"github.com/fluxninja/aperture/v2/pkg/policies/controlplane/iface"
"github.com/fluxninja/aperture/v2/pkg/policies/controlplane/runtime"
"github.com/fluxninja/aperture/v2/pkg/utils"
)
// NewNestedCircuitAndOptions parses a nested circuit and returns the parent, leaf components, and options.
func NewNestedCircuitAndOptions(
nestedCircuit *policylangv1.NestedCircuit,
nestedCircuitID runtime.ComponentID,
policyReadAPI iface.Policy,
) (Tree, []*runtime.ConfiguredComponent, fx.Option, error) {
retErr := func(err error) (Tree, []*runtime.ConfiguredComponent, fx.Option, error) {
return Tree{}, nil, nil, err
}
nestedCircConfComp, err := runtime.NewConfiguredComponent(
runtime.NewDummyComponent("NestedCircuit",
"Number of Components: "+fmt.Sprint(len(nestedCircuit.GetComponents())),
runtime.ComponentTypeSignalProcessor),
nestedCircuit,
nestedCircuitID,
false,
)
if err != nil {
return retErr(err)
}
tree, leafComponents, options, err := ParseNestedCircuit(nestedCircConfComp, nestedCircuit, nestedCircuitID, policyReadAPI)
if err != nil {
return retErr(err)
}
return tree, leafComponents, options, err
}
// ParseNestedCircuit parses a nested circuit and returns the port mapping, children trees, leaf components, and options.
func ParseNestedCircuit(
configuredComponent *runtime.ConfiguredComponent,
nestedCircuit *policylangv1.NestedCircuit,
nestedCircuitID runtime.ComponentID,
policyReadAPI iface.Policy,
) (Tree, []*runtime.ConfiguredComponent, fx.Option, error) {
retErr := func(err error) (Tree, []*runtime.ConfiguredComponent, fx.Option, error) {
return Tree{}, nil, nil, err
}
// serialize to jsonBytes
jsonBytes, err := json.Marshal(nestedCircuit)
if err != nil {
return retErr(err)
}
// unmarshal using our config layer to make sure defaults and validates happen
nestedCircuitProto := &policylangv1.NestedCircuit{}
err = config.UnmarshalJSON(jsonBytes, nestedCircuitProto)
if err != nil {
err = fmt.Errorf("failed to unmarshal nested circuit: %w", err)
log.Error().Err(err).RawJSON("nestedCircuit", jsonBytes).Msg("")
return retErr(err)
}
portMapping := runtime.NewPortMapping()
parentCircuitID, ok := nestedCircuitID.ParentID()
if !ok {
return retErr(fmt.Errorf("nested circuit %s does not have a parent circuit", nestedCircuitID))
}
inPortsMap := nestedCircuitProto.GetInPortsMap()
ins, err := DecodePortMap(inPortsMap, parentCircuitID.String())
if err != nil {
return retErr(err)
}
outPortsMap := nestedCircuitProto.GetOutPortsMap()
outs, err := DecodePortMap(outPortsMap, parentCircuitID.String())
if err != nil {
return retErr(err)
}
portMapping.Ins = ins
portMapping.Outs = outs
childrenTrees, leafComponents, options, err := CreateComponents(
nestedCircuitProto.GetComponents(),
nestedCircuitID,
policyReadAPI,
)
if err != nil {
return retErr(err)
}
// For tracking the ingress/egress port names in the nested circuit
ingressPorts := make(map[string]interface{})
egressPorts := make(map[string]interface{})
// Set in and out ports at signal ingress and egress components based on the port mapping
for i, configuredComponent := range leafComponents {
component := configuredComponent.Component
parentComponentID, found := configuredComponent.ComponentID.ParentID()
if !found {
// not expected to happen
log.Fatal().Msgf("parent component id not found for component %s", configuredComponent.ComponentID)
}
if parentComponentID.String() != nestedCircuitID.String() {
// this will be handled by the child component, skip
continue
}
// dynamic cast to signal ingress or egress
if nestedSignalIngress, ok := component.(*components.NestedSignalIngress); ok {
portName := nestedSignalIngress.PortName()
// tracking the port names in the nested circuit
if _, ok := ingressPorts[portName]; ok {
return retErr(fmt.Errorf("duplicate ingress port %s in nested circuit", portName))
}
ingressPorts[portName] = nil
signals, ok := portMapping.GetInPort(portName)
if ok {
// set the port mapping for the signal ingress component
leafComponents[i].PortMapping.AddInPort(components.NestedSignalPortName, signals)
}
} else if nestedSignalEgress, ok := component.(*components.NestedSignalEgress); ok {
portName := nestedSignalEgress.PortName()
// tracking the port names in the nested circuit
if _, ok := egressPorts[portName]; ok {
return retErr(fmt.Errorf("duplicate egress port %s in nested circuit", portName))
}
egressPorts[portName] = nil
signals, ok := portMapping.GetOutPort(portName)
if ok {
// set the port mapping for the signal egress component
leafComponents[i].PortMapping.AddOutPort(components.NestedSignalPortName, signals)
}
}
}
for portName := range portMapping.Ins {
if _, ok := ingressPorts[portName]; !ok {
return retErr(fmt.Errorf("port %s not found in nested circuit", portName))
}
}
for portName := range portMapping.Outs {
if _, ok := egressPorts[portName]; !ok {
return retErr(fmt.Errorf("port %s not found in nested circuit", portName))
}
}
configuredComponent.PortMapping = portMapping
tree := Tree{
Children: childrenTrees,
Node: configuredComponent,
}
return tree, leafComponents, options, err
}
// DecodePortMap decodes a proto port map into a PortToSignals map.
func DecodePortMap(config any, circuitID string) (runtime.PortToSignals, error) {
ports := make(runtime.PortToSignals)
if config == nil {
return ports, nil
}
mapStruct, err := utils.ToMapStruct(config)
if err != nil {
return nil, err
}
if len(mapStruct) == 0 {
return ports, nil
}
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
WeaklyTypedInput: true, // So that singular ports will transparently be converted to lists.
Result: &ports,
})
if err != nil {
return nil, err
}
err = decoder.Decode(mapStruct)
if err != nil {
return nil, err
}
for _, signals := range ports {
for i := range signals {
signals[i].SubCircuitID = circuitID
}
}
return ports, nil
}