Skip to content

Commit b009581

Browse files
loganmc10imiller0
andauthored
allow precreating ACM resources (#96)
* allow precreating ACM resources * Update docs/ACMRegistration.md Co-authored-by: Ian Miller <34246471+imiller0@users.noreply.github.com> --------- Co-authored-by: Ian Miller <34246471+imiller0@users.noreply.github.com>
1 parent 3fd3ce8 commit b009581

File tree

2 files changed

+50
-8
lines changed

2 files changed

+50
-8
lines changed

docs/ACMRegistration.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,38 @@ oc apply -f /tmp/acm-secret.yaml
5959
```
6060

6161
Now your target cluster has a Secret than will allow it to authenticate to the ACM cluster and register itself. Once the registration succeeds, the secret is deleted from the target cluster.
62+
63+
## Pre-creating the ManagedCluster and (optionally) KlusterletAddonConfig
64+
For use cases where flexible and ongoing control over the `ManagedCluster` and (optionally) the `klusterletAddonConfig` CRs is required you may pre-create these artifacts on the ACM hub cluster prior to relocation. This allows the user to control the lifetime and content (eg custom labels on the ManagedCluster) CRs beyond the initial installation phase.
65+
66+
When these CRs are pre-created for a cluster you can omit the `managedClusterSet` and `klusterletAddonConfig` fields from the relocation CR. In this case, the Service Account that you create only needs permissions to "get" Secrets for the cluster namespace created by the ManagedCluster.
67+
68+
For example (run these commands on your ACM cluster):
69+
```
70+
oc create sa -n multicluster-engine acm-registration-sa
71+
72+
cat << EOF | oc apply -f -
73+
apiVersion: rbac.authorization.k8s.io/v1
74+
kind: ClusterRole
75+
metadata:
76+
name: secret-reader
77+
rules:
78+
- apiGroups: [""]
79+
resources: ["secrets"]
80+
verbs: ["get"]
81+
---
82+
apiVersion: rbac.authorization.k8s.io/v1
83+
kind: RoleBinding
84+
metadata:
85+
name: read-secrets
86+
namespace: <managed-cluster-name>
87+
subjects:
88+
- kind: ServiceAccount
89+
name: acm-registration-sa
90+
namespace: multicluster-engine
91+
roleRef:
92+
kind: ClusterRole
93+
name: secret-reader
94+
apiGroup: rbac.authorization.k8s.io
95+
EOF
96+
```

internal/acm/reconcile.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,11 @@ func Reconcile(ctx context.Context, c client.Client, scheme *runtime.Scheme, rel
106106
}
107107

108108
// the acmSecret holds the credentials for the ACM cluster
109-
// these credentials should allow the following:
110-
// Creating ManagedClusters (these are cluster scoped resources)
111-
// Creating KlusterletAddonConfigs (these are namespace scoped resources)
112-
// Getting Secrets (these are namespace scoped resources)
113-
//
114-
// the secret is deleted once registration succeeds
109+
// there are 2 options:
110+
// 1. Have this operator create the ManagedCluster and (optionally) KlusterletAddonConfig.
111+
// In this case, the acmSecret token should have open-cluster-management:managedclusterset:admin:default (ClusterRole) permissions.
112+
// 2. Pre-create the ManagedCluster and (optionally) KlusterletAddonConfig.
113+
// In this case, the acmSecret token should have permissions to "get" Secrets for the namespace created by the ManagedCluster.
115114
acmSecret := &corev1.Secret{}
116115
if err := c.Get(ctx, types.NamespacedName{Name: relocation.Spec.ACMRegistration.ACMSecret.Name, Namespace: relocation.Spec.ACMRegistration.ACMSecret.Namespace}, acmSecret); err != nil {
117116
return err
@@ -177,18 +176,26 @@ func Reconcile(ctx context.Context, c client.Client, scheme *runtime.Scheme, rel
177176
},
178177
}
179178
if err := acmClient.Create(ctx, managedCluster); err != nil {
180-
if !errors.IsAlreadyExists(err) {
179+
if errors.IsForbidden(err) {
180+
logger.Info("could not create ManagedCluster, proceeding anyway (it may have been pre-created)")
181+
} else if !errors.IsAlreadyExists(err) {
181182
return err
182183
}
183184
}
184185

186+
startTime := time.Now()
185187
logger.Info("getting ACM import secret")
186188
importSecret := &corev1.Secret{}
187189
for {
188190
if err := acmClient.Get(ctx, types.NamespacedName{Name: fmt.Sprintf("%s-import", relocation.Spec.ACMRegistration.ClusterName), Namespace: relocation.Spec.ACMRegistration.ClusterName}, importSecret); err != nil {
189191
// after the ManagedCluster is created, it can take some time for this secret and the RBAC roles to be created
190192
if errors.IsNotFound(err) || errors.IsForbidden(err) {
191193
time.Sleep(time.Second * 10)
194+
195+
// we set a 5 minute timeout in case the ACM import secret can never be pulled
196+
if time.Since(startTime) > time.Minute*5 {
197+
return fmt.Errorf("could not get ACM import secret")
198+
}
192199
continue
193200
}
194201
return err
@@ -259,7 +266,7 @@ func Reconcile(ctx context.Context, c client.Client, scheme *runtime.Scheme, rel
259266

260267
logger.Info("waiting for Klusterlet to become Available")
261268
// wait for the Klusterlet to become Available
262-
startTime := time.Now()
269+
startTime = time.Now()
263270
for {
264271
if checkKlusterlet(ctx, c, relocation, logger) == nil {
265272
return nil

0 commit comments

Comments
 (0)