diff --git a/pkg/addon/agent/controller/token.go b/pkg/addon/agent/controller/token.go index ab63a089..ef06cf50 100644 --- a/pkg/addon/agent/controller/token.go +++ b/pkg/addon/agent/controller/token.go @@ -258,12 +258,12 @@ func (r *TokenReconciler) createTokenByDefaultSecret( if secret.Annotations[corev1.ServiceAccountNameKey] != managed.Name { continue } - if secret.Data["token"] == nil { + if secret.Data[corev1.ServiceAccountTokenKey] == nil { return "", metav1.Time{}, errors.Errorf("token is not found in secret %s", secret.Name) } defaultExpirationTime := metav1.NewTime(secret.CreationTimestamp.Add(managed.Spec.Rotation.Validity.Duration)) - return string(secret.Data["token"]), defaultExpirationTime, nil + return string(secret.Data[corev1.ServiceAccountTokenKey]), defaultExpirationTime, nil } return "", metav1.Time{}, errors.Errorf("no default token is found for service account %s", managed.Name) diff --git a/pkg/addon/agent/controller/token_test.go b/pkg/addon/agent/controller/token_test.go index 5aa6ba60..24572868 100644 --- a/pkg/addon/agent/controller/token_test.go +++ b/pkg/addon/agent/controller/token_test.go @@ -430,6 +430,16 @@ func newServiceAccount(namespace, name string) *corev1.ServiceAccount { } } +func newServiceAccountWithSecret(namespace, name string, secretName string) *corev1.ServiceAccount { + sa := newServiceAccount(namespace, name) + sa.Secrets = []corev1.ObjectReference{ + { + Name: secretName, + }, + } + return sa +} + // assertActions asserts the actual actions have the expected action verb func assertActions(t *testing.T, actualActions []clienttesting.Action, expectedVerbs ...string) { if len(actualActions) != len(expectedVerbs) { @@ -472,3 +482,143 @@ func assertMSAConditions(t *testing.T, client client.Client, msaNamespace, msaNa "condition %q with status %v not found", condition.Type, condition.Status) } } + +func TestReconcileCreateTokenByDefaultSecret(t *testing.T) { + clusterName := "cluster1" + msaName := "msa1" + token1 := "token1" + ca1 := "ca1" + + cases := []struct { + name string + msa *authv1beta1.ManagedServiceAccount + sa *corev1.ServiceAccount + spokeSecret *corev1.Secret + secret *corev1.Secret + spokeNamespace string + getError error + newToken string + isExistingTokenInvalid bool + expectedError string + validateFunc func(t *testing.T, hubClient client.Client, actions []clienttesting.Action) + }{ + { + name: "create token", + sa: newServiceAccountWithSecret(clusterName, msaName, msaName), + msa: newManagedServiceAccount(clusterName, msaName).build(), + spokeSecret: newSecret(clusterName, msaName, token1, ca1, func(secret *corev1.Secret) { + secret.Type = corev1.SecretTypeServiceAccountToken + secret.Annotations[corev1.ServiceAccountNameKey] = msaName + }), + spokeNamespace: clusterName, + newToken: token1, + validateFunc: func(t *testing.T, hubClient client.Client, actions []clienttesting.Action) { + assertActions(t, actions, "create", // create serviceaccount + "get", // get serviceaccount + "get", // get secret + ) + assertToken(t, hubClient, clusterName, msaName, token1, ca1) + assertMSAConditions(t, hubClient, clusterName, msaName, []metav1.Condition{ + { + Type: authv1beta1.ConditionTypeTokenReported, + Status: metav1.ConditionTrue, + }, + { + Type: authv1beta1.ConditionTypeSecretCreated, + Status: metav1.ConditionTrue, + }, + }) + }, + }, + { + name: "create token failed", + sa: newServiceAccountWithSecret(clusterName, msaName, msaName), + msa: newManagedServiceAccount(clusterName, msaName).build(), + spokeSecret: newSecret(clusterName, msaName, token1, ca1, func(secret *corev1.Secret) { + secret.Type = corev1.SecretTypeServiceAccountToken + }), + spokeNamespace: clusterName, + newToken: token1, + expectedError: "failed to sync token: failed to request token for service-account: no default token is found for service account msa1", + validateFunc: func(t *testing.T, hubClient client.Client, actions []clienttesting.Action) { + assertActions(t, actions, "create", // create serviceaccount + "get", // get serviceaccount + "get", // get secret + ) + assertMSAConditions(t, hubClient, clusterName, msaName, []metav1.Condition{ + { + Type: authv1beta1.ConditionTypeTokenReported, + Status: metav1.ConditionFalse, + }, + }) + }, + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + // create fake kube client of the managed cluster + objs := []runtime.Object{} + if c.sa != nil { + objs = append(objs, c.sa) + } + if c.spokeSecret != nil { + objs = append(objs, c.spokeSecret) + } + fakeKubeClient := fakekube.NewSimpleClientset(objs...) + fakeKubeClient.PrependReactor( + "create", + "tokenreviews", + func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &authv1.TokenReview{ + Status: authv1.TokenReviewStatus{ + Authenticated: !c.isExistingTokenInvalid, + }, + }, nil + }, + ) + + // create fake client of the hub cluster + testscheme := runtime.NewScheme() + authv1beta1.AddToScheme(testscheme) + corev1.AddToScheme(testscheme) + var objects []client.Object + if c.msa != nil { + objects = append(objects, c.msa) + } + if c.secret != nil { + objects = append(objects, c.secret) + } + + hubClient := fake.NewClientBuilder().WithScheme(testscheme).WithObjects(objects...). + WithStatusSubresource(objects...).Build() + reconciler := TokenReconciler{ + Cache: &fakeCache{ + msa: c.msa, + getError: c.getError, + }, + SpokeNativeClient: fakeKubeClient, + HubClient: hubClient, + SpokeClientConfig: &rest.Config{ + TLSClientConfig: rest.TLSClientConfig{ + CAData: []byte(ca1), + }, + }, + SpokeNamespace: c.spokeNamespace, + CreateTokenByDefaultSecret: true, + } + + _, err := reconciler.Reconcile(context.Background(), reconcile.Request{NamespacedName: types.NamespacedName{ + Name: msaName, + Namespace: clusterName, + }}) + + if len(c.expectedError) != 0 { + assert.EqualError(t, err, c.expectedError) + } + if c.validateFunc != nil { + c.validateFunc(t, hubClient, fakeKubeClient.Actions()) + } + + }) + } +}