Skip to content

Commit

Permalink
Add tests for BANP conversion.
Browse files Browse the repository at this point in the history
  • Loading branch information
fasaxc committed Dec 4, 2024
1 parent 261a14f commit af5b2a0
Show file tree
Hide file tree
Showing 3 changed files with 289 additions and 22 deletions.
244 changes: 244 additions & 0 deletions libcalico-go/lib/backend/k8s/conversion/adminnetworkpolicy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1960,3 +1960,247 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {
))
})
})

// Most of the conversion logic is shared with ANP, so only testing a few
// cases for BANP.
var _ = Describe("Test BaselineAdminNetworkPolicy conversion", func() {
// Use a single instance of the Converter for these tests.
c := NewConverter()

convertToGNP := func(
banp *adminpolicy.BaselineAdminNetworkPolicy,
expectedErr *cerrors.ErrorAdminPolicyConversion,
) *apiv3.GlobalNetworkPolicy {
// Parse the policy.
pol, err := c.K8sBaselineAdminNetworkPolicyToCalico(banp)

if expectedErr == nil {
Expect(err).To(BeNil())
} else {
Expect(err).To(Equal(*expectedErr))
}

// Assert key fields are correct.
policyName := fmt.Sprintf("%v%v", names.K8sBaselineAdminNetworkPolicyNamePrefix, banp.Name)
Expect(pol.Key.(model.ResourceKey).Name).To(Equal(policyName))

gnp, ok := pol.Value.(*apiv3.GlobalNetworkPolicy)
Expect(ok).To(BeTrue())

// Make sure the type information is correct.
Expect(gnp.Kind).To(Equal(apiv3.KindGlobalNetworkPolicy))
Expect(gnp.APIVersion).To(Equal(apiv3.GroupVersionCurrent))

// Assert value fields are correct. Order is always 1000 for a BANP.
Expect(*gnp.Spec.Order).To(Equal(1000.0))
Expect(gnp.Spec.Tier).To(Equal(names.BaselineAdminNetworkPolicyTierName))

return gnp
}

It("should parse a basic k8s BaselineAdminNetworkPolicy to a GlobalNetworkPolicy", func() {
ports := []adminpolicy.AdminNetworkPolicyPort{{
PortNumber: &adminpolicy.Port{
Port: 80,
},
}}
banp := adminpolicy.BaselineAdminNetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
UID: types.UID("30316465-6365-4463-ad63-3564622d3638"),
},
Spec: adminpolicy.BaselineAdminNetworkPolicySpec{
Subject: adminpolicy.AdminNetworkPolicySubject{
Pods: &adminpolicy.NamespacedPod{
NamespaceSelector: metav1.LabelSelector{
MatchLabels: map[string]string{
"label": "value",
"label2": "value2",
},
},
PodSelector: metav1.LabelSelector{
MatchLabels: map[string]string{
"foo": "bar",
},
},
},
},
Ingress: []adminpolicy.BaselineAdminNetworkPolicyIngressRule{
{
Name: "The first ingress rule",
Action: "Allow",
Ports: &ports,
From: []adminpolicy.AdminNetworkPolicyIngressPeer{
{
Namespaces: &metav1.LabelSelector{
MatchLabels: map[string]string{
"k": "v",
"k2": "v2",
},
},
},
},
},
},
Egress: []adminpolicy.BaselineAdminNetworkPolicyEgressRule{
{
Name: "The first egress rule",
Action: "Deny",
To: []adminpolicy.AdminNetworkPolicyEgressPeer{
{
Networks: []adminpolicy.CIDR{"10.0.0.0/8"},
},
},
},
{
Action: "Allow",
To: []adminpolicy.AdminNetworkPolicyEgressPeer{
{
Networks: []adminpolicy.CIDR{"0.0.0.0/0"},
},
},
},
},
},
}

// Convert the policy
gnp := convertToGNP(&banp, nil)

// Check the selector is correct, and that the matches are sorted.
Expect(gnp.Spec.NamespaceSelector).To(Equal("label == 'value' && label2 == 'value2'"))
Expect(gnp.Spec.Selector).To(Equal("projectcalico.org/orchestrator == 'k8s' && foo == 'bar'"))
protoTCP := numorstring.ProtocolFromString("TCP")
Expect(gnp.Spec.Ingress).To(ConsistOf(
apiv3.Rule{
Metadata: k8sAdminNetworkPolicyToCalicoMetadata("The first ingress rule"),
Action: "Allow",
Protocol: &protoTCP, // Defaulted to TCP.
Source: apiv3.EntityRule{
NamespaceSelector: "k == 'v' && k2 == 'v2'",
},
Destination: apiv3.EntityRule{
Ports: []numorstring.Port{numorstring.SinglePort(80)},
},
},
))

// There should be no Egress rules
Expect(gnp.Spec.Egress).To(ConsistOf(
apiv3.Rule{
Metadata: k8sAdminNetworkPolicyToCalicoMetadata("The first egress rule"),
Action: "Deny",
Destination: apiv3.EntityRule{
Nets: []string{"10.0.0.0/8"},
},
},
apiv3.Rule{
Action: "Allow",
Destination: apiv3.EntityRule{
Nets: []string{"0.0.0.0/0"},
},
},
))
})

It("should parse a k8s BaselineAdminNetworkPolicy with an invalid networks peer", func() {
ports := []adminpolicy.AdminNetworkPolicyPort{
{
PortNumber: &adminpolicy.Port{Port: 80},
},
}
anp := adminpolicy.BaselineAdminNetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
UID: types.UID("30316465-6365-4463-ad63-3564622d3638"),
},
Spec: adminpolicy.BaselineAdminNetworkPolicySpec{
Subject: adminpolicy.AdminNetworkPolicySubject{
Namespaces: &metav1.LabelSelector{
MatchLabels: map[string]string{
"label": "value",
"label2": "value2",
},
},
},
Ingress: []adminpolicy.BaselineAdminNetworkPolicyIngressRule{
{
Action: "Deny",
Ports: &ports,
From: []adminpolicy.AdminNetworkPolicyIngressPeer{
{},
},
},
},
Egress: []adminpolicy.BaselineAdminNetworkPolicyEgressRule{
{
Name: "A random egress rule",
Action: "Deny",
Ports: &ports,
To: []adminpolicy.AdminNetworkPolicyEgressPeer{
{
Namespaces: &metav1.LabelSelector{
MatchLabels: map[string]string{
"k3": "v3",
},
},
},
{
Networks: []adminpolicy.CIDR{"10.10.10.0/24", "1.1.1.1/66"},
},
},
},
},
},
}

expectedErr := cerrors.ErrorAdminPolicyConversion{
PolicyName: "default",
Rules: []cerrors.ErrorAdminPolicyConversionRule{
{
IngressRule: &adminpolicy.BaselineAdminNetworkPolicyIngressRule{
Action: "Deny",
Ports: &ports,
From: []adminpolicy.AdminNetworkPolicyIngressPeer{
{},
},
},
Reason: "k8s rule couldn't be converted: none of supported fields in 'From' is set.",
},
{
EgressRule: &adminpolicy.BaselineAdminNetworkPolicyEgressRule{
Name: "A random egress rule",
Action: "Deny",
Ports: &ports,
To: []adminpolicy.AdminNetworkPolicyEgressPeer{
{
Namespaces: &metav1.LabelSelector{
MatchLabels: map[string]string{"k3": "v3"},
MatchExpressions: nil,
},
},
{
Networks: []adminpolicy.CIDR{"10.10.10.0/24", "1.1.1.1/66"},
},
},
},
Reason: "k8s rule couldn't be converted: invalid CIDR in ANP rule: invalid CIDR address: 1.1.1.1/66",
},
},
}

// Convert the policy
gnp := convertToGNP(&anp, &expectedErr)

Expect(gnp.Spec.Ingress).To(ConsistOf(
apiv3.Rule{
Action: "Deny", // The invalid rule is replaced with a deny-all rule.
},
))
Expect(gnp.Spec.Egress).To(ConsistOf(
apiv3.Rule{
Action: "Deny", // The invalid rule is replaced with a deny-all rule.
},
))
})
})
2 changes: 1 addition & 1 deletion libcalico-go/lib/backend/k8s/conversion/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ func (c converter) K8sBaselineAdminNetworkPolicyToCalico(anp *adminpolicy.Baseli
if err != nil {
log.WithError(err).Warn("dropping k8s rule that couldn't be converted.")
// Add rule to conversion error slice
errorTracker.BadIngressRule(r, fmt.Sprintf("k8s rule couldn't be converted: %s", err))
errorTracker.BadIngressRule(&r, fmt.Sprintf("k8s rule couldn't be converted: %s", err))
failClosedRule := k8sBANPHandleFailedRules(r.Action)
if failClosedRule != nil {
ingressRules = append(ingressRules, *failClosedRule)
Expand Down
65 changes: 44 additions & 21 deletions libcalico-go/lib/clientv3/tier_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/format"
apiv3 "github.com/projectcalico/api/pkg/apis/projectcalico/v3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

Expand All @@ -33,6 +34,11 @@ import (
"github.com/projectcalico/calico/libcalico-go/lib/watch"
)

func init() {
// Stop Gomega from chopping off diffs in logs.
format.MaxLength = 0
}

var _ = testutils.E2eDatastoreDescribe("Tier tests", testutils.DatastoreAll, func(config apiconfig.CalicoAPIConfig) {
ctx := context.Background()
defaultOrder := apiv3.DefaultTierOrder
Expand Down Expand Up @@ -63,6 +69,12 @@ var _ = testutils.E2eDatastoreDescribe("Tier tests", testutils.DatastoreAll, fun
DefaultAction: &actionPass,
}

banpOrder := apiv3.BaselineAdminNetworkPolicyTierOrder
banpSpec := apiv3.TierSpec{
Order: &banpOrder,
DefaultAction: &actionPass,
}

npName1 := name1 + ".networkp-1"
npSpec1 := apiv3.NetworkPolicySpec{
Tier: name1,
Expand Down Expand Up @@ -95,7 +107,12 @@ var _ = testutils.E2eDatastoreDescribe("Tier tests", testutils.DatastoreAll, fun
}

DescribeTable("Tier e2e CRUD tests",
func(name1, name2, npName1, gnpName1, gnpName2, namespace1 string, spec1, spec2 apiv3.TierSpec, npSpec1 apiv3.NetworkPolicySpec, gnpSpec1, gnpSpec2 apiv3.GlobalNetworkPolicySpec) {
func(
name1, name2, npName1, gnpName1, gnpName2, namespace1 string,
spec1, spec2 apiv3.TierSpec,
npSpec1 apiv3.NetworkPolicySpec,
gnpSpec1, gnpSpec2 apiv3.GlobalNetworkPolicySpec,
) {
c, err := clientv3.New(config)
Expect(err).NotTo(HaveOccurred())

Expand Down Expand Up @@ -256,13 +273,21 @@ var _ = testutils.E2eDatastoreDescribe("Tier tests", testutils.DatastoreAll, fun
Expect(outError).To(HaveOccurred())
Expect(outError.Error()).To(ContainSubstring("resource does not exist: Tier(" + name2 + ") with error:"))

By("Listing all the Tiers, expecting a single result with name1/spec1")
By("Listing all the Tiers, expecting a single result with name1/spec1 plus our default tiers")
const numDefaultTiers = 3
checkAndFilterDefaultTiers := func(outList *apiv3.TierList) []apiv3.Tier {
// Tiers are returned in name order, and the default ones happen
// to sort first in these tests.
Expect(&outList.Items[0]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, names.AdminNetworkPolicyTierName, anpSpec))
Expect(&outList.Items[1]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, names.BaselineAdminNetworkPolicyTierName, banpSpec))
Expect(&outList.Items[2]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, defaultName, defaultSpec))
return outList.Items[3:]
}
outList, outError := c.Tiers().List(ctx, options.ListOptions{})
Expect(outError).NotTo(HaveOccurred())
Expect(outList.Items).To(HaveLen(3))
Expect(&outList.Items[0]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, names.AdminNetworkPolicyTierName, anpSpec))
Expect(&outList.Items[1]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, defaultName, defaultSpec))
Expect(&outList.Items[2]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, name1, spec1))
nonDefaultTiers := checkAndFilterDefaultTiers(outList)
Expect(nonDefaultTiers).To(HaveLen(1))
Expect(&nonDefaultTiers[0]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, name1, spec1))

By("Creating a new Tier with name2/spec2")
res2, outError := c.Tiers().Create(ctx, &apiv3.Tier{
Expand All @@ -281,11 +306,10 @@ var _ = testutils.E2eDatastoreDescribe("Tier tests", testutils.DatastoreAll, fun
By("Listing all the Tiers, expecting a two results with name1/spec1 and name2/spec2")
outList, outError = c.Tiers().List(ctx, options.ListOptions{})
Expect(outError).NotTo(HaveOccurred())
Expect(outList.Items).To(HaveLen(4))
Expect(&outList.Items[0]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, names.AdminNetworkPolicyTierName, anpSpec))
Expect(&outList.Items[1]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, defaultName, defaultSpec))
Expect(&outList.Items[2]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, name1, spec1))
Expect(&outList.Items[3]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, name2, spec2UpdatedOrder))
nonDefaultTiers = checkAndFilterDefaultTiers(outList)
Expect(nonDefaultTiers).To(HaveLen(2))
Expect(nonDefaultTiers[0]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, name1, spec1))
Expect(nonDefaultTiers[1]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, name2, spec2UpdatedOrder))

By("Updating Tier name1 with spec2")
res1.Spec = spec2
Expand Down Expand Up @@ -327,19 +351,19 @@ var _ = testutils.E2eDatastoreDescribe("Tier tests", testutils.DatastoreAll, fun
if config.Spec.DatastoreType != apiconfig.Kubernetes {
By("Listing Tiers with the original resource version and checking for a single result with name1/spec1")
outList, outError = c.Tiers().List(ctx, options.ListOptions{ResourceVersion: rv1_1})
nonDefaultTiers = checkAndFilterDefaultTiers(outList)
Expect(outError).NotTo(HaveOccurred())
Expect(outList.Items).To(HaveLen(3))
Expect(&outList.Items[2]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, name1, spec1))
Expect(nonDefaultTiers).To(HaveLen(1))
Expect(&nonDefaultTiers[0]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, name1, spec1))
}

By("Listing Tiers with the latest resource version and checking for two results with name1/spec2 and name2/spec2")
outList, outError = c.Tiers().List(ctx, options.ListOptions{})
nonDefaultTiers = checkAndFilterDefaultTiers(outList)
Expect(outError).NotTo(HaveOccurred())
Expect(outList.Items).To(HaveLen(4))
Expect(&outList.Items[0]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, names.AdminNetworkPolicyTierName, anpSpec))
Expect(&outList.Items[1]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, defaultName, defaultSpec))
Expect(&outList.Items[2]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, name1, spec2UpdatedOrder))
Expect(&outList.Items[3]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, name2, spec2UpdatedOrder))
Expect(nonDefaultTiers).To(HaveLen(2))
Expect(nonDefaultTiers[0]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, name1, spec2UpdatedOrder))
Expect(nonDefaultTiers[1]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, name2, spec2UpdatedOrder))

if config.Spec.DatastoreType != apiconfig.Kubernetes {
By("Deleting Tier (name1) with the old resource version")
Expand Down Expand Up @@ -446,9 +470,8 @@ var _ = testutils.E2eDatastoreDescribe("Tier tests", testutils.DatastoreAll, fun
By("Listing all Tiers and expecting only the default and adminnetworkpolicy tiers")
outList, outError = c.Tiers().List(ctx, options.ListOptions{})
Expect(outError).NotTo(HaveOccurred())
Expect(outList.Items).To(HaveLen(2))
Expect(&outList.Items[0]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, names.AdminNetworkPolicyTierName, anpSpec))
Expect(&outList.Items[1]).To(MatchResource(apiv3.KindTier, testutils.ExpectNoNamespace, defaultName, defaultSpec))
nonDefaultTiers = checkAndFilterDefaultTiers(outList)
Expect(nonDefaultTiers).To(HaveLen(0))

By("Getting Tier (name2) and expecting an error")
res, outError = c.Tiers().Get(ctx, name2, options.GetOptions{})
Expand Down

0 comments on commit af5b2a0

Please sign in to comment.