-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This controller gets a just created NamespaceOffloading resource and adds entries to NamespaceMaps (Spec->DesiredMappings) in order to require the creation of remote namespaces, based on requirements specified by users. When the NamespaceOffloading resource is deleted also the remote namespaces are requested to be deleted, so the entries in NamespaceMaps are removed.
- Loading branch information
1 parent
0c73e78
commit 20080ed
Showing
6 changed files
with
344 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
pkg/liqo-controller-manager/namespaceOffloading-controller/doc.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Package namespaceoffloadingctrl contains NamespaceOffloading Controller logic and some functions for adding | ||
// DesiredMappings to NamespaceMaps Spec | ||
package namespaceoffloadingctrl |
76 changes: 76 additions & 0 deletions
76
pkg/liqo-controller-manager/namespaceOffloading-controller/manage_desiredMappings.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/* | ||
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 namespaceoffloadingctrl | ||
|
||
import ( | ||
"context" | ||
|
||
"k8s.io/klog" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
mapsv1alpha1 "github.com/liqotech/liqo/apis/virtualKubelet/v1alpha1" | ||
) | ||
|
||
// Removes right entry from one NamespaceMap. | ||
func (r *NamespaceOffloadingReconciler) removeDesiredMapping(localName string, nm *mapsv1alpha1.NamespaceMap) error { | ||
if _, ok := nm.Spec.DesiredMapping[localName]; ok { | ||
delete(nm.Spec.DesiredMapping, localName) | ||
if err := r.Update(context.TODO(), nm); err != nil { | ||
klog.Errorf("%s --> Unable to update NamespaceMap '%s'", err, nm.GetName()) | ||
return err | ||
} | ||
klog.Infof(" Entries deleted correctly from '%s'", nm.GetName()) | ||
} | ||
return nil | ||
} | ||
|
||
// Removes right entries from more than one NamespaceMap (it depends on len(nms)). | ||
func (r *NamespaceOffloadingReconciler) removeDesiredMappings(localName string, nms map[string]*mapsv1alpha1.NamespaceMap) error { | ||
for _, nm := range nms { | ||
if err := r.removeDesiredMapping(localName, nm); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// Adds right entry on one NamespaceMap, if it isn't already there. | ||
func (r *NamespaceOffloadingReconciler) addDesiredMapping(localName, remoteName string, | ||
nm *mapsv1alpha1.NamespaceMap) error { | ||
if nm.Spec.DesiredMapping == nil { | ||
nm.Spec.DesiredMapping = map[string]string{} | ||
} | ||
|
||
if _, ok := nm.Spec.DesiredMapping[localName]; !ok { | ||
nm.Spec.DesiredMapping[localName] = remoteName | ||
if err := r.Patch(context.TODO(), nm, client.Merge); err != nil { | ||
klog.Errorf("%s --> Unable to add entry for namespace '%s' on NamespaceMap '%s'", | ||
err, localName, nm.GetName()) | ||
return err | ||
} | ||
klog.Infof("Entry for namespace '%s' successfully added on NamespaceMap '%s' ", | ||
localName, nm.GetName()) | ||
} | ||
return nil | ||
} | ||
|
||
// Adds right entries on more than one NamespaceMap (it depends on len(nms)), if entries aren't already there. | ||
func (r *NamespaceOffloadingReconciler) addDesiredMappings(localName, remoteName string, | ||
nms map[string]*mapsv1alpha1.NamespaceMap) error { | ||
for _, nm := range nms { | ||
if err := r.addDesiredMapping(localName, remoteName, nm); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} |
256 changes: 256 additions & 0 deletions
256
pkg/liqo-controller-manager/namespaceOffloading-controller/namespaceOffloading_controller.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
/* | ||
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 namespaceoffloadingctrl | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
corev1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/types" | ||
k8shelper "k8s.io/component-helpers/scheduling/corev1" | ||
"k8s.io/klog" | ||
"k8s.io/kubernetes/pkg/util/slice" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
ctrlutils "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" | ||
"sigs.k8s.io/controller-runtime/pkg/event" | ||
"sigs.k8s.io/controller-runtime/pkg/predicate" | ||
|
||
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" | ||
) | ||
|
||
// NamespaceOffloadingReconciler changes DesiredMapping field of NamespaceMaps if a remote Namespace must be | ||
// created or deleted. | ||
type NamespaceOffloadingReconciler struct { | ||
client.Client | ||
Scheme *runtime.Scheme | ||
} | ||
|
||
const ( | ||
namespaceOffloadingControllerFinalizer = "namespaceoffloading-controller.liqo.io/finalizer" | ||
) | ||
|
||
// Reconcile requires creation of remote namespaces according to ClusterSelector field. | ||
func (r *NamespaceOffloadingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { | ||
namespaceOffloading := &offv1alpha1.NamespaceOffloading{} | ||
if err := r.Get(context.TODO(), types.NamespacedName{ | ||
Namespace: req.Namespace, | ||
Name: liqoconst.DefaultNameNamespaceOffloading, | ||
}, namespaceOffloading); err != nil { | ||
klog.Errorf("%s --> Unable to get namespaceOffloading for the namespace '%s'", err, req.Namespace) | ||
return ctrl.Result{}, client.IgnoreNotFound(err) | ||
} | ||
|
||
// get all NamespaceMaps in the clsuter | ||
clusterNamespaceMaps := &mapsv1alpha1.NamespaceMapList{} | ||
if err := r.List(context.TODO(), clusterNamespaceMaps); err != nil { | ||
klog.Error(err, " --> Unable to List NamespaceMaps") | ||
return ctrl.Result{}, err | ||
} | ||
|
||
if len(clusterNamespaceMaps.Items) == 0 { | ||
err := fmt.Errorf("no NamespaceMaps at the moment in the cluster") | ||
klog.Info(err) | ||
return ctrl.Result{}, err | ||
} | ||
|
||
clusterWithNoOffloading := make(map[string]*mapsv1alpha1.NamespaceMap) | ||
for i := range clusterNamespaceMaps.Items { | ||
clusterWithNoOffloading[clusterNamespaceMaps.Items[i].Labels[liqoconst.RemoteClusterID]] = &clusterNamespaceMaps.Items[i] | ||
} | ||
|
||
// get Namespace associated with this NamespaceOffloading Resource | ||
namespace := &corev1.Namespace{} | ||
if err := r.Get(context.TODO(), types.NamespacedName{ | ||
Name: namespaceOffloading.Namespace, | ||
}, namespace); err != nil { | ||
klog.Errorf("%s --> Unable to get the namespace '%s'", err, namespaceOffloading.Namespace) | ||
return ctrl.Result{}, err | ||
} | ||
|
||
// Remove DesiredMappings, namespaceOffloadingControllerFinalizer, and Liqo label from associated Namespace | ||
if !namespaceOffloading.GetDeletionTimestamp().IsZero() { | ||
// remove DesiredMappings, namespaceOffloadingControllerFinalizer | ||
klog.Infof("The namespaceOffloading of namespace '%s' is requested to be deleted", | ||
namespaceOffloading.Namespace) | ||
if err := r.removeDesiredMappings(namespaceOffloading.Namespace, clusterWithNoOffloading); err != nil { | ||
return ctrl.Result{}, err | ||
} | ||
|
||
// remove Liqo label from the associated Namespace | ||
delete(namespace.Labels, liqoconst.EnablingLiqoLabel) | ||
if err := r.Update(context.TODO(), namespace); err != nil { | ||
klog.Errorf(" %s --> Unable to remove Liqo label from the namespace '%s'", err, namespace.GetName()) | ||
return ctrl.Result{}, err | ||
} | ||
|
||
ctrlutils.RemoveFinalizer(namespaceOffloading, namespaceOffloadingControllerFinalizer) | ||
if err := r.Update(context.TODO(), namespaceOffloading); err != nil { | ||
klog.Errorf("%s --> Unable to remove finalizer from namespaceOffloading", err) | ||
return ctrl.Result{}, err | ||
} | ||
klog.Info("Finalizer removed from namespaceOffloading") | ||
return ctrl.Result{}, nil | ||
} | ||
|
||
// If there is no namespaceOffloadingControllerFinalizer, we have to perform some initialization | ||
if !ctrlutils.ContainsFinalizer(namespaceOffloading, namespaceOffloadingControllerFinalizer) { | ||
// 1 - Add namespaceOffloadingControllerFinalizer | ||
ctrlutils.AddFinalizer(namespaceOffloading, namespaceOffloadingControllerFinalizer) | ||
// 2 - Add empty cluster selector if not specified by the user | ||
if namespaceOffloading.Spec.ClusterSelector.Size() == 0 { | ||
namespaceOffloading.Spec.ClusterSelector = corev1.NodeSelector{NodeSelectorTerms: []corev1.NodeSelectorTerm{}} | ||
} | ||
// 3 - Add remoteNamespaceName | ||
switch { | ||
case namespaceOffloading.Spec.NamespaceMappingStrategy == offv1alpha1.EnforceSameNameMappingStrategyType: | ||
namespaceOffloading.Status.RemoteNamespaceName = namespaceOffloading.Namespace | ||
default: | ||
clusterID, err := liqoutils.GetClusterID(r.Client) | ||
if err != nil { | ||
return ctrl.Result{}, err | ||
} | ||
namespaceOffloading.Status.RemoteNamespaceName = fmt.Sprintf("%s-%s", namespaceOffloading.Namespace, clusterID) | ||
} | ||
// 4 - Set global OffloadingPhase=InProgress | ||
namespaceOffloading.Status.OffloadingPhase = offv1alpha1.InProgressOffloadingPhaseType | ||
|
||
if err := r.Update(ctx, namespaceOffloading); err != nil { | ||
klog.Errorf("%s --> Unable to update NamespaceOffloading in namespace '%s'", | ||
err, namespaceOffloading.Namespace) | ||
return ctrl.Result{}, err | ||
} | ||
} | ||
|
||
// Add Liqo label to associated Namespace, if it is not already there | ||
if value, ok := namespace.Labels[liqoconst.EnablingLiqoLabel]; !ok || value != liqoconst.EnablingLiqoLabelValue { | ||
if namespace.Labels == nil { | ||
namespace.Labels = map[string]string{} | ||
} | ||
namespace.Labels[liqoconst.EnablingLiqoLabel] = liqoconst.EnablingLiqoLabelValue | ||
if err := r.Patch(context.TODO(), namespace, client.Merge); err != nil { | ||
klog.Errorf(" %s --> Unable to add liqo-label on namespace '%s'", err, namespace.GetName()) | ||
return ctrl.Result{}, err | ||
} | ||
} | ||
|
||
// Add dediredMappings to NamespaceMaps | ||
if namespaceOffloading.Spec.ClusterSelector.Size() == 0 { | ||
klog.Infof(" Offload namespace '%s' on all remote clusters", namespaceOffloading.Namespace) | ||
if err := r.addDesiredMappings(namespaceOffloading.Namespace, | ||
namespaceOffloading.Status.RemoteNamespaceName, | ||
clusterWithNoOffloading); err != nil { | ||
return ctrl.Result{}, err | ||
} | ||
clusterWithNoOffloading = nil | ||
} else { | ||
virtualNodes := &corev1.NodeList{} | ||
if err := r.List(context.TODO(), virtualNodes, | ||
client.MatchingLabels{liqoconst.TypeLabel: liqoconst.TypeNode}); err != nil { | ||
klog.Error(err, " --> Unable to List all virtual nodes") | ||
return ctrl.Result{}, err | ||
} | ||
|
||
if len(virtualNodes.Items) == 0 { | ||
err := fmt.Errorf(" No VirtualNodes at the moment in the cluster") | ||
klog.Info(err) | ||
return ctrl.Result{}, err | ||
} | ||
|
||
for i := range virtualNodes.Items { | ||
match, err := k8shelper.MatchNodeSelectorTerms(&virtualNodes.Items[i], | ||
&namespaceOffloading.Spec.ClusterSelector) | ||
if err != nil { | ||
klog.Infof("%s -> Unable to offload the namespace '%s'", err, namespaceOffloading.Namespace) | ||
if namespaceOffloading.Annotations == nil { | ||
namespaceOffloading.Annotations = map[string]string{} | ||
} | ||
namespaceOffloading.Annotations[liqoconst.EnablingLiqoLabel] = fmt.Sprintf("Invalid Cluster Selector : %s", err) | ||
break | ||
} | ||
|
||
if match { | ||
if err = r.addDesiredMapping(namespaceOffloading.Namespace, | ||
namespaceOffloading.Status.RemoteNamespaceName, | ||
clusterWithNoOffloading[virtualNodes.Items[i].Annotations[liqoconst.RemoteClusterID]]); err != nil { | ||
return ctrl.Result{}, err | ||
} | ||
delete(clusterWithNoOffloading, virtualNodes.Items[i].Annotations[liqoconst.RemoteClusterID]) | ||
klog.Infof(" Offload namespace '%s' on remote cluster: %s", namespaceOffloading.Namespace, | ||
virtualNodes.Items[i].Annotations[liqoconst.RemoteClusterID]) | ||
} | ||
} | ||
} | ||
|
||
// Namespace not offloaded will have only the condition OffloadingRequired="false" | ||
setOffloadingRequiredCondition(namespaceOffloading, clusterWithNoOffloading) | ||
|
||
if len(clusterWithNoOffloading) == len(clusterNamespaceMaps.Items) { | ||
namespaceOffloading.Status.OffloadingPhase = offv1alpha1.NoClusterSelectedOffloadingPhaseType | ||
} | ||
|
||
if err := r.Update(ctx, namespaceOffloading); err != nil { | ||
klog.Errorf("%s --> Unable to update NamespaceOffloading in namespace '%s'", | ||
err, namespaceOffloading.Namespace) | ||
return ctrl.Result{}, err | ||
} | ||
return ctrl.Result{}, nil | ||
} | ||
|
||
func setOffloadingRequiredCondition(nsof *offv1alpha1.NamespaceOffloading, | ||
clusterWithNoOff map[string]*mapsv1alpha1.NamespaceMap) { | ||
for clusterID := range clusterWithNoOff { | ||
liqoutils.UpdateNamespaceOffloadingCondition(&nsof.Status, &offv1alpha1.RemoteNamespaceCondition{ | ||
Type: offv1alpha1.NamespaceOffloadingRequired, | ||
Status: corev1.ConditionFalse, | ||
Reason: "ClusterNotSelected", | ||
Message: "You have not selected this cluster through ClusterSelector fields", | ||
}, clusterID) | ||
} | ||
} | ||
|
||
// Events not filtered: | ||
// 1 -- creation of a NamespaceOffloading resource with the right default name. | ||
// 2 -- deletion of a NamespaceOffloading resource with the right finalizer. | ||
func namespaceOffloadingPredicate() predicate.Predicate { | ||
return predicate.Funcs{ | ||
UpdateFunc: func(e event.UpdateEvent) bool { | ||
if !(e.ObjectNew.GetDeletionTimestamp().IsZero()) && slice.ContainsString(e.ObjectNew.GetFinalizers(), | ||
namespaceOffloadingControllerFinalizer, nil) { | ||
return true | ||
} | ||
return false | ||
}, | ||
CreateFunc: func(e event.CreateEvent) bool { | ||
return e.Object.GetName() == liqoconst.DefaultNameNamespaceOffloading | ||
}, | ||
DeleteFunc: func(e event.DeleteEvent) bool { | ||
return false | ||
}, | ||
} | ||
} | ||
|
||
// SetupWithManager reconciles NamespaceOffloading Resources. | ||
func (r *NamespaceOffloadingReconciler) SetupWithManager(mgr ctrl.Manager) error { | ||
return ctrl.NewControllerManagedBy(mgr). | ||
For(&offv1alpha1.NamespaceOffloading{}). | ||
WithEventFilter(namespaceOffloadingPredicate()). | ||
Complete(r) | ||
} |