From 3c2a8268447c15fc3196d1aca05f08b65c74e0f0 Mon Sep 17 00:00:00 2001 From: Leo Conforti Date: Sun, 3 Nov 2024 16:21:03 +0000 Subject: [PATCH 1/6] start docker compose --- src/engines/DockerCompose.ts | 152 +++++- src/engines/compose-spec.json | 935 ++++++++++++++++++++++++++++++++++ 2 files changed, 1080 insertions(+), 7 deletions(-) create mode 100644 src/engines/compose-spec.json diff --git a/src/engines/DockerCompose.ts b/src/engines/DockerCompose.ts index 4433154d..d42cccce 100644 --- a/src/engines/DockerCompose.ts +++ b/src/engines/DockerCompose.ts @@ -1,13 +1,151 @@ /** - * Docker compose engine + * Docker compose engine. * * @since 1.0.0 */ -// TODO: implement -export interface DockerComposeImpl { - up: () => void; - down: () => void; - pull: () => void; - build: () => void; +import * as FileSystem from "@effect/platform/FileSystem"; +import * as Context from "effect/Context"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as Stream from "effect/Stream"; + +import { Containers, ContainersError } from "../endpoints/Containers.js"; +import { Images, ImagesError } from "../endpoints/Images.js"; +import { Networks, NetworksError } from "../endpoints/Networks.js"; +import { Volumes, VolumesError } from "../endpoints/Volumes.js"; +import { JSONMessage } from "../generated/index.js"; +import { DockerLayer } from "./Docker.js"; + +/** + * @since 1.0.0 + * @category Type id + */ +export const TypeId: unique symbol = Symbol.for("@the-moby-effect/engines/DockerCompose") as TypeId; + +/** + * @since 1.0.0 + * @category Type id + */ +export type TypeId = typeof TypeId; + +/** + * @since 1.0.0 + * @category Models + */ +export interface DockerCompose { + readonly [TypeId]: TypeId; + + readonly build: ( + project: unknown, + options: {} + ) => Effect.Effect< + Record>, + ImagesError, + FileSystem.FileSystem + >; + + readonly pull: ( + project: unknown, + options: {} + ) => Effect.Effect>, ImagesError, never>; + + readonly up: ( + project: unknown, + options: {} + ) => Effect.Effect, ContainersError | VolumesError | NetworksError, never>; + + readonly down: (project: unknown, options: {}) => Effect.Effect, ContainersError, never>; + + readonly rm: ( + project: unknown, + options: {} + ) => Effect.Effect, ContainersError | VolumesError | NetworksError, never>; + + readonly kill: (project: unknown, options: {}) => Effect.Effect, ContainersError, never>; + + readonly forProject: (project: unknown) => DockerComposeProject; +} + +/** + * @since 1.0.0 + * @category Tags + */ +export const DockerCompose: Context.Tag = Context.GenericTag( + "@the-moby-effect/engines/DockerCompose" +); + +/** + * @since 1.0.0 + * @category Layers + */ +export const layer: Layer.Layer> = Layer.effect( + DockerCompose, + Effect.gen(function* () { + const images = yield* Images; + const volumes = yield* Volumes; + const networks = yield* Networks; + const containers = yield* Containers; + + return DockerCompose.of({}); + }) +); + +/** + * @since 1.0.0 + * @category Type id + */ +export const DockerComposeProjectTypeId: unique symbol = Symbol.for( + "@the-moby-effect/engines/DockerComposeProject" +) as DockerComposeProjectTypeId; + +/** + * @since 1.0.0 + * @category Type id + */ +export type DockerComposeProjectTypeId = typeof DockerComposeProjectTypeId; + +/** + * @since 1.0.0 + * @category Models + */ +export interface DockerComposeProject { + readonly [DockerComposeProjectTypeId]: DockerComposeProjectTypeId; + + readonly build: (options: {}) => Effect.Effect< + Record>, + ImagesError, + never + >; + + readonly up: (options: {}) => Effect.Effect< + Record, + ContainersError | VolumesError | NetworksError, + never + >; + + readonly down: (options: {}) => Effect.Effect, ContainersError, never>; + + readonly rm: (options: {}) => Effect.Effect< + Record, + ContainersError | VolumesError | NetworksError, + void + >; } + +/** + * @since 1.0.0 + * @category Layers + */ +export const layerProject: ( + project: string, + tagIdentifier: string +) => { + readonly tag: Context.Tag; + readonly layer: Layer.Layer; +} = (project: unknown, tagIdentifier: string) => { + const tag = Context.GenericTag(tagIdentifier); + const effect = Effect.map(DockerCompose, ({ forProject }) => forProject(project)); + const layer = Layer.effect(tag, effect); + return { tag, layer } as const; +}; diff --git a/src/engines/compose-spec.json b/src/engines/compose-spec.json new file mode 100644 index 00000000..4a24dfd0 --- /dev/null +++ b/src/engines/compose-spec.json @@ -0,0 +1,935 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema#", + + "id": "compose_spec.json", + "type": "object", + "title": "Compose Specification", + "description": "The Compose file is a YAML file defining a multi-containers based application.", + + "paths": {}, + + "properties": { + "version": { + "type": "string", + "description": "declared for backward compatibility, ignored." + }, + + "name": { + "type": "string", + "description": "define the Compose project name, until user defines one explicitly." + }, + + "include": { + "type": "array", + "items": { + "$ref": "#/definitions/include" + }, + "description": "compose sub-projects to be included." + }, + + "services": { + "id": "#/properties/services", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/service" + } + }, + "additionalProperties": false + }, + + "networks": { + "id": "#/properties/networks", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/network" + } + } + }, + + "volumes": { + "id": "#/properties/volumes", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/volume" + } + }, + "additionalProperties": false + }, + + "secrets": { + "id": "#/properties/secrets", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/secret" + } + }, + "additionalProperties": false + }, + + "configs": { + "id": "#/properties/configs", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/config" + } + }, + "additionalProperties": false + } + }, + + "patternProperties": { "^x-": {} }, + "additionalProperties": false, + + "definitions": { + "service": { + "id": "#/definitions/service", + "type": "object", + + "properties": { + "develop": { "$ref": "#/definitions/development" }, + "deploy": { "$ref": "#/definitions/deployment" }, + "annotations": { "$ref": "#/definitions/list_or_dict" }, + "attach": { "type": ["boolean", "string"] }, + "build": { + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "context": { "type": "string" }, + "dockerfile": { "type": "string" }, + "dockerfile_inline": { "type": "string" }, + "entitlements": { "type": "array", "items": { "type": "string" } }, + "args": { "$ref": "#/definitions/list_or_dict" }, + "ssh": { "$ref": "#/definitions/list_or_dict" }, + "labels": { "$ref": "#/definitions/list_or_dict" }, + "cache_from": { "type": "array", "items": { "type": "string" } }, + "cache_to": { "type": "array", "items": { "type": "string" } }, + "no_cache": { "type": ["boolean", "string"] }, + "additional_contexts": { "$ref": "#/definitions/list_or_dict" }, + "network": { "type": "string" }, + "pull": { "type": ["boolean", "string"] }, + "target": { "type": "string" }, + "shm_size": { "type": ["integer", "string"] }, + "extra_hosts": { "$ref": "#/definitions/extra_hosts" }, + "isolation": { "type": "string" }, + "privileged": { "type": ["boolean", "string"] }, + "secrets": { "$ref": "#/definitions/service_config_or_secret" }, + "tags": { "type": "array", "items": { "type": "string" } }, + "ulimits": { "$ref": "#/definitions/ulimits" }, + "platforms": { "type": "array", "items": { "type": "string" } } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + ] + }, + "blkio_config": { + "type": "object", + "properties": { + "device_read_bps": { + "type": "array", + "items": { "$ref": "#/definitions/blkio_limit" } + }, + "device_read_iops": { + "type": "array", + "items": { "$ref": "#/definitions/blkio_limit" } + }, + "device_write_bps": { + "type": "array", + "items": { "$ref": "#/definitions/blkio_limit" } + }, + "device_write_iops": { + "type": "array", + "items": { "$ref": "#/definitions/blkio_limit" } + }, + "weight": { "type": ["integer", "string"] }, + "weight_device": { + "type": "array", + "items": { "$ref": "#/definitions/blkio_weight" } + } + }, + "additionalProperties": false + }, + "cap_add": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, + "cap_drop": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, + "cgroup": { "type": "string", "enum": ["host", "private"] }, + "cgroup_parent": { "type": "string" }, + "command": { "$ref": "#/definitions/command" }, + "configs": { "$ref": "#/definitions/service_config_or_secret" }, + "container_name": { "type": "string" }, + "cpu_count": { "oneOf": [{ "type": "string" }, { "type": "integer", "minimum": 0 }] }, + "cpu_percent": { "oneOf": [{ "type": "string" }, { "type": "integer", "minimum": 0, "maximum": 100 }] }, + "cpu_shares": { "type": ["number", "string"] }, + "cpu_quota": { "type": ["number", "string"] }, + "cpu_period": { "type": ["number", "string"] }, + "cpu_rt_period": { "type": ["number", "string"] }, + "cpu_rt_runtime": { "type": ["number", "string"] }, + "cpus": { "type": ["number", "string"] }, + "cpuset": { "type": "string" }, + "credential_spec": { + "type": "object", + "properties": { + "config": { "type": "string" }, + "file": { "type": "string" }, + "registry": { "type": "string" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "depends_on": { + "oneOf": [ + { "$ref": "#/definitions/list_of_strings" }, + { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "type": "object", + "additionalProperties": false, + "patternProperties": { "^x-": {} }, + "properties": { + "restart": { "type": ["boolean", "string"] }, + "required": { + "type": "boolean", + "default": true + }, + "condition": { + "type": "string", + "enum": [ + "service_started", + "service_healthy", + "service_completed_successfully" + ] + } + }, + "required": ["condition"] + } + } + } + ] + }, + "device_cgroup_rules": { "$ref": "#/definitions/list_of_strings" }, + "devices": { + "type": "array", + "items": { + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "required": ["source"], + "properties": { + "source": { "type": "string" }, + "target": { "type": "string" }, + "permissions": { "type": "string" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + ] + } + }, + "dns": { "$ref": "#/definitions/string_or_list" }, + "dns_opt": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, + "dns_search": { "$ref": "#/definitions/string_or_list" }, + "domainname": { "type": "string" }, + "entrypoint": { "$ref": "#/definitions/command" }, + "env_file": { "$ref": "#/definitions/env_file" }, + "environment": { "$ref": "#/definitions/list_or_dict" }, + + "expose": { + "type": "array", + "items": { + "type": ["string", "number"], + "format": "expose" + }, + "uniqueItems": true + }, + "extends": { + "oneOf": [ + { "type": "string" }, + { + "type": "object", + + "properties": { + "service": { "type": "string" }, + "file": { "type": "string" } + }, + "required": ["service"], + "additionalProperties": false + } + ] + }, + "external_links": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, + "extra_hosts": { "$ref": "#/definitions/extra_hosts" }, + "group_add": { + "type": "array", + "items": { + "type": ["string", "number"] + }, + "uniqueItems": true + }, + "healthcheck": { "$ref": "#/definitions/healthcheck" }, + "hostname": { "type": "string" }, + "image": { "type": "string" }, + "init": { "type": ["boolean", "string"] }, + "ipc": { "type": "string" }, + "isolation": { "type": "string" }, + "labels": { "$ref": "#/definitions/list_or_dict" }, + "links": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, + "logging": { + "type": "object", + + "properties": { + "driver": { "type": "string" }, + "options": { + "type": "object", + "patternProperties": { + "^.+$": { "type": ["string", "number", "null"] } + } + } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "mac_address": { "type": "string" }, + "mem_limit": { "type": ["number", "string"] }, + "mem_reservation": { "type": ["string", "integer"] }, + "mem_swappiness": { "type": ["integer", "string"] }, + "memswap_limit": { "type": ["number", "string"] }, + "network_mode": { "type": "string" }, + "networks": { + "oneOf": [ + { "$ref": "#/definitions/list_of_strings" }, + { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "oneOf": [ + { + "type": "object", + "properties": { + "aliases": { "$ref": "#/definitions/list_of_strings" }, + "ipv4_address": { "type": "string" }, + "ipv6_address": { "type": "string" }, + "link_local_ips": { "$ref": "#/definitions/list_of_strings" }, + "mac_address": { "type": "string" }, + "driver_opts": { + "type": "object", + "patternProperties": { + "^.+$": { "type": ["string", "number"] } + } + }, + "priority": { "type": "number" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + { "type": "null" } + ] + } + }, + "additionalProperties": false + } + ] + }, + "oom_kill_disable": { "type": ["boolean", "string"] }, + "oom_score_adj": { + "oneOf": [{ "type": "string" }, { "type": "integer", "minimum": -1000, "maximum": 1000 }] + }, + "pid": { "type": ["string", "null"] }, + "pids_limit": { "type": ["number", "string"] }, + "platform": { "type": "string" }, + "ports": { + "type": "array", + "items": { + "oneOf": [ + { "type": "number" }, + { "type": "string" }, + { + "type": "object", + "properties": { + "name": { "type": "string" }, + "mode": { "type": "string" }, + "host_ip": { "type": "string" }, + "target": { "type": ["integer", "string"] }, + "published": { "type": ["string", "integer"] }, + "protocol": { "type": "string" }, + "app_protocol": { "type": "string" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + ] + }, + "uniqueItems": true + }, + "privileged": { "type": ["boolean", "string"] }, + "profiles": { "$ref": "#/definitions/list_of_strings" }, + "pull_policy": { "type": "string", "enum": ["always", "never", "if_not_present", "build", "missing"] }, + "read_only": { "type": ["boolean", "string"] }, + "restart": { "type": "string" }, + "runtime": { + "type": "string" + }, + "scale": { + "type": ["integer", "string"] + }, + "security_opt": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, + "shm_size": { "type": ["number", "string"] }, + "secrets": { "$ref": "#/definitions/service_config_or_secret" }, + "sysctls": { "$ref": "#/definitions/list_or_dict" }, + "stdin_open": { "type": ["boolean", "string"] }, + "stop_grace_period": { "type": "string" }, + "stop_signal": { "type": "string" }, + "storage_opt": { "type": "object" }, + "tmpfs": { "$ref": "#/definitions/string_or_list" }, + "tty": { "type": ["boolean", "string"] }, + "ulimits": { "$ref": "#/definitions/ulimits" }, + "user": { "type": "string" }, + "uts": { "type": "string" }, + "userns_mode": { "type": "string" }, + "volumes": { + "type": "array", + "items": { + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "required": ["type"], + "properties": { + "type": { "type": "string" }, + "source": { "type": "string" }, + "target": { "type": "string" }, + "read_only": { "type": ["boolean", "string"] }, + "consistency": { "type": "string" }, + "bind": { + "type": "object", + "properties": { + "propagation": { "type": "string" }, + "create_host_path": { "type": ["boolean", "string"] }, + "selinux": { "type": "string", "enum": ["z", "Z"] } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "volume": { + "type": "object", + "properties": { + "nocopy": { "type": ["boolean", "string"] }, + "subpath": { "type": "string" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "tmpfs": { + "type": "object", + "properties": { + "size": { + "oneOf": [{ "type": "integer", "minimum": 0 }, { "type": "string" }] + }, + "mode": { "type": ["number", "string"] } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + ] + }, + "uniqueItems": true + }, + "volumes_from": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + }, + "working_dir": { "type": "string" } + }, + "patternProperties": { "^x-": {} }, + "additionalProperties": false + }, + + "healthcheck": { + "id": "#/definitions/healthcheck", + "type": "object", + "properties": { + "disable": { "type": ["boolean", "string"] }, + "interval": { "type": "string" }, + "retries": { "type": ["number", "string"] }, + "test": { + "oneOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] + }, + "timeout": { "type": "string" }, + "start_period": { "type": "string" }, + "start_interval": { "type": "string" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "development": { + "id": "#/definitions/development", + "type": ["object", "null"], + "properties": { + "watch": { + "type": "array", + "items": { + "type": "object", + "required": ["path", "action"], + "properties": { + "ignore": { "type": "array", "items": { "type": "string" } }, + "path": { "type": "string" }, + "action": { "type": "string", "enum": ["rebuild", "sync", "sync+restart"] }, + "target": { "type": "string" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "deployment": { + "id": "#/definitions/deployment", + "type": ["object", "null"], + "properties": { + "mode": { "type": "string" }, + "endpoint_mode": { "type": "string" }, + "replicas": { "type": ["integer", "string"] }, + "labels": { "$ref": "#/definitions/list_or_dict" }, + "rollback_config": { + "type": "object", + "properties": { + "parallelism": { "type": ["integer", "string"] }, + "delay": { "type": "string" }, + "failure_action": { "type": "string" }, + "monitor": { "type": "string" }, + "max_failure_ratio": { "type": ["number", "string"] }, + "order": { "type": "string", "enum": ["start-first", "stop-first"] } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "update_config": { + "type": "object", + "properties": { + "parallelism": { "type": ["integer", "string"] }, + "delay": { "type": "string" }, + "failure_action": { "type": "string" }, + "monitor": { "type": "string" }, + "max_failure_ratio": { "type": ["number", "string"] }, + "order": { "type": "string", "enum": ["start-first", "stop-first"] } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "resources": { + "type": "object", + "properties": { + "limits": { + "type": "object", + "properties": { + "cpus": { "type": ["number", "string"] }, + "memory": { "type": "string" }, + "pids": { "type": ["integer", "string"] } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "reservations": { + "type": "object", + "properties": { + "cpus": { "type": ["number", "string"] }, + "memory": { "type": "string" }, + "generic_resources": { "$ref": "#/definitions/generic_resources" }, + "devices": { "$ref": "#/definitions/devices" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "restart_policy": { + "type": "object", + "properties": { + "condition": { "type": "string" }, + "delay": { "type": "string" }, + "max_attempts": { "type": ["integer", "string"] }, + "window": { "type": "string" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "placement": { + "type": "object", + "properties": { + "constraints": { "type": "array", "items": { "type": "string" } }, + "preferences": { + "type": "array", + "items": { + "type": "object", + "properties": { + "spread": { "type": "string" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + }, + "max_replicas_per_node": { "type": ["integer", "string"] } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + + "generic_resources": { + "id": "#/definitions/generic_resources", + "type": "array", + "items": { + "type": "object", + "properties": { + "discrete_resource_spec": { + "type": "object", + "properties": { + "kind": { "type": "string" }, + "value": { "type": ["number", "string"] } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + }, + + "devices": { + "id": "#/definitions/devices", + "type": "array", + "items": { + "type": "object", + "properties": { + "capabilities": { "$ref": "#/definitions/list_of_strings" }, + "count": { "type": ["string", "integer"] }, + "device_ids": { "$ref": "#/definitions/list_of_strings" }, + "driver": { "type": "string" }, + "options": { "$ref": "#/definitions/list_or_dict" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + }, + + "include": { + "id": "#/definitions/include", + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "path": { "$ref": "#/definitions/string_or_list" }, + "env_file": { "$ref": "#/definitions/string_or_list" }, + "project_directory": { "type": "string" } + }, + "additionalProperties": false + } + ] + }, + + "network": { + "id": "#/definitions/network", + "type": ["object", "null"], + "properties": { + "name": { "type": "string" }, + "driver": { "type": "string" }, + "driver_opts": { + "type": "object", + "patternProperties": { + "^.+$": { "type": ["string", "number"] } + } + }, + "ipam": { + "type": "object", + "properties": { + "driver": { "type": "string" }, + "config": { + "type": "array", + "items": { + "type": "object", + "properties": { + "subnet": { "type": "string" }, + "ip_range": { "type": "string" }, + "gateway": { "type": "string" }, + "aux_addresses": { + "type": "object", + "additionalProperties": false, + "patternProperties": { "^.+$": { "type": "string" } } + } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + }, + "options": { + "type": "object", + "additionalProperties": false, + "patternProperties": { "^.+$": { "type": "string" } } + } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "external": { + "type": ["boolean", "string", "object"], + "properties": { + "name": { + "deprecated": true, + "type": "string" + } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "internal": { "type": ["boolean", "string"] }, + "enable_ipv6": { "type": ["boolean", "string"] }, + "attachable": { "type": ["boolean", "string"] }, + "labels": { "$ref": "#/definitions/list_or_dict" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + + "volume": { + "id": "#/definitions/volume", + "type": ["object", "null"], + "properties": { + "name": { "type": "string" }, + "driver": { "type": "string" }, + "driver_opts": { + "type": "object", + "patternProperties": { + "^.+$": { "type": ["string", "number"] } + } + }, + "external": { + "type": ["boolean", "string", "object"], + "properties": { + "name": { + "deprecated": true, + "type": "string" + } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + "labels": { "$ref": "#/definitions/list_or_dict" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + + "secret": { + "id": "#/definitions/secret", + "type": "object", + "properties": { + "name": { "type": "string" }, + "environment": { "type": "string" }, + "file": { "type": "string" }, + "external": { + "type": ["boolean", "string", "object"], + "properties": { + "name": { "type": "string" } + } + }, + "labels": { "$ref": "#/definitions/list_or_dict" }, + "driver": { "type": "string" }, + "driver_opts": { + "type": "object", + "patternProperties": { + "^.+$": { "type": ["string", "number"] } + } + }, + "template_driver": { "type": "string" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + + "config": { + "id": "#/definitions/config", + "type": "object", + "properties": { + "name": { "type": "string" }, + "content": { "type": "string" }, + "environment": { "type": "string" }, + "file": { "type": "string" }, + "external": { + "type": ["boolean", "string", "object"], + "properties": { + "name": { + "deprecated": true, + "type": "string" + } + } + }, + "labels": { "$ref": "#/definitions/list_or_dict" }, + "template_driver": { "type": "string" } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + }, + + "command": { + "oneOf": [{ "type": "null" }, { "type": "string" }, { "type": "array", "items": { "type": "string" } }] + }, + + "env_file": { + "oneOf": [ + { "type": "string" }, + { + "type": "array", + "items": { + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "required": { + "type": ["boolean", "string"], + "default": true + } + }, + "required": ["path"] + } + ] + } + } + ] + }, + + "string_or_list": { + "oneOf": [{ "type": "string" }, { "$ref": "#/definitions/list_of_strings" }] + }, + + "list_of_strings": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + }, + + "list_or_dict": { + "oneOf": [ + { + "type": "object", + "patternProperties": { + ".+": { + "type": ["string", "number", "boolean", "null"] + } + }, + "additionalProperties": false + }, + { "type": "array", "items": { "type": "string" }, "uniqueItems": true } + ] + }, + + "extra_hosts": { + "oneOf": [ + { + "type": "object", + "patternProperties": { + ".+": { + "type": ["string", "array"] + } + }, + "additionalProperties": false + }, + { "type": "array", "items": { "type": "string" }, "uniqueItems": true } + ] + }, + + "blkio_limit": { + "type": "object", + "properties": { + "path": { "type": "string" }, + "rate": { "type": ["integer", "string"] } + }, + "additionalProperties": false + }, + "blkio_weight": { + "type": "object", + "properties": { + "path": { "type": "string" }, + "weight": { "type": ["integer", "string"] } + }, + "additionalProperties": false + }, + "service_config_or_secret": { + "type": "array", + "items": { + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "source": { "type": "string" }, + "target": { "type": "string" }, + "uid": { "type": "string" }, + "gid": { "type": "string" }, + "mode": { "type": ["number", "string"] } + }, + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + ] + } + }, + "ulimits": { + "type": "object", + "patternProperties": { + "^[a-z]+$": { + "oneOf": [ + { "type": ["integer", "string"] }, + { + "type": "object", + "properties": { + "hard": { "type": ["integer", "string"] }, + "soft": { "type": ["integer", "string"] } + }, + "required": ["soft", "hard"], + "additionalProperties": false, + "patternProperties": { "^x-": {} } + } + ] + } + } + }, + "constraints": { + "service": { + "id": "#/definitions/constraints/service", + "anyOf": [{ "required": ["build"] }, { "required": ["image"] }], + "properties": { + "build": { + "required": ["context"] + } + } + } + } + } +} From 2d7951050f51c11a657dd660fd1c908a563366e5 Mon Sep 17 00:00:00 2001 From: Leo Conforti Date: Sun, 3 Nov 2024 20:57:49 +0000 Subject: [PATCH 2/6] initial implementation --- src/engines/DockerCompose.ts | 359 ++++++++++--- src/engines/compose-spec.json | 935 ---------------------------------- 2 files changed, 298 insertions(+), 996 deletions(-) delete mode 100644 src/engines/compose-spec.json diff --git a/src/engines/DockerCompose.ts b/src/engines/DockerCompose.ts index d42cccce..51f37ea5 100644 --- a/src/engines/DockerCompose.ts +++ b/src/engines/DockerCompose.ts @@ -4,18 +4,24 @@ * @since 1.0.0 */ -import * as FileSystem from "@effect/platform/FileSystem"; +import * as PlatformError from "@effect/platform/Error"; +import * as Socket from "@effect/platform/Socket"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; +import * as ParseResult from "effect/ParseResult"; +import * as Predicate from "effect/Predicate"; import * as Stream from "effect/Stream"; +import * as DindEngine from "./Dind.js"; +import * as DockerEngine from "./Docker.js"; +import { Function } from "effect"; import { Containers, ContainersError } from "../endpoints/Containers.js"; -import { Images, ImagesError } from "../endpoints/Images.js"; -import { Networks, NetworksError } from "../endpoints/Networks.js"; -import { Volumes, VolumesError } from "../endpoints/Volumes.js"; -import { JSONMessage } from "../generated/index.js"; -import { DockerLayer } from "./Docker.js"; +import { ExecsError } from "../endpoints/Execs.js"; +import { ImagesError } from "../endpoints/Images.js"; +import { SystemsError } from "../endpoints/System.js"; +import { VolumesError } from "../endpoints/Volumes.js"; +import { MobyConnectionOptions } from "../MobyConnection.js"; /** * @since 1.0.0 @@ -36,35 +42,39 @@ export type TypeId = typeof TypeId; export interface DockerCompose { readonly [TypeId]: TypeId; - readonly build: ( - project: unknown, + readonly build: ( + project: Stream.Stream, options: {} - ) => Effect.Effect< - Record>, - ImagesError, - FileSystem.FileSystem - >; - - readonly pull: ( - project: unknown, + ) => Effect.Effect; + + readonly pull: ( + project: Stream.Stream, options: {} - ) => Effect.Effect>, ImagesError, never>; + ) => Effect.Effect; - readonly up: ( - project: unknown, + readonly up: ( + project: Stream.Stream, options: {} - ) => Effect.Effect, ContainersError | VolumesError | NetworksError, never>; + ) => Effect.Effect; - readonly down: (project: unknown, options: {}) => Effect.Effect, ContainersError, never>; + readonly down: ( + project: Stream.Stream, + options: {} + ) => Effect.Effect; - readonly rm: ( - project: unknown, + readonly rm: ( + project: Stream.Stream, options: {} - ) => Effect.Effect, ContainersError | VolumesError | NetworksError, never>; + ) => Effect.Effect; - readonly kill: (project: unknown, options: {}) => Effect.Effect, ContainersError, never>; + readonly kill: ( + project: Stream.Stream, + options: {} + ) => Effect.Effect; - readonly forProject: (project: unknown) => DockerComposeProject; + readonly forProject: ( + project: Stream.Stream + ) => Effect.Effect; } /** @@ -75,21 +85,224 @@ export const DockerCompose: Context.Tag = Context. "@the-moby-effect/engines/DockerCompose" ); +/** + * @since 1.0.0 + * @category Errors + * @internal + */ +export const DockerComposeErrorTypeId: unique symbol = Symbol.for( + "@the-moby-effect/engines/DockerCompose/DockerComposeError" +) as DockerComposeErrorTypeId; + +/** + * @since 1.0.0 + * @category Errors + * @internal + */ +export type DockerComposeErrorTypeId = typeof DockerComposeErrorTypeId; + +/** + * @since 1.0.0 + * @category Errors + */ +export const isDockerComposeError = (u: unknown): u is DockerComposeError => + Predicate.hasProperty(u, DockerComposeErrorTypeId); + +/** + * @since 1.0.0 + * @category Errors + */ +export class DockerComposeError extends PlatformError.TypeIdError(DockerComposeErrorTypeId, "DockerComposeError")<{ + method: string; + cause: ParseResult.ParseError | Socket.SocketError | ExecsError | unknown; +}> { + get message() { + return `${this.method}`; + } +} + /** * @since 1.0.0 * @category Layers */ -export const layer: Layer.Layer> = Layer.effect( - DockerCompose, - Effect.gen(function* () { - const images = yield* Images; - const volumes = yield* Volumes; - const networks = yield* Networks; - const containers = yield* Containers; +export const makeLayer = + < + DockerConstructor extends ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + connectionOptions: any + ) => Layer.Layer, unknown, unknown>, + SupportedConnectionOptions extends MobyConnectionOptions = DockerConstructor extends ( + connectionOptions: infer C + ) => Layer.Layer, infer _E, infer _R> + ? C + : never, + DockerConstructorError = ReturnType extends Layer.Layer< + Layer.Layer.Success, + infer E, + infer _R + > + ? E + : never, + DockerConstructorContext = ReturnType extends Layer.Layer< + Layer.Layer.Success, + infer _E, + infer R + > + ? R + : never, + >( + dockerLayerConstructor: DockerConstructor + ) => + ( + connectionOptionsToHost: SupportedConnectionOptions + ): Layer.Layer< + DockerCompose, + ImagesError | SystemsError | VolumesError | ParseResult.ParseError | ContainersError | DockerConstructorError, + DockerConstructorContext + > => + Effect.gen(function* () { + // The generic type of the layer constructor is too wide + // since we want to be able to pass it as the only required generic + const dockerLayerConstructorCasted = dockerLayerConstructor as ( + connectionOptions: SupportedConnectionOptions + ) => Layer.Layer< + Layer.Layer.Success, + DockerConstructorError, + DockerConstructorContext + >; - return DockerCompose.of({}); - }) -); + // Building a layer here instead of providing it to the final effect + // prevents conflicting services with the same tag in the final layer + const hostDocker = yield* Layer.build(dockerLayerConstructorCasted(connectionOptionsToHost)); + yield* DockerEngine.pingHead().pipe(Effect.provide(hostDocker)); + + // Now spawn a dind + const dindDocker = yield* Layer.build( + DindEngine.makeDindLayerFromPlatformConstructor(dockerLayerConstructorCasted)({ + connectionOptionsToHost, + exposeDindContainerBy: "https" as const, + dindBaseImage: "docker.io/library/docker:dind-rootless" as const, + }) + ); + + // FIXME: need to get this somehow + const dindContainerId: string = ""; + + // Helper to upload the project to the dind + const uploadProject = (project: Stream.Stream): Effect.Effect => + Effect.gen(function* () { + const containers = yield* Containers; + containers.putArchive(dindContainerId, { path: "/", stream: project }); + }).pipe(Effect.provide(hostDocker)); + + // Helper to run a command in the dind + const runCommand = (_command: string, method: string): Effect.Effect => + Function.pipe( + DockerEngine.exec({ containerId: dindContainerId, command: ["echo", "Hello, World!"] }), + Effect.provide(dindDocker), + Effect.mapError((cause) => new DockerComposeError({ method, cause })) + ); + + // Actual compose implementation + return DockerCompose.of({ + [TypeId]: TypeId, + + build: ( + project: Stream.Stream, + _options: {} + ): Effect.Effect => + Function.pipe( + uploadProject(project), + Effect.flatMap(() => runCommand("docker compose --file docker-compose.yml build", "build")) + ), + + pull: ( + project: Stream.Stream, + _options: {} + ): Effect.Effect => + Function.pipe( + uploadProject(project), + Effect.flatMap(() => runCommand("docker compose --file docker-compose.yml pull", "pull")) + ), + + up: ( + project: Stream.Stream, + _options: {} + ): Effect.Effect => + Function.pipe( + uploadProject(project), + Effect.flatMap(() => runCommand("docker compose --file docker-compose.yml up", "up")) + ), + + down: ( + project: Stream.Stream, + _options: {} + ): Effect.Effect => + Function.pipe( + uploadProject(project), + Effect.flatMap(() => runCommand("docker compose --file docker-compose.yml down", "down")) + ), + + rm: ( + project: Stream.Stream, + _options: {} + ): Effect.Effect => + Function.pipe( + uploadProject(project), + Effect.flatMap(() => runCommand("docker compose --file docker-compose.yml rm", "rm")) + ), + + kill: ( + project: Stream.Stream, + _options: {} + ): Effect.Effect => + Function.pipe( + uploadProject(project), + Effect.flatMap(() => runCommand("docker compose --file docker-compose.yml kill", "kill")) + ), + + forProject: ( + project: Stream.Stream + ): Effect.Effect => + makeProjectLayer(project, uploadProject, runCommand), + }); + }).pipe(Layer.scoped(DockerCompose)); + +/** + * @since 1.0.0 + * @category Layers + */ +export const layerNodeJS = makeLayer(DockerEngine.layerNodeJS); + +/** + * @since 1.0.0 + * @category Layers + */ +export const layerBun = makeLayer(DockerEngine.layerBun); + +/** + * @since 1.0.0 + * @category Layers + */ +export const layerDeno = makeLayer(DockerEngine.layerDeno); + +/** + * @since 1.0.0 + * @category Layers + */ +export const layerUndici = makeLayer(DockerEngine.layerUndici); + +/** + * @since 1.0.0 + * @category Layers + */ +export const layerWeb = makeLayer(DockerEngine.layerWeb); + +/** + * @since 1.0.0 + * @category Layers + */ +export const layerAgnostic = makeLayer(DockerEngine.layerAgnostic); /** * @since 1.0.0 @@ -111,41 +324,65 @@ export type DockerComposeProjectTypeId = typeof DockerComposeProjectTypeId; */ export interface DockerComposeProject { readonly [DockerComposeProjectTypeId]: DockerComposeProjectTypeId; - - readonly build: (options: {}) => Effect.Effect< - Record>, - ImagesError, - never - >; - - readonly up: (options: {}) => Effect.Effect< - Record, - ContainersError | VolumesError | NetworksError, - never - >; - - readonly down: (options: {}) => Effect.Effect, ContainersError, never>; - - readonly rm: (options: {}) => Effect.Effect< - Record, - ContainersError | VolumesError | NetworksError, - void - >; + readonly build: (options: {}) => Effect.Effect; + readonly pull: (options: {}) => Effect.Effect; + readonly up: (options: {}) => Effect.Effect; + readonly down: (options: {}) => Effect.Effect; + readonly rm: (options: {}) => Effect.Effect; + readonly kill: (options: {}) => Effect.Effect; } +/** @internal */ +export const makeProjectLayer = ( + project: Stream.Stream, + uploadProject: (project: Stream.Stream) => Effect.Effect, + runCommand: (command: string, method: string) => Effect.Effect +): Effect.Effect => + Effect.gen(function* () { + yield* uploadProject(project); + + const build = (_options: {}): Effect.Effect => + runCommand("docker compose --file docker-compose.yml build", "build"); + + const pull = (_options: {}): Effect.Effect => + runCommand("docker compose --file docker-compose.yml pull", "pull"); + + const up = (_options: {}): Effect.Effect => + runCommand("docker compose --file docker-compose.yml up", "up"); + + const down = (_options: {}): Effect.Effect => + runCommand("docker compose --file docker-compose.yml down", "down"); + + const rm = (_options: {}): Effect.Effect => + runCommand("docker compose --file docker-compose.yml rm", "rm"); + + const kill = (_options: {}): Effect.Effect => + runCommand("docker compose --file docker-compose.yml kill", "kill"); + + return { + [DockerComposeProjectTypeId]: DockerComposeProjectTypeId, + build, + pull, + up, + down, + rm, + kill, + }; + }); + /** * @since 1.0.0 * @category Layers */ -export const layerProject: ( - project: string, +export const layerProject: ( + project: Stream.Stream, tagIdentifier: string ) => { readonly tag: Context.Tag; - readonly layer: Layer.Layer; -} = (project: unknown, tagIdentifier: string) => { + readonly layer: Layer.Layer; +} = (project: Stream.Stream, tagIdentifier: string) => { const tag = Context.GenericTag(tagIdentifier); - const effect = Effect.map(DockerCompose, ({ forProject }) => forProject(project)); + const effect = Effect.flatMap(DockerCompose, ({ forProject }) => forProject(project)); const layer = Layer.effect(tag, effect); return { tag, layer } as const; }; diff --git a/src/engines/compose-spec.json b/src/engines/compose-spec.json deleted file mode 100644 index 4a24dfd0..00000000 --- a/src/engines/compose-spec.json +++ /dev/null @@ -1,935 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema#", - - "id": "compose_spec.json", - "type": "object", - "title": "Compose Specification", - "description": "The Compose file is a YAML file defining a multi-containers based application.", - - "paths": {}, - - "properties": { - "version": { - "type": "string", - "description": "declared for backward compatibility, ignored." - }, - - "name": { - "type": "string", - "description": "define the Compose project name, until user defines one explicitly." - }, - - "include": { - "type": "array", - "items": { - "$ref": "#/definitions/include" - }, - "description": "compose sub-projects to be included." - }, - - "services": { - "id": "#/properties/services", - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9._-]+$": { - "$ref": "#/definitions/service" - } - }, - "additionalProperties": false - }, - - "networks": { - "id": "#/properties/networks", - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9._-]+$": { - "$ref": "#/definitions/network" - } - } - }, - - "volumes": { - "id": "#/properties/volumes", - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9._-]+$": { - "$ref": "#/definitions/volume" - } - }, - "additionalProperties": false - }, - - "secrets": { - "id": "#/properties/secrets", - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9._-]+$": { - "$ref": "#/definitions/secret" - } - }, - "additionalProperties": false - }, - - "configs": { - "id": "#/properties/configs", - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9._-]+$": { - "$ref": "#/definitions/config" - } - }, - "additionalProperties": false - } - }, - - "patternProperties": { "^x-": {} }, - "additionalProperties": false, - - "definitions": { - "service": { - "id": "#/definitions/service", - "type": "object", - - "properties": { - "develop": { "$ref": "#/definitions/development" }, - "deploy": { "$ref": "#/definitions/deployment" }, - "annotations": { "$ref": "#/definitions/list_or_dict" }, - "attach": { "type": ["boolean", "string"] }, - "build": { - "oneOf": [ - { "type": "string" }, - { - "type": "object", - "properties": { - "context": { "type": "string" }, - "dockerfile": { "type": "string" }, - "dockerfile_inline": { "type": "string" }, - "entitlements": { "type": "array", "items": { "type": "string" } }, - "args": { "$ref": "#/definitions/list_or_dict" }, - "ssh": { "$ref": "#/definitions/list_or_dict" }, - "labels": { "$ref": "#/definitions/list_or_dict" }, - "cache_from": { "type": "array", "items": { "type": "string" } }, - "cache_to": { "type": "array", "items": { "type": "string" } }, - "no_cache": { "type": ["boolean", "string"] }, - "additional_contexts": { "$ref": "#/definitions/list_or_dict" }, - "network": { "type": "string" }, - "pull": { "type": ["boolean", "string"] }, - "target": { "type": "string" }, - "shm_size": { "type": ["integer", "string"] }, - "extra_hosts": { "$ref": "#/definitions/extra_hosts" }, - "isolation": { "type": "string" }, - "privileged": { "type": ["boolean", "string"] }, - "secrets": { "$ref": "#/definitions/service_config_or_secret" }, - "tags": { "type": "array", "items": { "type": "string" } }, - "ulimits": { "$ref": "#/definitions/ulimits" }, - "platforms": { "type": "array", "items": { "type": "string" } } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - ] - }, - "blkio_config": { - "type": "object", - "properties": { - "device_read_bps": { - "type": "array", - "items": { "$ref": "#/definitions/blkio_limit" } - }, - "device_read_iops": { - "type": "array", - "items": { "$ref": "#/definitions/blkio_limit" } - }, - "device_write_bps": { - "type": "array", - "items": { "$ref": "#/definitions/blkio_limit" } - }, - "device_write_iops": { - "type": "array", - "items": { "$ref": "#/definitions/blkio_limit" } - }, - "weight": { "type": ["integer", "string"] }, - "weight_device": { - "type": "array", - "items": { "$ref": "#/definitions/blkio_weight" } - } - }, - "additionalProperties": false - }, - "cap_add": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, - "cap_drop": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, - "cgroup": { "type": "string", "enum": ["host", "private"] }, - "cgroup_parent": { "type": "string" }, - "command": { "$ref": "#/definitions/command" }, - "configs": { "$ref": "#/definitions/service_config_or_secret" }, - "container_name": { "type": "string" }, - "cpu_count": { "oneOf": [{ "type": "string" }, { "type": "integer", "minimum": 0 }] }, - "cpu_percent": { "oneOf": [{ "type": "string" }, { "type": "integer", "minimum": 0, "maximum": 100 }] }, - "cpu_shares": { "type": ["number", "string"] }, - "cpu_quota": { "type": ["number", "string"] }, - "cpu_period": { "type": ["number", "string"] }, - "cpu_rt_period": { "type": ["number", "string"] }, - "cpu_rt_runtime": { "type": ["number", "string"] }, - "cpus": { "type": ["number", "string"] }, - "cpuset": { "type": "string" }, - "credential_spec": { - "type": "object", - "properties": { - "config": { "type": "string" }, - "file": { "type": "string" }, - "registry": { "type": "string" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "depends_on": { - "oneOf": [ - { "$ref": "#/definitions/list_of_strings" }, - { - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9._-]+$": { - "type": "object", - "additionalProperties": false, - "patternProperties": { "^x-": {} }, - "properties": { - "restart": { "type": ["boolean", "string"] }, - "required": { - "type": "boolean", - "default": true - }, - "condition": { - "type": "string", - "enum": [ - "service_started", - "service_healthy", - "service_completed_successfully" - ] - } - }, - "required": ["condition"] - } - } - } - ] - }, - "device_cgroup_rules": { "$ref": "#/definitions/list_of_strings" }, - "devices": { - "type": "array", - "items": { - "oneOf": [ - { "type": "string" }, - { - "type": "object", - "required": ["source"], - "properties": { - "source": { "type": "string" }, - "target": { "type": "string" }, - "permissions": { "type": "string" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - ] - } - }, - "dns": { "$ref": "#/definitions/string_or_list" }, - "dns_opt": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, - "dns_search": { "$ref": "#/definitions/string_or_list" }, - "domainname": { "type": "string" }, - "entrypoint": { "$ref": "#/definitions/command" }, - "env_file": { "$ref": "#/definitions/env_file" }, - "environment": { "$ref": "#/definitions/list_or_dict" }, - - "expose": { - "type": "array", - "items": { - "type": ["string", "number"], - "format": "expose" - }, - "uniqueItems": true - }, - "extends": { - "oneOf": [ - { "type": "string" }, - { - "type": "object", - - "properties": { - "service": { "type": "string" }, - "file": { "type": "string" } - }, - "required": ["service"], - "additionalProperties": false - } - ] - }, - "external_links": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, - "extra_hosts": { "$ref": "#/definitions/extra_hosts" }, - "group_add": { - "type": "array", - "items": { - "type": ["string", "number"] - }, - "uniqueItems": true - }, - "healthcheck": { "$ref": "#/definitions/healthcheck" }, - "hostname": { "type": "string" }, - "image": { "type": "string" }, - "init": { "type": ["boolean", "string"] }, - "ipc": { "type": "string" }, - "isolation": { "type": "string" }, - "labels": { "$ref": "#/definitions/list_or_dict" }, - "links": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, - "logging": { - "type": "object", - - "properties": { - "driver": { "type": "string" }, - "options": { - "type": "object", - "patternProperties": { - "^.+$": { "type": ["string", "number", "null"] } - } - } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "mac_address": { "type": "string" }, - "mem_limit": { "type": ["number", "string"] }, - "mem_reservation": { "type": ["string", "integer"] }, - "mem_swappiness": { "type": ["integer", "string"] }, - "memswap_limit": { "type": ["number", "string"] }, - "network_mode": { "type": "string" }, - "networks": { - "oneOf": [ - { "$ref": "#/definitions/list_of_strings" }, - { - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9._-]+$": { - "oneOf": [ - { - "type": "object", - "properties": { - "aliases": { "$ref": "#/definitions/list_of_strings" }, - "ipv4_address": { "type": "string" }, - "ipv6_address": { "type": "string" }, - "link_local_ips": { "$ref": "#/definitions/list_of_strings" }, - "mac_address": { "type": "string" }, - "driver_opts": { - "type": "object", - "patternProperties": { - "^.+$": { "type": ["string", "number"] } - } - }, - "priority": { "type": "number" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - { "type": "null" } - ] - } - }, - "additionalProperties": false - } - ] - }, - "oom_kill_disable": { "type": ["boolean", "string"] }, - "oom_score_adj": { - "oneOf": [{ "type": "string" }, { "type": "integer", "minimum": -1000, "maximum": 1000 }] - }, - "pid": { "type": ["string", "null"] }, - "pids_limit": { "type": ["number", "string"] }, - "platform": { "type": "string" }, - "ports": { - "type": "array", - "items": { - "oneOf": [ - { "type": "number" }, - { "type": "string" }, - { - "type": "object", - "properties": { - "name": { "type": "string" }, - "mode": { "type": "string" }, - "host_ip": { "type": "string" }, - "target": { "type": ["integer", "string"] }, - "published": { "type": ["string", "integer"] }, - "protocol": { "type": "string" }, - "app_protocol": { "type": "string" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - ] - }, - "uniqueItems": true - }, - "privileged": { "type": ["boolean", "string"] }, - "profiles": { "$ref": "#/definitions/list_of_strings" }, - "pull_policy": { "type": "string", "enum": ["always", "never", "if_not_present", "build", "missing"] }, - "read_only": { "type": ["boolean", "string"] }, - "restart": { "type": "string" }, - "runtime": { - "type": "string" - }, - "scale": { - "type": ["integer", "string"] - }, - "security_opt": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, - "shm_size": { "type": ["number", "string"] }, - "secrets": { "$ref": "#/definitions/service_config_or_secret" }, - "sysctls": { "$ref": "#/definitions/list_or_dict" }, - "stdin_open": { "type": ["boolean", "string"] }, - "stop_grace_period": { "type": "string" }, - "stop_signal": { "type": "string" }, - "storage_opt": { "type": "object" }, - "tmpfs": { "$ref": "#/definitions/string_or_list" }, - "tty": { "type": ["boolean", "string"] }, - "ulimits": { "$ref": "#/definitions/ulimits" }, - "user": { "type": "string" }, - "uts": { "type": "string" }, - "userns_mode": { "type": "string" }, - "volumes": { - "type": "array", - "items": { - "oneOf": [ - { "type": "string" }, - { - "type": "object", - "required": ["type"], - "properties": { - "type": { "type": "string" }, - "source": { "type": "string" }, - "target": { "type": "string" }, - "read_only": { "type": ["boolean", "string"] }, - "consistency": { "type": "string" }, - "bind": { - "type": "object", - "properties": { - "propagation": { "type": "string" }, - "create_host_path": { "type": ["boolean", "string"] }, - "selinux": { "type": "string", "enum": ["z", "Z"] } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "volume": { - "type": "object", - "properties": { - "nocopy": { "type": ["boolean", "string"] }, - "subpath": { "type": "string" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "tmpfs": { - "type": "object", - "properties": { - "size": { - "oneOf": [{ "type": "integer", "minimum": 0 }, { "type": "string" }] - }, - "mode": { "type": ["number", "string"] } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - ] - }, - "uniqueItems": true - }, - "volumes_from": { - "type": "array", - "items": { "type": "string" }, - "uniqueItems": true - }, - "working_dir": { "type": "string" } - }, - "patternProperties": { "^x-": {} }, - "additionalProperties": false - }, - - "healthcheck": { - "id": "#/definitions/healthcheck", - "type": "object", - "properties": { - "disable": { "type": ["boolean", "string"] }, - "interval": { "type": "string" }, - "retries": { "type": ["number", "string"] }, - "test": { - "oneOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] - }, - "timeout": { "type": "string" }, - "start_period": { "type": "string" }, - "start_interval": { "type": "string" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "development": { - "id": "#/definitions/development", - "type": ["object", "null"], - "properties": { - "watch": { - "type": "array", - "items": { - "type": "object", - "required": ["path", "action"], - "properties": { - "ignore": { "type": "array", "items": { "type": "string" } }, - "path": { "type": "string" }, - "action": { "type": "string", "enum": ["rebuild", "sync", "sync+restart"] }, - "target": { "type": "string" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "deployment": { - "id": "#/definitions/deployment", - "type": ["object", "null"], - "properties": { - "mode": { "type": "string" }, - "endpoint_mode": { "type": "string" }, - "replicas": { "type": ["integer", "string"] }, - "labels": { "$ref": "#/definitions/list_or_dict" }, - "rollback_config": { - "type": "object", - "properties": { - "parallelism": { "type": ["integer", "string"] }, - "delay": { "type": "string" }, - "failure_action": { "type": "string" }, - "monitor": { "type": "string" }, - "max_failure_ratio": { "type": ["number", "string"] }, - "order": { "type": "string", "enum": ["start-first", "stop-first"] } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "update_config": { - "type": "object", - "properties": { - "parallelism": { "type": ["integer", "string"] }, - "delay": { "type": "string" }, - "failure_action": { "type": "string" }, - "monitor": { "type": "string" }, - "max_failure_ratio": { "type": ["number", "string"] }, - "order": { "type": "string", "enum": ["start-first", "stop-first"] } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "resources": { - "type": "object", - "properties": { - "limits": { - "type": "object", - "properties": { - "cpus": { "type": ["number", "string"] }, - "memory": { "type": "string" }, - "pids": { "type": ["integer", "string"] } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "reservations": { - "type": "object", - "properties": { - "cpus": { "type": ["number", "string"] }, - "memory": { "type": "string" }, - "generic_resources": { "$ref": "#/definitions/generic_resources" }, - "devices": { "$ref": "#/definitions/devices" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "restart_policy": { - "type": "object", - "properties": { - "condition": { "type": "string" }, - "delay": { "type": "string" }, - "max_attempts": { "type": ["integer", "string"] }, - "window": { "type": "string" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "placement": { - "type": "object", - "properties": { - "constraints": { "type": "array", "items": { "type": "string" } }, - "preferences": { - "type": "array", - "items": { - "type": "object", - "properties": { - "spread": { "type": "string" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - }, - "max_replicas_per_node": { "type": ["integer", "string"] } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - - "generic_resources": { - "id": "#/definitions/generic_resources", - "type": "array", - "items": { - "type": "object", - "properties": { - "discrete_resource_spec": { - "type": "object", - "properties": { - "kind": { "type": "string" }, - "value": { "type": ["number", "string"] } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - }, - - "devices": { - "id": "#/definitions/devices", - "type": "array", - "items": { - "type": "object", - "properties": { - "capabilities": { "$ref": "#/definitions/list_of_strings" }, - "count": { "type": ["string", "integer"] }, - "device_ids": { "$ref": "#/definitions/list_of_strings" }, - "driver": { "type": "string" }, - "options": { "$ref": "#/definitions/list_or_dict" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - }, - - "include": { - "id": "#/definitions/include", - "oneOf": [ - { "type": "string" }, - { - "type": "object", - "properties": { - "path": { "$ref": "#/definitions/string_or_list" }, - "env_file": { "$ref": "#/definitions/string_or_list" }, - "project_directory": { "type": "string" } - }, - "additionalProperties": false - } - ] - }, - - "network": { - "id": "#/definitions/network", - "type": ["object", "null"], - "properties": { - "name": { "type": "string" }, - "driver": { "type": "string" }, - "driver_opts": { - "type": "object", - "patternProperties": { - "^.+$": { "type": ["string", "number"] } - } - }, - "ipam": { - "type": "object", - "properties": { - "driver": { "type": "string" }, - "config": { - "type": "array", - "items": { - "type": "object", - "properties": { - "subnet": { "type": "string" }, - "ip_range": { "type": "string" }, - "gateway": { "type": "string" }, - "aux_addresses": { - "type": "object", - "additionalProperties": false, - "patternProperties": { "^.+$": { "type": "string" } } - } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - }, - "options": { - "type": "object", - "additionalProperties": false, - "patternProperties": { "^.+$": { "type": "string" } } - } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "external": { - "type": ["boolean", "string", "object"], - "properties": { - "name": { - "deprecated": true, - "type": "string" - } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "internal": { "type": ["boolean", "string"] }, - "enable_ipv6": { "type": ["boolean", "string"] }, - "attachable": { "type": ["boolean", "string"] }, - "labels": { "$ref": "#/definitions/list_or_dict" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - - "volume": { - "id": "#/definitions/volume", - "type": ["object", "null"], - "properties": { - "name": { "type": "string" }, - "driver": { "type": "string" }, - "driver_opts": { - "type": "object", - "patternProperties": { - "^.+$": { "type": ["string", "number"] } - } - }, - "external": { - "type": ["boolean", "string", "object"], - "properties": { - "name": { - "deprecated": true, - "type": "string" - } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - "labels": { "$ref": "#/definitions/list_or_dict" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - - "secret": { - "id": "#/definitions/secret", - "type": "object", - "properties": { - "name": { "type": "string" }, - "environment": { "type": "string" }, - "file": { "type": "string" }, - "external": { - "type": ["boolean", "string", "object"], - "properties": { - "name": { "type": "string" } - } - }, - "labels": { "$ref": "#/definitions/list_or_dict" }, - "driver": { "type": "string" }, - "driver_opts": { - "type": "object", - "patternProperties": { - "^.+$": { "type": ["string", "number"] } - } - }, - "template_driver": { "type": "string" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - - "config": { - "id": "#/definitions/config", - "type": "object", - "properties": { - "name": { "type": "string" }, - "content": { "type": "string" }, - "environment": { "type": "string" }, - "file": { "type": "string" }, - "external": { - "type": ["boolean", "string", "object"], - "properties": { - "name": { - "deprecated": true, - "type": "string" - } - } - }, - "labels": { "$ref": "#/definitions/list_or_dict" }, - "template_driver": { "type": "string" } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - }, - - "command": { - "oneOf": [{ "type": "null" }, { "type": "string" }, { "type": "array", "items": { "type": "string" } }] - }, - - "env_file": { - "oneOf": [ - { "type": "string" }, - { - "type": "array", - "items": { - "oneOf": [ - { "type": "string" }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "path": { - "type": "string" - }, - "required": { - "type": ["boolean", "string"], - "default": true - } - }, - "required": ["path"] - } - ] - } - } - ] - }, - - "string_or_list": { - "oneOf": [{ "type": "string" }, { "$ref": "#/definitions/list_of_strings" }] - }, - - "list_of_strings": { - "type": "array", - "items": { "type": "string" }, - "uniqueItems": true - }, - - "list_or_dict": { - "oneOf": [ - { - "type": "object", - "patternProperties": { - ".+": { - "type": ["string", "number", "boolean", "null"] - } - }, - "additionalProperties": false - }, - { "type": "array", "items": { "type": "string" }, "uniqueItems": true } - ] - }, - - "extra_hosts": { - "oneOf": [ - { - "type": "object", - "patternProperties": { - ".+": { - "type": ["string", "array"] - } - }, - "additionalProperties": false - }, - { "type": "array", "items": { "type": "string" }, "uniqueItems": true } - ] - }, - - "blkio_limit": { - "type": "object", - "properties": { - "path": { "type": "string" }, - "rate": { "type": ["integer", "string"] } - }, - "additionalProperties": false - }, - "blkio_weight": { - "type": "object", - "properties": { - "path": { "type": "string" }, - "weight": { "type": ["integer", "string"] } - }, - "additionalProperties": false - }, - "service_config_or_secret": { - "type": "array", - "items": { - "oneOf": [ - { "type": "string" }, - { - "type": "object", - "properties": { - "source": { "type": "string" }, - "target": { "type": "string" }, - "uid": { "type": "string" }, - "gid": { "type": "string" }, - "mode": { "type": ["number", "string"] } - }, - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - ] - } - }, - "ulimits": { - "type": "object", - "patternProperties": { - "^[a-z]+$": { - "oneOf": [ - { "type": ["integer", "string"] }, - { - "type": "object", - "properties": { - "hard": { "type": ["integer", "string"] }, - "soft": { "type": ["integer", "string"] } - }, - "required": ["soft", "hard"], - "additionalProperties": false, - "patternProperties": { "^x-": {} } - } - ] - } - } - }, - "constraints": { - "service": { - "id": "#/definitions/constraints/service", - "anyOf": [{ "required": ["build"] }, { "required": ["image"] }], - "properties": { - "build": { - "required": ["context"] - } - } - } - } - } -} From a34bbe0577384f9267fc128ae50da58ba988cacb Mon Sep 17 00:00:00 2001 From: Leo Conforti Date: Sun, 3 Nov 2024 20:57:56 +0000 Subject: [PATCH 3/6] docgen --- docs/modules/engines/Dind.ts.md | 26 +- docs/modules/engines/Docker.ts.md | 84 ++-- docs/modules/engines/DockerCompose.ts.md | 542 ++++++++++++++++++++++- docs/modules/engines/Moby.ts.md | 42 +- docs/modules/engines/Podman.ts.md | 20 +- 5 files changed, 599 insertions(+), 115 deletions(-) diff --git a/docs/modules/engines/Dind.ts.md b/docs/modules/engines/Dind.ts.md index 296e9090..33072feb 100644 --- a/docs/modules/engines/Dind.ts.md +++ b/docs/modules/engines/Dind.ts.md @@ -42,7 +42,7 @@ export declare const makeDindLayerFromPlatformConstructor: < PlatformLayerConstructor extends ( connectionOptions: any ) => Layer.Layer, unknown, unknown>, - SupportedConnectionOptions extends Platforms.MobyConnectionOptions = PlatformLayerConstructor extends ( + SupportedConnectionOptions extends MobyConnectionOptions = PlatformLayerConstructor extends ( connectionOptions: infer C ) => Layer.Layer, infer _E, infer _R> ? C @@ -66,7 +66,7 @@ export type MakeDindLayerFromPlatformConstructor< // eslint-disable-next-line @typescript-eslint/no-explicit-any connectionOptions: any ) => Layer.Layer, unknown, unknown>, - SupportedConnectionOptions extends Platforms.MobyConnectionOptions = PlatformLayerConstructor extends ( + SupportedConnectionOptions extends MobyConnectionOptions = PlatformLayerConstructor extends ( connectionOptions: infer C ) => Layer.Layer, infer _E, infer _R> ? C @@ -91,12 +91,12 @@ export type MakeDindLayerFromPlatformConstructor< >(options: { exposeDindContainerBy: ConnectionOptionsToDind connectionOptionsToHost: ConnectionOptionsToHost - dindBaseImage: BlobConstants.RecommendedDindBaseImages + dindBaseImage: RecommendedDindBaseImages }) => Layer.Layer< Layer.Layer.Success, - | Images.ImagesError - | System.SystemsError - | Volumes.VolumesError + | ImagesError + | SystemsError + | VolumesError | ParseResult.ParseError | ContainersError | PlatformLayerConstructorError @@ -115,7 +115,7 @@ Added in v1.0.0 ```ts export declare const layerAgnostic: MakeDindLayerFromPlatformConstructor< ( - connectionOptions: Platforms.HttpConnectionOptionsTagged | Platforms.HttpsConnectionOptionsTagged + connectionOptions: HttpConnectionOptionsTagged | HttpsConnectionOptionsTagged ) => DockerEngine.DockerLayerWithoutHttpClient, | { readonly _tag: "http"; readonly host: string; readonly port: number; readonly path?: string | undefined } | { @@ -141,7 +141,7 @@ Added in v1.0.0 ```ts export declare const layerBun: MakeDindLayerFromPlatformConstructor< - (connectionOptions: Platforms.MobyConnectionOptions) => DockerEngine.DockerLayer, + (connectionOptions: MobyConnectionOptions) => DockerEngine.DockerLayer, | { readonly _tag: "socket"; readonly socketPath: string } | { readonly _tag: "ssh" @@ -198,7 +198,7 @@ Added in v1.0.0 ```ts export declare const layerDeno: MakeDindLayerFromPlatformConstructor< - (connectionOptions: Platforms.MobyConnectionOptions) => DockerEngine.DockerLayer, + (connectionOptions: MobyConnectionOptions) => DockerEngine.DockerLayer, | { readonly _tag: "socket"; readonly socketPath: string } | { readonly _tag: "ssh" @@ -255,7 +255,7 @@ Added in v1.0.0 ```ts export declare const layerNodeJS: MakeDindLayerFromPlatformConstructor< - (connectionOptions: Platforms.MobyConnectionOptions) => DockerEngine.DockerLayer, + (connectionOptions: MobyConnectionOptions) => DockerEngine.DockerLayer, | { readonly _tag: "socket"; readonly socketPath: string } | { readonly _tag: "ssh" @@ -312,7 +312,7 @@ Added in v1.0.0 ```ts export declare const layerUndici: MakeDindLayerFromPlatformConstructor< - (connectionOptions: Platforms.MobyConnectionOptions) => DockerEngine.DockerLayer, + (connectionOptions: MobyConnectionOptions) => DockerEngine.DockerLayer, | { readonly _tag: "socket"; readonly socketPath: string } | { readonly _tag: "ssh" @@ -369,9 +369,7 @@ Added in v1.0.0 ```ts export declare const layerWeb: MakeDindLayerFromPlatformConstructor< - ( - connectionOptions: Platforms.HttpConnectionOptionsTagged | Platforms.HttpsConnectionOptionsTagged - ) => DockerEngine.DockerLayer, + (connectionOptions: HttpConnectionOptionsTagged | HttpsConnectionOptionsTagged) => DockerEngine.DockerLayer, | { readonly _tag: "http"; readonly host: string; readonly port: number; readonly path?: string | undefined } | { readonly _tag: "https" diff --git a/docs/modules/engines/Docker.ts.md b/docs/modules/engines/Docker.ts.md index ef16971a..1f93a668 100644 --- a/docs/modules/engines/Docker.ts.md +++ b/docs/modules/engines/Docker.ts.md @@ -69,7 +69,7 @@ export declare const build: ({ dockerfile?: string | undefined context: Stream.Stream buildArgs?: Record | undefined -}) => Stream.Stream +}) => Stream.Stream ``` Added in v1.0.0 @@ -97,11 +97,7 @@ export declare const buildScoped: ({ dockerfile?: string | undefined buildArgs?: Record | undefined context: Stream.Stream -}) => Effect.Effect< - Stream.Stream, - Images.ImagesError, - Scope.Scope | Images.Images -> +}) => Effect.Effect, ImagesError, Scope.Scope | Images> ``` Added in v1.0.0 @@ -119,7 +115,7 @@ export declare const exec: ({ }: { containerId: string command: Array -}) => Effect.Effect +}) => Effect.Effect ``` Added in v1.0.0 @@ -137,7 +133,7 @@ export declare const execNonBlocking: ({ }: { containerId: string command: Array -}) => Effect.Effect +}) => Effect.Effect ``` Added in v1.0.0 @@ -150,8 +146,8 @@ Implements the `docker images` command. ```ts export declare const images: ( - options?: Parameters[0] -) => Effect.Effect, Images.ImagesError, Images.Images> + options?: Parameters[0] +) => Effect.Effect, ImagesError, Images> ``` Added in v1.0.0 @@ -163,11 +159,7 @@ Implements the `docker info` command. **Signature** ```ts -export declare const info: () => Effect.Effect< - Readonly, - System.SystemsError, - System.Systems -> +export declare const info: () => Effect.Effect, SystemsError, Systems> ``` Added in v1.0.0 @@ -179,7 +171,7 @@ Implements the `docker ping` command. **Signature** ```ts -export declare const ping: () => Effect.Effect<"OK", System.SystemsError, System.Systems> +export declare const ping: () => Effect.Effect<"OK", SystemsError, Systems> ``` Added in v1.0.0 @@ -191,7 +183,7 @@ Implements the `docker ping` command. **Signature** ```ts -export declare const pingHead: () => Effect.Effect +export declare const pingHead: () => Effect.Effect ``` Added in v1.0.0 @@ -204,12 +196,8 @@ Implements the `docker ps` command. ```ts export declare const ps: ( - options?: Parameters[0] -) => Effect.Effect< - ReadonlyArray, - Containers.ContainersError, - Containers.Containers -> + options?: Parameters[0] +) => Effect.Effect, ContainersError, Containers> ``` Added in v1.0.0 @@ -230,7 +218,7 @@ export declare const pull: ({ image: string auth?: string | undefined platform?: string | undefined -}) => Stream.Stream +}) => Stream.Stream ``` Added in v1.0.0 @@ -252,11 +240,7 @@ export declare const pullScoped: ({ image: string auth?: string | undefined platform?: string | undefined -}) => Effect.Effect< - Stream.Stream, - never, - Images.Images | Scope.Scope -> +}) => Effect.Effect, never, Images | Scope.Scope> ``` Added in v1.0.0 @@ -268,9 +252,7 @@ Implements the `docker push` command. **Signature** ```ts -export declare const push: ( - options: Parameters[0] -) => Stream.Stream +export declare const push: (options: Parameters[0]) => Stream.Stream ``` Added in v1.0.0 @@ -283,8 +265,8 @@ Implements `docker run` command. ```ts export declare const run: ( - containerOptions: Parameters[0] -) => Effect.Effect + containerOptions: Parameters[0] +) => Effect.Effect ``` Added in v1.0.0 @@ -298,12 +280,8 @@ both the image and the container is removed. ```ts export declare const runScoped: ( - containerOptions: Parameters[0] -) => Effect.Effect< - GeneratedSchemas.ContainerInspectResponse, - Containers.ContainersError, - Scope.Scope | Containers.Containers -> + containerOptions: Parameters[0] +) => Effect.Effect ``` Added in v1.0.0 @@ -316,8 +294,8 @@ Implements the `docker search` command. ```ts export declare const search: ( - options: Parameters[0] -) => Effect.Effect, Images.ImagesError, Images.Images> + options: Parameters[0] +) => Effect.Effect, ImagesError, Images> ``` Added in v1.0.0 @@ -329,9 +307,7 @@ Implements the `docker stop` command. **Signature** ```ts -export declare const stop: ( - containerId: string -) => Effect.Effect +export declare const stop: (containerId: string) => Effect.Effect ``` Added in v1.0.0 @@ -343,11 +319,7 @@ Implements the `docker version` command. **Signature** ```ts -export declare const version: () => Effect.Effect< - Readonly, - System.SystemsError, - System.Systems -> +export declare const version: () => Effect.Effect, SystemsError, Systems> ``` Added in v1.0.0 @@ -380,7 +352,7 @@ Added in v1.0.0 ```ts export declare const layerAgnostic: ( - connectionOptions: Platforms.HttpConnectionOptionsTagged | Platforms.HttpsConnectionOptionsTagged + connectionOptions: HttpConnectionOptionsTagged | HttpsConnectionOptionsTagged ) => DockerLayerWithoutHttpClient ``` @@ -391,7 +363,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const layerBun: (connectionOptions: Platforms.MobyConnectionOptions) => DockerLayer +export declare const layerBun: (connectionOptions: MobyConnectionOptions) => DockerLayer ``` Added in v1.0.0 @@ -401,7 +373,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const layerDeno: (connectionOptions: Platforms.MobyConnectionOptions) => DockerLayer +export declare const layerDeno: (connectionOptions: MobyConnectionOptions) => DockerLayer ``` Added in v1.0.0 @@ -411,7 +383,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const layerNodeJS: (connectionOptions: Platforms.MobyConnectionOptions) => DockerLayer +export declare const layerNodeJS: (connectionOptions: MobyConnectionOptions) => DockerLayer ``` Added in v1.0.0 @@ -421,7 +393,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const layerUndici: (connectionOptions: Platforms.MobyConnectionOptions) => DockerLayer +export declare const layerUndici: (connectionOptions: MobyConnectionOptions) => DockerLayer ``` Added in v1.0.0 @@ -432,7 +404,7 @@ Added in v1.0.0 ```ts export declare const layerWeb: ( - connectionOptions: Platforms.HttpConnectionOptionsTagged | Platforms.HttpsConnectionOptionsTagged + connectionOptions: HttpConnectionOptionsTagged | HttpsConnectionOptionsTagged ) => DockerLayer ``` diff --git a/docs/modules/engines/DockerCompose.ts.md b/docs/modules/engines/DockerCompose.ts.md index 39b8d322..7e0a683a 100644 --- a/docs/modules/engines/DockerCompose.ts.md +++ b/docs/modules/engines/DockerCompose.ts.md @@ -6,7 +6,7 @@ parent: Modules ## DockerCompose overview -Docker compose engine +Docker compose engine. Added in v1.0.0 @@ -14,26 +14,546 @@ Added in v1.0.0

Table of contents

-- [utils](#utils) - - [DockerComposeImpl (interface)](#dockercomposeimpl-interface) +- [Errors](#errors) + - [DockerComposeError (class)](#dockercomposeerror-class) + - [isDockerComposeError](#isdockercomposeerror) +- [Layers](#layers) + - [layerAgnostic](#layeragnostic) + - [layerBun](#layerbun) + - [layerDeno](#layerdeno) + - [layerNodeJS](#layernodejs) + - [layerProject](#layerproject) + - [layerUndici](#layerundici) + - [layerWeb](#layerweb) + - [makeLayer](#makelayer) +- [Models](#models) + - [DockerCompose (interface)](#dockercompose-interface) + - [DockerComposeProject (interface)](#dockercomposeproject-interface) +- [Tags](#tags) + - [DockerCompose](#dockercompose) +- [Type id](#type-id) + - [DockerComposeProjectTypeId](#dockercomposeprojecttypeid) + - [DockerComposeProjectTypeId (type alias)](#dockercomposeprojecttypeid-type-alias) + - [TypeId](#typeid) + - [TypeId (type alias)](#typeid-type-alias) --- -# utils +# Errors -## DockerComposeImpl (interface) +## DockerComposeError (class) -Docker compose engine +**Signature** + +```ts +export declare class DockerComposeError +``` + +Added in v1.0.0 + +## isDockerComposeError + +**Signature** + +```ts +export declare const isDockerComposeError: (u: unknown) => u is DockerComposeError +``` + +Added in v1.0.0 + +# Layers + +## layerAgnostic + +**Signature** + +```ts +export declare const layerAgnostic: ( + connectionOptionsToHost: + | { readonly _tag: "http"; readonly host: string; readonly port: number; readonly path?: string | undefined } + | { + readonly _tag: "https" + readonly host: string + readonly port: number + readonly path?: string | undefined + readonly cert?: string | undefined + readonly ca?: string | undefined + readonly key?: string | undefined + readonly passphrase?: string | undefined + } +) => Layer.Layer< + DockerCompose, + ImagesError | SystemsError | VolumesError | ParseResult.ParseError | ContainersError | never, + HttpClient +> +``` + +Added in v1.0.0 + +## layerBun + +**Signature** + +```ts +export declare const layerBun: ( + connectionOptionsToHost: + | { readonly _tag: "socket"; readonly socketPath: string } + | { + readonly _tag: "ssh" + readonly remoteSocketPath: string + readonly host: string + readonly port?: number + readonly forceIPv4?: boolean + readonly forceIPv6?: boolean + readonly hostHash?: string + readonly hostVerifier?: HostVerifier | SyncHostVerifier | HostFingerprintVerifier | SyncHostFingerprintVerifier + readonly username?: string + readonly password?: string + readonly agent?: BaseAgent | string + readonly privateKey?: Buffer | string + readonly passphrase?: Buffer | string + readonly localHostname?: string + readonly localUsername?: string + readonly tryKeyboard?: boolean + readonly keepaliveInterval?: number + readonly keepaliveCountMax?: number + readonly readyTimeout?: number + readonly strictVendor?: boolean + readonly sock?: Readable + readonly agentForward?: boolean + readonly algorithms?: Algorithms + readonly debug?: DebugFunction + readonly authHandler?: AuthenticationType[] | AuthHandlerMiddleware | AuthMethod[] + readonly localAddress?: string + readonly localPort?: number + readonly timeout?: number + readonly ident?: Buffer | string + } + | { readonly _tag: "http"; readonly host: string; readonly port: number; readonly path?: string | undefined } + | { + readonly _tag: "https" + readonly host: string + readonly port: number + readonly path?: string | undefined + readonly cert?: string | undefined + readonly ca?: string | undefined + readonly key?: string | undefined + readonly passphrase?: string | undefined + } +) => Layer.Layer< + DockerCompose, + ImagesError | SystemsError | VolumesError | ParseResult.ParseError | ContainersError | never, + never +> +``` + +Added in v1.0.0 + +## layerDeno + +**Signature** + +```ts +export declare const layerDeno: ( + connectionOptionsToHost: + | { readonly _tag: "socket"; readonly socketPath: string } + | { + readonly _tag: "ssh" + readonly remoteSocketPath: string + readonly host: string + readonly port?: number + readonly forceIPv4?: boolean + readonly forceIPv6?: boolean + readonly hostHash?: string + readonly hostVerifier?: HostVerifier | SyncHostVerifier | HostFingerprintVerifier | SyncHostFingerprintVerifier + readonly username?: string + readonly password?: string + readonly agent?: BaseAgent | string + readonly privateKey?: Buffer | string + readonly passphrase?: Buffer | string + readonly localHostname?: string + readonly localUsername?: string + readonly tryKeyboard?: boolean + readonly keepaliveInterval?: number + readonly keepaliveCountMax?: number + readonly readyTimeout?: number + readonly strictVendor?: boolean + readonly sock?: Readable + readonly agentForward?: boolean + readonly algorithms?: Algorithms + readonly debug?: DebugFunction + readonly authHandler?: AuthenticationType[] | AuthHandlerMiddleware | AuthMethod[] + readonly localAddress?: string + readonly localPort?: number + readonly timeout?: number + readonly ident?: Buffer | string + } + | { readonly _tag: "http"; readonly host: string; readonly port: number; readonly path?: string | undefined } + | { + readonly _tag: "https" + readonly host: string + readonly port: number + readonly path?: string | undefined + readonly cert?: string | undefined + readonly ca?: string | undefined + readonly key?: string | undefined + readonly passphrase?: string | undefined + } +) => Layer.Layer< + DockerCompose, + ImagesError | SystemsError | VolumesError | ParseResult.ParseError | ContainersError | never, + never +> +``` + +Added in v1.0.0 + +## layerNodeJS + +**Signature** + +```ts +export declare const layerNodeJS: ( + connectionOptionsToHost: + | { readonly _tag: "socket"; readonly socketPath: string } + | { + readonly _tag: "ssh" + readonly remoteSocketPath: string + readonly host: string + readonly port?: number + readonly forceIPv4?: boolean + readonly forceIPv6?: boolean + readonly hostHash?: string + readonly hostVerifier?: HostVerifier | SyncHostVerifier | HostFingerprintVerifier | SyncHostFingerprintVerifier + readonly username?: string + readonly password?: string + readonly agent?: BaseAgent | string + readonly privateKey?: Buffer | string + readonly passphrase?: Buffer | string + readonly localHostname?: string + readonly localUsername?: string + readonly tryKeyboard?: boolean + readonly keepaliveInterval?: number + readonly keepaliveCountMax?: number + readonly readyTimeout?: number + readonly strictVendor?: boolean + readonly sock?: Readable + readonly agentForward?: boolean + readonly algorithms?: Algorithms + readonly debug?: DebugFunction + readonly authHandler?: AuthenticationType[] | AuthHandlerMiddleware | AuthMethod[] + readonly localAddress?: string + readonly localPort?: number + readonly timeout?: number + readonly ident?: Buffer | string + } + | { readonly _tag: "http"; readonly host: string; readonly port: number; readonly path?: string | undefined } + | { + readonly _tag: "https" + readonly host: string + readonly port: number + readonly path?: string | undefined + readonly cert?: string | undefined + readonly ca?: string | undefined + readonly key?: string | undefined + readonly passphrase?: string | undefined + } +) => Layer.Layer< + DockerCompose, + ImagesError | SystemsError | VolumesError | ParseResult.ParseError | ContainersError | never, + never +> +``` + +Added in v1.0.0 + +## layerProject + +**Signature** + +```ts +export declare const layerProject: ( + project: Stream.Stream, + tagIdentifier: string +) => { + readonly tag: Context.Tag + readonly layer: Layer.Layer +} +``` + +Added in v1.0.0 + +## layerUndici + +**Signature** + +```ts +export declare const layerUndici: ( + connectionOptionsToHost: + | { readonly _tag: "socket"; readonly socketPath: string } + | { + readonly _tag: "ssh" + readonly remoteSocketPath: string + readonly host: string + readonly port?: number + readonly forceIPv4?: boolean + readonly forceIPv6?: boolean + readonly hostHash?: string + readonly hostVerifier?: HostVerifier | SyncHostVerifier | HostFingerprintVerifier | SyncHostFingerprintVerifier + readonly username?: string + readonly password?: string + readonly agent?: BaseAgent | string + readonly privateKey?: Buffer | string + readonly passphrase?: Buffer | string + readonly localHostname?: string + readonly localUsername?: string + readonly tryKeyboard?: boolean + readonly keepaliveInterval?: number + readonly keepaliveCountMax?: number + readonly readyTimeout?: number + readonly strictVendor?: boolean + readonly sock?: Readable + readonly agentForward?: boolean + readonly algorithms?: Algorithms + readonly debug?: DebugFunction + readonly authHandler?: AuthenticationType[] | AuthHandlerMiddleware | AuthMethod[] + readonly localAddress?: string + readonly localPort?: number + readonly timeout?: number + readonly ident?: Buffer | string + } + | { readonly _tag: "http"; readonly host: string; readonly port: number; readonly path?: string | undefined } + | { + readonly _tag: "https" + readonly host: string + readonly port: number + readonly path?: string | undefined + readonly cert?: string | undefined + readonly ca?: string | undefined + readonly key?: string | undefined + readonly passphrase?: string | undefined + } +) => Layer.Layer< + DockerCompose, + ImagesError | SystemsError | VolumesError | ParseResult.ParseError | ContainersError | never, + never +> +``` + +Added in v1.0.0 + +## layerWeb + +**Signature** + +```ts +export declare const layerWeb: ( + connectionOptionsToHost: + | { readonly _tag: "http"; readonly host: string; readonly port: number; readonly path?: string | undefined } + | { + readonly _tag: "https" + readonly host: string + readonly port: number + readonly path?: string | undefined + readonly cert?: string | undefined + readonly ca?: string | undefined + readonly key?: string | undefined + readonly passphrase?: string | undefined + } +) => Layer.Layer< + DockerCompose, + ImagesError | SystemsError | VolumesError | ParseResult.ParseError | ContainersError | never, + never +> +``` + +Added in v1.0.0 + +## makeLayer + +**Signature** + +```ts +export declare const makeLayer: < + DockerConstructor extends ( + connectionOptions: any + ) => Layer.Layer, unknown, unknown>, + SupportedConnectionOptions extends MobyConnectionOptions = DockerConstructor extends ( + connectionOptions: infer C + ) => Layer.Layer, infer _E, infer _R> + ? C + : never, + DockerConstructorError = ReturnType extends Layer.Layer< + | Configs + | Containers + | Distributions + | Execs + | Images + | Networks + | Nodes + | Plugins + | Secrets + | Services + | Sessions + | Swarm + | Systems + | Tasks + | Volumes, + infer E, + infer _R + > + ? E + : never, + DockerConstructorContext = ReturnType extends Layer.Layer< + | Configs + | Containers + | Distributions + | Execs + | Images + | Networks + | Nodes + | Plugins + | Secrets + | Services + | Sessions + | Swarm + | Systems + | Tasks + | Volumes, + infer _E, + infer R + > + ? R + : never +>( + dockerLayerConstructor: DockerConstructor +) => ( + connectionOptionsToHost: SupportedConnectionOptions +) => Layer.Layer< + DockerCompose, + ImagesError | SystemsError | VolumesError | ParseResult.ParseError | ContainersError | DockerConstructorError, + DockerConstructorContext +> +``` + +Added in v1.0.0 + +# Models + +## DockerCompose (interface) **Signature** ```ts -export interface DockerComposeImpl { - up: () => void - down: () => void - pull: () => void - build: () => void +export interface DockerCompose { + readonly [TypeId]: TypeId + + readonly build: ( + project: Stream.Stream, + options: {} + ) => Effect.Effect + + readonly pull: ( + project: Stream.Stream, + options: {} + ) => Effect.Effect + + readonly up: ( + project: Stream.Stream, + options: {} + ) => Effect.Effect + + readonly down: ( + project: Stream.Stream, + options: {} + ) => Effect.Effect + + readonly rm: ( + project: Stream.Stream, + options: {} + ) => Effect.Effect + + readonly kill: ( + project: Stream.Stream, + options: {} + ) => Effect.Effect + + readonly forProject: ( + project: Stream.Stream + ) => Effect.Effect } ``` Added in v1.0.0 + +## DockerComposeProject (interface) + +**Signature** + +```ts +export interface DockerComposeProject { + readonly [DockerComposeProjectTypeId]: DockerComposeProjectTypeId + readonly build: (options: {}) => Effect.Effect + readonly pull: (options: {}) => Effect.Effect + readonly up: (options: {}) => Effect.Effect + readonly down: (options: {}) => Effect.Effect + readonly rm: (options: {}) => Effect.Effect + readonly kill: (options: {}) => Effect.Effect +} +``` + +Added in v1.0.0 + +# Tags + +## DockerCompose + +**Signature** + +```ts +export declare const DockerCompose: Context.Tag +``` + +Added in v1.0.0 + +# Type id + +## DockerComposeProjectTypeId + +**Signature** + +```ts +export declare const DockerComposeProjectTypeId: typeof DockerComposeProjectTypeId +``` + +Added in v1.0.0 + +## DockerComposeProjectTypeId (type alias) + +**Signature** + +```ts +export type DockerComposeProjectTypeId = typeof DockerComposeProjectTypeId +``` + +Added in v1.0.0 + +## TypeId + +**Signature** + +```ts +export declare const TypeId: typeof TypeId +``` + +Added in v1.0.0 + +## TypeId (type alias) + +**Signature** + +```ts +export type TypeId = typeof TypeId +``` + +Added in v1.0.0 diff --git a/docs/modules/engines/Moby.ts.md b/docs/modules/engines/Moby.ts.md index 95b3f204..fae38f7b 100644 --- a/docs/modules/engines/Moby.ts.md +++ b/docs/modules/engines/Moby.ts.md @@ -49,21 +49,21 @@ Added in v1.0.0 ```ts export type MobyLayerWithoutHttpClient = Layer.Layer< - | Configs.Configs - | Containers.Containers - | Distributions.Distributions - | Execs.Execs - | Images.Images - | Networks.Networks - | Nodes.Nodes - | Plugins.Plugins - | Secrets.Secrets - | Services.Services - | Sessions.Sessions - | Swarm.Swarm - | System.Systems - | Tasks.Tasks - | Volumes.Volumes, + | Configs + | Containers + | Distributions + | Execs + | Images + | Networks + | Nodes + | Plugins + | Secrets + | Services + | Sessions + | Swarm + | Systems + | Tasks + | Volumes, never, HttpClient.HttpClient > @@ -77,7 +77,7 @@ Added in v1.0.0 ```ts export declare const layerAgnostic: ( - connectionOptions: Connection.HttpConnectionOptionsTagged | Connection.HttpsConnectionOptionsTagged + connectionOptions: HttpConnectionOptionsTagged | HttpsConnectionOptionsTagged ) => MobyLayerWithoutHttpClient ``` @@ -88,7 +88,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const layerBun: (connectionOptions: Connection.MobyConnectionOptions) => MobyLayer +export declare const layerBun: (connectionOptions: MobyConnectionOptions) => MobyLayer ``` Added in v1.0.0 @@ -98,7 +98,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const layerDeno: (connectionOptions: Connection.MobyConnectionOptions) => MobyLayer +export declare const layerDeno: (connectionOptions: MobyConnectionOptions) => MobyLayer ``` Added in v1.0.0 @@ -108,7 +108,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const layerNodeJS: (connectionOptions: Connection.MobyConnectionOptions) => MobyLayer +export declare const layerNodeJS: (connectionOptions: MobyConnectionOptions) => MobyLayer ``` Added in v1.0.0 @@ -118,7 +118,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const layerUndici: (connectionOptions: Connection.MobyConnectionOptions) => MobyLayer +export declare const layerUndici: (connectionOptions: MobyConnectionOptions) => MobyLayer ``` Added in v1.0.0 @@ -129,7 +129,7 @@ Added in v1.0.0 ```ts export declare const layerWeb: ( - connectionOptions: Connection.HttpConnectionOptionsTagged | Connection.HttpsConnectionOptionsTagged + connectionOptions: HttpConnectionOptionsTagged | HttpsConnectionOptionsTagged ) => MobyLayer ``` diff --git a/docs/modules/engines/Podman.ts.md b/docs/modules/engines/Podman.ts.md index 3d782e49..39eb0389 100644 --- a/docs/modules/engines/Podman.ts.md +++ b/docs/modules/engines/Podman.ts.md @@ -35,13 +35,7 @@ Added in v1.0.0 ```ts export type PodmanLayer = Layer.Layer< - | Containers.Containers - | Execs.Execs - | Images.Images - | Networks.Networks - | Secrets.Secrets - | System.Systems - | Volumes.Volumes, + Containers | Execs | Images | Networks | Secrets | Systems | Volumes, never, never > @@ -69,7 +63,7 @@ Added in v1.0.0 ```ts export declare const layerAgnostic: ( - connectionOptions: Platforms.HttpConnectionOptionsTagged | Platforms.HttpsConnectionOptionsTagged + connectionOptions: HttpConnectionOptionsTagged | HttpsConnectionOptionsTagged ) => PodmanLayerWithoutHttpCLient ``` @@ -80,7 +74,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const layerBun: (connectionOptions: Platforms.MobyConnectionOptions) => PodmanLayer +export declare const layerBun: (connectionOptions: MobyConnectionOptions) => PodmanLayer ``` Added in v1.0.0 @@ -90,7 +84,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const layerDeno: (connectionOptions: Platforms.MobyConnectionOptions) => PodmanLayer +export declare const layerDeno: (connectionOptions: MobyConnectionOptions) => PodmanLayer ``` Added in v1.0.0 @@ -100,7 +94,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const layerNodeJS: (connectionOptions: Platforms.MobyConnectionOptions) => PodmanLayer +export declare const layerNodeJS: (connectionOptions: MobyConnectionOptions) => PodmanLayer ``` Added in v1.0.0 @@ -110,7 +104,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const layerUndici: (connectionOptions: Platforms.MobyConnectionOptions) => PodmanLayer +export declare const layerUndici: (connectionOptions: MobyConnectionOptions) => PodmanLayer ``` Added in v1.0.0 @@ -121,7 +115,7 @@ Added in v1.0.0 ```ts export declare const layerWeb: ( - connectionOptions: Platforms.HttpConnectionOptionsTagged | Platforms.HttpsConnectionOptionsTagged + connectionOptions: HttpConnectionOptionsTagged | HttpsConnectionOptionsTagged ) => PodmanLayer ``` From 3dbc438cb9c64f28f5c1cc8c3c13ef9cd900eedc Mon Sep 17 00:00:00 2001 From: Leo Conforti Date: Sun, 3 Nov 2024 21:24:28 +0000 Subject: [PATCH 4/6] add docker compose example --- examples/docker-compose.ts | 49 ++++++++++++++++++++++++++++++++++++ examples/docker-compose.yml | 21 ++++++++++++++++ src/DockerComposeEngine.ts | 8 ++++++ src/engines/DockerCompose.ts | 4 +-- src/index.ts | 7 ++++++ 5 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 examples/docker-compose.ts create mode 100644 examples/docker-compose.yml create mode 100644 src/DockerComposeEngine.ts diff --git a/examples/docker-compose.ts b/examples/docker-compose.ts new file mode 100644 index 00000000..d439d132 --- /dev/null +++ b/examples/docker-compose.ts @@ -0,0 +1,49 @@ +// Run with: npx tsx examples/docker-compose.ts + +import { Path, Error as PlatformError } from "@effect/platform"; +import { NodeContext, NodeRuntime } from "@effect/platform-node"; +import { Array, ConfigError, Effect, Function, Layer, ParseResult, Stream } from "effect"; +import { DockerComposeEngine, MobyConnection, MobyConvey, MobyEndpoints } from "the-moby-effect"; + +const localDockerCompose = Function.pipe( + MobyConnection.connectionOptionsFromPlatformSystemSocketDefault, + Effect.map(DockerComposeEngine.layerNodeJS), + Layer.unwrapEffect +); + +const project = Effect.Do.pipe( + Effect.bind("cwd", () => Effect.flatMap(Path.Path, (path) => path.fromFileUrl(new URL(import.meta.url)))), + Effect.bind("contents", () => Effect.succeed(Array.make("docker-compose.yml"))), + Effect.map(({ contents, cwd }) => MobyConvey.packBuildContextIntoTarballStream(cwd, contents)), + Stream.unwrap, + Stream.provideLayer(NodeContext.layer) +); + +const { layer: composeForProjectLayer, tag: composeForProjectTag } = DockerComposeEngine.layerProject( + project, + "MyDockerComposeProject" +); + +const dockerComposeProjectLive: Layer.Layer< + DockerComposeEngine.DockerComposeProject, + | ParseResult.ParseError + | ConfigError.ConfigError + | PlatformError.PlatformError + | MobyEndpoints.ImagesError + | MobyEndpoints.SystemsError + | MobyEndpoints.VolumesError + | MobyEndpoints.ContainersError, + never +> = Layer.provide(composeForProjectLayer, localDockerCompose); + +const program = Effect.gen(function* () { + const compose = yield* composeForProjectTag; + + yield* compose.pull({}); + yield* compose.up({}); + yield* Effect.sleep("10 seconds"); + yield* compose.down({}); + yield* compose.rm({}); +}); + +program.pipe(Effect.provide(dockerComposeProjectLive)).pipe(NodeRuntime.runMain); diff --git a/examples/docker-compose.yml b/examples/docker-compose.yml new file mode 100644 index 00000000..5be90b9c --- /dev/null +++ b/examples/docker-compose.yml @@ -0,0 +1,21 @@ +services: + postgres: + container_name: postgres + image: postgres:latest + environment: + - POSTGRES_USER=yourUser + - POSTGRES_PASSWORD=changeit + - POSTGRES_DB=postgres + ports: + - "5432:5432" + restart: always + + pgadmin: + container_name: pgadmin + image: dpage/pgadmin4:latest + environment: + - PGADMIN_DEFAULT_EMAIL=your@email.com + - PGADMIN_DEFAULT_PASSWORD=changeit + ports: + - "5050:80" + restart: always diff --git a/src/DockerComposeEngine.ts b/src/DockerComposeEngine.ts new file mode 100644 index 00000000..97d33418 --- /dev/null +++ b/src/DockerComposeEngine.ts @@ -0,0 +1,8 @@ +/** + * Docker compose engine shortcut. + * + * @since 1.0.0 + */ + +/** @since 1.0.0 */ +export * from "./engines/DockerCompose.js"; diff --git a/src/engines/DockerCompose.ts b/src/engines/DockerCompose.ts index 51f37ea5..f29d219f 100644 --- a/src/engines/DockerCompose.ts +++ b/src/engines/DockerCompose.ts @@ -192,7 +192,7 @@ export const makeLayer = const uploadProject = (project: Stream.Stream): Effect.Effect => Effect.gen(function* () { const containers = yield* Containers; - containers.putArchive(dindContainerId, { path: "/", stream: project }); + containers.putArchive(dindContainerId, { path: "/tmp", stream: project }); }).pipe(Effect.provide(hostDocker)); // Helper to run a command in the dind @@ -328,7 +328,7 @@ export interface DockerComposeProject { readonly pull: (options: {}) => Effect.Effect; readonly up: (options: {}) => Effect.Effect; readonly down: (options: {}) => Effect.Effect; - readonly rm: (options: {}) => Effect.Effect; + readonly rm: (options: {}) => Effect.Effect; readonly kill: (options: {}) => Effect.Effect; } diff --git a/src/index.ts b/src/index.ts index d4c9b3b4..637f617b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,13 @@ */ export * as DindEngine from "./DindEngine.js" +/** + * Docker compose engine shortcut. + * + * @since 1.0.0 + */ +export * as DockerComposeEngine from "./DockerComposeEngine.js" + /** * Docker engine shortcut. * From ae489e5625a68f6bff60e0d433653873d246d0ff Mon Sep 17 00:00:00 2001 From: Leo Conforti Date: Mon, 4 Nov 2024 01:56:34 +0000 Subject: [PATCH 5/6] working --- examples/docker-compose.ts | 17 +- src/engines/DockerCompose.ts | 323 +++++++++++++++++------------------ 2 files changed, 164 insertions(+), 176 deletions(-) diff --git a/examples/docker-compose.ts b/examples/docker-compose.ts index d439d132..1244cc22 100644 --- a/examples/docker-compose.ts +++ b/examples/docker-compose.ts @@ -12,7 +12,7 @@ const localDockerCompose = Function.pipe( ); const project = Effect.Do.pipe( - Effect.bind("cwd", () => Effect.flatMap(Path.Path, (path) => path.fromFileUrl(new URL(import.meta.url)))), + Effect.bind("cwd", () => Effect.flatMap(Path.Path, (path) => path.fromFileUrl(new URL(".", import.meta.url)))), Effect.bind("contents", () => Effect.succeed(Array.make("docker-compose.yml"))), Effect.map(({ contents, cwd }) => MobyConvey.packBuildContextIntoTarballStream(cwd, contents)), Stream.unwrap, @@ -26,13 +26,12 @@ const { layer: composeForProjectLayer, tag: composeForProjectTag } = DockerCompo const dockerComposeProjectLive: Layer.Layer< DockerComposeEngine.DockerComposeProject, - | ParseResult.ParseError | ConfigError.ConfigError - | PlatformError.PlatformError - | MobyEndpoints.ImagesError | MobyEndpoints.SystemsError - | MobyEndpoints.VolumesError - | MobyEndpoints.ContainersError, + | PlatformError.PlatformError + | ParseResult.ParseError + | MobyEndpoints.ContainersError + | DockerComposeEngine.DockerComposeError, never > = Layer.provide(composeForProjectLayer, localDockerCompose); @@ -41,9 +40,9 @@ const program = Effect.gen(function* () { yield* compose.pull({}); yield* compose.up({}); - yield* Effect.sleep("10 seconds"); - yield* compose.down({}); - yield* compose.rm({}); + // yield* Effect.sleep("10 seconds"); + // yield* compose.down({}); + // yield* compose.rm({}); }); program.pipe(Effect.provide(dockerComposeProjectLive)).pipe(NodeRuntime.runMain); diff --git a/src/engines/DockerCompose.ts b/src/engines/DockerCompose.ts index f29d219f..f228c8fa 100644 --- a/src/engines/DockerCompose.ts +++ b/src/engines/DockerCompose.ts @@ -1,27 +1,28 @@ /** - * Docker compose engine. + * Compose engine. * * @since 1.0.0 */ import * as PlatformError from "@effect/platform/Error"; +import * as HttpClient from "@effect/platform/HttpClient"; +import * as HttpClientError from "@effect/platform/HttpClientError"; import * as Socket from "@effect/platform/Socket"; +import * as Console from "effect/Console"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; +import * as Function from "effect/Function"; import * as Layer from "effect/Layer"; import * as ParseResult from "effect/ParseResult"; import * as Predicate from "effect/Predicate"; +import * as Scope from "effect/Scope"; import * as Stream from "effect/Stream"; -import * as DindEngine from "./Dind.js"; import * as DockerEngine from "./Docker.js"; -import { Function } from "effect"; import { Containers, ContainersError } from "../endpoints/Containers.js"; -import { ExecsError } from "../endpoints/Execs.js"; -import { ImagesError } from "../endpoints/Images.js"; -import { SystemsError } from "../endpoints/System.js"; -import { VolumesError } from "../endpoints/Volumes.js"; -import { MobyConnectionOptions } from "../MobyConnection.js"; +import { Execs, ExecsError } from "../endpoints/Execs.js"; +import { Systems, SystemsError } from "../endpoints/System.js"; +import { HttpConnectionOptionsTagged, HttpsConnectionOptionsTagged, MobyConnectionOptions } from "../MobyConnection.js"; /** * @since 1.0.0 @@ -74,7 +75,7 @@ export interface DockerCompose { readonly forProject: ( project: Stream.Stream - ) => Effect.Effect; + ) => Effect.Effect; } /** @@ -121,188 +122,175 @@ export class DockerComposeError extends PlatformError.TypeIdError(DockerComposeE } } -/** - * @since 1.0.0 - * @category Layers - */ -export const makeLayer = - < - DockerConstructor extends ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - connectionOptions: any - ) => Layer.Layer, unknown, unknown>, - SupportedConnectionOptions extends MobyConnectionOptions = DockerConstructor extends ( - connectionOptions: infer C - ) => Layer.Layer, infer _E, infer _R> - ? C - : never, - DockerConstructorError = ReturnType extends Layer.Layer< - Layer.Layer.Success, - infer E, - infer _R - > - ? E - : never, - DockerConstructorContext = ReturnType extends Layer.Layer< - Layer.Layer.Success, - infer _E, - infer R - > - ? R - : never, - >( - dockerLayerConstructor: DockerConstructor - ) => - ( - connectionOptionsToHost: SupportedConnectionOptions - ): Layer.Layer< - DockerCompose, - ImagesError | SystemsError | VolumesError | ParseResult.ParseError | ContainersError | DockerConstructorError, - DockerConstructorContext - > => - Effect.gen(function* () { - // The generic type of the layer constructor is too wide - // since we want to be able to pass it as the only required generic - const dockerLayerConstructorCasted = dockerLayerConstructor as ( - connectionOptions: SupportedConnectionOptions - ) => Layer.Layer< - Layer.Layer.Success, - DockerConstructorError, - DockerConstructorContext - >; - - // Building a layer here instead of providing it to the final effect - // prevents conflicting services with the same tag in the final layer - const hostDocker = yield* Layer.build(dockerLayerConstructorCasted(connectionOptionsToHost)); - yield* DockerEngine.pingHead().pipe(Effect.provide(hostDocker)); - - // Now spawn a dind - const dindDocker = yield* Layer.build( - DindEngine.makeDindLayerFromPlatformConstructor(dockerLayerConstructorCasted)({ - connectionOptionsToHost, - exposeDindContainerBy: "https" as const, - dindBaseImage: "docker.io/library/docker:dind-rootless" as const, - }) +const make: Effect.Effect = + Effect.gen(function* () { + const execs = yield* Execs; + const containers = yield* Containers; + yield* DockerEngine.pingHead(); + + const dindContainerId = yield* DockerEngine.runScoped({ + spec: { + Image: "docker.io/library/docker:latest", + Cmd: ["sleep", "infinity"], + 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: Stream.Stream + ): Effect.Effect => + Effect.catchTag( + containers.putArchive(dindContainerId, { path: "/", stream: project }), + "ContainersError", + (cause) => new DockerComposeError({ method: "uploadProject", cause }) ); - // FIXME: need to get this somehow - const dindContainerId: string = ""; + // Helper to run a command in the dind + const runCommand = (command: string, method: string): Effect.Effect => + Function.pipe( + DockerEngine.exec({ containerId: dindContainerId, command: command.split(" ") }), + Effect.tap(Console.log), + Effect.mapError((cause) => new DockerComposeError({ method, cause })), + Effect.provide(Context.make(Execs, execs)) + ); - // Helper to upload the project to the dind - const uploadProject = (project: Stream.Stream): Effect.Effect => - Effect.gen(function* () { - const containers = yield* Containers; - containers.putArchive(dindContainerId, { path: "/tmp", stream: project }); - }).pipe(Effect.provide(hostDocker)); + // Actual compose implementation + return DockerCompose.of({ + [TypeId]: TypeId, - // Helper to run a command in the dind - const runCommand = (_command: string, method: string): Effect.Effect => + build: ( + project: Stream.Stream, + _options: {} + ): Effect.Effect => + Function.pipe( + uploadProject(project), + Effect.flatMap(() => + runCommand("docker compose --project-name test --file docker-compose.yml build", "build") + ) + ), + + pull: ( + project: Stream.Stream, + _options: {} + ): Effect.Effect => Function.pipe( - DockerEngine.exec({ containerId: dindContainerId, command: ["echo", "Hello, World!"] }), - Effect.provide(dindDocker), - Effect.mapError((cause) => new DockerComposeError({ method, cause })) - ); - - // Actual compose implementation - return DockerCompose.of({ - [TypeId]: TypeId, - - build: ( - project: Stream.Stream, - _options: {} - ): Effect.Effect => - Function.pipe( - uploadProject(project), - Effect.flatMap(() => runCommand("docker compose --file docker-compose.yml build", "build")) - ), - - pull: ( - project: Stream.Stream, - _options: {} - ): Effect.Effect => - Function.pipe( - uploadProject(project), - Effect.flatMap(() => runCommand("docker compose --file docker-compose.yml pull", "pull")) - ), - - up: ( - project: Stream.Stream, - _options: {} - ): Effect.Effect => - Function.pipe( - uploadProject(project), - Effect.flatMap(() => runCommand("docker compose --file docker-compose.yml up", "up")) - ), - - down: ( - project: Stream.Stream, - _options: {} - ): Effect.Effect => - Function.pipe( - uploadProject(project), - Effect.flatMap(() => runCommand("docker compose --file docker-compose.yml down", "down")) - ), - - rm: ( - project: Stream.Stream, - _options: {} - ): Effect.Effect => - Function.pipe( - uploadProject(project), - Effect.flatMap(() => runCommand("docker compose --file docker-compose.yml rm", "rm")) - ), - - kill: ( - project: Stream.Stream, - _options: {} - ): Effect.Effect => - Function.pipe( - uploadProject(project), - Effect.flatMap(() => runCommand("docker compose --file docker-compose.yml kill", "kill")) - ), - - forProject: ( - project: Stream.Stream - ): Effect.Effect => - makeProjectLayer(project, uploadProject, runCommand), - }); - }).pipe(Layer.scoped(DockerCompose)); + uploadProject(project), + Effect.flatMap(() => + runCommand("docker compose --project-name test --file docker-compose.yml pull", "pull") + ) + ), + + up: ( + project: Stream.Stream, + _options: {} + ): Effect.Effect => + Function.pipe( + uploadProject(project), + Effect.flatMap(() => + runCommand("docker compose --project-name test --file docker-compose.yml up", "up") + ) + ), + + down: ( + project: Stream.Stream, + _options: {} + ): Effect.Effect => + Function.pipe( + uploadProject(project), + Effect.flatMap(() => + runCommand("docker compose --project-name test --file docker-compose.yml down", "down") + ) + ), + + rm: ( + project: Stream.Stream, + _options: {} + ): Effect.Effect => + Function.pipe( + uploadProject(project), + Effect.flatMap(() => + runCommand("docker compose --project-name test --file docker-compose.yml rm", "rm") + ) + ), + + kill: ( + project: Stream.Stream, + _options: {} + ): Effect.Effect => + Function.pipe( + uploadProject(project), + Effect.flatMap(() => + runCommand("docker compose --project-name test --file docker-compose.yml kill", "kill") + ) + ), + + forProject: ( + project: Stream.Stream + ): Effect.Effect => + makeProjectLayer(project, uploadProject, runCommand), + }); + }); /** * @since 1.0.0 * @category Layers */ -export const layerNodeJS = makeLayer(DockerEngine.layerNodeJS); +export const layerNodeJS = ( + connectionOptions: MobyConnectionOptions +): Layer.Layer => + Layer.provide(Layer.scoped(DockerCompose, make), DockerEngine.layerNodeJS(connectionOptions)); /** * @since 1.0.0 * @category Layers */ -export const layerBun = makeLayer(DockerEngine.layerBun); +export const layerBun = ( + connectionOptions: MobyConnectionOptions +): Layer.Layer => + Layer.provide(Layer.scoped(DockerCompose, make), DockerEngine.layerBun(connectionOptions)); /** * @since 1.0.0 * @category Layers */ -export const layerDeno = makeLayer(DockerEngine.layerDeno); +export const layerDeno = ( + connectionOptions: MobyConnectionOptions +): Layer.Layer => + Layer.provide(Layer.scoped(DockerCompose, make), DockerEngine.layerDeno(connectionOptions)); /** * @since 1.0.0 * @category Layers */ -export const layerUndici = makeLayer(DockerEngine.layerUndici); +export const layerUndici = ( + connectionOptions: MobyConnectionOptions +): Layer.Layer => + Layer.provide(Layer.scoped(DockerCompose, make), DockerEngine.layerUndici(connectionOptions)); /** * @since 1.0.0 * @category Layers */ -export const layerWeb = makeLayer(DockerEngine.layerWeb); +export const layerWeb = ( + connectionOptions: HttpConnectionOptionsTagged | HttpsConnectionOptionsTagged +): Layer.Layer => + Layer.provide(Layer.scoped(DockerCompose, make), DockerEngine.layerWeb(connectionOptions)); /** * @since 1.0.0 * @category Layers */ -export const layerAgnostic = makeLayer(DockerEngine.layerAgnostic); +export const layerAgnostic = ( + connectionOptions: HttpConnectionOptionsTagged | HttpsConnectionOptionsTagged +): Layer.Layer< + DockerCompose, + SystemsError | ContainersError, + HttpClient.HttpClient +> => Layer.provide(Layer.scoped(DockerCompose, make), DockerEngine.layerAgnostic(connectionOptions)); /** * @since 1.0.0 @@ -332,32 +320,33 @@ export interface DockerComposeProject { readonly kill: (options: {}) => Effect.Effect; } -/** @internal */ -export const makeProjectLayer = ( +const makeProjectLayer = ( project: Stream.Stream, - uploadProject: (project: Stream.Stream) => Effect.Effect, + uploadProject: ( + project: Stream.Stream + ) => Effect.Effect, runCommand: (command: string, method: string) => Effect.Effect -): Effect.Effect => +): Effect.Effect => Effect.gen(function* () { yield* uploadProject(project); const build = (_options: {}): Effect.Effect => - runCommand("docker compose --file docker-compose.yml build", "build"); + runCommand("docker compose --project-name test --file docker-compose.yml build", "build"); const pull = (_options: {}): Effect.Effect => - runCommand("docker compose --file docker-compose.yml pull", "pull"); + runCommand("docker compose --project-name test --file docker-compose.yml pull", "pull"); const up = (_options: {}): Effect.Effect => - runCommand("docker compose --file docker-compose.yml up", "up"); + runCommand("docker compose --project-name test --file docker-compose.yml up", "up"); const down = (_options: {}): Effect.Effect => - runCommand("docker compose --file docker-compose.yml down", "down"); + runCommand("docker compose --project-name test --file docker-compose.yml down", "down"); const rm = (_options: {}): Effect.Effect => - runCommand("docker compose --file docker-compose.yml rm", "rm"); + runCommand("docker compose --project-name test --file docker-compose.yml rm", "rm"); const kill = (_options: {}): Effect.Effect => - runCommand("docker compose --file docker-compose.yml kill", "kill"); + runCommand("docker compose --project-name test --file docker-compose.yml kill", "kill"); return { [DockerComposeProjectTypeId]: DockerComposeProjectTypeId, @@ -379,7 +368,7 @@ export const layerProject: ( tagIdentifier: string ) => { readonly tag: Context.Tag; - readonly layer: Layer.Layer; + readonly layer: Layer.Layer; } = (project: Stream.Stream, tagIdentifier: string) => { const tag = Context.GenericTag(tagIdentifier); const effect = Effect.flatMap(DockerCompose, ({ forProject }) => forProject(project)); From 9c4303cb4c89e1288b42ab5e347731b69b83815e Mon Sep 17 00:00:00 2001 From: Leo Conforti Date: Mon, 4 Nov 2024 02:02:15 +0000 Subject: [PATCH 6/6] fix example --- examples/docker-compose.ts | 6 +++--- src/engines/DockerCompose.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/docker-compose.ts b/examples/docker-compose.ts index 1244cc22..35b25d21 100644 --- a/examples/docker-compose.ts +++ b/examples/docker-compose.ts @@ -40,9 +40,9 @@ const program = Effect.gen(function* () { yield* compose.pull({}); yield* compose.up({}); - // yield* Effect.sleep("10 seconds"); - // yield* compose.down({}); - // yield* compose.rm({}); + yield* Effect.sleep("10 seconds"); + yield* compose.down({}); + yield* compose.rm({}); }); program.pipe(Effect.provide(dockerComposeProjectLive)).pipe(NodeRuntime.runMain); diff --git a/src/engines/DockerCompose.ts b/src/engines/DockerCompose.ts index f228c8fa..3f2d9450 100644 --- a/src/engines/DockerCompose.ts +++ b/src/engines/DockerCompose.ts @@ -191,7 +191,7 @@ const make: Effect.Effect - runCommand("docker compose --project-name test --file docker-compose.yml up", "up") + runCommand("docker compose --project-name test --file docker-compose.yml up -d", "up") ) ), @@ -337,7 +337,7 @@ const makeProjectLayer = ( runCommand("docker compose --project-name test --file docker-compose.yml pull", "pull"); const up = (_options: {}): Effect.Effect => - runCommand("docker compose --project-name test --file docker-compose.yml up", "up"); + runCommand("docker compose --project-name test --file docker-compose.yml up -d", "up"); const down = (_options: {}): Effect.Effect => runCommand("docker compose --project-name test --file docker-compose.yml down", "down");