{"action":"created","issue":{"url":"https://api.github.com/repos/docker/docker/issues/28173","repository_url":"https://api.github.com/repos/docker/docker","labels_url":"https://api.github.com/repos/docker/docker/issues/28173/labels{/name}","comments_url":"https://api.github.com/repos/docker/docker/issues/28173/comments","events_url":"https://api.github.com/repos/docker/docker/issues/28173/events","html_url":"https://github.com/docker/docker/pull/28173","id":188064044,"number":28173,"title":"[WIP] Pin images by digest","user":{"login":"nishanttotla","id":1872537,"avatar_url":"https://avatars.githubusercontent.com/u/1872537?v=3","gravatar_id":"","url":"https://api.github.com/users/nishanttotla","html_url":"https://github.com/nishanttotla","followers_url":"https://api.github.com/users/nishanttotla/followers","following_url":"https://api.github.com/users/nishanttotla/following{/other_user}","gists_url":"https://api.github.com/users/nishanttotla/gists{/gist_id}","starred_url":"https://api.github.com/users/nishanttotla/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nishanttotla/subscriptions","organizations_url":"https://api.github.com/users/nishanttotla/orgs","repos_url":"https://api.github.com/users/nishanttotla/repos","events_url":"https://api.github.com/users/nishanttotla/events{/privacy}","received_events_url":"https://api.github.com/users/nishanttotla/received_events","type":"User","site_admin":false},"labels":[{"id":392678049,"url":"https://api.github.com/repos/docker/docker/labels/area/swarm","name":"area/swarm","color":"fbca04","default":false},{"id":175375318,"url":"https://api.github.com/repos/docker/docker/labels/status/1-design-review","name":"status/1-design-review","color":"fef2c0","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/docker/docker/milestones/56","html_url":"https://github.com/docker/docker/milestones/1.13.0","labels_url":"https://api.github.com/repos/docker/docker/milestones/56/labels","id":1839404,"number":56,"title":"1.13.0","description":"","creator":{"login":"thaJeztah","id":1804568,"avatar_url":"https://avatars.githubusercontent.com/u/1804568?v=3","gravatar_id":"","url":"https://api.github.com/users/thaJeztah","html_url":"https://github.com/thaJeztah","followers_url":"https://api.github.com/users/thaJeztah/followers","following_url":"https://api.github.com/users/thaJeztah/following{/other_user}","gists_url":"https://api.github.com/users/thaJeztah/gists{/gist_id}","starred_url":"https://api.github.com/users/thaJeztah/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thaJeztah/subscriptions","organizations_url":"https://api.github.com/users/thaJeztah/orgs","repos_url":"https://api.github.com/users/thaJeztah/repos","events_url":"https://api.github.com/users/thaJeztah/events{/privacy}","received_events_url":"https://api.github.com/users/thaJeztah/received_events","type":"User","site_admin":false},"open_issues":104,"closed_issues":692,"state":"open","created_at":"2016-06-20T22:51:25Z","updated_at":"2016-11-10T02:32:20Z","due_on":null,"closed_at":null},"comments":5,"created_at":"2016-11-08T18:11:45Z","updated_at":"2016-11-10T03:11:58Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/docker/docker/pulls/28173","html_url":"https://github.com/docker/docker/pull/28173","diff_url":"https://github.com/docker/docker/pull/28173.diff","patch_url":"https://github.com/docker/docker/pull/28173.patch"},"body":"**- What I did**\r\nWhen a Docker Swarm mode service is created using the image and tag, the agents pull image with the specified tag. This creates an issue for non-static tags like `latest`, which keep updating. This might cause multiple versions of the image running for the same service. The solution to this problem is to pin images by digest.\r\n\r\nFor example, on `docker service create alpine:latest top`, the manifest for the `alpine:latest` image is retrieved, and the digest is added to the image description before passing on the spec to the SwarmKit manager. In this case, `alpine:latest` is changed to `alpine:latest@sha256:1354db23ff5478120c980eca1611a51c9f2b88b61f24283ee8200bf9a54f2e5c` that specifies an image precisely. If at a future time, the image under `alpine:latest` were to have a different digest, this would be detected at the next `service update`, and all images would be pinned to the new digest.\r\n\r\nRelated issues: #24066, #27508, https://github.com/docker/swarmkit/issues/1334\r\n\r\n**- How I did it**\r\nThe change is fairly simple. A new daemon function `ResolveTagToDigest` is used to retrieve a digest for a `reference.NamedTagged` object, by making a request to the registry. This function is used by `docker service create` and `docker service update` to resolve image to digest, if the image used to create the service didn't already have it.\r\n\r\nThe service spec is modified before passing to the SwarmKit manager to always contain a digest string, thereby ensuring that a service image is always precisely specified. On image update, a digest is retrieved again, and if different from the existing one, will result in a service update.\r\n\r\nOne thing that remains to be done is to enable pinning by digest for private images (this is a minor addition which I will shortly finish) by passing `AuthConfig` to `ResolveTagToDigest`.\r\n\r\n**- How to verify it**\r\nTry the following commands:\r\n```\r\ndocker service create --name testsvc alpine:latest top\r\ndocker service ls\r\n```\r\nThis should show the image as `alpine:latest@sha256:1354db23ff5478120c980eca1611a51c9f2b88b61f24283ee8200bf9a54f2e5c`\r\n```\r\ndocker service update --image alpine:edge testsvc\r\ndocker service ls\r\n```\r\nThis will show the image updated to `alpine:edge@sha256:cd9c03c2d382fcf00c31dc1635445163ec185dfffb51242d9e097892b3b0d5b4`\r\n\r\nNOTE: It is by choice that the operation won't fail if the digest retrieval fails for some reason. It will only print a warning and move on. This is to make sure that the use case where a locally built image might be used to create a service still works.\r\n\r\n**- Description for the changelog**\r\n<!--\r\nWrite a short (one line) summary that describes the changes in this\r\npull request for inclusion in the changelog:\r\n-->\r\nPin images by digest for docker service create and update\r\n\r\n**- A picture of a cute animal (not mandatory but encouraged)**\r\n\r\n<img width=\"743\" alt=\"screen shot 2016-11-08 at 10 07 29 am\" src=\"https://cloud.githubusercontent.com/assets/1872537/20111030/9a1fba9e-a59b-11e6-9eb5-4a167e5e476b.png\">\r\n\r\nThis is Mutsu the kitten. For more of Mutsu, you can check [here](https://www.youtube.com/watch?v=PJpcEuSrEn8)"},"comment":{"url":"https://api.github.com/repos/docker/docker/issues/comments/259591258","html_url":"https://github.com/docker/docker/pull/28173#issuecomment-259591258","issue_url":"https://api.github.com/repos/docker/docker/issues/28173","id":259591258,"user":{"login":"aaronlehmann","id":10601430,"avatar_url":"https://avatars.githubusercontent.com/u/10601430?v=3","gravatar_id":"","url":"https://api.github.com/users/aaronlehmann","html_url":"https://github.com/aaronlehmann","followers_url":"https://api.github.com/users/aaronlehmann/followers","following_url":"https://api.github.com/users/aaronlehmann/following{/other_user}","gists_url":"https://api.github.com/users/aaronlehmann/gists{/gist_id}","starred_url":"https://api.github.com/users/aaronlehmann/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aaronlehmann/subscriptions","organizations_url":"https://api.github.com/users/aaronlehmann/orgs","repos_url":"https://api.github.com/users/aaronlehmann/repos","events_url":"https://api.github.com/users/aaronlehmann/events{/privacy}","received_events_url":"https://api.github.com/users/aaronlehmann/received_events","type":"User","site_admin":false},"created_at":"2016-11-10T03:11:58Z","updated_at":"2016-11-10T03:11:58Z","body":"@nishanttotla: It sound be straightforward to convert `encodedAuth` to an `AuthConfig` struct. Here is the code from `daemon/cluster/executor/container/adapter.go`:\r\n\r\n```go\r\n        authConfig := &types.AuthConfig{}\r\n        if encodedAuthConfig != \"\" {\r\n                if err := json.NewDecoder(base64.NewDecoder(base64.URLEncoding, strings.NewReader(encodedAuthConfig))).Decode(authConfig); err != nil {\r\n                        logrus.Warnf(\"invalid authconfig: %v\", err)\r\n                }\r\n        }\r\n```\r\n\r\nPossibly we should factor this into a utility function (though I'm not sure what the best place is). This could be done in a followup."}}
{"actor":{"display_login":"aaronlehmann"}}