Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ go.work

# custom
bin/
crd/
/crd/
hack/deploy/cluster.yaml
helm/
build/
Expand Down
4 changes: 2 additions & 2 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,9 @@ func capiCRDExists(k8sClient client.Client) (bool, error) {
if err != nil {
if k8serrors.IsNotFound(err) {
return false, nil
} else {
return false, err
}

return false, err
}
return true, nil
}
2 changes: 1 addition & 1 deletion config/crd/bases/argora.cloud.sap_clusterimports.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.1
controller-gen.kubebuilder.io/version: v0.20.0
name: clusterimports.argora.cloud.sap
spec:
group: argora.cloud.sap
Expand Down
2 changes: 1 addition & 1 deletion config/crd/bases/argora.cloud.sap_ippoolimports.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.1
controller-gen.kubebuilder.io/version: v0.20.0
name: ippoolimports.argora.cloud.sap
spec:
group: argora.cloud.sap
Expand Down
2 changes: 1 addition & 1 deletion config/crd/bases/argora.cloud.sap_updates.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.1
controller-gen.kubebuilder.io/version: v0.20.0
name: updates.argora.cloud.sap
spec:
group: argora.cloud.sap
Expand Down
17 changes: 17 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,20 @@ rules:
- patch
- update
- watch
- apiGroups:
- ipam.cluster.x-k8s.io
resources:
- ipaddressclaims
verbs:
- get
- list
- watch
- apiGroups:
- ipam.cluster.x-k8s.io
resources:
- ipaddresses
verbs:
- get
- list
- patch
- update
Expand All @@ -119,6 +128,14 @@ rules:
- patch
- update
- watch
- apiGroups:
- metal.ironcore.dev
resources:
- serverclaims
verbs:
- get
- list
- watch
- apiGroups:
- metal3.io
resources:
Expand Down
67 changes: 66 additions & 1 deletion internal/controller/ipupdate_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import (

metalv1alpha1 "github.com/ironcore-dev/metal-operator/api/v1alpha1"
"github.com/sapcc/go-netbox-go/models"
apierrors "k8s.io/apimachinery/pkg/api/errors"
ipamv1 "sigs.k8s.io/cluster-api/api/ipam/v1beta2"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

"github.com/sapcc/argora/internal/credentials"
"github.com/sapcc/argora/internal/netbox"
Expand All @@ -26,6 +28,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
)

const ipAddressFinalizer = "ipupdate.argora.cloud.sap.com/finalizer"

// IPUpdateReconciler reconciles a ipam.cluster.x-k8s.io.IPAddress object
type IPUpdateReconciler struct {
k8sClient client.Client
Expand Down Expand Up @@ -62,10 +66,15 @@ func (r *IPUpdateReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
ipAddress := &ipamv1.IPAddress{}
err := r.k8sClient.Get(ctx, req.NamespacedName, ipAddress)
if err != nil {
if apierrors.IsNotFound(err) {
return ctrl.Result{}, nil
}
logger.Error(err, "unable to get IP Address CR")
return ctrl.Result{}, err
}

logger = logger.WithValues("ipAddress", getIPWithMask(ipAddress))

err = r.credentials.Reload()
if err != nil {
logger.Error(err, "unable to reload credentials")
Expand All @@ -80,13 +89,42 @@ func (r *IPUpdateReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
return ctrl.Result{}, err
}

if !ipAddress.DeletionTimestamp.IsZero() {
ctx = log.IntoContext(ctx, logger)

if err = r.reconcileDelete(ctx, ipAddress); err != nil {
logger.Error(err, "unable to delete IP Address")
return ctrl.Result{}, err
}

base := ipAddress.DeepCopy()
if removed := controllerutil.RemoveFinalizer(ipAddress, ipAddressFinalizer); removed {
if err := r.k8sClient.Patch(ctx, ipAddress, client.MergeFrom(base)); err != nil {
logger.Error(err, "unable to remove finalizer")
return ctrl.Result{}, err
}
}

return ctrl.Result{}, nil
}

base := ipAddress.DeepCopy()
if added := controllerutil.AddFinalizer(ipAddress, ipAddressFinalizer); added {
if err := r.k8sClient.Patch(ctx, ipAddress, client.MergeFrom(base)); err != nil {
logger.Error(err, "unable to add finalizer")
return ctrl.Result{}, err
}

return ctrl.Result{}, nil
}

deviceName, err := r.findDeviceName(ctx, req.Namespace, ipAddress)
if err != nil {
logger.Error(err, "unable to find device name")
return ctrl.Result{}, err
}

logger = logger.WithValues("deviceName", deviceName, "deviceIP", ipAddress.Spec.Address)
logger = logger.WithValues("deviceName", deviceName)
logger.Info("device found")

device, err := r.netBox.DCIM().GetDeviceByName(deviceName)
Expand Down Expand Up @@ -117,6 +155,33 @@ func (r *IPUpdateReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
return ctrl.Result{}, nil
}

func getIPWithMask(ipaddr *ipamv1.IPAddress) string {
if ipaddr == nil || ipaddr.Spec.Prefix == nil {
return ""
}

return fmt.Sprintf("%s/%d", ipaddr.Spec.Address, *ipaddr.Spec.Prefix)
}

func (r *IPUpdateReconciler) reconcileDelete(ctx context.Context, ipAddr *ipamv1.IPAddress) error {
logger := log.FromContext(ctx)
logger.Info("deleting IP Address")

ipStr := getIPWithMask(ipAddr)

nbIP, err := r.netBox.IPAM().GetIPAddressByAddress(ipStr)
if err != nil {
logger.Error(err, "unable to find IP netbox ip address, NetBox IP will not be removed")
return nil
}
Comment on lines +172 to +176
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should check for ErrNotFound here and if so we handle it as a valid case that IP was already removed from NetBox by someone
And if there is another kind of err (400/500/502) we should return err, so Reconcile() will be called again

But I did not found any existing implementation of IsErrNotFound() in NetBox client.
Should I add this check by strings parsing or smth for better error handling here?

Or we do not allow the case when IP was already deleted by someone, so we should return error here anyways?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we should not handle it as error case if the IPAddress already removed from netbox and also not assigned to the LAG interface.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about how, I would ask Peter Gehrt for such details.


if err = r.netBox.IPAM().DeleteIPAddress(nbIP.ID); err != nil {
return fmt.Errorf("delete ip from netbox: %w", err)
}

return nil
}

func (r *IPUpdateReconciler) findDeviceName(ctx context.Context, namespace string, ipAddr *ipamv1.IPAddress) (string, error) {
claimName := ipAddr.Spec.ClaimRef.Name

Expand Down
Loading