diff --git a/.editorconfig b/.editorconfig index 7a8d3ee..49d2945 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,20 +1,18 @@ -[*] -charset = utf-8 -end_of_line = lf -indent_size = 4 -indent_style = space -insert_final_newline = true -trim_trailing_whitespace = true +# EditorConfig is awesome: https://EditorConfig.org -[*.{js,jsx,ts,tsx,vue,html,css,scss,json}] -indent_style = tab -indent_size = 2 +# top-most EditorConfig file +root = true -[*.md] +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 trim_trailing_whitespace = false +insert_final_newline = false -[*.{yml,yaml}] +[*.{js,jsx,ts,tsx,css,scss,html,svelte,postcss,glsl}] indent_size = 2 - -[docker-compose.yml] -indent_size = 4 +indent_style = tab +trim_trailing_whitespace = true +insert_final_newline = true \ No newline at end of file diff --git a/.env.example b/.env.example deleted file mode 100644 index 3646d14..0000000 --- a/.env.example +++ /dev/null @@ -1 +0,0 @@ -PORT=8000 diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..f9e3fad --- /dev/null +++ b/.env.sample @@ -0,0 +1,3 @@ +PORT="3001" +SERVER_NAME="api" +DOCKER_API_VERSION="1.44" \ No newline at end of file diff --git a/.github/workflows/bun.sh.yml b/.github/workflows/bun.sh.yml index dca3d4c..802db03 100644 --- a/.github/workflows/bun.sh.yml +++ b/.github/workflows/bun.sh.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - bun-version: [0.7.1, 0.7.2] + bun-version: [1.1.4] steps: - uses: actions/checkout@v3 - name: Use Bun.sh ${{ matrix.bun-version }} @@ -19,5 +19,5 @@ jobs: with: bun-version: ${{ matrix.bun-version }} - run: bun install - - run: bun build ./src/main.ts --compile --outfile grader + - run: bun run compile # - run: bun run test # TODO: Add unit tests diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 329ee39..c6c1092 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -12,6 +12,6 @@ jobs: steps: - uses: actions/checkout@v3 - name: Build Git Runner - run: docker build ./docker/git/ --tag w2wizard/git_runner:$(date +%s) - - name: Build Single Runner - run: docker build ./docker/single/ --tag w2wizard/single_runner:$(date +%s) + run: docker build ./docker/git/ --tag w2wizard/git:$(date +%s) + # - name: Build Single Runner + # run: docker build ./docker/single/ --tag w2wizard/code:$(date +%s) diff --git a/.gitignore b/.gitignore index 6872d7a..ad22606 100644 --- a/.gitignore +++ b/.gitignore @@ -1,78 +1,105 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + # Logs + logs -*.log -npm-debug.log* +_.log +npm-debug.log_ yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* +# Caches + +.cache + # Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json # Runtime data + pids -*.pid -*.seed +_.pid +_.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover + lib-cov # Coverage directory used by tools like istanbul + coverage *.lcov # nyc test coverage + .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + .grunt # Bower dependency directory (https://bower.io/) + bower_components # node-waf configuration + .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) + build/Release # Dependency directories + node_modules/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) + web_modules/ # TypeScript cache + *.tsbuildinfo # Optional npm cache directory + .npm # Optional eslint cache + .eslintcache # Optional stylelint cache + .stylelintcache # Microbundle cache + .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history + .node_repl_history # Output of 'npm pack' + *.tgz # Yarn Integrity file + .yarn-integrity # dotenv environment variable files + .env .env.development.local .env.test.local @@ -80,58 +107,71 @@ web_modules/ .env.local # parcel-bundler cache (https://parceljs.org/) -.cache + .parcel-cache # Next.js build output + .next out # Nuxt.js build / generate output + .nuxt dist # Gatsby files -.cache/ + # Comment in the public line in if your project uses Gatsby and not Next.js + # https://nextjs.org/blog/next-9-1#public-directory-support + # public # vuepress build output + .vuepress/dist # vuepress v2.x temp and cache directory + .temp -.cache # Docusaurus cache and generated files + .docusaurus # Serverless directories + .serverless/ # FuseBox cache + .fusebox/ # DynamoDB Local files + .dynamodb/ # TernJS port file + .tern-port # Stores VSCode versions used for testing VSCode extensions + .vscode-test # yarn v2 + .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* -.sqlite + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config .DS_Store -tmp -*.so -*.o -*.a -*.dylib + +build \ No newline at end of file diff --git a/LICENSE b/LICENSE index 0405dbc..4621ad2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License (modified) -Copyright (c) 2023 W2Wizard +Copyright (c) 2023-24 W2Wizard Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,3 +23,8 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +EXCEPTIONS: ANY ORGANIZATION REPRESENTING 42 (Central) IS NOT ALLOWED TO USE THIS +UNLESS EXPLICIT PERMISSION IS GIVEN! ANY CODE COPIED, USED, ... FROM BEFORE OR AFTER THIS LICENSE WILL BE MET WITH A LEGAL RESPONSE! + +HOWEVER THE ENTITY KNOWN AS CODAM CODING COLLEGE IS ALLOWED TO USE THIS SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 9c7a3db..76fb163 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,13 @@ RoboPeer is a **webserver** designed to grade your code in a safe environment. It allows you to submit code and have it "graded" without any worries about potential malicious activities. +This project was created using `bun init` in bun v1.1.4. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. + ## ๐ŸŽฏ How it Works The grading process involves comparing the output of the provided git repository (after compiling it) to a series of tests run with `bun:test`. RoboPeer then returns the results of these tests. With an appropriate status code. -RoboPeer can also grade direct code submissions via `/api/grade/git` and `/api/grade/single` respectively. +RoboPeer can also grade direct code submissions via `/evaluate/git`. - `200` - All tests passed. - `400` - Tests failed. @@ -22,10 +24,23 @@ RoboPeer can also grade direct code submissions via `/api/grade/git` and `/api/g ## ๐Ÿ› ๏ธ Installation Requires [Bun.sh](https://bun.sh) to install dependencies. +To install dependencies: ```bash bun install ``` +To run: +```bash +bun run api-get # Fetches Dockers OpenAPI spec +bun run dev +``` + +To build: +```bash +bun run build # Outputs JS +bun run compile # Outputs a 98~mb binary +``` + ## ๐Ÿš€ Usage ### Creating a Grading Project @@ -35,11 +50,6 @@ To create a new reference project, use the following command: bun run new < project-name > ``` -### Dashboard - -To view the dashboard, just visit [localhost](http://localhost:8000/) in your browser. -The dashboard merely displays statistics about the grading server itself and not much else. - ### ๐Ÿณ Docker Setup > **Note**: The docker image hasn't been configured properly yet to make sure the code isn't actually doing something stupid (deleting files, cd, ...) atm it uses Kornshell just a basic simple attempt but there are no serious measures whatsoever. @@ -51,27 +61,7 @@ Build the Docker image: #Git Image runner docker build -t w2wizard/git_runner ./docker/git -#Single Image runner -docker build -t w2wizard/single_runner ./docker/single -``` -### ๐Ÿงฐ Running the Server -Use the following command to run the server: -```bash -bun run start -# or -bun run ./src/main.ts -# or -bun build ./src/main.ts --compile --outfile robopeer ``` -`Output`: -``` -Registering /grade routes. -Connected to docker daemon. -Webserver: http://localhost:8000/ -``` - ---- - ### ๐Ÿ“จ Sending a Grading Request The server by default run on http://localhost:8080. To send a request to it you can use the following curl command: ```bash @@ -93,20 +83,3 @@ curl -XPOST -H "Content-type: application/json" -d '{ "language": "c" }' 'http://localhost:8000/api/grade/single' ``` - -`Output`: -```bash -#Client -[+] Cloning git repository... -[+] Checking out commit 67dc80ae6a5d2c56a4305f5194672fe19130e705... -[+] Compiling code... -... -[+] Running tests... -``` - -```bash -#Server -[03/08/2023 14:33:47] [INFO] ๐Ÿ“ : Received request for: libft -[03/08/2023 14:33:47] [INFO] ๐Ÿ“ : Running tests for: libft => {"gitURL":"https://github.com/fbescodam/libft.git","branch":"master","commit":"67dc80ae6a5d2c56a4305f5194672fe19130e705"} -[03/08/2023 14:33:47] [INFO] ๐Ÿ“ : Container 8c2de65dcd4f exited with: 1 -``` diff --git a/bun.lockb b/bun.lockb index 4b1b6ee..5d7c51d 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/docker/git/.dockerignore b/docker/git/.dockerignore deleted file mode 100644 index 77f35cd..0000000 --- a/docker/git/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -.DS_Store -*.md diff --git a/docker/git/Dockerfile b/docker/git/Dockerfile index b7c5c8e..34ef22b 100644 --- a/docker/git/Dockerfile +++ b/docker/git/Dockerfile @@ -1,17 +1,14 @@ #============================================================================= -# W2Wizard, Amsterdam @ 2018-2023 +# W2Wizard, Amsterdam @ 2024 # See README and LICENSE files for details. #============================================================================= # Docker image for running git based repositories with bun test framework. #============================================================================= -# TODO: Setup a runner user with very limited rights. -#============================================================================= FROM oven/bun:latest -WORKDIR /app +WORKDIR / RUN apt-get update && apt-get install -y \ - ksh \ git \ cmake \ coreutils \ @@ -21,7 +18,12 @@ RUN apt-get update && apt-get install -y \ build-essential \ && rm -rf /var/lib/apt/lists/* -RUN adduser --disabled-password --shell /bin/bash "runner" +# Keep the container running +COPY start.sh /usr/local/bin/init.sh +RUN chmod 755 /usr/local/bin/init.sh + +RUN addgroup --gid 1337 nxtdmy +RUN adduser --shell /bin/bash runner && adduser runner nxtdmy -COPY entry.sh /usr/local/bin/entry.sh -ENTRYPOINT [ "entry.sh" ] +USER runner +ENTRYPOINT [ "init.sh" ] \ No newline at end of file diff --git a/docker/git/entry.sh b/docker/git/entry.sh deleted file mode 100755 index a9b22ab..0000000 --- a/docker/git/entry.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -#============================================================================= - -echo "[+] Starting..." -if [ -z "$GIT_URL" ] || [ -z "$GIT_BRANCH" ] || [ -z "$GIT_COMMIT" ]; then - echo -e "GIT_URL, GIT_BRANCH and GIT_COMMIT must be set" - exit 1 -fi - -# See: https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html#timeout-invocation -echo "[+] Timeout: $TIMEOUT seconds" -timeout --kill-after=$TIMEOUT $TIMEOUT /bin/bash /app/start.sh - -exitCode=$? -if [ $exitCode -ne 0 ]; then - echo -e "[+] Failed: $exitCode" -else - echo -e "[+] Success" -fi -exit $exitCode diff --git a/docker/git/start.sh b/docker/git/start.sh new file mode 100644 index 0000000..46dd276 --- /dev/null +++ b/docker/git/start.sh @@ -0,0 +1,42 @@ +#!/bin/bash +#============================================================================= + +export ID=$(xxd -l 16 -ps /dev/urandom | tr -d ' \n') +export TMP_DIR="/tmp/$ID" +export GIT_DIR="$TMP_DIR/git" +export WRK_DIR="$HOME/app" + +echo "[+] Starting..." +if [ -z "$GIT_URL" ] || [ -z "$GIT_BRANCH" ] || [ -z "$GIT_COMMIT" ]; then + echo -e "GIT_URL, GIT_BRANCH and GIT_COMMIT must be set!" + exit 1 +fi + +# Clone the repository +mkdir -p $TMP_DIR +echo "[+] Cloning $GIT_URL" +git clone $GIT_URL $GIT_DIR -b $GIT_BRANCH --recurse-submodules --quiet + +# Switch to the commit +#TODO: Broken? +cd $GIT_DIR +echo "[+] Switching to $GIT_COMMIT" && git checkout $GIT_COMMIT + +# Clean up the work directory if anything is there +# Since the host is read-only, copy the unit test files +rm -rf $WRK_DIR && mkdir -p $WRK_DIR +cp /var/dev/index.test.ts $WRK_DIR +ls -laF $GIT_DIR + +echo "[+] ============================================================================" +echo "[+] Timeout: $TIMEOUT seconds" +timeout --kill-after=$TIMEOUT $TIMEOUT /bin/bash /var/dev/start.sh +#tail -f /dev/null # DEBUG + +exitCode=$? +if [ $exitCode -ne 0 ]; then + echo -e "[+] Failed: $exitCode" +else + echo -e "[+] Success" +fi +exit $exitCode \ No newline at end of file diff --git a/docker/single/.dockerignore b/docker/single/.dockerignore index 77f35cd..e08412e 100644 --- a/docker/single/.dockerignore +++ b/docker/single/.dockerignore @@ -1,2 +1,2 @@ .DS_Store -*.md +*.md \ No newline at end of file diff --git a/docker/single/Dockerfile b/docker/single/Dockerfile index 6ed48fc..3d8db8f 100644 --- a/docker/single/Dockerfile +++ b/docker/single/Dockerfile @@ -19,4 +19,4 @@ RUN useradd runner --shell /bin/bash --create-home COPY start.sh . COPY entry.sh /usr/local/bin/entry.sh -ENTRYPOINT [ "entry.sh" ] +ENTRYPOINT [ "entry.sh" ] \ No newline at end of file diff --git a/docker/single/entry.sh b/docker/single/entry.sh old mode 100755 new mode 100644 index 6eff8cf..e0d6eb7 --- a/docker/single/entry.sh +++ b/docker/single/entry.sh @@ -17,4 +17,4 @@ if [ $exitCode -ne 0 ]; then else echo -e "[+] Success" fi -exit $exitCode +exit $exitCode \ No newline at end of file diff --git a/docker/single/start.sh b/docker/single/start.sh index 4bd75a9..89a502e 100644 --- a/docker/single/start.sh +++ b/docker/single/start.sh @@ -24,4 +24,4 @@ IFS=';' read -ra ARGS <<< "$CODE_ARGS" #============================================================================== echo "[+] Running file..." -su - runner -s /bin/bash -c "pwd; ls -l; ./a.out ${ARGS[@]}" +su - runner -s /bin/bash -c "pwd; ls -l; ./a.out ${ARGS[@]}" \ No newline at end of file diff --git a/package.json b/package.json index 174292c..d975bcf 100644 --- a/package.json +++ b/package.json @@ -1,30 +1,23 @@ { - "name": "grader", - "author": "W2Wizard", - "description": "A code grading server", - "module": "./src/main.ts", + "name": "robopeer", + "module": "main.ts", + "type": "module", "scripts": { "new": "/bin/bash ./scripts/new.sh", - "start": "bun run ./src/main.ts", - "build": "bun build ./src/main.ts --compile --outfile grader" - }, - "directories": { - "src": "src" + "dev": "bun --bun run ./src/main.ts", + "api-get": "bun --bun run ./scripts/openapi.ts", + "build": "bun --bun build ./src/main.ts --outfile ./build/robopeer.js", + "compile": "bun --bun build --compile --minify ./src/main.ts --outfile ./build/robopeer" }, "devDependencies": { - "bun-types": "^1.0.17", - "typed-html": "^3.0.1" + "@types/bun": "latest", + "openapi-typescript": "^6.7.5" }, "peerDependencies": { "typescript": "^5.0.0" }, - "type": "module", "dependencies": { - "@elysiajs/fn": "^0.6.1", - "@elysiajs/html": "^0.7.3", - "@elysiajs/static": "^0.7.1", - "chalk": "^5.3.0", - "elysia": "^0.7.30", - "http-parser-js": "^0.5.8" + "itty-router": "^5.0.15", + "openapi-fetch": "^0.9.3" } -} +} \ No newline at end of file diff --git a/projects/README.md b/projects/README.md deleted file mode 100644 index dd89134..0000000 --- a/projects/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Projects ๐Ÿ“– - -## Creating new Projects ๐Ÿ”จ - -Use `bun run new ` to create new projects that comply with the docker image. -The projects in the repo are merely examples. diff --git a/projects/hello_world/index.test.ts b/projects/balls/index.test.ts similarity index 94% rename from projects/hello_world/index.test.ts rename to projects/balls/index.test.ts index 59f476a..ec43f42 100644 --- a/projects/hello_world/index.test.ts +++ b/projects/balls/index.test.ts @@ -8,6 +8,8 @@ import { beforeAll, describe, expect, it } from "bun:test"; //============================================================================= +// NOTE(W2): Please read: https://bun.sh/docs/api/ffi before using ffi. + /** * Run a command with arguments and return the output. * @param bin The command to run. diff --git a/projects/hello_world/start.sh b/projects/balls/start.sh old mode 100755 new mode 100644 similarity index 95% rename from projects/hello_world/start.sh rename to projects/balls/start.sh index 7836890..d13629b --- a/projects/hello_world/start.sh +++ b/projects/balls/start.sh @@ -1,5 +1,6 @@ #!/bin/bash #============================================================================== + ID=$(xxd -l 16 -ps /dev/urandom | tr -d " \n") ProjectDIR="/tmp/$ID/project" ObjectsDIR="/tmp/$ID/objects" @@ -22,7 +23,7 @@ function gitCloneCommit() { # Build the project function build() { echo "[+] Building ..." - make -C $ProjectDIR -j4 + timeout 1m make -C $ProjectDIR -j4 # Other steps ... diff --git a/projects/lib/index.test.ts b/projects/lib/index.test.ts new file mode 100644 index 0000000..04aec73 --- /dev/null +++ b/projects/lib/index.test.ts @@ -0,0 +1,569 @@ +//============================================================================= +// W2Wizard, Amsterdam @ 2018-2023 +// See README and LICENSE files for details. +//============================================================================= + +import { dlopen, FFIType, ptr, toArrayBuffer } from "bun:ffi"; +import { describe, expect, it, beforeAll, afterAll } from "bun:test"; + +/** + * WARNING: bun:ffi does not manage memory for you. + * You must free the memory when you're done with it. + * + * This is very important regarding functions that return allocated memory. + * (ft_split, ft_strtrim, ft_itoa, etc.) + * + * Might be useful for code execution of C files directly: + * @see https://github.com/TinyCC/tinycc + * + * TODO: What happens when you segfault in a function? + */ +//============================================================================= + +// TODO: Come up with a neater way to generate the type definitions? +// NOTE(W2): Convert the static library to a shared library for dlopen to work. +const { symbols, close } = dlopen("libft.so", { + ft_strdup: { + returns: FFIType.ptr, + args: [FFIType.ptr], + }, + ft_strlen: { + returns: FFIType.i32, + args: [FFIType.ptr], + }, + ft_isalpha: { + returns: FFIType.i32, + args: [FFIType.i32], + }, + ft_isdigit: { + returns: FFIType.i32, + args: [FFIType.i32], + }, + ft_isalnum: { + returns: FFIType.i32, + args: [FFIType.i32], + }, + ft_isascii: { + returns: FFIType.i32, + args: [FFIType.i32], + }, + ft_isprint: { + returns: FFIType.i32, + args: [FFIType.i32], + }, + ft_memset: { + returns: FFIType.ptr, + args: [FFIType.ptr, FFIType.i32, FFIType.i32], + }, + ft_bzero: { + returns: FFIType.ptr, + args: [FFIType.ptr, FFIType.i32], + }, + ft_memcpy: { + returns: FFIType.ptr, + args: [FFIType.ptr, FFIType.ptr, FFIType.i32], + }, + ft_memmove: { + returns: FFIType.ptr, + args: [FFIType.ptr, FFIType.ptr, FFIType.i32], + }, + ft_strlcpy: { + returns: FFIType.i32, + args: [FFIType.ptr, FFIType.ptr, FFIType.i32], + }, + ft_strlcat: { + returns: FFIType.i32, + args: [FFIType.ptr, FFIType.ptr, FFIType.i32], + }, + ft_toupper: { + returns: FFIType.i32, + args: [FFIType.i32], + }, + ft_tolower: { + returns: FFIType.i32, + args: [FFIType.i32], + }, + ft_strchr: { + returns: FFIType.ptr, + args: [FFIType.ptr, FFIType.i32], + }, + ft_strrchr: { + returns: FFIType.ptr, + args: [FFIType.ptr, FFIType.i32], + }, + ft_strncmp: { + returns: FFIType.i32, + args: [FFIType.ptr, FFIType.ptr, FFIType.i32], + }, + ft_memchr: { + returns: FFIType.ptr, + args: [FFIType.ptr, FFIType.i32, FFIType.i32], + }, + ft_memcmp: { + returns: FFIType.i32, + args: [FFIType.ptr, FFIType.ptr, FFIType.i32], + }, + ft_strnstr: { + returns: FFIType.ptr, + args: [FFIType.ptr, FFIType.ptr, FFIType.i32], + }, + ft_atoi: { + returns: FFIType.i32, + args: [FFIType.ptr], + }, +}); + +afterAll(close); +beforeAll(() => { + ["SIGINT", "SIGTERM", "SIGHUP"].forEach((signal) => { + process.on(signal, () => { + close(); + throw new Error(`Signal ${signal} received.`); + }); + }); +}); + + +// makefile +//============================================================================= +describe("makefile", () => { + it("compiles with '-Wextra -Werror -Wall'", async () => { + const makefile = Bun.file("Makefile"); + const text = await makefile.text(); + const requiredFlags = ["-Wall", "-Wextra", "-Werror"]; + + expect(requiredFlags.every((flag) => text.includes(flag))).toBe(true); + }); +}); + +// strdup +//============================================================================= +// describe("strdup", () => { +// it("duplicates a string", () => { +// const sample = "Hello, world!"; +// const ptrSample = ptr(Buffer.from(`${sample}\0`, "utf8")); +// const result = toArrayBuffer(symbols.ft_strdup(ptrSample), 0, sample.length); +// const output = Buffer.from(result).toString("utf8"); +// expect(output).toBe(sample); +// }); + +// it("returns null if the string is empty", () => { +// const emptyStr = Buffer.from(`\0`, "utf8"); +// expect(symbols.ft_strdup(ptr(emptyStr))).toBe(null); +// }); +// }); + +// isalpha +//============================================================================= +describe("isalpha", () => { + it("valid alphabetic characters", () => { + const alphabeticChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for (const char of alphabeticChars) { + expect(symbols.ft_isalpha(char.charCodeAt(0))).toBe(1); + } + }); + + it("invalid non-alphabetic characters", () => { + const nonAlphabeticChars = "0123456789!@#$%^&*()_+-=[]{};':\",.<>?/\\|"; + for (const char of nonAlphabeticChars) { + expect(symbols.ft_isalpha(char.charCodeAt(0))).toBe(0); + } + }); +}); + +// isdigit +//============================================================================= +describe("isdigit", () => { + it("valid numeric characters", () => { + const numericChars = "0123456789"; + for (const char of numericChars) { + expect(symbols.ft_isdigit(char.charCodeAt(0))).toBe(1); + } + }); + + it("invalid non-numeric characters", () => { + const nonNumericChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+-=[]{};':\",.<>?/\\|"; + for (const char of nonNumericChars) { + expect(symbols.ft_isdigit(char.charCodeAt(0))).toBe(0); + } + }); +}); + +// isalnum +//============================================================================= +describe("isalnum", () => { + it("valid alphanumeric characters", () => { + const alphanumericChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + for (const char of alphanumericChars) { + expect(symbols.ft_isalnum(char.charCodeAt(0))).toBe(1); + } + }); + + it("invalid non-alphanumeric characters", () => { + const nonAlphanumericChars = "!@#$%^&*()_+-=[]{};':\",.<>?/\\|"; + for (const char of nonAlphanumericChars) { + expect(symbols.ft_isalnum(char.charCodeAt(0))).toBe(0); + } + }); +}); + +// isascii +//============================================================================= +describe("isascii", () => { + it("valid ASCII characters", () => { + for (let i = 0; i < 128; i++) { + expect(symbols.ft_isascii(i)).toBe(1); + } + }); + + it("invalid non-ASCII characters", () => { + for (let i = 128; i < 256; i++) { + expect(symbols.ft_isascii(i)).toBe(0); + } + }); +}); + +// isprint +//============================================================================= +describe("isprint", () => { + it("valid printable characters", () => { + const printableChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=[]{};':\",.<>?/\\| "; + for (const char of printableChars) { + expect(symbols.ft_isprint(char.charCodeAt(0))).toBe(1); + } + }); + + it("invalid non-printable characters", () => { + for (let i = 0; i < 32; i++) { + expect(symbols.ft_isprint(i)).toBe(0); + } + }); +}); + +// strlen +//============================================================================= +describe("strlen", () => { + it("normal string", () => { + const sample = "Hello, world!"; + const str = ptr(Buffer.from(`${sample}\0`, "utf8")); + expect(symbols.ft_strlen(str)).toBe(sample.length); + }); + + it("empty string", () => { + const str = ptr(Buffer.from(`\0`, "utf8")); + expect(symbols.ft_strlen(str)).toBe(0); + }); +}); + +// memset +//============================================================================= +describe("memset", () => { + it("set characters to a value", () => { + const size = 42; + const value = 42; // * + const buffer = Buffer.alloc(size); + + symbols.ft_memset(ptr(buffer), value, size); + buffer.forEach((byte) => expect(byte).toBe(value)); + }); +}); + +// bzero (Note: bzero is equivalent to memset with value 0) +//============================================================================= +describe("bzero", () => { + it("zeroes out a buffer", () => { + const size = 10; + const buffer = Buffer.alloc(size, 42); + + symbols.ft_bzero(ptr(buffer), size); + buffer.forEach((byte) => expect(byte).toBe(0)); + }); +}); + +// memcpy +//============================================================================= +describe("memcpy", () => { + it("copies data from source to destination", () => { + const source = Buffer.from("Hello, world!\0"); + const destination = Buffer.alloc(source.length); + + symbols.ft_memcpy(ptr(destination), ptr(source), source.length); + expect(destination.toString("utf8")).toBe(source.toString("utf8")); + }); +}); + +// ft_memmove +//============================================================================= +describe("memmove", () => { + it("copies overlapping data from source to destination", () => { + const source = Buffer.from("Hello, world!\0"); + const destination = Buffer.alloc(source.length); + + symbols.ft_memmove(ptr(source), ptr(destination), source.length); + expect(destination.toString("utf8")).toBe(source.toString("utf8")); + }); +}); + +// // strlcpy +// //============================================================================= +// describe("strlcpy", () => { +// it("copies a string to a destination buffer", () => { +// const source = "Hello, world!"; +// const destination = Buffer.alloc(source.length + 1); +// const ptrSource = ptr(Buffer.from(`${source}\0`, "utf8")); +// const ptrDestination = ptr(destination); + +// const result = symbols.ft_strlcpy(ptrDestination, ptrSource, destination.length); +// expect(destination.toString("utf8")).toBe(Buffer.from(`${source}\0`, "utf8").toString("utf8")); +// expect(result).toBe(source.length); +// }); +// }); + +// // strlcat +// //============================================================================= +// describe("strlcat", () => { +// it("concatenates two strings to a destination buffer", () => { +// const source1 = "Hello, "; +// const source2 = "world!"; +// const expected = "Hello, world!"; +// const destination = Buffer.alloc(source1.length + source2.length + 1); + +// const ptrSource1 = ptr(Buffer.from(`${source1}\0`, "utf8")); +// const ptrSource2 = ptr(Buffer.from(`${source2}\0`, "utf8")); +// const ptrDestination = ptr(destination); + +// symbols.ft_strlcpy(ptrDestination, ptrSource1, destination.length); +// const result = symbols.ft_strlcat(ptrDestination, ptrSource2, destination.length); +// expect(destination.toString("utf8")).toBe(Buffer.from(`${expected}\0`, "utf8").toString("utf8")); +// expect(result).toBe(expected.length); +// }); +// }); + +// // toupper +// //============================================================================= +// describe("toupper", () => { +// it("converts lowercase characters to uppercase", () => { +// const lowercaseChars = "abcdefghijklmnopqrstuvwxyz"; +// const uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +// for (let i = 0; i < lowercaseChars.length; i++) { +// const char = lowercaseChars[i]; +// const expectedUppercaseChar = uppercaseChars[i]; +// expect(symbols.ft_toupper(char.charCodeAt(0))).toBe(expectedUppercaseChar.charCodeAt(0)); +// } +// }); + +// it("leaves non-lowercase characters unchanged", () => { +// const nonLowercaseChars = "0123456789!@#$%^&*()_+-=[]{};':\",.<>?/\\|ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +// for (const char of nonLowercaseChars) { +// expect(symbols.ft_toupper(char.charCodeAt(0))).toBe(char.charCodeAt(0)); +// } +// }); +// }); + +// // tolower +// //============================================================================= +// describe("tolower", () => { +// it("converts uppercase characters to lowercase", () => { +// const lowercaseChars = "abcdefghijklmnopqrstuvwxyz"; +// const uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +// for (let i = 0; i < lowercaseChars.length; i++) { +// const char = uppercaseChars[i]; +// const expectedLowercaseChar = lowercaseChars[i]; +// expect(symbols.ft_tolower(char.charCodeAt(0))).toBe(expectedLowercaseChar.charCodeAt(0)); +// } +// }); + +// it("leaves non-uppercase characters unchanged", () => { +// const nonUppercaseChars = "0123456789!@#$%^&*()_+-=[]{};':\",.<>?/\\|abcdefghijklmnopqrstuvwxyz"; +// for (const char of nonUppercaseChars) { +// expect(symbols.ft_tolower(char.charCodeAt(0))).toBe(char.charCodeAt(0)); +// } +// }); +// }); + +// // strchr +// //============================================================================= +// describe("strchr", () => { +// it("finds the first occurrence of a character in a string", () => { +// const str = "Hello, world!"; +// const char = "o"; +// const ptrStr = ptr(Buffer.from(`${str}\0`, "utf8")); +// const result = symbols.ft_strchr(ptrStr, char.charCodeAt(0)); +// expect(result).toBe(ptrStr + 4); +// }); + +// it("returns null if the character is not found", () => { +// const str = "Hello, world!"; +// const char = "z"; +// const ptrStr = ptr(Buffer.from(`${str}\0`, "utf8")); +// const result = symbols.ft_strchr(ptrStr, char.charCodeAt(0)); +// expect(result).toBe(null); +// }); +// }); + +// // strrchr +// //============================================================================= +// describe("strrchr", () => { +// it("finds the last occurrence of a character in a string", () => { +// const str = "Hello, world!"; +// const char = "o"; +// const ptrStr = ptr(Buffer.from(`${str}\0`, "utf8")); +// const result = symbols.ft_strrchr(ptrStr, char.charCodeAt(0)); +// expect(result).toBe(ptrStr + 8); +// }); + +// it("returns null if the character is not found", () => { +// const str = "Hello, world!"; +// const char = "z"; +// const ptrStr = ptr(Buffer.from(`${str}\0`, "utf8")); +// const result = symbols.ft_strrchr(ptrStr, char.charCodeAt(0)); +// expect(result).toBe(null); +// }); +// }); + +// // strncmp +// //============================================================================= +// describe("strncmp", () => { +// it("compares two strings up to a specified length", () => { +// const str1 = "Hello"; +// const str2 = "Hello, world!"; +// const length = 5; +// const ptrStr1 = ptr(Buffer.from(`${str1}\0`, "utf8")); +// const ptrStr2 = ptr(Buffer.from(`${str2}\0`, "utf8")); +// expect(symbols.ft_strncmp(ptrStr1, ptrStr2, length)).toBe(0); +// }); + +// it("returns a negative value if str1 is less than str2", () => { +// const str1 = "Hell"; +// const str2 = "Hello, world!"; +// const length = 5; +// const ptrStr1 = ptr(Buffer.from(`${str1}\0`, "utf8")); +// const ptrStr2 = ptr(Buffer.from(`${str2}\0`, "utf8")); +// expect(symbols.ft_strncmp(ptrStr1, ptrStr2, length)).toBeLessThan(0); +// }); + +// it("returns a positive value if str1 is greater than str2", () => { +// const str1 = "Hello!"; +// const str2 = "Hello, world!"; +// const length = 5; +// const ptrStr1 = ptr(Buffer.from(`${str1}\0`, "utf8")); +// const ptrStr2 = ptr(Buffer.from(`${str2}\0`, "utf8")); +// expect(symbols.ft_strncmp(ptrStr1, ptrStr2, length)).toBeGreaterThanOrEqual(0); +// }); +// }); + +// // memchr +// //============================================================================= +// describe("memchr", () => { +// it("finds the first occurrence of a byte in a memory region", () => { +// const data = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x45]); // ABCDE +// const byte = 0x43; // C + +// const ptrData = ptr(data); +// const result = symbols.ft_memchr(ptrData, byte, data.length); +// expect(result).toBe(ptrData + 2); +// }); + +// it("returns null if the byte is not found", () => { +// const data = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x45]); // ABCDE +// const byte = 0x46; // F + +// const ptrData = ptr(data); +// const result = symbols.ft_memchr(ptrData, byte, data.length); +// expect(result).toBe(null); +// }); +// }); + +// // memcmp +// //============================================================================= +// describe("memcmp", () => { +// it("compares two memory regions up to a specified length", () => { +// const data1 = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x45]); // ABCDE +// const data2 = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x46]); // ABCDF +// const length = 5; +// const ptrData1 = ptr(data1); +// const ptrData2 = ptr(data2); +// expect(symbols.ft_memcmp(ptrData1, ptrData2, length)).toBe(-1); +// }); + +// it("returns 0 if both memory regions are equal", () => { +// const data1 = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x45]); // ABCDE +// const data2 = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x45]); // ABCDE +// const length = 5; +// const ptrData1 = ptr(data1); +// const ptrData2 = ptr(data2); +// expect(symbols.ft_memcmp(ptrData1, ptrData2, length)).toBe(0); +// }); + +// it("returns a positive value if data1 is greater than data2", () => { +// const data1 = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x47]); // ABCDG +// const data2 = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x46]); // ABCDF +// const length = 5; +// const ptrData1 = ptr(data1); +// const ptrData2 = ptr(data2); +// expect(symbols.ft_memcmp(ptrData1, ptrData2, length)).toBe(1); +// }); +// }); + +// // strnstr +// //============================================================================= +// describe("strnstr", () => { +// it("finds the first occurrence of a substring in a string up to a specified length", () => { +// const str = "Hello, world!"; +// const substr = "world"; +// const length = 13; +// const ptrStr = ptr(Buffer.from(`${str}\0`, "utf8")); +// const ptrSubstr = ptr(Buffer.from(`${substr}\0`, "utf8")); +// const result = symbols.ft_strnstr(ptrStr, ptrSubstr, length); +// expect(result).toBe(ptrStr + 7); +// }); + +// it("returns null if the substring is not found", () => { +// const str = "Hello, world!"; +// const substr = "WORLD"; +// const length = 13; +// const ptrStr = ptr(Buffer.from(`${str}\0`, "utf8")); +// const ptrSubstr = ptr(Buffer.from(`${substr}\0`, "utf8")); +// const result = symbols.ft_strnstr(ptrStr, ptrSubstr, length); +// expect(result).toBe(null); +// }); +// }); + +// // atoi +// //============================================================================= +// describe("atoi", () => { +// it("converts a valid string to an integer", () => { +// const numberStr = "12345"; +// const ptrNumberStr = ptr(Buffer.from(`${numberStr}\0`, "utf8")); +// const result = symbols.ft_atoi(ptrNumberStr); +// expect(result).toBe(12345); +// }); + +// it("returns 0 for an empty string", () => { +// const emptyStr = ""; +// const ptrEmptyStr = ptr(Buffer.from(`${emptyStr}\0`, "utf8")); +// const result = symbols.ft_atoi(ptrEmptyStr); +// expect(result).toBe(0); +// }); + +// it("ignores leading whitespace characters", () => { +// const numberStr = " 789"; +// const ptrNumberStr = ptr(Buffer.from(`${numberStr}\0`, "utf8")); +// const result = symbols.ft_atoi(ptrNumberStr); +// expect(result).toBe(789); +// }); + +// it("handles negative numbers", () => { +// const numberStr = "-42"; +// const ptrNumberStr = ptr(Buffer.from(`${numberStr}\0`, "utf8")); +// const result = symbols.ft_atoi(ptrNumberStr); +// expect(result).toBe(-42); +// }); + +// it("stops parsing when encountering non-numeric characters", () => { +// const numberStr = "123abc"; +// const ptrNumberStr = ptr(Buffer.from(`${numberStr}\0`, "utf8")); +// const result = symbols.ft_atoi(ptrNumberStr); +// expect(result).toBe(123); +// }); +// }); diff --git a/projects/lib/start.sh b/projects/lib/start.sh new file mode 100644 index 0000000..9ba077a --- /dev/null +++ b/projects/lib/start.sh @@ -0,0 +1,37 @@ +#!/bin/bash +#============================================================================== + +# Exported variables from container: +# - ID = A unique identifier for the runner +# - TMP_DIR = A temporary directory for the runner +# - GIT_DIR = The directory where the repository is cloned +# - WRK_DIR = The directory where the tests are run + +OBJ_DIR="$TMP_DIR/obj" + +#============================================================================== + +# Build the project +function build() { + echo "[+] ============================================================================" + echo "[+] Building ..." + make -C $GIT_DIR -j4 + + rm -rf $OBJ_DIR && mkdir -p $OBJ_DIR + find $GIT_DIR -name "Makefile" -exec cp {} $WRK_DIR \; + find $GIT_DIR -name "*.o" -exec cp {} $OBJ_DIR \; + + # Build into a shared library for dlopen + cc -shared -o $WRK_DIR/libft.so $OBJ_DIR/*.o +} + +# Run the tests +function run() { + build + echo "[+] ============================================================================" + echo "[+] Running tests ..." + cd $WRK_DIR && ls -laF && bun test +} + +set -e +run \ No newline at end of file diff --git a/projects/libft/config.json b/projects/libft/config.json deleted file mode 100644 index e052634..0000000 --- a/projects/libft/config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "enabled": true, - "timeout": 20 -} diff --git a/projects/libft/index.test.ts b/projects/libft/index.test.ts deleted file mode 100644 index 8ba81d4..0000000 --- a/projects/libft/index.test.ts +++ /dev/null @@ -1,570 +0,0 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= - -import { sleep } from "bun"; -import { dlopen, FFIType, ptr, toArrayBuffer } from "bun:ffi"; -import { describe, expect, it, beforeAll, afterAll } from "bun:test"; - -/** - * WARNING: bun:ffi does not manage memory for you. - * You must free the memory when you're done with it. - * - * This is very important regarding functions that return allocated memory. - * (ft_split, ft_strtrim, ft_itoa, etc.) - * - * Might be useful for code execution of C files directly: - * @see https://github.com/TinyCC/tinycc - * - * TODO: What happens when you segfault in a function? - */ -//============================================================================= - -// TODO: Come up with a neater way to generate the type definitions? -// NOTE(W2): Convert the static library to a shared library for dlopen to work. -const { symbols, close } = dlopen("libft.so", { - ft_strdup: { - returns: FFIType.ptr, - args: [FFIType.ptr], - }, - ft_strlen: { - returns: FFIType.i32, - args: [FFIType.ptr], - }, - ft_isalpha: { - returns: FFIType.i32, - args: [FFIType.i32], - }, - ft_isdigit: { - returns: FFIType.i32, - args: [FFIType.i32], - }, - ft_isalnum: { - returns: FFIType.i32, - args: [FFIType.i32], - }, - ft_isascii: { - returns: FFIType.i32, - args: [FFIType.i32], - }, - ft_isprint: { - returns: FFIType.i32, - args: [FFIType.i32], - }, - ft_memset: { - returns: FFIType.ptr, - args: [FFIType.ptr, FFIType.i32, FFIType.i32], - }, - ft_bzero: { - returns: FFIType.ptr, - args: [FFIType.ptr, FFIType.i32], - }, - ft_memcpy: { - returns: FFIType.ptr, - args: [FFIType.ptr, FFIType.ptr, FFIType.i32], - }, - ft_memmove: { - returns: FFIType.ptr, - args: [FFIType.ptr, FFIType.ptr, FFIType.i32], - }, - ft_strlcpy: { - returns: FFIType.i32, - args: [FFIType.ptr, FFIType.ptr, FFIType.i32], - }, - ft_strlcat: { - returns: FFIType.i32, - args: [FFIType.ptr, FFIType.ptr, FFIType.i32], - }, - ft_toupper: { - returns: FFIType.i32, - args: [FFIType.i32], - }, - ft_tolower: { - returns: FFIType.i32, - args: [FFIType.i32], - }, - ft_strchr: { - returns: FFIType.ptr, - args: [FFIType.ptr, FFIType.i32], - }, - ft_strrchr: { - returns: FFIType.ptr, - args: [FFIType.ptr, FFIType.i32], - }, - ft_strncmp: { - returns: FFIType.i32, - args: [FFIType.ptr, FFIType.ptr, FFIType.i32], - }, - ft_memchr: { - returns: FFIType.ptr, - args: [FFIType.ptr, FFIType.i32, FFIType.i32], - }, - ft_memcmp: { - returns: FFIType.i32, - args: [FFIType.ptr, FFIType.ptr, FFIType.i32], - }, - ft_strnstr: { - returns: FFIType.ptr, - args: [FFIType.ptr, FFIType.ptr, FFIType.i32], - }, - ft_atoi: { - returns: FFIType.i32, - args: [FFIType.ptr], - }, -}); - -beforeAll(() => { - ["SIGINT", "SIGTERM", "SIGHUP"].forEach((signal) => { - process.on(signal, () => { - close(); - process.exit(1); - }); - }); -}); - -afterAll(() => close()); - -// makefile -//============================================================================= -describe("makefile", () => { - it("compiles with '-Wextra -Werror -Wall'", async () => { - const makefile = Bun.file("Makefile"); - const text = await makefile.text(); - const requiredFlags = ["-Wall", "-Wextra", "-Werror"]; - - expect(requiredFlags.every((flag) => text.includes(flag))).toBe(true); - }); -}); - -// strdup -//============================================================================= -describe("strdup", () => { - it("duplicates a string", () => { - const sample = "Hello, world!"; - const ptrSample = ptr(Buffer.from(`${sample}\0`, "utf8")); - const result = toArrayBuffer(symbols.ft_strdup(ptrSample), 0, sample.length); - const output = Buffer.from(result).toString("utf8"); - expect(output).toBe(sample); - }); - - it("returns null if the string is empty", () => { - const emptyStr = Buffer.from(`\0`, "utf8"); - expect(symbols.ft_strdup(ptr(emptyStr))).toBe(null); - }); -}); - -// isalpha -//============================================================================= -describe("isalpha", () => { - it("valid alphabetic characters", () => { - const alphabeticChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - for (const char of alphabeticChars) { - expect(symbols.ft_isalpha(char.charCodeAt(0))).toBe(1); - } - }); - - it("invalid non-alphabetic characters", () => { - const nonAlphabeticChars = "0123456789!@#$%^&*()_+-=[]{};':\",.<>?/\\|"; - for (const char of nonAlphabeticChars) { - expect(symbols.ft_isalpha(char.charCodeAt(0))).toBe(0); - } - }); -}); - -// isdigit -//============================================================================= -describe("isdigit", () => { - it("valid numeric characters", () => { - const numericChars = "0123456789"; - for (const char of numericChars) { - expect(symbols.ft_isdigit(char.charCodeAt(0))).toBe(1); - } - }); - - it("invalid non-numeric characters", () => { - const nonNumericChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+-=[]{};':\",.<>?/\\|"; - for (const char of nonNumericChars) { - expect(symbols.ft_isdigit(char.charCodeAt(0))).toBe(0); - } - }); -}); - -// isalnum -//============================================================================= -describe("isalnum", () => { - it("valid alphanumeric characters", () => { - const alphanumericChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - for (const char of alphanumericChars) { - expect(symbols.ft_isalnum(char.charCodeAt(0))).toBe(1); - } - }); - - it("invalid non-alphanumeric characters", () => { - const nonAlphanumericChars = "!@#$%^&*()_+-=[]{};':\",.<>?/\\|"; - for (const char of nonAlphanumericChars) { - expect(symbols.ft_isalnum(char.charCodeAt(0))).toBe(0); - } - }); -}); - -// isascii -//============================================================================= -describe("isascii", () => { - it("valid ASCII characters", () => { - for (let i = 0; i < 128; i++) { - expect(symbols.ft_isascii(i)).toBe(1); - } - }); - - it("invalid non-ASCII characters", () => { - for (let i = 128; i < 256; i++) { - expect(symbols.ft_isascii(i)).toBe(0); - } - }); -}); - -// isprint -//============================================================================= -describe("isprint", () => { - it("valid printable characters", () => { - const printableChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=[]{};':\",.<>?/\\| "; - for (const char of printableChars) { - expect(symbols.ft_isprint(char.charCodeAt(0))).toBe(1); - } - }); - - it("invalid non-printable characters", () => { - for (let i = 0; i < 32; i++) { - expect(symbols.ft_isprint(i)).toBe(0); - } - }); -}); - -// strlen -//============================================================================= -describe("strlen", () => { - it("normal string", () => { - const sample = "Hello, world!"; - const str = ptr(Buffer.from(`${sample}\0`, "utf8")); - expect(symbols.ft_strlen(str)).toBe(sample.length); - }); - - it("empty string", () => { - const str = ptr(Buffer.from(`\0`, "utf8")); - expect(symbols.ft_strlen(str)).toBe(0); - }); -}); - -// memset -//============================================================================= -describe("memset", () => { - it("set characters to a value", () => { - const size = 42; - const value = 42; // * - const buffer = Buffer.alloc(size); - - symbols.ft_memset(ptr(buffer), value, size); - buffer.forEach((byte) => expect(byte).toBe(value)); - }); -}); - -// bzero (Note: bzero is equivalent to memset with value 0) -//============================================================================= -describe("bzero", () => { - it("zeroes out a buffer", () => { - const size = 10; - const buffer = Buffer.alloc(size, 42); - - symbols.ft_bzero(ptr(buffer), size); - buffer.forEach((byte) => expect(byte).toBe(0)); - }); -}); - -// memcpy -//============================================================================= -describe("memcpy", () => { - it("copies data from source to destination", () => { - const source = Buffer.from("Hello, world!\0"); - const destination = Buffer.alloc(source.length); - - symbols.ft_memcpy(ptr(destination), ptr(source), source.length); - expect(destination.toString("utf8")).toBe(source.toString("utf8")); - }); -}); - -// ft_memmove -//============================================================================= -describe("memmove", () => { - it("copies overlapping data from source to destination", () => { - const source = Buffer.from("Hello, world!\0"); - const destination = Buffer.alloc(source.length); - - symbols.ft_memmove(ptr(source), ptr(destination), source.length); - expect(destination.toString("utf8")).toBe(source.toString("utf8")); - }); -}); - -// strlcpy -//============================================================================= -describe("strlcpy", () => { - it("copies a string to a destination buffer", () => { - const source = "Hello, world!"; - const destination = Buffer.alloc(source.length + 1); - const ptrSource = ptr(Buffer.from(`${source}\0`, "utf8")); - const ptrDestination = ptr(destination); - - const result = symbols.ft_strlcpy(ptrDestination, ptrSource, destination.length); - expect(destination.toString("utf8")).toBe(Buffer.from(`${source}\0`, "utf8").toString("utf8")); - expect(result).toBe(source.length); - }); -}); - -// strlcat -//============================================================================= -describe("strlcat", () => { - it("concatenates two strings to a destination buffer", () => { - const source1 = "Hello, "; - const source2 = "world!"; - const expected = "Hello, world!"; - const destination = Buffer.alloc(source1.length + source2.length + 1); - - const ptrSource1 = ptr(Buffer.from(`${source1}\0`, "utf8")); - const ptrSource2 = ptr(Buffer.from(`${source2}\0`, "utf8")); - const ptrDestination = ptr(destination); - - symbols.ft_strlcpy(ptrDestination, ptrSource1, destination.length); - const result = symbols.ft_strlcat(ptrDestination, ptrSource2, destination.length); - expect(destination.toString("utf8")).toBe(Buffer.from(`${expected}\0`, "utf8").toString("utf8")); - expect(result).toBe(expected.length); - }); -}); - -// toupper -//============================================================================= -describe("toupper", () => { - it("converts lowercase characters to uppercase", () => { - const lowercaseChars = "abcdefghijklmnopqrstuvwxyz"; - const uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - for (let i = 0; i < lowercaseChars.length; i++) { - const char = lowercaseChars[i]; - const expectedUppercaseChar = uppercaseChars[i]; - expect(symbols.ft_toupper(char.charCodeAt(0))).toBe(expectedUppercaseChar.charCodeAt(0)); - } - }); - - it("leaves non-lowercase characters unchanged", () => { - const nonLowercaseChars = "0123456789!@#$%^&*()_+-=[]{};':\",.<>?/\\|ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - for (const char of nonLowercaseChars) { - expect(symbols.ft_toupper(char.charCodeAt(0))).toBe(char.charCodeAt(0)); - } - }); -}); - -// tolower -//============================================================================= -describe("tolower", () => { - it("converts uppercase characters to lowercase", () => { - const lowercaseChars = "abcdefghijklmnopqrstuvwxyz"; - const uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - for (let i = 0; i < lowercaseChars.length; i++) { - const char = uppercaseChars[i]; - const expectedLowercaseChar = lowercaseChars[i]; - expect(symbols.ft_tolower(char.charCodeAt(0))).toBe(expectedLowercaseChar.charCodeAt(0)); - } - }); - - it("leaves non-uppercase characters unchanged", () => { - const nonUppercaseChars = "0123456789!@#$%^&*()_+-=[]{};':\",.<>?/\\|abcdefghijklmnopqrstuvwxyz"; - for (const char of nonUppercaseChars) { - expect(symbols.ft_tolower(char.charCodeAt(0))).toBe(char.charCodeAt(0)); - } - }); -}); - -// strchr -//============================================================================= -describe("strchr", () => { - it("finds the first occurrence of a character in a string", () => { - const str = "Hello, world!"; - const char = "o"; - const ptrStr = ptr(Buffer.from(`${str}\0`, "utf8")); - const result = symbols.ft_strchr(ptrStr, char.charCodeAt(0)); - expect(result).toBe(ptrStr + 4); - }); - - it("returns null if the character is not found", () => { - const str = "Hello, world!"; - const char = "z"; - const ptrStr = ptr(Buffer.from(`${str}\0`, "utf8")); - const result = symbols.ft_strchr(ptrStr, char.charCodeAt(0)); - expect(result).toBe(null); - }); -}); - -// strrchr -//============================================================================= -describe("strrchr", () => { - it("finds the last occurrence of a character in a string", () => { - const str = "Hello, world!"; - const char = "o"; - const ptrStr = ptr(Buffer.from(`${str}\0`, "utf8")); - const result = symbols.ft_strrchr(ptrStr, char.charCodeAt(0)); - expect(result).toBe(ptrStr + 8); - }); - - it("returns null if the character is not found", () => { - const str = "Hello, world!"; - const char = "z"; - const ptrStr = ptr(Buffer.from(`${str}\0`, "utf8")); - const result = symbols.ft_strrchr(ptrStr, char.charCodeAt(0)); - expect(result).toBe(null); - }); -}); - -// strncmp -//============================================================================= -describe("strncmp", () => { - it("compares two strings up to a specified length", () => { - const str1 = "Hello"; - const str2 = "Hello, world!"; - const length = 5; - const ptrStr1 = ptr(Buffer.from(`${str1}\0`, "utf8")); - const ptrStr2 = ptr(Buffer.from(`${str2}\0`, "utf8")); - expect(symbols.ft_strncmp(ptrStr1, ptrStr2, length)).toBe(0); - }); - - it("returns a negative value if str1 is less than str2", () => { - const str1 = "Hell"; - const str2 = "Hello, world!"; - const length = 5; - const ptrStr1 = ptr(Buffer.from(`${str1}\0`, "utf8")); - const ptrStr2 = ptr(Buffer.from(`${str2}\0`, "utf8")); - expect(symbols.ft_strncmp(ptrStr1, ptrStr2, length)).toBeLessThan(0); - }); - - it("returns a positive value if str1 is greater than str2", () => { - const str1 = "Hello!"; - const str2 = "Hello, world!"; - const length = 5; - const ptrStr1 = ptr(Buffer.from(`${str1}\0`, "utf8")); - const ptrStr2 = ptr(Buffer.from(`${str2}\0`, "utf8")); - expect(symbols.ft_strncmp(ptrStr1, ptrStr2, length)).toBeGreaterThanOrEqual(0); - }); -}); - -// memchr -//============================================================================= -describe("memchr", () => { - it("finds the first occurrence of a byte in a memory region", () => { - const data = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x45]); // ABCDE - const byte = 0x43; // C - - const ptrData = ptr(data); - const result = symbols.ft_memchr(ptrData, byte, data.length); - expect(result).toBe(ptrData + 2); - }); - - it("returns null if the byte is not found", () => { - const data = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x45]); // ABCDE - const byte = 0x46; // F - - const ptrData = ptr(data); - const result = symbols.ft_memchr(ptrData, byte, data.length); - expect(result).toBe(null); - }); -}); - -// memcmp -//============================================================================= -describe("memcmp", () => { - it("compares two memory regions up to a specified length", () => { - const data1 = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x45]); // ABCDE - const data2 = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x46]); // ABCDF - const length = 5; - const ptrData1 = ptr(data1); - const ptrData2 = ptr(data2); - expect(symbols.ft_memcmp(ptrData1, ptrData2, length)).toBe(-1); - }); - - it("returns 0 if both memory regions are equal", () => { - const data1 = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x45]); // ABCDE - const data2 = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x45]); // ABCDE - const length = 5; - const ptrData1 = ptr(data1); - const ptrData2 = ptr(data2); - expect(symbols.ft_memcmp(ptrData1, ptrData2, length)).toBe(0); - }); - - it("returns a positive value if data1 is greater than data2", () => { - const data1 = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x47]); // ABCDG - const data2 = Buffer.from([0x41, 0x42, 0x43, 0x44, 0x46]); // ABCDF - const length = 5; - const ptrData1 = ptr(data1); - const ptrData2 = ptr(data2); - expect(symbols.ft_memcmp(ptrData1, ptrData2, length)).toBe(1); - }); -}); - -// strnstr -//============================================================================= -describe("strnstr", () => { - it("finds the first occurrence of a substring in a string up to a specified length", () => { - const str = "Hello, world!"; - const substr = "world"; - const length = 13; - const ptrStr = ptr(Buffer.from(`${str}\0`, "utf8")); - const ptrSubstr = ptr(Buffer.from(`${substr}\0`, "utf8")); - const result = symbols.ft_strnstr(ptrStr, ptrSubstr, length); - expect(result).toBe(ptrStr + 7); - }); - - it("returns null if the substring is not found", () => { - const str = "Hello, world!"; - const substr = "WORLD"; - const length = 13; - const ptrStr = ptr(Buffer.from(`${str}\0`, "utf8")); - const ptrSubstr = ptr(Buffer.from(`${substr}\0`, "utf8")); - const result = symbols.ft_strnstr(ptrStr, ptrSubstr, length); - expect(result).toBe(null); - }); -}); - -// atoi -//============================================================================= -describe("atoi", () => { - it("converts a valid string to an integer", () => { - const numberStr = "12345"; - const ptrNumberStr = ptr(Buffer.from(`${numberStr}\0`, "utf8")); - const result = symbols.ft_atoi(ptrNumberStr); - expect(result).toBe(12345); - }); - - it("returns 0 for an empty string", () => { - const emptyStr = ""; - const ptrEmptyStr = ptr(Buffer.from(`${emptyStr}\0`, "utf8")); - const result = symbols.ft_atoi(ptrEmptyStr); - expect(result).toBe(0); - }); - - it("ignores leading whitespace characters", () => { - const numberStr = " 789"; - const ptrNumberStr = ptr(Buffer.from(`${numberStr}\0`, "utf8")); - const result = symbols.ft_atoi(ptrNumberStr); - expect(result).toBe(789); - }); - - it("handles negative numbers", () => { - const numberStr = "-42"; - const ptrNumberStr = ptr(Buffer.from(`${numberStr}\0`, "utf8")); - const result = symbols.ft_atoi(ptrNumberStr); - expect(result).toBe(-42); - }); - - it("stops parsing when encountering non-numeric characters", () => { - const numberStr = "123abc"; - const ptrNumberStr = ptr(Buffer.from(`${numberStr}\0`, "utf8")); - const result = symbols.ft_atoi(ptrNumberStr); - expect(result).toBe(123); - }); -}); diff --git a/projects/libft/start.sh b/projects/libft/start.sh deleted file mode 100644 index a89dfeb..0000000 --- a/projects/libft/start.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -#============================================================================== - -ID=$(xxd -l 16 -ps /dev/urandom | tr -d ' \n') -ProjectDIR="/tmp/$ID/project" -ObjectsDIR="/tmp/$ID/objects" -Home="/home/runner/" - -# Functions -#============================================================================== - -# Fetch the project -function gitCloneCommit() { - echo "[+] Cloning $GIT_URL" - git clone $GIT_URL $ProjectDIR -b $GIT_BRANCH --recurse-submodules --quiet - cd $ProjectDIR - - echo "[+] Switching to $GIT_COMMIT" - git checkout $GIT_COMMIT --quiet - cd - > /dev/null -} - -# Build the project -function build() { - echo "[+] Building ..." - make -C $ProjectDIR -j4 - - rm -rf $ObjectsDIR && mkdir -p $ObjectsDIR - find $ProjectDIR -name "Makefile" -exec cp {} $Home \; - find $ProjectDIR -name "*.o" -exec cp {} $ObjectsDIR \; - - gcc -shared -o $Home/libft.so $ObjectsDIR/*.o - cp /app/index.test.ts $Home -} - -# Run the tests -function run() { - echo "[+] Running tests ..." - su - runner -s /bin/rksh -c "bun test" -} - -# Main -#============================================================================== -set -e - -gitCloneCommit -build -run diff --git a/public/assets/styles.css b/public/assets/styles.css deleted file mode 100644 index d03b0c9..0000000 --- a/public/assets/styles.css +++ /dev/null @@ -1,146 +0,0 @@ -:root { - color-scheme: light dark; - - --border: #a8a8a8; - --header: #7a7a7a; - --background: #f0f2f5; - --foreground: #464646; - --card: #b1b1b1; - - --box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.2); - - @media (prefers-color-scheme: dark) { - --border: #50505f; - --background: #29292f; - --header: #1f1f23; - --foreground: #fff; - --card: #3a3a3a; - } - - color: var(--foreground); -} - -/* ========================================================================= */ - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -header { - background: var(--header); - border-bottom: 2px solid var(--border); - padding: 2rem 24px; - color: #fff; -} - -body { - height: 100vh; - display: grid; - grid-template-rows: auto 1fr; - color: var(--foreground); - background: var(--background); - font-family: 'Inter', sans-serif; - font-size: 16px; -} - -main { - padding: 2rem 24px; -} - -pre { - display: inline-block; -} - -hr { - border: none; - border-top: 2px solid var(--border); - margin: 8px 0; -} - -table { - width: 100%; - border-collapse: collapse; -} - -thead tr { - color: var(--foreground); -} - -th, td { - padding: 10px; - text-align: left; -} - -th { - background-color: var(--background); - - &:first-child { - border-top-left-radius: 8px; - } - &:last-child { - border-top-right-radius: 8px; - } -} - -tr { - color: #fff; - background-color: var(--card); - - &:nth-child(even) { - color: var(--foreground); - background-color: var(--background); - } - - &:last-child td:first-child { - border-bottom-left-radius: 8px; - } - - &:last-child td:last-child { - border-bottom-right-radius: 8px; - } - - &:not(:last-child) { - border-bottom: 2px solid var(--border); - } -} - -/* ========================================================================= */ - -.table-wrapper { - border-radius: 8px; - border: 2px solid var(--border); - margin-top: 10px; - box-shadow: var(--box-shadow); -} - -.toolbar { - display: flex; - justify-content: space-between; - gap: 8px; -} - -#kill-button { - transition: background-color 0.2s ease-in-out; - background-color: rgb(203, 0, 0); - color: #fff; - border: 2px solid white; - border-radius: 8px; - padding: 8px 16px; - text-transform: uppercase; - - &:hover { - background-color: #ff0000; - cursor: pointer; - } -} - -#counter { - display: inline-block; - padding: 8px 4px; - border-radius: 8px; - background: var(--card); - border: 2px solid var(--border); - box-shadow: var(--box-shadow); -} diff --git a/public/base.ts b/public/base.ts deleted file mode 100644 index 2783560..0000000 --- a/public/base.ts +++ /dev/null @@ -1,27 +0,0 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= - -import * as elements from "typed-html"; - -//============================================================================= - -/** The base HTML template for the entire application. */ -const Root = ({ children }: elements.Children) => ` - - - - - - Grader Dashboard - - - - - - -${children} -`; - -export { elements, Root }; diff --git a/public/index.tsx b/public/index.tsx deleted file mode 100644 index 4a10d76..0000000 --- a/public/index.tsx +++ /dev/null @@ -1,46 +0,0 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= - -import { OsInfo } from "./os"; -import { hostname } from "os"; -import { Root, elements } from "./base"; - -//============================================================================= - -export default async function index(html: (value: string) => Response) { - return html( - - -
-

Grader: {`${hostname()}@${process.pid}`}

-
-
-

Welcome to the Dashboard

-
-
- - {/* Container counter */} -
- Containers: -
-								0
-							
-
- - {/* Kill */} - -
- -
- -
- ); -} diff --git a/public/os.tsx b/public/os.tsx deleted file mode 100644 index 430e2e5..0000000 --- a/public/os.tsx +++ /dev/null @@ -1,42 +0,0 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= - -import os from "os"; -import { elements } from "./base"; - -//============================================================================= - -export function OsInfo(): JSX.Element { - const info = { - Platform: process.platform, - Arch: process.arch, - BunVersion: Bun.version, - CPU: os.cpus()[0].model, - Cores: os.cpus().length, - Uptime: process.uptime().toPrecision(4), - - }; - - return ( -
- - - - - - - - - {Object.entries(info).map(([key, value]) => ( - - - - - ))} - -
KeyValue
{key}{value}
-
- ); -} diff --git a/scripts/new.sh b/scripts/new.sh index c9828ae..5c7b0bd 100644 --- a/scripts/new.sh +++ b/scripts/new.sh @@ -12,7 +12,6 @@ if [ -d "$dir" ]; then exit 1 fi - test_template=' //============================================================================= // W2Wizard, Amsterdam @ 2018-2023 @@ -105,17 +104,8 @@ build run ' - -config_template=' -{ - "enabled": true, - "timeout": 25 -} -' - mkdir -p "$dir" && cd "$dir" echo "$script_template" > start.sh echo "$test_template" > index.test.ts -echo "$config_template" > config.json echo "New project $1 created in $dir" chmod +x start.sh diff --git a/scripts/openapi.ts b/scripts/openapi.ts new file mode 100644 index 0000000..9cdef17 --- /dev/null +++ b/scripts/openapi.ts @@ -0,0 +1,13 @@ +// ============================================================================ +// Copyright (C) 2024 W2Wizard +// See README in the root of the project for license details. +// ============================================================================ + +import { $ } from "bun"; + +// NOTE(W2): Due to docker still using Swagger 2.0, we need to convert it to OpenAPI 3.0. +await $`curl -o openapi.yaml https://docs.docker.com/reference/engine/v${Bun.env.DOCKER_VERSION ?? "1.44"}.yaml`; +await $`curl -o openapi.json https://converter.swagger.io/api/convert -H "Content-Type: application/yaml" --data-binary "@./openapi.yaml"`; +await $`bunx openapi-typescript ./openapi.json -o ./src/docker/dockerapi.d.ts`; +await $`rm openapi.yaml openapi.json`; +console.log("Docker OpenAPI types generated!"); diff --git a/src/docker/api.ts b/src/docker/api.ts index 1a7360b..b663e16 100644 --- a/src/docker/api.ts +++ b/src/docker/api.ts @@ -1,231 +1,70 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= - -import Container from "./container"; -import { RawRequest } from "@/http"; -import Modem, { ModemCB } from "./modem"; - -//============================================================================= - -/** Contains all the docker api functions. */ -export namespace Docker { - /** - * Parse the response buffer from the docker daemon. - * ```md - * Example response from the docker daemon: - * 01 00 00 00 00 00 00 1f 52 6f 73 65 73 20 61 72 65 ... - * โ”‚ โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€ R o s e s a r e ... - * โ”‚ โ”‚ โ”‚ - * โ””stdout โ”‚ โ”‚ - * โ”‚ โ””โ”€ 0x0000001f = 31 bytes (including the \n at the end) - * unused - * ``` - * - * @note Each line is always ending with a newline character. - * @see https://ahmet.im/blog/docker-logs-api-binary-format-explained/ - */ - export function parseLogBuffer(inBuff: ArrayBuffer): string { - let data = ""; - let offset = 0; - const buffer = Buffer.from(inBuff); - - while (offset < buffer.length) { - const length = buffer.readUInt32BE((offset += 4)); - const line = buffer.subarray((offset += 4), (offset += length)); - const spacePos = line.indexOf(" "); - - // Some formatting of the timestamp. - // BUG(W2): Will cause bugs if we request logs without timestamps. - const message = line.subarray(spacePos + 1); - const date = line.subarray(0, spacePos).subarray(0, 19); - date.set([32], date.indexOf("T")); - - data += `[${date}] ${message}`; - } - - return ( - data - // Regex away all the color codes. - .replace(/\033\[[0-9;]*m|\[\d+m/g, "") - .replaceAll("(pass)", "(โœ…)") - .replaceAll("(fail)", "(โŒ)") - ); - } - - /** - * Check if the docker daemon is running. - * @returns `true` if the daemon is running, `false` otherwise. - * @see https://docs.docker.com/engine/api/v1.43/#tag/System/operation/SystemPingHead - */ - export function ping(modem: Modem, cb?: ModemCB) { - const request = new RawRequest(`${modem.endpoint}/_ping`, { - method: "GET", - headers: { - Host: "localhost", - }, - }); - - modem.send(request, cb); - } - - /** - * List all containers. - * @returns A list of containers currently running as JSON. - * @see https://docs.docker.com/engine/api/v1.43/#operation/ContainerList - */ - export function list(modem: Modem, cb?: ModemCB) { - if (!modem.isConnected) throw new Error("Not connected to docker daemon."); - - const request = new RawRequest(`${modem.endpoint}/containers/json`, { - method: "GET", - headers: { - Host: "localhost", - }, - }); - - modem.send(request, cb); - } - - /** - * Create a new container. - * @param cb A callback that will be called when the response is received. - * - * @see https://docs.docker.com/engine/api/v1.43/#operation/ContainerCreate - */ - export function create(modem: Modem, container: Container, cb?: ModemCB) { - if (!modem.isConnected) throw new Error("Not connected to docker daemon."); - - const payload = JSON.stringify(container.payload); - const request = new RawRequest(`${modem.endpoint}/containers/create`, { - method: "POST", - headers: { - Host: "localhost", - "Content-Type": "application/json", - "Content-Length": payload.length.toString(), - }, - body: payload, - }); - - modem.send(request, cb); - } - - /** - * Start a container. - * @param id The id of the container to start. - * @param cb A callback that will be called when the response is received. - * - * @see https://docs.docker.com/engine/api/v1.43/#operation/ContainerStart - */ - export function start(modem: Modem, id: string, cb?: ModemCB) { - if (!modem.isConnected) throw new Error("Not connected to docker daemon."); - - const request = new RawRequest(`${modem.endpoint}/containers/${id}/start`, { - method: "POST", - headers: { Host: "localhost" }, - }); - - modem.send(request, cb); - } - - /** - * Stop a container. - * @param id The id of the container to stop. - * @param cb A callback that will be called when the response is received. - * - * @see https://docs.docker.com/engine/api/v1.43/#operation/ContainerStop - */ - export function stop(modem: Modem, id: string, cb?: ModemCB) { - if (!modem.isConnected) throw new Error("Not connected to docker daemon."); - - const request = new RawRequest(`${modem.endpoint}/containers/${id}/stop`, { - method: "POST", - headers: { Host: "localhost" }, - }); - - modem.send(request, cb); +// ============================================================================ +// Copyright (C) 2024 W2Wizard +// See README in the root of the project for license details. +// ============================================================================ + +import type { paths } from "./dockerapi"; +import createClient from "openapi-fetch"; + +// ============================================================================ + +/** + * Parse the response buffer from the docker daemon. + * ```md + * Example response from the docker daemon: + * 01 00 00 00 00 00 00 1f 52 6f 73 65 73 20 61 72 65 ... + * โ”‚ โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€ R o s e s a r e ... + * โ”‚ โ”‚ โ”‚ + * โ””stdout โ”‚ โ”‚ + * โ”‚ โ””โ”€ 0x0000001f = 31 bytes (including the \n at the end) + * unused + * ``` + * + * @note Each line is always ending with a newline character. + * @see https://ahmet.im/blog/docker-logs-api-binary-format-explained/ + */ +export function parseLogBuffer(buff: ArrayBuffer): string { + let data = ""; + let offset = 0; + const buffer = Buffer.from(buff); + + while (offset < buffer.length) { + const length = buffer.readUInt32BE((offset += 4)); + const line = buffer.subarray((offset += 4), (offset += length)); + const pos = line.indexOf(" "); + + // Some formatting of the timestamp. + // BUG(W2): Will cause bugs if we request logs without timestamps. + const message = line.subarray(pos + 1); + const date = line.subarray(0, pos).subarray(0, 19); + date.set([32], date.indexOf("T")); + + data += `[${date}] ${message}`; } - /** - * Wait for a container to finish. - * Blocks until the container stops, then returns the exit code. - * @param id The id of the container to wait for. - * @param cb A callback that will be called when the response is received. - * - * @see https://docs.docker.com/engine/api/v1.43/#operation/ContainerWait - */ - export function wait(modem: Modem, id: string, cb?: ModemCB) { - if (!modem.isConnected) throw new Error("Not connected to docker daemon."); - - const request = new RawRequest(`${modem.endpoint}/containers/${id}/wait`, { - method: "POST", - headers: { Host: "localhost" }, - }); - - modem.send(request, cb); - } - - /** - * Get the logs of a container. - * @param id The id of the container to get the logs from. - * @param cb A callback that will be called when the response is received. - * - * @see https://docs.docker.com/engine/api/v1.43/#operation/ContainerLogs - */ - export function getLogs( - modem: Modem, - id: string, - params: URLSearchParams, - cb?: ModemCB - ) { - if (!modem.isConnected) throw new Error("Not connected to docker daemon."); - - const url = `${modem.endpoint}/containers/${id}/logs?${params.toString()}`; - const request = new RawRequest(url, { - method: "GET", - headers: { - Host: "localhost", - Accept: "application/json", - }, - }); - - modem.send(request, cb); - } - - /** - * Remove a container. - * @param id The id of the container to remove. - * @param cb A callback that will be called when the response is received. - * - * @see https://docs.docker.com/engine/api/v1.43/#operation/ContainerDelete - */ - export function remove(modem: Modem, id: string, cb?: ModemCB) { - if (!modem.isConnected) throw new Error("Not connected to docker daemon."); - - const request = new RawRequest(`${modem.endpoint}/containers/${id}`, { - method: "DELETE", - headers: { Host: "localhost" }, - }); - - modem.send(request, cb); - } - - /** - * Kill a container. - * @param id The id of the container to kill. - * @param cb A callback that will be called when the response is received. - * - * @see https://docs.docker.com/engine/api/v1.43/#operation/ContainerKill - */ - export function kill(modem: Modem, id: string, cb?: ModemCB) { - if (!modem.isConnected) throw new Error("Not connected to docker daemon."); - - const request = new RawRequest(`${modem.endpoint}/containers/${id}/kill`, { - method: "POST", - headers: { Host: "localhost" }, - }); - - modem.send(request, cb); - } + return ( + data + // Regex away all the color codes. + .replace(/\033\[[0-9;]*m|\[\d+m/g, "") + .replaceAll("(pass)", "(โœ“)") + .replaceAll("(fail)", "(โœ—)") + ); } + +const socketFetch: typeof fetch = async (input, init) => { + return await fetch(input, { + // @ts-ignore // NOTE(W2): Bun supports this. + unix: "/var/run/docker.sock", + ...init, + }); +}; + +/** The docker client used to fetch requests with via unix socket */ +export const docker = createClient({ + baseUrl: `http://localhost/v${Bun.env.DOCKER_VERSION ?? "1.44"}`, + fetch: socketFetch, + headers: { + "Host": "localhost", + "User-Agent": Bun.env.SERVER ?? "robopeer", + }, +}); diff --git a/src/docker/container.ts b/src/docker/container.ts index 2a67820..96f7944 100644 --- a/src/docker/container.ts +++ b/src/docker/container.ts @@ -3,59 +3,90 @@ // See README and LICENSE files for details. //============================================================================= -import Modem from "./modem"; -import { Docker } from "./api"; +import { docker } from "./api"; +import type { components, operations } from "./dockerapi"; -//============================================================================= +// export enum ExitCode { +// Success = 0, +// MinorError = 1, +// MajorError = 2, +// Killed = 137, // SIGKILL +// Timeout = 124, // NOTE(W2): Requires coreutils from GNU. +// NotFound = 128, +// } -export enum ExitCode { - Success = 0, - MinorError = 1, - MajorError = 2, - Killed = 137, // SIGKILL - Timeout = 124, // NOTE(W2): Requires coreutils from GNU. - ScriptFail = 127, // The script is buggy. - NotFound = 128, -} +export type ContainerPayload = operations["ContainerCreate"]["requestBody"]["content"]["application/json"]; + +//============================================================================= /** A container to run docker commands in. */ export default class Container { - public payload: any; public id: string | null = null; - private modem: Modem | null = null; + private payload: ContainerPayload; - constructor(payload: any) { + constructor(payload: ContainerPayload) { this.payload = payload; } //= Public =// - /** Launch the container. */ - public async launch() { - this.modem = new Modem(); - await this.modem.connect(); - - const create = (modem: Modem, container: Container) => - new Promise((resolve, reject) => { - Docker.create(modem, container, async (res) => { - if (!res.ok) return reject(Error(await res.text())); - - const { Id } = (await res.json()) as { Id: string }; - return resolve(Id); - }); - }); - - const start = (modem: Modem, id: string) => - new Promise((resolve, reject) => { - Docker.start(modem, id, async (res) => { - if (!res.ok) return reject(Error(await res.text())); - return resolve(id); - }); - }); - - this.id = await create(this.modem, this); - await start(this.modem, this.id); - return this; + /** Create the container. */ + public async create() { + const { response, data, error } = await docker.POST("/containers/create", { + body: this.payload, + }); + + if (error) { + throw new Error(error.message); + } else if (!response.ok) { + throw new Error(response.statusText); + } + + this.id = data.Id; + } + + /** Start the container. */ + public async start() { + if (!this.id) { + await this.create(); + } + + const { response, error } = await docker.POST("/containers/{id}/start", { + params: { path: { id: this.id! } }, + }); + + if (error) { + throw new Error(error.message); + } else if (!response.ok) { + throw new Error(response.statusText); + } + } + + /** Get the logs of the container. */ + public async logs() { + if (!this.id) { + throw new Error("Container not created."); + } + + const { response, data, error } = await docker.GET("/containers/{id}/logs", { + parseAs: "arrayBuffer", + params: { + query: { + stdout: true, + stderr: true, + timestamps: true, + }, + path: { id: this.id } + }, + }); + + if (error) { + throw new Error(error.message); + } else if (!response.ok) { + throw new Error(response.statusText); + } + + return data; } /** @@ -64,70 +95,54 @@ export default class Container { * @returns The logs and exit code of the container. */ public async wait() { - if (!this.modem || !this.id) { - throw new Error("Container not launched."); + if (!this.id) { + throw new Error("Container not created."); } - // Promise for the exit code. - const waitCon = (modem: Modem, id: string) => - new Promise((resolve, reject) => { - Docker.wait(modem, id, async (res) => { - if (!res.ok) return reject(Error(await res.text())); - - const { StatusCode } = (await res.json()) as any; - return resolve(StatusCode); - }); - }); - - // Promise for the logs. - const logsCon = (modem: Modem, id: string) => - new Promise((resolve, reject) => { - const params = new URLSearchParams({ - stdout: "true", - stderr: "true", - timestamps: "true", - }); - - Docker.getLogs(modem, id, params, async (res) => { - if (!res.ok) return reject(Error(await res.text())); - return resolve(Docker.parseLogBuffer(await res.arrayBuffer())); - }); - }); - - const exitCode = await waitCon(this.modem, this.id); - const logs = await logsCon(this.modem, this.id); - return { exitCode, logs }; + const { response, data, error } = await docker.POST("/containers/{id}/wait", { + params: { path: { id: this.id } }, + }); + + if (error) { + throw new Error(error.message); + } else if (!response.ok) { + throw new Error(response.statusText); + } + + return data.StatusCode; } + /** Kill the container. */ public async kill() { - if (!this.modem || !this.id) { - throw new Error("Container not launched."); + if (!this.id) { + throw new Error("Container not created."); } - const kill = (modem: Modem, id: string) => - new Promise((resolve, reject) => { - Docker.kill(modem, id, async (res) => { - if (!res.ok) return reject(Error(await res.text())); - return resolve(); - }); - }); + const { response, error } = await docker.POST("/containers/{id}/kill", { + params: { path: { id: this.id } }, + }); - await kill(this.modem, this.id); + if (error) { + throw new Error(error.message); + } else if (!response.ok) { + throw new Error(response.statusText); + } } /** Remove the container. */ public async remove() { - if (!this.modem) throw new Error("Container not launched."); - - const remove = (modem: Modem, id: string) => - new Promise((resolve, reject) => { - Docker.remove(modem, id, async (res) => { - if (!res.ok) return reject(Error(await res.text())); - return resolve(); - }); - }); - - await remove(this.modem, this.id!); - this.modem.disconnect(); + if (!this.id) { + throw new Error("Container not created."); + } + + const { response, error } = await docker.DELETE("/containers/{id}", { + params: { path: { id: this.id } }, + }); + + if (error) { + throw new Error(error.message); + } else if (!response.ok) { + throw new Error(response.statusText); + } } } diff --git a/src/docker/dockerapi.d.ts b/src/docker/dockerapi.d.ts new file mode 100644 index 0000000..ea194c0 --- /dev/null +++ b/src/docker/dockerapi.d.ts @@ -0,0 +1,11393 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + + +export interface paths { + "/containers/json": { + /** + * List containers + * @description Returns a list of containers. For details on the format, see the + * [inspect endpoint](#operation/ContainerInspect). + * + * Note that it uses a different, smaller representation of a container + * than inspecting a single container. For example, the list of linked + * containers is not propagated . + */ + get: operations["ContainerList"]; + }; + "/containers/create": { + /** Create a container */ + post: operations["ContainerCreate"]; + }; + "/containers/{id}/json": { + /** + * Inspect a container + * @description Return low-level information about a container. + */ + get: operations["ContainerInspect"]; + }; + "/containers/{id}/top": { + /** + * List processes running inside a container + * @description On Unix systems, this is done by running the `ps` command. This endpoint + * is not supported on Windows. + */ + get: operations["ContainerTop"]; + }; + "/containers/{id}/logs": { + /** + * Get container logs + * @description Get `stdout` and `stderr` logs from a container. + * + * Note: This endpoint works only for containers with the `json-file` or + * `journald` logging driver. + */ + get: operations["ContainerLogs"]; + }; + "/containers/{id}/changes": { + /** + * Get changes on a containerโ€™s filesystem + * @description Returns which files in a container's filesystem have been added, deleted, + * or modified. The `Kind` of modification can be one of: + * + * - `0`: Modified ("C") + * - `1`: Added ("A") + * - `2`: Deleted ("D") + */ + get: operations["ContainerChanges"]; + }; + "/containers/{id}/export": { + /** + * Export a container + * @description Export the contents of a container as a tarball. + */ + get: operations["ContainerExport"]; + }; + "/containers/{id}/stats": { + /** + * Get container stats based on resource usage + * @description This endpoint returns a live stream of a containerโ€™s resource usage + * statistics. + * + * The `precpu_stats` is the CPU statistic of the *previous* read, and is + * used to calculate the CPU usage percentage. It is not an exact copy + * of the `cpu_stats` field. + * + * If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + * nil then for compatibility with older daemons the length of the + * corresponding `cpu_usage.percpu_usage` array should be used. + * + * On a cgroup v2 host, the following fields are not set + * * `blkio_stats`: all fields other than `io_service_bytes_recursive` + * * `cpu_stats`: `cpu_usage.percpu_usage` + * * `memory_stats`: `max_usage` and `failcnt` + * Also, `memory_stats.stats` fields are incompatible with cgroup v1. + * + * To calculate the values shown by the `stats` command of the docker cli tool + * the following formulas can be used: + * * used_memory = `memory_stats.usage - memory_stats.stats.cache` + * * available_memory = `memory_stats.limit` + * * Memory usage % = `(used_memory / available_memory) * 100.0` + * * cpu_delta = `cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage` + * * system_cpu_delta = `cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage` + * * number_cpus = `lenght(cpu_stats.cpu_usage.percpu_usage)` or `cpu_stats.online_cpus` + * * CPU usage % = `(cpu_delta / system_cpu_delta) * number_cpus * 100.0` + */ + get: operations["ContainerStats"]; + }; + "/containers/{id}/resize": { + /** + * Resize a container TTY + * @description Resize the TTY for a container. + */ + post: operations["ContainerResize"]; + }; + "/containers/{id}/start": { + /** Start a container */ + post: operations["ContainerStart"]; + }; + "/containers/{id}/stop": { + /** Stop a container */ + post: operations["ContainerStop"]; + }; + "/containers/{id}/restart": { + /** Restart a container */ + post: operations["ContainerRestart"]; + }; + "/containers/{id}/kill": { + /** + * Kill a container + * @description Send a POSIX signal to a container, defaulting to killing to the + * container. + */ + post: operations["ContainerKill"]; + }; + "/containers/{id}/update": { + /** + * Update a container + * @description Change various configuration options of a container without having to + * recreate it. + */ + post: operations["ContainerUpdate"]; + }; + "/containers/{id}/rename": { + /** Rename a container */ + post: operations["ContainerRename"]; + }; + "/containers/{id}/pause": { + /** + * Pause a container + * @description Use the freezer cgroup to suspend all processes in a container. + * + * Traditionally, when suspending a process the `SIGSTOP` signal is used, + * which is observable by the process being suspended. With the freezer + * cgroup the process is unaware, and unable to capture, that it is being + * suspended, and subsequently resumed. + */ + post: operations["ContainerPause"]; + }; + "/containers/{id}/unpause": { + /** + * Unpause a container + * @description Resume a container which has been paused. + */ + post: operations["ContainerUnpause"]; + }; + "/containers/{id}/attach": { + /** + * Attach to a container + * @description Attach to a container to read its output or send it input. You can attach + * to the same container multiple times and you can reattach to containers + * that have been detached. + * + * Either the `stream` or `logs` parameter must be `true` for this endpoint + * to do anything. + * + * See the [documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) + * for more details. + * + * ### Hijacking + * + * This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, + * and `stderr` on the same socket. + * + * This is the response from the daemon for an attach request: + * + * ``` + * HTTP/1.1 200 OK + * Content-Type: application/vnd.docker.raw-stream + * + * [STREAM] + * ``` + * + * After the headers and two new lines, the TCP connection can now be used + * for raw, bidirectional communication between the client and server. + * + * To hint potential proxies about connection hijacking, the Docker client + * can also optionally send connection upgrade headers. + * + * For example, the client sends this request to upgrade the connection: + * + * ``` + * POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + * Upgrade: tcp + * Connection: Upgrade + * ``` + * + * The Docker daemon will respond with a `101 UPGRADED` response, and will + * similarly follow with the raw stream: + * + * ``` + * HTTP/1.1 101 UPGRADED + * Content-Type: application/vnd.docker.raw-stream + * Connection: Upgrade + * Upgrade: tcp + * + * [STREAM] + * ``` + * + * ### Stream format + * + * When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), + * the HTTP Content-Type header is set to application/vnd.docker.multiplexed-stream + * and the stream over the hijacked connected is multiplexed to separate out + * `stdout` and `stderr`. The stream consists of a series of frames, each + * containing a header and a payload. + * + * The header contains the information which the stream writes (`stdout` or + * `stderr`). It also contains the size of the associated frame encoded in + * the last four bytes (`uint32`). + * + * It is encoded on the first eight bytes like this: + * + * ```go + * header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + * ``` + * + * `STREAM_TYPE` can be: + * + * - 0: `stdin` (is written on `stdout`) + * - 1: `stdout` + * - 2: `stderr` + * + * `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size + * encoded as big endian. + * + * Following the header is the payload, which is the specified number of + * bytes of `STREAM_TYPE`. + * + * The simplest way to implement this protocol is the following: + * + * 1. Read 8 bytes. + * 2. Choose `stdout` or `stderr` depending on the first byte. + * 3. Extract the frame size from the last four bytes. + * 4. Read the extracted size and output it on the correct output. + * 5. Goto 1. + * + * ### Stream format when using a TTY + * + * When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), + * the stream is not multiplexed. The data exchanged over the hijacked + * connection is simply the raw data from the process PTY and client's + * `stdin`. + */ + post: operations["ContainerAttach"]; + }; + "/containers/{id}/attach/ws": { + /** Attach to a container via a websocket */ + get: operations["ContainerAttachWebsocket"]; + }; + "/containers/{id}/wait": { + /** + * Wait for a container + * @description Block until a container stops, then returns the exit code. + */ + post: operations["ContainerWait"]; + }; + "/containers/{id}": { + /** Remove a container */ + delete: operations["ContainerDelete"]; + }; + "/containers/{id}/archive": { + /** + * Get an archive of a filesystem resource in a container + * @description Get a tar archive of a resource in the filesystem of container id. + */ + get: operations["ContainerArchive"]; + /** + * Extract an archive of files or folders to a directory in a container + * @description Upload a tar archive to be extracted to a path in the filesystem of container id. + * `path` parameter is asserted to be a directory. If it exists as a file, 400 error + * will be returned with message "not a directory". + */ + put: operations["PutContainerArchive"]; + /** + * Get information about files in a container + * @description A response header `X-Docker-Container-Path-Stat` is returned, containing + * a base64 - encoded JSON object with some filesystem header information + * about the path. + */ + head: operations["ContainerArchiveInfo"]; + }; + "/containers/prune": { + /** Delete stopped containers */ + post: operations["ContainerPrune"]; + }; + "/images/json": { + /** + * List Images + * @description Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image. + */ + get: operations["ImageList"]; + }; + "/build": { + /** + * Build an image + * @description Build an image from a tar archive with a `Dockerfile` in it. + * + * The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + * + * The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + * + * The build is canceled if the client drops the connection by quitting or being killed. + */ + post: operations["ImageBuild"]; + }; + "/build/prune": { + /** Delete builder cache */ + post: operations["BuildPrune"]; + }; + "/images/create": { + /** + * Create an image + * @description Pull or import an image. + */ + post: operations["ImageCreate"]; + }; + "/images/{name}/json": { + /** + * Inspect an image + * @description Return low-level information about an image. + */ + get: operations["ImageInspect"]; + }; + "/images/{name}/history": { + /** + * Get the history of an image + * @description Return parent layers of an image. + */ + get: operations["ImageHistory"]; + }; + "/images/{name}/push": { + /** + * Push an image + * @description Push an image to a registry. + * + * If you wish to push an image on to a private registry, that image must + * already have a tag which references the registry. For example, + * `registry.example.com/myimage:latest`. + * + * The push is cancelled if the HTTP connection is closed. + */ + post: operations["ImagePush"]; + }; + "/images/{name}/tag": { + /** + * Tag an image + * @description Tag an image so that it becomes part of a repository. + */ + post: operations["ImageTag"]; + }; + "/images/{name}": { + /** + * Remove an image + * @description Remove an image, along with any untagged parent images that were + * referenced by that image. + * + * Images can't be removed if they have descendant images, are being + * used by a running container or are being used by a build. + */ + delete: operations["ImageDelete"]; + }; + "/images/search": { + /** + * Search images + * @description Search for an image on Docker Hub. + */ + get: operations["ImageSearch"]; + }; + "/images/prune": { + /** Delete unused images */ + post: operations["ImagePrune"]; + }; + "/auth": { + /** + * Check auth configuration + * @description Validate credentials for a registry and, if available, get an identity + * token for accessing the registry without password. + */ + post: operations["SystemAuth"]; + }; + "/info": { + /** Get system information */ + get: operations["SystemInfo"]; + }; + "/version": { + /** + * Get version + * @description Returns the version of Docker that is running and various information about the system that Docker is running on. + */ + get: operations["SystemVersion"]; + }; + "/_ping": { + /** + * Ping + * @description This is a dummy endpoint you can use to test if the server is accessible. + */ + get: operations["SystemPing"]; + /** + * Ping + * @description This is a dummy endpoint you can use to test if the server is accessible. + */ + head: operations["SystemPingHead"]; + }; + "/commit": { + /** Create a new image from a container */ + post: operations["ImageCommit"]; + }; + "/events": { + /** + * Monitor events + * @description Stream real-time events from the server. + * + * Various objects within Docker report events when something happens to them. + * + * Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, `update`, and `prune` + * + * Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, `untag`, and `prune` + * + * Volumes report these events: `create`, `mount`, `unmount`, `destroy`, and `prune` + * + * Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, `remove`, and `prune` + * + * The Docker daemon reports these events: `reload` + * + * Services report these events: `create`, `update`, and `remove` + * + * Nodes report these events: `create`, `update`, and `remove` + * + * Secrets report these events: `create`, `update`, and `remove` + * + * Configs report these events: `create`, `update`, and `remove` + * + * The Builder reports `prune` events + */ + get: operations["SystemEvents"]; + }; + "/system/df": { + /** Get data usage information */ + get: operations["SystemDataUsage"]; + }; + "/images/{name}/get": { + /** + * Export an image + * @description Get a tarball containing all images and metadata for a repository. + * + * If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + * + * ### Image tarball format + * + * An image tarball contains one directory per image layer (named using its long ID), each containing these files: + * + * - `VERSION`: currently `1.0` - the file format version + * - `json`: detailed layer information, similar to `docker inspect layer_id` + * - `layer.tar`: A tarfile containing the filesystem changes in this layer + * + * The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + * + * If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + * + * ```json + * { + * "hello-world": { + * "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + * } + * } + * ``` + */ + get: operations["ImageGet"]; + }; + "/images/get": { + /** + * Export several images + * @description Get a tarball containing all images and metadata for several image + * repositories. + * + * For each value of the `names` parameter: if it is a specific name and + * tag (e.g. `ubuntu:latest`), then only that image (and its parents) are + * returned; if it is an image ID, similarly only that image (and its parents) + * are returned and there would be no names referenced in the 'repositories' + * file for this image ID. + * + * For details on the format, see the [export image endpoint](#operation/ImageGet). + */ + get: operations["ImageGetAll"]; + }; + "/images/load": { + /** + * Import images + * @description Load a set of images and tags into a repository. + * + * For details on the format, see the [export image endpoint](#operation/ImageGet). + */ + post: operations["ImageLoad"]; + }; + "/containers/{id}/exec": { + /** + * Create an exec instance + * @description Run a command inside a running container. + */ + post: operations["ContainerExec"]; + }; + "/exec/{id}/start": { + /** + * Start an exec instance + * @description Starts a previously set up exec instance. If detach is true, this endpoint + * returns immediately after starting the command. Otherwise, it sets up an + * interactive session with the command. + */ + post: operations["ExecStart"]; + }; + "/exec/{id}/resize": { + /** + * Resize an exec instance + * @description Resize the TTY session used by an exec instance. This endpoint only works + * if `tty` was specified as part of creating and starting the exec instance. + */ + post: operations["ExecResize"]; + }; + "/exec/{id}/json": { + /** + * Inspect an exec instance + * @description Return low-level information about an exec instance. + */ + get: operations["ExecInspect"]; + }; + "/volumes": { + /** List volumes */ + get: operations["VolumeList"]; + }; + "/volumes/create": { + /** Create a volume */ + post: operations["VolumeCreate"]; + }; + "/volumes/{name}": { + /** Inspect a volume */ + get: operations["VolumeInspect"]; + /** "Update a volume. Valid only for Swarm cluster volumes" */ + put: operations["VolumeUpdate"]; + /** + * Remove a volume + * @description Instruct the driver to remove the volume. + */ + delete: operations["VolumeDelete"]; + }; + "/volumes/prune": { + /** Delete unused volumes */ + post: operations["VolumePrune"]; + }; + "/networks": { + /** + * List networks + * @description Returns a list of networks. For details on the format, see the + * [network inspect endpoint](#operation/NetworkInspect). + * + * Note that it uses a different, smaller representation of a network than + * inspecting a single network. For example, the list of containers attached + * to the network is not propagated in API versions 1.28 and up. + */ + get: operations["NetworkList"]; + }; + "/networks/{id}": { + /** Inspect a network */ + get: operations["NetworkInspect"]; + /** Remove a network */ + delete: operations["NetworkDelete"]; + }; + "/networks/create": { + /** Create a network */ + post: operations["NetworkCreate"]; + }; + "/networks/{id}/connect": { + /** + * Connect a container to a network + * @description The network must be either a local-scoped network or a swarm-scoped network with the `attachable` option set. A network cannot be re-attached to a running container + */ + post: operations["NetworkConnect"]; + }; + "/networks/{id}/disconnect": { + /** Disconnect a container from a network */ + post: operations["NetworkDisconnect"]; + }; + "/networks/prune": { + /** Delete unused networks */ + post: operations["NetworkPrune"]; + }; + "/plugins": { + /** + * List plugins + * @description Returns information about installed plugins. + */ + get: operations["PluginList"]; + }; + "/plugins/privileges": { + /** Get plugin privileges */ + get: operations["GetPluginPrivileges"]; + }; + "/plugins/pull": { + /** + * Install a plugin + * @description Pulls and installs a plugin. After the plugin is installed, it can be + * enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + */ + post: operations["PluginPull"]; + }; + "/plugins/{name}/json": { + /** Inspect a plugin */ + get: operations["PluginInspect"]; + }; + "/plugins/{name}": { + /** Remove a plugin */ + delete: operations["PluginDelete"]; + }; + "/plugins/{name}/enable": { + /** Enable a plugin */ + post: operations["PluginEnable"]; + }; + "/plugins/{name}/disable": { + /** Disable a plugin */ + post: operations["PluginDisable"]; + }; + "/plugins/{name}/upgrade": { + /** Upgrade a plugin */ + post: operations["PluginUpgrade"]; + }; + "/plugins/create": { + /** Create a plugin */ + post: operations["PluginCreate"]; + }; + "/plugins/{name}/push": { + /** + * Push a plugin + * @description Push a plugin to the registry. + */ + post: operations["PluginPush"]; + }; + "/plugins/{name}/set": { + /** Configure a plugin */ + post: operations["PluginSet"]; + }; + "/nodes": { + /** List nodes */ + get: operations["NodeList"]; + }; + "/nodes/{id}": { + /** Inspect a node */ + get: operations["NodeInspect"]; + /** Delete a node */ + delete: operations["NodeDelete"]; + }; + "/nodes/{id}/update": { + /** Update a node */ + post: operations["NodeUpdate"]; + }; + "/swarm": { + /** Inspect swarm */ + get: operations["SwarmInspect"]; + }; + "/swarm/init": { + /** Initialize a new swarm */ + post: operations["SwarmInit"]; + }; + "/swarm/join": { + /** Join an existing swarm */ + post: operations["SwarmJoin"]; + }; + "/swarm/leave": { + /** Leave a swarm */ + post: operations["SwarmLeave"]; + }; + "/swarm/update": { + /** Update a swarm */ + post: operations["SwarmUpdate"]; + }; + "/swarm/unlockkey": { + /** Get the unlock key */ + get: operations["SwarmUnlockkey"]; + }; + "/swarm/unlock": { + /** Unlock a locked manager */ + post: operations["SwarmUnlock"]; + }; + "/services": { + /** List services */ + get: operations["ServiceList"]; + }; + "/services/create": { + /** Create a service */ + post: operations["ServiceCreate"]; + }; + "/services/{id}": { + /** Inspect a service */ + get: operations["ServiceInspect"]; + /** Delete a service */ + delete: operations["ServiceDelete"]; + }; + "/services/{id}/update": { + /** Update a service */ + post: operations["ServiceUpdate"]; + }; + "/services/{id}/logs": { + /** + * Get service logs + * @description Get `stdout` and `stderr` logs from a service. See also + * [`/containers/{id}/logs`](#operation/ContainerLogs). + * + * **Note**: This endpoint works only for services with the `local`, + * `json-file` or `journald` logging drivers. + */ + get: operations["ServiceLogs"]; + }; + "/tasks": { + /** List tasks */ + get: operations["TaskList"]; + }; + "/tasks/{id}": { + /** Inspect a task */ + get: operations["TaskInspect"]; + }; + "/tasks/{id}/logs": { + /** + * Get task logs + * @description Get `stdout` and `stderr` logs from a task. + * See also [`/containers/{id}/logs`](#operation/ContainerLogs). + * + * **Note**: This endpoint works only for services with the `local`, + * `json-file` or `journald` logging drivers. + */ + get: operations["TaskLogs"]; + }; + "/secrets": { + /** List secrets */ + get: operations["SecretList"]; + }; + "/secrets/create": { + /** Create a secret */ + post: operations["SecretCreate"]; + }; + "/secrets/{id}": { + /** Inspect a secret */ + get: operations["SecretInspect"]; + /** Delete a secret */ + delete: operations["SecretDelete"]; + }; + "/secrets/{id}/update": { + /** Update a Secret */ + post: operations["SecretUpdate"]; + }; + "/configs": { + /** List configs */ + get: operations["ConfigList"]; + }; + "/configs/create": { + /** Create a config */ + post: operations["ConfigCreate"]; + }; + "/configs/{id}": { + /** Inspect a config */ + get: operations["ConfigInspect"]; + /** Delete a config */ + delete: operations["ConfigDelete"]; + }; + "/configs/{id}/update": { + /** Update a Config */ + post: operations["ConfigUpdate"]; + }; + "/distribution/{name}/json": { + /** + * Get image information from the registry + * @description Return image digest and platform information by contacting the registry. + */ + get: operations["DistributionInspect"]; + }; + "/session": { + /** + * Initialize interactive session + * @description Start a new interactive session with a server. Session allows server to + * call back to the client for advanced capabilities. + * + * ### Hijacking + * + * This endpoint hijacks the HTTP connection to HTTP2 transport that allows + * the client to expose gPRC services on that connection. + * + * For example, the client sends this request to upgrade the connection: + * + * ``` + * POST /session HTTP/1.1 + * Upgrade: h2c + * Connection: Upgrade + * ``` + * + * The Docker daemon responds with a `101 UPGRADED` response follow with + * the raw stream: + * + * ``` + * HTTP/1.1 101 UPGRADED + * Connection: Upgrade + * Upgrade: h2c + * ``` + */ + post: operations["Session"]; + }; +} + +export type webhooks = Record; + +export interface components { + schemas: { + /** + * @description An open port on a container + * @example { + * "PrivatePort": 8080, + * "PublicPort": 80, + * "Type": "tcp" + * } + */ + Port: { + /** + * Format: ip-address + * @description Host IP address that the container's port is mapped to + */ + IP?: string; + /** + * Format: uint16 + * @description Port on the container + */ + PrivatePort: number; + /** + * Format: uint16 + * @description Port exposed on the host + */ + PublicPort?: number; + /** @enum {string} */ + Type: "tcp" | "udp" | "sctp"; + }; + /** + * @description MountPoint represents a mount point configuration inside the container. + * This is used for reporting the mountpoints in use by a container. + */ + MountPoint: { + /** + * @description The mount type: + * + * - `bind` a mount of a file or directory from the host into the container. + * - `volume` a docker volume with the given `Name`. + * - `tmpfs` a `tmpfs`. + * - `npipe` a named pipe from the host into the container. + * - `cluster` a Swarm cluster volume + * + * @example volume + * @enum {string} + */ + Type?: "bind" | "volume" | "tmpfs" | "npipe" | "cluster"; + /** + * @description Name is the name reference to the underlying data defined by `Source` + * e.g., the volume name. + * + * @example myvolume + */ + Name?: string; + /** + * @description Source location of the mount. + * + * For volumes, this contains the storage location of the volume (within + * `/var/lib/docker/volumes/`). For bind-mounts, and `npipe`, this contains + * the source (host) part of the bind-mount. For `tmpfs` mount points, this + * field is empty. + * + * @example /var/lib/docker/volumes/myvolume/_data + */ + Source?: string; + /** + * @description Destination is the path relative to the container root (`/`) where + * the `Source` is mounted inside the container. + * + * @example /usr/share/nginx/html/ + */ + Destination?: string; + /** + * @description Driver is the volume driver used to create the volume (if it is a volume). + * + * @example local + */ + Driver?: string; + /** + * @description Mode is a comma separated list of options supplied by the user when + * creating the bind/volume mount. + * + * The default is platform-specific (`"z"` on Linux, empty on Windows). + * + * @example z + */ + Mode?: string; + /** + * @description Whether the mount is mounted writable (read-write). + * + * @example true + */ + RW?: boolean; + /** + * @description Propagation describes how mounts are propagated from the host into the + * mount point, and vice-versa. Refer to the [Linux kernel documentation](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt) + * for details. This field is not used on Windows. + * + * @example + */ + Propagation?: string; + }; + /** + * @description A device mapping between the host and container + * @example { + * "PathOnHost": "/dev/deviceName", + * "PathInContainer": "/dev/deviceName", + * "CgroupPermissions": "mrw" + * } + */ + DeviceMapping: { + PathOnHost?: string; + PathInContainer?: string; + CgroupPermissions?: string; + }; + /** @description A request for devices to be sent to device drivers */ + DeviceRequest: { + /** @example nvidia */ + Driver?: string; + /** @example -1 */ + Count?: number; + /** + * @example [ + * "0", + * "1", + * "GPU-fef8089b-4820-abfc-e83e-94318197576e" + * ] + */ + DeviceIDs?: string[]; + /** + * @description A list of capabilities; an OR list of AND lists of capabilities. + * + * @example [ + * [ + * "gpu", + * "nvidia", + * "compute" + * ] + * ] + */ + Capabilities?: string[][]; + /** + * @description Driver-specific options, specified as a key/value pairs. These options + * are passed directly to the driver. + */ + Options?: { + [key: string]: string; + }; + }; + ThrottleDevice: { + /** @description Device path */ + Path?: string; + /** + * Format: int64 + * @description Rate + */ + Rate?: number; + }; + Mount: { + /** @description Container path. */ + Target?: string; + /** @description Mount source (e.g. a volume name, a host path). */ + Source?: string; + /** + * @description The mount type. Available types: + * + * - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + * - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + * - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + * - `npipe` Mounts a named pipe from the host into the container. Must exist prior to creating the container. + * - `cluster` a Swarm cluster volume + * + * @enum {string} + */ + Type?: "bind" | "volume" | "tmpfs" | "npipe" | "cluster"; + /** @description Whether the mount should be read-only. */ + ReadOnly?: boolean; + /** @description The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`. */ + Consistency?: string; + /** @description Optional configuration for the `bind` type. */ + BindOptions?: { + /** + * @description A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`. + * @enum {string} + */ + Propagation?: "private" | "rprivate" | "shared" | "rshared" | "slave" | "rslave"; + /** + * @description Disable recursive bind mount. + * @default false + */ + NonRecursive?: boolean; + /** + * @description Create mount point on host if missing + * @default false + */ + CreateMountpoint?: boolean; + /** + * @description Make the mount non-recursively read-only, but still leave the mount recursive + * (unless NonRecursive is set to true in conjunction). + * + * @default false + */ + ReadOnlyNonRecursive?: boolean; + /** + * @description Raise an error if the mount cannot be made recursively read-only. + * @default false + */ + ReadOnlyForceRecursive?: boolean; + }; + /** @description Optional configuration for the `volume` type. */ + VolumeOptions?: { + /** + * @description Populate volume with data from the target. + * @default false + */ + NoCopy?: boolean; + /** @description User-defined key/value metadata. */ + Labels?: { + [key: string]: string; + }; + /** @description Map of driver specific options */ + DriverConfig?: { + /** @description Name of the driver to use to create the volume. */ + Name?: string; + /** @description key/value map of driver specific options. */ + Options?: { + [key: string]: string; + }; + }; + }; + /** @description Optional configuration for the `tmpfs` type. */ + TmpfsOptions?: { + /** + * Format: int64 + * @description The size for the tmpfs mount in bytes. + */ + SizeBytes?: number; + /** @description The permission mode for the tmpfs mount in an integer. */ + Mode?: number; + }; + }; + /** + * @description The behavior to apply when the container exits. The default is not to + * restart. + * + * An ever increasing delay (double the previous delay, starting at 100ms) is + * added before each restart to prevent flooding the server. + */ + RestartPolicy: { + /** + * @description - Empty string means not to restart + * - `no` Do not automatically restart + * - `always` Always restart + * - `unless-stopped` Restart always except when the user has manually stopped the container + * - `on-failure` Restart only when the container exit code is non-zero + * + * @enum {string} + */ + Name?: "" | "no" | "always" | "unless-stopped" | "on-failure"; + /** @description If `on-failure` is used, the number of times to retry before giving up. */ + MaximumRetryCount?: number; + }; + /** @description A container's resources (cgroups config, ulimits, etc) */ + Resources: { + /** + * @description An integer value representing this container's relative CPU weight + * versus other containers. + */ + CpuShares?: number; + /** + * Format: int64 + * @description Memory limit in bytes. + * @default 0 + */ + Memory?: number; + /** + * @description Path to `cgroups` under which the container's `cgroup` is created. If + * the path is not absolute, the path is considered to be relative to the + * `cgroups` path of the init process. Cgroups are created if they do not + * already exist. + */ + CgroupParent?: string; + /** @description Block IO weight (relative weight). */ + BlkioWeight?: number; + /** + * @description Block IO weight (relative device weight) in the form: + * + * ``` + * [{"Path": "device_path", "Weight": weight}] + * ``` + */ + BlkioWeightDevice?: { + Path?: string; + Weight?: number; + }[]; + /** + * @description Limit read rate (bytes per second) from a device, in the form: + * + * ``` + * [{"Path": "device_path", "Rate": rate}] + * ``` + */ + BlkioDeviceReadBps?: components["schemas"]["ThrottleDevice"][]; + /** + * @description Limit write rate (bytes per second) to a device, in the form: + * + * ``` + * [{"Path": "device_path", "Rate": rate}] + * ``` + */ + BlkioDeviceWriteBps?: components["schemas"]["ThrottleDevice"][]; + /** + * @description Limit read rate (IO per second) from a device, in the form: + * + * ``` + * [{"Path": "device_path", "Rate": rate}] + * ``` + */ + BlkioDeviceReadIOps?: components["schemas"]["ThrottleDevice"][]; + /** + * @description Limit write rate (IO per second) to a device, in the form: + * + * ``` + * [{"Path": "device_path", "Rate": rate}] + * ``` + */ + BlkioDeviceWriteIOps?: components["schemas"]["ThrottleDevice"][]; + /** + * Format: int64 + * @description The length of a CPU period in microseconds. + */ + CpuPeriod?: number; + /** + * Format: int64 + * @description Microseconds of CPU time that the container can get in a CPU period. + */ + CpuQuota?: number; + /** + * Format: int64 + * @description The length of a CPU real-time period in microseconds. Set to 0 to + * allocate no time allocated to real-time tasks. + */ + CpuRealtimePeriod?: number; + /** + * Format: int64 + * @description The length of a CPU real-time runtime in microseconds. Set to 0 to + * allocate no time allocated to real-time tasks. + */ + CpuRealtimeRuntime?: number; + /** + * @description CPUs in which to allow execution (e.g., `0-3`, `0,1`). + * + * @example 0-3 + */ + CpusetCpus?: string; + /** + * @description Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only + * effective on NUMA systems. + */ + CpusetMems?: string; + /** @description A list of devices to add to the container. */ + Devices?: components["schemas"]["DeviceMapping"][]; + /** @description a list of cgroup rules to apply to the container */ + DeviceCgroupRules?: string[]; + /** @description A list of requests for devices to be sent to device drivers. */ + DeviceRequests?: components["schemas"]["DeviceRequest"][]; + /** + * Format: int64 + * @description Hard limit for kernel TCP buffer memory (in bytes). Depending on the + * OCI runtime in use, this option may be ignored. It is no longer supported + * by the default (runc) runtime. + * + * This field is omitted when empty. + */ + KernelMemoryTCP?: number; + /** + * Format: int64 + * @description Memory soft limit in bytes. + */ + MemoryReservation?: number; + /** + * Format: int64 + * @description Total memory limit (memory + swap). Set as `-1` to enable unlimited + * swap. + */ + MemorySwap?: number; + /** + * Format: int64 + * @description Tune a container's memory swappiness behavior. Accepts an integer + * between 0 and 100. + */ + MemorySwappiness?: number; + /** + * Format: int64 + * @description CPU quota in units of 10-9 CPUs. + */ + NanoCpus?: number; + /** @description Disable OOM Killer for the container. */ + OomKillDisable?: boolean; + /** + * @description Run an init inside the container that forwards signals and reaps + * processes. This field is omitted if empty, and the default (as + * configured on the daemon) is used. + */ + Init?: boolean | null; + /** + * Format: int64 + * @description Tune a container's PIDs limit. Set `0` or `-1` for unlimited, or `null` + * to not change. + */ + PidsLimit?: number | null; + /** + * @description A list of resource limits to set in the container. For example: + * + * ``` + * {"Name": "nofile", "Soft": 1024, "Hard": 2048} + * ``` + */ + Ulimits?: { + /** @description Name of ulimit */ + Name?: string; + /** @description Soft limit */ + Soft?: number; + /** @description Hard limit */ + Hard?: number; + }[]; + /** + * Format: int64 + * @description The number of usable CPUs (Windows only). + * + * On Windows Server containers, the processor resource controls are + * mutually exclusive. The order of precedence is `CPUCount` first, then + * `CPUShares`, and `CPUPercent` last. + */ + CpuCount?: number; + /** + * Format: int64 + * @description The usable percentage of the available CPUs (Windows only). + * + * On Windows Server containers, the processor resource controls are + * mutually exclusive. The order of precedence is `CPUCount` first, then + * `CPUShares`, and `CPUPercent` last. + */ + CpuPercent?: number; + /** + * Format: int64 + * @description Maximum IOps for the container system drive (Windows only) + */ + IOMaximumIOps?: number; + /** + * Format: int64 + * @description Maximum IO in bytes per second for the container system drive + * (Windows only). + */ + IOMaximumBandwidth?: number; + }; + /** @description An object describing a limit on resources which can be requested by a task. */ + Limit: { + /** + * Format: int64 + * @example 4000000000 + */ + NanoCPUs?: number; + /** + * Format: int64 + * @example 8272408576 + */ + MemoryBytes?: number; + /** + * Format: int64 + * @description Limits the maximum number of PIDs in the container. Set `0` for unlimited. + * + * @default 0 + * @example 100 + */ + Pids?: number; + }; + /** + * @description An object describing the resources which can be advertised by a node and + * requested by a task. + */ + ResourceObject: { + /** + * Format: int64 + * @example 4000000000 + */ + NanoCPUs?: number; + /** + * Format: int64 + * @example 8272408576 + */ + MemoryBytes?: number; + GenericResources?: components["schemas"]["GenericResources"]; + }; + /** + * @description User-defined resources can be either Integer resources (e.g, `SSD=3`) or + * String resources (e.g, `GPU=UUID1`). + * + * @example [ + * { + * "DiscreteResourceSpec": { + * "Kind": "SSD", + * "Value": 3 + * } + * }, + * { + * "NamedResourceSpec": { + * "Kind": "GPU", + * "Value": "UUID1" + * } + * }, + * { + * "NamedResourceSpec": { + * "Kind": "GPU", + * "Value": "UUID2" + * } + * } + * ] + */ + GenericResources: { + NamedResourceSpec?: { + Kind?: string; + Value?: string; + }; + DiscreteResourceSpec?: { + Kind?: string; + /** Format: int64 */ + Value?: number; + }; + }[]; + /** @description A test to perform to check that the container is healthy. */ + HealthConfig: { + /** + * @description The test to perform. Possible values are: + * + * - `[]` inherit healthcheck from image or parent image + * - `["NONE"]` disable healthcheck + * - `["CMD", args...]` exec arguments directly + * - `["CMD-SHELL", command]` run command with system's default shell + */ + Test?: string[]; + /** + * Format: int64 + * @description The time to wait between checks in nanoseconds. It should be 0 or at + * least 1000000 (1 ms). 0 means inherit. + */ + Interval?: number; + /** + * Format: int64 + * @description The time to wait before considering the check to have hung. It should + * be 0 or at least 1000000 (1 ms). 0 means inherit. + */ + Timeout?: number; + /** + * @description The number of consecutive failures needed to consider a container as + * unhealthy. 0 means inherit. + */ + Retries?: number; + /** + * Format: int64 + * @description Start period for the container to initialize before starting + * health-retries countdown in nanoseconds. It should be 0 or at least + * 1000000 (1 ms). 0 means inherit. + */ + StartPeriod?: number; + /** + * Format: int64 + * @description The time to wait between checks in nanoseconds during the start period. + * It should be 0 or at least 1000000 (1 ms). 0 means inherit. + */ + StartInterval?: number; + }; + /** @description Health stores information about the container's healthcheck results. */ + Health: ({ + /** + * @description Status is one of `none`, `starting`, `healthy` or `unhealthy` + * + * - "none" Indicates there is no healthcheck + * - "starting" Starting indicates that the container is not yet ready + * - "healthy" Healthy indicates that the container is running correctly + * - "unhealthy" Unhealthy indicates that the container has a problem + * + * @example healthy + * @enum {string} + */ + Status?: "none" | "starting" | "healthy" | "unhealthy"; + /** + * @description FailingStreak is the number of consecutive failures + * @example 0 + */ + FailingStreak?: number; + /** @description Log contains the last few results (oldest first) */ + Log?: components["schemas"]["HealthcheckResult"][]; + }) | null; + /** @description HealthcheckResult stores information about a single run of a healthcheck probe */ + HealthcheckResult: { + /** + * Format: date-time + * @description Date and time at which this check started in + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + * + * @example 2020-01-04T10:44:24.496525531Z + */ + Start?: string; + /** + * Format: dateTime + * @description Date and time at which this check ended in + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + * + * @example 2020-01-04T10:45:21.364524523Z + */ + End?: string; + /** + * @description ExitCode meanings: + * + * - `0` healthy + * - `1` unhealthy + * - `2` reserved (considered unhealthy) + * - other values: error running probe + * + * @example 0 + */ + ExitCode?: number; + /** @description Output from last check */ + Output?: string; + } | null; + /** @description Container configuration that depends on the host we are running on */ + HostConfig: components["schemas"]["Resources"] & ({ + /** + * @description A list of volume bindings for this container. Each volume binding + * is a string in one of these forms: + * + * - `host-src:container-dest[:options]` to bind-mount a host path + * into the container. Both `host-src`, and `container-dest` must + * be an _absolute_ path. + * - `volume-name:container-dest[:options]` to bind-mount a volume + * managed by a volume driver into the container. `container-dest` + * must be an _absolute_ path. + * + * `options` is an optional, comma-delimited list of: + * + * - `nocopy` disables automatic copying of data from the container + * path to the volume. The `nocopy` flag only applies to named volumes. + * - `[ro|rw]` mounts a volume read-only or read-write, respectively. + * If omitted or set to `rw`, volumes are mounted read-write. + * - `[z|Z]` applies SELinux labels to allow or deny multiple containers + * to read and write to the same volume. + * - `z`: a _shared_ content label is applied to the content. This + * label indicates that multiple containers can share the volume + * content, for both reading and writing. + * - `Z`: a _private unshared_ label is applied to the content. + * This label indicates that only the current container can use + * a private volume. Labeling systems such as SELinux require + * proper labels to be placed on volume content that is mounted + * into a container. Without a label, the security system can + * prevent a container's processes from using the content. By + * default, the labels set by the host operating system are not + * modified. + * - `[[r]shared|[r]slave|[r]private]` specifies mount + * [propagation behavior](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt). + * This only applies to bind-mounted volumes, not internal volumes + * or named volumes. Mount propagation requires the source mount + * point (the location where the source directory is mounted in the + * host operating system) to have the correct propagation properties. + * For shared volumes, the source mount point must be set to `shared`. + * For slave volumes, the mount must be set to either `shared` or + * `slave`. + */ + Binds?: string[]; + /** @description Path to a file where the container ID is written */ + ContainerIDFile?: string; + /** @description The logging configuration for this container */ + LogConfig?: { + /** @enum {string} */ + Type?: "json-file" | "syslog" | "journald" | "gelf" | "fluentd" | "awslogs" | "splunk" | "etwlogs" | "none"; + Config?: { + [key: string]: string; + }; + }; + /** + * @description Network mode to use for this container. Supported standard values + * are: `bridge`, `host`, `none`, and `container:`. Any + * other value is taken as a custom network's name to which this + * container should connect to. + */ + NetworkMode?: string; + PortBindings?: components["schemas"]["PortMap"]; + RestartPolicy?: components["schemas"]["RestartPolicy"]; + /** + * @description Automatically remove the container when the container's process + * exits. This has no effect if `RestartPolicy` is set. + */ + AutoRemove?: boolean; + /** @description Driver that this container uses to mount volumes. */ + VolumeDriver?: string; + /** + * @description A list of volumes to inherit from another container, specified in + * the form `[:]`. + */ + VolumesFrom?: string[]; + /** @description Specification for mounts to be added to the container. */ + Mounts?: components["schemas"]["Mount"][]; + /** @description Initial console size, as an `[height, width]` array. */ + ConsoleSize?: number[] | null; + /** + * @description Arbitrary non-identifying metadata attached to container and + * provided to the runtime when the container is started. + */ + Annotations?: { + [key: string]: string; + }; + /** + * @description A list of kernel capabilities to add to the container. Conflicts + * with option 'Capabilities'. + */ + CapAdd?: string[]; + /** + * @description A list of kernel capabilities to drop from the container. Conflicts + * with option 'Capabilities'. + */ + CapDrop?: string[]; + /** + * @description cgroup namespace mode for the container. Possible values are: + * + * - `"private"`: the container runs in its own private cgroup namespace + * - `"host"`: use the host system's cgroup namespace + * + * If not specified, the daemon default is used, which can either be `"private"` + * or `"host"`, depending on daemon version, kernel support and configuration. + * + * @enum {string} + */ + CgroupnsMode?: "private" | "host"; + /** @description A list of DNS servers for the container to use. */ + Dns?: string[]; + /** @description A list of DNS options. */ + DnsOptions?: string[]; + /** @description A list of DNS search domains. */ + DnsSearch?: string[]; + /** + * @description A list of hostnames/IP mappings to add to the container's `/etc/hosts` + * file. Specified in the form `["hostname:IP"]`. + */ + ExtraHosts?: string[]; + /** @description A list of additional groups that the container process will run as. */ + GroupAdd?: string[]; + /** + * @description IPC sharing mode for the container. Possible values are: + * + * - `"none"`: own private IPC namespace, with /dev/shm not mounted + * - `"private"`: own private IPC namespace + * - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + * - `"container:"`: join another (shareable) container's IPC namespace + * - `"host"`: use the host system's IPC namespace + * + * If not specified, daemon default is used, which can either be `"private"` + * or `"shareable"`, depending on daemon version and configuration. + */ + IpcMode?: string; + /** @description Cgroup to use for the container. */ + Cgroup?: string; + /** @description A list of links for the container in the form `container_name:alias`. */ + Links?: string[]; + /** + * @description An integer value containing the score given to the container in + * order to tune OOM killer preferences. + * + * @example 500 + */ + OomScoreAdj?: number; + /** + * @description Set the PID (Process) Namespace mode for the container. It can be + * either: + * + * - `"container:"`: joins another container's PID namespace + * - `"host"`: use the host's PID namespace inside the container + */ + PidMode?: string; + /** @description Gives the container full access to the host. */ + Privileged?: boolean; + /** + * @description Allocates an ephemeral host port for all of a container's + * exposed ports. + * + * Ports are de-allocated when the container stops and allocated when + * the container starts. The allocated port might be changed when + * restarting the container. + * + * The port is selected from the ephemeral port range that depends on + * the kernel. For example, on Linux the range is defined by + * `/proc/sys/net/ipv4/ip_local_port_range`. + */ + PublishAllPorts?: boolean; + /** @description Mount the container's root filesystem as read only. */ + ReadonlyRootfs?: boolean; + /** + * @description A list of string values to customize labels for MLS systems, such + * as SELinux. + */ + SecurityOpt?: string[]; + /** @description Storage driver options for this container, in the form `{"size": "120G"}`. */ + StorageOpt?: { + [key: string]: string; + }; + /** + * @description A map of container directories which should be replaced by tmpfs + * mounts, and their corresponding mount options. For example: + * + * ``` + * { "/run": "rw,noexec,nosuid,size=65536k" } + * ``` + */ + Tmpfs?: { + [key: string]: string; + }; + /** @description UTS namespace to use for the container. */ + UTSMode?: string; + /** + * @description Sets the usernamespace mode for the container when usernamespace + * remapping option is enabled. + */ + UsernsMode?: string; + /** + * Format: int64 + * @description Size of `/dev/shm` in bytes. If omitted, the system uses 64MB. + */ + ShmSize?: number; + /** + * @description A list of kernel parameters (sysctls) to set in the container. + * For example: + * + * ``` + * {"net.ipv4.ip_forward": "1"} + * ``` + */ + Sysctls?: { + [key: string]: string; + }; + /** @description Runtime to use with this container. */ + Runtime?: string; + /** + * @description Isolation technology of the container. (Windows only) + * + * @enum {string} + */ + Isolation?: "default" | "process" | "hyperv"; + /** + * @description The list of paths to be masked inside the container (this overrides + * the default set of paths). + */ + MaskedPaths?: string[]; + /** + * @description The list of paths to be set as read-only inside the container + * (this overrides the default set of paths). + */ + ReadonlyPaths?: string[]; + }); + /** + * @description Configuration for a container that is portable between hosts. + * + * When used as `ContainerConfig` field in an image, `ContainerConfig` is an + * optional field containing the configuration of the container that was last + * committed when creating the image. + * + * Previous versions of Docker builder used this field to store build cache, + * and it is not in active use anymore. + */ + ContainerConfig: { + /** + * @description The hostname to use for the container, as a valid RFC 1123 hostname. + * + * @example 439f4e91bd1d + */ + Hostname?: string; + /** @description The domain name to use for the container. */ + Domainname?: string; + /** @description The user that commands are run as inside the container. */ + User?: string; + /** + * @description Whether to attach to `stdin`. + * @default false + */ + AttachStdin?: boolean; + /** + * @description Whether to attach to `stdout`. + * @default true + */ + AttachStdout?: boolean; + /** + * @description Whether to attach to `stderr`. + * @default true + */ + AttachStderr?: boolean; + /** + * @description An object mapping ports to an empty object in the form: + * + * `{"/": {}}` + * + * @example { + * "80/tcp": {}, + * "443/tcp": {} + * } + */ + ExposedPorts?: { + [key: string]: Record; + } | null; + /** + * @description Attach standard streams to a TTY, including `stdin` if it is not closed. + * + * @default false + */ + Tty?: boolean; + /** + * @description Open `stdin` + * @default false + */ + OpenStdin?: boolean; + /** + * @description Close `stdin` after one attached client disconnects + * @default false + */ + StdinOnce?: boolean; + /** + * @description A list of environment variables to set inside the container in the + * form `["VAR=value", ...]`. A variable without `=` is removed from the + * environment, rather than to have an empty value. + * + * @example [ + * "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + * ] + */ + Env?: string[]; + /** + * @description Command to run specified as a string or an array of strings. + * + * @example [ + * "/bin/sh" + * ] + */ + Cmd?: string[]; + Healthcheck?: components["schemas"]["HealthConfig"]; + /** + * @description Command is already escaped (Windows only) + * @default false + * @example false + */ + ArgsEscaped?: boolean | null; + /** + * @description The name (or reference) of the image to use when creating the container, + * or which was used when the container was created. + * + * @example example-image:1.0 + */ + Image?: string; + /** + * @description An object mapping mount point paths inside the container to empty + * objects. + */ + Volumes?: { + [key: string]: Record; + }; + /** + * @description The working directory for commands to run in. + * @example /public/ + */ + WorkingDir?: string; + /** + * @description The entry point for the container as a string or an array of strings. + * + * If the array consists of exactly one empty string (`[""]`) then the + * entry point is reset to system default (i.e., the entry point used by + * docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + * + * @example [] + */ + Entrypoint?: string[]; + /** @description Disable networking for the container. */ + NetworkDisabled?: boolean | null; + /** + * @description MAC address of the container. + * + * Deprecated: this field is deprecated in API v1.44 and up. Use EndpointSettings.MacAddress instead. + */ + MacAddress?: string | null; + /** + * @description `ONBUILD` metadata that were defined in the image's `Dockerfile`. + * + * @example [] + */ + OnBuild?: string[] | null; + /** + * @description User-defined key/value metadata. + * @example { + * "com.example.some-label": "some-value", + * "com.example.some-other-label": "some-other-value" + * } + */ + Labels?: { + [key: string]: string; + }; + /** + * @description Signal to stop a container as a string or unsigned integer. + * + * @example SIGTERM + */ + StopSignal?: string | null; + /** @description Timeout to stop a container in seconds. */ + StopTimeout?: number | null; + /** + * @description Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell. + * + * @example [ + * "/bin/sh", + * "-c" + * ] + */ + Shell?: string[] | null; + }; + /** + * @description NetworkingConfig represents the container's networking configuration for + * each of its interfaces. + * It is used for the networking configs specified in the `docker create` + * and `docker network connect` commands. + * + * @example { + * "EndpointsConfig": { + * "isolated_nw": { + * "IPAMConfig": { + * "IPv4Address": "172.20.30.33", + * "IPv6Address": "2001:db8:abcd::3033", + * "LinkLocalIPs": [ + * "169.254.34.68", + * "fe80::3468" + * ] + * }, + * "MacAddress": "02:42:ac:12:05:02", + * "Links": [ + * "container_1", + * "container_2" + * ], + * "Aliases": [ + * "server_x", + * "server_y" + * ] + * }, + * "database_nw": {} + * } + * } + */ + NetworkingConfig: { + /** + * @description A mapping of network name to endpoint configuration for that network. + * The endpoint configuration can be left empty to connect to that + * network with no particular endpoint configuration. + */ + EndpointsConfig?: { + [key: string]: components["schemas"]["EndpointSettings"]; + }; + }; + /** @description NetworkSettings exposes the network settings in the API */ + NetworkSettings: { + /** + * @description Name of the default bridge interface when dockerd's --bridge flag is set. + * + * @example docker0 + */ + Bridge?: string; + /** + * @description SandboxID uniquely represents a container's network stack. + * @example 9d12daf2c33f5959c8bf90aa513e4f65b561738661003029ec84830cd503a0c3 + */ + SandboxID?: string; + /** + * @description Indicates if hairpin NAT should be enabled on the virtual interface. + * + * Deprecated: This field is never set and will be removed in a future release. + * + * @example false + */ + HairpinMode?: boolean; + /** + * @description IPv6 unicast address using the link-local prefix. + * + * Deprecated: This field is never set and will be removed in a future release. + * + * @example + */ + LinkLocalIPv6Address?: string; + /** + * @description Prefix length of the IPv6 unicast address. + * + * Deprecated: This field is never set and will be removed in a future release. + */ + LinkLocalIPv6PrefixLen?: number; + Ports?: components["schemas"]["PortMap"]; + /** + * @description SandboxKey is the full path of the netns handle + * @example /var/run/docker/netns/8ab54b426c38 + */ + SandboxKey?: string; + /** @description Deprecated: This field is never set and will be removed in a future release. */ + SecondaryIPAddresses?: components["schemas"]["Address"][] | null; + /** @description Deprecated: This field is never set and will be removed in a future release. */ + SecondaryIPv6Addresses?: components["schemas"]["Address"][] | null; + /** + * @description EndpointID uniquely represents a service endpoint in a Sandbox. + * + *


+ * + * > **Deprecated**: This field is only propagated when attached to the + * > default "bridge" network. Use the information from the "bridge" + * > network inside the `Networks` map instead, which contains the same + * > information. This field was deprecated in Docker 1.9 and is scheduled + * > to be removed in Docker 17.12.0 + * + * @example b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b + */ + EndpointID?: string; + /** + * @description Gateway address for the default "bridge" network. + * + *


+ * + * > **Deprecated**: This field is only propagated when attached to the + * > default "bridge" network. Use the information from the "bridge" + * > network inside the `Networks` map instead, which contains the same + * > information. This field was deprecated in Docker 1.9 and is scheduled + * > to be removed in Docker 17.12.0 + * + * @example 172.17.0.1 + */ + Gateway?: string; + /** + * @description Global IPv6 address for the default "bridge" network. + * + *


+ * + * > **Deprecated**: This field is only propagated when attached to the + * > default "bridge" network. Use the information from the "bridge" + * > network inside the `Networks` map instead, which contains the same + * > information. This field was deprecated in Docker 1.9 and is scheduled + * > to be removed in Docker 17.12.0 + * + * @example 2001:db8::5689 + */ + GlobalIPv6Address?: string; + /** + * @description Mask length of the global IPv6 address. + * + *


+ * + * > **Deprecated**: This field is only propagated when attached to the + * > default "bridge" network. Use the information from the "bridge" + * > network inside the `Networks` map instead, which contains the same + * > information. This field was deprecated in Docker 1.9 and is scheduled + * > to be removed in Docker 17.12.0 + * + * @example 64 + */ + GlobalIPv6PrefixLen?: number; + /** + * @description IPv4 address for the default "bridge" network. + * + *


+ * + * > **Deprecated**: This field is only propagated when attached to the + * > default "bridge" network. Use the information from the "bridge" + * > network inside the `Networks` map instead, which contains the same + * > information. This field was deprecated in Docker 1.9 and is scheduled + * > to be removed in Docker 17.12.0 + * + * @example 172.17.0.4 + */ + IPAddress?: string; + /** + * @description Mask length of the IPv4 address. + * + *


+ * + * > **Deprecated**: This field is only propagated when attached to the + * > default "bridge" network. Use the information from the "bridge" + * > network inside the `Networks` map instead, which contains the same + * > information. This field was deprecated in Docker 1.9 and is scheduled + * > to be removed in Docker 17.12.0 + * + * @example 16 + */ + IPPrefixLen?: number; + /** + * @description IPv6 gateway address for this network. + * + *


+ * + * > **Deprecated**: This field is only propagated when attached to the + * > default "bridge" network. Use the information from the "bridge" + * > network inside the `Networks` map instead, which contains the same + * > information. This field was deprecated in Docker 1.9 and is scheduled + * > to be removed in Docker 17.12.0 + * + * @example 2001:db8:2::100 + */ + IPv6Gateway?: string; + /** + * @description MAC address for the container on the default "bridge" network. + * + *


+ * + * > **Deprecated**: This field is only propagated when attached to the + * > default "bridge" network. Use the information from the "bridge" + * > network inside the `Networks` map instead, which contains the same + * > information. This field was deprecated in Docker 1.9 and is scheduled + * > to be removed in Docker 17.12.0 + * + * @example 02:42:ac:11:00:04 + */ + MacAddress?: string; + /** @description Information about all networks that the container is connected to. */ + Networks?: { + [key: string]: components["schemas"]["EndpointSettings"]; + }; + }; + /** @description Address represents an IPv4 or IPv6 IP address. */ + Address: { + /** @description IP address. */ + Addr?: string; + /** @description Mask length of the IP address. */ + PrefixLen?: number; + }; + /** + * @description PortMap describes the mapping of container ports to host ports, using the + * container's port-number and protocol as key in the format `/`, + * for example, `80/udp`. + * + * If a container's port is mapped for multiple protocols, separate entries + * are added to the mapping table. + * + * @example { + * "443/tcp": [ + * { + * "HostIp": "127.0.0.1", + * "HostPort": "4443" + * } + * ], + * "80/tcp": [ + * { + * "HostIp": "0.0.0.0", + * "HostPort": "80" + * }, + * { + * "HostIp": "0.0.0.0", + * "HostPort": "8080" + * } + * ], + * "80/udp": [ + * { + * "HostIp": "0.0.0.0", + * "HostPort": "80" + * } + * ], + * "53/udp": [ + * { + * "HostIp": "0.0.0.0", + * "HostPort": "53" + * } + * ] + * } + */ + PortMap: { + [key: string]: components["schemas"]["PortBinding"][] | null; + }; + /** + * @description PortBinding represents a binding between a host IP address and a host + * port. + */ + PortBinding: { + /** + * @description Host IP address that the container's port is mapped to. + * @example 127.0.0.1 + */ + HostIp?: string; + /** + * @description Host port number that the container's port is mapped to. + * @example 4443 + */ + HostPort?: string; + }; + /** + * @description Information about the storage driver used to store the container's and + * image's filesystem. + */ + GraphDriverData: { + /** + * @description Name of the storage driver. + * @example overlay2 + */ + Name: string; + /** + * @description Low-level storage metadata, provided as key/value pairs. + * + * This information is driver-specific, and depends on the storage-driver + * in use, and should be used for informational purposes only. + * + * @example { + * "MergedDir": "/var/lib/docker/overlay2/ef749362d13333e65fc95c572eb525abbe0052e16e086cb64bc3b98ae9aa6d74/merged", + * "UpperDir": "/var/lib/docker/overlay2/ef749362d13333e65fc95c572eb525abbe0052e16e086cb64bc3b98ae9aa6d74/diff", + * "WorkDir": "/var/lib/docker/overlay2/ef749362d13333e65fc95c572eb525abbe0052e16e086cb64bc3b98ae9aa6d74/work" + * } + */ + Data: { + [key: string]: string; + }; + }; + /** @description Change in the container's filesystem. */ + FilesystemChange: { + /** @description Path to file or directory that has changed. */ + Path: string; + Kind: components["schemas"]["ChangeType"]; + }; + /** + * Format: uint8 + * @description Kind of change + * + * Can be one of: + * + * - `0`: Modified ("C") + * - `1`: Added ("A") + * - `2`: Deleted ("D") + * + * @enum {integer} + */ + ChangeType: 0 | 1 | 2; + /** @description Information about an image in the local image cache. */ + ImageInspect: { + /** + * @description ID is the content-addressable ID of an image. + * + * This identifier is a content-addressable digest calculated from the + * image's configuration (which includes the digests of layers used by + * the image). + * + * Note that this digest differs from the `RepoDigests` below, which + * holds digests of image manifests that reference the image. + * + * @example sha256:ec3f0931a6e6b6855d76b2d7b0be30e81860baccd891b2e243280bf1cd8ad710 + */ + Id?: string; + /** + * @description List of image names/tags in the local image cache that reference this + * image. + * + * Multiple image tags can refer to the same image, and this list may be + * empty if no tags reference the image, in which case the image is + * "untagged", in which case it can still be referenced by its ID. + * + * @example [ + * "example:1.0", + * "example:latest", + * "example:stable", + * "internal.registry.example.com:5000/example:1.0" + * ] + */ + RepoTags?: string[]; + /** + * @description List of content-addressable digests of locally available image manifests + * that the image is referenced from. Multiple manifests can refer to the + * same image. + * + * These digests are usually only available if the image was either pulled + * from a registry, or if the image was pushed to a registry, which is when + * the manifest is generated and its digest calculated. + * + * @example [ + * "example@sha256:afcc7f1ac1b49db317a7196c902e61c6c3c4607d63599ee1a82d702d249a0ccb", + * "internal.registry.example.com:5000/example@sha256:b69959407d21e8a062e0416bf13405bb2b71ed7a84dde4158ebafacfa06f5578" + * ] + */ + RepoDigests?: string[]; + /** + * @description ID of the parent image. + * + * Depending on how the image was created, this field may be empty and + * is only set for images that were built/created locally. This field + * is empty if the image was pulled from an image registry. + * + * @example + */ + Parent?: string; + /** + * @description Optional message that was set when committing or importing the image. + * + * @example + */ + Comment?: string; + /** + * Format: dateTime + * @description Date and time at which the image was created, formatted in + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + * + * This information is only available if present in the image, + * and omitted otherwise. + * + * @example 2022-02-04T21:20:12.497794809Z + */ + Created?: string | null; + /** + * @description The ID of the container that was used to create the image. + * + * Depending on how the image was created, this field may be empty. + * + * **Deprecated**: this field is kept for backward compatibility, but + * will be removed in API v1.45. + * + * @example 65974bc86f1770ae4bff79f651ebdbce166ae9aada632ee3fa9af3a264911735 + */ + Container?: string; + ContainerConfig?: components["schemas"]["ContainerConfig"]; + /** + * @description The version of Docker that was used to build the image. + * + * Depending on how the image was created, this field may be empty. + * + * @example 20.10.7 + */ + DockerVersion?: string; + /** + * @description Name of the author that was specified when committing the image, or as + * specified through MAINTAINER (deprecated) in the Dockerfile. + * + * @example + */ + Author?: string; + Config?: components["schemas"]["ContainerConfig"]; + /** + * @description Hardware CPU architecture that the image runs on. + * + * @example arm + */ + Architecture?: string; + /** + * @description CPU architecture variant (presently ARM-only). + * + * @example v7 + */ + Variant?: string | null; + /** + * @description Operating System the image is built to run on. + * + * @example linux + */ + Os?: string; + /** + * @description Operating System version the image is built to run on (especially + * for Windows). + * + * @example + */ + OsVersion?: string | null; + /** + * Format: int64 + * @description Total size of the image including all layers it is composed of. + * + * @example 1239828 + */ + Size?: number; + /** + * Format: int64 + * @description Total size of the image including all layers it is composed of. + * + * Deprecated: this field is omitted in API v1.44, but kept for backward compatibility. Use Size instead. + * + * @example 1239828 + */ + VirtualSize?: number; + GraphDriver?: components["schemas"]["GraphDriverData"]; + /** @description Information about the image's RootFS, including the layer IDs. */ + RootFS?: { + /** @example layers */ + Type: string; + /** + * @example [ + * "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6", + * "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + * ] + */ + Layers?: string[]; + }; + /** + * @description Additional metadata of the image in the local cache. This information + * is local to the daemon, and not part of the image itself. + */ + Metadata?: { + /** + * Format: dateTime + * @description Date and time at which the image was last tagged in + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + * + * This information is only available if the image was tagged locally, + * and omitted otherwise. + * + * @example 2022-02-28T14:40:02.623929178Z + */ + LastTagTime?: string | null; + }; + }; + ImageSummary: { + /** + * @description ID is the content-addressable ID of an image. + * + * This identifier is a content-addressable digest calculated from the + * image's configuration (which includes the digests of layers used by + * the image). + * + * Note that this digest differs from the `RepoDigests` below, which + * holds digests of image manifests that reference the image. + * + * @example sha256:ec3f0931a6e6b6855d76b2d7b0be30e81860baccd891b2e243280bf1cd8ad710 + */ + Id: string; + /** + * @description ID of the parent image. + * + * Depending on how the image was created, this field may be empty and + * is only set for images that were built/created locally. This field + * is empty if the image was pulled from an image registry. + * + * @example + */ + ParentId: string; + /** + * @description List of image names/tags in the local image cache that reference this + * image. + * + * Multiple image tags can refer to the same image, and this list may be + * empty if no tags reference the image, in which case the image is + * "untagged", in which case it can still be referenced by its ID. + * + * @example [ + * "example:1.0", + * "example:latest", + * "example:stable", + * "internal.registry.example.com:5000/example:1.0" + * ] + */ + RepoTags: string[]; + /** + * @description List of content-addressable digests of locally available image manifests + * that the image is referenced from. Multiple manifests can refer to the + * same image. + * + * These digests are usually only available if the image was either pulled + * from a registry, or if the image was pushed to a registry, which is when + * the manifest is generated and its digest calculated. + * + * @example [ + * "example@sha256:afcc7f1ac1b49db317a7196c902e61c6c3c4607d63599ee1a82d702d249a0ccb", + * "internal.registry.example.com:5000/example@sha256:b69959407d21e8a062e0416bf13405bb2b71ed7a84dde4158ebafacfa06f5578" + * ] + */ + RepoDigests: string[]; + /** + * @description Date and time at which the image was created as a Unix timestamp + * (number of seconds sinds EPOCH). + * + * @example 1644009612 + */ + Created: number; + /** + * Format: int64 + * @description Total size of the image including all layers it is composed of. + * + * @example 172064416 + */ + Size: number; + /** + * Format: int64 + * @description Total size of image layers that are shared between this image and other + * images. + * + * This size is not calculated by default. `-1` indicates that the value + * has not been set / calculated. + * + * @example 1239828 + */ + SharedSize: number; + /** + * Format: int64 + * @description Total size of the image including all layers it is composed of. + * + * Deprecated: this field is omitted in API v1.44, but kept for backward compatibility. Use Size instead. + * @example 172064416 + */ + VirtualSize?: number; + /** + * @description User-defined key/value metadata. + * @example { + * "com.example.some-label": "some-value", + * "com.example.some-other-label": "some-other-value" + * } + */ + Labels: { + [key: string]: string; + }; + /** + * @description Number of containers using this image. Includes both stopped and running + * containers. + * + * This size is not calculated by default, and depends on which API endpoint + * is used. `-1` indicates that the value has not been set / calculated. + * + * @example 2 + */ + Containers: number; + }; + /** + * @example { + * "username": "hannibal", + * "password": "xxxx", + * "serveraddress": "https://index.docker.io/v1/" + * } + */ + AuthConfig: { + username?: string; + password?: string; + email?: string; + serveraddress?: string; + }; + ProcessConfig: { + privileged?: boolean; + user?: string; + tty?: boolean; + entrypoint?: string; + arguments?: string[]; + }; + Volume: { + /** + * @description Name of the volume. + * @example tardis + */ + Name: string; + /** + * @description Name of the volume driver used by the volume. + * @example custom + */ + Driver: string; + /** + * @description Mount path of the volume on the host. + * @example /var/lib/docker/volumes/tardis + */ + Mountpoint: string; + /** + * Format: dateTime + * @description Date/Time the volume was created. + * @example 2016-06-07T20:31:11.853781916Z + */ + CreatedAt?: string; + /** + * @description Low-level details about the volume, provided by the volume driver. + * Details are returned as a map with key/value pairs: + * `{"key":"value","key2":"value2"}`. + * + * The `Status` field is optional, and is omitted if the volume driver + * does not support this feature. + * + * @example { + * "hello": "world" + * } + */ + Status?: { + [key: string]: Record; + }; + /** + * @description User-defined key/value metadata. + * @example { + * "com.example.some-label": "some-value", + * "com.example.some-other-label": "some-other-value" + * } + */ + Labels: { + [key: string]: string; + }; + /** + * @description The level at which the volume exists. Either `global` for cluster-wide, + * or `local` for machine level. + * + * @default local + * @example local + * @enum {string} + */ + Scope: "local" | "global"; + ClusterVolume?: components["schemas"]["ClusterVolume"]; + /** + * @description The driver specific options used when creating the volume. + * + * @example { + * "device": "tmpfs", + * "o": "size=100m,uid=1000", + * "type": "tmpfs" + * } + */ + Options: { + [key: string]: string; + }; + /** + * @description Usage details about the volume. This information is used by the + * `GET /system/df` endpoint, and omitted in other endpoints. + */ + UsageData?: { + /** + * Format: int64 + * @description Amount of disk space used by the volume (in bytes). This information + * is only available for volumes created with the `"local"` volume + * driver. For volumes created with other volume drivers, this field + * is set to `-1` ("not available") + * + * @default -1 + */ + Size: number; + /** + * Format: int64 + * @description The number of containers referencing this volume. This field + * is set to `-1` if the reference-count is not available. + * + * @default -1 + */ + RefCount: number; + } | null; + }; + /** + * VolumeConfig + * @description Volume configuration + */ + VolumeCreateOptions: { + /** + * @description The new volume's name. If not specified, Docker generates a name. + * + * @example tardis + */ + Name?: string; + /** + * @description Name of the volume driver to use. + * @default local + * @example custom + */ + Driver?: string; + /** + * @description A mapping of driver options and values. These options are + * passed directly to the driver and are driver specific. + * + * @example { + * "device": "tmpfs", + * "o": "size=100m,uid=1000", + * "type": "tmpfs" + * } + */ + DriverOpts?: { + [key: string]: string; + }; + /** + * @description User-defined key/value metadata. + * @example { + * "com.example.some-label": "some-value", + * "com.example.some-other-label": "some-other-value" + * } + */ + Labels?: { + [key: string]: string; + }; + ClusterVolumeSpec?: components["schemas"]["ClusterVolumeSpec"]; + }; + /** + * VolumeListResponse + * @description Volume list response + */ + VolumeListResponse: { + /** @description List of volumes */ + Volumes?: components["schemas"]["Volume"][]; + /** + * @description Warnings that occurred when fetching the list of volumes. + * + * @example [] + */ + Warnings?: string[]; + }; + /** + * @example { + * "Name": "net01", + * "Id": "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99", + * "Created": "2016-10-19T04:33:30.360899459Z", + * "Scope": "local", + * "Driver": "bridge", + * "EnableIPv6": false, + * "IPAM": { + * "Driver": "default", + * "Config": [ + * { + * "Subnet": "172.19.0.0/16", + * "Gateway": "172.19.0.1" + * } + * ], + * "Options": { + * "foo": "bar" + * } + * }, + * "Internal": false, + * "Attachable": false, + * "Ingress": false, + * "Containers": { + * "19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c": { + * "Name": "test", + * "EndpointID": "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a", + * "MacAddress": "02:42:ac:13:00:02", + * "IPv4Address": "172.19.0.2/16", + * "IPv6Address": "" + * } + * }, + * "Options": { + * "com.docker.network.bridge.default_bridge": "true", + * "com.docker.network.bridge.enable_icc": "true", + * "com.docker.network.bridge.enable_ip_masquerade": "true", + * "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", + * "com.docker.network.bridge.name": "docker0", + * "com.docker.network.driver.mtu": "1500" + * }, + * "Labels": { + * "com.example.some-label": "some-value", + * "com.example.some-other-label": "some-other-value" + * } + * } + */ + Network: { + Name?: string; + Id?: string; + /** Format: dateTime */ + Created?: string; + Scope?: string; + Driver?: string; + EnableIPv6?: boolean; + IPAM?: components["schemas"]["IPAM"]; + Internal?: boolean; + Attachable?: boolean; + Ingress?: boolean; + Containers?: { + [key: string]: components["schemas"]["NetworkContainer"]; + }; + Options?: { + [key: string]: string; + }; + Labels?: { + [key: string]: string; + }; + }; + IPAM: { + /** + * @description Name of the IPAM driver to use. + * @default default + */ + Driver?: string; + /** + * @description List of IPAM configuration options, specified as a map: + * + * ``` + * {"Subnet": , "IPRange": , "Gateway": , "AuxAddress": } + * ``` + */ + Config?: components["schemas"]["IPAMConfig"][]; + /** @description Driver-specific options, specified as a map. */ + Options?: { + [key: string]: string; + }; + }; + IPAMConfig: { + Subnet?: string; + IPRange?: string; + Gateway?: string; + AuxiliaryAddresses?: { + [key: string]: string; + }; + }; + NetworkContainer: { + Name?: string; + EndpointID?: string; + MacAddress?: string; + IPv4Address?: string; + IPv6Address?: string; + }; + BuildInfo: { + id?: string; + stream?: string; + error?: string; + errorDetail?: components["schemas"]["ErrorDetail"]; + status?: string; + progress?: string; + progressDetail?: components["schemas"]["ProgressDetail"]; + aux?: components["schemas"]["ImageID"]; + }; + /** @description BuildCache contains information about a build cache record. */ + BuildCache: { + /** + * @description Unique ID of the build cache record. + * + * @example ndlpt0hhvkqcdfkputsk4cq9c + */ + ID?: string; + /** + * @description ID of the parent build cache record. + * + * > **Deprecated**: This field is deprecated, and omitted if empty. + * + * @example + */ + Parent?: string | null; + /** + * @description List of parent build cache record IDs. + * + * @example [ + * "hw53o5aio51xtltp5xjp8v7fx" + * ] + */ + Parents?: string[] | null; + /** + * @description Cache record type. + * + * @example regular + * @enum {string} + */ + Type?: "internal" | "frontend" | "source.local" | "source.git.checkout" | "exec.cachemount" | "regular"; + /** + * @description Description of the build-step that produced the build cache. + * + * @example mount / from exec /bin/sh -c echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache + */ + Description?: string; + /** + * @description Indicates if the build cache is in use. + * + * @example false + */ + InUse?: boolean; + /** + * @description Indicates if the build cache is shared. + * + * @example true + */ + Shared?: boolean; + /** + * @description Amount of disk space used by the build cache (in bytes). + * + * @example 51 + */ + Size?: number; + /** + * Format: dateTime + * @description Date and time at which the build cache was created in + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + * + * @example 2016-08-18T10:44:24.496525531Z + */ + CreatedAt?: string; + /** + * Format: dateTime + * @description Date and time at which the build cache was last used in + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + * + * @example 2017-08-09T07:09:37.632105588Z + */ + LastUsedAt?: string | null; + /** @example 26 */ + UsageCount?: number; + }; + /** + * @description Image ID or Digest + * @example { + * "ID": "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + * } + */ + ImageID: { + ID?: string; + }; + CreateImageInfo: { + id?: string; + error?: string; + errorDetail?: components["schemas"]["ErrorDetail"]; + status?: string; + progress?: string; + progressDetail?: components["schemas"]["ProgressDetail"]; + }; + PushImageInfo: { + error?: string; + status?: string; + progress?: string; + progressDetail?: components["schemas"]["ProgressDetail"]; + }; + ErrorDetail: { + code?: number; + message?: string; + }; + ProgressDetail: { + current?: number; + total?: number; + }; + /** + * @description Represents an error. + * @example { + * "message": "Something went wrong." + * } + */ + ErrorResponse: { + /** @description The error message. */ + message: string; + }; + /** @description Response to an API call that returns just an Id */ + IdResponse: { + /** @description The id of the newly created object. */ + Id: string; + }; + /** @description Configuration for a network endpoint. */ + EndpointSettings: { + IPAMConfig?: components["schemas"]["EndpointIPAMConfig"]; + /** + * @example [ + * "container_1", + * "container_2" + * ] + */ + Links?: string[]; + /** + * @description MAC address for the endpoint on this network. The network driver might ignore this parameter. + * + * @example 02:42:ac:11:00:04 + */ + MacAddress?: string; + /** + * @example [ + * "server_x", + * "server_y" + * ] + */ + Aliases?: string[]; + /** + * @description Unique ID of the network. + * + * @example 08754567f1f40222263eab4102e1c733ae697e8e354aa9cd6e18d7402835292a + */ + NetworkID?: string; + /** + * @description Unique ID for the service endpoint in a Sandbox. + * + * @example b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b + */ + EndpointID?: string; + /** + * @description Gateway address for this network. + * + * @example 172.17.0.1 + */ + Gateway?: string; + /** + * @description IPv4 address. + * + * @example 172.17.0.4 + */ + IPAddress?: string; + /** + * @description Mask length of the IPv4 address. + * + * @example 16 + */ + IPPrefixLen?: number; + /** + * @description IPv6 gateway address. + * + * @example 2001:db8:2::100 + */ + IPv6Gateway?: string; + /** + * @description Global IPv6 address. + * + * @example 2001:db8::5689 + */ + GlobalIPv6Address?: string; + /** + * Format: int64 + * @description Mask length of the global IPv6 address. + * + * @example 64 + */ + GlobalIPv6PrefixLen?: number; + /** + * @description DriverOpts is a mapping of driver options and values. These options + * are passed directly to the driver and are driver specific. + * + * @example { + * "com.example.some-label": "some-value", + * "com.example.some-other-label": "some-other-value" + * } + */ + DriverOpts?: { + [key: string]: string; + } | null; + /** + * @description List of all DNS names an endpoint has on a specific network. This + * list is based on the container name, network aliases, container short + * ID, and hostname. + * + * These DNS names are non-fully qualified but can contain several dots. + * You can get fully qualified DNS names by appending `.`. + * For instance, if container name is `my.ctr` and the network is named + * `testnet`, `DNSNames` will contain `my.ctr` and the FQDN will be + * `my.ctr.testnet`. + * + * @example [ + * "foobar", + * "server_x", + * "server_y", + * "my.ctr" + * ] + */ + DNSNames?: string[]; + }; + /** @description EndpointIPAMConfig represents an endpoint's IPAM configuration. */ + EndpointIPAMConfig: { + /** @example 172.20.30.33 */ + IPv4Address?: string; + /** @example 2001:db8:abcd::3033 */ + IPv6Address?: string; + /** + * @example [ + * "169.254.34.68", + * "fe80::3468" + * ] + */ + LinkLocalIPs?: string[]; + } | null; + PluginMount: { + /** @example some-mount */ + Name: string; + /** @example This is a mount that's used by the plugin. */ + Description: string; + Settable: string[]; + /** @example /var/lib/docker/plugins/ */ + Source: string; + /** @example /mnt/state */ + Destination: string; + /** @example bind */ + Type: string; + /** + * @example [ + * "rbind", + * "rw" + * ] + */ + Options: string[]; + }; + PluginDevice: { + Name: string; + Description: string; + Settable: string[]; + /** @example /dev/fuse */ + Path: string; + }; + PluginEnv: { + Name: string; + Description: string; + Settable: string[]; + Value: string; + }; + PluginInterfaceType: { + Prefix: string; + Capability: string; + Version: string; + }; + /** + * @description Describes a permission the user has to accept upon installing + * the plugin. + */ + PluginPrivilege: { + /** @example network */ + Name?: string; + Description?: string; + /** + * @example [ + * "host" + * ] + */ + Value?: string[]; + }; + /** @description A plugin for the Engine API */ + Plugin: { + /** @example 5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078 */ + Id?: string; + /** @example tiborvass/sample-volume-plugin */ + Name: string; + /** + * @description True if the plugin is running. False if the plugin is not running, only installed. + * @example true + */ + Enabled: boolean; + /** @description Settings that can be modified by users. */ + Settings: { + Mounts: components["schemas"]["PluginMount"][]; + /** + * @example [ + * "DEBUG=0" + * ] + */ + Env: string[]; + Args: string[]; + Devices: components["schemas"]["PluginDevice"][]; + }; + /** + * @description plugin remote reference used to push/pull the plugin + * @example localhost:5000/tiborvass/sample-volume-plugin:latest + */ + PluginReference?: string; + /** @description The config of a plugin. */ + Config: { + /** + * @description Docker Version used to create the plugin + * @example 17.06.0-ce + */ + DockerVersion?: string; + /** @example A sample volume plugin for Docker */ + Description: string; + /** @example https://docs.docker.com/engine/extend/plugins/ */ + Documentation: string; + /** @description The interface between Docker and the plugin */ + Interface: { + /** + * @example [ + * "docker.volumedriver/1.0" + * ] + */ + Types: components["schemas"]["PluginInterfaceType"][]; + /** @example plugins.sock */ + Socket: string; + /** + * @description Protocol to use for clients connecting to the plugin. + * @example some.protocol/v1.0 + * @enum {string} + */ + ProtocolScheme?: "" | "moby.plugins.http/v1"; + }; + /** + * @example [ + * "/usr/bin/sample-volume-plugin", + * "/data" + * ] + */ + Entrypoint: string[]; + /** @example /bin/ */ + WorkDir: string; + User?: { + /** + * Format: uint32 + * @example 1000 + */ + UID?: number; + /** + * Format: uint32 + * @example 1000 + */ + GID?: number; + }; + Network: { + /** @example host */ + Type: string; + }; + Linux: { + /** + * @example [ + * "CAP_SYS_ADMIN", + * "CAP_SYSLOG" + * ] + */ + Capabilities: string[]; + /** @example false */ + AllowAllDevices: boolean; + Devices: components["schemas"]["PluginDevice"][]; + }; + /** @example /mnt/volumes */ + PropagatedMount: string; + /** @example false */ + IpcHost: boolean; + /** @example false */ + PidHost: boolean; + Mounts: components["schemas"]["PluginMount"][]; + /** + * @example [ + * { + * "Name": "DEBUG", + * "Description": "If set, prints debug messages", + * "Settable": null, + * "Value": "0" + * } + * ] + */ + Env: components["schemas"]["PluginEnv"][]; + Args: { + /** @example args */ + Name: string; + /** @example command line arguments */ + Description: string; + Settable: string[]; + Value: string[]; + }; + rootfs?: { + /** @example layers */ + type?: string; + /** + * @example [ + * "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887", + * "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + * ] + */ + diff_ids?: string[]; + }; + }; + }; + /** + * @description The version number of the object such as node, service, etc. This is needed + * to avoid conflicting writes. The client must send the version number along + * with the modified specification when updating these objects. + * + * This approach ensures safe concurrency and determinism in that the change + * on the object may not be applied if the version number has changed from the + * last read. In other words, if two update requests specify the same base + * version, only one of the requests can succeed. As a result, two separate + * update requests that happen at the same time will not unintentionally + * overwrite each other. + */ + ObjectVersion: { + /** + * Format: uint64 + * @example 373531 + */ + Index?: number; + }; + /** + * @example { + * "Availability": "active", + * "Name": "node-name", + * "Role": "manager", + * "Labels": { + * "foo": "bar" + * } + * } + */ + NodeSpec: { + /** + * @description Name for the node. + * @example my-node + */ + Name?: string; + /** @description User-defined key/value metadata. */ + Labels?: { + [key: string]: string; + }; + /** + * @description Role of the node. + * @example manager + * @enum {string} + */ + Role?: "worker" | "manager"; + /** + * @description Availability of the node. + * @example active + * @enum {string} + */ + Availability?: "active" | "pause" | "drain"; + }; + Node: { + /** @example 24ifsmvkjbyhk */ + ID?: string; + Version?: components["schemas"]["ObjectVersion"]; + /** + * Format: dateTime + * @description Date and time at which the node was added to the swarm in + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + * + * @example 2016-08-18T10:44:24.496525531Z + */ + CreatedAt?: string; + /** + * Format: dateTime + * @description Date and time at which the node was last updated in + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + * + * @example 2017-08-09T07:09:37.632105588Z + */ + UpdatedAt?: string; + Spec?: components["schemas"]["NodeSpec"]; + Description?: components["schemas"]["NodeDescription"]; + Status?: components["schemas"]["NodeStatus"]; + ManagerStatus?: components["schemas"]["ManagerStatus"]; + }; + /** + * @description NodeDescription encapsulates the properties of the Node as reported by the + * agent. + */ + NodeDescription: { + /** @example bf3067039e47 */ + Hostname?: string; + Platform?: components["schemas"]["Platform"]; + Resources?: components["schemas"]["ResourceObject"]; + Engine?: components["schemas"]["EngineDescription"]; + TLSInfo?: components["schemas"]["TLSInfo"]; + }; + /** @description Platform represents the platform (Arch/OS). */ + Platform: { + /** + * @description Architecture represents the hardware architecture (for example, + * `x86_64`). + * + * @example x86_64 + */ + Architecture?: string; + /** + * @description OS represents the Operating System (for example, `linux` or `windows`). + * + * @example linux + */ + OS?: string; + }; + /** @description EngineDescription provides information about an engine. */ + EngineDescription: { + /** @example 17.06.0 */ + EngineVersion?: string; + /** + * @example { + * "foo": "bar" + * } + */ + Labels?: { + [key: string]: string; + }; + /** + * @example [ + * { + * "Type": "Log", + * "Name": "awslogs" + * }, + * { + * "Type": "Log", + * "Name": "fluentd" + * }, + * { + * "Type": "Log", + * "Name": "gcplogs" + * }, + * { + * "Type": "Log", + * "Name": "gelf" + * }, + * { + * "Type": "Log", + * "Name": "journald" + * }, + * { + * "Type": "Log", + * "Name": "json-file" + * }, + * { + * "Type": "Log", + * "Name": "splunk" + * }, + * { + * "Type": "Log", + * "Name": "syslog" + * }, + * { + * "Type": "Network", + * "Name": "bridge" + * }, + * { + * "Type": "Network", + * "Name": "host" + * }, + * { + * "Type": "Network", + * "Name": "ipvlan" + * }, + * { + * "Type": "Network", + * "Name": "macvlan" + * }, + * { + * "Type": "Network", + * "Name": "null" + * }, + * { + * "Type": "Network", + * "Name": "overlay" + * }, + * { + * "Type": "Volume", + * "Name": "local" + * }, + * { + * "Type": "Volume", + * "Name": "localhost:5000/vieux/sshfs:latest" + * }, + * { + * "Type": "Volume", + * "Name": "vieux/sshfs:latest" + * } + * ] + */ + Plugins?: { + Type?: string; + Name?: string; + }[]; + }; + /** + * @description Information about the issuer of leaf TLS certificates and the trusted root + * CA certificate. + * + * @example { + * "TrustRoot": "-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0\nMzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf\n3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO\nPQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz\npxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H\n-----END CERTIFICATE-----\n", + * "CertIssuerSubject": "MBMxETAPBgNVBAMTCHN3YXJtLWNh", + * "CertIssuerPublicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + * } + */ + TLSInfo: { + /** + * @description The root CA certificate(s) that are used to validate leaf TLS + * certificates. + */ + TrustRoot?: string; + /** @description The base64-url-safe-encoded raw subject bytes of the issuer. */ + CertIssuerSubject?: string; + /** @description The base64-url-safe-encoded raw public key bytes of the issuer. */ + CertIssuerPublicKey?: string; + }; + /** + * @description NodeStatus represents the status of a node. + * + * It provides the current status of the node, as seen by the manager. + */ + NodeStatus: { + State?: components["schemas"]["NodeState"]; + /** @example */ + Message?: string; + /** + * @description IP address of the node. + * @example 172.17.0.2 + */ + Addr?: string; + }; + /** + * @description NodeState represents the state of a node. + * @example ready + * @enum {string} + */ + NodeState: "unknown" | "down" | "ready" | "disconnected"; + /** + * @description ManagerStatus represents the status of a manager. + * + * It provides the current status of a node's manager component, if the node + * is a manager. + */ + ManagerStatus: { + /** + * @default false + * @example true + */ + Leader?: boolean; + Reachability?: components["schemas"]["Reachability"]; + /** + * @description The IP address and port at which the manager is reachable. + * + * @example 10.0.0.46:2377 + */ + Addr?: string; + } | null; + /** + * @description Reachability represents the reachability of a node. + * @example reachable + * @enum {string} + */ + Reachability: "unknown" | "unreachable" | "reachable"; + /** @description User modifiable swarm configuration. */ + SwarmSpec: { + /** + * @description Name of the swarm. + * @example default + */ + Name?: string; + /** + * @description User-defined key/value metadata. + * @example { + * "com.example.corp.type": "production", + * "com.example.corp.department": "engineering" + * } + */ + Labels?: { + [key: string]: string; + }; + /** @description Orchestration configuration. */ + Orchestration?: { + /** + * Format: int64 + * @description The number of historic tasks to keep per instance or node. If + * negative, never remove completed or failed tasks. + * + * @example 10 + */ + TaskHistoryRetentionLimit?: number; + } | null; + /** @description Raft configuration. */ + Raft?: { + /** + * Format: uint64 + * @description The number of log entries between snapshots. + * @example 10000 + */ + SnapshotInterval?: number; + /** + * Format: uint64 + * @description The number of snapshots to keep beyond the current snapshot. + */ + KeepOldSnapshots?: number; + /** + * Format: uint64 + * @description The number of log entries to keep around to sync up slow followers + * after a snapshot is created. + * + * @example 500 + */ + LogEntriesForSlowFollowers?: number; + /** + * @description The number of ticks that a follower will wait for a message from + * the leader before becoming a candidate and starting an election. + * `ElectionTick` must be greater than `HeartbeatTick`. + * + * A tick currently defaults to one second, so these translate + * directly to seconds currently, but this is NOT guaranteed. + * + * @example 3 + */ + ElectionTick?: number; + /** + * @description The number of ticks between heartbeats. Every HeartbeatTick ticks, + * the leader will send a heartbeat to the followers. + * + * A tick currently defaults to one second, so these translate + * directly to seconds currently, but this is NOT guaranteed. + * + * @example 1 + */ + HeartbeatTick?: number; + }; + /** @description Dispatcher configuration. */ + Dispatcher?: { + /** + * Format: int64 + * @description The delay for an agent to send a heartbeat to the dispatcher. + * + * @example 5000000000 + */ + HeartbeatPeriod?: number; + } | null; + /** @description CA configuration. */ + CAConfig?: { + /** + * Format: int64 + * @description The duration node certificates are issued for. + * @example 7776000000000000 + */ + NodeCertExpiry?: number; + /** + * @description Configuration for forwarding signing requests to an external + * certificate authority. + */ + ExternalCAs?: { + /** + * @description Protocol for communication with the external CA (currently + * only `cfssl` is supported). + * + * @default cfssl + * @enum {string} + */ + Protocol?: "cfssl"; + /** @description URL where certificate signing requests should be sent. */ + URL?: string; + /** + * @description An object with key/value pairs that are interpreted as + * protocol-specific options for the external CA driver. + */ + Options?: { + [key: string]: string; + }; + /** + * @description The root CA certificate (in PEM format) this external CA uses + * to issue TLS certificates (assumed to be to the current swarm + * root CA certificate if not provided). + */ + CACert?: string; + }[]; + /** + * @description The desired signing CA certificate for all swarm node TLS leaf + * certificates, in PEM format. + */ + SigningCACert?: string; + /** + * @description The desired signing CA key for all swarm node TLS leaf certificates, + * in PEM format. + */ + SigningCAKey?: string; + /** + * Format: uint64 + * @description An integer whose purpose is to force swarm to generate a new + * signing CA certificate and key, if none have been specified in + * `SigningCACert` and `SigningCAKey` + */ + ForceRotate?: number; + } | null; + /** @description Parameters related to encryption-at-rest. */ + EncryptionConfig?: { + /** + * @description If set, generate a key and use it to lock data stored on the + * managers. + * + * @example false + */ + AutoLockManagers?: boolean; + }; + /** @description Defaults for creating tasks in this cluster. */ + TaskDefaults?: { + /** + * @description The log driver to use for tasks created in the orchestrator if + * unspecified by a service. + * + * Updating this value only affects new tasks. Existing tasks continue + * to use their previously configured log driver until recreated. + */ + LogDriver?: { + /** + * @description The log driver to use as a default for new tasks. + * + * @example json-file + */ + Name?: string; + /** + * @description Driver-specific options for the selectd log driver, specified + * as key/value pairs. + * + * @example { + * "max-file": "10", + * "max-size": "100m" + * } + */ + Options?: { + [key: string]: string; + }; + }; + }; + }; + /** + * @description ClusterInfo represents information about the swarm as is returned by the + * "/info" endpoint. Join-tokens are not included. + */ + ClusterInfo: { + /** + * @description The ID of the swarm. + * @example abajmipo7b4xz5ip2nrla6b11 + */ + ID?: string; + Version?: components["schemas"]["ObjectVersion"]; + /** + * Format: dateTime + * @description Date and time at which the swarm was initialised in + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + * + * @example 2016-08-18T10:44:24.496525531Z + */ + CreatedAt?: string; + /** + * Format: dateTime + * @description Date and time at which the swarm was last updated in + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + * + * @example 2017-08-09T07:09:37.632105588Z + */ + UpdatedAt?: string; + Spec?: components["schemas"]["SwarmSpec"]; + TLSInfo?: components["schemas"]["TLSInfo"]; + /** + * @description Whether there is currently a root CA rotation in progress for the swarm + * + * @example false + */ + RootRotationInProgress?: boolean; + /** + * Format: uint32 + * @description DataPathPort specifies the data path port number for data traffic. + * Acceptable port range is 1024 to 49151. + * If no port is set or is set to 0, the default port (4789) is used. + * + * @example 4789 + */ + DataPathPort?: number; + /** + * @description Default Address Pool specifies default subnet pools for global scope + * networks. + */ + DefaultAddrPool?: string[]; + /** + * Format: uint32 + * @description SubnetSize specifies the subnet size of the networks created from the + * default subnet pool. + * + * @example 24 + */ + SubnetSize?: number; + } | null; + /** @description JoinTokens contains the tokens workers and managers need to join the swarm. */ + JoinTokens: { + /** + * @description The token workers can use to join the swarm. + * + * @example SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx + */ + Worker?: string; + /** + * @description The token managers can use to join the swarm. + * + * @example SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2 + */ + Manager?: string; + }; + Swarm: components["schemas"]["ClusterInfo"] & { + JoinTokens?: components["schemas"]["JoinTokens"]; + }; + /** @description User modifiable task configuration. */ + TaskSpec: { + /** + * @description Plugin spec for the service. *(Experimental release only.)* + * + *


+ * + * > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + * > mutually exclusive. PluginSpec is only used when the Runtime field + * > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + * > field is set to `attachment`. + */ + PluginSpec?: { + /** @description The name or 'alias' to use for the plugin. */ + Name?: string; + /** @description The plugin image reference to use. */ + Remote?: string; + /** @description Disable the plugin once scheduled. */ + Disabled?: boolean; + PluginPrivilege?: components["schemas"]["PluginPrivilege"][]; + }; + /** + * @description Container spec for the service. + * + *


+ * + * > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + * > mutually exclusive. PluginSpec is only used when the Runtime field + * > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + * > field is set to `attachment`. + */ + ContainerSpec?: { + /** @description The image name to use for the container */ + Image?: string; + /** @description User-defined key/value data. */ + Labels?: { + [key: string]: string; + }; + /** @description The command to be run in the image. */ + Command?: string[]; + /** @description Arguments to the command. */ + Args?: string[]; + /** + * @description The hostname to use for the container, as a valid + * [RFC 1123](https://tools.ietf.org/html/rfc1123) hostname. + */ + Hostname?: string; + /** @description A list of environment variables in the form `VAR=value`. */ + Env?: string[]; + /** @description The working directory for commands to run in. */ + Dir?: string; + /** @description The user inside the container. */ + User?: string; + /** @description A list of additional groups that the container process will run as. */ + Groups?: string[]; + /** @description Security options for the container */ + Privileges?: { + /** @description CredentialSpec for managed service account (Windows only) */ + CredentialSpec?: { + /** + * @description Load credential spec from a Swarm Config with the given ID. + * The specified config must also be present in the Configs + * field with the Runtime property set. + * + *


+ * + * + * > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + * > and `CredentialSpec.Config` are mutually exclusive. + * + * @example 0bt9dmxjvjiqermk6xrop3ekq + */ + Config?: string; + /** + * @description Load credential spec from this file. The file is read by + * the daemon, and must be present in the `CredentialSpecs` + * subdirectory in the docker data directory, which defaults + * to `C:\ProgramData\Docker\` on Windows. + * + * For example, specifying `spec.json` loads + * `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + * + *


+ * + * > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + * > and `CredentialSpec.Config` are mutually exclusive. + * + * @example spec.json + */ + File?: string; + /** + * @description Load credential spec from this value in the Windows + * registry. The specified registry value must be located in: + * + * `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + * + *


+ * + * + * > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + * > and `CredentialSpec.Config` are mutually exclusive. + */ + Registry?: string; + }; + /** @description SELinux labels of the container */ + SELinuxContext?: { + /** @description Disable SELinux */ + Disable?: boolean; + /** @description SELinux user label */ + User?: string; + /** @description SELinux role label */ + Role?: string; + /** @description SELinux type label */ + Type?: string; + /** @description SELinux level label */ + Level?: string; + }; + /** @description Options for configuring seccomp on the container */ + Seccomp?: { + /** @enum {string} */ + Mode?: "default" | "unconfined" | "custom"; + /** @description The custom seccomp profile as a json object */ + Profile?: string; + }; + /** @description Options for configuring AppArmor on the container */ + AppArmor?: { + /** @enum {string} */ + Mode?: "default" | "disabled"; + }; + /** @description Configuration of the no_new_privs bit in the container */ + NoNewPrivileges?: boolean; + }; + /** @description Whether a pseudo-TTY should be allocated. */ + TTY?: boolean; + /** @description Open `stdin` */ + OpenStdin?: boolean; + /** @description Mount the container's root filesystem as read only. */ + ReadOnly?: boolean; + /** + * @description Specification for mounts to be added to containers created as part + * of the service. + */ + Mounts?: components["schemas"]["Mount"][]; + /** @description Signal to stop the container. */ + StopSignal?: string; + /** + * Format: int64 + * @description Amount of time to wait for the container to terminate before + * forcefully killing it. + */ + StopGracePeriod?: number; + HealthCheck?: components["schemas"]["HealthConfig"]; + /** + * @description A list of hostname/IP mappings to add to the container's `hosts` + * file. The format of extra hosts is specified in the + * [hosts(5)](http://man7.org/linux/man-pages/man5/hosts.5.html) + * man page: + * + * IP_address canonical_hostname [aliases...] + */ + Hosts?: string[]; + /** + * @description Specification for DNS related configurations in resolver configuration + * file (`resolv.conf`). + */ + DNSConfig?: { + /** @description The IP addresses of the name servers. */ + Nameservers?: string[]; + /** @description A search list for host-name lookup. */ + Search?: string[]; + /** + * @description A list of internal resolver variables to be modified (e.g., + * `debug`, `ndots:3`, etc.). + */ + Options?: string[]; + }; + /** + * @description Secrets contains references to zero or more secrets that will be + * exposed to the service. + */ + Secrets?: { + /** @description File represents a specific target that is backed by a file. */ + File?: { + /** @description Name represents the final filename in the filesystem. */ + Name?: string; + /** @description UID represents the file UID. */ + UID?: string; + /** @description GID represents the file GID. */ + GID?: string; + /** + * Format: uint32 + * @description Mode represents the FileMode of the file. + */ + Mode?: number; + }; + /** + * @description SecretID represents the ID of the specific secret that we're + * referencing. + */ + SecretID?: string; + /** + * @description SecretName is the name of the secret that this references, + * but this is just provided for lookup/display purposes. The + * secret in the reference will be identified by its ID. + */ + SecretName?: string; + }[]; + /** + * @description Configs contains references to zero or more configs that will be + * exposed to the service. + */ + Configs?: { + /** + * @description File represents a specific target that is backed by a file. + * + *


+ * + * > **Note**: `Configs.File` and `Configs.Runtime` are mutually exclusive + */ + File?: { + /** @description Name represents the final filename in the filesystem. */ + Name?: string; + /** @description UID represents the file UID. */ + UID?: string; + /** @description GID represents the file GID. */ + GID?: string; + /** + * Format: uint32 + * @description Mode represents the FileMode of the file. + */ + Mode?: number; + }; + /** + * @description Runtime represents a target that is not mounted into the + * container but is used by the task + * + *


+ * + * > **Note**: `Configs.File` and `Configs.Runtime` are mutually + * > exclusive + */ + Runtime?: Record; + /** + * @description ConfigID represents the ID of the specific config that we're + * referencing. + */ + ConfigID?: string; + /** + * @description ConfigName is the name of the config that this references, + * but this is just provided for lookup/display purposes. The + * config in the reference will be identified by its ID. + */ + ConfigName?: string; + }[]; + /** + * @description Isolation technology of the containers running the service. + * (Windows only) + * + * @enum {string} + */ + Isolation?: "default" | "process" | "hyperv"; + /** + * @description Run an init inside the container that forwards signals and reaps + * processes. This field is omitted if empty, and the default (as + * configured on the daemon) is used. + */ + Init?: boolean | null; + /** + * @description Set kernel namedspaced parameters (sysctls) in the container. + * The Sysctls option on services accepts the same sysctls as the + * are supported on containers. Note that while the same sysctls are + * supported, no guarantees or checks are made about their + * suitability for a clustered environment, and it's up to the user + * to determine whether a given sysctl will work properly in a + * Service. + */ + Sysctls?: { + [key: string]: string; + }; + /** + * @description A list of kernel capabilities to add to the default set + * for the container. + * + * @example [ + * "CAP_NET_RAW", + * "CAP_SYS_ADMIN", + * "CAP_SYS_CHROOT", + * "CAP_SYSLOG" + * ] + */ + CapabilityAdd?: string[]; + /** + * @description A list of kernel capabilities to drop from the default set + * for the container. + * + * @example [ + * "CAP_NET_RAW" + * ] + */ + CapabilityDrop?: string[]; + /** @description A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" */ + Ulimits?: { + /** @description Name of ulimit */ + Name?: string; + /** @description Soft limit */ + Soft?: number; + /** @description Hard limit */ + Hard?: number; + }[]; + }; + /** + * @description Read-only spec type for non-swarm containers attached to swarm overlay + * networks. + * + *


+ * + * > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + * > mutually exclusive. PluginSpec is only used when the Runtime field + * > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + * > field is set to `attachment`. + */ + NetworkAttachmentSpec?: { + /** @description ID of the container represented by this task */ + ContainerID?: string; + }; + /** + * @description Resource requirements which apply to each individual container created + * as part of the service. + */ + Resources?: { + Limits?: components["schemas"]["Limit"]; + Reservations?: components["schemas"]["ResourceObject"]; + }; + /** + * @description Specification for the restart policy which applies to containers + * created as part of this service. + */ + RestartPolicy?: { + /** + * @description Condition for restart. + * @enum {string} + */ + Condition?: "none" | "on-failure" | "any"; + /** + * Format: int64 + * @description Delay between restart attempts. + */ + Delay?: number; + /** + * Format: int64 + * @description Maximum attempts to restart a given container before giving up + * (default value is 0, which is ignored). + * + * @default 0 + */ + MaxAttempts?: number; + /** + * Format: int64 + * @description Windows is the time window used to evaluate the restart policy + * (default value is 0, which is unbounded). + * + * @default 0 + */ + Window?: number; + }; + Placement?: { + /** + * @description An array of constraint expressions to limit the set of nodes where + * a task can be scheduled. Constraint expressions can either use a + * _match_ (`==`) or _exclude_ (`!=`) rule. Multiple constraints find + * nodes that satisfy every expression (AND match). Constraints can + * match node or Docker Engine labels as follows: + * + * node attribute | matches | example + * ---------------------|--------------------------------|----------------------------------------------- + * `node.id` | Node ID | `node.id==2ivku8v2gvtg4` + * `node.hostname` | Node hostname | `node.hostname!=node-2` + * `node.role` | Node role (`manager`/`worker`) | `node.role==manager` + * `node.platform.os` | Node operating system | `node.platform.os==windows` + * `node.platform.arch` | Node architecture | `node.platform.arch==x86_64` + * `node.labels` | User-defined node labels | `node.labels.security==high` + * `engine.labels` | Docker Engine's labels | `engine.labels.operatingsystem==ubuntu-14.04` + * + * `engine.labels` apply to Docker Engine labels like operating system, + * drivers, etc. Swarm administrators add `node.labels` for operational + * purposes by using the [`node update endpoint`](#operation/NodeUpdate). + * + * @example [ + * "node.hostname!=node3.corp.example.com", + * "node.role!=manager", + * "node.labels.type==production", + * "node.platform.os==linux", + * "node.platform.arch==x86_64" + * ] + */ + Constraints?: string[]; + /** + * @description Preferences provide a way to make the scheduler aware of factors + * such as topology. They are provided in order from highest to + * lowest precedence. + * + * @example [ + * { + * "Spread": { + * "SpreadDescriptor": "node.labels.datacenter" + * } + * }, + * { + * "Spread": { + * "SpreadDescriptor": "node.labels.rack" + * } + * } + * ] + */ + Preferences?: { + Spread?: { + /** @description label descriptor, such as `engine.labels.az`. */ + SpreadDescriptor?: string; + }; + }[]; + /** + * Format: int64 + * @description Maximum number of replicas for per node (default value is 0, which + * is unlimited) + * + * @default 0 + */ + MaxReplicas?: number; + /** + * @description Platforms stores all the platforms that the service's image can + * run on. This field is used in the platform filter for scheduling. + * If empty, then the platform filter is off, meaning there are no + * scheduling restrictions. + */ + Platforms?: components["schemas"]["Platform"][]; + }; + /** + * @description A counter that triggers an update even if no relevant parameters have + * been changed. + */ + ForceUpdate?: number; + /** @description Runtime is the type of runtime specified for the task executor. */ + Runtime?: string; + /** @description Specifies which networks the service should attach to. */ + Networks?: components["schemas"]["NetworkAttachmentConfig"][]; + /** + * @description Specifies the log driver to use for tasks created from this spec. If + * not present, the default one for the swarm will be used, finally + * falling back to the engine default if not specified. + */ + LogDriver?: { + Name?: string; + Options?: { + [key: string]: string; + }; + }; + }; + /** @enum {string} */ + TaskState: "new" | "allocated" | "pending" | "assigned" | "accepted" | "preparing" | "ready" | "starting" | "running" | "complete" | "shutdown" | "failed" | "rejected" | "remove" | "orphaned"; + /** @description represents the status of a container. */ + ContainerStatus: { + ContainerID?: string; + PID?: number; + ExitCode?: number; + }; + /** @description represents the port status of a task's host ports whose service has published host ports */ + PortStatus: { + Ports?: components["schemas"]["EndpointPortConfig"][]; + }; + /** @description represents the status of a task. */ + TaskStatus: { + /** Format: dateTime */ + Timestamp?: string; + State?: components["schemas"]["TaskState"]; + Message?: string; + Err?: string; + ContainerStatus?: components["schemas"]["ContainerStatus"]; + PortStatus?: components["schemas"]["PortStatus"]; + }; + /** + * @example { + * "ID": "0kzzo1i0y4jz6027t0k7aezc7", + * "Version": { + * "Index": 71 + * }, + * "CreatedAt": "2016-06-07T21:07:31.171892745Z", + * "UpdatedAt": "2016-06-07T21:07:31.376370513Z", + * "Spec": { + * "ContainerSpec": { + * "Image": "redis" + * }, + * "Resources": { + * "Limits": {}, + * "Reservations": {} + * }, + * "RestartPolicy": { + * "Condition": "any", + * "MaxAttempts": 0 + * }, + * "Placement": {} + * }, + * "ServiceID": "9mnpnzenvg8p8tdbtq4wvbkcz", + * "Slot": 1, + * "NodeID": "60gvrl6tm78dmak4yl7srz94v", + * "Status": { + * "Timestamp": "2016-06-07T21:07:31.290032978Z", + * "State": "running", + * "Message": "started", + * "ContainerStatus": { + * "ContainerID": "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035", + * "PID": 677 + * } + * }, + * "DesiredState": "running", + * "NetworksAttachments": [ + * { + * "Network": { + * "ID": "4qvuz4ko70xaltuqbt8956gd1", + * "Version": { + * "Index": 18 + * }, + * "CreatedAt": "2016-06-07T20:31:11.912919752Z", + * "UpdatedAt": "2016-06-07T21:07:29.955277358Z", + * "Spec": { + * "Name": "ingress", + * "Labels": { + * "com.docker.swarm.internal": "true" + * }, + * "DriverConfiguration": {}, + * "IPAMOptions": { + * "Driver": {}, + * "Configs": [ + * { + * "Subnet": "10.255.0.0/16", + * "Gateway": "10.255.0.1" + * } + * ] + * } + * }, + * "DriverState": { + * "Name": "overlay", + * "Options": { + * "com.docker.network.driver.overlay.vxlanid_list": "256" + * } + * }, + * "IPAMOptions": { + * "Driver": { + * "Name": "default" + * }, + * "Configs": [ + * { + * "Subnet": "10.255.0.0/16", + * "Gateway": "10.255.0.1" + * } + * ] + * } + * }, + * "Addresses": [ + * "10.255.0.10/16" + * ] + * } + * ], + * "AssignedGenericResources": [ + * { + * "DiscreteResourceSpec": { + * "Kind": "SSD", + * "Value": 3 + * } + * }, + * { + * "NamedResourceSpec": { + * "Kind": "GPU", + * "Value": "UUID1" + * } + * }, + * { + * "NamedResourceSpec": { + * "Kind": "GPU", + * "Value": "UUID2" + * } + * } + * ] + * } + */ + Task: { + /** @description The ID of the task. */ + ID?: string; + Version?: components["schemas"]["ObjectVersion"]; + /** Format: dateTime */ + CreatedAt?: string; + /** Format: dateTime */ + UpdatedAt?: string; + /** @description Name of the task. */ + Name?: string; + /** @description User-defined key/value metadata. */ + Labels?: { + [key: string]: string; + }; + Spec?: components["schemas"]["TaskSpec"]; + /** @description The ID of the service this task is part of. */ + ServiceID?: string; + Slot?: number; + /** @description The ID of the node that this task is on. */ + NodeID?: string; + AssignedGenericResources?: components["schemas"]["GenericResources"]; + Status?: components["schemas"]["TaskStatus"]; + DesiredState?: components["schemas"]["TaskState"]; + JobIteration?: components["schemas"]["ObjectVersion"]; + }; + /** @description User modifiable configuration for a service. */ + ServiceSpec: { + /** @description Name of the service. */ + Name?: string; + /** @description User-defined key/value metadata. */ + Labels?: { + [key: string]: string; + }; + TaskTemplate?: components["schemas"]["TaskSpec"]; + /** @description Scheduling mode for the service. */ + Mode?: { + Replicated?: { + /** Format: int64 */ + Replicas?: number; + }; + Global?: Record; + /** + * @description The mode used for services with a finite number of tasks that run + * to a completed state. + */ + ReplicatedJob?: { + /** + * Format: int64 + * @description The maximum number of replicas to run simultaneously. + * + * @default 1 + */ + MaxConcurrent?: number; + /** + * Format: int64 + * @description The total number of replicas desired to reach the Completed + * state. If unset, will default to the value of `MaxConcurrent` + */ + TotalCompletions?: number; + }; + /** + * @description The mode used for services which run a task to the completed state + * on each valid node. + */ + GlobalJob?: Record; + }; + /** @description Specification for the update strategy of the service. */ + UpdateConfig?: { + /** + * Format: int64 + * @description Maximum number of tasks to be updated in one iteration (0 means + * unlimited parallelism). + */ + Parallelism?: number; + /** + * Format: int64 + * @description Amount of time between updates, in nanoseconds. + */ + Delay?: number; + /** + * @description Action to take if an updated task fails to run, or stops running + * during the update. + * + * @enum {string} + */ + FailureAction?: "continue" | "pause" | "rollback"; + /** + * Format: int64 + * @description Amount of time to monitor each updated task for failures, in + * nanoseconds. + */ + Monitor?: number; + /** + * @description The fraction of tasks that may fail during an update before the + * failure action is invoked, specified as a floating point number + * between 0 and 1. + */ + MaxFailureRatio?: number; + /** + * @description The order of operations when rolling out an updated task. Either + * the old task is shut down before the new task is started, or the + * new task is started before the old task is shut down. + * + * @enum {string} + */ + Order?: "stop-first" | "start-first"; + }; + /** @description Specification for the rollback strategy of the service. */ + RollbackConfig?: { + /** + * Format: int64 + * @description Maximum number of tasks to be rolled back in one iteration (0 means + * unlimited parallelism). + */ + Parallelism?: number; + /** + * Format: int64 + * @description Amount of time between rollback iterations, in nanoseconds. + */ + Delay?: number; + /** + * @description Action to take if an rolled back task fails to run, or stops + * running during the rollback. + * + * @enum {string} + */ + FailureAction?: "continue" | "pause"; + /** + * Format: int64 + * @description Amount of time to monitor each rolled back task for failures, in + * nanoseconds. + */ + Monitor?: number; + /** + * @description The fraction of tasks that may fail during a rollback before the + * failure action is invoked, specified as a floating point number + * between 0 and 1. + */ + MaxFailureRatio?: number; + /** + * @description The order of operations when rolling back a task. Either the old + * task is shut down before the new task is started, or the new task + * is started before the old task is shut down. + * + * @enum {string} + */ + Order?: "stop-first" | "start-first"; + }; + /** + * @description Specifies which networks the service should attach to. + * + * Deprecated: This field is deprecated since v1.44. The Networks field in TaskSpec should be used instead. + */ + Networks?: components["schemas"]["NetworkAttachmentConfig"][]; + EndpointSpec?: components["schemas"]["EndpointSpec"]; + }; + EndpointPortConfig: { + Name?: string; + /** @enum {string} */ + Protocol?: "tcp" | "udp" | "sctp"; + /** @description The port inside the container. */ + TargetPort?: number; + /** @description The port on the swarm hosts. */ + PublishedPort?: number; + /** + * @description The mode in which port is published. + * + *


+ * + * - "ingress" makes the target port accessible on every node, + * regardless of whether there is a task for the service running on + * that node or not. + * - "host" bypasses the routing mesh and publish the port directly on + * the swarm node where that service is running. + * + * @default ingress + * @example ingress + * @enum {string} + */ + PublishMode?: "ingress" | "host"; + }; + /** @description Properties that can be configured to access and load balance a service. */ + EndpointSpec: { + /** + * @description The mode of resolution to use for internal load balancing between tasks. + * + * @default vip + * @enum {string} + */ + Mode?: "vip" | "dnsrr"; + /** + * @description List of exposed ports that this service is accessible on from the + * outside. Ports can only be provided if `vip` resolution mode is used. + */ + Ports?: components["schemas"]["EndpointPortConfig"][]; + }; + /** + * @example { + * "ID": "9mnpnzenvg8p8tdbtq4wvbkcz", + * "Version": { + * "Index": 19 + * }, + * "CreatedAt": "2016-06-07T21:05:51.880065305Z", + * "UpdatedAt": "2016-06-07T21:07:29.962229872Z", + * "Spec": { + * "Name": "hopeful_cori", + * "TaskTemplate": { + * "ContainerSpec": { + * "Image": "redis" + * }, + * "Resources": { + * "Limits": {}, + * "Reservations": {} + * }, + * "RestartPolicy": { + * "Condition": "any", + * "MaxAttempts": 0 + * }, + * "Placement": {}, + * "ForceUpdate": 0 + * }, + * "Mode": { + * "Replicated": { + * "Replicas": 1 + * } + * }, + * "UpdateConfig": { + * "Parallelism": 1, + * "Delay": 1000000000, + * "FailureAction": "pause", + * "Monitor": 15000000000, + * "MaxFailureRatio": 0.15 + * }, + * "RollbackConfig": { + * "Parallelism": 1, + * "Delay": 1000000000, + * "FailureAction": "pause", + * "Monitor": 15000000000, + * "MaxFailureRatio": 0.15 + * }, + * "EndpointSpec": { + * "Mode": "vip", + * "Ports": [ + * { + * "Protocol": "tcp", + * "TargetPort": 6379, + * "PublishedPort": 30001 + * } + * ] + * } + * }, + * "Endpoint": { + * "Spec": { + * "Mode": "vip", + * "Ports": [ + * { + * "Protocol": "tcp", + * "TargetPort": 6379, + * "PublishedPort": 30001 + * } + * ] + * }, + * "Ports": [ + * { + * "Protocol": "tcp", + * "TargetPort": 6379, + * "PublishedPort": 30001 + * } + * ], + * "VirtualIPs": [ + * { + * "NetworkID": "4qvuz4ko70xaltuqbt8956gd1", + * "Addr": "10.255.0.2/16" + * }, + * { + * "NetworkID": "4qvuz4ko70xaltuqbt8956gd1", + * "Addr": "10.255.0.3/16" + * } + * ] + * } + * } + */ + Service: { + ID?: string; + Version?: components["schemas"]["ObjectVersion"]; + /** Format: dateTime */ + CreatedAt?: string; + /** Format: dateTime */ + UpdatedAt?: string; + Spec?: components["schemas"]["ServiceSpec"]; + Endpoint?: { + Spec?: components["schemas"]["EndpointSpec"]; + Ports?: components["schemas"]["EndpointPortConfig"][]; + VirtualIPs?: { + NetworkID?: string; + Addr?: string; + }[]; + }; + /** @description The status of a service update. */ + UpdateStatus?: { + /** @enum {string} */ + State?: "updating" | "paused" | "completed"; + /** Format: dateTime */ + StartedAt?: string; + /** Format: dateTime */ + CompletedAt?: string; + Message?: string; + }; + /** + * @description The status of the service's tasks. Provided only when requested as + * part of a ServiceList operation. + */ + ServiceStatus?: { + /** + * Format: uint64 + * @description The number of tasks for the service currently in the Running state. + * + * @example 7 + */ + RunningTasks?: number; + /** + * Format: uint64 + * @description The number of tasks for the service desired to be running. + * For replicated services, this is the replica count from the + * service spec. For global services, this is computed by taking + * count of all tasks for the service with a Desired State other + * than Shutdown. + * + * @example 10 + */ + DesiredTasks?: number; + /** + * Format: uint64 + * @description The number of tasks for a job that are in the Completed state. + * This field must be cross-referenced with the service type, as the + * value of 0 may mean the service is not in a job mode, or it may + * mean the job-mode service has no tasks yet Completed. + */ + CompletedTasks?: number; + }; + /** + * @description The status of the service when it is in one of ReplicatedJob or + * GlobalJob modes. Absent on Replicated and Global mode services. The + * JobIteration is an ObjectVersion, but unlike the Service's version, + * does not need to be sent with an update request. + */ + JobStatus?: { + JobIteration?: components["schemas"]["ObjectVersion"]; + /** + * Format: dateTime + * @description The last time, as observed by the server, that this job was + * started. + */ + LastExecution?: string; + }; + }; + ImageDeleteResponseItem: { + /** @description The image ID of an image that was untagged */ + Untagged?: string; + /** @description The image ID of an image that was deleted */ + Deleted?: string; + }; + /** + * @description contains the information returned to a client on the + * creation of a new service. + */ + ServiceCreateResponse: { + /** + * @description The ID of the created service. + * @example ak7w3gjqoa3kuz8xcpnyy0pvl + */ + ID?: string; + /** + * @description Optional warning message. + * + * FIXME(thaJeztah): this should have "omitempty" in the generated type. + * + * @example [ + * "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + * ] + */ + Warnings?: string[] | null; + }; + /** + * @example { + * "Warnings": [ + * "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + * ] + * } + */ + ServiceUpdateResponse: { + /** @description Optional warning messages */ + Warnings?: string[]; + }; + ContainerSummary: { + /** @description The ID of this container */ + Id?: string; + /** @description The names that this container has been given */ + Names?: string[]; + /** @description The name of the image used when creating this container */ + Image?: string; + /** @description The ID of the image that this container was created from */ + ImageID?: string; + /** @description Command to run when starting the container */ + Command?: string; + /** + * Format: int64 + * @description When the container was created + */ + Created?: number; + /** @description The ports exposed by this container */ + Ports?: components["schemas"]["Port"][]; + /** + * Format: int64 + * @description The size of files that have been created or changed by this container + */ + SizeRw?: number; + /** + * Format: int64 + * @description The total size of all the files in this container + */ + SizeRootFs?: number; + /** @description User-defined key/value metadata. */ + Labels?: { + [key: string]: string; + }; + /** @description The state of this container (e.g. `Exited`) */ + State?: string; + /** @description Additional human-readable status of this container (e.g. `Exit 0`) */ + Status?: string; + HostConfig?: { + NetworkMode?: string; + }; + /** @description A summary of the container's network settings */ + NetworkSettings?: { + Networks?: { + [key: string]: components["schemas"]["EndpointSettings"]; + }; + }; + Mounts?: components["schemas"]["MountPoint"][]; + }; + /** @description Driver represents a driver (network, logging, secrets). */ + Driver: { + /** + * @description Name of the driver. + * @example some-driver + */ + Name: string; + /** + * @description Key/value map of driver-specific options. + * @example { + * "OptionA": "value for driver-specific option A", + * "OptionB": "value for driver-specific option B" + * } + */ + Options?: { + [key: string]: string; + }; + }; + SecretSpec: { + /** @description User-defined name of the secret. */ + Name?: string; + /** + * @description User-defined key/value metadata. + * @example { + * "com.example.some-label": "some-value", + * "com.example.some-other-label": "some-other-value" + * } + */ + Labels?: { + [key: string]: string; + }; + /** + * @description Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) + * data to store as secret. + * + * This field is only used to _create_ a secret, and is not returned by + * other endpoints. + * + * @example + */ + Data?: string; + Driver?: components["schemas"]["Driver"]; + Templating?: components["schemas"]["Driver"]; + }; + Secret: { + /** @example blt1owaxmitz71s9v5zh81zun */ + ID?: string; + Version?: components["schemas"]["ObjectVersion"]; + /** + * Format: dateTime + * @example 2017-07-20T13:55:28.678958722Z + */ + CreatedAt?: string; + /** + * Format: dateTime + * @example 2017-07-20T13:55:28.678958722Z + */ + UpdatedAt?: string; + Spec?: components["schemas"]["SecretSpec"]; + }; + ConfigSpec: { + /** @description User-defined name of the config. */ + Name?: string; + /** @description User-defined key/value metadata. */ + Labels?: { + [key: string]: string; + }; + /** + * @description Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) + * config data. + */ + Data?: string; + Templating?: components["schemas"]["Driver"]; + }; + Config: { + ID?: string; + Version?: components["schemas"]["ObjectVersion"]; + /** Format: dateTime */ + CreatedAt?: string; + /** Format: dateTime */ + UpdatedAt?: string; + Spec?: components["schemas"]["ConfigSpec"]; + }; + /** + * @description ContainerState stores container's running state. It's part of ContainerJSONBase + * and will be returned by the "inspect" command. + */ + ContainerState: ({ + /** + * @description String representation of the container state. Can be one of "created", + * "running", "paused", "restarting", "removing", "exited", or "dead". + * + * @example running + * @enum {string} + */ + Status?: "created" | "running" | "paused" | "restarting" | "removing" | "exited" | "dead"; + /** + * @description Whether this container is running. + * + * Note that a running container can be _paused_. The `Running` and `Paused` + * booleans are not mutually exclusive: + * + * When pausing a container (on Linux), the freezer cgroup is used to suspend + * all processes in the container. Freezing the process requires the process to + * be running. As a result, paused containers are both `Running` _and_ `Paused`. + * + * Use the `Status` field instead to determine if a container's state is "running". + * + * @example true + */ + Running?: boolean; + /** + * @description Whether this container is paused. + * @example false + */ + Paused?: boolean; + /** + * @description Whether this container is restarting. + * @example false + */ + Restarting?: boolean; + /** + * @description Whether a process within this container has been killed because it ran + * out of memory since the container was last started. + * + * @example false + */ + OOMKilled?: boolean; + /** @example false */ + Dead?: boolean; + /** + * @description The process ID of this container + * @example 1234 + */ + Pid?: number; + /** + * @description The last exit code of this container + * @example 0 + */ + ExitCode?: number; + Error?: string; + /** + * @description The time when this container was last started. + * @example 2020-01-06T09:06:59.461876391Z + */ + StartedAt?: string; + /** + * @description The time when this container last exited. + * @example 2020-01-06T09:07:59.461876391Z + */ + FinishedAt?: string; + Health?: components["schemas"]["Health"]; + }) | null; + /** + * ContainerCreateResponse + * @description OK response to ContainerCreate operation + */ + ContainerCreateResponse: { + /** + * @description The ID of the created container + * @example ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743 + */ + Id: string; + /** + * @description Warnings encountered when creating the container + * @example [] + */ + Warnings: string[]; + }; + /** + * ContainerWaitResponse + * @description OK response to ContainerWait operation + */ + ContainerWaitResponse: { + /** + * Format: int64 + * @description Exit code of the container + */ + StatusCode: number; + Error?: components["schemas"]["ContainerWaitExitError"]; + }; + /** @description container waiting error, if any */ + ContainerWaitExitError: { + /** @description Details of an error */ + Message?: string; + }; + /** @description Response of Engine API: GET "/version" */ + SystemVersion: { + Platform?: { + Name: string; + }; + /** @description Information about system components */ + Components?: ({ + /** + * @description Name of the component + * + * @example Engine + */ + Name: string; + /** + * @description Version of the component + * + * @example 19.03.12 + */ + Version: string; + /** + * @description Key/value pairs of strings with additional information about the + * component. These values are intended for informational purposes + * only, and their content is not defined, and not part of the API + * specification. + * + * These messages can be printed by the client as information to the user. + */ + Details?: Record | null; + })[]; + /** + * @description The version of the daemon + * @example 19.03.12 + */ + Version?: string; + /** + * @description The default (and highest) API version that is supported by the daemon + * + * @example 1.40 + */ + ApiVersion?: string; + /** + * @description The minimum API version that is supported by the daemon + * + * @example 1.12 + */ + MinAPIVersion?: string; + /** + * @description The Git commit of the source code that was used to build the daemon + * + * @example 48a66213fe + */ + GitCommit?: string; + /** + * @description The version Go used to compile the daemon, and the version of the Go + * runtime in use. + * + * @example go1.13.14 + */ + GoVersion?: string; + /** + * @description The operating system that the daemon is running on ("linux" or "windows") + * + * @example linux + */ + Os?: string; + /** + * @description The architecture that the daemon is running on + * + * @example amd64 + */ + Arch?: string; + /** + * @description The kernel version (`uname -r`) that the daemon is running on. + * + * This field is omitted when empty. + * + * @example 4.19.76-linuxkit + */ + KernelVersion?: string; + /** + * @description Indicates if the daemon is started with experimental features enabled. + * + * This field is omitted when empty / false. + * + * @example true + */ + Experimental?: boolean; + /** + * @description The date and time that the daemon was compiled. + * + * @example 2020-06-22T15:49:27.000000000+00:00 + */ + BuildTime?: string; + }; + SystemInfo: { + /** + * @description Unique identifier of the daemon. + * + *


+ * + * > **Note**: The format of the ID itself is not part of the API, and + * > should not be considered stable. + * + * @example 7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS + */ + ID?: string; + /** + * @description Total number of containers on the host. + * @example 14 + */ + Containers?: number; + /** + * @description Number of containers with status `"running"`. + * + * @example 3 + */ + ContainersRunning?: number; + /** + * @description Number of containers with status `"paused"`. + * + * @example 1 + */ + ContainersPaused?: number; + /** + * @description Number of containers with status `"stopped"`. + * + * @example 10 + */ + ContainersStopped?: number; + /** + * @description Total number of images on the host. + * + * Both _tagged_ and _untagged_ (dangling) images are counted. + * + * @example 508 + */ + Images?: number; + /** + * @description Name of the storage driver in use. + * @example overlay2 + */ + Driver?: string; + /** + * @description Information specific to the storage driver, provided as + * "label" / "value" pairs. + * + * This information is provided by the storage driver, and formatted + * in a way consistent with the output of `docker info` on the command + * line. + * + *


+ * + * > **Note**: The information returned in this field, including the + * > formatting of values and labels, should not be considered stable, + * > and may change without notice. + * + * @example [ + * [ + * "Backing Filesystem", + * "extfs" + * ], + * [ + * "Supports d_type", + * "true" + * ], + * [ + * "Native Overlay Diff", + * "true" + * ] + * ] + */ + DriverStatus?: string[][]; + /** + * @description Root directory of persistent Docker state. + * + * Defaults to `/var/lib/docker` on Linux, and `C:\ProgramData\docker` + * on Windows. + * + * @example /var/lib/docker + */ + DockerRootDir?: string; + Plugins?: components["schemas"]["PluginsInfo"]; + /** + * @description Indicates if the host has memory limit support enabled. + * @example true + */ + MemoryLimit?: boolean; + /** + * @description Indicates if the host has memory swap limit support enabled. + * @example true + */ + SwapLimit?: boolean; + /** + * @description Indicates if the host has kernel memory TCP limit support enabled. This + * field is omitted if not supported. + * + * Kernel memory TCP limits are not supported when using cgroups v2, which + * does not support the corresponding `memory.kmem.tcp.limit_in_bytes` cgroup. + * + * @example true + */ + KernelMemoryTCP?: boolean; + /** + * @description Indicates if CPU CFS(Completely Fair Scheduler) period is supported by + * the host. + * + * @example true + */ + CpuCfsPeriod?: boolean; + /** + * @description Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by + * the host. + * + * @example true + */ + CpuCfsQuota?: boolean; + /** + * @description Indicates if CPU Shares limiting is supported by the host. + * + * @example true + */ + CPUShares?: boolean; + /** + * @description Indicates if CPUsets (cpuset.cpus, cpuset.mems) are supported by the host. + * + * See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) + * + * @example true + */ + CPUSet?: boolean; + /** + * @description Indicates if the host kernel has PID limit support enabled. + * @example true + */ + PidsLimit?: boolean; + /** @description Indicates if OOM killer disable is supported on the host. */ + OomKillDisable?: boolean; + /** + * @description Indicates IPv4 forwarding is enabled. + * @example true + */ + IPv4Forwarding?: boolean; + /** + * @description Indicates if `bridge-nf-call-iptables` is available on the host. + * @example true + */ + BridgeNfIptables?: boolean; + /** + * @description Indicates if `bridge-nf-call-ip6tables` is available on the host. + * @example true + */ + BridgeNfIp6tables?: boolean; + /** + * @description Indicates if the daemon is running in debug-mode / with debug-level + * logging enabled. + * + * @example true + */ + Debug?: boolean; + /** + * @description The total number of file Descriptors in use by the daemon process. + * + * This information is only returned if debug-mode is enabled. + * + * @example 64 + */ + NFd?: number; + /** + * @description The number of goroutines that currently exist. + * + * This information is only returned if debug-mode is enabled. + * + * @example 174 + */ + NGoroutines?: number; + /** + * @description Current system-time in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) + * format with nano-seconds. + * + * @example 2017-08-08T20:28:29.06202363Z + */ + SystemTime?: string; + /** @description The logging driver to use as a default for new containers. */ + LoggingDriver?: string; + /** + * @description The driver to use for managing cgroups. + * + * @default cgroupfs + * @example cgroupfs + * @enum {string} + */ + CgroupDriver?: "cgroupfs" | "systemd" | "none"; + /** + * @description The version of the cgroup. + * + * @default 1 + * @example 1 + * @enum {string} + */ + CgroupVersion?: "1" | "2"; + /** + * @description Number of event listeners subscribed. + * @example 30 + */ + NEventsListener?: number; + /** + * @description Kernel version of the host. + * + * On Linux, this information obtained from `uname`. On Windows this + * information is queried from the HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ + * registry value, for example _"10.0 14393 (14393.1198.amd64fre.rs1_release_sec.170427-1353)"_. + * + * @example 4.9.38-moby + */ + KernelVersion?: string; + /** + * @description Name of the host's operating system, for example: "Ubuntu 16.04.2 LTS" + * or "Windows Server 2016 Datacenter" + * + * @example Alpine Linux v3.5 + */ + OperatingSystem?: string; + /** + * @description Version of the host's operating system + * + *


+ * + * > **Note**: The information returned in this field, including its + * > very existence, and the formatting of values, should not be considered + * > stable, and may change without notice. + * + * @example 16.04 + */ + OSVersion?: string; + /** + * @description Generic type of the operating system of the host, as returned by the + * Go runtime (`GOOS`). + * + * Currently returned values are "linux" and "windows". A full list of + * possible values can be found in the [Go documentation](https://go.dev/doc/install/source#environment). + * + * @example linux + */ + OSType?: string; + /** + * @description Hardware architecture of the host, as returned by the Go runtime + * (`GOARCH`). + * + * A full list of possible values can be found in the [Go documentation](https://go.dev/doc/install/source#environment). + * + * @example x86_64 + */ + Architecture?: string; + /** + * @description The number of logical CPUs usable by the daemon. + * + * The number of available CPUs is checked by querying the operating + * system when the daemon starts. Changes to operating system CPU + * allocation after the daemon is started are not reflected. + * + * @example 4 + */ + NCPU?: number; + /** + * Format: int64 + * @description Total amount of physical memory available on the host, in bytes. + * + * @example 2095882240 + */ + MemTotal?: number; + /** + * @description Address / URL of the index server that is used for image search, + * and as a default for user authentication for Docker Hub and Docker Cloud. + * + * @default https://index.docker.io/v1/ + * @example https://index.docker.io/v1/ + */ + IndexServerAddress?: string; + RegistryConfig?: components["schemas"]["RegistryServiceConfig"]; + GenericResources?: components["schemas"]["GenericResources"]; + /** + * @description HTTP-proxy configured for the daemon. This value is obtained from the + * [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + * Credentials ([user info component](https://tools.ietf.org/html/rfc3986#section-3.2.1)) in the proxy URL + * are masked in the API response. + * + * Containers do not automatically inherit this configuration. + * + * @example http://xxxxx:xxxxx@proxy.corp.example.com:8080 + */ + HttpProxy?: string; + /** + * @description HTTPS-proxy configured for the daemon. This value is obtained from the + * [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + * Credentials ([user info component](https://tools.ietf.org/html/rfc3986#section-3.2.1)) in the proxy URL + * are masked in the API response. + * + * Containers do not automatically inherit this configuration. + * + * @example https://xxxxx:xxxxx@proxy.corp.example.com:4443 + */ + HttpsProxy?: string; + /** + * @description Comma-separated list of domain extensions for which no proxy should be + * used. This value is obtained from the [`NO_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) + * environment variable. + * + * Containers do not automatically inherit this configuration. + * + * @example *.local, 169.254/16 + */ + NoProxy?: string; + /** + * @description Hostname of the host. + * @example node5.corp.example.com + */ + Name?: string; + /** + * @description User-defined labels (key/value metadata) as set on the daemon. + * + *


+ * + * > **Note**: When part of a Swarm, nodes can both have _daemon_ labels, + * > set through the daemon configuration, and _node_ labels, set from a + * > manager node in the Swarm. Node labels are not included in this + * > field. Node labels can be retrieved using the `/nodes/(id)` endpoint + * > on a manager node in the Swarm. + * + * @example [ + * "storage=ssd", + * "production" + * ] + */ + Labels?: string[]; + /** + * @description Indicates if experimental features are enabled on the daemon. + * + * @example true + */ + ExperimentalBuild?: boolean; + /** + * @description Version string of the daemon. + * + * @example 24.0.2 + */ + ServerVersion?: string; + /** + * @description List of [OCI compliant](https://github.com/opencontainers/runtime-spec) + * runtimes configured on the daemon. Keys hold the "name" used to + * reference the runtime. + * + * The Docker daemon relies on an OCI compliant runtime (invoked via the + * `containerd` daemon) as its interface to the Linux kernel namespaces, + * cgroups, and SELinux. + * + * The default runtime is `runc`, and automatically configured. Additional + * runtimes can be configured by the user and will be listed here. + * + * @example { + * "runc": { + * "path": "runc" + * }, + * "runc-master": { + * "path": "/go/bin/runc" + * }, + * "custom": { + * "path": "/usr/local/bin/my-oci-runtime", + * "runtimeArgs": [ + * "--debug", + * "--systemd-cgroup=false" + * ] + * } + * } + */ + Runtimes?: { + [key: string]: components["schemas"]["Runtime"]; + }; + /** + * @description Name of the default OCI runtime that is used when starting containers. + * + * The default can be overridden per-container at create time. + * + * @default runc + * @example runc + */ + DefaultRuntime?: string; + Swarm?: components["schemas"]["SwarmInfo"]; + /** + * @description Indicates if live restore is enabled. + * + * If enabled, containers are kept running when the daemon is shutdown + * or upon daemon start if running containers are detected. + * + * @default false + * @example false + */ + LiveRestoreEnabled?: boolean; + /** + * @description Represents the isolation technology to use as a default for containers. + * The supported values are platform-specific. + * + * If no isolation value is specified on daemon start, on Windows client, + * the default is `hyperv`, and on Windows server, the default is `process`. + * + * This option is currently not used on other platforms. + * + * @default default + * @enum {string} + */ + Isolation?: "default" | "hyperv" | "process"; + /** + * @description Name and, optional, path of the `docker-init` binary. + * + * If the path is omitted, the daemon searches the host's `$PATH` for the + * binary and uses the first result. + * + * @example docker-init + */ + InitBinary?: string; + ContainerdCommit?: components["schemas"]["Commit"]; + RuncCommit?: components["schemas"]["Commit"]; + InitCommit?: components["schemas"]["Commit"]; + /** + * @description List of security features that are enabled on the daemon, such as + * apparmor, seccomp, SELinux, user-namespaces (userns), rootless and + * no-new-privileges. + * + * Additional configuration options for each security feature may + * be present, and are included as a comma-separated list of key/value + * pairs. + * + * @example [ + * "name=apparmor", + * "name=seccomp,profile=default", + * "name=selinux", + * "name=userns", + * "name=rootless" + * ] + */ + SecurityOptions?: string[]; + /** + * @description Reports a summary of the product license on the daemon. + * + * If a commercial license has been applied to the daemon, information + * such as number of nodes, and expiration are included. + * + * @example Community Engine + */ + ProductLicense?: string; + /** + * @description List of custom default address pools for local networks, which can be + * specified in the daemon.json file or dockerd option. + * + * Example: a Base "10.10.0.0/16" with Size 24 will define the set of 256 + * 10.10.[0-255].0/24 address pools. + */ + DefaultAddressPools?: { + /** + * @description The network address in CIDR format + * @example 10.10.0.0/16 + */ + Base?: string; + /** + * @description The network pool size + * @example 24 + */ + Size?: number; + }[]; + /** + * @description List of warnings / informational messages about missing features, or + * issues related to the daemon configuration. + * + * These messages can be printed by the client as information to the user. + * + * @example [ + * "WARNING: No memory limit support", + * "WARNING: bridge-nf-call-iptables is disabled", + * "WARNING: bridge-nf-call-ip6tables is disabled" + * ] + */ + Warnings?: string[]; + /** + * @description List of directories where (Container Device Interface) CDI + * specifications are located. + * + * These specifications define vendor-specific modifications to an OCI + * runtime specification for a container being created. + * + * An empty list indicates that CDI device injection is disabled. + * + * Note that since using CDI device injection requires the daemon to have + * experimental enabled. For non-experimental daemons an empty list will + * always be returned. + * + * @example [ + * "/etc/cdi", + * "/var/run/cdi" + * ] + */ + CDISpecDirs?: string[]; + }; + /** + * @description Available plugins per type. + * + *


+ * + * > **Note**: Only unmanaged (V1) plugins are included in this list. + * > V1 plugins are "lazily" loaded, and are not returned in this list + * > if there is no resource using the plugin. + */ + PluginsInfo: { + /** + * @description Names of available volume-drivers, and network-driver plugins. + * @example [ + * "local" + * ] + */ + Volume?: string[]; + /** + * @description Names of available network-drivers, and network-driver plugins. + * @example [ + * "bridge", + * "host", + * "ipvlan", + * "macvlan", + * "null", + * "overlay" + * ] + */ + Network?: string[]; + /** + * @description Names of available authorization plugins. + * @example [ + * "img-authz-plugin", + * "hbm" + * ] + */ + Authorization?: string[]; + /** + * @description Names of available logging-drivers, and logging-driver plugins. + * @example [ + * "awslogs", + * "fluentd", + * "gcplogs", + * "gelf", + * "journald", + * "json-file", + * "splunk", + * "syslog" + * ] + */ + Log?: string[]; + }; + /** @description RegistryServiceConfig stores daemon registry services configuration. */ + RegistryServiceConfig: { + /** + * @description List of IP ranges to which nondistributable artifacts can be pushed, + * using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632). + * + * Some images (for example, Windows base images) contain artifacts + * whose distribution is restricted by license. When these images are + * pushed to a registry, restricted artifacts are not included. + * + * This configuration override this behavior, and enables the daemon to + * push nondistributable artifacts to all registries whose resolved IP + * address is within the subnet described by the CIDR syntax. + * + * This option is useful when pushing images containing + * nondistributable artifacts to a registry on an air-gapped network so + * hosts on that network can pull the images without connecting to + * another server. + * + * > **Warning**: Nondistributable artifacts typically have restrictions + * > on how and where they can be distributed and shared. Only use this + * > feature to push artifacts to private registries and ensure that you + * > are in compliance with any terms that cover redistributing + * > nondistributable artifacts. + * + * @example [ + * "::1/128", + * "127.0.0.0/8" + * ] + */ + AllowNondistributableArtifactsCIDRs?: string[]; + /** + * @description List of registry hostnames to which nondistributable artifacts can be + * pushed, using the format `[:]` or `[:]`. + * + * Some images (for example, Windows base images) contain artifacts + * whose distribution is restricted by license. When these images are + * pushed to a registry, restricted artifacts are not included. + * + * This configuration override this behavior for the specified + * registries. + * + * This option is useful when pushing images containing + * nondistributable artifacts to a registry on an air-gapped network so + * hosts on that network can pull the images without connecting to + * another server. + * + * > **Warning**: Nondistributable artifacts typically have restrictions + * > on how and where they can be distributed and shared. Only use this + * > feature to push artifacts to private registries and ensure that you + * > are in compliance with any terms that cover redistributing + * > nondistributable artifacts. + * + * @example [ + * "registry.internal.corp.example.com:3000", + * "[2001:db8:a0b:12f0::1]:443" + * ] + */ + AllowNondistributableArtifactsHostnames?: string[]; + /** + * @description List of IP ranges of insecure registries, using the CIDR syntax + * ([RFC 4632](https://tools.ietf.org/html/4632)). Insecure registries + * accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates + * from unknown CAs) communication. + * + * By default, local registries (`127.0.0.0/8`) are configured as + * insecure. All other registries are secure. Communicating with an + * insecure registry is not possible if the daemon assumes that registry + * is secure. + * + * This configuration override this behavior, insecure communication with + * registries whose resolved IP address is within the subnet described by + * the CIDR syntax. + * + * Registries can also be marked insecure by hostname. Those registries + * are listed under `IndexConfigs` and have their `Secure` field set to + * `false`. + * + * > **Warning**: Using this option can be useful when running a local + * > registry, but introduces security vulnerabilities. This option + * > should therefore ONLY be used for testing purposes. For increased + * > security, users should add their CA to their system's list of trusted + * > CAs instead of enabling this option. + * + * @example [ + * "::1/128", + * "127.0.0.0/8" + * ] + */ + InsecureRegistryCIDRs?: string[]; + /** + * @example { + * "127.0.0.1:5000": { + * "Name": "127.0.0.1:5000", + * "Mirrors": [], + * "Secure": false, + * "Official": false + * }, + * "[2001:db8:a0b:12f0::1]:80": { + * "Name": "[2001:db8:a0b:12f0::1]:80", + * "Mirrors": [], + * "Secure": false, + * "Official": false + * }, + * "docker.io": { + * "Name": "docker.io", + * "Mirrors": [ + * "https://hub-mirror.corp.example.com:5000/" + * ], + * "Secure": true, + * "Official": true + * }, + * "registry.internal.corp.example.com:3000": { + * "Name": "registry.internal.corp.example.com:3000", + * "Mirrors": [], + * "Secure": false, + * "Official": false + * } + * } + */ + IndexConfigs?: { + [key: string]: components["schemas"]["IndexInfo"]; + }; + /** + * @description List of registry URLs that act as a mirror for the official + * (`docker.io`) registry. + * + * @example [ + * "https://hub-mirror.corp.example.com:5000/", + * "https://[2001:db8:a0b:12f0::1]/" + * ] + */ + Mirrors?: string[]; + } | null; + /** @description IndexInfo contains information about a registry. */ + IndexInfo: { + /** + * @description Name of the registry, such as "docker.io". + * + * @example docker.io + */ + Name?: string; + /** + * @description List of mirrors, expressed as URIs. + * + * @example [ + * "https://hub-mirror.corp.example.com:5000/", + * "https://registry-2.docker.io/", + * "https://registry-3.docker.io/" + * ] + */ + Mirrors?: string[]; + /** + * @description Indicates if the registry is part of the list of insecure + * registries. + * + * If `false`, the registry is insecure. Insecure registries accept + * un-encrypted (HTTP) and/or untrusted (HTTPS with certificates from + * unknown CAs) communication. + * + * > **Warning**: Insecure registries can be useful when running a local + * > registry. However, because its use creates security vulnerabilities + * > it should ONLY be enabled for testing purposes. For increased + * > security, users should add their CA to their system's list of + * > trusted CAs instead of enabling this option. + * + * @example true + */ + Secure?: boolean; + /** + * @description Indicates whether this is an official registry (i.e., Docker Hub / docker.io) + * + * @example true + */ + Official?: boolean; + } | null; + /** + * @description Runtime describes an [OCI compliant](https://github.com/opencontainers/runtime-spec) + * runtime. + * + * The runtime is invoked by the daemon via the `containerd` daemon. OCI + * runtimes act as an interface to the Linux kernel namespaces, cgroups, + * and SELinux. + */ + Runtime: { + /** + * @description Name and, optional, path, of the OCI executable binary. + * + * If the path is omitted, the daemon searches the host's `$PATH` for the + * binary and uses the first result. + * + * @example /usr/local/bin/my-oci-runtime + */ + path?: string; + /** + * @description List of command-line arguments to pass to the runtime when invoked. + * + * @example [ + * "--debug", + * "--systemd-cgroup=false" + * ] + */ + runtimeArgs?: string[] | null; + /** + * @description Information specific to the runtime. + * + * While this API specification does not define data provided by runtimes, + * the following well-known properties may be provided by runtimes: + * + * `org.opencontainers.runtime-spec.features`: features structure as defined + * in the [OCI Runtime Specification](https://github.com/opencontainers/runtime-spec/blob/main/features.md), + * in a JSON string representation. + * + *


+ * + * > **Note**: The information returned in this field, including the + * > formatting of values and labels, should not be considered stable, + * > and may change without notice. + * + * @example { + * "org.opencontainers.runtime-spec.features": "{\"ociVersionMin\":\"1.0.0\",\"ociVersionMax\":\"1.1.0\",\"...\":\"...\"}" + * } + */ + status?: { + [key: string]: string; + } | null; + }; + /** + * @description Commit holds the Git-commit (SHA1) that a binary was built from, as + * reported in the version-string of external tools, such as `containerd`, + * or `runC`. + */ + Commit: { + /** + * @description Actual commit ID of external tool. + * @example cfb82a876ecc11b5ca0977d1733adbe58599088a + */ + ID?: string; + /** + * @description Commit ID of external tool expected by dockerd as set at build time. + * + * @example 2d41c047c83e09a6d61d464906feb2a2f3c52aa4 + */ + Expected?: string; + }; + /** @description Represents generic information about swarm. */ + SwarmInfo: { + /** + * @description Unique identifier of for this node in the swarm. + * @default + * @example k67qz4598weg5unwwffg6z1m1 + */ + NodeID?: string; + /** + * @description IP address at which this node can be reached by other nodes in the + * swarm. + * + * @default + * @example 10.0.0.46 + */ + NodeAddr?: string; + LocalNodeState?: components["schemas"]["LocalNodeState"]; + /** + * @default false + * @example true + */ + ControlAvailable?: boolean; + /** @default */ + Error?: string; + /** + * @description List of ID's and addresses of other managers in the swarm. + * + * @example [ + * { + * "NodeID": "71izy0goik036k48jg985xnds", + * "Addr": "10.0.0.158:2377" + * }, + * { + * "NodeID": "79y6h1o4gv8n120drcprv5nmc", + * "Addr": "10.0.0.159:2377" + * }, + * { + * "NodeID": "k67qz4598weg5unwwffg6z1m1", + * "Addr": "10.0.0.46:2377" + * } + * ] + */ + RemoteManagers?: components["schemas"]["PeerNode"][] | null; + /** + * @description Total number of nodes in the swarm. + * @example 4 + */ + Nodes?: number | null; + /** + * @description Total number of managers in the swarm. + * @example 3 + */ + Managers?: number | null; + Cluster?: components["schemas"]["ClusterInfo"]; + }; + /** + * @description Current local status of this node. + * @default + * @example active + * @enum {string} + */ + LocalNodeState: "" | "inactive" | "pending" | "active" | "error" | "locked"; + /** @description Represents a peer-node in the swarm */ + PeerNode: { + /** @description Unique identifier of for this node in the swarm. */ + NodeID?: string; + /** @description IP address and ports at which this node can be reached. */ + Addr?: string; + }; + /** @description Specifies how a service should be attached to a particular network. */ + NetworkAttachmentConfig: { + /** @description The target network for attachment. Must be a network name or ID. */ + Target?: string; + /** @description Discoverable alternate names for the service on this network. */ + Aliases?: string[]; + /** @description Driver attachment options for the network target. */ + DriverOpts?: { + [key: string]: string; + }; + }; + /** + * @description Actor describes something that generates events, like a container, network, + * or a volume. + */ + EventActor: { + /** + * @description The ID of the object emitting the event + * @example ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743 + */ + ID?: string; + /** + * @description Various key/value attributes of the object, depending on its type. + * + * @example { + * "com.example.some-label": "some-label-value", + * "image": "alpine:latest", + * "name": "my-container" + * } + */ + Attributes?: { + [key: string]: string; + }; + }; + /** + * SystemEventsResponse + * @description EventMessage represents the information an event contains. + */ + EventMessage: { + /** + * @description The type of object emitting the event + * @example container + * @enum {string} + */ + Type?: "builder" | "config" | "container" | "daemon" | "image" | "network" | "node" | "plugin" | "secret" | "service" | "volume"; + /** + * @description The type of event + * @example create + */ + Action?: string; + Actor?: components["schemas"]["EventActor"]; + /** + * @description Scope of the event. Engine events are `local` scope. Cluster (Swarm) + * events are `swarm` scope. + * + * @enum {string} + */ + scope?: "local" | "swarm"; + /** + * Format: int64 + * @description Timestamp of event + * @example 1629574695 + */ + time?: number; + /** + * Format: int64 + * @description Timestamp of event, with nanosecond accuracy + * @example 1629574695515050000 + */ + timeNano?: number; + }; + /** + * @description A descriptor struct containing digest, media type, and size, as defined in + * the [OCI Content Descriptors Specification](https://github.com/opencontainers/image-spec/blob/v1.0.1/descriptor.md). + */ + OCIDescriptor: { + /** + * @description The media type of the object this schema refers to. + * + * @example application/vnd.docker.distribution.manifest.v2+json + */ + mediaType?: string; + /** + * @description The digest of the targeted content. + * + * @example sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96 + */ + digest?: string; + /** + * Format: int64 + * @description The size in bytes of the blob. + * + * @example 3987495 + */ + size?: number; + }; + /** + * @description Describes the platform which the image in the manifest runs on, as defined + * in the [OCI Image Index Specification](https://github.com/opencontainers/image-spec/blob/v1.0.1/image-index.md). + */ + OCIPlatform: { + /** + * @description The CPU architecture, for example `amd64` or `ppc64`. + * + * @example arm + */ + architecture?: string; + /** + * @description The operating system, for example `linux` or `windows`. + * + * @example windows + */ + os?: string; + /** + * @description Optional field specifying the operating system version, for example on + * Windows `10.0.19041.1165`. + * + * @example 10.0.19041.1165 + */ + "os.version"?: string; + /** + * @description Optional field specifying an array of strings, each listing a required + * OS feature (for example on Windows `win32k`). + * + * @example [ + * "win32k" + * ] + */ + "os.features"?: string[]; + /** + * @description Optional field specifying a variant of the CPU, for example `v7` to + * specify ARMv7 when architecture is `arm`. + * + * @example v7 + */ + variant?: string; + }; + /** + * DistributionInspectResponse + * @description Describes the result obtained from contacting the registry to retrieve + * image metadata. + */ + DistributionInspect: { + Descriptor: components["schemas"]["OCIDescriptor"]; + /** @description An array containing all platforms supported by the image. */ + Platforms: components["schemas"]["OCIPlatform"][]; + }; + /** + * @description Options and information specific to, and only present on, Swarm CSI + * cluster volumes. + */ + ClusterVolume: { + /** + * @description The Swarm ID of this volume. Because cluster volumes are Swarm + * objects, they have an ID, unlike non-cluster volumes. This ID can + * be used to refer to the Volume instead of the name. + */ + ID?: string; + Version?: components["schemas"]["ObjectVersion"]; + /** Format: dateTime */ + CreatedAt?: string; + /** Format: dateTime */ + UpdatedAt?: string; + Spec?: components["schemas"]["ClusterVolumeSpec"]; + /** @description Information about the global status of the volume. */ + Info?: { + /** + * Format: int64 + * @description The capacity of the volume in bytes. A value of 0 indicates that + * the capacity is unknown. + */ + CapacityBytes?: number; + /** + * @description A map of strings to strings returned from the storage plugin when + * the volume is created. + */ + VolumeContext?: { + [key: string]: string; + }; + /** + * @description The ID of the volume as returned by the CSI storage plugin. This + * is distinct from the volume's ID as provided by Docker. This ID + * is never used by the user when communicating with Docker to refer + * to this volume. If the ID is blank, then the Volume has not been + * successfully created in the plugin yet. + */ + VolumeID?: string; + /** @description The topology this volume is actually accessible from. */ + AccessibleTopology?: components["schemas"]["Topology"][]; + }; + /** + * @description The status of the volume as it pertains to its publishing and use on + * specific nodes + */ + PublishStatus?: ({ + /** @description The ID of the Swarm node the volume is published on. */ + NodeID?: string; + /** + * @description The published state of the volume. + * * `pending-publish` The volume should be published to this node, but the call to the controller plugin to do so has not yet been successfully completed. + * * `published` The volume is published successfully to the node. + * * `pending-node-unpublish` The volume should be unpublished from the node, and the manager is awaiting confirmation from the worker that it has done so. + * * `pending-controller-unpublish` The volume is successfully unpublished from the node, but has not yet been successfully unpublished on the controller. + * + * @enum {string} + */ + State?: "pending-publish" | "published" | "pending-node-unpublish" | "pending-controller-unpublish"; + /** + * @description A map of strings to strings returned by the CSI controller + * plugin when a volume is published. + */ + PublishContext?: { + [key: string]: string; + }; + })[]; + }; + /** @description Cluster-specific options used to create the volume. */ + ClusterVolumeSpec: { + /** + * @description Group defines the volume group of this volume. Volumes belonging to + * the same group can be referred to by group name when creating + * Services. Referring to a volume by group instructs Swarm to treat + * volumes in that group interchangeably for the purpose of scheduling. + * Volumes with an empty string for a group technically all belong to + * the same, emptystring group. + */ + Group?: string; + /** @description Defines how the volume is used by tasks. */ + AccessMode?: { + /** + * @description The set of nodes this volume can be used on at one time. + * - `single` The volume may only be scheduled to one node at a time. + * - `multi` the volume may be scheduled to any supported number of nodes at a time. + * + * @default single + * @enum {string} + */ + Scope?: "single" | "multi"; + /** + * @description The number and way that different tasks can use this volume + * at one time. + * - `none` The volume may only be used by one task at a time. + * - `readonly` The volume may be used by any number of tasks, but they all must mount the volume as readonly + * - `onewriter` The volume may be used by any number of tasks, but only one may mount it as read/write. + * - `all` The volume may have any number of readers and writers. + * + * @default none + * @enum {string} + */ + Sharing?: "none" | "readonly" | "onewriter" | "all"; + /** + * @description Options for using this volume as a Mount-type volume. + * + * Either MountVolume or BlockVolume, but not both, must be + * present. + * properties: + * FsType: + * type: "string" + * description: | + * Specifies the filesystem type for the mount volume. + * Optional. + * MountFlags: + * type: "array" + * description: | + * Flags to pass when mounting the volume. Optional. + * items: + * type: "string" + * BlockVolume: + * type: "object" + * description: | + * Options for using this volume as a Block-type volume. + * Intentionally empty. + */ + MountVolume?: Record; + /** + * @description Swarm Secrets that are passed to the CSI storage plugin when + * operating on this volume. + */ + Secrets?: { + /** + * @description Key is the name of the key of the key-value pair passed to + * the plugin. + */ + Key?: string; + /** + * @description Secret is the swarm Secret object from which to read data. + * This can be a Secret name or ID. The Secret data is + * retrieved by swarm and used as the value of the key-value + * pair passed to the plugin. + */ + Secret?: string; + }[]; + /** + * @description Requirements for the accessible topology of the volume. These + * fields are optional. For an in-depth description of what these + * fields mean, see the CSI specification. + */ + AccessibilityRequirements?: { + /** + * @description A list of required topologies, at least one of which the + * volume must be accessible from. + */ + Requisite?: components["schemas"]["Topology"][]; + /** + * @description A list of topologies that the volume should attempt to be + * provisioned in. + */ + Preferred?: components["schemas"]["Topology"][]; + }; + /** + * @description The desired capacity that the volume should be created with. If + * empty, the plugin will decide the capacity. + */ + CapacityRange?: { + /** + * Format: int64 + * @description The volume must be at least this big. The value of 0 + * indicates an unspecified minimum + */ + RequiredBytes?: number; + /** + * Format: int64 + * @description The volume must not be bigger than this. The value of 0 + * indicates an unspecified maximum. + */ + LimitBytes?: number; + }; + /** + * @description The availability of the volume for use in tasks. + * - `active` The volume is fully available for scheduling on the cluster + * - `pause` No new workloads should use the volume, but existing workloads are not stopped. + * - `drain` All workloads using this volume should be stopped and rescheduled, and no new ones should be started. + * + * @default active + * @enum {string} + */ + Availability?: "active" | "pause" | "drain"; + }; + }; + /** + * @description A map of topological domains to topological segments. For in depth + * details, see documentation for the Topology object in the CSI + * specification. + */ + Topology: { + [key: string]: string; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} + +export type $defs = Record; + +export type external = Record; + +export interface operations { + + /** + * List containers + * @description Returns a list of containers. For details on the format, see the + * [inspect endpoint](#operation/ContainerInspect). + * + * Note that it uses a different, smaller representation of a container + * than inspecting a single container. For example, the list of linked + * containers is not propagated . + */ + ContainerList: { + parameters: { + query?: { + /** @description Return all containers. By default, only running containers are shown. */ + all?: boolean; + /** + * @description Return this number of most recently created containers, including + * non-running ones. + */ + limit?: number; + /** @description Return the size of container as fields `SizeRw` and `SizeRootFs`. */ + size?: boolean; + /** + * @description Filters to process on the container list, encoded as JSON (a + * `map[string][]string`). For example, `{"status": ["paused"]}` will + * only return paused containers. + * + * Available filters: + * + * - `ancestor`=(`[:]`, ``, or ``) + * - `before`=(`` or ``) + * - `expose`=(`[/]`|`/[]`) + * - `exited=` containers with exit code of `` + * - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + * - `id=` a container's ID + * - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + * - `is-task=`(`true`|`false`) + * - `label=key` or `label="key=value"` of a container label + * - `name=` a container's name + * - `network`=(`` or ``) + * - `publish`=(`[/]`|`/[]`) + * - `since`=(`` or ``) + * - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + * - `volume`=(`` or ``) + */ + filters?: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["ContainerSummary"][]; + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Create a container */ + ContainerCreate: { + parameters: { + query?: { + /** + * @description Assign the specified name to the container. Must match + * `/?[a-zA-Z0-9][a-zA-Z0-9_.-]+`. + */ + name?: string; + /** + * @description Platform in the format `os[/arch[/variant]]` used for image lookup. + * + * When specified, the daemon checks if the requested image is present + * in the local image cache with the given OS and Architecture, and + * otherwise returns a `404` status. + * + * If the option is not set, the host's native OS and Architecture are + * used to look up the image in the image cache. However, if no platform + * is passed and the given image does exist in the local image cache, + * but its OS or architecture does not match, the container is created + * with the available image, and a warning is added to the `Warnings` + * field in the response, for example; + * + * WARNING: The requested image's platform (linux/arm64/v8) does not + * match the detected host platform (linux/amd64) and no + * specific platform was requested + */ + platform?: string; + }; + }; + /** @description Container to create */ + requestBody: { + content: { + "application/json": components["schemas"]["ContainerConfig"] & { + HostConfig?: components["schemas"]["HostConfig"]; + NetworkingConfig?: components["schemas"]["NetworkingConfig"]; + }; + "application/octet-stream": components["schemas"]["ContainerConfig"] & { + HostConfig?: components["schemas"]["HostConfig"]; + NetworkingConfig?: components["schemas"]["NetworkingConfig"]; + }; + }; + }; + responses: { + /** @description Container created successfully */ + 201: { + content: { + "application/json": components["schemas"]["ContainerCreateResponse"]; + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description no such image */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description conflict */ + 409: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Inspect a container + * @description Return low-level information about a container. + */ + ContainerInspect: { + parameters: { + query?: { + /** @description Return the size of container as fields `SizeRw` and `SizeRootFs` */ + size?: boolean; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": { + /** @description The ID of the container */ + Id?: string; + /** @description The time the container was created */ + Created?: string; + /** @description The path to the command being run */ + Path?: string; + /** @description The arguments to the command being run */ + Args?: string[]; + State?: components["schemas"]["ContainerState"]; + /** @description The container's image ID */ + Image?: string; + ResolvConfPath?: string; + HostnamePath?: string; + HostsPath?: string; + LogPath?: string; + Name?: string; + RestartCount?: number; + Driver?: string; + Platform?: string; + MountLabel?: string; + ProcessLabel?: string; + AppArmorProfile?: string; + /** @description IDs of exec instances that are running in the container. */ + ExecIDs?: string[] | null; + HostConfig?: components["schemas"]["HostConfig"]; + GraphDriver?: components["schemas"]["GraphDriverData"]; + /** + * Format: int64 + * @description The size of files that have been created or changed by this + * container. + */ + SizeRw?: number; + /** + * Format: int64 + * @description The total size of all the files in this container. + */ + SizeRootFs?: number; + Mounts?: components["schemas"]["MountPoint"][]; + Config?: components["schemas"]["ContainerConfig"]; + NetworkSettings?: components["schemas"]["NetworkSettings"]; + }; + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * List processes running inside a container + * @description On Unix systems, this is done by running the `ps` command. This endpoint + * is not supported on Windows. + */ + ContainerTop: { + parameters: { + query?: { + /** @description The arguments to pass to `ps`. For example, `aux` */ + ps_args?: string; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": { + /** @description The ps column titles */ + Titles?: string[]; + /** + * @description Each process running in the container, where each is process + * is an array of values corresponding to the titles. + */ + Processes?: string[][]; + }; + "text/plain": { + /** @description The ps column titles */ + Titles?: string[]; + /** + * @description Each process running in the container, where each is process + * is an array of values corresponding to the titles. + */ + Processes?: string[][]; + }; + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Get container logs + * @description Get `stdout` and `stderr` logs from a container. + * + * Note: This endpoint works only for containers with the `json-file` or + * `journald` logging driver. + */ + ContainerLogs: { + parameters: { + query?: { + /** @description Keep connection after returning logs. */ + follow?: boolean; + /** @description Return logs from `stdout` */ + stdout?: boolean; + /** @description Return logs from `stderr` */ + stderr?: boolean; + /** @description Only return logs since this time, as a UNIX timestamp */ + since?: number; + /** @description Only return logs before this time, as a UNIX timestamp */ + until?: number; + /** @description Add timestamps to every log line */ + timestamps?: boolean; + /** + * @description Only return this number of log lines from the end of the logs. + * Specify as an integer or `all` to output all log lines. + */ + tail?: string; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** + * @description logs returned as a stream in response body. + * For the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + * Note that unlike the attach endpoint, the logs endpoint does not + * upgrade the connection and does not set Content-Type. + */ + 200: { + content: { + "application/vnd.docker.raw-stream": string; + "application/vnd.docker.multiplexed-stream": string; + }; + }; + /** @description no such container */ + 404: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + "application/json": unknown; + }; + }; + /** @description server error */ + 500: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Get changes on a containerโ€™s filesystem + * @description Returns which files in a container's filesystem have been added, deleted, + * or modified. The `Kind` of modification can be one of: + * + * - `0`: Modified ("C") + * - `1`: Added ("A") + * - `2`: Deleted ("D") + */ + ContainerChanges: { + parameters: { + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description The list of changes */ + 200: { + content: { + "application/json": components["schemas"]["FilesystemChange"][]; + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Export a container + * @description Export the contents of a container as a tarball. + */ + ContainerExport: { + parameters: { + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description no such container */ + 404: { + content: { + "application/octet-stream": components["schemas"]["ErrorResponse"]; + "application/json": unknown; + }; + }; + /** @description server error */ + 500: { + content: { + "application/octet-stream": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Get container stats based on resource usage + * @description This endpoint returns a live stream of a containerโ€™s resource usage + * statistics. + * + * The `precpu_stats` is the CPU statistic of the *previous* read, and is + * used to calculate the CPU usage percentage. It is not an exact copy + * of the `cpu_stats` field. + * + * If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + * nil then for compatibility with older daemons the length of the + * corresponding `cpu_usage.percpu_usage` array should be used. + * + * On a cgroup v2 host, the following fields are not set + * * `blkio_stats`: all fields other than `io_service_bytes_recursive` + * * `cpu_stats`: `cpu_usage.percpu_usage` + * * `memory_stats`: `max_usage` and `failcnt` + * Also, `memory_stats.stats` fields are incompatible with cgroup v1. + * + * To calculate the values shown by the `stats` command of the docker cli tool + * the following formulas can be used: + * * used_memory = `memory_stats.usage - memory_stats.stats.cache` + * * available_memory = `memory_stats.limit` + * * Memory usage % = `(used_memory / available_memory) * 100.0` + * * cpu_delta = `cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage` + * * system_cpu_delta = `cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage` + * * number_cpus = `lenght(cpu_stats.cpu_usage.percpu_usage)` or `cpu_stats.online_cpus` + * * CPU usage % = `(cpu_delta / system_cpu_delta) * number_cpus * 100.0` + */ + ContainerStats: { + parameters: { + query?: { + /** + * @description Stream the output. If false, the stats will be output once and then + * it will disconnect. + */ + stream?: boolean; + /** + * @description Only get a single stat instead of waiting for 2 cycles. Must be used + * with `stream=false`. + */ + "one-shot"?: boolean; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": Record; + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Resize a container TTY + * @description Resize the TTY for a container. + */ + ContainerResize: { + parameters: { + query?: { + /** @description Height of the TTY session in characters */ + h?: number; + /** @description Width of the TTY session in characters */ + w?: number; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description no such container */ + 404: { + content: { + "text/plain": components["schemas"]["ErrorResponse"]; + "application/json": unknown; + }; + }; + /** @description cannot resize container */ + 500: { + content: { + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Start a container */ + ContainerStart: { + parameters: { + query?: { + /** + * @description Override the key sequence for detaching a container. Format is a + * single character `[a-Z]` or `ctrl-` where `` is one + * of: `a-z`, `@`, `^`, `[`, `,` or `_`. + */ + detachKeys?: string; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description container already started */ + 304: { + content: { + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Stop a container */ + ContainerStop: { + parameters: { + query?: { + /** @description Signal to send to the container as an integer or string (e.g. `SIGINT`). */ + signal?: string; + /** @description Number of seconds to wait before killing the container */ + t?: number; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description container already stopped */ + 304: { + content: { + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Restart a container */ + ContainerRestart: { + parameters: { + query?: { + /** @description Signal to send to the container as an integer or string (e.g. `SIGINT`). */ + signal?: string; + /** @description Number of seconds to wait before killing the container */ + t?: number; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Kill a container + * @description Send a POSIX signal to a container, defaulting to killing to the + * container. + */ + ContainerKill: { + parameters: { + query?: { + /** @description Signal to send to the container as an integer or string (e.g. `SIGINT`). */ + signal?: string; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description container is not running */ + 409: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Update a container + * @description Change various configuration options of a container without having to + * recreate it. + */ + ContainerUpdate: { + parameters: { + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["Resources"] & { + RestartPolicy?: components["schemas"]["RestartPolicy"]; + }; + }; + }; + responses: { + /** @description The container has been updated. */ + 200: { + content: { + "application/json": { + Warnings?: string[]; + }; + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Rename a container */ + ContainerRename: { + parameters: { + query: { + /** @description New name for the container */ + name: string; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description name already in use */ + 409: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Pause a container + * @description Use the freezer cgroup to suspend all processes in a container. + * + * Traditionally, when suspending a process the `SIGSTOP` signal is used, + * which is observable by the process being suspended. With the freezer + * cgroup the process is unaware, and unable to capture, that it is being + * suspended, and subsequently resumed. + */ + ContainerPause: { + parameters: { + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Unpause a container + * @description Resume a container which has been paused. + */ + ContainerUnpause: { + parameters: { + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Attach to a container + * @description Attach to a container to read its output or send it input. You can attach + * to the same container multiple times and you can reattach to containers + * that have been detached. + * + * Either the `stream` or `logs` parameter must be `true` for this endpoint + * to do anything. + * + * See the [documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) + * for more details. + * + * ### Hijacking + * + * This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, + * and `stderr` on the same socket. + * + * This is the response from the daemon for an attach request: + * + * ``` + * HTTP/1.1 200 OK + * Content-Type: application/vnd.docker.raw-stream + * + * [STREAM] + * ``` + * + * After the headers and two new lines, the TCP connection can now be used + * for raw, bidirectional communication between the client and server. + * + * To hint potential proxies about connection hijacking, the Docker client + * can also optionally send connection upgrade headers. + * + * For example, the client sends this request to upgrade the connection: + * + * ``` + * POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + * Upgrade: tcp + * Connection: Upgrade + * ``` + * + * The Docker daemon will respond with a `101 UPGRADED` response, and will + * similarly follow with the raw stream: + * + * ``` + * HTTP/1.1 101 UPGRADED + * Content-Type: application/vnd.docker.raw-stream + * Connection: Upgrade + * Upgrade: tcp + * + * [STREAM] + * ``` + * + * ### Stream format + * + * When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), + * the HTTP Content-Type header is set to application/vnd.docker.multiplexed-stream + * and the stream over the hijacked connected is multiplexed to separate out + * `stdout` and `stderr`. The stream consists of a series of frames, each + * containing a header and a payload. + * + * The header contains the information which the stream writes (`stdout` or + * `stderr`). It also contains the size of the associated frame encoded in + * the last four bytes (`uint32`). + * + * It is encoded on the first eight bytes like this: + * + * ```go + * header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + * ``` + * + * `STREAM_TYPE` can be: + * + * - 0: `stdin` (is written on `stdout`) + * - 1: `stdout` + * - 2: `stderr` + * + * `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size + * encoded as big endian. + * + * Following the header is the payload, which is the specified number of + * bytes of `STREAM_TYPE`. + * + * The simplest way to implement this protocol is the following: + * + * 1. Read 8 bytes. + * 2. Choose `stdout` or `stderr` depending on the first byte. + * 3. Extract the frame size from the last four bytes. + * 4. Read the extracted size and output it on the correct output. + * 5. Goto 1. + * + * ### Stream format when using a TTY + * + * When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), + * the stream is not multiplexed. The data exchanged over the hijacked + * connection is simply the raw data from the process PTY and client's + * `stdin`. + */ + ContainerAttach: { + parameters: { + query?: { + /** + * @description Override the key sequence for detaching a container.Format is a single + * character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, + * `@`, `^`, `[`, `,` or `_`. + */ + detachKeys?: string; + /** + * @description Replay previous logs from the container. + * + * This is useful for attaching to a container that has started and you + * want to output everything since the container started. + * + * If `stream` is also enabled, once all the previous output has been + * returned, it will seamlessly transition into streaming current + * output. + */ + logs?: boolean; + /** @description Stream attached streams from the time the request was made onwards. */ + stream?: boolean; + /** @description Attach to `stdin` */ + stdin?: boolean; + /** @description Attach to `stdout` */ + stdout?: boolean; + /** @description Attach to `stderr` */ + stderr?: boolean; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error, hints proxy about hijacking */ + 101: { + content: { + }; + }; + /** @description no error, no upgrade header found */ + 200: { + content: { + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description no such container */ + 404: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + "application/json": unknown; + }; + }; + /** @description server error */ + 500: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Attach to a container via a websocket */ + ContainerAttachWebsocket: { + parameters: { + query?: { + /** + * @description Override the key sequence for detaching a container.Format is a single + * character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, + * `@`, `^`, `[`, `,`, or `_`. + */ + detachKeys?: string; + /** @description Return logs */ + logs?: boolean; + /** @description Return stream */ + stream?: boolean; + /** @description Attach to `stdin` */ + stdin?: boolean; + /** @description Attach to `stdout` */ + stdout?: boolean; + /** @description Attach to `stderr` */ + stderr?: boolean; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error, hints proxy about hijacking */ + 101: { + content: { + }; + }; + /** @description no error, no upgrade header found */ + 200: { + content: { + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Wait for a container + * @description Block until a container stops, then returns the exit code. + */ + ContainerWait: { + parameters: { + query?: { + /** + * @description Wait until a container state reaches the given condition. + * + * Defaults to `not-running` if omitted or empty. + */ + condition?: "not-running" | "next-exit" | "removed"; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description The container has exit. */ + 200: { + content: { + "application/json": components["schemas"]["ContainerWaitResponse"]; + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Remove a container */ + ContainerDelete: { + parameters: { + query?: { + /** @description Remove anonymous volumes associated with the container. */ + v?: boolean; + /** @description If the container is running, kill it before removing it. */ + force?: boolean; + /** @description Remove the specified link associated with the container. */ + link?: boolean; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description conflict */ + 409: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Get an archive of a filesystem resource in a container + * @description Get a tar archive of a resource in the filesystem of container id. + */ + ContainerArchive: { + parameters: { + query: { + /** @description Resource in the containerโ€™s filesystem to archive. */ + path: string; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description Bad parameter */ + 400: { + content: { + "application/x-tar": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Container or path does not exist */ + 404: { + content: { + "application/x-tar": components["schemas"]["ErrorResponse"]; + "application/json": unknown; + }; + }; + /** @description server error */ + 500: { + content: { + "application/x-tar": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Extract an archive of files or folders to a directory in a container + * @description Upload a tar archive to be extracted to a path in the filesystem of container id. + * `path` parameter is asserted to be a directory. If it exists as a file, 400 error + * will be returned with message "not a directory". + */ + PutContainerArchive: { + parameters: { + query: { + /** @description Path to a directory in the container to extract the archiveโ€™s contents into. */ + path: string; + /** + * @description If `1`, `true`, or `True` then it will be an error if unpacking the + * given content would cause an existing directory to be replaced with + * a non-directory and vice versa. + */ + noOverwriteDirNonDir?: string; + /** + * @description If `1`, `true`, then it will copy UID/GID maps to the dest file or + * dir + */ + copyUIDGID?: string; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + /** + * @description The input stream must be a tar archive compressed with one of the + * following algorithms: `identity` (no compression), `gzip`, `bzip2`, + * or `xz`. + */ + requestBody: { + content: { + "application/x-tar": string; + "application/octet-stream": string; + }; + }; + responses: { + /** @description The content was extracted successfully */ + 200: { + content: { + }; + }; + /** @description Bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Permission denied, the volume or container rootfs is marked as read-only. */ + 403: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description No such container or path does not exist inside the container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Get information about files in a container + * @description A response header `X-Docker-Container-Path-Stat` is returned, containing + * a base64 - encoded JSON object with some filesystem header information + * about the path. + */ + ContainerArchiveInfo: { + parameters: { + query: { + /** @description Resource in the containerโ€™s filesystem to archive. */ + path: string; + }; + path: { + /** @description ID or name of the container */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + headers: { + /** + * @description A base64 - encoded JSON object with some filesystem header + * information about the path + */ + "X-Docker-Container-Path-Stat"?: string; + }; + content: { + }; + }; + /** @description Bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Container or path does not exist */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Delete stopped containers */ + ContainerPrune: { + parameters: { + query?: { + /** + * @description Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + * + * Available filters: + * - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machineโ€™s time. + * - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + */ + filters?: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + "application/json": { + /** @description Container IDs that were deleted */ + ContainersDeleted?: string[]; + /** + * Format: int64 + * @description Disk space reclaimed in bytes + */ + SpaceReclaimed?: number; + }; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * List Images + * @description Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image. + */ + ImageList: { + parameters: { + query?: { + /** @description Show all images. Only images from a final layer (no children) are shown by default. */ + all?: boolean; + /** + * @description A JSON encoded value of the filters (a `map[string][]string`) to + * process on the images list. + * + * Available filters: + * + * - `before`=(`[:]`, `` or ``) + * - `dangling=true` + * - `label=key` or `label="key=value"` of an image label + * - `reference`=(`[:]`) + * - `since`=(`[:]`, `` or ``) + * - `until=` + */ + filters?: string; + /** @description Compute and show shared size as a `SharedSize` field on each image. */ + "shared-size"?: boolean; + /** @description Show digest information as a `RepoDigests` field on each image. */ + digests?: boolean; + }; + }; + responses: { + /** @description Summary image data for the images matching the query */ + 200: { + content: { + "application/json": components["schemas"]["ImageSummary"][]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Build an image + * @description Build an image from a tar archive with a `Dockerfile` in it. + * + * The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + * + * The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + * + * The build is canceled if the client drops the connection by quitting or being killed. + */ + ImageBuild: { + parameters: { + query?: { + /** @description Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`. */ + dockerfile?: string; + /** @description A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters. */ + t?: string; + /** @description Extra hosts to add to /etc/hosts */ + extrahosts?: string; + /** @description A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the fileโ€™s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball. */ + remote?: string; + /** @description Suppress verbose build output. */ + q?: boolean; + /** @description Do not use the cache when building the image. */ + nocache?: boolean; + /** @description JSON array of images used for build cache resolution. */ + cachefrom?: string; + /** @description Attempt to pull the image even if an older image exists locally. */ + pull?: string; + /** @description Remove intermediate containers after a successful build. */ + rm?: boolean; + /** @description Always remove intermediate containers, even upon failure. */ + forcerm?: boolean; + /** @description Set memory limit for build. */ + memory?: number; + /** @description Total memory (memory + swap). Set as `-1` to disable swap. */ + memswap?: number; + /** @description CPU shares (relative weight). */ + cpushares?: number; + /** @description CPUs in which to allow execution (e.g., `0-3`, `0,1`). */ + cpusetcpus?: string; + /** @description The length of a CPU period in microseconds. */ + cpuperiod?: number; + /** @description Microseconds of CPU time that the container can get in a CPU period. */ + cpuquota?: number; + /** + * @description JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. + * + * For example, the build arg `FOO=bar` would become `{"FOO":"bar"}` in JSON. This would result in the query parameter `buildargs={"FOO":"bar"}`. Note that `{"FOO":"bar"}` should be URI component encoded. + * + * [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg) + */ + buildargs?: string; + /** @description Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB. */ + shmsize?: number; + /** @description Squash the resulting images layers into a single layer. *(Experimental release only.)* */ + squash?: boolean; + /** @description Arbitrary key/value labels to set on the image, as a JSON map of string pairs. */ + labels?: string; + /** + * @description Sets the networking mode for the run commands during build. Supported + * standard values are: `bridge`, `host`, `none`, and `container:`. + * Any other value is taken as a custom network's name or ID to which this + * container should connect to. + */ + networkmode?: string; + /** @description Platform in the format os[/arch[/variant]] */ + platform?: string; + /** @description Target build stage */ + target?: string; + /** @description BuildKit output configuration */ + outputs?: string; + /** + * @description Version of the builder backend to use. + * + * - `1` is the first generation classic (deprecated) builder in the Docker daemon (default) + * - `2` is [BuildKit](https://github.com/moby/buildkit) + */ + version?: "1" | "2"; + }; + header?: { + "Content-type"?: "application/x-tar"; + /** + * @description This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + * + * The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + * + * ``` + * { + * "docker.example.com": { + * "username": "janedoe", + * "password": "hunter2" + * }, + * "https://index.docker.io/v1/": { + * "username": "mobydock", + * "password": "conta1n3rize14" + * } + * } + * ``` + * + * Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + */ + "X-Registry-Config"?: string; + }; + }; + /** @description A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz. */ + requestBody?: { + content: { + "application/octet-stream": string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description Bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Delete builder cache */ + BuildPrune: { + parameters: { + query?: { + /** @description Amount of disk space in bytes to keep for cache */ + "keep-storage"?: number; + /** @description Remove all types of build cache */ + all?: boolean; + /** + * @description A JSON encoded value of the filters (a `map[string][]string`) to + * process on the list of build cache objects. + * + * Available filters: + * + * - `until=` remove cache older than ``. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon's local time. + * - `id=` + * - `parent=` + * - `type=` + * - `description=` + * - `inuse` + * - `shared` + * - `private` + */ + filters?: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + "application/json": { + CachesDeleted?: string[]; + /** + * Format: int64 + * @description Disk space reclaimed in bytes + */ + SpaceReclaimed?: number; + }; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Create an image + * @description Pull or import an image. + */ + ImageCreate: { + parameters: { + query?: { + /** @description Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed. */ + fromImage?: string; + /** @description Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image. */ + fromSrc?: string; + /** @description Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image. */ + repo?: string; + /** @description Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled. */ + tag?: string; + /** @description Set commit message for imported image. */ + message?: string; + /** + * @description Apply `Dockerfile` instructions to the image that is created, + * for example: `changes=ENV DEBUG=true`. + * Note that `ENV DEBUG=true` should be URI component encoded. + * + * Supported `Dockerfile` instructions: + * `CMD`|`ENTRYPOINT`|`ENV`|`EXPOSE`|`ONBUILD`|`USER`|`VOLUME`|`WORKDIR` + */ + changes?: string[]; + /** + * @description Platform in the format os[/arch[/variant]]. + * + * When used in combination with the `fromImage` option, the daemon checks + * if the given image is present in the local image cache with the given + * OS and Architecture, and otherwise attempts to pull the image. If the + * option is not set, the host's native OS and Architecture are used. + * If the given image does not exist in the local image cache, the daemon + * attempts to pull the image with the host's native OS and Architecture. + * If the given image does exists in the local image cache, but its OS or + * architecture does not match, a warning is produced. + * + * When used with the `fromSrc` option to import an image from an archive, + * this option sets the platform information for the imported image. If + * the option is not set, the host's native OS and Architecture are used + * for the imported image. + */ + platform?: string; + }; + header?: { + /** + * @description A base64url-encoded auth configuration. + * + * Refer to the [authentication section](#section/Authentication) for + * details. + */ + "X-Registry-Auth"?: string; + }; + }; + /** @description Image content if the value `-` has been specified in fromSrc query parameter */ + requestBody?: { + content: { + "text/plain": string; + "application/octet-stream": string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description repository does not exist or no read access */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Inspect an image + * @description Return low-level information about an image. + */ + ImageInspect: { + parameters: { + path: { + /** @description Image name or id */ + name: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + "application/json": components["schemas"]["ImageInspect"]; + }; + }; + /** @description No such image */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Get the history of an image + * @description Return parent layers of an image. + */ + ImageHistory: { + parameters: { + path: { + /** @description Image name or ID */ + name: string; + }; + }; + responses: { + /** @description List of image layers */ + 200: { + content: { + "application/json": { + Id: string; + /** Format: int64 */ + Created: number; + CreatedBy: string; + Tags: string[]; + /** Format: int64 */ + Size: number; + Comment: string; + }[]; + }; + }; + /** @description No such image */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Push an image + * @description Push an image to a registry. + * + * If you wish to push an image on to a private registry, that image must + * already have a tag which references the registry. For example, + * `registry.example.com/myimage:latest`. + * + * The push is cancelled if the HTTP connection is closed. + */ + ImagePush: { + parameters: { + query?: { + /** @description The tag to associate with the image on the registry. */ + tag?: string; + }; + header: { + /** + * @description A base64url-encoded auth configuration. + * + * Refer to the [authentication section](#section/Authentication) for + * details. + */ + "X-Registry-Auth": string; + }; + path: { + /** @description Image name or ID. */ + name: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + }; + }; + /** @description No such image */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Tag an image + * @description Tag an image so that it becomes part of a repository. + */ + ImageTag: { + parameters: { + query?: { + /** @description The repository to tag in. For example, `someuser/someimage`. */ + repo?: string; + /** @description The name of the new tag. */ + tag?: string; + }; + path: { + /** @description Image name or ID to tag. */ + name: string; + }; + }; + responses: { + /** @description No error */ + 201: { + content: { + }; + }; + /** @description Bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description No such image */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Conflict */ + 409: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Remove an image + * @description Remove an image, along with any untagged parent images that were + * referenced by that image. + * + * Images can't be removed if they have descendant images, are being + * used by a running container or are being used by a build. + */ + ImageDelete: { + parameters: { + query?: { + /** @description Remove the image even if it is being used by stopped containers or has other tags */ + force?: boolean; + /** @description Do not delete untagged parent images */ + noprune?: boolean; + }; + path: { + /** @description Image name or ID */ + name: string; + }; + }; + responses: { + /** @description The image was deleted successfully */ + 200: { + content: { + "application/json": components["schemas"]["ImageDeleteResponseItem"][]; + }; + }; + /** @description No such image */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Conflict */ + 409: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Search images + * @description Search for an image on Docker Hub. + */ + ImageSearch: { + parameters: { + query: { + /** @description Term to search */ + term: string; + /** @description Maximum number of results to return */ + limit?: number; + /** + * @description A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + * + * - `is-automated=(true|false)` (deprecated, see below) + * - `is-official=(true|false)` + * - `stars=` Matches images that has at least 'number' stars. + * + * The `is-automated` filter is deprecated. The `is_automated` field has + * been deprecated by Docker Hub's search API. Consequently, searching + * for `is-automated=true` will yield no results. + */ + filters?: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + "application/json": { + description?: string; + is_official?: boolean; + /** + * @description Whether this repository has automated builds enabled. + * + *


+ * + * > **Deprecated**: This field is deprecated and will always + * > be "false" in future. + * + * @example false + */ + is_automated?: boolean; + name?: string; + star_count?: number; + }[]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Delete unused images */ + ImagePrune: { + parameters: { + query?: { + /** + * @description Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + * + * - `dangling=` When set to `true` (or `1`), prune only + * unused *and* untagged images. When set to `false` + * (or `0`), all unused images are pruned. + * - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machineโ€™s time. + * - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + */ + filters?: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + "application/json": { + /** @description Images that were deleted */ + ImagesDeleted?: components["schemas"]["ImageDeleteResponseItem"][]; + /** + * Format: int64 + * @description Disk space reclaimed in bytes + */ + SpaceReclaimed?: number; + }; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Check auth configuration + * @description Validate credentials for a registry and, if available, get an identity + * token for accessing the registry without password. + */ + SystemAuth: { + /** @description Authentication to check */ + requestBody?: { + content: { + "application/json": components["schemas"]["AuthConfig"]; + }; + }; + responses: { + /** @description An identity token was generated successfully. */ + 200: { + content: { + "application/json": { + /** @description The status of the authentication */ + Status: string; + /** @description An opaque token used to authenticate a user after a successful login */ + IdentityToken?: string; + }; + }; + }; + /** @description No error */ + 204: { + content: { + }; + }; + /** @description Auth error */ + 401: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Get system information */ + SystemInfo: { + responses: { + /** @description No error */ + 200: { + content: { + "application/json": components["schemas"]["SystemInfo"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Get version + * @description Returns the version of Docker that is running and various information about the system that Docker is running on. + */ + SystemVersion: { + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["SystemVersion"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Ping + * @description This is a dummy endpoint you can use to test if the server is accessible. + */ + SystemPing: { + responses: { + /** @description no error */ + 200: { + headers: { + /** + * @description Contains information about Swarm status of the daemon, + * and if the daemon is acting as a manager or worker node. + */ + Swarm?: "inactive" | "pending" | "error" | "locked" | "active/worker" | "active/manager"; + /** @description If the server is running with experimental mode enabled */ + "Docker-Experimental"?: boolean; + "Cache-Control"?: string; + Pragma?: string; + /** @description Max API Version the server supports */ + "API-Version"?: string; + /** + * @description Default version of docker image builder + * + * The default on Linux is version "2" (BuildKit), but the daemon + * can be configured to recommend version "1" (classic Builder). + * Windows does not yet support BuildKit for native Windows images, + * and uses "1" (classic builder) as a default. + * + * This value is a recommendation as advertised by the daemon, and + * it is up to the client to choose which builder to use. + */ + "Builder-Version"?: string; + }; + content: { + "text/plain": string; + }; + }; + /** @description server error */ + 500: { + headers: { + "Cache-Control"?: string; + Pragma?: string; + }; + content: { + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Ping + * @description This is a dummy endpoint you can use to test if the server is accessible. + */ + SystemPingHead: { + responses: { + /** @description no error */ + 200: { + headers: { + /** + * @description Contains information about Swarm status of the daemon, + * and if the daemon is acting as a manager or worker node. + */ + Swarm?: "inactive" | "pending" | "error" | "locked" | "active/worker" | "active/manager"; + /** @description If the server is running with experimental mode enabled */ + "Docker-Experimental"?: boolean; + "Cache-Control"?: string; + Pragma?: string; + /** @description Max API Version the server supports */ + "API-Version"?: string; + /** @description Default version of docker image builder */ + "Builder-Version"?: string; + }; + content: { + "text/plain": string; + }; + }; + /** @description server error */ + 500: { + content: { + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Create a new image from a container */ + ImageCommit: { + parameters: { + query?: { + /** @description The ID or name of the container to commit */ + container?: string; + /** @description Repository name for the created image */ + repo?: string; + /** @description Tag name for the create image */ + tag?: string; + /** @description Commit message */ + comment?: string; + /** @description Author of the image (e.g., `John Hannibal Smith `) */ + author?: string; + /** @description Whether to pause the container before committing */ + pause?: boolean; + /** @description `Dockerfile` instructions to apply while committing */ + changes?: string; + }; + }; + /** @description The container configuration */ + requestBody?: { + content: { + "application/json": components["schemas"]["ContainerConfig"]; + }; + }; + responses: { + /** @description no error */ + 201: { + content: { + "application/json": components["schemas"]["IdResponse"]; + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Monitor events + * @description Stream real-time events from the server. + * + * Various objects within Docker report events when something happens to them. + * + * Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, `update`, and `prune` + * + * Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, `untag`, and `prune` + * + * Volumes report these events: `create`, `mount`, `unmount`, `destroy`, and `prune` + * + * Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, `remove`, and `prune` + * + * The Docker daemon reports these events: `reload` + * + * Services report these events: `create`, `update`, and `remove` + * + * Nodes report these events: `create`, `update`, and `remove` + * + * Secrets report these events: `create`, `update`, and `remove` + * + * Configs report these events: `create`, `update`, and `remove` + * + * The Builder reports `prune` events + */ + SystemEvents: { + parameters: { + query?: { + /** @description Show events created since this timestamp then stream new events. */ + since?: string; + /** @description Show events created until this timestamp then stop streaming. */ + until?: string; + /** + * @description A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + * + * - `config=` config name or ID + * - `container=` container name or ID + * - `daemon=` daemon name or ID + * - `event=` event type + * - `image=` image name or ID + * - `label=` image or container label + * - `network=` network name or ID + * - `node=` node ID + * - `plugin`= plugin name or ID + * - `scope`= local or swarm + * - `secret=` secret name or ID + * - `service=` service name or ID + * - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config` + * - `volume=` volume name + */ + filters?: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["EventMessage"]; + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Get data usage information */ + SystemDataUsage: { + parameters: { + query?: { + /** @description Object types, for which to compute and return data. */ + type?: ("container" | "image" | "volume" | "build-cache")[]; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": { + /** Format: int64 */ + LayersSize?: number; + Images?: components["schemas"]["ImageSummary"][]; + Containers?: components["schemas"]["ContainerSummary"][]; + Volumes?: components["schemas"]["Volume"][]; + BuildCache?: components["schemas"]["BuildCache"][]; + }; + "text/plain": { + /** Format: int64 */ + LayersSize?: number; + Images?: components["schemas"]["ImageSummary"][]; + Containers?: components["schemas"]["ContainerSummary"][]; + Volumes?: components["schemas"]["Volume"][]; + BuildCache?: components["schemas"]["BuildCache"][]; + }; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Export an image + * @description Get a tarball containing all images and metadata for a repository. + * + * If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + * + * ### Image tarball format + * + * An image tarball contains one directory per image layer (named using its long ID), each containing these files: + * + * - `VERSION`: currently `1.0` - the file format version + * - `json`: detailed layer information, similar to `docker inspect layer_id` + * - `layer.tar`: A tarfile containing the filesystem changes in this layer + * + * The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + * + * If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + * + * ```json + * { + * "hello-world": { + * "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + * } + * } + * ``` + */ + ImageGet: { + parameters: { + path: { + /** @description Image name or ID */ + name: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/x-tar": string; + }; + }; + /** @description server error */ + 500: { + content: { + "application/x-tar": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Export several images + * @description Get a tarball containing all images and metadata for several image + * repositories. + * + * For each value of the `names` parameter: if it is a specific name and + * tag (e.g. `ubuntu:latest`), then only that image (and its parents) are + * returned; if it is an image ID, similarly only that image (and its parents) + * are returned and there would be no names referenced in the 'repositories' + * file for this image ID. + * + * For details on the format, see the [export image endpoint](#operation/ImageGet). + */ + ImageGetAll: { + parameters: { + query?: { + /** @description Image names to filter by */ + names?: string[]; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/x-tar": string; + }; + }; + /** @description server error */ + 500: { + content: { + "application/x-tar": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Import images + * @description Load a set of images and tags into a repository. + * + * For details on the format, see the [export image endpoint](#operation/ImageGet). + */ + ImageLoad: { + parameters: { + query?: { + /** @description Suppress progress details during load. */ + quiet?: boolean; + }; + }; + /** @description Tar archive containing images */ + requestBody?: { + content: { + "application/x-tar": string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Create an exec instance + * @description Run a command inside a running container. + */ + ContainerExec: { + parameters: { + path: { + /** @description ID or name of container */ + id: string; + }; + }; + /** @description Exec configuration */ + requestBody: { + content: { + "application/json": { + /** @description Attach to `stdin` of the exec command. */ + AttachStdin?: boolean; + /** @description Attach to `stdout` of the exec command. */ + AttachStdout?: boolean; + /** @description Attach to `stderr` of the exec command. */ + AttachStderr?: boolean; + /** @description Initial console size, as an `[height, width]` array. */ + ConsoleSize?: number[] | null; + /** + * @description Override the key sequence for detaching a container. Format is + * a single character `[a-Z]` or `ctrl-` where `` + * is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. + */ + DetachKeys?: string; + /** @description Allocate a pseudo-TTY. */ + Tty?: boolean; + /** @description A list of environment variables in the form `["VAR=value", ...]`. */ + Env?: string[]; + /** @description Command to run, as a string or array of strings. */ + Cmd?: string[]; + /** + * @description Runs the exec process with extended privileges. + * @default false + */ + Privileged?: boolean; + /** + * @description The user, and optionally, group to run the exec process inside + * the container. Format is one of: `user`, `user:group`, `uid`, + * or `uid:gid`. + */ + User?: string; + /** @description The working directory for the exec process inside the container. */ + WorkingDir?: string; + }; + }; + }; + responses: { + /** @description no error */ + 201: { + content: { + "application/json": components["schemas"]["IdResponse"]; + }; + }; + /** @description no such container */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description container is paused */ + 409: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Start an exec instance + * @description Starts a previously set up exec instance. If detach is true, this endpoint + * returns immediately after starting the command. Otherwise, it sets up an + * interactive session with the command. + */ + ExecStart: { + parameters: { + path: { + /** @description Exec instance ID */ + id: string; + }; + }; + requestBody?: { + content: { + "application/json": { + /** @description Detach from the command. */ + Detach?: boolean; + /** @description Allocate a pseudo-TTY. */ + Tty?: boolean; + /** @description Initial console size, as an `[height, width]` array. */ + ConsoleSize?: number[] | null; + }; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + }; + }; + /** @description No such exec instance */ + 404: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Container is stopped or paused */ + 409: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Resize an exec instance + * @description Resize the TTY session used by an exec instance. This endpoint only works + * if `tty` was specified as part of creating and starting the exec instance. + */ + ExecResize: { + parameters: { + query?: { + /** @description Height of the TTY session in characters */ + h?: number; + /** @description Width of the TTY session in characters */ + w?: number; + }; + path: { + /** @description Exec instance ID */ + id: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description No such exec instance */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Inspect an exec instance + * @description Return low-level information about an exec instance. + */ + ExecInspect: { + parameters: { + path: { + /** @description Exec instance ID */ + id: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + "application/json": { + CanRemove?: boolean; + DetachKeys?: string; + ID?: string; + Running?: boolean; + ExitCode?: number; + ProcessConfig?: components["schemas"]["ProcessConfig"]; + OpenStdin?: boolean; + OpenStderr?: boolean; + OpenStdout?: boolean; + ContainerID?: string; + /** @description The system process ID for the exec process. */ + Pid?: number; + }; + }; + }; + /** @description No such exec instance */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** List volumes */ + VolumeList: { + parameters: { + query?: { + /** + * @description JSON encoded value of the filters (a `map[string][]string`) to + * process on the volumes list. Available filters: + * + * - `dangling=` When set to `true` (or `1`), returns all + * volumes that are not in use by a container. When set to `false` + * (or `0`), only volumes that are in use by one or more + * containers are returned. + * - `driver=` Matches volumes based on their driver. + * - `label=` or `label=:` Matches volumes based on + * the presence of a `label` alone or a `label` and a value. + * - `name=` Matches all or part of a volume name. + */ + filters?: string; + }; + }; + responses: { + /** @description Summary volume data that matches the query */ + 200: { + content: { + "application/json": components["schemas"]["VolumeListResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Create a volume */ + VolumeCreate: { + /** @description Volume configuration */ + requestBody: { + content: { + "application/json": components["schemas"]["VolumeCreateOptions"]; + }; + }; + responses: { + /** @description The volume was created successfully */ + 201: { + content: { + "application/json": components["schemas"]["Volume"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Inspect a volume */ + VolumeInspect: { + parameters: { + path: { + /** @description Volume name or ID */ + name: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + "application/json": components["schemas"]["Volume"]; + }; + }; + /** @description No such volume */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** "Update a volume. Valid only for Swarm cluster volumes" */ + VolumeUpdate: { + parameters: { + query: { + /** + * @description The version number of the volume being updated. This is required to + * avoid conflicting writes. Found in the volume's `ClusterVolume` + * field. + */ + version: number; + }; + path: { + /** @description The name or ID of the volume */ + name: string; + }; + }; + /** + * @description The spec of the volume to update. Currently, only Availability may + * change. All other fields must remain unchanged. + */ + requestBody?: { + content: { + "application/json": { + Spec?: components["schemas"]["ClusterVolumeSpec"]; + }; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description no such volume */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Remove a volume + * @description Instruct the driver to remove the volume. + */ + VolumeDelete: { + parameters: { + query?: { + /** @description Force the removal of the volume */ + force?: boolean; + }; + path: { + /** @description Volume name or ID */ + name: string; + }; + }; + responses: { + /** @description The volume was removed */ + 204: { + content: { + }; + }; + /** @description No such volume or volume driver */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Volume is in use and cannot be removed */ + 409: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Delete unused volumes */ + VolumePrune: { + parameters: { + query?: { + /** + * @description Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + * + * Available filters: + * - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + * - `all` (`all=true`) - Consider all (local) volumes for pruning and not just anonymous volumes. + */ + filters?: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + "application/json": { + /** @description Volumes that were deleted */ + VolumesDeleted?: string[]; + /** + * Format: int64 + * @description Disk space reclaimed in bytes + */ + SpaceReclaimed?: number; + }; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * List networks + * @description Returns a list of networks. For details on the format, see the + * [network inspect endpoint](#operation/NetworkInspect). + * + * Note that it uses a different, smaller representation of a network than + * inspecting a single network. For example, the list of containers attached + * to the network is not propagated in API versions 1.28 and up. + */ + NetworkList: { + parameters: { + query?: { + /** + * @description JSON encoded value of the filters (a `map[string][]string`) to process + * on the networks list. + * + * Available filters: + * + * - `dangling=` When set to `true` (or `1`), returns all + * networks that are not in use by a container. When set to `false` + * (or `0`), only networks that are in use by one or more + * containers are returned. + * - `driver=` Matches a network's driver. + * - `id=` Matches all or part of a network ID. + * - `label=` or `label==` of a network label. + * - `name=` Matches all or part of a network name. + * - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + * - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + */ + filters?: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + "application/json": components["schemas"]["Network"][]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Inspect a network */ + NetworkInspect: { + parameters: { + query?: { + /** @description Detailed inspect output for troubleshooting */ + verbose?: boolean; + /** @description Filter the network by scope (swarm, global, or local) */ + scope?: string; + }; + path: { + /** @description Network ID or name */ + id: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + "application/json": components["schemas"]["Network"]; + }; + }; + /** @description Network not found */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Remove a network */ + NetworkDelete: { + parameters: { + path: { + /** @description Network ID or name */ + id: string; + }; + }; + responses: { + /** @description No error */ + 204: { + content: { + }; + }; + /** @description operation not supported for pre-defined networks */ + 403: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description no such network */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Create a network */ + NetworkCreate: { + /** @description Network configuration */ + requestBody: { + content: { + "application/json": { + /** @description The network's name. */ + Name: string; + /** @description Deprecated: CheckDuplicate is now always enabled. */ + CheckDuplicate?: boolean; + /** + * @description Name of the network driver plugin to use. + * @default bridge + */ + Driver?: string; + /** @description Restrict external access to the network. */ + Internal?: boolean; + /** + * @description Globally scoped network is manually attachable by regular + * containers from workers in swarm mode. + */ + Attachable?: boolean; + /** + * @description Ingress network is the network which provides the routing-mesh + * in swarm mode. + */ + Ingress?: boolean; + IPAM?: components["schemas"]["IPAM"]; + /** @description Enable IPv6 on the network. */ + EnableIPv6?: boolean; + /** @description Network specific options to be used by the drivers. */ + Options?: { + [key: string]: string; + }; + /** @description User-defined key/value metadata. */ + Labels?: { + [key: string]: string; + }; + }; + }; + }; + responses: { + /** @description No error */ + 201: { + content: { + "application/json": { + /** @description The ID of the created network. */ + Id?: string; + Warning?: string; + }; + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** + * @description Forbidden operation. This happens when trying to create a network named after a pre-defined network, + * or when trying to create an overlay network on a daemon which is not part of a Swarm cluster. + */ + 403: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description plugin not found */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Connect a container to a network + * @description The network must be either a local-scoped network or a swarm-scoped network with the `attachable` option set. A network cannot be re-attached to a running container + */ + NetworkConnect: { + parameters: { + path: { + /** @description Network ID or name */ + id: string; + }; + }; + requestBody: { + content: { + "application/json": { + /** @description The ID or name of the container to connect to the network. */ + Container?: string; + EndpointConfig?: components["schemas"]["EndpointSettings"]; + }; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Operation forbidden */ + 403: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Network or container not found */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Disconnect a container from a network */ + NetworkDisconnect: { + parameters: { + path: { + /** @description Network ID or name */ + id: string; + }; + }; + requestBody: { + content: { + "application/json": { + /** @description The ID or name of the container to disconnect from the network. */ + Container?: string; + /** @description Force the container to disconnect from the network. */ + Force?: boolean; + }; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + }; + }; + /** @description Operation not supported for swarm scoped networks */ + 403: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Network or container not found */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Delete unused networks */ + NetworkPrune: { + parameters: { + query?: { + /** + * @description Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + * + * Available filters: + * - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machineโ€™s time. + * - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + */ + filters?: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + "application/json": { + /** @description Networks that were deleted */ + NetworksDeleted?: string[]; + }; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * List plugins + * @description Returns information about installed plugins. + */ + PluginList: { + parameters: { + query?: { + /** + * @description A JSON encoded value of the filters (a `map[string][]string`) to + * process on the plugin list. + * + * Available filters: + * + * - `capability=` + * - `enable=|` + */ + filters?: string; + }; + }; + responses: { + /** @description No error */ + 200: { + content: { + "application/json": components["schemas"]["Plugin"][]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Get plugin privileges */ + GetPluginPrivileges: { + parameters: { + query: { + /** + * @description The name of the plugin. The `:latest` tag is optional, and is the + * default if omitted. + */ + remote: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["PluginPrivilege"][]; + "text/plain": components["schemas"]["PluginPrivilege"][]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Install a plugin + * @description Pulls and installs a plugin. After the plugin is installed, it can be + * enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + */ + PluginPull: { + parameters: { + query: { + /** + * @description Remote reference for plugin to install. + * + * The `:latest` tag is optional, and is used as the default if omitted. + */ + remote: string; + /** + * @description Local name for the pulled plugin. + * + * The `:latest` tag is optional, and is used as the default if omitted. + */ + name?: string; + }; + header?: { + /** + * @description A base64url-encoded auth configuration to use when pulling a plugin + * from a registry. + * + * Refer to the [authentication section](#section/Authentication) for + * details. + */ + "X-Registry-Auth"?: string; + }; + }; + requestBody?: { + content: { + "application/json": components["schemas"]["PluginPrivilege"][]; + "text/plain": components["schemas"]["PluginPrivilege"][]; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Inspect a plugin */ + PluginInspect: { + parameters: { + path: { + /** + * @description The name of the plugin. The `:latest` tag is optional, and is the + * default if omitted. + */ + name: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Plugin"]; + "text/plain": components["schemas"]["Plugin"]; + }; + }; + /** @description plugin is not installed */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Remove a plugin */ + PluginDelete: { + parameters: { + query?: { + /** + * @description Disable the plugin before removing. This may result in issues if the + * plugin is in use by a container. + */ + force?: boolean; + }; + path: { + /** + * @description The name of the plugin. The `:latest` tag is optional, and is the + * default if omitted. + */ + name: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Plugin"]; + "text/plain": components["schemas"]["Plugin"]; + }; + }; + /** @description plugin is not installed */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Enable a plugin */ + PluginEnable: { + parameters: { + query?: { + /** @description Set the HTTP client timeout (in seconds) */ + timeout?: number; + }; + path: { + /** + * @description The name of the plugin. The `:latest` tag is optional, and is the + * default if omitted. + */ + name: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description plugin is not installed */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Disable a plugin */ + PluginDisable: { + parameters: { + query?: { + /** @description Force disable a plugin even if still in use. */ + force?: boolean; + }; + path: { + /** + * @description The name of the plugin. The `:latest` tag is optional, and is the + * default if omitted. + */ + name: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description plugin is not installed */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Upgrade a plugin */ + PluginUpgrade: { + parameters: { + query: { + /** + * @description Remote reference to upgrade to. + * + * The `:latest` tag is optional, and is used as the default if omitted. + */ + remote: string; + }; + header?: { + /** + * @description A base64url-encoded auth configuration to use when pulling a plugin + * from a registry. + * + * Refer to the [authentication section](#section/Authentication) for + * details. + */ + "X-Registry-Auth"?: string; + }; + path: { + /** + * @description The name of the plugin. The `:latest` tag is optional, and is the + * default if omitted. + */ + name: string; + }; + }; + requestBody?: { + content: { + "application/json": components["schemas"]["PluginPrivilege"][]; + "text/plain": components["schemas"]["PluginPrivilege"][]; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description plugin not installed */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Create a plugin */ + PluginCreate: { + parameters: { + query: { + /** + * @description The name of the plugin. The `:latest` tag is optional, and is the + * default if omitted. + */ + name: string; + }; + }; + /** @description Path to tar containing plugin rootfs and manifest */ + requestBody?: { + content: { + "application/x-tar": string; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Push a plugin + * @description Push a plugin to the registry. + */ + PluginPush: { + parameters: { + path: { + /** + * @description The name of the plugin. The `:latest` tag is optional, and is the + * default if omitted. + */ + name: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description plugin not installed */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Configure a plugin */ + PluginSet: { + parameters: { + path: { + /** + * @description The name of the plugin. The `:latest` tag is optional, and is the + * default if omitted. + */ + name: string; + }; + }; + requestBody?: { + content: { + "application/json": string[]; + }; + }; + responses: { + /** @description No error */ + 204: { + content: { + }; + }; + /** @description Plugin not installed */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** List nodes */ + NodeList: { + parameters: { + query?: { + /** + * @description Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + * + * Available filters: + * - `id=` + * - `label=` + * - `membership=`(`accepted`|`pending`)` + * - `name=` + * - `node.label=` + * - `role=`(`manager`|`worker`)` + */ + filters?: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Node"][]; + "text/plain": components["schemas"]["Node"][]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Inspect a node */ + NodeInspect: { + parameters: { + path: { + /** @description The ID or name of the node */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Node"]; + "text/plain": components["schemas"]["Node"]; + }; + }; + /** @description no such node */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Delete a node */ + NodeDelete: { + parameters: { + query?: { + /** @description Force remove a node from the swarm */ + force?: boolean; + }; + path: { + /** @description The ID or name of the node */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description no such node */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Update a node */ + NodeUpdate: { + parameters: { + query: { + /** + * @description The version number of the node object being updated. This is required + * to avoid conflicting writes. + */ + version: number; + }; + path: { + /** @description The ID of the node */ + id: string; + }; + }; + requestBody?: { + content: { + "application/json": components["schemas"]["NodeSpec"]; + "text/plain": components["schemas"]["NodeSpec"]; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description no such node */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Inspect swarm */ + SwarmInspect: { + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Swarm"]; + "text/plain": components["schemas"]["Swarm"]; + }; + }; + /** @description no such swarm */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Initialize a new swarm */ + SwarmInit: { + requestBody: { + content: { + "application/json": { + /** + * @description Listen address used for inter-manager communication, as well + * as determining the networking interface used for the VXLAN + * Tunnel Endpoint (VTEP). This can either be an address/port + * combination in the form `192.168.1.1:4567`, or an interface + * followed by a port number, like `eth0:4567`. If the port number + * is omitted, the default swarm listening port is used. + */ + ListenAddr?: string; + /** + * @description Externally reachable address advertised to other nodes. This + * can either be an address/port combination in the form + * `192.168.1.1:4567`, or an interface followed by a port number, + * like `eth0:4567`. If the port number is omitted, the port + * number from the listen address is used. If `AdvertiseAddr` is + * not specified, it will be automatically detected when possible. + */ + AdvertiseAddr?: string; + /** + * @description Address or interface to use for data path traffic (format: + * ``), for example, `192.168.1.1`, or an interface, + * like `eth0`. If `DataPathAddr` is unspecified, the same address + * as `AdvertiseAddr` is used. + * + * The `DataPathAddr` specifies the address that global scope + * network drivers will publish towards other nodes in order to + * reach the containers running on this node. Using this parameter + * it is possible to separate the container data traffic from the + * management traffic of the cluster. + */ + DataPathAddr?: string; + /** + * Format: uint32 + * @description DataPathPort specifies the data path port number for data traffic. + * Acceptable port range is 1024 to 49151. + * if no port is set or is set to 0, default port 4789 will be used. + */ + DataPathPort?: number; + /** + * @description Default Address Pool specifies default subnet pools for global + * scope networks. + */ + DefaultAddrPool?: string[]; + /** @description Force creation of a new swarm. */ + ForceNewCluster?: boolean; + /** + * Format: uint32 + * @description SubnetSize specifies the subnet size of the networks created + * from the default subnet pool. + */ + SubnetSize?: number; + Spec?: components["schemas"]["SwarmSpec"]; + }; + "text/plain": { + /** + * @description Listen address used for inter-manager communication, as well + * as determining the networking interface used for the VXLAN + * Tunnel Endpoint (VTEP). This can either be an address/port + * combination in the form `192.168.1.1:4567`, or an interface + * followed by a port number, like `eth0:4567`. If the port number + * is omitted, the default swarm listening port is used. + */ + ListenAddr?: string; + /** + * @description Externally reachable address advertised to other nodes. This + * can either be an address/port combination in the form + * `192.168.1.1:4567`, or an interface followed by a port number, + * like `eth0:4567`. If the port number is omitted, the port + * number from the listen address is used. If `AdvertiseAddr` is + * not specified, it will be automatically detected when possible. + */ + AdvertiseAddr?: string; + /** + * @description Address or interface to use for data path traffic (format: + * ``), for example, `192.168.1.1`, or an interface, + * like `eth0`. If `DataPathAddr` is unspecified, the same address + * as `AdvertiseAddr` is used. + * + * The `DataPathAddr` specifies the address that global scope + * network drivers will publish towards other nodes in order to + * reach the containers running on this node. Using this parameter + * it is possible to separate the container data traffic from the + * management traffic of the cluster. + */ + DataPathAddr?: string; + /** + * Format: uint32 + * @description DataPathPort specifies the data path port number for data traffic. + * Acceptable port range is 1024 to 49151. + * if no port is set or is set to 0, default port 4789 will be used. + */ + DataPathPort?: number; + /** + * @description Default Address Pool specifies default subnet pools for global + * scope networks. + */ + DefaultAddrPool?: string[]; + /** @description Force creation of a new swarm. */ + ForceNewCluster?: boolean; + /** + * Format: uint32 + * @description SubnetSize specifies the subnet size of the networks created + * from the default subnet pool. + */ + SubnetSize?: number; + Spec?: components["schemas"]["SwarmSpec"]; + }; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": string; + "text/plain": string; + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is already part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Join an existing swarm */ + SwarmJoin: { + requestBody: { + content: { + "application/json": { + /** + * @description Listen address used for inter-manager communication if the node + * gets promoted to manager, as well as determining the networking + * interface used for the VXLAN Tunnel Endpoint (VTEP). + */ + ListenAddr?: string; + /** + * @description Externally reachable address advertised to other nodes. This + * can either be an address/port combination in the form + * `192.168.1.1:4567`, or an interface followed by a port number, + * like `eth0:4567`. If the port number is omitted, the port + * number from the listen address is used. If `AdvertiseAddr` is + * not specified, it will be automatically detected when possible. + */ + AdvertiseAddr?: string; + /** + * @description Address or interface to use for data path traffic (format: + * ``), for example, `192.168.1.1`, or an interface, + * like `eth0`. If `DataPathAddr` is unspecified, the same address + * as `AdvertiseAddr` is used. + * + * The `DataPathAddr` specifies the address that global scope + * network drivers will publish towards other nodes in order to + * reach the containers running on this node. Using this parameter + * it is possible to separate the container data traffic from the + * management traffic of the cluster. + */ + DataPathAddr?: string; + /** @description Addresses of manager nodes already participating in the swarm. */ + RemoteAddrs?: string[]; + /** @description Secret token for joining this swarm. */ + JoinToken?: string; + }; + "text/plain": { + /** + * @description Listen address used for inter-manager communication if the node + * gets promoted to manager, as well as determining the networking + * interface used for the VXLAN Tunnel Endpoint (VTEP). + */ + ListenAddr?: string; + /** + * @description Externally reachable address advertised to other nodes. This + * can either be an address/port combination in the form + * `192.168.1.1:4567`, or an interface followed by a port number, + * like `eth0:4567`. If the port number is omitted, the port + * number from the listen address is used. If `AdvertiseAddr` is + * not specified, it will be automatically detected when possible. + */ + AdvertiseAddr?: string; + /** + * @description Address or interface to use for data path traffic (format: + * ``), for example, `192.168.1.1`, or an interface, + * like `eth0`. If `DataPathAddr` is unspecified, the same address + * as `AdvertiseAddr` is used. + * + * The `DataPathAddr` specifies the address that global scope + * network drivers will publish towards other nodes in order to + * reach the containers running on this node. Using this parameter + * it is possible to separate the container data traffic from the + * management traffic of the cluster. + */ + DataPathAddr?: string; + /** @description Addresses of manager nodes already participating in the swarm. */ + RemoteAddrs?: string[]; + /** @description Secret token for joining this swarm. */ + JoinToken?: string; + }; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is already part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Leave a swarm */ + SwarmLeave: { + parameters: { + query?: { + /** + * @description Force leave swarm, even if this is the last manager or that it will + * break the cluster. + */ + force?: boolean; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Update a swarm */ + SwarmUpdate: { + parameters: { + query: { + /** + * @description The version number of the swarm object being updated. This is + * required to avoid conflicting writes. + */ + version: number; + /** @description Rotate the worker join token. */ + rotateWorkerToken?: boolean; + /** @description Rotate the manager join token. */ + rotateManagerToken?: boolean; + /** @description Rotate the manager unlock key. */ + rotateManagerUnlockKey?: boolean; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SwarmSpec"]; + "text/plain": components["schemas"]["SwarmSpec"]; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Get the unlock key */ + SwarmUnlockkey: { + responses: { + /** @description no error */ + 200: { + content: { + "application/json": { + /** @description The swarm's unlock key. */ + UnlockKey?: string; + }; + "text/plain": { + /** @description The swarm's unlock key. */ + UnlockKey?: string; + }; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Unlock a locked manager */ + SwarmUnlock: { + requestBody: { + content: { + "application/json": { + /** @description The swarm's unlock key. */ + UnlockKey?: string; + }; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** List services */ + ServiceList: { + parameters: { + query?: { + /** + * @description A JSON encoded value of the filters (a `map[string][]string`) to + * process on the services list. + * + * Available filters: + * + * - `id=` + * - `label=` + * - `mode=["replicated"|"global"]` + * - `name=` + */ + filters?: string; + /** @description Include service status, with count of running and desired tasks. */ + status?: boolean; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Service"][]; + "text/plain": components["schemas"]["Service"][]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Create a service */ + ServiceCreate: { + parameters: { + header?: { + /** + * @description A base64url-encoded auth configuration for pulling from private + * registries. + * + * Refer to the [authentication section](#section/Authentication) for + * details. + */ + "X-Registry-Auth"?: string; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ServiceSpec"] & Record; + }; + }; + responses: { + /** @description no error */ + 201: { + content: { + "application/json": components["schemas"]["ServiceCreateResponse"]; + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description network is not eligible for services */ + 403: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description name conflicts with an existing service */ + 409: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Inspect a service */ + ServiceInspect: { + parameters: { + query?: { + /** @description Fill empty fields with default values. */ + insertDefaults?: boolean; + }; + path: { + /** @description ID or name of service. */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Service"]; + "text/plain": components["schemas"]["Service"]; + }; + }; + /** @description no such service */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Delete a service */ + ServiceDelete: { + parameters: { + path: { + /** @description ID or name of service. */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description no such service */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Update a service */ + ServiceUpdate: { + parameters: { + query: { + /** + * @description The version number of the service object being updated. This is + * required to avoid conflicting writes. + * This version number should be the value as currently set on the + * service *before* the update. You can find the current version by + * calling `GET /services/{id}` + */ + version: number; + /** + * @description If the `X-Registry-Auth` header is not specified, this parameter + * indicates where to find registry authorization credentials. + */ + registryAuthFrom?: "spec" | "previous-spec"; + /** + * @description Set to this parameter to `previous` to cause a server-side rollback + * to the previous service spec. The supplied spec will be ignored in + * this case. + */ + rollback?: string; + }; + header?: { + /** + * @description A base64url-encoded auth configuration for pulling from private + * registries. + * + * Refer to the [authentication section](#section/Authentication) for + * details. + */ + "X-Registry-Auth"?: string; + }; + path: { + /** @description ID or name of service. */ + id: string; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ServiceSpec"] & Record; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["ServiceUpdateResponse"]; + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description no such service */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Get service logs + * @description Get `stdout` and `stderr` logs from a service. See also + * [`/containers/{id}/logs`](#operation/ContainerLogs). + * + * **Note**: This endpoint works only for services with the `local`, + * `json-file` or `journald` logging drivers. + */ + ServiceLogs: { + parameters: { + query?: { + /** @description Show service context and extra details provided to logs. */ + details?: boolean; + /** @description Keep connection after returning logs. */ + follow?: boolean; + /** @description Return logs from `stdout` */ + stdout?: boolean; + /** @description Return logs from `stderr` */ + stderr?: boolean; + /** @description Only return logs since this time, as a UNIX timestamp */ + since?: number; + /** @description Add timestamps to every log line */ + timestamps?: boolean; + /** + * @description Only return this number of log lines from the end of the logs. + * Specify as an integer or `all` to output all log lines. + */ + tail?: string; + }; + path: { + /** @description ID or name of the service */ + id: string; + }; + }; + responses: { + /** @description logs returned as a stream in response body */ + 200: { + content: { + "application/vnd.docker.raw-stream": string; + "application/vnd.docker.multiplexed-stream": string; + }; + }; + /** @description no such service */ + 404: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + "application/json": unknown; + }; + }; + /** @description server error */ + 500: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** List tasks */ + TaskList: { + parameters: { + query?: { + /** + * @description A JSON encoded value of the filters (a `map[string][]string`) to + * process on the tasks list. + * + * Available filters: + * + * - `desired-state=(running | shutdown | accepted)` + * - `id=` + * - `label=key` or `label="key=value"` + * - `name=` + * - `node=` + * - `service=` + */ + filters?: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Task"][]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Inspect a task */ + TaskInspect: { + parameters: { + path: { + /** @description ID of the task */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Task"]; + }; + }; + /** @description no such task */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Get task logs + * @description Get `stdout` and `stderr` logs from a task. + * See also [`/containers/{id}/logs`](#operation/ContainerLogs). + * + * **Note**: This endpoint works only for services with the `local`, + * `json-file` or `journald` logging drivers. + */ + TaskLogs: { + parameters: { + query?: { + /** @description Show task context and extra details provided to logs. */ + details?: boolean; + /** @description Keep connection after returning logs. */ + follow?: boolean; + /** @description Return logs from `stdout` */ + stdout?: boolean; + /** @description Return logs from `stderr` */ + stderr?: boolean; + /** @description Only return logs since this time, as a UNIX timestamp */ + since?: number; + /** @description Add timestamps to every log line */ + timestamps?: boolean; + /** + * @description Only return this number of log lines from the end of the logs. + * Specify as an integer or `all` to output all log lines. + */ + tail?: string; + }; + path: { + /** @description ID of the task */ + id: string; + }; + }; + responses: { + /** @description logs returned as a stream in response body */ + 200: { + content: { + "application/vnd.docker.raw-stream": string; + "application/vnd.docker.multiplexed-stream": string; + }; + }; + /** @description no such task */ + 404: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + "application/json": unknown; + }; + }; + /** @description server error */ + 500: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + "application/vnd.docker.multiplexed-stream": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** List secrets */ + SecretList: { + parameters: { + query?: { + /** + * @description A JSON encoded value of the filters (a `map[string][]string`) to + * process on the secrets list. + * + * Available filters: + * + * - `id=` + * - `label= or label==value` + * - `name=` + * - `names=` + */ + filters?: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Secret"][]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Create a secret */ + SecretCreate: { + requestBody?: { + content: { + "application/json": components["schemas"]["SecretSpec"] & Record; + }; + }; + responses: { + /** @description no error */ + 201: { + content: { + "application/json": components["schemas"]["IdResponse"]; + }; + }; + /** @description name conflicts with an existing object */ + 409: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Inspect a secret */ + SecretInspect: { + parameters: { + path: { + /** @description ID of the secret */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Secret"]; + }; + }; + /** @description secret not found */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Delete a secret */ + SecretDelete: { + parameters: { + path: { + /** @description ID of the secret */ + id: string; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description secret not found */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Update a Secret */ + SecretUpdate: { + parameters: { + query: { + /** + * @description The version number of the secret object being updated. This is + * required to avoid conflicting writes. + */ + version: number; + }; + path: { + /** @description The ID or name of the secret */ + id: string; + }; + }; + /** + * @description The spec of the secret to update. Currently, only the Labels field + * can be updated. All other fields must remain unchanged from the + * [SecretInspect endpoint](#operation/SecretInspect) response values. + */ + requestBody?: { + content: { + "application/json": components["schemas"]["SecretSpec"]; + "text/plain": components["schemas"]["SecretSpec"]; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description no such secret */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** List configs */ + ConfigList: { + parameters: { + query?: { + /** + * @description A JSON encoded value of the filters (a `map[string][]string`) to + * process on the configs list. + * + * Available filters: + * + * - `id=` + * - `label= or label==value` + * - `name=` + * - `names=` + */ + filters?: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Config"][]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Create a config */ + ConfigCreate: { + requestBody?: { + content: { + "application/json": components["schemas"]["ConfigSpec"] & Record; + }; + }; + responses: { + /** @description no error */ + 201: { + content: { + "application/json": components["schemas"]["IdResponse"]; + }; + }; + /** @description name conflicts with an existing object */ + 409: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Inspect a config */ + ConfigInspect: { + parameters: { + path: { + /** @description ID of the config */ + id: string; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + "application/json": components["schemas"]["Config"]; + }; + }; + /** @description config not found */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Delete a config */ + ConfigDelete: { + parameters: { + path: { + /** @description ID of the config */ + id: string; + }; + }; + responses: { + /** @description no error */ + 204: { + content: { + }; + }; + /** @description config not found */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** Update a Config */ + ConfigUpdate: { + parameters: { + query: { + /** + * @description The version number of the config object being updated. This is + * required to avoid conflicting writes. + */ + version: number; + }; + path: { + /** @description The ID or name of the config */ + id: string; + }; + }; + /** + * @description The spec of the config to update. Currently, only the Labels field + * can be updated. All other fields must remain unchanged from the + * [ConfigInspect endpoint](#operation/ConfigInspect) response values. + */ + requestBody?: { + content: { + "application/json": components["schemas"]["ConfigSpec"]; + "text/plain": components["schemas"]["ConfigSpec"]; + }; + }; + responses: { + /** @description no error */ + 200: { + content: { + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description no such config */ + 404: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description node is not part of a swarm */ + 503: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + "text/plain": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Get image information from the registry + * @description Return image digest and platform information by contacting the registry. + */ + DistributionInspect: { + parameters: { + path: { + /** @description Image name or id */ + name: string; + }; + }; + responses: { + /** @description descriptor and platform information */ + 200: { + content: { + "application/json": components["schemas"]["DistributionInspect"]; + }; + }; + /** @description Failed authentication or no image found */ + 401: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Server error */ + 500: { + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + /** + * Initialize interactive session + * @description Start a new interactive session with a server. Session allows server to + * call back to the client for advanced capabilities. + * + * ### Hijacking + * + * This endpoint hijacks the HTTP connection to HTTP2 transport that allows + * the client to expose gPRC services on that connection. + * + * For example, the client sends this request to upgrade the connection: + * + * ``` + * POST /session HTTP/1.1 + * Upgrade: h2c + * Connection: Upgrade + * ``` + * + * The Docker daemon responds with a `101 UPGRADED` response follow with + * the raw stream: + * + * ``` + * HTTP/1.1 101 UPGRADED + * Connection: Upgrade + * Upgrade: h2c + * ``` + */ + Session: { + responses: { + /** @description no error, hijacking successful */ + 101: { + content: { + }; + }; + /** @description bad parameter */ + 400: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description server error */ + 500: { + content: { + "application/vnd.docker.raw-stream": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; +} diff --git a/src/docker/modem.ts b/src/docker/modem.ts deleted file mode 100644 index 556ba44..0000000 --- a/src/docker/modem.ts +++ /dev/null @@ -1,79 +0,0 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= - -import { Socket } from "bun"; -import { RawRequest, ResponseParser } from "@/http"; -import { Docker } from "./api"; - -export type ModemCB = (res: Response) => void | Promise; - -//============================================================================= - -/** A class that represents a connection to the docker daemon. */ -export default class Modem { - public endpoint = "http://localhost/"; - private socket: Socket | null = null; - private callback: ModemCB | null = null; - - constructor(endpoint: string = "v1.43") { - this.endpoint += endpoint; - } - - //= Public =// - - public get isConnected() { - return this.socket !== null; - } - - public async connect() { - const parser = new ResponseParser(); - const appendOrResolve = async (buffer: Buffer) => { - parser.append(buffer); - - if (parser.isComplete) { - const response = parser.toResponse(); - if (this.callback) await this.callback(response); - parser.reset(); - } - }; - - this.socket = await Bun.connect({ - unix: "/var/run/docker.sock", - socket: { - error(_, error) { - throw error; - }, - connectError(_, error) { - throw error; - }, - data(_, data) { - appendOrResolve(data); - }, - end() { - console.log("Socket closed."); - }, - }, - }); - } - - /** Disconnect from the docker daemon. */ - public disconnect() { - if (!this.socket) throw new Error("Not connected to docker daemon."); - this.socket.end(); - this.socket = null; - } - - /** - * Send a request to the docker daemon. - * @param request The request to send. - * @param cb A callback that will be called when the response is received. - */ - public async send(request: RawRequest, cb?: ModemCB) { - if (!this.socket) throw new Error("Not connected to docker daemon."); - - this.callback = cb || null; - this.socket.write(request.toString()); - } -} diff --git a/src/git.ts b/src/git.ts new file mode 100644 index 0000000..926cb19 --- /dev/null +++ b/src/git.ts @@ -0,0 +1,77 @@ +// ============================================================================ +// Copyright (C) 2024 W2Wizard +// See README in the root of the project for license details. +// ============================================================================ + +import type { GitBody } from "./types"; +import Container from "./docker/container"; +import { parseLogBuffer } from "./docker/api"; +import type { ContainerPayload } from "./docker/container"; + +// ============================================================================ + +/** A namespace for the git container. */ +export namespace Git { + /** + * Create a payload for the git container. + * @param project The project to benchmark. + * @param body The body of the request. + * @returns The payload for the container. + */ + export function payload(project: string, body: GitBody): ContainerPayload { + return { + Image: "w2wizard/git", + Tty: false, + NetworkDisabled: false, // TODO: Build scripts may rely on this! + AttachStdin: false, + AttachStdout: false, + AttachStderr: false, + OpenStdin: false, + + // Thank you docker for wasting my time. + // See: https://github.com/moby/moby/issues/1905 + //StopTimeout: config.timeout, + //StopSignal: "SIGTERM", + HostConfig: { + AutoRemove: false, + Binds: [ + `${process.cwd()}/projects/${project}:/var/dev:ro`, // Set as read-only + ], + Memory: 50 * 1024 * 1024, // ~50MB + MemorySwap: -1, + Privileged: false, + CpusetCpus: "0", // only use one core + }, + Env: [ + `TIMEOUT=20s`, + `GIT_URL=${body.data.repo}`, + `GIT_BRANCH=${body.data.branch}`, + `GIT_COMMIT=${body.data.commit}`, + ...Object.entries(body.envs ?? []).map(([key, value]) => `${key}=${value}`), + ], + }; + } + + /** + * Run's the given payload in a container and returns the logs. + * @param project The project to benchmark. + * @param body The body of the request. + * @returns The logs from the container. + */ + export async function run(project: string, body: GitBody) { + const container = new Container(payload(project, body)); + await container.start(); + + const response = new Response( + // @ts-ignore // NOTE(W2): Bun supports this. Somehow still throws an error. + async function* stream() { + yield parseLogBuffer(await container.logs()); + } + ) + + await container.wait(); + return response; + } +} + +export default Git; diff --git a/src/http.ts b/src/http.ts deleted file mode 100644 index 205b044..0000000 --- a/src/http.ts +++ /dev/null @@ -1,129 +0,0 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= - -import { HTTPParser, HTTPParserJS } from "http-parser-js"; - -//============================================================================= - -/** - * A mirror class of the Request class but instead its used to build the string - * representation of the request. - */ -export class RawRequest { - public url: string; - public method?: string; - public headers?: Headers; - public body?: string; - - constructor(url: string, options: RequestInit) { - this.url = url; - this.method = options.method!; - this.headers = new Headers(options.headers); - if (options.body) { - this.body = options.body.toString(); - } - } - - /** Returns the string representation of the request. */ - public toString() { - let requestString = `${this.method} ${this.url} HTTP/1.1\r\n`; - - if (this.headers) - for (const [name, value] of this.headers.entries()) - requestString += `${name}: ${value}\r\n`; - - requestString += "\r\n"; - return this.body ? requestString + this.body : requestString; - } -} - -//============================================================================= - -/** - * Wrapper class for the HTTP response parser. - * - * @note Handles trailers as well but they are not exposed. - * @see http://npmjs.com/package/http-parser-js - */ -export class ResponseParser { - private parser: HTTPParserJS; - private complete: boolean; - private statusCode?: number; - private statusMessage?: string; - private headers: string[] = []; - private trailers: string[] = []; - private bodyChunks: Buffer[] = []; - - constructor() { - this.parser = new HTTPParser(HTTPParser.RESPONSE); - this.complete = false; - - const { parser } = this; - parser.onHeadersComplete = (res) => { - this.statusCode = res.statusCode; - this.statusMessage = res.statusMessage; - this.headers = res.headers; - }; - - parser.onBody = (chunk, offset, length) => { - this.bodyChunks.push(chunk.subarray(offset, offset + length)); - }; - - parser.onHeaders = (t) => { - this.trailers = t; - }; - - parser.onMessageComplete = () => { - this.complete = true; - }; - } - - //= Public =// - - /** Reset the parser to its initial state. */ - public reset(): void { - this.complete = false; - this.statusCode = undefined; - this.statusMessage = undefined; - this.headers = []; - this.trailers = []; - this.bodyChunks = []; - this.parser.reinitialize(HTTPParser.RESPONSE); - } - - /** Append a chunk of data to the parser. */ - public append(data: Buffer): void { - this.parser.execute(data); - this.parser.finish(); - } - - /** Returns true if the parser has received the entire response. */ - public get isComplete(): boolean { - return this.complete; - } - - /** Returns the parsed response. */ - public toResponse(): Response { - if (!this.complete) { - throw new Error("Could not parse"); - } - - const body = Buffer.concat(this.bodyChunks); - return new Response(body, { - status: this.statusCode, - statusText: this.statusMessage, - headers: this.toHeaders(this.headers), - }); - } - - //= Private =// - - private toHeaders(headers: string[]): Headers { - const result = new Headers(); - for (let i = 0; i < headers.length; i += 2) - result.append(headers[i], headers[i + 1]); - return result; - } -} diff --git a/src/logger.ts b/src/logger.ts deleted file mode 100644 index 395e633..0000000 --- a/src/logger.ts +++ /dev/null @@ -1,122 +0,0 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= - -import chalk from "chalk"; -import fs, { mkdirSync } from "fs"; - -export enum LogLevel { - DEBUG = "DEBUG", - INFO = "INFO", - WARN = "WARN", - FAIL = "FAIL", -} - -//============================================================================= - -/** A simple logger class that writes to a file and the console. */ -class Logger { - private logFilePath: string; - - constructor(logDirectoryPath: string) { - mkdirSync(logDirectoryPath, { recursive: true }); - const date = new Date() - .toLocaleString("en-GB", { - year: "numeric", - month: "2-digit", - day: "2-digit", - }) - .replace(",", "") - .replace(/\//g, "-"); - - this.logFilePath = `${logDirectoryPath}/log-${date}.log`; - } - - private getCurrentTimestamp(): string { - return new Date() - .toLocaleString("en-GB", { - year: "numeric", - month: "2-digit", - day: "2-digit", - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: false, - }) - .replace(",", ""); - } - - private format(level: LogLevel, message: string): string { - const emojis = { - [LogLevel.DEBUG]: "๐Ÿ›", - [LogLevel.INFO]: "๐Ÿ“", - [LogLevel.WARN]: "โš ๏ธ", - [LogLevel.FAIL]: "โŒ", - }; - - return `[${this.getCurrentTimestamp()}] [${level.toString()}] ${ - emojis[level] - } : ${message}`; - } - - private writeToConsole(message: string): void { - console.log(message); - } - - private writeToFile(message: string): void { - fs.appendFileSync(this.logFilePath, message + "\n"); - } - - //= Public =// - - /** Log a message to the console and the log file. */ - public write(level: LogLevel = LogLevel.INFO, ...args: unknown[]): void { - args = args.map((arg) => - typeof arg === "string" ? arg : JSON.stringify(arg) - ); - - let formattedMessage = this.format(level, args.join(" ")); - this.writeToFile(formattedMessage); - - switch (level) { - case LogLevel.DEBUG: - formattedMessage = chalk.bgMagenta(formattedMessage); - case LogLevel.INFO: - formattedMessage = chalk.whiteBright(formattedMessage); - break; - case LogLevel.WARN: - formattedMessage = chalk.yellow(formattedMessage); - break; - case LogLevel.FAIL: - formattedMessage = chalk.red.bold(formattedMessage); - break; - default: - break; - } - this.writeToConsole(formattedMessage); - } - - /** Log a message to the console and the log file. */ - public info(...args: unknown[]): void { - this.write(LogLevel.INFO, ...args); - } - - /** Log a warning to the console and the log file. */ - public warn(...args: unknown[]): void { - this.write(LogLevel.WARN, ...args); - } - - /** Log an error to the console and the log file. */ - public error(...args: unknown[]): void { - this.write(LogLevel.FAIL, ...args); - } - - /** Log a debug message to the console and the log file. */ - public debug(...args: unknown[]): void { - if (process.env.NODE_ENV !== "production") - this.write(LogLevel.DEBUG, ...args); - } -} - -export default Logger; diff --git a/src/main.ts b/src/main.ts index fae0cb2..b1402da 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,70 +1,7 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= +// ============================================================================ +// Copyright (C) 2024 W2Wizard +// See README in the root of the project for license details. +// ============================================================================ -import Logger from "./logger"; -import { Elysia } from "elysia"; -import Modem from "./docker/modem"; -import htmlPlugin from "@elysiajs/html"; -import staticPlugin from "@elysiajs/static"; -import registerGit from "./routes/git"; -import registerStats from "./routes/stats"; -import registerSingle from "./routes/single"; -import Container from "./docker/container"; - -export type Server = typeof server; - -//============================================================================= - -if (import.meta.main !== (import.meta.path === Bun.main)) - throw new Error("This module cannot be imported."); - -async function isDockerRunning() { - const modem = new Modem(); - try { - await modem.connect(); - modem.disconnect(); - } catch (error) { - console.error(error); - return false; - } -} - -// Entry point -//============================================================================= - -export const log = new Logger(`./logs`); -//if (!await isDockerRunning()) { -// log.error("Docker is not running!"); -// process.exit(1); -//} - -log.info("Starting server..."); -const server = new Elysia().use(htmlPlugin()).use( - staticPlugin({ - assets: "public/assets", - prefix: "assets", - }) -); - -export let containers: Map = new Map(); -[registerGit, registerSingle, registerStats].forEach((route) => route(server)); -server.listen(Number(Bun.env.PORT ?? 8000), ({ port }) => { - log.info(`Hosted: http://localhost:${port}/`); -}); - -process.on("SIGINT", async () => { - log.info(`Killing all ${containers.size} containers`); - - for (const [id, container] of containers) { - log.debug(`Killing container ${id}`); - try { - await container.kill(); - } catch (error) { - log.error(`Failed to kill container ${id}: ${error}`); - } - } - log.info("Shutting down..."); - server.stop(); -}); +import router from "./router"; +export default router; diff --git a/src/router.ts b/src/router.ts new file mode 100644 index 0000000..71f4392 --- /dev/null +++ b/src/router.ts @@ -0,0 +1,76 @@ +// ============================================================================ +// Copyright (C) 2024 W2Wizard +// See README in the root of the project for license details. +// ============================================================================ + +import Container from './docker/container' +import Git from './git' +import { AutoRouter, json, type IRequest, type ResponseHandler, error, status, StatusError } from 'itty-router' +import type { FileBody, GitBody } from './types' +import { $ } from 'bun' +import Single from './single' + +// Middleware +// ============================================================================ + +const withHeaders: ResponseHandler = (response: IRequest) => { + response.headers.set('X-Server', Bun.env.SERVER ?? 'robopeer') + response.headers.set('X-Powered-By', 'itty-router') + response.headers.set('X-Runtime', `Bun ${Bun.version}`) +} + +// ============================================================================ + +const router = AutoRouter({ + port: Bun.env.PORT, + finally: [withHeaders], + format: json, + catch: error, +}) + +// router.get('/', () => ({ message: 'Hello, from itty-router v5.' })) +// router.get('/hello', async () => { +// const { response, data, error } = await docker.GET("/containers/json") +// if (error) throw new Error(error.message) + +// return data +// }) + +router.post('/evaluate/git/:project', async (req) => { + let body: GitBody = await req.json() + .catch(() => { throw new StatusError(400, 'Invalid JSON body.') }) + .then((data) => data); + + if (!body.data.branch || !body.data.commit || !body.data.repo) { + throw new StatusError(400, 'Invalid JSON body.') + } + + if (!await Bun.file(`./projects/${req.params.project}/index.test.ts`).exists()) { + throw new StatusError(404, 'Project not found.') + } + + return await Git.run(req.params.project, body) +}) + +router.post('/evaluate/code', async (req) => { + let body: FileBody = await req.json() + .catch(() => { throw new StatusError(400, 'Invalid JSON body.') }) + .then((data) => data); + + if ( + !body.data.args || + !body.data.content || + !body.data.flags || + !body.data.lang + ) { + throw new StatusError(400, 'Invalid JSON body.') + } + + return await Single.run(body) +}) + +console.log(`Running: https://localhost:${Bun.env.PORT ?? 8080}`); + +// ============================================================================ + +export default router diff --git a/src/routes/git.ts b/src/routes/git.ts deleted file mode 100644 index 67fd925..0000000 --- a/src/routes/git.ts +++ /dev/null @@ -1,152 +0,0 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= - -import { accessSync, constants } from "fs"; -import { Server, containers, log } from "@/main"; -import Container, { ExitCode } from "@/docker/container"; - -interface Config { - enabled: boolean; - timeout: number; -} - -interface Body { - gitURL: string; - branch: string; - commit: string; -} - -//============================================================================= - -/** - * Construct a container to run the code in. - * @param project The project to run the code in. - * @param request The request to get the code from. - * @returns The container object. - */ -export function payload(project: string, request: Body, config: Config) { - return { - Image: "w2wizard/git_runner", - NetworkDisabled: false, - AttachStdin: false, - AttachStdout: false, - AttachStderr: false, - OpenStdin: false, - Privileged: false, - Tty: false, - // Thank you docker for wasting my time. - // See: https://github.com/moby/moby/issues/1905 - //StopTimeout: config.timeout, - //StopSignal: "SIGTERM", - HostConfig: { - AutoRemove: false, - Binds: [ - // TODO: Set as read-only. - `${process.cwd()}/projects/${project}:/app`, - ], - Memory: 50 * 1024 * 1024, // ~50MB - MemorySwap: -1, - Privileged: false, - CpusetCpus: "0", // only use one core - }, - Env: [ - `TIMEOUT=${config.timeout}`, - `GIT_URL=${request.gitURL}`, - `GIT_BRANCH=${request.branch}`, - `GIT_COMMIT=${request.commit}`, - ], - }; -} - -/** - * Launch a container to run the code in. - * @param project The project name to use as a comparison. - * @param request The incoming request. - * @returns Response with the stdout or stderr of the container. - */ -async function launchContainer(dir: string, project: string, request: Body) { - const config = (await Bun.file(`${dir}/config.json`).json()) as Config; - if (!config.enabled) { - log.warn("Project disabled:", project); - return new Response("Project is currently disabled.", { status: 410 }); - } - - const container = await new Container( - payload(project, request, config) - ).launch(); - - if (!container.id) throw new Error("Container not launched."); - containers.set(container.id, container); - - let response: Response; - const { exitCode, logs } = await container.wait(); - switch (exitCode as ExitCode) { - case ExitCode.Success: { - response = new Response(logs, { status: 200 }); - break; - } - case ExitCode.Killed: - case ExitCode.NotFound: - case ExitCode.MinorError: - case ExitCode.MajorError: { - response = new Response(logs, { status: 400 }); - break; - } - case ExitCode.Timeout: { - response = new Response(logs, { status: 408 }); - break; - } - case ExitCode.ScriptFail: - throw new Error(`Ooops! That's a buggy script:\n${logs}`); - default: - throw new Error(`Unkown code: ${exitCode}:\n${logs}`); - } - log.info("Container", container.id, "exited with:", exitCode); - containers.delete(container.id); - await container.remove(); - return response; -} - -//============================================================================= - -/** Register the routes for the /git endpoint. */ -export default function register(server: Server) { - log.debug("Registering /api/grade endpoint"); - - server.post("/api/grade/git/:name", async ({ params, request }) => { - let body: Body; - const project = params.name; - const dir = `./projects/${project}`; - const testFile = `${dir}/index.test.ts`; - log.info("Received request for:", project); - - try { - accessSync(testFile, constants.F_OK | constants.R_OK); - } catch (error) { - log.warn("Project not found:", project); - return new Response("Project not found.", { status: 404 }); - } - - try { - body = await request.json(); - if (!body.gitURL || !body.branch || !body.commit) { - log.warn("Missing parameters:", body); - return new Response("Missing parameters", { status: 400 }); - } - } catch (exception) { - log.warn("Invalid Body received"); - return new Response("Invalid JSON.", { status: 400 }); - } - - try { - log.info("Running tests for:", project, "=>", body); - return await launchContainer(dir, project, body); - } catch (exception) { - const error = exception as Error; - log.error(`Exception triggered: ${error.message}`); - return new Response(error.message, { status: 500 }); - } - }); -} diff --git a/src/routes/index.ts b/src/routes/index.ts deleted file mode 100644 index e9dcf63..0000000 --- a/src/routes/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= - -import Modem from "@/docker/modem"; -import index from "$/index"; -import { Server, log } from "@/main"; -import { Docker } from "@/docker/api"; - -//============================================================================= - -async function getContainerCount() { - log.debug("getContainerCount()"); - return await new Promise(async (resolve, reject) => { - const modem = new Modem(); - await modem.connect(); - - Docker.list(modem, async (response) => { - if (!response.ok) return reject(response.statusText); - - const body = await response.json() as []; - modem.disconnect(); - return resolve(body.length.toString()); - }); - }); -} - -//============================================================================= - -/** Register the routes for the /stats endpoint. */ -export default function register(server: Server) { - log.debug("Registering /index endpoint..."); - server.get("/", ({ html }) => index(html)); - server.get("/api/count", async () => await getContainerCount()); - server.post("/api/stop", () => { - log.info("Stopping server..."); - server.stop(); - return "Killed"; - }); -} diff --git a/src/routes/single.ts b/src/routes/single.ts deleted file mode 100644 index d79f799..0000000 --- a/src/routes/single.ts +++ /dev/null @@ -1,122 +0,0 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= - -import Container, { ExitCode } from "@/docker/container"; -import { Server, containers, log } from "@/main"; - -interface Body { - code: string; - flags: string; - args: string; // Semi-colon separated list of arguments. - timeout: number; - language: string; -} - -//============================================================================= - -/** - * Construct a container to run the code in. - * @param project The project to run the code in. - * @param request The request to get the code from. - * @returns The container object. - */ -export function payload(request: Body) { - return { - Image: "w2wizard/single_runner", - NetworkDisabled: true, - AttachStdin: false, - AttachStdout: false, - AttachStderr: false, - OpenStdin: false, - Privileged: false, - Tty: false, - HostConfig: { - AutoRemove: false, - Memory: 50 * 1024 * 1024, // ~50MB - MemorySwap: -1, - Privileged: false, - CpusetCpus: "0", // only use one core - }, - Env: [ - `TIMEOUT=${request.timeout}`, - `CODE_FLAGS=${request.flags}`, - `CODE_SOURCE=${request.code}`, - `CODE_LANGUAGE=${request.language}`, - `CODE_ARGV=${request.args}`, - `CODE_ARGC=${request.args.length}`, - ], - }; -} - -/** - * Launch a container to run the code in. - * @param project The project name to use as a comparison. - * @param request The incoming request. - * @returns Response with the stdout or stderr of the container. - */ -async function launchContainer(request: Body) { - const container = await new Container(payload(request)).launch(); - - if (!container.id) throw new Error("Container not launched."); - containers.set(container.id, container); - - let response: Response; - const { exitCode, logs } = await container.wait(); - switch (exitCode as ExitCode) { - case ExitCode.Success: { - response = new Response(logs, { status: 200 }); - break; - } - case ExitCode.Killed: - case ExitCode.NotFound: - case ExitCode.MinorError: - case ExitCode.MajorError: { - response = new Response(logs, { status: 400 }); - break; - } - case ExitCode.Timeout: { - response = new Response(logs, { status: 408 }); - break; - } - case ExitCode.ScriptFail: - throw new Error(`Ooops! That's a buggy script: ${logs}`); - default: - throw new Error(`Unkown code: ${exitCode}: ${logs}`); - } - log.info("Container", container.id, "exited with:", exitCode); - containers.delete(container.id); - await container.remove(); - return response; -} - -/** Register the routes for the /single endpoint. */ -export default function register(server: Server) { - log.debug("Registering /api/single endpoint"); - - server.post("/api/grade/single", async ({ params, request }) => { - let body: Body; - log.info("Received single code request"); - - try { - body = await request.json(); - if (!body.language || !body.timeout) { - log.warn("Missing parameters:", body); - return new Response("Missing parameters", { status: 400 }); - } - } catch (exception) { - log.warn("Invalid Body received"); - return new Response("Invalid JSON.", { status: 400 }); - } - - try { - log.info("Running single code test:", "=>", body); - return await launchContainer(body); - } catch (exception) { - const error = exception as Error; - log.error(`Exception triggered: ${error.message}`); - return new Response(error.message, { status: 500 }); - } - }); -} diff --git a/src/routes/stats.ts b/src/routes/stats.ts deleted file mode 100644 index 4a991e8..0000000 --- a/src/routes/stats.ts +++ /dev/null @@ -1,44 +0,0 @@ -//============================================================================= -// W2Wizard, Amsterdam @ 2018-2023 -// See README and LICENSE files for details. -//============================================================================= - -import index from "$/index"; -import Modem from "@/docker/modem"; -import { Server, log } from "@/main"; -import { Docker } from "@/docker/api"; - -//============================================================================= - -async function getContainerCount() { - log.debug("getContainerCount()"); - return await new Promise(async (resolve, reject) => { - const modem = new Modem(); - await modem.connect(); - - Docker.list(modem, async (response) => { - if (!response.ok) return reject(response.statusText); - - const body = await response.json() as []; - modem.disconnect(); - return resolve(body.length.toString()); - }); - }); -} - -//============================================================================= - -/** Register the routes for the /stats endpoint. */ -export default function register(server: Server) { - log.debug("Registering /api/count endpoint"); - log.debug("Registering /api/stop endpoint"); - log.debug("Registering /index endpoint..."); - - server.get("/", ({ html }) => index(html)); - server.get("/api/count", async () => await getContainerCount()); - server.post("/api/stop", () => { - log.info("Stopping server..."); - server.stop(); - return "Killed"; - }); -} diff --git a/src/single.ts b/src/single.ts new file mode 100644 index 0000000..659580e --- /dev/null +++ b/src/single.ts @@ -0,0 +1,86 @@ +// ============================================================================ +// Copyright (C) 2024 W2Wizard +// See README in the root of the project for license details. +// ============================================================================ + +import type { FileBody } from "./types"; +import Container from "./docker/container"; +import { parseLogBuffer } from "./docker/api"; +import type { ContainerPayload } from "./docker/container"; +import { StatusError } from "itty-router"; + +// ============================================================================ + +/** A namespace for the git container. */ +export namespace Single { + /** + * Create a payload for the git container. + * @param project The project to benchmark. + * @param body The body of the request. + * @returns The payload for the container. + */ + export function payload(body: FileBody): ContainerPayload { + return { + Image: "w2wizard/single", + NetworkDisabled: true, + AttachStdin: false, + AttachStdout: false, + AttachStderr: false, + OpenStdin: false, + Tty: false, + HostConfig: { + AutoRemove: false, + Memory: 50 * 1024 * 1024, // ~50MB + MemorySwap: -1, + Privileged: false, + CpusetCpus: "0", // only use one core + }, + Env: [ + `TIMEOUT=20s`, + `CODE_FLAGS=${body.data.flags}`, + `CODE_SOURCE=${body.data.content}`, + `CODE_LANGUAGE=${body.data.lang}`, + `CODE_ARGV=${body.data.args}`, + `CODE_ARGC=${body.data.args.length}`, + ...Object.entries(body.envs ?? []).map(([key, value]) => `${key}=${value}`), + ], + }; + } + + /** + * Run's the given payload in a container and returns the logs. + * @param project The project to benchmark. + * @param body The body of the request. + * @returns The logs from the container. + */ + export async function run(body: FileBody) { + const container = new Container(payload(body)); + await container.start(); + + const response = new Response( + // @ts-ignore // NOTE(W2): Bun supports this. Somehow still throws an error. + async function* stream() { + yield parseLogBuffer(await container.logs()); + } + ) + + // Handle container exit codes. + switch (await container.wait()) { + case 1: + case 2: + case 127: + return new Response(response.body, { + status: 422 + }); + case 128: + throw new StatusError(500, "Robopeer is buggy. Please report this issue."); + case 124: // SIGALRM + throw new StatusError(408, "Program timed out."); + case 137: // SIGKILL + throw new StatusError(422, "Program killed due to memory limit. Are you doing something nasty?"); + } + return response; + } +} + +export default Single; diff --git a/src/types.d.ts b/src/types.d.ts new file mode 100644 index 0000000..5a780c4 --- /dev/null +++ b/src/types.d.ts @@ -0,0 +1,38 @@ +//============================================================================= +// W2Wizard, Amsterdam @ 2024 +// See README and LICENSE files for details. +//============================================================================= + +/** Body in regards to a git project test / execution. */ +export interface GitData { + /** The repository URL */ + repo: string; + /** The branch */ + branch: string; + /** The commit to be tested */ + commit: string; +} + +/** Body in regards to a single file test / execution */ +export interface FileData { + /** The language of the program */ + lang: "c" | "cpp"; + /** The content of the file */ + content: string; + /** Flags to be passed to the compiler */ + flags: string[]; + /** Arguments to be passed to the program */ + args: string[]; +} + +export interface GitBody extends RequestBody { + data: GitData; + envs: Record?; +} + +export interface FileBody extends RequestBody { + data: FileData; + envs: Record?; +} + +//============================================================================= diff --git a/tsconfig.json b/tsconfig.json index 38867cc..4450763 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,28 @@ { "compilerOptions": { - "lib": ["ESNext"], - "module": "esnext", - "target": "esnext", - "moduleResolution": "bundler", + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices "strict": true, - "downlevelIteration": true, "skipLibCheck": true, - "jsx": "react", - "jsxFactory": "elements.createElement", - "allowSyntheticDefaultImports": true, - "forceConsistentCasingInFileNames": true, - "allowJs": true, - "noEmit": true, - "types": [ - "bun-types" // add Bun global - ], - "paths": { - "@/*": ["./src/*"], - "$/*": ["./public/*"] - } + "noFallthroughCasesInSwitch": true, + "resolvePackageJsonImports": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false } }