Skip to content

Commit

Permalink
add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bruelea committed Nov 18, 2024
1 parent 7204b59 commit 549e32a
Show file tree
Hide file tree
Showing 19 changed files with 899 additions and 236 deletions.
7 changes: 0 additions & 7 deletions api/v1/ipaddressclaim_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,3 @@ var ConditionIpAssignedFalse = metav1.Condition{
Reason: "IPAddressCRNotCreated",
Message: "Failed to fetch new IP from NetBox",
}

var ConditionIpAssignedFalseSizeMissmatch = metav1.Condition{
Type: "IPAssigned",
Status: "False",
Reason: "IPAddressCRNotCreated",
Message: "Size of restored IpRange does not match the requested size",
}
14 changes: 8 additions & 6 deletions api/v1/iprange_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ import (

// IpRangeSpec defines the desired state of IpRange
type IpRangeSpec struct {
// the startAddress is the first ip address included in the ip range
//+kubebuilder:validation:Format=cidr
//+kubebuilder:validation:XValidation:rule="self == oldSelf",message="Field 'startAddress' is immutable"
//+kubebuilder:validation:Required
StartAddress string `json:"startAddress"`

// the endAddress is the last ip address included in the ip range
//+kubebuilder:validation:Format=cidr
//+kubebuilder:validation:XValidation:rule="self == oldSelf",message="Field 'endAddress' is immutable"
//+kubebuilder:validation:Required
Expand Down Expand Up @@ -92,20 +94,20 @@ func init() {
var ConditionIpRangeReadyTrue = metav1.Condition{
Type: "Ready",
Status: "True",
Reason: "IpReservedInNetbox",
Message: "IP was reserved/updated in NetBox",
Reason: "IpRangeReservedInNetbox",
Message: "Ip Range was reserved/updated in NetBox",
}

var ConditionIpRangeReadyFalse = metav1.Condition{
Type: "Ready",
Status: "False",
Reason: "FailedToReserveIpInNetbox",
Message: "Failed to reserve IP Range in NetBox",
Reason: "FailedToReserveIpRangeInNetbox",
Message: "Failed to reserve Ip Range in NetBox",
}

var ConditionIpRangeReadyFalseDeletionFailed = metav1.Condition{
Type: "Ready",
Status: "False",
Reason: "FailedToDeleteIpInNetbox",
Message: "Failed to delete IP Range in NetBox",
Reason: "FailedToDeleteIpRangeInNetbox",
Message: "Failed to delete Ip Range in NetBox",
}
27 changes: 17 additions & 10 deletions api/v1/iprangeclaim_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,27 +90,34 @@ func init() {
var ConditionIpRangeClaimReadyTrue = metav1.Condition{
Type: "Ready",
Status: "True",
Reason: "IPAddressResourceReady",
Message: "IPAddress Resource is ready",
Reason: "IpRangeResourceReady",
Message: "Ip Range Resource is ready",
}

var ConditionIpRangeClaimReadyFalse = metav1.Condition{
Type: "Ready",
Status: "False",
Reason: "IPAddressResourceNotReady",
Message: "IPAddress Resource is not ready",
Reason: "IpRangeResourceNotReady",
Message: "Ip Range Resource is not ready",
}

var ConditionIpRangeAssignedTrue = metav1.Condition{
Type: "IPAssigned",
Type: "IpRangeAssigned",
Status: "True",
Reason: "IPAddressCRCreated",
Message: "New IP fetched from NetBox and IPAddress CR was created",
Reason: "IpRangeCRCreated",
Message: "New Ip Range fetched from NetBox and IpRange CR was created",
}

var ConditionIpRangeAssignedFalse = metav1.Condition{
Type: "IPAssigned",
Type: "IpRangeAssigned",
Status: "False",
Reason: "IPAddressCRNotCreated",
Message: "Failed to fetch new IP from NetBox",
Reason: "IpRangeCRNotCreated",
Message: "Failed to fetch new Ip Range from NetBox",
}

var ConditionIpAssignedFalseSizeMissmatch = metav1.Condition{
Type: "IpRangeAssigned",
Status: "False",
Reason: "IpRangeCRNotCreated",
Message: "Size of restored IpRange does not match the requested size",
}
2 changes: 1 addition & 1 deletion config/crd/bases/netbox.dev_iprangeclaims.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ spec:
listKind: IpRangeClaimList
plural: iprangeclaims
shortNames:
- iprc
- irc
singular: iprangeclaim
scope: Namespaced
versions:
Expand Down
6 changes: 5 additions & 1 deletion config/crd/bases/netbox.dev_ipranges.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ spec:
listKind: IpRangeList
plural: ipranges
shortNames:
- ipr
- ir
singular: iprange
scope: Namespaced
versions:
Expand Down Expand Up @@ -69,6 +69,8 @@ spec:
description:
type: string
endAddress:
description: the endAddress is the last ip address included in the
ip range
format: cidr
type: string
x-kubernetes-validations:
Expand All @@ -77,6 +79,8 @@ spec:
preserveInNetbox:
type: boolean
startAddress:
description: the startAddress is the first ip address included in
the ip range
format: cidr
type: string
x-kubernetes-validations:
Expand Down
51 changes: 24 additions & 27 deletions internal/controller/iprange_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (r *IpRangeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
if o.Spec.PreserveInNetbox {
// if there's a finalizer remove it and return
// this can be the case if a CR used to have spec.preserveInNetbox set to false
return r.removeFinalizer(ctx, o)
return ctrl.Result{}, r.removeFinalizer(ctx, o)
}

return r.deleteFromNetboxAndRemoveFinalizer(ctx, o)
Expand All @@ -96,13 +96,9 @@ func (r *IpRangeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
// 1. try to lock lease of parent prefix if IpRange status condition is not true
// and IpRange is owned by an IpRangeClaim
or := o.ObjectMeta.OwnerReferences
var ll *leaselocker.LeaseLocker
if len(or) > 0 /* len(nil array) = 0 */ && !apismeta.IsStatusConditionTrue(o.Status.Conditions, "Ready") {
ll, res, err := r.tryLockOnParentPrefix(ctx, o)
defer func() {
if ll != nil {
ll.Unlock()
}
}()
res, err := r.tryLockOnParentPrefix(ctx, ll, o)
if err != nil {
return res, err
}
Expand All @@ -129,6 +125,11 @@ func (r *IpRangeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
}
}

// 3. unlock lease of parent prefix
if ll != nil {
ll.Unlock()
}

// 4. update status fields
o.Status.IpRangeId = netboxIpRangeModel.ID
o.Status.IpRangeUrl = config.GetBaseUrl() + "/ipam/ip-ranges/" + strconv.FormatInt(netboxIpRangeModel.ID, 10)
Expand Down Expand Up @@ -185,7 +186,7 @@ func (r *IpRangeReconciler) SetConditionAndCreateEvent(ctx context.Context, o *n
}

func (r *IpRangeReconciler) generateNetboxIpRangeModelFromIpRangeSpec(ctx context.Context, o *netboxv1.IpRange, req ctrl.Request, lastIpRangeMetadata string) (*models.IpRange, error) {
debugLogger := log.FromContext(context.Background()).V(4)
debugLogger := log.FromContext(ctx).V(4)
// unmarshal lastIpRangeMetadata json string to map[string]string
lastAppliedCustomFields := make(map[string]string)
if lastIpRangeMetadata != "" {
Expand Down Expand Up @@ -242,9 +243,9 @@ func (r *IpRangeReconciler) deleteFromNetboxAndRemoveFinalizer(ctx context.Conte
return ctrl.Result{Requeue: true}, nil
}

res, err := r.removeFinalizer(ctx, o)
err = r.removeFinalizer(ctx, o)
if err != nil {
return res, err
return ctrl.Result{}, err
}

err = r.Update(ctx, o)
Expand All @@ -255,30 +256,30 @@ func (r *IpRangeReconciler) deleteFromNetboxAndRemoveFinalizer(ctx context.Conte
return ctrl.Result{}, nil
}

func (r *IpRangeReconciler) tryLockOnParentPrefix(ctx context.Context, o *netboxv1.IpRange) (*leaselocker.LeaseLocker, ctrl.Result, error) {
func (r *IpRangeReconciler) tryLockOnParentPrefix(ctx context.Context, ll *leaselocker.LeaseLocker, o *netboxv1.IpRange) (ctrl.Result, error) {

Check failure on line 259 in internal/controller/iprange_controller.go

View workflow job for this annotation

GitHub Actions / run

`(*IpRangeReconciler).tryLockOnParentPrefix` - `ll` is unused (unparam)
logger := log.FromContext(ctx)
debugLogger := log.FromContext(ctx).V(4)

// determine NamespacedName of IpRangeClaim owning the IpRange CR
orLookupKey := types.NamespacedName{
Name: o.ObjectMeta.OwnerReferences[0].Name,
Namespace: o.Namespace,
}

logger := log.FromContext(ctx)
debugLogger := log.FromContext(ctx).V(4)

ipRangeClaim := &netboxv1.IpRangeClaim{}
err := r.Client.Get(ctx, orLookupKey, ipRangeClaim)
if err != nil {
return nil, ctrl.Result{}, err
return ctrl.Result{}, err
}

// get name of parent prefix
leaseLockerNSN := types.NamespacedName{
Name: convertCIDRToLeaseLockName(ipRangeClaim.Spec.ParentPrefix),
Namespace: r.OperatorNamespace,
}
ll, err := leaselocker.NewLeaseLocker(r.RestConfig, leaseLockerNSN, orLookupKey.String())
ll, err = leaselocker.NewLeaseLocker(r.RestConfig, leaseLockerNSN, orLookupKey.String())

Check failure on line 280 in internal/controller/iprange_controller.go

View workflow job for this annotation

GitHub Actions / run

SA4009(related information): assignment to ll (staticcheck)
if err != nil {
return nil, ctrl.Result{}, err
return ctrl.Result{}, err
}

lockCtx, cancel := context.WithCancel(ctx)
Expand All @@ -290,11 +291,11 @@ func (r *IpRangeReconciler) tryLockOnParentPrefix(ctx context.Context, o *netbox
logger.Info(fmt.Sprintf("failed to lock parent prefix %s", ipRangeClaim.Spec.ParentPrefix))
r.Recorder.Eventf(o, corev1.EventTypeWarning, "FailedToLockParentPrefix", "failed to lock parent prefix %s",
ipRangeClaim.Spec.ParentPrefix)
return ll, ctrl.Result{}, nil
return ctrl.Result{}, nil
}
debugLogger.Info(fmt.Sprintf("successfully locked parent prefix %s", ipRangeClaim.Spec.ParentPrefix))

return ll, ctrl.Result{}, nil
return ctrl.Result{}, nil
}

func (r *IpRangeReconciler) updateLastMetadataAnnotation(ctx context.Context, annotations map[string]string, o *netboxv1.IpRange, accessor apismeta.MetadataAccessor) error {
Expand All @@ -320,22 +321,18 @@ func (r *IpRangeReconciler) updateLastMetadataAnnotation(ctx context.Context, an
}

// update object to store lastIpRangeMetadata annotation
if err := r.Update(ctx, o); err != nil {
return err
}

return nil
return r.Update(ctx, o)
}

func (r *IpRangeReconciler) removeFinalizer(ctx context.Context, o *netboxv1.IpRange) (ctrl.Result, error) {
func (r *IpRangeReconciler) removeFinalizer(ctx context.Context, o *netboxv1.IpRange) error {
debugLogger := log.FromContext(ctx).V(4)
if controllerutil.ContainsFinalizer(o, IpRangeFinalizerName) {
debugLogger.Info("removing the finalizer")
controllerutil.RemoveFinalizer(o, IpRangeFinalizerName)
if err := r.Update(ctx, o); err != nil {
return ctrl.Result{}, err
return err
}
}

return ctrl.Result{}, nil
return nil
}
112 changes: 56 additions & 56 deletions internal/controller/iprange_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,69 +16,69 @@ limitations under the License.

package controller

import (
"context"
// import (
// "context"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
// . "github.com/onsi/ginkgo/v2"
// . "github.com/onsi/gomega"
// "k8s.io/apimachinery/pkg/api/errors"
// "k8s.io/apimachinery/pkg/types"
// "sigs.k8s.io/controller-runtime/pkg/reconcile"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

netboxdevv1 "github.com/netbox-community/netbox-operator/api/v1"
)
// netboxdevv1 "github.com/netbox-community/netbox-operator/api/v1"
// )

var _ = Describe("IpRange Controller", func() {
Context("When reconciling a resource", func() {
const resourceName = "test-resource"
// var _ = Describe("IpRange Controller", func() {
// Context("When reconciling a resource", func() {
// const resourceName = "test-resource"

ctx := context.Background()
// ctx := context.Background()

typeNamespacedName := types.NamespacedName{
Name: resourceName,
Namespace: "default", // TODO(user):Modify as needed
}
iprange := &netboxdevv1.IpRange{}
// typeNamespacedName := types.NamespacedName{
// Name: resourceName,
// Namespace: "default", // TODO(user):Modify as needed
// }
// iprange := &netboxdevv1.IpRange{}

BeforeEach(func() {
By("creating the custom resource for the Kind IpRange")
err := k8sClient.Get(ctx, typeNamespacedName, iprange)
if err != nil && errors.IsNotFound(err) {
resource := &netboxdevv1.IpRange{
ObjectMeta: metav1.ObjectMeta{
Name: resourceName,
Namespace: "default",
},
// TODO(user): Specify other spec details if needed.
}
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
}
})
// BeforeEach(func() {
// By("creating the custom resource for the Kind IpRange")
// err := k8sClient.Get(ctx, typeNamespacedName, iprange)
// if err != nil && errors.IsNotFound(err) {
// resource := &netboxdevv1.IpRange{
// ObjectMeta: metav1.ObjectMeta{
// Name: resourceName,
// Namespace: "default",
// },
// // TODO(user): Specify other spec details if needed.
// }
// Expect(k8sClient.Create(ctx, resource)).To(Succeed())
// }
// })

AfterEach(func() {
// TODO(user): Cleanup logic after each test, like removing the resource instance.
resource := &netboxdevv1.IpRange{}
err := k8sClient.Get(ctx, typeNamespacedName, resource)
Expect(err).NotTo(HaveOccurred())
// AfterEach(func() {
// // TODO(user): Cleanup logic after each test, like removing the resource instance.
// resource := &netboxdevv1.IpRange{}
// err := k8sClient.Get(ctx, typeNamespacedName, resource)
// Expect(err).NotTo(HaveOccurred())

By("Cleanup the specific resource instance IpRange")
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
})
It("should successfully reconcile the resource", func() {
By("Reconciling the created resource")
controllerReconciler := &IpRangeReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
}
// By("Cleanup the specific resource instance IpRange")
// Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
// })
// It("should successfully reconcile the resource", func() {
// By("Reconciling the created resource")
// controllerReconciler := &IpRangeReconciler{
// Client: k8sClient,
// Scheme: k8sClient.Scheme(),
// }

_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})
Expect(err).NotTo(HaveOccurred())
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
// Example: If you expect a certain status condition after reconciliation, verify it here.
})
})
})
// _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
// NamespacedName: typeNamespacedName,
// })
// Expect(err).NotTo(HaveOccurred())
// // TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
// // Example: If you expect a certain status condition after reconciliation, verify it here.
// })
// })
// })
Loading

0 comments on commit 549e32a

Please sign in to comment.