diff --git a/lib/src/writer.js b/lib/src/writer.js index ac24fda1..fb9b2b49 100644 --- a/lib/src/writer.js +++ b/lib/src/writer.js @@ -17,6 +17,7 @@ 'use strict'; const imageWrite = require('resin-image-write'); +const zipImage = require('resin-zip-image'); const Bluebird = require('bluebird'); const umount = Bluebird.promisifyAll(require('umount')); const fs = require('fs'); @@ -43,9 +44,17 @@ if (isWindows) { * const stream = writer.getImageStream('foo/bar/baz.img'); */ exports.getImageStream = function(image) { + if (zipImage.isZip(image)) { + if (!zipImage.isValidZipImage(image)) { + return Bluebird.reject(new Error('Invalid zip image')); + } + + return zipImage.extractImage(image); + } + let stream = fs.createReadStream(image); stream.length = fs.statSync(image).size; - return stream; + return Bluebird.resolve(stream); }; /** @@ -78,7 +87,8 @@ exports.getImageStream = function(image) { */ exports.writeImage = function(image, drive, options, onProgress) { return umount.umountAsync(drive.device).then(function() { - let stream = exports.getImageStream(image); + return exports.getImageStream(image); + }).then(function(stream) { return imageWrite.write(drive.device, stream); }).then(function(writer) { return new Bluebird(function(resolve, reject) { diff --git a/package.json b/package.json index 11317e5f..fecdd74b 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "lodash": "^4.5.1", "ngstorage": "^0.3.10", "resin-image-write": "^2.0.5", + "resin-zip-image": "^1.1.0", "sudo-prompt": "^2.2.0", "trackjs": "^2.1.16", "umount": "^1.1.1", @@ -78,6 +79,8 @@ "gulp-sass": "^2.0.4", "jshint": "^2.9.1", "jshint-stylish": "^2.0.1", - "mochainon": "^1.0.0" + "mochainon": "^1.0.0", + "rindle": "^1.3.0", + "tmp": "0.0.28" } } diff --git a/tests/src/writer.spec.js b/tests/src/writer.spec.js index 0feea72f..482bc382 100644 --- a/tests/src/writer.spec.js +++ b/tests/src/writer.spec.js @@ -2,27 +2,84 @@ const m = require('mochainon'); const ReadableStream = require('stream').Readable; +const Bluebird = require('bluebird'); +const fs = Bluebird.promisifyAll(require('fs')); const path = require('path'); +const tmp = require('tmp'); +const rindle = require('rindle'); const writer = require('../../lib/src/writer'); describe('Writer:', function() { describe('.getImageStream()', function() { - describe('given a valid image', function() { + describe('given a valid image file', function() { beforeEach(function() { this.image = path.join(__dirname, '..', 'utils', 'data.random'); }); - it('should return a readable stream', function() { - const stream = writer.getImageStream(this.image); - m.chai.expect(stream).to.be.an.instanceof(ReadableStream); + it('should return a readable stream', function(done) { + writer.getImageStream(this.image).then(function(stream) { + m.chai.expect(stream).to.be.an.instanceof(ReadableStream); + }).nodeify(done); }); - it('should append a .length property with the correct size', function() { - const stream = writer.getImageStream(this.image); - m.chai.expect(stream.length).to.equal(2097152); + it('should append a .length property with the correct size', function(done) { + writer.getImageStream(this.image).then(function(stream) { + m.chai.expect(stream.length).to.equal(2097152); + }).nodeify(done); + }); + + }); + + describe('given a valid image zip', function() { + + beforeEach(function() { + this.image = path.join(__dirname, '..', 'utils', 'data.zip'); + }); + + it('should return a readable stream', function(done) { + writer.getImageStream(this.image).then(function(stream) { + m.chai.expect(stream).to.be.an.instanceof(ReadableStream); + }).nodeify(done); + }); + + it('should append a .length property with the correct size', function(done) { + writer.getImageStream(this.image).then(function(stream) { + m.chai.expect(stream.length).to.equal(2097152); + }).nodeify(done); + }); + + it('should pipe the image from the zip', function(done) { + const tmpFile = tmp.tmpNameSync(); + const image = path.join(__dirname, '..', 'utils', 'data.random'); + const output = fs.createWriteStream(tmpFile); + + writer.getImageStream(this.image).then(function(stream) { + return stream.pipe(output); + }).then(rindle.wait).then(function() { + return Bluebird.props({ + output: fs.readFileAsync(tmpFile), + data: fs.readFileAsync(image) + }); + }).then(function(results) { + m.chai.expect(results.output).to.deep.equal(results.data); + return fs.unlinkAsync(tmpFile); + }).nodeify(done); + }); + + }); + + describe('given an invalid image zip', function() { + + beforeEach(function() { + this.image = path.join(__dirname, '..', 'utils', 'invalid.zip'); + }); + + it('should be rejected with an error', function() { + const promise = writer.getImageStream(this.image); + m.chai.expect(promise).to.be.rejectedWith('Invalid zip image'); }); }); diff --git a/tests/utils/data.zip b/tests/utils/data.zip new file mode 100644 index 00000000..617a734d Binary files /dev/null and b/tests/utils/data.zip differ diff --git a/tests/utils/invalid.zip b/tests/utils/invalid.zip new file mode 100644 index 00000000..3e4181c9 Binary files /dev/null and b/tests/utils/invalid.zip differ