import type { DockerHost } from './classes.host.js'; import type { ISecretCreationDescriptor, ISecretSpec, } from './interfaces/secret.js'; import type { TLabels } from './interfaces/label.js'; import { DockerResource } from './classes.base.js'; import { assertDockerResponseStatus, encodeDockerResourceData, formatDockerResponseBody, } from './helpers.docker.js'; export class DockerSecret extends DockerResource { /** * Internal: Get all secrets * Public API: Use dockerHost.listSecrets() instead */ public static async _list( dockerHostArg: DockerHost, ): Promise { const response = await dockerHostArg.request('GET', '/secrets'); assertDockerResponseStatus('Docker secret list', 200, response); return response.body.map((secretArg: Record) => { const secret = new DockerSecret(dockerHostArg); Object.assign(secret, secretArg); return secret; }); } /** * Internal: Get secret by ID * Public API: Use dockerHost.getSecretById(id) instead */ public static async _fromId( dockerHostArg: DockerHost, idArg: string, ): Promise { const response = await dockerHostArg.request( 'GET', `/secrets/${encodeURIComponent(idArg)}`, ); if (response.statusCode === 404) { return undefined; } assertDockerResponseStatus(`Docker secret inspect "${idArg}"`, 200, response); const secret = new DockerSecret(dockerHostArg); Object.assign(secret, response.body); return secret; } /** * Internal: Get secret by name * Public API: Use dockerHost.getSecretByName(name) instead */ public static async _fromName( dockerHostArg: DockerHost, nameArg: string, ): Promise { const secrets = await this._list(dockerHostArg); return secrets.find((secretArg) => secretArg.Spec.Name === nameArg); } /** * Internal: Create a secret * Public API: Use dockerHost.createSecret(descriptor) instead */ public static async _create( dockerHostArg: DockerHost, secretDescriptorArg: ISecretCreationDescriptor, ): Promise { const response = await dockerHostArg.request('POST', '/secrets/create', { Name: secretDescriptorArg.name, Labels: { ...secretDescriptorArg.labels, version: secretDescriptorArg.version, }, Data: encodeDockerResourceData(secretDescriptorArg.contentArg), }); assertDockerResponseStatus('Docker secret create', 201, response); const createdId = response.body?.ID; if (typeof createdId !== 'string' || createdId.length === 0) { throw new Error( `Docker secret create failed: expected response body.ID to be a nonempty string; response body: ${formatDockerResponseBody(response.body)}`, ); } const createdSecret = await DockerSecret._fromId(dockerHostArg, createdId); if (!createdSecret) { throw new Error( `Docker secret create failed: direct inspect did not find created ID "${createdId}"`, ); } return createdSecret; } public ID!: string; public Spec!: ISecretSpec; public Version!: { Index: number; }; constructor(dockerHostArg: DockerHost) { super(dockerHostArg); } /** * Refreshes this secret through direct ID inspection. * @throws If Docker no longer has the secret or inspection fails. */ public async refresh(): Promise { const updatedSecret = await DockerSecret._fromId(this.dockerHost, this.ID); if (!updatedSecret) { throw new Error(`Docker secret refresh failed: secret "${this.ID}" was not found`); } Object.assign(this, updatedSecret); } /** * Replaces the complete label map, preserves the inspected Spec, and refreshes. * @param replacementLabelsArg The complete replacement label map. */ public async updateLabels( replacementLabelsArg: TLabels, ): Promise { const response = await this.dockerHost.request( 'POST', `/secrets/${encodeURIComponent(this.ID)}/update?version=${this.Version.Index}`, { ...this.Spec, Labels: replacementLabelsArg, }, ); assertDockerResponseStatus( `Docker secret label update "${this.ID}"`, 200, response, ); await this.refresh(); } /** * Removes this secret. Docker must respond with status 204. */ public async remove(): Promise { const response = await this.dockerHost.request( 'DELETE', `/secrets/${encodeURIComponent(this.ID)}`, ); assertDockerResponseStatus( `Docker secret remove "${this.ID}"`, 204, response, ); } /** * Returns the version label stored in the secret Spec. */ public async getVersion(): Promise { return this.Spec.Labels.version; } }