Skip to content

Commit

Permalink
Remote: abstract receiving from remote process; to replace for all re…
Browse files Browse the repository at this point in the history
…mote process error/data handlers
  • Loading branch information
rwaldron committed Oct 6, 2015
1 parent 8848a73 commit 860684d
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 71 deletions.
28 changes: 8 additions & 20 deletions lib/tessel/deploy.js
Expand Up @@ -168,28 +168,17 @@ actions.findProject = function(opts) {
});
};

actions.sendBundle = function(t, filepath, opts) {
actions.sendBundle = function(tessel, filepath, opts) {
return new Promise(function(resolve, reject) {
// Execute the remote untar process command
return t.connection.exec(commands.untarStdin(filepath))
return tessel.connection.exec(commands.untarStdin(filepath))
// Once the process starts running
.then(function(remoteProcess) {
var error = '';

actions.findProject(opts).then(function(project) {
opts.target = path.resolve(process.cwd(), project.pushdir);
opts.resolvedEntryPoint = project.entryPoint;

// Collect error data as received
remoteProcess.stderr.on('data', function(data) {
error += data.toString();
});

actions.tarBundle(opts).then(function(bundle) {
if (error) {
return reject(error);
}

// RAM or Flash for log
var memtype;
if (opts.push) {
Expand All @@ -199,13 +188,12 @@ actions.sendBundle = function(t, filepath, opts) {
}

// Log write
logs.info('Writing %s to %s on %s (%d kB)...', project.entryPoint, memtype, t.name, bundle.length / 1000);
logs.info('Writing %s to %s on %s (%d kB)...', project.entryPoint, memtype, tessel.name, bundle.length / 1000);

// Wait for the transfer to finish...
remoteProcess.once('close', function() {
tessel.receive(remoteProcess).then(function() {
logs.info('Deployed.');
resolve(project.entryPoint);
});
}).catch(reject);

// Write the code bundle to the hardware
remoteProcess.stdin.end(bundle);
Expand Down Expand Up @@ -315,12 +303,12 @@ actions.writeToFile = function(t) {
});
};

actions.startPushedScript = function(t, entryPoint) {
actions.startPushedScript = function(tessel, entryPoint) {
return new Promise(function(resolve) {
// Once it has been written, run the script with Node
return t.connection.exec(commands.startPushedScript())
return tessel.connection.exec(commands.startPushedScript())
.then(function(remoteProcess) {
remoteProcess.once('close', function() {
return tessel.receive(remoteProcess).then(function() {
logs.info('Running %s...', entryPoint);
return resolve();
});
Expand Down
30 changes: 8 additions & 22 deletions lib/tessel/provision.js
Expand Up @@ -166,30 +166,16 @@ function copyKey(tessel, authFile, pubKey) {
return new Promise(function(resolve, reject) {
// Open up stdin to the authorized_keys file
return tessel.connection.exec(commands.appendStdinToFile(authFile))
.then(function(remoteProc) {
// Var to concat error data
var errBuf = '';
// If error data comes in
remoteProc.stderr.on('data', function(err) {
// Save it
errBuf += err.toString();
});
// Handle the end of the stream
remoteProc.once('close', function() {
// If errors were logged
if (errBuf.length) {
// Report the failure
return reject(errBuf);
} else {
// Everything worked as expected
logs.info('Tessel authenticated with public key.');
// End the process
return resolve();
}
});
.then(function(remoteProcess) {
tessel.receive(remoteProcess).then(function() {
// Everything worked as expected
logs.info('Tessel authenticated with public key.');
// End the process
return resolve();
}).catch(reject);

// Send the key
remoteProc.stdin.end(pubKey);
remoteProcess.stdin.end(pubKey);
});
});
}
Expand Down
54 changes: 26 additions & 28 deletions lib/tessel/tessel.js
Expand Up @@ -69,40 +69,38 @@ Object.defineProperty(Tessel.prototype, 'connection', {
}
});

Tessel.prototype.receive = function(remote) {
var error = '';
var received = new Buffer(0);

remote.stderr.on('data', function(buffer) {
error += buffer.toString();
});

remote.stdout.on('data', function(buffer) {
received = Buffer.concat([received, buffer]);
});

return new Promise(function(resolve, reject) {
remote.once('close', function() {
if (error) {
return reject(new Error(error));
} else {
return resolve(received);
}
});
});
};

Tessel.prototype.simpleExec = function(command) {
var self = this;
return new Promise(function(resolve, reject) {
// Stop processes and delete everything in the folder
return self.connection.exec(command)
.then(function(remoteProcess) {

// Buffer to store incoming error data
var errBuf = new Buffer(0);
// If we receive error data
remoteProcess.stderr.on('data', function(e) {
// Concatenate the data
errBuf = Buffer.concat([errBuf, e]);
});

// Buffer to store incoming stdout data
var dataBuf = new Buffer(0);
// If we receive stdout data
remoteProcess.stdout.on('data', function(d) {
// Concatenate the data
dataBuf = Buffer.concat([dataBuf, d]);
});

// Once the process completes
remoteProcess.once('close', function() {
// Check if an error occurred
if (errBuf.length) {
return reject(new Error(errBuf.toString()));
}
// Assume it worked if there was no error
else {
resolve(dataBuf.toString());
}
});
return self.receive(remoteProcess).then(function(received) {
return resolve(received.toString());
}).catch(reject);
});
});
};
Expand Down
2 changes: 1 addition & 1 deletion lib/tessel/wifi.js
Expand Up @@ -95,7 +95,7 @@ Tessel.prototype.connectToNetwork = function(opts) {
return self.connection.exec(commands.reconnectWifi())
.then(function(remoteProcess) {
// Once the wifi restart process closes
remoteProcess.once('close', function() {
return self.receive(remoteProcess).then(function() {
logs.info('Credentials set!');
// End the connection
return self.connection.end()
Expand Down
163 changes: 163 additions & 0 deletions test/unit/constructor.js
@@ -0,0 +1,163 @@
var sinon = require('sinon');
var Tessel = require('../../lib/tessel/tessel');
var RemoteProcessSimulator = require('../common/remote-process-simulator');
var TesselSimulator = require('../common/tessel-simulator');

exports['Tessel'] = {
setUp: function(done) {
done();
},

tearDown: function(done) {
done();
},

construction: function(test) {
test.expect(14);

var usb = new Tessel({
connectionType: 'USB'
});

var lan = new Tessel({
connectionType: 'LAN'
});

test.deepEqual(usb.usbConnection, {
connectionType: 'USB'
});
test.deepEqual(usb.connection, {
connectionType: 'USB'
});
test.equal(usb.lanConnection, undefined);

test.deepEqual(lan.lanConnection, {
connectionType: 'LAN'
});
test.deepEqual(lan.connection, {
connectionType: 'LAN'
});
test.equal(lan.usbConnection, undefined);

var usbOwnProperties = Object.getOwnPropertyNames(usb);
var lanOwnProperties = Object.getOwnPropertyNames(lan);

test.ok(usbOwnProperties.indexOf('addConnection') !== -1);
test.ok(usbOwnProperties.indexOf('closed') !== -1);
test.ok(usbOwnProperties.indexOf('name') !== -1);
test.ok(usbOwnProperties.indexOf('serialNumber') !== -1);

test.ok(lanOwnProperties.indexOf('addConnection') !== -1);
test.ok(lanOwnProperties.indexOf('closed') !== -1);
test.ok(lanOwnProperties.indexOf('name') !== -1);
test.ok(lanOwnProperties.indexOf('serialNumber') !== -1);

test.done();
},
};

exports['Tessel.prototype.receive'] = {
setUp: function(done) {
this.rps = new RemoteProcessSimulator();
this.tessel = new Tessel({
connectionType: 'USB'
});

done();
},

tearDown: function(done) {
done();
},

error: function(test) {
test.expect(1);

this.tessel.receive(this.rps).catch(function(error) {
test.equal(error.message, 'Some Error');
test.done();
});

this.rps.stderr.emit('data', new Buffer('Some Error'));
this.rps.emit('close');
},

dataReceived: function(test) {
test.expect(1);

this.tessel.receive(this.rps).then(function(received) {
test.equal(received.toString(), 'Some Data');
test.done();
});

this.rps.stdout.emit('data', new Buffer('Some Data'));
this.rps.emit('close');
},
};

exports['Tessel.prototype.simpleExec'] = {
setUp: function(done) {
this.sandbox = sinon.sandbox.create();

this.rps = new RemoteProcessSimulator();
this.tessel = new TesselSimulator();

this.receive = this.sandbox.spy(this.tessel, 'receive');
this.connectionExec = this.sandbox.stub(this.tessel.connection, 'exec', function() {
return Promise.resolve(this.rps);
}.bind(this));

done();
},

tearDown: function(done) {
this.sandbox.restore();
done();
},

callsConnectionExecWithCommand: function(test) {
test.expect(2);

this.tessel.simpleExec('some command').then(function() {
test.equal(this.connectionExec.callCount, 1);
test.equal(this.receive.callCount, 1);
test.done();
}.bind(this));

setImmediate(function() {
this.rps.emit('close');
}.bind(this));
},

callsConnectionExecWithCommandAndErrors: function(test) {
test.expect(3);

this.tessel.simpleExec('some command').catch(function(error) {
test.equal(error.message, 'Some Error');
test.equal(this.connectionExec.callCount, 1);
test.equal(this.receive.callCount, 1);
test.done();
}.bind(this));

setImmediate(function() {
this.rps.stderr.emit('data', new Buffer('Some Error'));
this.rps.emit('close');
}.bind(this));
},

callsConnectionExecWithCommandAndDataReceived: function(test) {
test.expect(3);

this.tessel.simpleExec('some command').then(function(received) {
test.equal(received, 'Some Data');
test.equal(this.connectionExec.callCount, 1);
test.equal(this.receive.callCount, 1);
test.done();
}.bind(this));

setImmediate(function() {
this.rps.stdout.emit('data', new Buffer('Some Data'));
this.rps.emit('close');
}.bind(this));
},
};

0 comments on commit 860684d

Please sign in to comment.