From cbde94456577be1a91255147316d8d551a93bb06 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Wed, 29 Apr 2015 23:13:47 +0000 Subject: [PATCH 01/38] Implement rsync diff fetching method --- Dockerfile.amd64 | 2 +- Dockerfile.armv7hf | 2 +- Dockerfile.i386 | 2 +- Dockerfile.rpi | 2 +- package.json | 1 + src/application.coffee | 8 +++- src/config.coffee | 1 + src/docker-utils.coffee | 90 ++++++++++++++++++++++++++++++++++++++++- 8 files changed, 101 insertions(+), 7 deletions(-) diff --git a/Dockerfile.amd64 b/Dockerfile.amd64 index c5534a50..a6280016 100644 --- a/Dockerfile.amd64 +++ b/Dockerfile.amd64 @@ -4,7 +4,7 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor --no-install-recommends \ + && apt-get install -qqy socat supervisor rsync --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ diff --git a/Dockerfile.armv7hf b/Dockerfile.armv7hf index 2d1e65e3..453c9227 100644 --- a/Dockerfile.armv7hf +++ b/Dockerfile.armv7hf @@ -4,7 +4,7 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor --no-install-recommends \ + && apt-get install -qqy socat supervisor rsync --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ diff --git a/Dockerfile.i386 b/Dockerfile.i386 index a617f0e4..470f1b00 100644 --- a/Dockerfile.i386 +++ b/Dockerfile.i386 @@ -4,7 +4,7 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor --no-install-recommends \ + && apt-get install -qqy socat supervisor rsync --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ diff --git a/Dockerfile.rpi b/Dockerfile.rpi index 4e00cdbe..23c0858a 100644 --- a/Dockerfile.rpi +++ b/Dockerfile.rpi @@ -4,7 +4,7 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor --no-install-recommends \ + && apt-get install -qqy socat supervisor rsync --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ diff --git a/package.json b/package.json index 00473bfa..10de7afe 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "pubnub": "^3.7.13", "request": "^2.51.0", "resin-register-device": "^2.0.0", + "request-progress": "^0.3.1", "rwlock": "^5.0.0", "server-destroy": "^1.0.0", "sqlite3": "~3.0.4", diff --git a/src/application.coffee b/src/application.coffee index db972e66..0bc6b088 100644 --- a/src/application.coffee +++ b/src/application.coffee @@ -126,12 +126,16 @@ isValidPort = (port) -> return parseFloat(port) is maybePort and maybePort > 0 and maybePort < 65535 fetch = (app) -> + onProgress = (progress) -> + device.updateState(download_progress: progress.percentage) + docker.getImage(app.imageId).inspectAsync() .catch (error) -> logSystemEvent(logTypes.downloadApp, app) device.updateState(status: 'Downloading', download_progress: 0) - dockerUtils.fetchImageWithProgress app.imageId, (progress) -> - device.updateState(download_progress: progress.percentage) + dockerUtils.rsyncImageWithProgress(app.imageId, onProgress) + .catch -> + dockerUtils.fetchImageWithProgress(app.imageId, onProgress) .then -> logSystemEvent(logTypes.downloadAppSuccess, app) device.updateState(status: 'Idle', download_progress: null) diff --git a/src/config.coffee b/src/config.coffee index 58bf6f29..d632b633 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -18,6 +18,7 @@ module.exports = config = apiEndpoint: process.env.API_ENDPOINT ? 'https://api.resin.io' listenPort: process.env.LISTEN_PORT ? 80 gosuperAddress: "http://unix:#{process.env.GOSUPER_SOCKET}:" + deltaEndpoint: process.env.DELTA_ENDPOINT ? 'https://delta.staging.resin.io' registryEndpoint: process.env.REGISTRY_ENDPOINT ? 'registry.resin.io' pubnub: subscribe_key: checkValidKey(process.env.PUBNUB_SUBSCRIBE_KEY) ? process.env.DEFAULT_PUBNUB_SUBSCRIBE_KEY diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 6ab2dd92..961cfe49 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -1,6 +1,9 @@ Docker = require 'dockerode' DockerProgress = require 'docker-progress' Promise = require 'bluebird' +request = Promise.promisifyAll require('request') +{ spawn } = require 'child_process' +progress = require 'request-progress' config = require './config' _ = require 'lodash' knex = require './db' @@ -13,6 +16,92 @@ Promise.promisifyAll(docker.getContainer().constructor.prototype) exports.docker = docker dockerProgress = new DockerProgress(socketPath: config.dockerSocket) +createContainerDisposed = (config) -> + docker.createContainerAsync(config) + .tap (container) -> + container.startAsync() + .disposer (container) -> + container.removeAsync(force: true) + +# Trailing slashes are important to rsync +rootDir = ({Driver, Id}) -> + switch Driver + when 'aufs' + "/var/lib/docker/#{Driver}/mnt/#{Id}/" + when 'btrfs' + "/var/lib/docker/#{Driver}/subvolumes/#{Id}/" + when 'devicemapper' + "/var/lib/docker/#{Driver}/mnt/#{Id}/rootfs/" + when 'overlay' + "/var/lib/docker/#{Driver}/#{Id}/merged/" + when 'vfs' + "/var/lib/docker/#{Driver}/dir/#{Id}/" + else + throw new Error("Unsupported driver: #{Driver}") + +# Create an array of (repoTag, image_id, created) tuples like the output of `docker images` +listRepoTagsAsync = -> + docker.listImagesAsync() + .then (images) -> + images = _.sortByOrder(images, 'Created', [ false ]) + ret = [] + for image in images + for repoTag in image.RepoTags + ret.push [ repoTag, image.Id, image.Created ] + return ret + +# Find either the most recent image of the same app or the image of the supervisor +findSimilarImage = (repoTag) -> + application = repoTag.split('/')[1] + + listRepoTagsAsync() + .then (repoTags) -> + # Find the most recent image of the same application + for repoTag in repoTags + otherApplication = repoTag[0].split('/')[1] + if otherApplication is application + return repoTag + + return config.supervisorImage + +exports.rsyncImageWithProgress = (image, onProgress) -> + findSimilarImage(image) + .spread (repoTag, id) -> + config = + Image: id + Cmd: [ '/bin/sh', '-c', 'sleep 1000000' ] + NetworkDisabled: true + + Promise.using createContainerDisposed(config), (container) -> + container.inspectAsync() + .then(rootDir) + .then (dest) -> + delta = new Promise (resolve, reject) -> + rsync = spawn('rsync', [ '--archive', '--read-batch=-', dest ]) + .on 'error', reject + .on 'exit', (code, signal) -> + if code isnt 0 + reject(new Error("rsync exited. code: #{code} signal: #{signal}")) + else + resolve() + + progress request.get("#{config.deltaEndpoint}/api/v1/delta?src=#{repoTag}&dest=#{image}") + .on 'progress', onProgress + .on 'response', ({statusCode}) -> reject() if statusCode isnt 200 + .on 'error', reject + .pipe rsync.stdin + + config = request.getAsync("#{config.deltaEndpoint}/api/v1/config?image=#{image}", json: true) + + Promise.all [ config, delta ] + .get(0) + .spread ({statusCode}, config) -> + if statusCode isnt 200 + throw new Error("Invalid configuration: #{config}") + + config.repo = image + container.commitAsync(config) + do -> # Keep track of the images being fetched, so we don't clean them up whilst fetching. imagesBeingFetched = 0 @@ -77,4 +166,3 @@ do -> docker.getContainer(id).inspectAsync() .then (data) -> return not data.State.Running - From 11dfbd168750861c7a769eccaaab7621a531f397 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Thu, 30 Apr 2015 17:14:16 +0000 Subject: [PATCH 02/38] Don't timeout while waiting for delta --- src/docker-utils.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 961cfe49..3cab52ea 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -1,7 +1,6 @@ Docker = require 'dockerode' DockerProgress = require 'docker-progress' Promise = require 'bluebird' -request = Promise.promisifyAll require('request') { spawn } = require 'child_process' progress = require 'request-progress' config = require './config' @@ -85,13 +84,13 @@ exports.rsyncImageWithProgress = (image, onProgress) -> else resolve() - progress request.get("#{config.deltaEndpoint}/api/v1/delta?src=#{repoTag}&dest=#{image}") + progress request.get("#{config.deltaEndpoint}/api/v1/delta?src=#{repoTag}&dest=#{image}", timeout: 0) .on 'progress', onProgress .on 'response', ({statusCode}) -> reject() if statusCode isnt 200 .on 'error', reject .pipe rsync.stdin - config = request.getAsync("#{config.deltaEndpoint}/api/v1/config?image=#{image}", json: true) + config = request.getAsync("#{config.deltaEndpoint}/api/v1/config?image=#{image}", {json: true, timeout: 0}) Promise.all [ config, delta ] .get(0) From 9adbd81bc006b6ce626f6beeffaa40d95c917c53 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Thu, 30 Apr 2015 17:14:33 +0000 Subject: [PATCH 03/38] Avoid shadowing the config variable --- src/docker-utils.coffee | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 3cab52ea..fa46123a 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -66,12 +66,12 @@ findSimilarImage = (repoTag) -> exports.rsyncImageWithProgress = (image, onProgress) -> findSimilarImage(image) .spread (repoTag, id) -> - config = + containerConfig = Image: id Cmd: [ '/bin/sh', '-c', 'sleep 1000000' ] NetworkDisabled: true - Promise.using createContainerDisposed(config), (container) -> + Promise.using createContainerDisposed(containerConfig), (container) -> container.inspectAsync() .then(rootDir) .then (dest) -> @@ -90,16 +90,16 @@ exports.rsyncImageWithProgress = (image, onProgress) -> .on 'error', reject .pipe rsync.stdin - config = request.getAsync("#{config.deltaEndpoint}/api/v1/config?image=#{image}", {json: true, timeout: 0}) + imageConfig = request.getAsync("#{config.deltaEndpoint}/api/v1/config?image=#{image}", {json: true, timeout: 0}) - Promise.all [ config, delta ] + Promise.all [ imageConfig, delta ] .get(0) - .spread ({statusCode}, config) -> + .spread ({statusCode}, imageConfig) -> if statusCode isnt 200 - throw new Error("Invalid configuration: #{config}") + throw new Error("Invalid configuration: #{imageConfig}") - config.repo = image - container.commitAsync(config) + imageConfig.repo = image + container.commitAsync(imageConfig) do -> # Keep track of the images being fetched, so we don't clean them up whilst fetching. From c13e16c96add35959420c40064912d7dd1fa58cc Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Sat, 2 May 2015 01:02:37 +0000 Subject: [PATCH 04/38] Remove fallback to docker pull --- src/application.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/application.coffee b/src/application.coffee index 0bc6b088..b08ab23f 100644 --- a/src/application.coffee +++ b/src/application.coffee @@ -134,8 +134,6 @@ fetch = (app) -> logSystemEvent(logTypes.downloadApp, app) device.updateState(status: 'Downloading', download_progress: 0) dockerUtils.rsyncImageWithProgress(app.imageId, onProgress) - .catch -> - dockerUtils.fetchImageWithProgress(app.imageId, onProgress) .then -> logSystemEvent(logTypes.downloadAppSuccess, app) device.updateState(status: 'Idle', download_progress: null) From 10e166020e6dd7a259ffe3f53ff9476d39cf88fe Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Sat, 2 May 2015 01:07:23 +0000 Subject: [PATCH 05/38] Speed up rsync diff application --- Dockerfile.amd64 | 2 +- Dockerfile.armv7hf | 2 +- Dockerfile.i386 | 2 +- Dockerfile.rpi | 2 +- empty.tar | Bin 0 -> 10240 bytes src/docker-utils.coffee | 64 +++++++++++++++++++--------------------- src/dockersync.sh | 22 ++++++++++++++ 7 files changed, 56 insertions(+), 38 deletions(-) create mode 100644 empty.tar create mode 100755 src/dockersync.sh diff --git a/Dockerfile.amd64 b/Dockerfile.amd64 index a6280016..6db092fe 100644 --- a/Dockerfile.amd64 +++ b/Dockerfile.amd64 @@ -4,7 +4,7 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor rsync --no-install-recommends \ + && apt-get install -qqy socat supervisor rsync btrfs-tools jq --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ diff --git a/Dockerfile.armv7hf b/Dockerfile.armv7hf index 453c9227..739b80ae 100644 --- a/Dockerfile.armv7hf +++ b/Dockerfile.armv7hf @@ -4,7 +4,7 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor rsync --no-install-recommends \ + && apt-get install -qqy socat supervisor rsync btrfs-tools jq --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ diff --git a/Dockerfile.i386 b/Dockerfile.i386 index 470f1b00..14b3bb6e 100644 --- a/Dockerfile.i386 +++ b/Dockerfile.i386 @@ -4,7 +4,7 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor rsync --no-install-recommends \ + && apt-get install -qqy socat supervisor rsync btrfs-tools jq --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ diff --git a/Dockerfile.rpi b/Dockerfile.rpi index 23c0858a..1bba692e 100644 --- a/Dockerfile.rpi +++ b/Dockerfile.rpi @@ -4,7 +4,7 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor rsync --no-install-recommends \ + && apt-get install -qqy socat supervisor rsync btrfs-tools jq --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ diff --git a/empty.tar b/empty.tar new file mode 100644 index 0000000000000000000000000000000000000000..025d5fa1f3b2a2d263ab5948f2a898ceec3a2847 GIT binary patch literal 10240 zcmeIuu?>VU5ClM%;tBwxKU~jOfCPO17(xO_o@_Qb$*a>@)Ai}~3@tSx9kb;+&$lwh zIMgyt< return config.supervisorImage -exports.rsyncImageWithProgress = (image, onProgress) -> - findSimilarImage(image) - .spread (repoTag, id) -> - containerConfig = - Image: id - Cmd: [ '/bin/sh', '-c', 'sleep 1000000' ] - NetworkDisabled: true +exports.rsyncImageWithProgress = (imgDest, onProgress) -> + findSimilarImage(imgDest) + .spread (imgSrc) -> + rsyncDiff = new Promise (resolve, reject) -> + console.log("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 0) + progress request.get("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 0) + .on 'progress', onProgress + .on 'response', (res) -> + if res.statusCode isnt 200 + reject() + else + resolve(res) + .on 'error', reject + .pause() - Promise.using createContainerDisposed(containerConfig), (container) -> - container.inspectAsync() - .then(rootDir) - .then (dest) -> - delta = new Promise (resolve, reject) -> - rsync = spawn('rsync', [ '--archive', '--read-batch=-', dest ]) - .on 'error', reject - .on 'exit', (code, signal) -> - if code isnt 0 - reject(new Error("rsync exited. code: #{code} signal: #{signal}")) - else - resolve() + 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 - progress request.get("#{config.deltaEndpoint}/api/v1/delta?src=#{repoTag}&dest=#{image}", timeout: 0) - .on 'progress', onProgress - .on 'response', ({statusCode}) -> reject() if statusCode isnt 200 - .on 'error', reject - .pipe rsync.stdin + return [ rsyncDiff, imageConfig, imgSrc ] + .spread (rsyncDiff, imageConfig, imgSrc) -> + new Promise (resolve, reject) -> + dockersync = spawn('/app/src/dockersync.sh', [ imgSrc, imgDest, JSON.stringify(imageConfig) ]) + .on 'error', reject + .on 'exit', (code, signal) -> + if code isnt 0 + reject(new Error("rsync exited. code: #{code} signal: #{signal}")) + else + resolve() - imageConfig = request.getAsync("#{config.deltaEndpoint}/api/v1/config?image=#{image}", {json: true, timeout: 0}) - - Promise.all [ imageConfig, delta ] - .get(0) - .spread ({statusCode}, imageConfig) -> - if statusCode isnt 200 - throw new Error("Invalid configuration: #{imageConfig}") - - imageConfig.repo = image - container.commitAsync(imageConfig) + rsyncDiff.pipe(dockersync.stdin).resume() do -> # Keep track of the images being fetched, so we don't clean them up whilst fetching. diff --git a/src/dockersync.sh b/src/dockersync.sh new file mode 100755 index 00000000..023ff373 --- /dev/null +++ b/src/dockersync.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -o errexit +set -o pipefail + +DOCKER_ROOT=${DOCKER_ROOT:-/var/lib/docker} +BTRFS_ROOT=${BTRFS_ROOT:-$DOCKER_ROOT/btrfs/subvolumes} + +src=$1 +dest=$2 +config=$3 + +src_id=$(docker inspect -f '{{ .Id }}' "$src") +dest_id=$(cat /app/empty.tar | docker import - "$dest") + +jq ".config=$config" "$DOCKER_ROOT/graph/$dest_id/json" > "$DOCKER_ROOT/graph/$dest_id/json.tmp" +mv "$DOCKER_ROOT/graph/$dest_id/json.tmp" "$DOCKER_ROOT/graph/$dest_id/json" + +btrfs subvolume delete "$BTRFS_ROOT/$dest_id" +btrfs subvolume snapshot "$BTRFS_ROOT/$src_id" "$BTRFS_ROOT/$dest_id" + +rsync --archive --read-batch=- "$BTRFS_ROOT/$dest_id" From bd2f31624bddccb4d646e0ae3a68df9cc03d87a6 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Sat, 2 May 2015 01:07:35 +0000 Subject: [PATCH 06/38] Delete unused functions --- src/docker-utils.coffee | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index e5a35b7d..78279d41 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -15,29 +15,6 @@ Promise.promisifyAll(docker.getContainer().constructor.prototype) exports.docker = docker dockerProgress = new DockerProgress(socketPath: config.dockerSocket) -createContainerDisposed = (config) -> - docker.createContainerAsync(config) - .tap (container) -> - container.startAsync() - .disposer (container) -> - container.removeAsync(force: true) - -# Trailing slashes are important to rsync -rootDir = ({Driver, Id}) -> - switch Driver - when 'aufs' - "/var/lib/docker/#{Driver}/mnt/#{Id}/" - when 'btrfs' - "/var/lib/docker/#{Driver}/subvolumes/#{Id}/" - when 'devicemapper' - "/var/lib/docker/#{Driver}/mnt/#{Id}/rootfs/" - when 'overlay' - "/var/lib/docker/#{Driver}/#{Id}/merged/" - when 'vfs' - "/var/lib/docker/#{Driver}/dir/#{Id}/" - else - throw new Error("Unsupported driver: #{Driver}") - # Create an array of (repoTag, image_id, created) tuples like the output of `docker images` listRepoTagsAsync = -> docker.listImagesAsync() From 8bab80d3d773fd3b1ede152eff2edddf70f90497 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Mon, 4 May 2015 00:20:31 +0000 Subject: [PATCH 07/38] Add symlink for the host docker directory --- Dockerfile.amd64 | 1 + Dockerfile.armv7hf | 1 + Dockerfile.i386 | 1 + Dockerfile.rpi | 1 + 4 files changed, 4 insertions(+) diff --git a/Dockerfile.amd64 b/Dockerfile.amd64 index 6db092fe..8895ac57 100644 --- a/Dockerfile.amd64 +++ b/Dockerfile.amd64 @@ -30,6 +30,7 @@ COPY . /app/ RUN chmod +x /app/src/enterContainer.sh \ && /app/node_modules/.bin/coffee -c /app/src \ + && ln -sf /mnt/root/var/lib/rce /var/lib/docker \ && ln -sf /app/entry.sh /start # Needed for legacy RUN chmod +x /app/bin/gosuper diff --git a/Dockerfile.armv7hf b/Dockerfile.armv7hf index 739b80ae..27302de4 100644 --- a/Dockerfile.armv7hf +++ b/Dockerfile.armv7hf @@ -30,6 +30,7 @@ COPY . /app/ RUN chmod +x /app/src/enterContainer.sh \ && /app/node_modules/.bin/coffee -c /app/src \ + && ln -sf /mnt/root/var/lib/rce /var/lib/docker \ && ln -sf /app/entry.sh /start # Needed for legacy RUN chmod +x /app/bin/gosuper diff --git a/Dockerfile.i386 b/Dockerfile.i386 index 14b3bb6e..608772db 100644 --- a/Dockerfile.i386 +++ b/Dockerfile.i386 @@ -30,6 +30,7 @@ COPY . /app/ RUN chmod +x /app/src/enterContainer.sh \ && /app/node_modules/.bin/coffee -c /app/src \ + && ln -sf /mnt/root/var/lib/rce /var/lib/docker \ && ln -sf /app/entry.sh /start # Needed for legacy RUN chmod +x /app/bin/gosuper diff --git a/Dockerfile.rpi b/Dockerfile.rpi index 1bba692e..d0b85547 100644 --- a/Dockerfile.rpi +++ b/Dockerfile.rpi @@ -30,6 +30,7 @@ COPY . /app/ RUN chmod +x /app/src/enterContainer.sh \ && /app/node_modules/.bin/coffee -c /app/src \ + && ln -sf /mnt/root/var/lib/rce /var/lib/docker \ && ln -sf /app/entry.sh /start # Needed for legacy RUN chmod +x /app/bin/gosuper From d0b6ba17211ac9d6922d8eec491dda98b9fe1925 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Mon, 4 May 2015 00:20:53 +0000 Subject: [PATCH 08/38] Switch to http default transport --- src/config.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.coffee b/src/config.coffee index d632b633..9ffbe414 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -18,7 +18,7 @@ module.exports = config = apiEndpoint: process.env.API_ENDPOINT ? 'https://api.resin.io' listenPort: process.env.LISTEN_PORT ? 80 gosuperAddress: "http://unix:#{process.env.GOSUPER_SOCKET}:" - deltaEndpoint: process.env.DELTA_ENDPOINT ? 'https://delta.staging.resin.io' + deltaEndpoint: process.env.DELTA_ENDPOINT ? 'http://delta.staging.resin.io' registryEndpoint: process.env.REGISTRY_ENDPOINT ? 'registry.resin.io' pubnub: subscribe_key: checkValidKey(process.env.PUBNUB_SUBSCRIBE_KEY) ? process.env.DEFAULT_PUBNUB_SUBSCRIBE_KEY From 8c7964684ade7e37f7f9647ec083067191faa164 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Mon, 4 May 2015 00:21:27 +0000 Subject: [PATCH 09/38] Return the supervisor tag containing the git commit --- src/docker-utils.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 78279d41..0bfdaab2 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -38,6 +38,10 @@ findSimilarImage = (repoTag) -> if otherApplication is application return repoTag + # Otherwise return the most specific supervisor tag (commit hash) + for repoTag in repoTags when /resin\/.*-supervisor.*:[0-9a-f]{6}/.test(repoTag) + return repoTag + return config.supervisorImage exports.rsyncImageWithProgress = (imgDest, onProgress) -> From 7eefee77d019ee68fcfedd7d6219d397e3beec46 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Mon, 4 May 2015 02:41:18 +0100 Subject: [PATCH 10/38] Fix config var typo --- src/config.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.coffee b/src/config.coffee index 9ffbe414..4782a973 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -18,7 +18,7 @@ module.exports = config = apiEndpoint: process.env.API_ENDPOINT ? 'https://api.resin.io' listenPort: process.env.LISTEN_PORT ? 80 gosuperAddress: "http://unix:#{process.env.GOSUPER_SOCKET}:" - deltaEndpoint: process.env.DELTA_ENDPOINT ? 'http://delta.staging.resin.io' + deltaHost: process.env.DELTA_ENDPOINT ? 'http://delta.staging.resin.io' registryEndpoint: process.env.REGISTRY_ENDPOINT ? 'registry.resin.io' pubnub: subscribe_key: checkValidKey(process.env.PUBNUB_SUBSCRIBE_KEY) ? process.env.DEFAULT_PUBNUB_SUBSCRIBE_KEY From 0ad68b6e47c459d70328656173d90aafc23c7caf Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Mon, 4 May 2015 12:53:03 +0100 Subject: [PATCH 11/38] Add docker.io in apt deps --- Dockerfile.amd64 | 9 ++++++++- Dockerfile.armv7hf | 9 ++++++++- Dockerfile.i386 | 9 ++++++++- Dockerfile.rpi | 9 ++++++++- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Dockerfile.amd64 b/Dockerfile.amd64 index 8895ac57..ff328e39 100644 --- a/Dockerfile.amd64 +++ b/Dockerfile.amd64 @@ -4,7 +4,14 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor rsync btrfs-tools jq --no-install-recommends \ + && apt-get install -qqy \ + btrfs-tools \ + docker.io \ + jq \ + rsync \ + socat \ + supervisor \ + --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ diff --git a/Dockerfile.armv7hf b/Dockerfile.armv7hf index 27302de4..23f49ae7 100644 --- a/Dockerfile.armv7hf +++ b/Dockerfile.armv7hf @@ -4,7 +4,14 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor rsync btrfs-tools jq --no-install-recommends \ + && apt-get install -qqy \ + btrfs-tools \ + docker.io \ + jq \ + rsync \ + socat \ + supervisor \ + --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ diff --git a/Dockerfile.i386 b/Dockerfile.i386 index 608772db..dc770e9a 100644 --- a/Dockerfile.i386 +++ b/Dockerfile.i386 @@ -4,7 +4,14 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor rsync btrfs-tools jq --no-install-recommends \ + && apt-get install -qqy \ + btrfs-tools \ + docker.io \ + jq \ + rsync \ + socat \ + supervisor \ + --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ diff --git a/Dockerfile.rpi b/Dockerfile.rpi index d0b85547..8bb35941 100644 --- a/Dockerfile.rpi +++ b/Dockerfile.rpi @@ -4,7 +4,14 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor rsync btrfs-tools jq --no-install-recommends \ + && apt-get install -qqy \ + btrfs-tools \ + docker.io \ + jq \ + rsync \ + socat \ + supervisor \ + --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ From 595f80e0319f3ec7dd6dab5fd7a7febf65ae67ce Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Mon, 4 May 2015 13:03:10 +0000 Subject: [PATCH 12/38] Fix rsync stream resumption --- src/docker-utils.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 0bfdaab2..f1bf4c46 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -76,7 +76,8 @@ exports.rsyncImageWithProgress = (imgDest, onProgress) -> else resolve() - rsyncDiff.pipe(dockersync.stdin).resume() + rsyncDiff.pipe(dockersync.stdin) + rsyncDiff.resume() do -> # Keep track of the images being fetched, so we don't clean them up whilst fetching. From 004286a07eca7d8b2fa49084a9600d44e98b1e9c Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Mon, 4 May 2015 16:45:04 +0000 Subject: [PATCH 13/38] Tag the target image at the last step --- src/dockersync.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dockersync.sh b/src/dockersync.sh index 023ff373..0bae5302 100755 --- a/src/dockersync.sh +++ b/src/dockersync.sh @@ -11,7 +11,7 @@ dest=$2 config=$3 src_id=$(docker inspect -f '{{ .Id }}' "$src") -dest_id=$(cat /app/empty.tar | docker import - "$dest") +dest_id=$(cat /app/empty.tar | docker import -) jq ".config=$config" "$DOCKER_ROOT/graph/$dest_id/json" > "$DOCKER_ROOT/graph/$dest_id/json.tmp" mv "$DOCKER_ROOT/graph/$dest_id/json.tmp" "$DOCKER_ROOT/graph/$dest_id/json" @@ -20,3 +20,5 @@ btrfs subvolume delete "$BTRFS_ROOT/$dest_id" btrfs subvolume snapshot "$BTRFS_ROOT/$src_id" "$BTRFS_ROOT/$dest_id" rsync --archive --read-batch=- "$BTRFS_ROOT/$dest_id" + +docker tag -f "$dest_id" "$dest" From f6eafb7b96d072a13c034789ba4fad2481641971 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Tue, 5 May 2015 16:16:36 +0100 Subject: [PATCH 14/38] Always send the final progress event --- src/docker-utils.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index f1bf4c46..3dec390b 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -51,6 +51,8 @@ exports.rsyncImageWithProgress = (imgDest, onProgress) -> console.log("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 0) progress request.get("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 0) .on 'progress', onProgress + .on 'end', -> + onProgress(percent: 100) .on 'response', (res) -> if res.statusCode isnt 200 reject() From 79d638f4f5451fa5ed63df630be752336ec8e33f Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Thu, 7 May 2015 17:45:10 +0000 Subject: [PATCH 15/38] Pass the --delete parameter to rsync --- src/dockersync.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dockersync.sh b/src/dockersync.sh index 0bae5302..84f9001a 100755 --- a/src/dockersync.sh +++ b/src/dockersync.sh @@ -19,6 +19,6 @@ mv "$DOCKER_ROOT/graph/$dest_id/json.tmp" "$DOCKER_ROOT/graph/$dest_id/json" btrfs subvolume delete "$BTRFS_ROOT/$dest_id" btrfs subvolume snapshot "$BTRFS_ROOT/$src_id" "$BTRFS_ROOT/$dest_id" -rsync --archive --read-batch=- "$BTRFS_ROOT/$dest_id" +rsync --archive --delete --read-batch=- "$BTRFS_ROOT/$dest_id" docker tag -f "$dest_id" "$dest" From 071ee3dcf4fe2285e4df615e6b80cb28320c4d2b Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Thu, 7 May 2015 17:45:25 +0000 Subject: [PATCH 16/38] Ignore output from dockersync.sh --- src/docker-utils.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 3dec390b..f7050a51 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -70,7 +70,7 @@ exports.rsyncImageWithProgress = (imgDest, onProgress) -> return [ rsyncDiff, imageConfig, imgSrc ] .spread (rsyncDiff, imageConfig, imgSrc) -> new Promise (resolve, reject) -> - dockersync = spawn('/app/src/dockersync.sh', [ imgSrc, imgDest, JSON.stringify(imageConfig) ]) + dockersync = spawn('/app/src/dockersync.sh', [ imgSrc, imgDest, JSON.stringify(imageConfig) ], stdio: [ 'pipe', 'ignore', 'ignore' ]) .on 'error', reject .on 'exit', (code, signal) -> if code isnt 0 From 83931a09155cb840fe518dad557c75b2a64f5030 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Wed, 17 Jun 2015 23:56:29 +0000 Subject: [PATCH 17/38] Add timeout to prevent rsync from hanging --- src/dockersync.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dockersync.sh b/src/dockersync.sh index 84f9001a..2c0bd5f9 100755 --- a/src/dockersync.sh +++ b/src/dockersync.sh @@ -19,6 +19,6 @@ mv "$DOCKER_ROOT/graph/$dest_id/json.tmp" "$DOCKER_ROOT/graph/$dest_id/json" btrfs subvolume delete "$BTRFS_ROOT/$dest_id" btrfs subvolume snapshot "$BTRFS_ROOT/$src_id" "$BTRFS_ROOT/$dest_id" -rsync --archive --delete --read-batch=- "$BTRFS_ROOT/$dest_id" +rsync --timeout=30 --archive --delete --read-batch=- "$BTRFS_ROOT/$dest_id" docker tag -f "$dest_id" "$dest" From dbb7a2382cc830ddf8807e10b8c4977412fade07 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Fri, 14 Aug 2015 15:02:43 +0000 Subject: [PATCH 18/38] Use precomplied docker binary from resin github --- Dockerfile.armv7hf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile.armv7hf b/Dockerfile.armv7hf index 23f49ae7..70d9f5a9 100644 --- a/Dockerfile.armv7hf +++ b/Dockerfile.armv7hf @@ -6,7 +6,6 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ RUN apt-get -q update \ && apt-get install -qqy \ btrfs-tools \ - docker.io \ jq \ rsync \ socat \ @@ -15,6 +14,8 @@ RUN apt-get -q update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ +RUN curl -sL https://github.com/resin-io/docker/releases/download/1.5.0-dev/docker-arm-1.5.0-dev.tar.xz | tar xJv --strip-components=1 -C /usr/bin + # Copy supervisord configuration files COPY config/supervisor/ /etc/supervisor/ From a12ebf884b77e027edd87fa984a50d406aa97948 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Sun, 23 Aug 2015 21:29:17 +0300 Subject: [PATCH 19/38] Increase rsync timeout to 5 mins --- src/dockersync.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dockersync.sh b/src/dockersync.sh index 2c0bd5f9..cb3f7d48 100755 --- a/src/dockersync.sh +++ b/src/dockersync.sh @@ -19,6 +19,6 @@ mv "$DOCKER_ROOT/graph/$dest_id/json.tmp" "$DOCKER_ROOT/graph/$dest_id/json" btrfs subvolume delete "$BTRFS_ROOT/$dest_id" btrfs subvolume snapshot "$BTRFS_ROOT/$src_id" "$BTRFS_ROOT/$dest_id" -rsync --timeout=30 --archive --delete --read-batch=- "$BTRFS_ROOT/$dest_id" +rsync --timeout=300 --archive --delete --read-batch=- "$BTRFS_ROOT/$dest_id" docker tag -f "$dest_id" "$dest" From 9fb62a0faaa5660f951b87de3b4e8a3090e428e8 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Mon, 7 Sep 2015 16:29:54 +0300 Subject: [PATCH 20/38] Add missing utilities --- Dockerfile.armv7hf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile.armv7hf b/Dockerfile.armv7hf index 70d9f5a9..2735766f 100644 --- a/Dockerfile.armv7hf +++ b/Dockerfile.armv7hf @@ -6,10 +6,12 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ RUN apt-get -q update \ && apt-get install -qqy \ btrfs-tools \ + curl \ jq \ rsync \ socat \ supervisor \ + xz \ --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ From 88f0e4e229a33848fed8f29305dd312f24bd7f07 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Wed, 9 Sep 2015 19:31:20 +0300 Subject: [PATCH 21/38] Fix typo --- src/application.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/application.coffee b/src/application.coffee index b08ab23f..128dbf2f 100644 --- a/src/application.coffee +++ b/src/application.coffee @@ -127,7 +127,7 @@ isValidPort = (port) -> fetch = (app) -> onProgress = (progress) -> - device.updateState(download_progress: progress.percentage) + device.updateState(download_progress: progress.percent) docker.getImage(app.imageId).inspectAsync() .catch (error) -> From 582e3322ec627953e75215302555a1754e6d3ae3 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Wed, 16 Sep 2015 12:55:20 +0000 Subject: [PATCH 22/38] Set a 5 min timeout to delta requests --- src/docker-utils.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index f7050a51..0b6d5c33 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -48,8 +48,8 @@ exports.rsyncImageWithProgress = (imgDest, onProgress) -> findSimilarImage(imgDest) .spread (imgSrc) -> rsyncDiff = new Promise (resolve, reject) -> - console.log("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 0) - progress request.get("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 0) + console.log("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 5 * 60 * 1000) + progress request.get("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 5 * 60 * 1000) .on 'progress', onProgress .on 'end', -> onProgress(percent: 100) From f6f88941eb8567dd28f8e686bdcb3ea5c2d475c7 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Wed, 30 Sep 2015 02:45:36 +0000 Subject: [PATCH 23/38] delta: only use deltas when RESIN_DELTA is set Signed-off-by: Petros Angelatos --- src/application.coffee | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/application.coffee b/src/application.coffee index 128dbf2f..94c948d7 100644 --- a/src/application.coffee +++ b/src/application.coffee @@ -133,8 +133,13 @@ fetch = (app) -> .catch (error) -> logSystemEvent(logTypes.downloadApp, app) device.updateState(status: 'Downloading', download_progress: 0) - dockerUtils.rsyncImageWithProgress(app.imageId, onProgress) - .then -> + + if app.env.RESIN_DELTA? + fetchPromise = dockerUtils.rsyncImageWithProgress(app.imageId, onProgress) + else + fetchPromise = dockerUtils.fetchImageWithProgress(app.imageId, onProgress) + + fetchPromise.then -> logSystemEvent(logTypes.downloadAppSuccess, app) device.updateState(status: 'Idle', download_progress: null) docker.getImage(app.imageId).inspectAsync() From f7a2668c4f141339178631a8f2666062fe207764 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Wed, 30 Sep 2015 02:49:52 +0000 Subject: [PATCH 24/38] config: change default delta endpoint to https and production Signed-off-by: Petros Angelatos --- src/config.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.coffee b/src/config.coffee index 4782a973..b0efeefb 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -18,7 +18,7 @@ module.exports = config = apiEndpoint: process.env.API_ENDPOINT ? 'https://api.resin.io' listenPort: process.env.LISTEN_PORT ? 80 gosuperAddress: "http://unix:#{process.env.GOSUPER_SOCKET}:" - deltaHost: process.env.DELTA_ENDPOINT ? 'http://delta.staging.resin.io' + deltaHost: process.env.DELTA_ENDPOINT ? 'https://delta.resin.io' registryEndpoint: process.env.REGISTRY_ENDPOINT ? 'registry.resin.io' pubnub: subscribe_key: checkValidKey(process.env.PUBNUB_SUBSCRIBE_KEY) ? process.env.DEFAULT_PUBNUB_SUBSCRIBE_KEY From 607bfdf698a271ae2103e5ad5a96f670e26a268b Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Wed, 7 Oct 2015 18:43:05 +0100 Subject: [PATCH 25/38] Fix armv7 build Signed-off-by: Petros Angelatos --- Dockerfile.armv7hf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.armv7hf b/Dockerfile.armv7hf index 2735766f..b3454808 100644 --- a/Dockerfile.armv7hf +++ b/Dockerfile.armv7hf @@ -6,12 +6,12 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ RUN apt-get -q update \ && apt-get install -qqy \ btrfs-tools \ - curl \ + curl \ jq \ rsync \ socat \ supervisor \ - xz \ + xz-utils \ --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ From 7deab91368f2f6fedee353aa0ba7a85fa557d4d5 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Wed, 7 Oct 2015 18:52:35 +0100 Subject: [PATCH 26/38] Add ca-certificates Signed-off-by: Petros Angelatos --- Dockerfile.armv7hf | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile.armv7hf b/Dockerfile.armv7hf index b3454808..499d14a1 100644 --- a/Dockerfile.armv7hf +++ b/Dockerfile.armv7hf @@ -6,6 +6,7 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ RUN apt-get -q update \ && apt-get install -qqy \ btrfs-tools \ + ca-certificates \ curl \ jq \ rsync \ From 5aa643aba47cbe468ba95851d4b816fd937a22a2 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Mon, 2 Nov 2015 23:33:32 +0000 Subject: [PATCH 27/38] Fix Dockerfile dependencies on all platforms --- Dockerfile.amd64 | 6 +++++- Dockerfile.armv7hf | 3 ++- Dockerfile.i386 | 7 ++++++- Dockerfile.rpi | 2 ++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Dockerfile.amd64 b/Dockerfile.amd64 index ff328e39..e1fb0991 100644 --- a/Dockerfile.amd64 +++ b/Dockerfile.amd64 @@ -6,7 +6,8 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ RUN apt-get -q update \ && apt-get install -qqy \ btrfs-tools \ - docker.io \ + ca-certificates \ + curl \ jq \ rsync \ socat \ @@ -15,6 +16,9 @@ RUN apt-get -q update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ +ENV DOCKER_VERSION 1.6.2 +RUN curl -sL https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_VERSION}.tgz | tar xzv --strip-components=3 -C /usr/bin + # Copy supervisord configuration files COPY config/supervisor/ /etc/supervisor/ diff --git a/Dockerfile.armv7hf b/Dockerfile.armv7hf index 499d14a1..56a86ec6 100644 --- a/Dockerfile.armv7hf +++ b/Dockerfile.armv7hf @@ -17,7 +17,8 @@ RUN apt-get -q update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ -RUN curl -sL https://github.com/resin-io/docker/releases/download/1.5.0-dev/docker-arm-1.5.0-dev.tar.xz | tar xJv --strip-components=1 -C /usr/bin +ENV DOCKER_VERSION 1.6.2 +RUN curl -sL https://github.com/resin-io/docker/releases/download/${DOCKER_VERSION}/docker-arm-${DOCKER_VERSION}.tar.xz | tar xJv --strip-components=1 -C /usr/bin # Copy supervisord configuration files COPY config/supervisor/ /etc/supervisor/ diff --git a/Dockerfile.i386 b/Dockerfile.i386 index dc770e9a..f5ad78e8 100644 --- a/Dockerfile.i386 +++ b/Dockerfile.i386 @@ -6,15 +6,20 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ RUN apt-get -q update \ && apt-get install -qqy \ btrfs-tools \ - docker.io \ + ca-certificates \ + curl \ jq \ rsync \ socat \ supervisor \ + xz-utils \ --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ +ENV DOCKER_VERSION 1.6.2 +RUN curl -sL https://github.com/resin-io/docker/releases/download/${DOCKER_VERSION}/docker-386-${DOCKER_VERSION}.tar.xz | tar xJv --strip-components=1 -C /usr/bin + # Copy supervisord configuration files COPY config/supervisor/ /etc/supervisor/ diff --git a/Dockerfile.rpi b/Dockerfile.rpi index 8bb35941..f1bf4246 100644 --- a/Dockerfile.rpi +++ b/Dockerfile.rpi @@ -6,6 +6,8 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ RUN apt-get -q update \ && apt-get install -qqy \ btrfs-tools \ + ca-certificates \ + curl \ docker.io \ jq \ rsync \ From 1bc149866d82946df8e7f1c8fdc57010ca42f2c6 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Wed, 4 Nov 2015 05:49:32 +0000 Subject: [PATCH 28/38] deltas: sync() after running rsync on the new subvolume --- src/dockersync.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dockersync.sh b/src/dockersync.sh index cb3f7d48..4c3adbd7 100755 --- a/src/dockersync.sh +++ b/src/dockersync.sh @@ -21,4 +21,6 @@ btrfs subvolume snapshot "$BTRFS_ROOT/$src_id" "$BTRFS_ROOT/$dest_id" rsync --timeout=300 --archive --delete --read-batch=- "$BTRFS_ROOT/$dest_id" +sync + docker tag -f "$dest_id" "$dest" From 8e4e844cfa5ea3c494ed84044c8c5f6decf1b593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Carranza=20V=C3=A9lez?= Date: Wed, 11 Nov 2015 00:51:51 -0300 Subject: [PATCH 29/38] JSON.parse env before checking for delta, and use progress.percentage --- src/application.coffee | 22 ++++++++++++---------- src/docker-utils.coffee | 5 +++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/application.coffee b/src/application.coffee index 94c948d7..ae4f98da 100644 --- a/src/application.coffee +++ b/src/application.coffee @@ -127,22 +127,24 @@ isValidPort = (port) -> fetch = (app) -> onProgress = (progress) -> - device.updateState(download_progress: progress.percent) + device.updateState(download_progress: progress.percentage) docker.getImage(app.imageId).inspectAsync() .catch (error) -> logSystemEvent(logTypes.downloadApp, app) device.updateState(status: 'Downloading', download_progress: 0) - if app.env.RESIN_DELTA? - fetchPromise = dockerUtils.rsyncImageWithProgress(app.imageId, onProgress) - else - fetchPromise = dockerUtils.fetchImageWithProgress(app.imageId, onProgress) - - fetchPromise.then -> - logSystemEvent(logTypes.downloadAppSuccess, app) - device.updateState(status: 'Idle', download_progress: null) - docker.getImage(app.imageId).inspectAsync() + Promise.try -> + JSON.parse(app.env) + .then (env) -> + if env['RESIN_DELTA'] == '1' + fetchPromise = dockerUtils.rsyncImageWithProgress(app.imageId, onProgress) + else + fetchPromise = dockerUtils.fetchImageWithProgress(app.imageId, onProgress) + fetchPromise.then -> + logSystemEvent(logTypes.downloadAppSuccess, app) + device.updateState(status: 'Idle', download_progress: null) + docker.getImage(app.imageId).inspectAsync() .catch (err) -> logSystemEvent(logTypes.downloadAppError, app, err) throw err diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 0b6d5c33..6f230d29 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -50,9 +50,10 @@ exports.rsyncImageWithProgress = (imgDest, onProgress) -> rsyncDiff = new Promise (resolve, reject) -> console.log("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 5 * 60 * 1000) progress request.get("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 5 * 60 * 1000) - .on 'progress', onProgress + .on 'progress', (progress) -> + onProgress(percentage: progress.percent) .on 'end', -> - onProgress(percent: 100) + onProgress(percentage: 100) .on 'response', (res) -> if res.statusCode isnt 200 reject() From 1a5a9b0e23360f399c6c6cb2d4ecbbd9cb0eba92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Carranza=20V=C3=A9lez?= Date: Wed, 11 Nov 2015 01:23:39 -0300 Subject: [PATCH 30/38] findSimilarImage should always return an array, and test the repoTag[0] --- src/docker-utils.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 6f230d29..17f5ff22 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -39,10 +39,10 @@ findSimilarImage = (repoTag) -> return repoTag # Otherwise return the most specific supervisor tag (commit hash) - for repoTag in repoTags when /resin\/.*-supervisor.*:[0-9a-f]{6}/.test(repoTag) + for repoTag in repoTags when /resin\/.*-supervisor.*:[0-9a-f]{6}/.test(repoTag[0]) return repoTag - return config.supervisorImage + return [ config.supervisorImage ] exports.rsyncImageWithProgress = (imgDest, onProgress) -> findSimilarImage(imgDest) From 5b48856c9af54793e52394284a7363f85d25f0fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Carranza=20V=C3=A9lez?= Date: Wed, 11 Nov 2015 01:42:15 -0300 Subject: [PATCH 31/38] Bring back request - and show an error when talking to delta server fails --- src/docker-utils.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 17f5ff22..8eed2bb1 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -7,6 +7,8 @@ config = require './config' _ = require 'lodash' knex = require './db' +{ request } = require './request' + docker = Promise.promisifyAll(new Docker(socketPath: config.dockerSocket)) # Hack dockerode to promisify internal classes' prototypes Promise.promisifyAll(docker.getImage().constructor.prototype) @@ -56,7 +58,7 @@ exports.rsyncImageWithProgress = (imgDest, onProgress) -> onProgress(percentage: 100) .on 'response', (res) -> if res.statusCode isnt 200 - reject() + reject(new Error("Got #{res.statusCode} when requesting image from delta server.")) else resolve(res) .on 'error', reject From b9eb6836bef509263ccf6ad710641f21f0d8683f Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Tue, 1 Dec 2015 15:32:42 -0800 Subject: [PATCH 32/38] Better check for source image. Don't use config.supervisorImage, and use image Id's instead of tags. Also, change the RESIN_DELTA var into the RESIN_SUPERVISOR_ namespace. --- src/application.coffee | 14 +++++++------- src/docker-utils.coffee | 17 +++++++++++------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/application.coffee b/src/application.coffee index ae4f98da..0b18c48f 100644 --- a/src/application.coffee +++ b/src/application.coffee @@ -137,14 +137,14 @@ fetch = (app) -> Promise.try -> JSON.parse(app.env) .then (env) -> - if env['RESIN_DELTA'] == '1' - fetchPromise = dockerUtils.rsyncImageWithProgress(app.imageId, onProgress) + if env['RESIN_SUPERVISOR_DELTA'] == '1' + dockerUtils.rsyncImageWithProgress(app.imageId, onProgress) else - fetchPromise = dockerUtils.fetchImageWithProgress(app.imageId, onProgress) - fetchPromise.then -> - logSystemEvent(logTypes.downloadAppSuccess, app) - device.updateState(status: 'Idle', download_progress: null) - docker.getImage(app.imageId).inspectAsync() + dockerUtils.fetchImageWithProgress(app.imageId, onProgress) + .then -> + logSystemEvent(logTypes.downloadAppSuccess, app) + device.updateState(status: 'Idle', download_progress: null) + docker.getImage(app.imageId).inspectAsync() .catch (err) -> logSystemEvent(logTypes.downloadAppError, app, err) throw err diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 8eed2bb1..f4143268 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -28,7 +28,8 @@ listRepoTagsAsync = -> ret.push [ repoTag, image.Id, image.Created ] return ret -# Find either the most recent image of the same app or the image of the supervisor +# Find either the most recent image of the same app or the image of the supervisor. +# Returns an image Id or Tag (depending on whatever's available) findSimilarImage = (repoTag) -> application = repoTag.split('/')[1] @@ -38,19 +39,23 @@ findSimilarImage = (repoTag) -> for repoTag in repoTags otherApplication = repoTag[0].split('/')[1] if otherApplication is application - return repoTag + return repoTag[1] - # Otherwise return the most specific supervisor tag (commit hash) + # Otherwise return the image for the most specific supervisor tag (commit hash) for repoTag in repoTags when /resin\/.*-supervisor.*:[0-9a-f]{6}/.test(repoTag[0]) - return repoTag + return repoTag[1] - return [ config.supervisorImage ] + # Or return *any* supervisor image available + for repoTag in repoTags when /resin\/.*-supervisor.*:/.test(repoTag[0]) + return repoTag[1] + + # If all else fails, return the newest image available + return repoTags[0][1] exports.rsyncImageWithProgress = (imgDest, onProgress) -> findSimilarImage(imgDest) .spread (imgSrc) -> rsyncDiff = new Promise (resolve, reject) -> - console.log("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 5 * 60 * 1000) progress request.get("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 5 * 60 * 1000) .on 'progress', (progress) -> onProgress(percentage: progress.percent) From d3fea26c115f1ce82135dbf88cb699ccb45d51dd Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Thu, 3 Dec 2015 13:01:48 -0800 Subject: [PATCH 33/38] Send both source tag and Id to delta server --- src/docker-utils.coffee | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index f4143268..e73fd88a 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -39,24 +39,24 @@ findSimilarImage = (repoTag) -> for repoTag in repoTags otherApplication = repoTag[0].split('/')[1] if otherApplication is application - return repoTag[1] + return repoTag # Otherwise return the image for the most specific supervisor tag (commit hash) for repoTag in repoTags when /resin\/.*-supervisor.*:[0-9a-f]{6}/.test(repoTag[0]) - return repoTag[1] + return repoTag - # Or return *any* supervisor image available - for repoTag in repoTags when /resin\/.*-supervisor.*:/.test(repoTag[0]) - return repoTag[1] + # Or return *any* supervisor image available (except latest which is usually a phony tag) + for repoTag in repoTags when /resin\/.*-supervisor.*:(?!latest)/.test(repoTag[0]) + return repoTag # If all else fails, return the newest image available - return repoTags[0][1] + return repoTags[0] exports.rsyncImageWithProgress = (imgDest, onProgress) -> findSimilarImage(imgDest) - .spread (imgSrc) -> + .spread (imgSrc, imgSrcId) -> rsyncDiff = new Promise (resolve, reject) -> - progress request.get("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 5 * 60 * 1000) + progress request.get("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&srcId=#{imgSrcId}&dest=#{imgDest}", timeout: 5 * 60 * 1000) .on 'progress', (progress) -> onProgress(percentage: progress.percent) .on 'end', -> From 9dd1fa24089a45b33e64cc035be30cd120ccea4b Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Tue, 22 Dec 2015 00:02:59 +0000 Subject: [PATCH 34/38] Catch out of sync errors falling back to scratch. Make update retries more dependent on number of failures --- src/application.coffee | 2 +- src/docker-utils.coffee | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/application.coffee b/src/application.coffee index 0b18c48f..705bb537 100644 --- a/src/application.coffee +++ b/src/application.coffee @@ -586,7 +586,7 @@ application.update = update = (force) -> if updateStatus.state is UPDATE_REQUIRED console.log('Updating failed, but there is already another update scheduled immediately: ', err) return - delayTime = Math.min(updateStatus.failed * 500, 30000) + delayTime = Math.min((2 ** updateStatus.failed) * 500, 30000) # If there was an error then schedule another attempt briefly in the future. console.log('Scheduling another update attempt due to failure: ', delayTime, err) setTimeout(update, delayTime, force) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index e73fd88a..c31089a7 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -6,9 +6,11 @@ progress = require 'request-progress' config = require './config' _ = require 'lodash' knex = require './db' - +TypedError = require 'typed-error' { request } = require './request' +class OutOfSyncError extends TypedError + docker = Promise.promisifyAll(new Docker(socketPath: config.dockerSocket)) # Hack dockerode to promisify internal classes' prototypes Promise.promisifyAll(docker.getImage().constructor.prototype) @@ -39,24 +41,32 @@ findSimilarImage = (repoTag) -> for repoTag in repoTags otherApplication = repoTag[0].split('/')[1] if otherApplication is application - return repoTag + return repoTag[0] # Otherwise return the image for the most specific supervisor tag (commit hash) for repoTag in repoTags when /resin\/.*-supervisor.*:[0-9a-f]{6}/.test(repoTag[0]) - return repoTag + return repoTag[0] # Or return *any* supervisor image available (except latest which is usually a phony tag) for repoTag in repoTags when /resin\/.*-supervisor.*:(?!latest)/.test(repoTag[0]) - return repoTag + return repoTag[0] # If all else fails, return the newest image available - return repoTags[0] + for repoTag in repoTags when repoTag[0] isnt ':' + return repoTag[0] -exports.rsyncImageWithProgress = (imgDest, onProgress) -> - findSimilarImage(imgDest) - .spread (imgSrc, imgSrcId) -> + return 'resin/scratch' + +DELTA_OUT_OF_SYNC_CODES = [23, 24] + +exports.rsyncImageWithProgress = (imgDest, onProgress, startFromEmpty = false) -> + Promise.try -> + if startFromEmpty + return 'resin/scratch' + findSimilarImage(imgDest) + .then (imgSrc) -> rsyncDiff = new Promise (resolve, reject) -> - progress request.get("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&srcId=#{imgSrcId}&dest=#{imgDest}", timeout: 5 * 60 * 1000) + progress request.get("#{config.deltaHost}/api/v1/delta?src=#{imgSrc}&dest=#{imgDest}", timeout: 5 * 60 * 1000) .on 'progress', (progress) -> onProgress(percentage: progress.percent) .on 'end', -> @@ -78,16 +88,23 @@ exports.rsyncImageWithProgress = (imgDest, onProgress) -> return [ rsyncDiff, imageConfig, imgSrc ] .spread (rsyncDiff, imageConfig, imgSrc) -> new Promise (resolve, reject) -> - dockersync = spawn('/app/src/dockersync.sh', [ imgSrc, imgDest, JSON.stringify(imageConfig) ], stdio: [ 'pipe', 'ignore', 'ignore' ]) + dockersync = spawn('/app/src/dockersync.sh', [ imgSrc, imgDest, JSON.stringify(imageConfig) ], stdio: [ 'pipe', 'pipe', 'pipe' ]) .on 'error', reject .on 'exit', (code, signal) -> - if code isnt 0 + 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(dockersync.stdin) + dockersync.stdout.pipe(process.stdout) + dockersync.stderr.pipe(process.stdout) rsyncDiff.resume() + .catch OutOfSyncError, (err) -> + console.log('Falling back to delta-from-empty') + exports.rsyncImageWithProgress(imgDest, onProgress, true) do -> # Keep track of the images being fetched, so we don't clean them up whilst fetching. From 676f6699fe7470006ec4baac6bf5bb13ea4b53fa Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Thu, 14 Jan 2016 18:15:26 +0000 Subject: [PATCH 35/38] Nicer use of spawn --- src/docker-utils.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index c31089a7..787dae5b 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -88,7 +88,7 @@ exports.rsyncImageWithProgress = (imgDest, onProgress, startFromEmpty = false) - return [ rsyncDiff, imageConfig, imgSrc ] .spread (rsyncDiff, imageConfig, imgSrc) -> new Promise (resolve, reject) -> - dockersync = spawn('/app/src/dockersync.sh', [ imgSrc, imgDest, JSON.stringify(imageConfig) ], stdio: [ 'pipe', 'pipe', 'pipe' ]) + dockersync = spawn('/app/src/dockersync.sh', [ imgSrc, imgDest, JSON.stringify(imageConfig) ], stdio: 'pipe') .on 'error', reject .on 'exit', (code, signal) -> if code in DELTA_OUT_OF_SYNC_CODES From 78f0df281b012b3821fe1391941f287060b409cf Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 20 Jan 2016 14:56:09 +0000 Subject: [PATCH 36/38] Replace dockersync.sh with node and dockerode stuff. Don't install docker. Add Dockerfile changes for armel. --- Dockerfile.amd64 | 6 ---- Dockerfile.armel | 8 ++++- Dockerfile.armv7hf | 7 ---- Dockerfile.i386 | 7 ---- Dockerfile.rpi | 4 --- package.json | 4 +-- src/config.coffee | 4 +++ src/docker-utils.coffee | 74 +++++++++++++++++++++++++++++++---------- src/dockersync.sh | 26 --------------- 9 files changed, 70 insertions(+), 70 deletions(-) delete mode 100755 src/dockersync.sh diff --git a/Dockerfile.amd64 b/Dockerfile.amd64 index e1fb0991..9c249b38 100644 --- a/Dockerfile.amd64 +++ b/Dockerfile.amd64 @@ -7,8 +7,6 @@ RUN apt-get -q update \ && apt-get install -qqy \ btrfs-tools \ ca-certificates \ - curl \ - jq \ rsync \ socat \ supervisor \ @@ -16,9 +14,6 @@ RUN apt-get -q update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ -ENV DOCKER_VERSION 1.6.2 -RUN curl -sL https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_VERSION}.tgz | tar xzv --strip-components=3 -C /usr/bin - # Copy supervisord configuration files COPY config/supervisor/ /etc/supervisor/ @@ -41,7 +36,6 @@ COPY . /app/ RUN chmod +x /app/src/enterContainer.sh \ && /app/node_modules/.bin/coffee -c /app/src \ - && ln -sf /mnt/root/var/lib/rce /var/lib/docker \ && ln -sf /app/entry.sh /start # Needed for legacy RUN chmod +x /app/bin/gosuper diff --git a/Dockerfile.armel b/Dockerfile.armel index e68e1190..eeef7035 100644 --- a/Dockerfile.armel +++ b/Dockerfile.armel @@ -4,7 +4,13 @@ COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/ # Supervisor apt dependencies RUN apt-get -q update \ - && apt-get install -qqy socat supervisor --no-install-recommends \ + && apt-get install -qqy \ + btrfs-tools \ + ca-certificates \ + rsync \ + socat \ + supervisor \ + --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ diff --git a/Dockerfile.armv7hf b/Dockerfile.armv7hf index 56a86ec6..972b2dbf 100644 --- a/Dockerfile.armv7hf +++ b/Dockerfile.armv7hf @@ -7,19 +7,13 @@ RUN apt-get -q update \ && apt-get install -qqy \ btrfs-tools \ ca-certificates \ - curl \ - jq \ rsync \ socat \ supervisor \ - xz-utils \ --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ -ENV DOCKER_VERSION 1.6.2 -RUN curl -sL https://github.com/resin-io/docker/releases/download/${DOCKER_VERSION}/docker-arm-${DOCKER_VERSION}.tar.xz | tar xJv --strip-components=1 -C /usr/bin - # Copy supervisord configuration files COPY config/supervisor/ /etc/supervisor/ @@ -42,7 +36,6 @@ COPY . /app/ RUN chmod +x /app/src/enterContainer.sh \ && /app/node_modules/.bin/coffee -c /app/src \ - && ln -sf /mnt/root/var/lib/rce /var/lib/docker \ && ln -sf /app/entry.sh /start # Needed for legacy RUN chmod +x /app/bin/gosuper diff --git a/Dockerfile.i386 b/Dockerfile.i386 index f5ad78e8..137e487f 100644 --- a/Dockerfile.i386 +++ b/Dockerfile.i386 @@ -7,19 +7,13 @@ RUN apt-get -q update \ && apt-get install -qqy \ btrfs-tools \ ca-certificates \ - curl \ - jq \ rsync \ socat \ supervisor \ - xz-utils \ --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/ -ENV DOCKER_VERSION 1.6.2 -RUN curl -sL https://github.com/resin-io/docker/releases/download/${DOCKER_VERSION}/docker-386-${DOCKER_VERSION}.tar.xz | tar xJv --strip-components=1 -C /usr/bin - # Copy supervisord configuration files COPY config/supervisor/ /etc/supervisor/ @@ -42,7 +36,6 @@ COPY . /app/ RUN chmod +x /app/src/enterContainer.sh \ && /app/node_modules/.bin/coffee -c /app/src \ - && ln -sf /mnt/root/var/lib/rce /var/lib/docker \ && ln -sf /app/entry.sh /start # Needed for legacy RUN chmod +x /app/bin/gosuper diff --git a/Dockerfile.rpi b/Dockerfile.rpi index f1bf4246..4eedf1be 100644 --- a/Dockerfile.rpi +++ b/Dockerfile.rpi @@ -7,9 +7,6 @@ RUN apt-get -q update \ && apt-get install -qqy \ btrfs-tools \ ca-certificates \ - curl \ - docker.io \ - jq \ rsync \ socat \ supervisor \ @@ -39,7 +36,6 @@ COPY . /app/ RUN chmod +x /app/src/enterContainer.sh \ && /app/node_modules/.bin/coffee -c /app/src \ - && ln -sf /mnt/root/var/lib/rce /var/lib/docker \ && ln -sf /app/entry.sh /start # Needed for legacy RUN chmod +x /app/bin/gosuper diff --git a/package.json b/package.json index 10de7afe..d1e02c07 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,8 @@ "bluebird": "^2.9.24", "body-parser": "^1.12.0", "coffee-script": "~1.9.1", - "docker-progress": "^1.1.0", - "dockerode": "~2.2.1", + "docker-progress": "^2.0.0", + "dockerode": "~2.2.9", "event-stream": "^3.0.20", "express": "^4.0.0", "knex": "~0.8.3", diff --git a/src/config.coffee b/src/config.coffee index b0efeefb..857d92b1 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -13,6 +13,8 @@ checkValidKey = (s) -> return return s +dockerRoot = process.env.DOCKER_ROOT ? '/mnt/root/var/lib/rce' + # Defaults needed for both gosuper and node supervisor are declared in entry.sh module.exports = config = apiEndpoint: process.env.API_ENDPOINT ? 'https://api.resin.io' @@ -38,3 +40,5 @@ module.exports = config = vpnStatusPath: process.env.VPN_STATUS_PATH ? '/mnt/root/run/openvpn/vpn_status' checkInt: checkInt hostOsVersionPath: process.env.HOST_OS_VERSION_PATH ? '/mnt/root/etc/os-release' + dockerRoot: dockerRoot + btrfsRoot: process.env.BTRFS_ROOT ? "#{dockerRoot}/btrfs/subvolumes" diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 787dae5b..9d260d12 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -1,13 +1,14 @@ Docker = require 'dockerode' -DockerProgress = require 'docker-progress' +{ getRegistryAndName, DockerProgress } = require 'docker-progress' Promise = require 'bluebird' -{ spawn } = require 'child_process' +{ spawn, execAsync } = Promise.promisifyAll require 'child_process' progress = require 'request-progress' config = require './config' _ = require 'lodash' knex = require './db' TypedError = require 'typed-error' { request } = require './request' +fs = Promise.promisifyAll require 'fs' class OutOfSyncError extends TypedError @@ -87,25 +88,64 @@ exports.rsyncImageWithProgress = (imgDest, onProgress, startFromEmpty = false) - return [ rsyncDiff, imageConfig, imgSrc ] .spread (rsyncDiff, imageConfig, imgSrc) -> - new Promise (resolve, reject) -> - dockersync = spawn('/app/src/dockersync.sh', [ imgSrc, imgDest, JSON.stringify(imageConfig) ], 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(dockersync.stdin) - dockersync.stdout.pipe(process.stdout) - dockersync.stderr.pipe(process.stdout) - rsyncDiff.resume() + dockerSync(imgSrc, imgDest, rsyncDiff, imageConfig) .catch OutOfSyncError, (err) -> console.log('Falling back to delta-from-empty') exports.rsyncImageWithProgress(imgDest, onProgress, true) +getRepoAndTag = (image) -> + getRegistryAndName(image) + .then ({ registry, imageName, tagName }) -> + 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 -> # Keep track of the images being fetched, so we don't clean them up whilst fetching. imagesBeingFetched = 0 diff --git a/src/dockersync.sh b/src/dockersync.sh deleted file mode 100755 index 4c3adbd7..00000000 --- a/src/dockersync.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -set -o errexit -set -o pipefail - -DOCKER_ROOT=${DOCKER_ROOT:-/var/lib/docker} -BTRFS_ROOT=${BTRFS_ROOT:-$DOCKER_ROOT/btrfs/subvolumes} - -src=$1 -dest=$2 -config=$3 - -src_id=$(docker inspect -f '{{ .Id }}' "$src") -dest_id=$(cat /app/empty.tar | docker import -) - -jq ".config=$config" "$DOCKER_ROOT/graph/$dest_id/json" > "$DOCKER_ROOT/graph/$dest_id/json.tmp" -mv "$DOCKER_ROOT/graph/$dest_id/json.tmp" "$DOCKER_ROOT/graph/$dest_id/json" - -btrfs subvolume delete "$BTRFS_ROOT/$dest_id" -btrfs subvolume snapshot "$BTRFS_ROOT/$src_id" "$BTRFS_ROOT/$dest_id" - -rsync --timeout=300 --archive --delete --read-batch=- "$BTRFS_ROOT/$dest_id" - -sync - -docker tag -f "$dest_id" "$dest" From 27e8e3c0278954b213d0537aab1aa7405099e6ea Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 27 Jan 2016 19:21:39 +0000 Subject: [PATCH 37/38] Update docker-progress and drop default port from repo tag --- package.json | 2 +- src/docker-utils.coffee | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d1e02c07..e3e3cb58 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "bluebird": "^2.9.24", "body-parser": "^1.12.0", "coffee-script": "~1.9.1", - "docker-progress": "^2.0.0", + "docker-progress": "^2.0.1", "dockerode": "~2.2.9", "event-stream": "^3.0.20", "express": "^4.0.0", diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 9d260d12..b08929bc 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -96,6 +96,7 @@ exports.rsyncImageWithProgress = (imgDest, onProgress, startFromEmpty = false) - getRepoAndTag = (image) -> getRegistryAndName(image) .then ({ registry, imageName, tagName }) -> + registry = registry.toString().replace(':443','') return { repo: "#{registry}/#{imageName}", tag: tagName } dockerSync = (imgSrc, imgDest, rsyncDiff, conf) -> From 6dcee245fdfb06f9ac672dfc9bfb95c4679ebd45 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Thu, 28 Jan 2016 11:10:15 -0300 Subject: [PATCH 38/38] Changelog entry for deltas --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b810237..fcd6664a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +* Add support for delta image download [petrosagg and Pablo] + # v1.4.0 * Report Host OS version to the API [Pablo]