Skip to content

Commit

Permalink
NamespaceMap Controller refactor
Browse files Browse the repository at this point in the history
- When there is a problem in remote namespace creation onto a remote cluster, the associated NamespaceMap is updated with a Refused phase for that namespace (Status->CurrentMapping->NamespaceEntry->RemoteStatus->Phase=Refused). The NamespaceMap Controller will try to create that Namespace until the problem will be solved with an exponential backoff.
- The controller updates the remote namespace condition in the NamespaceOffloading resource associated with the just created remote namespace.
  • Loading branch information
Andreagit97 committed May 21, 2021
1 parent 8476758 commit 9218feb
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 28 deletions.
7 changes: 7 additions & 0 deletions pkg/consts/namespaceMapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,11 @@ const (
NamespaceMapControllerFinalizer = "namespacemap-controller.liqo.io/finalizer"
// DocumentationURL is the URL to official Liqo Documentation.
DocumentationURL = "https://doc.liqo.io/"
// DefaultNameNamespaceOffloading is the default name of NamespaceOffloading resources, every namespace that have
// to be offloaded with Liqo, must have a NamespaceOffloading resource with this name.
DefaultNameNamespaceOffloading = "offloading"
// EnablingLiqoLabel is necessary in order to allow Pods to be scheduled on remote clusters.
EnablingLiqoLabel = "liqo.io/enabled"
// EnablingLiqoLabelValue unique value allowed for EnablingLiqoLabel.
EnablingLiqoLabelValue = "true"
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package namespacemapctrl

import (
"context"
"fmt"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -58,49 +59,58 @@ func (r *NamespaceMapReconciler) deleteRemoteNamespace(clusterID, remoteName str
}

// This function checks if there are Namespaces that have to be created or deleted, in accordance with DesiredMapping
// and CurrentMapping fields.
func (r *NamespaceMapReconciler) manageRemoteNamespaces(nm *mapsv1alpha1.NamespaceMap) error {
// and CurrentMapping fields. It updates also NamespaceOffloading status in according to the remote Namespace Status.
func (r *NamespaceMapReconciler) manageRemoteNamespaces(nm *mapsv1alpha1.NamespaceMap, requeue *bool) error {
if nm.Status.CurrentMapping == nil {
nm.Status.CurrentMapping = map[string]mapsv1alpha1.RemoteNamespaceStatus{}
}

// if DesiredMapping field has more entries than CurrentMapping, is necessary to create new remote namespaces
for localName, remoteName := range nm.Spec.DesiredMapping {
if _, ok := nm.Status.CurrentMapping[localName]; ok {
if remoteStatus, ok := nm.Status.CurrentMapping[localName]; ok && remoteStatus.Phase == mapsv1alpha1.MappingAccepted {
continue
}
namespacePhase := mapsv1alpha1.MappingAccepted
message := "Your remote namespace is correctly created on this cluster"
if err := r.createRemoteNamespace(nm.Labels[liqoconst.RemoteClusterID], remoteName); err != nil {
return err
namespacePhase = mapsv1alpha1.MappingCreationLoopBackOff
message = fmt.Sprintf("%s", err)
*requeue = true
klog.Errorf("%s -> Namespace '%s' cannot be created on cluster '%s'",
err, localName, nm.Labels[liqoconst.RemoteClusterID])
}
nm.Status.CurrentMapping[localName] = mapsv1alpha1.RemoteNamespaceStatus{
RemoteNamespace: remoteName,
Phase: mapsv1alpha1.MappingAccepted,
Phase: namespacePhase,
}
if err := r.Update(context.TODO(), nm); err != nil {
klog.Errorf("unable to update NamespaceMap '%s' Status", nm.Name)

if err := r.setReadyConditionOnRemoteNamespace(localName, message,
nm.Labels[liqoconst.RemoteClusterID], namespacePhase); err != nil {
return err
}
klog.Infof("Status of NamespaceMap '%s' is correctly updated, new remote Namespace '%s'",
nm.Name, remoteName)
}

// if DesiredMapping field has less entries than CurrentMapping, is necessary to remove some remote namespaces
// If DesiredMapping field has less entries than CurrentMapping, is necessary to remove some remote namespaces
for localName, remoteStatus := range nm.Status.CurrentMapping {
if _, ok := nm.Spec.DesiredMapping[localName]; ok {
continue
}
if err := r.deleteRemoteNamespace(nm.Labels[liqoconst.RemoteClusterID],
remoteStatus.RemoteNamespace); err != nil {
return err
// If exist, delete remote Namespaces
if remoteStatus.Phase == mapsv1alpha1.MappingAccepted {
if err := r.deleteRemoteNamespace(nm.Labels[liqoconst.RemoteClusterID],
remoteStatus.RemoteNamespace); err != nil {
return err
}
klog.Infof("Remote Namespace '%s' successfully deleted.", remoteStatus.RemoteNamespace)
}
// Update Map status
delete(nm.Status.CurrentMapping, localName)
if err := r.Update(context.TODO(), nm); err != nil {
klog.Errorf("unable to update NamespaceMap '%s' Status", nm.Name)
return err
}
klog.Infof("Status of NamespaceMap '%s' is correctly updated.", nm.Name)
klog.Infof("Delete remote Namespace '%s'.", remoteStatus.RemoteNamespace)
}

if err := r.Update(context.TODO(), nm); err != nil {
klog.Errorf("unable to update NamespaceMap '%s' Status", nm.Name)
return err
}
klog.Infof("Status of the NamespaceMap '%s' is correctly updated", nm.Name)

return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package namespacemapctrl

import (
"context"
"fmt"

"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -32,7 +33,7 @@ import (
liqoconst "github.com/liqotech/liqo/pkg/consts"
)

// NamespaceMapReconciler creates remote namespaces and updates NamespaceMaps.
// NamespaceMapReconciler creates remote namespaces and updates NamespaceMaps and NamespaceOffloading Status.
type NamespaceMapReconciler struct {
client.Client
Scheme *runtime.Scheme
Expand All @@ -45,42 +46,46 @@ const (
)

// Reconcile adds/removes NamespaceMap finalizer, and checks differences
// between DesiredMapping and CurrentMapping.
// between DesiredMapping and CurrentMapping in order to create/delete remote Namespaces if it is necessary.
func (r *NamespaceMapReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
requeue := false
namespaceMap := &mapsv1alpha1.NamespaceMap{}
if err := r.Get(context.TODO(), req.NamespacedName, namespaceMap); err != nil {
if err := r.Get(ctx, req.NamespacedName, namespaceMap); err != nil {
klog.Errorf("%s --> Unable to get NamespaceMap '%s'", err, req.Name)
return ctrl.Result{}, client.IgnoreNotFound(err)
}

if !ctrlutils.ContainsFinalizer(namespaceMap, liqoconst.NamespaceMapControllerFinalizer) {
ctrlutils.AddFinalizer(namespaceMap, liqoconst.NamespaceMapControllerFinalizer)
if err := r.Patch(context.TODO(), namespaceMap, client.Merge); err != nil {
if err := r.Patch(ctx, namespaceMap, client.Merge); err != nil {
klog.Errorf("%s --> Unable to add finalizer to the NamespaceMap '%s'", err, namespaceMap.GetName())
return ctrl.Result{}, err
}
klog.Infof("Finalizer correctly added on NamespaceMap '%s'", namespaceMap.GetName())
}

if err := r.manageRemoteNamespaces(namespaceMap); err != nil {
if err := r.manageRemoteNamespaces(namespaceMap, &requeue); err != nil {
return ctrl.Result{}, err
}

if len(namespaceMap.Status.CurrentMapping) == 0 {
ctrlutils.RemoveFinalizer(namespaceMap, liqoconst.NamespaceMapControllerFinalizer)
if err := r.Update(context.TODO(), namespaceMap); err != nil {
if err := r.Update(ctx, namespaceMap); err != nil {
klog.Errorf("%s --> Unable to remove '%s' from NamespaceMap '%s'", err,
liqoconst.NamespaceMapControllerFinalizer, namespaceMap.GetName())
return ctrl.Result{}, err
}
klog.Infof("Finalizer correctly removed from NamespaceMap '%s'", namespaceMap.GetName())
}

if requeue {
return ctrl.Result{}, fmt.Errorf("unable to create some remote namespace")
}
return ctrl.Result{}, nil
}

// The Controller is triggered only when the number of entries in DesiredMapping changes, so only when
// a namespace's request is added or removed.
// Events not filtered:
// 1 -- the number of entries in DesiredMapping changes, so only when a namespace's request is added or removed.
func manageDesiredMappings() predicate.Predicate {
return predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package namespacemapctrl

import (
"context"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/klog"

offv1alpha1 "github.com/liqotech/liqo/apis/offloading/v1alpha1"
mapsv1alpha1 "github.com/liqotech/liqo/apis/virtualKubelet/v1alpha1"
liqoconst "github.com/liqotech/liqo/pkg/consts"
liqoutils "github.com/liqotech/liqo/pkg/utils"
)

func (r *NamespaceMapReconciler) setReadyConditionOnRemoteNamespace(localName, message, clusterID string,
namespacePhase mapsv1alpha1.MappingPhase) error {
namespaceOffloading := &offv1alpha1.NamespaceOffloading{}
if err := r.Get(context.TODO(), types.NamespacedName{
Namespace: localName,
Name: liqoconst.DefaultNameNamespaceOffloading,
}, namespaceOffloading); err != nil {
klog.Errorf("%s -> unable to get namespaceOffloading for the namespace '%s'",
err, localName)
return err
}

var remoteNamespaceCondition offv1alpha1.RemoteNamespaceCondition
remoteNamespaceCondition.Type = offv1alpha1.NamespaceReady
remoteNamespaceCondition.Message = message
if namespacePhase == mapsv1alpha1.MappingAccepted {
remoteNamespaceCondition.Status = corev1.ConditionTrue
remoteNamespaceCondition.Reason = "RemoteNamespaceCreated"
} else {
remoteNamespaceCondition.Status = corev1.ConditionFalse
remoteNamespaceCondition.Reason = "RemoteNamespaceNotCreated"
}

_ = liqoutils.UpdateNamespaceOffloadingCondition(&namespaceOffloading.Status,
&remoteNamespaceCondition,
clusterID)

if err := r.Update(context.TODO(), namespaceOffloading); err != nil {
klog.Errorf("%s -> unable to update namespaceOffloading for the namespace '%s'",
err, localName)
return err
}

return nil
}

0 comments on commit 9218feb

Please sign in to comment.