From 0d92b75922e32ce057e80bec4154d9d1a3d24e55 Mon Sep 17 00:00:00 2001 From: zaucy Date: Wed, 27 Jun 2018 22:24:54 -0700 Subject: [PATCH 1/2] docker-compose build support #11 --- index.js | 28 +++++++++++- test/build-test.Dockerfile | 3 ++ test/docker-compose-build.yml | 23 ++++++++++ test/index.js | 86 ++++++++++++++++++++++++++++++++++- 4 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 test/build-test.Dockerfile create mode 100644 test/docker-compose-build.yml diff --git a/index.js b/index.js index fb93154..7b5fe67 100644 --- a/index.js +++ b/index.js @@ -143,4 +143,30 @@ const run = function (container, command, options) { return execCompose(`run -T ${container} ${command}`, options); }; -module.exports = { up, kill, down, stop, rm, exec, run }; +/** + * Build command + * @param {string|string[]|object} service service name + * @param {object} options + * @param {string} options.cwd + * @param {boolean} [options.log] + * @param {?(string|string[])} [options.config] + * + * @return {object} std.out / std.err + */ +const build = function (service, options) { + let services = []; + + if (Array.isArray(service)) { + services = service; + } else if (typeof service === 'object') { + options = service; + } else { + services = [ service || '' ]; + } + + services = services.join(' ').trim(); + + return execCompose(`build ${services}`, options); +}; + +module.exports = { up, kill, down, stop, rm, exec, run, build }; diff --git a/test/build-test.Dockerfile b/test/build-test.Dockerfile new file mode 100644 index 0000000..fbc0740 --- /dev/null +++ b/test/build-test.Dockerfile @@ -0,0 +1,3 @@ +FROM alpine:3.7 + +RUN echo "hello build test" \ No newline at end of file diff --git a/test/docker-compose-build.yml b/test/docker-compose-build.yml new file mode 100644 index 0000000..806cf1b --- /dev/null +++ b/test/docker-compose-build.yml @@ -0,0 +1,23 @@ +version: '2' + +services: + build_test_1: + image: compose-test-build-image-1:test + build: + context: ./ + dockerfile: build-test.Dockerfile + build_test_2: + image: compose-test-build-image-2:test + build: + context: ./ + dockerfile: build-test.Dockerfile + build_test_3: + image: compose-test-build-image-3:test + build: + context: ./ + dockerfile: build-test.Dockerfile + build_test_4: + image: compose-test-build-image-4:test + build: + context: ./ + dockerfile: build-test.Dockerfile \ No newline at end of file diff --git a/test/index.js b/test/index.js index 4f6d7c0..9d81949 100644 --- a/test/index.js +++ b/test/index.js @@ -20,6 +20,29 @@ const isContainerRunning = async name => new Promise((resolve, reject) => { }); }); +const imageExists = async name => { + const images = await docker.listImages(); + + const foundImage = images.findIndex(imageInfo => imageInfo.RepoTags.includes(name)); + + return foundImage > -1; +}; + +const removeImagesStartingWith = async searchString => { + const images = await docker.listImages(); + + for (const image of images) { + for (const repoTag of image.RepoTags) { + if (repoTag.startsWith(searchString)) { + const dockerImage = docker.getImage(repoTag); + + console.log(`removing image ${repoTag} ${dockerImage.id || ''}`); + await dockerImage.remove(); + } + } + } +}; + test('ensure container gets started', async assert => { await compose.up({ cwd: path.join(__dirname), log: true }); @@ -97,7 +120,66 @@ test('ensure run and exec are working', async assert => { assert.end(); }); -test('teardown', assert => { +test('build single service', async assert => { + const opts = { + cwd: path.join(__dirname), + log: false, + config: 'docker-compose-build.yml' + }; + + await removeImagesStartingWith('compose-test-build-image'); + + await compose.build('build_test_1', opts); + + assert.true(await imageExists('compose-test-build-image-1:test')); + assert.false(await imageExists('compose-test-build-image-2:test')); + assert.false(await imageExists('compose-test-build-image-3:test')); + assert.false(await imageExists('compose-test-build-image-4:test')); + + await removeImagesStartingWith('compose-test-build-image'); + + assert.end(); +}); + +test('build multiple services', async assert => { + const opts = { + cwd: path.join(__dirname), + log: false, + config: 'docker-compose-build.yml' + }; + + await compose.build([ 'build_test_2', 'build_test_3' ], opts); + + assert.false(await imageExists('compose-test-build-image-1:test')); + assert.true(await imageExists('compose-test-build-image-2:test')); + assert.true(await imageExists('compose-test-build-image-3:test')); + assert.false(await imageExists('compose-test-build-image-4:test')); + + await removeImagesStartingWith('compose-test-build-image'); + + assert.end(); +}); + +test('build all services', async assert => { + const opts = { + cwd: path.join(__dirname), + log: false, + config: 'docker-compose-build.yml' + }; + + await compose.build(opts); + + assert.true(await imageExists('compose-test-build-image-1:test')); + assert.true(await imageExists('compose-test-build-image-2:test')); + assert.true(await imageExists('compose-test-build-image-3:test')); + assert.true(await imageExists('compose-test-build-image-4:test')); + + await removeImagesStartingWith('compose-test-build-image'); + + assert.end(); +}); + +test('teardown', async assert => { docker.listContainers((err, containers) => { if (err) { throw err; @@ -113,5 +195,7 @@ test('teardown', assert => { }); }); + await removeImagesStartingWith('compose-test-build-image'); + assert.end(); }); From dc38a6f52fdbfe0f67c1b30fe2ae3a89a7df1a72 Mon Sep 17 00:00:00 2001 From: zaucy Date: Thu, 28 Jun 2018 19:25:42 -0700 Subject: [PATCH 2/2] Changed build function names, fixed execCompose which was not resolving its promise, fixed issue with run/exec with the spawn change --- index.js | 90 +++++++++++++++++++++++++++++++++++---------------- test/index.js | 6 ++-- 2 files changed, 65 insertions(+), 31 deletions(-) diff --git a/index.js b/index.js index 9e5f23d..f196fa8 100644 --- a/index.js +++ b/index.js @@ -29,19 +29,31 @@ const configToArgs = config => { * @param {?object} [options.env] */ const execCompose = (command, args, options) => new Promise((resolve, reject) => { - const composeArgs = configToArgs(options.config); + const composeArgs = configToArgs(options.config).concat([ command ], args); const cwd = options.cwd; const env = options.env || null; - const childProc = childProcess.spawn('docker-compose', composeArgs.concat([ command ], args), { cwd, env }, (err, stdout, stderr) => { - if (err) { - reject(err); - } else { - resolve({ - err: stderr, - out: stdout - }); - } + const childProc = childProcess.spawn('docker-compose', composeArgs, { cwd, env }); + + childProc.on('error', err => { + reject(err); + }); + + const result = { + err: '', + out: '' + }; + + childProc.stdout.on('data', chunk => { + result.out += chunk.toString(); + }); + + childProc.stderr.on('data', chunk => { + result.err += chunk.toString(); + }); + + childProc.on('close', () => { + resolve(result); }); if (options.log) { @@ -118,7 +130,9 @@ const rm = function (options) { * @return {object} std.out / std.err */ const exec = function (container, command, options) { - return execCompose('exec', [ '-T', container, command ], options); + const args = command.split(/\s+/); + + return execCompose('exec', [ '-T', container ].concat(args), options); }; /** @@ -134,33 +148,53 @@ const exec = function (container, command, options) { * @return {object} std.out / std.err */ const run = function (container, command, options) { - return execCompose('run', [ '-T', container, command ], options); + const args = command.split(/\s+/); + + return execCompose('run', [ '-T', container ].concat(args), options); }; /** * Build command - * @param {string|string[]|object} service service name * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] + * @param {?object} [options.env] * * @return {object} std.out / std.err */ -const build = function (service, options) { - let services = []; - - if (Array.isArray(service)) { - services = service; - } else if (typeof service === 'object') { - options = service; - } else { - services = [ service || '' ]; - } - - services = services.join(' ').trim(); - - return execCompose(`build ${services}`, options); +const buildAll = function (options) { + return execCompose('build', [], options); }; -module.exports = { up, kill, down, stop, rm, exec, run, build }; +/** + * Build command + * @param {string[]} services list of service names + * @param {object} options + * @param {string} options.cwd + * @param {boolean} [options.log] + * @param {?(string|string[])} [options.config] + * @param {?object} [options.env] + * + * @return {object} std.out / std.err + */ +const buildMany = function (services, options) { + return execCompose('build', services, options); +}; + +/** + * Build command + * @param {string} service service name + * @param {object} options + * @param {string} options.cwd + * @param {boolean} [options.log] + * @param {?(string|string[])} [options.config] + * @param {?object} [options.env] + * + * @return {object} std.out / std.err + */ +const buildOne = function (service, options) { + return execCompose('build', [ service ], options); +}; + +module.exports = { up, kill, down, stop, rm, exec, run, buildAll, buildMany, buildOne }; diff --git a/test/index.js b/test/index.js index 9d81949..b59521b 100644 --- a/test/index.js +++ b/test/index.js @@ -129,7 +129,7 @@ test('build single service', async assert => { await removeImagesStartingWith('compose-test-build-image'); - await compose.build('build_test_1', opts); + await compose.buildOne('build_test_1', opts); assert.true(await imageExists('compose-test-build-image-1:test')); assert.false(await imageExists('compose-test-build-image-2:test')); @@ -148,7 +148,7 @@ test('build multiple services', async assert => { config: 'docker-compose-build.yml' }; - await compose.build([ 'build_test_2', 'build_test_3' ], opts); + await compose.buildMany([ 'build_test_2', 'build_test_3' ], opts); assert.false(await imageExists('compose-test-build-image-1:test')); assert.true(await imageExists('compose-test-build-image-2:test')); @@ -167,7 +167,7 @@ test('build all services', async assert => { config: 'docker-compose-build.yml' }; - await compose.build(opts); + await compose.buildAll(opts); assert.true(await imageExists('compose-test-build-image-1:test')); assert.true(await imageExists('compose-test-build-image-2:test'));