1package client // import "github.com/docker/docker/client"
2
3import (
4	"context"
5	"encoding/json"
6	"net/url"
7	"strconv"
8
9	"github.com/docker/docker/api/types"
10	"github.com/docker/docker/api/types/swarm"
11)
12
13// ServiceUpdate updates a Service. The version number is required to avoid conflicting writes.
14// It should be the value as set *before* the update. You can find this value in the Meta field
15// of swarm.Service, which can be found using ServiceInspectWithRaw.
16func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
17	var (
18		query    = url.Values{}
19		response = types.ServiceUpdateResponse{}
20	)
21
22	headers := map[string][]string{
23		"version": {cli.version},
24	}
25
26	if options.EncodedRegistryAuth != "" {
27		headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth}
28	}
29
30	if options.RegistryAuthFrom != "" {
31		query.Set("registryAuthFrom", options.RegistryAuthFrom)
32	}
33
34	if options.Rollback != "" {
35		query.Set("rollback", options.Rollback)
36	}
37
38	query.Set("version", strconv.FormatUint(version.Index, 10))
39
40	if err := validateServiceSpec(service); err != nil {
41		return response, err
42	}
43
44	// ensure that the image is tagged
45	var resolveWarning string
46	switch {
47	case service.TaskTemplate.ContainerSpec != nil:
48		if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
49			service.TaskTemplate.ContainerSpec.Image = taggedImg
50		}
51		if options.QueryRegistry {
52			resolveWarning = resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
53		}
54	case service.TaskTemplate.PluginSpec != nil:
55		if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
56			service.TaskTemplate.PluginSpec.Remote = taggedImg
57		}
58		if options.QueryRegistry {
59			resolveWarning = resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
60		}
61	}
62
63	resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers)
64	defer ensureReaderClosed(resp)
65	if err != nil {
66		return response, err
67	}
68
69	err = json.NewDecoder(resp.body).Decode(&response)
70	if resolveWarning != "" {
71		response.Warnings = append(response.Warnings, resolveWarning)
72	}
73
74	return response, err
75}
76