dockerode-compose
Advanced tools
Comparing version 1.2.2 to 1.3.1
@@ -30,2 +30,19 @@ const yaml = require('js-yaml'); | ||
async down(options) { | ||
var output = {}; | ||
try { | ||
output.file = this.file; | ||
output.services = await services.down(this.docker, this.projectName, this.recipe, output, options); | ||
output.networks = await networks.down(this.docker, this.projectName, this.recipe, output); | ||
if (options !== undefined) { | ||
if (options.volumes) { | ||
output.volumes = await volumes.down(this.docker, this.projectName, this.recipe, output); | ||
} | ||
} | ||
return output; | ||
} catch (e) { | ||
throw e; | ||
} | ||
} | ||
async up(options) { | ||
@@ -36,6 +53,6 @@ var output = {}; | ||
output.secrets = await secrets(this.docker, this.projectName, this.recipe, output); | ||
output.volumes = await volumes(this.docker, this.projectName, this.recipe, output); | ||
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(this.docker, this.projectName, this.recipe, output); | ||
output.services = await services(this.docker, this.projectName, this.recipe, output, options); | ||
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); | ||
return output; | ||
@@ -42,0 +59,0 @@ } catch (e) { |
@@ -5,6 +5,19 @@ var Dockerode = require('dockerode'); | ||
var docker = new Dockerode(); | ||
var compose = new DockerodeCompose(docker, './test/assets/wordpress.yml', 'wordpress'); | ||
var yamlFile = './test/assets/wordpress_original.yml' | ||
var projectName = 'wordpress' | ||
if (process.argv.length > 2) { | ||
if (process.argv[2] !== undefined) { | ||
yamlFile = process.argv[2] | ||
} | ||
if (process.argv[3] !== undefined) { | ||
projectName = process.argv[3] | ||
} | ||
} | ||
var compose = new DockerodeCompose(docker, yamlFile, projectName); | ||
(async () => { | ||
console.log(await compose.pull()); | ||
})(); |
@@ -5,4 +5,17 @@ var Dockerode = require('dockerode'); | ||
var docker = new Dockerode(); | ||
var compose = new DockerodeCompose(docker, './test/assets/wordpress_original.yml', 'wordpress'); | ||
var yamlFile = './test/assets/wordpress_original.yml' | ||
var projectName = 'wordpress' | ||
if (process.argv.length > 2) { | ||
if (process.argv[2] !== undefined) { | ||
yamlFile = process.argv[2] | ||
} | ||
if (process.argv[3] !== undefined) { | ||
projectName = process.argv[3] | ||
} | ||
} | ||
var compose = new DockerodeCompose(docker, yamlFile, projectName); | ||
(async () => { | ||
@@ -9,0 +22,0 @@ var state = await compose.up(); |
@@ -1,3 +0,18 @@ | ||
module.exports = async function (docker, projectName, recipe, output) { | ||
async function down(docker, projectName, recipe) { | ||
var networks = []; | ||
var networkNames = Object.keys(recipe.networks || { default: null }); | ||
for (var networkName of networkNames) { | ||
try { | ||
var network = await docker.getNetwork(projectName + '_' + networkName); | ||
} catch (e) {} | ||
try { | ||
await network.remove(); | ||
} catch (e) {} | ||
} | ||
return networks; | ||
} | ||
async function up(docker, projectName, recipe, output) { | ||
var networks = []; | ||
var networkNames = Object.keys(recipe.networks || []); | ||
@@ -8,7 +23,21 @@ for (var networkName of networkNames) { | ||
try { | ||
networks.push({ 'name': projectName + '_' + networkName, 'network': await docker.createNetwork({ 'Name': projectName + '_' + networkName, 'CheckDuplicate': true }) }); | ||
networks.push({ | ||
name: projectName + '_' + networkName, | ||
network: await docker.createNetwork({ | ||
Name: projectName + '_' + networkName, | ||
CheckDuplicate: true, | ||
}), | ||
}); | ||
} catch (err) { | ||
if (err.statusCode == 409 && err.json.message.includes('already exists')) { | ||
let returnedNetwork = await docker.listNetworks({ 'filters': { 'name': [projectName + '_' + networkName] } }); | ||
networks.push({ 'name': projectName + '_' + networkName, 'network': await docker.getNetwork(returnedNetwork[0].Id) }); | ||
if ( | ||
err.statusCode == 409 && | ||
err.json.message.includes('already exists') | ||
) { | ||
let returnedNetwork = await docker.listNetworks({ | ||
filters: { name: [projectName + '_' + networkName] }, | ||
}); | ||
networks.push({ | ||
name: projectName + '_' + networkName, | ||
network: await docker.getNetwork(returnedNetwork[0].Id), | ||
}); | ||
} else { | ||
@@ -18,14 +47,20 @@ throw err; | ||
} | ||
continue | ||
continue; | ||
} | ||
if (network.external === true) continue; | ||
var opts = { | ||
'Name': projectName + '_' + networkName, | ||
'Driver': network.driver, | ||
'DriverOpts': network.driver_opts, | ||
'Labels': network.labels, | ||
'Attachable': network.attachable, | ||
'EnableIPv6': network.enable_ipv6, | ||
'Internal': network.internal, | ||
'CheckDuplicate': true | ||
Name: projectName + '_' + networkName, | ||
Driver: network.driver, | ||
DriverOpts: network.driver_opts, | ||
Labels: { | ||
...network.labels, | ||
...{ | ||
'com.docker.compose.network': 'default', | ||
'com.docker.compose.project': projectName, | ||
}, | ||
}, | ||
Attachable: network.attachable, | ||
EnableIPv6: network.enable_ipv6, | ||
Internal: network.internal, | ||
CheckDuplicate: true, | ||
}; | ||
@@ -37,11 +72,11 @@ if (network.name !== undefined) { | ||
opts.IPAM = { | ||
'Driver': network.ipam.driver, | ||
'Options': network.ipam.options | ||
Driver: network.ipam.driver, | ||
Options: network.ipam.options, | ||
}; | ||
if (network.ipam.config !== undefined) { | ||
opts.IPAM['Config'] = { | ||
'Subnet': network.ipam.config.subnet, | ||
'IPRange': network.ipam.config.ip_range, | ||
'Gateway': network.ipam.config.gateway, | ||
'AuxAddress': network.ipam.config.aux_addresses | ||
Subnet: network.ipam.config.subnet, | ||
IPRange: network.ipam.config.ip_range, | ||
Gateway: network.ipam.config.gateway, | ||
AuxAddress: network.ipam.config.aux_addresses, | ||
}; | ||
@@ -51,3 +86,6 @@ } | ||
//if exists we have to compare with the existing network | ||
networks.push({ 'name': projectName + '_' + networkName, 'network': await docker.createNetwork(opts) }); | ||
networks.push({ | ||
name: projectName + '_' + networkName, | ||
network: await docker.createNetwork(opts), | ||
}); | ||
} | ||
@@ -57,7 +95,21 @@ | ||
try { | ||
networks.push({ 'name': projectName + '_default', 'network': await docker.createNetwork({ 'Name': projectName + '_default', 'CheckDuplicate': true }) }); | ||
networks.push({ | ||
name: projectName + '_default', | ||
network: await docker.createNetwork({ | ||
Name: projectName + '_default', | ||
CheckDuplicate: true, | ||
}), | ||
}); | ||
} catch (err) { | ||
if (err.statusCode == 409 && err.json.message.includes('already exists')) { | ||
let returnedNetwork = await docker.listNetworks({ 'filters': { 'name': [projectName + '_default'] } }); | ||
networks.push({ 'name': projectName + '_' + networkName, 'network': await docker.getNetwork(returnedNetwork[0].Id) }); | ||
if ( | ||
err.statusCode == 409 && | ||
err.json.message.includes('already exists') | ||
) { | ||
let returnedNetwork = await docker.listNetworks({ | ||
filters: { name: [projectName + '_default'] }, | ||
}); | ||
networks.push({ | ||
name: projectName + '_' + networkName, | ||
network: await docker.getNetwork(returnedNetwork[0].Id), | ||
}); | ||
} else { | ||
@@ -70,1 +122,6 @@ throw err; | ||
} | ||
module.exports = { | ||
down, | ||
up, | ||
}; |
@@ -7,6 +7,23 @@ const tools = require('./tools'); | ||
module.exports = async function (docker, projectName, recipe, output, options) { | ||
async function down(docker, projectName, recipe, output, options) { | ||
var services = []; | ||
var serviceNames = tools.sortServices(recipe); | ||
for (var serviceName of serviceNames) { | ||
let container = docker.getContainer(projectName + '_' + serviceName + '_1'); | ||
try { | ||
await container.stop(); | ||
} catch (e) {} | ||
try { | ||
await container.remove(); | ||
} catch (e) {} | ||
} | ||
return services; | ||
} | ||
async function up(docker, projectName, recipe, output, options) { | ||
var services = []; | ||
var serviceNames = tools.sortServices(recipe); | ||
for (var serviceName of serviceNames) { | ||
var pathScope = {}; | ||
@@ -18,7 +35,6 @@ pathScope.file = output.file; | ||
if (service.extends.service !== undefined) { | ||
service = extendsServices(service, recipe, pathScope) | ||
service = extendsServices(service, recipe, pathScope); | ||
} else { | ||
throw new Error('Service key in extends is required!'); | ||
} | ||
} | ||
@@ -39,5 +55,6 @@ | ||
if (service.build.context !== undefined) { | ||
var buildContextPath = path.resolve(path.join(absolutePath, service.build.context)); | ||
var buildContextPath = path.resolve( | ||
path.join(absolutePath, service.build.context) | ||
); | ||
if (fs.existsSync(buildContextPath)) { | ||
if (service.build.args !== undefined) { | ||
@@ -47,3 +64,3 @@ var out = {}; | ||
for (let arg_line of service.build.args) { | ||
var arg = arg_line.split("="); | ||
var arg = arg_line.split('='); | ||
out[arg[0]] = arg[1]; | ||
@@ -86,3 +103,5 @@ } | ||
// RE ARRAGE the function "convertSizeStringToByteValue" to a generic one | ||
obj['shmsize'] = servicesTools.convertSizeStringToByteValue([{ 'path': '', 'rate': service.build.shm_size }]).Rate; | ||
obj['shmsize'] = servicesTools.convertSizeStringToByteValue([ | ||
{ path: '', rate: service.build.shm_size }, | ||
]).Rate; | ||
} | ||
@@ -95,8 +114,22 @@ | ||
if (service.build.dockerfile === undefined) { | ||
await servicesTools.buildDockerImage(docker, buildContextPath, obj, null, options); | ||
await servicesTools.buildDockerImage( | ||
docker, | ||
buildContextPath, | ||
obj, | ||
null, | ||
options | ||
); | ||
} else { | ||
await servicesTools.buildDockerImage(docker, buildContextPath, obj, service.build.dockerfile, options); | ||
await servicesTools.buildDockerImage( | ||
docker, | ||
buildContextPath, | ||
obj, | ||
service.build.dockerfile, | ||
options | ||
); | ||
} | ||
} else { | ||
throw new Error(`build path ${buildContextPath} either does not exist, is not accessible, or is not a valid URL.`); | ||
throw new Error( | ||
`build path ${buildContextPath} either does not exist, is not accessible, or is not a valid URL.` | ||
); | ||
} | ||
@@ -107,7 +140,17 @@ } else { | ||
} else { | ||
var dockerFilePath = path.resolve(path.join(absolutePath, service.build)); | ||
var dockerFilePath = path.resolve( | ||
path.join(absolutePath, service.build) | ||
); | ||
if (fs.existsSync(dockerFilePath)) { | ||
await servicesTools.buildDockerImage(docker, dockerFilePath, obj, null, options); | ||
await servicesTools.buildDockerImage( | ||
docker, | ||
dockerFilePath, | ||
obj, | ||
null, | ||
options | ||
); | ||
} else { | ||
throw new Error(`build path ${dockerFilePath} either does not exist, is not accessible, or is not a valid URL.`); | ||
throw new Error( | ||
`build path ${dockerFilePath} either does not exist, is not accessible, or is not a valid URL.` | ||
); | ||
} | ||
@@ -118,10 +161,13 @@ } | ||
var opts = { | ||
'name': projectName + '_' + serviceName, | ||
'Image': service.image, | ||
'HostConfig': servicesTools.buildHostConfig(service, recipe), | ||
'Env': servicesTools.buildEnvVars(service), | ||
'NetworkingConfig': { | ||
'EndpointsConfig': { | ||
} | ||
} | ||
name: projectName + '_' + serviceName + '_1', | ||
Image: service.image, | ||
HostConfig: servicesTools.buildHostConfig(projectName, service, recipe), | ||
Env: servicesTools.buildEnvVars(service), | ||
NetworkingConfig: { | ||
EndpointsConfig: {}, | ||
}, | ||
Labels: { | ||
'com.docker.compose.project': projectName, | ||
'com.docker.compose.service': serviceName, | ||
}, | ||
}; | ||
@@ -132,8 +178,7 @@ | ||
} else { | ||
opts.HostConfig.NetworkMode = projectName + '_default' | ||
opts.NetworkingConfig.EndpointsConfig[projectName + '_default'] = { | ||
'IPAMConfig': {}, | ||
'Links': [], | ||
'Aliases': [ | ||
serviceName, | ||
] | ||
IPAMConfig: null, | ||
Links: null, | ||
Aliases: [serviceName], | ||
}; | ||
@@ -173,8 +218,24 @@ } | ||
opts.StopTimeout = service.stop_grace_period; | ||
} else if (service.stop_grace_period.includes('m') && service.stop_grace_period.includes('s')) { | ||
let minutes = parseInt(service.stop_grace_period.substring(0, service.stop_grace_period.indexOf('m'))); | ||
let seconds = parseInt(service.stop_grace_period.substring(service.stop_grace_period.indexOf('m') + 1, service.stop_grace_period.indexOf('s'))); | ||
opts.StopTimeout = (minutes * 60) + seconds; | ||
} else if ( | ||
service.stop_grace_period.includes('m') && | ||
service.stop_grace_period.includes('s') | ||
) { | ||
let minutes = parseInt( | ||
service.stop_grace_period.substring( | ||
0, | ||
service.stop_grace_period.indexOf('m') | ||
) | ||
); | ||
let seconds = parseInt( | ||
service.stop_grace_period.substring( | ||
service.stop_grace_period.indexOf('m') + 1, | ||
service.stop_grace_period.indexOf('s') | ||
) | ||
); | ||
opts.StopTimeout = minutes * 60 + seconds; | ||
} else { | ||
opts.StopTimeout = service.stop_grace_period.substring(0, service.stop_grace_period.length - 2); | ||
opts.StopTimeout = service.stop_grace_period.substring( | ||
0, | ||
service.stop_grace_period.length - 2 | ||
); | ||
} | ||
@@ -219,6 +280,12 @@ } | ||
healthcheck.Test = service.healthcheck.test; | ||
healthcheck.Interval = convertFancyDurationToMs(service.healthcheck.interval); | ||
healthcheck.Timeout = convertFancyDurationToMs(service.healthcheck.timeout); | ||
healthcheck.Interval = convertFancyDurationToMs( | ||
service.healthcheck.interval | ||
); | ||
healthcheck.Timeout = convertFancyDurationToMs( | ||
service.healthcheck.timeout | ||
); | ||
healthcheck.Retries = service.healthcheck.retries; | ||
healthcheck.StartPeriod = convertFancyDurationToMs(service.healthcheck.start_period); | ||
healthcheck.StartPeriod = convertFancyDurationToMs( | ||
service.healthcheck.start_period | ||
); | ||
opts.Healthcheck = healthcheck; | ||
@@ -242,9 +309,13 @@ } | ||
let networkNames = Object.keys(networksToAttach[0]); | ||
await findNetwork(output, networkNames[0]).disconnect({ 'Container': container.id }); | ||
await findNetwork(output, networkNames[0]).disconnect({ | ||
Container: container.id, | ||
}); | ||
let networksToAttachSorted = tools.sortNetworksToAttach(networksToAttach); | ||
for (var networkToAttach of networksToAttachSorted) { | ||
let networkName = Object.keys(networkToAttach); | ||
await findNetwork(output, networkName).connect({ 'Container': container.id, 'EndpointConfig': networkToAttach[networkName] }); | ||
await findNetwork(output, networkName).connect({ | ||
Container: container.id, | ||
EndpointConfig: networkToAttach[networkName], | ||
}); | ||
} | ||
} | ||
@@ -257,9 +328,7 @@ await container.start(); | ||
var findNetwork = function (output, name) { | ||
for (var network of output.networks) { | ||
if (network.name == name) | ||
return network.network; | ||
if (network.name == name) return network.network; | ||
} | ||
} | ||
}; | ||
@@ -272,10 +341,11 @@ var convertFancyDurationToMs = function (value) { | ||
let minutes = parseInt(value.substring(0, value.indexOf('m'))); | ||
let seconds = parseInt(value.substring(value.indexOf('m') + 1, value.indexOf('s'))); | ||
return ((minutes * 60) + seconds) * 1000 * 1000000; | ||
let seconds = parseInt( | ||
value.substring(value.indexOf('m') + 1, value.indexOf('s')) | ||
); | ||
return (minutes * 60 + seconds) * 1000 * 1000000; | ||
} else { | ||
return parseInt(value.substring(0, value.length - 2)) * 1000 * 1000000; | ||
} | ||
} | ||
}; | ||
// https://github.com/compose-spec/compose-spec/blob/master/spec.md#extends | ||
@@ -286,12 +356,32 @@ var extendsServices = function (service, recipe, pathScope) { | ||
// EXTENDS OF THE SAME RECIPE | ||
return buildExtendsService(service, service.extends.service, recipe, pathScope); | ||
return buildExtendsService( | ||
service, | ||
service.extends.service, | ||
recipe, | ||
pathScope | ||
); | ||
} 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')); | ||
return buildExtendsService(service, service.extends.service, extendsRecipe, pathScope); | ||
var extendsRecipe = yaml.load( | ||
fs.readFileSync( | ||
path.resolve(path.join(absolutePath, service.extends.file)), | ||
'utf8' | ||
) | ||
); | ||
return buildExtendsService( | ||
service, | ||
service.extends.service, | ||
extendsRecipe, | ||
pathScope | ||
); | ||
} | ||
} | ||
}; | ||
var buildExtendsService = function (service, extendsServiceName, recipe, pathScope) { | ||
var buildExtendsService = function ( | ||
service, | ||
extendsServiceName, | ||
recipe, | ||
pathScope | ||
) { | ||
var extend = false; | ||
@@ -316,3 +406,3 @@ var extendsRecipeServiceNames = Object.keys(recipe.services); | ||
if (key != 'extends') { | ||
mergingService(key, service, oldService) | ||
mergingService(key, service, oldService); | ||
} | ||
@@ -322,6 +412,7 @@ } | ||
var absolutePath = path.dirname(pathScope.file); | ||
pathScope.file = path.resolve(path.join(absolutePath, oldService.extends.file)) | ||
pathScope.file = path.resolve( | ||
path.join(absolutePath, oldService.extends.file) | ||
); | ||
} | ||
if (extend) | ||
service = extendsServices(service, recipe, pathScope); | ||
if (extend) service = extendsServices(service, recipe, pathScope); | ||
@@ -332,3 +423,3 @@ return service; | ||
throw new Error('Extends service not found'); | ||
} | ||
}; | ||
@@ -349,3 +440,3 @@ // https://github.com/compose-spec/compose-spec/blob/master/spec.md#restrictions | ||
} | ||
} | ||
}; | ||
@@ -360,10 +451,21 @@ // https://github.com/compose-spec/compose-spec/blob/master/spec.md#merging-service-definitions | ||
'extra_hosts', | ||
'ulimits' | ||
'ulimits', | ||
]; | ||
var objectMappings = { | ||
'build': { 'args': '', 'labels': '', 'extra_hosts': '', }, | ||
'deploy': { 'labels': '', 'update_config': '', 'rollback_config': '', 'restart_policy': '', 'resources': { 'limits': '' } }, | ||
'blkio_config': { 'device_read_bps': '', 'device_read_iops': '', 'device_write_bps': '', 'device_write_iops': '' }, | ||
'logging': { 'options': '' } | ||
} | ||
build: { args: '', labels: '', extra_hosts: '' }, | ||
deploy: { | ||
labels: '', | ||
update_config: '', | ||
rollback_config: '', | ||
restart_policy: '', | ||
resources: { limits: '' }, | ||
}, | ||
blkio_config: { | ||
device_read_bps: '', | ||
device_read_iops: '', | ||
device_write_bps: '', | ||
device_write_iops: '', | ||
}, | ||
logging: { options: '' }, | ||
}; | ||
var sequences = [ | ||
@@ -378,13 +480,18 @@ 'cap_add', | ||
//'secrets', // not fully implemented yet | ||
'security_opt' | ||
] | ||
'security_opt', | ||
]; | ||
var objectSequences = { | ||
'deploy': { | ||
'placement': { 'constraints': '', 'preferences': '' }, | ||
'reservations': { 'generic_resources': '' } | ||
} | ||
} | ||
deploy: { | ||
placement: { constraints: '', preferences: '' }, | ||
reservations: { generic_resources: '' }, | ||
}, | ||
}; | ||
// https://github.com/compose-spec/compose-spec/blob/master/spec.md#mappings - MAPPINGS | ||
if (key == 'build' || key == 'deploy' || key == 'blkio_config' || key == 'logging') { | ||
if ( | ||
key == 'build' || | ||
key == 'deploy' || | ||
key == 'blkio_config' || | ||
key == 'logging' | ||
) { | ||
// one object level missing in deploy resources | ||
@@ -418,4 +525,11 @@ var objectMappingsKeys = Object.keys(objectMappings[key]); | ||
for (let serviceLine of service[key]) { | ||
if (serviceLine.split('=')[0] == oldServiceLine.split('=')[0] || serviceLine.split(':')[0] == oldServiceLine.split(':')[0]) { | ||
service[key].splice(service[key].indexOf(serviceLine), 1, oldServiceLine) | ||
if ( | ||
serviceLine.split('=')[0] == oldServiceLine.split('=')[0] || | ||
serviceLine.split(':')[0] == oldServiceLine.split(':')[0] | ||
) { | ||
service[key].splice( | ||
service[key].indexOf(serviceLine), | ||
1, | ||
oldServiceLine | ||
); | ||
} | ||
@@ -431,3 +545,8 @@ } | ||
// https://github.com/compose-spec/compose-spec/blob/master/spec.md#sequences - SEQUENCES | ||
} else if (key == 'dns' || key == 'dns_search' || key == 'env_file' || key == 'tmpfs') { | ||
} else if ( | ||
key == 'dns' || | ||
key == 'dns_search' || | ||
key == 'env_file' || | ||
key == 'tmpfs' | ||
) { | ||
if (Array.isArray(oldService[key]) || Array.isArray(service[key])) { | ||
@@ -447,3 +566,3 @@ if (!Array.isArray(service[key])) { | ||
service[key] = []; | ||
service[key].push(service[key]) | ||
service[key].push(service[key]); | ||
service[key].push(oldService[key]); | ||
@@ -470,2 +589,7 @@ } | ||
} | ||
} | ||
}; | ||
module.exports = { | ||
down, | ||
up, | ||
}; |
@@ -7,3 +7,3 @@ const fs = require('fs'); | ||
module.exports = { | ||
'buildPorts': function (servicePorts, output) { | ||
buildPorts: function (servicePorts, output) { | ||
var ports = {}; | ||
@@ -14,3 +14,5 @@ if (typeof servicePorts[0] === 'object') { | ||
for (let port of servicePorts) { | ||
ports[port.target + '/' + port.protocol] = [{ 'HostPort': port.published.toString() }]; | ||
ports[port.target + '/' + port.protocol] = [ | ||
{ HostPort: port.published.toString() }, | ||
]; | ||
} | ||
@@ -30,12 +32,19 @@ output['PortBindings'] = ports; | ||
let split_port_split0_array = []; | ||
split_port_split0_array = fillPortArray(parseInt(split_port_split0[0]), parseInt(split_port_split0[1])); | ||
split_port_split0_array = fillPortArray( | ||
parseInt(split_port_split0[0]), | ||
parseInt(split_port_split0[1]) | ||
); | ||
let split_port_split1 = port_split[1].split('-'); | ||
let split_port_split1_array = []; | ||
split_port_split1_array = fillPortArray(parseInt(split_port_split1[0]), parseInt(split_port_split1[1])); | ||
split_port_split1_array = fillPortArray( | ||
parseInt(split_port_split1[0]), | ||
parseInt(split_port_split1[1]) | ||
); | ||
for (let index in split_port_split0_array) { | ||
ports[split_port_split1_array[index] + '/tcp'] = [{ 'HostPort': split_port_split0_array[index].toString() }]; | ||
ports[split_port_split1_array[index] + '/tcp'] = [ | ||
{ HostPort: split_port_split0_array[index].toString() }, | ||
]; | ||
} | ||
} else if (port_split[0].includes('-')) { | ||
@@ -46,15 +55,11 @@ // "3000-3005" | ||
for (let i = split_port_split[0]; i <= split_port_split[1]; i++) { | ||
ports[port_split[1] + '/tcp'].push({ 'HostPort': i.toString() }); | ||
ports[port_split[1] + '/tcp'].push({ HostPort: i.toString() }); | ||
} | ||
} else if (port_split[1].includes('/')) { | ||
// "6060:6060/udp" | ||
ports[port_split[1]] = [{ 'HostPort': port_split[0] }]; | ||
ports[port_split[1]] = [{ HostPort: port_split[0] }]; | ||
} else { | ||
// "8000:8000" | ||
ports[port_split[1] + '/tcp'] = [{ 'HostPort': port_split[0] }]; | ||
ports[port_split[1] + '/tcp'] = [{ HostPort: port_split[0] }]; | ||
} | ||
} else if (port_split.length == 3) { | ||
@@ -66,22 +71,33 @@ // "x.x.x.x:xxxx:xxxx" | ||
let split_port_split1_array = []; | ||
split_port_split1_array = fillPortArray(parseInt(split_port_split1[0]), parseInt(split_port_split1[1])); | ||
split_port_split1_array = fillPortArray( | ||
parseInt(split_port_split1[0]), | ||
parseInt(split_port_split1[1]) | ||
); | ||
let split_port_split2 = port_split[2].split('-'); | ||
let split_port_split2_array = []; | ||
split_port_split2_array = fillPortArray(parseInt(split_port_split2[0]), parseInt(split_port_split2[1])); | ||
split_port_split2_array = fillPortArray( | ||
parseInt(split_port_split2[0]), | ||
parseInt(split_port_split2[1]) | ||
); | ||
for (let index in split_port_split1_array) { | ||
ports[split_port_split2_array[index] + '/tcp'] = [{ 'HostPort': split_port_split1_array[index].toString(), 'HostIp': port_split[0] }]; | ||
ports[split_port_split2_array[index] + '/tcp'] = [ | ||
{ | ||
HostPort: split_port_split1_array[index].toString(), | ||
HostIp: port_split[0], | ||
}, | ||
]; | ||
} | ||
} else if (port_split[1] == '') { | ||
// "127.0.0.1::5000 | ||
ports[port_split[2] + '/tcp'] = [{ 'HostPort': port_split[2], 'HostIp': port_split[0] }]; | ||
ports[port_split[2] + '/tcp'] = [ | ||
{ HostPort: port_split[2], HostIp: port_split[0] }, | ||
]; | ||
} else { | ||
// "127.0.0.1:8001:8001" | ||
ports[port_split[2] + '/tcp'] = [{ 'HostPort': port_split[1], 'HostIp': port_split[0] }]; | ||
ports[port_split[2] + '/tcp'] = [ | ||
{ HostPort: port_split[1], HostIp: port_split[0] }, | ||
]; | ||
} | ||
} else { | ||
@@ -93,9 +109,7 @@ // "xxxx" | ||
for (let i = split_port_split[0]; i <= split_port_split[1]; i++) { | ||
ports[i + '/tcp'] = [{ 'HostPort': i.toString() }]; | ||
ports[i + '/tcp'] = [{ HostPort: i.toString() }]; | ||
} | ||
} else { | ||
// "3000" | ||
ports[port + '/tcp'] = [{ 'HostPort': port }]; | ||
ports[port + '/tcp'] = [{ HostPort: port }]; | ||
} | ||
@@ -109,5 +123,5 @@ } | ||
//ToDo: complete the compose specification | ||
'buildHostConfig': function (service, recipe) { | ||
buildHostConfig: function (projectName, service, recipe) { | ||
var output = { | ||
'RestartPolicy': { 'Name': service.restart } | ||
RestartPolicy: { Name: service.restart }, | ||
}; | ||
@@ -119,3 +133,3 @@ | ||
var svf = recipe.services[vf[0]]; | ||
this.buildVolumesHostconfig(svf.volumes, output, vf[1]); | ||
this.buildVolumesHostconfig(projectName, svf.volumes, output, vf[1]); | ||
} | ||
@@ -125,3 +139,3 @@ } | ||
if (service.volumes !== undefined) { | ||
this.buildVolumesHostconfig(service.volumes, output); | ||
this.buildVolumesHostconfig(projectName, service.volumes, output); | ||
} | ||
@@ -292,21 +306,30 @@ | ||
weight_device[0]['Path'] = service.blkio_config.weight_device[0].path; | ||
weight_device[0]['Weight'] = service.blkio_config.weight_device[0].weight; | ||
weight_device[0]['Weight'] = | ||
service.blkio_config.weight_device[0].weight; | ||
output.BlkioWeightDevice = weight_device; | ||
} | ||
if (service.blkio_config.device_read_bps !== undefined) { | ||
output.BlkioDeviceReadBps = convertSizeStringToByteValue(service.blkio_config.device_read_bps); | ||
output.BlkioDeviceReadBps = convertSizeStringToByteValue( | ||
service.blkio_config.device_read_bps | ||
); | ||
} | ||
if (service.blkio_config.device_read_iops !== undefined) { | ||
let device_read_iops = [{}]; | ||
device_read_iops[0]['Path'] = service.blkio_config.device_read_iops[0].path; | ||
device_read_iops[0]['Rate'] = service.blkio_config.device_read_iops[0].rate; | ||
device_read_iops[0]['Path'] = | ||
service.blkio_config.device_read_iops[0].path; | ||
device_read_iops[0]['Rate'] = | ||
service.blkio_config.device_read_iops[0].rate; | ||
output.BlkioDeviceReadIOps = device_read_iops; | ||
} | ||
if (service.blkio_config.device_write_bps !== undefined) { | ||
output.BlkioDeviceWriteBps = convertSizeStringToByteValue(service.blkio_config.device_write_bps); | ||
output.BlkioDeviceWriteBps = convertSizeStringToByteValue( | ||
service.blkio_config.device_write_bps | ||
); | ||
} | ||
if (service.blkio_config.device_write_iops !== undefined) { | ||
let device_write_iops = [{}]; | ||
device_write_iops[0]['Path'] = service.blkio_config.device_write_iops[0].path; | ||
device_write_iops[0]['Rate'] = service.blkio_config.device_write_iops[0].rate; | ||
device_write_iops[0]['Path'] = | ||
service.blkio_config.device_write_iops[0].path; | ||
device_write_iops[0]['Rate'] = | ||
service.blkio_config.device_write_iops[0].rate; | ||
output.BlkioDeviceWriteIOps = device_write_iops; | ||
@@ -324,3 +347,3 @@ } | ||
'buildVolumesHostconfig': function (volumes, output, type) { | ||
buildVolumesHostconfig: function (projectName, volumes, output, type) { | ||
if (output['Binds'] === undefined) { | ||
@@ -331,3 +354,3 @@ output['Binds'] = []; | ||
if (typeof volume === 'string' || volume instanceof String) { | ||
var aux = volume; | ||
var aux = projectName + '_' + volume; | ||
if (type == 'ro') { | ||
@@ -340,3 +363,4 @@ aux += ':ro'; | ||
if (volume.source && volume.target) { | ||
volumestr += volume.source + ':' + volume.target + ':'; | ||
volumestr += | ||
projectName + '_' + volume.source + ':' + volume.target + ':'; | ||
} | ||
@@ -358,3 +382,3 @@ if (volume.read_only || type == 'ro') { | ||
'buildVolumes': function (volumes, opts) { | ||
buildVolumes: function (volumes, opts) { | ||
if (opts['Volumes'] === undefined) { | ||
@@ -375,3 +399,3 @@ opts['Volumes'] = {}; | ||
'buildEnvVars': function (service) { | ||
buildEnvVars: function (service) { | ||
var output = []; | ||
@@ -404,3 +428,3 @@ | ||
'buildNetworks': function (serviceNetworks, networksToAttach) { | ||
buildNetworks: function (serviceNetworks, networksToAttach) { | ||
if (Array.isArray(serviceNetworks)) { | ||
@@ -410,11 +434,13 @@ for (let index = 0; index < serviceNetworks.length; index++) { | ||
let networkTemplate = { | ||
'NetworkingConfig': { | ||
'EndpointsConfig': { | ||
} | ||
} | ||
NetworkingConfig: { | ||
EndpointsConfig: {}, | ||
}, | ||
}; | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName] = {}; | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName]['Aliases'] = [serviceName]; | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName][ | ||
'Aliases' | ||
] = [serviceName]; | ||
if (index === 0) | ||
opts.NetworkingConfig.EndpointsConfig = networkTemplate.NetworkingConfig.EndpointsConfig; | ||
opts.NetworkingConfig.EndpointsConfig = | ||
networkTemplate.NetworkingConfig.EndpointsConfig; | ||
@@ -429,28 +455,42 @@ networksToAttach.push(networkTemplate.NetworkingConfig.EndpointsConfig); | ||
let networkTemplate = { | ||
'NetworkingConfig': { | ||
'EndpointsConfig': { | ||
} | ||
} | ||
NetworkingConfig: { | ||
EndpointsConfig: {}, | ||
}, | ||
}; | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName] = {}; | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName]['IPAMConfig'] = {}; | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName][ | ||
'IPAMConfig' | ||
] = {}; | ||
if (network.aliases !== undefined) { | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName]['Aliases'] = network.aliases; | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName][ | ||
'Aliases' | ||
] = network.aliases; | ||
} | ||
if (network.ipv4_address !== undefined) { | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName].IPAMConfig['IPv4Address'] = network.ipv4_address; | ||
networkTemplate.NetworkingConfig.EndpointsConfig[ | ||
networkName | ||
].IPAMConfig['IPv4Address'] = network.ipv4_address; | ||
} | ||
if (network.ipv6_address !== undefined) { | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName].IPAMConfig['IPv6Address'] = network.ipv6_address; | ||
networkTemplate.NetworkingConfig.EndpointsConfig[ | ||
networkName | ||
].IPAMConfig['IPv6Address'] = network.ipv6_address; | ||
} | ||
if (network.link_local_ips !== undefined) { | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName].IPAMConfig['LinkLocalIPs'] = network.link_local_ips; | ||
networkTemplate.NetworkingConfig.EndpointsConfig[ | ||
networkName | ||
].IPAMConfig['LinkLocalIPs'] = network.link_local_ips; | ||
} | ||
if (network.priority !== undefined) { | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName].priority = network.priority; | ||
networkTemplate.NetworkingConfig.EndpointsConfig[ | ||
networkName | ||
].priority = network.priority; | ||
} else { | ||
networkTemplate.NetworkingConfig.EndpointsConfig[networkName].priority = 0; | ||
networkTemplate.NetworkingConfig.EndpointsConfig[ | ||
networkName | ||
].priority = 0; | ||
} | ||
if (index === 0) { | ||
opts.NetworkingConfig.EndpointsConfig = networkTemplate.NetworkingConfig.EndpointsConfig; | ||
opts.NetworkingConfig.EndpointsConfig = | ||
networkTemplate.NetworkingConfig.EndpointsConfig; | ||
} | ||
@@ -463,3 +503,3 @@ networksToAttach.push(networkTemplate.NetworkingConfig.EndpointsConfig); | ||
// TODO: OPTIMIZE! | ||
'convertSizeStringToByteValue': function (obj) { | ||
convertSizeStringToByteValue: function (obj) { | ||
let rate = obj[0].rate.toLowerCase(); | ||
@@ -497,5 +537,8 @@ let new_obj = [{}]; | ||
'buildEnvVarsFromFile': function (env_file_path, output) { | ||
buildEnvVarsFromFile: function (env_file_path, output) { | ||
// Each line in an env file MUST be in `VAR=VAL` format. | ||
let env_file = fs.readFileSync(env_file_path, 'utf8').toString().split('\n'); | ||
let env_file = fs | ||
.readFileSync(env_file_path, 'utf8') | ||
.toString() | ||
.split('\n'); | ||
for (let env_line of env_file) { | ||
@@ -513,14 +556,25 @@ // Lines beginning with `#` MUST be ignored. Blank lines MUST also be ignored. | ||
'fillPortArray': function (start, end) { | ||
return Array(end - start + 1).fill().map((_, idx) => start + idx); | ||
fillPortArray: function (start, end) { | ||
return Array(end - start + 1) | ||
.fill() | ||
.map((_, idx) => start + idx); | ||
}, | ||
'buildDockerImage': async function (docker, buildPath, obj, dockerfile, options) { | ||
buildDockerImage: async function ( | ||
docker, | ||
buildPath, | ||
obj, | ||
dockerfile, | ||
options | ||
) { | ||
options = options || {}; | ||
if (dockerfile !== null) { | ||
obj['dockerfile'] = path.basename(dockerfile); | ||
let streami = await docker.buildImage({ | ||
context: buildPath, | ||
src: [dockerfile] | ||
}, obj); | ||
let streami = await docker.buildImage( | ||
{ | ||
context: buildPath, | ||
src: [dockerfile], | ||
}, | ||
obj | ||
); | ||
if (options.verbose === true) { | ||
@@ -531,3 +585,3 @@ streami.pipe(process.stdout); | ||
} | ||
await new Promise(fulfill => streami.once('end', fulfill)); | ||
await new Promise((fulfill) => streami.once('end', fulfill)); | ||
} else { | ||
@@ -541,5 +595,5 @@ var tarStream = tar.pack(buildPath); | ||
} | ||
await new Promise(fulfill => streami.once('end', fulfill)); | ||
await new Promise((fulfill) => streami.once('end', fulfill)); | ||
} | ||
} | ||
} | ||
}, | ||
}; |
@@ -20,3 +20,9 @@ module.exports = { | ||
if (order.indexOf(serviceName) === -1) { | ||
insertService(serviceName, recipe.services[serviceName].depends_on || [], order); | ||
let depends_on = recipe.services[serviceName].depends_on; | ||
if (typeof depends_on === "object" && !Array.isArray(depends_on)) { | ||
// if we are using Long Syntax https://github.com/compose-spec/compose-spec/blob/master/spec.md#long-syntax-1 | ||
// we will need to get keys from object and use them as dependencies | ||
depends_on = Object.keys(depends_on) | ||
} | ||
insertService(serviceName, depends_on || [], order); | ||
} | ||
@@ -23,0 +29,0 @@ } |
@@ -1,5 +0,20 @@ | ||
module.exports = async function (docker, projectName, recipe, output) { | ||
async function down(docker, projectName, recipe, output) { | ||
var volumes = []; | ||
var volumeNames = Object.keys(recipe.volumes || []); | ||
for (var volumeName of volumeNames) { | ||
try { | ||
var volume = await docker.getVolume(projectName + '_' + volumeName); | ||
} catch (e) {} | ||
try { | ||
await volume.remove(); | ||
} catch (e) {} | ||
} | ||
return volumes; | ||
} | ||
async function up(docker, projectName, recipe, output) { | ||
var volumes = []; | ||
var volumeNames = Object.keys(recipe.volumes || []); | ||
for (var volumeName of volumeNames) { | ||
var volume = recipe.volumes[volumeName]; | ||
@@ -9,6 +24,12 @@ if (volume === null) volume = {}; | ||
var opts = { | ||
'Name': projectName + '_' + volumeName, | ||
'Driver': volume.driver, | ||
'DriverOpts': volume.driver_opts, | ||
'Labels': volume.labels | ||
Name: projectName + '_' + volumeName, | ||
Driver: volume.driver, | ||
DriverOpts: volume.driver_opts, | ||
Labels: { | ||
...volume.labels, | ||
...{ | ||
'com.docker.compose.project': projectName, | ||
'com.docker.compose.volume': volumeName, | ||
}, | ||
}, | ||
}; | ||
@@ -21,2 +42,7 @@ if (volume.name !== undefined) { | ||
return volumes; | ||
} | ||
} | ||
module.exports = { | ||
down, | ||
up, | ||
}; |
{ | ||
"name": "dockerode-compose", | ||
"version": "1.2.2", | ||
"version": "1.3.1", | ||
"description": "docker-compose in nodejs using dockerode", | ||
@@ -24,3 +24,3 @@ "main": "./compose.js", | ||
"dependencies": { | ||
"dockerode": "^3.2.1", | ||
"dockerode": "^3.3.1", | ||
"js-yaml": "^4.0.0", | ||
@@ -27,0 +27,0 @@ "tar-fs": "^2.1.1" |
@@ -44,17 +44,38 @@ const expect = require('chai').expect, | ||
expect(report.services).to.be.ok; | ||
await new Promise(r => setTimeout(r, 2000)); | ||
let listContainers = await docker.listContainers({ 'all': true }); | ||
for (var containerInfo of listContainers) { | ||
if (containerInfo.Names[0].includes("dockerodec_wordpress")) { | ||
let container = docker.getContainer(containerInfo.Id); | ||
if (containerInfo.State == 'running') | ||
await container.stop(); | ||
await container.remove(); | ||
} | ||
} | ||
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); | ||
(async () => { | ||
await compose.up(); | ||
done(); | ||
})(); | ||
}); | ||
it("should do compose down", function (done) { | ||
this.timeout(60000); | ||
(async () => { | ||
await compose.down({volumes: true}); | ||
let listContainers = await docker.listContainers({ 'all': true, 'filters': {"label":[`com.docker.compose.project=${compose.projectName}`]}}); | ||
expect(listContainers).to.be.empty | ||
let listVolumes = await docker.listVolumes({ 'filters': {"label":[`com.docker.compose.project=${compose.projectName}`]}}) | ||
expect(listVolumes.Volumes).to.be.empty | ||
expect(listVolumes.Warnings).to.be.null | ||
let listNetworks = await docker.listNetworks({ 'filters': {"label":[`com.docker.compose.project=${compose.projectName}`]}}) | ||
expect(listNetworks).to.be.empty | ||
done(); | ||
})(); | ||
}); | ||
}); | ||
describe('#up_complex', function () { | ||
@@ -66,17 +87,38 @@ it("should do compose up complex example with extends and build", function (done) { | ||
expect(report.services).to.be.ok; | ||
await new Promise(r => setTimeout(r, 5000)); | ||
let listContainers = await docker.listContainers({ 'all': true }); | ||
for (var containerInfo of listContainers) { | ||
if (containerInfo.Names[0].includes("dockerodec_complex")) { | ||
let container = docker.getContainer(containerInfo.Id); | ||
if (containerInfo.State == 'running') | ||
await container.stop(); | ||
await container.remove(); | ||
} | ||
} | ||
done(); | ||
})(); | ||
}); | ||
afterEach('clean up', function (done) { | ||
this.timeout(60000); | ||
(async () => { | ||
await compose_complex.down({ volumes: true }); | ||
done(); | ||
})(); | ||
}); | ||
}); | ||
describe('#down_complex', function () { | ||
beforeEach('bring up', function (done) { | ||
this.timeout(300000); | ||
(async () => { | ||
await compose_complex.up(); | ||
done(); | ||
})(); | ||
}); | ||
it("should do compose down complex example with extends and build", function (done) { | ||
this.timeout(60000); | ||
(async () => { | ||
await compose_complex.down({volumes: true}); | ||
let listContainers = await docker.listContainers({ 'all': true, 'filters': {"label":[`com.docker.compose.project=${compose.projectName}`]}}); | ||
expect(listContainers).to.be.empty | ||
let listVolumes = await docker.listVolumes({ 'filters': {"label":[`com.docker.compose.project=${compose.projectName}`]}}) | ||
expect(listVolumes.Volumes).to.be.empty | ||
expect(listVolumes.Warnings).to.be.null | ||
let listNetworks = await docker.listNetworks({ 'filters': {"label":[`com.docker.compose.project=${compose.projectName}`]}}) | ||
expect(listNetworks).to.be.empty | ||
done(); | ||
})(); | ||
}); | ||
}); | ||
describe('#up_build', function () { | ||
@@ -88,12 +130,2 @@ it("should do compose up example with build", function (done) { | ||
expect(report.services).to.be.ok; | ||
await new Promise(r => setTimeout(r, 5000)); | ||
let listContainers = await docker.listContainers({ 'all': true }); | ||
for (var containerInfo of listContainers) { | ||
if (containerInfo.Names[0].includes("dockerodec_build")) { | ||
let container = docker.getContainer(containerInfo.Id); | ||
if (containerInfo.State == 'running') | ||
await container.stop(); | ||
await container.remove(); | ||
} | ||
} | ||
done(); | ||
@@ -107,17 +139,38 @@ })(); | ||
expect(report.services).to.be.ok; | ||
await new Promise(r => setTimeout(r, 5000)); | ||
let listContainers = await docker.listContainers({ 'all': true }); | ||
for (var containerInfo of listContainers) { | ||
if (containerInfo.Names[0].includes("dockerodec_build")) { | ||
let container = docker.getContainer(containerInfo.Id); | ||
if (containerInfo.State == 'running') | ||
await container.stop(); | ||
await container.remove(); | ||
} | ||
} | ||
done(); | ||
})(); | ||
}); | ||
afterEach('clean up', function (done) { | ||
this.timeout(60000); | ||
(async () => { | ||
await compose_build.down({ volumes: true }); | ||
done(); | ||
})(); | ||
}); | ||
}); | ||
describe('#down_build', function () { | ||
beforeEach('bring up', function (done) { | ||
this.timeout(300000); | ||
(async () => { | ||
await compose_build.up(); | ||
done(); | ||
})(); | ||
}); | ||
it("should do compose down example with build", function (done) { | ||
this.timeout(60000); | ||
(async () => { | ||
await compose_build.down({volumes: true}); | ||
let listContainers = await docker.listContainers({ 'all': true, 'filters': {"label":[`com.docker.compose.project=${compose.projectName}`]}}); | ||
expect(listContainers).to.be.empty | ||
let listVolumes = await docker.listVolumes({ 'filters': {"label":[`com.docker.compose.project=${compose.projectName}`]}}) | ||
expect(listVolumes.Volumes).to.be.empty | ||
expect(listVolumes.Warnings).to.be.null | ||
let listNetworks = await docker.listNetworks({ 'filters': {"label":[`com.docker.compose.project=${compose.projectName}`]}}) | ||
expect(listNetworks).to.be.empty | ||
done(); | ||
})(); | ||
}); | ||
}); | ||
}); |
@@ -17,2 +17,3 @@ const expect = require('chai').expect; | ||
'wordpress2', | ||
'wordpress6', | ||
'wordpress4', | ||
@@ -19,0 +20,0 @@ 'wordpress5' |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
79489
38
1680
Updateddockerode@^3.3.1