diff --git a/pkg/operator/network_policy_drift_test.go b/pkg/operator/network_policy_drift_test.go new file mode 100644 index 0000000000..3d67323c1f --- /dev/null +++ b/pkg/operator/network_policy_drift_test.go @@ -0,0 +1,156 @@ +package operator + +import ( + "context" + "testing" + + mcfgv1 "github.com/openshift/api/machineconfiguration/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + ctrlcommon "github.com/openshift/machine-config-operator/pkg/controller/common" +) + +func TestSyncNetworkPolicies_DriftProtection(t *testing.T) { + optr := testOperatorForNetworkPolicies(t, nil) + config := testRenderConfig() + + err := optr.syncNetworkPolicies(config, nil) + require.NoError(t, err) + + policy, err := optr.kubeClient.NetworkingV1().NetworkPolicies(ctrlcommon.MCONamespace).Get( + context.TODO(), "allow-machine-config-operator", metav1.GetOptions{}) + require.NoError(t, err) + + policy.Spec.Ingress = nil + _, err = optr.kubeClient.NetworkingV1().NetworkPolicies(ctrlcommon.MCONamespace).Update( + context.TODO(), policy, metav1.UpdateOptions{}) + require.NoError(t, err) + + modified, err := optr.kubeClient.NetworkingV1().NetworkPolicies(ctrlcommon.MCONamespace).Get( + context.TODO(), "allow-machine-config-operator", metav1.GetOptions{}) + require.NoError(t, err) + assert.Empty(t, modified.Spec.Ingress, "ingress should be empty after manual modification") + + err = optr.syncNetworkPolicies(config, nil) + require.NoError(t, err) + + restored, err := optr.kubeClient.NetworkingV1().NetworkPolicies(ctrlcommon.MCONamespace).Get( + context.TODO(), "allow-machine-config-operator", metav1.GetOptions{}) + require.NoError(t, err) + require.Len(t, restored.Spec.Ingress, 1, "ingress rules should be restored after resync") +} + +func TestSyncNetworkPolicies_Idempotent(t *testing.T) { + optr := testOperatorForNetworkPolicies(t, nil) + config := testRenderConfig() + + err := optr.syncNetworkPolicies(config, nil) + require.NoError(t, err) + firstCount := len(listNetworkPolicies(t, optr)) + + err = optr.syncNetworkPolicies(config, nil) + require.NoError(t, err) + secondCount := len(listNetworkPolicies(t, optr)) + + err = optr.syncNetworkPolicies(config, nil) + require.NoError(t, err) + thirdCount := len(listNetworkPolicies(t, optr)) + + assert.Equal(t, firstCount, secondCount, "policy count should be stable across syncs") + assert.Equal(t, secondCount, thirdCount, "policy count should be stable across syncs") + assert.Equal(t, 3, thirdCount, "should have exactly 3 static policies without layered pools") +} + +func TestSyncNetworkPolicies_DeletionRecreation(t *testing.T) { + optr := testOperatorForNetworkPolicies(t, nil) + config := testRenderConfig() + + err := optr.syncNetworkPolicies(config, nil) + require.NoError(t, err) + require.Len(t, listNetworkPolicies(t, optr), 3, "should start with 3 static policies") + + err = optr.kubeClient.NetworkingV1().NetworkPolicies(ctrlcommon.MCONamespace).Delete( + context.TODO(), "allow-machine-config-operator", metav1.DeleteOptions{}) + require.NoError(t, err) + + policies := listNetworkPolicies(t, optr) + require.Len(t, policies, 2, "should have 2 policies after deletion") + assert.Nil(t, findPolicy(policies, "allow-machine-config-operator"), "deleted policy should be gone") + + err = optr.syncNetworkPolicies(config, nil) + require.NoError(t, err) + + policies = listNetworkPolicies(t, optr) + require.Len(t, policies, 3, "deleted policy should be re-created after resync") + + recreated := findPolicy(policies, "allow-machine-config-operator") + require.NotNil(t, recreated, "allow-machine-config-operator must be re-created") + assert.Equal(t, "machine-config-operator", recreated.Spec.PodSelector.MatchLabels["k8s-app"]) + require.Len(t, recreated.Spec.Ingress, 1, "re-created policy should have ingress rule") + require.Len(t, recreated.Spec.Ingress[0].Ports, 1) + assert.Equal(t, int32(9001), recreated.Spec.Ingress[0].Ports[0].Port.IntVal, "re-created policy should have metrics port") + require.Len(t, recreated.Spec.Egress, 1, "re-created policy should have egress rule") +} + +func TestSyncNetworkPolicies_AllDeletedRecreation(t *testing.T) { + optr := testOperatorForNetworkPolicies(t, nil) + config := testRenderConfig() + + err := optr.syncNetworkPolicies(config, nil) + require.NoError(t, err) + require.Len(t, listNetworkPolicies(t, optr), 3) + + staticPolicies := []string{"default-deny", "allow-machine-config-operator", "allow-machine-config-controller"} + for _, name := range staticPolicies { + err = optr.kubeClient.NetworkingV1().NetworkPolicies(ctrlcommon.MCONamespace).Delete( + context.TODO(), name, metav1.DeleteOptions{}) + require.NoError(t, err) + } + require.Empty(t, listNetworkPolicies(t, optr), "all policies should be deleted") + + err = optr.syncNetworkPolicies(config, nil) + require.NoError(t, err) + + policies := listNetworkPolicies(t, optr) + require.Len(t, policies, 3, "all static policies should be re-created after resync") + + names := make([]string, len(policies)) + for i, p := range policies { + names[i] = p.Name + } + for _, expected := range staticPolicies { + assert.Contains(t, names, expected, "policy %s should be re-created", expected) + } +} + +func TestSyncNetworkPolicies_MOBDeletionRecreation(t *testing.T) { + pools := []*mcfgv1.MachineConfigPool{layeredPool("layered-worker")} + optr := testOperatorForNetworkPolicies(t, pools) + config := testRenderConfig() + + err := optr.syncNetworkPolicies(config, nil) + require.NoError(t, err) + + policies := listNetworkPolicies(t, optr) + require.Len(t, policies, 4, "should have 4 policies with layered pools") + require.NotNil(t, findPolicy(policies, "allow-machine-os-builder"), "MOB policy should exist") + + err = optr.kubeClient.NetworkingV1().NetworkPolicies(ctrlcommon.MCONamespace).Delete( + context.TODO(), "allow-machine-os-builder", metav1.DeleteOptions{}) + require.NoError(t, err) + require.Len(t, listNetworkPolicies(t, optr), 3, "MOB policy should be deleted") + + err = optr.syncNetworkPolicies(config, nil) + require.NoError(t, err) + + policies = listNetworkPolicies(t, optr) + require.Len(t, policies, 4, "MOB policy should be re-created after resync") + + mobPolicy := findPolicy(policies, "allow-machine-os-builder") + require.NotNil(t, mobPolicy, "allow-machine-os-builder must be re-created") + assert.Equal(t, "machine-os-builder", mobPolicy.Spec.PodSelector.MatchLabels["k8s-app"]) + require.Len(t, mobPolicy.Spec.Ingress, 1, "re-created MOB policy should have ingress rule") + require.Len(t, mobPolicy.Spec.Egress, 1, "re-created MOB policy should have egress rule") +}