From c5a6c239af114fe6f10e06f2cfcebe6fe6e51f0a Mon Sep 17 00:00:00 2001 From: Wolfgang Faust Date: Thu, 25 Jun 2020 20:21:51 -0700 Subject: [PATCH 1/5] refactor(docker-compose): rename docker-compose.yml fixture to clarify version --- .../{docker-compose.1.yml => docker-compose.3.yml} | 0 .../docker-compose/__snapshots__/extract.spec.ts.snap | 2 +- lib/manager/docker-compose/extract.spec.ts | 8 ++++---- 3 files changed, 5 insertions(+), 5 deletions(-) rename lib/manager/docker-compose/__fixtures__/{docker-compose.1.yml => docker-compose.3.yml} (100%) diff --git a/lib/manager/docker-compose/__fixtures__/docker-compose.1.yml b/lib/manager/docker-compose/__fixtures__/docker-compose.3.yml similarity index 100% rename from lib/manager/docker-compose/__fixtures__/docker-compose.1.yml rename to lib/manager/docker-compose/__fixtures__/docker-compose.3.yml diff --git a/lib/manager/docker-compose/__snapshots__/extract.spec.ts.snap b/lib/manager/docker-compose/__snapshots__/extract.spec.ts.snap index f310aa03483d8b..b38fe98c62b373 100644 --- a/lib/manager/docker-compose/__snapshots__/extract.spec.ts.snap +++ b/lib/manager/docker-compose/__snapshots__/extract.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`lib/manager/docker-compose/extract extractPackageFile() extracts multiple image lines 1`] = ` +exports[`lib/manager/docker-compose/extract extractPackageFile() extracts multiple image lines for version 3 1`] = ` Array [ Object { "autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}", diff --git a/lib/manager/docker-compose/extract.spec.ts b/lib/manager/docker-compose/extract.spec.ts index 19727a4e3ddeb7..8b6bfcbebaf3f7 100644 --- a/lib/manager/docker-compose/extract.spec.ts +++ b/lib/manager/docker-compose/extract.spec.ts @@ -1,8 +1,8 @@ import { readFileSync } from 'fs'; import { extractPackageFile } from './extract'; -const yamlFile = readFileSync( - 'lib/manager/docker-compose/__fixtures__/docker-compose.1.yml', +const yamlFile3 = readFileSync( + 'lib/manager/docker-compose/__fixtures__/docker-compose.3.yml', 'utf8' ); @@ -14,8 +14,8 @@ describe('lib/manager/docker-compose/extract', () => { it('returns null for malformed YAML', () => { expect(extractPackageFile('nothing here\n:::::::')).toBeNull(); }); - it('extracts multiple image lines', () => { - const res = extractPackageFile(yamlFile); + it('extracts multiple image lines for version 3', () => { + const res = extractPackageFile(yamlFile3); expect(res.deps).toMatchSnapshot(); expect(res.deps).toHaveLength(8); }); From 22f9ba8b6ec01c1b9fe775f39708c35eb1b6507f Mon Sep 17 00:00:00 2001 From: Wolfgang Faust Date: Fri, 26 Jun 2020 08:14:23 -0700 Subject: [PATCH 2/5] test(docker-compose): correct confused tests * 'returns null for empty' now actually checks empty string (previously this branch was ignored in test coverage) * 'returns null for non-object YAML' - existing test renamed (previously named 'for empty' which isn't what it was checking) --- lib/manager/docker-compose/extract.spec.ts | 3 +++ lib/manager/docker-compose/extract.ts | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/manager/docker-compose/extract.spec.ts b/lib/manager/docker-compose/extract.spec.ts index 8b6bfcbebaf3f7..2404bf3103a54f 100644 --- a/lib/manager/docker-compose/extract.spec.ts +++ b/lib/manager/docker-compose/extract.spec.ts @@ -9,6 +9,9 @@ const yamlFile3 = readFileSync( describe('lib/manager/docker-compose/extract', () => { describe('extractPackageFile()', () => { it('returns null for empty', () => { + expect(extractPackageFile('')).toBeNull(); + }); + it('returns null for non-object YAML', () => { expect(extractPackageFile('nothing here')).toBeNull(); }); it('returns null for malformed YAML', () => { diff --git a/lib/manager/docker-compose/extract.ts b/lib/manager/docker-compose/extract.ts index 24733098f3601a..3dbf7cec87ebf1 100644 --- a/lib/manager/docker-compose/extract.ts +++ b/lib/manager/docker-compose/extract.ts @@ -47,7 +47,6 @@ export function extractPackageFile( let config: DockerComposeConfig; try { config = safeLoad(content, { json: true }); - // istanbul ignore if if (!config) { logger.debug( { fileName }, From e2a58f6772f968cd98e8dc2eeb9134e1b78a9c28 Mon Sep 17 00:00:00 2001 From: Wolfgang Faust Date: Fri, 26 Jun 2020 17:57:26 -0700 Subject: [PATCH 3/5] refactor(docker-compose): add check that YAML is object There is a test ('returns null for non-object YAML') for this behaviour, but it was relying on the loop iterating over individual characters in the resulting string and not finding anything useful in them, which seems more like an accident than the way things were intentionally structured. So, this adds an explicit check for this case. --- lib/manager/docker-compose/extract.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/manager/docker-compose/extract.ts b/lib/manager/docker-compose/extract.ts index 3dbf7cec87ebf1..9cfa0ab2df4150 100644 --- a/lib/manager/docker-compose/extract.ts +++ b/lib/manager/docker-compose/extract.ts @@ -54,6 +54,13 @@ export function extractPackageFile( ); return null; } + if (typeof config !== 'object') { + logger.debug( + { fileName, type: typeof config }, + 'Unexpected type for Docker Compose content' + ); + return null; + } } catch (err) { logger.debug({ err }, 'err'); logger.debug({ fileName }, 'Parsing Docker Compose config YAML'); @@ -78,6 +85,7 @@ export function extractPackageFile( .filter(Boolean); logger.trace({ deps }, 'Docker Compose image'); + // istanbul ignore if if (!deps.length) { return null; } From 07e00f393578c13d60218721670888a2322d6f43 Mon Sep 17 00:00:00 2001 From: Wolfgang Faust Date: Fri, 26 Jun 2020 18:03:40 -0700 Subject: [PATCH 4/5] refactor(docker-compose): remove seemingly unnecessary if statement This was previously necessary to get the test 'returns null for non-object YAML' to pass, but that seems to have been accidental and was fixed in the previous commit. This does change the behaviour of this function, but not (I think) in a way which will cause issues: it will now return an empty array rather than null in the case of a compose file that has no services with an image, but doing so makes sense: the file was extracted successfully, it's just that there's nothing of interest to be found in it. --- lib/manager/docker-compose/extract.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/manager/docker-compose/extract.ts b/lib/manager/docker-compose/extract.ts index 9cfa0ab2df4150..ada157a46eaee6 100644 --- a/lib/manager/docker-compose/extract.ts +++ b/lib/manager/docker-compose/extract.ts @@ -85,10 +85,6 @@ export function extractPackageFile( .filter(Boolean); logger.trace({ deps }, 'Docker Compose image'); - // istanbul ignore if - if (!deps.length) { - return null; - } return { deps }; } catch (err) /* istanbul ignore next */ { logger.warn( From a160423fe04a0acdc216131cbd7f436da4f05d47 Mon Sep 17 00:00:00 2001 From: Wolfgang Faust Date: Fri, 26 Jun 2020 08:14:05 -0700 Subject: [PATCH 5/5] fix(docker-compose): support docker-compose.yml version 1 Closes #6570. --- .../__fixtures__/docker-compose.1.yml | 83 +++++++++++++++++++ .../__snapshots__/extract.spec.ts.snap | 68 +++++++++++++++ lib/manager/docker-compose/extract.spec.ts | 10 +++ lib/manager/docker-compose/extract.ts | 7 +- 4 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 lib/manager/docker-compose/__fixtures__/docker-compose.1.yml diff --git a/lib/manager/docker-compose/__fixtures__/docker-compose.1.yml b/lib/manager/docker-compose/__fixtures__/docker-compose.1.yml new file mode 100644 index 00000000000000..a2ddd4ca6e322e --- /dev/null +++ b/lib/manager/docker-compose/__fixtures__/docker-compose.1.yml @@ -0,0 +1,83 @@ +redis: + image: quay.io/something/redis:alpine + ports: + - "6379" + deploy: + replicas: 2 + update_config: + parallelism: 2 + delay: 10s + restart_policy: + condition: on-failure + +worker: + image: "node:10.0.0" + +db: + image: "postgres:9.4.0" + volumes: + - db-data:/var/lib/postgresql/data + deploy: + placement: + constraints: [node.role == manager] + +vote: + image: dockersamples/examplevotingapp_vote:before + ports: + - 5000:80 + depends_on: + - redis + deploy: + replicas: 2 + update_config: + parallelism: 2 + restart_policy: + condition: on-failure + +result: + image: 'dockersamples/examplevotingapp_result:before' + ports: + - 5001:80 + depends_on: + - db + deploy: + replicas: 1 + update_config: + parallelism: 2 + delay: 10s + restart_policy: + condition: on-failure + +votingworker: + image: dockersamples/examplevotingapp_worker + deploy: + mode: replicated + replicas: 1 + labels: [APP=VOTING] + restart_policy: + condition: on-failure + delay: 10s + max_attempts: 3 + window: 120s + placement: + constraints: [node.role == manager] + +visualizer: + image: dockersamples/visualizer:stable + ports: + - "8080:8080" + stop_grace_period: 1m30s + volumes: + - "/var/run/docker.sock:/var/run/docker.sock" + deploy: + placement: + constraints: [node.role == manager] + +edplugins: + image: ${IMAGE:-synkodevelopers/edplugins}:${TAG:-latest} + +debugapp: + image: app-local-debug + build: + context: . + dockerfile: Dockerfile.local diff --git a/lib/manager/docker-compose/__snapshots__/extract.spec.ts.snap b/lib/manager/docker-compose/__snapshots__/extract.spec.ts.snap index b38fe98c62b373..c632b013181a1b 100644 --- a/lib/manager/docker-compose/__snapshots__/extract.spec.ts.snap +++ b/lib/manager/docker-compose/__snapshots__/extract.spec.ts.snap @@ -1,5 +1,73 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`lib/manager/docker-compose/extract extractPackageFile() extracts multiple image lines for version 1 1`] = ` +Array [ + Object { + "autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}", + "currentDigest": undefined, + "currentValue": "alpine", + "datasource": "docker", + "depName": "quay.io/something/redis", + "replaceString": "quay.io/something/redis:alpine", + }, + Object { + "autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}", + "commitMessageTopic": "Node.js", + "currentDigest": undefined, + "currentValue": "10.0.0", + "datasource": "docker", + "depName": "node", + "replaceString": "node:10.0.0", + }, + Object { + "autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}", + "currentDigest": undefined, + "currentValue": "9.4.0", + "datasource": "docker", + "depName": "postgres", + "replaceString": "postgres:9.4.0", + }, + Object { + "autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}", + "currentDigest": undefined, + "currentValue": "before", + "datasource": "docker", + "depName": "dockersamples/examplevotingapp_vote", + "replaceString": "dockersamples/examplevotingapp_vote:before", + }, + Object { + "autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}", + "currentDigest": undefined, + "currentValue": "before", + "datasource": "docker", + "depName": "dockersamples/examplevotingapp_result", + "replaceString": "dockersamples/examplevotingapp_result:before", + }, + Object { + "autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}", + "currentDigest": undefined, + "currentValue": undefined, + "datasource": "docker", + "depName": "dockersamples/examplevotingapp_worker", + "replaceString": "dockersamples/examplevotingapp_worker", + }, + Object { + "autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}", + "currentDigest": undefined, + "currentValue": "stable", + "datasource": "docker", + "depName": "dockersamples/visualizer", + "replaceString": "dockersamples/visualizer:stable", + }, + Object { + "autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}", + "datasource": "docker", + "replaceString": "\${IMAGE:-synkodevelopers/edplugins}:\${TAG:-latest}", + "skipReason": "contains-variable", + }, +] +`; + exports[`lib/manager/docker-compose/extract extractPackageFile() extracts multiple image lines for version 3 1`] = ` Array [ Object { diff --git a/lib/manager/docker-compose/extract.spec.ts b/lib/manager/docker-compose/extract.spec.ts index 2404bf3103a54f..e0f972bf125b26 100644 --- a/lib/manager/docker-compose/extract.spec.ts +++ b/lib/manager/docker-compose/extract.spec.ts @@ -1,6 +1,11 @@ import { readFileSync } from 'fs'; import { extractPackageFile } from './extract'; +const yamlFile1 = readFileSync( + 'lib/manager/docker-compose/__fixtures__/docker-compose.1.yml', + 'utf8' +); + const yamlFile3 = readFileSync( 'lib/manager/docker-compose/__fixtures__/docker-compose.3.yml', 'utf8' @@ -17,6 +22,11 @@ describe('lib/manager/docker-compose/extract', () => { it('returns null for malformed YAML', () => { expect(extractPackageFile('nothing here\n:::::::')).toBeNull(); }); + it('extracts multiple image lines for version 1', () => { + const res = extractPackageFile(yamlFile1); + expect(res.deps).toMatchSnapshot(); + expect(res.deps).toHaveLength(8); + }); it('extracts multiple image lines for version 3', () => { const res = extractPackageFile(yamlFile3); expect(res.deps).toMatchSnapshot(); diff --git a/lib/manager/docker-compose/extract.ts b/lib/manager/docker-compose/extract.ts index ada157a46eaee6..d4ded82ded2c2b 100644 --- a/lib/manager/docker-compose/extract.ts +++ b/lib/manager/docker-compose/extract.ts @@ -69,9 +69,14 @@ export function extractPackageFile( try { const lineMapper = new LineMapper(content, /^\s*image:/); + const services = + 'version' in config + ? config.services // docker-compose version 2+ + : config; // docker-compose version 1 (services at top level) + // Image name/tags for services are only eligible for update if they don't // use variables and if the image is not built locally - const deps = Object.values(config.services || {}) + const deps = Object.values(services || {}) .filter((service) => service && service.image && !service.build) .map((service) => { const dep = getDep(service.image);