Skip to content

Commit

Permalink
improvement(framework): allow users to specify proxy hostname in project
Browse files Browse the repository at this point in the history
config
  • Loading branch information
eysi09 committed Feb 6, 2023
1 parent 1393ce7 commit d17f332
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 9 deletions.
18 changes: 18 additions & 0 deletions core/src/config/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ export interface OutputSpec {
value: Primitive
}

export interface ProxyConfig {
hostname: string
}

export interface ProjectConfig {
apiVersion: string
kind: "Project"
Expand All @@ -179,6 +183,7 @@ export interface ProjectConfig {
id?: string
domain?: string
configPath?: string
proxy?: ProxyConfig
defaultEnvironment: string
dotIgnoreFiles: string[]
environments: EnvironmentConfig[]
Expand Down Expand Up @@ -319,6 +324,19 @@ export const projectDocsSchema = () =>
`
)
.example([".gardenignore", ".gitignore"]),
proxy: joi.object().keys({
hostname: joi
.string()
.default("localhost")
.description(
dedent`
The URL that Garden uses when creating port forwards. Defaults to "localhost".
Note that the \`GARDEN_PROXY_DEFAULT_ADDRESS\` enviorment variable takes precedence over this value.
`
)
.example(["127.0.0.1"]),
}),
modules: projectModulesSchema().description("Control where to scan for modules in the project."),
outputs: joiSparseArray(projectOutputSchema())
.unique("name")
Expand Down
22 changes: 21 additions & 1 deletion core/src/garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
parseEnvironment,
getDefaultEnvironmentName,
projectSourcesSchema,
ProxyConfig,
} from "./config/project"
import {
findByName,
Expand Down Expand Up @@ -117,6 +118,8 @@ import { ConfigContext } from "./config/template-contexts/base"
import { validateSchema, validateWithPath } from "./config/validation"
import { pMemoizeDecorator } from "./lib/p-memoize"

const defaultLocalAddress = "localhost"

export interface ActionHandlerMap<T extends keyof PluginActionHandlers> {
[actionName: string]: PluginActionHandlers[T]
}
Expand Down Expand Up @@ -164,6 +167,7 @@ export interface GardenParams {
cache: TreeCache
disablePortForwards?: boolean
dotIgnoreFiles: string[]
proxy: ProxyConfig
environmentName: string
environmentConfigs: EnvironmentConfig[]
namespace: string
Expand Down Expand Up @@ -234,6 +238,7 @@ export class Garden {
private readonly providerConfigs: GenericProviderConfig[]
public readonly workingCopyId: string
public readonly dotIgnoreFiles: string[]
public readonly proxy: ProxyConfig
public readonly moduleIncludePatterns?: string[]
public readonly moduleExcludePatterns: string[]
public readonly persistent: boolean
Expand Down Expand Up @@ -269,6 +274,7 @@ export class Garden {
this.secrets = params.secrets
this.workingCopyId = params.workingCopyId
this.dotIgnoreFiles = params.dotIgnoreFiles
this.proxy = params.proxy
this.moduleIncludePatterns = params.moduleIncludePatterns
this.moduleExcludePatterns = params.moduleExcludePatterns || []
this.persistent = !!params.opts.persistent
Expand Down Expand Up @@ -1393,6 +1399,19 @@ export const resolveGardenParams = profileAsync(async function _resolveGardenPar
...fixedProjectExcludes,
]

// Set proxy hostname with the following order of precedence: env var > config > default value ("localhost")
let proxyHostname: string
if (gardenEnv.GARDEN_PROXY_DEFAULT_ADDRESS) {
proxyHostname = gardenEnv.GARDEN_PROXY_DEFAULT_ADDRESS
} else if (config.proxy?.hostname) {
proxyHostname = config.proxy.hostname
} else {
proxyHostname = defaultLocalAddress
}
const proxy = {
hostname: proxyHostname,
}

return {
artifactsPath,
vcsInfo,
Expand All @@ -1418,8 +1437,9 @@ export const resolveGardenParams = profileAsync(async function _resolveGardenPar
moduleExcludePatterns,
workingCopyId,
dotIgnoreFiles: config.dotIgnoreFiles,
moduleIncludePatterns: (config.modules || {}).include,
proxy,
log,
moduleIncludePatterns: (config.modules || {}).include,
username: _username,
forceRefresh: opts.forceRefresh,
cloudApi,
Expand Down
10 changes: 3 additions & 7 deletions core/src/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { registerCleanupFunction, sleep } from "./util/util"
import { LogEntry } from "./logger/log-entry"
import { GetPortForwardResult } from "./types/plugin/service/getPortForward"
import { ConfigGraph } from "./config-graph"
import { gardenEnv } from "./constants"

interface PortProxy {
key: string
Expand All @@ -29,8 +28,6 @@ interface PortProxy {
spec: ForwardablePort
}

const defaultLocalAddress = "localhost"

const activeProxies: { [key: string]: PortProxy } = {}

registerCleanupFunction("kill-service-port-proxies", () => {
Expand Down Expand Up @@ -225,13 +222,12 @@ async function createProxy({ garden, graph, log, service, spec }: StartPortProxy
})
}

const defaultLocalAddress = garden.proxy.hostname
let localIp = defaultLocalAddress
let localPort: number | undefined
const preferredLocalPort = spec.preferredLocalPort || spec.targetPort

if (gardenEnv.GARDEN_PROXY_DEFAULT_ADDRESS) {
localIp = gardenEnv.GARDEN_PROXY_DEFAULT_ADDRESS
} else if (!spec.preferredLocalPort) {
if (!spec.preferredLocalPort) {
// TODO: drop this in 0.13, it causes more issues than it solves
// Only try a non-default IP if a preferred port isn't set
// Note: lazy-loading for startup performance
Expand All @@ -250,7 +246,7 @@ async function createProxy({ garden, graph, log, service, spec }: StartPortProxy
try {
localPort = await getPort({ host: localIp, port: preferredLocalPort })
} catch (err) {
if (err.code === "EADDRNOTAVAIL") {
if (err.code === "EADDRNOTAVAIL" && localIp !== defaultLocalAddress) {
// If we're not allowed to bind to other 127.x.x.x addresses, we fall back to localhost. This will almost always
// be the case on Mac, until we come up with something more clever (that doesn't require sudo).
localIp = defaultLocalAddress
Expand Down
99 changes: 98 additions & 1 deletion core/test/unit/src/garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import {
baseBuildSpecSchema,
defaultBuildTimeout,
} from "../../../src/config/module"
import { DEFAULT_API_VERSION } from "../../../src/constants"
import { DEFAULT_API_VERSION, gardenEnv } from "../../../src/constants"
import { providerConfigBaseSchema } from "../../../src/config/provider"
import { keyBy, set, mapValues } from "lodash"
import stripAnsi from "strip-ansi"
Expand Down Expand Up @@ -377,6 +377,103 @@ describe("Garden", () => {

expect(garden.variables).to.eql({ foo: "override", bar: "something" })
})

it("should set the default proxy config if non is specified", async () => {
const config: ProjectConfig = {
apiVersion: DEFAULT_API_VERSION,
kind: "Project",
name: "test",
path: pathFoo,
defaultEnvironment: "default",
dotIgnoreFiles: [],
environments: [{ name: "default", defaultNamespace: "foo", variables: {} }],
providers: [{ name: "foo" }],
variables: { foo: "default", bar: "something" },
}

const garden = await TestGarden.factory(pathFoo, {
config,
environmentName: "default",
variables: { foo: "override" },
})

expect(garden.proxy).to.eql({ domain: "localhost" })
})

it("should optionally read the proxy config from the project config", async () => {
const config: ProjectConfig = {
apiVersion: DEFAULT_API_VERSION,
kind: "Project",
name: "test",
path: pathFoo,
proxy: {
hostname: "127.0.0.1", // <--- Proxy config is set here
},
defaultEnvironment: "default",
dotIgnoreFiles: [],
environments: [{ name: "default", defaultNamespace: "foo", variables: {} }],
providers: [{ name: "foo" }],
variables: { foo: "default", bar: "something" },
}

const garden = await TestGarden.factory(pathFoo, {
config,
environmentName: "default",
variables: { foo: "override" },
})

expect(garden.proxy).to.eql({ domain: "127.0.0.1" })
})

it("should use the GARDEN_PROXY_DEFAULT_ADDRESS env variable if set", async () => {
const saveEnv = gardenEnv.GARDEN_PROXY_DEFAULT_ADDRESS
try {
gardenEnv.GARDEN_PROXY_DEFAULT_ADDRESS = "example.com"
const configNoProxy: ProjectConfig = {
apiVersion: DEFAULT_API_VERSION,
kind: "Project",
name: "test",
path: pathFoo,
defaultEnvironment: "default",
dotIgnoreFiles: [],
environments: [{ name: "default", defaultNamespace: "foo", variables: {} }],
providers: [{ name: "foo" }],
variables: { foo: "default", bar: "something" },
}
const configWithProxy: ProjectConfig = {
apiVersion: DEFAULT_API_VERSION,
kind: "Project",
name: "test",
path: pathFoo,
proxy: {
hostname: "127.0.0.1", // <--- This should be overwritten
},
defaultEnvironment: "default",
dotIgnoreFiles: [],
environments: [{ name: "default", defaultNamespace: "foo", variables: {} }],
providers: [{ name: "foo" }],
variables: { foo: "default", bar: "something" },
}

const gardenWithProxyConfig = await TestGarden.factory(pathFoo, {
config: configWithProxy,
environmentName: "default",
variables: { foo: "override" },
noCache: true,
})
const gardenNoProxyConfig = await TestGarden.factory(pathFoo, {
config: configNoProxy,
environmentName: "default",
variables: { foo: "override" },
noCache: true,
})

expect(gardenWithProxyConfig.proxy).to.eql({ domain: "example.com" })
expect(gardenNoProxyConfig.proxy).to.eql({ domain: "example.com" })
} finally {
gardenEnv.GARDEN_PROXY_DEFAULT_ADDRESS = saveEnv
}
})
})

describe("getAllPlugins", () => {
Expand Down

0 comments on commit d17f332

Please sign in to comment.