allow prebuilding the device config and reusing it

This commit is contained in:
Eugene Mirotin 2017-06-12 11:42:08 +03:00
parent 9fb5b52069
commit 2e7e033bb9
8 changed files with 178 additions and 62 deletions

2
.gitignore vendored
View File

@ -31,3 +31,5 @@ resinrc.yml
.idea .idea
.vscode .vscode
.DS_Store .DS_Store
/tmp

View File

@ -15,6 +15,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Actually tolerate the `--yes` param to `resin device init` - Actually tolerate the `--yes` param to `resin device init`
- Allows passing `--drive` to `resin device init` - Allows passing `--drive` to `resin device init`
- List detected drives with `resin os available-drives` - List detected drives with `resin os available-drives`
- Add the `resin os build-config` method to pass the interactive config step once
and reuse the built file for consequent `resin os configure` calls (added the new `--config` param to it),
and for `resin device init` (same `--config` param)
### Fixed ### Fixed

View File

@ -276,6 +276,10 @@ exports.init = {
description: 'the drive to write the image to, like /dev/sdb. Careful with this as you can erase your hard drive. Check `resin os available-drives` for available options.', description: 'the drive to write the image to, like /dev/sdb. Careful with this as you can erase your hard drive. Check `resin os available-drives` for available options.',
parameter: 'drive', parameter: 'drive',
alias: 'd' alias: 'd'
}, {
signature: 'config',
description: 'stringified JSON with the device config, see `resin os build-config`',
parameter: 'config'
} }
], ],
permission: 'user', permission: 'user',
@ -310,7 +314,9 @@ exports.init = {
return capitanoRunAsync("device register " + application.app_name).then(resin.models.device.get).tap(function(device) { return capitanoRunAsync("device register " + application.app_name).then(resin.models.device.get).tap(function(device) {
var configureCommand; var configureCommand;
configureCommand = "os configure '" + tempPath + "' " + device.uuid; configureCommand = "os configure '" + tempPath + "' " + device.uuid;
if (options.advanced) { if (options.config) {
configureCommand += " --config '" + options.config + "'";
} else if (options.advanced) {
configureCommand += ' --advanced'; configureCommand += ' --advanced';
} }
return capitanoRunAsync(configureCommand).then(function() { return capitanoRunAsync(configureCommand).then(function() {

View File

@ -15,7 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
var _, commandOptions, formatVersion, initWarningMessage, resolveVersion; var INIT_WARNING_MESSAGE, _, buildConfig, commandOptions, formatVersion, resolveVersion;
commandOptions = require('./command-options'); commandOptions = require('./command-options');
@ -135,10 +135,34 @@ exports.download = {
} }
}; };
exports.configure = { buildConfig = function(image, deviceType, advanced) {
signature: 'os configure <image> <uuid>', var form, helpers;
description: 'configure an os image', if (advanced == null) {
help: 'Use this command to configure a previously download operating system image with a device.\n\nExamples:\n\n $ resin os configure ../path/rpi.img 7cf02a6', advanced = false;
}
form = require('resin-cli-form');
helpers = require('../utils/helpers');
return helpers.getManifest(image, deviceType).get('options').then(function(questions) {
var advancedGroup, override;
if (!advanced) {
advancedGroup = _.findWhere(questions, {
name: 'advanced',
isGroup: true
});
if (advancedGroup != null) {
override = helpers.getGroupDefaults(advancedGroup);
}
}
return form.run(questions, {
override: override
});
});
};
exports.buildConfig = {
signature: 'os build-config <image> <device-type>',
description: 'build the OS config and save it to the JSON file',
help: 'Use this command to prebuild the OS config once and skip the interactive part of `resin os configure`.\n\nExamples:\n\n $ resin os build-config ../path/rpi3.img raspberrypi3 --output rpi3-config.json\n $ resin os configure ../path/rpi3.img 7cf02a6 --config "$(cat rpi3-config.json)"',
permission: 'user', permission: 'user',
options: [ options: [
{ {
@ -146,38 +170,58 @@ exports.configure = {
description: 'show advanced commands', description: 'show advanced commands',
boolean: true, boolean: true,
alias: 'v' alias: 'v'
}, {
signature: 'output',
description: 'the path to the output JSON file',
alias: 'o',
required: 'the output path is required',
parameter: 'output'
} }
], ],
action: function(params, options, done) { action: function(params, options, done) {
var form, helpers, init, resin; var Promise, fs, writeFileAsync;
resin = require('resin-sdk-preconfigured'); fs = require('fs');
form = require('resin-cli-form'); Promise = require('bluebird');
init = require('resin-device-init'); writeFileAsync = Promise.promisify(fs.writeFile);
helpers = require('../utils/helpers'); return buildConfig(params.image, params['device-type'], options.advanced).then(function(answers) {
console.info('Configuring operating system image'); return writeFileAsync(options.output, JSON.stringify(answers, null, 4));
return resin.models.device.get(params.uuid).then(function(device) {
return helpers.getManifest(params.image, device.device_type).get('options').then(function(questions) {
var advancedGroup, override;
if (!options.advanced) {
advancedGroup = _.findWhere(questions, {
name: 'advanced',
isGroup: true
});
if (advancedGroup != null) {
override = helpers.getGroupDefaults(advancedGroup);
}
}
return form.run(questions, {
override: override
});
}).then(function(answers) {
return init.configure(params.image, params.uuid, answers).then(helpers.osProgressHandler);
});
}).nodeify(done); }).nodeify(done);
} }
}; };
initWarningMessage = 'Note: Initializing the device may ask for administrative permissions\nbecause we need to access the raw devices directly.'; exports.configure = {
signature: 'os configure <image> <uuid>',
description: 'configure an os image',
help: 'Use this command to configure a previously downloaded operating system image for the specific device.\n\nExamples:\n\n $ resin os configure ../path/rpi.img 7cf02a6',
permission: 'user',
options: [
{
signature: 'advanced',
description: 'show advanced commands',
boolean: true,
alias: 'v'
}, {
signature: 'config',
description: 'stringified JSON with the device config, see `resin os build-config`',
parameter: 'config'
}
],
action: function(params, options, done) {
var helpers, init, resin;
resin = require('resin-sdk-preconfigured');
init = require('resin-device-init');
helpers = require('../utils/helpers');
console.info('Configuring operating system image');
return resin.models.device.get(params.uuid).then(function(device) {
if (options.config) {
return JSON.parse(options.config);
}
return buildConfig(params.image, device.device_type, options.advanced);
}).then(function(answers) {
return init.configure(params.image, params.uuid, answers).then(helpers.osProgressHandler);
}).nodeify(done);
}
};
exports.availableDrives = { exports.availableDrives = {
signature: 'os available-drives', signature: 'os available-drives',
@ -212,10 +256,12 @@ exports.availableDrives = {
} }
}; };
INIT_WARNING_MESSAGE = 'Note: Initializing the device may ask for administrative permissions\nbecause we need to access the raw devices directly.';
exports.initialize = { exports.initialize = {
signature: 'os initialize <image>', signature: 'os initialize <image>',
description: 'initialize an os image', description: 'initialize an os image',
help: "Use this command to initialize a device with previously configured operating system image.\n\n" + initWarningMessage + "\n\nExamples:\n\n $ resin os initialize ../path/rpi.img --type 'raspberry-pi'", help: "Use this command to initialize a device with previously configured operating system image.\n\n" + INIT_WARNING_MESSAGE + "\n\nExamples:\n\n $ resin os initialize ../path/rpi.img --type 'raspberry-pi'",
permission: 'user', permission: 'user',
options: [ options: [
commandOptions.yes, { commandOptions.yes, {
@ -238,7 +284,7 @@ exports.initialize = {
form = require('resin-cli-form'); form = require('resin-cli-form');
patterns = require('../utils/patterns'); patterns = require('../utils/patterns');
helpers = require('../utils/helpers'); helpers = require('../utils/helpers');
console.info("Initializing device\n\n" + initWarningMessage); console.info("Initializing device\n\n" + INIT_WARNING_MESSAGE);
return helpers.getManifest(params.image, options.type).then(function(manifest) { return helpers.getManifest(params.image, options.type).then(function(manifest) {
var ref; var ref;
return (ref = manifest.initialization) != null ? ref.options : void 0; return (ref = manifest.initialization) != null ? ref.options : void 0;

View File

@ -172,6 +172,8 @@ capitano.command(actions.os.availableDrives);
capitano.command(actions.os.download); capitano.command(actions.os.download);
capitano.command(actions.os.buildConfig);
capitano.command(actions.os.configure); capitano.command(actions.os.configure);
capitano.command(actions.os.initialize); capitano.command(actions.os.initialize);

View File

@ -380,6 +380,11 @@ exports.init =
parameter: 'drive' parameter: 'drive'
alias: 'd' alias: 'd'
} }
{
signature: 'config'
description: 'stringified JSON with the device config, see `resin os build-config`'
parameter: 'config'
}
] ]
permission: 'user' permission: 'user'
action: (params, options, done) -> action: (params, options, done) ->
@ -412,7 +417,9 @@ exports.init =
.then(resin.models.device.get) .then(resin.models.device.get)
.tap (device) -> .tap (device) ->
configureCommand = "os configure '#{tempPath}' #{device.uuid}" configureCommand = "os configure '#{tempPath}' #{device.uuid}"
if options.advanced if options.config
configureCommand += " --config '#{options.config}'"
else if options.advanced
configureCommand += ' --advanced' configureCommand += ' --advanced'
capitanoRunAsync(configureCommand) capitanoRunAsync(configureCommand)
.then -> .then ->

View File

@ -144,11 +144,65 @@ exports.download =
console.info('The image was downloaded successfully') console.info('The image was downloaded successfully')
.nodeify(done) .nodeify(done)
buildConfig = (image, deviceType, advanced = false) ->
form = require('resin-cli-form')
helpers = require('../utils/helpers')
helpers.getManifest(image, deviceType)
.get('options')
.then (questions) ->
if not advanced
advancedGroup = _.findWhere questions,
name: 'advanced'
isGroup: true
if advancedGroup?
override = helpers.getGroupDefaults(advancedGroup)
return form.run(questions, { override })
exports.buildConfig =
signature: 'os build-config <image> <device-type>'
description: 'build the OS config and save it to the JSON file'
help: '''
Use this command to prebuild the OS config once and skip the interactive part of `resin os configure`.
Examples:
$ resin os build-config ../path/rpi3.img raspberrypi3 --output rpi3-config.json
$ resin os configure ../path/rpi3.img 7cf02a6 --config "$(cat rpi3-config.json)"
'''
permission: 'user'
options: [
{
signature: 'advanced'
description: 'show advanced commands'
boolean: true
alias: 'v'
}
{
signature: 'output'
description: 'the path to the output JSON file'
alias: 'o'
required: 'the output path is required'
parameter: 'output'
}
]
action: (params, options, done) ->
fs = require('fs')
Promise = require('bluebird')
writeFileAsync = Promise.promisify(fs.writeFile)
buildConfig(params.image, params['device-type'], options.advanced)
.then (answers) ->
writeFileAsync(options.output, JSON.stringify(answers, null, 4))
.nodeify(done)
exports.configure = exports.configure =
signature: 'os configure <image> <uuid>' signature: 'os configure <image> <uuid>'
description: 'configure an os image' description: 'configure an os image'
help: ''' help: '''
Use this command to configure a previously download operating system image with a device. Use this command to configure a previously downloaded operating system image for the specific device.
Examples: Examples:
@ -156,41 +210,32 @@ exports.configure =
''' '''
permission: 'user' permission: 'user'
options: [ options: [
signature: 'advanced' {
description: 'show advanced commands' signature: 'advanced'
boolean: true description: 'show advanced commands'
alias: 'v' boolean: true
alias: 'v'
}
{
signature: 'config'
description: 'stringified JSON with the device config, see `resin os build-config`'
parameter: 'config'
}
] ]
action: (params, options, done) -> action: (params, options, done) ->
resin = require('resin-sdk-preconfigured') resin = require('resin-sdk-preconfigured')
form = require('resin-cli-form')
init = require('resin-device-init') init = require('resin-device-init')
helpers = require('../utils/helpers') helpers = require('../utils/helpers')
console.info('Configuring operating system image') console.info('Configuring operating system image')
resin.models.device.get(params.uuid).then (device) -> resin.models.device.get(params.uuid).then (device) ->
helpers.getManifest(params.image, device.device_type) if options.config
.get('options') return JSON.parse(options.config)
.then (questions) -> buildConfig(params.image, device.device_type, options.advanced)
.then (answers) ->
if not options.advanced init.configure(params.image, params.uuid, answers).then(helpers.osProgressHandler)
advancedGroup = _.findWhere questions,
name: 'advanced'
isGroup: true
if advancedGroup?
override = helpers.getGroupDefaults(advancedGroup)
return form.run(questions, { override })
.then (answers) ->
init.configure(params.image, params.uuid, answers).then(helpers.osProgressHandler)
.nodeify(done) .nodeify(done)
initWarningMessage = '''
Note: Initializing the device may ask for administrative permissions
because we need to access the raw devices directly.
'''
exports.availableDrives = exports.availableDrives =
# TODO: dedupe with https://github.com/resin-io-modules/resin-cli-visuals/blob/master/lib/widgets/drive/index.coffee # TODO: dedupe with https://github.com/resin-io-modules/resin-cli-visuals/blob/master/lib/widgets/drive/index.coffee
signature: 'os available-drives' signature: 'os available-drives'
@ -217,6 +262,10 @@ exports.availableDrives =
drives.forEach (drive) -> drives.forEach (drive) ->
console.log(formatDrive(drive)) console.log(formatDrive(drive))
INIT_WARNING_MESSAGE = '''
Note: Initializing the device may ask for administrative permissions
because we need to access the raw devices directly.
'''
exports.initialize = exports.initialize =
signature: 'os initialize <image>' signature: 'os initialize <image>'
@ -224,7 +273,7 @@ exports.initialize =
help: """ help: """
Use this command to initialize a device with previously configured operating system image. Use this command to initialize a device with previously configured operating system image.
#{initWarningMessage} #{INIT_WARNING_MESSAGE}
Examples: Examples:
@ -257,7 +306,7 @@ exports.initialize =
console.info(""" console.info("""
Initializing device Initializing device
#{initWarningMessage} #{INIT_WARNING_MESSAGE}
""") """)
helpers.getManifest(params.image, options.type) helpers.getManifest(params.image, options.type)
.then (manifest) -> .then (manifest) ->

View File

@ -138,6 +138,7 @@ capitano.command(actions.env.remove)
capitano.command(actions.os.versions) capitano.command(actions.os.versions)
capitano.command(actions.os.availableDrives) capitano.command(actions.os.availableDrives)
capitano.command(actions.os.download) capitano.command(actions.os.download)
capitano.command(actions.os.buildConfig)
capitano.command(actions.os.configure) capitano.command(actions.os.configure)
capitano.command(actions.os.initialize) capitano.command(actions.os.initialize)