From 3d4d2529a13cf86659ac3967b91736edace96968 Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Wed, 28 Jul 2021 08:07:20 +0200
Subject: [PATCH] refactor(docker): better url parsing (#10996)

---
 lib/datasource/docker/common.ts     |  5 ++++-
 lib/datasource/docker/index.spec.ts |  1 +
 lib/datasource/docker/index.ts      |  4 +++-
 lib/util/url.spec.ts                | 13 +++++++++++++
 lib/util/url.ts                     |  9 +++++++++
 5 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/lib/datasource/docker/common.ts b/lib/datasource/docker/common.ts
index b90cf21ea0..f50a1ae99d 100644
--- a/lib/datasource/docker/common.ts
+++ b/lib/datasource/docker/common.ts
@@ -196,7 +196,10 @@ export function getRegistryRepository(
   }
   let dockerRepository = split.join('/');
   if (!registryHost) {
-    registryHost = registryUrl;
+    registryHost = registryUrl.replace(
+      'https://docker.io',
+      'https://index.docker.io'
+    );
   }
   if (registryHost === 'docker.io') {
     registryHost = 'index.docker.io';
diff --git a/lib/datasource/docker/index.spec.ts b/lib/datasource/docker/index.spec.ts
index 7be603c7cb..965c272c86 100644
--- a/lib/datasource/docker/index.spec.ts
+++ b/lib/datasource/docker/index.spec.ts
@@ -391,6 +391,7 @@ describe(getName(), () => {
       const res = await getPkgReleases({
         datasource: id,
         depName: 'node',
+        registryUrls: ['https://docker.io'],
       });
       expect(res).toBeNull();
       expect(httpMock.getTrace()).toMatchSnapshot();
diff --git a/lib/datasource/docker/index.ts b/lib/datasource/docker/index.ts
index 3c2739cdb3..f81fc9f8de 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 { ensurePathPrefix } from '../../util/url';
 import {
   api as dockerVersioning,
   id as dockerVersioningId,
@@ -65,7 +66,8 @@ async function getDockerApiTags(
   // AWS ECR limits the maximum number of results to 1000
   // See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_DescribeRepositories.html#ECR-DescribeRepositories-request-maxResults
   const limit = ecrRegex.test(registryHost) ? 1000 : 10000;
-  let url = `${registryHost}/v2/${dockerRepository}/tags/list?n=${limit}`;
+  let url = `${registryHost}/${dockerRepository}/tags/list?n=${limit}`;
+  url = ensurePathPrefix(url, '/v2');
   const headers = await getAuthHeaders(registryHost, dockerRepository);
   if (!headers) {
     logger.debug('Failed to get authHeaders for getTags lookup');
diff --git a/lib/util/url.spec.ts b/lib/util/url.spec.ts
index e6425b7a84..a3ed562d9a 100644
--- a/lib/util/url.spec.ts
+++ b/lib/util/url.spec.ts
@@ -1,5 +1,6 @@
 import { getName } from '../../test/util';
 import {
+  ensurePathPrefix,
   parseUrl,
   resolveBaseUrl,
   trimTrailingSlash,
@@ -77,4 +78,16 @@ describe(getName(), () => {
     expect(trimTrailingSlash('foo/')).toBe('foo');
     expect(trimTrailingSlash('foo//////')).toBe('foo');
   });
+
+  it('ensures path prefix', () => {
+    expect(ensurePathPrefix('https://index.docker.io', '/v2')).toBe(
+      'https://index.docker.io/v2'
+    );
+    expect(ensurePathPrefix('https://index.docker.io/v2', '/v2')).toBe(
+      'https://index.docker.io/v2'
+    );
+    expect(
+      ensurePathPrefix('https://index.docker.io/v2/something', '/v2')
+    ).toBe('https://index.docker.io/v2/something');
+  });
 });
diff --git a/lib/util/url.ts b/lib/util/url.ts
index 0c9b975f7e..b84548b241 100644
--- a/lib/util/url.ts
+++ b/lib/util/url.ts
@@ -1,5 +1,14 @@
 import urlJoin from 'url-join';
 
+export function ensurePathPrefix(url: string, prefix: string): string {
+  const parsed = new URL(url);
+  const fullPath = url.replace(parsed.origin, '');
+  if (fullPath.startsWith(prefix)) {
+    return url;
+  }
+  return parsed.origin + prefix + fullPath;
+}
+
 export function ensureTrailingSlash(url: string): string {
   return url.replace(/\/?$/, '/');
 }
-- 
GitLab