UNPKG

the-moby-effect

Version:
194 lines (176 loc) 7.37 kB
import type * as HttpBody from "@effect/platform/HttpBody"; import type * as HttpClientError from "@effect/platform/HttpClientError"; import type * as Socket from "@effect/platform/Socket"; import type * as Layer from "effect/Layer"; import type * as ParseResult from "effect/ParseResult"; import * as PlatformError from "@effect/platform/Error"; import * as HttpClient from "@effect/platform/HttpClient"; import * as HttpClientRequest from "@effect/platform/HttpClientRequest"; import * as HttpClientResponse from "@effect/platform/HttpClientResponse"; import * as Effect from "effect/Effect"; import * as Function from "effect/Function"; import * as Option from "effect/Option"; import * as Predicate from "effect/Predicate"; import * as Schema from "effect/Schema"; import * as Tuple from "effect/Tuple"; import * as MobyDemux from "../../MobyDemux.js"; import { ContainerExecOptions as ContainerExecStartConfig, ContainerExecOptions as ExecConfig, ContainerExecInspect as ExecInspectResponse, IDResponse, } from "../generated/index.js"; import { maybeAddQueryParameter } from "./common.js"; /** * @since 1.0.0 * @category Errors */ export const ExecsErrorTypeId: unique symbol = Symbol.for("@the-moby-effect/endpoints/ExecsError") as ExecsErrorTypeId; /** * @since 1.0.0 * @category Errors */ export type ExecsErrorTypeId = typeof ExecsErrorTypeId; /** * @since 1.0.0 * @category Errors */ export const isExecsError = (u: unknown): u is ExecsError => Predicate.hasProperty(u, ExecsErrorTypeId); /** * @since 1.0.0 * @category Errors */ export class ExecsError extends PlatformError.TypeIdError(ExecsErrorTypeId, "ExecsError")<{ method: string; cause: | ParseResult.ParseError | HttpClientError.HttpClientError | HttpBody.HttpBodyError | Socket.SocketError | unknown; }> { get message() { return `${this.method}`; } } /** * Execs service * * @since 1.0.0 * @category Tags * @see https://docs.docker.com/reference/api/engine/latest/#tag/Exec */ export class Execs extends Effect.Service<Execs>()("@the-moby-effect/endpoints/Execs", { accessors: false, dependencies: [], effect: Effect.gen(function* () { const contextClient = yield* HttpClient.HttpClient; const client = contextClient.pipe(HttpClient.filterStatusOk); const maybeUpgradedClient = contextClient.pipe( HttpClient.filterStatus((status) => (status >= 200 && status < 300) || status === 101) ); /** * Create an exec instance * * @param execConfig - Exec configuration * @param id - ID or name of container * @see https://docs.docker.com/reference/api/engine/latest/#tag/Exec/operation/ContainerExec */ const container_ = ( id: string, execConfig: typeof ExecConfig.Encoded ): Effect.Effect<Readonly<IDResponse>, ExecsError, never> => Function.pipe( Schema.decode(ExecConfig)(execConfig), Effect.map((body) => Tuple.make(HttpClientRequest.post(`/containers/${encodeURIComponent(id)}/exec`), body) ), Effect.flatMap(Function.tupled(HttpClientRequest.schemaBodyJson(ExecConfig))), Effect.flatMap(client.execute), Effect.flatMap(HttpClientResponse.schemaBodyJson(IDResponse)), Effect.mapError((cause) => new ExecsError({ method: "container", cause })) ); /** * Start an exec instance * * @param execStartConfig - * @param id - Exec instance ID * @see https://docs.docker.com/reference/api/engine/latest/#tag/Exec/operation/ExecStart */ const start_ = <T extends boolean | undefined = undefined>( id: string, execStartConfig: Omit<typeof ContainerExecStartConfig.Encoded, "Detach"> & { Detach?: T } ): Effect.Effect< T extends true ? void : MobyDemux.MultiplexedSocket | MobyDemux.RawSocket, ExecsError, never > => { type U = Effect.Effect< T extends true ? void : MobyDemux.MultiplexedSocket | MobyDemux.RawSocket, ExecsError, never >; const response = Function.pipe( Schema.decode(ContainerExecStartConfig)(execStartConfig), Effect.map((body) => Tuple.make(HttpClientRequest.post(`/exec/${encodeURIComponent(id)}/start`), body)), Effect.flatMap(Function.tupled(HttpClientRequest.schemaBodyJson(ContainerExecStartConfig))), Effect.map(HttpClientRequest.setHeader("Upgrade", "tcp")), Effect.map(HttpClientRequest.setHeader("Connection", "Upgrade")), Effect.flatMap(maybeUpgradedClient.execute), Effect.mapError((cause) => new ExecsError({ method: "start", cause })) ); const toStreamingSock = Function.compose( MobyDemux.responseToStreamingSocketOrFailUnsafe, Effect.mapError((cause) => new ExecsError({ method: "start", cause })) ); return execStartConfig.Detach ? (Function.pipe(response, Effect.asVoid) as U) : (Function.pipe(response, Effect.flatMap(toStreamingSock)) as U); }; /** * Resize an exec instance * * @param id - Exec instance ID * @param h - Height of the TTY session in characters * @param w - Width of the TTY session in characters * @see https://docs.docker.com/reference/api/engine/latest/#tag/Exec/operation/ExecResize */ const resize_ = ( id: string, options?: | { readonly h?: number; readonly w?: number; } | undefined ): Effect.Effect<void, ExecsError, never> => Function.pipe( HttpClientRequest.post(`/exec/${encodeURIComponent(id)}/resize`), maybeAddQueryParameter("h", Option.fromNullable(options?.h)), maybeAddQueryParameter("w", Option.fromNullable(options?.w)), client.execute, Effect.asVoid, Effect.mapError((cause) => new ExecsError({ method: "resize", cause })) ); /** @see https://docs.docker.com/reference/api/engine/latest/#tag/Exec/operation/ExecInspect */ const inspect_ = (id: string): Effect.Effect<ExecInspectResponse, ExecsError, never> => Function.pipe( HttpClientRequest.get(`/exec/${encodeURIComponent(id)}/json`), client.execute, Effect.flatMap(HttpClientResponse.schemaBodyJson(ExecInspectResponse)), Effect.mapError((cause) => new ExecsError({ method: "inspect", cause })) ); return { container: container_, start: start_, resize: resize_, inspect: inspect_, }; }), }) {} /** * @since 1.0.0 * @category Layers * @see https://docs.docker.com/reference/api/engine/latest/#tag/Exec */ export const ExecsLayer: Layer.Layer<Execs, never, HttpClient.HttpClient> = Execs.Default;