diff --git a/src/docker/templates/image.js b/src/docker/templates/image.js new file mode 100644 index 0000000..a64bd14 --- /dev/null +++ b/src/docker/templates/image.js @@ -0,0 +1,46 @@ +// npm packages +const fs = require('fs'); +const path = require('path'); + +// template name +exports.name = 'image'; + +// function to check if the template fits this recipe +exports.checkTemplate = async ({config}) => { + // if project has image field defined in config + try { + return config.image && config.image.length; + } catch (e) { + return false; + } +}; + +// function to execute current template +exports.executeTemplate = async ({config, username, tempDockerDir, folder, resultStream, util, docker}) => { + // build docker image + try { + const {image, imageFile} = config; + util.writeStatus(resultStream, {message: `Deploying project from image: ${image}..`, level: 'info'}); + + // import from tar if needed + if (imageFile && imageFile.length) { + util.writeStatus(resultStream, {message: `Importing image from file: ${imageFile}..`, level: 'info'}); + // get packed stream + const tarStream = fs.createReadStream(path.join(tempDockerDir, folder, imageFile)); + const importRes = await docker.daemon.loadImage(tarStream, {tag: image}); + util.logger.debug('Import result:', importRes); + } + + // start image + const container = await docker.start({image, username, folder, resultStream}); + util.logger.debug(container); + + // return new deployments + util.writeStatus(resultStream, {message: 'Deployment success!', deployments: [container], level: 'info'}); + resultStream.end(''); + } catch (e) { + util.logger.debug('build failed!', e); + util.writeStatus(resultStream, {message: e.error, error: e.error, log: e.log, level: 'error'}); + resultStream.end(''); + } +}; diff --git a/src/docker/templates/index.js b/src/docker/templates/index.js index 222afbc..7112e5a 100644 --- a/src/docker/templates/index.js +++ b/src/docker/templates/index.js @@ -7,6 +7,7 @@ const path = require('path'); const {extensionsFolder} = require('../../config'); // hard-coded templates +const imageTemplate = require('./image'); const composeTemplate = require('./compose'); const dockerfileTemplate = require('./dockerfile'); const nodeTemplate = require('./node'); @@ -23,5 +24,5 @@ module.exports = () => { return require(templatePath); }); - return [composeTemplate, dockerfileTemplate, nodeTemplate, nginxTemplate].concat(userTemplates); + return [imageTemplate, composeTemplate, dockerfileTemplate, nodeTemplate, nginxTemplate].concat(userTemplates); }; diff --git a/test/deploy.test.js b/test/deploy.test.js index 96da5d3..a87a918 100644 --- a/test/deploy.test.js +++ b/test/deploy.test.js @@ -17,6 +17,7 @@ const docker = require('../src/docker/docker'); const {initNetwork} = require('../src/docker/network'); // create tar streams +const streamDockerImage = tar.pack(path.join(__dirname, 'fixtures', 'docker-image-project')); const streamDocker = tar.pack(path.join(__dirname, 'fixtures', 'docker-project')); const streamNode = tar.pack(path.join(__dirname, 'fixtures', 'node-project')); const streamNodeLock = tar.pack(path.join(__dirname, 'fixtures', 'node-lock-project')); @@ -111,6 +112,52 @@ test('Should deploy simple docker project', async done => { done(); }); +test('Should deploy simple project from image and image tar', async done => { + const options = Object.assign(optionsBase, { + payload: streamDockerImage, + }); + + const response = await fastify.inject(options); + // parse result into lines + const result = response.payload + .split('\n') + .filter(l => l && l.length) + .map(line => JSON.parse(line)); + + // find deployments + const completeDeployments = result.find(it => it.deployments && it.deployments.length).deployments; + + // check response + expect(response.statusCode).toEqual(200); + expect(completeDeployments.length).toEqual(1); + expect(completeDeployments[0].Name.startsWith('/exo-test-image-')).toBeTruthy(); + + // check docker services + const allContainers = await docker.listContainers(); + const containerInfo = allContainers.find(c => c.Names.includes(completeDeployments[0].Name)); + const name = completeDeployments[0].Name.slice(1); + + expect(containerInfo).toBeDefined(); + expect(containerInfo.Labels['exoframe.deployment']).toEqual(name); + expect(containerInfo.Labels['exoframe.user']).toEqual('admin'); + expect(containerInfo.Labels['exoframe.project']).toEqual('test-image-project'); + expect(containerInfo.Labels['traefik.backend']).toEqual(`${name}.test`); + expect(containerInfo.Labels['traefik.docker.network']).toEqual('exoframe'); + expect(containerInfo.Labels['traefik.enable']).toEqual('true'); + expect(containerInfo.NetworkSettings.Networks.exoframe).toBeDefined(); + + const containerData = docker.getContainer(containerInfo.Id); + const container = await containerData.inspect(); + expect(container.NetworkSettings.Networks.exoframe.Aliases.includes('testimage')).toBeTruthy(); + expect(container.HostConfig.RestartPolicy).toMatchObject({Name: 'no', MaximumRetryCount: 0}); + + // cleanup + const instance = docker.getContainer(containerInfo.Id); + await instance.remove({force: true}); + + done(); +}); + test('Should deploy simple node project', async done => { const options = Object.assign(optionsBase, { payload: streamNode, diff --git a/test/fixtures/docker-image-project/Dockerfile b/test/fixtures/docker-image-project/Dockerfile new file mode 100644 index 0000000..abf700e --- /dev/null +++ b/test/fixtures/docker-image-project/Dockerfile @@ -0,0 +1,3 @@ +FROM busybox + +CMD ["sleep", "300"] diff --git a/test/fixtures/docker-image-project/exoframe.json b/test/fixtures/docker-image-project/exoframe.json new file mode 100644 index 0000000..ed8869e --- /dev/null +++ b/test/fixtures/docker-image-project/exoframe.json @@ -0,0 +1,8 @@ +{ + "name": "test-docker-image-deploy", + "hostname": "testimage", + "project": "test-image-project", + "restart": "no", + "image": "exo-test-image", + "imageFile": "image.tar" +} diff --git a/test/fixtures/docker-image-project/image.tar b/test/fixtures/docker-image-project/image.tar new file mode 100644 index 0000000..835046b Binary files /dev/null and b/test/fixtures/docker-image-project/image.tar differ