Skip to content

Commit 1f62556

Browse files
author
OpenShift Bot
authored
Merge pull request #11394 from mfojtik/dockercfg-controller-2
Merged by openshift-bot
2 parents f0ed2ab + 6744424 commit 1f62556

File tree

2 files changed

+166
-60
lines changed

2 files changed

+166
-60
lines changed

pkg/cmd/server/origin/run_components.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ func (c *MasterConfig) RunBuildPodController() {
304304
func (c *MasterConfig) RunBuildImageChangeTriggerController() {
305305
bcClient, _ := c.BuildImageChangeTriggerControllerClients()
306306
bcInstantiator := buildclient.NewOSClientBuildConfigInstantiatorClient(bcClient)
307-
bcIndex := &oscache.StoreToBuildConfigListerImpl{c.Informers.BuildConfigs().Indexer()}
307+
bcIndex := &oscache.StoreToBuildConfigListerImpl{Indexer: c.Informers.BuildConfigs().Indexer()}
308308
bcIndexSynced := c.Informers.BuildConfigs().Informer().HasSynced
309309
factory := buildcontrollerfactory.ImageChangeControllerFactory{Client: bcClient, BuildConfigInstantiator: bcInstantiator, BuildConfigIndex: bcIndex, BuildConfigIndexSynced: bcIndexSynced}
310310
go func() {

pkg/serviceaccounts/controllers/create_dockercfg_secrets.go

Lines changed: 165 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ import (
1515
"k8s.io/kubernetes/pkg/controller"
1616
"k8s.io/kubernetes/pkg/controller/framework"
1717
"k8s.io/kubernetes/pkg/credentialprovider"
18+
"k8s.io/kubernetes/pkg/fields"
1819
"k8s.io/kubernetes/pkg/registry/secret"
1920
"k8s.io/kubernetes/pkg/runtime"
21+
"k8s.io/kubernetes/pkg/types"
2022
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
2123
"k8s.io/kubernetes/pkg/util/sets"
2224
"k8s.io/kubernetes/pkg/util/wait"
@@ -33,6 +35,14 @@ const (
3335
// ServiceAccountTokenValueAnnotation stores the actual value of the token so that a dockercfg secret can be
3436
// made without having a value dockerURL
3537
ServiceAccountTokenValueAnnotation = "openshift.io/token-secret.value"
38+
39+
// CreateDockercfgSecretsController is the name of this controller that should be
40+
// attached to all token secrets this controller create
41+
CreateDockercfgSecretsController = "openshift.io/create-dockercfg-secrets"
42+
43+
// PendingTokenAnnotation contains the name of the token secret that is waiting for the
44+
// token data population
45+
PendingTokenAnnotation = "openshift.io/create-dockercfg-secrets.pending-token"
3646
)
3747

3848
// DockercfgControllerOptions contains options for the DockercfgController
@@ -81,8 +91,29 @@ func NewDockercfgController(cl client.Interface, options DockercfgControllerOpti
8191
},
8292
},
8393
)
84-
8594
e.serviceAccountCache = NewEtcdMutationCache(serviceAccountCache)
95+
96+
tokenSecretSelector := fields.OneTermEqualSelector(api.SecretTypeField, string(api.SecretTypeServiceAccountToken))
97+
e.secretCache, e.secretController = framework.NewInformer(
98+
&cache.ListWatch{
99+
ListFunc: func(options api.ListOptions) (runtime.Object, error) {
100+
options.FieldSelector = tokenSecretSelector
101+
return e.client.Secrets(api.NamespaceAll).List(options)
102+
},
103+
WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
104+
options.FieldSelector = tokenSecretSelector
105+
return e.client.Secrets(api.NamespaceAll).Watch(options)
106+
},
107+
},
108+
&api.Secret{},
109+
options.Resync,
110+
framework.ResourceEventHandlerFuncs{
111+
AddFunc: func(cur interface{}) { e.handleTokenSecretUpdate(nil, cur) },
112+
UpdateFunc: func(old, cur interface{}) { e.handleTokenSecretUpdate(old, cur) },
113+
DeleteFunc: e.handleTokenSecretDelete,
114+
},
115+
)
116+
86117
e.syncHandler = e.syncServiceAccount
87118

88119
return e
@@ -98,13 +129,80 @@ type DockercfgController struct {
98129

99130
serviceAccountCache MutationCache
100131
serviceAccountController *framework.Controller
132+
secretCache cache.Store
133+
secretController *framework.Controller
101134

102135
queue workqueue.RateLimitingInterface
103136

104137
// syncHandler does the work. It's factored out for unit testing
105138
syncHandler func(serviceKey string) error
106139
}
107140

141+
// handleTokenSecretUpdate checks if the service account token secret is populated with
142+
// token data and triggers re-sync of service account when the data are observed.
143+
func (e *DockercfgController) handleTokenSecretUpdate(oldObj, newObj interface{}) {
144+
secret := newObj.(*api.Secret)
145+
if secret.Annotations[api.CreatedByAnnotation] != CreateDockercfgSecretsController {
146+
return
147+
}
148+
isPopulated := len(secret.Data[api.ServiceAccountTokenKey]) > 0
149+
150+
wasPopulated := false
151+
if oldObj != nil {
152+
oldSecret := oldObj.(*api.Secret)
153+
wasPopulated = len(oldSecret.Data[api.ServiceAccountTokenKey]) > 0
154+
glog.V(5).Infof("Updating token secret %s/%s", secret.Namespace, secret.Name)
155+
} else {
156+
glog.V(5).Infof("Adding token secret %s/%s", secret.Namespace, secret.Name)
157+
}
158+
159+
if !wasPopulated && isPopulated {
160+
e.enqueueServiceAccountForToken(secret)
161+
}
162+
}
163+
164+
// handleTokenSecretDelete handles token secrets deletion and re-sync the service account
165+
// which will cause a token to be re-created.
166+
func (e *DockercfgController) handleTokenSecretDelete(obj interface{}) {
167+
secret, isSecret := obj.(*api.Secret)
168+
if !isSecret {
169+
tombstone, objIsTombstone := obj.(cache.DeletedFinalStateUnknown)
170+
if !objIsTombstone {
171+
glog.V(2).Infof("Expected tombstone object when deleting token, got %v", obj)
172+
return
173+
}
174+
secret, isSecret = tombstone.Obj.(*api.Secret)
175+
if !isSecret {
176+
glog.V(2).Infof("Expected tombstone object to contain secret, got: %v", obj)
177+
return
178+
}
179+
}
180+
if secret.Annotations[api.CreatedByAnnotation] != CreateDockercfgSecretsController {
181+
return
182+
}
183+
if len(secret.Data[api.ServiceAccountTokenKey]) > 0 {
184+
// Let deleted_token_secrets handle deletion of populated tokens
185+
return
186+
}
187+
e.enqueueServiceAccountForToken(secret)
188+
}
189+
190+
func (e *DockercfgController) enqueueServiceAccountForToken(tokenSecret *api.Secret) {
191+
serviceAccount := &api.ServiceAccount{
192+
ObjectMeta: api.ObjectMeta{
193+
Name: tokenSecret.Annotations[api.ServiceAccountNameKey],
194+
Namespace: tokenSecret.Namespace,
195+
UID: types.UID(tokenSecret.Annotations[api.ServiceAccountUIDKey]),
196+
},
197+
}
198+
key, err := controller.KeyFunc(serviceAccount)
199+
if err != nil {
200+
utilruntime.HandleError(fmt.Errorf("error syncing token secret %s/%s: %v", tokenSecret.Namespace, tokenSecret.Name, err))
201+
return
202+
}
203+
e.queue.Add(key)
204+
}
205+
108206
func (e *DockercfgController) Run(workers int, stopCh <-chan struct{}) {
109207
defer utilruntime.HandleCrash()
110208

@@ -119,6 +217,11 @@ func (e *DockercfgController) Run(workers int, stopCh <-chan struct{}) {
119217
glog.Infof("Dockercfg secret controller initialized, starting.")
120218

121219
go e.serviceAccountController.Run(stopCh)
220+
go e.secretController.Run(stopCh)
221+
for !e.serviceAccountController.HasSynced() || !e.secretController.HasSynced() {
222+
time.Sleep(100 * time.Millisecond)
223+
}
224+
122225
for i := 0; i < workers; i++ {
123226
go wait.Until(e.worker, time.Second, stopCh)
124227
}
@@ -200,7 +303,6 @@ func (e *DockercfgController) SetDockerURLs(newDockerURLs ...string) {
200303
}
201304

202305
func needsDockercfgSecret(serviceAccount *api.ServiceAccount) bool {
203-
204306
mountableDockercfgSecrets, imageDockercfgPullSecrets := getGeneratedDockercfgSecretNames(serviceAccount)
205307

206308
// look for an ImagePullSecret in the form
@@ -244,6 +346,8 @@ func (e *DockercfgController) syncServiceAccount(key string) error {
244346
case foundMountableSecret:
245347
serviceAccount.ImagePullSecrets = append(serviceAccount.ImagePullSecrets, api.LocalObjectReference{Name: mountableDockercfgSecrets.List()[0]})
246348
}
349+
// Clear the pending token annotation when updating
350+
delete(serviceAccount.Annotations, PendingTokenAnnotation)
247351

248352
updatedSA, err := e.client.ServiceAccounts(serviceAccount.Namespace).Update(serviceAccount)
249353
if err == nil {
@@ -252,10 +356,14 @@ func (e *DockercfgController) syncServiceAccount(key string) error {
252356
return err
253357
}
254358

255-
dockercfgSecret, err := e.createDockerPullSecret(serviceAccount)
359+
dockercfgSecret, created, err := e.createDockerPullSecret(serviceAccount)
256360
if err != nil {
257361
return err
258362
}
363+
if !created {
364+
glog.V(5).Infof("The dockercfg secret was not created for service account %s/%s, will retry", serviceAccount.Namespace, serviceAccount.Name)
365+
return nil
366+
}
259367

260368
first := true
261369
err = client.RetryOnConflict(client.DefaultBackoff, func() error {
@@ -281,6 +389,8 @@ func (e *DockercfgController) syncServiceAccount(key string) error {
281389

282390
serviceAccount.Secrets = append(serviceAccount.Secrets, api.ObjectReference{Name: dockercfgSecret.Name})
283391
serviceAccount.ImagePullSecrets = append(serviceAccount.ImagePullSecrets, api.LocalObjectReference{Name: dockercfgSecret.Name})
392+
// Clear the pending token annotation when updating
393+
delete(serviceAccount.Annotations, PendingTokenAnnotation)
284394

285395
updatedSA, err := e.client.ServiceAccounts(serviceAccount.Namespace).Update(serviceAccount)
286396
if err == nil {
@@ -298,62 +408,74 @@ func (e *DockercfgController) syncServiceAccount(key string) error {
298408
return err
299409
}
300410

301-
const (
302-
tokenSecretWaitInterval = 20 * time.Millisecond
303-
tokenSecretWaitTimes = 100
304-
)
305-
306411
// createTokenSecret creates a token secret for a given service account. Returns the name of the token
307-
func (e *DockercfgController) createTokenSecret(serviceAccount *api.ServiceAccount) (*api.Secret, error) {
412+
func (e *DockercfgController) createTokenSecret(serviceAccount *api.ServiceAccount) (*api.Secret, bool, error) {
413+
pendingTokenName := serviceAccount.Annotations[PendingTokenAnnotation]
414+
415+
// If this service account has no record of a pending token name, record one
416+
if len(pendingTokenName) == 0 {
417+
pendingTokenName = secret.Strategy.GenerateName(osautil.GetTokenSecretNamePrefix(serviceAccount))
418+
if serviceAccount.Annotations == nil {
419+
serviceAccount.Annotations = map[string]string{}
420+
}
421+
serviceAccount.Annotations[PendingTokenAnnotation] = pendingTokenName
422+
updatedServiceAccount, err := e.client.ServiceAccounts(serviceAccount.Namespace).Update(serviceAccount)
423+
// Conflicts mean we'll get called to sync this service account again
424+
if kapierrors.IsConflict(err) {
425+
return nil, false, nil
426+
}
427+
if err != nil {
428+
return nil, false, err
429+
}
430+
serviceAccount = updatedServiceAccount
431+
}
432+
433+
// Return the token from cache
434+
existingTokenSecretObj, exists, err := e.secretCache.GetByKey(serviceAccount.Namespace + "/" + pendingTokenName)
435+
if err != nil {
436+
return nil, false, err
437+
}
438+
if exists {
439+
existingTokenSecret := existingTokenSecretObj.(*api.Secret)
440+
return existingTokenSecret, len(existingTokenSecret.Data[api.ServiceAccountTokenKey]) > 0, nil
441+
}
442+
443+
// Try to create the named pending token
308444
tokenSecret := &api.Secret{
309445
ObjectMeta: api.ObjectMeta{
310-
Name: secret.Strategy.GenerateName(osautil.GetTokenSecretNamePrefix(serviceAccount)),
446+
Name: pendingTokenName,
311447
Namespace: serviceAccount.Namespace,
312448
Annotations: map[string]string{
313449
api.ServiceAccountNameKey: serviceAccount.Name,
314450
api.ServiceAccountUIDKey: string(serviceAccount.UID),
451+
api.CreatedByAnnotation: CreateDockercfgSecretsController,
315452
},
316453
},
317454
Type: api.SecretTypeServiceAccountToken,
318455
Data: map[string][]byte{},
319456
}
320457

321-
_, err := e.client.Secrets(tokenSecret.Namespace).Create(tokenSecret)
322-
if err != nil {
323-
return nil, err
324-
}
325-
326-
// now we have to wait for the service account token controller to make this valid
327-
// TODO remove this once we have a create-token endpoint
328-
for i := 0; i <= tokenSecretWaitTimes; i++ {
329-
liveTokenSecret, err2 := e.client.Secrets(tokenSecret.Namespace).Get(tokenSecret.Name)
330-
if err2 != nil {
331-
return nil, err2
332-
}
333-
334-
if len(liveTokenSecret.Data[api.ServiceAccountTokenKey]) > 0 {
335-
return liveTokenSecret, nil
336-
}
337-
338-
time.Sleep(wait.Jitter(tokenSecretWaitInterval, 0.0))
339-
458+
glog.V(4).Infof("Creating token secret %q for service account %s/%s", tokenSecret.Name, serviceAccount.Namespace, serviceAccount.Name)
459+
token, err := e.client.Secrets(tokenSecret.Namespace).Create(tokenSecret)
460+
// Already exists but not in cache means we'll get an add watch event and resync
461+
if kapierrors.IsAlreadyExists(err) {
462+
return nil, false, nil
340463
}
341-
342-
// the token wasn't ever created, attempt deletion
343-
glog.Warningf("Deleting unfilled token secret %s/%s", tokenSecret.Namespace, tokenSecret.Name)
344-
if deleteErr := e.client.Secrets(tokenSecret.Namespace).Delete(tokenSecret.Name); (deleteErr != nil) && !kapierrors.IsNotFound(deleteErr) {
345-
utilruntime.HandleError(deleteErr)
464+
if err != nil {
465+
return nil, false, err
346466
}
347-
return nil, fmt.Errorf("token never generated for %s", tokenSecret.Name)
467+
return token, len(token.Data[api.ServiceAccountTokenKey]) > 0, nil
348468
}
349469

350470
// createDockerPullSecret creates a dockercfg secret based on the token secret
351-
func (e *DockercfgController) createDockerPullSecret(serviceAccount *api.ServiceAccount) (*api.Secret, error) {
352-
glog.V(4).Infof("Creating secret for %s/%s", serviceAccount.Namespace, serviceAccount.Name)
353-
354-
tokenSecret, err := e.createTokenSecret(serviceAccount)
471+
func (e *DockercfgController) createDockerPullSecret(serviceAccount *api.ServiceAccount) (*api.Secret, bool, error) {
472+
tokenSecret, isPopulated, err := e.createTokenSecret(serviceAccount)
355473
if err != nil {
356-
return nil, err
474+
return nil, false, err
475+
}
476+
if !isPopulated {
477+
glog.V(5).Infof("Token secret for service account %s/%s is not populated yet", serviceAccount.Namespace, serviceAccount.Name)
478+
return nil, false, nil
357479
}
358480

359481
dockercfgSecret := &api.Secret{
@@ -370,6 +492,7 @@ func (e *DockercfgController) createDockerPullSecret(serviceAccount *api.Service
370492
Type: api.SecretTypeDockercfg,
371493
Data: map[string][]byte{},
372494
}
495+
glog.V(4).Infof("Creating dockercfg secret %q for service account %s/%s", dockercfgSecret.Name, serviceAccount.Namespace, serviceAccount.Name)
373496

374497
// prevent updating the DockerURL until we've created the secret
375498
e.dockerURLLock.Lock()
@@ -385,30 +508,13 @@ func (e *DockercfgController) createDockerPullSecret(serviceAccount *api.Service
385508
}
386509
dockercfgContent, err := json.Marshal(&dockercfg)
387510
if err != nil {
388-
return nil, err
511+
return nil, false, err
389512
}
390513
dockercfgSecret.Data[api.DockerConfigKey] = dockercfgContent
391514

392515
// Save the secret
393516
createdSecret, err := e.client.Secrets(tokenSecret.Namespace).Create(dockercfgSecret)
394-
if err != nil {
395-
// Clean up the generated token secret if we're not going to use it
396-
glog.V(2).Infof("deleting unused token secret %s/%s, error creating dockercfgSecret: %v", tokenSecret.Namespace, tokenSecret.Name, err)
397-
if deleteErr := e.client.Secrets(tokenSecret.Namespace).Delete(tokenSecret.Name); (deleteErr != nil) && !kapierrors.IsNotFound(deleteErr) {
398-
utilruntime.HandleError(deleteErr)
399-
}
400-
return nil, err
401-
}
402-
403-
return createdSecret, err
404-
}
405-
406-
func getSecretReferences(serviceAccount *api.ServiceAccount) sets.String {
407-
references := sets.NewString()
408-
for _, secret := range serviceAccount.Secrets {
409-
references.Insert(secret.Name)
410-
}
411-
return references
517+
return createdSecret, err == nil, err
412518
}
413519

414520
func getGeneratedDockercfgSecretNames(serviceAccount *api.ServiceAccount) (sets.String, sets.String) {

0 commit comments

Comments
 (0)