Update resin-preload to 4.0.2 to support preloading Edison images

Change-Type: patch
This commit is contained in:
Alexis Svinartchouk 2017-10-06 11:05:24 +02:00
parent dad655c9ec
commit 119fa78927
3 changed files with 203 additions and 138 deletions

View File

@ -127,8 +127,8 @@ offerToDisableAutomaticUpdates = function(application, commit) {
module.exports = { module.exports = {
signature: 'preload <image>', signature: 'preload <image>',
description: '(beta) preload an app on a disk image', description: '(beta) preload an app on a disk image (or Edison zip archive)',
help: 'Warning: "resin preload" requires Docker to be correctly installed in\nyour shell environment. For more information (including Windows support)\nplease check the README here: https://github.com/resin-io/resin-cli .\n\nUse this command to preload an application to a local disk image with a\nbuilt commit from Resin.io.\nThis can be used with cloud builds, or images deployed with resin deploy.\n\nExamples:\n $ resin preload resin.img --app 1234 --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0 --splash-image some-image.png\n $ resin preload resin.img', help: 'Warning: "resin preload" requires Docker to be correctly installed in\nyour shell environment. For more information (including Windows support)\nplease check the README here: https://github.com/resin-io/resin-cli .\n\nUse this command to preload an application to a local disk image (or\nEdison zip archive) with a built commit from Resin.io.\nThis can be used with cloud builds, or images deployed with resin deploy.\n\nExamples:\n $ resin preload resin.img --app 1234 --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0 --splash-image some-image.png\n $ resin preload resin.img',
permission: 'user', permission: 'user',
primary: true, primary: true,
options: dockerUtils.appendConnectionOptions([ options: dockerUtils.appendConnectionOptions([
@ -158,7 +158,7 @@ module.exports = {
} }
]), ]),
action: function(params, options, done) { action: function(params, options, done) {
var Promise, _, errors, expectedError, form, imageInfoSpinner, preload, resin, streamToPromise, visuals; var Promise, _, errors, expectedError, form, nodeCleanup, preload, progressBars, progressHandler, resin, spinnerHandler, spinners, streamToPromise, visuals;
_ = require('lodash'); _ = require('lodash');
Promise = require('bluebird'); Promise = require('bluebird');
resin = require('resin-sdk-preconfigured'); resin = require('resin-sdk-preconfigured');
@ -167,8 +167,33 @@ module.exports = {
preload = require('resin-preload'); preload = require('resin-preload');
errors = require('resin-errors'); errors = require('resin-errors');
visuals = require('resin-cli-visuals'); visuals = require('resin-cli-visuals');
nodeCleanup = require('node-cleanup');
expectedError = require('../utils/patterns').expectedError; expectedError = require('../utils/patterns').expectedError;
imageInfoSpinner = new visuals.Spinner('Reading image device type and preloaded builds.'); progressBars = {};
progressHandler = function(event) {
var progressBar;
progressBar = progressBars[event.name];
if (!progressBar) {
progressBar = progressBars[event.name] = new visuals.Progress(event.name);
}
return progressBar.update({
percentage: event.percentage
});
};
spinners = {};
spinnerHandler = function(event) {
var spinner;
spinner = spinners[event.name];
if (!spinner) {
spinner = spinners[event.name] = new visuals.Spinner(event.name);
}
if (event.action === 'start') {
return spinner.start();
} else {
console.log();
return spinner.stop();
}
};
options.image = params.image; options.image = params.image;
options.appId = options.app; options.appId = options.app;
delete options.app; delete options.app;
@ -176,32 +201,43 @@ module.exports = {
delete options['dont-detect-flasher-type-images']; delete options['dont-detect-flasher-type-images'];
options.splashImage = options['splash-image']; options.splashImage = options['splash-image'];
delete options['splash-image']; delete options['splash-image'];
return dockerUtils.getDocker(options).then(function(docker) {
var buildOutputStream;
buildOutputStream = preload.build(docker);
if (process.env.DEBUG) {
buildOutputStream.pipe(process.stdout);
}
return streamToPromise(buildOutputStream).then(resin.settings.getAll).then(function(settings) {
options.proxy = settings.proxy;
options.apiHost = settings.apiUrl;
imageInfoSpinner.start();
return preload.getDeviceTypeSlugAndPreloadedBuilds(docker, options)["catch"](preload.errors.ResinError, expectedError);
}).then(function(arg) {
var builds, slug;
slug = arg.slug, builds = arg.builds;
imageInfoSpinner.stop();
return Promise["try"](function() {
if (options['dont-check-device-type'] && !options.appId) { if (options['dont-check-device-type'] && !options.appId) {
expectedError('You need to specify an app id if you disable the device type check.'); expectedError('You need to specify an app id if you disable the device type check.');
} }
if (options.appId) { return dockerUtils.getDocker(options).then(function(docker) {
return preload.getApplication(resin, options.appId)["catch"](errors.ResinApplicationNotFound, expectedError); var gotSignal, preloader;
preloader = new preload.Preloader(resin, docker, options.appId, options.commit, options.image, options.splashImage, options.proxy, options.dontDetectFlasherTypeImages);
gotSignal = false;
nodeCleanup(function(exitCode, signal) {
if (signal) {
gotSignal = true;
nodeCleanup.uninstall();
preloader.cleanup().then(function() {
return process.kill(process.pid, signal);
});
return false;
} }
return selectApplication(slug); });
if (process.env.DEBUG) {
preloader.stderr.pipe(process.stderr);
}
preloader.on('progress', progressHandler);
preloader.on('spinner', spinnerHandler);
return new Promise(function(resolve, reject) {
preloader.on('error', reject);
return preloader.build().then(function() {
return preloader.prepare();
}).then(function() {
return preloader.getDeviceTypeAndPreloadedBuilds();
}).then(function(info) {
return Promise["try"](function() {
if (options.appId) {
return preloader.fetchApplication()["catch"](errors.ResinApplicationNotFound, expectedError);
}
return selectApplication(info.device_type);
}).then(function(application) { }).then(function(application) {
options.application = application; preloader.setApplication(application);
if (slug !== application.device_type) { if (info.device_type !== application.device_type) {
expectedError("Image device type (" + application.device_type + ") and application device type (" + slug + ") do not match"); expectedError("Image device type (" + application.device_type + ") and application device type (" + slug + ") do not match");
} }
return Promise["try"](function() { return Promise["try"](function() {
@ -216,32 +252,28 @@ module.exports = {
return selectApplicationCommit(application.build); return selectApplicationCommit(application.build);
}).then(function(commit) { }).then(function(commit) {
if (commit === LATEST) { if (commit === LATEST) {
options.commit = application.commit; preloader.commit = application.commit;
} else { } else {
options.commit = commit; preloader.commit = commit;
} }
return offerToDisableAutomaticUpdates(application, commit); return offerToDisableAutomaticUpdates(application, commit);
}); });
}).then(function() { }).then(function() {
var ref; var builds, ref;
builds = builds.map(function(build) { builds = info.preloaded_builds.map(function(build) {
return build.slice(-preload.BUILD_HASH_LENGTH); return build.slice(-preload.BUILD_HASH_LENGTH);
}); });
if (ref = options.commit, indexOf.call(builds, ref) >= 0) { if (ref = preloader.commit, indexOf.call(builds, ref) >= 0) {
console.log('This build is already preloaded in this image.'); throw new preload.errors.ResinError('This build is already preloaded in this image.');
process.exit(0); }
return preloader.preload()["catch"](preload.errors.ResinError, expectedError);
});
}).then(resolve)["catch"](reject);
}).then(done)["finally"](function() {
if (!gotSignal) {
return preloader.cleanup();
} }
return preload.run(resin, docker, options)["catch"](preload.errors.ResinError, expectedError);
}); });
}); });
}).then(function(info) {
info.stdout.pipe(process.stdout);
info.stderr.pipe(process.stderr);
return info.statusCodePromise;
}).then(function(statusCode) {
if (statusCode !== 0) {
return process.exit(statusCode);
}
}).then(done);
} }
}; };

View File

@ -102,14 +102,14 @@ offerToDisableAutomaticUpdates = (application, commit) ->
module.exports = module.exports =
signature: 'preload <image>' signature: 'preload <image>'
description: '(beta) preload an app on a disk image' description: '(beta) preload an app on a disk image (or Edison zip archive)'
help: ''' help: '''
Warning: "resin preload" requires Docker to be correctly installed in Warning: "resin preload" requires Docker to be correctly installed in
your shell environment. For more information (including Windows support) your shell environment. For more information (including Windows support)
please check the README here: https://github.com/resin-io/resin-cli . please check the README here: https://github.com/resin-io/resin-cli .
Use this command to preload an application to a local disk image with a Use this command to preload an application to a local disk image (or
built commit from Resin.io. Edison zip archive) with a built commit from Resin.io.
This can be used with cloud builds, or images deployed with resin deploy. This can be used with cloud builds, or images deployed with resin deploy.
Examples: Examples:
@ -157,9 +157,28 @@ module.exports =
preload = require('resin-preload') preload = require('resin-preload')
errors = require('resin-errors') errors = require('resin-errors')
visuals = require('resin-cli-visuals') visuals = require('resin-cli-visuals')
nodeCleanup = require('node-cleanup')
{ expectedError } = require('../utils/patterns') { expectedError } = require('../utils/patterns')
imageInfoSpinner = new visuals.Spinner('Reading image device type and preloaded builds.') progressBars = {}
progressHandler = (event) ->
progressBar = progressBars[event.name]
if not progressBar
progressBar = progressBars[event.name] = new visuals.Progress(event.name)
progressBar.update(percentage: event.percentage)
spinners = {}
spinnerHandler = (event) ->
spinner = spinners[event.name]
if not spinner
spinner = spinners[event.name] = new visuals.Spinner(event.name)
if event.action == 'start'
spinner.start()
else
console.log()
spinner.stop()
options.image = params.image options.image = params.image
options.appId = options.app options.appId = options.app
@ -171,43 +190,60 @@ module.exports =
options.splashImage = options['splash-image'] options.splashImage = options['splash-image']
delete options['splash-image'] delete options['splash-image']
if options['dont-check-device-type'] and not options.appId
expectedError('You need to specify an app id if you disable the device type check.')
# Get a configured dockerode instance # Get a configured dockerode instance
dockerUtils.getDocker(options) dockerUtils.getDocker(options)
.then (docker) -> .then (docker) ->
# Build the preloader image preloader = new preload.Preloader(
buildOutputStream = preload.build(docker) resin,
docker,
options.appId,
options.commit,
options.image,
options.splashImage,
options.proxy,
options.dontDetectFlasherTypeImages
)
gotSignal = false
nodeCleanup (exitCode, signal) ->
if signal
gotSignal = true
nodeCleanup.uninstall() # don't call cleanup handler again
preloader.cleanup()
.then ->
# calling process.exit() won't inform parent process of signal
process.kill(process.pid, signal)
return false
if process.env.DEBUG if process.env.DEBUG
buildOutputStream.pipe(process.stdout) preloader.stderr.pipe(process.stderr)
streamToPromise(buildOutputStream) preloader.on('progress', progressHandler)
preloader.on('spinner', spinnerHandler)
# Get resin sdk settings so we can pass them to the preloader return new Promise (resolve, reject) ->
.then(resin.settings.getAll) preloader.on('error', reject)
.then (settings) ->
options.proxy = settings.proxy
options.apiHost = settings.apiUrl
# Use the preloader docker image to extract the deviceType of the image preloader.build()
imageInfoSpinner.start() .then ->
preload.getDeviceTypeSlugAndPreloadedBuilds(docker, options) preloader.prepare()
.catch(preload.errors.ResinError, expectedError) .then ->
.then ({ slug, builds }) -> preloader.getDeviceTypeAndPreloadedBuilds()
imageInfoSpinner.stop() .then (info) ->
# Use the appId given as --app or show an interactive app selection menu
Promise.try -> Promise.try ->
if options['dont-check-device-type'] and not options.appId
expectedError('You need to specify an app id if you disable the device type check.')
if options.appId if options.appId
return preload.getApplication(resin, options.appId) return preloader.fetchApplication()
.catch(errors.ResinApplicationNotFound, expectedError) .catch(errors.ResinApplicationNotFound, expectedError)
selectApplication(slug) selectApplication(info.device_type)
.then (application) -> .then (application) ->
options.application = application preloader.setApplication(application)
# Check that the app device type and the image device type match # Check that the app device type and the image device type match
if slug != application.device_type if info.device_type != application.device_type
expectedError( expectedError(
"Image device type (#{application.device_type}) and application device type (#{slug}) do not match" "Image device type (#{application.device_type}) and application device type (#{slug}) do not match"
) )
@ -223,27 +259,23 @@ module.exports =
# No commit specified => use the latest commit # No commit specified => use the latest commit
if commit == LATEST if commit == LATEST
options.commit = application.commit preloader.commit = application.commit
else else
options.commit = commit preloader.commit = commit
# Propose to disable automatic app updates if the commit is not the latest # Propose to disable automatic app updates if the commit is not the latest
offerToDisableAutomaticUpdates(application, commit) offerToDisableAutomaticUpdates(application, commit)
.then -> .then ->
builds = info.preloaded_builds.map (build) ->
builds = builds.map (build) ->
build.slice(-preload.BUILD_HASH_LENGTH) build.slice(-preload.BUILD_HASH_LENGTH)
if options.commit in builds if preloader.commit in builds
console.log('This build is already preloaded in this image.') throw new preload.errors.ResinError('This build is already preloaded in this image.')
process.exit(0)
# All options are ready: preload the image. # All options are ready: preload the image.
preload.run(resin, docker, options) preloader.preload()
.catch(preload.errors.ResinError, expectedError) .catch(preload.errors.ResinError, expectedError)
.then (info) -> .then(resolve)
info.stdout.pipe(process.stdout) .catch(reject)
info.stderr.pipe(process.stderr)
info.statusCodePromise
.then (statusCode) ->
if statusCode != 0
process.exit(statusCode)
.then(done) .then(done)
.finally ->
if not gotSignal
preloader.cleanup()

View File

@ -66,6 +66,7 @@
"mixpanel": "^0.4.0", "mixpanel": "^0.4.0",
"moment": "^2.12.0", "moment": "^2.12.0",
"mz": "^2.6.0", "mz": "^2.6.0",
"node-cleanup": "^2.1.2",
"nplugm": "^3.0.0", "nplugm": "^3.0.0",
"president": "^2.0.1", "president": "^2.0.1",
"prettyjson": "^1.1.3", "prettyjson": "^1.1.3",
@ -85,7 +86,7 @@
"resin-doodles": "0.0.1", "resin-doodles": "0.0.1",
"resin-image-fs": "^2.3.0", "resin-image-fs": "^2.3.0",
"resin-image-manager": "^4.1.1", "resin-image-manager": "^4.1.1",
"resin-preload": "^3.1.4", "resin-preload": "^4.0.2",
"resin-sdk-preconfigured": "^6.9.0", "resin-sdk-preconfigured": "^6.9.0",
"resin-settings-client": "^3.6.1", "resin-settings-client": "^3.6.1",
"resin-stream-logger": "^0.0.4", "resin-stream-logger": "^0.0.4",