123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666 |
- /*
- Copyright 2014 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package serviceaccount
- import (
- "errors"
- "reflect"
- "testing"
- "time"
- "github.com/golang/glog"
- "k8s.io/kubernetes/pkg/api"
- apierrors "k8s.io/kubernetes/pkg/api/errors"
- "k8s.io/kubernetes/pkg/api/unversioned"
- "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
- "k8s.io/kubernetes/pkg/client/testing/core"
- "k8s.io/kubernetes/pkg/runtime"
- utilrand "k8s.io/kubernetes/pkg/util/rand"
- )
- type testGenerator struct {
- GeneratedServiceAccounts []api.ServiceAccount
- GeneratedSecrets []api.Secret
- Token string
- Err error
- }
- func (t *testGenerator) GenerateToken(serviceAccount api.ServiceAccount, secret api.Secret) (string, error) {
- t.GeneratedSecrets = append(t.GeneratedSecrets, secret)
- t.GeneratedServiceAccounts = append(t.GeneratedServiceAccounts, serviceAccount)
- return t.Token, t.Err
- }
- // emptySecretReferences is used by a service account without any secrets
- func emptySecretReferences() []api.ObjectReference {
- return []api.ObjectReference{}
- }
- // missingSecretReferences is used by a service account that references secrets which do no exist
- func missingSecretReferences() []api.ObjectReference {
- return []api.ObjectReference{{Name: "missing-secret-1"}}
- }
- // regularSecretReferences is used by a service account that references secrets which are not ServiceAccountTokens
- func regularSecretReferences() []api.ObjectReference {
- return []api.ObjectReference{{Name: "regular-secret-1"}}
- }
- // tokenSecretReferences is used by a service account that references a ServiceAccountToken secret
- func tokenSecretReferences() []api.ObjectReference {
- return []api.ObjectReference{{Name: "token-secret-1"}}
- }
- // addTokenSecretReference adds a reference to the ServiceAccountToken that will be created
- func addTokenSecretReference(refs []api.ObjectReference) []api.ObjectReference {
- return addNamedTokenSecretReference(refs, "default-token-fplln")
- }
- // addNamedTokenSecretReference adds a reference to the named ServiceAccountToken
- func addNamedTokenSecretReference(refs []api.ObjectReference, name string) []api.ObjectReference {
- return append(refs, api.ObjectReference{Name: name})
- }
- // serviceAccount returns a service account with the given secret refs
- func serviceAccount(secretRefs []api.ObjectReference) *api.ServiceAccount {
- return &api.ServiceAccount{
- ObjectMeta: api.ObjectMeta{
- Name: "default",
- UID: "12345",
- Namespace: "default",
- ResourceVersion: "1",
- },
- Secrets: secretRefs,
- }
- }
- // updatedServiceAccount returns a service account with the resource version modified
- func updatedServiceAccount(secretRefs []api.ObjectReference) *api.ServiceAccount {
- sa := serviceAccount(secretRefs)
- sa.ResourceVersion = "2"
- return sa
- }
- // opaqueSecret returns a persisted non-ServiceAccountToken secret named "regular-secret-1"
- func opaqueSecret() *api.Secret {
- return &api.Secret{
- ObjectMeta: api.ObjectMeta{
- Name: "regular-secret-1",
- Namespace: "default",
- UID: "23456",
- ResourceVersion: "1",
- },
- Type: "Opaque",
- Data: map[string][]byte{
- "mykey": []byte("mydata"),
- },
- }
- }
- // createdTokenSecret returns the ServiceAccountToken secret posted when creating a new token secret.
- // Named "default-token-fplln", since that is the first generated name after rand.Seed(1)
- func createdTokenSecret(overrideName ...string) *api.Secret {
- return namedCreatedTokenSecret("default-token-fplln")
- }
- // namedTokenSecret returns the ServiceAccountToken secret posted when creating a new token secret with the given name.
- func namedCreatedTokenSecret(name string) *api.Secret {
- return &api.Secret{
- ObjectMeta: api.ObjectMeta{
- Name: name,
- Namespace: "default",
- Annotations: map[string]string{
- api.ServiceAccountNameKey: "default",
- api.ServiceAccountUIDKey: "12345",
- },
- },
- Type: api.SecretTypeServiceAccountToken,
- Data: map[string][]byte{
- "token": []byte("ABC"),
- "ca.crt": []byte("CA Data"),
- "namespace": []byte("default"),
- },
- }
- }
- // serviceAccountTokenSecret returns an existing ServiceAccountToken secret named "token-secret-1"
- func serviceAccountTokenSecret() *api.Secret {
- return &api.Secret{
- ObjectMeta: api.ObjectMeta{
- Name: "token-secret-1",
- Namespace: "default",
- UID: "23456",
- ResourceVersion: "1",
- Annotations: map[string]string{
- api.ServiceAccountNameKey: "default",
- api.ServiceAccountUIDKey: "12345",
- },
- },
- Type: api.SecretTypeServiceAccountToken,
- Data: map[string][]byte{
- "token": []byte("ABC"),
- "ca.crt": []byte("CA Data"),
- "namespace": []byte("default"),
- },
- }
- }
- // serviceAccountTokenSecretWithoutTokenData returns an existing ServiceAccountToken secret that lacks token data
- func serviceAccountTokenSecretWithoutTokenData() *api.Secret {
- secret := serviceAccountTokenSecret()
- delete(secret.Data, api.ServiceAccountTokenKey)
- return secret
- }
- // serviceAccountTokenSecretWithoutCAData returns an existing ServiceAccountToken secret that lacks ca data
- func serviceAccountTokenSecretWithoutCAData() *api.Secret {
- secret := serviceAccountTokenSecret()
- delete(secret.Data, api.ServiceAccountRootCAKey)
- return secret
- }
- // serviceAccountTokenSecretWithCAData returns an existing ServiceAccountToken secret with the specified ca data
- func serviceAccountTokenSecretWithCAData(data []byte) *api.Secret {
- secret := serviceAccountTokenSecret()
- secret.Data[api.ServiceAccountRootCAKey] = data
- return secret
- }
- // serviceAccountTokenSecretWithoutNamespaceData returns an existing ServiceAccountToken secret that lacks namespace data
- func serviceAccountTokenSecretWithoutNamespaceData() *api.Secret {
- secret := serviceAccountTokenSecret()
- delete(secret.Data, api.ServiceAccountNamespaceKey)
- return secret
- }
- // serviceAccountTokenSecretWithNamespaceData returns an existing ServiceAccountToken secret with the specified namespace data
- func serviceAccountTokenSecretWithNamespaceData(data []byte) *api.Secret {
- secret := serviceAccountTokenSecret()
- secret.Data[api.ServiceAccountNamespaceKey] = data
- return secret
- }
- type reaction struct {
- verb string
- resource string
- reactor func(t *testing.T) core.ReactionFunc
- }
- func TestTokenCreation(t *testing.T) {
- testcases := map[string]struct {
- ClientObjects []runtime.Object
- IsAsync bool
- MaxRetries int
- Reactors []reaction
- ExistingServiceAccount *api.ServiceAccount
- ExistingSecrets []*api.Secret
- AddedServiceAccount *api.ServiceAccount
- UpdatedServiceAccount *api.ServiceAccount
- DeletedServiceAccount *api.ServiceAccount
- AddedSecret *api.Secret
- UpdatedSecret *api.Secret
- DeletedSecret *api.Secret
- ExpectedActions []core.Action
- }{
- "new serviceaccount with no secrets": {
- ClientObjects: []runtime.Object{serviceAccount(emptySecretReferences())},
- AddedServiceAccount: serviceAccount(emptySecretReferences()),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewCreateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, createdTokenSecret()),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, serviceAccount(addTokenSecretReference(emptySecretReferences()))),
- },
- },
- "new serviceaccount with no secrets encountering create error": {
- ClientObjects: []runtime.Object{serviceAccount(emptySecretReferences())},
- MaxRetries: 10,
- IsAsync: true,
- Reactors: []reaction{{
- verb: "create",
- resource: "secrets",
- reactor: func(t *testing.T) core.ReactionFunc {
- i := 0
- return func(core.Action) (bool, runtime.Object, error) {
- i++
- if i < 3 {
- return true, nil, apierrors.NewForbidden(api.Resource("secrets"), "foo", errors.New("No can do"))
- }
- return false, nil, nil
- }
- },
- }},
- AddedServiceAccount: serviceAccount(emptySecretReferences()),
- ExpectedActions: []core.Action{
- // Attempt 1
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewCreateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, createdTokenSecret()),
- // Attempt 2
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewCreateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, namedCreatedTokenSecret("default-token-gziey")),
- // Attempt 3
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewCreateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, namedCreatedTokenSecret("default-token-oh43e")),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, serviceAccount(addNamedTokenSecretReference(emptySecretReferences(), "default-token-oh43e"))),
- },
- },
- "new serviceaccount with no secrets encountering unending create error": {
- ClientObjects: []runtime.Object{serviceAccount(emptySecretReferences()), createdTokenSecret()},
- MaxRetries: 2,
- IsAsync: true,
- Reactors: []reaction{{
- verb: "create",
- resource: "secrets",
- reactor: func(t *testing.T) core.ReactionFunc {
- return func(core.Action) (bool, runtime.Object, error) {
- return true, nil, apierrors.NewForbidden(api.Resource("secrets"), "foo", errors.New("No can do"))
- }
- },
- }},
- AddedServiceAccount: serviceAccount(emptySecretReferences()),
- ExpectedActions: []core.Action{
- // Attempt
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewCreateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, createdTokenSecret()),
- // Retry 1
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewCreateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, namedCreatedTokenSecret("default-token-gziey")),
- // Retry 2
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewCreateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, namedCreatedTokenSecret("default-token-oh43e")),
- },
- },
- "new serviceaccount with missing secrets": {
- ClientObjects: []runtime.Object{serviceAccount(missingSecretReferences())},
- AddedServiceAccount: serviceAccount(missingSecretReferences()),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewCreateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, createdTokenSecret()),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, serviceAccount(addTokenSecretReference(missingSecretReferences()))),
- },
- },
- "new serviceaccount with non-token secrets": {
- ClientObjects: []runtime.Object{serviceAccount(regularSecretReferences()), opaqueSecret()},
- AddedServiceAccount: serviceAccount(regularSecretReferences()),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewCreateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, createdTokenSecret()),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, serviceAccount(addTokenSecretReference(regularSecretReferences()))),
- },
- },
- "new serviceaccount with token secrets": {
- ClientObjects: []runtime.Object{serviceAccount(tokenSecretReferences()), serviceAccountTokenSecret()},
- ExistingSecrets: []*api.Secret{serviceAccountTokenSecret()},
- AddedServiceAccount: serviceAccount(tokenSecretReferences()),
- ExpectedActions: []core.Action{},
- },
- "new serviceaccount with no secrets with resource conflict": {
- ClientObjects: []runtime.Object{updatedServiceAccount(emptySecretReferences()), createdTokenSecret()},
- AddedServiceAccount: serviceAccount(emptySecretReferences()),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- },
- },
- "updated serviceaccount with no secrets": {
- ClientObjects: []runtime.Object{serviceAccount(emptySecretReferences())},
- UpdatedServiceAccount: serviceAccount(emptySecretReferences()),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewCreateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, createdTokenSecret()),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, serviceAccount(addTokenSecretReference(emptySecretReferences()))),
- },
- },
- "updated serviceaccount with missing secrets": {
- ClientObjects: []runtime.Object{serviceAccount(missingSecretReferences())},
- UpdatedServiceAccount: serviceAccount(missingSecretReferences()),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewCreateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, createdTokenSecret()),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, serviceAccount(addTokenSecretReference(missingSecretReferences()))),
- },
- },
- "updated serviceaccount with non-token secrets": {
- ClientObjects: []runtime.Object{serviceAccount(regularSecretReferences()), opaqueSecret()},
- UpdatedServiceAccount: serviceAccount(regularSecretReferences()),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewCreateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, createdTokenSecret()),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, serviceAccount(addTokenSecretReference(regularSecretReferences()))),
- },
- },
- "updated serviceaccount with token secrets": {
- ExistingSecrets: []*api.Secret{serviceAccountTokenSecret()},
- UpdatedServiceAccount: serviceAccount(tokenSecretReferences()),
- ExpectedActions: []core.Action{},
- },
- "updated serviceaccount with no secrets with resource conflict": {
- ClientObjects: []runtime.Object{updatedServiceAccount(emptySecretReferences())},
- UpdatedServiceAccount: serviceAccount(emptySecretReferences()),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- },
- },
- "deleted serviceaccount with no secrets": {
- DeletedServiceAccount: serviceAccount(emptySecretReferences()),
- ExpectedActions: []core.Action{},
- },
- "deleted serviceaccount with missing secrets": {
- DeletedServiceAccount: serviceAccount(missingSecretReferences()),
- ExpectedActions: []core.Action{},
- },
- "deleted serviceaccount with non-token secrets": {
- ClientObjects: []runtime.Object{opaqueSecret()},
- DeletedServiceAccount: serviceAccount(regularSecretReferences()),
- ExpectedActions: []core.Action{},
- },
- "deleted serviceaccount with token secrets": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecret()},
- ExistingSecrets: []*api.Secret{serviceAccountTokenSecret()},
- DeletedServiceAccount: serviceAccount(tokenSecretReferences()),
- ExpectedActions: []core.Action{
- core.NewDeleteAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, "token-secret-1"),
- },
- },
- "added secret without serviceaccount": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecret()},
- AddedSecret: serviceAccountTokenSecret(),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewDeleteAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, "token-secret-1"),
- },
- },
- "added secret with serviceaccount": {
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- AddedSecret: serviceAccountTokenSecret(),
- ExpectedActions: []core.Action{},
- },
- "added token secret without token data": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecretWithoutTokenData()},
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- AddedSecret: serviceAccountTokenSecretWithoutTokenData(),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, "token-secret-1"),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, serviceAccountTokenSecret()),
- },
- },
- "added token secret without ca data": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecretWithoutCAData()},
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- AddedSecret: serviceAccountTokenSecretWithoutCAData(),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, "token-secret-1"),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, serviceAccountTokenSecret()),
- },
- },
- "added token secret with mismatched ca data": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecretWithCAData([]byte("mismatched"))},
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- AddedSecret: serviceAccountTokenSecretWithCAData([]byte("mismatched")),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, "token-secret-1"),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, serviceAccountTokenSecret()),
- },
- },
- "added token secret without namespace data": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecretWithoutNamespaceData()},
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- AddedSecret: serviceAccountTokenSecretWithoutNamespaceData(),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, "token-secret-1"),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, serviceAccountTokenSecret()),
- },
- },
- "added token secret with custom namespace data": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecretWithNamespaceData([]byte("custom"))},
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- AddedSecret: serviceAccountTokenSecretWithNamespaceData([]byte("custom")),
- ExpectedActions: []core.Action{
- // no update is performed... the custom namespace is preserved
- },
- },
- "updated secret without serviceaccount": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecret()},
- UpdatedSecret: serviceAccountTokenSecret(),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewDeleteAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, "token-secret-1"),
- },
- },
- "updated secret with serviceaccount": {
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- UpdatedSecret: serviceAccountTokenSecret(),
- ExpectedActions: []core.Action{},
- },
- "updated token secret without token data": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecretWithoutTokenData()},
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- UpdatedSecret: serviceAccountTokenSecretWithoutTokenData(),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, "token-secret-1"),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, serviceAccountTokenSecret()),
- },
- },
- "updated token secret without ca data": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecretWithoutCAData()},
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- UpdatedSecret: serviceAccountTokenSecretWithoutCAData(),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, "token-secret-1"),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, serviceAccountTokenSecret()),
- },
- },
- "updated token secret with mismatched ca data": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecretWithCAData([]byte("mismatched"))},
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- UpdatedSecret: serviceAccountTokenSecretWithCAData([]byte("mismatched")),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, "token-secret-1"),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, serviceAccountTokenSecret()),
- },
- },
- "updated token secret without namespace data": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecretWithoutNamespaceData()},
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- UpdatedSecret: serviceAccountTokenSecretWithoutNamespaceData(),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, "token-secret-1"),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "secrets"}, api.NamespaceDefault, serviceAccountTokenSecret()),
- },
- },
- "updated token secret with custom namespace data": {
- ClientObjects: []runtime.Object{serviceAccountTokenSecretWithNamespaceData([]byte("custom"))},
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- UpdatedSecret: serviceAccountTokenSecretWithNamespaceData([]byte("custom")),
- ExpectedActions: []core.Action{
- // no update is performed... the custom namespace is preserved
- },
- },
- "deleted secret without serviceaccount": {
- DeletedSecret: serviceAccountTokenSecret(),
- ExpectedActions: []core.Action{},
- },
- "deleted secret with serviceaccount with reference": {
- ClientObjects: []runtime.Object{serviceAccount(tokenSecretReferences())},
- ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
- DeletedSecret: serviceAccountTokenSecret(),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- core.NewUpdateAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, serviceAccount(emptySecretReferences())),
- },
- },
- "deleted secret with serviceaccount without reference": {
- ExistingServiceAccount: serviceAccount(emptySecretReferences()),
- DeletedSecret: serviceAccountTokenSecret(),
- ExpectedActions: []core.Action{
- core.NewGetAction(unversioned.GroupVersionResource{Resource: "serviceaccounts"}, api.NamespaceDefault, "default"),
- },
- },
- }
- for k, tc := range testcases {
- glog.Infof(k)
- // Re-seed to reset name generation
- utilrand.Seed(1)
- generator := &testGenerator{Token: "ABC"}
- client := fake.NewSimpleClientset(tc.ClientObjects...)
- for _, reactor := range tc.Reactors {
- client.Fake.PrependReactor(reactor.verb, reactor.resource, reactor.reactor(t))
- }
- controller := NewTokensController(client, TokensControllerOptions{TokenGenerator: generator, RootCA: []byte("CA Data"), MaxRetries: tc.MaxRetries})
- if tc.ExistingServiceAccount != nil {
- controller.serviceAccounts.Add(tc.ExistingServiceAccount)
- }
- for _, s := range tc.ExistingSecrets {
- controller.secrets.Add(s)
- }
- if tc.AddedServiceAccount != nil {
- controller.serviceAccounts.Add(tc.AddedServiceAccount)
- controller.queueServiceAccountSync(tc.AddedServiceAccount)
- }
- if tc.UpdatedServiceAccount != nil {
- controller.serviceAccounts.Add(tc.UpdatedServiceAccount)
- controller.queueServiceAccountUpdateSync(nil, tc.UpdatedServiceAccount)
- }
- if tc.DeletedServiceAccount != nil {
- controller.serviceAccounts.Delete(tc.DeletedServiceAccount)
- controller.queueServiceAccountSync(tc.DeletedServiceAccount)
- }
- if tc.AddedSecret != nil {
- controller.secrets.Add(tc.AddedSecret)
- controller.queueSecretSync(tc.AddedSecret)
- }
- if tc.UpdatedSecret != nil {
- controller.secrets.Add(tc.UpdatedSecret)
- controller.queueSecretUpdateSync(nil, tc.UpdatedSecret)
- }
- if tc.DeletedSecret != nil {
- controller.secrets.Delete(tc.DeletedSecret)
- controller.queueSecretSync(tc.DeletedSecret)
- }
- // This is the longest we'll wait for async tests
- timeout := time.Now().Add(30 * time.Second)
- waitedForAdditionalActions := false
- for {
- if controller.syncServiceAccountQueue.Len() > 0 {
- controller.syncServiceAccount()
- }
- if controller.syncSecretQueue.Len() > 0 {
- controller.syncSecret()
- }
- // The queues still have things to work on
- if controller.syncServiceAccountQueue.Len() > 0 || controller.syncSecretQueue.Len() > 0 {
- continue
- }
- // If we expect this test to work asynchronously...
- if tc.IsAsync {
- // if we're still missing expected actions within our test timeout
- if len(client.Actions()) < len(tc.ExpectedActions) && time.Now().Before(timeout) {
- // wait for the expected actions (without hotlooping)
- time.Sleep(time.Millisecond)
- continue
- }
- // if we exactly match our expected actions, wait a bit to make sure no other additional actions show up
- if len(client.Actions()) == len(tc.ExpectedActions) && !waitedForAdditionalActions {
- time.Sleep(time.Second)
- waitedForAdditionalActions = true
- continue
- }
- }
- break
- }
- if controller.syncServiceAccountQueue.Len() > 0 {
- t.Errorf("%s: unexpected items in service account queue: %d", k, controller.syncServiceAccountQueue.Len())
- }
- if controller.syncSecretQueue.Len() > 0 {
- t.Errorf("%s: unexpected items in secret queue: %d", k, controller.syncSecretQueue.Len())
- }
- actions := client.Actions()
- for i, action := range actions {
- if len(tc.ExpectedActions) < i+1 {
- t.Errorf("%s: %d unexpected actions: %+v", k, len(actions)-len(tc.ExpectedActions), actions[i:])
- break
- }
- expectedAction := tc.ExpectedActions[i]
- if !reflect.DeepEqual(expectedAction, action) {
- t.Errorf("%s: Expected\n\t%#v\ngot\n\t%#v", k, expectedAction, action)
- continue
- }
- }
- if len(tc.ExpectedActions) > len(actions) {
- t.Errorf("%s: %d additional expected actions", k, len(tc.ExpectedActions)-len(actions))
- for _, a := range tc.ExpectedActions[len(actions):] {
- t.Logf(" %+v", a)
- }
- }
- }
- }
|