balena-supervisor/test/16-ports.spec.coffee
Cameron Diver 0fa47f635b
fix: Correctly handle multiple hosts ports pointing to a container port
When assigning multiple host ports to a single container port before
this change, the supervisor would incorrectly take only the first host
port into consideration. This change makes it so that every host port
per container port is considered.

Closes: #986
Change-type: patch
Signed-off-by: Cameron Diver <cameron@balena.io>
2019-04-25 13:48:04 +01:00

359 lines
8.7 KiB
CoffeeScript

m = require 'mochainon'
{ expect } = m.chai
{ 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')
])