Repository: open-cluster-management-io/api
Pull request #363: ✨add ClusterClaimConfiguration to API
Author: login: o-farag
Labels: name: lgtm
name: approved
Created: 2025-03-26T21:22:55Z | Merged: 2025-04-22T07:21:21Z

<!--
Thanks for creating a pull request!

If this is your first time, please make sure to review CONTRIBUTING.MD.

Please copy the appropriate `:text:` or icon to the beginning of your PR title:

:sparkles: ✨ feature
:bug: 🐛 bug fix
:book: 📖 docs
:memo: 📝 proposal
:warning: ⚠️ breaking change
:seedling: 🌱 other/misc
:question: ❓ requires manual review/categorization

-->
## Summary
Enhances API to allow support for custom ClusterClaim configurations. 
## Related issue(s)

Required for https://github.com/open-cluster-management-io/ocm/issues/897


Diff:
diff --git a/operator/v1/0000_00_operator.open-cluster-management.io_klusterlets.crd.yaml b/operator/v1/0000_00_operator.open-cluster-management.io_klusterlets.crd.yaml
index acd44f13e..ec7dabab5 100644
--- a/operator/v1/0000_00_operator.open-cluster-management.io_klusterlets.crd.yaml
+++ b/operator/v1/0000_00_operator.open-cluster-management.io_klusterlets.crd.yaml
@@ -251,6 +251,24 @@ spec:
                       ClusterAnnotations is annotations with the reserve prefix "agent.open-cluster-management.io" set on
                       ManagedCluster when creating only, other actors can update it afterwards.
                     type: object
+                  clusterClaimConfiguration:
+                    description: |-
+                      ClusterClaimConfiguration represents the configuration of ClusterClaim
+                      Effective only when the `ClusterClaim` feature gate is enabled.
+                    properties:
+                      maxCustomClusterClaims:
+                        default: 20
+                        description: Maximum number of custom ClusterClaims allowed.
+                        format: int32
+                        type: integer
+                      reservedClusterClaimSuffixes:
+                        description: Custom suffixes for reserved ClusterClaims.
+                        items:
+                          type: string
+                        type: array
+                    required:
+                    - maxCustomClusterClaims
+                    type: object
                   featureGates:
                     description: "FeatureGates represents the list of feature gates
                       for registration\nIf it is set empty, default feature gates
diff --git a/operator/v1/types_klusterlet.go b/operator/v1/types_klusterlet.go
index 524b350d3..706db8901 100644
--- a/operator/v1/types_klusterlet.go
+++ b/operator/v1/types_klusterlet.go
@@ -176,6 +176,24 @@ type RegistrationConfiguration struct {
 	// This provides driver details required to register with hub
 	// +optional
 	RegistrationDriver RegistrationDriver `json:"registrationDriver,omitempty"`
+
+	// ClusterClaimConfiguration represents the configuration of ClusterClaim
+	// Effective only when the `ClusterClaim` feature gate is enabled.
+	// +optional
+	ClusterClaimConfiguration *ClusterClaimConfiguration `json:"clusterClaimConfiguration,omitempty"`
+}
+
+// ClusterClaimConfiguration represents the configuration of ClusterClaim
+type ClusterClaimConfiguration struct {
+	// Maximum number of custom ClusterClaims allowed.
+	// +kubebuilder:validation:Required
+	// +kubebuilder:default:=20
+	// +required
+	MaxCustomClusterClaims int32 `json:"maxCustomClusterClaims"`
+
+	// Custom suffixes for reserved ClusterClaims.
+	// +optional
+	ReservedClusterClaimSuffixes []string `json:"reservedClusterClaimSuffixes,omitempty"`
 }
 
 type RegistrationDriver struct {
diff --git a/operator/v1/zz_generated.deepcopy.go b/operator/v1/zz_generated.deepcopy.go
index 1cfb0760a..240612641 100644
--- a/operator/v1/zz_generated.deepcopy.go
+++ b/operator/v1/zz_generated.deepcopy.go
@@ -116,6 +116,27 @@ func (in *CSRConfig) DeepCopy() *CSRConfig {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterClaimConfiguration) DeepCopyInto(out *ClusterClaimConfiguration) {
+	*out = *in
+	if in.ReservedClusterClaimSuffixes != nil {
+		in, out := &in.ReservedClusterClaimSuffixes, &out.ReservedClusterClaimSuffixes
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterClaimConfiguration.
+func (in *ClusterClaimConfiguration) DeepCopy() *ClusterClaimConfiguration {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterClaimConfiguration)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ClusterManager) DeepCopyInto(out *ClusterManager) {
 	*out = *in
@@ -574,6 +595,11 @@ func (in *RegistrationConfiguration) DeepCopyInto(out *RegistrationConfiguration
 	}
 	in.BootstrapKubeConfigs.DeepCopyInto(&out.BootstrapKubeConfigs)
 	in.RegistrationDriver.DeepCopyInto(&out.RegistrationDriver)
+	if in.ClusterClaimConfiguration != nil {
+		in, out := &in.ClusterClaimConfiguration, &out.ClusterClaimConfiguration
+		*out = new(ClusterClaimConfiguration)
+		(*in).DeepCopyInto(*out)
+	}
 	return
 }
 
diff --git a/operator/v1/zz_generated.swagger_doc_generated.go b/operator/v1/zz_generated.swagger_doc_generated.go
index d2308a0b5..1c56a8551 100644
--- a/operator/v1/zz_generated.swagger_doc_generated.go
+++ b/operator/v1/zz_generated.swagger_doc_generated.go
@@ -210,6 +210,16 @@ func (BootstrapKubeConfigs) SwaggerDoc() map[string]string {
 	return map_BootstrapKubeConfigs
 }
 
+var map_ClusterClaimConfiguration = map[string]string{
+	"":                             "ClusterClaimConfiguration represents the configuration of ClusterClaim",
+	"maxCustomClusterClaims":       "Maximum number of custom ClusterClaims allowed.",
+	"reservedClusterClaimSuffixes": "Custom suffixes for reserved ClusterClaims.",
+}
+
+func (ClusterClaimConfiguration) SwaggerDoc() map[string]string {
+	return map_ClusterClaimConfiguration
+}
+
 var map_HubApiServerHostAlias = map[string]string{
 	"":         "HubApiServerHostAlias holds the mapping between IP and hostname that will be injected as an entry in the pod's hosts file.",
 	"ip":       "IP address of the host file entry.",
@@ -307,6 +317,7 @@ var map_RegistrationConfiguration = map[string]string{
 	"kubeAPIBurst":                "KubeAPIBurst indicates the maximum burst of the throttle while talking with apiserver of hub cluster from the spoke cluster. If it is set empty, use the default value: 100",
 	"bootstrapKubeConfigs":        "BootstrapKubeConfigs defines the ordered list of bootstrap kubeconfigs. The order decides which bootstrap kubeconfig to use first when rebootstrap.\n\nWhen the agent loses the connection to the current hub over HubConnectionTimeoutSeconds, or the managedcluster CR is set `hubAcceptsClient=false` on the hub, the controller marks the related bootstrap kubeconfig as \"failed\".\n\nA failed bootstrapkubeconfig won't be used for the duration specified by SkipFailedBootstrapKubeConfigSeconds. But if the user updates the content of a failed bootstrapkubeconfig, the \"failed\" mark will be cleared.",
 	"registrationDriver":          "This provides driver details required to register with hub",
+	"clusterClaimConfiguration":   "ClusterClaimConfiguration represents the configuration of ClusterClaim Effective only when the `ClusterClaim` feature gate is enabled.",
 }
 
 func (RegistrationConfiguration) SwaggerDoc() map[string]string {


Reviews:
user: login: zhujian7
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-17T02:28:39Z
user: login: o-farag
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T02:51:30Z
user: login: o-farag
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T02:51:43Z
user: login: elgnay
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T03:36:33Z
user: login: elgnay
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T03:40:24Z
user: login: elgnay
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T04:04:02Z
user: login: qiujian16
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-17T12:51:14Z
user: login: qiujian16
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-17T12:52:56Z
user: login: o-farag
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T19:45:53Z

Review comments:
diff_hunk: @@ -176,6 +176,24 @@ type RegistrationConfiguration struct {
 	// This provides driver details required to register with hub
 	// +optional
 	RegistrationDriver RegistrationDriver `json:"registrationDriver,omitempty"`
+
+	// ClusterClaimConfiguration represents the configuration of ClusterClaim
+	// Effective only when the `ClusterClaim` feature gate is enabled.
+	// +optional
+	ClusterClaimConfiguration *ClusterClaimConfiguration `json:"clusterClaimConfiguration,omitempty"`
path: operator/v1/types_klusterlet.go
user: login: zhujian7
body: ```suggestion
	ClusterClaims *ClusterClaimConfiguration `json:"clusterClaims,omitempty"`
```
nit, not sure if it is better/simpler to remove the suffix configuration and make it as `clusterClaim` or `clusterClaims`
created_at: 2025-04-17T02:26:42Z
author_association: MEMBER
diff_hunk: @@ -176,6 +176,24 @@ type RegistrationConfiguration struct {
 	// This provides driver details required to register with hub
 	// +optional
 	RegistrationDriver RegistrationDriver `json:"registrationDriver,omitempty"`
+
+	// ClusterClaimConfiguration represents the configuration of ClusterClaim
+	// Effective only when the `ClusterClaim` feature gate is enabled.
+	// +optional
+	ClusterClaimConfiguration *ClusterClaimConfiguration `json:"clusterClaimConfiguration,omitempty"`
+}
+
+// ClusterClaimConfiguration represents the configuration of ClusterClaim
+type ClusterClaimConfiguration struct {
+	// Maximum number of custom ClusterClaims allowed.
+	// +kubebuilder:validation:Required
+	// +kubebuilder:default:=20
+	// +required
+	MaxCustomClusterClaims int32 `json:"maxCustomClusterClaims"`
+
+	// Custom suffixes for reserved ClusterClaims.
+	// +optional
+	ReservedClusterClaimSuffixes []string `json:"reservedClusterClaimSuffixes,omitempty"`
path: operator/v1/types_klusterlet.go
user: login: zhujian7
body: I do not quite understand why a customer needs to configure the reserved cluster claims?
created_at: 2025-04-17T02:28:26Z
author_association: MEMBER
diff_hunk: @@ -176,6 +176,24 @@ type RegistrationConfiguration struct {
 	// This provides driver details required to register with hub
 	// +optional
 	RegistrationDriver RegistrationDriver `json:"registrationDriver,omitempty"`
+
+	// ClusterClaimConfiguration represents the configuration of ClusterClaim
+	// Effective only when the `ClusterClaim` feature gate is enabled.
+	// +optional
+	ClusterClaimConfiguration *ClusterClaimConfiguration `json:"clusterClaimConfiguration,omitempty"`
+}
+
+// ClusterClaimConfiguration represents the configuration of ClusterClaim
+type ClusterClaimConfiguration struct {
+	// Maximum number of custom ClusterClaims allowed.
+	// +kubebuilder:validation:Required
+	// +kubebuilder:default:=20
+	// +required
+	MaxCustomClusterClaims int32 `json:"maxCustomClusterClaims"`
+
+	// Custom suffixes for reserved ClusterClaims.
+	// +optional
+	ReservedClusterClaimSuffixes []string `json:"reservedClusterClaimSuffixes,omitempty"`
path: operator/v1/types_klusterlet.go
user: login: o-farag
body: > I do not quite understand why a customer needs to configure the reserved cluster claims?

@zhujian7 the [proposed enhancement](https://github.com/open-cluster-management-io/ocm/issues/897) outlines this addition, I'm not sure if a specific customer requested it
created_at: 2025-04-17T02:51:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -176,6 +176,24 @@ type RegistrationConfiguration struct {
 	// This provides driver details required to register with hub
 	// +optional
 	RegistrationDriver RegistrationDriver `json:"registrationDriver,omitempty"`
+
+	// ClusterClaimConfiguration represents the configuration of ClusterClaim
+	// Effective only when the `ClusterClaim` feature gate is enabled.
+	// +optional
+	ClusterClaimConfiguration *ClusterClaimConfiguration `json:"clusterClaimConfiguration,omitempty"`
path: operator/v1/types_klusterlet.go
user: login: o-farag
body: @elgnay what do you think?
created_at: 2025-04-17T02:51:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -176,6 +176,24 @@ type RegistrationConfiguration struct {
 	// This provides driver details required to register with hub
 	// +optional
 	RegistrationDriver RegistrationDriver `json:"registrationDriver,omitempty"`
+
+	// ClusterClaimConfiguration represents the configuration of ClusterClaim
+	// Effective only when the `ClusterClaim` feature gate is enabled.
+	// +optional
+	ClusterClaimConfiguration *ClusterClaimConfiguration `json:"clusterClaimConfiguration,omitempty"`
+}
+
+// ClusterClaimConfiguration represents the configuration of ClusterClaim
+type ClusterClaimConfiguration struct {
+	// Maximum number of custom ClusterClaims allowed.
+	// +kubebuilder:validation:Required
+	// +kubebuilder:default:=20
+	// +required
+	MaxCustomClusterClaims int32 `json:"maxCustomClusterClaims"`
+
+	// Custom suffixes for reserved ClusterClaims.
+	// +optional
+	ReservedClusterClaimSuffixes []string `json:"reservedClusterClaimSuffixes,omitempty"`
path: operator/v1/types_klusterlet.go
user: login: elgnay
body: It is guaranteed that reserved ClusterClaims will be reported in the status of the ManagedCluster. However, custom ClusterClaims are not guaranteed to appear due to the limitation on the number of custom ClusterClaims, even if the user sets a relatively high limit.
created_at: 2025-04-17T03:36:33Z
author_association: NONE
diff_hunk: @@ -176,6 +176,24 @@ type RegistrationConfiguration struct {
 	// This provides driver details required to register with hub
 	// +optional
 	RegistrationDriver RegistrationDriver `json:"registrationDriver,omitempty"`
+
+	// ClusterClaimConfiguration represents the configuration of ClusterClaim
+	// Effective only when the `ClusterClaim` feature gate is enabled.
+	// +optional
+	ClusterClaimConfiguration *ClusterClaimConfiguration `json:"clusterClaimConfiguration,omitempty"`
path: operator/v1/types_klusterlet.go
user: login: elgnay
body: I'm good with the name `ClusterClaims`. @qiujian16 Is there any naming convention or best practice we should follow when defining the API?
created_at: 2025-04-17T03:40:24Z
author_association: NONE
diff_hunk: @@ -176,6 +176,24 @@ type RegistrationConfiguration struct {
 	// This provides driver details required to register with hub
 	// +optional
 	RegistrationDriver RegistrationDriver `json:"registrationDriver,omitempty"`
+
+	// ClusterClaimConfiguration represents the configuration of ClusterClaim
+	// Effective only when the `ClusterClaim` feature gate is enabled.
+	// +optional
+	ClusterClaimConfiguration *ClusterClaimConfiguration `json:"clusterClaimConfiguration,omitempty"`
+}
+
+// ClusterClaimConfiguration represents the configuration of ClusterClaim
+type ClusterClaimConfiguration struct {
+	// Maximum number of custom ClusterClaims allowed.
+	// +kubebuilder:validation:Required
+	// +kubebuilder:default:=20
+	// +required
+	MaxCustomClusterClaims int32 `json:"maxCustomClusterClaims"`
+
+	// Custom suffixes for reserved ClusterClaims.
+	// +optional
+	ReservedClusterClaimSuffixes []string `json:"reservedClusterClaimSuffixes,omitempty"`
path: operator/v1/types_klusterlet.go
user: login: elgnay
body: Moreover, ClusterClaims matching the `ReservedClusterClaimSuffixes` should be treated as additional reserved claims. They must be merged with, not override, the built-in (hardcoded) reserved claims when reported to the hub. Please either include this information in the field comment or consider renaming the field to `AdditionalReservedClusterClaimSuffixes` for clarity.
created_at: 2025-04-17T04:04:01Z
author_association: NONE
diff_hunk: @@ -176,6 +176,24 @@ type RegistrationConfiguration struct {
 	// This provides driver details required to register with hub
 	// +optional
 	RegistrationDriver RegistrationDriver `json:"registrationDriver,omitempty"`
+
+	// ClusterClaimConfiguration represents the configuration of ClusterClaim
+	// Effective only when the `ClusterClaim` feature gate is enabled.
+	// +optional
+	ClusterClaimConfiguration *ClusterClaimConfiguration `json:"clusterClaimConfiguration,omitempty"`
path: operator/v1/types_klusterlet.go
user: login: qiujian16
body: I kinda think it is a configuration, clusterClaims might be confusing on allowing user to specify claims they would like to create.
created_at: 2025-04-17T12:51:14Z
author_association: MEMBER
diff_hunk: @@ -176,6 +176,24 @@ type RegistrationConfiguration struct {
 	// This provides driver details required to register with hub
 	// +optional
 	RegistrationDriver RegistrationDriver `json:"registrationDriver,omitempty"`
+
+	// ClusterClaimConfiguration represents the configuration of ClusterClaim
+	// Effective only when the `ClusterClaim` feature gate is enabled.
+	// +optional
+	ClusterClaimConfiguration *ClusterClaimConfiguration `json:"clusterClaimConfiguration,omitempty"`
+}
+
+// ClusterClaimConfiguration represents the configuration of ClusterClaim
+type ClusterClaimConfiguration struct {
+	// Maximum number of custom ClusterClaims allowed.
+	// +kubebuilder:validation:Required
+	// +kubebuilder:default:=20
+	// +required
+	MaxCustomClusterClaims int32 `json:"maxCustomClusterClaims"`
+
+	// Custom suffixes for reserved ClusterClaims.
+	// +optional
+	ReservedClusterClaimSuffixes []string `json:"reservedClusterClaimSuffixes,omitempty"`
path: operator/v1/types_klusterlet.go
user: login: qiujian16
body: We could start with a predefined suffix. I do not know if there are use cases that need is to be configurable, and I am not sure what it should be when user change the suffix from one to another.
created_at: 2025-04-17T12:52:56Z
author_association: MEMBER
diff_hunk: @@ -176,6 +176,24 @@ type RegistrationConfiguration struct {
 	// This provides driver details required to register with hub
 	// +optional
 	RegistrationDriver RegistrationDriver `json:"registrationDriver,omitempty"`
+
+	// ClusterClaimConfiguration represents the configuration of ClusterClaim
+	// Effective only when the `ClusterClaim` feature gate is enabled.
+	// +optional
+	ClusterClaimConfiguration *ClusterClaimConfiguration `json:"clusterClaimConfiguration,omitempty"`
path: operator/v1/types_klusterlet.go
user: login: o-farag
body: I agree with quijian16, since this configures cluster claims and aren't actual clusterclaims
created_at: 2025-04-17T19:45:53Z
author_association: CONTRIBUTOR

PR comments:
user: login: qiujian16
created_at: 2025-04-09T02:11:31Z
body: I think this should under registrationConfiguration field

cc @elgnay 
author_association: MEMBER
user: login: elgnay
created_at: 2025-04-10T09:39:57Z
body: The structure looks good to me. As @qiujian16 suggested, please move `ClusterClaimConfiguration` under `registrationConfiguration`.
author_association: NONE
user: login: o-farag
created_at: 2025-04-11T20:11:02Z
body: @qiujian16 may I get this reviewed? I'd like to merge it for https://github.com/open-cluster-management-io/ocm/pull/933
author_association: CONTRIBUTOR
user: login: qiujian16
created_at: 2025-04-12T01:53:37Z
body: https://github.com/open-cluster-management-io/api/pull/363#issuecomment-2788110590
author_association: MEMBER
user: login: o-farag
created_at: 2025-04-14T20:06:12Z
body: @qiujian16 sorry, I didn't see your comments for some reason. Updated!
author_association: CONTRIBUTOR
user: login: qiujian16
created_at: 2025-04-14T20:40:24Z
body: /approve

looks good to me but I will let @elgnay to final review
/assign @elgnay 
author_association: MEMBER
user: login: openshift-ci[bot]
created_at: 2025-04-14T20:40:30Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/open-cluster-management-io/api/pull/363#" title="Author self-approved">o-farag</a>*, *<a href="https://github.com/open-cluster-management-io/api/pull/363#issuecomment-2802934090" title="Approved">qiujian16</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=open-cluster-management-io%2Fapi).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/open-cluster-management-io/api/blob/main/OWNERS)~~ [qiujian16]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR
user: login: elgnay
created_at: 2025-04-22T03:23:37Z
body:  /lgtm
author_association: NONE
user: login: elgnay
created_at: 2025-04-22T07:18:07Z
body: /lgtm
author_association: NONE