diff --git a/go-controller/pkg/ovn/admin_network_policy_test.go b/go-controller/pkg/ovn/admin_network_policy_test.go index 1730f60f14..bb2a199bab 100644 --- a/go-controller/pkg/ovn/admin_network_policy_test.go +++ b/go-controller/pkg/ovn/admin_network_policy_test.go @@ -7,6 +7,7 @@ import ( "strings" "time" + nadapi "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config" @@ -15,6 +16,7 @@ import ( "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb" addressset "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/address_set" anpovn "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/controller/admin_network_policy" + ovntest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing" libovsdbtest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing/libovsdb" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util" @@ -74,11 +76,15 @@ func getANPRulePriority(basePriority, index int32) int32 { } func getDefaultPGForANPSubject(anpName string, portUUIDs []string, acls []*nbdb.ACL, banp bool) *nbdb.PortGroup { + return getUDNPGForANPSubject(anpName, portUUIDs, acls, banp, types.DefaultNetworkName) +} + +func getUDNPGForANPSubject(anpName string, portUUIDs []string, acls []*nbdb.ACL, banp bool, networkName string) *nbdb.PortGroup { lsps := []*nbdb.LogicalSwitchPort{} for _, uuid := range portUUIDs { lsps = append(lsps, &nbdb.LogicalSwitchPort{UUID: uuid}) } - pgDbIDs := anpovn.GetANPPortGroupDbIDs(anpName, banp, DefaultNetworkControllerName) + pgDbIDs := anpovn.GetANPPortGroupDbIDs(anpName, banp, networkName) pg := libovsdbutil.BuildPortGroup( pgDbIDs, @@ -91,7 +97,7 @@ func getDefaultPGForANPSubject(anpName string, portUUIDs []string, acls []*nbdb. func getANPGressACL(action, anpName, direction string, rulePriority int32, ruleIndex int32, ports *[]anpapi.AdminNetworkPolicyPort, - namedPorts map[string][]libovsdbutil.NamedNetworkPolicyPort, banp bool) []*nbdb.ACL { + namedPorts map[string][]libovsdbutil.NamedNetworkPolicyPort, banp bool, networkName string) []*nbdb.ACL { retACLs := []*nbdb.ACL{} // we are not using BuildACL and instead manually building it on purpose so that the code path for BuildACL is also tested acl := nbdb.ACL{} @@ -105,24 +111,24 @@ func getANPGressACL(action, anpName, direction string, rulePriority int32, acl.Tier = types.DefaultBANPACLTier } acl.ExternalIDs = map[string]string{ - libovsdbops.OwnerControllerKey.String(): DefaultNetworkControllerName, + libovsdbops.OwnerControllerKey.String(): networkName, libovsdbops.ObjectNameKey.String(): anpName, libovsdbops.GressIdxKey.String(): fmt.Sprintf("%d", ruleIndex), libovsdbops.PolicyDirectionKey.String(): direction, libovsdbops.PortPolicyProtocolKey.String(): "None", libovsdbops.OwnerTypeKey.String(): "AdminNetworkPolicy", - libovsdbops.PrimaryIDKey.String(): fmt.Sprintf("%s:AdminNetworkPolicy:%s:%s:%d:None", DefaultNetworkControllerName, anpName, direction, ruleIndex), + libovsdbops.PrimaryIDKey.String(): fmt.Sprintf("%s:AdminNetworkPolicy:%s:%s:%d:None", networkName, anpName, direction, ruleIndex), } acl.Name = utilpointer.String(fmt.Sprintf("ANP:%s:%s:%d", anpName, direction, ruleIndex)) // tests logic for GetACLName if banp { acl.ExternalIDs[libovsdbops.OwnerTypeKey.String()] = "BaselineAdminNetworkPolicy" acl.ExternalIDs[libovsdbops.PrimaryIDKey.String()] = fmt.Sprintf("%s:BaselineAdminNetworkPolicy:%s:%s:%d:None", - DefaultNetworkControllerName, anpName, direction, ruleIndex) + networkName, anpName, direction, ruleIndex) acl.Name = utilpointer.String(fmt.Sprintf("BANP:%s:%s:%d", anpName, direction, ruleIndex)) // tests logic for GetACLName } acl.UUID = fmt.Sprintf("%s_%s_%d-%f-UUID", anpName, direction, ruleIndex, rand.Float64()) // determine ACL match - pgName := libovsdbutil.GetPortGroupName(anpovn.GetANPPortGroupDbIDs(anpName, banp, DefaultNetworkControllerName)) + pgName := libovsdbutil.GetPortGroupName(anpovn.GetANPPortGroupDbIDs(anpName, banp, networkName)) var lPortMatch, l3Match, matchDirection, match string if direction == string(libovsdbutil.ACLIngress) { acl.Direction = nbdb.ACLDirectionToLport @@ -137,7 +143,7 @@ func getANPGressACL(action, anpName, direction string, rulePriority int32, matchDirection = "dst" } asIndex := anpovn.GetANPPeerAddrSetDbIDs(anpName, direction, fmt.Sprintf("%d", ruleIndex), - DefaultNetworkControllerName, banp) + networkName, banp) asv4, asv6 := addressset.GetHashNamesForAS(asIndex) if config.IPv4Mode && config.IPv6Mode { l3Match = fmt.Sprintf("((ip4.%s == $%s || ip6.%s == $%s))", matchDirection, asv4, matchDirection, asv6) @@ -180,10 +186,10 @@ func getANPGressACL(action, anpName, direction string, rulePriority int32, aclCopy.ExternalIDs[libovsdbops.PortPolicyProtocolKey.String()] = protocol aclCopy.Match = match aclCopy.ExternalIDs[libovsdbops.PrimaryIDKey.String()] = fmt.Sprintf("%s:AdminNetworkPolicy:%s:%s:%d:%s", - DefaultNetworkControllerName, anpName, direction, ruleIndex, protocol) + networkName, anpName, direction, ruleIndex, protocol) if banp { aclCopy.ExternalIDs[libovsdbops.PrimaryIDKey.String()] = fmt.Sprintf("%s:BaselineAdminNetworkPolicy:%s:%s:%d:%s", - DefaultNetworkControllerName, anpName, direction, ruleIndex, protocol) + networkName, anpName, direction, ruleIndex, protocol) } aclCopy.UUID = fmt.Sprintf("%s_%s_%d.%s-%f-UUID", anpName, direction, ruleIndex, protocol, rand.Float64()) retACLs = append(retACLs, &aclCopy) @@ -203,10 +209,10 @@ func getANPGressACL(action, anpName, direction string, rulePriority int32, aclCopy.ExternalIDs[libovsdbops.PortPolicyProtocolKey.String()] = protocol + "-namedPort" aclCopy.Match = match aclCopy.ExternalIDs[libovsdbops.PrimaryIDKey.String()] = fmt.Sprintf("%s:AdminNetworkPolicy:%s:%s:%d:%s", - DefaultNetworkControllerName, anpName, direction, ruleIndex, protocol+"-namedPort") + networkName, anpName, direction, ruleIndex, protocol+"-namedPort") if banp { aclCopy.ExternalIDs[libovsdbops.PrimaryIDKey.String()] = fmt.Sprintf("%s:BaselineAdminNetworkPolicy:%s:%s:%d:%s", - DefaultNetworkControllerName, anpName, direction, ruleIndex, protocol+"-namedPort") + networkName, anpName, direction, ruleIndex, protocol+"-namedPort") } retACLs = append(retACLs, &aclCopy) } @@ -218,7 +224,7 @@ func getACLsForANPRulesWithNamedPorts(anp *anpapi.AdminNetworkPolicy, namedIPort ovnBaseANPPriority := getBaseRulePriority(anp.Spec.Priority) for i, ingress := range anp.Spec.Ingress { acls := getANPGressACL(anpovn.GetACLActionForANPRule(ingress.Action), anp.Name, string(libovsdbutil.ACLIngress), - getANPRulePriority(ovnBaseANPPriority, int32(i)), int32(i), ingress.Ports, namedIPorts, false) + getANPRulePriority(ovnBaseANPPriority, int32(i)), int32(i), ingress.Ports, namedIPorts, false, DefaultNetworkControllerName) aclResults = append(aclResults, acls...) } for i, egress := range anp.Spec.Egress { @@ -227,7 +233,7 @@ func getACLsForANPRulesWithNamedPorts(anp *anpapi.AdminNetworkPolicy, namedIPort namedEPorts = nil } acls := getANPGressACL(anpovn.GetACLActionForANPRule(egress.Action), anp.Name, string(libovsdbutil.ACLEgress), - getANPRulePriority(ovnBaseANPPriority, int32(i)), int32(i), egress.Ports, namedEPorts, false) + getANPRulePriority(ovnBaseANPPriority, int32(i)), int32(i), egress.Ports, namedEPorts, false, DefaultNetworkControllerName) aclResults = append(aclResults, acls...) } return aclResults @@ -238,17 +244,52 @@ func getACLsForANPRules(anp *anpapi.AdminNetworkPolicy) []*nbdb.ACL { ovnBaseANPPriority := getBaseRulePriority(anp.Spec.Priority) for i, ingress := range anp.Spec.Ingress { acls := getANPGressACL(anpovn.GetACLActionForANPRule(ingress.Action), anp.Name, string(libovsdbutil.ACLIngress), - getANPRulePriority(ovnBaseANPPriority, int32(i)), int32(i), ingress.Ports, nil, false) + getANPRulePriority(ovnBaseANPPriority, int32(i)), int32(i), ingress.Ports, nil, false, DefaultNetworkControllerName) + aclResults = append(aclResults, acls...) + } + for i, egress := range anp.Spec.Egress { + acls := getANPGressACL(anpovn.GetACLActionForANPRule(egress.Action), anp.Name, string(libovsdbutil.ACLEgress), + getANPRulePriority(ovnBaseANPPriority, int32(i)), int32(i), egress.Ports, nil, false, DefaultNetworkControllerName) + aclResults = append(aclResults, acls...) + } + return aclResults +} + +// pass the networkName to return ACL representations for that network +func getUDNACLsForANPRules(anp *anpapi.AdminNetworkPolicy, networkName string) []*nbdb.ACL { + aclResults := []*nbdb.ACL{} + ovnBaseANPPriority := getBaseRulePriority(anp.Spec.Priority) + for i, ingress := range anp.Spec.Ingress { + acls := getANPGressACL(anpovn.GetACLActionForANPRule(ingress.Action), anp.Name, string(libovsdbutil.ACLIngress), + getANPRulePriority(ovnBaseANPPriority, int32(i)), int32(i), ingress.Ports, nil, false, networkName) aclResults = append(aclResults, acls...) } for i, egress := range anp.Spec.Egress { acls := getANPGressACL(anpovn.GetACLActionForANPRule(egress.Action), anp.Name, string(libovsdbutil.ACLEgress), - getANPRulePriority(ovnBaseANPPriority, int32(i)), int32(i), egress.Ports, nil, false) + getANPRulePriority(ovnBaseANPPriority, int32(i)), int32(i), egress.Ports, nil, false, networkName) aclResults = append(aclResults, acls...) } return aclResults } +func getUDNPG() *nbdb.PortGroup { + pgIDs := libovsdbops.NewDbObjectIDs(libovsdbops.PortGroupUDN, DefaultNetworkControllerName, + map[libovsdbops.ExternalIDKey]string{ + libovsdbops.ObjectNameKey: "SecondaryPods", + }) + return &nbdb.PortGroup{ + Name: libovsdbutil.GetPortGroupName(pgIDs), + UUID: "UDN-Isolation-PG-UUID", + } +} + +func buildUDNANPAddressSets(anp *anpapi.AdminNetworkPolicy, index int32, ips []string, + gressPrefix libovsdbutil.ACLDirection, networkName string) (*nbdb.AddressSet, *nbdb.AddressSet) { + asIndex := anpovn.GetANPPeerAddrSetDbIDs(anp.Name, string(gressPrefix), + fmt.Sprintf("%d", index), networkName, false) + return addressset.GetTestDbAddrSets(asIndex, ips) +} + func buildANPAddressSets(anp *anpapi.AdminNetworkPolicy, index int32, ips []string, gressPrefix libovsdbutil.ACLDirection) (*nbdb.AddressSet, *nbdb.AddressSet) { asIndex := anpovn.GetANPPeerAddrSetDbIDs(anp.Name, string(gressPrefix), fmt.Sprintf("%d", index), DefaultNetworkControllerName, false) @@ -257,8 +298,16 @@ func buildANPAddressSets(anp *anpapi.AdminNetworkPolicy, index int32, ips []stri var _ = ginkgo.Describe("OVN ANP Operations", func() { var ( - app *cli.App - fakeOVN *FakeOVN + app *cli.App + fakeOVN *FakeOVN + udnACLs []*nbdb.ACL + udnPG, udnIsolationPG *nbdb.PortGroup + peerUDNASIngressRule0v4, peerUDNASIngressRule0v6 *nbdb.AddressSet + peerUDNASIngressRule1v4, peerUDNASIngressRule1v6 *nbdb.AddressSet + peerUDNASIngressRule2v4, peerUDNASIngressRule2v6 *nbdb.AddressSet + peerUDNASEgressRule0v4, peerUDNASEgressRule0v6 *nbdb.AddressSet + peerUDNASEgressRule1v4, peerUDNASEgressRule1v6 *nbdb.AddressSet + peerUDNASEgressRule2v4, peerUDNASEgressRule2v6 *nbdb.AddressSet ) const ( @@ -286,6 +335,7 @@ var _ = ginkgo.Describe("OVN ANP Operations", func() { node2IPv6Subnet string = "fe00:10:128:2::/64" node2transitIPv4 string = "100.88.0.3" node2transitIPv6 string = "fd97::3" + nadName string = "bluenad" ) ginkgo.BeforeEach(func() { @@ -294,6 +344,9 @@ var _ = ginkgo.Describe("OVN ANP Operations", func() { config.OVNKubernetesFeature.EnableAdminNetworkPolicy = true // IC true or false does not really effect this feature config.OVNKubernetesFeature.EnableInterconnect = true + // We have mix of tests for default network and UDN networks + config.OVNKubernetesFeature.EnableMultiNetwork = true + config.OVNKubernetesFeature.EnableNetworkSegmentation = true app = cli.NewApp() app.Name = "test" @@ -307,673 +360,985 @@ var _ = ginkgo.Describe("OVN ANP Operations", func() { }) ginkgo.Context("On admin network policy changes", func() { - ginkgo.It("should create/update/delete address-sets, acls, port-groups correctly", func() { - app.Action = func(ctx *cli.Context) error { - anpNamespaceSubject := *newNamespaceWithLabels(anpSubjectNamespaceName, anpLabel) - anpNamespacePeer := *newNamespaceWithLabels(anpPeerNamespaceName, peerDenyLabel) - config.IPv4Mode = true - config.IPv6Mode = true - node1 := nodeFor(node1Name, "100.100.100.0", "fc00:f853:ccd:e793::1", "10.128.1.0/24", "fe00:10:128:1::/64", "", "") - - node1Switch := &nbdb.LogicalSwitch{ - Name: node1Name, - UUID: node1Name + "-UUID", - } - subjectNSASIPv4, subjectNSASIPv6 := buildNamespaceAddressSets(anpSubjectNamespaceName, []string{}) - peerNSASIPv4, peerNSASIPv6 := buildNamespaceAddressSets(anpPeerNamespaceName, []string{}) - dbSetup := libovsdbtest.TestSetup{ - NBData: []libovsdbtest.TestData{ - node1Switch, - subjectNSASIPv4, - subjectNSASIPv6, - peerNSASIPv4, - peerNSASIPv6, - }, - } - fakeOVN.startWithDBSetup(dbSetup, - &v1.NamespaceList{ - Items: []v1.Namespace{ - anpNamespaceSubject, - anpNamespacePeer, + ginkgo.DescribeTable("should create/update/delete address-sets, acls, port-groups correctly", + func(anpSubjectNetwork, anpPeerNetwork string, isUDNEnabled bool) { + app.Action = func(ctx *cli.Context) error { + anpNamespaceSubject := *newNamespaceWithLabels(anpSubjectNamespaceName, anpLabel) + anpNamespacePeer := *newNamespaceWithLabels(anpPeerNamespaceName, peerDenyLabel) + config.IPv4Mode = true + config.IPv6Mode = true + node1 := nodeFor(node1Name, "100.100.100.0", "fc00:f853:ccd:e793::1", "10.128.1.0/24", "fe00:10:128:1::/64", "", "") + + node1Switch := &nbdb.LogicalSwitch{ + Name: node1Name, + UUID: node1Name + "-UUID", + } + anpSubjectNetworkSwitch := node1Switch + subjectNSASIPv4, subjectNSASIPv6 := buildNamespaceAddressSets(anpSubjectNamespaceName, []string{}) + peerNSASIPv4, peerNSASIPv6 := buildNamespaceAddressSets(anpPeerNamespaceName, []string{}) + dbSetup := libovsdbtest.TestSetup{ + NBData: []libovsdbtest.TestData{ + node1Switch, + subjectNSASIPv4, + subjectNSASIPv6, + peerNSASIPv4, + peerNSASIPv6, }, - }, - &v1.NodeList{ - Items: []v1.Node{ - *node1, + } + k8sObjects := []runtime.Object{ + &v1.NamespaceList{ + Items: []v1.Namespace{ + anpNamespaceSubject, + anpNamespacePeer, + }, + }, + &v1.NodeList{ + Items: []v1.Node{ + *node1, + }, }, - }, - ) - - fakeOVN.controller.zone = node1Name // ensure we set the controller's zone as the node's zone - err := fakeOVN.controller.WatchNamespaces() - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - err = fakeOVN.controller.WatchPods() - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - fakeOVN.InitAndRunANPController() - fakeOVN.fakeClient.ANPClient.(*anpfake.Clientset).PrependReactor("update", "adminnetworkpolicies", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { - update := action.(clienttesting.UpdateAction) - // Since fake client (NewSimpleClientset) does not differentiate between - // an update and updatestatus, updatestatus in tests updates the spec as - // well causing race conditions. Thus adding a hack here to ensure update - // status is caught and processed by the reactor while update spec is - // delegated to the main code for handling - if action.GetSubresource() == "status" { - klog.Infof("Got an update status action for %v", update.GetObject()) - return true, update.GetObject(), nil } - klog.Infof("Got an update spec action for %v", update.GetObject()) - return false, update.GetObject(), nil - }) + if isUDNEnabled { + if anpSubjectNetwork != types.DefaultNetworkName { + gNAD := ovntest.GenerateNAD(anpSubjectNetwork, nadName, anpNamespaceSubject.Name, + types.Layer3Topology, "100.128.0.0/16/24,2010:100:200::0/60/64", types.NetworkRolePrimary) + k8sObjects = append(k8sObjects, + &nadapi.NetworkAttachmentDefinitionList{ + Items: []nadapi.NetworkAttachmentDefinition{ + *gNAD, + }, + }, + ) + anpSubjectNetworkSwitch = &nbdb.LogicalSwitch{ + Name: anpSubjectNetwork + "_" + node1Name, + UUID: anpSubjectNetwork + "_" + node1Name + "-UUID", + } + // we don't call WatchNodes for ANP tests; so the UDN isolation PG won't be + // created; hence we need to manually add it during setup phase so that pod + // creation on default network succeeds + udnIsolationPG = getUDNPG() + dbSetup.NBData = append(dbSetup.NBData, anpSubjectNetworkSwitch, udnIsolationPG) + } + } - ginkgo.By("1. creating an admin network policy with 0 rules; check if port group is created for the subject") - anpSubject := newANPSubjectObject( - &metav1.LabelSelector{ - MatchLabels: anpLabel, - }, - nil, - ) - anp := newANPObject("harry-potter", 5, anpSubject, []anpapi.AdminNetworkPolicyIngressRule{}, []anpapi.AdminNetworkPolicyEgressRule{}) - anp.ResourceVersion = "1" - anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Create(context.TODO(), anp, metav1.CreateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - pg := getDefaultPGForANPSubject(anp.Name, nil, nil, false) - expectedDatabaseState := []libovsdbtest.TestData{node1Switch, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6, pg} - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + fakeOVN.startWithDBSetup(dbSetup, k8sObjects...) + if isUDNEnabled { + gomega.Expect(fakeOVN.networkManager.Start()).Should(gomega.Succeed()) + defer fakeOVN.networkManager.Stop() + } + fakeOVN.controller.zone = node1Name // ensure we set the controller's zone as the node's zone + err := fakeOVN.controller.WatchNamespaces() + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + err = fakeOVN.controller.WatchPods() + gomega.Expect(err).NotTo(gomega.HaveOccurred()) - ginkgo.By("2. creating a pod that will act as subject of admin network policy; check if lsp is added to port-group after retries") - t := newTPod( - node1Name, - "10.128.1.0/24", - "10.128.1.2", - "10.128.1.1", - anpSubjectPodName, - anpPodV4IP, - anpPodMAC, - anpSubjectNamespaceName, - ) - t.portName = util.GetLogicalPortName(t.namespace, t.podName) - t.populateLogicalSwitchCache(fakeOVN) - anpSubjectPod := *newPod(anpSubjectNamespaceName, anpSubjectPodName, node1Name, anpPodV4IP) - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Create(context.TODO(), &anpSubjectPod, metav1.CreateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - // The pod takes some time to get created so the first add pod event for ANP does nothing as it waits for LSP to be created - this triggers a retry. - // Next when the pod is scheduled and IP is allocated and LSP is created we get an update event. - // When ANP controller receives the pod update event, it will not trigger an update in unit testing since ResourceVersion and pod.status.Running - // is not updated by the test, hence we need to trigger a manual update in the unit test framework that is accompanied by label update. - // NOTE: This is not an issue in real deployments with actual kapi server; its just a potential flake in the UT framework that we need to protect against. - time.Sleep(time.Second) - anpSubjectPod.ResourceVersion = "5" - // - this is to trigger an actual update technically speaking the pod already matches the namespace of the subject - anpSubjectPod.Labels["rv"] = "resourceVersionUTHack" - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Update(context.TODO(), &anpSubjectPod, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - subjectNSASIPv4, subjectNSASIPv6 = buildNamespaceAddressSets(anpSubjectNamespaceName, []string{t.podIP}) - pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, nil, false) - expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t}, []string{node1Name})...) - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + fakeOVN.InitAndRunANPController() + fakeOVN.fakeClient.ANPClient.(*anpfake.Clientset).PrependReactor("update", "adminnetworkpolicies", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { + update := action.(clienttesting.UpdateAction) + // Since fake client (NewSimpleClientset) does not differentiate between + // an update and updatestatus, updatestatus in tests updates the spec as + // well causing race conditions. Thus adding a hack here to ensure update + // status is caught and processed by the reactor while update spec is + // delegated to the main code for handling + if action.GetSubresource() == "status" { + klog.Infof("Got an update status action for %v", update.GetObject()) + return true, update.GetObject(), nil + } + klog.Infof("Got an update spec action for %v", update.GetObject()) + return false, update.GetObject(), nil + }) + + ginkgo.By("1. creating an admin network policy with 0 rules; check if port group is created for the subject") + anpSubject := newANPSubjectObject( + &metav1.LabelSelector{ + MatchLabels: anpLabel, + }, + nil, + ) + anp := newANPObject("harry-potter", 5, anpSubject, []anpapi.AdminNetworkPolicyIngressRule{}, []anpapi.AdminNetworkPolicyEgressRule{}) + anp.ResourceVersion = "1" + anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Create(context.TODO(), anp, metav1.CreateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + pg := getDefaultPGForANPSubject(anp.Name, nil, nil, false) + expectedDatabaseState := []libovsdbtest.TestData{pg, node1Switch, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} + if isUDNEnabled { + expectedDatabaseState = append(expectedDatabaseState, anpSubjectNetworkSwitch, udnIsolationPG) + // since there is no namespace in default network that matches this ANP; replace this with blue P-UDN's representation + expectedDatabaseState[0] = getUDNPGForANPSubject(anp.Name, nil, nil, false, anpSubjectNetwork) + } + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + + ginkgo.By("2. creating a pod that will act as subject of admin network policy; check if lsp is added to port-group after retries") + t := newTPod( + node1Name, + "10.128.1.0/24", + "10.128.1.2", + "10.128.1.1", + anpSubjectPodName, + anpPodV4IP, + anpPodMAC, + anpSubjectNamespaceName, + ) + copyT := t + t.portName = util.GetLogicalPortName(t.namespace, t.podName) + t.populateLogicalSwitchCache(fakeOVN) + if isUDNEnabled { + secondaryNetController, ok := fakeOVN.secondaryControllers[anpSubjectNetwork] + gomega.Expect(ok).To(gomega.BeTrue()) + t.addNetwork(anpSubjectNetwork, util.GetNADName(anpSubjectNamespaceName, nadName), + "100.128.0.0/24", "100.128.0.2", "", "100.128.0.3", anpPodMAC, types.NetworkRolePrimary, 1, nil) + gomega.Expect(secondaryNetController.bnc.WatchNamespaces()).To(gomega.Succeed()) + gomega.Expect(secondaryNetController.bnc.WatchPods()).To(gomega.Succeed()) + t.populateSecondaryNetworkLogicalSwitchCache(fakeOVN, secondaryNetController) + } + anpSubjectPod := *newPod(anpSubjectNamespaceName, anpSubjectPodName, node1Name, anpPodV4IP) + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Create(context.TODO(), &anpSubjectPod, metav1.CreateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + // The pod takes some time to get created so the first add pod event for ANP does nothing as it waits for LSP to be created - this triggers a retry. + // Next when the pod is scheduled and IP is allocated and LSP is created we get an update event. + // When ANP controller receives the pod update event, it will not trigger an update in unit testing since ResourceVersion and pod.status.Running + // is not updated by the test, hence we need to trigger a manual update in the unit test framework that is accompanied by label update. + // NOTE: This is not an issue in real deployments with actual kapi server; its just a potential flake in the UT framework that we need to protect against. + time.Sleep(time.Second) + anpSubjectPod.ResourceVersion = "5" + // - this is to trigger an actual update technically speaking the pod already matches the namespace of the subject + anpSubjectPod.Labels["rv"] = "resourceVersionUTHack" + if isUDNEnabled { + // unforunately we rely on pod annotations to determine the podIPs for UDN pods since pod status doesn't have them + // and hence we need to patch the pod with the annotation of the P-UDN to simulate that + anpSubjectPod.Annotations = map[string]string{ + "k8s.ovn.org/pod-networks": fmt.Sprintf(`{%q:{"ip_addresses":["%s/24"],"mac_address":"%s"}}`, + util.GetNADName(anpSubjectNamespaceName, nadName), "100.128.0.3", + util.IPAddrToHWAddr(ovntest.MustParseIP("100.128.0.3")).String()), + } + } + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Update(context.TODO(), &anpSubjectPod, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + subjectNSASIPv4, subjectNSASIPv6 = buildNamespaceAddressSets(anpSubjectNamespaceName, []string{t.podIP}) + pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, nil, false) + expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} + expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t}, []string{node1Name})...) + if isUDNEnabled { + netInfo := fakeOVN.controller.networkManager.GetNetwork(anpSubjectNetwork) + // we need to update the pod's UDN copy so that we can fetch the appropriate items for building the expected database + // for the subject pod's blue P-UDN + copyT.podIP = "100.128.0.3" + copyT.podMAC = util.IPAddrToHWAddr(ovntest.MustParseIP("100.128.0.3")).String() + copyT.portName = util.GetSecondaryNetworkLogicalPortName(anpSubjectNamespaceName, anpSubjectPodName, netInfo.GetNADs()[0]) + copyT.portUUID = copyT.portName + "-UUID" + expectedDatabaseState = append(expectedDatabaseState, getExpectedPodsAndSwitches( + netInfo, []testPod{copyT}, []string{node1Name})...) + udnIsolationPG.Ports = []string{t.portUUID} // add the default network port for this pod + expectedDatabaseState = append(expectedDatabaseState, udnIsolationPG) + // add portgroup with LSP of the pod in the bue P-UDN and address-sets of the blue P-UDN + subjectUDNASIPv4, subjectUDNASIPv6 := buildUDNNamespaceAddressSets(anpSubjectNetwork, anpSubjectNamespaceName, []string{copyT.podIP}) + udnPG = getUDNPGForANPSubject(anp.Name, []string{copyT.portUUID}, nil, false, anpSubjectNetwork) + expectedDatabaseState = append(expectedDatabaseState, + subjectUDNASIPv4, subjectUDNASIPv6, udnPG) + expectedDatabaseState = expectedDatabaseState[1:] // there is no representation for default network yet. + } + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - ginkgo.By("3. update the ANP by adding one ingress rule; check if acl is created and added on the port-group") - ingressRules := []anpapi.AdminNetworkPolicyIngressRule{ - { - Name: "deny-traffic-from-slytherin-to-gryffindor", - Action: anpapi.AdminNetworkPolicyRuleActionDeny, - From: []anpapi.AdminNetworkPolicyIngressPeer{ - { - Namespaces: &metav1.LabelSelector{ - MatchLabels: peerDenyLabel, + ginkgo.By("3. update the ANP by adding one ingress rule; check if acl is created and added on the port-group") + ingressRules := []anpapi.AdminNetworkPolicyIngressRule{ + { + Name: "deny-traffic-from-slytherin-to-gryffindor", + Action: anpapi.AdminNetworkPolicyRuleActionDeny, + From: []anpapi.AdminNetworkPolicyIngressPeer{ + { + Namespaces: &metav1.LabelSelector{ + MatchLabels: peerDenyLabel, + }, }, }, }, - }, - } - anp = newANPObject("harry-potter", 5, anpSubject, ingressRules, []anpapi.AdminNetworkPolicyEgressRule{}) - anp.ResourceVersion = "2" - anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Update(context.TODO(), anp, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - acls := getACLsForANPRules(anp) - pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, acls, false) - expectedDatabaseState[0] = pg // acl should be added to the port group - for _, acl := range acls { - acl := acl - expectedDatabaseState = append(expectedDatabaseState, acl) - } - peerASIngressRule0v4, peerASIngressRule0v6 := buildANPAddressSets(anp, 0, []string{}, libovsdbutil.ACLIngress) // address-set will be empty since no pods match it yet - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v4) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v6) - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - - ginkgo.By("4. creating a pod that will act as peer of admin network policy; check if IP is added to address-set after retries") - t2 := newTPod( - node1Name, - "10.128.1.1/24", - "10.128.1.2", - "10.128.1.1", - anpPeerPodName, - anpPodV4IP2, - anpPodMAC2, - anpPeerNamespaceName, - ) - t2.portName = util.GetLogicalPortName(t2.namespace, t2.podName) - t2.populateLogicalSwitchCache(fakeOVN) - anpPeerPod := *newPod(anpPeerNamespaceName, anpPeerPodName, node1Name, anpPodV4IP2) - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerPod.Namespace).Create(context.TODO(), &anpPeerPod, metav1.CreateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - // The pod takes some time to get created so the first add pod event for ANP does nothing as it waits for LSP to be created - this triggers a retry. - // Next when the pod is scheduled and IP is allocated and LSP is created we get an update event. - // When ANP controller receives the pod update event, it will not trigger an update in unit testing since ResourceVersion and pod.status.Running - // is not updated by the test, hence we need to trigger a manual update in the unit test framework that is accompanied by label update. - // NOTE: This is not an issue in real deployments with actual kapi server; its just a potential flake in the UT framework that we need to protect against. - time.Sleep(time.Second) - anpPeerPod.ResourceVersion = "5" - // - this is to trigger an actual update technically speaking the pod already matches the namespace of the subject - anpPeerPod.Labels["rv"] = "resourceVersionUTHack" - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerPod.Namespace).Update(context.TODO(), &anpPeerPod, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - peerNSASIPv4, peerNSASIPv6 = buildNamespaceAddressSets(anpPeerNamespaceName, []string{t2.podIP}) - expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} - for _, acl := range acls { - acl := acl - expectedDatabaseState = append(expectedDatabaseState, acl) - } - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) - peerASIngressRule0v4, peerASIngressRule0v6 = buildANPAddressSets(anp, 0, []string{t2.podIP}, libovsdbutil.ACLIngress) // podIP should be added to v4 address-set - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v4) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v6) - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + } + anp = newANPObject("harry-potter", 5, anpSubject, ingressRules, []anpapi.AdminNetworkPolicyEgressRule{}) + anp.ResourceVersion = "2" + anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Update(context.TODO(), anp, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + acls := getACLsForANPRules(anp) + pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, acls, false) + if isUDNEnabled { + // fill in the blue network's representation for this ANP + udnACLs = getUDNACLsForANPRules(anp, anpSubjectNetwork) + udnPG = getUDNPGForANPSubject(anp.Name, []string{copyT.portUUID}, udnACLs, false, anpSubjectNetwork) + expectedDatabaseState[len(expectedDatabaseState)-1] = udnPG + for _, acl := range udnACLs { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + peerUDNASIngressRule0v4, peerUDNASIngressRule0v6 = buildUDNANPAddressSets(anp, 0, []string{}, libovsdbutil.ACLIngress, anpSubjectNetwork) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerUDNASIngressRule0v4, peerUDNASIngressRule0v6) + // if pod is UDN then its not part of default network PG + pg.Ports = nil + // since the peer namespace of this ANP matches default network; we will have an empty representation PG in default network + expectedDatabaseState = append(expectedDatabaseState, pg) + } else { + expectedDatabaseState[0] = pg + } + for _, acl := range acls { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + peerASIngressRule0v4, peerASIngressRule0v6 := buildANPAddressSets(anp, 0, []string{}, libovsdbutil.ACLIngress) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v4, peerASIngressRule0v6) + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + + ginkgo.By("4. creating a pod that will act as peer of admin network policy; check if IP is added to address-set after retries") + t2 := newTPod( + node1Name, + "10.128.1.1/24", + "10.128.1.2", + "10.128.1.1", + anpPeerPodName, + anpPodV4IP2, + anpPodMAC2, + anpPeerNamespaceName, + ) + t2.portName = util.GetLogicalPortName(t2.namespace, t2.podName) + t2.populateLogicalSwitchCache(fakeOVN) + anpPeerPod := *newPod(anpPeerNamespaceName, anpPeerPodName, node1Name, anpPodV4IP2) + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerPod.Namespace).Create(context.TODO(), &anpPeerPod, metav1.CreateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + // The pod takes some time to get created so the first add pod event for ANP does nothing as it waits for LSP to be created - this triggers a retry. + // Next when the pod is scheduled and IP is allocated and LSP is created we get an update event. + // When ANP controller receives the pod update event, it will not trigger an update in unit testing since ResourceVersion and pod.status.Running + // is not updated by the test, hence we need to trigger a manual update in the unit test framework that is accompanied by label update. + // NOTE: This is not an issue in real deployments with actual kapi server; its just a potential flake in the UT framework that we need to protect against. + time.Sleep(time.Second) + anpPeerPod.ResourceVersion = "5" + // - this is to trigger an actual update technically speaking the pod already matches the namespace of the subject + anpPeerPod.Labels["rv"] = "resourceVersionUTHack" + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerPod.Namespace).Update(context.TODO(), &anpPeerPod, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + peerNSASIPv4, peerNSASIPv6 = buildNamespaceAddressSets(anpPeerNamespaceName, []string{t2.podIP}) + expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} + for _, acl := range acls { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) + peerASIngressRule0v4, peerASIngressRule0v6 = buildANPAddressSets(anp, 0, []string{t2.podIP}, libovsdbutil.ACLIngress) // podIP should be added to v4 address-set + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v4) + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v6) + if isUDNEnabled { + // fill in the blue network's representation for this ANP + expectedDatabaseState = append(expectedDatabaseState, udnPG) + for _, acl := range udnACLs { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + //peerUDNASIngressRule0v4, peerUDNASIngressRule0v6 := buildUDNANPAddressSets(anp, 0, []string{}, libovsdbutil.ACLIngress, anpSubjectNetwork) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerUDNASIngressRule0v4, peerUDNASIngressRule0v6) + expectedDatabaseState = append(expectedDatabaseState, getExpectedPodsAndSwitches( + fakeOVN.controller.networkManager.GetNetwork(anpSubjectNetwork), []testPod{copyT}, []string{node1Name})...) + subjectUDNASIPv4, subjectUDNASIPv6 := buildUDNNamespaceAddressSets(anpSubjectNetwork, anpSubjectNamespaceName, []string{copyT.podIP}) + expectedDatabaseState = append(expectedDatabaseState, + subjectUDNASIPv4, subjectUDNASIPv6, udnIsolationPG) + } + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - ginkgo.By("5. update the ANP by adding two more ingress rules with allow and pass actions; check if AS-es are created + acls are created and added on the port-group") - ingressRules = append(ingressRules, - anpapi.AdminNetworkPolicyIngressRule{ - Name: "allow-traffic-from-hufflepuff-to-gryffindor", - Action: anpapi.AdminNetworkPolicyRuleActionAllow, - From: []anpapi.AdminNetworkPolicyIngressPeer{ - { - Pods: &anpapi.NamespacedPod{ // test different kind of peer expression - NamespaceSelector: metav1.LabelSelector{ - MatchLabels: peerAllowLabel, - }, - PodSelector: metav1.LabelSelector{ - MatchLabels: peerAllowLabel, + ginkgo.By("5. update the ANP by adding two more ingress rules with allow and pass actions; check if AS-es are created + acls are created and added on the port-group") + ingressRules = append(ingressRules, + anpapi.AdminNetworkPolicyIngressRule{ + Name: "allow-traffic-from-hufflepuff-to-gryffindor", + Action: anpapi.AdminNetworkPolicyRuleActionAllow, + From: []anpapi.AdminNetworkPolicyIngressPeer{ + { + Pods: &anpapi.NamespacedPod{ // test different kind of peer expression + NamespaceSelector: metav1.LabelSelector{ + MatchLabels: peerAllowLabel, + }, + PodSelector: metav1.LabelSelector{ + MatchLabels: peerAllowLabel, + }, }, }, }, }, - }, - anpapi.AdminNetworkPolicyIngressRule{ - Name: "pass-traffic-from-ravenclaw-to-gryffindor", - Action: anpapi.AdminNetworkPolicyRuleActionPass, - From: []anpapi.AdminNetworkPolicyIngressPeer{ - { - Namespaces: &metav1.LabelSelector{ - MatchLabels: peerPassLabel, + anpapi.AdminNetworkPolicyIngressRule{ + Name: "pass-traffic-from-ravenclaw-to-gryffindor", + Action: anpapi.AdminNetworkPolicyRuleActionPass, + From: []anpapi.AdminNetworkPolicyIngressPeer{ + { + Namespaces: &metav1.LabelSelector{ + MatchLabels: peerPassLabel, + }, }, }, - }, - Ports: &[]anpapi.AdminNetworkPolicyPort{ // test different ports combination - { - PortNumber: &anpapi.Port{ - Protocol: v1.ProtocolTCP, - Port: int32(12345), + Ports: &[]anpapi.AdminNetworkPolicyPort{ // test different ports combination + { + PortNumber: &anpapi.Port{ + Protocol: v1.ProtocolTCP, + Port: int32(12345), + }, + }, + { + PortNumber: &anpapi.Port{ + Protocol: v1.ProtocolUDP, + Port: int32(1235), + }, + }, + { + PortNumber: &anpapi.Port{ + Protocol: v1.ProtocolSCTP, + Port: int32(1345), + }, }, }, - { - PortNumber: &anpapi.Port{ - Protocol: v1.ProtocolUDP, - Port: int32(1235), + }, + ) + anp = newANPObject("harry-potter", 5, anpSubject, ingressRules, []anpapi.AdminNetworkPolicyEgressRule{}) + anp.ResourceVersion = "3" + anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Update(context.TODO(), anp, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + acls = getACLsForANPRules(anp) + pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, acls, false) + expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} + for _, acl := range acls { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) + peerASIngressRule0v4, peerASIngressRule0v6 = buildANPAddressSets(anp, + 0, []string{t2.podIP}, libovsdbutil.ACLIngress) // podIP should be added to v4 + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v4) + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v6) + peerASIngressRule1v4, peerASIngressRule1v6 := buildANPAddressSets(anp, + 1, []string{}, libovsdbutil.ACLIngress) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v4) + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v6) + peerASIngressRule2v4, peerASIngressRule2v6 := buildANPAddressSets(anp, + 2, []string{}, libovsdbutil.ACLIngress) // address-set will be empty since no pods match it yet + + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule2v4) + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule2v6) + + if isUDNEnabled { + // fill in the blue network's representation for this ANP + udnACLs = getUDNACLsForANPRules(anp, anpSubjectNetwork) + udnPG = getUDNPGForANPSubject(anp.Name, []string{copyT.portUUID}, udnACLs, false, anpSubjectNetwork) + expectedDatabaseState = append(expectedDatabaseState, udnPG) + for _, acl := range udnACLs { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + // peer address-sets will be empty because peers are in default network not BLUE UDN + // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerUDNASIngressRule0v4, peerUDNASIngressRule0v6) + peerUDNASIngressRule1v4, peerUDNASIngressRule1v6 = buildUDNANPAddressSets( + anp, 1, []string{}, libovsdbutil.ACLIngress, anpSubjectNetwork) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerUDNASIngressRule1v4, peerUDNASIngressRule1v6) + peerUDNASIngressRule2v4, peerUDNASIngressRule2v6 = buildUDNANPAddressSets( + anp, 2, []string{}, libovsdbutil.ACLIngress, anpSubjectNetwork) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerUDNASIngressRule2v4, peerUDNASIngressRule2v6) + expectedDatabaseState = append(expectedDatabaseState, getExpectedPodsAndSwitches( + fakeOVN.controller.networkManager.GetNetwork(anpSubjectNetwork), []testPod{copyT}, []string{node1Name})...) + subjectUDNASIPv4, subjectUDNASIPv6 := buildUDNNamespaceAddressSets(anpSubjectNetwork, anpSubjectNamespaceName, []string{copyT.podIP}) + expectedDatabaseState = append(expectedDatabaseState, + subjectUDNASIPv4, subjectUDNASIPv6, udnIsolationPG) + pg.Ports = nil // since subject pod is part of blue UDN not default network + } + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + + ginkgo.By("6. update the ANP by adding three egress rules with deny, allow and pass actions;" + + "check if acls are created and added on the port-group") + egressRules := []anpapi.AdminNetworkPolicyEgressRule{ + { + Name: "deny-traffic-to-slytherin-from-gryffindor", + Action: anpapi.AdminNetworkPolicyRuleActionDeny, + To: []anpapi.AdminNetworkPolicyEgressPeer{ + { + Namespaces: &metav1.LabelSelector{ + MatchLabels: peerDenyLabel, + }, }, }, - { - PortNumber: &anpapi.Port{ - Protocol: v1.ProtocolSCTP, - Port: int32(1345), + }, + { + Name: "allow-traffic-to-hufflepuff-from-gryffindor", + Action: anpapi.AdminNetworkPolicyRuleActionAllow, + To: []anpapi.AdminNetworkPolicyEgressPeer{ + { + Pods: &anpapi.NamespacedPod{ // test different kind of peer expression + NamespaceSelector: metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "house", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"slytherin", "hufflepuff"}, + }, + }, + }, + PodSelector: metav1.LabelSelector{ + MatchLabels: peerAllowLabel, + }, + }, }, }, }, - }, - ) - anp = newANPObject("harry-potter", 5, anpSubject, ingressRules, []anpapi.AdminNetworkPolicyEgressRule{}) - anp.ResourceVersion = "3" - anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Update(context.TODO(), anp, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - acls = getACLsForANPRules(anp) - pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, acls, false) - expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} - for _, acl := range acls { - acl := acl - expectedDatabaseState = append(expectedDatabaseState, acl) - } - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) - peerASIngressRule0v4, peerASIngressRule0v6 = buildANPAddressSets(anp, - 0, []string{t2.podIP}, libovsdbutil.ACLIngress) // podIP should be added to v4 - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v4) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v6) - peerASIngressRule1v4, peerASIngressRule1v6 := buildANPAddressSets(anp, - 1, []string{}, libovsdbutil.ACLIngress) // address-set will be empty since no pods match it yet - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v4) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v6) - peerASIngressRule2v4, peerASIngressRule2v6 := buildANPAddressSets(anp, - 2, []string{}, libovsdbutil.ACLIngress) // address-set will be empty since no pods match it yet - - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule2v4) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule2v6) - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - - ginkgo.By("6. update the ANP by adding three egress rules with deny, allow and pass actions;" + - "check if acls are created and added on the port-group") - egressRules := []anpapi.AdminNetworkPolicyEgressRule{ - { - Name: "deny-traffic-to-slytherin-from-gryffindor", - Action: anpapi.AdminNetworkPolicyRuleActionDeny, - To: []anpapi.AdminNetworkPolicyEgressPeer{ - { - Namespaces: &metav1.LabelSelector{ - MatchLabels: peerDenyLabel, + { + Name: "pass-traffic-to-ravenclaw-from-gryffindor", + Action: anpapi.AdminNetworkPolicyRuleActionPass, + To: []anpapi.AdminNetworkPolicyEgressPeer{ + { + Namespaces: &metav1.LabelSelector{ + MatchLabels: peerPassLabel, + }, }, }, - }, - }, - { - Name: "allow-traffic-to-hufflepuff-from-gryffindor", - Action: anpapi.AdminNetworkPolicyRuleActionAllow, - To: []anpapi.AdminNetworkPolicyEgressPeer{ - { - Pods: &anpapi.NamespacedPod{ // test different kind of peer expression - NamespaceSelector: metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "house", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"slytherin", "hufflepuff"}, - }, - }, + Ports: &[]anpapi.AdminNetworkPolicyPort{ // test different ports combination + { + PortNumber: &anpapi.Port{ + Protocol: v1.ProtocolTCP, + Port: int32(12345), }, - PodSelector: metav1.LabelSelector{ - MatchLabels: peerAllowLabel, + }, + { + PortNumber: &anpapi.Port{ + Protocol: v1.ProtocolUDP, + Port: int32(12345), }, }, - }, - }, - }, - { - Name: "pass-traffic-to-ravenclaw-from-gryffindor", - Action: anpapi.AdminNetworkPolicyRuleActionPass, - To: []anpapi.AdminNetworkPolicyEgressPeer{ - { - Namespaces: &metav1.LabelSelector{ - MatchLabels: peerPassLabel, + { + PortNumber: &anpapi.Port{ + Protocol: v1.ProtocolSCTP, + Port: int32(12345), + }, }, }, }, - Ports: &[]anpapi.AdminNetworkPolicyPort{ // test different ports combination - { - PortNumber: &anpapi.Port{ - Protocol: v1.ProtocolTCP, - Port: int32(12345), - }, - }, - { - PortNumber: &anpapi.Port{ - Protocol: v1.ProtocolUDP, - Port: int32(12345), - }, - }, - { - PortNumber: &anpapi.Port{ - Protocol: v1.ProtocolSCTP, - Port: int32(12345), - }, - }, + } + anp = newANPObject("harry-potter", 5, anpSubject, ingressRules, egressRules) + anp.ResourceVersion = "4" + anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Update(context.TODO(), anp, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + acls = getACLsForANPRules(anp) + pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, acls, false) + expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} + if isUDNEnabled { + // fill in the blue network's representation for this ANP + udnACLs = getUDNACLsForANPRules(anp, anpSubjectNetwork) + udnPG = getUDNPGForANPSubject(anp.Name, []string{copyT.portUUID}, udnACLs, false, anpSubjectNetwork) + expectedDatabaseState = append(expectedDatabaseState, udnPG) + for _, acl := range udnACLs { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + // peer address-sets will be empty because peers are in default network not BLUE UDN + // ingressRule AddressSets - nothing has changed + // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerUDNASIngressRule0v4, peerUDNASIngressRule0v6) + expectedDatabaseState = append(expectedDatabaseState, peerUDNASIngressRule1v4, peerUDNASIngressRule1v6) + expectedDatabaseState = append(expectedDatabaseState, peerUDNASIngressRule2v4, peerUDNASIngressRule2v6) + + // new egress rule addresssets + peerUDNASEgressRule0v4, peerUDNASEgressRule0v6 = buildUDNANPAddressSets( + anp, 0, []string{}, libovsdbutil.ACLEgress, anpSubjectNetwork) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerUDNASEgressRule0v4, peerUDNASEgressRule0v6) + peerUDNASEgressRule1v4, peerUDNASEgressRule1v6 = buildUDNANPAddressSets( + anp, 1, []string{}, libovsdbutil.ACLEgress, anpSubjectNetwork) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerUDNASEgressRule1v4, peerUDNASEgressRule1v6) + peerUDNASEgressRule2v4, peerUDNASEgressRule2v6 = buildUDNANPAddressSets( + anp, 2, []string{}, libovsdbutil.ACLEgress, anpSubjectNetwork) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerUDNASEgressRule2v4, peerUDNASEgressRule2v6) + expectedDatabaseState = append(expectedDatabaseState, getExpectedPodsAndSwitches( + fakeOVN.controller.networkManager.GetNetwork(anpSubjectNetwork), []testPod{copyT}, []string{node1Name})...) + subjectUDNASIPv4, subjectUDNASIPv6 := buildUDNNamespaceAddressSets(anpSubjectNetwork, anpSubjectNamespaceName, []string{copyT.podIP}) + expectedDatabaseState = append(expectedDatabaseState, + subjectUDNASIPv4, subjectUDNASIPv6, udnIsolationPG) + pg.Ports = nil // since subject pod is part of blue UDN not default network + } + for _, acl := range acls { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) + // ingressRule AddressSets - nothing has changed + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v4) + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v6) + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v4) + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v6) + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule2v4) + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule2v6) + + // egressRule AddressSets + peerASEgressRule0v4, peerASEgressRule0v6 := buildANPAddressSets(anp, + 0, []string{t2.podIP}, libovsdbutil.ACLEgress) // podIP should be added to v4 + expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule0v4) + expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule0v6) + peerASEgressRule1v4, peerASEgressRule1v6 := buildANPAddressSets(anp, + 1, []string{}, libovsdbutil.ACLEgress) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule1v4) + expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule1v6) + peerASEgressRule2v4, peerASEgressRule2v6 := buildANPAddressSets(anp, + 2, []string{}, libovsdbutil.ACLEgress) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule2v4) + expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule2v6) + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + + ginkgo.By("7. update the labels of the pod that matches the peer selector; check if IPs are updated in address-sets") + anpPeerPod.ResourceVersion = "2" + anpPeerPod.Labels = peerAllowLabel // pod is now in namespace {house: slytherin} && pod {house: hufflepuff} + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerPod.Namespace).Update(context.TODO(), &anpPeerPod, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + // ingressRule AddressSets - podIP should stay in deny rule's address-set since deny rule matches only on nsSelector which has not changed + // egressRule AddressSets - podIP should now be present in both deny and allow rule's address-sets + expectedDatabaseState[len(expectedDatabaseState)-4].(*nbdb.AddressSet).Addresses = []string{t2.podIP} // podIP should be added to v4 allow address-set + expectedDatabaseState[len(expectedDatabaseState)-6].(*nbdb.AddressSet).Addresses = []string{t2.podIP} // podIP should be added to v4 deny address-set + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + + ginkgo.By("8. update the labels of the namespace that matches the peer selector; check if IPs are updated in address-sets") + anpNamespacePeer.ResourceVersion = "2" + anpNamespacePeer.Labels = peerPassLabel // pod is now in namespace {house: ravenclaw} && pod {house: hufflepuff} + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Update(context.TODO(), &anpNamespacePeer, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + // ingressRule AddressSets - podIP should stay in only pass rule's address-set + expectedDatabaseState[len(expectedDatabaseState)-8].(*nbdb.AddressSet).Addresses = []string{t2.podIP} // podIP should be added to v4 Pass address-set + expectedDatabaseState[len(expectedDatabaseState)-10].(*nbdb.AddressSet).Addresses = []string{} // podIP should be removed from allow rule AS + expectedDatabaseState[len(expectedDatabaseState)-12].(*nbdb.AddressSet).Addresses = []string{} // podIP should be removed from deny rule AS + + // egressRule AddressSets - podIP should stay in only pass rule's address-set + expectedDatabaseState[len(expectedDatabaseState)-2].(*nbdb.AddressSet).Addresses = []string{t2.podIP} // podIP should be added to v4 Pass address-set + expectedDatabaseState[len(expectedDatabaseState)-4].(*nbdb.AddressSet).Addresses = []string{} // podIP should be removed from allow rule AS + expectedDatabaseState[len(expectedDatabaseState)-6].(*nbdb.AddressSet).Addresses = []string{} // podIP should be removed from deny rule AS + + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + + ginkgo.By("9. update the subject of admin network policy so that subject pod stops matching; check if port group is updated") + anpSubject = newANPSubjectObject( + &metav1.LabelSelector{ // test different match expression + MatchLabels: anpLabel, }, - }, - } - anp = newANPObject("harry-potter", 5, anpSubject, ingressRules, egressRules) - anp.ResourceVersion = "4" - anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Update(context.TODO(), anp, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - acls = getACLsForANPRules(anp) - pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, acls, false) - expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} - for _, acl := range acls { - acl := acl - expectedDatabaseState = append(expectedDatabaseState, acl) - } - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) - - // ingressRule AddressSets - nothing has changed - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v4) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v6) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v4) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v6) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule2v4) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule2v6) - - // egressRule AddressSets - peerASEgressRule0v4, peerASEgressRule0v6 := buildANPAddressSets(anp, - 0, []string{t2.podIP}, libovsdbutil.ACLEgress) // podIP should be added to v4 - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule0v4) - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule0v6) - peerASEgressRule1v4, peerASEgressRule1v6 := buildANPAddressSets(anp, - 1, []string{}, libovsdbutil.ACLEgress) // address-set will be empty since no pods match it yet - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule1v4) - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule1v6) - peerASEgressRule2v4, peerASEgressRule2v6 := buildANPAddressSets(anp, - 2, []string{}, libovsdbutil.ACLEgress) // address-set will be empty since no pods match it yet - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule2v4) - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule2v6) - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - - ginkgo.By("7. update the labels of the pod that matches the peer selector; check if IPs are updated in address-sets") - anpPeerPod.ResourceVersion = "2" - anpPeerPod.Labels = peerAllowLabel // pod is now in namespace {house: slytherin} && pod {house: hufflepuff} - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerPod.Namespace).Update(context.TODO(), &anpPeerPod, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - // ingressRule AddressSets - podIP should stay in deny rule's address-set since deny rule matches only on nsSelector which has not changed - // egressRule AddressSets - podIP should now be present in both deny and allow rule's address-sets - expectedDatabaseState[len(expectedDatabaseState)-4].(*nbdb.AddressSet).Addresses = []string{t2.podIP} // podIP should be added to v4 allow address-set - expectedDatabaseState[len(expectedDatabaseState)-6].(*nbdb.AddressSet).Addresses = []string{t2.podIP} // podIP should be added to v4 deny address-set - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - - ginkgo.By("8. update the labels of the namespace that matches the peer selector; check if IPs are updated in address-sets") - anpNamespacePeer.ResourceVersion = "2" - anpNamespacePeer.Labels = peerPassLabel // pod is now in namespace {house: ravenclaw} && pod {house: hufflepuff} - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Update(context.TODO(), &anpNamespacePeer, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - // ingressRule AddressSets - podIP should stay in only pass rule's address-set - expectedDatabaseState[len(expectedDatabaseState)-8].(*nbdb.AddressSet).Addresses = []string{t2.podIP} // podIP should be added to v4 Pass address-set - expectedDatabaseState[len(expectedDatabaseState)-10].(*nbdb.AddressSet).Addresses = []string{} // podIP should be removed from allow rule AS - expectedDatabaseState[len(expectedDatabaseState)-12].(*nbdb.AddressSet).Addresses = []string{} // podIP should be removed from deny rule AS - - // egressRule AddressSets - podIP should stay in only pass rule's address-set - expectedDatabaseState[len(expectedDatabaseState)-2].(*nbdb.AddressSet).Addresses = []string{t2.podIP} // podIP should be added to v4 Pass address-set - expectedDatabaseState[len(expectedDatabaseState)-4].(*nbdb.AddressSet).Addresses = []string{} // podIP should be removed from allow rule AS - expectedDatabaseState[len(expectedDatabaseState)-6].(*nbdb.AddressSet).Addresses = []string{} // podIP should be removed from deny rule AS - - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + &metav1.LabelSelector{ + MatchLabels: peerDenyLabel, // subject pod won't match any more + }, + ) + anp = newANPObject("harry-potter", 5, anpSubject, ingressRules, egressRules) + anp.ResourceVersion = "5" + anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Update(context.TODO(), anp, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + pg = getDefaultPGForANPSubject(anp.Name, nil, acls, false) // no ports in PG + expectedDatabaseState[0] = pg + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + + ginkgo.By("10. update the resource version and labels of the pod that matches the new subject selector; check if port group is updated") + anpSubjectPod.ResourceVersion = "2" + anpSubjectPod.Labels = peerDenyLabel // pod is now in namespace {house: gryffindor} && pod {house: slytherin} + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Update(context.TODO(), &anpSubjectPod, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, acls, false) // port added back to PG + if !isUDNEnabled { + // if pod is part of blue UDN + expectedDatabaseState[0] = pg + } + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - ginkgo.By("9. update the subject of admin network policy so that subject pod stops matching; check if port group is updated") - anpSubject = newANPSubjectObject( - &metav1.LabelSelector{ // test different match expression - MatchLabels: anpLabel, - }, - &metav1.LabelSelector{ - MatchLabels: peerDenyLabel, // subject pod won't match any more - }, - ) - anp = newANPObject("harry-potter", 5, anpSubject, ingressRules, egressRules) - anp.ResourceVersion = "5" - anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Update(context.TODO(), anp, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - pg = getDefaultPGForANPSubject(anp.Name, nil, acls, false) // no ports in PG - expectedDatabaseState[0] = pg - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + ginkgo.By("11. update the labels of the namespace that matches the subject selector to stop matching; check if port group is updated") + anpNamespaceSubject.ResourceVersion = "1" + anpNamespaceSubject.Labels = peerPassLabel // pod is now in namespace {house: ravenclaw} && pod {house: slytherin}; stops matching + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Update(context.TODO(), &anpNamespaceSubject, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + pg = getDefaultPGForANPSubject(anp.Name, nil, acls, false) // no ports in PG + expectedDatabaseState[0] = pg + if isUDNEnabled { + udnPG.Ports = nil + expectedDatabaseState[5] = udnPG + // subject namespace has started matching as peer label selector for pass rule + // update blue network's representation + expectedDatabaseState[20].(*nbdb.AddressSet).Addresses = []string{copyT.podIP} // podIP should be added to v4 Pass address-set + expectedDatabaseState[26].(*nbdb.AddressSet).Addresses = []string{copyT.podIP} // podIP should be added to v4 Pass address-set + } else { + // Coincidentally the subject pod also ends up matching the peer label for Pass rule, so let's add that IP to the AS + expectedDatabaseState[len(expectedDatabaseState)-8].(*nbdb.AddressSet).Addresses = []string{t.podIP, t2.podIP} // podIP should be added to v4 Pass address-set + expectedDatabaseState[len(expectedDatabaseState)-2].(*nbdb.AddressSet).Addresses = []string{t.podIP, t2.podIP} // podIP should be added to v4 Pass address-set + } + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - ginkgo.By("10. update the resource version and labels of the pod that matches the new subject selector; check if port group is updated") - anpSubjectPod.ResourceVersion = "2" - anpSubjectPod.Labels = peerDenyLabel // pod is now in namespace {house: gryffindor} && pod {house: slytherin} - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Update(context.TODO(), &anpSubjectPod, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, acls, false) // port added back to PG - expectedDatabaseState[0] = pg - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + ginkgo.By("12. update the labels of the namespace that matches the subject selector to start matching; check if port group is updated") + anpNamespaceSubject.ResourceVersion = "2" + anpNamespaceSubject.Labels = anpLabel // pod is now in namespace {house: gryffindor} && pod {house: slytherin}; starts matching + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Update(context.TODO(), &anpNamespaceSubject, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, acls, false) + if isUDNEnabled { + udnPG.Ports = []string{copyT.portUUID} + expectedDatabaseState[5] = udnPG + expectedDatabaseState[20].(*nbdb.AddressSet).Addresses = nil // podIP should be removed to v4 Pass address-set + expectedDatabaseState[26].(*nbdb.AddressSet).Addresses = nil // podIP should be removed to v4 Pass address-set + } else { + expectedDatabaseState[0] = pg + // Let us remove the IPs from the peer label AS as it stops matching + expectedDatabaseState[len(expectedDatabaseState)-8].(*nbdb.AddressSet).Addresses = []string{t2.podIP} // podIP should be removed from v4 Pass address-set + expectedDatabaseState[len(expectedDatabaseState)-2].(*nbdb.AddressSet).Addresses = []string{t2.podIP} // podIP should be removed from v4 Pass address-set + } + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + + ginkgo.By("13. update the ANP by changing its priority and deleting 1 ingress rule & 1 egress rule; check if all objects are re-created correctly") + anp.ResourceVersion = "6" + anp.Spec.Priority = 6 + anp.Spec.Ingress = []anpapi.AdminNetworkPolicyIngressRule{ingressRules[1], ingressRules[2]} // deny is gone + anp.Spec.Egress = []anpapi.AdminNetworkPolicyEgressRule{egressRules[0], egressRules[2]} // allow is gone + anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Update(context.TODO(), anp, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) - ginkgo.By("11. update the labels of the namespace that matches the subject selector to stop matching; check if port group is updated") - anpNamespaceSubject.ResourceVersion = "1" - anpNamespaceSubject.Labels = peerPassLabel // pod is now in namespace {house: ravenclaw} && pod {house: slytherin}; stops matching - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Update(context.TODO(), &anpNamespaceSubject, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - pg = getDefaultPGForANPSubject(anp.Name, nil, acls, false) // no ports in PG - expectedDatabaseState[0] = pg - // Coincidentally the subject pod also ends up matching the peer label for Pass rule, so let's add that IP to the AS - expectedDatabaseState[len(expectedDatabaseState)-8].(*nbdb.AddressSet).Addresses = []string{t.podIP, t2.podIP} // podIP should be added to v4 Pass address-set - expectedDatabaseState[len(expectedDatabaseState)-2].(*nbdb.AddressSet).Addresses = []string{t.podIP, t2.podIP} // podIP should be added to v4 Pass address-set - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + newACLs := getACLsForANPRules(anp) + pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, newACLs, false) // only newACLs are hosted + expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} + expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) + for _, acl := range newACLs { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + // ensure address-sets for the rules that were deleted are also gone (index 2 in the list) + // ensure new address-sets have expected IPs + peerASIngressRule0v4, peerASIngressRule0v6 = buildANPAddressSets(anp, + 0, []string{}, libovsdbutil.ACLIngress) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v4) + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v6) + peerASIngressRule1v4, peerASIngressRule1v6 = buildANPAddressSets(anp, + 1, []string{t2.podIP}, libovsdbutil.ACLIngress) // podIP should be added to v4 Pass address-set + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v4) + expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v6) + peerASEgressRule0v4, peerASEgressRule0v6 = buildANPAddressSets(anp, + 0, []string{}, libovsdbutil.ACLEgress) // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule0v4) + expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule0v6) + peerASEgressRule1v4, peerASEgressRule1v6 = buildANPAddressSets(anp, + 1, []string{t2.podIP}, libovsdbutil.ACLEgress) // podIP should be added to v4 Pass address-set + expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule1v4) + expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule1v6) + if isUDNEnabled { + // fill in the blue network's representation for this ANP + udnACLs = getUDNACLsForANPRules(anp, anpSubjectNetwork) + udnPG = getUDNPGForANPSubject(anp.Name, []string{copyT.portUUID}, udnACLs, false, anpSubjectNetwork) + expectedDatabaseState = append(expectedDatabaseState, udnPG) + for _, acl := range udnACLs { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + // peer address-sets will be empty because peers are in default network not BLUE UDN + // ingressRule AddressSets - nothing has changed + // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerUDNASIngressRule0v4, peerUDNASIngressRule0v6) + expectedDatabaseState = append(expectedDatabaseState, peerUDNASIngressRule1v4, peerUDNASIngressRule1v6) + + // new egress rule addresssets + // address-set will be empty since no pods match it yet + expectedDatabaseState = append(expectedDatabaseState, peerUDNASEgressRule0v4, peerUDNASEgressRule0v6) + expectedDatabaseState = append(expectedDatabaseState, peerUDNASEgressRule1v4, peerUDNASEgressRule1v6) + + expectedDatabaseState = append(expectedDatabaseState, getExpectedPodsAndSwitches( + fakeOVN.controller.networkManager.GetNetwork(anpSubjectNetwork), []testPod{copyT}, []string{node1Name})...) + subjectUDNASIPv4, subjectUDNASIPv6 := buildUDNNamespaceAddressSets(anpSubjectNetwork, anpSubjectNamespaceName, []string{copyT.podIP}) + expectedDatabaseState = append(expectedDatabaseState, + subjectUDNASIPv4, subjectUDNASIPv6, udnIsolationPG) + pg.Ports = nil // since subject pod is part of blue UDN not default network + } + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - ginkgo.By("12. update the labels of the namespace that matches the subject selector to start matching; check if port group is updated") - anpNamespaceSubject.ResourceVersion = "2" - anpNamespaceSubject.Labels = anpLabel // pod is now in namespace {house: gryffindor} && pod {house: slytherin}; starts matching - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Update(context.TODO(), &anpNamespaceSubject, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, acls, false) // no ports in PG - expectedDatabaseState[0] = pg - // Let us remove the IPs from the peer label AS as it stops matching - expectedDatabaseState[len(expectedDatabaseState)-8].(*nbdb.AddressSet).Addresses = []string{t2.podIP} // podIP should be added to v4 Pass address-set - expectedDatabaseState[len(expectedDatabaseState)-2].(*nbdb.AddressSet).Addresses = []string{t2.podIP} // podIP should be added to v4 Pass address-set - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + ginkgo.By("14. delete pod matching peer selector; check if IPs are updated in address-sets") + anpPeerPod.ResourceVersion = "3" + err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerPod.Namespace).Delete(context.TODO(), anpPeerPod.Name, metav1.DeleteOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + expectedDatabaseState[3].(*nbdb.AddressSet).Addresses = nil // pod is gone from peer namespace address-set + expectedDatabaseState[18].(*nbdb.AddressSet).Addresses = []string{} // pod is gone from peer namespace address-set + expectedDatabaseState[22].(*nbdb.AddressSet).Addresses = []string{} // pod is gone from peer namespace address-set + expectedDatabaseState[7].(*nbdb.LogicalSwitch).Ports = []string{t.portUUID} // remove peer pod's LSP from switch + expectedDatabaseState = append(expectedDatabaseState[:6], expectedDatabaseState[7:]...) // remove peer pod's LSP + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + + ginkgo.By("15. update the subject pod to go into completed state; check if port group and address-set is updated") + anpSubjectPod.ResourceVersion = "3" + anpSubjectPod.Status.Phase = v1.PodSucceeded + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Update(context.TODO(), &anpSubjectPod, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + pg = getDefaultPGForANPSubject(anp.Name, nil, newACLs, false) // no ports in PG + expectedDatabaseState[0] = pg + expectedDatabaseState[1].(*nbdb.AddressSet).Addresses = nil + expectedDatabaseState[6].(*nbdb.LogicalSwitch).Ports = nil // remove subject pod's LSP from switch + expectedDatabaseState = append(expectedDatabaseState[:5], expectedDatabaseState[6:]...) // remove subject pod's LSP + if isUDNEnabled { + udnPG.Ports = nil + expectedDatabaseState[22] = udnPG // remove subject pod's LSP from ANP PG + expectedDatabaseState[40].(*nbdb.LogicalSwitch).Ports = nil // remove subject pod's LSP from switch + expectedDatabaseState = append(expectedDatabaseState[:39], expectedDatabaseState[40:]...) // remove subject pod's LSP + udnIsolationPG.Ports = nil + expectedDatabaseState[len(expectedDatabaseState)-1] = udnIsolationPG // remove subject pod's LSP + expectedDatabaseState[len(expectedDatabaseState)-3].(*nbdb.AddressSet).Addresses = nil // remove subject pod's IP from namespace address-set + } + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Delete(context.TODO(), anpSubjectPod.Name, metav1.DeleteOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) - ginkgo.By("13. update the ANP by changing its priority and deleting 1 ingress rule & 1 egress rule; check if all objects are re-created correctly") - anp.ResourceVersion = "6" - anp.Spec.Priority = 6 - anp.Spec.Ingress = []anpapi.AdminNetworkPolicyIngressRule{ingressRules[1], ingressRules[2]} // deny is gone - anp.Spec.Egress = []anpapi.AdminNetworkPolicyEgressRule{egressRules[0], egressRules[2]} // allow is gone - anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Update(context.TODO(), anp, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) + ginkgo.By("16. delete the subject and peer selected namespaces; check if port group and address-set's are updated") + // create a new pod in subject and peer namespaces so that we can check namespace deletion properly + anpSubjectPod = *newPodWithLabels(anpSubjectNamespaceName, anpSubjectPodName, node1Name, "10.128.1.5", peerDenyLabel) + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Create(context.TODO(), &anpSubjectPod, metav1.CreateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + // The pod takes some time to get created so the first add pod event for ANP does nothing as it waits for LSP to be created - this triggers a retry. + // Next when the pod is scheduled and IP is allocated and LSP is created we get an update event. + // When ANP controller receives the pod update event, it will not trigger an update in unit testing since ResourceVersion and pod.status.Running + // is not updated by the test, hence we need to trigger a manual update in the unit test framework that is accompanied by label update. + // NOTE: This is not an issue in real deployments with actual kapi server; its just a potential flake in the UT framework that we need to protect against. + time.Sleep(time.Second) + anpSubjectPod.ResourceVersion = "500" + // - this is to trigger an actual update technically speaking the pod already matches the namespace of the subject + anpSubjectPod.Labels["rv"] = "resourceVersionUTHack" + if isUDNEnabled { + // unforunately we rely on pod annotations to determine the podIPs for UDN pods since pod status doesn't have them + // and hence we need to patch the pod with the annotation of the P-UDN to simulate that + anpSubjectPod.Annotations = map[string]string{ + "k8s.ovn.org/pod-networks": fmt.Sprintf(`{%q:{"ip_addresses":["%s/24"],"mac_address":"%s"}}`, + util.GetNADName(anpSubjectNamespaceName, nadName), "100.128.0.4", + util.IPAddrToHWAddr(ovntest.MustParseIP("100.128.0.4")).String()), + } + } + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Update(context.TODO(), &anpSubjectPod, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + t.podIP = "10.128.1.5" + t.podMAC = "0a:58:0a:80:01:05" + copyT.podIP = "100.128.0.4" + copyT.podMAC = "0a:58:64:80:00:04" + pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, newACLs, false) + // deleting the namespace doesn't mean pod will get deleted in unit tests + subjectNSASIPv4, subjectNSASIPv6 = buildNamespaceAddressSets(anpSubjectNamespaceName, []string{t.podIP}) + expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} + for _, acl := range newACLs { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t}, []string{node1Name})...) + expectedDatabaseState = append(expectedDatabaseState, []libovsdbtest.TestData{peerASIngressRule0v4, peerASIngressRule0v6, peerASIngressRule1v4, + peerASIngressRule1v6, peerASEgressRule0v4, peerASEgressRule0v6, peerASEgressRule1v4, peerASEgressRule1v6}...) + if isUDNEnabled { + // fill in the blue network's representation for this ANP + udnACLs = getUDNACLsForANPRules(anp, anpSubjectNetwork) + udnPG = getUDNPGForANPSubject(anp.Name, []string{copyT.portUUID}, udnACLs, false, anpSubjectNetwork) + expectedDatabaseState = append(expectedDatabaseState, udnPG) + for _, acl := range udnACLs { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + expectedDatabaseState = append(expectedDatabaseState, []libovsdbtest.TestData{peerUDNASIngressRule0v4, peerUDNASIngressRule0v6, + peerUDNASIngressRule1v4, peerUDNASIngressRule1v6, peerUDNASEgressRule0v4, peerUDNASEgressRule0v6, + peerUDNASEgressRule1v4, peerUDNASEgressRule1v6}...) + + expectedDatabaseState = append(expectedDatabaseState, getExpectedPodsAndSwitches( + fakeOVN.controller.networkManager.GetNetwork(anpSubjectNetwork), []testPod{copyT}, []string{node1Name})...) + subjectUDNASIPv4, subjectUDNASIPv6 := buildUDNNamespaceAddressSets(anpSubjectNetwork, anpSubjectNamespaceName, []string{copyT.podIP}) + udnIsolationPG.Ports = []string{t.portUUID} + expectedDatabaseState = append(expectedDatabaseState, + subjectUDNASIPv4, subjectUDNASIPv6, udnIsolationPG) + pg.Ports = nil // since subject pod is part of blue UDN not default network + } + gomega.Eventually(fakeOVN.nbClient, "3s").Should(libovsdbtest.HaveData(expectedDatabaseState)) + anpPeerPod = *newPodWithLabels(anpPeerNamespaceName, anpPeerPodName, node1Name, "10.128.1.6", peerAllowLabel) + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerPod.Namespace).Create(context.TODO(), &anpPeerPod, metav1.CreateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + // The pod takes some time to get created so the first add pod event for ANP does nothing as it waits for LSP to be created - this triggers a retry. + // Next when the pod is scheduled and IP is allocated and LSP is created we get an update event. + // When ANP controller receives the pod update event, it will not trigger an update in unit testing since ResourceVersion and pod.status.Running + // is not updated by the test, hence we need to trigger a manual update in the unit test framework that is accompanied by label update. + // NOTE: This is not an issue in real deployments with actual kapi server; its just a potential flake in the UT framework that we need to protect against. + time.Sleep(time.Second) + anpPeerPod.ResourceVersion = "500" + // - this is to trigger an actual update technically speaking the pod already matches the namespace of the subject + anpPeerPod.Labels["rv"] = "resourceVersionUTHack" + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerPod.Namespace).Update(context.TODO(), &anpPeerPod, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + t2.podIP = "10.128.1.6" + t2.podMAC = "0a:58:0a:80:01:06" + peerNSASIPv4, peerNSASIPv6 = buildNamespaceAddressSets(anpPeerNamespaceName, []string{t2.podIP}) + expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} + for _, acl := range newACLs { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) + peerASIngressRule1v4, peerASIngressRule1v6 = buildANPAddressSets(anp, + 1, []string{t2.podIP}, libovsdbutil.ACLIngress) + peerASEgressRule1v4, peerASEgressRule1v6 = buildANPAddressSets(anp, + 1, []string{t2.podIP}, libovsdbutil.ACLEgress) + expectedDatabaseState = append(expectedDatabaseState, []libovsdbtest.TestData{peerASIngressRule0v4, peerASIngressRule0v6, peerASIngressRule1v4, + peerASIngressRule1v6, peerASEgressRule0v4, peerASEgressRule0v6, peerASEgressRule1v4, peerASEgressRule1v6}...) + if isUDNEnabled { + // fill in the blue network's representation for this ANP + udnACLs = getUDNACLsForANPRules(anp, anpSubjectNetwork) + udnPG = getUDNPGForANPSubject(anp.Name, []string{copyT.portUUID}, udnACLs, false, anpSubjectNetwork) + expectedDatabaseState = append(expectedDatabaseState, udnPG) + for _, acl := range udnACLs { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + expectedDatabaseState = append(expectedDatabaseState, []libovsdbtest.TestData{peerUDNASIngressRule0v4, peerUDNASIngressRule0v6, + peerUDNASIngressRule1v4, peerUDNASIngressRule1v6, peerUDNASEgressRule0v4, peerUDNASEgressRule0v6, + peerUDNASEgressRule1v4, peerUDNASEgressRule1v6}...) + + expectedDatabaseState = append(expectedDatabaseState, getExpectedPodsAndSwitches( + fakeOVN.controller.networkManager.GetNetwork(anpSubjectNetwork), []testPod{copyT}, []string{node1Name})...) + subjectUDNASIPv4, subjectUDNASIPv6 := buildUDNNamespaceAddressSets(anpSubjectNetwork, anpSubjectNamespaceName, []string{copyT.podIP}) + udnIsolationPG.Ports = []string{t.portUUID} + expectedDatabaseState = append(expectedDatabaseState, + subjectUDNASIPv4, subjectUDNASIPv6, udnIsolationPG) + pg.Ports = nil // since subject pod is part of blue UDN not default network + } + gomega.Eventually(fakeOVN.nbClient, "3s").Should(libovsdbtest.HaveData(expectedDatabaseState)) - newACLs := getACLsForANPRules(anp) - pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, newACLs, false) // only newACLs are hosted - expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} - for _, acl := range newACLs { - acl := acl - expectedDatabaseState = append(expectedDatabaseState, acl) - } - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) - // ensure address-sets for the rules that were deleted are also gone (index 2 in the list) - // ensure new address-sets have expected IPs - peerASIngressRule0v4, peerASIngressRule0v6 = buildANPAddressSets(anp, - 0, []string{}, libovsdbutil.ACLIngress) // address-set will be empty since no pods match it yet - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v4) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v6) - peerASIngressRule1v4, peerASIngressRule1v6 = buildANPAddressSets(anp, - 1, []string{t2.podIP}, libovsdbutil.ACLIngress) // podIP should be added to v4 Pass address-set - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v4) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v6) - peerASEgressRule0v4, peerASEgressRule0v6 = buildANPAddressSets(anp, - 0, []string{}, libovsdbutil.ACLEgress) // address-set will be empty since no pods match it yet - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule0v4) - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule0v6) - peerASEgressRule1v4, peerASEgressRule1v6 = buildANPAddressSets(anp, - 1, []string{t2.podIP}, libovsdbutil.ACLEgress) // podIP should be added to v4 Pass address-set - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule1v4) - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule1v6) - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + // now delete the namespaces + anpNamespaceSubject.ResourceVersion = "3" + err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Delete(context.TODO(), anpNamespaceSubject.Name, metav1.DeleteOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + anpNamespacePeer.ResourceVersion = "3" + err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Delete(context.TODO(), anpNamespacePeer.Name, metav1.DeleteOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + // network representation for the ANP is fully gone - only thing remaining is the pod rep given in UTs deleting namespace doesn't delete pods + expectedDatabaseState = []libovsdbtest.TestData{} // port group should be deleted + expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) + if isUDNEnabled { + expectedDatabaseState = append(expectedDatabaseState, getExpectedPodsAndSwitches( + fakeOVN.controller.networkManager.GetNetwork(anpSubjectNetwork), []testPod{copyT}, []string{node1Name})...) + expectedDatabaseState = append(expectedDatabaseState, udnIsolationPG) + // in unit tests since pods are left behind, we will still have those address-sets for the leftover peers + // stale Address-sets for namespace in P-UDN network + // TODO: Check with Peri; this looks like a bug + subjectUDNASIPv4, subjectUDNASIPv6 := buildUDNNamespaceAddressSets(anpSubjectNetwork, anpSubjectNamespaceName, []string{copyT.podIP}) + expectedDatabaseState = append(expectedDatabaseState, subjectUDNASIPv4, subjectUDNASIPv6) + } + gomega.Eventually(fakeOVN.nbClient, "25s").Should(libovsdbtest.HaveData(expectedDatabaseState)) + + ginkgo.By("17. re-add the namespaces to re-simulate ANP topology for the default network") + // NOTE (tssurya): Prior to UDNs support, when all namespaces that are matching the ANP either in the subject + // OR peers get deleted, it was treated as a peer/subject deletion and hence only ports from portgroup OR IPs from address/sets + // were deleted while retaining the port-group/address-sets themselves + // However now with implementing the UDNs, the semantics have changed internally - if all namespaces matching the ANP + // are deleted then its taken as that network representation for the ANP got deleted - implementation/cache wise + // Hence if all namespaces are gone matching an ANP that belong to a specific network; its counted as the network + // got deleted and we remove all port-group/address-sets associated with that network for this ANP + // The alternative here is to start watching for network addition/deletion which is just an overhead here not worth the effort + // Hence we have this extra step to add back the peer/subject networks so that we can test the next two steps + // now delete the namespaces + anpNamespaceSubject.ResourceVersion = "4" + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Create(context.TODO(), &anpNamespaceSubject, metav1.CreateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + anpNamespacePeer.ResourceVersion = "4" + _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Create(context.TODO(), &anpNamespacePeer, metav1.CreateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} + for _, acl := range newACLs { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + if isUDNEnabled { + // fill in the blue network's representation for this ANP + udnACLs = getUDNACLsForANPRules(anp, anpSubjectNetwork) + udnPG = getUDNPGForANPSubject(anp.Name, []string{copyT.portUUID}, udnACLs, false, anpSubjectNetwork) + expectedDatabaseState = append(expectedDatabaseState, udnPG) + for _, acl := range udnACLs { + acl := acl + expectedDatabaseState = append(expectedDatabaseState, acl) + } + expectedDatabaseState = append(expectedDatabaseState, getExpectedPodsAndSwitches( + fakeOVN.controller.networkManager.GetNetwork(anpSubjectNetwork), []testPod{copyT}, []string{node1Name})...) + expectedDatabaseState = append(expectedDatabaseState, udnIsolationPG) + // in unit tests since pods are left behind, we will still have those address-sets for the leftover peers + // stale Address-sets for namespace in P-UDN network + // TODO: Check with Peri; this looks like a bug + subjectUDNASIPv4, subjectUDNASIPv6 := buildUDNNamespaceAddressSets(anpSubjectNetwork, anpSubjectNamespaceName, []string{copyT.podIP}) + expectedDatabaseState = append(expectedDatabaseState, subjectUDNASIPv4, subjectUDNASIPv6) + expectedDatabaseState = append(expectedDatabaseState, []libovsdbtest.TestData{peerUDNASIngressRule0v4, peerUDNASIngressRule0v6, + peerUDNASIngressRule1v4, peerUDNASIngressRule1v6, peerUDNASEgressRule0v4, peerUDNASEgressRule0v6, peerUDNASEgressRule1v4, + peerUDNASEgressRule1v6}...) + } + peerASIngressRule1v4, peerASIngressRule1v6 = buildANPAddressSets(anp, + 1, []string{t2.podIP}, libovsdbutil.ACLIngress) + peerASEgressRule1v4, peerASEgressRule1v6 = buildANPAddressSets(anp, + 1, []string{t2.podIP}, libovsdbutil.ACLEgress) + expectedDatabaseState = append(expectedDatabaseState, []libovsdbtest.TestData{peerASIngressRule0v4, peerASIngressRule0v6, + peerASIngressRule1v4, peerASIngressRule1v6, peerASEgressRule0v4, peerASEgressRule0v6, peerASEgressRule1v4, peerASEgressRule1v6}...) + expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) + gomega.Eventually(fakeOVN.nbClient, "3s").Should(libovsdbtest.HaveData(expectedDatabaseState)) + + ginkgo.By("18. delete all the pods matching the ANP and check if port group and address-set's are updated") + err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectNamespaceName).Delete(context.TODO(), anpSubjectPodName, metav1.DeleteOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerNamespaceName).Delete(context.TODO(), anpPeerPodName, metav1.DeleteOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + expectedDatabaseState[0].(*nbdb.PortGroup).Ports = nil // no ports in PG + expectedDatabaseState = expectedDatabaseState[:len(expectedDatabaseState)-3] // no LSPs in PG + // Let us remove the IPs from the peer label AS as it stops matching + expectedDatabaseState[1].(*nbdb.AddressSet).Addresses = []string{} // subject podIP should be removed from namespace address-set + expectedDatabaseState[2].(*nbdb.AddressSet).Addresses = []string{} // subject podIP should be removed from namespace address-set + expectedDatabaseState[3].(*nbdb.AddressSet).Addresses = []string{} // peer podIP should be removed from namespace address-set + expectedDatabaseState[4].(*nbdb.AddressSet).Addresses = []string{} // peer podIP should be removed from namespace-addresset + expectedDatabaseState[len(expectedDatabaseState)-1].(*nbdb.AddressSet).Addresses = []string{} // peer podIP should be removed from ANP rule address-set + expectedDatabaseState[len(expectedDatabaseState)-2].(*nbdb.AddressSet).Addresses = []string{} // peer podIP should be removed from ANP rule address-set + expectedDatabaseState[len(expectedDatabaseState)-5].(*nbdb.AddressSet).Addresses = []string{} // peer podIP should be removed from ANP rule address-set + expectedDatabaseState[len(expectedDatabaseState)-6].(*nbdb.AddressSet).Addresses = []string{} // peer podIP should be removed from ANP rule address-set + expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{}, []string{node1Name})...) + if isUDNEnabled { + // reset subject represention on blue P-UDN + expectedDatabaseState[25].(*nbdb.AddressSet).Addresses = []string{} // subject podIP should be removed from namespace address-set + expectedDatabaseState[13].(*nbdb.PortGroup).Ports = nil + expectedDatabaseState[24].(*nbdb.PortGroup).Ports = nil + expectedDatabaseState[23].(*nbdb.LogicalSwitch).Ports = nil + expectedDatabaseState = append(expectedDatabaseState[:22], expectedDatabaseState[23:]...) // remove subject pod's LSP + } + gomega.Eventually(fakeOVN.nbClient, "3s").Should(libovsdbtest.HaveData(expectedDatabaseState)) - ginkgo.By("14. delete pod matching peer selector; check if IPs are updated in address-sets") - anpPeerPod.ResourceVersion = "3" - err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerPod.Namespace).Delete(context.TODO(), anpPeerPod.Name, metav1.DeleteOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, newACLs, false) // only newACLs are hosted - peerNSASIPv4, peerNSASIPv6 = buildNamespaceAddressSets(anpPeerNamespaceName, []string{}) // pod is gone from peer namespace address-set - expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} - for _, acl := range newACLs { - acl := acl - expectedDatabaseState = append(expectedDatabaseState, acl) - } - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t}, []string{node1Name})...) - peerASIngressRule0v4, peerASIngressRule0v6 = buildANPAddressSets(anp, - 0, []string{}, libovsdbutil.ACLIngress) // address-set will be empty since no pods match it yet - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v4) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule0v6) - peerASIngressRule1v4, peerASIngressRule1v6 = buildANPAddressSets(anp, - 1, []string{}, libovsdbutil.ACLIngress) // address-set will be empty since no pods match it yet - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v4) - expectedDatabaseState = append(expectedDatabaseState, peerASIngressRule1v6) - peerASEgressRule0v4, peerASEgressRule0v6 = buildANPAddressSets(anp, - 0, []string{}, libovsdbutil.ACLEgress) // address-set will be empty since no pods match it yet - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule0v4) - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule0v6) - peerASEgressRule1v4, peerASEgressRule1v6 = buildANPAddressSets(anp, - 1, []string{}, libovsdbutil.ACLEgress) // address-set will be empty since no pods match it yet - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule1v4) - expectedDatabaseState = append(expectedDatabaseState, peerASEgressRule1v6) - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) + ginkgo.By("19. update the ANP by deleting all rules; check if all objects are deleted correctly") + anp.Spec.Ingress = []anpapi.AdminNetworkPolicyIngressRule{} + anp.Spec.Egress = []anpapi.AdminNetworkPolicyEgressRule{} + anp.ResourceVersion = "7" + anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Update(context.TODO(), anp, metav1.UpdateOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + pg = getDefaultPGForANPSubject(anp.Name, nil, nil, false) // no ports and acls in PG + subjectNSASIPv4, subjectNSASIPv6 = buildNamespaceAddressSets(anpSubjectNamespaceName, []string{}) + peerNSASIPv4, peerNSASIPv6 = buildNamespaceAddressSets(anpPeerNamespaceName, []string{}) + expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} + expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{}, []string{node1Name})...) + if isUDNEnabled { + udnPG = getUDNPGForANPSubject(anp.Name, nil, nil, false, anpSubjectNetwork) // no ports and acls in PG + subjectUDNASIPv4, subjectUDNASIPv6 := buildUDNNamespaceAddressSets(anpSubjectNetwork, anpSubjectNamespaceName, []string{}) + expectedDatabaseState = append(expectedDatabaseState, udnPG, subjectUDNASIPv4, subjectUDNASIPv6) + expectedDatabaseState = append(expectedDatabaseState, getExpectedPodsAndSwitches( + fakeOVN.controller.networkManager.GetNetwork(anpSubjectNetwork), []testPod{}, []string{node1Name})...) + expectedDatabaseState = append(expectedDatabaseState, udnIsolationPG) + // given all peers are gone in default network, there won't be ANP rep for default network since there is no matching namespaces + // TODO: Double check this logic because peer namespace still exists... + expectedDatabaseState = expectedDatabaseState[1:] + } + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - ginkgo.By("15. update the subject pod to go into completed state; check if port group and address-set is updated") - anpSubjectPod.ResourceVersion = "3" - anpSubjectPod.Status.Phase = v1.PodSucceeded - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Update(context.TODO(), &anpSubjectPod, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - pg = getDefaultPGForANPSubject(anp.Name, nil, newACLs, false) // no ports in PG - subjectNSASIPv4, subjectNSASIPv6 = buildNamespaceAddressSets(anpSubjectNamespaceName, []string{}) - expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} - for _, acl := range newACLs { - acl := acl - expectedDatabaseState = append(expectedDatabaseState, acl) - } - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{}, []string{node1Name})...) - expectedDatabaseState = append(expectedDatabaseState, []libovsdbtest.TestData{peerASIngressRule0v4, peerASIngressRule0v6, peerASIngressRule1v4, - peerASIngressRule1v6, peerASEgressRule0v4, peerASEgressRule0v6, peerASEgressRule1v4, peerASEgressRule1v6}...) - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Delete(context.TODO(), anpSubjectPod.Name, metav1.DeleteOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) + ginkgo.By("20. delete the ANP; check if all objects are deleted correctly") + anp.ResourceVersion = "8" + err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Delete(context.TODO(), anp.Name, metav1.DeleteOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + expectedDatabaseState = []libovsdbtest.TestData{subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} // port group should be deleted + expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{}, []string{node1Name})...) + if isUDNEnabled { + expectedDatabaseState = append(expectedDatabaseState, getExpectedPodsAndSwitches( + fakeOVN.controller.networkManager.GetNetwork(anpSubjectNetwork), []testPod{}, []string{node1Name})...) + subjectUDNASIPv4, subjectUDNASIPv6 := buildUDNNamespaceAddressSets(anpSubjectNetwork, anpSubjectNamespaceName, []string{}) + expectedDatabaseState = append(expectedDatabaseState, udnIsolationPG, subjectUDNASIPv4, subjectUDNASIPv6) + } + gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - ginkgo.By("16. delete the subject and peer selected namespaces; check if port group and address-set's are updated") - // create a new pod in subject and peer namespaces so that we can check namespace deletion properly - anpSubjectPod = *newPodWithLabels(anpSubjectNamespaceName, anpSubjectPodName, node1Name, "10.128.1.5", peerDenyLabel) - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Create(context.TODO(), &anpSubjectPod, metav1.CreateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - // The pod takes some time to get created so the first add pod event for ANP does nothing as it waits for LSP to be created - this triggers a retry. - // Next when the pod is scheduled and IP is allocated and LSP is created we get an update event. - // When ANP controller receives the pod update event, it will not trigger an update in unit testing since ResourceVersion and pod.status.Running - // is not updated by the test, hence we need to trigger a manual update in the unit test framework that is accompanied by label update. - // NOTE: This is not an issue in real deployments with actual kapi server; its just a potential flake in the UT framework that we need to protect against. - time.Sleep(time.Second) - anpSubjectPod.ResourceVersion = "500" - // - this is to trigger an actual update technically speaking the pod already matches the namespace of the subject - anpSubjectPod.Labels["rv"] = "resourceVersionUTHack" - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectPod.Namespace).Update(context.TODO(), &anpSubjectPod, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - t.podIP = "10.128.1.5" - t.podMAC = "0a:58:0a:80:01:05" - pg = getDefaultPGForANPSubject(anp.Name, []string{t.portUUID}, newACLs, false) - subjectNSASIPv4, subjectNSASIPv6 = buildNamespaceAddressSets(anpSubjectNamespaceName, []string{t.podIP}) - expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} - for _, acl := range newACLs { - acl := acl - expectedDatabaseState = append(expectedDatabaseState, acl) - } - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t}, []string{node1Name})...) - expectedDatabaseState = append(expectedDatabaseState, []libovsdbtest.TestData{peerASIngressRule0v4, peerASIngressRule0v6, peerASIngressRule1v4, - peerASIngressRule1v6, peerASEgressRule0v4, peerASEgressRule0v6, peerASEgressRule1v4, peerASEgressRule1v6}...) - gomega.Eventually(fakeOVN.nbClient, "3s").Should(libovsdbtest.HaveData(expectedDatabaseState)) - anpPeerPod = *newPodWithLabels(anpPeerNamespaceName, anpPeerPodName, node1Name, "10.128.1.6", peerAllowLabel) - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerPod.Namespace).Create(context.TODO(), &anpPeerPod, metav1.CreateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - // The pod takes some time to get created so the first add pod event for ANP does nothing as it waits for LSP to be created - this triggers a retry. - // Next when the pod is scheduled and IP is allocated and LSP is created we get an update event. - // When ANP controller receives the pod update event, it will not trigger an update in unit testing since ResourceVersion and pod.status.Running - // is not updated by the test, hence we need to trigger a manual update in the unit test framework that is accompanied by label update. - // NOTE: This is not an issue in real deployments with actual kapi server; its just a potential flake in the UT framework that we need to protect against. - time.Sleep(time.Second) - anpPeerPod.ResourceVersion = "500" - // - this is to trigger an actual update technically speaking the pod already matches the namespace of the subject - anpPeerPod.Labels["rv"] = "resourceVersionUTHack" - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerPod.Namespace).Update(context.TODO(), &anpPeerPod, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - t2.podIP = "10.128.1.6" - t2.podMAC = "0a:58:0a:80:01:06" - peerNSASIPv4, peerNSASIPv6 = buildNamespaceAddressSets(anpPeerNamespaceName, []string{t2.podIP}) - expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} - for _, acl := range newACLs { - acl := acl - expectedDatabaseState = append(expectedDatabaseState, acl) + return nil } - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) - peerASIngressRule1v4, peerASIngressRule1v6 = buildANPAddressSets(anp, - 1, []string{t2.podIP}, libovsdbutil.ACLIngress) - peerASEgressRule1v4, peerASEgressRule1v6 = buildANPAddressSets(anp, - 1, []string{t2.podIP}, libovsdbutil.ACLEgress) - expectedDatabaseState = append(expectedDatabaseState, []libovsdbtest.TestData{peerASIngressRule0v4, peerASIngressRule0v6, peerASIngressRule1v4, - peerASIngressRule1v6, peerASEgressRule0v4, peerASEgressRule0v6, peerASEgressRule1v4, peerASEgressRule1v6}...) - gomega.Eventually(fakeOVN.nbClient, "3s").Should(libovsdbtest.HaveData(expectedDatabaseState)) - - // now delete the namespaces - anpNamespaceSubject.ResourceVersion = "3" - err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Delete(context.TODO(), anpNamespaceSubject.Name, metav1.DeleteOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - anpNamespacePeer.ResourceVersion = "3" - err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Delete(context.TODO(), anpNamespacePeer.Name, metav1.DeleteOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - // network representation for the ANP is fully gone - only thing remaining is the pod rep given in UTs deleting namespace doesn't delete pods - expectedDatabaseState = []libovsdbtest.TestData{} // port group should be deleted - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) - gomega.Eventually(fakeOVN.nbClient, "25s").Should(libovsdbtest.HaveData(expectedDatabaseState)) - - ginkgo.By("17. re-add the namespaces to re-simulate ANP topology for the default network") - // NOTE (tssurya): Prior to UDNs support, when all namespaces that are matching the ANP either in the subject - // OR peers get deleted, it was treated as a peer/subject deletion and hence only ports from portgroup OR IPs from address/sets - // were deleted while retaining the port-group/address-sets themselves - // However now with implementing the UDNs, the semantics have changed internally - if all namespaces matching the ANP - // are deleted then its taken as that network representation for the ANP got deleted - implementation/cache wise - // Hence if all namespaces are gone matching an ANP that belong to a specific network; its counted as the network - // got deleted and we remove all port-group/address-sets associated with that network for this ANP - // The alternative here is to start watching for network addition/deletion which is just an overhead here not worth the effort - // Hence we have this extra step to add back the peer/subject networks so that we can test the next two steps - // now delete the namespaces - anpNamespaceSubject.ResourceVersion = "4" - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Create(context.TODO(), &anpNamespaceSubject, metav1.CreateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - anpNamespacePeer.ResourceVersion = "4" - _, err = fakeOVN.fakeClient.KubeClient.CoreV1().Namespaces().Create(context.TODO(), &anpNamespacePeer, metav1.CreateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} - for _, acl := range newACLs { - acl := acl - expectedDatabaseState = append(expectedDatabaseState, acl) - } - peerASIngressRule1v4, peerASIngressRule1v6 = buildANPAddressSets(anp, - 1, []string{t2.podIP}, libovsdbutil.ACLIngress) - peerASEgressRule1v4, peerASEgressRule1v6 = buildANPAddressSets(anp, - 1, []string{t2.podIP}, libovsdbutil.ACLEgress) - expectedDatabaseState = append(expectedDatabaseState, []libovsdbtest.TestData{peerASIngressRule0v4, peerASIngressRule0v6, peerASIngressRule1v4, - peerASIngressRule1v6, peerASEgressRule0v4, peerASEgressRule0v6, peerASEgressRule1v4, peerASEgressRule1v6}...) - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{t, t2}, []string{node1Name})...) - gomega.Eventually(fakeOVN.nbClient, "3s").Should(libovsdbtest.HaveData(expectedDatabaseState)) - - ginkgo.By("18. delete all the pods matching the ANP and check if port group and address-set's are updated") - err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpSubjectNamespaceName).Delete(context.TODO(), anpSubjectPodName, metav1.DeleteOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - err = fakeOVN.fakeClient.KubeClient.CoreV1().Pods(anpPeerNamespaceName).Delete(context.TODO(), anpPeerPodName, metav1.DeleteOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - expectedDatabaseState[0].(*nbdb.PortGroup).Ports = nil // no ports in PG - expectedDatabaseState = expectedDatabaseState[:len(expectedDatabaseState)-3] // no LSPs in PG - // Let us remove the IPs from the peer label AS as it stops matching - expectedDatabaseState[1].(*nbdb.AddressSet).Addresses = []string{} // subject podIP should be removed from namespace address-set - expectedDatabaseState[2].(*nbdb.AddressSet).Addresses = []string{} // subject podIP should be removed from namespace address-set - expectedDatabaseState[3].(*nbdb.AddressSet).Addresses = []string{} // peer podIP should be removed from namespace address-set - expectedDatabaseState[4].(*nbdb.AddressSet).Addresses = []string{} // peer podIP should be removed from namespace-addresset - expectedDatabaseState[len(expectedDatabaseState)-1].(*nbdb.AddressSet).Addresses = []string{} // peer podIP should be removed from ANP rule address-set - expectedDatabaseState[len(expectedDatabaseState)-2].(*nbdb.AddressSet).Addresses = []string{} // peer podIP should be removed from ANP rule address-set - expectedDatabaseState[len(expectedDatabaseState)-5].(*nbdb.AddressSet).Addresses = []string{} // peer podIP should be removed from ANP rule address-set - expectedDatabaseState[len(expectedDatabaseState)-6].(*nbdb.AddressSet).Addresses = []string{} // peer podIP should be removed from ANP rule address-set - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{}, []string{node1Name})...) - gomega.Eventually(fakeOVN.nbClient, "3s").Should(libovsdbtest.HaveData(expectedDatabaseState)) - - ginkgo.By("19. update the ANP by deleting all rules; check if all objects are deleted correctly") - anp.Spec.Ingress = []anpapi.AdminNetworkPolicyIngressRule{} - anp.Spec.Egress = []anpapi.AdminNetworkPolicyEgressRule{} - anp.ResourceVersion = "7" - anp, err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Update(context.TODO(), anp, metav1.UpdateOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - pg = getDefaultPGForANPSubject(anp.Name, nil, nil, false) // no ports and acls in PG - subjectNSASIPv4, subjectNSASIPv6 = buildNamespaceAddressSets(anpSubjectNamespaceName, []string{}) - peerNSASIPv4, peerNSASIPv6 = buildNamespaceAddressSets(anpPeerNamespaceName, []string{}) - expectedDatabaseState = []libovsdbtest.TestData{pg, subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{}, []string{node1Name})...) - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - - ginkgo.By("20. delete the ANP; check if all objects are deleted correctly") - anp.ResourceVersion = "8" - err = fakeOVN.fakeClient.ANPClient.PolicyV1alpha1().AdminNetworkPolicies().Delete(context.TODO(), anp.Name, metav1.DeleteOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - expectedDatabaseState = []libovsdbtest.TestData{subjectNSASIPv4, subjectNSASIPv6, peerNSASIPv4, peerNSASIPv6} // port group should be deleted - expectedDatabaseState = append(expectedDatabaseState, getDefaultNetExpectedPodsAndSwitches([]testPod{}, []string{node1Name})...) - gomega.Eventually(fakeOVN.nbClient).Should(libovsdbtest.HaveData(expectedDatabaseState)) - - return nil - } - err := app.Run([]string{app.Name}) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - }) + err := app.Run([]string{app.Name}) + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + }, + ginkgo.Entry("all namespaces are part of default network", + types.DefaultNetworkName, types.DefaultNetworkName, false), + ginkgo.Entry("subject namespace is part of blue P-UDN network while peer namespace is part of default network", + "blue", types.DefaultNetworkName, true), + /*ginkgo.Entry("subject namespace is default network while peer namespace is part of blue P-UDN network", + types.DefaultNetworkName, "blue", true), + ginkgo.Entry("all namespaces are part of blue P-UDN network", + "blue", "blue", true),*/ + ) ginkgo.It("PortNumber, PortRange and NamedPort (no matching pods) Rules for ANP", func() { app.Action = func(ctx *cli.Context) error { config.IPv4Mode = true diff --git a/go-controller/pkg/ovn/baseline_admin_network_policy_test.go b/go-controller/pkg/ovn/baseline_admin_network_policy_test.go index 80051e654a..897925be04 100644 --- a/go-controller/pkg/ovn/baseline_admin_network_policy_test.go +++ b/go-controller/pkg/ovn/baseline_admin_network_policy_test.go @@ -50,12 +50,12 @@ func getACLsForBANPRulesWithNamedPorts(banp *anpapi.BaselineAdminNetworkPolicy, aclResults := []*nbdb.ACL{} for i, ingress := range banp.Spec.Ingress { acls := getANPGressACL(anpovn.GetACLActionForBANPRule(ingress.Action), banp.Name, string(libovsdbutil.ACLIngress), - getBANPRulePriority(int32(i)), int32(i), ingress.Ports, namedIPorts, true) + getBANPRulePriority(int32(i)), int32(i), ingress.Ports, namedIPorts, true, DefaultNetworkControllerName) aclResults = append(aclResults, acls...) } for i, egress := range banp.Spec.Egress { acls := getANPGressACL(anpovn.GetACLActionForBANPRule(egress.Action), banp.Name, string(libovsdbutil.ACLEgress), - getBANPRulePriority(int32(i)), int32(i), egress.Ports, namedEPorts, true) + getBANPRulePriority(int32(i)), int32(i), egress.Ports, namedEPorts, true, DefaultNetworkControllerName) aclResults = append(aclResults, acls...) } return aclResults @@ -65,12 +65,12 @@ func getACLsForBANPRules(banp *anpapi.BaselineAdminNetworkPolicy) []*nbdb.ACL { aclResults := []*nbdb.ACL{} for i, ingress := range banp.Spec.Ingress { acls := getANPGressACL(anpovn.GetACLActionForBANPRule(ingress.Action), banp.Name, string(libovsdbutil.ACLIngress), - getBANPRulePriority(int32(i)), int32(i), ingress.Ports, nil, true) + getBANPRulePriority(int32(i)), int32(i), ingress.Ports, nil, true, DefaultNetworkControllerName) aclResults = append(aclResults, acls...) } for i, egress := range banp.Spec.Egress { acls := getANPGressACL(anpovn.GetACLActionForBANPRule(egress.Action), banp.Name, string(libovsdbutil.ACLEgress), - getBANPRulePriority(int32(i)), int32(i), egress.Ports, nil, true) + getBANPRulePriority(int32(i)), int32(i), egress.Ports, nil, true, DefaultNetworkControllerName) aclResults = append(aclResults, acls...) } return aclResults diff --git a/go-controller/pkg/ovn/namespace_test.go b/go-controller/pkg/ovn/namespace_test.go index 31e380159b..ed64dca633 100644 --- a/go-controller/pkg/ovn/namespace_test.go +++ b/go-controller/pkg/ovn/namespace_test.go @@ -79,6 +79,10 @@ func buildNamespaceAddressSets(namespace string, ips []string) (*nbdb.AddressSet return addressset.GetTestDbAddrSets(getNamespaceAddrSetDbIDs(namespace, "default-network-controller"), ips) } +func buildUDNNamespaceAddressSets(networkName, namespace string, ips []string) (*nbdb.AddressSet, *nbdb.AddressSet) { + return addressset.GetTestDbAddrSets(getNamespaceAddrSetDbIDs(namespace, networkName+"-network-controller"), ips) +} + var _ = ginkgo.Describe("OVN Namespace Operations", func() { const ( namespaceName = "namespace1" diff --git a/go-controller/pkg/ovn/pods.go b/go-controller/pkg/ovn/pods.go index 3f9da4bd8a..d6520ef9a9 100644 --- a/go-controller/pkg/ovn/pods.go +++ b/go-controller/pkg/ovn/pods.go @@ -235,7 +235,6 @@ func (oc *DefaultNetworkController) addLogicalPort(pod *kapi.Pod) (err error) { if len(networkMap) > 1 { return fmt.Errorf("more than one NAD requested on default network for pod %s/%s", pod.Namespace, pod.Name) } - var network *nadapi.NetworkSelectionElement for _, network = range networkMap { break @@ -267,7 +266,8 @@ func (oc *DefaultNetworkController) addLogicalPort(pod *kapi.Pod) (err error) { if networkRole != ovntypes.NetworkRolePrimary && util.IsNetworkSegmentationSupportEnabled() { pgName := libovsdbutil.GetPortGroupName(oc.getSecondaryPodsPortGroupDbIDs()) if ops, err = libovsdbops.AddPortsToPortGroupOps(oc.nbClient, ops, pgName, lsp.UUID); err != nil { - return err + return fmt.Errorf("unable to add ports to port group %s for %s/%s part of NAD %s: %w", + pgName, pod.Namespace, pod.Name, nadName, err) } // set open ports for UDN pods, use function without transact, since lsp is not created yet. var parseErr error