Skip to content

Commit

Permalink
add internal and external URL handling for the docker pull secret
Browse files Browse the repository at this point in the history
  • Loading branch information
deads2k committed May 25, 2018
1 parent 6a90f58 commit dad692c
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ func RunServiceAccountPullSecretsController(ctx ControllerContext) (bool, error)
go dockercfgController.Run(5, ctx.Stop)

dockerRegistryControllerOptions := serviceaccountcontrollers.DockerRegistryServiceControllerOptions{
DockercfgController: dockercfgController,
DockerURLsInitialized: dockerURLsInitialized,
DockercfgController: dockercfgController,
DockerURLsInitialized: dockerURLsInitialized,
AdditionalRegistryURLs: ctx.OpenshiftControllerConfig.DockerPullSecret.RegistryURLs,
}
go serviceaccountcontrollers.NewDockerRegistryServiceController(
ctx.ExternalKubeInformers.Core().V1().Secrets(),
Expand Down
11 changes: 11 additions & 0 deletions pkg/cmd/openshift-controller-manager/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ func ConvertMasterConfigToOpenshiftControllerConfig(input *configapi.MasterConfi
// deep copy to make sure no linger references are shared
in := input.DeepCopy()

registryURLs := []string{}
if len(in.ImagePolicyConfig.ExternalRegistryHostname) > 0 {
registryURLs = append(registryURLs, in.ImagePolicyConfig.ExternalRegistryHostname)
}
if len(in.ImagePolicyConfig.InternalRegistryHostname) > 0 {
registryURLs = append(registryURLs, in.ImagePolicyConfig.InternalRegistryHostname)
}

ret := &configapi.OpenshiftControllerConfig{
ClientConnectionOverrides: in.MasterClients.OpenShiftLoopbackClientConnectionOverrides,
ServingInfo: &in.ServingInfo,
Expand Down Expand Up @@ -56,6 +64,9 @@ func ConvertMasterConfigToOpenshiftControllerConfig(input *configapi.MasterConfi
ServiceAccount: configapi.ServiceAccountControllerConfig{
ManagedNames: in.ServiceAccountConfig.ManagedNames,
},
DockerPullSecret: configapi.DockerPullSecretControllerConfig{
RegistryURLs: registryURLs,
},
Network: configapi.NetworkControllerConfig{
ClusterNetworks: in.NetworkConfig.ClusterNetworks,
NetworkPluginName: in.NetworkConfig.NetworkPluginName,
Expand Down
6 changes: 6 additions & 0 deletions pkg/cmd/server/apis/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1523,6 +1523,7 @@ type OpenshiftControllerConfig struct {
Deployer DeployerControllerConfig
Build BuildControllerConfig
ServiceAccount ServiceAccountControllerConfig
DockerPullSecret DockerPullSecretControllerConfig
Network NetworkControllerConfig
Ingress IngressControllerConfig
ImageImport ImageImportControllerConfig
Expand Down Expand Up @@ -1574,6 +1575,11 @@ type ServiceAccountControllerConfig struct {
ManagedNames []string
}

type DockerPullSecretControllerConfig struct {
// RegistryURLs is a list of urls that the docker pull secrets should be valid for.
RegistryURLs []string
}

type ImageImportControllerConfig struct {
// MaxScheduledImageImportsPerMinute is the maximum number of image streams that will be imported in the background per minute.
// The default value is 60. Set to -1 for unlimited.
Expand Down
7 changes: 5 additions & 2 deletions pkg/serviceaccounts/controllers/create_dockercfg_secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@ func (e *DockercfgController) Run(workers int, stopCh <-chan struct{}) {
defer utilruntime.HandleCrash()
defer e.queue.ShutDown()

glog.Infof("Starting DockercfgController controller")
defer glog.Infof("Shutting down DockercfgController controller")

// Wait for the store to sync before starting any work in this controller.
ready := make(chan struct{})
go e.waitForDockerURLs(ready, stopCh)
Expand All @@ -208,18 +211,18 @@ func (e *DockercfgController) Run(workers int, stopCh <-chan struct{}) {
case <-stopCh:
return
}
glog.V(1).Infof("urls found")

// Wait for the stores to fill
if !cache.WaitForCacheSync(stopCh, e.serviceAccountController.HasSynced, e.secretController.HasSynced) {
return
}
glog.V(1).Infof("caches synced")

glog.V(5).Infof("Starting workers")
for i := 0; i < workers; i++ {
go wait.Until(e.worker, time.Second, stopCh)
}
<-stopCh
glog.V(1).Infof("Shutting down")
}

func (c *DockercfgController) waitForDockerURLs(ready chan<- struct{}, stopCh <-chan struct{}) {
Expand Down
5 changes: 3 additions & 2 deletions pkg/serviceaccounts/controllers/deleted_dockercfg_secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,16 @@ type DockercfgDeletedController struct {
// Run processes the queue.
func (e *DockercfgDeletedController) Run(stopCh <-chan struct{}) {
defer utilruntime.HandleCrash()
glog.Infof("Starting DockercfgDeletedController controller")
defer glog.Infof("Shutting down DockercfgDeletedController controller")

// Wait for the stores to fill
if !cache.WaitForCacheSync(stopCh, e.secretController.HasSynced) {
return
}
glog.V(1).Infof("caches synced")

glog.V(5).Infof("Worker started")
<-stopCh
glog.V(1).Infof("Shutting down")
}

// secretDeleted reacts to a Secret being deleted by looking to see if it's a dockercfg secret for a service account, in which case it
Expand Down
5 changes: 3 additions & 2 deletions pkg/serviceaccounts/controllers/deleted_token_secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,16 @@ type DockercfgTokenDeletedController struct {
// Runs controller loops and returns on shutdown
func (e *DockercfgTokenDeletedController) Run(stopCh <-chan struct{}) {
defer utilruntime.HandleCrash()
glog.Infof("Starting DockercfgTokenDeletedController controller")
defer glog.Infof("Shutting down DockercfgTokenDeletedController controller")

// Wait for the stores to fill
if !cache.WaitForCacheSync(stopCh, e.secretController.HasSynced) {
return
}
glog.V(1).Infof("caches synced")

glog.V(5).Infof("Worker started")
<-stopCh
glog.V(1).Infof("Shutting down")
}

// secretDeleted reacts to a token secret being deleted by looking for a corresponding dockercfg secret and deleting it if it exists
Expand Down
29 changes: 20 additions & 9 deletions pkg/serviceaccounts/controllers/docker_registry_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ type DockerRegistryServiceControllerOptions struct {

DockercfgController *DockercfgController

// AdditionalRegistryURLs is a list of URLs that are always included
AdditionalRegistryURLs []string

// DockerURLsInitialized is used to send a signal to the DockercfgController that it has the correct set of docker urls
DockerURLsInitialized chan struct{}
}
Expand All @@ -48,11 +51,12 @@ var serviceLocations = []serviceLocation{
// NewDockerRegistryServiceController returns a new *DockerRegistryServiceController.
func NewDockerRegistryServiceController(secrets informers.SecretInformer, serviceInformer informers.ServiceInformer, cl kclientset.Interface, options DockerRegistryServiceControllerOptions) *DockerRegistryServiceController {
e := &DockerRegistryServiceController{
client: cl,
dockercfgController: options.DockercfgController,
registryLocationQueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
secretsToUpdate: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
dockerURLsInitialized: options.DockerURLsInitialized,
client: cl,
additionalRegistryURLs: options.AdditionalRegistryURLs,
dockercfgController: options.DockercfgController,
registryLocationQueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
secretsToUpdate: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
dockerURLsInitialized: options.DockerURLsInitialized,
}

// we're only watching two of these, but we already watch all services for the service serving cert signer
Expand Down Expand Up @@ -98,6 +102,9 @@ func NewDockerRegistryServiceController(secrets informers.SecretInformer, servic
type DockerRegistryServiceController struct {
client kclientset.Interface

// AdditionalRegistryURLs is a list of URLs that are always included
additionalRegistryURLs []string

dockercfgController *DockercfgController

serviceLister listers.ServiceLister
Expand Down Expand Up @@ -127,6 +134,9 @@ func (e *DockerRegistryServiceController) Run(workers int, stopCh <-chan struct{
defer utilruntime.HandleCrash()
defer e.registryLocationQueue.ShutDown()

glog.Infof("Starting DockerRegistryServiceController controller")
defer glog.Infof("Shutting down DockerRegistryServiceController controller")

// Wait for the store to sync before starting any work in this controller.
ready := make(chan struct{})
go e.waitForDockerURLs(ready, stopCh)
Expand All @@ -135,14 +145,13 @@ func (e *DockerRegistryServiceController) Run(workers int, stopCh <-chan struct{
case <-stopCh:
return
}
glog.V(1).Infof("caches synced")

glog.V(5).Infof("Starting workers")
go wait.Until(e.watchForDockerURLChanges, time.Second, stopCh)
for i := 0; i < workers; i++ {
go wait.Until(e.watchForDockercfgSecretUpdates, time.Second, stopCh)
}
<-stopCh
glog.V(1).Infof("Shutting down")
}

// enqueue adds to our queue. We only have one entry, but we never have to check it since we already know the things
Expand Down Expand Up @@ -218,10 +227,11 @@ func (e *DockerRegistryServiceController) watchForDockerURLChanges() {

// getDockerRegistryLocations returns the dns form and the ip form of the secret
func (e *DockerRegistryServiceController) getDockerRegistryLocations() []string {
ret := []string{}
ret := append([]string{}, e.additionalRegistryURLs...)
for _, location := range serviceLocations {
ret = append(ret, getDockerRegistryLocations(e.serviceLister, location)...)
}
glog.V(4).Infof("found docker registry urls: %v", ret)
return ret
}

Expand All @@ -248,9 +258,10 @@ func (e *DockerRegistryServiceController) syncRegistryLocationChange() error {
newDockerRegistryLocations := sets.NewString(newLocations...)
existingURLs := e.getRegistryURLs()
if existingURLs.Equal(newDockerRegistryLocations) && e.initialSecretsCheckDone {
glog.V(4).Infof("No effective update: %v", newDockerRegistryLocations)
glog.V(3).Infof("No effective update: %v", newDockerRegistryLocations)
return nil
}
glog.V(1).Infof("Updating registry URLs from %v to %v", existingURLs, newDockerRegistryLocations)

// make sure that new dockercfg secrets get the correct locations
e.dockercfgController.SetDockerURLs(newDockerRegistryLocations.List()...)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func TestNoChangeNoOp(t *testing.T) {
}
}

func TestUpdateNewStyleSecret(t *testing.T) {
func TestUpdateNewStyleSecretAndAdditionalURLs(t *testing.T) {
stopChannel := make(chan struct{})
defer close(stopChannel)
received := make(chan bool)
Expand All @@ -142,6 +142,8 @@ func TestUpdateNewStyleSecret(t *testing.T) {
}

kubeclient, fakeWatch, controller, informerFactory := controllerSetup([]runtime.Object{newStyleDockercfgSecret}, t, stopChannel)
// this bit also tests the additional registryURL options
controller.additionalRegistryURLs = []string{"foo.bar.com"}
controller.syncRegistryLocationHandler = wrapHandler(received, controller.syncRegistryLocationChange, t)
controller.syncSecretHandler = wrapStringHandler(updatedSecret, controller.syncSecretUpdate, t)
controller.initialSecretsCheckDone = false
Expand Down Expand Up @@ -179,7 +181,7 @@ func TestUpdateNewStyleSecret(t *testing.T) {
}

expectedDockercfgMap := credentialprovider.DockerConfig{}
for _, key := range []string{"172.16.123.123:1235", "docker-registry.default.svc:1235"} {
for _, key := range []string{"foo.bar.com", "172.16.123.123:1235", "docker-registry.default.svc:1235"} {
expectedDockercfgMap[key] = credentialprovider.DockerConfigEntry{
Username: "serviceaccount",
Password: newStyleDockercfgSecret.Annotations[ServiceAccountTokenValueAnnotation],
Expand Down
22 changes: 17 additions & 5 deletions test/integration/service_account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,15 @@ func TestAutomaticCreationOfPullSecrets(t *testing.T) {
saNamespace := api.NamespaceDefault
saName := serviceaccountadmission.DefaultServiceAccountName

masterConfig, clusterAdminConfig, err := testserver.StartTestMaster()
masterConfig, err := testserver.DefaultMasterOptions()
if err != nil {
t.Fatalf("unexpected error: %v", err)
t.Fatalf("error creating config: %v", err)
}
masterConfig.ImagePolicyConfig.InternalRegistryHostname = "internal.registry.com:8080"
masterConfig.ImagePolicyConfig.ExternalRegistryHostname = "external.registry.com"
clusterAdminConfig, err := testserver.StartConfiguredMaster(masterConfig)
if err != nil {
t.Fatalf("error starting server: %v", err)
}
defer testserver.CleanupMasterEtcd(t, masterConfig)
clusterAdminKubeClient, err := testutil.GetClusterAdminKubeClient(clusterAdminConfig)
Expand All @@ -192,17 +198,23 @@ func TestAutomaticCreationOfPullSecrets(t *testing.T) {
if len(saPullSecret) == 0 {
t.Errorf("pull secret was not created")
}
if !strings.Contains(saPullSecret, masterConfig.ImagePolicyConfig.InternalRegistryHostname) {
t.Errorf("missing %q in %v", masterConfig.ImagePolicyConfig.InternalRegistryHostname, saPullSecret)
}
if !strings.Contains(saPullSecret, masterConfig.ImagePolicyConfig.ExternalRegistryHostname) {
t.Errorf("missing %q in %v", masterConfig.ImagePolicyConfig.ExternalRegistryHostname, saPullSecret)
}
}

func waitForServiceAccountPullSecret(client kclientset.Interface, ns, name string, attempts int, interval time.Duration) (string, string, error) {
for i := 0; i <= attempts; i++ {
time.Sleep(interval)
secretName, token, err := getServiceAccountPullSecret(client, ns, name)
secretName, dockerCfg, err := getServiceAccountPullSecret(client, ns, name)
if err != nil {
return "", "", err
}
if len(token) > 0 {
return secretName, token, nil
if len(dockerCfg) > 2 {
return secretName, dockerCfg, nil
}
}
return "", "", nil
Expand Down

0 comments on commit dad692c

Please sign in to comment.