1 | const Container = require('./container')
|
2 | const Docker = require('dockerode')
|
3 | const tarstream = require('tar-stream')
|
4 | const tarfs = require('tar-fs')
|
5 | const chalk = require('chalk')
|
6 | const async = require('async')
|
7 | const jsonpath = require('jsonpath')
|
8 |
|
9 | const docker = new Docker({
|
10 | socketPath: '/var/run/docker.sock'
|
11 | })
|
12 |
|
13 | class ContainerBuildError extends Error {
|
14 | constructor (...args) {
|
15 | super(...args)
|
16 | this.name = 'ContainerBuildError'
|
17 | }
|
18 | }
|
19 |
|
20 | class ComponentContainer extends Container {
|
21 | constructor (component, devServer) {
|
22 | super(devServer, component.name, 'component')
|
23 | this.component = component
|
24 | this.buildOutput = []
|
25 | this.dynamicParams = {resources: {}}
|
26 | }
|
27 |
|
28 | getImage() {
|
29 | return `noop/${this.component.app.id}/component/${this.component.name}`.toLowerCase()
|
30 | }
|
31 |
|
32 | getEnv() {
|
33 | const env = {}
|
34 | Object.keys(this.component.env).forEach((key) => {
|
35 | env[key] = this.component.env[key].default
|
36 | })
|
37 | Object.keys(this.devServer.envOverrides['_all']).forEach((key) => {
|
38 | env[key] = this.devServer.envOverrides['_all'][key]
|
39 | })
|
40 | if (this.devServer.envOverrides[this.component.name]) {
|
41 | Object.keys(this.devServer.envOverrides[this.component.name]).forEach((key) => {
|
42 | env[key] = this.devServer.envOverrides[this.component.name][key]
|
43 | })
|
44 | }
|
45 | Object.keys(env).filter((key) => {
|
46 | return /^\$\..+$/.test(env[key])
|
47 | }).forEach((key) => {
|
48 | let dynamicValue
|
49 | try {
|
50 | dynamicValue = jsonpath.query(this.dynamicParams, env[key])
|
51 | } catch (err) {
|
52 | console.log(chalk.yellow(`Unable to resolve dynamic value '${env[key]}'`), err)
|
53 | }
|
54 | if (dynamicValue) env[key] = dynamicValue
|
55 | })
|
56 | return env
|
57 | }
|
58 |
|
59 | build (done) {
|
60 | this.buildStart = new Date()
|
61 | console.log(`Building '${this.component.name}' component container...`)
|
62 | const pack = tarstream.pack()
|
63 | pack.entry({name: 'Dockerfile'}, this.component.dockerfile, (err) => {
|
64 | if (err) return done(err)
|
65 | tarfs.pack(this.component.rootPath, {pack: pack})
|
66 | const buildOpts = {t: this.getImage()}
|
67 | docker.buildImage(pack, buildOpts, (err, output) => {
|
68 | if (err) return done(err)
|
69 | const complete = (err) => {
|
70 | this.buildEnd = new Date()
|
71 | if (err) {
|
72 | console.log(chalk.red(`Build failed for '${this.component.name}' component container`))
|
73 | console.log(' ', err)
|
74 | done(new ContainerBuildError(err))
|
75 | } else {
|
76 | const duration = (this.buildEnd.getTime() - this.buildStart.getTime()) / 1000
|
77 | console.log(`Build completed for '${this.component.name}' in ${duration}s`)
|
78 | done()
|
79 | }
|
80 | }
|
81 | docker.modem.followProgress(output, complete, (event) => {
|
82 | if (event.stream && /\w/.test(event.stream)) {
|
83 | let line = event.stream.trim()
|
84 | this.buildOutput.push(line)
|
85 | if (/^Step \d+\/\d+ :/.test(line)) line = chalk.cyan(line)
|
86 | console.log(' ', line.replace(/(\r\n|\r|\n)/g, '\n '))
|
87 | }
|
88 | })
|
89 | })
|
90 | })
|
91 | }
|
92 |
|
93 | reload (done) {
|
94 | async.auto({
|
95 | build: (done) => this.build(done),
|
96 | stop: ['build', (results, done) => this.stop(done)],
|
97 | start: ['stop', (results, done) => this.start(done)]
|
98 | }, done)
|
99 | }
|
100 | }
|
101 |
|
102 | module.exports = ComponentContainer
|