Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote: abstract receiving from remote process; to replace for all remote process error/data handlers #365

Merged
merged 1 commit into from Oct 8, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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);
}
});
});
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johnnyman727 start here.

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));
},
};