From b3a256700f4215f3dbf9d54ef033bd3e3c26f486 Mon Sep 17 00:00:00 2001
From: Zach Willard <zach.willard@gmail.com>
Date: Sun, 3 Nov 2019 01:38:17 -0600
Subject: [PATCH] fix(docker): added a workaround for ports not being set
 correctly during a redirect to S3 (#4733)

---
 lib/datasource/docker/index.ts | 46 ++++++++++++++---------
 test/util/got.spec.ts          | 69 ++++++++++++++++++++++++++++++++++
 2 files changed, 98 insertions(+), 17 deletions(-)
 create mode 100644 test/util/got.spec.ts

diff --git a/lib/datasource/docker/index.ts b/lib/datasource/docker/index.ts
index 260db1131f..65fe046632 100644
--- a/lib/datasource/docker/index.ts
+++ b/lib/datasource/docker/index.ts
@@ -434,23 +434,7 @@ async function getLabels(
       return {};
     }
     const url = `${registry}/v2/${repository}/blobs/${configDigest}`;
-    const configResponse = await got(url, {
-      headers,
-      hooks: {
-        beforeRedirect: [
-          (options: any) => {
-            if (
-              options.search &&
-              options.search.indexOf('X-Amz-Algorithm') !== -1
-            ) {
-              // docker registry is hosted on amazon, redirect url includes authentication.
-              // eslint-disable-next-line no-param-reassign
-              delete options.headers.authorization;
-            }
-          },
-        ],
-      },
-    });
+    const configResponse = await getConfigResponse(url, headers);
     labels = JSON.parse(configResponse.body).config.Labels;
 
     if (labels) {
@@ -509,6 +493,34 @@ async function getLabels(
   }
 }
 
+export function getConfigResponse(url: string, headers: OutgoingHttpHeaders) {
+  return got(url, {
+    headers,
+    hooks: {
+      beforeRedirect: [
+        (options: any) => {
+          if (
+            options.search &&
+            options.search.indexOf('X-Amz-Algorithm') !== -1
+          ) {
+            // if there is no port in the redirect URL string, then delete it from the redirect options.
+            // This can be evaluated for removal after upgrading to Got v10
+            const portInUrl = options.href.split('/')[2].split(':')[1];
+            if (!portInUrl) {
+              // eslint-disable-next-line no-param-reassign
+              delete options.port; // Redirect will instead use 80 or 443 for HTTP or HTTPS respectively
+            }
+
+            // docker registry is hosted on amazon, redirect url includes authentication.
+            // eslint-disable-next-line no-param-reassign
+            delete options.headers.authorization;
+          }
+        },
+      ],
+    },
+  });
+}
+
 /**
  * docker.getPkgReleases
  *
diff --git a/test/util/got.spec.ts b/test/util/got.spec.ts
new file mode 100644
index 0000000000..9ccc40c6d9
--- /dev/null
+++ b/test/util/got.spec.ts
@@ -0,0 +1,69 @@
+import nock from 'nock';
+import { getConfigResponse } from '../../lib/datasource/docker';
+
+describe('getConfigResponse', () => {
+  beforeEach(() => {
+    nock.disableNetConnect();
+  });
+
+  afterEach(() => {
+    nock.cleanAll();
+    nock.enableNetConnect();
+  });
+
+  it('redirects correctly when the original and redirect url both have a port', async () => {
+    const url =
+      'http://docker.registry.com:5000/v2/image:latest/blobs/some-digest';
+    const redirectURL =
+      'https://s3.aws.amazon.com:3000/docker/registry/v2/blobs/sha256/d4/some-digest/data?X-Amz-Algorithm=AWS4-HMAC-SHA256';
+    nock('http://docker.registry.com:5000')
+      .get('/v2/image:latest/blobs/some-digest')
+      .reply(307, undefined, {
+        location: redirectURL,
+      });
+    nock('https://s3.aws.amazon.com:3000')
+      .get(
+        '/docker/registry/v2/blobs/sha256/d4/some-digest/data?X-Amz-Algorithm=AWS4-HMAC-SHA256'
+      )
+      .reply(200, 'test body');
+    const response = await getConfigResponse(url, {});
+    expect(response.body).toEqual('test body');
+  });
+
+  it('redirects correctly when original url has a port, but the redirect url does not', async () => {
+    const url =
+      'http://docker.registry.com:5001/v2/image:latest/blobs/some-digest';
+    const redirectURL =
+      'https://s3.aws.amazon.com/docker/registry/v2/blobs/sha256/d4/some-digest/data?X-Amz-Algorithm=AWS4-HMAC-SHA256';
+    nock('http://docker.registry.com:5001')
+      .get('/v2/image:latest/blobs/some-digest')
+      .reply(307, undefined, {
+        location: redirectURL,
+      });
+    nock('https://s3.aws.amazon.com')
+      .get(
+        '/docker/registry/v2/blobs/sha256/d4/some-digest/data?X-Amz-Algorithm=AWS4-HMAC-SHA256'
+      )
+      .reply(200, 'test body');
+    const response = await getConfigResponse(url, {});
+    expect(response.body).toEqual('test body');
+  });
+
+  it('redirects correctly when the original url does not have a port, but the redirect to url does', async () => {
+    const url = 'http://docker.registry.com/v2/image:latest/blobs/some-digest';
+    const redirectURL =
+      'https://s3.aws.amazon.com:3001/docker/registry/v2/blobs/sha256/d4/some-digest/data?X-Amz-Algorithm=AWS4-HMAC-SHA256';
+    nock('http://docker.registry.com')
+      .get('/v2/image:latest/blobs/some-digest')
+      .reply(307, undefined, {
+        location: redirectURL,
+      });
+    nock('https://s3.aws.amazon.com:3001')
+      .get(
+        '/docker/registry/v2/blobs/sha256/d4/some-digest/data?X-Amz-Algorithm=AWS4-HMAC-SHA256'
+      )
+      .reply(200, 'test body');
+    const response = await getConfigResponse(url, {});
+    expect(response.body).toEqual('test body');
+  });
+});
-- 
GitLab