diff --git a/compose.js b/compose.js index fad4d1d..74334ce 100644 --- a/compose.js +++ b/compose.js @@ -1,5 +1,4 @@ -const yaml = require('js-yaml'); -const fs = require('fs'); +const { readYamlEnvSync } = require('yaml-env-defaults'); const stream = require('stream'); const secrets = require('./lib/secrets'); @@ -10,7 +9,7 @@ const services = require('./lib/services'); const tools = require('./lib/tools'); class Compose { - constructor(dockerode, file, projectName) { + constructor(dockerode, file, projectName, customEnv = undefined) { this.docker = dockerode; if (file === undefined || projectName === undefined) { @@ -19,9 +18,10 @@ class Compose { this.file = file; this.projectName = projectName; + this.loadYaml = (path) => readYamlEnvSync(path, customEnv); try { - this.recipe = yaml.load(fs.readFileSync(file, 'utf8')); + this.recipe = this.loadYaml(file); } catch (e) { throw e; } @@ -52,7 +52,7 @@ class Compose { output.volumes = await volumes.up(this.docker, this.projectName, this.recipe, output); output.configs = await configs(this.docker, this.projectName, this.recipe, output); output.networks = await networks.up(this.docker, this.projectName, this.recipe, output); - output.services = await services.up(this.docker, this.projectName, this.recipe, output, options); + output.services = await services.up(this.docker, this.projectName, this.recipe, output, options, this.loadYaml); return output; } catch (e) { throw e; diff --git a/lib/services.js b/lib/services.js index 42d3cdf..2a46358 100644 --- a/lib/services.js +++ b/lib/services.js @@ -1,7 +1,6 @@ const tools = require('./tools'); const servicesTools = require('./servicesTools'); const fs = require('fs'); -const yaml = require('js-yaml'); const path = require('path'); async function down(docker, projectName, recipe, output, options) { @@ -21,7 +20,7 @@ async function down(docker, projectName, recipe, output, options) { return services; } -async function up(docker, projectName, recipe, output, options) { +async function up(docker, projectName, recipe, output, options, loadYaml) { var services = []; var serviceNames = tools.sortServices(recipe); const cwdPath = path.dirname(output.file); @@ -32,7 +31,7 @@ async function up(docker, projectName, recipe, output, options) { var service = recipe.services[serviceName]; if (service.extends !== undefined) { if (service.extends.service !== undefined) { - service = extendsServices(service, recipe, pathScope); + service = extendsServices(service, recipe, pathScope, loadYaml); } else { throw new Error('Service key in extends is required!'); } @@ -341,7 +340,7 @@ var convertFancyDurationToMs = function (value) { }; // https://github.com/compose-spec/compose-spec/blob/master/spec.md#extends -var extendsServices = function (service, recipe, pathScope) { +var extendsServices = function (service, recipe, pathScope, loadYaml) { // https://github.com/compose-spec/compose-spec/blob/master/spec.md#finding-referenced-service if (service.extends.file === undefined) { // EXTENDS OF THE SAME RECIPE @@ -349,22 +348,19 @@ var extendsServices = function (service, recipe, pathScope) { service, service.extends.service, recipe, - pathScope + pathScope, + loadYaml ); } else { // EXTENDS OF ANOTHER RECIPE var absolutePath = path.dirname(pathScope.file); - var extendsRecipe = yaml.load( - fs.readFileSync( - path.resolve(path.join(absolutePath, service.extends.file)), - 'utf8' - ) - ); + var extendsRecipe = loadYaml(path.resolve(path.join(absolutePath, service.extends.file))); return buildExtendsService( service, service.extends.service, extendsRecipe, - pathScope + pathScope, + loadYaml ); } }; @@ -373,7 +369,8 @@ var buildExtendsService = function ( service, extendsServiceName, recipe, - pathScope + pathScope, + loadYaml ) { var extend = false; var extendsRecipeServiceNames = Object.keys(recipe.services); @@ -403,7 +400,7 @@ var buildExtendsService = function ( path.join(absolutePath, oldService.extends.file) ); } - if (extend) service = extendsServices(service, recipe, pathScope); + if (extend) service = extendsServices(service, recipe, pathScope, loadYaml); return service; } diff --git a/package-lock.json b/package-lock.json index 4059666..ae2147b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "Apache-2.0", "dependencies": { "dockerode": "^3.3.0", - "js-yaml": "^4.0.0", - "tar-fs": "^2.1.1" + "tar-fs": "^2.1.1", + "yaml-env-defaults": "^2.0.2" }, "devDependencies": { "chai": "~4.2.0", @@ -1181,8 +1181,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/log-symbols": { "version": "3.0.0", @@ -1889,6 +1888,15 @@ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, + "node_modules/yaml-env-defaults": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/yaml-env-defaults/-/yaml-env-defaults-2.0.2.tgz", + "integrity": "sha512-Ehl51pQZaRnUSsaqUGRHYUvEH+Kg4V9WNrcXtSbKPdav70NHdiXe/PALXXclycaLXx2N0wEG9qI2dhVu95F9SA==", + "dependencies": { + "js-yaml": "^4.1.0", + "lodash": "^4.17.21" + } + }, "node_modules/yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", @@ -2798,8 +2806,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "log-symbols": { "version": "3.0.0", @@ -3349,6 +3356,15 @@ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, + "yaml-env-defaults": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/yaml-env-defaults/-/yaml-env-defaults-2.0.2.tgz", + "integrity": "sha512-Ehl51pQZaRnUSsaqUGRHYUvEH+Kg4V9WNrcXtSbKPdav70NHdiXe/PALXXclycaLXx2N0wEG9qI2dhVu95F9SA==", + "requires": { + "js-yaml": "^4.1.0", + "lodash": "^4.17.21" + } + }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", diff --git a/package.json b/package.json index bcb6e9e..d6c3ae8 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ "homepage": "https://github.com/apocas/dockerode-compose#readme", "dependencies": { "dockerode": "^3.3.0", - "js-yaml": "^4.0.0", - "tar-fs": "^2.1.1" + "tar-fs": "^2.1.1", + "yaml-env-defaults": "^2.0.2" }, "devDependencies": { "chai": "~4.2.0", diff --git a/test/assets/wordpress_env.yml b/test/assets/wordpress_env.yml new file mode 100644 index 0000000..97e76f9 --- /dev/null +++ b/test/assets/wordpress_env.yml @@ -0,0 +1,30 @@ +version: "3.9" + +services: + db: + image: mysql:5.7 + volumes: + - db_data:/var/lib/mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: somewordpress + MYSQL_DATABASE: wordpress + MYSQL_USER: wordpress + MYSQL_PASSWORD: wordpress + + wordpress: + depends_on: + - db + image: wordpress:latest + ports: + - "8000:80" + restart: always + volumes_from: + - db:ro + environment: + WORDPRESS_DB_HOST: db:3306 + WORDPRESS_DB_USER: ${WORDPRESS_DB_USER} + WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD} + WORDPRESS_DB_NAME: wordpress +volumes: + db_data: {} \ No newline at end of file diff --git a/test/compose.js b/test/compose.js index af160d4..ed62a7f 100644 --- a/test/compose.js +++ b/test/compose.js @@ -1,5 +1,6 @@ const expect = require('chai').expect, assert = require('assert'); +const DockerodeCompose = require('../compose'); var compose = require('./spec_helper').compose; var compose_complex = require('./spec_helper').compose_complex; @@ -53,6 +54,35 @@ describe('compose', function () { }); }); + describe('#up with env', function () { + + const envMap = { + WORDPRESS_DB_USER: 'wordpress', + WORDPRESS_DB_PASSWORD: 'password' + }; + const getProperty = (key) => { + // without default value will be throwed error in case of missing + return envMap[key] || 'Default value'; + } + + var compose = new DockerodeCompose(docker, './test/assets/wordpress_env.yml', 'wordpress_env', getProperty); + it("should do compose up", function (done) { + this.timeout(60000); + (async () => { + var report = await compose.up(); + expect(report.services).to.be.ok; + done(); + })(); + }); + afterEach('clean up', function (done) { + this.timeout(60000); + (async () => { + await compose.down({ volumes: true }); + done(); + })(); + }); + }); + describe('#down', function () { beforeEach('bring up', function (done) { this.timeout(20000);