Skip to content

Commit 69e626d

Browse files
committed
feat: add support for OCM role creation
1 parent e172382 commit 69e626d

16 files changed

Lines changed: 2995 additions & 1 deletion
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
---
2+
apiVersion: apiextensions.k8s.io/v1
3+
kind: CustomResourceDefinition
4+
metadata:
5+
annotations:
6+
controller-gen.kubebuilder.io/version: v0.19.0
7+
name: rosaocmroleconfigs.infrastructure.cluster.x-k8s.io
8+
spec:
9+
group: infrastructure.cluster.x-k8s.io
10+
names:
11+
categories:
12+
- cluster-api
13+
kind: ROSAOCMRoleConfig
14+
listKind: ROSAOCMRoleConfigList
15+
plural: rosaocmroleconfigs
16+
shortNames:
17+
- rosaocmrole
18+
singular: rosaocmroleconfig
19+
scope: Cluster
20+
versions:
21+
- name: v1beta2
22+
schema:
23+
openAPIV3Schema:
24+
description: ROSAOCMRoleConfig is the Schema for the rosaocmroleconfigs API
25+
properties:
26+
apiVersion:
27+
description: |-
28+
APIVersion defines the versioned schema of this representation of an object.
29+
Servers should convert recognized schemas to the latest internal value, and
30+
may reject unrecognized values.
31+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
32+
type: string
33+
kind:
34+
description: |-
35+
Kind is a string value representing the REST resource this object represents.
36+
Servers may infer this from the endpoint the client submits requests to.
37+
Cannot be updated.
38+
In CamelCase.
39+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
40+
type: string
41+
metadata:
42+
type: object
43+
spec:
44+
description: ROSAOCMRoleConfigSpec defines the desired state of ROSAOCMRoleConfig
45+
properties:
46+
credentialsSecretRef:
47+
description: CredentialsSecretRef references a secret with necessary
48+
credentials to connect to the OCM API.
49+
properties:
50+
name:
51+
default: ""
52+
description: |-
53+
Name of the referent.
54+
This field is effectively required, but due to backwards compatibility is
55+
allowed to be empty. Instances of this type with an empty value here are
56+
almost certainly wrong.
57+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
58+
type: string
59+
type: object
60+
x-kubernetes-map-type: atomic
61+
deletionPolicy:
62+
default: Delete
63+
description: |-
64+
DeletionPolicy determines what happens to the OCM role when this CR is deleted.
65+
Delete will unlink and delete the OCM role.
66+
Retain will keep the OCM role intact.
67+
This is useful when reprovisioning management clusters to avoid disrupting users in the same organization.
68+
enum:
69+
- Delete
70+
- Retain
71+
type: string
72+
identityRef:
73+
description: |-
74+
IdentityRef is a reference to an identity to be used when reconciling the OCM Role Config.
75+
If no identity is specified, the default identity for this controller will be used.
76+
properties:
77+
kind:
78+
description: Kind of the identity.
79+
enum:
80+
- AWSClusterControllerIdentity
81+
- AWSClusterRoleIdentity
82+
- AWSClusterStaticIdentity
83+
type: string
84+
name:
85+
description: Name of the identity.
86+
minLength: 1
87+
type: string
88+
required:
89+
- kind
90+
- name
91+
type: object
92+
path:
93+
description: Path is the IAM path for the OCM role.
94+
pattern: ^\/.*$
95+
type: string
96+
permissionsBoundaryARN:
97+
description: PermissionsBoundaryARN is the ARN of the policy that
98+
is used to set the permissions boundary for the OCM role.
99+
type: string
100+
profile:
101+
default: Standard
102+
description: Profile defines the permission level for the OCM role.
103+
enum:
104+
- Standard
105+
- Admin
106+
- NoConsole
107+
type: string
108+
x-kubernetes-validations:
109+
- message: profile is immutable
110+
rule: self == oldSelf
111+
rolePrefix:
112+
description: |-
113+
RolePrefix is the user-defined prefix for the OCM role name.
114+
The final role name will be: {RolePrefix}-OCM-Role-{ExternalID}
115+
where ExternalID is the organization's external identifier from OCM.
116+
maxLength: 4
117+
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
118+
type: string
119+
x-kubernetes-validations:
120+
- message: rolePrefix is immutable
121+
rule: self == oldSelf
122+
required:
123+
- profile
124+
- rolePrefix
125+
type: object
126+
status:
127+
description: ROSAOCMRoleConfigStatus defines the observed state of ROSAOCMRoleConfig
128+
properties:
129+
conditions:
130+
description: Conditions specifies the ROSAOCMRoleConfig conditions
131+
items:
132+
description: Condition defines an observation of a Cluster API resource
133+
operational state.
134+
properties:
135+
lastTransitionTime:
136+
description: |-
137+
lastTransitionTime is the last time the condition transitioned from one status to another.
138+
This should be when the underlying condition changed. If that is not known, then using the time when
139+
the API field changed is acceptable.
140+
format: date-time
141+
type: string
142+
message:
143+
description: |-
144+
message is a human readable message indicating details about the transition.
145+
This field may be empty.
146+
maxLength: 10240
147+
minLength: 1
148+
type: string
149+
reason:
150+
description: |-
151+
reason is the reason for the condition's last transition in CamelCase.
152+
The specific API may choose whether or not this field is considered a guaranteed API.
153+
This field may be empty.
154+
maxLength: 256
155+
minLength: 1
156+
type: string
157+
severity:
158+
description: |-
159+
severity provides an explicit classification of Reason code, so the users or machines can immediately
160+
understand the current situation and act accordingly.
161+
The Severity field MUST be set only when Status=False.
162+
maxLength: 32
163+
type: string
164+
status:
165+
description: status of the condition, one of True, False, Unknown.
166+
type: string
167+
type:
168+
description: |-
169+
type of condition in CamelCase or in foo.example.com/CamelCase.
170+
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions
171+
can be useful (see .node.status.conditions), the ability to deconflict is important.
172+
maxLength: 256
173+
minLength: 1
174+
type: string
175+
required:
176+
- lastTransitionTime
177+
- status
178+
- type
179+
type: object
180+
type: array
181+
organizationID:
182+
description: OrganizationID is the OCM organization ID that this role
183+
is linked to.
184+
type: string
185+
roleARN:
186+
description: RoleARN is the ARN of the created OCM role.
187+
type: string
188+
type: object
189+
type: object
190+
served: true
191+
storage: true
192+
subresources:
193+
status: {}

config/rbac/role.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ rules:
200200
- awsmachinetemplates/status
201201
- rosaclusters/status
202202
- rosanetworks/status
203+
- rosaocmroleconfigs/status
203204
- rosaroleconfigs/status
204205
verbs:
205206
- get
@@ -231,6 +232,7 @@ rules:
231232
- awsmachines
232233
- rosamachinepools
233234
- rosanetworks
235+
- rosaocmroleconfigs
234236
- rosaroleconfigs
235237
verbs:
236238
- create
@@ -245,6 +247,7 @@ rules:
245247
resources:
246248
- rosamachinepools/finalizers
247249
- rosanetworks/finalizers
250+
- rosaocmroleconfigs/finalizers
248251
- rosaroleconfigs/finalizers
249252
verbs:
250253
- update

exp/api/v1beta2/finalizers.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,7 @@ const (
3131

3232
// RosaRoleConfigFinalizer allows the controller to clean up resources on delete.
3333
RosaRoleConfigFinalizer = "rosaroleconfigs.infrastructure.cluster.x-k8s.io"
34+
35+
// ROSAOCMRoleConfigFinalizer allows the controller to clean up resources on delete.
36+
ROSAOCMRoleConfigFinalizer = "rosaocmroleconfigs.infrastructure.cluster.x-k8s.io"
3437
)
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
Copyright 2026 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1beta2
18+
19+
import (
20+
corev1 "k8s.io/api/core/v1"
21+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
23+
infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
24+
clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1"
25+
)
26+
27+
// ROSAOCMRoleProfile defines the permission level for the OCM role
28+
type ROSAOCMRoleProfile string
29+
30+
const (
31+
// ROSAOCMRoleProfileStandard provides standard OCM permissions
32+
ROSAOCMRoleProfileStandard ROSAOCMRoleProfile = "Standard"
33+
34+
// ROSAOCMRoleProfileAdmin provides admin OCM permissions
35+
ROSAOCMRoleProfileAdmin ROSAOCMRoleProfile = "Admin"
36+
37+
// ROSAOCMRoleProfileNoConsole provides minimal OCM permissions (cannot use console.redhat.com)
38+
ROSAOCMRoleProfileNoConsole ROSAOCMRoleProfile = "NoConsole"
39+
)
40+
41+
// ROSAOCMRoleDeletionPolicy defines what happens to the OCM role when the CR is deleted.
42+
type ROSAOCMRoleDeletionPolicy string
43+
44+
const (
45+
// ROSAOCMRoleDeletionPolicyDelete unlinks and deletes the OCM role when the CR is deleted.
46+
ROSAOCMRoleDeletionPolicyDelete ROSAOCMRoleDeletionPolicy = "Delete"
47+
48+
// ROSAOCMRoleDeletionPolicyRetain keeps the OCM role intact when the CR is deleted.
49+
ROSAOCMRoleDeletionPolicyRetain ROSAOCMRoleDeletionPolicy = "Retain"
50+
)
51+
52+
const (
53+
// ROSAOCMRoleConfigReadyCondition condition reports on the successful reconciliation of ROSAOCMRoleConfig.
54+
ROSAOCMRoleConfigReadyCondition = "ROSAOCMRoleConfigReady"
55+
56+
// ROSAOCMRoleConfigDeletionFailedReason used to report failures while deleting ROSAOCMRoleConfig.
57+
ROSAOCMRoleConfigDeletionFailedReason = "DeletionFailed"
58+
59+
// ROSAOCMRoleConfigReconciliationFailedReason used to report reconciliation failures.
60+
ROSAOCMRoleConfigReconciliationFailedReason = "ReconciliationFailed"
61+
62+
// ROSAOCMRoleConfigDeletionStarted used to indicate that the deletion of ROSAOCMRoleConfig has started.
63+
ROSAOCMRoleConfigDeletionStarted = "DeletionStarted"
64+
65+
// ROSAOCMRoleConfigCreatedReason used to indicate that the ROSAOCMRoleConfig has been created.
66+
ROSAOCMRoleConfigCreatedReason = "Created"
67+
68+
// ROSAOCMRoleConfigLinkedReason used to indicate that the OCM role has been linked to the organization.
69+
ROSAOCMRoleConfigLinkedReason = "Linked"
70+
)
71+
72+
// ROSAOCMRoleConfigSpec defines the desired state of ROSAOCMRoleConfig
73+
type ROSAOCMRoleConfigSpec struct {
74+
// RolePrefix is the user-defined prefix for the OCM role name.
75+
// The final role name will be: {RolePrefix}-OCM-Role-{ExternalID}
76+
// where ExternalID is the organization's external identifier from OCM.
77+
// +kubebuilder:validation:Required
78+
// +kubebuilder:validation:MaxLength:=4
79+
// +kubebuilder:validation:Pattern:=`^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$`
80+
// +kubebuilder:validation:XValidation:rule="self == oldSelf", message="rolePrefix is immutable"
81+
RolePrefix string `json:"rolePrefix"`
82+
83+
// Profile defines the permission level for the OCM role.
84+
// +kubebuilder:validation:Enum=Standard;Admin;NoConsole
85+
// +kubebuilder:default=Standard
86+
// +kubebuilder:validation:XValidation:rule="self == oldSelf", message="profile is immutable"
87+
Profile ROSAOCMRoleProfile `json:"profile"`
88+
89+
// PermissionsBoundaryARN is the ARN of the policy that is used to set the permissions boundary for the OCM role.
90+
// +optional
91+
PermissionsBoundaryARN string `json:"permissionsBoundaryARN,omitempty"`
92+
93+
// Path is the IAM path for the OCM role.
94+
// +optional
95+
// +kubebuilder:validation:Pattern=`^\/.*$`
96+
Path string `json:"path,omitempty"`
97+
98+
// IdentityRef is a reference to an identity to be used when reconciling the OCM Role Config.
99+
// If no identity is specified, the default identity for this controller will be used.
100+
// +optional
101+
IdentityRef *infrav1.AWSIdentityReference `json:"identityRef,omitempty"`
102+
103+
// CredentialsSecretRef references a secret with necessary credentials to connect to the OCM API.
104+
// +optional
105+
CredentialsSecretRef *corev1.LocalObjectReference `json:"credentialsSecretRef,omitempty"`
106+
107+
// DeletionPolicy determines what happens to the OCM role when this CR is deleted.
108+
// Delete will unlink and delete the OCM role.
109+
// Retain will keep the OCM role intact.
110+
// This is useful when reprovisioning management clusters to avoid disrupting users in the same organization.
111+
// +kubebuilder:validation:Enum=Delete;Retain
112+
// +kubebuilder:default=Delete
113+
// +optional
114+
DeletionPolicy ROSAOCMRoleDeletionPolicy `json:"deletionPolicy,omitempty"`
115+
}
116+
117+
// ROSAOCMRoleConfigStatus defines the observed state of ROSAOCMRoleConfig
118+
type ROSAOCMRoleConfigStatus struct {
119+
// RoleARN is the ARN of the created OCM role.
120+
RoleARN string `json:"roleARN,omitempty"`
121+
122+
// OrganizationID is the OCM organization ID that this role is linked to.
123+
OrganizationID string `json:"organizationID,omitempty"`
124+
125+
// Conditions specifies the ROSAOCMRoleConfig conditions
126+
Conditions clusterv1beta1.Conditions `json:"conditions,omitempty"`
127+
}
128+
129+
// ROSAOCMRoleConfig is the Schema for the rosaocmroleconfigs API
130+
// +kubebuilder:object:root=true
131+
// +kubebuilder:resource:path=rosaocmroleconfigs,scope=Cluster,categories=cluster-api,shortName=rosaocmrole
132+
// +kubebuilder:storageversion
133+
// +kubebuilder:subresource:status
134+
type ROSAOCMRoleConfig struct {
135+
metav1.TypeMeta `json:",inline"`
136+
metav1.ObjectMeta `json:"metadata,omitempty"`
137+
138+
Spec ROSAOCMRoleConfigSpec `json:"spec,omitempty"`
139+
Status ROSAOCMRoleConfigStatus `json:"status,omitempty"`
140+
}
141+
142+
// ROSAOCMRoleConfigList contains a list of ROSAOCMRoleConfig
143+
// +kubebuilder:object:root=true
144+
type ROSAOCMRoleConfigList struct {
145+
metav1.TypeMeta `json:",inline"`
146+
metav1.ListMeta `json:"metadata,omitempty"`
147+
Items []ROSAOCMRoleConfig `json:"items"`
148+
}
149+
150+
// SetConditions sets the conditions of the ROSAOCMRoleConfig.
151+
func (r *ROSAOCMRoleConfig) SetConditions(conditions clusterv1beta1.Conditions) {
152+
r.Status.Conditions = conditions
153+
}
154+
155+
// GetConditions returns the observations of the operational state of the ROSAOCMRoleConfig resource.
156+
func (r *ROSAOCMRoleConfig) GetConditions() clusterv1beta1.Conditions {
157+
return r.Status.Conditions
158+
}
159+
160+
func init() {
161+
SchemeBuilder.Register(&ROSAOCMRoleConfig{}, &ROSAOCMRoleConfigList{})
162+
}

0 commit comments

Comments
 (0)