mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-04-16 23:38:52 +00:00
Merge pull request #1240 from balena-io/tests-js
Convert some tests to javascript
This commit is contained in:
commit
67b22cee54
@ -94,7 +94,9 @@ export class Network {
|
||||
public static fromComposeObject(
|
||||
name: string,
|
||||
appId: number,
|
||||
network: Partial<ComposeNetworkConfig>,
|
||||
network: Partial<Omit<ComposeNetworkConfig, 'ipam'>> & {
|
||||
ipam?: Partial<ComposeNetworkConfig['ipam']>;
|
||||
},
|
||||
opts: NetworkOptions,
|
||||
): Network {
|
||||
const net = new Network(opts);
|
||||
|
@ -18,7 +18,7 @@ export interface DockerPortOptions {
|
||||
portBindings: PortBindings;
|
||||
}
|
||||
|
||||
interface PortRange {
|
||||
export interface PortRange {
|
||||
internalStart: number;
|
||||
internalEnd: number;
|
||||
externalStart: number;
|
||||
|
@ -1,116 +0,0 @@
|
||||
https = require 'https'
|
||||
stream = require 'stream'
|
||||
zlib = require 'zlib'
|
||||
|
||||
Promise = require 'bluebird'
|
||||
{ expect } = require './lib/chai-config'
|
||||
sinon = require 'sinon'
|
||||
{ stub } = sinon
|
||||
|
||||
{ Logger } = require '../src/logger'
|
||||
{ ContainerLogs } = require '../src/logging/container'
|
||||
describe 'Logger', ->
|
||||
beforeEach ->
|
||||
@_req = new stream.PassThrough()
|
||||
@_req.flushHeaders = sinon.spy()
|
||||
@_req.end = sinon.spy()
|
||||
|
||||
@_req.body = ''
|
||||
@_req
|
||||
.pipe(zlib.createGunzip())
|
||||
.on 'data', (chunk) =>
|
||||
@_req.body += chunk
|
||||
|
||||
stub(https, 'request').returns(@_req)
|
||||
|
||||
@fakeEventTracker = {
|
||||
track: sinon.spy()
|
||||
}
|
||||
|
||||
@logger = new Logger({ eventTracker: @fakeEventTracker })
|
||||
@logger.init({
|
||||
apiEndpoint: 'https://example.com'
|
||||
uuid: 'deadbeef'
|
||||
deviceApiKey: 'secretkey'
|
||||
unmanaged: false
|
||||
enableLogs: true
|
||||
localMode: false
|
||||
})
|
||||
|
||||
afterEach ->
|
||||
https.request.restore()
|
||||
|
||||
it 'waits the grace period before sending any logs', ->
|
||||
clock = sinon.useFakeTimers()
|
||||
@logger.log({ message: 'foobar', serviceId: 15 })
|
||||
clock.tick(4999)
|
||||
clock.restore()
|
||||
|
||||
Promise.delay(100)
|
||||
.then =>
|
||||
expect(@_req.body).to.equal('')
|
||||
|
||||
it 'tears down the connection after inactivity', ->
|
||||
clock = sinon.useFakeTimers()
|
||||
@logger.log({ message: 'foobar', serviceId: 15 })
|
||||
clock.tick(61000)
|
||||
clock.restore()
|
||||
|
||||
Promise.delay(100)
|
||||
.then =>
|
||||
expect(@_req.end.calledOnce).to.be.true
|
||||
|
||||
|
||||
it 'sends logs as gzipped ndjson', ->
|
||||
timestamp = Date.now()
|
||||
@logger.log({ message: 'foobar', serviceId: 15 })
|
||||
@logger.log({ timestamp: 1337, message: 'foobar', serviceId: 15 })
|
||||
@logger.log({ message: 'foobar' }) # shold be ignored
|
||||
|
||||
Promise.delay(5500).then =>
|
||||
expect(https.request.calledOnce).to.be.true
|
||||
opts = https.request.firstCall.args[0]
|
||||
|
||||
expect(opts.href).to.equal('https://example.com/device/v2/deadbeef/log-stream')
|
||||
expect(opts.method).to.equal('POST')
|
||||
expect(opts.headers).to.deep.equal({
|
||||
'Authorization': 'Bearer secretkey'
|
||||
'Content-Type': 'application/x-ndjson'
|
||||
'Content-Encoding': 'gzip'
|
||||
})
|
||||
|
||||
lines = @_req.body.split('\n')
|
||||
expect(lines.length).to.equal(3)
|
||||
expect(lines[2]).to.equal('')
|
||||
|
||||
msg = JSON.parse(lines[0])
|
||||
expect(msg).to.have.property('message').that.equals('foobar')
|
||||
expect(msg).to.have.property('serviceId').that.equals(15)
|
||||
expect(msg).to.have.property('timestamp').that.is.at.least(timestamp)
|
||||
msg = JSON.parse(lines[1])
|
||||
expect(msg).to.deep.equal({ timestamp: 1337, message: 'foobar', serviceId: 15 })
|
||||
|
||||
it 'allows logging system messages which are also reported to the eventTracker', ->
|
||||
timestamp = Date.now()
|
||||
@logger.logSystemMessage('Hello there!', { someProp: 'someVal' }, 'Some event name')
|
||||
|
||||
Promise.delay(5500)
|
||||
.then =>
|
||||
expect(@fakeEventTracker.track).to.be.calledWith('Some event name', { someProp: 'someVal' })
|
||||
lines = @_req.body.split('\n')
|
||||
expect(lines.length).to.equal(2)
|
||||
expect(lines[1]).to.equal('')
|
||||
|
||||
msg = JSON.parse(lines[0])
|
||||
expect(msg).to.have.property('message').that.equals('Hello there!')
|
||||
expect(msg).to.have.property('isSystem').that.equals(true)
|
||||
expect(msg).to.have.property('timestamp').that.is.at.least(timestamp)
|
||||
|
||||
it 'should support non-tty log lines', ->
|
||||
message = '\u0001\u0000\u0000\u0000\u0000\u0000\u0000?2018-09-21T12:37:09.819134000Z this is the message'
|
||||
buffer = Buffer.from(message)
|
||||
|
||||
expect(ContainerLogs.extractMessage(buffer)).to.deep.equal({
|
||||
message: 'this is the message',
|
||||
timestamp: 1537533429819
|
||||
})
|
148
test/12-logger.spec.js
Normal file
148
test/12-logger.spec.js
Normal file
@ -0,0 +1,148 @@
|
||||
import * as https from 'https';
|
||||
import * as stream from 'stream';
|
||||
import * as zlib from 'zlib';
|
||||
import * as Promise from 'bluebird';
|
||||
import { expect } from './lib/chai-config';
|
||||
import * as sinon from 'sinon';
|
||||
|
||||
import { Logger } from '../src/logger';
|
||||
import { ContainerLogs } from '../src/logging/container';
|
||||
describe('Logger', function() {
|
||||
beforeEach(function() {
|
||||
this._req = new stream.PassThrough();
|
||||
this._req.flushHeaders = sinon.spy();
|
||||
this._req.end = sinon.spy();
|
||||
|
||||
this._req.body = '';
|
||||
this._req.pipe(zlib.createGunzip()).on('data', chunk => {
|
||||
this._req.body += chunk;
|
||||
});
|
||||
|
||||
this.requestStub = sinon.stub(https, 'request').returns(this._req);
|
||||
|
||||
this.fakeEventTracker = {
|
||||
track: sinon.spy(),
|
||||
};
|
||||
|
||||
// @ts-ignore missing db property
|
||||
this.logger = new Logger({ eventTracker: this.fakeEventTracker });
|
||||
return this.logger.init({
|
||||
apiEndpoint: 'https://example.com',
|
||||
uuid: 'deadbeef',
|
||||
deviceApiKey: 'secretkey',
|
||||
unmanaged: false,
|
||||
enableLogs: true,
|
||||
localMode: false,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
this.requestStub.restore();
|
||||
});
|
||||
|
||||
it('waits the grace period before sending any logs', function() {
|
||||
const clock = sinon.useFakeTimers();
|
||||
this.logger.log({ message: 'foobar', serviceId: 15 });
|
||||
clock.tick(4999);
|
||||
clock.restore();
|
||||
|
||||
return Promise.delay(100).then(() => {
|
||||
expect(this._req.body).to.equal('');
|
||||
});
|
||||
});
|
||||
|
||||
it('tears down the connection after inactivity', function() {
|
||||
const clock = sinon.useFakeTimers();
|
||||
this.logger.log({ message: 'foobar', serviceId: 15 });
|
||||
clock.tick(61000);
|
||||
clock.restore();
|
||||
|
||||
return Promise.delay(100).then(() => {
|
||||
expect(this._req.end.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('sends logs as gzipped ndjson', function() {
|
||||
const timestamp = Date.now();
|
||||
this.logger.log({ message: 'foobar', serviceId: 15 });
|
||||
this.logger.log({ timestamp: 1337, message: 'foobar', serviceId: 15 });
|
||||
this.logger.log({ message: 'foobar' }); // shold be ignored
|
||||
|
||||
return Promise.delay(5500).then(() => {
|
||||
expect(this.requestStub.calledOnce).to.be.true;
|
||||
const opts = this.requestStub.firstCall.args[0];
|
||||
|
||||
expect(opts.href).to.equal(
|
||||
'https://example.com/device/v2/deadbeef/log-stream',
|
||||
);
|
||||
expect(opts.method).to.equal('POST');
|
||||
expect(opts.headers).to.deep.equal({
|
||||
Authorization: 'Bearer secretkey',
|
||||
'Content-Type': 'application/x-ndjson',
|
||||
'Content-Encoding': 'gzip',
|
||||
});
|
||||
|
||||
const lines = this._req.body.split('\n');
|
||||
expect(lines.length).to.equal(3);
|
||||
expect(lines[2]).to.equal('');
|
||||
|
||||
let msg = JSON.parse(lines[0]);
|
||||
expect(msg)
|
||||
.to.have.property('message')
|
||||
.that.equals('foobar');
|
||||
expect(msg)
|
||||
.to.have.property('serviceId')
|
||||
.that.equals(15);
|
||||
expect(msg)
|
||||
.to.have.property('timestamp')
|
||||
.that.is.at.least(timestamp);
|
||||
msg = JSON.parse(lines[1]);
|
||||
expect(msg).to.deep.equal({
|
||||
timestamp: 1337,
|
||||
message: 'foobar',
|
||||
serviceId: 15,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('allows logging system messages which are also reported to the eventTracker', function() {
|
||||
const timestamp = Date.now();
|
||||
this.logger.logSystemMessage(
|
||||
'Hello there!',
|
||||
{ someProp: 'someVal' },
|
||||
'Some event name',
|
||||
);
|
||||
|
||||
return Promise.delay(5500).then(() => {
|
||||
expect(this.fakeEventTracker.track).to.be.calledWith('Some event name', {
|
||||
someProp: 'someVal',
|
||||
});
|
||||
const lines = this._req.body.split('\n');
|
||||
expect(lines.length).to.equal(2);
|
||||
expect(lines[1]).to.equal('');
|
||||
|
||||
const msg = JSON.parse(lines[0]);
|
||||
expect(msg)
|
||||
.to.have.property('message')
|
||||
.that.equals('Hello there!');
|
||||
expect(msg)
|
||||
.to.have.property('isSystem')
|
||||
.that.equals(true);
|
||||
expect(msg)
|
||||
.to.have.property('timestamp')
|
||||
.that.is.at.least(timestamp);
|
||||
});
|
||||
});
|
||||
|
||||
it('should support non-tty log lines', function() {
|
||||
const message =
|
||||
'\u0001\u0000\u0000\u0000\u0000\u0000\u0000?2018-09-21T12:37:09.819134000Z this is the message';
|
||||
const buffer = Buffer.from(message);
|
||||
|
||||
// @ts-ignore accessing a private function
|
||||
expect(ContainerLogs.extractMessage(buffer)).to.deep.equal({
|
||||
message: 'this is the message',
|
||||
timestamp: 1537533429819,
|
||||
});
|
||||
});
|
||||
});
|
@ -1,52 +0,0 @@
|
||||
{ expect } = require './lib/chai-config'
|
||||
|
||||
conversion = require '../src/lib/conversions'
|
||||
|
||||
describe 'conversions', ->
|
||||
|
||||
describe 'envArrayToObject', ->
|
||||
it 'should convert an env array to an object', ->
|
||||
expect(conversion.envArrayToObject([
|
||||
'key=value'
|
||||
'test1=test2'
|
||||
'k=v'
|
||||
'equalsvalue=thisvaluehasan=char'
|
||||
'asd='
|
||||
'number=123'
|
||||
])).to.deep.equal({
|
||||
key: 'value'
|
||||
test1: 'test2'
|
||||
k: 'v'
|
||||
equalsvalue: 'thisvaluehasan=char'
|
||||
asd: ''
|
||||
number: '123'
|
||||
})
|
||||
|
||||
it 'should ignore invalid env array entries', ->
|
||||
expect(conversion.envArrayToObject([
|
||||
'key1',
|
||||
'key1=value1'
|
||||
])).to.deep.equal({
|
||||
key1: 'value1'
|
||||
})
|
||||
|
||||
it 'should return an empty object with an empty input', ->
|
||||
expect(conversion.envArrayToObject(null)).to.deep.equal({})
|
||||
expect(conversion.envArrayToObject('')).to.deep.equal({})
|
||||
expect(conversion.envArrayToObject([])).to.deep.equal({})
|
||||
expect(conversion.envArrayToObject(1)).to.deep.equal({})
|
||||
|
||||
it 'should correctly handle whitespace', ->
|
||||
expect(conversion.envArrayToObject([
|
||||
'key1= test',
|
||||
'key2=test\ntest',
|
||||
'key3=test ',
|
||||
'key4= test '
|
||||
'key5=test\r\ntest',
|
||||
])).to.deep.equal({
|
||||
key1: ' test',
|
||||
key2: 'test\ntest',
|
||||
key3: 'test ',
|
||||
key4: ' test ',
|
||||
key5: 'test\r\ntest'
|
||||
})
|
59
test/15-conversions.spec.js
Normal file
59
test/15-conversions.spec.js
Normal file
@ -0,0 +1,59 @@
|
||||
import { expect } from './lib/chai-config';
|
||||
import * as conversion from '../src/lib/conversions';
|
||||
|
||||
describe('conversions', function() {
|
||||
describe('envArrayToObject', function() {
|
||||
it('should convert an env array to an object', () =>
|
||||
expect(
|
||||
conversion.envArrayToObject([
|
||||
'key=value',
|
||||
'test1=test2',
|
||||
'k=v',
|
||||
'equalsvalue=thisvaluehasan=char',
|
||||
'asd=',
|
||||
'number=123',
|
||||
]),
|
||||
).to.deep.equal({
|
||||
key: 'value',
|
||||
test1: 'test2',
|
||||
k: 'v',
|
||||
equalsvalue: 'thisvaluehasan=char',
|
||||
asd: '',
|
||||
number: '123',
|
||||
}));
|
||||
|
||||
it('should ignore invalid env array entries', () =>
|
||||
expect(
|
||||
conversion.envArrayToObject(['key1', 'key1=value1']),
|
||||
).to.deep.equal({
|
||||
key1: 'value1',
|
||||
}));
|
||||
|
||||
it('should return an empty object with an empty input', function() {
|
||||
// @ts-ignore passing invalid value to test
|
||||
expect(conversion.envArrayToObject(null)).to.deep.equal({});
|
||||
// @ts-ignore passing invalid value to test
|
||||
expect(conversion.envArrayToObject('')).to.deep.equal({});
|
||||
expect(conversion.envArrayToObject([])).to.deep.equal({});
|
||||
// @ts-ignore passing invalid value to test
|
||||
expect(conversion.envArrayToObject(1)).to.deep.equal({});
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly handle whitespace', () =>
|
||||
expect(
|
||||
conversion.envArrayToObject([
|
||||
'key1= test',
|
||||
'key2=test\ntest',
|
||||
'key3=test ',
|
||||
'key4= test ',
|
||||
'key5=test\r\ntest',
|
||||
]),
|
||||
).to.deep.equal({
|
||||
key1: ' test',
|
||||
key2: 'test\ntest',
|
||||
key3: 'test ',
|
||||
key4: ' test ',
|
||||
key5: 'test\r\ntest',
|
||||
}));
|
||||
});
|
@ -1,357 +0,0 @@
|
||||
{ expect } = require './lib/chai-config'
|
||||
|
||||
{ PortMap } = require '../src/compose/ports'
|
||||
|
||||
describe 'Ports', ->
|
||||
|
||||
describe 'Port string parsing', ->
|
||||
|
||||
it 'should correctly parse a port string without a range', ->
|
||||
|
||||
expect(new PortMap('80')).to.deep.equal(new PortMap({
|
||||
internalStart: 80
|
||||
internalEnd: 80
|
||||
externalStart: 80
|
||||
externalEnd: 80
|
||||
protocol: 'tcp'
|
||||
host: ''
|
||||
}))
|
||||
|
||||
expect(new PortMap('80:80')).to.deep.equal(new PortMap({
|
||||
internalStart: 80
|
||||
internalEnd: 80
|
||||
externalStart: 80
|
||||
externalEnd: 80
|
||||
protocol: 'tcp'
|
||||
host: ''
|
||||
}))
|
||||
|
||||
it 'should correctly parse a port string without an external range', ->
|
||||
|
||||
expect(new PortMap('80-90')).to.deep.equal(new PortMap({
|
||||
internalStart: 80
|
||||
internalEnd: 90
|
||||
externalStart: 80
|
||||
externalEnd: 90
|
||||
protocol: 'tcp'
|
||||
host: ''
|
||||
}))
|
||||
|
||||
it 'should correctly parse a port string with a range', ->
|
||||
expect(new PortMap('80-100:100-120')).to.deep.equal(new PortMap({
|
||||
internalStart: 100
|
||||
internalEnd: 120
|
||||
externalStart: 80
|
||||
externalEnd: 100
|
||||
protocol: 'tcp'
|
||||
host: ''
|
||||
}))
|
||||
|
||||
it 'should correctly parse a protocol', ->
|
||||
expect(new PortMap('80/udp')).to.deep.equal(new PortMap({
|
||||
internalStart: 80
|
||||
internalEnd: 80
|
||||
externalStart: 80
|
||||
externalEnd: 80
|
||||
protocol: 'udp'
|
||||
host: ''
|
||||
}))
|
||||
|
||||
expect(new PortMap('80:80/udp')).to.deep.equal(new PortMap({
|
||||
internalStart: 80
|
||||
internalEnd: 80
|
||||
externalStart: 80
|
||||
externalEnd: 80
|
||||
protocol: 'udp'
|
||||
host: ''
|
||||
}))
|
||||
|
||||
expect(new PortMap('80-90:100-110/udp')).to.deep.equal(new PortMap({
|
||||
internalStart: 100
|
||||
internalEnd: 110
|
||||
externalStart: 80
|
||||
externalEnd: 90
|
||||
protocol: 'udp'
|
||||
host: ''
|
||||
}))
|
||||
|
||||
it 'should throw when the port string is incorrect', ->
|
||||
expect(-> new PortMap('80-90:80-85')).to.throw
|
||||
|
||||
describe 'toDockerOpts', ->
|
||||
|
||||
it 'should correctly generate docker options', ->
|
||||
|
||||
expect(new PortMap('80').toDockerOpts()).to.deep.equal({
|
||||
exposedPorts: {
|
||||
'80/tcp': {}
|
||||
}
|
||||
portBindings: {
|
||||
'80/tcp': [{ HostIp: '', HostPort: '80' }]
|
||||
}
|
||||
})
|
||||
|
||||
it 'should correctly generate docker options for a port range', ->
|
||||
expect(new PortMap('80-85').toDockerOpts()).to.deep.equal({
|
||||
exposedPorts: {
|
||||
'80/tcp': {}
|
||||
'81/tcp': {}
|
||||
'82/tcp': {}
|
||||
'83/tcp': {}
|
||||
'84/tcp': {}
|
||||
'85/tcp': {}
|
||||
}
|
||||
portBindings: {
|
||||
'80/tcp': [{ HostIp: '', HostPort: '80' }]
|
||||
'81/tcp': [{ HostIp: '', HostPort: '81' }]
|
||||
'82/tcp': [{ HostIp: '', HostPort: '82' }]
|
||||
'83/tcp': [{ HostIp: '', HostPort: '83' }]
|
||||
'84/tcp': [{ HostIp: '', HostPort: '84' }]
|
||||
'85/tcp': [{ HostIp: '', HostPort: '85' }]
|
||||
}
|
||||
})
|
||||
|
||||
describe 'fromDockerOpts', ->
|
||||
|
||||
it 'should correctly detect a port range', ->
|
||||
|
||||
expect(PortMap.fromDockerOpts({
|
||||
'100/tcp': [{ HostIp: '123', HostPort: 200 }]
|
||||
'101/tcp': [{ HostIp: '123', HostPort: 201 }]
|
||||
'102/tcp': [{ HostIp: '123', HostPort: 202 }]
|
||||
})).to.deep.equal([new PortMap({
|
||||
internalStart: 100
|
||||
internalEnd: 102
|
||||
externalStart: 200
|
||||
externalEnd: 202
|
||||
protocol: 'tcp'
|
||||
host: '123'
|
||||
})])
|
||||
|
||||
it 'should correctly split ports into ranges', ->
|
||||
expect(PortMap.fromDockerOpts({
|
||||
'100/tcp': [{ HostIp: '123', HostPort: 200 }]
|
||||
'101/tcp': [{ HostIp: '123', HostPort: 201 }]
|
||||
'105/tcp': [{ HostIp: '123', HostPort: 205 }]
|
||||
'106/tcp': [{ HostIp: '123', HostPort: 206 }]
|
||||
'110/tcp': [{ HostIp: '123', HostPort: 210 }]
|
||||
})).to.deep.equal([
|
||||
new PortMap({
|
||||
internalStart: 100
|
||||
internalEnd: 101
|
||||
externalStart: 200
|
||||
externalEnd: 201
|
||||
protocol: 'tcp'
|
||||
host: '123'
|
||||
})
|
||||
new PortMap({
|
||||
internalStart: 105
|
||||
internalEnd: 106
|
||||
externalStart: 205
|
||||
externalEnd: 206
|
||||
protocol: 'tcp'
|
||||
host: '123'
|
||||
})
|
||||
new PortMap({
|
||||
internalStart: 110
|
||||
internalEnd: 110
|
||||
externalStart: 210
|
||||
externalEnd: 210
|
||||
protocol: 'tcp'
|
||||
host: '123'
|
||||
})
|
||||
])
|
||||
|
||||
it 'should correctly consider internal and external ports', ->
|
||||
expect(PortMap.fromDockerOpts({
|
||||
'100/tcp': [{ HostIp: '123', HostPort: 200 }]
|
||||
'101/tcp': [{ HostIp: '123', HostPort: 101 }]
|
||||
'102/tcp': [{ HostIp: '123', HostPort: 202 }]
|
||||
})).to.deep.equal([
|
||||
new PortMap({
|
||||
internalStart: 100
|
||||
internalEnd: 100
|
||||
externalStart: 200
|
||||
externalEnd: 200
|
||||
protocol: 'tcp'
|
||||
host: '123'
|
||||
})
|
||||
new PortMap({
|
||||
internalStart: 101
|
||||
internalEnd: 101
|
||||
externalStart: 101
|
||||
externalEnd: 101
|
||||
protocol: 'tcp'
|
||||
host: '123'
|
||||
})
|
||||
new PortMap({
|
||||
internalStart: 102
|
||||
internalEnd: 102
|
||||
externalStart: 202
|
||||
externalEnd: 202
|
||||
protocol: 'tcp'
|
||||
host: '123'
|
||||
})
|
||||
])
|
||||
|
||||
it 'should consider the host when generating ranges', ->
|
||||
expect(PortMap.fromDockerOpts({
|
||||
'100/tcp': [{ HostIp: '123', HostPort: 200 }]
|
||||
'101/tcp': [{ HostIp: '456', HostPort: 201 }]
|
||||
'102/tcp': [{ HostIp: '456', HostPort: 202 }]
|
||||
})).to.deep.equal([
|
||||
new PortMap({
|
||||
internalStart: 100
|
||||
internalEnd: 100
|
||||
externalStart: 200
|
||||
externalEnd: 200
|
||||
protocol: 'tcp'
|
||||
host: '123'
|
||||
})
|
||||
new PortMap({
|
||||
internalStart: 101
|
||||
internalEnd: 102
|
||||
externalStart: 201
|
||||
externalEnd: 202
|
||||
protocol: 'tcp'
|
||||
host: '456'
|
||||
})
|
||||
])
|
||||
|
||||
it 'should consider the protocol when generating ranges', ->
|
||||
expect(PortMap.fromDockerOpts({
|
||||
'100/tcp': [{ HostIp: '123', HostPort: 200 }]
|
||||
'101/udp': [{ HostIp: '123', HostPort: 201 }]
|
||||
'102/udp': [{ HostIp: '123', HostPort: 202 }]
|
||||
})).to.deep.equal([
|
||||
new PortMap({
|
||||
internalStart: 100
|
||||
internalEnd: 100
|
||||
externalStart: 200
|
||||
externalEnd: 200
|
||||
protocol: 'tcp'
|
||||
host: '123'
|
||||
})
|
||||
new PortMap({
|
||||
internalStart: 101
|
||||
internalEnd: 102
|
||||
externalStart: 201
|
||||
externalEnd: 202
|
||||
protocol: 'udp'
|
||||
host: '123'
|
||||
})
|
||||
])
|
||||
|
||||
it 'should correctly detect multiple hosts ports on an internal port', ->
|
||||
expect(PortMap.fromDockerOpts({
|
||||
'100/tcp': [{ HostIp: '123', HostPort: 200 }, { HostIp: '123', HostPort: 201 }]
|
||||
})).to.deep.equal([
|
||||
new PortMap({
|
||||
internalStart: 100,
|
||||
internalEnd: 100,
|
||||
externalStart: 200,
|
||||
externalEnd: 200,
|
||||
protocol: 'tcp',
|
||||
host: '123'
|
||||
})
|
||||
new PortMap({
|
||||
internalStart: 100,
|
||||
internalEnd: 100,
|
||||
externalStart: 201,
|
||||
externalEnd: 201,
|
||||
protocol: 'tcp',
|
||||
host: '123'
|
||||
})
|
||||
])
|
||||
|
||||
describe 'Running container comparison', ->
|
||||
it 'should not consider order when comparing current and target state', ->
|
||||
portBindings = require('./data/ports/not-ascending/port-bindings.json')
|
||||
compose = require('./data/ports/not-ascending/compose.json')
|
||||
portMapsCurrent = PortMap.fromDockerOpts(portBindings)
|
||||
portMapsTarget = PortMap.fromComposePorts(compose.ports)
|
||||
|
||||
expect(portMapsTarget).to.deep.equal(portMapsCurrent)
|
||||
|
||||
describe 'fromComposePorts', ->
|
||||
it 'should normalise compose ports', ->
|
||||
expect(PortMap.fromComposePorts([
|
||||
'80:80',
|
||||
'81:81',
|
||||
'82:82',
|
||||
])).to.deep.equal([
|
||||
new PortMap('80-82')
|
||||
])
|
||||
|
||||
describe 'normalisePortMaps', ->
|
||||
|
||||
it 'should correctly normalise PortMap lists', ->
|
||||
expect(PortMap.normalisePortMaps([
|
||||
new PortMap('80:90')
|
||||
new PortMap('81:91')
|
||||
])).to.deep.equal([
|
||||
new PortMap('80-81:90-91')
|
||||
])
|
||||
|
||||
expect(PortMap.normalisePortMaps([
|
||||
new PortMap('80:90')
|
||||
new PortMap('81:91')
|
||||
new PortMap('82:92')
|
||||
new PortMap('83:93')
|
||||
new PortMap('84:94')
|
||||
new PortMap('85:95')
|
||||
new PortMap('86:96')
|
||||
new PortMap('87:97')
|
||||
new PortMap('88:98')
|
||||
new PortMap('89:99')
|
||||
new PortMap('90:100')
|
||||
])).to.deep.equal([
|
||||
new PortMap('80-90:90-100')
|
||||
])
|
||||
|
||||
expect(PortMap.normalisePortMaps([])).to.deep.equal([])
|
||||
|
||||
it 'should correctly consider protocols', ->
|
||||
expect(PortMap.normalisePortMaps([
|
||||
new PortMap('80:90')
|
||||
new PortMap('81:91/udp')
|
||||
])).to.deep.equal([
|
||||
new PortMap('80:90')
|
||||
new PortMap('81:91/udp')
|
||||
])
|
||||
|
||||
expect(PortMap.normalisePortMaps([
|
||||
new PortMap('80:90')
|
||||
new PortMap('100:110/udp')
|
||||
new PortMap('81:91')
|
||||
])).to.deep.equal([
|
||||
new PortMap('80-81:90-91')
|
||||
new PortMap('100:110/udp')
|
||||
])
|
||||
|
||||
# This shouldn't ever be provided, but it shows the algorithm
|
||||
# working properly
|
||||
expect(PortMap.normalisePortMaps([
|
||||
new PortMap('80:90')
|
||||
new PortMap('81:91/udp')
|
||||
new PortMap('81:91')
|
||||
])).to.deep.equal([
|
||||
new PortMap('80-81:90-91')
|
||||
new PortMap('81:91/udp')
|
||||
])
|
||||
|
||||
it 'should correctly consider hosts', ->
|
||||
expect(PortMap.normalisePortMaps([
|
||||
new PortMap('127.0.0.1:80:80')
|
||||
new PortMap('81:81')
|
||||
])).to.deep.equal([
|
||||
new PortMap('127.0.0.1:80:80')
|
||||
new PortMap('81:81')
|
||||
])
|
||||
|
||||
expect(PortMap.normalisePortMaps([
|
||||
new PortMap('127.0.0.1:80:80')
|
||||
new PortMap('127.0.0.1:81:81')
|
||||
])).to.deep.equal([
|
||||
new PortMap('127.0.0.1:80-81:80-81')
|
||||
])
|
398
test/16-ports.spec.ts
Normal file
398
test/16-ports.spec.ts
Normal file
@ -0,0 +1,398 @@
|
||||
import { PortMap, PortRange } from '../src/compose/ports';
|
||||
import { expect } from './lib/chai-config';
|
||||
|
||||
// Force cast `PortMap` as a public version so we can test it
|
||||
const PortMapPublic = (PortMap as any) as new (
|
||||
portStrOrObj: string | PortRange,
|
||||
) => PortMap;
|
||||
|
||||
describe('Ports', function() {
|
||||
describe('Port string parsing', function() {
|
||||
it('should correctly parse a port string without a range', function() {
|
||||
expect(new PortMapPublic('80')).to.deep.equal(
|
||||
new PortMapPublic({
|
||||
internalStart: 80,
|
||||
internalEnd: 80,
|
||||
externalStart: 80,
|
||||
externalEnd: 80,
|
||||
protocol: 'tcp',
|
||||
host: '',
|
||||
}),
|
||||
);
|
||||
|
||||
expect(new PortMapPublic('80:80')).to.deep.equal(
|
||||
new PortMapPublic({
|
||||
internalStart: 80,
|
||||
internalEnd: 80,
|
||||
externalStart: 80,
|
||||
externalEnd: 80,
|
||||
protocol: 'tcp',
|
||||
host: '',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should correctly parse a port string without an external range', () =>
|
||||
expect(new PortMapPublic('80-90')).to.deep.equal(
|
||||
new PortMapPublic({
|
||||
internalStart: 80,
|
||||
internalEnd: 90,
|
||||
externalStart: 80,
|
||||
externalEnd: 90,
|
||||
protocol: 'tcp',
|
||||
host: '',
|
||||
}),
|
||||
));
|
||||
|
||||
it('should correctly parse a port string with a range', () =>
|
||||
expect(new PortMapPublic('80-100:100-120')).to.deep.equal(
|
||||
new PortMapPublic({
|
||||
internalStart: 100,
|
||||
internalEnd: 120,
|
||||
externalStart: 80,
|
||||
externalEnd: 100,
|
||||
protocol: 'tcp',
|
||||
host: '',
|
||||
}),
|
||||
));
|
||||
|
||||
it('should correctly parse a protocol', function() {
|
||||
expect(new PortMapPublic('80/udp')).to.deep.equal(
|
||||
new PortMapPublic({
|
||||
internalStart: 80,
|
||||
internalEnd: 80,
|
||||
externalStart: 80,
|
||||
externalEnd: 80,
|
||||
protocol: 'udp',
|
||||
host: '',
|
||||
}),
|
||||
);
|
||||
|
||||
expect(new PortMapPublic('80:80/udp')).to.deep.equal(
|
||||
new PortMapPublic({
|
||||
internalStart: 80,
|
||||
internalEnd: 80,
|
||||
externalStart: 80,
|
||||
externalEnd: 80,
|
||||
protocol: 'udp',
|
||||
host: '',
|
||||
}),
|
||||
);
|
||||
|
||||
expect(new PortMapPublic('80-90:100-110/udp')).to.deep.equal(
|
||||
new PortMapPublic({
|
||||
internalStart: 100,
|
||||
internalEnd: 110,
|
||||
externalStart: 80,
|
||||
externalEnd: 90,
|
||||
protocol: 'udp',
|
||||
host: '',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw when the port string is incorrect', () =>
|
||||
expect(() => new PortMapPublic('80-90:80-85')).to.throw);
|
||||
});
|
||||
|
||||
describe('toDockerOpts', function() {
|
||||
it('should correctly generate docker options', () =>
|
||||
expect(new PortMapPublic('80').toDockerOpts()).to.deep.equal({
|
||||
exposedPorts: {
|
||||
'80/tcp': {},
|
||||
},
|
||||
portBindings: {
|
||||
'80/tcp': [{ HostIp: '', HostPort: '80' }],
|
||||
},
|
||||
}));
|
||||
|
||||
it('should correctly generate docker options for a port range', () =>
|
||||
expect(new PortMapPublic('80-85').toDockerOpts()).to.deep.equal({
|
||||
exposedPorts: {
|
||||
'80/tcp': {},
|
||||
'81/tcp': {},
|
||||
'82/tcp': {},
|
||||
'83/tcp': {},
|
||||
'84/tcp': {},
|
||||
'85/tcp': {},
|
||||
},
|
||||
portBindings: {
|
||||
'80/tcp': [{ HostIp: '', HostPort: '80' }],
|
||||
'81/tcp': [{ HostIp: '', HostPort: '81' }],
|
||||
'82/tcp': [{ HostIp: '', HostPort: '82' }],
|
||||
'83/tcp': [{ HostIp: '', HostPort: '83' }],
|
||||
'84/tcp': [{ HostIp: '', HostPort: '84' }],
|
||||
'85/tcp': [{ HostIp: '', HostPort: '85' }],
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
describe('fromDockerOpts', function() {
|
||||
it('should correctly detect a port range', () =>
|
||||
expect(
|
||||
PortMap.fromDockerOpts({
|
||||
'100/tcp': [{ HostIp: '123', HostPort: '200' }],
|
||||
'101/tcp': [{ HostIp: '123', HostPort: '201' }],
|
||||
'102/tcp': [{ HostIp: '123', HostPort: '202' }],
|
||||
}),
|
||||
).to.deep.equal([
|
||||
new PortMapPublic({
|
||||
internalStart: 100,
|
||||
internalEnd: 102,
|
||||
externalStart: 200,
|
||||
externalEnd: 202,
|
||||
protocol: 'tcp',
|
||||
host: '123',
|
||||
}),
|
||||
]));
|
||||
|
||||
it('should correctly split ports into ranges', () =>
|
||||
expect(
|
||||
PortMap.fromDockerOpts({
|
||||
'100/tcp': [{ HostIp: '123', HostPort: '200' }],
|
||||
'101/tcp': [{ HostIp: '123', HostPort: '201' }],
|
||||
'105/tcp': [{ HostIp: '123', HostPort: '205' }],
|
||||
'106/tcp': [{ HostIp: '123', HostPort: '206' }],
|
||||
'110/tcp': [{ HostIp: '123', HostPort: '210' }],
|
||||
}),
|
||||
).to.deep.equal([
|
||||
new PortMapPublic({
|
||||
internalStart: 100,
|
||||
internalEnd: 101,
|
||||
externalStart: 200,
|
||||
externalEnd: 201,
|
||||
protocol: 'tcp',
|
||||
host: '123',
|
||||
}),
|
||||
new PortMapPublic({
|
||||
internalStart: 105,
|
||||
internalEnd: 106,
|
||||
externalStart: 205,
|
||||
externalEnd: 206,
|
||||
protocol: 'tcp',
|
||||
host: '123',
|
||||
}),
|
||||
new PortMapPublic({
|
||||
internalStart: 110,
|
||||
internalEnd: 110,
|
||||
externalStart: 210,
|
||||
externalEnd: 210,
|
||||
protocol: 'tcp',
|
||||
host: '123',
|
||||
}),
|
||||
]));
|
||||
|
||||
it('should correctly consider internal and external ports', () =>
|
||||
expect(
|
||||
PortMap.fromDockerOpts({
|
||||
'100/tcp': [{ HostIp: '123', HostPort: '200' }],
|
||||
'101/tcp': [{ HostIp: '123', HostPort: '101' }],
|
||||
'102/tcp': [{ HostIp: '123', HostPort: '202' }],
|
||||
}),
|
||||
).to.deep.equal([
|
||||
new PortMapPublic({
|
||||
internalStart: 100,
|
||||
internalEnd: 100,
|
||||
externalStart: 200,
|
||||
externalEnd: 200,
|
||||
protocol: 'tcp',
|
||||
host: '123',
|
||||
}),
|
||||
new PortMapPublic({
|
||||
internalStart: 101,
|
||||
internalEnd: 101,
|
||||
externalStart: 101,
|
||||
externalEnd: 101,
|
||||
protocol: 'tcp',
|
||||
host: '123',
|
||||
}),
|
||||
new PortMapPublic({
|
||||
internalStart: 102,
|
||||
internalEnd: 102,
|
||||
externalStart: 202,
|
||||
externalEnd: 202,
|
||||
protocol: 'tcp',
|
||||
host: '123',
|
||||
}),
|
||||
]));
|
||||
|
||||
it('should consider the host when generating ranges', () =>
|
||||
expect(
|
||||
PortMap.fromDockerOpts({
|
||||
'100/tcp': [{ HostIp: '123', HostPort: '200' }],
|
||||
'101/tcp': [{ HostIp: '456', HostPort: '201' }],
|
||||
'102/tcp': [{ HostIp: '456', HostPort: '202' }],
|
||||
}),
|
||||
).to.deep.equal([
|
||||
new PortMapPublic({
|
||||
internalStart: 100,
|
||||
internalEnd: 100,
|
||||
externalStart: 200,
|
||||
externalEnd: 200,
|
||||
protocol: 'tcp',
|
||||
host: '123',
|
||||
}),
|
||||
new PortMapPublic({
|
||||
internalStart: 101,
|
||||
internalEnd: 102,
|
||||
externalStart: 201,
|
||||
externalEnd: 202,
|
||||
protocol: 'tcp',
|
||||
host: '456',
|
||||
}),
|
||||
]));
|
||||
|
||||
it('should consider the protocol when generating ranges', () =>
|
||||
expect(
|
||||
PortMap.fromDockerOpts({
|
||||
'100/tcp': [{ HostIp: '123', HostPort: '200' }],
|
||||
'101/udp': [{ HostIp: '123', HostPort: '201' }],
|
||||
'102/udp': [{ HostIp: '123', HostPort: '202' }],
|
||||
}),
|
||||
).to.deep.equal([
|
||||
new PortMapPublic({
|
||||
internalStart: 100,
|
||||
internalEnd: 100,
|
||||
externalStart: 200,
|
||||
externalEnd: 200,
|
||||
protocol: 'tcp',
|
||||
host: '123',
|
||||
}),
|
||||
new PortMapPublic({
|
||||
internalStart: 101,
|
||||
internalEnd: 102,
|
||||
externalStart: 201,
|
||||
externalEnd: 202,
|
||||
protocol: 'udp',
|
||||
host: '123',
|
||||
}),
|
||||
]));
|
||||
|
||||
it('should correctly detect multiple hosts ports on an internal port', () =>
|
||||
expect(
|
||||
PortMap.fromDockerOpts({
|
||||
'100/tcp': [
|
||||
{ HostIp: '123', HostPort: '200' },
|
||||
{ HostIp: '123', HostPort: '201' },
|
||||
],
|
||||
}),
|
||||
).to.deep.equal([
|
||||
new PortMapPublic({
|
||||
internalStart: 100,
|
||||
internalEnd: 100,
|
||||
externalStart: 200,
|
||||
externalEnd: 200,
|
||||
protocol: 'tcp',
|
||||
host: '123',
|
||||
}),
|
||||
new PortMapPublic({
|
||||
internalStart: 100,
|
||||
internalEnd: 100,
|
||||
externalStart: 201,
|
||||
externalEnd: 201,
|
||||
protocol: 'tcp',
|
||||
host: '123',
|
||||
}),
|
||||
]));
|
||||
});
|
||||
|
||||
describe('Running container comparison', () =>
|
||||
it('should not consider order when comparing current and target state', function() {
|
||||
const portBindings = require('./data/ports/not-ascending/port-bindings.json');
|
||||
const compose = require('./data/ports/not-ascending/compose.json');
|
||||
const portMapsCurrent = PortMap.fromDockerOpts(portBindings);
|
||||
const portMapsTarget = PortMap.fromComposePorts(compose.ports);
|
||||
|
||||
expect(portMapsTarget).to.deep.equal(portMapsCurrent);
|
||||
}));
|
||||
|
||||
describe('fromComposePorts', () =>
|
||||
it('should normalise compose ports', () =>
|
||||
expect(
|
||||
PortMap.fromComposePorts(['80:80', '81:81', '82:82']),
|
||||
).to.deep.equal([new PortMapPublic('80-82')])));
|
||||
|
||||
describe('normalisePortMaps', function() {
|
||||
it('should correctly normalise PortMap lists', function() {
|
||||
expect(
|
||||
PortMap.normalisePortMaps([
|
||||
new PortMapPublic('80:90'),
|
||||
new PortMapPublic('81:91'),
|
||||
]),
|
||||
).to.deep.equal([new PortMapPublic('80-81:90-91')]);
|
||||
|
||||
expect(
|
||||
PortMap.normalisePortMaps([
|
||||
new PortMapPublic('80:90'),
|
||||
new PortMapPublic('81:91'),
|
||||
new PortMapPublic('82:92'),
|
||||
new PortMapPublic('83:93'),
|
||||
new PortMapPublic('84:94'),
|
||||
new PortMapPublic('85:95'),
|
||||
new PortMapPublic('86:96'),
|
||||
new PortMapPublic('87:97'),
|
||||
new PortMapPublic('88:98'),
|
||||
new PortMapPublic('89:99'),
|
||||
new PortMapPublic('90:100'),
|
||||
]),
|
||||
).to.deep.equal([new PortMapPublic('80-90:90-100')]);
|
||||
|
||||
expect(PortMap.normalisePortMaps([])).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('should correctly consider protocols', function() {
|
||||
expect(
|
||||
PortMap.normalisePortMaps([
|
||||
new PortMapPublic('80:90'),
|
||||
new PortMapPublic('81:91/udp'),
|
||||
]),
|
||||
).to.deep.equal([
|
||||
new PortMapPublic('80:90'),
|
||||
new PortMapPublic('81:91/udp'),
|
||||
]);
|
||||
|
||||
expect(
|
||||
PortMap.normalisePortMaps([
|
||||
new PortMapPublic('80:90'),
|
||||
new PortMapPublic('100:110/udp'),
|
||||
new PortMapPublic('81:91'),
|
||||
]),
|
||||
).to.deep.equal([
|
||||
new PortMapPublic('80-81:90-91'),
|
||||
new PortMapPublic('100:110/udp'),
|
||||
]);
|
||||
|
||||
// This shouldn't ever be provided, but it shows the algorithm
|
||||
// working properly
|
||||
expect(
|
||||
PortMap.normalisePortMaps([
|
||||
new PortMapPublic('80:90'),
|
||||
new PortMapPublic('81:91/udp'),
|
||||
new PortMapPublic('81:91'),
|
||||
]),
|
||||
).to.deep.equal([
|
||||
new PortMapPublic('80-81:90-91'),
|
||||
new PortMapPublic('81:91/udp'),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly consider hosts', function() {
|
||||
expect(
|
||||
PortMap.normalisePortMaps([
|
||||
new PortMapPublic('127.0.0.1:80:80'),
|
||||
new PortMapPublic('81:81'),
|
||||
]),
|
||||
).to.deep.equal([
|
||||
new PortMapPublic('127.0.0.1:80:80'),
|
||||
new PortMapPublic('81:81'),
|
||||
]);
|
||||
|
||||
expect(
|
||||
PortMap.normalisePortMaps([
|
||||
new PortMapPublic('127.0.0.1:80:80'),
|
||||
new PortMapPublic('127.0.0.1:81:81'),
|
||||
]),
|
||||
).to.deep.equal([new PortMapPublic('127.0.0.1:80-81:80-81')]);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,120 +0,0 @@
|
||||
{ expect } = require './lib/chai-config'
|
||||
{ stub } = require 'sinon'
|
||||
|
||||
{ fs } = require 'mz'
|
||||
|
||||
configUtils = require '../src/config/utils'
|
||||
{ ExtlinuxConfigBackend, RPiConfigBackend } = require '../src/config/backend'
|
||||
|
||||
extlinuxBackend = new ExtlinuxConfigBackend()
|
||||
rpiBackend = new RPiConfigBackend()
|
||||
|
||||
describe 'Config Utilities', ->
|
||||
|
||||
describe 'Boot config utilities', ->
|
||||
|
||||
describe 'Env <-> Config', ->
|
||||
|
||||
it 'correctly transforms environments to boot config objects', ->
|
||||
bootConfig = configUtils.envToBootConfig(rpiBackend, {
|
||||
HOST_CONFIG_initramfs: 'initramf.gz 0x00800000'
|
||||
HOST_CONFIG_dtparam: '"i2c=on","audio=on"'
|
||||
HOST_CONFIG_dtoverlay: '"ads7846","lirc-rpi,gpio_out_pin=17,gpio_in_pin=13"'
|
||||
HOST_CONFIG_foobar: 'baz'
|
||||
})
|
||||
expect(bootConfig).to.deep.equal({
|
||||
initramfs: 'initramf.gz 0x00800000'
|
||||
dtparam: [ 'i2c=on', 'audio=on' ]
|
||||
dtoverlay: [ 'ads7846', 'lirc-rpi,gpio_out_pin=17,gpio_in_pin=13' ]
|
||||
foobar: 'baz'
|
||||
})
|
||||
|
||||
describe 'TX2 boot config utilities', ->
|
||||
|
||||
it 'should parse a extlinux.conf file', ->
|
||||
text = '''
|
||||
DEFAULT primary
|
||||
# Comment
|
||||
TIMEOUT 30
|
||||
|
||||
MENU TITLE Boot Options
|
||||
LABEL primary
|
||||
MENU LABEL primary Image
|
||||
LINUX /Image
|
||||
APPEND ${cbootargs} ${resin_kernel_root} ro rootwait
|
||||
'''
|
||||
|
||||
parsed = ExtlinuxConfigBackend.parseExtlinuxFile(text)
|
||||
expect(parsed.globals).to.have.property('DEFAULT').that.equals('primary')
|
||||
expect(parsed.globals).to.have.property('TIMEOUT').that.equals('30')
|
||||
expect(parsed.globals).to.have.property('MENU TITLE').that.equals('Boot Options')
|
||||
|
||||
expect(parsed.labels).to.have.property('primary')
|
||||
primary = parsed.labels.primary
|
||||
expect(primary).to.have.property('MENU LABEL').that.equals('primary Image')
|
||||
expect(primary).to.have.property('LINUX').that.equals('/Image')
|
||||
expect(primary).to.have.property('APPEND').that.equals('${cbootargs} ${resin_kernel_root} ro rootwait')
|
||||
|
||||
it 'should parse multiple service entries', ->
|
||||
text = '''
|
||||
DEFAULT primary
|
||||
# Comment
|
||||
TIMEOUT 30
|
||||
|
||||
MENU TITLE Boot Options
|
||||
LABEL primary
|
||||
LINUX test1
|
||||
APPEND test2
|
||||
LABEL secondary
|
||||
LINUX test3
|
||||
APPEND test4
|
||||
'''
|
||||
|
||||
parsed = ExtlinuxConfigBackend.parseExtlinuxFile(text)
|
||||
expect(parsed.labels).to.have.property('primary').that.deep.equals({
|
||||
LINUX: 'test1'
|
||||
APPEND: 'test2'
|
||||
})
|
||||
expect(parsed.labels).to.have.property('secondary').that.deep.equals({
|
||||
LINUX: 'test3'
|
||||
APPEND: 'test4'
|
||||
})
|
||||
|
||||
it 'should parse configuration options from an extlinux.conf file', ->
|
||||
text = '''
|
||||
DEFAULT primary
|
||||
# Comment
|
||||
TIMEOUT 30
|
||||
|
||||
MENU TITLE Boot Options
|
||||
LABEL primary
|
||||
MENU LABEL primary Image
|
||||
LINUX /Image
|
||||
APPEND ${cbootargs} ${resin_kernel_root} ro rootwait isolcpus=3
|
||||
'''
|
||||
|
||||
stub(fs, 'readFile').resolves(text)
|
||||
parsed = extlinuxBackend.getBootConfig()
|
||||
|
||||
expect(parsed).to.eventually.have.property('isolcpus').that.equals('3')
|
||||
fs.readFile.restore()
|
||||
|
||||
|
||||
text = '''
|
||||
DEFAULT primary
|
||||
# Comment
|
||||
TIMEOUT 30
|
||||
|
||||
MENU TITLE Boot Options
|
||||
LABEL primary
|
||||
MENU LABEL primary Image
|
||||
LINUX /Image
|
||||
APPEND ${cbootargs} ${resin_kernel_root} ro rootwait isolcpus=3,4,5
|
||||
'''
|
||||
stub(fs, 'readFile').resolves(text)
|
||||
|
||||
parsed = extlinuxBackend.getBootConfig()
|
||||
|
||||
fs.readFile.restore()
|
||||
|
||||
expect(parsed).to.eventually.have.property('isolcpus').that.equals('3,4,5')
|
142
test/17-config-utils.spec.js
Normal file
142
test/17-config-utils.spec.js
Normal file
@ -0,0 +1,142 @@
|
||||
import { expect } from './lib/chai-config';
|
||||
import { stub } from 'sinon';
|
||||
import { fs } from 'mz';
|
||||
import * as configUtils from '../src/config/utils';
|
||||
import { ExtlinuxConfigBackend, RPiConfigBackend } from '../src/config/backend';
|
||||
|
||||
const extlinuxBackend = new ExtlinuxConfigBackend();
|
||||
const rpiBackend = new RPiConfigBackend();
|
||||
|
||||
describe('Config Utilities', () =>
|
||||
describe('Boot config utilities', function() {
|
||||
describe('Env <-> Config', () =>
|
||||
it('correctly transforms environments to boot config objects', function() {
|
||||
const bootConfig = configUtils.envToBootConfig(rpiBackend, {
|
||||
HOST_CONFIG_initramfs: 'initramf.gz 0x00800000',
|
||||
HOST_CONFIG_dtparam: '"i2c=on","audio=on"',
|
||||
HOST_CONFIG_dtoverlay:
|
||||
'"ads7846","lirc-rpi,gpio_out_pin=17,gpio_in_pin=13"',
|
||||
HOST_CONFIG_foobar: 'baz',
|
||||
});
|
||||
expect(bootConfig).to.deep.equal({
|
||||
initramfs: 'initramf.gz 0x00800000',
|
||||
dtparam: ['i2c=on', 'audio=on'],
|
||||
dtoverlay: ['ads7846', 'lirc-rpi,gpio_out_pin=17,gpio_in_pin=13'],
|
||||
foobar: 'baz',
|
||||
});
|
||||
}));
|
||||
|
||||
describe('TX2 boot config utilities', function() {
|
||||
it('should parse a extlinux.conf file', function() {
|
||||
const text = `\
|
||||
DEFAULT primary
|
||||
# Comment
|
||||
TIMEOUT 30
|
||||
|
||||
MENU TITLE Boot Options
|
||||
LABEL primary
|
||||
MENU LABEL primary Image
|
||||
LINUX /Image
|
||||
APPEND \${cbootargs} \${resin_kernel_root} ro rootwait\
|
||||
`;
|
||||
|
||||
// @ts-ignore accessing private method
|
||||
const parsed = ExtlinuxConfigBackend.parseExtlinuxFile(text);
|
||||
expect(parsed.globals)
|
||||
.to.have.property('DEFAULT')
|
||||
.that.equals('primary');
|
||||
expect(parsed.globals)
|
||||
.to.have.property('TIMEOUT')
|
||||
.that.equals('30');
|
||||
expect(parsed.globals)
|
||||
.to.have.property('MENU TITLE')
|
||||
.that.equals('Boot Options');
|
||||
|
||||
expect(parsed.labels).to.have.property('primary');
|
||||
const { primary } = parsed.labels;
|
||||
expect(primary)
|
||||
.to.have.property('MENU LABEL')
|
||||
.that.equals('primary Image');
|
||||
expect(primary)
|
||||
.to.have.property('LINUX')
|
||||
.that.equals('/Image');
|
||||
expect(primary)
|
||||
.to.have.property('APPEND')
|
||||
.that.equals('${cbootargs} ${resin_kernel_root} ro rootwait');
|
||||
});
|
||||
|
||||
it('should parse multiple service entries', function() {
|
||||
const text = `\
|
||||
DEFAULT primary
|
||||
# Comment
|
||||
TIMEOUT 30
|
||||
|
||||
MENU TITLE Boot Options
|
||||
LABEL primary
|
||||
LINUX test1
|
||||
APPEND test2
|
||||
LABEL secondary
|
||||
LINUX test3
|
||||
APPEND test4\
|
||||
`;
|
||||
|
||||
// @ts-ignore accessing private method
|
||||
const parsed = ExtlinuxConfigBackend.parseExtlinuxFile(text);
|
||||
expect(parsed.labels)
|
||||
.to.have.property('primary')
|
||||
.that.deep.equals({
|
||||
LINUX: 'test1',
|
||||
APPEND: 'test2',
|
||||
});
|
||||
expect(parsed.labels)
|
||||
.to.have.property('secondary')
|
||||
.that.deep.equals({
|
||||
LINUX: 'test3',
|
||||
APPEND: 'test4',
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse configuration options from an extlinux.conf file', function() {
|
||||
let text = `\
|
||||
DEFAULT primary
|
||||
# Comment
|
||||
TIMEOUT 30
|
||||
|
||||
MENU TITLE Boot Options
|
||||
LABEL primary
|
||||
MENU LABEL primary Image
|
||||
LINUX /Image
|
||||
APPEND \${cbootargs} \${resin_kernel_root} ro rootwait isolcpus=3\
|
||||
`;
|
||||
|
||||
let readFileStub = stub(fs, 'readFile').resolves(text);
|
||||
let parsed = extlinuxBackend.getBootConfig();
|
||||
|
||||
expect(parsed)
|
||||
.to.eventually.have.property('isolcpus')
|
||||
.that.equals('3');
|
||||
readFileStub.restore();
|
||||
|
||||
text = `\
|
||||
DEFAULT primary
|
||||
# Comment
|
||||
TIMEOUT 30
|
||||
|
||||
MENU TITLE Boot Options
|
||||
LABEL primary
|
||||
MENU LABEL primary Image
|
||||
LINUX /Image
|
||||
APPEND \${cbootargs} \${resin_kernel_root} ro rootwait isolcpus=3,4,5\
|
||||
`;
|
||||
readFileStub = stub(fs, 'readFile').resolves(text);
|
||||
|
||||
parsed = extlinuxBackend.getBootConfig();
|
||||
|
||||
readFileStub.restore();
|
||||
|
||||
expect(parsed)
|
||||
.to.eventually.have.property('isolcpus')
|
||||
.that.equals('3,4,5');
|
||||
});
|
||||
});
|
||||
}));
|
@ -1,104 +0,0 @@
|
||||
{ expect } = require './lib/chai-config'
|
||||
|
||||
{ Network } = require '../src/compose/network'
|
||||
|
||||
describe 'compose/network', ->
|
||||
|
||||
describe 'compose config -> internal config', ->
|
||||
|
||||
it 'should convert a compose configuration to an internal representation', ->
|
||||
|
||||
network = Network.fromComposeObject('test', 123, {
|
||||
'driver': 'bridge',
|
||||
'ipam': {
|
||||
'driver': 'default',
|
||||
'config': [
|
||||
{
|
||||
'subnet': '172.25.0.0/25',
|
||||
'gateway': '172.25.0.1'
|
||||
}
|
||||
]
|
||||
}
|
||||
}, { logger: null, docker: null })
|
||||
|
||||
expect(network.config).to.deep.equal({
|
||||
driver: 'bridge'
|
||||
ipam: {
|
||||
driver: 'default'
|
||||
config: [
|
||||
subnet: '172.25.0.0/25'
|
||||
gateway: '172.25.0.1'
|
||||
]
|
||||
options: {}
|
||||
}
|
||||
enableIPv6: false,
|
||||
internal: false,
|
||||
labels: {}
|
||||
options: {}
|
||||
})
|
||||
|
||||
it 'should handle an incomplete ipam configuration', ->
|
||||
network = Network.fromComposeObject('test', 123, {
|
||||
ipam: {
|
||||
config: [
|
||||
{
|
||||
subnet: '172.25.0.0/25',
|
||||
gateway: '172.25.0.1'
|
||||
}
|
||||
]
|
||||
}
|
||||
}, { logger: null, docker: null })
|
||||
|
||||
expect(network.config).to.deep.equal({
|
||||
driver: 'bridge',
|
||||
enableIPv6: false,
|
||||
internal: false,
|
||||
labels: {}
|
||||
options: {}
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
options: {},
|
||||
config: [
|
||||
{
|
||||
subnet: '172.25.0.0/25',
|
||||
gateway: '172.25.0.1'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
describe 'internal config -> docker config', ->
|
||||
|
||||
it 'should convert an internal representation to a docker representation', ->
|
||||
|
||||
network = Network.fromComposeObject('test', 123, {
|
||||
'driver': 'bridge',
|
||||
'ipam': {
|
||||
'driver': 'default',
|
||||
'config': [
|
||||
{
|
||||
'subnet': '172.25.0.0/25',
|
||||
'gateway': '172.25.0.1'
|
||||
}
|
||||
]
|
||||
}
|
||||
}, { logger: null, docker: null })
|
||||
|
||||
expect(network.toDockerConfig()).to.deep.equal({
|
||||
Name: '123_test',
|
||||
Driver: 'bridge',
|
||||
CheckDuplicate: true,
|
||||
IPAM: {
|
||||
Driver: 'default',
|
||||
Config: [{
|
||||
Subnet: '172.25.0.0/25'
|
||||
Gateway: '172.25.0.1'
|
||||
}]
|
||||
Options: {}
|
||||
}
|
||||
EnableIPv6: false,
|
||||
Internal: false,
|
||||
Labels: {
|
||||
'io.balena.supervised': 'true'
|
||||
}
|
||||
})
|
125
test/18-compose-network.js
Normal file
125
test/18-compose-network.js
Normal file
@ -0,0 +1,125 @@
|
||||
import { expect } from './lib/chai-config';
|
||||
import { Network } from '../src/compose/network';
|
||||
|
||||
describe('compose/network', function() {
|
||||
describe('compose config -> internal config', function() {
|
||||
it('should convert a compose configuration to an internal representation', function() {
|
||||
const network = Network.fromComposeObject(
|
||||
'test',
|
||||
123,
|
||||
{
|
||||
driver: 'bridge',
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
config: [
|
||||
{
|
||||
subnet: '172.25.0.0/25',
|
||||
gateway: '172.25.0.1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// @ts-ignore ignore passing nulls instead of actual objects
|
||||
{ logger: null, docker: null },
|
||||
);
|
||||
|
||||
expect(network.config).to.deep.equal({
|
||||
driver: 'bridge',
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
config: [
|
||||
{
|
||||
subnet: '172.25.0.0/25',
|
||||
gateway: '172.25.0.1',
|
||||
},
|
||||
],
|
||||
options: {},
|
||||
},
|
||||
enableIPv6: false,
|
||||
internal: false,
|
||||
labels: {},
|
||||
options: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle an incomplete ipam configuration', function() {
|
||||
const network = Network.fromComposeObject(
|
||||
'test',
|
||||
123,
|
||||
{
|
||||
ipam: {
|
||||
config: [
|
||||
{
|
||||
subnet: '172.25.0.0/25',
|
||||
gateway: '172.25.0.1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// @ts-ignore ignore passing nulls instead of actual objects
|
||||
{ logger: null, docker: null },
|
||||
);
|
||||
|
||||
expect(network.config).to.deep.equal({
|
||||
driver: 'bridge',
|
||||
enableIPv6: false,
|
||||
internal: false,
|
||||
labels: {},
|
||||
options: {},
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
options: {},
|
||||
config: [
|
||||
{
|
||||
subnet: '172.25.0.0/25',
|
||||
gateway: '172.25.0.1',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('internal config -> docker config', () =>
|
||||
it('should convert an internal representation to a docker representation', function() {
|
||||
const network = Network.fromComposeObject(
|
||||
'test',
|
||||
123,
|
||||
{
|
||||
driver: 'bridge',
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
config: [
|
||||
{
|
||||
subnet: '172.25.0.0/25',
|
||||
gateway: '172.25.0.1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// @ts-ignore ignore passing nulls instead of actual objects
|
||||
{ logger: null, docker: null },
|
||||
);
|
||||
|
||||
expect(network.toDockerConfig()).to.deep.equal({
|
||||
Name: '123_test',
|
||||
Driver: 'bridge',
|
||||
CheckDuplicate: true,
|
||||
IPAM: {
|
||||
Driver: 'default',
|
||||
Config: [
|
||||
{
|
||||
Subnet: '172.25.0.0/25',
|
||||
Gateway: '172.25.0.1',
|
||||
},
|
||||
],
|
||||
Options: {},
|
||||
},
|
||||
EnableIPv6: false,
|
||||
Internal: false,
|
||||
Labels: {
|
||||
'io.balena.supervised': 'true',
|
||||
},
|
||||
});
|
||||
}));
|
||||
});
|
@ -1,13 +0,0 @@
|
||||
{ expect } = require './lib/chai-config'
|
||||
|
||||
{ Supervisor } = require '../src/supervisor'
|
||||
|
||||
describe 'Startup', ->
|
||||
it 'should startup correctly', ->
|
||||
supervisor = new Supervisor()
|
||||
expect(supervisor.init()).to.not.throw
|
||||
expect(supervisor.db).to.not.be.null
|
||||
expect(supervisor.config).to.not.be.null
|
||||
expect(supervisor.logger).to.not.be.null
|
||||
expect(supervisor.deviceState).to.not.be.null
|
||||
expect(supervisor.apiBinder).to.not.be.null
|
16
test/18-startup.ts
Normal file
16
test/18-startup.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Supervisor } from '../src/supervisor';
|
||||
import { expect } from './lib/chai-config';
|
||||
|
||||
describe('Startup', () => {
|
||||
it('should startup correctly', function() {
|
||||
const supervisor = new Supervisor();
|
||||
expect(supervisor.init()).to.not.throw;
|
||||
// Cast as any to access private properties
|
||||
const anySupervisor = supervisor as any;
|
||||
expect(anySupervisor.db).to.not.be.null;
|
||||
expect(anySupervisor.config).to.not.be.null;
|
||||
expect(anySupervisor.logger).to.not.be.null;
|
||||
expect(anySupervisor.deviceState).to.not.be.null;
|
||||
expect(anySupervisor.apiBinder).to.not.be.null;
|
||||
});
|
||||
});
|
@ -1,22 +0,0 @@
|
||||
require('mocha')
|
||||
|
||||
{ expect } = require './lib/chai-config'
|
||||
|
||||
ComposeUtils = require('../src/compose/utils')
|
||||
|
||||
describe 'Composition utilities', ->
|
||||
|
||||
it 'Should correctly camel case the configuration', ->
|
||||
config =
|
||||
networks: [
|
||||
'test',
|
||||
'test2',
|
||||
]
|
||||
|
||||
expect(ComposeUtils.camelCaseConfig(config)).to.deep.equal({
|
||||
networks: [
|
||||
'test'
|
||||
'test2'
|
||||
]
|
||||
})
|
||||
|
13
test/19-compose-utils.js
Normal file
13
test/19-compose-utils.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { expect } from './lib/chai-config';
|
||||
import * as ComposeUtils from '../src/compose/utils';
|
||||
|
||||
describe('Composition utilities', () =>
|
||||
it('Should correctly camel case the configuration', function() {
|
||||
const config = {
|
||||
networks: ['test', 'test2'],
|
||||
};
|
||||
|
||||
expect(ComposeUtils.camelCaseConfig(config)).to.deep.equal({
|
||||
networks: ['test', 'test2'],
|
||||
});
|
||||
}));
|
@ -4,5 +4,5 @@
|
||||
"noImplicitAny": false,
|
||||
"checkJs": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.js", "typings/**/*.d.ts"]
|
||||
"include": ["src/**/*", "test/**/*", "typings/**/*.d.ts"]
|
||||
}
|
||||
|
@ -11,5 +11,5 @@
|
||||
"resolveJsonModule": true,
|
||||
"allowJs": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.js", "test/**/*.ts", "typings/**/*.d.ts"]
|
||||
"include": ["src/**/*", "test/**/*", "typings/**/*.d.ts"]
|
||||
}
|
||||
|
@ -6,5 +6,5 @@
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.js", "typings/**/*.d.ts"]
|
||||
"include": ["src/**/*", "typings/**/*.d.ts"]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user