From 0e536e9913681eac314477948b871d3846010980 Mon Sep 17 00:00:00 2001 From: Sampo Silvennoinen <20028934+stscoundrel@users.noreply.github.com> Date: Sun, 12 Jan 2025 19:31:07 +0200 Subject: [PATCH] Add MinIO module (#893) --- docs/modules/minio.md | 21 ++ mkdocs.yml | 1 + package-lock.json | 235 +++++++++++++++++- packages/modules/minio/jest.config.ts | 11 + packages/modules/minio/package.json | 37 +++ packages/modules/minio/src/dummy-file.txt | 1 + packages/modules/minio/src/index.ts | 1 + .../modules/minio/src/minio-container.test.ts | 60 +++++ packages/modules/minio/src/minio-container.ts | 65 +++++ packages/modules/minio/tsconfig.build.json | 14 ++ packages/modules/minio/tsconfig.json | 21 ++ 11 files changed, 457 insertions(+), 10 deletions(-) create mode 100644 docs/modules/minio.md create mode 100644 packages/modules/minio/jest.config.ts create mode 100644 packages/modules/minio/package.json create mode 100644 packages/modules/minio/src/dummy-file.txt create mode 100644 packages/modules/minio/src/index.ts create mode 100644 packages/modules/minio/src/minio-container.test.ts create mode 100644 packages/modules/minio/src/minio-container.ts create mode 100644 packages/modules/minio/tsconfig.build.json create mode 100644 packages/modules/minio/tsconfig.json diff --git a/docs/modules/minio.md b/docs/modules/minio.md new file mode 100644 index 000000000..69baab5a9 --- /dev/null +++ b/docs/modules/minio.md @@ -0,0 +1,21 @@ +# MinIO Module + +[MinIO](https://min.io/) is a high performance object storage solution. It is API compatible with the Amazon S3 cloud storage service and can handle unstructured data such as photos, videos, log files, backups, and container images + + + +## Install + +```bash +npm install @testcontainers/minio --save-dev +``` + +## Examples + + +[Connect with default credentials:](../../packages/modules/minio/src/minio-container.test.ts) inside_block:connectWithDefaultCredentials + + + +[Connect with custom credentials:](../../packages/modules/minio/src/minio-container.test.ts) inside_block:connectWithCustomCredentials + diff --git a/mkdocs.yml b/mkdocs.yml index 1d5481a23..8f0ca6e79 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -55,6 +55,7 @@ nav: - Localstack: modules/localstack.md - MariaDB: modules/mariadb.md - Mockserver: modules/mockserver.md + - MinIO: modules/minio.md - MongoDB: modules/mongodb.md - MSSQLServer: modules/mssqlserver.md - MySQL: modules/mysql.md diff --git a/package-lock.json b/package-lock.json index 7ca24abf8..2d51f9db5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5756,6 +5756,10 @@ "resolved": "packages/modules/mariadb", "link": true }, + "node_modules/@testcontainers/minio": { + "resolved": "packages/modules/minio", + "link": true + }, "node_modules/@testcontainers/mockserver": { "resolved": "packages/modules/mockserver", "link": true @@ -6712,6 +6716,13 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@zxing/text-encoding": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "dev": true, + "optional": true + }, "node_modules/abbrev": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", @@ -7662,6 +7673,15 @@ "readable-stream": "^3.4.0" } }, + "node_modules/block-stream2": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz", + "integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==", + "dev": true, + "dependencies": { + "readable-stream": "^3.4.0" + } + }, "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", @@ -9039,6 +9059,15 @@ "node": "*" } }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -10274,6 +10303,15 @@ "node": ">=8" } }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -12410,6 +12448,15 @@ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/is": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", @@ -12602,6 +12649,24 @@ "node": ">=6" } }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -12725,13 +12790,15 @@ "dev": true }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -14948,6 +15015,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minio": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/minio/-/minio-8.0.3.tgz", + "integrity": "sha512-+FIYQ+HZ5GrBjEmIYienRgEikqaTWAflXIV5lJOtUzfYxn3NvjQx7BsJSORXExlqgzWxKTWsqkyk2wiyFjs9/w==", + "dev": true, + "dependencies": { + "async": "^3.2.4", + "block-stream2": "^2.1.0", + "browser-or-node": "^2.1.1", + "buffer-crc32": "^1.0.0", + "eventemitter3": "^5.0.1", + "fast-xml-parser": "^4.4.1", + "ipaddr.js": "^2.0.1", + "lodash": "^4.17.21", + "mime-types": "^2.1.35", + "query-string": "^7.1.3", + "stream-json": "^1.8.0", + "through2": "^4.0.2", + "web-encoding": "^1.1.5", + "xml2js": "^0.5.0 || ^0.6.2" + }, + "engines": { + "node": "^16 || ^18 || >=20" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -17671,6 +17763,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "dev": true, + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -18252,14 +18362,14 @@ ] }, "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -18273,6 +18383,12 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true + }, "node_modules/secure-json-parse": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", @@ -18791,6 +18907,15 @@ "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==" }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -18924,6 +19049,12 @@ "node": ">= 0.10.0" } }, + "node_modules/stream-chain": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", + "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", + "dev": true + }, "node_modules/stream-events": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", @@ -18933,6 +19064,15 @@ "stubs": "^3.0.0" } }, + "node_modules/stream-json": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz", + "integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==", + "dev": true, + "dependencies": { + "stream-chain": "^2.2.5" + } + }, "node_modules/stream-shift": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.2.tgz", @@ -18955,6 +19095,15 @@ "dev": true, "license": "MIT" }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -19546,6 +19695,15 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "dependencies": { + "readable-stream": "3" + } + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -20206,6 +20364,19 @@ "node": ">=6.14.2" } }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -20310,6 +20481,18 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/web-encoding": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", + "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", + "dev": true, + "dependencies": { + "util": "^0.12.3" + }, + "optionalDependencies": { + "@zxing/text-encoding": "0.9.0" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -20670,6 +20853,28 @@ "xtend": "^4.0.0" } }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -21023,6 +21228,16 @@ "mariadb": "^3.4.0" } }, + "packages/modules/minio": { + "version": "10.16.0", + "license": "MIT", + "dependencies": { + "testcontainers": "^10.16.0" + }, + "devDependencies": { + "minio": "^8.0.3" + } + }, "packages/modules/mockserver": { "name": "@testcontainers/mockserver", "version": "10.16.0", diff --git a/packages/modules/minio/jest.config.ts b/packages/modules/minio/jest.config.ts new file mode 100644 index 000000000..1f677baaf --- /dev/null +++ b/packages/modules/minio/jest.config.ts @@ -0,0 +1,11 @@ +import type { Config } from "jest"; +import * as path from "path"; + +const config: Config = { + preset: "ts-jest", + moduleNameMapper: { + "^testcontainers$": path.resolve(__dirname, "../../testcontainers/src"), + }, +}; + +export default config; diff --git a/packages/modules/minio/package.json b/packages/modules/minio/package.json new file mode 100644 index 000000000..889d33a29 --- /dev/null +++ b/packages/modules/minio/package.json @@ -0,0 +1,37 @@ +{ + "name": "@testcontainers/minio", + "version": "10.16.0", + "license": "MIT", + "keywords": [ + "minio", + "testing", + "docker", + "testcontainers" + ], + "description": "MinIO module for Testcontainers", + "homepage": "https://github.com/testcontainers/testcontainers-node#readme", + "repository": { + "type": "git", + "url": "https://github.com/testcontainers/testcontainers-node" + }, + "bugs": { + "url": "https://github.com/testcontainers/testcontainers-node/issues" + }, + "main": "build/index.js", + "files": [ + "build" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "prepack": "shx cp ../../../README.md . && shx cp ../../../LICENSE .", + "build": "tsc --project tsconfig.build.json" + }, + "dependencies": { + "testcontainers": "^10.16.0" + }, + "devDependencies": { + "minio": "^8.0.3" + } +} diff --git a/packages/modules/minio/src/dummy-file.txt b/packages/modules/minio/src/dummy-file.txt new file mode 100644 index 000000000..3ffe13981 --- /dev/null +++ b/packages/modules/minio/src/dummy-file.txt @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet. \ No newline at end of file diff --git a/packages/modules/minio/src/index.ts b/packages/modules/minio/src/index.ts new file mode 100644 index 000000000..6eba16014 --- /dev/null +++ b/packages/modules/minio/src/index.ts @@ -0,0 +1 @@ +export { MinioContainer, StartedMinioContainer } from "./minio-container"; diff --git a/packages/modules/minio/src/minio-container.test.ts b/packages/modules/minio/src/minio-container.test.ts new file mode 100644 index 000000000..8dde3e0d4 --- /dev/null +++ b/packages/modules/minio/src/minio-container.test.ts @@ -0,0 +1,60 @@ +import * as Minio from "minio"; +import { MinioContainer } from "./minio-container"; + +describe("MinIO", () => { + jest.setTimeout(240_000); + + // connectWithDefaultCredentials { + it("should connect and upload a file", async () => { + const container = await new MinioContainer().start(); + + const minioClient = new Minio.Client({ + endPoint: container.getHost(), + port: container.getPort(), + useSSL: false, + accessKey: "minioadmin", + secretKey: "minioadmin", + }); + + // Upload dummy test file. + const testFile = `${__dirname}/dummy-file.txt`; + + await minioClient.makeBucket("test-bucket"); + await minioClient.fPutObject("test-bucket", "minio-test-file.txt", testFile); + + // Verify upload + const objectExists = await minioClient + .statObject("test-bucket", "minio-test-file.txt") + .then(() => true) + .catch(() => false); + + expect(objectExists).toBeTruthy(); + + await container.stop(); + }); + // } + + // connectWithCustomCredentials { + it("should work with custom credentials", async () => { + const container = await new MinioContainer().withUsername("AzureDiamond").withPassword("hunter2!").start(); + + const minioClient = new Minio.Client({ + endPoint: container.getHost(), + port: container.getPort(), + useSSL: false, + accessKey: "AzureDiamond", + secretKey: "hunter2!", + }); + + // Create a bucket. + await minioClient.makeBucket("test-bucket"); + + // Verify bucket. + const bucketExits = await minioClient.bucketExists("test-bucket"); + + expect(bucketExits).toBeTruthy(); + + await container.stop(); + }); + // } +}); diff --git a/packages/modules/minio/src/minio-container.ts b/packages/modules/minio/src/minio-container.ts new file mode 100644 index 000000000..dd8a837e1 --- /dev/null +++ b/packages/modules/minio/src/minio-container.ts @@ -0,0 +1,65 @@ +import { AbstractStartedContainer, GenericContainer, Wait, type StartedTestContainer } from "testcontainers"; + +const MINIO_PORT = 9000; +const MINIO_UI_PORT = 9001; + +export class MinioContainer extends GenericContainer { + private username = "minioadmin"; + private password = "minioadmin"; + + constructor(image = "minio/minio:RELEASE.2024-12-13T22-19-12Z") { + super(image); + this.withExposedPorts(MINIO_PORT, MINIO_UI_PORT); + this.withWaitStrategy(Wait.forAll([Wait.forHttp("/minio/health/live", MINIO_PORT)])); + this.withCommand(["server", "--console-address", `:${MINIO_UI_PORT}`, "/data"]); + } + + public withUsername(username: string): this { + this.username = username; + return this; + } + + public withPassword(password: string): this { + this.password = password; + return this; + } + + public override async start(): Promise { + this.withEnvironment({ + MINIO_ROOT_USER: this.username, + MINIO_ROOT_PASSWORD: this.password, + }); + const startedContainer = await super.start(); + return new StartedMinioContainer(startedContainer, this.username, this.password); + } +} + +export class StartedMinioContainer extends AbstractStartedContainer { + constructor( + startedTestContainer: StartedTestContainer, + private readonly username: string, + private readonly password: string + ) { + super(startedTestContainer); + } + + public getPort(): number { + return this.startedTestContainer.getMappedPort(MINIO_PORT); + } + + public getUiPort(): number { + return this.startedTestContainer.getMappedPort(MINIO_UI_PORT); + } + + public getUsername(): string { + return this.username; + } + + public getPassword(): string { + return this.password; + } + + public getConnectionUrl(): string { + return `http://${this.getHost()}:${this.getPort()}`; + } +} diff --git a/packages/modules/minio/tsconfig.build.json b/packages/modules/minio/tsconfig.build.json new file mode 100644 index 000000000..6b7778712 --- /dev/null +++ b/packages/modules/minio/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "build", + "jest.config.ts", + "src/**/*.test.ts", + "src/dummy-file.txt" + ], + "references": [ + { + "path": "../../testcontainers" + } + ] +} \ No newline at end of file diff --git a/packages/modules/minio/tsconfig.json b/packages/modules/minio/tsconfig.json new file mode 100644 index 000000000..39b165817 --- /dev/null +++ b/packages/modules/minio/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build", + "paths": { + "testcontainers": [ + "../../testcontainers/src" + ] + } + }, + "exclude": [ + "build", + "jest.config.ts" + ], + "references": [ + { + "path": "../../testcontainers" + } + ] +} \ No newline at end of file