From 902ee0209635c3524ce76a2c240b56c909c87d41 Mon Sep 17 00:00:00 2001 From: Michael Kriese <michael.kriese@visualon.de> Date: Tue, 31 Aug 2021 10:23:22 +0200 Subject: [PATCH] feat(manager:docker): use head requests (#11484) --- .../docker/__snapshots__/index.spec.ts.snap | 1063 +---------------- lib/datasource/docker/common.ts | 12 +- lib/datasource/docker/index.spec.ts | 136 +-- lib/datasource/docker/index.ts | 24 +- lib/util/http/index.ts | 14 +- 5 files changed, 107 insertions(+), 1142 deletions(-) diff --git a/lib/datasource/docker/__snapshots__/index.spec.ts.snap b/lib/datasource/docker/__snapshots__/index.spec.ts.snap index 031abb8ee9..8cdb641738 100644 --- a/lib/datasource/docker/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/docker/__snapshots__/index.spec.ts.snap @@ -1,690 +1,22 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`datasource/docker/index getDigest continues without token if ECR authentication could not be extracted 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", - }, -] -`; - -exports[`datasource/docker/index getDigest continues without token if ECR authentication fails 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", - }, -] -`; - -exports[`datasource/docker/index getDigest continues without token, when no header is present 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value", - }, -] -`; - -exports[`datasource/docker/index getDigest falls back to body for digest 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", - "host": "auth.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/some-dep:pull", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Bearer some-token", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value", - }, -] -`; - -exports[`datasource/docker/index getDigest passes credentials to ECR client 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Basic abcdef", - "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/manifests/some-tag", - }, -] -`; - -exports[`datasource/docker/index getDigest returns digest 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "auth.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/some-dep:pull", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Bearer some-token", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/some-dep/manifests/latest", - }, -] -`; - -exports[`datasource/docker/index getDigest returns null for 403 with basic authentication 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/some-dep/manifests/some-tag", - }, -] -`; - -exports[`datasource/docker/index getDigest returns null if empty header 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value", - }, -] -`; - -exports[`datasource/docker/index getDigest returns null if errored 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value", - }, -] -`; - -exports[`datasource/docker/index getDigest returns null if no token 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value", - }, -] -`; - -exports[`datasource/docker/index getDigest supports ECR authentication 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Basic abcdef", - "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/manifests/some-tag", - }, -] -`; - -exports[`datasource/docker/index getDigest supports basic authentication 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/some-dep/manifests/some-tag", - }, -] -`; - -exports[`datasource/docker/index getDigest supports docker insecure registry 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "http://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "http://index.docker.io/v2/library/some-dep/manifests/latest", - }, -] -`; - -exports[`datasource/docker/index getDigest supports scoped names 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", - "host": "auth.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/some-other-dep:pull", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Bearer some-token", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/some-other-dep/manifests/8.0.0-alpine", - }, -] -`; - -exports[`datasource/docker/index getReleases adds library/ prefix for Docker Hub (explicit) 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", - "host": "auth.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/node:pull", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Bearer some-token ", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/node/tags/list?n=10000", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/node/manifests/1.0.0", - }, -] -`; - -exports[`datasource/docker/index getReleases adds library/ prefix for Docker Hub (implicit) 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", - "host": "auth.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/node:pull", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Bearer some-token ", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/node/tags/list?n=10000", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/node/manifests/1.0.0", - }, -] -`; - -exports[`datasource/docker/index getReleases adds no library/ prefix for other registries 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "k8s.gcr.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://k8s.gcr.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", - "host": "k8s.gcr.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://k8s.gcr.io/v2/token?service=k8s.gcr.io&scope=repository:kubernetes-dashboard-amd64:pull", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Bearer some-token ", - "host": "k8s.gcr.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://k8s.gcr.io/v2/kubernetes-dashboard-amd64/tags/list?n=10000", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "k8s.gcr.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://k8s.gcr.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "k8s.gcr.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://k8s.gcr.io/v2/kubernetes-dashboard-amd64/manifests/1.0.0", - }, -] -`; - exports[`datasource/docker/index getReleases ignores unsupported manifest 1`] = ` Object { - "registryUrl": "https://index.docker.io", + "registryUrl": "https://registry.company.com", "releases": Array [], } `; -exports[`datasource/docker/index getReleases ignores unsupported manifest 2`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/tags/list?n=10000", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/manifests/latest", - }, -] -`; - exports[`datasource/docker/index getReleases ignores unsupported schema version 1`] = ` Object { - "registryUrl": "https://index.docker.io", + "registryUrl": "https://registry.company.com", "releases": Array [], } `; -exports[`datasource/docker/index getReleases ignores unsupported schema version 2`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/tags/list?n=10000", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/manifests/latest", - }, -] -`; - -exports[`datasource/docker/index getReleases returns null if no auth 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, -] -`; - -exports[`datasource/docker/index getReleases returns null if no token 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/library/node/tags/list?n=10000", - }, -] -`; - -exports[`datasource/docker/index getReleases returns null on error 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "index.docker.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://index.docker.io/v2/my/node/tags/list?n=10000", - }, -] -`; - exports[`datasource/docker/index getReleases supports labels 1`] = ` Object { - "registryUrl": "https://index.docker.io", + "registryUrl": "https://registry.company.com", "releases": Array [ Object { "version": "1.0.0", @@ -709,402 +41,17 @@ Object { } `; -exports[`datasource/docker/index getReleases supports labels 2`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/tags/list?n=10000", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/manifests/2-alpine", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/blobs/some-config-digest", - }, -] -`; - exports[`datasource/docker/index getReleases supports manifest lists 1`] = ` Object { - "registryUrl": "https://index.docker.io", + "registryUrl": "https://registry.company.com", "releases": Array [], "sourceUrl": "https://github.com/renovatebot/renovate", } `; -exports[`datasource/docker/index getReleases supports manifest lists 2`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/tags/list?n=10000", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/manifests/abc", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/manifests/some-image-digest", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/blobs/some-config-digest", - }, -] -`; - exports[`datasource/docker/index getReleases supports redirect 1`] = ` Object { - "registryUrl": "https://index.docker.io", + "registryUrl": "https://registry.company.com", "releases": Array [], } `; - -exports[`datasource/docker/index getReleases supports redirect 2`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/tags/list?n=10000", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/manifests/latest", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/blobs/some-config-digest", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "abc.s3.amazon.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://abc.s3.amazon.com/some-config-digest?X-Amz-Algorithm=xxxx", - }, -] -`; - -exports[`datasource/docker/index getReleases uses custom registry in depName 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/tags/list?n=10000", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/manifests/1.0.0", - }, -] -`; - -exports[`datasource/docker/index getReleases uses custom registry with registryUrls 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/tags/list?n=10000", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "api.github.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://api.github.com/user/9287/repos?page=3&per_page=100", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "registry.company.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://registry.company.com/v2/node/manifests/latest", - }, -] -`; - -exports[`datasource/docker/index getReleases uses lower tag limit for ECR deps 1`] = ` -Array [ - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/tags/list?n=1000", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/manifests/undefined", - }, -] -`; - -exports[`datasource/docker/index getReleases uses quay api 1`] = ` -Array [ - Object { - "headers": Object { - "accept": "application/json", - "accept-encoding": "gzip, deflate, br", - "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", - "host": "quay.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://quay.io/api/v1/repository/bitnami/redis/tag/?limit=100&page=1&onlyActiveTags=true", - }, - Object { - "headers": Object { - "accept-encoding": "gzip, deflate, br", - "host": "quay.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://quay.io/v2/", - }, - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json", - "accept-encoding": "gzip, deflate, br", - "host": "quay.io", - "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)", - }, - "method": "GET", - "url": "https://quay.io/v2/bitnami/redis/manifests/5.0.12", - }, -] -`; diff --git a/lib/datasource/docker/common.ts b/lib/datasource/docker/common.ts index 72cb51214a..3b101a6a25 100644 --- a/lib/datasource/docker/common.ts +++ b/lib/datasource/docker/common.ts @@ -260,20 +260,18 @@ function digestFromManifestStr(str: hasha.HashaInput): string { return 'sha256:' + hasha(str, { algorithm: 'sha256' }); } -export function extractDigestFromResponse( +export function extractDigestFromResponseBody( manifestResponse: HttpResponse ): string { - if (manifestResponse.headers['docker-content-digest'] === undefined) { - return digestFromManifestStr(manifestResponse.body); - } - return manifestResponse.headers['docker-content-digest'] as string; + return digestFromManifestStr(manifestResponse.body); } // TODO: debug why quay throws errors (#9612) export async function getManifestResponse( registryHost: string, dockerRepository: string, - tag: string + tag: string, + mode: 'head' | 'get' = 'get' ): Promise<HttpResponse> { logger.debug( `getManifestResponse(${registryHost}, ${dockerRepository}, ${tag})` @@ -287,7 +285,7 @@ export async function getManifestResponse( headers.accept = 'application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json'; const url = `${registryHost}/v2/${dockerRepository}/manifests/${tag}`; - const manifestResponse = await http.get(url, { + const manifestResponse = await http[mode](url, { headers, noAuth: true, }); diff --git a/lib/datasource/docker/index.spec.ts b/lib/datasource/docker/index.spec.ts index 038fde1a0b..33cd9df8d4 100644 --- a/lib/datasource/docker/index.spec.ts +++ b/lib/datasource/docker/index.spec.ts @@ -59,46 +59,47 @@ describe('datasource/docker/index', () => { it('returns null if no token', async () => { httpMock .scope(baseUrl) - .get('/') + .get('/', undefined, { badheaders: ['authorization'] }) .reply(200, '', {}) - .get('/library/some-dep/manifests/some-new-value') + .head('/library/some-dep/manifests/some-new-value', undefined, { + badheaders: ['authorization'], + }) .reply(401); const res = await getDigest( { datasource: 'docker', depName: 'some-dep' }, 'some-new-value' ); expect(res).toBeNull(); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null if errored', async () => { httpMock .scope(baseUrl) - .get('/') - .reply(200, { token: 'some-token' }) - .get('/library/some-dep/manifests/some-new-value') + .get('/', undefined, { badheaders: ['authorization'] }) + .reply(200, { token: 'abc' }) + .head('/library/some-dep/manifests/some-new-value', undefined, { + reqheaders: { authorization: 'Bearer abc' }, + }) .replyWithError('error'); const res = await getDigest( { datasource: 'docker', depName: 'some-dep' }, 'some-new-value' ); expect(res).toBeNull(); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null if empty header', async () => { httpMock .scope(baseUrl) - .get('/') + .get('/', undefined, { badheaders: ['authorization'] }) .reply(200, { token: 'some-token' }) - .get('/library/some-dep/manifests/some-new-value') + .head('/library/some-dep/manifests/some-new-value') .reply(200, undefined, { 'docker-content-digest': '' }); const res = await getDigest( { datasource: 'docker', depName: 'some-dep' }, 'some-new-value' ); expect(res).toBeNull(); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns digest', async () => { @@ -109,7 +110,7 @@ describe('datasource/docker/index', () => { 'www-authenticate': 'Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull "', }) - .get('/library/some-dep/manifests/latest') + .head('/library/some-dep/manifests/latest') .reply(200, {}, { 'docker-content-digest': 'some-digest' }); httpMock .scope(authUrl) @@ -124,17 +125,19 @@ describe('datasource/docker/index', () => { depName: 'some-dep', }); expect(res).toBe('some-digest'); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('falls back to body for digest', async () => { httpMock .scope(baseUrl) .get('/') + .twice() .reply(401, '', { 'www-authenticate': 'Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull "', }) + .head('/library/some-dep/manifests/some-new-value') + .reply(200, undefined, {}) .get('/library/some-dep/manifests/some-new-value') .reply( 200, @@ -165,6 +168,7 @@ describe('datasource/docker/index', () => { .get( '/token?service=registry.docker.io&scope=repository:library/some-dep:pull' ) + .twice() .reply(200, { token: 'some-token' }); const res = await getDigest( { datasource: 'docker', depName: 'some-dep' }, @@ -173,15 +177,14 @@ describe('datasource/docker/index', () => { expect(res).toBe( 'sha256:b3d6068234f3a18ebeedd2dab81e67b6a192e81192a099df4112ecfc7c3be84f' ); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('supports docker insecure registry', async () => { httpMock .scope(baseUrl.replace('https', 'http')) - .get('/') + .get('/', undefined, { badheaders: ['authorization'] }) .reply(200) - .get('/library/some-dep/manifests/latest') + .head('/library/some-dep/manifests/latest') .reply(200, '', { 'docker-content-digest': 'some-digest' }); hostRules.find.mockReturnValueOnce({ insecureRegistry: true }); const res = await getDigest({ @@ -189,45 +192,42 @@ describe('datasource/docker/index', () => { depName: 'some-dep', }); expect(res).toBe('some-digest'); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('supports basic authentication', async () => { httpMock .scope(baseUrl) - .get('/') + .get('/', undefined, { badheaders: ['authorization'] }) .reply(401, '', { 'www-authenticate': 'Basic realm="My Private Docker Registry Server"', }) - .get('/library/some-dep/manifests/some-tag') + .head('/library/some-dep/manifests/some-tag', undefined, { + reqheaders: { + authorization: 'Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk', + }, + }) .reply(200, '', { 'docker-content-digest': 'some-digest' }); const res = await getDigest( { datasource: 'docker', depName: 'some-dep' }, 'some-tag' ); - const trace = httpMock.getTrace(); expect(res).toBe('some-digest'); - expect(trace[1].headers.authorization).toBe( - 'Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk' - ); - expect(trace).toMatchSnapshot(); }); it('returns null for 403 with basic authentication', async () => { httpMock .scope(baseUrl) - .get('/') + .get('/', undefined, { badheaders: ['authorization'] }) .reply(401, '', { 'www-authenticate': 'Basic realm="My Private Docker Registry Server"', }) - .get('/library/some-dep/manifests/some-tag') + .head('/library/some-dep/manifests/some-tag') .reply(403); const res = await getDigest( { datasource: 'docker', depName: 'some-dep' }, 'some-tag' ); expect(res).toBeNull(); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('passes credentials to ECR client', async () => { @@ -237,7 +237,9 @@ describe('datasource/docker/index', () => { .reply(401, '', { 'www-authenticate': 'Basic realm="My Private Docker Registry Server"', }) - .get('/node/manifests/some-tag') + .head('/node/manifests/some-tag', undefined, { + reqheaders: { authorization: 'Basic abc' }, + }) .reply(200, '', { 'docker-content-digest': 'some-digest' }); mockEcrAuthResolve({ @@ -252,8 +254,6 @@ describe('datasource/docker/index', () => { 'some-tag' ); - const trace = httpMock.getTrace(); - expect(trace).toMatchSnapshot(); expect(AWS.ECR).toHaveBeenCalledWith({ credentials: { accessKeyId: 'some-username', @@ -270,11 +270,13 @@ describe('datasource/docker/index', () => { .reply(401, '', { 'www-authenticate': 'Basic realm="My Private Docker Registry Server"', }) - .get('/node/manifests/some-tag') + .head('/node/manifests/some-tag', undefined, { + reqheaders: { authorization: 'Basic abc' }, + }) .reply(200, '', { 'docker-content-digest': 'some-digest' }); mockEcrAuthResolve({ - authorizationData: [{ authorizationToken: 'abcdef' }], + authorizationData: [{ authorizationToken: 'abc' }], }); const res = await getDigest( @@ -284,10 +286,8 @@ describe('datasource/docker/index', () => { }, 'some-tag' ); - const trace = httpMock.getTrace(); + expect(res).toBe('some-digest'); - expect(trace[1].headers.authorization).toBe('Basic abcdef'); - expect(trace).toMatchSnapshot(); }); it('continues without token if ECR authentication could not be extracted', async () => { @@ -304,7 +304,6 @@ describe('datasource/docker/index', () => { 'some-tag' ); expect(res).toBeNull(); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('continues without token if ECR authentication fails', async () => { @@ -321,7 +320,6 @@ describe('datasource/docker/index', () => { 'some-tag' ); expect(res).toBeNull(); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('continues without token, when no header is present', async () => { @@ -331,14 +329,13 @@ describe('datasource/docker/index', () => { .reply(200, '', { 'content-type': 'text/plain', }) - .get('/library/some-dep/manifests/some-new-value') + .head('/library/some-dep/manifests/some-new-value') .reply(200, {}, { 'docker-content-digest': 'some-digest' }); const res = await getDigest( { datasource: 'docker', depName: 'some-dep' }, 'some-new-value' ); expect(res).toBe('some-digest'); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('supports scoped names', async () => { @@ -349,7 +346,7 @@ describe('datasource/docker/index', () => { 'www-authenticate': 'Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull "', }) - .get('/library/some-other-dep/manifests/8.0.0-alpine') + .head('/library/some-other-dep/manifests/8.0.0-alpine') .reply(200, {}, { 'docker-content-digest': 'some-digest' }); httpMock .scope(authUrl) @@ -362,7 +359,6 @@ describe('datasource/docker/index', () => { '8.0.0-alpine' ); expect(res).toBe('some-digest'); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('should throw error for 429', async () => { @@ -379,6 +375,7 @@ describe('datasource/docker/index', () => { ).rejects.toThrow(EXTERNAL_HOST_ERROR); }); }); + describe('getReleases', () => { it('returns null if no token', async () => { httpMock @@ -393,7 +390,6 @@ describe('datasource/docker/index', () => { registryUrls: ['https://docker.io'], }); expect(res).toBeNull(); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('uses custom registry with registryUrls', async () => { @@ -425,7 +421,6 @@ describe('datasource/docker/index', () => { }; const res = await getPkgReleases(config); expect(res.releases).toHaveLength(1); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('uses custom registry in depName', async () => { @@ -445,7 +440,6 @@ describe('datasource/docker/index', () => { depName: 'registry.company.com/node', }); expect(res.releases).toHaveLength(1); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('uses quay api', async () => { @@ -455,7 +449,11 @@ describe('datasource/docker/index', () => { .get( '/api/v1/repository/bitnami/redis/tag/?limit=100&page=1&onlyActiveTags=true' ) - .reply(200, { tags, has_additional: false }) + .reply(200, { tags, has_additional: true }) + .get( + '/api/v1/repository/bitnami/redis/tag/?limit=100&page=2&onlyActiveTags=true' + ) + .reply(200, { tags: [], has_additional: false }) .get('/v2/') .reply(200, '', {}) .get('/v2/bitnami/redis/manifests/5.0.12') @@ -467,7 +465,6 @@ describe('datasource/docker/index', () => { }; const res = await getPkgReleases(config); expect(res.releases).toHaveLength(1); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('uses quay api and test error', async () => { @@ -495,16 +492,20 @@ describe('datasource/docker/index', () => { // The tag limit parameter `n` needs to be limited to 1000 for ECR // See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_DescribeRepositories.html#ECR-DescribeRepositories-request-maxResults .get('/node/tags/list?n=1000') - .reply(200, {}, {}) + .reply(200, { tags: ['some'] }, {}) .get('/') .reply(200, '', {}) - .get('/node/manifests/undefined') + .get('/node/manifests/some') .reply(200); - await getPkgReleases({ - datasource: id, - depName: '123456789.dkr.ecr.us-east-1.amazonaws.com/node', + expect( + await getPkgReleases({ + datasource: id, + depName: '123456789.dkr.ecr.us-east-1.amazonaws.com/node', + }) + ).toEqual({ + registryUrl: 'https://123456789.dkr.ecr.us-east-1.amazonaws.com', + releases: [], }); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('adds library/ prefix for Docker Hub (implicit)', async () => { @@ -533,7 +534,6 @@ describe('datasource/docker/index', () => { depName: 'node', }); expect(res.releases).toHaveLength(1); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('adds library/ prefix for Docker Hub (explicit)', async () => { @@ -562,7 +562,6 @@ describe('datasource/docker/index', () => { depName: 'docker.io/node', }); expect(res.releases).toHaveLength(1); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('adds no library/ prefix for other registries', async () => { @@ -589,7 +588,6 @@ describe('datasource/docker/index', () => { depName: 'k8s.gcr.io/kubernetes-dashboard-amd64', }); expect(res.releases).toHaveLength(1); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null on error', async () => { @@ -604,7 +602,6 @@ describe('datasource/docker/index', () => { depName: 'my/node', }); expect(res).toBeNull(); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('strips trailing slash from registry', async () => { @@ -643,7 +640,6 @@ describe('datasource/docker/index', () => { depName: 'node', }); expect(res).toBeNull(); - expect(httpMock.getTrace()).toMatchSnapshot(); }); it('supports labels', async () => { @@ -683,9 +679,7 @@ describe('datasource/docker/index', () => { datasource: id, depName: 'registry.company.com/node', }); - const trace = httpMock.getTrace(); expect(res).toMatchSnapshot(); - expect(trace).toMatchSnapshot(); }); it('supports manifest lists', async () => { @@ -721,9 +715,7 @@ describe('datasource/docker/index', () => { datasource: id, depName: 'registry.company.com/node', }); - const trace = httpMock.getTrace(); expect(res).toMatchSnapshot(); - expect(trace).toMatchSnapshot(); }); it('ignores unsupported manifest', async () => { @@ -743,9 +735,7 @@ describe('datasource/docker/index', () => { datasource: id, depName: 'registry.company.com/node', }); - const trace = httpMock.getTrace(); expect(res).toMatchSnapshot(); - expect(trace).toMatchSnapshot(); }); it('ignores unsupported schema version', async () => { @@ -762,17 +752,25 @@ describe('datasource/docker/index', () => { datasource: id, depName: 'registry.company.com/node', }); - const trace = httpMock.getTrace(); expect(res).toMatchSnapshot(); - expect(trace).toMatchSnapshot(); }); it('supports redirect', async () => { httpMock - .scope('https://registry.company.com/v2') + .scope('https://registry.company.com/v2', { + badheaders: ['authorization'], + }) .get('/') .times(3) - .reply(200) + .reply(401, '', { + 'www-authenticate': 'Basic realm="My Private Docker Registry Server"', + }); + httpMock + .scope('https://registry.company.com/v2', { + reqheaders: { + authorization: 'Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk', + }, + }) .get('/node/tags/list?n=10000') .reply(200, { tags: ['latest'] }) .get('/node/manifests/latest') @@ -787,7 +785,7 @@ describe('datasource/docker/index', () => { 'https://abc.s3.amazon.com/some-config-digest?X-Amz-Algorithm=xxxx', }); httpMock - .scope('https://abc.s3.amazon.com') + .scope('https://abc.s3.amazon.com', { badheaders: ['authorization'] }) .get('/some-config-digest') .query({ 'X-Amz-Algorithm': 'xxxx' }) .reply(200, { @@ -797,11 +795,7 @@ describe('datasource/docker/index', () => { datasource: id, depName: 'registry.company.com/node', }); - const trace = httpMock.getTrace(); expect(res).toMatchSnapshot(); - expect(trace).toMatchSnapshot(); - expect(trace[1].headers.authorization).toBeUndefined(); - expect(trace[trace.length - 1].headers.authorization).toBeUndefined(); }); }); }); diff --git a/lib/datasource/docker/index.ts b/lib/datasource/docker/index.ts index 7bba890ce3..c76659ef57 100644 --- a/lib/datasource/docker/index.ts +++ b/lib/datasource/docker/index.ts @@ -3,6 +3,7 @@ import parseLinkHeader from 'parse-link-header'; import { logger } from '../../logger'; import { ExternalHostError } from '../../types/errors/external-host-error'; import * as packageCache from '../../util/cache/package'; +import { hasKey } from '../../util/object'; import { ensurePathPrefix } from '../../util/url'; import { api as dockerVersioning, @@ -12,7 +13,7 @@ import type { GetReleasesConfig, ReleaseResult } from '../types'; import { defaultRegistryUrls, ecrRegex, - extractDigestFromResponse, + extractDigestFromResponseBody, getAuthHeaders, getLabels, getManifestResponse, @@ -191,13 +192,27 @@ export async function getDigest( if (cachedResult !== undefined) { return cachedResult; } - const manifestResponse = await getManifestResponse( + let manifestResponse = await getManifestResponse( registryHost, dockerRepository, - newTag + newTag, + 'head' ); if (manifestResponse) { - digest = extractDigestFromResponse(manifestResponse) || null; + if (hasKey('docker-content-digest', manifestResponse.headers)) { + digest = manifestResponse.headers['docker-content-digest'] || null; + } else { + logger.debug( + { registryHost }, + 'Missing docker content digest header, pulling full manifest' + ); + manifestResponse = await getManifestResponse( + registryHost, + dockerRepository, + newTag + ); + digest = extractDigestFromResponseBody(manifestResponse); + } logger.debug({ digest }, 'Got docker digest'); } } catch (err) /* istanbul ignore next */ { @@ -243,6 +258,7 @@ export async function getReleases({ } const releases = tags.map((version) => ({ version })); const ret: ReleaseResult = { + registryUrl: registryHost, releases, }; diff --git a/lib/util/http/index.ts b/lib/util/http/index.ts index 91d706ef91..a60501444f 100644 --- a/lib/util/http/index.ts +++ b/lib/util/http/index.ts @@ -142,13 +142,23 @@ export class Http<GetOptions = HttpOptions, PostOptions = HttpPostOptions> { const cacheKey = crypto .createHash('md5') - .update('got-' + JSON.stringify({ url, headers: options.headers })) + .update( + 'got-' + + JSON.stringify({ + url, + headers: options.headers, + method: options.method, + }) + ) .digest('hex'); let resPromise; // Cache GET requests unless useCache=false - if (options.method === 'get' && options.useCache !== false) { + if ( + ['get', 'head'].includes(options.method) && + options.useCache !== false + ) { resPromise = memCache.get(cacheKey); } -- GitLab