diff --git a/components/engine/api/server/router/swarm/backend.go b/components/engine/api/server/router/swarm/backend.go index 37ec52957d..3b7933d7b1 100644 --- a/components/engine/api/server/router/swarm/backend.go +++ b/components/engine/api/server/router/swarm/backend.go @@ -19,8 +19,8 @@ type Backend interface { GetServices(basictypes.ServiceListOptions) ([]types.Service, error) GetService(idOrName string, insertDefaults bool) (types.Service, error) - CreateService(types.ServiceSpec, string) (*basictypes.ServiceCreateResponse, error) - UpdateService(string, uint64, types.ServiceSpec, basictypes.ServiceUpdateOptions) (*basictypes.ServiceUpdateResponse, error) + CreateService(types.ServiceSpec, string, bool) (*basictypes.ServiceCreateResponse, error) + UpdateService(string, uint64, types.ServiceSpec, basictypes.ServiceUpdateOptions, bool) (*basictypes.ServiceUpdateResponse, error) RemoveService(string) error ServiceLogs(context.Context, *backend.LogSelector, *basictypes.ContainerLogsOptions) (<-chan *backend.LogMessage, error) diff --git a/components/engine/api/server/router/swarm/cluster_routes.go b/components/engine/api/server/router/swarm/cluster_routes.go index 438f2de705..91461da764 100644 --- a/components/engine/api/server/router/swarm/cluster_routes.go +++ b/components/engine/api/server/router/swarm/cluster_routes.go @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/filters" types "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/api/types/versions" "golang.org/x/net/context" ) @@ -178,8 +179,13 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter, // Get returns "" if the header does not exist encodedAuth := r.Header.Get("X-Registry-Auth") + cliVersion := r.Header.Get("version") + queryRegistry := false + if cliVersion != "" && versions.LessThan(cliVersion, "1.30") { + queryRegistry = true + } - resp, err := sr.backend.CreateService(service, encodedAuth) + resp, err := sr.backend.CreateService(service, encodedAuth, queryRegistry) if err != nil { logrus.Errorf("Error creating service %s: %v", service.Name, err) return err @@ -207,8 +213,13 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, flags.EncodedRegistryAuth = r.Header.Get("X-Registry-Auth") flags.RegistryAuthFrom = r.URL.Query().Get("registryAuthFrom") flags.Rollback = r.URL.Query().Get("rollback") + cliVersion := r.Header.Get("version") + queryRegistry := false + if cliVersion != "" && versions.LessThan(cliVersion, "1.30") { + queryRegistry = true + } - resp, err := sr.backend.UpdateService(vars["id"], version, service, flags) + resp, err := sr.backend.UpdateService(vars["id"], version, service, flags, queryRegistry) if err != nil { logrus.Errorf("Error updating service %s: %v", vars["id"], err) return err diff --git a/components/engine/client/service_create.go b/components/engine/client/service_create.go index a88d1fba3e..90082d150f 100644 --- a/components/engine/client/service_create.go +++ b/components/engine/client/service_create.go @@ -13,15 +13,14 @@ import ( // ServiceCreate creates a new Service. func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options types.ServiceCreateOptions) (types.ServiceCreateResponse, error) { - var ( - headers map[string][]string - distErr error - ) + var distErr error + + headers := map[string][]string{ + "version": {cli.version}, + } if options.EncodedRegistryAuth != "" { - headers = map[string][]string{ - "X-Registry-Auth": {options.EncodedRegistryAuth}, - } + headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth} } // Contact the registry to retrieve digest and platform information diff --git a/components/engine/client/service_update.go b/components/engine/client/service_update.go index e3f93ee86e..8c7776aad7 100644 --- a/components/engine/client/service_update.go +++ b/components/engine/client/service_update.go @@ -13,15 +13,16 @@ import ( // ServiceUpdate updates a Service. func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) { var ( - headers map[string][]string query = url.Values{} distErr error ) + headers := map[string][]string{ + "version": {cli.version}, + } + if options.EncodedRegistryAuth != "" { - headers = map[string][]string{ - "X-Registry-Auth": {options.EncodedRegistryAuth}, - } + headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth} } if options.RegistryAuthFrom != "" { diff --git a/components/engine/daemon/cluster/services.go b/components/engine/daemon/cluster/services.go index 0bfc417860..f4416c24c3 100644 --- a/components/engine/daemon/cluster/services.go +++ b/components/engine/daemon/cluster/services.go @@ -117,7 +117,7 @@ func (c *Cluster) GetService(input string, insertDefaults bool) (types.Service, } // CreateService creates a new service in a managed swarm cluster. -func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (*apitypes.ServiceCreateResponse, error) { +func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string, queryRegistry bool) (*apitypes.ServiceCreateResponse, error) { var resp *apitypes.ServiceCreateResponse err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error { err := c.populateNetworkID(ctx, state.controlClient, &s) @@ -151,8 +151,11 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (*apity } } - // pin image by digest - if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" { + // pin image by digest for API versions < 1.30 + // TODO(nishanttotla): The check on "DOCKER_SERVICE_PREFER_OFFLINE_IMAGE" + // should be removed in the future. Since integration tests only use the + // latest API version, so this is no longer required. + if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" && queryRegistry { digestImage, err := c.imageWithDigestString(ctx, ctnr.Image, authConfig) if err != nil { logrus.Warnf("unable to pin image %s to digest: %s", ctnr.Image, err.Error()) @@ -193,7 +196,7 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (*apity } // UpdateService updates existing service to match new properties. -func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec types.ServiceSpec, flags apitypes.ServiceUpdateOptions) (*apitypes.ServiceUpdateResponse, error) { +func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec types.ServiceSpec, flags apitypes.ServiceUpdateOptions, queryRegistry bool) (*apitypes.ServiceUpdateResponse, error) { var resp *apitypes.ServiceUpdateResponse err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error { @@ -256,8 +259,11 @@ func (c *Cluster) UpdateService(serviceIDOrName string, version uint64, spec typ resp = &apitypes.ServiceUpdateResponse{} - // pin image by digest - if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" { + // pin image by digest for API versions < 1.30 + // TODO(nishanttotla): The check on "DOCKER_SERVICE_PREFER_OFFLINE_IMAGE" + // should be removed in the future. Since integration tests only use the + // latest API version, so this is no longer required. + if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" && queryRegistry { digestImage, err := c.imageWithDigestString(ctx, newCtnr.Image, authConfig) if err != nil { logrus.Warnf("unable to pin image %s to digest: %s", newCtnr.Image, err.Error())