Page MenuHomePhabricator

Some HEAD requests to docker-registry yields 405 Method not allowed
Closed, ResolvedPublic

Description

docker-pkg checks whether images are published by issuing a HEAD request to docker-registry.wikimedia.org. However I often get a HTTP Error: 405 Method not allowed.

The relevant python code (with some debug statements):

docker_pkg/builder.py
class ImageFSM(object):

    def _is_published(self):
        """Check the registry for the image"""
        proxies = {
            'https': self.config.get('http_proxy', None)
        }
        if self.config.get('registry', False):
            url = 'https://{registry}/v2'.format(registry=self.config['registry'])
        else:
            # TODO: support dockerhub somehow!
            # Probably will need a different strategy there.
            return False
        if self.config.get('namespace', False):
            url += '/{}'.format(self.config['namespace'])
        url += '/{}'.format(self.image.short_name)
        manifest_url = '{url}/manifests/{tag}'.format(
            url=url,
            tag=self.image.tag,
        )
        resp = requests.head(manifest_url, proxies=proxies)
        isPublished = (resp.status_code == requests.codes.ok)
        log.error("%s: %s.\nCode: %s\nHeaders: %s\nContent: %s" % (isPublished, manifest_url, resp.status_code, resp.headers, resp.content))
        if not isPublished:
            raise Exception("total failure")
        return isPublished

And I have:

docker-pkg.yaml
registry: docker-registry.wikimedia.org
namespace: releng

When running docker-pkg against the 96 images in integration/config.git I soon get the error:

False: https://docker-registry.wikimedia.org/v2/releng/composer-php73/manifests/0.1.4.
Code: 405
Headers: {
 'Age': '0',
 'Allow': 'DELETE, GET, PUT',
 'Cache-Control': 'no-cache, must-revalidate',
 'Connection': 'keep-alive',
 'Content-Length': '19',
 'Content-Type': 'text/plain; charset=utf-8',
 'Date': 'Tue, 22 Jan 2019 21:44:33 GMT',
 'Docker-Distribution-Api-Version': 'registry/2.0, registry/2.0',
 'Server': 'nginx/1.13.6',
 'Server-Timing': 'cache;desc="pass"',
 'Set-Cookie': 'WMF-Last-Access=22-Jan-2019;Path=/;HttpOnly;secure;Expires=Sat 23 Feb 2019 12:00:00 GMT',
 'Strict-Transport-Security': 'max-age=106384710; includeSubDomains; preload',
 'Via': '1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)',
 'X-Analytics': 'https=1;nocookies=1',
 'X-Cache': 'cp1087 pass, cp3030 pass, cp3032 pass',
 'X-Cache-Status': 'pass',
 'X-Client-IP': 'XXXX',
 'X-Content-Type-Options': 'nosniff',
 'X-Varnish': '873514290, 828282958, 927435803',
}
Content: b'' (builder.py:75)

For reference a good one is:

True: https://docker-registry.wikimedia.org/v2/releng/npm-test-3d2png/manifests/0.2.1.
Code: 200
Headers: {
 'Accept-Ranges': 'bytes',
 'Age': '0',
 'Cache-Control': 'no-cache, must-revalidate',
 'Connection': 'keep-alive',
 'Content-Encoding': 'gzip',
 'Content-Type': 'application/json; charset=utf-8',
 'Date': 'Tue, 22 Jan 2019 21:44:33 GMT',
 'Docker-Content-Digest': 'sha256:7c583098906ed63242705e9c9054260a4a61ee85f9e0d53fb02d1dc99cd3b754',
 'Docker-Distribution-Api-Version': 'registry/2.0, registry/2.0',
 'ETag': 'W/"sha256:7c583098906ed63242705e9c9054260a4a61ee85f9e0d53fb02d1dc99cd3b754"',
 'Server': 'nginx/1.13.6',
 'Server-Timing': 'cache;desc="pass"',
 'Set-Cookie': 'WMF-Last-Access=22-Jan-2019;Path=/;HttpOnly;secure;Expires=Sat, 23 Feb 2019 12:00:00 GMT',
 'Strict-Transport-Security': 'max-age=106384710; includeSubDomains; preload',
 'Vary': 'Accept-Encoding',
 'Via': '1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)',
 'X-Analytics': 'https=1;nocookies=1',
 'X-Cache': 'cp1075 pass, cp3040 pass, cp3032 pass',
 'X-Cache-Status': 'pass',
 'X-Client-IP': 'XXXX',
 'X-Varnish': '544805201, 289695908, 920990020',
}
Content: b'' (builder.py:75)

And an unified diff:

--- GOOD	2019-01-22 22:57:41.423556629 +0100
+++ BAD	2019-01-22 22:49:44.032360393 +0100
@@ -1,26 +1,24 @@
-True: https://docker-registry.wikimedia.org/v2/releng/npm-test-3d2png/manifests/0.2.1.
-Code: 200
+False: https://docker-registry.wikimedia.org/v2/releng/composer-php73/manifests/0.1.4.
+Code: 405
 Headers: {
- 'Accept-Ranges': 'bytes',
  'Age': '0',
+ 'Allow': 'DELETE, GET, PUT',
  'Cache-Control': 'no-cache, must-revalidate',
  'Connection': 'keep-alive',
- 'Content-Encoding': 'gzip',
- 'Content-Type': 'application/json; charset=utf-8',
+ 'Content-Length': '19',
+ 'Content-Type': 'text/plain; charset=utf-8',
  'Date': 'Tue, 22 Jan 2019 21:44:33 GMT',
- 'Docker-Content-Digest': 'sha256:7c583098906ed63242705e9c9054260a4a61ee85f9e0d53fb02d1dc99cd3b754',
  'Docker-Distribution-Api-Version': 'registry/2.0, registry/2.0',
- 'ETag': 'W/"sha256:7c583098906ed63242705e9c9054260a4a61ee85f9e0d53fb02d1dc99cd3b754"',
  'Server': 'nginx/1.13.6',
  'Server-Timing': 'cache;desc="pass"',
- 'Set-Cookie': 'WMF-Last-Access=22-Jan-2019;Path=/;HttpOnly;secure;Expires=Sat, 23 Feb 2019 12:00:00 GMT',
+ 'Set-Cookie': 'WMF-Last-Access=22-Jan-2019;Path=/;HttpOnly;secure;Expires=Sat 23 Feb 2019 12:00:00 GMT',
  'Strict-Transport-Security': 'max-age=106384710; includeSubDomains; preload',
- 'Vary': 'Accept-Encoding',
  'Via': '1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)',
  'X-Analytics': 'https=1;nocookies=1',
- 'X-Cache': 'cp1075 pass, cp3040 pass, cp3032 pass',
+ 'X-Cache': 'cp1087 pass, cp3030 pass, cp3032 pass',
  'X-Cache-Status': 'pass',
  'X-Client-IP': 'XXXX',
- 'X-Varnish': '544805201, 289695908, 920990020',
+ 'X-Content-Type-Options': 'nosniff',
+ 'X-Varnish': '873514290, 828282958, 927435803',
 }
 Content: b'' (builder.py:75)

The bad response has Allow: DELETE, GET, PUT, while docker-pkg does a HEAD and there is X-Content-Type-Options: nosniff.

Event Timeline

I have subscribed giuseppe/alexandros/fabian seems you know about the Docker registry/nginx.

Or just now using:

curl --HEAD https://docker-registry.wikimedia.org/v2/releng/composer-php73/manifests/0.1.4
GOOD
HTTP/2 200 
date: Tue, 22 Jan 2019 22:17:57 GMT
content-type: application/json; charset=utf-8
server: nginx/1.13.6
docker-content-digest: sha256:1119a4e98b89e79cfbf11e482fbba231c94cb87268f31b6555a542876e0f8fce
docker-distribution-api-version: registry/2.0
docker-distribution-api-version: registry/2.0
cache-control: no-cache,must-revalidate
etag: W/"sha256:1119a4e98b89e79cfbf11e482fbba231c94cb87268f31b6555a542876e0f8fce"
content-encoding: gzip
vary: Accept-Encoding
x-varnish: 349618986, 443317734, 970675056
via: 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)
accept-ranges: bytes
age: 0
x-cache: cp1089 pass, cp3032 pass, cp3032 pass
x-cache-status: pass
server-timing: cache;desc="pass"
strict-transport-security: max-age=106384710; includeSubDomains; preload
set-cookie: WMF-Last-Access=22-Jan-2019;Path=/;HttpOnly;secure;Expires=Sat, 23 Feb 2019 12:00:00 GMT
x-analytics: https=1;nocookies=1
HTTP/2 405 
date: Tue, 22 Jan 2019 22:18:14 GMT
content-type: text/plain; charset=utf-8
content-length: 19
server: nginx/1.13.6
allow: DELETE, GET, PUT
docker-distribution-api-version: registry/2.0
x-content-type-options: nosniff
docker-distribution-api-version: registry/2.0
cache-control: no-cache,must-revalidate
x-varnish: 371176615, 618864460, 961989236
via: 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)
age: 0
x-cache: cp1089 pass, cp3033 pass, cp3032 pass
x-cache-status: pass
server-timing: cache;desc="pass"
strict-transport-security: max-age=106384710; includeSubDomains; preload
set-cookie: WMF-Last-Access=22-Jan-2019;Path=/;HttpOnly;secure;Expires=Sat, 23 Feb 2019 12:00:00 GMT
x-analytics: https=1;nocookies=1

The diff:

--- GOOD	2019-01-22 23:20:52.169854080 +0100
+++ BAD	2019-01-22 23:20:20.725783890 +0100
@@ -1,19 +1,17 @@
-HTTP/2 200 
-date: Tue, 22 Jan 2019 22:17:57 GMT
-content-type: application/json; charset=utf-8
+HTTP/2 405 
+date: Tue, 22 Jan 2019 22:18:14 GMT
+content-type: text/plain; charset=utf-8
+content-length: 19
 server: nginx/1.13.6
-docker-content-digest: sha256:1119a4e98b89e79cfbf11e482fbba231c94cb87268f31b6555a542876e0f8fce
+allow: DELETE, GET, PUT
 docker-distribution-api-version: registry/2.0
+x-content-type-options: nosniff
 docker-distribution-api-version: registry/2.0
 cache-control: no-cache,must-revalidate
-etag: W/"sha256:1119a4e98b89e79cfbf11e482fbba231c94cb87268f31b6555a542876e0f8fce"
-content-encoding: gzip
-vary: Accept-Encoding
-x-varnish: 349618986, 443317734, 970675056
+x-varnish: 371176615, 618864460, 961989236
 via: 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)
-accept-ranges: bytes
 age: 0
-x-cache: cp1089 pass, cp3032 pass, cp3032 pass
+x-cache: cp1089 pass, cp3033 pass, cp3032 pass
 x-cache-status: pass
 server-timing: cache;desc="pass"
 strict-transport-security: max-age=106384710; includeSubDomains; preload

Change 486029 had a related patch set uploaded (by Hashar; owner: Hashar):
[operations/docker-images/docker-pkg@master] (DO NOT SUBMIT) Debug for _is_published

https://gerrit.wikimedia.org/r/486029

FWIW, https://docker-registry.wikimedia.org/v2/releng/composer-php73/manifests/0.1.4 now responds with 200 OK.

Also, I would expect docker-pkg to use the internal url for the registry when run in production, does this happen in production too?

The bug is in the docker registry itself. It returns consistently 405 to HEAD requests to that URL:

curl -X HEAD localhost:5000/v2/releng/composer-php73/manifests/0.1.4 -v
...
HTTP/1.1 405 Method Not Allowed

So I'd say we should stall this bug until we migrate to a newer version of the registry, which is part of serviceops goals for this quarter.

On second thoughts - we should probably use a GET in docker-pkg since that's more reliable.

Change 486038 had a related patch set uploaded (by Giuseppe Lavagetto; owner: Giuseppe Lavagetto):
[operations/docker-images/docker-pkg@master] Switch ImageFSM.is_published to use GET instead of HEAD

https://gerrit.wikimedia.org/r/486038

Change 486029 abandoned by Hashar:
(DO NOT SUBMIT) Debug for _is_published

Reason:
our docker-registry fails to properly handle HEAD requests on manifest :/ https://gerrit.wikimedia.org/r/#/c/operations/docker-images/docker-pkg/ /486038/ switches to use GET instead.

https://gerrit.wikimedia.org/r/486029

Change 486038 merged by jenkins-bot:
[operations/docker-images/docker-pkg@master] Switch ImageFSM.is_published to use GET instead of HEAD

https://gerrit.wikimedia.org/r/486038

Change 486071 had a related patch set uploaded (by Giuseppe Lavagetto; owner: Giuseppe Lavagetto):
[operations/docker-images/docker-pkg@1.x] Switch ImageFSM.is_published to use GET instead of HEAD

https://gerrit.wikimedia.org/r/486071

Change 486071 merged by Giuseppe Lavagetto:
[operations/docker-images/docker-pkg@1.x] Switch ImageFSM.is_published to use GET instead of HEAD

https://gerrit.wikimedia.org/r/486071

Change 486056 had a related patch set uploaded (by Hashar; owner: Hashar):
[operations/docker-images/docker-pkg@master] ImageFSM._is_published error handling

https://gerrit.wikimedia.org/r/486056

Change 486056 abandoned by Hashar:
ImageFSM._is_published error handling

https://gerrit.wikimedia.org/r/486056

hashar claimed this task.