diff --git a/garden-service/src/plugins/kubernetes/port-forward.ts b/garden-service/src/plugins/kubernetes/port-forward.ts index 339802880f..0fef113dce 100644 --- a/garden-service/src/plugins/kubernetes/port-forward.ts +++ b/garden-service/src/plugins/kubernetes/port-forward.ts @@ -8,7 +8,6 @@ import { ChildProcess } from "child_process" -import getPort = require("get-port") const AsyncLock = require("async-lock") import { V1Service } from "@kubernetes/client-node" @@ -23,6 +22,7 @@ import { ForwardablePort } from "../../types/service" import { isBuiltIn } from "./util" import { LogEntry } from "../../logger/log-entry" import { RuntimeError } from "../../exceptions" +import { getPort } from "../../util/network" // TODO: implement stopPortForward handler diff --git a/garden-service/src/proxy.ts b/garden-service/src/proxy.ts index 589c78038c..cb564088d7 100644 --- a/garden-service/src/proxy.ts +++ b/garden-service/src/proxy.ts @@ -10,12 +10,12 @@ import { isEqual, invert } from "lodash" import Bluebird from "bluebird" import { createServer, Server, Socket } from "net" const AsyncLock = require("async-lock") -import getPort = require("get-port") import { Service, ServiceStatus, ForwardablePort } from "./types/service" import { Garden } from "./garden" import { registerCleanupFunction } from "./util/util" import { LogEntry } from "./logger/log-entry" import { GetPortForwardResult } from "./types/plugin/service/getPortForward" +import { getPort } from "./util/network" interface PortProxy { key: string diff --git a/garden-service/src/server/server.ts b/garden-service/src/server/server.ts index ea36c30214..d3bf7e0b12 100644 --- a/garden-service/src/server/server.ts +++ b/garden-service/src/server/server.ts @@ -15,7 +15,6 @@ import serve = require("koa-static") import Router = require("koa-router") import websockify from "koa-websocket" import bodyParser = require("koa-bodyparser") -import getPort = require("get-port") import { omit } from "lodash" import { Garden } from "../garden" @@ -28,6 +27,7 @@ import { EventName, Events } from "../events" import { ValueOf } from "../util/util" import { AnalyticsHandler } from "../analytics/analytics" import { joi } from "../config/common" +import { getPort } from "../util/network" export const DEFAULT_PORT = 9777 const notReadyMessage = "Waiting for Garden instance to initialize" diff --git a/garden-service/src/util/network.ts b/garden-service/src/util/network.ts new file mode 100644 index 0000000000..eaaab3c840 --- /dev/null +++ b/garden-service/src/util/network.ts @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2018 Garden Technologies, Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import _getPort from "get-port" +import { omit } from "lodash" + +export async function getPort(options?: _getPort.Options) { + try { + return await _getPort(options) + } catch (error) { + if (error.code === "EACCES") { + // Upstream library doesn't handle errors where a port is free but we're not allowed to listen on it. + // Fall back to using a random port. + return _getPort(omit(options, "port")) + } else { + throw error + } + } +} diff --git a/garden-service/test/unit/src/server/server.ts b/garden-service/test/unit/src/server/server.ts index cd6d7e7f01..dc207b3ee5 100644 --- a/garden-service/test/unit/src/server/server.ts +++ b/garden-service/test/unit/src/server/server.ts @@ -14,8 +14,8 @@ import { expect } from "chai" import { deepOmitUndefined } from "../../../../src/util/util" import uuid from "uuid" import request = require("supertest") -import getPort = require("get-port") import WebSocket = require("ws") +import { getPort } from "../../../../src/util/network" describe("startServer", () => { let garden: Garden diff --git a/garden-service/test/unit/src/util/network.ts b/garden-service/test/unit/src/util/network.ts new file mode 100644 index 0000000000..ff07bc100c --- /dev/null +++ b/garden-service/test/unit/src/util/network.ts @@ -0,0 +1,10 @@ +import { expect } from "chai" +import { getPort } from "../../../../src/util/network" + +describe("getPort", () => { + it("should fall back to random port if not allowed to bind to specified port", async () => { + const port = await getPort({ port: 0 }) + expect(port).to.not.equal(1) + expect(typeof port).to.equal("number") + }) +})