1 package nodefirewall
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "reflect"
8
9 ctrl "sigs.k8s.io/controller-runtime"
10 "sigs.k8s.io/controller-runtime/pkg/client"
11 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
12
13 "edge-infra.dev/pkg/k8s/meta/status"
14 "edge-infra.dev/pkg/k8s/runtime/conditions"
15 "edge-infra.dev/pkg/k8s/runtime/controller/reconcile"
16 "edge-infra.dev/pkg/k8s/runtime/patch"
17 v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
18 "edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/config"
19 )
20
21 type Plugin struct {
22 config config.Config
23 name string
24 owner string
25 conditions reconcile.Conditions
26 defaultInterface string
27 }
28
29 var nodeFirewallConditions = reconcile.Conditions{
30 Target: status.ReadyCondition,
31 Owned: []string{
32 string(v1ien.NodeFirewallController),
33 },
34 Summarize: []string{
35 string(v1ien.NodeFirewallController),
36 },
37 NegativePolarity: []string{},
38 }
39
40 func (fw Plugin) Reconcile(ctx context.Context, object client.Object, cfg config.Config) (recErr error) {
41 log := ctrl.LoggerFrom(ctx)
42 ctx = ctrl.LoggerInto(ctx, log)
43
44 if reflect.DeepEqual(fw.conditions, reconcile.Conditions{}) {
45 fw.conditions = nodeFirewallConditions
46 }
47
48 nodefirewall, ok := object.(*v1ien.NodeFirewall)
49 if !ok {
50 return nil
51 }
52
53 fw.config = cfg
54 fw.name = nodefirewall.Name
55 if len(nodefirewall.OwnerReferences) != 1 {
56 return fmt.Errorf("invalid NodeFirewall - invalid OwnerReferences: %+v", nodefirewall.OwnerReferences)
57 }
58 fw.owner = nodefirewall.OwnerReferences[0].Name
59
60 result := reconcile.ResultEmpty
61
62 patcher := patch.NewSerialPatcher(nodefirewall, fw.config.GetClient())
63 defer func() {
64 recErr = fw.summarizer(ctx, patcher, nodefirewall, result, recErr)
65 }()
66
67 conditions.MarkFalse(nodefirewall, status.ReadyCondition, string(v1ien.NfwProgressing), "%s", v1ien.Reconciling.String())
68
69
70 if !controllerutil.ContainsFinalizer(nodefirewall, v1ien.NodeFirewallFinalizer) {
71 controllerutil.AddFinalizer(nodefirewall, v1ien.NodeFirewallFinalizer)
72 }
73
74
75 if !nodefirewall.ObjectMeta.DeletionTimestamp.IsZero() {
76 recErr = fw.removeFiles(ctx, nodefirewall)
77 return
78 }
79
80 if recErr = fw.updateFiles(ctx, nodefirewall); recErr != nil {
81 return
82 }
83
84 conditions.MarkTrue(nodefirewall, status.ReadyCondition, string(v1ien.NfwSuccessful), "%s", v1ien.Succeeded.String())
85 result = reconcile.ResultSuccess
86 return
87 }
88
89 func (fw Plugin) updateFiles(ctx context.Context, nodefirewall *v1ien.NodeFirewall) error {
90 log := ctrl.LoggerFrom(ctx)
91
92
93 if valid, reason := fw.validateNodeFirewall(nodefirewall); !valid {
94 err := errors.New("validation failed")
95 log.Error(err, fmt.Sprintf(v1ien.Invalid, reason))
96 conditions.MarkTrue(nodefirewall, status.ReadyCondition, string(v1ien.CfwFailed), v1ien.Invalid, reason)
97 return err
98 }
99
100 if err := fw.setDefaultInterface(ctx); err != nil {
101 log.Error(err, v1ien.InterfaceRequeueing)
102 conditions.MarkFalse(nodefirewall, status.ReadyCondition, string(v1ien.CfwFailed), "%s", v1ien.InterfaceRequeueing)
103 return err
104 }
105
106
107 files, err := fw.writeFiles(nodefirewall.Spec.Rules)
108 if err != nil {
109
110 _ = fw.deleteFiles(files)
111 log.Error(err, v1ien.WriteFilesRequeueing)
112 conditions.MarkFalse(nodefirewall, status.ReadyCondition, string(v1ien.CfwFailed), "%s", v1ien.WriteFilesRequeueing)
113 return err
114 }
115
116
117 if err := fw.deleteStaleFiles(fw.getFileInventory(nodefirewall), files); err != nil {
118 log.Error(err, v1ien.RemoveFilesRequeueing)
119 conditions.MarkFalse(nodefirewall, status.ReadyCondition, string(v1ien.CfwFailed), "%s", v1ien.RemoveFilesRequeueing)
120 return err
121 }
122
123
124 fw.setFileInventory(nodefirewall, files)
125
126 return nil
127 }
128
129 func (fw Plugin) validateNodeFirewall(nodefirewall *v1ien.NodeFirewall) (bool, string) {
130 if valid, reason := nodefirewall.ValidateRules(); !valid {
131 return valid, reason
132 }
133
134 for _, rule := range nodefirewall.Spec.Rules {
135 if rule.InterfaceMAC == "" {
136 continue
137 }
138 if _, err := fw.config.GetInterfaceFromHardwareAddress(rule.InterfaceMAC); err != nil {
139 return false, fmt.Sprintf("couldn't get interface from provided hardware address %s: %v", rule.InterfaceMAC, err)
140 }
141 }
142
143 return true, ""
144 }
145
146 func (fw Plugin) removeFiles(ctx context.Context, nodefirewall *v1ien.NodeFirewall) error {
147
148 if err := fw.deleteFiles(fw.getFileInventory(nodefirewall)); err != nil {
149 ctrl.LoggerFrom(ctx).Error(err, v1ien.RemoveFilesRequeueing)
150 conditions.MarkFalse(nodefirewall, status.ReadyCondition, string(v1ien.NfwFailed), "%s", v1ien.RemoveFilesRequeueing)
151 return err
152 }
153
154 conditions.MarkTrue(nodefirewall, status.ReadyCondition, string(v1ien.NfwSuccessful), "%s", v1ien.Succeeded.String())
155 controllerutil.RemoveFinalizer(nodefirewall, v1ien.NodeFirewallFinalizer)
156 return nil
157 }
158
159 func (fw Plugin) getFileInventory(nodefirewall *v1ien.NodeFirewall) []string {
160 if nodefirewall.Status != nil && nodefirewall.Status.Inventory != nil {
161 return nodefirewall.Status.Inventory
162 }
163 return []string{}
164 }
165
166 func (fw Plugin) setFileInventory(nodefirewall *v1ien.NodeFirewall, files []string) {
167 if nodefirewall.Status == nil {
168 nodefirewall.Status = &v1ien.NodeFirewallStatus{}
169 }
170 nodefirewall.Status.Inventory = files
171 }
172
173 func (fw *Plugin) setDefaultInterface(ctx context.Context) error {
174 ienode, err := fw.config.GetHostIENode(ctx)
175 if err != nil {
176 return err
177 }
178
179 iface := ienode.Status.Network.DefaultInterfaceName
180 if iface == "" {
181 return errors.New("default interface not set")
182 }
183 fw.defaultInterface = iface
184 return nil
185 }
186
187 func (fw *Plugin) summarizer(ctx context.Context, patcher *patch.SerialPatcher, nodeFirewall *v1ien.NodeFirewall, result reconcile.Result, recErr error) error {
188 s := reconcile.NewSummarizer(patcher)
189 _, err := s.SummarizeAndPatch(ctx, nodeFirewall,
190 reconcile.WithConditions(fw.conditions),
191 reconcile.WithResult(result),
192 reconcile.WithError(recErr),
193 reconcile.WithIgnoreNotFound(),
194 reconcile.WithProcessors(
195 reconcile.RecordReconcileReq,
196 reconcile.RecordResult,
197 ),
198 reconcile.WithFieldOwner(fw.name),
199 )
200 return err
201 }
202
View as plain text