...

Source file src/edge-infra.dev/pkg/sds/ien/k8s/controllers/firewallctl/integration/integration_test.go

Documentation: edge-infra.dev/pkg/sds/ien/k8s/controllers/firewallctl/integration

     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  			// Override timeouts if we aren't using a live cluster
   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  			// verify cluster firewall exists
   156  			k.WaitOn(t, k.ObjExists(clusterFirewall))
   157  
   158  			// check that cluster firewall is reconciled and status/inventory are propagated
   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  			// check NodeFirewalls were added to ClusterFirewall inventory
   174  			k.WaitOn(t, k.InventoryExists(clusterFirewall.Status.Inventory))
   175  
   176  			// check finalizer was added to clusterfirewall
   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  			// modify ClusterFirewall
   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  			// check that un-needed node fw rules are deleted
   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  			// delete cluster firewall
   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  			// check that cluster firewall is deleted
   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