UNPKG

the-moby-effect

Version:
318 lines (317 loc) 16.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.makeProjectLayer = exports.make = exports.layerProject = exports.layer = exports.isDockerComposeError = exports.TypeId = exports.DockerComposeProjectTypeId = exports.DockerComposeErrorTypeId = exports.DockerComposeError = exports.DockerCompose = void 0; var PlatformError = _interopRequireWildcard(require("@effect/platform/Error")); var Socket = _interopRequireWildcard(require("@effect/platform/Socket")); var Array = _interopRequireWildcard(require("effect/Array")); var Context = _interopRequireWildcard(require("effect/Context")); var Effect = _interopRequireWildcard(require("effect/Effect")); var Function = _interopRequireWildcard(require("effect/Function")); var Layer = _interopRequireWildcard(require("effect/Layer")); var Predicate = _interopRequireWildcard(require("effect/Predicate")); var Record = _interopRequireWildcard(require("effect/Record")); var Sink = _interopRequireWildcard(require("effect/Sink")); var Stream = _interopRequireWildcard(require("effect/Stream")); var String = _interopRequireWildcard(require("effect/String")); var Tuple = _interopRequireWildcard(require("effect/Tuple")); var DockerEngine = _interopRequireWildcard(require("../../DockerEngine.js")); 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 DockerComposeErrorTypeId = exports.DockerComposeErrorTypeId = /*#__PURE__*/Symbol.for("@the-moby-effect/engines/DockerCompose/DockerComposeError"); /** @internal */ const TypeId = exports.TypeId = /*#__PURE__*/Symbol.for("@the-moby-effect/engines/DockerCompose"); /** @internal */ const DockerComposeProjectTypeId = exports.DockerComposeProjectTypeId = /*#__PURE__*/Symbol.for("@the-moby-effect/engines/DockerComposeProject"); /** @internal */ const isDockerComposeError = u => Predicate.hasProperty(u, DockerComposeErrorTypeId); /** @internal */ exports.isDockerComposeError = isDockerComposeError; class DockerComposeError extends /*#__PURE__*/PlatformError.TypeIdError(DockerComposeErrorTypeId, "DockerComposeError") { get message() { return `${this.method}`; } } /** @internal */ exports.DockerComposeError = DockerComposeError; const DockerCompose = exports.DockerCompose = /*#__PURE__*/Context.GenericTag("@the-moby-effect/engines/DockerCompose"); /** @internal */ const make = exports.make = /*#__PURE__*/Effect.gen(function* () { const containers = yield* MobyEndpoints.Containers; yield* DockerEngine.pingHead(); const dindContainerId = yield* DockerEngine.runScoped({ spec: { Image: "docker.io/library/docker:latest", Entrypoint: ["/bin/sh"], Tty: false, OpenStdin: true, AttachStdin: true, AttachStdout: true, AttachStderr: true, HostConfig: { Privileged: true, Binds: ["/var/run/docker.sock:/var/run/docker.sock"] } } }).pipe(Effect.map(({ Id }) => Id)); // Helper to upload the project to the dind const uploadProject = project => Effect.catchTag(containers.putArchive(dindContainerId, { path: "/", stream: project }), "ContainersError", cause => new DockerComposeError({ method: "uploadProject", cause })); // Convert a string from camelCase to kebab-case const camelToKebab = Function.compose(String.camelToSnake, String.snakeToKebab); // Stringify options for a command const stringifyOptions = options => Function.pipe(options, Record.toEntries, Array.filter(Function.flow(Tuple.getSecond, Predicate.isNotUndefined)), Array.flatMap(([key, value]) => { if (Predicate.isString(value) || Predicate.isNumber(value) || Predicate.isBoolean(value)) { return [Tuple.make(key, value)]; } else if (Array.isArray(value)) { return Array.map(value, v => [key, v]); } else if (Predicate.isRecord(value)) { return Record.toEntries(value); } else { return Function.absurd(value); } }), Array.map(([key, value]) => `--${camelToKebab(key)}=${value}`), Array.join(" ")); // Helper to run a command in the dind const runCommand = (method, services = [], options = {}) => Effect.gen(function* () { const multiplexed = yield* DockerEngine.execWebsocketsNonBlocking({ containerId: dindContainerId, command: `COMPOSE_STATUS_STDOUT=1 docker compose ${method} ${stringifyOptions(options)} ${Array.join(services, " ")}` }); const { stderr, stdin, stdout } = yield* MobyDemux.fan(multiplexed, { requestedCapacity: 16 }); const streamFailableEnsuring = (stream, effect) => Stream.flatMap(stream, a => Stream.fromEffect(Effect.map(effect, Function.constant(a)))); return streamFailableEnsuring(MobyDemux.mergeRawToTaggedStream(stdout, stderr), MobyDemux.demuxRawToSingleSink(stdin, Stream.make(new Socket.CloseEvent()), Sink.drain)); }).pipe(Stream.unwrap, Stream.mapEffect(entry => { if (Predicate.isTagged(entry, "stdout")) { return Effect.succeed(entry.value); } else { const decoded = new TextDecoder("utf-8").decode(entry.value); return Effect.fail(new DockerComposeError({ method, cause: decoded })); } }), Stream.mapError(cause => new DockerComposeError({ method, cause })), Stream.provideService(MobyEndpoints.Containers, containers)); // Actual compose implementation return DockerCompose.of({ [TypeId]: TypeId, build: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("build", services, { ...options })), Stream.unwrap, Stream.decodeText(), Stream.splitLines), config: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("config", services, { ...options })), Stream.unwrap, Stream.decodeText(), Stream.splitLines, Stream.run(Sink.mkString)), // cp: <E1>( // project: Stream.Stream<Uint8Array, E1, never>, // service: string, // srcPath: string, // destPath: string, // options?: DockerComposeEngine.CopyOptions | undefined // ): Stream.Stream<Uint8Array, E1 | DockerComposeError, never> => {}, create: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("create", services, { ...options })), Stream.unwrap, Stream.runDrain), down: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("down", services, { ...options })), Stream.unwrap, Stream.runDrain), // events: <E1>( // project: Stream.Stream<Uint8Array, E1, never>, // services?: Array<string> | undefined, // options?: DockerComposeEngine.EventsOptions | undefined // ): Stream.Stream<Uint8Array, E1 | DockerComposeError, never> => {}, // exec: <E1>( // project: Stream.Stream<Uint8Array, E1, never>, // service: string, // command: string, // args?: Array<string> | undefined, // options?: DockerComposeEngine.ExecOptions | undefined // ): Stream.Stream<Uint8Array, E1 | DockerComposeError, never> => {}, images: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("images", services, { ...options })), Stream.unwrap, Stream.decodeText(), Stream.splitLines), kill: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("kill", services, { ...options })), Stream.unwrap, Stream.runDrain), logs: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("logs", services, { ...options })), Stream.unwrap, Stream.decodeText(), Stream.splitLines), ls: (project, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("ls", [], { ...options })), Stream.unwrap, Stream.decodeText(), Stream.splitLines), pause: (project, services) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("pause", services)), Stream.unwrap, Stream.runDrain), // port: <E1>( // project: Stream.Stream<Uint8Array, E1, never>, // service: string, // privatePort: number, // options?: DockerComposeEngine.PortOptions | undefined // ): Effect.Effect<number, E1 | DockerComposeError, never> => {}, ps: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("ps", services, { ...options })), Stream.unwrap, Stream.decodeText(), Stream.splitLines), pull: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("pull", services, { ...options })), Stream.unwrap, Stream.decodeText(), Stream.splitLines), push: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("push", services, { ...options })), Stream.unwrap, Stream.runDrain), restart: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("restart", services, { ...options })), Stream.unwrap, Stream.runDrain), rm: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("rm", services, { ...options })), Stream.unwrap, Stream.runDrain), // run: <E1>( // project: Stream.Stream<Uint8Array, E1, never>, // service: string, // command: string, // args?: Array<string> | undefined, // options?: DockerComposeEngine.RunOptions | undefined // ): Stream.Stream<Uint8Array, E1 | DockerComposeError, never> => {}, start: (project, services) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("start", services)), Stream.unwrap, Stream.runDrain), stop: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("stop", services, { ...options })), Stream.unwrap, Stream.runDrain), top: (project, services) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("top", services)), Stream.unwrap, Stream.decodeText(), Stream.splitLines), unpause: (project, services) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("unpause", services)), Stream.unwrap, Stream.runDrain), up: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("up", services, { ...options })), Stream.unwrap, Stream.runDrain), version: (project, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("version", [], { ...options })), Stream.unwrap, Stream.decodeText(), Stream.run(Sink.mkString)), wait: (project, services, options) => Function.pipe(uploadProject(project), Effect.map(() => runCommand("wait", services, { ...options })), Stream.unwrap, Stream.runDrain), forProject: project => makeProjectLayer(project, uploadProject, runCommand) }); }); /** @internal */ const layer = exports.layer = /*#__PURE__*/Layer.scoped(DockerCompose, make); /** @internal */ const makeProjectLayer = (project, uploadProject, runCommand) => Effect.gen(function* () { yield* uploadProject(project); return { [DockerComposeProjectTypeId]: DockerComposeProjectTypeId, build: (services, options) => Function.pipe(runCommand("build", services, { ...options }), Stream.decodeText(), Stream.splitLines), config: (services, options) => Function.pipe(runCommand("config", services, { ...options }), Stream.decodeText(), Stream.splitLines, Stream.run(Sink.mkString)), // cp: ( // // service: string, // srcPath: string, // destPath: string, // options?: DockerComposeEngine.CopyOptions | undefined // ): Stream.Stream<Uint8Array, DockerComposeError, never> => {}, create: (services, options) => Function.pipe(runCommand("create", services, { ...options }), Stream.runDrain), down: (services, options) => Function.pipe(runCommand("down", services, { ...options }), Stream.runDrain), // events: ( // // services?: Array<string> | undefined, // options?: DockerComposeEngine.EventsOptions | undefined // ): Stream.Stream<Uint8Array, DockerComposeError, never> => {}, // exec: ( // // service: string, // command: string, // args?: Array<string> | undefined, // options?: DockerComposeEngine.ExecOptions | undefined // ): Stream.Stream<Uint8Array, DockerComposeError, never> => {}, images: (services, options) => Function.pipe(runCommand("images", services, { ...options }), Stream.decodeText(), Stream.splitLines), kill: (services, options) => Function.pipe(runCommand("kill", services, { ...options }), Stream.runDrain), logs: (services, options) => Function.pipe(runCommand("logs", services, { ...options }), Stream.decodeText(), Stream.splitLines), ls: options => Function.pipe(runCommand("ls", [], { ...options }), Stream.decodeText(), Stream.splitLines), pause: services => Function.pipe(runCommand("pause", services), Stream.runDrain), // port: ( // // service: string, // privatePort: number, // options?: DockerComposeEngine.PortOptions | undefined // ): Effect.Effect<number, DockerComposeError, never> => {}, ps: (services, options) => Function.pipe(runCommand("ps", services, { ...options }), Stream.decodeText(), Stream.splitLines), pull: (services, options) => Function.pipe(runCommand("pull", services, { ...options }), Stream.decodeText(), Stream.splitLines), push: (services, options) => Function.pipe(runCommand("push", services, { ...options }), Stream.runDrain), restart: (services, options) => Function.pipe(runCommand("restart", services, { ...options }), Stream.runDrain), rm: (services, options) => Function.pipe(runCommand("rm", services, { ...options }), Stream.runDrain), // run: ( // // service: string, // command: string, // args?: Array<string> | undefined, // options?: DockerComposeEngine.RunOptions | undefined // ): Stream.Stream<Uint8Array, DockerComposeError, never> => {}, start: services => Function.pipe(runCommand("start", services), Stream.runDrain), stop: (services, options) => Function.pipe(runCommand("stop", services, { ...options }), Stream.runDrain), top: services => Function.pipe(runCommand("top", services), Stream.decodeText(), Stream.splitLines), unpause: services => Function.pipe(runCommand("unpause", services), Stream.runDrain), up: (services, options) => Function.pipe(runCommand("up", services, { ...options }), Stream.runDrain), version: options => Function.pipe(runCommand("version", [], { ...options }), Stream.decodeText(), Stream.run(Sink.mkString)), wait: (services, options) => Function.pipe(runCommand("wait", services, { ...options }), Stream.runDrain) }; }); /** @internal */ exports.makeProjectLayer = makeProjectLayer; const layerProject = (project, tagIdentifier) => { const tag = Context.GenericTag(tagIdentifier); const effect = Effect.flatMap(DockerCompose, ({ forProject }) => forProject(project)); const layer = Layer.effect(tag, effect); return { tag, layer }; }; exports.layerProject = layerProject; //# sourceMappingURL=dockerCompose.js.map