Skip to content

Commit 4b03634

Browse files
committed
read credentials(token) from a referenced secret
1 parent 0f3a162 commit 4b03634

File tree

10 files changed

+152
-62
lines changed

10 files changed

+152
-62
lines changed

config/crd/bases/controlplane.cluster.x-k8s.io_rosacontrolplanes.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ spec:
7272
type: object
7373
creatorARN:
7474
type: string
75+
credentialsSecretRef:
76+
description: 'CredentialsSecretRef references a secret with necessary
77+
credentials to connect to the OCM API. The secret should contain
78+
the following data keys: - ocmToken: eyJhbGciOiJIUzI1NiIsI.... -
79+
ocmApiUrl: Optional, defaults to ''https://api.openshift.com'''
80+
properties:
81+
name:
82+
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
83+
TODO: Add other useful fields. apiVersion, kind, uid?'
84+
type: string
85+
type: object
86+
x-kubernetes-map-type: atomic
7587
installerRoleARN:
7688
type: string
7789
machineCIDR:

controlplane/rosa/api/v1beta2/rosacontrolplane_types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package v1beta2
1818

1919
import (
20+
corev1 "k8s.io/api/core/v1"
2021
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2122

2223
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -66,6 +67,13 @@ type RosaControlPlaneSpec struct { //nolint: maligned
6667
InstallerRoleARN *string `json:"installerRoleARN"`
6768
SupportRoleARN *string `json:"supportRoleARN"`
6869
WorkerRoleARN *string `json:"workerRoleARN"`
70+
71+
// CredentialsSecretRef references a secret with necessary credentials to connect to the OCM API.
72+
// The secret should contain the following data keys:
73+
// - ocmToken: eyJhbGciOiJIUzI1NiIsI....
74+
// - ocmApiUrl: Optional, defaults to 'https://api.openshift.com'
75+
// +optional
76+
CredentialsSecretRef *corev1.LocalObjectReference `json:"credentialsSecretRef,omitempty"`
6977
}
7078

7179
// AWSRolesRef contains references to various AWS IAM roles required for operators to make calls against the AWS API.

controlplane/rosa/api/v1beta2/zz_generated.deepcopy.go

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

controlplane/rosa/controllers/rosacontrolplane_controller.go

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"context"
2121
"errors"
2222
"fmt"
23-
"os"
2423
"strings"
2524
"time"
2625

@@ -170,18 +169,13 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc
170169
}
171170
}
172171

173-
// TODO: token should be read from a secret: https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues/4460
174-
token := os.Getenv("OCM_TOKEN")
175-
rosaClient, err := rosa.NewRosaClient(token)
172+
rosaClient, err := rosa.NewRosaClient(ctx, rosaScope)
176173
if err != nil {
177174
return ctrl.Result{}, fmt.Errorf("failed to create a rosa client: %w", err)
178175
}
176+
defer rosaClient.Close()
179177

180-
defer func() {
181-
rosaClient.Close()
182-
}()
183-
184-
cluster, err := rosaClient.GetCluster(rosaScope.RosaClusterName(), rosaScope.ControlPlane.Spec.CreatorARN)
178+
cluster, err := rosaClient.GetCluster()
185179
if err != nil {
186180
return ctrl.Result{}, err
187181
}
@@ -333,22 +327,16 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc
333327
return ctrl.Result{}, nil
334328
}
335329

336-
func (r *ROSAControlPlaneReconciler) reconcileDelete(_ context.Context, rosaScope *scope.ROSAControlPlaneScope) (res ctrl.Result, reterr error) {
330+
func (r *ROSAControlPlaneReconciler) reconcileDelete(ctx context.Context, rosaScope *scope.ROSAControlPlaneScope) (res ctrl.Result, reterr error) {
337331
rosaScope.Info("Reconciling ROSAControlPlane delete")
338332

339-
// Create the connection, and remember to close it:
340-
// TODO: token should be read from a secret: https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues/4460
341-
token := os.Getenv("OCM_TOKEN")
342-
rosaClient, err := rosa.NewRosaClient(token)
333+
rosaClient, err := rosa.NewRosaClient(ctx, rosaScope)
343334
if err != nil {
344335
return ctrl.Result{}, fmt.Errorf("failed to create a rosa client: %w", err)
345336
}
337+
defer rosaClient.Close()
346338

347-
defer func() {
348-
rosaClient.Close()
349-
}()
350-
351-
cluster, err := rosaClient.GetCluster(rosaScope.RosaClusterName(), rosaScope.ControlPlane.Spec.CreatorARN)
339+
cluster, err := rosaClient.GetCluster()
352340
if err != nil {
353341
return ctrl.Result{}, err
354342
}

docs/book/src/topics/rosa/creating-a-cluster.md

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,25 @@ CAPA controller requires an API token in order to be able to provision ROSA clus
55

66
1. Visit [https://console.redhat.com/openshift/token](https://console.redhat.com/openshift/token) to retrieve your API authentication token
77

8-
2. Edit CAPA controller deployment:
8+
1. Create a credentials secret with the token to be referenced later by `ROSAControlePlane`
9+
```shell
10+
kubectl create secret generic rosa-creds-secret \
11+
--from-literal=ocmToken='eyJhbGciOiJIUzI1NiIsI....' \
12+
--from-literal=ocmApiUrl='https://api.openshift.com'
13+
```
14+
15+
Alternatively, you can edit CAPA controller deployment to provide the credentials:
916
```shell
1017
kubectl edit deployment -n capa-system capa-controller-manager
1118
```
12-
19+
1320
and add the following environment variables to the manager container:
1421
```yaml
15-
env:
16-
- name: OCM_TOKEN
17-
value: "<token>"
18-
- name: OCM_API_URL
19-
value: "https://api.openshift.com" # or https://api.stage.openshift.com
22+
env:
23+
- name: OCM_TOKEN
24+
value: "<token>"
25+
- name: OCM_API_URL
26+
value: "https://api.openshift.com" # or https://api.stage.openshift.com
2027
```
2128

2229
## Prerequisites
@@ -45,9 +52,25 @@ Once Step 3 is done, you will be ready to proceed with creating a ROSA cluster u
4552
export PRIVATE_SUBNET_ID="subnet-05e72222222222222"
4653
```
4754

48-
1. Create a cluster using the ROSA cluster template:
49-
```bash
55+
1. Render the cluster manifest using the ROSA cluster template:
56+
```shell
5057
cat templates/cluster-template-rosa.yaml | envsubst > rosa-capi-cluster.yaml
58+
```
5159

60+
1. If a credentials secret was created earlier, edit `ROSAControlPlane` to refernce it:
61+
62+
```yaml
63+
apiVersion: controlplane.cluster.x-k8s.io/v1beta2
64+
kind: ROSAControlPlane
65+
metadata:
66+
name: "capi-rosa-quickstart-control-plane"
67+
spec:
68+
credentialsSecretRef:
69+
name: rosa-creds-secret
70+
...
71+
```
72+
73+
1. Finally apply the manifest to create your Rosa cluster:
74+
```shell
5275
kubectl apply -f rosa-capi-cluster.yaml
5376
```

exp/controllers/rosamachinepool_controller.go

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package controllers
33
import (
44
"context"
55
"fmt"
6-
"os"
76
"time"
87

98
cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
@@ -129,6 +128,16 @@ func (r *ROSAMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Requ
129128
return ctrl.Result{}, errors.Wrap(err, "failed to create scope")
130129
}
131130

131+
rosaControlPlaneScope, err := scope.NewROSAControlPlaneScope(scope.ROSAControlPlaneScopeParams{
132+
Client: r.Client,
133+
Cluster: cluster,
134+
ControlPlane: controlPlane,
135+
ControllerName: "rosaControlPlane",
136+
})
137+
if err != nil {
138+
return ctrl.Result{}, errors.Wrap(err, "failed to create control plane scope")
139+
}
140+
132141
if !controlPlane.Status.Ready {
133142
log.Info("Control plane is not ready yet")
134143
err := machinePoolScope.RosaMchinePoolReadyFalse(expinfrav1.WaitingForRosaControlPlaneReason, "")
@@ -144,14 +153,16 @@ func (r *ROSAMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Requ
144153
}()
145154

146155
if !rosaMachinePool.ObjectMeta.DeletionTimestamp.IsZero() {
147-
return ctrl.Result{}, r.reconcileDelete(ctx, machinePoolScope)
156+
return ctrl.Result{}, r.reconcileDelete(ctx, machinePoolScope, rosaControlPlaneScope)
148157
}
149158

150-
return r.reconcileNormal(ctx, machinePoolScope)
159+
return r.reconcileNormal(ctx, machinePoolScope, rosaControlPlaneScope)
151160
}
152161

153-
func (r *ROSAMachinePoolReconciler) reconcileNormal(
154-
ctx context.Context, machinePoolScope *scope.RosaMachinePoolScope) (ctrl.Result, error) {
162+
func (r *ROSAMachinePoolReconciler) reconcileNormal(ctx context.Context,
163+
machinePoolScope *scope.RosaMachinePoolScope,
164+
rosaControlPlaneScope *scope.ROSAControlPlaneScope,
165+
) (ctrl.Result, error) {
155166
machinePoolScope.Info("Reconciling RosaMachinePool")
156167

157168
if controllerutil.AddFinalizer(machinePoolScope.RosaMachinePool, expinfrav1.RosaMachinePoolFinalizer) {
@@ -160,16 +171,11 @@ func (r *ROSAMachinePoolReconciler) reconcileNormal(
160171
}
161172
}
162173

163-
// TODO: token should be read from a secret: https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues/4460
164-
token := os.Getenv("OCM_TOKEN")
165-
rosaClient, err := rosa.NewRosaClient(token)
174+
rosaClient, err := rosa.NewRosaClient(ctx, rosaControlPlaneScope)
166175
if err != nil {
167176
return ctrl.Result{}, fmt.Errorf("failed to create a rosa client: %w", err)
168177
}
169-
170-
defer func() {
171-
rosaClient.Close()
172-
}()
178+
defer rosaClient.Close()
173179

174180
rosaMachinePool := machinePoolScope.RosaMachinePool
175181
machinePool := machinePoolScope.MachinePool
@@ -211,7 +217,7 @@ func (r *ROSAMachinePoolReconciler) reconcileNormal(
211217
MinReplica(rosaMachinePool.Spec.Autoscaling.MinReplicas).
212218
MaxReplica(rosaMachinePool.Spec.Autoscaling.MaxReplicas))
213219
} else {
214-
var replicas int = 1
220+
replicas := 1
215221
if machinePool.Spec.Replicas != nil {
216222
replicas = int(*machinePool.Spec.Replicas)
217223
}
@@ -240,19 +246,16 @@ func (r *ROSAMachinePoolReconciler) reconcileNormal(
240246
}
241247

242248
func (r *ROSAMachinePoolReconciler) reconcileDelete(
243-
_ context.Context, machinePoolScope *scope.RosaMachinePoolScope) error {
249+
ctx context.Context, machinePoolScope *scope.RosaMachinePoolScope,
250+
rosaControlPlaneScope *scope.ROSAControlPlaneScope,
251+
) error {
244252
machinePoolScope.Info("Reconciling deletion of RosaMachinePool")
245253

246-
// TODO: token should be read from a secret: https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues/4460
247-
token := os.Getenv("OCM_TOKEN")
248-
rosaClient, err := rosa.NewRosaClient(token)
254+
rosaClient, err := rosa.NewRosaClient(ctx, rosaControlPlaneScope)
249255
if err != nil {
250256
return fmt.Errorf("failed to create a rosa client: %w", err)
251257
}
252-
253-
defer func() {
254-
rosaClient.Close()
255-
}()
258+
defer rosaClient.Close()
256259

257260
nodePool, found, err := rosaClient.GetNodePool(*machinePoolScope.ControlPlane.Status.ID, machinePoolScope.NodePoolName())
258261
if err != nil {

pkg/cloud/scope/rosacontrolplane.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"context"
2121

2222
"github.com/pkg/errors"
23+
corev1 "k8s.io/api/core/v1"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2325
"k8s.io/klog/v2"
2426
"sigs.k8s.io/controller-runtime/pkg/client"
2527

@@ -95,12 +97,29 @@ func (s *ROSAControlPlaneScope) Namespace() string {
9597
return s.Cluster.Namespace
9698
}
9799

100+
// CredentialsSecret returns the CredentialsSecret object.
101+
func (s *ROSAControlPlaneScope) CredentialsSecret() *corev1.Secret {
102+
secretRef := s.ControlPlane.Spec.CredentialsSecretRef
103+
if secretRef == nil {
104+
return nil
105+
}
106+
107+
return &corev1.Secret{
108+
ObjectMeta: metav1.ObjectMeta{
109+
Name: s.ControlPlane.Spec.CredentialsSecretRef.Name,
110+
Namespace: s.ControlPlane.Namespace,
111+
},
112+
}
113+
}
114+
98115
// PatchObject persists the control plane configuration and status.
99116
func (s *ROSAControlPlaneScope) PatchObject() error {
100117
return s.patchHelper.Patch(
101118
context.TODO(),
102119
s.ControlPlane,
103-
patch.WithOwnedConditions{Conditions: []clusterv1.ConditionType{}})
120+
patch.WithOwnedConditions{Conditions: []clusterv1.ConditionType{
121+
rosacontrolplanev1.ROSAControlPlaneReadyCondition,
122+
}})
104123
}
105124

106125
// Close closes the current scope persisting the control plane configuration and status.

pkg/cloud/scope/rosamachinepool.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func (s *RosaMachinePoolScope) NodePoolName() string {
109109
return s.RosaMachinePool.Spec.NodePoolName
110110
}
111111

112-
// ClusterName returns the cluster name.
112+
// RosaClusterName returns the cluster name.
113113
func (s *RosaMachinePoolScope) RosaClusterName() string {
114114
return s.ControlPlane.Spec.RosaClusterName
115115
}

0 commit comments

Comments
 (0)