-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdocker-upgrade.js
executable file
·87 lines (76 loc) · 3.17 KB
/
docker-upgrade.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/usr/bin/env node
//@ts-check
const { once } = require('events')
const { spawn } = require('child_process')
const Docker = require('dockerode')
const imageExists = image => image.inspect().then(() => true,
e => (e.statusCode === 404 ? false : Promise.reject(e)))
process.on('unhandledRejection', up => { throw up })
/** @type {{ argv: any }} */
const yargs = require('yargs')
.command('$0 <container> [image_tag]',
'Re-launch a container with a new image, preserving its config.')
.option('pull', {
description: "Pull the image first. If not set, the image will only be pulled if it doesn't exist, or if no image tag was passed.",
type: 'boolean',
})
/** @type {{ argv: { container: string, image_tag: string, pull: boolean? }}} */
const { argv } = yargs
;(async function main() {
// Connect to docker
const engine = new Docker()
const container = engine.getContainer(argv.container)
const { Config: { Image: currentImage } } = await container.inspect()
let imageId = argv.image_tag
if (!imageId) {
// If no image tag was given, use current one
imageId = currentImage
if (argv.pull === undefined) argv.pull = true
console.log(`Resolved to ${imageId}`)
} else if (imageId.startsWith(':')) {
// If only a tag was given, combine with running image
const repo = /^(.+?)(:[^:]+)?$/.exec(currentImage)[1]
imageId = repo + imageId
console.log(`Resolved to ${imageId}`)
}
// Check image exists, pull otherwise
const image = engine.getImage(imageId)
const exists = await imageExists(image)
if (argv.pull === false && !exists)
throw Error("Image doesn't exist")
if (argv.pull === true || (argv.pull === undefined && !exists)) {
console.log('Pulling image...')
const child = spawn('docker', ['pull', imageId])
const [code, signal] = await once(child, 'exit')
if (code || signal)
throw Error("Couldn't pull image")
}
// Check container exists and get its config
const containerInfo = await container.inspect()
console.log('Stopping & removing...')
await container.stop() // ignore 'container already stopped'
.catch(e => (e.statusCode !== 304 && Promise.reject(e)))
await container.remove()
console.log('Launching new container...')
const { Config, HostConfig, NetworkSettings, Name: name } = containerInfo
const EndpointsConfig = getEndpointsConfig(NetworkSettings.Networks)
const newContainer = await engine.createContainer({
name, ...Config, HostConfig,
NetworkingConfig: { EndpointsConfig },
// overrides
Image: imageId,
})
if (!['exited', 'created'].includes(containerInfo.State.Status))
await newContainer.start()
console.log('Done.')
})()
function getEndpointsConfig(Networks) {
const EndpointsConfig = { ...Networks }
Object.keys(Networks).forEach(key => {
const value = { ...Networks[key] }
EndpointsConfig[key] = value
value.IPAMConfig = { IPv4Address: value.IPAddress, ...value.IPAMConfig }
delete value.Aliases // FIXME: look into why this is needed
})
return EndpointsConfig
}