@@ -3,6 +3,7 @@ package managedcluster
33import (
44 "context"
55 "fmt"
6+ "time"
67
78 "github.com/openshift/library-go/pkg/controller/factory"
89 "github.com/openshift/library-go/pkg/operator/events"
@@ -12,6 +13,7 @@ import (
1213 "k8s.io/apimachinery/pkg/api/errors"
1314 "k8s.io/apimachinery/pkg/api/meta"
1415 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16+ "k8s.io/apimachinery/pkg/types"
1517 rbacv1informers "k8s.io/client-go/informers/rbac/v1"
1618 "k8s.io/client-go/kubernetes"
1719 "k8s.io/klog/v2"
@@ -20,14 +22,20 @@ import (
2022 informerv1 "open-cluster-management.io/api/client/cluster/informers/externalversions/cluster/v1"
2123 listerv1 "open-cluster-management.io/api/client/cluster/listers/cluster/v1"
2224 v1 "open-cluster-management.io/api/cluster/v1"
25+ ocmfeature "open-cluster-management.io/api/feature"
2326 "open-cluster-management.io/sdk-go/pkg/patcher"
2427
2528 "open-cluster-management.io/ocm/pkg/common/apply"
2629 "open-cluster-management.io/ocm/pkg/common/queue"
30+ "open-cluster-management.io/ocm/pkg/features"
2731 "open-cluster-management.io/ocm/pkg/registration/helpers"
2832 "open-cluster-management.io/ocm/pkg/registration/hub/manifests"
2933)
3034
35+ // this is an internal annotation to indicate a managed cluster is already accepted automatically, it is not
36+ // expected to be changed or removed outside.
37+ const clusterAcceptedAnnotationKey = "open-cluster-management.io/automatically-accepted-on"
38+
3139var staticFiles = []string {
3240 "rbac/managedcluster-clusterrole.yaml" ,
3341 "rbac/managedcluster-clusterrolebinding.yaml" ,
@@ -38,6 +46,7 @@ var staticFiles = []string{
3846// managedClusterController reconciles instances of ManagedCluster on the hub.
3947type managedClusterController struct {
4048 kubeClient kubernetes.Interface
49+ clusterClient clientset.Interface
4150 clusterLister listerv1.ManagedClusterLister
4251 applier * apply.PermissionApplier
4352 patcher patcher.Patcher [* v1.ManagedCluster , v1.ManagedClusterSpec , v1.ManagedClusterStatus ]
@@ -56,6 +65,7 @@ func NewManagedClusterController(
5665 recorder events.Recorder ) factory.Controller {
5766 c := & managedClusterController {
5867 kubeClient : kubeClient ,
68+ clusterClient : clusterClient ,
5969 clusterLister : clusterInformer .Lister (),
6070 applier : apply .NewPermissionApplier (
6171 kubeClient ,
@@ -103,6 +113,14 @@ func (c *managedClusterController) sync(ctx context.Context, syncCtx factory.Syn
103113 }
104114
105115 if ! managedCluster .Spec .HubAcceptsClient {
116+ // If the ManagedClusterAutoApproval feature is enabled, we automatically accept a cluster only
117+ // when it joins for the first time, afterwards users can deny it again.
118+ if features .HubMutableFeatureGate .Enabled (ocmfeature .ManagedClusterAutoApproval ) {
119+ if _ , ok := managedCluster .Annotations [clusterAcceptedAnnotationKey ]; ! ok {
120+ return c .acceptCluster (ctx , managedClusterName )
121+ }
122+ }
123+
106124 // Current spoke cluster is not accepted, do nothing.
107125 if ! meta .IsStatusConditionTrue (managedCluster .Status .Conditions , v1 .ManagedClusterConditionHubAccepted ) {
108126 return nil
@@ -199,3 +217,13 @@ func (c *managedClusterController) removeManagedClusterResources(ctx context.Con
199217 }
200218 return operatorhelpers .NewMultiLineAggregate (errs )
201219}
220+
221+ func (c * managedClusterController ) acceptCluster (ctx context.Context , managedClusterName string ) error {
222+ // TODO support patching both annotations and spec simultaneously in the patcher
223+ acceptedTime := time .Now ()
224+ patch := fmt .Sprintf (`{"metadata":{"annotations":{"%s":"%s"}},"spec":{"hubAcceptsClient":true}}` ,
225+ clusterAcceptedAnnotationKey , acceptedTime .Format (time .RFC3339 ))
226+ _ , err := c .clusterClient .ClusterV1 ().ManagedClusters ().Patch (ctx , managedClusterName ,
227+ types .MergePatchType , []byte (patch ), metav1.PatchOptions {})
228+ return err
229+ }
0 commit comments