Call os initialize as an elevated process

Currently, the fact that `os initialize` requires elevated permissions
forced us to require calling commands that reuse it, such as `device
init` and `quickstart` with administrator permissions as well.

This ended up causing issues like saving images in the cache that belong
to root, or initializing git repositories that requires `sudo` to
commit.

The solution is to call `os initialize` as a child process preppending
`sudo` within `device init`.

Fixes: https://github.com/resin-io/resin-cli/issues/109
This commit is contained in:
Juan Cruz Viotti 2015-10-01 13:07:53 -04:00
parent ed6427c541
commit 445e37ccaf
8 changed files with 41 additions and 8 deletions

View File

@ -1,5 +1,5 @@
(function() { (function() {
var Promise, _, capitano, commandOptions, events, form, patterns, resin, rimraf, tmp, visuals; var Promise, _, capitano, commandOptions, events, form, helpers, patterns, resin, rimraf, tmp, visuals;
Promise = require('bluebird'); Promise = require('bluebird');
@ -19,6 +19,8 @@
patterns = require('../utils/patterns'); patterns = require('../utils/patterns');
helpers = require('../utils/helpers');
tmp = Promise.promisifyAll(require('tmp')); tmp = Promise.promisifyAll(require('tmp'));
tmp.setGracefulCleanup(); tmp.setGracefulCleanup();
@ -131,7 +133,6 @@
help: 'Use this command to download the OS image of a certain application and write it to an SD Card.\n\nNotice this command may ask for confirmation interactively.\nYou can avoid this by passing the `--yes` boolean option.\n\nExamples:\n\n $ resin device init\n $ resin device init --application MyApp', help: 'Use this command to download the OS image of a certain application and write it to an SD Card.\n\nNotice this command may ask for confirmation interactively.\nYou can avoid this by passing the `--yes` boolean option.\n\nExamples:\n\n $ resin device init\n $ resin device init --application MyApp',
options: [commandOptions.optionalApplication, commandOptions.yes], options: [commandOptions.optionalApplication, commandOptions.yes],
permission: 'user', permission: 'user',
root: true,
action: function(params, options, done) { action: function(params, options, done) {
return Promise["try"](function() { return Promise["try"](function() {
if (options.application != null) { if (options.application != null) {
@ -148,7 +149,7 @@
return Promise.using(download(), function(temporalPath) { return Promise.using(download(), function(temporalPath) {
return capitano.runAsync("device register " + application.app_name).then(resin.models.device.get).tap(function(device) { return capitano.runAsync("device register " + application.app_name).then(resin.models.device.get).tap(function(device) {
return capitano.runAsync("os configure " + temporalPath + " " + device.uuid).then(function() { return capitano.runAsync("os configure " + temporalPath + " " + device.uuid).then(function() {
return capitano.runAsync("os initialize " + temporalPath + " " + application.device_type); return helpers.sudo(['os', 'initialize', temporalPath, application.device_type]);
}); });
}); });
}).then(function(device) { }).then(function(device) {

View File

@ -102,6 +102,7 @@
description: 'initialize an os image', description: 'initialize an os image',
help: 'Use this command to initialize a previously configured operating system image.\n\nExamples:\n\n $ resin os initialize ../path/rpi.img \'raspberry-pi\'', help: 'Use this command to initialize a previously configured operating system image.\n\nExamples:\n\n $ resin os initialize ../path/rpi.img \'raspberry-pi\'',
permission: 'user', permission: 'user',
root: true,
action: function(params, options, done) { action: function(params, options, done) {
console.info('Initializing device'); console.info('Initializing device');
return resin.models.device.getManifestBySlug(params.type).then(function(manifest) { return resin.models.device.getManifestBySlug(params.type).then(function(manifest) {

View File

@ -17,7 +17,6 @@
signature: 'quickstart [name]', signature: 'quickstart [name]',
description: 'getting started with resin.io', description: 'getting started with resin.io',
help: 'Use this command to run a friendly wizard to get started with resin.io.\n\nThe wizard will guide you through:\n\n - Create an application.\n - Initialise an SDCard with the resin.io operating system.\n - Associate an existing project directory with your resin.io application.\n - Push your project to your devices.\n\nExamples:\n\n $ sudo resin quickstart\n $ sudo resin quickstart MyApp', help: 'Use this command to run a friendly wizard to get started with resin.io.\n\nThe wizard will guide you through:\n\n - Create an application.\n - Initialise an SDCard with the resin.io operating system.\n - Associate an existing project directory with your resin.io application.\n - Push your project to your devices.\n\nExamples:\n\n $ sudo resin quickstart\n $ sudo resin quickstart MyApp',
root: true,
permission: 'user', permission: 'user',
action: function(params, options, done) { action: function(params, options, done) {
return Promise["try"](function() { return Promise["try"](function() {

View File

@ -1,12 +1,16 @@
(function() { (function() {
var Promise, _, chalk, os; var Promise, _, capitano, chalk, child_process, os;
Promise = require('bluebird'); Promise = require('bluebird');
capitano = Promise.promisifyAll(require('capitano'));
_ = require('lodash'); _ = require('lodash');
_.str = require('underscore.string'); _.str = require('underscore.string');
child_process = require('child_process');
os = require('os'); os = require('os');
chalk = require('chalk'); chalk = require('chalk');
@ -44,4 +48,16 @@
}); });
}; };
exports.sudo = function(command) {
var spawn;
if (os.platform() === 'win32') {
return capitano.runAsync(command.join(' '));
}
command = _.union(_.take(process.argv, 2), command);
spawn = child_process.spawn('sudo', command, {
stdio: 'inherit'
});
return exports.waitStream(spawn);
};
}).call(this); }).call(this);

View File

@ -7,6 +7,7 @@ form = require('resin-cli-form')
events = require('resin-cli-events') events = require('resin-cli-events')
rimraf = Promise.promisify(require('rimraf')) rimraf = Promise.promisify(require('rimraf'))
patterns = require('../utils/patterns') patterns = require('../utils/patterns')
helpers = require('../utils/helpers')
tmp = Promise.promisifyAll(require('tmp')) tmp = Promise.promisifyAll(require('tmp'))
tmp.setGracefulCleanup() tmp.setGracefulCleanup()
@ -188,7 +189,6 @@ exports.init =
commandOptions.yes commandOptions.yes
] ]
permission: 'user' permission: 'user'
root: true
action: (params, options, done) -> action: (params, options, done) ->
Promise.try -> Promise.try ->
return options.application if options.application? return options.application if options.application?
@ -206,7 +206,7 @@ exports.init =
.then(resin.models.device.get) .then(resin.models.device.get)
.tap (device) -> .tap (device) ->
capitano.runAsync("os configure #{temporalPath} #{device.uuid}").then -> capitano.runAsync("os configure #{temporalPath} #{device.uuid}").then ->
capitano.runAsync("os initialize #{temporalPath} #{application.device_type}") helpers.sudo([ 'os', 'initialize', temporalPath, application.device_type ])
.then (device) -> .then (device) ->
console.log('Done') console.log('Done')
return device.uuid return device.uuid

View File

@ -105,6 +105,7 @@ exports.initialize =
$ resin os initialize ../path/rpi.img 'raspberry-pi' $ resin os initialize ../path/rpi.img 'raspberry-pi'
''' '''
permission: 'user' permission: 'user'
root: true
action: (params, options, done) -> action: (params, options, done) ->
console.info('Initializing device') console.info('Initializing device')
resin.models.device.getManifestBySlug(params.type) resin.models.device.getManifestBySlug(params.type)

View File

@ -23,7 +23,6 @@ exports.wizard =
$ sudo resin quickstart $ sudo resin quickstart
$ sudo resin quickstart MyApp $ sudo resin quickstart MyApp
''' '''
root: true
permission: 'user' permission: 'user'
action: (params, options, done) -> action: (params, options, done) ->
Promise.try -> Promise.try ->

View File

@ -1,6 +1,8 @@
Promise = require('bluebird') Promise = require('bluebird')
capitano = Promise.promisifyAll(require('capitano'))
_ = require('lodash') _ = require('lodash')
_.str = require('underscore.string') _.str = require('underscore.string')
child_process = require('child_process')
os = require('os') os = require('os')
chalk = require('chalk') chalk = require('chalk')
@ -28,3 +30,17 @@ exports.waitStream = (stream) ->
stream.on('close', resolve) stream.on('close', resolve)
stream.on('end', resolve) stream.on('end', resolve)
stream.on('error', reject) stream.on('error', reject)
exports.sudo = (command) ->
# Bypass privilege elevation for Windows for now.
# We should use `windosu` in this case.
if os.platform() is 'win32'
return capitano.runAsync(command.join(' '))
command = _.union(_.take(process.argv, 2), command)
spawn = child_process.spawn 'sudo', command,
stdio: 'inherit'
return exports.waitStream(spawn)