1 package integration
2
3 import (
4 "context"
5 _ "embed"
6 "os"
7 "slices"
8 "testing"
9 "time"
10
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13 "gotest.tools/v3/assert/cmp"
14 corev1 "k8s.io/api/core/v1"
15 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16 ctrl "sigs.k8s.io/controller-runtime"
17 "sigs.k8s.io/controller-runtime/pkg/client"
18
19 "edge-infra.dev/pkg/lib/fog"
20 v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
21 "edge-infra.dev/pkg/sds/ien/k8s/controllers/firewallctl"
22 "edge-infra.dev/test/f2"
23 "edge-infra.dev/test/f2/x/ktest"
24 )
25
26 var (
27 fakeHardwareAddr = "ab:cd:ef:12:34:56"
28
29 rules = []v1ien.NodeRule{
30 {
31 ID: "a388bde1",
32 Name: "rule1",
33 InterfaceMAC: fakeHardwareAddr,
34 Direction: v1ien.Input,
35 SourceRanges: []string{"172.23.1.1/16"},
36 DestinationRanges: []string{"172.23.1.10/32"},
37 Filters: []v1ien.Filter{
38 {
39 IPProtocol: v1ien.TCP,
40 PortRange: "6443",
41 Action: v1ien.Allow,
42 },
43 },
44 },
45 {
46 ID: "2f8d93ba",
47 Name: "rule2",
48 Direction: v1ien.Output,
49 SourceRanges: []string{},
50 DestinationRanges: []string{"172.23.1.10/32"},
51 Filters: []v1ien.Filter{
52 {
53 IPProtocol: v1ien.TCP,
54 PortRange: "8080",
55 Action: v1ien.Allow,
56 },
57 {
58 IPProtocol: v1ien.TCP,
59 PortRange: "493",
60 Action: v1ien.Allow,
61 },
62 },
63 },
64 }
65
66 clusterFirewallTest = v1ien.NewClusterFirewall(
67 "clusterfirewalltest",
68 []v1ien.ClusterRule{
69 {
70 NodeSelector: make(map[string]string),
71 NodeRule: rules[0],
72 },
73 {
74 NodeSelector: map[string]string{"exampleFilterLabel": "true"},
75 NodeRule: rules[1],
76 },
77 },
78 )
79
80 testNodes = []*corev1.Node{
81 {
82 ObjectMeta: metav1.ObjectMeta{
83 Name: "edge-worker1",
84 },
85 },
86 {
87 ObjectMeta: metav1.ObjectMeta{
88 Name: "edge-worker2",
89 Labels: map[string]string{
90 "exampleFilterLabel": "true",
91 },
92 },
93 },
94 }
95
96 ownerRef = *metav1.NewControllerRef(clusterFirewallTest, v1ien.ClusterFirewallGVK)
97
98 expectedNodeFirewalls = []*v1ien.NodeFirewall{
99 v1ien.NewNodeFirewall("clusterfirewalltest-edge-worker1", []v1ien.NodeRule{rules[0]}, ownerRef),
100 v1ien.NewNodeFirewall("clusterfirewalltest-edge-worker2", []v1ien.NodeRule{rules[0], rules[1]}, ownerRef),
101 v1ien.NewNodeFirewall("clusterfirewalltest-edge-worker2", []v1ien.NodeRule{rules[1]}, ownerRef),
102 }
103 )
104
105 var f f2.Framework
106
107 func TestMain(m *testing.M) {
108 ctrl.SetLogger(fog.New())
109 firewallctl.MetricsBindAddress = ":0"
110 firewallctl.HealthBindAddress = ":0"
111 f = f2.New(
112 context.Background(),
113 f2.WithExtensions(
114 ktest.New(
115 ktest.WithCtrlManager(firewallctl.SetupManager),
116 ),
117 )).
118 Setup(func(ctx f2.Context) (f2.Context, error) {
119 k, err := ktest.FromContext(ctx)
120 if err != nil {
121 return ctx, err
122 }
123
124
125 if !*k.Env.UseExistingCluster {
126 k.Timeout = 5 * time.Second
127 k.Tick = 10 * time.Millisecond
128 }
129
130 return ctx, nil
131 })
132
133 os.Exit(f.Run(m))
134 }
135
136 func TestClusterFirewall(t *testing.T) {
137 var clusterFirewall = clusterFirewallTest.DeepCopy()
138
139 feature := f2.NewFeature("new ClusterFirewall").
140 Setup("create fake client", func(ctx f2.Context, t *testing.T) f2.Context {
141 k := ktest.FromContextT(ctx, t)
142
143 require.NoError(t, k.Client.Create(ctx, clusterFirewall))
144
145 require.NoError(t, k.Client.Create(ctx, testNodes[0]))
146 require.NoError(t, k.Client.Create(ctx, testNodes[1]))
147
148 return ctx
149 }).
150 Test("ClusterFirewall creates NodeFirewalls", func(ctx f2.Context, t *testing.T) f2.Context {
151 var (
152 k = ktest.FromContextT(ctx, t)
153 )
154
155
156 k.WaitOn(t, k.ObjExists(clusterFirewall))
157
158
159 k.WaitOn(t, k.Check(clusterFirewall, func(o client.Object) cmp.Result {
160 fw := o.(*v1ien.ClusterFirewall)
161 if fw.Status == nil {
162 return cmp.ResultFailure("cluster firewall status is nil")
163 }
164 if fw.Status.Inventory == nil {
165 return cmp.ResultFailure("inventory is nil")
166 }
167 if len(fw.Status.Inventory.Entries) != 2 {
168 return cmp.ResultFailure("inventory has no been populated")
169 }
170 return cmp.ResultSuccess
171 }))
172
173
174 k.WaitOn(t, k.InventoryExists(clusterFirewall.Status.Inventory))
175
176
177 assert.True(t, slices.Contains(clusterFirewall.Finalizers, v1ien.ClusterFirewallFinalizer))
178
179 return ctx
180 }).
181 Test("ClusterFirewall removes unneeded NodeFirewalls", func(ctx f2.Context, t *testing.T) f2.Context {
182 k := ktest.FromContextT(ctx, t)
183
184
185 patch := client.MergeFrom(clusterFirewall.DeepCopy())
186 clusterFirewall.Spec.ClusterRules = clusterFirewall.Spec.ClusterRules[1:]
187 require.NoError(t, k.Client.Patch(ctx, clusterFirewall, patch))
188
189
190 k.WaitOn(t, k.ObjDeleted(expectedNodeFirewalls[0]))
191
192 return ctx
193 }).
194 Test("ClusterFirewall removes NodeFirewalls on deletion", func(ctx f2.Context, t *testing.T) f2.Context {
195 k := ktest.FromContextT(ctx, t)
196
197
198 err := k.Client.Delete(ctx, clusterFirewall)
199 require.NoError(t, err)
200
201 for _, o := range expectedNodeFirewalls {
202 k.WaitOn(t, k.ObjDeleted(o))
203 }
204
205
206 k.WaitOn(t, k.ObjDeleted(clusterFirewall))
207
208 return ctx
209 }).
210 Feature()
211
212 f.Test(t, feature)
213 }
214
View as plain text