Skip to content

Commit 7a7993c

Browse files
authored
Merge pull request #1665 from srm09/automated-cherry-pick-of-#1653-release-1.3
Automated cherry pick of #1653: Creates token secret for service account
2 parents 3f63bbc + 14b6f7f commit 7a7993c

File tree

3 files changed

+106
-53
lines changed

3 files changed

+106
-53
lines changed

controllers/serviceaccount_controller.go

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,10 @@ func AddServiceAccountProviderControllerToManager(ctx *context.ControllerManager
8989
&source.Kind{Type: &vmwarev1.ProviderServiceAccount{}},
9090
handler.EnqueueRequestsFromMapFunc(r.providerServiceAccountToVSphereCluster),
9191
).
92+
// Watches the secrets to re-enqueue once the service account token is set
9293
Watches(
93-
&source.Kind{Type: &corev1.ServiceAccount{}},
94-
handler.EnqueueRequestsFromMapFunc(r.serviceAccountToVSphereCluster),
94+
&source.Kind{Type: &corev1.Secret{}},
95+
handler.EnqueueRequestsFromMapFunc(r.secretToVSphereCluster),
9596
).
9697
Complete(r)
9798
}
@@ -228,18 +229,22 @@ func (r ServiceAccountReconciler) ensureProviderServiceAccounts(ctx *vmwareconte
228229
if err := r.ensureServiceAccountConfigMap(ctx.ClusterContext, pSvcAccount); err != nil {
229230
return errors.Wrapf(err, "unable to sync configmap for provider serviceaccount %s", pSvcAccount.Name)
230231
}
232+
// 3. Create secret of Service account token type for the service account
233+
if err := r.ensureServiceAccountSecret(ctx.ClusterContext, pSvcAccount); err != nil {
234+
return errors.Wrapf(err, "unable to create provider serviceaccount secret %s", getServiceAccountSecretName(pSvcAccount))
235+
}
231236

232-
// 3. Create the associated role for the service account
237+
// 4. Create the associated role for the service account
233238
if err := r.ensureRole(ctx.ClusterContext, pSvcAccount); err != nil {
234239
return errors.Wrapf(err, "unable to create role for provider serviceaccount %s", pSvcAccount.Name)
235240
}
236241

237-
// 4. Create the associated roleBinding for the service account
242+
// 5. Create the associated roleBinding for the service account
238243
if err := r.ensureRoleBinding(ctx.ClusterContext, pSvcAccount); err != nil {
239244
return errors.Wrapf(err, "unable to create rolebinding for provider serviceaccount %s", pSvcAccount.Name)
240245
}
241246

242-
// 5. Sync the service account with the target
247+
// 6. Sync the service account with the target
243248
if err := r.syncServiceAccountSecret(ctx, pSvcAccount); err != nil {
244249
return errors.Wrapf(err, "unable to sync secret for provider serviceaccount %s", pSvcAccount.Name)
245250
}
@@ -269,6 +274,34 @@ func (r ServiceAccountReconciler) ensureServiceAccount(ctx *vmwarecontext.Cluste
269274
return nil
270275
}
271276

277+
func (r ServiceAccountReconciler) ensureServiceAccountSecret(ctx *vmwarecontext.ClusterContext, pSvcAccount vmwarev1.ProviderServiceAccount) error {
278+
secret := corev1.Secret{
279+
Type: corev1.SecretTypeServiceAccountToken,
280+
ObjectMeta: metav1.ObjectMeta{
281+
Name: getServiceAccountSecretName(pSvcAccount),
282+
Namespace: pSvcAccount.Namespace,
283+
Annotations: map[string]string{
284+
// denotes that this secret holds the token for the service account
285+
corev1.ServiceAccountNameKey: getServiceAccountName(pSvcAccount),
286+
},
287+
},
288+
}
289+
290+
logger := ctx.Logger.WithValues("providerserviceaccount", pSvcAccount.Name, "secret", secret.Name)
291+
err := controllerutil.SetControllerReference(&pSvcAccount, &secret, ctx.Scheme)
292+
if err != nil {
293+
return err
294+
}
295+
logger.V(4).Info("Creating service account secret")
296+
err = ctx.Client.Create(ctx, &secret)
297+
if err != nil && !apierrors.IsAlreadyExists(err) {
298+
// Note: We skip updating the service account because the token controller updates the service account with a
299+
// secret and we don't want to overwrite it with an empty secret.
300+
return err
301+
}
302+
return nil
303+
}
304+
272305
func (r ServiceAccountReconciler) ensureRole(ctx *vmwarecontext.ClusterContext, pSvcAccount vmwarev1.ProviderServiceAccount) error {
273306
role := rbacv1.Role{
274307
ObjectMeta: metav1.ObjectMeta{
@@ -323,29 +356,23 @@ func (r ServiceAccountReconciler) ensureRoleBinding(ctx *vmwarecontext.ClusterCo
323356

324357
func (r ServiceAccountReconciler) syncServiceAccountSecret(ctx *vmwarecontext.GuestClusterContext, pSvcAccount vmwarev1.ProviderServiceAccount) error {
325358
logger := ctx.Logger.WithValues("providerserviceaccount", pSvcAccount.Name)
326-
logger.V(4).Info("Attempting to sync secret for provider service account")
327-
var svcAccount corev1.ServiceAccount
328-
err := ctx.Client.Get(ctx, types.NamespacedName{Name: getServiceAccountName(pSvcAccount), Namespace: pSvcAccount.Namespace}, &svcAccount)
359+
logger.V(4).Info("Attempting to sync token secret for provider service account")
360+
361+
secretName := getServiceAccountSecretName(pSvcAccount)
362+
logger.V(4).Info("Fetching secret for service account token details", "secret", secretName)
363+
var svcAccountTokenSecret corev1.Secret
364+
err := ctx.Client.Get(ctx, types.NamespacedName{Name: secretName, Namespace: pSvcAccount.Namespace}, &svcAccountTokenSecret)
329365
if err != nil {
330366
return err
331367
}
332-
// Check if token secret exists
333-
if len(svcAccount.Secrets) == 0 {
334-
// Note: We don't have to requeue here because we have a watch on the service account and the cluster should be reconciled
335-
// when a secret is added to the service account by the token controller.
336-
logger.Info("Skipping sync secret for provider service account: serviceaccount has no secrets", "serviceaccount", svcAccount.Name)
368+
// Check if token data exists
369+
if len(svcAccountTokenSecret.Data) == 0 {
370+
// Note: We don't have to requeue here because we have a watch on the secret and the cluster should be reconciled
371+
// when a secret has token data populated.
372+
logger.Info("Skipping sync secret for provider service account: secret has no data", "secret", secretName)
337373
return nil
338374
}
339375

340-
// Choose the default secret
341-
secretRef := svcAccount.Secrets[0]
342-
logger.V(4).Info("Fetching secret for provider service account", "secret", secretRef.Name)
343-
var sourceSecret corev1.Secret
344-
err = ctx.Client.Get(ctx, types.NamespacedName{Name: secretRef.Name, Namespace: svcAccount.Namespace}, &sourceSecret)
345-
if err != nil {
346-
return err
347-
}
348-
349376
// Create the target namespace if it is not existing
350377
targetNamespace := &corev1.Namespace{
351378
ObjectMeta: metav1.ObjectMeta{
@@ -372,7 +399,7 @@ func (r ServiceAccountReconciler) syncServiceAccountSecret(ctx *vmwarecontext.Gu
372399
}
373400
logger.V(4).Info("Creating or updating secret in cluster", "namespace", targetSecret.Namespace, "name", targetSecret.Name)
374401
_, err = controllerutil.CreateOrPatch(ctx, ctx.GuestClient, targetSecret, func() error {
375-
targetSecret.Data = sourceSecret.Data
402+
targetSecret.Data = svcAccountTokenSecret.Data
376403
return nil
377404
})
378405
return err
@@ -468,6 +495,10 @@ func getServiceAccountName(pSvcAccount vmwarev1.ProviderServiceAccount) string {
468495
return pSvcAccount.Name
469496
}
470497

498+
func getServiceAccountSecretName(pSvcAccount vmwarev1.ProviderServiceAccount) string {
499+
return fmt.Sprintf("%s-secret", pSvcAccount.Name)
500+
}
501+
471502
func getSystemServiceAccountFullName(pSvcAccount vmwarev1.ProviderServiceAccount) string {
472503
return fmt.Sprintf("%s.%s.%s", systemServiceAccountPrefix, getServiceAccountNamespace(pSvcAccount), getServiceAccountName(pSvcAccount))
473504
}
@@ -484,6 +515,33 @@ func GetCMNamespaceName() types.NamespacedName {
484515
}
485516
}
486517

518+
// secretToVSphereCluster is a mapper function used to enqueue reconcile.Request objects.
519+
// It accepts a Secret object owned by the controller and fetches the service account
520+
// that contains the token and creates a reconcile.Request for the vmwarev1.VSphereCluster object.
521+
func (r ServiceAccountReconciler) secretToVSphereCluster(o client.Object) []reconcile.Request {
522+
secret, ok := o.(*corev1.Secret)
523+
if !ok {
524+
return nil
525+
}
526+
527+
ownerRef := metav1.GetControllerOf(secret)
528+
if ownerRef != nil && ownerRef.Kind == kindProviderServiceAccount {
529+
if !metav1.HasAnnotation(secret.ObjectMeta, corev1.ServiceAccountNameKey) {
530+
return nil
531+
}
532+
svcAccountName := secret.GetAnnotations()[corev1.ServiceAccountNameKey]
533+
svcAccount := &corev1.ServiceAccount{}
534+
if err := r.Client.Get(r.Context, client.ObjectKey{
535+
Namespace: secret.Namespace,
536+
Name: svcAccountName,
537+
}, svcAccount); err != nil {
538+
return nil
539+
}
540+
return r.serviceAccountToVSphereCluster(svcAccount)
541+
}
542+
return nil
543+
}
544+
487545
// serviceAccountToVSphereCluster is a mapper function used to enqueue reconcile.Request objects.
488546
// From the watched object owned by this controller, it creates reconcile.Request object
489547
// for the vmwarev1.VSphereCluster object that owns the watched object.

controllers/serviceaccount_controller_suite_test.go

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package controllers
1818

1919
import (
2020
goctx "context"
21+
"fmt"
2122
"strings"
2223
"time"
2324

@@ -35,13 +36,10 @@ import (
3536
)
3637

3738
const (
38-
testProviderSvcAccountName = "test-pvcsi"
39-
40-
testTargetNS = "test-pvcsi-system"
41-
testTargetSecret = "test-pvcsi-secret" // nolint:gosec
42-
testSvcAccountSecretName = testProviderSvcAccountName + "-token-abcdef"
43-
testSystemSvcAcctNs = "test-system-svc-acct-namespace"
44-
testSystemSvcAcctCM = "test-system-svc-acct-cm"
39+
testTargetNS = "test-pvcsi-system"
40+
testTargetSecret = "test-pvcsi-secret" //nolint:gosec
41+
testSystemSvcAcctNs = "test-system-svc-acct-namespace"
42+
testSystemSvcAcctCM = "test-system-svc-acct-cm"
4543

4644
testSecretToken = "ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklp" // nolint:gosec
4745
)
@@ -94,15 +92,14 @@ func assertNoEntities(ctx goctx.Context, ctrlClient client.Client, namespace str
9492
func assertServiceAccountAndUpdateSecret(ctx goctx.Context, ctrlClient client.Client, namespace, name string) {
9593
svcAccount := &corev1.ServiceAccount{}
9694
assertEventuallyExistsInNamespace(ctx, ctrlClient, namespace, name, svcAccount)
97-
// Update the service account with a prototype secret
98-
secret := getTestSvcAccountSecret(namespace, testSvcAccountSecretName)
99-
Expect(ctrlClient.Create(ctx, secret)).To(Succeed())
100-
svcAccount.Secrets = []corev1.ObjectReference{
101-
{
102-
Name: testSvcAccountSecretName,
103-
},
95+
secret := &corev1.Secret{}
96+
assertEventuallyExistsInNamespace(ctx, ctrlClient, namespace, fmt.Sprintf("%s-secret", name), secret)
97+
98+
// Update the data on the secret
99+
secret.Data = map[string][]byte{
100+
"token": []byte(testSecretToken),
104101
}
105-
Expect(ctrlClient.Update(ctx, svcAccount)).To(Succeed())
102+
Expect(ctrlClient.Update(ctx, secret)).To(Succeed())
106103
}
107104

108105
func assertTargetSecret(ctx goctx.Context, guestClient client.Client, namespace, name string) { // nolint
@@ -159,9 +156,8 @@ func assertRoleBinding(_ *helpers.UnitTestContextForController, ctrlClient clien
159156
}))
160157
}
161158

162-
// nolint
163-
func assertProviderServiceAccountsCondition(vCluster *vmwarev1.VSphereCluster, status corev1.ConditionStatus,
164-
message string, reason string, severity clusterv1.ConditionSeverity) {
159+
// assertProviderServiceAccountsCondition asserts the condition on the ProviderServiceAccount CR.
160+
func assertProviderServiceAccountsCondition(vCluster *vmwarev1.VSphereCluster, status corev1.ConditionStatus, message string, reason string, severity clusterv1.ConditionSeverity) { //nolint
165161
c := conditions.Get(vCluster, vmwarev1.ProviderServiceAccountsReadyCondition)
166162
Expect(c).NotTo(BeNil())
167163
Expect(c.Status).To(Equal(status))
@@ -246,18 +242,6 @@ func getSystemServiceAccountsConfigMap(namespace, name string) *corev1.ConfigMap
246242
}
247243
}
248244

249-
func getTestSvcAccountSecret(namespace, name string) *corev1.Secret {
250-
return &corev1.Secret{
251-
ObjectMeta: metav1.ObjectMeta{
252-
Name: name,
253-
Namespace: namespace,
254-
},
255-
Data: map[string][]byte{
256-
"token": []byte(testSecretToken),
257-
},
258-
}
259-
}
260-
261245
func getTestRoleWithGetPod(namespace, name string) *rbacv1.Role {
262246
return &rbacv1.Role{
263247
ObjectMeta: metav1.ObjectMeta{

controllers/serviceaccount_controller_unit_test.go

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

1919
import (
20+
"fmt"
2021
"os"
2122

2223
. "github.com/onsi/ginkgo"
@@ -79,6 +80,16 @@ func unitTestsReconcileNormal() {
7980
getTestProviderServiceAccount(namespace, vsphereCluster, false),
8081
}
8182
})
83+
It("should create a service account and a secret", func() {
84+
_, err := reconciler.ReconcileNormal(ctx.GuestClusterContext)
85+
Expect(err).NotTo(HaveOccurred())
86+
87+
svcAccount := &corev1.ServiceAccount{}
88+
assertEventuallyExistsInNamespace(ctx, ctx.Client, namespace, vsphereCluster.GetName(), svcAccount)
89+
90+
secret := &corev1.Secret{}
91+
assertEventuallyExistsInNamespace(ctx, ctx.Client, namespace, fmt.Sprintf("%s-secret", vsphereCluster.GetName()), secret)
92+
})
8293
Context("When serviceaccount secret is created", func() {
8394
It("Should reconcile", func() {
8495
assertTargetNamespace(ctx, ctx.GuestClient, testTargetNS, false)

0 commit comments

Comments
 (0)