1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Fix port availability test (#333)

* Let OS allocate port number

Port availability might be tricky if some port is already in use on the
'all' interface '0.0.0.0'.

The proposed solution is to let the OS allocate the port for us using 0
as port specifier :

- first create a server instance to allocate a port number
- save port number
- close the server
- return the port number to the caller

This should be safe granted the OS doesn't reuse the port numbers on
consecutive port allocations.

see :
- about Node.js Net module : https://nodejs.org/docs/latest-v12.x/api/net.html#net_server_listen_port_host_backlog_callback
- about safety around reusing port number : https://unix.stackexchange.com/a/132524

Signed-off-by: Alexis Deruelle <alexis.deruelle@gmail.com>
This commit is contained in:
Alexis Deruelle 2020-05-05 06:31:58 +02:00 committed by GitHub
parent 0a3b5dae73
commit 6412d73a5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 26 additions and 30 deletions

View File

@ -2,13 +2,10 @@ import { EventEmitter } from 'events'
class MockServer extends EventEmitter { class MockServer extends EventEmitter {
listen = jest.fn((obj) => { listen = jest.fn((obj) => {
if(obj.port < 9003) {
this.emit('error', new Error("fail!"))
} else {
this.emit('listening', {}) this.emit('listening', {})
}
return this return this
}) })
address = () => { return { port: 12345 }}
unref = jest.fn() unref = jest.fn()
close = jest.fn((cb) => { close = jest.fn((cb) => {
cb() cb()
@ -29,11 +26,7 @@ describe("getFreePort", () => {
jest.clearAllMocks() jest.clearAllMocks()
}) })
it("fails for an invalid range", async () => {
return expect(port.getFreePort(1, 2)).rejects.toMatch('free port')
})
it("finds the next free port", async () => { it("finds the next free port", async () => {
return expect(port.getFreePort(9000, 9005)).resolves.toBe(9003) return expect(port.getFreePort()).resolves.toEqual(expect.any(Number))
}) })
}) })

View File

@ -135,7 +135,7 @@ export class ContextHandler {
let serverPort: number = null let serverPort: number = null
try { try {
serverPort = await getFreePort(49901, 65535) serverPort = await getFreePort()
} catch(error) { } catch(error) {
logger.error(error) logger.error(error)
throw(error) throw(error)

View File

@ -48,7 +48,7 @@ async function main() {
let port: number = null let port: number = null
// find free port // find free port
try { try {
port = await getFreePort(49152, 65535) port = await getFreePort()
} catch (error) { } catch (error) {
logger.error(error) logger.error(error)
await dialog.showErrorBox("Lens Error", "Could not find a free port for the cluster proxy") await dialog.showErrorBox("Lens Error", "Could not find a free port for the cluster proxy")

View File

@ -1,27 +1,30 @@
import logger from "./logger" import logger from "./logger"
import { createServer } from "net" import { createServer } from "net"
import { AddressInfo } from "net"
// Adapted from https://gist.github.com/mikeal/1840641#gistcomment-2896667 const getNextAvailablePort = () => {
function checkPort(port: number) { logger.debug("getNextAvailablePort() start")
const server = createServer() const server = createServer()
server.unref() server.unref()
return new Promise((resolve, reject) => return new Promise<number>((resolve, reject) =>
server server
.on('error', error => reject(error)) .on('error', (error: any) => reject(error))
.on('listening', () => server.close(() => resolve(port))) .on('listening', () => {
.listen({host: "127.0.0.1", port: port})) logger.debug("*** server listening event ***")
const _port = (server.address() as AddressInfo).port
server.close(() => resolve(_port))
})
.listen({host: "127.0.0.1", port: 0}))
} }
export async function getFreePort(firstPort: number, lastPort: number): Promise<number> { export const getFreePort = async () => {
let port = firstPort logger.debug("getFreePort() start")
let freePort: number = null
while(true) {
try { try {
logger.debug("Checking port " + port + " availability ...") freePort = await getNextAvailablePort()
await checkPort(port) logger.debug("got port : " + freePort)
return(port)
} catch(error) { } catch(error) {
if(++port > lastPort) throw("Could not find a free port") throw("getNextAvailablePort() threw: '" + error + "'")
}
} }
return freePort
} }

View File

@ -36,7 +36,7 @@ class PortForward {
} }
public async start() { public async start() {
this.localPort = await getFreePort(8000, 9999) this.localPort = await getFreePort()
const kubectlBin = await bundledKubectl.kubectlPath() const kubectlBin = await bundledKubectl.kubectlPath()
const args = [ const args = [
"--kubeconfig", this.kubeConfig, "--kubeconfig", this.kubeConfig,