diff --git a/lib/manager/docker-compose/__fixtures__/docker-compose.1.yml b/lib/manager/docker-compose/__fixtures__/docker-compose.1.yml index 602f4886336b77..a2ddd4ca6e322e 100644 --- a/lib/manager/docker-compose/__fixtures__/docker-compose.1.yml +++ b/lib/manager/docker-compose/__fixtures__/docker-compose.1.yml @@ -1,104 +1,83 @@ -version: "3" -services: +redis: + image: quay.io/something/redis:alpine + ports: + - "6379" + deploy: + replicas: 2 + update_config: + parallelism: 2 + delay: 10s + restart_policy: + condition: on-failure - redis: - image: quay.io/something/redis:alpine - ports: - - "6379" - networks: - - frontend - deploy: - replicas: 2 - update_config: - parallelism: 2 - delay: 10s - restart_policy: - condition: on-failure +worker: + image: "node:10.0.0" - 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] - db: - image: "postgres:9.4.0" - volumes: - - db-data:/var/lib/postgresql/data - networks: - - backend - 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 - vote: - image: dockersamples/examplevotingapp_vote:before - ports: - - 5000:80 - networks: - - frontend - 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 - result: - image: 'dockersamples/examplevotingapp_result:before' - ports: - - 5001:80 - networks: - - backend - 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] - votingworker: - image: dockersamples/examplevotingapp_worker - networks: - - frontend - - backend - 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] - 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} - edplugins: - image: ${IMAGE:-synkodevelopers/edplugins}:${TAG:-latest} - - debugapp: - image: app-local-debug - build: - context: . - dockerfile: Dockerfile.local - -networks: - frontend: - backend: - -volumes: - db-data: +debugapp: + image: app-local-debug + build: + context: . + dockerfile: Dockerfile.local diff --git a/lib/manager/docker-compose/__fixtures__/docker-compose.3.yml b/lib/manager/docker-compose/__fixtures__/docker-compose.3.yml new file mode 100644 index 00000000000000..602f4886336b77 --- /dev/null +++ b/lib/manager/docker-compose/__fixtures__/docker-compose.3.yml @@ -0,0 +1,104 @@ +version: "3" +services: + + redis: + image: quay.io/something/redis:alpine + ports: + - "6379" + networks: + - frontend + 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 + networks: + - backend + deploy: + placement: + constraints: [node.role == manager] + + vote: + image: dockersamples/examplevotingapp_vote:before + ports: + - 5000:80 + networks: + - frontend + depends_on: + - redis + deploy: + replicas: 2 + update_config: + parallelism: 2 + restart_policy: + condition: on-failure + + result: + image: 'dockersamples/examplevotingapp_result:before' + ports: + - 5001:80 + networks: + - backend + depends_on: + - db + deploy: + replicas: 1 + update_config: + parallelism: 2 + delay: 10s + restart_policy: + condition: on-failure + + votingworker: + image: dockersamples/examplevotingapp_worker + networks: + - frontend + - backend + 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 + +networks: + frontend: + backend: + +volumes: + db-data: diff --git a/lib/manager/docker-compose/__snapshots__/extract.spec.ts.snap b/lib/manager/docker-compose/__snapshots__/extract.spec.ts.snap index f310aa03483d8b..c632b013181a1b 100644 --- a/lib/manager/docker-compose/__snapshots__/extract.spec.ts.snap +++ b/lib/manager/docker-compose/__snapshots__/extract.spec.ts.snap @@ -1,6 +1,74 @@ // 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 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 { "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..e0f972bf125b26 100644 --- a/lib/manager/docker-compose/extract.spec.ts +++ b/lib/manager/docker-compose/extract.spec.ts @@ -1,21 +1,34 @@ import { readFileSync } from 'fs'; import { extractPackageFile } from './extract'; -const yamlFile = readFileSync( +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' +); + 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', () => { expect(extractPackageFile('nothing here\n:::::::')).toBeNull(); }); - it('extracts multiple image lines', () => { - const res = extractPackageFile(yamlFile); + 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(); expect(res.deps).toHaveLength(8); }); diff --git a/lib/manager/docker-compose/extract.ts b/lib/manager/docker-compose/extract.ts index 24733098f3601a..d4ded82ded2c2b 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 }, @@ -55,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'); @@ -63,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); @@ -79,9 +90,6 @@ export function extractPackageFile( .filter(Boolean); logger.trace({ deps }, 'Docker Compose image'); - if (!deps.length) { - return null; - } return { deps }; } catch (err) /* istanbul ignore next */ { logger.warn(