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,72 +201,79 @@ 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'];
if (options['dont-check-device-type'] && !options.appId) {
expectedError('You need to specify an app id if you disable the device type check.');
}
return dockerUtils.getDocker(options).then(function(docker) { return dockerUtils.getDocker(options).then(function(docker) {
var buildOutputStream; var gotSignal, preloader;
buildOutputStream = preload.build(docker); preloader = new preload.Preloader(resin, docker, options.appId, options.commit, options.image, options.splashImage, options.proxy, options.dontDetectFlasherTypeImages);
if (process.env.DEBUG) { gotSignal = false;
buildOutputStream.pipe(process.stdout); nodeCleanup(function(exitCode, signal) {
} if (signal) {
return streamToPromise(buildOutputStream).then(resin.settings.getAll).then(function(settings) { gotSignal = true;
options.proxy = settings.proxy; nodeCleanup.uninstall();
options.apiHost = settings.apiUrl; preloader.cleanup().then(function() {
imageInfoSpinner.start(); return process.kill(process.pid, signal);
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) {
expectedError('You need to specify an app id if you disable the device type check.');
}
if (options.appId) {
return preload.getApplication(resin, options.appId)["catch"](errors.ResinApplicationNotFound, expectedError);
}
return selectApplication(slug);
}).then(function(application) {
options.application = application;
if (slug !== application.device_type) {
expectedError("Image device type (" + application.device_type + ") and application device type (" + slug + ") do not match");
}
return Promise["try"](function() {
if (options.commit) {
if (!_.find(application.build, {
commit_hash: options.commit
})) {
expectedError('There is no build matching this commit');
}
return options.commit;
}
return selectApplicationCommit(application.build);
}).then(function(commit) {
if (commit === LATEST) {
options.commit = application.commit;
} else {
options.commit = commit;
}
return offerToDisableAutomaticUpdates(application, commit);
}); });
}).then(function() { return false;
var ref; }
builds = builds.map(function(build) {
return build.slice(-preload.BUILD_HASH_LENGTH);
});
if (ref = options.commit, indexOf.call(builds, ref) >= 0) {
console.log('This build is already preloaded in this image.');
process.exit(0);
}
return preload.run(resin, docker, options)["catch"](preload.errors.ResinError, expectedError);
});
}); });
}).then(function(info) { if (process.env.DEBUG) {
info.stdout.pipe(process.stdout); preloader.stderr.pipe(process.stderr);
info.stderr.pipe(process.stderr);
return info.statusCodePromise;
}).then(function(statusCode) {
if (statusCode !== 0) {
return process.exit(statusCode);
} }
}).then(done); 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) {
preloader.setApplication(application);
if (info.device_type !== application.device_type) {
expectedError("Image device type (" + application.device_type + ") and application device type (" + slug + ") do not match");
}
return Promise["try"](function() {
if (options.commit) {
if (!_.find(application.build, {
commit_hash: options.commit
})) {
expectedError('There is no build matching this commit');
}
return options.commit;
}
return selectApplicationCommit(application.build);
}).then(function(commit) {
if (commit === LATEST) {
preloader.commit = application.commit;
} else {
preloader.commit = commit;
}
return offerToDisableAutomaticUpdates(application, commit);
});
}).then(function() {
var builds, ref;
builds = info.preloaded_builds.map(function(build) {
return build.slice(-preload.BUILD_HASH_LENGTH);
});
if (ref = preloader.commit, indexOf.call(builds, ref) >= 0) {
throw new preload.errors.ResinError('This build is already preloaded in this image.');
}
return preloader.preload()["catch"](preload.errors.ResinError, expectedError);
});
}).then(resolve)["catch"](reject);
}).then(done)["finally"](function() {
if (!gotSignal) {
return preloader.cleanup();
}
});
});
} }
}; };

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,79 +190,92 @@ 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()
preload.getDeviceTypeSlugAndPreloadedBuilds(docker, options)
.catch(preload.errors.ResinError, expectedError)
.then ({ slug, builds }) ->
imageInfoSpinner.stop()
# Use the appId given as --app or show an interactive app selection menu
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
return preload.getApplication(resin, options.appId)
.catch(errors.ResinApplicationNotFound, expectedError)
selectApplication(slug)
.then (application) ->
options.application = application
# Check that the app device type and the image device type match
if slug != application.device_type
expectedError(
"Image device type (#{application.device_type}) and application device type (#{slug}) do not match"
)
# Use the commit given as --commit or show an interactive commit selection menu
Promise.try ->
if options.commit
if not _.find(application.build, commit_hash: options.commit)
expectedError('There is no build matching this commit')
return options.commit
selectApplicationCommit(application.build)
.then (commit) ->
# No commit specified => use the latest commit
if commit == LATEST
options.commit = application.commit
else
options.commit = commit
# Propose to disable automatic app updates if the commit is not the latest
offerToDisableAutomaticUpdates(application, commit)
.then -> .then ->
preloader.prepare()
.then ->
preloader.getDeviceTypeAndPreloadedBuilds()
.then (info) ->
Promise.try ->
if options.appId
return preloader.fetchApplication()
.catch(errors.ResinApplicationNotFound, expectedError)
selectApplication(info.device_type)
.then (application) ->
preloader.setApplication(application)
# Check that the app device type and the image device type match
if info.device_type != application.device_type
expectedError(
"Image device type (#{application.device_type}) and application device type (#{slug}) do not match"
)
builds = builds.map (build) -> # Use the commit given as --commit or show an interactive commit selection menu
build.slice(-preload.BUILD_HASH_LENGTH) Promise.try ->
if options.commit in builds if options.commit
console.log('This build is already preloaded in this image.') if not _.find(application.build, commit_hash: options.commit)
process.exit(0) expectedError('There is no build matching this commit')
# All options are ready: preload the image. return options.commit
preload.run(resin, docker, options) selectApplicationCommit(application.build)
.catch(preload.errors.ResinError, expectedError) .then (commit) ->
.then (info) ->
info.stdout.pipe(process.stdout) # No commit specified => use the latest commit
info.stderr.pipe(process.stderr) if commit == LATEST
info.statusCodePromise preloader.commit = application.commit
.then (statusCode) -> else
if statusCode != 0 preloader.commit = commit
process.exit(statusCode)
.then(done) # Propose to disable automatic app updates if the commit is not the latest
offerToDisableAutomaticUpdates(application, commit)
.then ->
builds = info.preloaded_builds.map (build) ->
build.slice(-preload.BUILD_HASH_LENGTH)
if preloader.commit in builds
throw new preload.errors.ResinError('This build is already preloaded in this image.')
# All options are ready: preload the image.
preloader.preload()
.catch(preload.errors.ResinError, expectedError)
.then(resolve)
.catch(reject)
.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",
@ -103,4 +104,4 @@
"optionalDependencies": { "optionalDependencies": {
"removedrive": "^1.0.0" "removedrive": "^1.0.0"
} }
} }