Skip to content
Draft
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
12 changes: 12 additions & 0 deletions api/v1alpha1/acl_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ func (acl *AccessControlList) SetConditions(conditions []metav1.Condition) {
acl.Status.Conditions = conditions
}

func (acl *AccessControlList) GetDeviceRef() LocalObjectReference {
return acl.Spec.DeviceRef
}

func (acl *AccessControlList) GetProviderConfigRef() *TypedLocalObjectReference {
return acl.Spec.ProviderConfigRef
}

func (acl *AccessControlList) GetStatus() any {
return acl.Status
}

// +kubebuilder:object:root=true

// AccessControlListList contains a list of AccessControlList
Expand Down
159 changes: 12 additions & 147 deletions internal/controller/acl_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ import (
"context"
"fmt"

"k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
kerrors "k8s.io/apimachinery/pkg/util/errors"
Expand All @@ -21,7 +18,6 @@ import (

"github.com/ironcore-dev/network-operator/api/v1alpha1"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
"github.com/ironcore-dev/network-operator/internal/provider"
)

Expand All @@ -46,129 +42,6 @@ type AccessControlListReconciler struct {
// +kubebuilder:rbac:groups=networking.cloud.sap,resources=accesscontrollists/finalizers,verbs=update
// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/reconcile
//
// For more details about the method shape, read up here:
// - https://ahmet.im/blog/controller-pitfalls/#reconcile-method-shape
func (r *AccessControlListReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
log := ctrl.LoggerFrom(ctx)
log.Info("Reconciling resource")

obj := new(v1alpha1.AccessControlList)
if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
if apierrors.IsNotFound(err) {
// If the custom resource is not found then it usually means that it was deleted or not created
// In this way, we will stop the reconciliation
log.Info("Resource not found. Ignoring since object must be deleted")
return ctrl.Result{}, nil
}
// Error reading the object - requeue the request.
log.Error(err, "Failed to get resource")
return ctrl.Result{}, err
}

prov, ok := r.Provider().(provider.ACLProvider)
if !ok {
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
Reason: v1alpha1.NotImplementedReason,
Message: "Provider does not implement provider.AccessControlListProvider",
}) {
return ctrl.Result{}, r.Status().Update(ctx, obj)
}
return ctrl.Result{}, nil
}

device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name)
if err != nil {
return ctrl.Result{}, err
}

conn, err := deviceutil.GetDeviceConnection(ctx, r, device)
if err != nil {
return ctrl.Result{}, err
}

var cfg *provider.ProviderConfig
if obj.Spec.ProviderConfigRef != nil {
cfg, err = provider.GetProviderConfig(ctx, r, obj.Namespace, obj.Spec.ProviderConfigRef)
if err != nil {
return ctrl.Result{}, err
}
}

s := &aclScope{
Device: device,
ACL: obj,
Connection: conn,
ProviderConfig: cfg,
Provider: prov,
}

if !obj.DeletionTimestamp.IsZero() {
if controllerutil.ContainsFinalizer(obj, v1alpha1.FinalizerName) {
if err := r.finalize(ctx, s); err != nil {
log.Error(err, "Failed to finalize resource")
return ctrl.Result{}, err
}
controllerutil.RemoveFinalizer(obj, v1alpha1.FinalizerName)
if err := r.Update(ctx, obj); err != nil {
log.Error(err, "Failed to remove finalizer from resource")
return ctrl.Result{}, err
}
}
log.Info("Resource is being deleted, skipping reconciliation")
return ctrl.Result{}, nil
}

// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers
if !controllerutil.ContainsFinalizer(obj, v1alpha1.FinalizerName) {
controllerutil.AddFinalizer(obj, v1alpha1.FinalizerName)
if err := r.Update(ctx, obj); err != nil {
log.Error(err, "Failed to add finalizer to resource")
return ctrl.Result{}, err
}
log.Info("Added finalizer to resource")
return ctrl.Result{}, nil
}

orig := obj.DeepCopy()
if conditions.InitializeConditions(obj, v1alpha1.ReadyCondition) {
log.Info("Initializing status conditions")
return ctrl.Result{}, r.Status().Update(ctx, obj)
}

// Always attempt to update the metadata/status after reconciliation
defer func() {
if !equality.Semantic.DeepEqual(orig.ObjectMeta, obj.ObjectMeta) {
if err := r.Patch(ctx, obj, client.MergeFrom(orig)); err != nil {
log.Error(err, "Failed to update resource metadata")
reterr = kerrors.NewAggregate([]error{reterr, err})
}
return
}

if !equality.Semantic.DeepEqual(orig.Status, obj.Status) {
if err := r.Status().Patch(ctx, obj, client.MergeFrom(orig)); err != nil {
log.Error(err, "Failed to update status")
reterr = kerrors.NewAggregate([]error{reterr, err})
}
}
}()

if err := r.reconcile(ctx, s); err != nil {
log.Error(err, "Failed to reconcile resource")
return ctrl.Result{}, err
}

return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *AccessControlListReconciler) SetupWithManager(mgr ctrl.Manager) error {
labelSelector := metav1.LabelSelector{}
Expand All @@ -181,32 +54,24 @@ func (r *AccessControlListReconciler) SetupWithManager(mgr ctrl.Manager) error {
return fmt.Errorf("failed to create label selector predicate: %w", err)
}

rec := AsReconciler(r.Client, r.Provider, r)
return ctrl.NewControllerManagedBy(mgr).
For(&v1alpha1.AccessControlList{}).
Named("accesscontrollist").
WithEventFilter(filter).
Complete(r)
}

// scope holds the different objects that are read and used during the reconcile.
type aclScope struct {
Device *v1alpha1.Device
ACL *v1alpha1.AccessControlList
Connection *deviceutil.Connection
ProviderConfig *provider.ProviderConfig
Provider provider.ACLProvider
Complete(rec)
}

func (r *AccessControlListReconciler) reconcile(ctx context.Context, s *aclScope) (reterr error) {
if s.ACL.Labels == nil {
s.ACL.Labels = make(map[string]string)
func (r *AccessControlListReconciler) Reconcile(ctx context.Context, s *TypedScope[*v1alpha1.AccessControlList, provider.ACLProvider]) (reterr error) {
if s.Resource.Labels == nil {
s.Resource.Labels = make(map[string]string)
}

s.ACL.Labels[v1alpha1.DeviceLabel] = s.Device.Name
s.Resource.Labels[v1alpha1.DeviceLabel] = s.Device.Name

// Ensure the AccessControlList is owned by the Device.
if !controllerutil.HasControllerReference(s.ACL) {
if err := controllerutil.SetOwnerReference(s.Device, s.ACL, r.Scheme, controllerutil.WithBlockOwnerDeletion(true)); err != nil {
if !controllerutil.HasControllerReference(s.Resource) {
if err := controllerutil.SetOwnerReference(s.Device, s.Resource, r.Scheme, controllerutil.WithBlockOwnerDeletion(true)); err != nil {
return err
}
}
Expand All @@ -222,19 +87,19 @@ func (r *AccessControlListReconciler) reconcile(ctx context.Context, s *aclScope

// Ensure the AccessControlList is realized on the provider.
err := s.Provider.EnsureACL(ctx, &provider.EnsureACLRequest{
ACL: s.ACL,
ACL: s.Resource,
ProviderConfig: s.ProviderConfig,
})

cond := conditions.FromError(err)
// As this resource is configuration only, we use the Configured condition as top-level Ready condition.
cond.Type = v1alpha1.ReadyCondition
conditions.Set(s.ACL, cond)
conditions.Set(s.Resource, cond)

return err
}

func (r *AccessControlListReconciler) finalize(ctx context.Context, s *aclScope) (reterr error) {
func (r *AccessControlListReconciler) Finalize(ctx context.Context, s *TypedScope[*v1alpha1.AccessControlList, provider.ACLProvider]) (reterr error) {
if err := s.Provider.Connect(ctx, s.Connection); err != nil {
return fmt.Errorf("failed to connect to provider: %w", err)
}
Expand All @@ -245,7 +110,7 @@ func (r *AccessControlListReconciler) finalize(ctx context.Context, s *aclScope)
}()

return s.Provider.DeleteACL(ctx, &provider.DeleteACLRequest{
Name: s.ACL.Spec.Name,
Name: s.Resource.Spec.Name,
ProviderConfig: s.ProviderConfig,
})
}
Loading
Loading