mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-19 21:57:54 +00:00
Switch to docker-delta library to use deltas v2
This npm library implements the new delta format and also works with docker 1.10. Signed-off-by: Petros Angelatos <petrosagg@gmail.com>
This commit is contained in:
parent
9b3381453e
commit
20419bee3b
@ -11,6 +11,7 @@
|
||||
"body-parser": "^1.12.0",
|
||||
"buffer-equal-constant-time": "^1.0.1",
|
||||
"coffee-script": "~1.9.1",
|
||||
"docker-delta": "0.0.5",
|
||||
"docker-progress": "^2.1.0",
|
||||
"dockerode": "~2.2.9",
|
||||
"event-stream": "^3.0.20",
|
||||
|
@ -1,16 +1,14 @@
|
||||
Docker = require 'dockerode'
|
||||
{ getRegistryAndName, DockerProgress } = require 'docker-progress'
|
||||
Promise = require 'bluebird'
|
||||
{ spawn, execAsync } = Promise.promisifyAll require 'child_process'
|
||||
progress = require 'request-progress'
|
||||
dockerDelta = require 'docker-delta'
|
||||
config = require './config'
|
||||
_ = require 'lodash'
|
||||
knex = require './db'
|
||||
TypedError = require 'typed-error'
|
||||
{ request } = require './request'
|
||||
fs = Promise.promisifyAll require 'fs'
|
||||
Lock = require 'rwlock'
|
||||
class OutOfSyncError extends TypedError
|
||||
|
||||
docker = Promise.promisifyAll(new Docker(socketPath: config.dockerSocket))
|
||||
# Hack dockerode to promisify internal classes' prototypes
|
||||
@ -58,7 +56,6 @@ findSimilarImage = (repoTag) ->
|
||||
|
||||
return 'resin/scratch'
|
||||
|
||||
DELTA_OUT_OF_SYNC_CODES = [23, 24]
|
||||
DELTA_REQUEST_TIMEOUT = 15 * 60 * 1000
|
||||
|
||||
getRepoAndTag = (image) ->
|
||||
@ -67,54 +64,6 @@ getRepoAndTag = (image) ->
|
||||
registry = registry.toString().replace(':443','')
|
||||
return { repo: "#{registry}/#{imageName}", tag: tagName }
|
||||
|
||||
dockerSync = (imgSrc, imgDest, rsyncDiff, conf) ->
|
||||
docker.importImageAsync('/app/empty.tar')
|
||||
.then (stream) ->
|
||||
new Promise (resolve, reject) ->
|
||||
streamOutput = ''
|
||||
stream.on 'data', (data) ->
|
||||
streamOutput += data
|
||||
stream.on 'error', reject
|
||||
stream.on 'end', ->
|
||||
resolve(JSON.parse(streamOutput).status)
|
||||
.then (destId) ->
|
||||
jsonPath = "#{config.dockerRoot}/graph/#{destId}/json"
|
||||
fs.readFileAsync(jsonPath)
|
||||
.then(JSON.parse)
|
||||
.then (destJson) ->
|
||||
destJson.config = conf
|
||||
fs.writeFileAsync(jsonPath + '.tmp', JSON.stringify(destJson))
|
||||
.then ->
|
||||
fs.renameAsync(jsonPath + '.tmp', jsonPath)
|
||||
.then ->
|
||||
if imgSrc isnt 'resin/scratch'
|
||||
execAsync("btrfs subvolume delete \"#{config.btrfsRoot}/#{destId}\"")
|
||||
.then ->
|
||||
docker.getImage(imgSrc).inspectAsync().get('Id')
|
||||
.then (srcId) ->
|
||||
execAsync("btrfs subvolume snapshot \"#{config.btrfsRoot}/#{srcId}\" \"#{config.btrfsRoot}/#{destId}\"")
|
||||
.then ->
|
||||
new Promise (resolve, reject) ->
|
||||
rsync = spawn('rsync', ['--timeout=300', '--archive', '--delete' , '--read-batch=-', "#{config.btrfsRoot}/#{destId}"], stdio: 'pipe')
|
||||
.on 'error', reject
|
||||
.on 'exit', (code, signal) ->
|
||||
if code in DELTA_OUT_OF_SYNC_CODES
|
||||
reject(new OutOfSyncError('Incompatible image'))
|
||||
else if code isnt 0
|
||||
reject(new Error("rsync exited. code: #{code} signal: #{signal}"))
|
||||
else
|
||||
resolve()
|
||||
rsyncDiff.pipe(rsync.stdin)
|
||||
rsync.stdout.pipe(process.stdout)
|
||||
rsync.stderr.pipe(process.stdout)
|
||||
rsyncDiff.resume()
|
||||
.then ->
|
||||
execAsync('sync')
|
||||
.then ->
|
||||
getRepoAndTag(imgDest)
|
||||
.then ({ repo, tag }) ->
|
||||
docker.getImage(destId).tagAsync({ repo, tag, force: true })
|
||||
|
||||
do ->
|
||||
_lock = new Lock()
|
||||
_writeLock = Promise.promisify(_lock.async.writeLock)
|
||||
@ -135,8 +84,8 @@ do ->
|
||||
return 'resin/scratch'
|
||||
findSimilarImage(imgDest)
|
||||
.then (imgSrc) ->
|
||||
rsyncDiff = new Promise (resolve, reject) ->
|
||||
progress request.get("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: DELTA_REQUEST_TIMEOUT)
|
||||
new Promise (resolve, reject) ->
|
||||
progress request.get("#{config.deltaHost}/api/v2/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: DELTA_REQUEST_TIMEOUT)
|
||||
.on 'progress', (progress) ->
|
||||
onProgress(percentage: progress.percent)
|
||||
.on 'end', ->
|
||||
@ -145,20 +94,17 @@ do ->
|
||||
if res.statusCode isnt 200
|
||||
reject(new Error("Got #{res.statusCode} when requesting image from delta server."))
|
||||
else
|
||||
resolve(res)
|
||||
if imgSrc is 'resin/scratch'
|
||||
deltaSrc = null
|
||||
else
|
||||
deltaSrc = imgSrc
|
||||
res.pipe(dockerDelta.applyDelta(deltaSrc, imgDest)).on('id', resolve)
|
||||
.on 'error', reject
|
||||
.pause()
|
||||
|
||||
imageConfig = request.getAsync("#{config.deltaHost}/api/v1/config?image=#{imgDest}", {json: true, timeout: 0})
|
||||
.spread ({statusCode}, imageConfig) ->
|
||||
if statusCode isnt 200
|
||||
throw new Error("Invalid configuration: #{imageConfig}")
|
||||
return imageConfig
|
||||
|
||||
return [ rsyncDiff, imageConfig, imgSrc ]
|
||||
.spread (rsyncDiff, imageConfig, imgSrc) ->
|
||||
dockerSync(imgSrc, imgDest, rsyncDiff, imageConfig)
|
||||
.catch OutOfSyncError, (err) ->
|
||||
.then (id) ->
|
||||
getRepoAndTag(imgDest)
|
||||
.then ({ repo, tag }) ->
|
||||
docker.getImage(id).tagAsync({ repo, tag, force: true })
|
||||
.catch dockerDelta.OutOfSyncError, (err) ->
|
||||
console.log('Falling back to delta-from-empty')
|
||||
exports.rsyncImageWithProgress(imgDest, onProgress, true)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user