-
-
Notifications
You must be signed in to change notification settings - Fork 187
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extract Docker client strategies to own modules (#561)
- Loading branch information
1 parent
715b282
commit e26e744
Showing
38 changed files
with
238 additions
and
233 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
2 changes: 1 addition & 1 deletion
2
src/docker/docker-client-config.ts → src/docker/client/docker-client-config.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import Dockerode from "dockerode"; | ||
import { log } from "../../logger"; | ||
import { HostIps, lookupHostIps } from "../lookup-host-ips"; | ||
import { getSystemInfo } from "../../system-info"; | ||
import { RootlessUnixSocketStrategy } from "./strategy/rootless-unix-socket-strategy"; | ||
import { streamToString } from "../../stream-utils"; | ||
import { Readable } from "stream"; | ||
import { resolveHost } from "../resolve-host"; | ||
import { DockerClientStrategy } from "./strategy/docker-client-strategy"; | ||
import { ConfigurationStrategy } from "./strategy/configuration-strategy"; | ||
import { UnixSocketStrategy } from "./strategy/unix-socket-strategy"; | ||
import { NpipeSocketStrategy } from "./strategy/npipe-socket-strategy"; | ||
|
||
export type Provider = "docker" | "podman"; | ||
|
||
export type DockerClient = { | ||
uri: string; | ||
provider: Provider; | ||
host: string; | ||
hostIps: HostIps; | ||
dockerode: Dockerode; | ||
indexServerAddress: string; | ||
composeEnvironment: NodeJS.ProcessEnv; | ||
}; | ||
|
||
export type DockerClientInit = { | ||
uri: string; | ||
dockerode: Dockerode; | ||
composeEnvironment: NodeJS.ProcessEnv; | ||
}; | ||
|
||
const getDockerClient = async (): Promise<DockerClient> => { | ||
const strategies: DockerClientStrategy[] = [ | ||
new ConfigurationStrategy(), | ||
new UnixSocketStrategy(), | ||
new RootlessUnixSocketStrategy(), | ||
new NpipeSocketStrategy(), | ||
]; | ||
|
||
for (const strategy of strategies) { | ||
if (strategy.init) { | ||
await strategy.init(); | ||
} | ||
if (strategy.isApplicable()) { | ||
log.debug(`Found Docker client strategy "${strategy.getName()}"`); | ||
const { uri, dockerode, composeEnvironment } = await strategy.getDockerClient(); | ||
|
||
log.debug(`Testing Docker client strategy "${uri}"...`); | ||
if (await isDockerDaemonReachable(dockerode)) { | ||
const indexServerAddress = (await getSystemInfo(dockerode)).dockerInfo.indexServerAddress; | ||
const provider: Provider = uri.includes("podman.sock") ? "podman" : "docker"; | ||
const host = await resolveHost(dockerode, provider, indexServerAddress, uri); | ||
const hostIps = await lookupHostIps(host); | ||
logDockerClient(strategy.getName(), host, hostIps); | ||
return { uri, provider, host, hostIps, dockerode, indexServerAddress, composeEnvironment }; | ||
} else { | ||
log.warn(`Docker client strategy ${strategy.getName()} is not reachable`); | ||
} | ||
} | ||
} | ||
|
||
throw new Error("No Docker client strategy found"); | ||
}; | ||
|
||
const isDockerDaemonReachable = async (dockerode: Dockerode): Promise<boolean> => { | ||
try { | ||
const response = await dockerode.ping(); | ||
return (await streamToString(Readable.from(response))) === "OK"; | ||
} catch (err) { | ||
log.warn(`Docker daemon is not reachable: ${err}`); | ||
return false; | ||
} | ||
}; | ||
|
||
const logDockerClient = (strategyName: string, host: string, hostIps: HostIps) => { | ||
const formattedHostIps = hostIps.map((hostIp) => hostIp.address).join(", "); | ||
log.info(`Using Docker client strategy "${strategyName}", Docker host "${host}" (${formattedHostIps})`); | ||
}; | ||
|
||
let _dockerClient: Promise<DockerClient>; | ||
|
||
export const dockerClient: () => Promise<DockerClient> = () => { | ||
if (!_dockerClient) { | ||
_dockerClient = getDockerClient(); | ||
} | ||
return _dockerClient; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { DockerClientConfig, getDockerClientConfig } from "../docker-client-config"; | ||
import Dockerode, { DockerOptions } from "dockerode"; | ||
import { sessionId } from "../../session-id"; | ||
import { URL } from "url"; | ||
import { promises as fs } from "fs"; | ||
import path from "path"; | ||
import { DockerClientInit } from "../docker-client"; | ||
import { DockerClientStrategy } from "./docker-client-strategy"; | ||
|
||
export class ConfigurationStrategy implements DockerClientStrategy { | ||
private dockerConfig!: DockerClientConfig; | ||
|
||
async init(): Promise<void> { | ||
this.dockerConfig = await getDockerClientConfig(); | ||
} | ||
|
||
async getDockerClient(): Promise<DockerClientInit> { | ||
const { dockerHost, dockerTlsVerify, dockerCertPath } = this.dockerConfig; | ||
|
||
const dockerOptions: DockerOptions = { headers: { "x-tc-sid": sessionId } }; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const { pathname, hostname, port } = new URL(dockerHost!); | ||
if (hostname !== "") { | ||
dockerOptions.host = hostname; | ||
dockerOptions.port = port; | ||
} else { | ||
dockerOptions.socketPath = pathname; | ||
} | ||
|
||
if (dockerTlsVerify === "1" && dockerCertPath !== undefined) { | ||
dockerOptions.ca = await fs.readFile(path.resolve(dockerCertPath, "ca.pem")); | ||
dockerOptions.cert = await fs.readFile(path.resolve(dockerCertPath, "cert.pem")); | ||
dockerOptions.key = await fs.readFile(path.resolve(dockerCertPath, "key.pem")); | ||
} | ||
|
||
return { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
uri: dockerHost!, | ||
dockerode: new Dockerode(dockerOptions), | ||
composeEnvironment: { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
DOCKER_HOST: dockerHost!, | ||
DOCKER_TLS_VERIFY: dockerTlsVerify, | ||
DOCKER_CERT_PATH: dockerCertPath, | ||
}, | ||
}; | ||
} | ||
|
||
isApplicable(): boolean { | ||
return this.dockerConfig.dockerHost !== undefined; | ||
} | ||
|
||
getName(): string { | ||
return "ConfigurationStrategy"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { DockerClientInit } from "../docker-client"; | ||
|
||
export interface DockerClientStrategy { | ||
init?(): Promise<void>; | ||
|
||
isApplicable(): boolean; | ||
|
||
getDockerClient(): Promise<DockerClientInit>; | ||
|
||
getName(): string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import Dockerode from "dockerode"; | ||
import { DockerClientInit } from "../docker-client"; | ||
import { DockerClientStrategy } from "./docker-client-strategy"; | ||
|
||
export class NpipeSocketStrategy implements DockerClientStrategy { | ||
async getDockerClient(): Promise<DockerClientInit> { | ||
return { | ||
uri: "npipe:////./pipe/docker_engine", | ||
dockerode: new Dockerode({ socketPath: "//./pipe/docker_engine" }), | ||
composeEnvironment: {}, | ||
}; | ||
} | ||
|
||
isApplicable(): boolean { | ||
return process.platform === "win32"; | ||
} | ||
|
||
getName(): string { | ||
return "NpipeSocketStrategy"; | ||
} | ||
} |
File renamed without changes.
5 changes: 3 additions & 2 deletions
5
src/docker/rootless-unix-socket-strategy.ts → ...strategy/rootless-unix-socket-strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import Dockerode from "dockerode"; | ||
import { existsSync } from "fs"; | ||
import { DockerClientInit } from "../docker-client"; | ||
import { DockerClientStrategy } from "./docker-client-strategy"; | ||
|
||
export class UnixSocketStrategy implements DockerClientStrategy { | ||
async getDockerClient(): Promise<DockerClientInit> { | ||
return { | ||
uri: "unix:///var/run/docker.sock", | ||
dockerode: new Dockerode({ socketPath: "/var/run/docker.sock" }), | ||
composeEnvironment: {}, | ||
}; | ||
} | ||
|
||
isApplicable(): boolean { | ||
return (process.platform === "linux" || process.platform === "darwin") && existsSync("/var/run/docker.sock"); | ||
} | ||
|
||
getName(): string { | ||
return "UnixSocketStrategy"; | ||
} | ||
} |
Oops, something went wrong.