Skip to content

Commit a31bff3

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

16 files changed

Lines changed: 2877 additions & 1 deletion
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
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+
identityRef:
62+
description: |-
63+
IdentityRef is a reference to an identity to be used when reconciling the OCM Role Config.
64+
If no identity is specified, the default identity for this controller will be used.
65+
properties:
66+
kind:
67+
description: Kind of the identity.
68+
enum:
69+
- AWSClusterControllerIdentity
70+
- AWSClusterRoleIdentity
71+
- AWSClusterStaticIdentity
72+
type: string
73+
name:
74+
description: Name of the identity.
75+
minLength: 1
76+
type: string
77+
required:
78+
- kind
79+
- name
80+
type: object
81+
path:
82+
description: Path is the IAM path for the OCM role.
83+
pattern: ^\/.*$
84+
type: string
85+
permissionsBoundaryARN:
86+
description: PermissionsBoundaryARN is the ARN of the policy that
87+
is used to set the permissions boundary for the OCM role.
88+
type: string
89+
profile:
90+
default: Standard
91+
description: Profile defines the permission level for the OCM role.
92+
enum:
93+
- Standard
94+
- Admin
95+
- NoConsole
96+
type: string
97+
x-kubernetes-validations:
98+
- message: profile is immutable
99+
rule: self == oldSelf
100+
rolePrefix:
101+
description: |-
102+
RolePrefix is the user-defined prefix for the OCM role name.
103+
The final role name will be: {RolePrefix}-OCM-Role-{ExternalID}
104+
where ExternalID is the organization's external identifier from OCM.
105+
maxLength: 4
106+
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
107+
type: string
108+
x-kubernetes-validations:
109+
- message: rolePrefix is immutable
110+
rule: self == oldSelf
111+
required:
112+
- profile
113+
- rolePrefix
114+
type: object
115+
status:
116+
description: ROSAOCMRoleConfigStatus defines the observed state of ROSAOCMRoleConfig
117+
properties:
118+
conditions:
119+
description: Conditions specifies the ROSAOCMRoleConfig conditions
120+
items:
121+
description: Condition defines an observation of a Cluster API resource
122+
operational state.
123+
properties:
124+
lastTransitionTime:
125+
description: |-
126+
lastTransitionTime is the last time the condition transitioned from one status to another.
127+
This should be when the underlying condition changed. If that is not known, then using the time when
128+
the API field changed is acceptable.
129+
format: date-time
130+
type: string
131+
message:
132+
description: |-
133+
message is a human readable message indicating details about the transition.
134+
This field may be empty.
135+
maxLength: 10240
136+
minLength: 1
137+
type: string
138+
reason:
139+
description: |-
140+
reason is the reason for the condition's last transition in CamelCase.
141+
The specific API may choose whether or not this field is considered a guaranteed API.
142+
This field may be empty.
143+
maxLength: 256
144+
minLength: 1
145+
type: string
146+
severity:
147+
description: |-
148+
severity provides an explicit classification of Reason code, so the users or machines can immediately
149+
understand the current situation and act accordingly.
150+
The Severity field MUST be set only when Status=False.
151+
maxLength: 32
152+
type: string
153+
status:
154+
description: status of the condition, one of True, False, Unknown.
155+
type: string
156+
type:
157+
description: |-
158+
type of condition in CamelCase or in foo.example.com/CamelCase.
159+
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions
160+
can be useful (see .node.status.conditions), the ability to deconflict is important.
161+
maxLength: 256
162+
minLength: 1
163+
type: string
164+
required:
165+
- lastTransitionTime
166+
- status
167+
- type
168+
type: object
169+
type: array
170+
organizationID:
171+
description: OrganizationID is the OCM organization ID that this role
172+
is linked to.
173+
type: string
174+
roleARN:
175+
description: RoleARN is the ARN of the created OCM role.
176+
type: string
177+
type: object
178+
type: object
179+
served: true
180+
storage: true
181+
subresources:
182+
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: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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+
const (
42+
// ROSAOCMRoleConfigReadyCondition condition reports on the successful reconciliation of ROSAOCMRoleConfig.
43+
ROSAOCMRoleConfigReadyCondition = "ROSAOCMRoleConfigReady"
44+
45+
// ROSAOCMRoleConfigDeletionFailedReason used to report failures while deleting ROSAOCMRoleConfig.
46+
ROSAOCMRoleConfigDeletionFailedReason = "DeletionFailed"
47+
48+
// ROSAOCMRoleConfigReconciliationFailedReason used to report reconciliation failures.
49+
ROSAOCMRoleConfigReconciliationFailedReason = "ReconciliationFailed"
50+
51+
// ROSAOCMRoleConfigDeletionStarted used to indicate that the deletion of ROSAOCMRoleConfig has started.
52+
ROSAOCMRoleConfigDeletionStarted = "DeletionStarted"
53+
54+
// ROSAOCMRoleConfigCreatedReason used to indicate that the ROSAOCMRoleConfig has been created.
55+
ROSAOCMRoleConfigCreatedReason = "Created"
56+
57+
// ROSAOCMRoleConfigLinkedReason used to indicate that the OCM role has been linked to the organization.
58+
ROSAOCMRoleConfigLinkedReason = "Linked"
59+
)
60+
61+
// ROSAOCMRoleConfigSpec defines the desired state of ROSAOCMRoleConfig
62+
type ROSAOCMRoleConfigSpec struct {
63+
// RolePrefix is the user-defined prefix for the OCM role name.
64+
// The final role name will be: {RolePrefix}-OCM-Role-{ExternalID}
65+
// where ExternalID is the organization's external identifier from OCM.
66+
// +kubebuilder:validation:Required
67+
// +kubebuilder:validation:MaxLength:=4
68+
// +kubebuilder:validation:Pattern:=`^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$`
69+
// +kubebuilder:validation:XValidation:rule="self == oldSelf", message="rolePrefix is immutable"
70+
RolePrefix string `json:"rolePrefix"`
71+
72+
// Profile defines the permission level for the OCM role.
73+
// +kubebuilder:validation:Enum=Standard;Admin;NoConsole
74+
// +kubebuilder:default=Standard
75+
// +kubebuilder:validation:XValidation:rule="self == oldSelf", message="profile is immutable"
76+
Profile ROSAOCMRoleProfile `json:"profile"`
77+
78+
// PermissionsBoundaryARN is the ARN of the policy that is used to set the permissions boundary for the OCM role.
79+
// +optional
80+
PermissionsBoundaryARN string `json:"permissionsBoundaryARN,omitempty"`
81+
82+
// Path is the IAM path for the OCM role.
83+
// +optional
84+
// +kubebuilder:validation:Pattern=`^\/.*$`
85+
Path string `json:"path,omitempty"`
86+
87+
// IdentityRef is a reference to an identity to be used when reconciling the OCM Role Config.
88+
// If no identity is specified, the default identity for this controller will be used.
89+
// +optional
90+
IdentityRef *infrav1.AWSIdentityReference `json:"identityRef,omitempty"`
91+
92+
// CredentialsSecretRef references a secret with necessary credentials to connect to the OCM API.
93+
// +optional
94+
CredentialsSecretRef *corev1.LocalObjectReference `json:"credentialsSecretRef,omitempty"`
95+
}
96+
97+
// ROSAOCMRoleConfigStatus defines the observed state of ROSAOCMRoleConfig
98+
type ROSAOCMRoleConfigStatus struct {
99+
// RoleARN is the ARN of the created OCM role.
100+
RoleARN string `json:"roleARN,omitempty"`
101+
102+
// OrganizationID is the OCM organization ID that this role is linked to.
103+
OrganizationID string `json:"organizationID,omitempty"`
104+
105+
// Conditions specifies the ROSAOCMRoleConfig conditions
106+
Conditions clusterv1beta1.Conditions `json:"conditions,omitempty"`
107+
}
108+
109+
// ROSAOCMRoleConfig is the Schema for the rosaocmroleconfigs API
110+
// +kubebuilder:object:root=true
111+
// +kubebuilder:resource:path=rosaocmroleconfigs,scope=Cluster,categories=cluster-api,shortName=rosaocmrole
112+
// +kubebuilder:storageversion
113+
// +kubebuilder:subresource:status
114+
type ROSAOCMRoleConfig struct {
115+
metav1.TypeMeta `json:",inline"`
116+
metav1.ObjectMeta `json:"metadata,omitempty"`
117+
118+
Spec ROSAOCMRoleConfigSpec `json:"spec,omitempty"`
119+
Status ROSAOCMRoleConfigStatus `json:"status,omitempty"`
120+
}
121+
122+
// ROSAOCMRoleConfigList contains a list of ROSAOCMRoleConfig
123+
// +kubebuilder:object:root=true
124+
type ROSAOCMRoleConfigList struct {
125+
metav1.TypeMeta `json:",inline"`
126+
metav1.ListMeta `json:"metadata,omitempty"`
127+
Items []ROSAOCMRoleConfig `json:"items"`
128+
}
129+
130+
// SetConditions sets the conditions of the ROSAOCMRoleConfig.
131+
func (r *ROSAOCMRoleConfig) SetConditions(conditions clusterv1beta1.Conditions) {
132+
r.Status.Conditions = conditions
133+
}
134+
135+
// GetConditions returns the observations of the operational state of the ROSAOCMRoleConfig resource.
136+
func (r *ROSAOCMRoleConfig) GetConditions() clusterv1beta1.Conditions {
137+
return r.Status.Conditions
138+
}
139+
140+
func init() {
141+
SchemeBuilder.Register(&ROSAOCMRoleConfig{}, &ROSAOCMRoleConfigList{})
142+
}

0 commit comments

Comments
 (0)