Files
open-cluster-management/pkg/spoke/managedcluster/claim_controller.go
2020-12-03 17:09:51 +08:00

143 lines
5.8 KiB
Go

package managedcluster
import (
"context"
"fmt"
"sort"
clientset "github.com/open-cluster-management/api/client/cluster/clientset/versioned"
clusterv1informer "github.com/open-cluster-management/api/client/cluster/informers/externalversions/cluster/v1"
clusterv1alpha1informer "github.com/open-cluster-management/api/client/cluster/informers/externalversions/cluster/v1alpha1"
clusterv1listers "github.com/open-cluster-management/api/client/cluster/listers/cluster/v1"
clusterv1alpha1listers "github.com/open-cluster-management/api/client/cluster/listers/cluster/v1alpha1"
clusterv1 "github.com/open-cluster-management/api/cluster/v1"
clusterv1alpha1 "github.com/open-cluster-management/api/cluster/v1alpha1"
"github.com/open-cluster-management/registration/pkg/helpers"
"github.com/openshift/library-go/pkg/controller/factory"
"github.com/openshift/library-go/pkg/operator/events"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2"
)
// managedClusterClaimController exposes cluster claims created on managed cluster on hub after it joins the hub.
type managedClusterClaimController struct {
clusterName string
hubClusterClient clientset.Interface
hubClusterLister clusterv1listers.ManagedClusterLister
claimLister clusterv1alpha1listers.ClusterClaimLister
maxCustomClusterClaims int
}
// NewManagedClusterClaimController creates a new managed cluster claim controller on the managed cluster.
func NewManagedClusterClaimController(
clusterName string,
maxCustomClusterClaims int,
hubClusterClient clientset.Interface,
hubManagedClusterInformer clusterv1informer.ManagedClusterInformer,
claimInformer clusterv1alpha1informer.ClusterClaimInformer,
recorder events.Recorder) factory.Controller {
c := &managedClusterClaimController{
clusterName: clusterName,
maxCustomClusterClaims: maxCustomClusterClaims,
hubClusterClient: hubClusterClient,
hubClusterLister: hubManagedClusterInformer.Lister(),
claimLister: claimInformer.Lister(),
}
return factory.New().
WithInformers(claimInformer.Informer()).
WithInformersQueueKeyFunc(func(obj runtime.Object) string {
accessor, _ := meta.Accessor(obj)
return accessor.GetName()
}, hubManagedClusterInformer.Informer()).
WithSync(c.sync).
ToController("ClusterClaimController", recorder)
}
// sync maintains the cluster claims in status of the managed cluster on hub once it joins the hub.
func (c managedClusterClaimController) sync(ctx context.Context, syncCtx factory.SyncContext) error {
managedCluster, err := c.hubClusterLister.Get(c.clusterName)
if err != nil {
return fmt.Errorf("unable to get managed cluster with name %q from hub: %w", c.clusterName, err)
}
// current managed cluster has not joined the hub yet, do nothing.
if !meta.IsStatusConditionTrue(managedCluster.Status.Conditions, clusterv1.ManagedClusterConditionJoined) {
syncCtx.Recorder().Eventf("ManagedClusterIsNotAccepted", "Managed cluster %q does not join the hub yet", c.clusterName)
return nil
}
return c.exposeClaims(ctx, syncCtx, managedCluster)
}
// exposeClaims saves cluster claims fetched on managed cluster into status of the
// managed cluster on hub. Some of the customized claims might not be exposed once
// the total number of the claims exceeds the value of `cluster-claims-max`.
func (c managedClusterClaimController) exposeClaims(ctx context.Context, syncCtx factory.SyncContext,
managedCluster *clusterv1.ManagedCluster) error {
reservedClaims := []clusterv1.ManagedClusterClaim{}
customClaims := []clusterv1.ManagedClusterClaim{}
clusterClaims, err := c.claimLister.List(labels.Everything())
if err != nil {
return fmt.Errorf("unable to list cluster claims: %w", err)
}
reservedClaimNames := sets.NewString(clusterv1alpha1.ReservedClusterClaimNames[:]...)
for _, clusterClaim := range clusterClaims {
managedClusterClaim := clusterv1.ManagedClusterClaim{
Name: clusterClaim.Name,
Value: clusterClaim.Spec.Value,
}
if reservedClaimNames.Has(clusterClaim.Name) {
reservedClaims = append(reservedClaims, managedClusterClaim)
continue
}
customClaims = append(customClaims, managedClusterClaim)
}
// sort claims by name
sort.SliceStable(reservedClaims, func(i, j int) bool {
return reservedClaims[i].Name < reservedClaims[j].Name
})
sort.SliceStable(customClaims, func(i, j int) bool {
return customClaims[i].Name < customClaims[j].Name
})
// truncate custom claims if the number exceeds `max-custom-cluster-claims`
if n := len(customClaims); n > c.maxCustomClusterClaims {
customClaims = customClaims[:c.maxCustomClusterClaims]
syncCtx.Recorder().Eventf("CustomClusterClaimsTruncated", "%d cluster claims are found. It exceeds the max number of custom cluster claims (%d). %d custom cluster claims are not exposed.",
n, c.maxCustomClusterClaims, n-c.maxCustomClusterClaims)
}
// merge reserved claims and custom claims
claims := append(reservedClaims, customClaims...)
// update the status of the managed cluster
updateStatusFuncs := []helpers.UpdateManagedClusterStatusFunc{updateClusterClaimsFn(clusterv1.ManagedClusterStatus{
ClusterClaims: claims,
})}
_, updated, err := helpers.UpdateManagedClusterStatus(ctx, c.hubClusterClient, c.clusterName, updateStatusFuncs...)
if err != nil {
return fmt.Errorf("unable to update status of managed cluster %q: %w", c.clusterName, err)
}
if updated {
klog.V(4).Infof("The cluster claims in status of managed cluster %q has been updated", c.clusterName)
}
return nil
}
func updateClusterClaimsFn(status clusterv1.ManagedClusterStatus) helpers.UpdateManagedClusterStatusFunc {
return func(oldStatus *clusterv1.ManagedClusterStatus) error {
oldStatus.ClusterClaims = status.ClusterClaims
return nil
}
}