UNPKG

the-moby-effect

Version:
239 lines (221 loc) 8.91 kB
import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; import * as HttpClient from "effect/unstable/http/HttpClient"; import * as HttpApi from "effect/unstable/httpapi/HttpApi"; import * as HttpApiClient from "effect/unstable/httpapi/HttpApiClient"; import * as HttpApiEndpoint from "effect/unstable/httpapi/HttpApiEndpoint"; import * as HttpApiGroup from "effect/unstable/httpapi/HttpApiGroup"; import * as HttpApiSchema from "effect/unstable/httpapi/HttpApiSchema"; import { MobyConnectionOptions } from "../../MobyConnection.ts"; import { makeAgnosticHttpClientLayer } from "../../MobyPlatforms.ts"; import { SwarmSwarm as SwarmData, SwarmInitRequest, SwarmJoinRequest, SwarmSpec } from "../generated/index.ts"; import { DockerError } from "./circular.ts"; import { BadRequest, InternalServerError, NotFound } from "./errors.ts"; /** @since 1.0.0 */ export class NodeNotPartOfSwarm extends Schema.ErrorClass<NodeNotPartOfSwarm>( "@the-moby-effect/endpoints/NodeNotPartOfSwarm" )( { _tag: Schema.tagDefaultOmit("NodeNotPartOfSwarm"), message: Schema.optionalKey(Schema.String), }, { identifier: "NodeNotPartOfSwarm", description: "NodeNotPartOfSwarm", httpApiStatus: 503, } ) {} /** @since 1.0.0 */ export class NodeAlreadyPartOfSwarm extends Schema.ErrorClass<NodeAlreadyPartOfSwarm>( "@the-moby-effect/endpoints/NodeAlreadyPartOfSwarm" )( { _tag: Schema.tagDefaultOmit("NodeAlreadyPartOfSwarm"), message: Schema.optionalKey(Schema.String), }, { identifier: "NodeAlreadyPartOfSwarm", description: "NodeAlreadyPartOfSwarm", httpApiStatus: 503, } ) {} /** @see https://docs.docker.com/reference/api/engine/latest/#tag/Swarm/operation/SwarmInspect */ const inspectSwarmEndpoint = HttpApiEndpoint.get("inspect", "/", { success: SwarmData, // 200 OK error: [ NotFound, // 404 No such swarm NodeNotPartOfSwarm, // 503 Node is not part of a swarm InternalServerError, ], }); /** @see https://docs.docker.com/reference/api/engine/latest/#tag/Swarm/operation/SwarmInit */ const initSwarmEndpoint = HttpApiEndpoint.post("init", "/init", { payload: SwarmInitRequest, success: Schema.String, // 200 OK error: [ BadRequest, // 400 Bad parameter NodeAlreadyPartOfSwarm, // 503 Node is already part of a swarm InternalServerError, ], }); /** @see https://docs.docker.com/reference/api/engine/latest/#tag/Swarm/operation/SwarmJoin */ const joinSwarmEndpoint = HttpApiEndpoint.post("join", "/join", { payload: SwarmJoinRequest, success: HttpApiSchema.Empty(200), // 200 OK error: [ BadRequest, // 400 Bad parameter NodeAlreadyPartOfSwarm, // 503 Node is already part of a swarm InternalServerError, ], }); /** @see https://docs.docker.com/reference/api/engine/latest/#tag/Swarm/operation/SwarmLeave */ const leaveSwarmEndpoint = HttpApiEndpoint.post("leave", "/leave", { query: { force: Schema.optional(Schema.Boolean) }, success: HttpApiSchema.Empty(200), // 200 OK error: [ NodeNotPartOfSwarm, // 503 Node is not part of a swarm InternalServerError, ], }); /** @see https://docs.docker.com/reference/api/engine/latest/#tag/Swarm/operation/SwarmUpdate */ const updateSwarmEndpoint = HttpApiEndpoint.post("update", "/update", { query: { version: Schema.BigIntFromString, rotateWorkerToken: Schema.optional(Schema.Boolean), rotateManagerToken: Schema.optional(Schema.Boolean), rotateManagerUnlockKey: Schema.optional(Schema.Boolean), }, payload: SwarmSpec, success: HttpApiSchema.Empty(200), // 200 OK error: [ BadRequest, // 400 Bad parameter NodeNotPartOfSwarm, // 503 Node is not part of a swarm InternalServerError, ], }); /** @see https://docs.docker.com/reference/api/engine/latest/#tag/Swarm/operation/SwarmUnlockkey */ const unlockkeySwarmEndpoint = HttpApiEndpoint.get("unlockkey", "/unlockkey", { success: Schema.Struct({ UnlockKey: Schema.String }), // 200 OK error: [ NodeNotPartOfSwarm, // 503 Node is not part of a swarm InternalServerError, ], }); /** @see https://docs.docker.com/reference/api/engine/latest/#tag/Swarm/operation/SwarmUnlock */ const unlockSwarmEndpoint = HttpApiEndpoint.post("unlock", "/unlock", { payload: Schema.Struct({ UnlockKey: Schema.String }), success: HttpApiSchema.Empty(200), // 200 OK error: [ NodeNotPartOfSwarm, // 503 Node is not part of a swarm InternalServerError, ], }); /** @see https://docs.docker.com/reference/api/engine/latest/#tag/Swarm */ const SwarmGroup = HttpApiGroup.make("swarm") .add( inspectSwarmEndpoint, initSwarmEndpoint, joinSwarmEndpoint, leaveSwarmEndpoint, updateSwarmEndpoint, unlockkeySwarmEndpoint, unlockSwarmEndpoint ) .prefix("/swarm"); /** * Engines can be clustered together in a swarm. Refer to the swarm mode * documentation for more information. * * @since 1.0.0 * @category HttpApi * @see https://docs.docker.com/reference/api/engine/latest/#tag/Swarm */ export const SwarmApi = HttpApi.make("SwarmApi").add(SwarmGroup); /** * Engines can be clustered together in a swarm. Refer to the swarm mode * documentation for more information. * * @since 1.0.0 * @category Services * @see https://docs.docker.com/reference/api/engine/latest/#tag/Swarm */ export class Swarm extends Context.Service<Swarm>()("@the-moby-effect/endpoints/Swarm", { make: Effect.gen(function* () { const httpClient = yield* HttpClient.HttpClient; const SwarmsError = DockerError.WrapForModule("swarm"); const client = yield* HttpApiClient.group(SwarmApi, { group: "swarm", httpClient }); const inspect_ = () => client.inspect().pipe(Effect.mapError(SwarmsError("inspect"))); const init_ = (payload: (typeof SwarmInitRequest)["~type.make.in"]) => SwarmInitRequest.makeEffect(payload).pipe( Effect.flatMap((payload) => client.init({ payload })), Effect.mapError(SwarmsError("init")) ); const join_ = (payload: (typeof SwarmJoinRequest)["~type.make.in"]) => SwarmJoinRequest.makeEffect(payload).pipe( Effect.flatMap((payload) => client.join({ payload })), Effect.mapError(SwarmsError("join")) ); const leave_ = (options?: { force?: boolean | undefined } | undefined) => client.leave({ query: { ...options } }).pipe(Effect.mapError(SwarmsError("leave"))); const update_ = ( spec: (typeof SwarmSpec)["~type.make.in"], version: bigint, rotate?: | { readonly rotateWorkerToken?: boolean | undefined; readonly rotateManagerToken?: boolean | undefined; readonly rotateManagerUnlockKey?: boolean | undefined; } | undefined ) => SwarmSpec.makeEffect(spec).pipe( Effect.flatMap((payload) => client.update({ payload, query: { version, ...rotate }, }) ), Effect.mapError(SwarmsError("update")) ); const unlockkey_ = () => client.unlockkey().pipe(Effect.mapError(SwarmsError("unlockkey"))); const unlock_ = (unlockKey: string) => client.unlock({ payload: { UnlockKey: unlockKey } }).pipe(Effect.mapError(SwarmsError("unlock"))); return { inspect: inspect_, init: init_, join: join_, leave: leave_, update: update_, unlockkey: unlockkey_, unlock: unlock_, }; }), }) {} /** * Engines can be clustered together in a swarm. Refer to the swarm mode * documentation for more information. * * @since 1.0.0 * @category Layers * @see https://docs.docker.com/reference/api/engine/latest/#tag/Swarm */ export const SwarmLayer: Layer.Layer<Swarm, never, HttpClient.HttpClient> = Layer.effect(Swarm, Swarm.make); /** * Engines can be clustered together in a swarm. Refer to the swarm mode * documentation for more information. * * @since 1.0.0 * @category Layers * @see https://docs.docker.com/reference/api/engine/latest/#tag/Swarm */ export const SwarmLayerLocalSocket: Layer.Layer<Swarm, never, HttpClient.HttpClient> = SwarmLayer.pipe( Layer.provide( makeAgnosticHttpClientLayer( MobyConnectionOptions.socket({ socketPath: "/var/run/docker.sock", }) ) ) );