From 93ec5cda55e78dbb683b59bde244a7984b323574 Mon Sep 17 00:00:00 2001 From: Nishant Totla Date: Wed, 15 Jun 2016 11:50:49 -0700 Subject: [PATCH 1/4] Passing registry auth token for service create and update Signed-off-by: Nishant Totla --- api/client/registry.go | 29 +++++++++++++++++++++++ api/client/service/create.go | 15 ++++++++++-- api/client/service/scale.go | 16 ++++++++++++- api/client/service/update.go | 25 ++++++++++++++++--- api/server/router/swarm/backend.go | 4 ++-- api/server/router/swarm/cluster_routes.go | 14 +++++++++-- daemon/cluster/cluster.go | 13 ++++++++-- 7 files changed, 104 insertions(+), 12 deletions(-) diff --git a/api/client/registry.go b/api/client/registry.go index 26a8c8f268..1e67dcc4d7 100644 --- a/api/client/registry.go +++ b/api/client/registry.go @@ -13,6 +13,7 @@ import ( "golang.org/x/net/context" "github.com/docker/docker/pkg/term" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/docker/engine-api/types" registrytypes "github.com/docker/engine-api/types/registry" @@ -148,6 +149,34 @@ func (cli *DockerCli) ConfigureAuth(flUser, flPassword, serverAddress string, is return authconfig, nil } +// ResolveAuthConfigFromImage retrieves that AuthConfig using the image string +func (cli *DockerCli) ResolveAuthConfigFromImage(ctx context.Context, image string) (types.AuthConfig, error) { + registryRef, err := reference.ParseNamed(image) + if err != nil { + return types.AuthConfig{}, err + } + repoInfo, err := registry.ParseRepositoryInfo(registryRef) + if err != nil { + return types.AuthConfig{}, err + } + authConfig := cli.ResolveAuthConfig(ctx, repoInfo.Index) + return authConfig, nil +} + +// RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete image +func (cli *DockerCli) RetrieveAuthTokenFromImage(ctx context.Context, image string) (string, error) { + // Retrieve encoded auth token from the image reference + authConfig, err := cli.ResolveAuthConfigFromImage(ctx, image) + if err != nil { + return "", err + } + encodedAuth, err := EncodeAuthToBase64(authConfig) + if err != nil { + return "", err + } + return encodedAuth, nil +} + func readInput(in io.Reader, out io.Writer) string { reader := bufio.NewReader(in) line, _, err := reader.ReadLine() diff --git a/api/client/service/create.go b/api/client/service/create.go index 1f2e2a053f..9333ec411f 100644 --- a/api/client/service/create.go +++ b/api/client/service/create.go @@ -32,14 +32,25 @@ func newCreateCommand(dockerCli *client.DockerCli) *cobra.Command { } func runCreate(dockerCli *client.DockerCli, opts *serviceOptions) error { - client := dockerCli.Client() + apiClient := dockerCli.Client() service, err := opts.ToService() if err != nil { return err } - response, err := client.ServiceCreate(context.Background(), service) + ctx := context.Background() + // Retrieve encoded auth token from the image reference + encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, opts.image) + if err != nil { + return err + } + + headers := map[string][]string{ + "x-registry-auth": {encodedAuth}, + } + + response, err := apiClient.ServiceCreate(ctx, service, headers) if err != nil { return err } diff --git a/api/client/service/scale.go b/api/client/service/scale.go index e313948aca..d272b557cf 100644 --- a/api/client/service/scale.go +++ b/api/client/service/scale.go @@ -60,6 +60,7 @@ func runScale(dockerCli *client.DockerCli, args []string) error { func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string) error { client := dockerCli.Client() ctx := context.Background() + headers := map[string][]string{} service, _, err := client.ServiceInspectWithRaw(ctx, serviceID) @@ -67,6 +68,19 @@ func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string return err } + // TODO(nishanttotla): Is this the best way to get the image? + image := service.Spec.TaskTemplate.ContainerSpec.Image + if image != "" { + // Retrieve encoded auth token from the image reference + encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, image) + if err != nil { + return err + } + headers = map[string][]string{ + "x-registry-auth": {encodedAuth}, + } + } + serviceMode := &service.Spec.Mode if serviceMode.Replicated == nil { return fmt.Errorf("scale can only be used with replicated mode") @@ -77,7 +91,7 @@ func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string } serviceMode.Replicated.Replicas = &uintScale - err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec) + err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, headers) if err != nil { return err } diff --git a/api/client/service/update.go b/api/client/service/update.go index ebbf799fda..be356b2c4d 100644 --- a/api/client/service/update.go +++ b/api/client/service/update.go @@ -37,10 +37,28 @@ func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command { } func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID string) error { - client := dockerCli.Client() + apiClient := dockerCli.Client() ctx := context.Background() + headers := map[string][]string{} - service, _, err := client.ServiceInspectWithRaw(ctx, serviceID) + // TODO(nishanttotla): Is this the best way to get the new image? + image, err := flags.GetString("image") + if err != nil { + return err + } + if image != "" { + // Retrieve encoded auth token from the image reference + // only do this if a new image has been provided as part of the udpate + encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, image) + if err != nil { + return err + } + headers = map[string][]string{ + "x-registry-auth": {encodedAuth}, + } + } + + service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID) if err != nil { return err } @@ -49,7 +67,8 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID stri if err != nil { return err } - err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec) + + err = apiClient.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, headers) if err != nil { return err } diff --git a/api/server/router/swarm/backend.go b/api/server/router/swarm/backend.go index 05fe00a0c2..f9ee0a1bbe 100644 --- a/api/server/router/swarm/backend.go +++ b/api/server/router/swarm/backend.go @@ -14,8 +14,8 @@ type Backend interface { Update(uint64, types.Spec) error GetServices(basictypes.ServiceListOptions) ([]types.Service, error) GetService(string) (types.Service, error) - CreateService(types.ServiceSpec) (string, error) - UpdateService(string, uint64, types.ServiceSpec) error + CreateService(types.ServiceSpec, string) (string, error) + UpdateService(string, uint64, types.ServiceSpec, string) error RemoveService(string) error GetNodes(basictypes.NodeListOptions) ([]types.Node, error) GetNode(string) (types.Node, error) diff --git a/api/server/router/swarm/cluster_routes.go b/api/server/router/swarm/cluster_routes.go index 2f678d706c..7744de2d2b 100644 --- a/api/server/router/swarm/cluster_routes.go +++ b/api/server/router/swarm/cluster_routes.go @@ -107,7 +107,12 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter, return err } - id, err := sr.backend.CreateService(service) + encodedAuth := "" + if auth, ok := r.Header["x-registry-auth"]; ok { + encodedAuth = auth[0] + } + + id, err := sr.backend.CreateService(service, encodedAuth) if err != nil { logrus.Errorf("Error creating service %s: %v", id, err) return err @@ -130,7 +135,12 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, return fmt.Errorf("Invalid service version '%s': %s", rawVersion, err.Error()) } - if err := sr.backend.UpdateService(vars["id"], version, service); err != nil { + encodedAuth := "" + if auth, ok := r.Header["x-registry-auth"]; ok { + encodedAuth = auth[0] + } + + if err := sr.backend.UpdateService(vars["id"], version, service, encodedAuth); err != nil { logrus.Errorf("Error updating service %s: %v", vars["id"], err) return err } diff --git a/daemon/cluster/cluster.go b/daemon/cluster/cluster.go index 23320f06b8..1b260becdb 100644 --- a/daemon/cluster/cluster.go +++ b/daemon/cluster/cluster.go @@ -668,7 +668,7 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv } // CreateService creates a new service in a managed swarm cluster. -func (c *Cluster) CreateService(s types.ServiceSpec) (string, error) { +func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string, error) { c.RLock() defer c.RUnlock() @@ -687,6 +687,11 @@ func (c *Cluster) CreateService(s types.ServiceSpec) (string, error) { if err != nil { return "", err } + + if encodedAuth != "" { + serviceSpec.Task.Runtime.(*swarmapi.TaskSpec_Container).Container.PullOptions = &swarmapi.ContainerSpec_PullOptions{RegistryAuth: encodedAuth} + } + r, err := c.client.CreateService(ctx, &swarmapi.CreateServiceRequest{Spec: &serviceSpec}) if err != nil { return "", err @@ -712,7 +717,7 @@ func (c *Cluster) GetService(input string) (types.Service, error) { } // UpdateService updates existing service to match new properties. -func (c *Cluster) UpdateService(serviceID string, version uint64, spec types.ServiceSpec) error { +func (c *Cluster) UpdateService(serviceID string, version uint64, spec types.ServiceSpec, encodedAuth string) error { c.RLock() defer c.RUnlock() @@ -725,6 +730,10 @@ func (c *Cluster) UpdateService(serviceID string, version uint64, spec types.Ser return err } + if encodedAuth != "" { + serviceSpec.Task.Runtime.(*swarmapi.TaskSpec_Container).Container.PullOptions = &swarmapi.ContainerSpec_PullOptions{RegistryAuth: encodedAuth} + } + _, err = c.client.UpdateService( c.getRequestContext(), &swarmapi.UpdateServiceRequest{ From e13a14340dde563fa799fb95090940c74636e398 Mon Sep 17 00:00:00 2001 From: Nishant Totla Date: Thu, 16 Jun 2016 15:57:08 -0700 Subject: [PATCH 2/4] Update engine-api vendored version Signed-off-by: Nishant Totla --- vendor/src/github.com/docker/engine-api/client/interface.go | 4 ++-- .../src/github.com/docker/engine-api/client/service_create.go | 4 ++-- .../src/github.com/docker/engine-api/client/service_update.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/vendor/src/github.com/docker/engine-api/client/interface.go b/vendor/src/github.com/docker/engine-api/client/interface.go index 929a6bc72a..f99e88897a 100644 --- a/vendor/src/github.com/docker/engine-api/client/interface.go +++ b/vendor/src/github.com/docker/engine-api/client/interface.go @@ -100,11 +100,11 @@ type NodeAPIClient interface { // ServiceAPIClient defines API client methods for the services type ServiceAPIClient interface { - ServiceCreate(ctx context.Context, service swarm.ServiceSpec) (types.ServiceCreateResponse, error) + ServiceCreate(ctx context.Context, service swarm.ServiceSpec, headers map[string][]string) (types.ServiceCreateResponse, error) ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) ServiceRemove(ctx context.Context, serviceID string) error - ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec) error + ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, headers map[string][]string) error TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) } diff --git a/vendor/src/github.com/docker/engine-api/client/service_create.go b/vendor/src/github.com/docker/engine-api/client/service_create.go index f87851e43f..4153e04d57 100644 --- a/vendor/src/github.com/docker/engine-api/client/service_create.go +++ b/vendor/src/github.com/docker/engine-api/client/service_create.go @@ -9,9 +9,9 @@ import ( ) // ServiceCreate creates a new Service. -func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec) (types.ServiceCreateResponse, error) { +func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, headers map[string][]string) (types.ServiceCreateResponse, error) { var response types.ServiceCreateResponse - resp, err := cli.post(ctx, "/services/create", nil, service, nil) + resp, err := cli.post(ctx, "/services/create", nil, service, headers) if err != nil { return response, err } diff --git a/vendor/src/github.com/docker/engine-api/client/service_update.go b/vendor/src/github.com/docker/engine-api/client/service_update.go index a3f22fafac..4281b1e2c1 100644 --- a/vendor/src/github.com/docker/engine-api/client/service_update.go +++ b/vendor/src/github.com/docker/engine-api/client/service_update.go @@ -9,10 +9,10 @@ import ( ) // ServiceUpdate updates a Service. -func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec) error { +func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, headers map[string][]string) error { query := url.Values{} query.Set("version", strconv.FormatUint(version.Index, 10)) - resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, nil) + resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers) ensureReaderClosed(resp) return err } From af5df117a805c8df661c7efa58c57680019b5752 Mon Sep 17 00:00:00 2001 From: Nishant Totla Date: Thu, 16 Jun 2016 19:09:44 -0700 Subject: [PATCH 3/4] Updating header name and executor Signed-off-by: Nishant Totla --- api/client/service/create.go | 2 +- api/client/service/scale.go | 4 +--- api/client/service/update.go | 4 +--- api/client/stack/deploy.go | 5 ++++- api/server/router/swarm/cluster_routes.go | 10 ++-------- daemon/cluster/executor/container/adapter.go | 1 - hack/vendor.sh | 2 +- 7 files changed, 10 insertions(+), 18 deletions(-) diff --git a/api/client/service/create.go b/api/client/service/create.go index 9333ec411f..d169f32c79 100644 --- a/api/client/service/create.go +++ b/api/client/service/create.go @@ -47,7 +47,7 @@ func runCreate(dockerCli *client.DockerCli, opts *serviceOptions) error { } headers := map[string][]string{ - "x-registry-auth": {encodedAuth}, + "X-Registry-Auth": {encodedAuth}, } response, err := apiClient.ServiceCreate(ctx, service, headers) diff --git a/api/client/service/scale.go b/api/client/service/scale.go index d272b557cf..b0583e79a0 100644 --- a/api/client/service/scale.go +++ b/api/client/service/scale.go @@ -76,9 +76,7 @@ func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string if err != nil { return err } - headers = map[string][]string{ - "x-registry-auth": {encodedAuth}, - } + headers["X-Registry-Auth"] = []string{encodedAuth} } serviceMode := &service.Spec.Mode diff --git a/api/client/service/update.go b/api/client/service/update.go index be356b2c4d..d5b3f4fef1 100644 --- a/api/client/service/update.go +++ b/api/client/service/update.go @@ -53,9 +53,7 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID stri if err != nil { return err } - headers = map[string][]string{ - "x-registry-auth": {encodedAuth}, - } + headers["X-Registry-Auth"] = []string{encodedAuth} } service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID) diff --git a/api/client/stack/deploy.go b/api/client/stack/deploy.go index 4bd8eb8eb9..4dcd19dc4d 100644 --- a/api/client/stack/deploy.go +++ b/api/client/stack/deploy.go @@ -184,18 +184,21 @@ func deployServices( if service, exists := existingServiceMap[name]; exists { fmt.Fprintf(out, "Updating service %s (id: %s)\n", name, service.ID) + // TODO(nishanttotla): Pass headers with X-Registry-Auth if err := apiClient.ServiceUpdate( ctx, service.ID, service.Version, serviceSpec, + nil, ); err != nil { return err } } else { fmt.Fprintf(out, "Creating service %s\n", name) - if _, err := apiClient.ServiceCreate(ctx, serviceSpec); err != nil { + // TODO(nishanttotla): Pass headers with X-Registry-Auth + if _, err := apiClient.ServiceCreate(ctx, serviceSpec, nil); err != nil { return err } } diff --git a/api/server/router/swarm/cluster_routes.go b/api/server/router/swarm/cluster_routes.go index 7744de2d2b..a8be21cd87 100644 --- a/api/server/router/swarm/cluster_routes.go +++ b/api/server/router/swarm/cluster_routes.go @@ -107,10 +107,7 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter, return err } - encodedAuth := "" - if auth, ok := r.Header["x-registry-auth"]; ok { - encodedAuth = auth[0] - } + encodedAuth := r.Header.Get("X-Registry-Auth") id, err := sr.backend.CreateService(service, encodedAuth) if err != nil { @@ -135,10 +132,7 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, return fmt.Errorf("Invalid service version '%s': %s", rawVersion, err.Error()) } - encodedAuth := "" - if auth, ok := r.Header["x-registry-auth"]; ok { - encodedAuth = auth[0] - } + encodedAuth := r.Header.Get("X-Registry-Auth") if err := sr.backend.UpdateService(vars["id"], version, service, encodedAuth); err != nil { logrus.Errorf("Error updating service %s: %v", vars["id"], err) diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go index 50718fd3d2..9a959ae4cc 100644 --- a/daemon/cluster/executor/container/adapter.go +++ b/daemon/cluster/executor/container/adapter.go @@ -44,7 +44,6 @@ func (c *containerAdapter) pullImage(ctx context.Context) error { var encodedAuthConfig string if spec.PullOptions != nil { encodedAuthConfig = spec.PullOptions.RegistryAuth - } authConfig := &types.AuthConfig{} diff --git a/hack/vendor.sh b/hack/vendor.sh index a089858a89..79023252bb 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -60,7 +60,7 @@ clone git golang.org/x/net 2beffdc2e92c8a3027590f898fe88f69af48a3f8 https://gith clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3 clone git github.com/docker/go-connections fa2850ff103453a9ad190da0df0af134f0314b3d -clone git github.com/docker/engine-api c57d0447ea1ae71f6dad83c8d8a1215a89869a0c +clone git github.com/docker/engine-api 19b4fb48a86c3318e610e156ec06b684f79ac31d clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837 clone git github.com/imdario/mergo 0.2.1 From 538bac39d7fe93562922b89e9a294096be48fb59 Mon Sep 17 00:00:00 2001 From: Nishant Totla Date: Wed, 29 Jun 2016 17:08:00 -0700 Subject: [PATCH 4/4] Adding a flag to specify sending of registry auth Signed-off-by: Nishant Totla --- api/client/registry.go | 6 ++--- api/client/service/create.go | 16 +++++++----- api/client/service/opts.go | 7 ++++- api/client/service/scale.go | 14 +--------- api/client/service/update.go | 31 ++++++++++++----------- api/server/router/swarm/cluster_routes.go | 2 ++ daemon/cluster/cluster.go | 24 ++++++++++++++++-- 7 files changed, 59 insertions(+), 41 deletions(-) diff --git a/api/client/registry.go b/api/client/registry.go index 1e67dcc4d7..ed78b7e60b 100644 --- a/api/client/registry.go +++ b/api/client/registry.go @@ -149,8 +149,8 @@ func (cli *DockerCli) ConfigureAuth(flUser, flPassword, serverAddress string, is return authconfig, nil } -// ResolveAuthConfigFromImage retrieves that AuthConfig using the image string -func (cli *DockerCli) ResolveAuthConfigFromImage(ctx context.Context, image string) (types.AuthConfig, error) { +// resolveAuthConfigFromImage retrieves that AuthConfig using the image string +func (cli *DockerCli) resolveAuthConfigFromImage(ctx context.Context, image string) (types.AuthConfig, error) { registryRef, err := reference.ParseNamed(image) if err != nil { return types.AuthConfig{}, err @@ -166,7 +166,7 @@ func (cli *DockerCli) ResolveAuthConfigFromImage(ctx context.Context, image stri // RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete image func (cli *DockerCli) RetrieveAuthTokenFromImage(ctx context.Context, image string) (string, error) { // Retrieve encoded auth token from the image reference - authConfig, err := cli.ResolveAuthConfigFromImage(ctx, image) + authConfig, err := cli.resolveAuthConfigFromImage(ctx, image) if err != nil { return "", err } diff --git a/api/client/service/create.go b/api/client/service/create.go index d169f32c79..10fcb20f9a 100644 --- a/api/client/service/create.go +++ b/api/client/service/create.go @@ -33,6 +33,7 @@ func newCreateCommand(dockerCli *client.DockerCli) *cobra.Command { func runCreate(dockerCli *client.DockerCli, opts *serviceOptions) error { apiClient := dockerCli.Client() + headers := map[string][]string{} service, err := opts.ToService() if err != nil { @@ -40,14 +41,15 @@ func runCreate(dockerCli *client.DockerCli, opts *serviceOptions) error { } ctx := context.Background() - // Retrieve encoded auth token from the image reference - encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, opts.image) - if err != nil { - return err - } - headers := map[string][]string{ - "X-Registry-Auth": {encodedAuth}, + // only send auth if flag was set + if opts.registryAuth { + // Retrieve encoded auth token from the image reference + encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, opts.image) + if err != nil { + return err + } + headers["X-Registry-Auth"] = []string{encodedAuth} } response, err := apiClient.ServiceCreate(ctx, service, headers) diff --git a/api/client/service/opts.go b/api/client/service/opts.go index 640aaa0205..10cde24314 100644 --- a/api/client/service/opts.go +++ b/api/client/service/opts.go @@ -373,6 +373,8 @@ type serviceOptions struct { update updateOptions networks []string endpoint endpointOptions + + registryAuth bool } func newServiceOptions() *serviceOptions { @@ -436,7 +438,7 @@ func (opts *serviceOptions) ToService() (swarm.ServiceSpec, error) { return service, nil } -// addServiceFlags adds all flags that are common to both `create` and `update. +// addServiceFlags adds all flags that are common to both `create` and `update`. // Any flags that are not common are added separately in the individual command func addServiceFlags(cmd *cobra.Command, opts *serviceOptions) { flags := cmd.Flags() @@ -469,6 +471,8 @@ func addServiceFlags(cmd *cobra.Command, opts *serviceOptions) { flags.StringSliceVar(&opts.networks, flagNetwork, []string{}, "Network attachments") flags.StringVar(&opts.endpoint.mode, flagEndpointMode, "", "Endpoint mode(Valid values: vip, dnsrr)") flags.VarP(&opts.endpoint.ports, flagPublish, "p", "Publish a port as a node port") + + flags.BoolVar(&opts.registryAuth, flagRegistryAuth, false, "Send registry authentication details to Swarm agents") } const ( @@ -493,4 +497,5 @@ const ( flagUpdateDelay = "update-delay" flagUpdateParallelism = "update-parallelism" flagUser = "user" + flagRegistryAuth = "registry-auth" ) diff --git a/api/client/service/scale.go b/api/client/service/scale.go index b0583e79a0..2643fdcf27 100644 --- a/api/client/service/scale.go +++ b/api/client/service/scale.go @@ -60,7 +60,6 @@ func runScale(dockerCli *client.DockerCli, args []string) error { func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string) error { client := dockerCli.Client() ctx := context.Background() - headers := map[string][]string{} service, _, err := client.ServiceInspectWithRaw(ctx, serviceID) @@ -68,17 +67,6 @@ func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string return err } - // TODO(nishanttotla): Is this the best way to get the image? - image := service.Spec.TaskTemplate.ContainerSpec.Image - if image != "" { - // Retrieve encoded auth token from the image reference - encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, image) - if err != nil { - return err - } - headers["X-Registry-Auth"] = []string{encodedAuth} - } - serviceMode := &service.Spec.Mode if serviceMode.Replicated == nil { return fmt.Errorf("scale can only be used with replicated mode") @@ -89,7 +77,7 @@ func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string } serviceMode.Replicated.Replicas = &uintScale - err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, headers) + err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, nil) if err != nil { return err } diff --git a/api/client/service/update.go b/api/client/service/update.go index d5b3f4fef1..5442fe2ae7 100644 --- a/api/client/service/update.go +++ b/api/client/service/update.go @@ -41,21 +41,6 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID stri ctx := context.Background() headers := map[string][]string{} - // TODO(nishanttotla): Is this the best way to get the new image? - image, err := flags.GetString("image") - if err != nil { - return err - } - if image != "" { - // Retrieve encoded auth token from the image reference - // only do this if a new image has been provided as part of the udpate - encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, image) - if err != nil { - return err - } - headers["X-Registry-Auth"] = []string{encodedAuth} - } - service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID) if err != nil { return err @@ -66,6 +51,22 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID stri return err } + // only send auth if flag was set + sendAuth, err := flags.GetBool(flagRegistryAuth) + if err != nil { + return err + } + if sendAuth { + // Retrieve encoded auth token from the image reference + // This would be the old image if it didn't change in this update + image := service.Spec.TaskTemplate.ContainerSpec.Image + encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, image) + if err != nil { + return err + } + headers["X-Registry-Auth"] = []string{encodedAuth} + } + err = apiClient.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, headers) if err != nil { return err diff --git a/api/server/router/swarm/cluster_routes.go b/api/server/router/swarm/cluster_routes.go index a8be21cd87..d91b4a972e 100644 --- a/api/server/router/swarm/cluster_routes.go +++ b/api/server/router/swarm/cluster_routes.go @@ -107,6 +107,7 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter, return err } + // Get returns "" if the header does not exist encodedAuth := r.Header.Get("X-Registry-Auth") id, err := sr.backend.CreateService(service, encodedAuth) @@ -132,6 +133,7 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, return fmt.Errorf("Invalid service version '%s': %s", rawVersion, err.Error()) } + // Get returns "" if the header does not exist encodedAuth := r.Header.Get("X-Registry-Auth") if err := sr.backend.UpdateService(vars["id"], version, service, encodedAuth); err != nil { diff --git a/daemon/cluster/cluster.go b/daemon/cluster/cluster.go index 1b260becdb..3f5670192f 100644 --- a/daemon/cluster/cluster.go +++ b/daemon/cluster/cluster.go @@ -689,7 +689,11 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string } if encodedAuth != "" { - serviceSpec.Task.Runtime.(*swarmapi.TaskSpec_Container).Container.PullOptions = &swarmapi.ContainerSpec_PullOptions{RegistryAuth: encodedAuth} + ctnr := serviceSpec.Task.GetContainer() + if ctnr == nil { + return "", fmt.Errorf("service does not use container tasks") + } + ctnr.PullOptions = &swarmapi.ContainerSpec_PullOptions{RegistryAuth: encodedAuth} } r, err := c.client.CreateService(ctx, &swarmapi.CreateServiceRequest{Spec: &serviceSpec}) @@ -731,7 +735,23 @@ func (c *Cluster) UpdateService(serviceID string, version uint64, spec types.Ser } if encodedAuth != "" { - serviceSpec.Task.Runtime.(*swarmapi.TaskSpec_Container).Container.PullOptions = &swarmapi.ContainerSpec_PullOptions{RegistryAuth: encodedAuth} + ctnr := serviceSpec.Task.GetContainer() + if ctnr == nil { + return fmt.Errorf("service does not use container tasks") + } + ctnr.PullOptions = &swarmapi.ContainerSpec_PullOptions{RegistryAuth: encodedAuth} + } else { + // this is needed because if the encodedAuth isn't being updated then we + // shouldn't lose it, and continue to use the one that was already present + currentService, err := getService(c.getRequestContext(), c.client, serviceID) + if err != nil { + return err + } + ctnr := currentService.Spec.Task.GetContainer() + if ctnr == nil { + return fmt.Errorf("service does not use container tasks") + } + serviceSpec.Task.GetContainer().PullOptions = ctnr.PullOptions } _, err = c.client.UpdateService(