Skip to content

Commit

Permalink
Add UT for custom resource List/Get.
Browse files Browse the repository at this point in the history
  • Loading branch information
fasaxc committed Jan 7, 2025
1 parent 36ebf0c commit fcc3afe
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 65 deletions.
64 changes: 3 additions & 61 deletions libcalico-go/lib/backend/k8s/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"path/filepath"
"reflect"
"strings"
"sync"

apiv3 "github.com/projectcalico/api/pkg/apis/projectcalico/v3"
log "github.com/sirupsen/logrus"
Expand All @@ -41,6 +40,7 @@ import (
"github.com/projectcalico/calico/libcalico-go/lib/backend/api"
"github.com/projectcalico/calico/libcalico-go/lib/backend/k8s/conversion"
"github.com/projectcalico/calico/libcalico-go/lib/backend/k8s/resources"
calischeme "github.com/projectcalico/calico/libcalico-go/lib/backend/k8s/scheme"
"github.com/projectcalico/calico/libcalico-go/lib/backend/model"
cerrors "github.com/projectcalico/calico/libcalico-go/lib/errors"
"github.com/projectcalico/calico/libcalico-go/lib/net"
Expand Down Expand Up @@ -517,8 +517,6 @@ func (c *KubeClient) Close() error {
return nil
}

var addToSchemeOnce sync.Once

// buildK8SAdminPolicyClient builds a RESTClient configured to interact (Baseline) Admin Network Policy.
func buildK8SAdminPolicyClient(cfg *rest.Config) (*adminpolicyclient.PolicyV1alpha1Client, error) {
return adminpolicyclient.NewForConfig(cfg)
Expand All @@ -540,64 +538,8 @@ func buildCRDClientV1(cfg rest.Config) (*rest.RESTClient, error) {
return nil, err
}

// We're operating on the pkg level scheme.Scheme, so make sure that multiple
// calls to this function don't do this simultaneously, which can cause crashes
// due to concurrent access to underlying maps. For good measure, use a once
// since this really only needs to happen one time.
addToSchemeOnce.Do(func() {
// We also need to register resources.
schemeBuilder := runtime.NewSchemeBuilder(
func(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(
*cfg.GroupVersion,
&apiv3.FelixConfiguration{},
&apiv3.FelixConfigurationList{},
&apiv3.IPPool{},
&apiv3.IPPoolList{},
&apiv3.IPReservation{},
&apiv3.IPReservationList{},
&apiv3.BGPPeer{},
&apiv3.BGPPeerList{},
&apiv3.BGPConfiguration{},
&apiv3.BGPConfigurationList{},
&apiv3.ClusterInformation{},
&apiv3.ClusterInformationList{},
&apiv3.GlobalNetworkSet{},
&apiv3.GlobalNetworkSetList{},
&apiv3.GlobalNetworkPolicy{},
&apiv3.GlobalNetworkPolicyList{},
&apiv3.NetworkPolicy{},
&apiv3.NetworkPolicyList{},
&apiv3.NetworkSet{},
&apiv3.NetworkSetList{},
&apiv3.Tier{},
&apiv3.TierList{},
&apiv3.HostEndpoint{},
&apiv3.HostEndpointList{},
&libapiv3.BlockAffinity{},
&libapiv3.BlockAffinityList{},
&libapiv3.IPAMBlock{},
&libapiv3.IPAMBlockList{},
&libapiv3.IPAMHandle{},
&libapiv3.IPAMHandleList{},
&libapiv3.IPAMConfig{},
&libapiv3.IPAMConfigList{},
&apiv3.KubeControllersConfiguration{},
&apiv3.KubeControllersConfigurationList{},
&apiv3.CalicoNodeStatus{},
&apiv3.CalicoNodeStatusList{},
&apiv3.BGPFilter{},
&apiv3.BGPFilterList{},
)
return nil
})

err := schemeBuilder.AddToScheme(scheme.Scheme)
if err != nil {
log.WithError(err).Fatal("failed to add calico resources to scheme")
}
metav1.AddToGroupVersion(scheme.Scheme, schema.GroupVersion{Group: "crd.projectcalico.org", Version: "v1"})
})
calischeme.AddCalicoResourcesToScheme()

return cli, nil
}

Expand Down
9 changes: 5 additions & 4 deletions libcalico-go/lib/backend/k8s/resources/customresource.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,8 @@ func (c *customK8sResourceClient) List(ctx context.Context, list model.ListInter
logContext.Debug("Received List request")

// If it is a namespaced resource, then we'll need the namespace.
namespace := list.(model.ResourceListOptions).Namespace
resList := list.(model.ResourceListOptions)
namespace := resList.Namespace
key := c.listInterfaceToKey(list)

// listFunc performs a list with the given options.
Expand All @@ -369,12 +370,12 @@ func (c *customK8sResourceClient) List(ctx context.Context, list model.ListInter

// If the prefix is specified, look for the resources with the label
// of prefix.
if list.(model.ResourceListOptions).Prefix {
if resList.Prefix {
// The prefix has a trailing "." character, remove it, since it is not valid for k8s labels
if !strings.HasSuffix(list.(model.ResourceListOptions).Name, ".") {
if !strings.HasSuffix(resList.Name, ".") {
return nil, errors.New("internal error: custom resource list invoked for a prefix not in the form '<tier>.'")
}
name := list.(model.ResourceListOptions).Name[:len(list.(model.ResourceListOptions).Name)-1]
name := resList.Name[:len(resList.Name)-1]
if name == "default" {
req = req.VersionedParams(&metav1.ListOptions{
LabelSelector: "!" + apiv3.LabelTier,
Expand Down
81 changes: 81 additions & 0 deletions libcalico-go/lib/backend/k8s/resources/customresource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,29 @@
package resources

import (
"context"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
apiv3 "github.com/projectcalico/api/pkg/apis/projectcalico/v3"
"github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest/fake"

calischeme "github.com/projectcalico/calico/libcalico-go/lib/backend/k8s/scheme"
"github.com/projectcalico/calico/libcalico-go/lib/backend/model"
"github.com/projectcalico/calico/libcalico-go/lib/net"
)

func init() {
// Need to set up the scheme in order to use the fake REST client.
calischeme.AddCalicoResourcesToScheme()
}

var _ = Describe("Custom resource conversion methods (tested using BGPPeer)", func() {
// Create an empty client since we are only testing conversion functions.
client := NewBGPPeerClient(nil, nil).(*customK8sResourceClient)
Expand Down Expand Up @@ -225,3 +238,71 @@ var _ = Describe("Custom resource conversion methods (tested using BGPPeer)", fu
Expect(resConverted.(*apiv3.BGPPeer).ObjectMeta.Labels).NotTo(HaveKey("foo2"))
})
})

var _ = Describe("Custom resource conversion methods (tested using namespaced NetworkSet)", func() {
var client *customK8sResourceClient
var fakeREST *fake.RESTClient

BeforeEach(func() {
fakeREST = &fake.RESTClient{
NegotiatedSerializer: serializer.WithoutConversionCodecFactory{CodecFactory: scheme.Codecs},
GroupVersion: schema.GroupVersion{
Group: "crd.projectcalico.org",
Version: "v1",
},
VersionedAPIPath: "/apis",
}
client = NewNetworkSetClient(nil, fakeREST).(*customK8sResourceClient)
})

It("should get by name", func() {
o, err := client.Get(context.TODO(), model.ResourceKey{
Name: "mynetset",
Namespace: "mynamespace",
Kind: apiv3.KindNetworkSet,
}, "")

// Expect an error since the client is not implemented.
Expect(err).To(HaveOccurred())
Expect(o).To(BeNil())

// But we should be able to check the request...
url := fakeREST.Req.URL
logrus.Debug("URL: ", url)
Expect(url.Path).To(Equal("/apis/namespaces/mynamespace/networksets/mynetset"))
})

It("should list all", func() {
l, err := client.List(context.TODO(), model.ResourceListOptions{
Kind: apiv3.KindNetworkSet,
}, "")

// Expect an error since the client is not implemented.
Expect(err).To(HaveOccurred())
Expect(l).To(BeNil())

// But we should be able to check the request...
url := fakeREST.Req.URL
logrus.Debug("URL: ", url)
Expect(url.Path).To(Equal("/apis/networksets"))
Expect(url.Query()).NotTo(HaveKey("metadata.name"))
})

It("should use a fieldSelector for a list name match", func() {
l, err := client.List(context.TODO(), model.ResourceListOptions{
Name: "foo",
Namespace: "mynamespace",
Kind: apiv3.KindNetworkSet,
}, "")

// Expect an error since the client is not implemented.
Expect(err).To(HaveOccurred())
Expect(l).To(BeNil())

// But we should be able to check the request...
url := fakeREST.Req.URL
logrus.Debug("URL: ", url)
Expect(url.Path).To(Equal("/apis/namespaces/mynamespace/networksets"))
Expect(url.Query().Get("fieldSelector")).To(Equal("metadata.name=foo"))
})
})
90 changes: 90 additions & 0 deletions libcalico-go/lib/backend/k8s/scheme/scheme.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) 2025 Tigera, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package scheme

import (
"sync"

apiv3 "github.com/projectcalico/api/pkg/apis/projectcalico/v3"
log "github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes/scheme"

libapiv3 "github.com/projectcalico/calico/libcalico-go/lib/apis/v3"
)

var addToSchemeOnce sync.Once

func AddCalicoResourcesToScheme() {
addToSchemeOnce.Do(func() {
// We also need to register resources.
schemeBuilder := runtime.NewSchemeBuilder(
func(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(
schema.GroupVersion{
Group: "crd.projectcalico.org",
Version: "v1",
},
&apiv3.FelixConfiguration{},
&apiv3.FelixConfigurationList{},
&apiv3.IPPool{},
&apiv3.IPPoolList{},
&apiv3.IPReservation{},
&apiv3.IPReservationList{},
&apiv3.BGPPeer{},
&apiv3.BGPPeerList{},
&apiv3.BGPConfiguration{},
&apiv3.BGPConfigurationList{},
&apiv3.ClusterInformation{},
&apiv3.ClusterInformationList{},
&apiv3.GlobalNetworkSet{},
&apiv3.GlobalNetworkSetList{},
&apiv3.GlobalNetworkPolicy{},
&apiv3.GlobalNetworkPolicyList{},
&apiv3.NetworkPolicy{},
&apiv3.NetworkPolicyList{},
&apiv3.NetworkSet{},
&apiv3.NetworkSetList{},
&apiv3.Tier{},
&apiv3.TierList{},
&apiv3.HostEndpoint{},
&apiv3.HostEndpointList{},
&libapiv3.BlockAffinity{},
&libapiv3.BlockAffinityList{},
&libapiv3.IPAMBlock{},
&libapiv3.IPAMBlockList{},
&libapiv3.IPAMHandle{},
&libapiv3.IPAMHandleList{},
&libapiv3.IPAMConfig{},
&libapiv3.IPAMConfigList{},
&apiv3.KubeControllersConfiguration{},
&apiv3.KubeControllersConfigurationList{},
&apiv3.CalicoNodeStatus{},
&apiv3.CalicoNodeStatusList{},
&apiv3.BGPFilter{},
&apiv3.BGPFilterList{},
)
return nil
})

err := schemeBuilder.AddToScheme(scheme.Scheme)
if err != nil {
log.WithError(err).Fatal("failed to add calico resources to scheme")
}
metav1.AddToGroupVersion(scheme.Scheme, schema.GroupVersion{Group: "crd.projectcalico.org", Version: "v1"})
})
}

0 comments on commit fcc3afe

Please sign in to comment.