Merge pull request #64 from resin-io/feature/config-inject

Implement config injection
This commit is contained in:
Juan Cruz Viotti 2015-06-05 12:06:54 -04:00
commit 47e6371e2e
9 changed files with 188 additions and 384 deletions

View File

@ -1,5 +1,7 @@
(function() {
var _, async, capitano, commandOptions, osAction, path, resin, tmp, vcs, visuals;
var _, async, capitano, commandOptions, fse, image, inject, manager, path, pine, registerDevice, resin, tmp, vcs, visuals;
fse = require('fs-extra');
capitano = require('capitano');
@ -15,14 +17,22 @@
vcs = require('resin-vcs');
manager = require('resin-image-manager');
image = require('resin-image');
inject = require('resin-config-inject');
registerDevice = require('resin-register-device');
pine = require('resin-pine');
tmp = require('tmp');
tmp.setGracefulCleanup();
commandOptions = require('./command-options');
osAction = require('./os');
exports.list = {
signature: 'devices',
description: 'list all devices',
@ -159,10 +169,17 @@
exports.init = {
signature: 'device init [device]',
description: 'initialise a device with resin os',
help: 'Use this command to download the OS image of a certain application and write it to an SD Card.\n\nNote that this command requires admin privileges.\n\nIf `device` is omitted, you will be prompted to select a device interactively.\n\nNotice this command asks for confirmation interactively.\nYou can avoid this by passing the `--yes` boolean option.\n\nYou can quiet the progress bar by passing the `--quiet` boolean option.\n\nYou may have to unmount the device before attempting this operation.\n\nYou need to configure the network type and other settings:\n\nEthernet:\n You can setup the device OS to use ethernet by setting the `--network` option to "ethernet".\n\nWifi:\n You can setup the device OS to use wifi by setting the `--network` option to "wifi".\n If you set "network" to "wifi", you will need to specify the `--ssid` and `--key` option as well.\n\nYou can omit network related options to be asked about them interactively.\n\nExamples:\n\n $ resin device init\n $ resin device init --application MyApp\n $ resin device init --application MyApp --network ethernet\n $ resin device init /dev/disk2 --application MyApp --network wifi --ssid MyNetwork --key secret',
help: 'Use this command to download the OS image of a certain application and write it to an SD Card.\n\nNote that this command requires admin privileges.\n\nIf `device` is omitted, you will be prompted to select a device interactively.\n\nNotice this command asks for confirmation interactively.\nYou can avoid this by passing the `--yes` boolean option.\n\nYou can quiet the progress bar and other logging information by passing the `--quiet` boolean option.\n\nYou need to configure the network type and other settings:\n\nEthernet:\n You can setup the device OS to use ethernet by setting the `--network` option to "ethernet".\n\nWifi:\n You can setup the device OS to use wifi by setting the `--network` option to "wifi".\n If you set "network" to "wifi", you will need to specify the `--ssid` and `--key` option as well.\n\nYou can omit network related options to be asked about them interactively.\n\nExamples:\n\n $ resin device init\n $ resin device init --application MyApp\n $ resin device init --application MyApp --network ethernet\n $ resin device init /dev/disk2 --application MyApp --network wifi --ssid MyNetwork --key secret',
options: [commandOptions.optionalApplication, commandOptions.network, commandOptions.wifiSsid, commandOptions.wifiKey],
permission: 'user',
root: true,
action: function(params, options, done) {
var networkOptions;
networkOptions = {
network: options.network,
wifiSsid: options.ssid,
wifiKey: options.key
};
return async.waterfall([
function(callback) {
if (options.application != null) {
@ -170,7 +187,7 @@
}
return vcs.getApplicationName(process.cwd(), callback);
}, function(applicationName, callback) {
params.name = applicationName;
options.application = applicationName;
if (params.device != null) {
return callback(null, params.device);
}
@ -184,27 +201,81 @@
if (!confirmed) {
return done();
}
options.yes = confirmed;
return tmp.file({
prefix: 'resin-image-',
postfix: '.img'
}, callback);
}, function(tmpPath, tmpFd, cleanupCallback, callback) {
options.output = tmpPath;
return osAction.download.action(params, options, function(error, outputFile) {
if (networkOptions.network != null) {
return callback();
}
return visuals.patterns.selectNetworkParameters(function(error, parameters) {
if (error != null) {
return callback(error);
}
return callback(null, outputFile, cleanupCallback);
});
}, function(outputFile, cleanupCallback, callback) {
return capitano.run("os install " + outputFile + " " + params.device, function(error) {
if (error != null) {
return callback(error);
}
cleanupCallback();
_.extend(networkOptions, parameters);
return callback();
});
}, function(callback) {
console.info("Checking application: " + options.application);
return resin.models.application.get(options.application, callback);
}, function(application, callback) {
return async.parallel({
manifest: function(callback) {
console.info('Getting device manifest for the application');
return resin.models.device.getManifestBySlug(application.device_type, callback);
},
config: function(callback) {
console.info('Fetching application configuration');
return resin.models.application.getConfiguration(options.application, networkOptions, callback);
}
}, callback);
}, function(results, callback) {
console.info('Associating the device');
return registerDevice.register(pine, results.config, function(error, device) {
if (error != null) {
return callback(error);
}
results.config.deviceId = device.id;
results.config.uuid = device.uuid;
params.uuid = results.config.uuid;
return callback(null, results);
});
}, function(results, callback) {
var bar, spinner;
console.info('Configuring device operating system image');
if (process.env.DEBUG) {
console.log(results.config);
}
bar = new visuals.widgets.Progress('Downloading Device OS');
spinner = new visuals.widgets.Spinner('Downloading Device OS (size unknown)');
return manager.configure(results.manifest, results.config, function(error, imagePath, removeCallback) {
spinner.stop();
return callback(error, imagePath, removeCallback);
}, function(state) {
if (state != null) {
return bar.update(state);
} else {
return spinner.start();
}
});
}, function(configuredImagePath, removeCallback, callback) {
var bar;
console.info('Attempting to write operating system image to drive');
bar = new visuals.widgets.Progress('Writing Device OS');
return image.write({
device: params.device,
image: configuredImagePath,
progress: _.bind(bar.update, bar)
}, function(error) {
if (error != null) {
return callback(error);
}
return callback(null, configuredImagePath, removeCallback);
});
}, function(temporalImagePath, removeCallback, callback) {
console.info('Image written successfully');
return removeCallback(callback);
}, function(callback) {
return resin.models.device.getByUUID(params.uuid, callback);
}, function(device, callback) {
console.info("Device created: " + device.name);
return callback(null, device.name);
}
], done);
}

View File

@ -10,7 +10,6 @@
logs: require('./logs'),
notes: require('./notes'),
preferences: require('./preferences'),
os: require('./os'),
help: require('./help'),
examples: require('./examples'),
plugin: require('./plugin'),

View File

@ -1,155 +0,0 @@
(function() {
var _, async, capitano, commandOptions, elevate, image, mkdirp, os, packageJSON, path, resin, selfupdate, visuals;
capitano = require('capitano');
_ = require('lodash-contrib');
os = require('os');
async = require('async');
path = require('path');
mkdirp = require('mkdirp');
resin = require('resin-sdk');
image = require('resin-image');
visuals = require('resin-cli-visuals');
selfupdate = require('selfupdate');
commandOptions = require('./command-options');
packageJSON = require('../../package.json');
elevate = require('../elevate');
exports.download = {
signature: 'os download <name>',
description: 'download device OS',
help: 'Use this command to download the device OS configured to a specific network.\n\nEthernet:\n You can setup the device OS to use ethernet by setting the `--network` option to "ethernet".\n\nWifi:\n You can setup the device OS to use wifi by setting the `--network` option to "wifi".\n If you set "network" to "wifi", you will need to specify the `--ssid` and `--key` option as well.\n\nAlternatively, you can omit all kind of network configuration options to configure interactively.\n\nYou have to specify an output location with the `--output` option.\n\nExamples:\n\n $ resin os download MyApp --output ~/MyResinOS.zip\n $ resin os download MyApp --network ethernet --output ~/MyResinOS.zip\n $ resin os download MyApp --network wifi --ssid MyNetwork --key secreykey123 --output ~/MyResinOS.zip\n $ resin os download MyApp --network ethernet --output ~/MyResinOS.zip',
options: [
commandOptions.network, commandOptions.wifiSsid, commandOptions.wifiKey, {
signature: 'output',
parameter: 'output',
description: 'output file',
alias: 'o',
required: 'You need to specify an output file'
}
],
permission: 'user',
action: function(params, options, done) {
return resin.models.application.get(params.name, function(error, application) {
var osParams;
if (error != null) {
return done(error);
}
osParams = {
network: options.network,
wifiSsid: options.ssid,
wifiKey: options.key,
appId: application.id
};
return async.waterfall([
function(callback) {
if (osParams.network != null) {
return callback();
}
return visuals.patterns.selectNetworkParameters(function(error, parameters) {
if (error != null) {
return callback(error);
}
_.extend(osParams, parameters);
return callback();
});
}, function(callback) {
return mkdirp(path.dirname(options.output), _.unary(callback));
}, function(callback) {
var bar, spinner;
console.info("Destination file: " + options.output + "\n");
bar = new visuals.widgets.Progress('Downloading Device OS');
spinner = new visuals.widgets.Spinner('Downloading Device OS (size unknown)');
return resin.models.os.download(osParams, options.output, function(error) {
spinner.stop();
return callback(error);
}, function(state) {
if (state != null) {
return bar.update(state);
} else {
return spinner.start();
}
});
}
], function(error) {
if (error != null) {
return done(error);
}
console.info("\nFinished downloading " + options.output);
return done(null, options.output);
});
});
}
};
exports.install = {
signature: 'os install <image> [device]',
description: 'write an operating system image to a device',
help: 'Use this command to write an operating system image to a device.\n\nNote that this command requires admin privileges.\n\nIf `device` is omitted, you will be prompted to select a device interactively.\n\nNotice this command asks for confirmation interactively.\nYou can avoid this by passing the `--yes` boolean option.\n\nYou can quiet the progress bar by passing the `--quiet` boolean option.\n\nExamples:\n\n $ resin os install rpi.iso /dev/disk2',
options: [commandOptions.yes],
permission: 'user',
action: function(params, options, done) {
return async.waterfall([
function(callback) {
return selfupdate.isUpdated(packageJSON, callback);
}, function(isUpdated, callback) {
if (isUpdated) {
return callback();
}
console.info('Resin CLI is outdated.\n\nIn order to avoid device compatibility issues, this command\nrequires that you have the Resin CLI updated.\n\nUpdating now...');
return capitano.run('update', _.unary(callback));
}, function(callback) {
if (params.device != null) {
return callback(null, params.device);
}
return visuals.patterns.selectDrive(function(error, device) {
if (error != null) {
return callback(error);
}
if (device == null) {
return callback(new Error('No removable devices available'));
}
return callback(null, device);
});
}, function(device, callback) {
var message;
params.device = device;
message = "This will completely erase " + params.device + ". Are you sure you want to continue?";
return visuals.patterns.confirm(options.yes, message, callback);
}, function(confirmed, callback) {
var bar;
if (!confirmed) {
return done();
}
bar = new visuals.widgets.Progress('Writing Device OS');
params.progress = _.bind(bar.update, bar);
return image.write(params, callback);
}
], function(error) {
var resinWritePath;
if (error == null) {
return done();
}
if (elevate.shouldElevate(error) && !options.fromScript) {
resinWritePath = "\"" + (path.join(__dirname, '..', '..', 'bin', 'resin-write')) + "\"";
return elevate.run("\"" + process.argv[0] + "\" " + resinWritePath + " \"" + params.image + "\" \"" + params.device + "\"");
} else {
return done(error);
}
});
}
};
}).call(this);

View File

@ -128,10 +128,6 @@
capitano.command(actions.logs);
capitano.command(actions.os.download);
capitano.command(actions.os.install);
capitano.command(actions.examples.list);
capitano.command(actions.examples.clone);

View File

@ -1,3 +1,4 @@
fse = require('fs-extra')
capitano = require('capitano')
_ = require('lodash-contrib')
path = require('path')
@ -5,13 +6,17 @@ async = require('async')
resin = require('resin-sdk')
visuals = require('resin-cli-visuals')
vcs = require('resin-vcs')
manager = require('resin-image-manager')
image = require('resin-image')
inject = require('resin-config-inject')
registerDevice = require('resin-register-device')
pine = require('resin-pine')
tmp = require('tmp')
# Cleanup the temporary files even when an uncaught exception occurs
tmp.setGracefulCleanup()
commandOptions = require('./command-options')
osAction = require('./os')
exports.list =
signature: 'devices'
@ -217,9 +222,7 @@ exports.init =
Notice this command asks for confirmation interactively.
You can avoid this by passing the `--yes` boolean option.
You can quiet the progress bar by passing the `--quiet` boolean option.
You may have to unmount the device before attempting this operation.
You can quiet the progress bar and other logging information by passing the `--quiet` boolean option.
You need to configure the network type and other settings:
@ -246,8 +249,14 @@ exports.init =
commandOptions.wifiKey
]
permission: 'user'
root: true
action: (params, options, done) ->
networkOptions =
network: options.network
wifiSsid: options.ssid
wifiKey: options.key
async.waterfall([
(callback) ->
@ -255,7 +264,7 @@ exports.init =
vcs.getApplicationName(process.cwd(), callback)
(applicationName, callback) ->
params.name = applicationName
options.application = applicationName
return callback(null, params.device) if params.device?
visuals.patterns.selectDrive(callback)
@ -266,27 +275,82 @@ exports.init =
(confirmed, callback) ->
return done() if not confirmed
options.yes = confirmed
tmp.file
prefix: 'resin-image-'
postfix: '.img'
, callback
(tmpPath, tmpFd, cleanupCallback, callback) ->
options.output = tmpPath
# TODO: Figure out how to make use of capitano.run()
# here given the complexity of converting network
# params object to string options
osAction.download.action params, options, (error, outputFile) ->
return callback() if networkOptions.network?
visuals.patterns.selectNetworkParameters (error, parameters) ->
return callback(error) if error?
return callback(null, outputFile, cleanupCallback)
(outputFile, cleanupCallback, callback) ->
capitano.run "os install #{outputFile} #{params.device}", (error) ->
return callback(error) if error?
cleanupCallback()
_.extend(networkOptions, parameters)
return callback()
(callback) ->
console.info("Checking application: #{options.application}")
resin.models.application.get(options.application, callback)
(application, callback) ->
async.parallel
manifest: (callback) ->
console.info('Getting device manifest for the application')
resin.models.device.getManifestBySlug(application.device_type, callback)
config: (callback) ->
console.info('Fetching application configuration')
resin.models.application.getConfiguration(options.application, networkOptions, callback)
, callback
(results, callback) ->
console.info('Associating the device')
registerDevice.register pine, results.config, (error, device) ->
return callback(error) if error?
# Associate a device
results.config.deviceId = device.id
results.config.uuid = device.uuid
params.uuid = results.config.uuid
return callback(null, results)
(results, callback) ->
console.info('Configuring device operating system image')
if process.env.DEBUG
console.log(results.config)
bar = new visuals.widgets.Progress('Downloading Device OS')
spinner = new visuals.widgets.Spinner('Downloading Device OS (size unknown)')
manager.configure results.manifest, results.config, (error, imagePath, removeCallback) ->
spinner.stop()
return callback(error, imagePath, removeCallback)
, (state) ->
if state?
bar.update(state)
else
spinner.start()
(configuredImagePath, removeCallback, callback) ->
console.info('Attempting to write operating system image to drive')
bar = new visuals.widgets.Progress('Writing Device OS')
image.write
device: params.device
image: configuredImagePath
progress: _.bind(bar.update, bar)
, (error) ->
return callback(error) if error?
return callback(null, configuredImagePath, removeCallback)
(temporalImagePath, removeCallback, callback) ->
console.info('Image written successfully')
removeCallback(callback)
(callback) ->
resin.models.device.getByUUID(params.uuid, callback)
(device, callback) ->
console.info("Device created: #{device.name}")
return callback(null, device.name)
], done)

View File

@ -9,7 +9,6 @@ module.exports =
logs: require('./logs')
notes: require('./notes')
preferences: require('./preferences')
os: require('./os')
help: require('./help')
examples: require('./examples')
plugin: require('./plugin')

View File

@ -1,171 +0,0 @@
capitano = require('capitano')
_ = require('lodash-contrib')
os = require('os')
async = require('async')
path = require('path')
mkdirp = require('mkdirp')
resin = require('resin-sdk')
image = require('resin-image')
visuals = require('resin-cli-visuals')
selfupdate = require('selfupdate')
commandOptions = require('./command-options')
packageJSON = require('../../package.json')
elevate = require('../elevate')
exports.download =
signature: 'os download <name>'
description: 'download device OS'
help: '''
Use this command to download the device OS configured to a specific network.
Ethernet:
You can setup the device OS to use ethernet by setting the `--network` option to "ethernet".
Wifi:
You can setup the device OS to use wifi by setting the `--network` option to "wifi".
If you set "network" to "wifi", you will need to specify the `--ssid` and `--key` option as well.
Alternatively, you can omit all kind of network configuration options to configure interactively.
You have to specify an output location with the `--output` option.
Examples:
$ resin os download MyApp --output ~/MyResinOS.zip
$ resin os download MyApp --network ethernet --output ~/MyResinOS.zip
$ resin os download MyApp --network wifi --ssid MyNetwork --key secreykey123 --output ~/MyResinOS.zip
$ resin os download MyApp --network ethernet --output ~/MyResinOS.zip
'''
options: [
commandOptions.network
commandOptions.wifiSsid
commandOptions.wifiKey
{
signature: 'output'
parameter: 'output'
description: 'output file'
alias: 'o'
required: 'You need to specify an output file'
}
]
permission: 'user'
action: (params, options, done) ->
resin.models.application.get params.name, (error, application) ->
return done(error) if error?
osParams =
network: options.network
wifiSsid: options.ssid
wifiKey: options.key
appId: application.id
async.waterfall [
(callback) ->
return callback() if osParams.network?
visuals.patterns.selectNetworkParameters (error, parameters) ->
return callback(error) if error?
_.extend(osParams, parameters)
return callback()
(callback) ->
# We need to ensure this directory exists
mkdirp(path.dirname(options.output), _.unary(callback))
(callback) ->
console.info("Destination file: #{options.output}\n")
bar = new visuals.widgets.Progress('Downloading Device OS')
spinner = new visuals.widgets.Spinner('Downloading Device OS (size unknown)')
resin.models.os.download osParams, options.output, (error) ->
spinner.stop()
return callback(error)
, (state) ->
if state?
bar.update(state)
else
spinner.start()
], (error) ->
return done(error) if error?
console.info("\nFinished downloading #{options.output}")
return done(null, options.output)
exports.install =
signature: 'os install <image> [device]'
description: 'write an operating system image to a device'
help: '''
Use this command to write an operating system image to a device.
Note that this command requires admin privileges.
If `device` is omitted, you will be prompted to select a device interactively.
Notice this command asks for confirmation interactively.
You can avoid this by passing the `--yes` boolean option.
You can quiet the progress bar by passing the `--quiet` boolean option.
Examples:
$ resin os install rpi.iso /dev/disk2
'''
options: [ commandOptions.yes ]
permission: 'user'
action: (params, options, done) ->
async.waterfall [
(callback) ->
selfupdate.isUpdated(packageJSON, callback)
(isUpdated, callback) ->
return callback() if isUpdated
console.info '''
Resin CLI is outdated.
In order to avoid device compatibility issues, this command
requires that you have the Resin CLI updated.
Updating now...
'''
capitano.run('update', _.unary(callback))
(callback) ->
return callback(null, params.device) if params.device?
# TODO: See if we can reuse the drives action somehow here
visuals.patterns.selectDrive (error, device) ->
return callback(error) if error?
if not device?
return callback(new Error('No removable devices available'))
return callback(null, device)
(device, callback) ->
params.device = device
message = "This will completely erase #{params.device}. Are you sure you want to continue?"
visuals.patterns.confirm(options.yes, message, callback)
(confirmed, callback) ->
return done() if not confirmed
bar = new visuals.widgets.Progress('Writing Device OS')
params.progress = _.bind(bar.update, bar)
image.write(params, callback)
], (error) ->
return done() if not error?
if elevate.shouldElevate(error) and not options.fromScript
# Need to escape every path to avoid errors
resinWritePath = "\"#{path.join(__dirname, '..', '..', 'bin', 'resin-write')}\""
elevate.run("\"#{process.argv[0]}\" #{resinWritePath} \"#{params.image}\" \"#{params.device}\"")
else
return done(error)

View File

@ -100,10 +100,6 @@ capitano.command(actions.env.remove)
# ---------- Logs Module ----------
capitano.command(actions.logs)
# ---------- OS Module ----------
capitano.command(actions.os.download)
capitano.command(actions.os.install)
# ---------- Examples Module ----------
capitano.command(actions.examples.list)
capitano.command(actions.examples.clone)

View File

@ -52,15 +52,20 @@
"coffee-script": "~1.8.0",
"conf.js": "^0.1.1",
"drivelist": "^1.2.2",
"fs-extra": "^0.18.3",
"lodash": "~2.4.1",
"lodash-contrib": "~241.4.14",
"mkdirp": "~0.5.0",
"nplugm": "^2.2.0",
"npm": "^2.6.1",
"open": "0.0.5",
"resin-cli-visuals": "^0.1.0",
"resin-cli-visuals": "^0.1.1",
"resin-config-inject": "^2.0.0",
"resin-image": "^1.1.3",
"resin-sdk": "^1.7.3",
"resin-image-manager": "^1.1.0",
"resin-pine": "^1.1.1",
"resin-register-device": "^1.0.1",
"resin-sdk": "^1.7.4",
"resin-settings-client": "^1.1.0",
"resin-vcs": "^1.2.0",
"selfupdate": "^1.1.0",