I create a simple service using the API and setting some information (such as labels, name, env variables...)
When I try to update the service via the api, by sending the following form data, ommited json key (such as labels, environment variables, and even the service name) are resetted.
{
"TaskTemplate": {
"ContainerSpec": {
"Image": "my.serv/myimage:mytag"
}
}
}
It seems to be a design choice, since using the docker cli utility, and a docker service update myservice --image my.serv/myimage:mytag sends the full json, containing ulterior labels, name and environment variables.
Why has this choice been made? I can't find any other ulterior related issue.
~/docker
โฏ docker version
Client:
Version: 1.12.1-rc1
API version: 1.24
Go version: go1.6.3
Git commit: 7889dc7
Built: Fri Aug 12 18:35:53 2016
OS/Arch: darwin/amd64
Experimental: true
Server:
Version: 1.12.1-rc1
API version: 1.24
Go version: go1.6.3
Git commit: 7889dc7
Built: Fri Aug 12 18:35:53 2016
OS/Arch: linux/amd64
Experimental: true
I'm not sure why the JSON API was designed this way, but I would guess it was to avoid ambiguity between removing attributes from the service and leaving them unchanged. For example, if you omitted EndpointSpec, would that mean that you want to remove all the port bindings, or that you didn't want to change them?
The JSON API is implemented outside this repository, so you may be able to get a better answer by filing an issue against https://github.com/docker/docker.
ping @stevvooe
For services, Spec are changed as a unit. The pattern, in pseudo-code, looks like this:
service := getCurrentService()
spec := service.Spec
spec.Image = "my.serv/myimage:mytag"
update(spec, service.Version)
We do this to avoid ambiguity in how the target object should look. For a simple field, like image, this isn't interesting, but when we look at more complex fields, like mounts, patching an object requires a semantic to express whether you are adding, removing or updating entries. Such systems generally also require cross language "Tri-state" field semantics, meaning that you have to be able to discern empty, null and remove. Usually such systems require a separate patch language, with instructions on which fields to modify and how to make insertions, resulting in a complex and error prone API. By having the client express _exactly_ what they want the object to have within it, this problem is completely avoided, for the cost of sending up the whole specification, which is relatively small anyways.
The other component of this approach is the Version field on the specification. By requiring the client to send this, along with the modified specification, we ensure that the change will _only_ apply when made to that version. We see similar provisions in other Consistent-PartitionTolerant systems such as zookeeper and etcd. This ensures that changes from two separate actors don't unintentionally overwrite each other, providing safe concurrency and determinism when operating on the API.
These two design aspects complement each other well and allow API users to get the safety guarantees provided by raft in a relatively simple JSON API.
@MagicMicky Does this answer your question well enough? If so, can you close the issue? If not, can you let us know what we need to clarify?
@MagicMicky Let us know and we'll re-open if not.
Yes it absolutely does answer my question. Thank you very much!
Most helpful comment
For services,
Specare changed as a unit. The pattern, in pseudo-code, looks like this:We do this to avoid ambiguity in how the target object should look. For a simple field, like image, this isn't interesting, but when we look at more complex fields, like mounts, patching an object requires a semantic to express whether you are adding, removing or updating entries. Such systems generally also require cross language "Tri-state" field semantics, meaning that you have to be able to discern empty, null and remove. Usually such systems require a separate patch language, with instructions on which fields to modify and how to make insertions, resulting in a complex and error prone API. By having the client express _exactly_ what they want the object to have within it, this problem is completely avoided, for the cost of sending up the whole specification, which is relatively small anyways.
The other component of this approach is the
Versionfield on the specification. By requiring the client to send this, along with the modified specification, we ensure that the change will _only_ apply when made to that version. We see similar provisions in other Consistent-PartitionTolerant systems such as zookeeper and etcd. This ensures that changes from two separate actors don't unintentionally overwrite each other, providing safe concurrency and determinism when operating on the API.These two design aspects complement each other well and allow API users to get the safety guarantees provided by raft in a relatively simple JSON API.