130 lines
3.6 KiB
Go
130 lines
3.6 KiB
Go
/*
|
|
* Copyright 2024 the KubeSphere Authors.
|
|
* Please refer to the LICENSE file in the root directory of the project.
|
|
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
|
*/
|
|
|
|
package clusterlabel
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
"k8s.io/klog/v2"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/builder"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
|
|
|
clusterv1alpha1 "kubesphere.io/api/cluster/v1alpha1"
|
|
|
|
kscontroller "kubesphere.io/kubesphere/pkg/controller"
|
|
)
|
|
|
|
// Reconciler is a reconciler for the Label object.
|
|
type Reconciler struct {
|
|
client.Client
|
|
}
|
|
|
|
func (r *Reconciler) Name() string {
|
|
return "clusterlabel"
|
|
}
|
|
|
|
func (r *Reconciler) Enabled(clusterRole string) bool {
|
|
return strings.EqualFold(clusterRole, string(clusterv1alpha1.ClusterRoleHost))
|
|
}
|
|
|
|
// Reconcile reconciles the Label object, sync label to the individual Cluster CRs.
|
|
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
|
label := &clusterv1alpha1.Label{}
|
|
if err := r.Get(ctx, req.NamespacedName, label); err != nil {
|
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
|
}
|
|
|
|
if label.DeletionTimestamp != nil {
|
|
return ctrl.Result{}, r.deleteLabel(ctx, label)
|
|
}
|
|
|
|
if len(label.Finalizers) == 0 {
|
|
label.Finalizers = []string{clusterv1alpha1.LabelFinalizer}
|
|
if err := r.Update(ctx, label); err != nil {
|
|
return ctrl.Result{}, err
|
|
}
|
|
return ctrl.Result{Requeue: true}, nil
|
|
}
|
|
|
|
return ctrl.Result{}, r.syncLabelToClusters(ctx, label)
|
|
}
|
|
|
|
func (r *Reconciler) syncLabelToClusters(ctx context.Context, label *clusterv1alpha1.Label) error {
|
|
klog.V(4).Infof("sync label %s[%s/%v] to clusters: %v", label.Name, label.Spec.Key, label.Spec.Value, label.Spec.Clusters)
|
|
clusterSets := sets.NewString(label.Spec.Clusters...)
|
|
for name := range clusterSets {
|
|
cluster := &clusterv1alpha1.Cluster{}
|
|
if err := r.Get(ctx, client.ObjectKey{Name: name}, cluster); err != nil {
|
|
if errors.IsNotFound(err) {
|
|
clusterSets.Delete(name)
|
|
continue
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if cluster.Labels == nil {
|
|
cluster.Labels = make(map[string]string)
|
|
}
|
|
if _, ok := cluster.Labels[fmt.Sprintf(clusterv1alpha1.ClusterLabelFormat, label.Name)]; ok {
|
|
continue
|
|
}
|
|
cluster.Labels[fmt.Sprintf(clusterv1alpha1.ClusterLabelFormat, label.Name)] = ""
|
|
if err := r.Update(ctx, cluster); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
clusters := clusterSets.List()
|
|
// some clusters have been deleted and this list needs to be updated
|
|
if len(clusters) != len(label.Spec.Clusters) {
|
|
label.Spec.Clusters = clusters
|
|
return r.Update(ctx, label)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *Reconciler) deleteLabel(ctx context.Context, label *clusterv1alpha1.Label) error {
|
|
klog.V(4).Infof("deleting label %s, removing cluster %v related label", label.Name, label.Spec.Clusters)
|
|
for _, name := range label.Spec.Clusters {
|
|
cluster := &clusterv1alpha1.Cluster{}
|
|
if err := r.Get(ctx, client.ObjectKey{Name: name}, cluster); err != nil {
|
|
if errors.IsNotFound(err) {
|
|
continue
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
delete(cluster.Labels, fmt.Sprintf(clusterv1alpha1.ClusterLabelFormat, label.Name))
|
|
if err := r.Update(ctx, cluster); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
label.Finalizers = nil
|
|
return r.Update(ctx, label)
|
|
}
|
|
|
|
func (r *Reconciler) SetupWithManager(mgr *kscontroller.Manager) error {
|
|
r.Client = mgr.GetClient()
|
|
|
|
return builder.
|
|
ControllerManagedBy(mgr).
|
|
For(
|
|
&clusterv1alpha1.Label{},
|
|
builder.WithPredicates(
|
|
predicate.ResourceVersionChangedPredicate{},
|
|
),
|
|
).
|
|
Named(r.Name()).
|
|
Complete(r)
|
|
}
|