package integration import ( "context" _ "embed" "os" "slices" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gotest.tools/v3/assert/cmp" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "edge-infra.dev/pkg/lib/fog" v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1" "edge-infra.dev/pkg/sds/ien/k8s/controllers/firewallctl" "edge-infra.dev/test/f2" "edge-infra.dev/test/f2/x/ktest" ) var ( fakeHardwareAddr = "ab:cd:ef:12:34:56" rules = []v1ien.NodeRule{ { ID: "a388bde1", Name: "rule1", InterfaceMAC: fakeHardwareAddr, Direction: v1ien.Input, SourceRanges: []string{"172.23.1.1/16"}, DestinationRanges: []string{"172.23.1.10/32"}, Filters: []v1ien.Filter{ { IPProtocol: v1ien.TCP, PortRange: "6443", Action: v1ien.Allow, }, }, }, { ID: "2f8d93ba", Name: "rule2", Direction: v1ien.Output, SourceRanges: []string{}, DestinationRanges: []string{"172.23.1.10/32"}, Filters: []v1ien.Filter{ { IPProtocol: v1ien.TCP, PortRange: "8080", Action: v1ien.Allow, }, { IPProtocol: v1ien.TCP, PortRange: "493", Action: v1ien.Allow, }, }, }, } clusterFirewallTest = v1ien.NewClusterFirewall( "clusterfirewalltest", []v1ien.ClusterRule{ { NodeSelector: make(map[string]string), NodeRule: rules[0], }, { NodeSelector: map[string]string{"exampleFilterLabel": "true"}, NodeRule: rules[1], }, }, ) testNodes = []*corev1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "edge-worker1", }, }, { ObjectMeta: metav1.ObjectMeta{ Name: "edge-worker2", Labels: map[string]string{ "exampleFilterLabel": "true", }, }, }, } ownerRef = *metav1.NewControllerRef(clusterFirewallTest, v1ien.ClusterFirewallGVK) expectedNodeFirewalls = []*v1ien.NodeFirewall{ v1ien.NewNodeFirewall("clusterfirewalltest-edge-worker1", []v1ien.NodeRule{rules[0]}, ownerRef), v1ien.NewNodeFirewall("clusterfirewalltest-edge-worker2", []v1ien.NodeRule{rules[0], rules[1]}, ownerRef), v1ien.NewNodeFirewall("clusterfirewalltest-edge-worker2", []v1ien.NodeRule{rules[1]}, ownerRef), } ) var f f2.Framework func TestMain(m *testing.M) { ctrl.SetLogger(fog.New()) firewallctl.MetricsBindAddress = ":0" firewallctl.HealthBindAddress = ":0" f = f2.New( context.Background(), f2.WithExtensions( ktest.New( ktest.WithCtrlManager(firewallctl.SetupManager), ), )). Setup(func(ctx f2.Context) (f2.Context, error) { k, err := ktest.FromContext(ctx) if err != nil { return ctx, err } // Override timeouts if we aren't using a live cluster if !*k.Env.UseExistingCluster { k.Timeout = 5 * time.Second k.Tick = 10 * time.Millisecond } return ctx, nil }) os.Exit(f.Run(m)) } func TestClusterFirewall(t *testing.T) { var clusterFirewall = clusterFirewallTest.DeepCopy() feature := f2.NewFeature("new ClusterFirewall"). Setup("create fake client", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) require.NoError(t, k.Client.Create(ctx, clusterFirewall)) require.NoError(t, k.Client.Create(ctx, testNodes[0])) require.NoError(t, k.Client.Create(ctx, testNodes[1])) return ctx }). Test("ClusterFirewall creates NodeFirewalls", func(ctx f2.Context, t *testing.T) f2.Context { var ( k = ktest.FromContextT(ctx, t) ) // verify cluster firewall exists k.WaitOn(t, k.ObjExists(clusterFirewall)) // check that cluster firewall is reconciled and status/inventory are propagated k.WaitOn(t, k.Check(clusterFirewall, func(o client.Object) cmp.Result { fw := o.(*v1ien.ClusterFirewall) if fw.Status == nil { return cmp.ResultFailure("cluster firewall status is nil") } if fw.Status.Inventory == nil { return cmp.ResultFailure("inventory is nil") } if len(fw.Status.Inventory.Entries) != 2 { return cmp.ResultFailure("inventory has no been populated") } return cmp.ResultSuccess })) // check NodeFirewalls were added to ClusterFirewall inventory k.WaitOn(t, k.InventoryExists(clusterFirewall.Status.Inventory)) // check finalizer was added to clusterfirewall assert.True(t, slices.Contains(clusterFirewall.Finalizers, v1ien.ClusterFirewallFinalizer)) return ctx }). Test("ClusterFirewall removes unneeded NodeFirewalls", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) // modify ClusterFirewall patch := client.MergeFrom(clusterFirewall.DeepCopy()) clusterFirewall.Spec.ClusterRules = clusterFirewall.Spec.ClusterRules[1:] require.NoError(t, k.Client.Patch(ctx, clusterFirewall, patch)) // check that un-needed node fw rules are deleted k.WaitOn(t, k.ObjDeleted(expectedNodeFirewalls[0])) return ctx }). Test("ClusterFirewall removes NodeFirewalls on deletion", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) // delete cluster firewall err := k.Client.Delete(ctx, clusterFirewall) require.NoError(t, err) for _, o := range expectedNodeFirewalls { k.WaitOn(t, k.ObjDeleted(o)) } // check that cluster firewall is deleted k.WaitOn(t, k.ObjDeleted(clusterFirewall)) return ctx }). Feature() f.Test(t, feature) }