UNPKG

the-moby-effect

Version:
322 lines (321 loc) 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.version = exports.stop = exports.start = exports.search = exports.runScoped = exports.run = exports.push = exports.pullScoped = exports.pull = exports.ps = exports.pingHead = exports.ping = exports.info = exports.images = exports.execWebsocketsNonBlocking = exports.execWebsockets = exports.execNonBlocking = exports.exec = exports.buildScoped = exports.build = void 0; var Array = _interopRequireWildcard(require("effect/Array")); var Channel = _interopRequireWildcard(require("effect/Channel")); var Effect = _interopRequireWildcard(require("effect/Effect")); var Function = _interopRequireWildcard(require("effect/Function")); var Global = _interopRequireWildcard(require("effect/GlobalValue")); var Match = _interopRequireWildcard(require("effect/Match")); var MutableHashMap = _interopRequireWildcard(require("effect/MutableHashMap")); var Option = _interopRequireWildcard(require("effect/Option")); var Predicate = _interopRequireWildcard(require("effect/Predicate")); var Schedule = _interopRequireWildcard(require("effect/Schedule")); var Schema = _interopRequireWildcard(require("effect/Schema")); var Sink = _interopRequireWildcard(require("effect/Sink")); var Stream = _interopRequireWildcard(require("effect/Stream")); var Tuple = _interopRequireWildcard(require("effect/Tuple")); var MobyDemux = _interopRequireWildcard(require("../../MobyDemux.js")); var MobyEndpoints = _interopRequireWildcard(require("../../MobyEndpoints.js")); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /** @internal */ const pull = ({ auth, image, platform }) => Stream.unwrap(MobyEndpoints.Images.use(images => images.create({ fromImage: image, "X-Registry-Auth": auth, platform }))); /** @internal */ exports.pull = pull; const pullScoped = ({ auth, image, platform }) => Effect.Do.pipe(Effect.bind("images", () => MobyEndpoints.Images), Effect.let("stream", () => pull({ image, auth, platform })), Effect.let("acquire", ({ images, stream }) => Stream.provideService(stream, MobyEndpoints.Images, images)), Effect.let("release", ({ images }) => images.delete({ name: image })), Effect.flatMap(({ acquire, release }) => Effect.acquireRelease(Effect.sync(() => acquire), () => Effect.orDie(release)))); /** @internal */ exports.pullScoped = pullScoped; const build = ({ auth, buildArgs, context, dockerfile, platform, tag }) => Stream.unwrap(MobyEndpoints.Images.use(images => images.build({ context, buildArgs, dockerfile, platform, t: tag, "X-Registry-Config": auth }))); /** @internal */ exports.build = build; const buildScoped = ({ auth, buildArgs, context, dockerfile, platform, tag }) => Effect.Do.pipe(Effect.bind("images", () => MobyEndpoints.Images), Effect.let("stream", () => build({ tag, buildArgs, auth, context, platform, dockerfile })), Effect.let("acquire", ({ images, stream }) => Stream.provideService(stream, MobyEndpoints.Images, images)), Effect.let("release", ({ images }) => images.delete({ name: tag })), Effect.flatMap(({ acquire, release }) => Effect.acquireRelease(Effect.sync(() => acquire), () => Effect.orDie(release)))); /** @internal */ exports.buildScoped = buildScoped; const start = containerId => MobyEndpoints.Containers.use(containers => containers.start(containerId)); /** @internal */ exports.start = start; const stop = containerId => MobyEndpoints.Containers.use(containers => containers.stop(containerId)); /** @internal */ exports.stop = stop; const run = containerOptions => Effect.gen(function* () { const containers = yield* MobyEndpoints.Containers; const containerCreateResponse = yield* containers.create(containerOptions); yield* containers.start(containerCreateResponse.Id); // Helper to wait until a container is dead or running const waitUntilContainerDeadOrRunning = Function.pipe(containers.inspect(containerCreateResponse.Id), // Effect.tap(({ State }) => Effect.log(`Waiting for container to be running, state=${State?.Status}`)), Effect.flatMap(({ State }) => Function.pipe(Match.value(State?.Status), Match.when("running", _s => Effect.void), Match.when("created", _s => Effect.fail("Waiting")), // Match.when(Schemas.ContainerState_Status.RUNNING, (_s) => Effect.void), // Match.when(Schemas.ContainerState_Status.CREATED, (_s) => Effect.fail("Waiting")), Match.orElse(_s => Effect.fail("Container is dead or killed"))).pipe(Effect.mapError(s => new MobyEndpoints.ContainersError({ method: "inspect", cause: new Error(s) }))))).pipe(Effect.retry(Schedule.spaced(500).pipe(Schedule.whileInput(({ message }) => message === "Waiting")))); // Helper for if the container has a healthcheck, wait for it to report healthy const waitUntilContainerHealthy = Function.pipe(containers.inspect(containerCreateResponse.Id), // Effect.tap(({ State }) => // Effect.log(`Waiting for container to be healthy, health=${State?.Health?.Status}`) // ), Effect.flatMap(({ State }) => Function.pipe(Match.value(State?.Health?.Status), Match.when(undefined, _s => Effect.void), Match.when("healthy", _s => Effect.void), Match.when("starting", _s => Effect.fail("Waiting")), // Match.when(Schemas.Health_Status.HEALTHY, (_s) => Effect.void), // Match.when(Schemas.Health_Status.STARTING, (_s) => Effect.fail("Waiting")), Match.orElse(_s => Effect.fail("Container is unhealthy"))).pipe(Effect.mapError(s => new MobyEndpoints.ContainersError({ method: "inspect", cause: new Error(s) }))))).pipe(Effect.retry(Schedule.spaced(500).pipe(Schedule.whileInput(({ message }) => message === "Waiting")))); yield* waitUntilContainerDeadOrRunning; yield* waitUntilContainerHealthy; return yield* containers.inspect(containerCreateResponse.Id); }); /** @internal */ exports.run = run; const runScoped = containerOptions => { const acquire = run(containerOptions); const release = containerData => Effect.orDie(Effect.gen(function* () { const containers = yield* MobyEndpoints.Containers; // FIXME: this cleanup should be better yield* Effect.catchTag(containers.stop(containerData.Id), "ContainersError", () => Effect.void); yield* containers.delete(containerData.Id, { force: true }); })); return Effect.acquireRelease(acquire, release); }; /** @internal */ exports.runScoped = runScoped; const execNonBlocking = ({ command, containerId, detach }) => Effect.gen(function* () { const execs = yield* MobyEndpoints.Execs; const execId = yield* execs.container(containerId, { AttachStdin: true, AttachStderr: true, AttachStdout: true, Cmd: Predicate.isString(command) ? command.split(" ") : command }); const socket = yield* execs.start(execId.Id, { Detach: detach }); return Tuple.make(socket, execId.Id); }); /** @internal */ exports.execNonBlocking = execNonBlocking; const exec = ({ command, containerId }) => Effect.gen(function* () { const [socket, execId] = yield* execNonBlocking({ command, containerId, detach: false }); const output = yield* MobyDemux.demuxToSingleSink(socket, Stream.never, Sink.mkString); const execInspectResponse = yield* MobyEndpoints.Execs.use(execs => execs.inspect(execId)); if (execInspectResponse.Running === true) { return yield* new MobyEndpoints.ExecsError({ method: "exec", cause: new Error("Exec is still running") }); } else { return Tuple.make(execInspectResponse.ExitCode, output); } }); /** @internal */ exports.exec = exec; const execWebsocketsRegistry = /*#__PURE__*/Global.globalValue("the-moby-effect/engines/docker/execWebsocketsRegistry", () => MutableHashMap.empty()); /** @internal */ const execWebsocketsNonBlocking = ({ command, containerId }) => Effect.gen(function* () { const containers = yield* MobyEndpoints.Containers; const mutex = MutableHashMap.get(execWebsocketsRegistry, containerId).pipe(Option.getOrElse(() => { const semaphore = Effect.unsafeMakeSemaphore(1); MutableHashMap.set(execWebsocketsRegistry, containerId, semaphore); return semaphore; })); const acquire = Effect.gen(function* () { const inspect = yield* containers.inspect(containerId); const command = Array.join(inspect.Config?.Cmd ?? [], " "); yield* Effect.mapError(Schema.decodeUnknown(Schema.Literal("/bin/sh", // Basic shell available in most containers "/bin/bash", // Bash shell (common in many Linux distributions) "/usr/bin/bash", // Alternative location for bash "/bin/dash", // Debian Almquist shell (lightweight shell) "/bin/ash", // Lightweight shell used in Alpine Linux "/bin/zsh", // Z shell "/usr/bin/zsh", // Alternative location for zsh "/bin/ksh", // Korn shell "/bin/tcsh", // TENEX C shell "/bin/csh", // C shell "/usr/bin/fish", // Friendly interactive shell "/usr/local/bin/bash", // Alternative location for bash "/usr/local/bin/sh", // Alternative location for sh "/busybox/sh" // Busybox shell ))(command), cause => new MobyEndpoints.ContainersError({ method: "exec", cause })); yield* mutex.take(1); }); // TODO: should this be un-interuptible? const release = Effect.fnUntraced(function* () { yield* mutex.release(1); yield* containers.wait(containerId, { condition: "not-running" }); yield* containers.start(containerId); }, Effect.orDie); const use = Effect.gen(function* () { const cmd = Predicate.isString(command) ? command : Array.join(command, " "); const input = Stream.succeed(`${cmd}; exit\n`); const stdinSocket = yield* containers.attachWebsocket(containerId, { stdin: true, stream: true }); const stdoutSocket = yield* containers.attachWebsocket(containerId, { stdout: true, stream: true }); const stderrSocket = yield* containers.attachWebsocket(containerId, { stderr: true, stream: true }); const sockets = { stdin: stdinSocket, stdout: stdoutSocket, stderr: stderrSocket }; const multiplexedSocket = yield* MobyDemux.pack(sockets, { requestedCapacity: 16 }); const producer = Channel.fromEffect(MobyDemux.demuxRawToSingleSink(stdinSocket, input, Sink.drain)); const consumer = multiplexedSocket.underlying; const zipped = Channel.zipLeft(producer, consumer, { concurrent: true }); return zipped; }); const multiplexedChannel = Effect.acquireRelease(acquire, release).pipe(Effect.map(() => Channel.unwrap(use))).pipe(Channel.unwrapScoped).pipe(Channel.provideService(MobyEndpoints.Containers, containers)); return MobyDemux.makeMultiplexedChannel(multiplexedChannel); }); /** @internal */ exports.execWebsocketsNonBlocking = execWebsocketsNonBlocking; const execWebsockets = ({ command, containerId }) => Function.pipe(execWebsocketsNonBlocking({ command, containerId }), Effect.flatMap(MobyDemux.demuxMultiplexedToSeparateSinks(Stream.empty, Sink.mkString, Sink.mkString))); /** @internal */ exports.execWebsockets = execWebsockets; const ps = options => MobyEndpoints.Containers.use(containers => containers.list(options)); /** @internal */ exports.ps = ps; const push = options => Stream.unwrap(MobyEndpoints.Images.use(images => images.push(options))); /** @internal */ exports.push = push; const images = options => MobyEndpoints.Images.use(images => images.list(options)); /** @internal */ exports.images = images; const search = options => MobyEndpoints.Images.use(images => images.search(options)); /** @internal */ exports.search = search; const version = exports.version = /*#__PURE__*/Function.constant(/*#__PURE__*/MobyEndpoints.Systems.use(systems => systems.version())); /** @internal */ const info = exports.info = /*#__PURE__*/Function.constant(/*#__PURE__*/MobyEndpoints.Systems.use(systems => systems.info())); /** @internal */ const ping = exports.ping = /*#__PURE__*/Function.constant(/*#__PURE__*/MobyEndpoints.Systems.use(systems => systems.ping())); /** @internal */ const pingHead = exports.pingHead = /*#__PURE__*/Function.constant(/*#__PURE__*/MobyEndpoints.Systems.use(systems => systems.ping())); //# sourceMappingURL=docker.js.map