diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..ca3d19a7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +* Switched to using the dockerode pull progress mechanism. diff --git a/package.json b/package.json index 3d12d255..b7fd109a 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "bluebird": "^2.6.0", "body-parser": "^1.12.0", "coffee-script": "~1.9.1", - "dockerode": "~2.0.0", + "dockerode": "~2.1.1", "event-stream": "^3.0.20", "express": "^4.0.0", "knex": "~0.7.3", diff --git a/src/docker-utils.coffee b/src/docker-utils.coffee index 771703a4..47c84d0c 100644 --- a/src/docker-utils.coffee +++ b/src/docker-utils.coffee @@ -37,21 +37,15 @@ do -> # Pull docker image returning a stream that reports progress # This is less safe than fetchImage and shouldnt be used for supervisor updates - exports.fetchImageWithProgress = (image, progressUpdates) -> + exports.fetchImageWithProgress = (image, onProgress) -> imagesBeingFetched++ - docker.createImageAsync(fromImage: image) - .then (stream) -> - return new Promise (resolve, reject) -> - stream.on('error', reject) - .pipe(JSONStream.parse()) - .on('error', reject) - .pipe(pullProgressStream(image)) - .pipe es.mapSync (data) -> - if data.error? - reject(data.error) - else if data.totalProgress? - progressUpdates?(data.totalProgress) - .on('end', resolve) + Promise.join( + docker.pullAsync(image) + pullProgress(image, onProgress) + (stream, onProgress) -> + Promise.fromNode (callback) -> + docker.modem.followProgress(stream, callback, onProgress) + ) .finally -> imagesBeingFetched-- @@ -178,23 +172,14 @@ do -> throw new Error("Invalid image name, expected domain.tld/repo/image format.") return {registry, imageName} - # Create a stream that transforms docker pull output + # Create a stream that transforms `docker.modem.followProgress` onProgress events # to include total progress metrics. - # - # The docker pull output should be piped to this stream as separate javascript objects. - # - # Stream: - # { status: "status", progressDetail: { current } } - # => - # { status: "status", progressDetail: { current }, totalProgress: { downloadedSize, totalSize, percentage } } - pullProgressStream = (image) -> - totalSize = 0 - completedSize = 0 - currentSize = 0 - sizePromise = getLayerDownloadSizes(image).tap (layerSizes) -> - totalSize = _.reduce(layerSizes, ((t,x) -> t+x), 0) - es.map (pull, callback) -> - sizePromise.then (layerSizes) -> + pullProgress = (image, onProgress) -> + getLayerDownloadSizes(image).then (layerSizes) -> + currentSize = 0 + completedSize = 0 + totalSize = _.sum(layerSizes) + return (pull) -> if pull.status == 'Downloading' currentSize = pull.progressDetail.current else if pull.status == 'Download complete' @@ -209,7 +194,5 @@ do -> totalSize = null downloadedSize = completedSize + currentSize percentage = calculatePercentage(downloadedSize, totalSize) - pull.totalProgress = { downloadedSize, totalSize, percentage } - callback(null, pull) - .catch (e) -> - callback(null, { error: e.message }) + + onProgress({ downloadedSize, totalSize, percentage })