diff --git a/pkg/controller/serviceaccount/tokens_controller.go b/pkg/controller/serviceaccount/tokens_controller.go index 253c1efe2f3..2b418d61ac7 100644 --- a/pkg/controller/serviceaccount/tokens_controller.go +++ b/pkg/controller/serviceaccount/tokens_controller.go @@ -331,8 +331,12 @@ func (e *TokensController) createSecret(serviceAccount *api.ServiceAccount) erro } // Save the secret - if _, err := e.client.Core().Secrets(serviceAccount.Namespace).Create(secret); err != nil { + if createdToken, err := e.client.Core().Secrets(serviceAccount.Namespace).Create(secret); err != nil { return err + } else { + // Manually add the new token to the cache store. + // This prevents the service account update (below) triggering another token creation, if the referenced token couldn't be found in the store + e.secrets.Add(createdToken) } liveServiceAccount.Secrets = append(liveServiceAccount.Secrets, api.ObjectReference{Name: secret.Name}) diff --git a/test/e2e/service_accounts.go b/test/e2e/service_accounts.go index f1a9a8e994c..74a9ff7fa84 100644 --- a/test/e2e/service_accounts.go +++ b/test/e2e/service_accounts.go @@ -28,6 +28,7 @@ import ( "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount" . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" ) var serviceAccountTokenNamespaceVersion = version.MustParse("v1.2.0") @@ -35,6 +36,121 @@ var serviceAccountTokenNamespaceVersion = version.MustParse("v1.2.0") var _ = Describe("ServiceAccounts", func() { f := NewFramework("svcaccounts") + It("should ensure a single API token exists", func() { + // wait for the service account to reference a single secret + var secrets []api.ObjectReference + expectNoError(wait.Poll(time.Millisecond*500, time.Second*10, func() (bool, error) { + By("waiting for a single token reference") + sa, err := f.Client.ServiceAccounts(f.Namespace.Name).Get("default") + if apierrors.IsNotFound(err) { + Logf("default service account was not found") + return false, nil + } + if err != nil { + Logf("error getting default service account: %v", err) + return false, err + } + switch len(sa.Secrets) { + case 0: + Logf("default service account has no secret references") + return false, nil + case 1: + Logf("default service account has a single secret reference") + secrets = sa.Secrets + return true, nil + default: + return false, fmt.Errorf("default service account has too many secret references: %#v", sa.Secrets) + } + })) + + // make sure the reference doesn't flutter + { + By("ensuring the single token reference persists") + time.Sleep(2 * time.Second) + sa, err := f.Client.ServiceAccounts(f.Namespace.Name).Get("default") + expectNoError(err) + Expect(sa.Secrets).To(Equal(secrets)) + } + + // delete the referenced secret + By("deleting the service account token") + expectNoError(f.Client.Secrets(f.Namespace.Name).Delete(secrets[0].Name)) + + // wait for the referenced secret to be removed, and another one autocreated + expectNoError(wait.Poll(time.Millisecond*500, time.Second*10, func() (bool, error) { + By("waiting for a new token reference") + sa, err := f.Client.ServiceAccounts(f.Namespace.Name).Get("default") + if err != nil { + Logf("error getting default service account: %v", err) + return false, err + } + switch len(sa.Secrets) { + case 0: + Logf("default service account has no secret references") + return false, nil + case 1: + if sa.Secrets[0] == secrets[0] { + Logf("default service account still has the deleted secret reference") + return false, nil + } + Logf("default service account has a new single secret reference") + secrets = sa.Secrets + return true, nil + default: + return false, fmt.Errorf("default service account has too many secret references: %#v", sa.Secrets) + } + })) + + // make sure the reference doesn't flutter + { + By("ensuring the single token reference persists") + time.Sleep(2 * time.Second) + sa, err := f.Client.ServiceAccounts(f.Namespace.Name).Get("default") + expectNoError(err) + Expect(sa.Secrets).To(Equal(secrets)) + } + + // delete the reference from the service account + By("deleting the reference to the service account token") + { + sa, err := f.Client.ServiceAccounts(f.Namespace.Name).Get("default") + expectNoError(err) + sa.Secrets = nil + _, updateErr := f.Client.ServiceAccounts(f.Namespace.Name).Update(sa) + expectNoError(updateErr) + } + + // wait for another one to be autocreated + expectNoError(wait.Poll(time.Millisecond*500, time.Second*10, func() (bool, error) { + By("waiting for a new token to be created and added") + sa, err := f.Client.ServiceAccounts(f.Namespace.Name).Get("default") + if err != nil { + Logf("error getting default service account: %v", err) + return false, err + } + switch len(sa.Secrets) { + case 0: + Logf("default service account has no secret references") + return false, nil + case 1: + Logf("default service account has a new single secret reference") + secrets = sa.Secrets + return true, nil + default: + return false, fmt.Errorf("default service account has too many secret references: %#v", sa.Secrets) + } + })) + + // make sure the reference doesn't flutter + { + By("ensuring the single token reference persists") + time.Sleep(2 * time.Second) + sa, err := f.Client.ServiceAccounts(f.Namespace.Name).Get("default") + expectNoError(err) + Expect(sa.Secrets).To(Equal(secrets)) + } + }) + It("should mount an API token into pods [Conformance]", func() { var tokenContent string var rootCAContent string