Skip to content

Commit

Permalink
fix: fix mapping ipv6-based port mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
Steveb-p authored and AlexZeitler committed May 11, 2021
1 parent 963b515 commit e7013df
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 57 deletions.
31 changes: 4 additions & 27 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import childProcess from 'child_process'
import yaml from 'yaml'
import mapPorts from './port-mapper'

export interface IDockerComposeOptions {
cwd?: string
executablePath?: string
Expand Down Expand Up @@ -75,33 +77,6 @@ export type DockerComposePsResult = {
}>
}

export const mapPorts = (
ports: string
): Array<{
mapped?: { address: string; port: number }
exposed: { port: number; protocol: string }
}> => {
const result = !ports
? []
: (() => {
return ports.split(',').map((untypedPort) => {
const exposedFragments = untypedPort.trim().split('->')
const [port, protocol] =
exposedFragments.length === 1
? exposedFragments[0].split('/')
: exposedFragments[1].split('/')
const [address, mappedPort] =
exposedFragments.length === 2 ? exposedFragments[0].split(':') : []
return {
exposed: { port: Number(port), protocol },
...(address &&
mappedPort && { mapped: { port: Number(mappedPort), address } })
}
})
})()
return result
}

export const mapPsOutput = (output: string): DockerComposePsResult => {
const services = output
.split(`\n`)
Expand Down Expand Up @@ -514,3 +489,5 @@ export const version = async function (
return Promise.reject(error)
}
}

export { mapPorts }
31 changes: 31 additions & 0 deletions src/port-mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const mapPorts = (
ports: string
): Array<{
mapped?: { address: string; port: number }
exposed: { port: number; protocol: string }
}> => {
const result = !ports
? []
: (() => {
return ports.split(',').map((untypedPort) => {
const exposedFragments = untypedPort.trim().split('->')

console.log(exposedFragments)

const [port, protocol] =
exposedFragments.length === 1
? exposedFragments[0].split('/')
: exposedFragments[1].split('/')
const [address, mappedPort] =
exposedFragments.length === 2 ? exposedFragments[0].split(':') : []
return {
exposed: { port: Number(port), protocol },
...(address &&
mappedPort && { mapped: { port: Number(mappedPort), address } })
}
})
})()
return result
}

export default mapPorts
4 changes: 2 additions & 2 deletions test/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ services:
container_name: compose_test_web
command: 'nginx -g "daemon off;"'
ports:
- 80:80
- 443:443
- '0.0.0.0:80:80'
- '0.0.0.0:443:443'
proxy:
image: nginx:1.19.9-alpine
container_name: compose_test_proxy
Expand Down
28 changes: 0 additions & 28 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -699,31 +699,3 @@ test('parse ps output', () => {
]
})
})

test('map ports', () => {
const noPort = ''
const exposedTcp = '80/tcp'
const mappedExposedTcp = '0.0.0.0:443->443/tcp'
const multipleExposedMappedTcp = '0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp'

expect(mapPorts(noPort)).toEqual([])
expect(mapPorts(exposedTcp)).toEqual([
{ exposed: { port: 80, protocol: 'tcp' } }
])
expect(mapPorts(mappedExposedTcp)).toEqual([
{
exposed: { port: 443, protocol: 'tcp' },
mapped: { address: '0.0.0.0', port: 443 }
}
])
expect(mapPorts(multipleExposedMappedTcp)).toEqual([
{
exposed: { port: 443, protocol: 'tcp' },
mapped: { address: '0.0.0.0', port: 443 }
},
{
exposed: { port: 80, protocol: 'tcp' },
mapped: { address: '0.0.0.0', port: 80 }
}
])
})
54 changes: 54 additions & 0 deletions test/port-mapper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import mapPorts from "../src/port-mapper";

test('map ports for empty string', () => {
expect(mapPorts('')).toEqual([])
})

test('map ports for exposed tcp', () => {
expect(mapPorts('80/tcp')).toEqual([
{ exposed: { port: 80, protocol: 'tcp' } }
])
})

test('map ports for exposed tcp on ivp4 interface', () => {
expect(mapPorts('0.0.0.0:443->443/tcp')).toEqual([
{
exposed: { port: 443, protocol: 'tcp' },
mapped: { address: '0.0.0.0', port: 443 }
}
])
})

test('map multiple tcp ports exposed on ivp4 interfaces', () => {
expect(mapPorts('0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp')).toEqual([
{
exposed: { port: 443, protocol: 'tcp' },
mapped: { address: '0.0.0.0', port: 443 }
},
{
exposed: { port: 80, protocol: 'tcp' },
mapped: { address: '0.0.0.0', port: 80 }
}
])
})

test('map multiple tcp ports exposed on ipv4 and ipv6 interfaces', () => {
expect(mapPorts('0.0.0.0:443->443/tcp,:::443->443/tcp, 0.0.0.0:80->80/tcp,:::80->80/tcp')).toEqual([
{
exposed: { port: 443, protocol: 'tcp' },
mapped: { address: '0.0.0.0', port: 443 },
},
{
exposed: { port: 443, protocol: 'tcp' },
mapped: { address: ':::', port: 443 },
},
{
exposed: { port: 80, protocol: 'tcp' },
mapped: { address: '0.0.0.0', port: 80 },
},
{
exposed: { port: 80, protocol: 'tcp' },
mapped: { address: ':::', port: 80 },
},
])
})

0 comments on commit e7013df

Please sign in to comment.