diff --git a/src/context.ts b/src/context.ts index 43c2ca6..ab282e1 100644 --- a/src/context.ts +++ b/src/context.ts @@ -4,6 +4,7 @@ import Dockerode from "dockerode"; import {getCurrent} from "./docker-api.js"; import {initHashedConfigs} from "./hashed-config.js"; import {ArgumentsCamelCase} from "yargs"; +import {loadDockerAuths} from "./docker-config.js"; export async function initContext (args: ArgumentsCamelCase) { @@ -24,5 +25,7 @@ export async function initContext (args: ArgumentsCamelCase) { const hashedConfigs = await initHashedConfigs(config); - return {appName, config, dockerode, current, hashedConfigs}; + const dockerAuths = await loadDockerAuths(); + + return {appName, config, dockerode, current, hashedConfigs, dockerAuths}; } diff --git a/src/docker-api.ts b/src/docker-api.ts index 344a7a4..d89defa 100644 --- a/src/docker-api.ts +++ b/src/docker-api.ts @@ -3,6 +3,8 @@ import {initServiceSpec, sortServiceSpec} from "./service-spec.js"; import {HashedConfigs} from "./hashed-config.js"; import {assertString} from "./asserts.js"; import {SwarmAppConfig} from "./swarm-app-config.js"; +import {resolveAuthConfig} from "./docker-config.js"; +import {AuthConfigObject} from "dockerode"; import timers from "timers/promises"; import assert from "assert"; @@ -116,18 +118,21 @@ interface UpsertServicesOpts { current: DockerResources; appName: string; hashedConfigs: HashedConfigs; + dockerAuths?: Record | undefined; } -export async function upsertServices ({dockerode, config, current, appName, hashedConfigs}: UpsertServicesOpts) { +export async function upsertServices ({dockerode, config, current, appName, hashedConfigs, dockerAuths}: UpsertServicesOpts) { for (const serviceName of Object.keys(config.service_specs)) { const serviceSpec = initServiceSpec({appName, serviceName, config, hashedConfigs, current}); + const image = config.service_specs[serviceName]?.image; + const authconfig = image && dockerAuths ? resolveAuthConfig(image, dockerAuths) : undefined; const foundService = current.services.find((s) => s.Spec?.Name === `${appName}_${serviceName}`); if (!foundService) { console.log(`Creating service ${appName}_${serviceName}`); - await dockerode.createService(serviceSpec); + await dockerode.createService({...serviceSpec, authconfig}); } else { serviceSpec.version = foundService.Version?.Index ?? 0; console.log(`Updating service ${appName}_${serviceName}`); - await dockerode.getService(foundService.ID).update(serviceSpec); + await dockerode.getService(foundService.ID).update({...serviceSpec, authconfig}); } } } diff --git a/src/docker-config.ts b/src/docker-config.ts new file mode 100644 index 0000000..718909a --- /dev/null +++ b/src/docker-config.ts @@ -0,0 +1,27 @@ +import fs from "fs"; +import path from "path"; +import {AuthConfig, AuthConfigObject} from "dockerode"; + +export async function loadDockerAuths (): Promise> { + const configDir = process.env.DOCKER_CONFIG ?? path.join(process.env.HOME ?? "~", ".docker"); + const configPath = path.join(configDir, "config.json"); + + try { + const content = await fs.promises.readFile(configPath, "utf-8"); + const config = JSON.parse(content) as {auths?: Record}; + return config.auths ?? {}; + } catch { + return {}; + } +} + +export function resolveAuthConfig (image: string, auths: Record): AuthConfig | undefined { + const parts = image.split("/"); + const first = parts[0]; + const registry = parts.length >= 2 && first && (first.includes(".") || first.includes(":")) ? first : "https://index.docker.io/v1/"; + + const entry = auths[registry]; + if (!entry?.auth) return undefined; + + return {auth: entry.auth, serveraddress: registry}; +}