mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-02-21 10:01:55 +00:00
Application management code in its own module
This commit is contained in:
parent
614b24c796
commit
637d68921f
@ -1,10 +1,8 @@
|
|||||||
Promise = require 'bluebird'
|
Promise = require 'bluebird'
|
||||||
fs = Promise.promisifyAll require 'fs'
|
fs = Promise.promisifyAll require 'fs'
|
||||||
url = require 'url'
|
|
||||||
knex = require './db'
|
|
||||||
utils = require './utils'
|
utils = require './utils'
|
||||||
express = require 'express'
|
express = require 'express'
|
||||||
request = Promise.promisify require 'request'
|
application = require './application'
|
||||||
|
|
||||||
api = express()
|
api = express()
|
||||||
|
|
||||||
@ -24,22 +22,9 @@ api.post('/v1/blink', (req, res) ->
|
|||||||
)
|
)
|
||||||
|
|
||||||
api.post('/v1/update', (req, res) ->
|
api.post('/v1/update', (req, res) ->
|
||||||
|
console.log("Got application update")
|
||||||
|
application.update()
|
||||||
res.send(204)
|
res.send(204)
|
||||||
Promise.all([
|
|
||||||
knex('config').select('value').where(key: 'apiKey')
|
|
||||||
knex('config').select('value').where(key: 'uuid')
|
|
||||||
]).then(([[apiKey], [uuid]]) ->
|
|
||||||
apiKey = apiKey.value
|
|
||||||
uuid = uuid.value
|
|
||||||
request(
|
|
||||||
method: 'GET'
|
|
||||||
url: url.resolve(process.env.API_ENDPOINT, "/ewa/application?$filter=device/uuid eq '#{uuid}'&apikey=#{apiKey}")
|
|
||||||
json: true
|
|
||||||
).spread((request, body) ->
|
|
||||||
for app in body.d
|
|
||||||
console.log("Got application", app)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
module.exports = api
|
module.exports = api
|
||||||
|
@ -46,8 +46,14 @@ Promise.all([newUuid, oldUuid]).then(([newUuid, [oldUuid]]) ->
|
|||||||
|
|
||||||
console.log('Starting Apps..')
|
console.log('Starting Apps..')
|
||||||
knex('app').select().then((apps) ->
|
knex('app').select().then((apps) ->
|
||||||
Promise.all(apps.map(application.start))
|
Promise.all(apps.map((app) -> app.imageId).map(application.restart))
|
||||||
).catch((error) ->
|
).catch((error) ->
|
||||||
console.log(error)
|
console.error("Error starting apps:", error)
|
||||||
|
).then(->
|
||||||
|
console.log('Starting periodic check for updates..')
|
||||||
|
setInterval(->
|
||||||
|
application.update()
|
||||||
|
, 15 * 60 * 1000) # Every 15 mins
|
||||||
|
application.update()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1,32 +1,59 @@
|
|||||||
Promise = require 'bluebird'
|
_ = require 'lodash'
|
||||||
Docker = require 'dockerode'
|
|
||||||
JSONStream = require 'JSONStream'
|
|
||||||
es = require 'event-stream'
|
es = require 'event-stream'
|
||||||
|
url = require 'url'
|
||||||
http = require 'http'
|
http = require 'http'
|
||||||
|
knex = require './db'
|
||||||
|
path = require 'path'
|
||||||
|
Docker = require 'dockerode'
|
||||||
|
Promise = require 'bluebird'
|
||||||
|
request = Promise.promisify require 'request'
|
||||||
|
JSONStream = require 'JSONStream'
|
||||||
|
|
||||||
docker = Promise.promisifyAll(new Docker(socketPath: '/hostrun/docker.sock'))
|
docker = Promise.promisifyAll(new Docker(socketPath: '/hostrun/docker.sock'))
|
||||||
# Hack dockerode to promisify internal classes' prototype
|
# Hack dockerode to promisify internal classes' prototypes
|
||||||
Promise.promisifyAll(docker.getImage().__proto__)
|
Promise.promisifyAll(docker.getImage().__proto__)
|
||||||
|
Promise.promisifyAll(docker.getContainer().__proto__)
|
||||||
|
|
||||||
exports.start = (app) ->
|
exports.kill = kill = (app) ->
|
||||||
docker.getImage(app.imageId).inspectAsync().catch(->
|
docker.listContainersAsync(all: 1).then((containers) ->
|
||||||
|
Promise.all(
|
||||||
|
containers
|
||||||
|
.filter((container) ->
|
||||||
|
return container.Image is "#{app}:latest"
|
||||||
|
)
|
||||||
|
.map((container) -> docker.getContainer(container.Id))
|
||||||
|
.map((container) ->
|
||||||
|
console.log("Stopping and deleting container:", container)
|
||||||
|
container.stopAsync().then(->
|
||||||
|
container.removeAsync()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
exports.start = start = (app) ->
|
||||||
|
docker.getImage(app).inspectAsync().catch((error) ->
|
||||||
|
console.log(error)
|
||||||
deferred = Promise.defer()
|
deferred = Promise.defer()
|
||||||
options =
|
options =
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
path: "/v1.8/images/create?fromImage=#{app.imageId}"
|
path: "/v1.8/images/create?fromImage=#{app}"
|
||||||
socketPath: '/hostrun/docker.sock'
|
socketPath: '/hostrun/docker.sock'
|
||||||
|
|
||||||
req = http.request(options, (res) ->
|
req = http.request(options, (res) ->
|
||||||
if res.statusCode isnt 200
|
if res.headers['content-type'] is 'application/json'
|
||||||
return deferred.reject()
|
res.pipe(JSONStream.parse('error'))
|
||||||
|
.pipe(es.mapSync((error) ->
|
||||||
res.pipe(JSONStream.parse('error'))
|
deferred.reject(error)
|
||||||
.pipe(es.mapSync((error) ->
|
))
|
||||||
deferred.reject(error)
|
else
|
||||||
))
|
res.pipe(es.wait((error, text) -> deferred.reject(text)))
|
||||||
|
|
||||||
res.on('end', ->
|
res.on('end', ->
|
||||||
deferred.resolve()
|
if res.statusCode is 200
|
||||||
|
deferred.resolve()
|
||||||
|
else
|
||||||
|
deferred.reject(res.statusCode)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
req.end()
|
req.end()
|
||||||
@ -34,5 +61,56 @@ exports.start = (app) ->
|
|||||||
|
|
||||||
return deferred.promise
|
return deferred.promise
|
||||||
).then(->
|
).then(->
|
||||||
docker.runAsync(app.imageId, ['/bin/bash', '-c', '/start web'], process.stdout, true)
|
console.log("Creating container:", app)
|
||||||
|
docker.createContainerAsync(
|
||||||
|
Image: app
|
||||||
|
Cmd: ['/bin/bash', '-c', '/start web']
|
||||||
|
)
|
||||||
|
).then((container) ->
|
||||||
|
container.startAsync()
|
||||||
|
)
|
||||||
|
|
||||||
|
exports.restart = restart = (app) ->
|
||||||
|
kill(app).then(->
|
||||||
|
start(app)
|
||||||
|
)
|
||||||
|
|
||||||
|
exports.update = ->
|
||||||
|
Promise.all([
|
||||||
|
knex('config').select('value').where(key: 'apiKey')
|
||||||
|
knex('config').select('value').where(key: 'uuid')
|
||||||
|
knex('app').select()
|
||||||
|
]).then(([[apiKey], [uuid], apps]) ->
|
||||||
|
apiKey = apiKey.value
|
||||||
|
uuid = uuid.value
|
||||||
|
request(
|
||||||
|
method: 'GET'
|
||||||
|
url: url.resolve(process.env.API_ENDPOINT, "/ewa/application?$filter=device/uuid eq '#{uuid}'&apikey=#{apiKey}")
|
||||||
|
json: true
|
||||||
|
).spread((request, body) ->
|
||||||
|
console.log("Remote apps")
|
||||||
|
remoteApps = ("registry.resin.io:5000/#{path.basename(app.git_repository, '.git')}/#{app.commit}" for app in body.d)
|
||||||
|
console.log(remoteApps)
|
||||||
|
|
||||||
|
console.log("Local apps")
|
||||||
|
localApps = (app.imageId for app in apps)
|
||||||
|
console.log(localApps)
|
||||||
|
|
||||||
|
console.log("Apps to be removed")
|
||||||
|
toBeRemoved = _.difference(localApps, remoteApps)
|
||||||
|
console.log(toBeRemoved)
|
||||||
|
|
||||||
|
console.log("Apps to be installed")
|
||||||
|
toBeInstalled = _.difference(remoteApps, localApps)
|
||||||
|
console.log(toBeInstalled)
|
||||||
|
|
||||||
|
Promise.all(toBeRemoved.map(kill)).then(->
|
||||||
|
Promise.all(toBeInstalled.map(start))
|
||||||
|
).then(->
|
||||||
|
knex('app').whereIn('imageId', toBeRemoved).delete().then(->
|
||||||
|
knex('app').insert(({imageId: app} for app in toBeInstalled))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user