Compare commits

..

4 Commits

20 changed files with 467 additions and 258 deletions

View File

@@ -6,8 +6,8 @@ on:
- '**' - '**'
env: env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci IMAGE: code.foss.global/host.today/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}} NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}} NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}} NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
@@ -26,7 +26,7 @@ jobs:
- name: Install pnpm and npmci - name: Install pnpm and npmci
run: | run: |
pnpm install -g pnpm pnpm install -g pnpm
pnpm install -g @shipzone/npmci pnpm install -g @ship.zone/npmci
- name: Run npm prepare - name: Run npm prepare
run: npmci npm prepare run: npmci npm prepare

View File

@@ -6,8 +6,8 @@ on:
- '*' - '*'
env: env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci IMAGE: code.foss.global/host.today/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}} NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}} NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}} NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
@@ -26,7 +26,7 @@ jobs:
- name: Prepare - name: Prepare
run: | run: |
pnpm install -g pnpm pnpm install -g pnpm
pnpm install -g @shipzone/npmci pnpm install -g @ship.zone/npmci
npmci npm prepare npmci npm prepare
- name: Audit production dependencies - name: Audit production dependencies
@@ -54,7 +54,7 @@ jobs:
- name: Prepare - name: Prepare
run: | run: |
pnpm install -g pnpm pnpm install -g pnpm
pnpm install -g @shipzone/npmci pnpm install -g @ship.zone/npmci
npmci npm prepare npmci npm prepare
- name: Test stable - name: Test stable
@@ -82,7 +82,7 @@ jobs:
- name: Prepare - name: Prepare
run: | run: |
pnpm install -g pnpm pnpm install -g pnpm
pnpm install -g @shipzone/npmci pnpm install -g @ship.zone/npmci
npmci npm prepare npmci npm prepare
- name: Release - name: Release
@@ -104,7 +104,7 @@ jobs:
- name: Prepare - name: Prepare
run: | run: |
pnpm install -g pnpm pnpm install -g pnpm
pnpm install -g @shipzone/npmci pnpm install -g @ship.zone/npmci
npmci npm prepare npmci npm prepare
- name: Code quality - name: Code quality

7
.gitignore vendored
View File

@@ -3,7 +3,6 @@
# artifacts # artifacts
coverage/ coverage/
public/ public/
pages/
# installs # installs
node_modules/ node_modules/
@@ -17,4 +16,8 @@ node_modules/
dist/ dist/
dist_*/ dist_*/
# custom # AI
.claude/
.serena/
#------# custom

View File

@@ -1,6 +1,27 @@
# Changelog # Changelog
## 2025-08-19 - 1.3.5 - fix(core)
Stabilize CI/workflows and runtime: update CI images/metadata, improve streaming requests and image handling, and fix tests & package metadata
- Update CI workflows and images: switch workflow IMAGE to code.foss.global/host.today/ht-docker-node:npmci, fix NPMCI_COMPUTED_REPOURL placeholders, and replace @shipzone/npmci with @ship.zone/npmci in workflows
- Update npmextra.json gitzone metadata (githost -> code.foss.global, gitscope -> apiclient.xyz, npmPackagename -> @apiclient.xyz/docker) and npmdocker.baseImage -> host.today/ht-docker-node:npmci
- Adjust package.json repository/bugs/homepage to code.foss.global, add pnpm overrides entry and normalize package metadata
- Improve DockerHost streaming and request handling: reduce requestStreaming timeout to 30s, enable autoDrain for streaming requests, improve response parsing for streaming vs JSON endpoints to avoid hangs
- Enhance DockerImage and DockerImageStore stream handling and tar processing: more robust import/export parsing, safer stream-to-file writes, repackaging steps, and error handling
- Unskip and update tests: re-enable DockerImageStore integration test, change stored image name to 'hello2', add formatting fixes and ensure cleanup stops the test DockerHost
- Miscellaneous code and docs cleanup: numerous formatting fixes and trailing-comma normalization across README and TS sources, update commitinfo and logger newline fixes, and add local tool ignores (.claude/.serena) to .gitignore
## 2025-08-19 - 1.3.4 - fix(test)
Increase test timeout, enable DockerImageStore test, update test image name, bump smartrequest patch, and add local claude settings
- Increase tstest timeout from 120s to 600s in package.json to accommodate longer-running integration tests.
- Unskip the DockerImageStore integration test and change stored image name from 'hello' to 'hello2' in test/test.nonci.node.ts.
- Bump dependency @push.rocks/smartrequest from ^4.3.0 to ^4.3.1.
- Add .claude/settings.local.json to allow local agent permissions for running tests and related tooling.
## 2025-08-19 - 1.3.3 - fix(classes.host) ## 2025-08-19 - 1.3.3 - fix(classes.host)
Adjust requestStreaming timeout and autoDrain; stabilize tests Adjust requestStreaming timeout and autoDrain; stabilize tests
- Reduced requestStreaming timeout from 10 minutes to 30 seconds to avoid long-running hanging requests. - Reduced requestStreaming timeout from 10 minutes to 30 seconds to avoid long-running hanging requests.
@@ -9,6 +30,7 @@ Adjust requestStreaming timeout and autoDrain; stabilize tests
- Added local tool settings file (.claude/settings.local.json) with local permissions (development-only). - Added local tool settings file (.claude/settings.local.json) with local permissions (development-only).
## 2025-08-18 - 1.3.2 - fix(package.json) ## 2025-08-18 - 1.3.2 - fix(package.json)
Fix test script timeout typo, update dependency versions, and add typings & project configs Fix test script timeout typo, update dependency versions, and add typings & project configs
- Fix test script: correct 'tineout' -> 'timeout' for npm test command and set timeout to 120s - Fix test script: correct 'tineout' -> 'timeout' for npm test command and set timeout to 120s
@@ -18,6 +40,7 @@ Fix test script timeout typo, update dependency versions, and add typings & proj
- Include generated cache/metadata files (typescript document symbols cache) — not source changes but tooling/cache artifacts - Include generated cache/metadata files (typescript document symbols cache) — not source changes but tooling/cache artifacts
## 2025-08-18 - 1.3.1 - fix(test) ## 2025-08-18 - 1.3.1 - fix(test)
Update test setup and devDependencies; adjust test import and add package metadata Update test setup and devDependencies; adjust test import and add package metadata
- Update test script to run with additional flags: --verbose, --logfile and --tineout 120 - Update test script to run with additional flags: --verbose, --logfile and --tineout 120
@@ -27,26 +50,29 @@ Update test setup and devDependencies; adjust test import and add package metada
- Add packageManager field for pnpm@10.14.0 with integrity hash - Add packageManager field for pnpm@10.14.0 with integrity hash
## 2024-12-23 - 1.3.0 - feat(core) ## 2024-12-23 - 1.3.0 - feat(core)
Initial release of Docker client with TypeScript support Initial release of Docker client with TypeScript support
- Provides easy communication with Docker's remote API from Node.js - Provides easy communication with Docker's remote API from Node.js
- Includes implementations for managing Docker services, networks, secrets, containers, and images - Includes implementations for managing Docker services, networks, secrets, containers, and images
## 2024-12-23 - 1.2.8 - fix(core) ## 2024-12-23 - 1.2.8 - fix(core)
Improved the image creation process from tar stream in DockerImage class. Improved the image creation process from tar stream in DockerImage class.
- Enhanced `DockerImage.createFromTarStream` method to handle streamed response and parse imported image details. - Enhanced `DockerImage.createFromTarStream` method to handle streamed response and parse imported image details.
- Fixed the dependency version for `@push.rocks/smartarchive` in package.json. - Fixed the dependency version for `@push.rocks/smartarchive` in package.json.
## 2024-10-13 - 1.2.7 - fix(core) ## 2024-10-13 - 1.2.7 - fix(core)
Prepare patch release with minor fixes and improvements Prepare patch release with minor fixes and improvements
## 2024-10-13 - 1.2.6 - fix(core) ## 2024-10-13 - 1.2.6 - fix(core)
Minor refactoring and code quality improvements. Minor refactoring and code quality improvements.
## 2024-10-13 - 1.2.5 - fix(dependencies) ## 2024-10-13 - 1.2.5 - fix(dependencies)
Update dependencies for stability improvements Update dependencies for stability improvements
- Updated @push.rocks/smartstream to version ^3.0.46 - Updated @push.rocks/smartstream to version ^3.0.46
@@ -54,129 +80,152 @@ Update dependencies for stability improvements
- Updated @types/node to version 22.7.5 - Updated @types/node to version 22.7.5
## 2024-10-13 - 1.2.4 - fix(core) ## 2024-10-13 - 1.2.4 - fix(core)
Refactored DockerImageStore constructor to remove DockerHost dependency Refactored DockerImageStore constructor to remove DockerHost dependency
- Adjusted DockerImageStore constructor to remove dependency on DockerHost - Adjusted DockerImageStore constructor to remove dependency on DockerHost
- Updated ts/classes.host.ts to align with DockerImageStore's new constructor signature - Updated ts/classes.host.ts to align with DockerImageStore's new constructor signature
## 2024-08-21 - 1.2.3 - fix(dependencies) ## 2024-08-21 - 1.2.3 - fix(dependencies)
Update dependencies to the latest versions and fix image export test Update dependencies to the latest versions and fix image export test
- Updated several dependencies to their latest versions in package.json. - Updated several dependencies to their latest versions in package.json.
- Enabled the previously skipped 'should export images' test. - Enabled the previously skipped 'should export images' test.
## 2024-06-10 - 1.2.1-1.2.2 - Core/General ## 2024-06-10 - 1.2.1-1.2.2 - Core/General
General updates and fixes. General updates and fixes.
- Fix core update - Fix core update
## 2024-06-10 - 1.2.0 - Core ## 2024-06-10 - 1.2.0 - Core
Core updates and bug fixes. Core updates and bug fixes.
- Fix core update - Fix core update
## 2024-06-08 - 1.2.0 - General/Core ## 2024-06-08 - 1.2.0 - General/Core
Major release with core enhancements. Major release with core enhancements.
- Processing images with extraction, retagging, repackaging, and long-term storage - Processing images with extraction, retagging, repackaging, and long-term storage
## 2024-06-06 - 1.1.4 - General/Imagestore ## 2024-06-06 - 1.1.4 - General/Imagestore
Significant feature addition. Significant feature addition.
- Add feature to process images with extraction, retagging, repackaging, and long-term storage - Add feature to process images with extraction, retagging, repackaging, and long-term storage
## 2024-05-08 - 1.0.112 - Images ## 2024-05-08 - 1.0.112 - Images
Add new functionality for image handling. Add new functionality for image handling.
- Can now import and export images - Can now import and export images
- Start work on local 100% JS OCI image registry - Start work on local 100% JS OCI image registry
## 2024-06-05 - 1.1.0-1.1.3 - Core ## 2024-06-05 - 1.1.0-1.1.3 - Core
Regular updates and fixes. Regular updates and fixes.
- Fix core update - Fix core update
## 2024-02-02 - 1.0.105-1.0.110 - Core ## 2024-02-02 - 1.0.105-1.0.110 - Core
Routine core updates and fixes. Routine core updates and fixes.
- Fix core update - Fix core update
## 2022-10-17 - 1.0.103-1.0.104 - Core ## 2022-10-17 - 1.0.103-1.0.104 - Core
Routine core updates. Routine core updates.
- Fix core update - Fix core update
## 2020-10-01 - 1.0.99-1.0.102 - Core ## 2020-10-01 - 1.0.99-1.0.102 - Core
Routine core updates. Routine core updates.
- Fix core update - Fix core update
## 2019-09-22 - 1.0.73-1.0.78 - Core ## 2019-09-22 - 1.0.73-1.0.78 - Core
Routine updates and core fixes. Routine updates and core fixes.
- Fix core update - Fix core update
## 2019-09-13 - 1.0.60-1.0.72 - Core ## 2019-09-13 - 1.0.60-1.0.72 - Core
Routine updates and core fixes. Routine updates and core fixes.
- Fix core update - Fix core update
## 2019-08-16 - 1.0.43-1.0.59 - Core ## 2019-08-16 - 1.0.43-1.0.59 - Core
Routine updates and core fixes. Routine updates and core fixes.
- Fix core update - Fix core update
## 2019-08-15 - 1.0.37-1.0.42 - Core ## 2019-08-15 - 1.0.37-1.0.42 - Core
Routine updates and core fixes. Routine updates and core fixes.
- Fix core update - Fix core update
## 2019-08-14 - 1.0.31-1.0.36 - Core ## 2019-08-14 - 1.0.31-1.0.36 - Core
Routine updates and core fixes. Routine updates and core fixes.
- Fix core update - Fix core update
## 2019-01-10 - 1.0.27-1.0.30 - Core ## 2019-01-10 - 1.0.27-1.0.30 - Core
Routine updates and core fixes. Routine updates and core fixes.
- Fix core update - Fix core update
## 2018-07-16 - 1.0.23-1.0.24 - Core ## 2018-07-16 - 1.0.23-1.0.24 - Core
Routine updates and core fixes. Routine updates and core fixes.
- Fix core shift to new style - Fix core shift to new style
## 2017-07-16 - 1.0.20-1.0.22 - General ## 2017-07-16 - 1.0.20-1.0.22 - General
Routine updates and fixes. Routine updates and fixes.
- Update node_modules within npmdocker - Update node_modules within npmdocker
## 2017-04-02 - 1.0.18-1.0.19 - General ## 2017-04-02 - 1.0.18-1.0.19 - General
Routine updates and fixes. Routine updates and fixes.
- Work with npmdocker and npmts 7.x.x - Work with npmdocker and npmts 7.x.x
- CI updates - CI updates
## 2016-07-31 - 1.0.17 - General ## 2016-07-31 - 1.0.17 - General
Enhancements and fixes. Enhancements and fixes.
- Now waiting for response to be stored before ending streaming request - Now waiting for response to be stored before ending streaming request
- Cosmetic fix - Cosmetic fix
## 2016-07-29 - 1.0.14-1.0.16 - General ## 2016-07-29 - 1.0.14-1.0.16 - General
Multiple updates and features added. Multiple updates and features added.
- Fix request for change observable and add npmdocker - Fix request for change observable and add npmdocker
- Add request typings - Add request typings
## 2016-07-28 - 1.0.13 - Core ## 2016-07-28 - 1.0.13 - Core
Fixes and preparations. Fixes and preparations.
- Fixed request for newer docker - Fixed request for newer docker
- Prepare for npmdocker - Prepare for npmdocker
## 2016-06-16 - 1.0.0-1.0.2 - General ## 2016-06-16 - 1.0.0-1.0.2 - General
Initial sequence of releases, significant feature additions and CI setups. Initial sequence of releases, significant feature additions and CI setups.
- Implement container start and stop - Implement container start and stop
@@ -184,7 +233,7 @@ Initial sequence of releases, significant feature additions and CI setups.
- Add tests with in docker environment - Add tests with in docker environment
## 2016-04-12 - unknown - Initial Commit ## 2016-04-12 - unknown - Initial Commit
Initial project setup. Initial project setup.
- Initial commit - Initial commit

View File

@@ -1,6 +1,6 @@
{ {
"npmdocker": { "npmdocker": {
"baseImage": "hosttoday/ht-docker-node:npmci", "baseImage": "host.today/ht-docker-node:npmci",
"command": "(ls -a && rm -r node_modules && yarn global add npmts && yarn install && npmts)", "command": "(ls -a && rm -r node_modules && yarn global add npmts && yarn install && npmts)",
"dockerSock": true "dockerSock": true
}, },
@@ -12,11 +12,11 @@
"gitzone": { "gitzone": {
"projectType": "npm", "projectType": "npm",
"module": { "module": {
"githost": "gitlab.com", "githost": "code.foss.global",
"gitscope": "mojoio", "gitscope": "apiclient.xyz",
"gitrepo": "docker", "gitrepo": "docker",
"description": "Provides easy communication with Docker remote API from Node.js, with TypeScript support.", "description": "Provides easy communication with Docker remote API from Node.js, with TypeScript support.",
"npmPackagename": "@mojoio/docker", "npmPackagename": "@apiclient.xyz/docker",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [
"Docker", "Docker",

View File

@@ -1,19 +1,19 @@
{ {
"name": "@apiclient.xyz/docker", "name": "@apiclient.xyz/docker",
"version": "1.3.3", "version": "1.3.5",
"description": "Provides easy communication with Docker remote API from Node.js, with TypeScript support.", "description": "Provides easy communication with Docker remote API from Node.js, with TypeScript support.",
"private": false, "private": false,
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts", "typings": "dist_ts/index.d.ts",
"type": "module", "type": "module",
"scripts": { "scripts": {
"test": "(tstest test/ --verbose --logfile --timeout 120)", "test": "(tstest test/ --verbose --logfile --timeout 600)",
"build": "(tsbuild --web --allowimplicitany)", "build": "(tsbuild --web --allowimplicitany)",
"buildDocs": "tsdoc" "buildDocs": "tsdoc"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://gitlab.com/mojoio/docker.git" "url": "https://code.foss.global/apiclient.xyz/docker.git"
}, },
"keywords": [ "keywords": [
"Docker", "Docker",
@@ -29,9 +29,9 @@
"author": "Lossless GmbH", "author": "Lossless GmbH",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://gitlab.com/mojoio/docker/issues" "url": "https://code.foss.global/apiclient.xyz/docker/issues"
}, },
"homepage": "https://gitlab.com/mojoio/docker#readme", "homepage": "https://code.foss.global/apiclient.xyz/docker#readme",
"dependencies": { "dependencies": {
"@push.rocks/lik": "^6.2.2", "@push.rocks/lik": "^6.2.2",
"@push.rocks/smartarchive": "^4.2.2", "@push.rocks/smartarchive": "^4.2.2",
@@ -42,7 +42,7 @@
"@push.rocks/smartnetwork": "^4.1.2", "@push.rocks/smartnetwork": "^4.1.2",
"@push.rocks/smartpath": "^6.0.0", "@push.rocks/smartpath": "^6.0.0",
"@push.rocks/smartpromise": "^4.2.3", "@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartrequest": "^4.3.0", "@push.rocks/smartrequest": "^4.3.1",
"@push.rocks/smartstream": "^3.2.5", "@push.rocks/smartstream": "^3.2.5",
"@push.rocks/smartstring": "^4.0.15", "@push.rocks/smartstring": "^4.0.15",
"@push.rocks/smartunique": "^3.0.9", "@push.rocks/smartunique": "^3.0.9",
@@ -72,5 +72,8 @@
"browserslist": [ "browserslist": [
"last 1 chrome versions" "last 1 chrome versions"
], ],
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748" "packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748",
"pnpm": {
"overrides": {}
}
} }

22
pnpm-lock.yaml generated
View File

@@ -36,8 +36,8 @@ importers:
specifier: ^4.2.3 specifier: ^4.2.3
version: 4.2.3 version: 4.2.3
'@push.rocks/smartrequest': '@push.rocks/smartrequest':
specifier: ^4.3.0 specifier: ^4.3.1
version: 4.3.0 version: 4.3.1
'@push.rocks/smartstream': '@push.rocks/smartstream':
specifier: ^3.2.5 specifier: ^3.2.5
version: 3.2.5 version: 3.2.5
@@ -715,8 +715,8 @@ packages:
'@push.rocks/smartrequest@2.1.0': '@push.rocks/smartrequest@2.1.0':
resolution: {integrity: sha512-3eHLTRInHA+u+W98TqJwgTES7rRimBAsJC4JxVNQC3UUezmblAhM5/TIQsEBQTsbjAY8SeQKy6NHzW6iTiaD8w==} resolution: {integrity: sha512-3eHLTRInHA+u+W98TqJwgTES7rRimBAsJC4JxVNQC3UUezmblAhM5/TIQsEBQTsbjAY8SeQKy6NHzW6iTiaD8w==}
'@push.rocks/smartrequest@4.3.0': '@push.rocks/smartrequest@4.3.1':
resolution: {integrity: sha512-bFfPOoe1LmNC51/62pvbyO1wg0AzhZu+jU1FZ6/HVkA8tCYQCqSagEL/dfUDCFyOqOKmYJnY/01dsKqKG3Zu5g==} resolution: {integrity: sha512-H5FQSfFEbSJCHpE2A+SasQQcxM5FlxhHIUEzhUsSLjtlCTEu9T7Xb1WzVLFYvdWfyP5VIrg+XM4AMOols8cG+Q==}
'@push.rocks/smartrouter@1.3.3': '@push.rocks/smartrouter@1.3.3':
resolution: {integrity: sha512-1+xZEnWlhzqLWAaJ1zFNhQ0zgbfCWQl1DBT72LygLxTs+P0K8AwJKgqo/IX6CT55kGCFnPAZIYSbVJlGsgrB0w==} resolution: {integrity: sha512-1+xZEnWlhzqLWAaJ1zFNhQ0zgbfCWQl1DBT72LygLxTs+P0K8AwJKgqo/IX6CT55kGCFnPAZIYSbVJlGsgrB0w==}
@@ -3666,7 +3666,7 @@ snapshots:
'@push.rocks/smartopen': 2.0.0 '@push.rocks/smartopen': 2.0.0
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 4.3.0 '@push.rocks/smartrequest': 4.3.1
'@push.rocks/smartrx': 3.0.10 '@push.rocks/smartrx': 3.0.10
'@push.rocks/smartsitemap': 2.0.3 '@push.rocks/smartsitemap': 2.0.3
'@push.rocks/smartstream': 3.2.5 '@push.rocks/smartstream': 3.2.5
@@ -4385,7 +4385,7 @@ snapshots:
'@push.rocks/smartlog': 3.1.8 '@push.rocks/smartlog': 3.1.8
'@push.rocks/smartnpm': 2.0.6 '@push.rocks/smartnpm': 2.0.6
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartrequest': 4.3.0 '@push.rocks/smartrequest': 4.3.1
'@push.rocks/smartshell': 3.3.0 '@push.rocks/smartshell': 3.3.0
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
@@ -4416,7 +4416,7 @@ snapshots:
'@push.rocks/smartmongo': 2.0.12(socks@2.8.7) '@push.rocks/smartmongo': 2.0.12(socks@2.8.7)
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 4.3.0 '@push.rocks/smartrequest': 4.3.1
'@push.rocks/smarts3': 2.2.6 '@push.rocks/smarts3': 2.2.6
'@push.rocks/smartshell': 3.3.0 '@push.rocks/smartshell': 3.3.0
'@push.rocks/smarttime': 4.1.1 '@push.rocks/smarttime': 4.1.1
@@ -4648,7 +4648,7 @@ snapshots:
'@push.rocks/smartfile': 11.2.7 '@push.rocks/smartfile': 11.2.7
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 4.3.0 '@push.rocks/smartrequest': 4.3.1
'@push.rocks/smartrx': 3.0.10 '@push.rocks/smartrx': 3.0.10
'@push.rocks/smartstream': 3.2.5 '@push.rocks/smartstream': 3.2.5
'@push.rocks/smartunique': 3.0.9 '@push.rocks/smartunique': 3.0.9
@@ -4799,7 +4799,7 @@ snapshots:
'@push.rocks/smartmime': 2.0.4 '@push.rocks/smartmime': 2.0.4
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 4.3.0 '@push.rocks/smartrequest': 4.3.1
'@push.rocks/smartstream': 3.2.5 '@push.rocks/smartstream': 3.2.5
'@types/fs-extra': 11.0.4 '@types/fs-extra': 11.0.4
'@types/js-yaml': 4.0.9 '@types/js-yaml': 4.0.9
@@ -4917,7 +4917,7 @@ snapshots:
'@push.rocks/smartfile': 11.2.7 '@push.rocks/smartfile': 11.2.7
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 4.3.0 '@push.rocks/smartrequest': 4.3.1
'@push.rocks/smarttime': 4.1.1 '@push.rocks/smarttime': 4.1.1
'@push.rocks/smartversion': 3.0.5 '@push.rocks/smartversion': 3.0.5
package-json: 8.1.1 package-json: 8.1.1
@@ -4999,7 +4999,7 @@ snapshots:
agentkeepalive: 4.5.0 agentkeepalive: 4.5.0
form-data: 4.0.4 form-data: 4.0.4
'@push.rocks/smartrequest@4.3.0': '@push.rocks/smartrequest@4.3.1':
dependencies: dependencies:
'@push.rocks/smartenv': 5.0.13 '@push.rocks/smartenv': 5.0.13
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0

111
readme.md
View File

@@ -38,7 +38,7 @@ const docker = new DockerHost();
// Or connect to remote Docker host // Or connect to remote Docker host
const remoteDocker = new DockerHost({ const remoteDocker = new DockerHost({
socketPath: 'tcp://remote-docker-host:2375' socketPath: 'tcp://remote-docker-host:2375',
}); });
``` ```
@@ -58,7 +58,7 @@ const docker = new DockerHost();
const customDocker = new DockerHost({ const customDocker = new DockerHost({
socketPath: '/var/run/docker.sock', // Unix socket path socketPath: '/var/run/docker.sock', // Unix socket path
// or // or
socketPath: 'tcp://192.168.1.100:2375' // TCP connection socketPath: 'tcp://192.168.1.100:2375', // TCP connection
}); });
// Start and stop (for lifecycle management) // Start and stop (for lifecycle management)
@@ -76,7 +76,7 @@ await docker.stop();
const allContainers = await docker.getContainers(); const allContainers = await docker.getContainers();
// Each container includes detailed information // Each container includes detailed information
allContainers.forEach(container => { allContainers.forEach((container) => {
console.log(`Container: ${container.Names[0]}`); console.log(`Container: ${container.Names[0]}`);
console.log(` ID: ${container.Id}`); console.log(` ID: ${container.Id}`);
console.log(` Status: ${container.Status}`); console.log(` Status: ${container.Status}`);
@@ -96,21 +96,18 @@ const container = await DockerContainer.create(docker, {
name: 'my-nginx-server', name: 'my-nginx-server',
HostConfig: { HostConfig: {
PortBindings: { PortBindings: {
'80/tcp': [{ HostPort: '8080' }] '80/tcp': [{ HostPort: '8080' }],
}, },
RestartPolicy: { RestartPolicy: {
Name: 'unless-stopped' Name: 'unless-stopped',
}, },
Memory: 512 * 1024 * 1024, // 512MB memory limit Memory: 512 * 1024 * 1024, // 512MB memory limit
}, },
Env: [ Env: ['NODE_ENV=production', 'LOG_LEVEL=info'],
'NODE_ENV=production',
'LOG_LEVEL=info'
],
Labels: { Labels: {
'app': 'web-server', app: 'web-server',
'environment': 'production' environment: 'production',
} },
}); });
console.log(`Container created: ${container.Id}`); console.log(`Container created: ${container.Id}`);
@@ -124,7 +121,10 @@ console.log(`Container created: ${container.Id}`);
#### Get Container by ID #### Get Container by ID
```typescript ```typescript
const container = await DockerContainer.getContainerById(docker, 'container-id-here'); const container = await DockerContainer.getContainerById(
docker,
'container-id-here',
);
if (container) { if (container) {
console.log(`Found container: ${container.Names[0]}`); console.log(`Found container: ${container.Names[0]}`);
} }
@@ -142,7 +142,7 @@ const image = await DockerImage.createFromRegistry(docker, {
imageName: 'node', imageName: 'node',
imageTag: '18-alpine', imageTag: '18-alpine',
// Optional: provide registry authentication // Optional: provide registry authentication
authToken: 'your-registry-auth-token' authToken: 'your-registry-auth-token',
}); });
console.log(`Image pulled: ${image.RepoTags[0]}`); console.log(`Image pulled: ${image.RepoTags[0]}`);
@@ -159,7 +159,7 @@ const tarStream = fs.createReadStream('./my-image.tar');
const importedImage = await DockerImage.createFromTarStream(docker, { const importedImage = await DockerImage.createFromTarStream(docker, {
tarStream, tarStream,
imageUrl: 'file://./my-image.tar', imageUrl: 'file://./my-image.tar',
imageTag: 'my-app:v1.0.0' imageTag: 'my-app:v1.0.0',
}); });
``` ```
@@ -182,7 +182,7 @@ exportStream.pipe(writeStream);
await DockerImage.tagImageByIdOrName(docker, 'node:18-alpine', { await DockerImage.tagImageByIdOrName(docker, 'node:18-alpine', {
registry: 'myregistry.com', registry: 'myregistry.com',
imageName: 'my-node-app', imageName: 'my-node-app',
imageTag: 'v2.0.0' imageTag: 'v2.0.0',
}); });
// Result: myregistry.com/my-node-app:v2.0.0 // Result: myregistry.com/my-node-app:v2.0.0
``` ```
@@ -201,15 +201,17 @@ const network = await DockerNetwork.createNetwork(docker, {
EnableIPv6: false, EnableIPv6: false,
IPAM: { IPAM: {
Driver: 'default', Driver: 'default',
Config: [{ Config: [
{
Subnet: '172.28.0.0/16', Subnet: '172.28.0.0/16',
Gateway: '172.28.0.1' Gateway: '172.28.0.1',
}] },
],
}, },
Labels: { Labels: {
'project': 'my-app', project: 'my-app',
'environment': 'production' environment: 'production',
} },
}); });
console.log(`Network created: ${network.Id}`); console.log(`Network created: ${network.Id}`);
@@ -220,14 +222,17 @@ console.log(`Network created: ${network.Id}`);
```typescript ```typescript
// Get all networks // Get all networks
const networks = await docker.getNetworks(); const networks = await docker.getNetworks();
networks.forEach(net => { networks.forEach((net) => {
console.log(`Network: ${net.Name} (${net.Driver})`); console.log(`Network: ${net.Name} (${net.Driver})`);
console.log(` Scope: ${net.Scope}`); console.log(` Scope: ${net.Scope}`);
console.log(` Internal: ${net.Internal}`); console.log(` Internal: ${net.Internal}`);
}); });
// Get specific network // Get specific network
const appNetwork = await DockerNetwork.getNetworkByName(docker, 'my-app-network'); const appNetwork = await DockerNetwork.getNetworkByName(
docker,
'my-app-network',
);
// Get containers on network // Get containers on network
const containers = await appNetwork.getContainersOnNetwork(); const containers = await appNetwork.getContainersOnNetwork();
@@ -246,28 +251,32 @@ const service = await DockerService.createService(docker, {
name: 'web-api', name: 'web-api',
image: 'my-api:latest', image: 'my-api:latest',
replicas: 3, replicas: 3,
ports: [{ ports: [
{
Protocol: 'tcp', Protocol: 'tcp',
PublishedPort: 80, PublishedPort: 80,
TargetPort: 3000 TargetPort: 3000,
}], },
],
networks: ['my-app-network'], networks: ['my-app-network'],
labels: { labels: {
'app': 'api', app: 'api',
'version': '2.0.0' version: '2.0.0',
}, },
resources: { resources: {
limits: { limits: {
Memory: 256 * 1024 * 1024, // 256MB Memory: 256 * 1024 * 1024, // 256MB
CPUs: 0.5 CPUs: 0.5,
} },
}, },
secrets: ['api-key', 'db-password'], secrets: ['api-key', 'db-password'],
mounts: [{ mounts: [
{
Target: '/data', Target: '/data',
Source: 'app-data', Source: 'app-data',
Type: 'volume' Type: 'volume',
}] },
],
}); });
console.log(`Service deployed: ${service.ID}`); console.log(`Service deployed: ${service.ID}`);
@@ -278,7 +287,7 @@ console.log(`Service deployed: ${service.ID}`);
```typescript ```typescript
// List all services // List all services
const services = await docker.getServices(); const services = await docker.getServices();
services.forEach(service => { services.forEach((service) => {
console.log(`Service: ${service.Spec.Name}`); console.log(`Service: ${service.Spec.Name}`);
console.log(` Replicas: ${service.Spec.Mode.Replicated.Replicas}`); console.log(` Replicas: ${service.Spec.Mode.Replicated.Replicas}`);
console.log(` Image: ${service.Spec.TaskTemplate.ContainerSpec.Image}`); console.log(` Image: ${service.Spec.TaskTemplate.ContainerSpec.Image}`);
@@ -307,16 +316,16 @@ const secret = await DockerSecret.createSecret(docker, {
name: 'api-key', name: 'api-key',
data: Buffer.from('super-secret-key-123').toString('base64'), data: Buffer.from('super-secret-key-123').toString('base64'),
labels: { labels: {
'app': 'my-app', app: 'my-app',
'type': 'api-key' type: 'api-key',
} },
}); });
console.log(`Secret created: ${secret.ID}`); console.log(`Secret created: ${secret.ID}`);
// List secrets // List secrets
const secrets = await DockerSecret.getSecrets(docker); const secrets = await DockerSecret.getSecrets(docker);
secrets.forEach(secret => { secrets.forEach((secret) => {
console.log(`Secret: ${secret.Spec.Name}`); console.log(`Secret: ${secret.Spec.Name}`);
}); });
@@ -325,7 +334,7 @@ const apiKeySecret = await DockerSecret.getSecretByName(docker, 'api-key');
// Update secret // Update secret
await apiKeySecret.update({ await apiKeySecret.update({
data: Buffer.from('new-secret-key-456').toString('base64') data: Buffer.from('new-secret-key-456').toString('base64'),
}); });
// Remove secret // Remove secret
@@ -342,7 +351,7 @@ await docker.addS3Storage({
endpoint: 's3.amazonaws.com', endpoint: 's3.amazonaws.com',
accessKeyId: 'your-access-key', accessKeyId: 'your-access-key',
secretAccessKey: 'your-secret-key', secretAccessKey: 'your-secret-key',
bucket: 'docker-images' bucket: 'docker-images',
}); });
// Store an image to S3 // Store an image to S3
@@ -368,7 +377,7 @@ const subscription = eventStream.subscribe({
console.log(`Time: ${new Date(event.time * 1000).toISOString()}`); console.log(`Time: ${new Date(event.time * 1000).toISOString()}`);
}, },
error: (err) => console.error('Event stream error:', err), error: (err) => console.error('Event stream error:', err),
complete: () => console.log('Event stream completed') complete: () => console.log('Event stream completed'),
}); });
// Unsubscribe when done // Unsubscribe when done
@@ -384,7 +393,7 @@ Authenticate with Docker registries for private images:
await docker.auth({ await docker.auth({
username: 'your-username', username: 'your-username',
password: 'your-password', password: 'your-password',
serveraddress: 'https://index.docker.io/v1/' serveraddress: 'https://index.docker.io/v1/',
}); });
// Or use existing Docker config // Or use existing Docker config
@@ -394,7 +403,7 @@ const authToken = await docker.getAuthTokenFromDockerConfig('myregistry.com');
const privateImage = await DockerImage.createFromRegistry(docker, { const privateImage = await DockerImage.createFromRegistry(docker, {
imageName: 'myregistry.com/private/image', imageName: 'myregistry.com/private/image',
imageTag: 'latest', imageTag: 'latest',
authToken authToken,
}); });
``` ```
@@ -407,14 +416,14 @@ Initialize and manage Docker Swarm:
await docker.activateSwarm({ await docker.activateSwarm({
ListenAddr: '0.0.0.0:2377', ListenAddr: '0.0.0.0:2377',
AdvertiseAddr: '192.168.1.100:2377', AdvertiseAddr: '192.168.1.100:2377',
ForceNewCluster: false ForceNewCluster: false,
}); });
// Now you can create services, secrets, and use swarm features // Now you can create services, secrets, and use swarm features
const service = await DockerService.createService(docker, { const service = await DockerService.createService(docker, {
name: 'my-swarm-service', name: 'my-swarm-service',
image: 'nginx:latest', image: 'nginx:latest',
replicas: 5 replicas: 5,
// ... more service config // ... more service config
}); });
``` ```
@@ -430,13 +439,13 @@ async function deployStack() {
// Create network // Create network
const network = await DockerNetwork.createNetwork(docker, { const network = await DockerNetwork.createNetwork(docker, {
Name: 'app-network', Name: 'app-network',
Driver: 'overlay' // for swarm mode Driver: 'overlay', // for swarm mode
}); });
// Create secrets // Create secrets
const dbPassword = await DockerSecret.createSecret(docker, { const dbPassword = await DockerSecret.createSecret(docker, {
name: 'db-password', name: 'db-password',
data: Buffer.from('strong-password').toString('base64') data: Buffer.from('strong-password').toString('base64'),
}); });
// Deploy database service // Deploy database service
@@ -445,7 +454,7 @@ async function deployStack() {
image: 'postgres:14', image: 'postgres:14',
networks: ['app-network'], networks: ['app-network'],
secrets: ['db-password'], secrets: ['db-password'],
env: ['POSTGRES_PASSWORD_FILE=/run/secrets/db-password'] env: ['POSTGRES_PASSWORD_FILE=/run/secrets/db-password'],
}); });
// Deploy application service // Deploy application service
@@ -454,7 +463,7 @@ async function deployStack() {
image: 'my-app:latest', image: 'my-app:latest',
replicas: 3, replicas: 3,
networks: ['app-network'], networks: ['app-network'],
ports: [{ Protocol: 'tcp', PublishedPort: 80, TargetPort: 3000 }] ports: [{ Protocol: 'tcp', PublishedPort: 80, TargetPort: 3000 }],
}); });
console.log('Stack deployed successfully!'); console.log('Stack deployed successfully!');
@@ -471,7 +480,7 @@ import type {
IServiceCreationDescriptor, IServiceCreationDescriptor,
INetworkCreationDescriptor, INetworkCreationDescriptor,
IImageCreationDescriptor, IImageCreationDescriptor,
ISecretCreationDescriptor ISecretCreationDescriptor,
} from '@apiclient.xyz/docker'; } from '@apiclient.xyz/docker';
// Full IntelliSense support for all configuration options // Full IntelliSense support for all configuration options

View File

@@ -41,7 +41,10 @@ tap.test('should create a network', async () => {
}); });
tap.test('should remove a network', async () => { tap.test('should remove a network', async () => {
const webgateway = await docker.DockerNetwork.getNetworkByName(testDockerHost, 'webgateway'); const webgateway = await docker.DockerNetwork.getNetworkByName(
testDockerHost,
'webgateway',
);
await webgateway.remove(); await webgateway.remove();
}); });
@@ -78,7 +81,10 @@ tap.test('should create a secret', async () => {
}); });
tap.test('should remove a secret by name', async () => { tap.test('should remove a secret by name', async () => {
const mySecret = await docker.DockerSecret.getSecretByName(testDockerHost, 'testSecret'); const mySecret = await docker.DockerSecret.getSecretByName(
testDockerHost,
'testSecret',
);
await mySecret.remove(); await mySecret.remove();
}); });
@@ -102,11 +108,14 @@ tap.test('should create a service', async () => {
labels: {}, labels: {},
contentArg: '{"hi": "wow"}', contentArg: '{"hi": "wow"}',
}); });
const testImage = await docker.DockerImage.createFromRegistry(testDockerHost, { const testImage = await docker.DockerImage.createFromRegistry(
testDockerHost,
{
creationObject: { creationObject: {
imageUrl: 'code.foss.global/host.today/ht-docker-node:latest', imageUrl: 'code.foss.global/host.today/ht-docker-node:latest',
} },
}); },
);
const testService = await docker.DockerService.createService(testDockerHost, { const testService = await docker.DockerService.createService(testDockerHost, {
image: testImage, image: testImage,
labels: {}, labels: {},
@@ -124,13 +133,16 @@ tap.test('should create a service', async () => {
tap.test('should export images', async (toolsArg) => { tap.test('should export images', async (toolsArg) => {
const done = toolsArg.defer(); const done = toolsArg.defer();
const testImage = await docker.DockerImage.createFromRegistry(testDockerHost, { const testImage = await docker.DockerImage.createFromRegistry(
testDockerHost,
{
creationObject: { creationObject: {
imageUrl: 'code.foss.global/host.today/ht-docker-node:latest', imageUrl: 'code.foss.global/host.today/ht-docker-node:latest',
} },
}); },
);
const fsWriteStream = plugins.smartfile.fsStream.createWriteStream( const fsWriteStream = plugins.smartfile.fsStream.createWriteStream(
plugins.path.join(paths.nogitDir, 'testimage.tar') plugins.path.join(paths.nogitDir, 'testimage.tar'),
); );
const exportStream = await testImage.exportToTarStream(); const exportStream = await testImage.exportToTarStream();
exportStream.pipe(fsWriteStream).on('finish', () => { exportStream.pipe(fsWriteStream).on('finish', () => {
@@ -141,18 +153,21 @@ tap.test('should export images', async (toolsArg) => {
tap.test('should import images', async () => { tap.test('should import images', async () => {
const fsReadStream = plugins.smartfile.fsStream.createReadStream( const fsReadStream = plugins.smartfile.fsStream.createReadStream(
plugins.path.join(paths.nogitDir, 'testimage.tar') plugins.path.join(paths.nogitDir, 'testimage.tar'),
); );
const importedImage = await docker.DockerImage.createFromTarStream(testDockerHost, { const importedImage = await docker.DockerImage.createFromTarStream(
testDockerHost,
{
tarStream: fsReadStream, tarStream: fsReadStream,
creationObject: { creationObject: {
imageUrl: 'code.foss.global/host.today/ht-docker-node:latest', imageUrl: 'code.foss.global/host.today/ht-docker-node:latest',
} },
}); },
);
expect(importedImage).toBeInstanceOf(docker.DockerImage); expect(importedImage).toBeInstanceOf(docker.DockerImage);
}); });
tap.skip.test('should expose a working DockerImageStore', async () => { tap.test('should expose a working DockerImageStore', async () => {
// lets first add am s3 target // lets first add am s3 target
const s3Descriptor = { const s3Descriptor = {
endpoint: await testQenv.getEnvVarOnDemand('S3_ENDPOINT'), endpoint: await testQenv.getEnvVarOnDemand('S3_ENDPOINT'),
@@ -163,11 +178,16 @@ tap.skip.test('should expose a working DockerImageStore', async () => {
await testDockerHost.addS3Storage(s3Descriptor); await testDockerHost.addS3Storage(s3Descriptor);
// //
await testDockerHost.imageStore.storeImage('hello', plugins.smartfile.fsStream.createReadStream(plugins.path.join(paths.nogitDir, 'testimage.tar'))); await testDockerHost.imageStore.storeImage(
}) 'hello2',
plugins.smartfile.fsStream.createReadStream(
plugins.path.join(paths.nogitDir, 'testimage.tar'),
),
);
});
tap.test('cleanup', async () => { tap.test('cleanup', async () => {
await testDockerHost.stop(); await testDockerHost.stop();
}) });
export default tap.start(); export default tap.start();

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@apiclient.xyz/docker', name: '@apiclient.xyz/docker',
version: '1.3.3', version: '1.3.5',
description: 'Provides easy communication with Docker remote API from Node.js, with TypeScript support.' description: 'Provides easy communication with Docker remote API from Node.js, with TypeScript support.'
} }

View File

@@ -10,7 +10,9 @@ export class DockerContainer {
/** /**
* get all containers * get all containers
*/ */
public static async getContainers(dockerHostArg: DockerHost): Promise<DockerContainer[]> { public static async getContainers(
dockerHostArg: DockerHost,
): Promise<DockerContainer[]> {
const result: DockerContainer[] = []; const result: DockerContainer[] = [];
const response = await dockerHostArg.request('GET', '/containers/json'); const response = await dockerHostArg.request('GET', '/containers/json');
@@ -34,7 +36,7 @@ export class DockerContainer {
*/ */
public static async create( public static async create(
dockerHost: DockerHost, dockerHost: DockerHost,
containerCreationDescriptor: interfaces.IContainerCreationDescriptor containerCreationDescriptor: interfaces.IContainerCreationDescriptor,
) { ) {
// check for unique hostname // check for unique hostname
const existingContainers = await DockerContainer.getContainers(dockerHost); const existingContainers = await DockerContainer.getContainers(dockerHost);
@@ -50,7 +52,10 @@ export class DockerContainer {
if (response.statusCode < 300) { if (response.statusCode < 300) {
logger.log('info', 'Container created successfully'); logger.log('info', 'Container created successfully');
} else { } else {
logger.log('error', 'There has been a problem when creating the container'); logger.log(
'error',
'There has been a problem when creating the container',
);
} }
} }

View File

@@ -37,10 +37,13 @@ export class DockerHost {
constructor(optionsArg: IDockerHostConstructorOptions) { constructor(optionsArg: IDockerHostConstructorOptions) {
this.options = { this.options = {
...{ ...{
imageStoreDir: plugins.path.join(paths.nogitDir, 'temp-docker-image-store'), imageStoreDir: plugins.path.join(
paths.nogitDir,
'temp-docker-image-store',
),
}, },
...optionsArg, ...optionsArg,
} };
let pathToUse: string; let pathToUse: string;
if (optionsArg.dockerSockPath) { if (optionsArg.dockerSockPath) {
pathToUse = optionsArg.dockerSockPath; pathToUse = optionsArg.dockerSockPath;
@@ -62,7 +65,7 @@ export class DockerHost {
this.imageStore = new DockerImageStore({ this.imageStore = new DockerImageStore({
bucketDir: null, bucketDir: null,
localDirPath: this.options.imageStoreDir, localDirPath: this.options.imageStoreDir,
}) });
} }
public async start() { public async start() {
@@ -84,17 +87,22 @@ export class DockerHost {
throw new Error(response.body.Status); throw new Error(response.body.Status);
} }
console.log(response.body.Status); console.log(response.body.Status);
this.registryToken = plugins.smartstring.base64.encode(plugins.smartjson.stringify(authData)); this.registryToken = plugins.smartstring.base64.encode(
plugins.smartjson.stringify(authData),
);
} }
/** /**
* gets the token from the .docker/config.json file for GitLab registry * gets the token from the .docker/config.json file for GitLab registry
*/ */
public async getAuthTokenFromDockerConfig(registryUrlArg: string) { public async getAuthTokenFromDockerConfig(registryUrlArg: string) {
const dockerConfigPath = plugins.smartpath.get.home('~/.docker/config.json'); const dockerConfigPath = plugins.smartpath.get.home(
'~/.docker/config.json',
);
const configObject = plugins.smartfile.fs.toObjectSync(dockerConfigPath); const configObject = plugins.smartfile.fs.toObjectSync(dockerConfigPath);
const gitlabAuthBase64 = configObject.auths[registryUrlArg].auth; const gitlabAuthBase64 = configObject.auths[registryUrlArg].auth;
const gitlabAuth: string = plugins.smartstring.base64.decode(gitlabAuthBase64); const gitlabAuth: string =
plugins.smartstring.base64.decode(gitlabAuthBase64);
const gitlabAuthArray = gitlabAuth.split(':'); const gitlabAuthArray = gitlabAuth.split(':');
await this.auth({ await this.auth({
username: gitlabAuthArray[0], username: gitlabAuthArray[0],
@@ -116,7 +124,9 @@ export class DockerHost {
/** /**
* create a network * create a network
*/ */
public async createNetwork(optionsArg: Parameters<typeof DockerNetwork.createNetwork>[1]) { public async createNetwork(
optionsArg: Parameters<typeof DockerNetwork.createNetwork>[1],
) {
return await DockerNetwork.createNetwork(this, optionsArg); return await DockerNetwork.createNetwork(this, optionsArg);
} }
@@ -127,7 +137,6 @@ export class DockerHost {
return await DockerNetwork.getNetworkByName(this, networkNameArg); return await DockerNetwork.getNetworkByName(this, networkNameArg);
} }
// ============== // ==============
// CONTAINERS // CONTAINERS
// ============== // ==============
@@ -265,7 +274,8 @@ export class DockerHost {
// Docker's streaming endpoints (like /images/create) return newline-delimited JSON // Docker's streaming endpoints (like /images/create) return newline-delimited JSON
// which can't be parsed as a single JSON object // which can't be parsed as a single JSON object
const isStreamingEndpoint = routeArg.includes('/images/create') || const isStreamingEndpoint =
routeArg.includes('/images/create') ||
routeArg.includes('/images/load') || routeArg.includes('/images/load') ||
routeArg.includes('/build'); routeArg.includes('/build');
@@ -274,7 +284,11 @@ export class DockerHost {
} else { } else {
body = await response.text(); body = await response.text();
// Try to parse as JSON if it looks like JSON and is not a streaming response // Try to parse as JSON if it looks like JSON and is not a streaming response
if (!isStreamingEndpoint && body && (body.startsWith('{') || body.startsWith('['))) { if (
!isStreamingEndpoint &&
body &&
(body.startsWith('{') || body.startsWith('['))
) {
try { try {
body = JSON.parse(body); body = JSON.parse(body);
} catch { } catch {
@@ -287,7 +301,7 @@ export class DockerHost {
const legacyResponse = { const legacyResponse = {
statusCode: response.status, statusCode: response.status,
body: body, body: body,
headers: response.headers headers: response.headers,
}; };
if (response.status !== 200) { if (response.status !== 200) {
@@ -297,7 +311,11 @@ export class DockerHost {
return legacyResponse; return legacyResponse;
} }
public async requestStreaming(methodArg: string, routeArg: string, readStream?: plugins.smartstream.stream.Readable) { public async requestStreaming(
methodArg: string,
routeArg: string,
readStream?: plugins.smartstream.stream.Readable,
) {
const requestUrl = `${this.socketPath}${routeArg}`; const requestUrl = `${this.socketPath}${routeArg}`;
// Build the request using the fluent API // Build the request using the fluent API
@@ -319,7 +337,7 @@ export class DockerHost {
} }
counter++; counter++;
return chunkArg; return chunkArg;
} },
}); });
// Pipe through the logging duplex stream // Pipe through the logging duplex stream
@@ -362,7 +380,7 @@ export class DockerHost {
return { return {
statusCode: response.status, statusCode: response.status,
body: body, body: body,
headers: response.headers headers: response.headers,
}; };
} }
@@ -382,10 +400,14 @@ export class DockerHost {
if (!optionsArg.bucketName) { if (!optionsArg.bucketName) {
throw new Error('bucketName is required'); throw new Error('bucketName is required');
} }
const bucket = await this.smartBucket.getBucketByName(optionsArg.bucketName); const bucket = await this.smartBucket.getBucketByName(
optionsArg.bucketName,
);
let wantedDirectory = await bucket.getBaseDirectory(); let wantedDirectory = await bucket.getBaseDirectory();
if (optionsArg.directoryPath) { if (optionsArg.directoryPath) {
wantedDirectory = await wantedDirectory.getSubDirectoryByName(optionsArg.directoryPath); wantedDirectory = await wantedDirectory.getSubDirectoryByName(
optionsArg.directoryPath,
);
} }
this.imageStore.options.bucketDir = wantedDirectory; this.imageStore.options.bucketDir = wantedDirectory;
} }

View File

@@ -17,7 +17,10 @@ export class DockerImage {
return images; return images;
} }
public static async getImageByName(dockerHost: DockerHost, imageNameArg: string) { public static async getImageByName(
dockerHost: DockerHost,
imageNameArg: string,
) {
const images = await this.getImages(dockerHost); const images = await this.getImages(dockerHost);
const result = images.find((image) => { const result = images.find((image) => {
if (image.RepoTags) { if (image.RepoTags) {
@@ -32,8 +35,8 @@ export class DockerImage {
public static async createFromRegistry( public static async createFromRegistry(
dockerHostArg: DockerHost, dockerHostArg: DockerHost,
optionsArg: { optionsArg: {
creationObject: interfaces.IImageCreationDescriptor creationObject: interfaces.IImageCreationDescriptor;
} },
): Promise<DockerImage> { ): Promise<DockerImage> {
// lets create a sanatized imageUrlObject // lets create a sanatized imageUrlObject
const imageUrlObject: { const imageUrlObject: {
@@ -50,7 +53,7 @@ export class DockerImage {
const imageTag = imageUrlObject.imageUrl.split(':')[1]; const imageTag = imageUrlObject.imageUrl.split(':')[1];
if (imageUrlObject.imageTag) { if (imageUrlObject.imageTag) {
throw new Error( throw new Error(
`imageUrl ${imageUrlObject.imageUrl} can't be tagged with ${imageUrlObject.imageTag} because it is already tagged with ${imageTag}` `imageUrl ${imageUrlObject.imageUrl} can't be tagged with ${imageUrlObject.imageTag} because it is already tagged with ${imageTag}`,
); );
} else { } else {
imageUrlObject.imageUrl = imageUrl; imageUrlObject.imageUrl = imageUrl;
@@ -65,12 +68,18 @@ export class DockerImage {
const response = await dockerHostArg.request( const response = await dockerHostArg.request(
'POST', 'POST',
`/images/create?fromImage=${encodeURIComponent( `/images/create?fromImage=${encodeURIComponent(
imageUrlObject.imageUrl imageUrlObject.imageUrl,
)}&tag=${encodeURIComponent(imageUrlObject.imageTag)}` )}&tag=${encodeURIComponent(imageUrlObject.imageTag)}`,
); );
if (response.statusCode < 300) { if (response.statusCode < 300) {
logger.log('info', `Successfully pulled image ${imageUrlObject.imageUrl} from the registry`); logger.log(
const image = await DockerImage.getImageByName(dockerHostArg, imageUrlObject.imageOriginTag); 'info',
`Successfully pulled image ${imageUrlObject.imageUrl} from the registry`,
);
const image = await DockerImage.getImageByName(
dockerHostArg,
imageUrlObject.imageOriginTag,
);
return image; return image;
} else { } else {
logger.log('error', `Failed at the attempt of creating a new image`); logger.log('error', `Failed at the attempt of creating a new image`);
@@ -87,13 +96,13 @@ export class DockerImage {
optionsArg: { optionsArg: {
creationObject: interfaces.IImageCreationDescriptor; creationObject: interfaces.IImageCreationDescriptor;
tarStream: plugins.smartstream.stream.Readable; tarStream: plugins.smartstream.stream.Readable;
} },
): Promise<DockerImage> { ): Promise<DockerImage> {
// Start the request for importing an image // Start the request for importing an image
const response = await dockerHostArg.requestStreaming( const response = await dockerHostArg.requestStreaming(
'POST', 'POST',
'/images/load', '/images/load',
optionsArg.tarStream optionsArg.tarStream,
); );
/** /**
@@ -144,7 +153,7 @@ export class DockerImage {
if (!loadedImageTag) { if (!loadedImageTag) {
throw new Error( throw new Error(
`Could not parse the loaded image info from Docker response.\nResponse was:\n${rawOutput}` `Could not parse the loaded image info from Docker response.\nResponse was:\n${rawOutput}`,
); );
} }
@@ -153,34 +162,31 @@ export class DockerImage {
// "myrepo/myimage:latest" OR "sha256:someHash..." // "myrepo/myimage:latest" OR "sha256:someHash..."
// If Docker gave you an ID (e.g. "sha256:..."), you may need a separate // If Docker gave you an ID (e.g. "sha256:..."), you may need a separate
// DockerImage.getImageById method; or if you prefer, you can treat it as a name. // DockerImage.getImageById method; or if you prefer, you can treat it as a name.
const newlyImportedImage = await DockerImage.getImageByName(dockerHostArg, loadedImageTag); const newlyImportedImage = await DockerImage.getImageByName(
dockerHostArg,
loadedImageTag,
);
if (!newlyImportedImage) { if (!newlyImportedImage) {
throw new Error( throw new Error(
`Image load succeeded, but no local reference found for "${loadedImageTag}".` `Image load succeeded, but no local reference found for "${loadedImageTag}".`,
); );
} }
logger.log( logger.log('info', `Successfully imported image "${loadedImageTag}".`);
'info',
`Successfully imported image "${loadedImageTag}".`
);
return newlyImportedImage; return newlyImportedImage;
} }
public static async tagImageByIdOrName( public static async tagImageByIdOrName(
dockerHost: DockerHost, dockerHost: DockerHost,
idOrNameArg: string, idOrNameArg: string,
newTagArg: string newTagArg: string,
) { ) {
const response = await dockerHost.request( const response = await dockerHost.request(
'POST', 'POST',
`/images/${encodeURIComponent(idOrNameArg)}/${encodeURIComponent(newTagArg)}` `/images/${encodeURIComponent(idOrNameArg)}/${encodeURIComponent(newTagArg)}`,
); );
} }
public static async buildImage(dockerHostArg: DockerHost, dockerImageTag) { public static async buildImage(dockerHostArg: DockerHost, dockerImageTag) {
@@ -249,7 +255,10 @@ export class DockerImage {
*/ */
public async exportToTarStream(): Promise<plugins.smartstream.stream.Readable> { public async exportToTarStream(): Promise<plugins.smartstream.stream.Readable> {
logger.log('info', `Exporting image ${this.RepoTags[0]} to tar stream.`); logger.log('info', `Exporting image ${this.RepoTags[0]} to tar stream.`);
const response = await this.dockerHost.requestStreaming('GET', `/images/${encodeURIComponent(this.RepoTags[0])}/get`); const response = await this.dockerHost.requestStreaming(
'GET',
`/images/${encodeURIComponent(this.RepoTags[0])}/get`,
);
// Check if response is a Node.js stream // Check if response is a Node.js stream
if (!response || typeof response.on !== 'function') { if (!response || typeof response.on !== 'function') {
@@ -259,11 +268,10 @@ export class DockerImage {
let counter = 0; let counter = 0;
const webduplexStream = new plugins.smartstream.SmartDuplex({ const webduplexStream = new plugins.smartstream.SmartDuplex({
writeFunction: async (chunk, tools) => { writeFunction: async (chunk, tools) => {
if (counter % 1000 === 0) if (counter % 1000 === 0) console.log(`Got chunk: ${counter}`);
console.log(`Got chunk: ${counter}`);
counter++; counter++;
return chunk; return chunk;
} },
}); });
response.on('data', (chunk) => { response.on('data', (chunk) => {

View File

@@ -22,14 +22,25 @@ export class DockerImageStore {
} }
// Method to store tar stream // Method to store tar stream
public async storeImage(imageName: string, tarStream: plugins.smartstream.stream.Readable): Promise<void> { public async storeImage(
imageName: string,
tarStream: plugins.smartstream.stream.Readable,
): Promise<void> {
logger.log('info', `Storing image ${imageName}...`); logger.log('info', `Storing image ${imageName}...`);
const uniqueProcessingId = plugins.smartunique.shortId(); const uniqueProcessingId = plugins.smartunique.shortId();
const initialTarDownloadPath = plugins.path.join(this.options.localDirPath, `${uniqueProcessingId}.tar`); const initialTarDownloadPath = plugins.path.join(
const extractionDir = plugins.path.join(this.options.localDirPath, uniqueProcessingId); this.options.localDirPath,
`${uniqueProcessingId}.tar`,
);
const extractionDir = plugins.path.join(
this.options.localDirPath,
uniqueProcessingId,
);
// Create a write stream to store the tar file // Create a write stream to store the tar file
const writeStream = plugins.smartfile.fsStream.createWriteStream(initialTarDownloadPath); const writeStream = plugins.smartfile.fsStream.createWriteStream(
initialTarDownloadPath,
);
// lets wait for the write stream to finish // lets wait for the write stream to finish
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
@@ -37,23 +48,43 @@ export class DockerImageStore {
writeStream.on('finish', resolve); writeStream.on('finish', resolve);
writeStream.on('error', reject); writeStream.on('error', reject);
}); });
logger.log('info', `Image ${imageName} stored locally for processing. Extracting...`); logger.log(
'info',
`Image ${imageName} stored locally for processing. Extracting...`,
);
// lets process the image // lets process the image
const tarArchive = await plugins.smartarchive.SmartArchive.fromArchiveFile(initialTarDownloadPath); const tarArchive = await plugins.smartarchive.SmartArchive.fromArchiveFile(
initialTarDownloadPath,
);
await tarArchive.exportToFs(extractionDir); await tarArchive.exportToFs(extractionDir);
logger.log('info', `Image ${imageName} extracted.`); logger.log('info', `Image ${imageName} extracted.`);
await plugins.smartfile.fs.remove(initialTarDownloadPath); await plugins.smartfile.fs.remove(initialTarDownloadPath);
logger.log('info', `deleted original tar to save space.`); logger.log('info', `deleted original tar to save space.`);
logger.log('info', `now repackaging for s3...`); logger.log('info', `now repackaging for s3...`);
const smartfileIndexJson = await plugins.smartfile.SmartFile.fromFilePath(plugins.path.join(extractionDir, 'index.json')); const smartfileIndexJson = await plugins.smartfile.SmartFile.fromFilePath(
const smartfileManifestJson = await plugins.smartfile.SmartFile.fromFilePath(plugins.path.join(extractionDir, 'manifest.json')); plugins.path.join(extractionDir, 'index.json'),
const smartfileOciLayoutJson = await plugins.smartfile.SmartFile.fromFilePath(plugins.path.join(extractionDir, 'oci-layout')); );
const smartfileRepositoriesJson = await plugins.smartfile.SmartFile.fromFilePath(plugins.path.join(extractionDir, 'repositories')); const smartfileManifestJson =
await plugins.smartfile.SmartFile.fromFilePath(
plugins.path.join(extractionDir, 'manifest.json'),
);
const smartfileOciLayoutJson =
await plugins.smartfile.SmartFile.fromFilePath(
plugins.path.join(extractionDir, 'oci-layout'),
);
const smartfileRepositoriesJson =
await plugins.smartfile.SmartFile.fromFilePath(
plugins.path.join(extractionDir, 'repositories'),
);
const indexJson = JSON.parse(smartfileIndexJson.contents.toString()); const indexJson = JSON.parse(smartfileIndexJson.contents.toString());
const manifestJson = JSON.parse(smartfileManifestJson.contents.toString()); const manifestJson = JSON.parse(smartfileManifestJson.contents.toString());
const ociLayoutJson = JSON.parse(smartfileOciLayoutJson.contents.toString()); const ociLayoutJson = JSON.parse(
const repositoriesJson = JSON.parse(smartfileRepositoriesJson.contents.toString()); smartfileOciLayoutJson.contents.toString(),
);
const repositoriesJson = JSON.parse(
smartfileRepositoriesJson.contents.toString(),
);
indexJson.manifests[0].annotations['io.containerd.image.name'] = imageName; indexJson.manifests[0].annotations['io.containerd.image.name'] = imageName;
manifestJson[0].RepoTags[0] = imageName; manifestJson[0].RepoTags[0] = imageName;
@@ -62,10 +93,18 @@ export class DockerImageStore {
repositoriesJson[imageName] = repoFirstValue; repositoriesJson[imageName] = repoFirstValue;
delete repositoriesJson[repoFirstKey]; delete repositoriesJson[repoFirstKey];
smartfileIndexJson.contents = Buffer.from(JSON.stringify(indexJson, null, 2)); smartfileIndexJson.contents = Buffer.from(
smartfileManifestJson.contents = Buffer.from(JSON.stringify(manifestJson, null, 2)); JSON.stringify(indexJson, null, 2),
smartfileOciLayoutJson.contents = Buffer.from(JSON.stringify(ociLayoutJson, null, 2)); );
smartfileRepositoriesJson.contents = Buffer.from(JSON.stringify(repositoriesJson, null, 2)); smartfileManifestJson.contents = Buffer.from(
JSON.stringify(manifestJson, null, 2),
);
smartfileOciLayoutJson.contents = Buffer.from(
JSON.stringify(ociLayoutJson, null, 2),
);
smartfileRepositoriesJson.contents = Buffer.from(
JSON.stringify(repositoriesJson, null, 2),
);
await Promise.all([ await Promise.all([
smartfileIndexJson.write(), smartfileIndexJson.write(),
smartfileManifestJson.write(), smartfileManifestJson.write(),
@@ -77,8 +116,12 @@ export class DockerImageStore {
const tartools = new plugins.smartarchive.TarTools(); const tartools = new plugins.smartarchive.TarTools();
const newTarPack = await tartools.packDirectory(extractionDir); const newTarPack = await tartools.packDirectory(extractionDir);
const finalTarName = `${uniqueProcessingId}.processed.tar`; const finalTarName = `${uniqueProcessingId}.processed.tar`;
const finalTarPath = plugins.path.join(this.options.localDirPath, finalTarName); const finalTarPath = plugins.path.join(
const finalWriteStream = plugins.smartfile.fsStream.createWriteStream(finalTarPath); this.options.localDirPath,
finalTarName,
);
const finalWriteStream =
plugins.smartfile.fsStream.createWriteStream(finalTarPath);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
newTarPack.finalize(); newTarPack.finalize();
newTarPack.pipe(finalWriteStream); newTarPack.pipe(finalWriteStream);
@@ -87,7 +130,8 @@ export class DockerImageStore {
}); });
logger.log('ok', `Repackaged image ${imageName} for s3.`); logger.log('ok', `Repackaged image ${imageName} for s3.`);
await plugins.smartfile.fs.remove(extractionDir); await plugins.smartfile.fs.remove(extractionDir);
const finalTarReadStream = plugins.smartfile.fsStream.createReadStream(finalTarPath); const finalTarReadStream =
plugins.smartfile.fsStream.createReadStream(finalTarPath);
await this.options.bucketDir.fastPutStream({ await this.options.bucketDir.fastPutStream({
stream: finalTarReadStream, stream: finalTarReadStream,
path: `${imageName}.tar`, path: `${imageName}.tar`,
@@ -102,8 +146,13 @@ export class DockerImageStore {
public async stop() {} public async stop() {}
// Method to retrieve tar stream // Method to retrieve tar stream
public async getImage(imageName: string): Promise<plugins.smartstream.stream.Readable> { public async getImage(
const imagePath = plugins.path.join(this.options.localDirPath, `${imageName}.tar`); imageName: string,
): Promise<plugins.smartstream.stream.Readable> {
const imagePath = plugins.path.join(
this.options.localDirPath,
`${imageName}.tar`,
);
if (!(await plugins.smartfile.fs.fileExists(imagePath))) { if (!(await plugins.smartfile.fs.fileExists(imagePath))) {
throw new Error(`Image ${imageName} does not exist.`); throw new Error(`Image ${imageName} does not exist.`);

View File

@@ -6,7 +6,9 @@ import { DockerService } from './classes.service.js';
import { logger } from './logger.js'; import { logger } from './logger.js';
export class DockerNetwork { export class DockerNetwork {
public static async getNetworks(dockerHost: DockerHost): Promise<DockerNetwork[]> { public static async getNetworks(
dockerHost: DockerHost,
): Promise<DockerNetwork[]> {
const dockerNetworks: DockerNetwork[] = []; const dockerNetworks: DockerNetwork[] = [];
const response = await dockerHost.request('GET', '/networks'); const response = await dockerHost.request('GET', '/networks');
for (const networkObject of response.body) { for (const networkObject of response.body) {
@@ -17,14 +19,19 @@ export class DockerNetwork {
return dockerNetworks; return dockerNetworks;
} }
public static async getNetworkByName(dockerHost: DockerHost, dockerNetworkNameArg: string) { public static async getNetworkByName(
dockerHost: DockerHost,
dockerNetworkNameArg: string,
) {
const networks = await DockerNetwork.getNetworks(dockerHost); const networks = await DockerNetwork.getNetworks(dockerHost);
return networks.find((dockerNetwork) => dockerNetwork.Name === dockerNetworkNameArg); return networks.find(
(dockerNetwork) => dockerNetwork.Name === dockerNetworkNameArg,
);
} }
public static async createNetwork( public static async createNetwork(
dockerHost: DockerHost, dockerHost: DockerHost,
networkCreationDescriptor: interfaces.INetworkCreationDescriptor networkCreationDescriptor: interfaces.INetworkCreationDescriptor,
): Promise<DockerNetwork> { ): Promise<DockerNetwork> {
const response = await dockerHost.request('POST', '/networks/create', { const response = await dockerHost.request('POST', '/networks/create', {
Name: networkCreationDescriptor.Name, Name: networkCreationDescriptor.Name,
@@ -47,9 +54,15 @@ export class DockerNetwork {
}); });
if (response.statusCode < 300) { if (response.statusCode < 300) {
logger.log('info', 'Created network successfully'); logger.log('info', 'Created network successfully');
return await DockerNetwork.getNetworkByName(dockerHost, networkCreationDescriptor.Name); return await DockerNetwork.getNetworkByName(
dockerHost,
networkCreationDescriptor.Name,
);
} else { } else {
logger.log('error', 'There has been an error creating the wanted network'); logger.log(
'error',
'There has been an error creating the wanted network',
);
return null; return null;
} }
} }
@@ -75,7 +88,7 @@ export class DockerNetwork {
Subnet: string; Subnet: string;
IPRange: string; IPRange: string;
Gateway: string; Gateway: string;
} },
]; ];
}; };
@@ -87,7 +100,10 @@ export class DockerNetwork {
* removes the network * removes the network
*/ */
public async remove() { public async remove() {
const response = await this.dockerHost.request('DELETE', `/networks/${this.Id}`); const response = await this.dockerHost.request(
'DELETE',
`/networks/${this.Id}`,
);
} }
public async getContainersOnNetwork(): Promise< public async getContainersOnNetwork(): Promise<
@@ -100,7 +116,10 @@ export class DockerNetwork {
}> }>
> { > {
const returnArray = []; const returnArray = [];
const response = await this.dockerHost.request('GET', `/networks/${this.Id}`); const response = await this.dockerHost.request(
'GET',
`/networks/${this.Id}`,
);
for (const key of Object.keys(response.body.Containers)) { for (const key of Object.keys(response.body.Containers)) {
returnArray.push(response.body.Containers[key]); returnArray.push(response.body.Containers[key]);
} }

View File

@@ -22,14 +22,17 @@ export class DockerSecret {
return secrets.find((secret) => secret.ID === idArg); return secrets.find((secret) => secret.ID === idArg);
} }
public static async getSecretByName(dockerHostArg: DockerHost, nameArg: string) { public static async getSecretByName(
dockerHostArg: DockerHost,
nameArg: string,
) {
const secrets = await this.getSecrets(dockerHostArg); const secrets = await this.getSecrets(dockerHostArg);
return secrets.find((secret) => secret.Spec.Name === nameArg); return secrets.find((secret) => secret.Spec.Name === nameArg);
} }
public static async createSecret( public static async createSecret(
dockerHostArg: DockerHost, dockerHostArg: DockerHost,
secretDescriptor: interfaces.ISecretCreationDescriptor secretDescriptor: interfaces.ISecretCreationDescriptor,
) { ) {
const labels: interfaces.TLabels = { const labels: interfaces.TLabels = {
...secretDescriptor.labels, ...secretDescriptor.labels,
@@ -45,7 +48,7 @@ export class DockerSecret {
Object.assign(newSecretInstance, response.body); Object.assign(newSecretInstance, response.body);
Object.assign( Object.assign(
newSecretInstance, newSecretInstance,
await DockerSecret.getSecretByID(dockerHostArg, newSecretInstance.ID) await DockerSecret.getSecretByID(dockerHostArg, newSecretInstance.ID),
); );
return newSecretInstance; return newSecretInstance;
} }
@@ -77,7 +80,7 @@ export class DockerSecret {
Name: this.Spec.Name, Name: this.Spec.Name,
Labels: this.Spec.Labels, Labels: this.Spec.Labels,
Data: plugins.smartstring.base64.encode(contentArg), Data: plugins.smartstring.base64.encode(contentArg),
} },
); );
} }

View File

@@ -21,7 +21,7 @@ export class DockerService {
public static async getServiceByName( public static async getServiceByName(
dockerHost: DockerHost, dockerHost: DockerHost,
networkName: string networkName: string,
): Promise<DockerService> { ): Promise<DockerService> {
const allServices = await DockerService.getServices(dockerHost); const allServices = await DockerService.getServices(dockerHost);
const wantedService = allServices.find((service) => { const wantedService = allServices.find((service) => {
@@ -35,10 +35,13 @@ export class DockerService {
*/ */
public static async createService( public static async createService(
dockerHost: DockerHost, dockerHost: DockerHost,
serviceCreationDescriptor: interfaces.IServiceCreationDescriptor serviceCreationDescriptor: interfaces.IServiceCreationDescriptor,
): Promise<DockerService> { ): Promise<DockerService> {
// lets get the image // lets get the image
logger.log('info', `now creating service ${serviceCreationDescriptor.name}`); logger.log(
'info',
`now creating service ${serviceCreationDescriptor.name}`,
);
// await serviceCreationDescriptor.image.pullLatestImageFromRegistry(); // await serviceCreationDescriptor.image.pullLatestImageFromRegistry();
const serviceVersion = await serviceCreationDescriptor.image.getVersion(); const serviceVersion = await serviceCreationDescriptor.image.getVersion();
@@ -71,8 +74,12 @@ export class DockerService {
}); });
} }
if (serviceCreationDescriptor.resources && serviceCreationDescriptor.resources.volumeMounts) { if (
for (const volumeMount of serviceCreationDescriptor.resources.volumeMounts) { serviceCreationDescriptor.resources &&
serviceCreationDescriptor.resources.volumeMounts
) {
for (const volumeMount of serviceCreationDescriptor.resources
.volumeMounts) {
mounts.push({ mounts.push({
Target: volumeMount.containerFsPath, Target: volumeMount.containerFsPath,
Source: volumeMount.hostFsPath, Source: volumeMount.hostFsPath,
@@ -130,7 +137,8 @@ export class DockerService {
// lets configure limits // lets configure limits
const memoryLimitMB = const memoryLimitMB =
serviceCreationDescriptor.resources && serviceCreationDescriptor.resources.memorySizeMB serviceCreationDescriptor.resources &&
serviceCreationDescriptor.resources.memorySizeMB
? serviceCreationDescriptor.resources.memorySizeMB ? serviceCreationDescriptor.resources.memorySizeMB
: 1000; : 1000;
@@ -139,7 +147,8 @@ export class DockerService {
}; };
if (serviceCreationDescriptor.resources) { if (serviceCreationDescriptor.resources) {
limits.MemoryBytes = serviceCreationDescriptor.resources.memorySizeMB * 1000000; limits.MemoryBytes =
serviceCreationDescriptor.resources.memorySizeMB * 1000000;
} }
const response = await dockerHost.request('POST', '/services/create', { const response = await dockerHost.request('POST', '/services/create', {
@@ -182,7 +191,7 @@ export class DockerService {
const createdService = await DockerService.getServiceByName( const createdService = await DockerService.getServiceByName(
dockerHost, dockerHost,
serviceCreationDescriptor.name serviceCreationDescriptor.name,
); );
return createdService; return createdService;
} }
@@ -228,7 +237,10 @@ export class DockerService {
} }
public async reReadFromDockerEngine() { public async reReadFromDockerEngine() {
const dockerData = await this.dockerHostRef.request('GET', `/services/${this.ID}`); const dockerData = await this.dockerHostRef.request(
'GET',
`/services/${this.ID}`,
);
// TODO: Better assign: Object.assign(this, dockerData); // TODO: Better assign: Object.assign(this, dockerData);
} }
@@ -236,14 +248,21 @@ export class DockerService {
// TODO: implement digest based update recognition // TODO: implement digest based update recognition
await this.reReadFromDockerEngine(); await this.reReadFromDockerEngine();
const dockerImage = await DockerImage.createFromRegistry(this.dockerHostRef, { const dockerImage = await DockerImage.createFromRegistry(
this.dockerHostRef,
{
creationObject: { creationObject: {
imageUrl: this.Spec.TaskTemplate.ContainerSpec.Image, imageUrl: this.Spec.TaskTemplate.ContainerSpec.Image,
} },
}); },
);
const imageVersion = new plugins.smartversion.SmartVersion(dockerImage.Labels.version); const imageVersion = new plugins.smartversion.SmartVersion(
const serviceVersion = new plugins.smartversion.SmartVersion(this.Spec.Labels.version); dockerImage.Labels.version,
);
const serviceVersion = new plugins.smartversion.SmartVersion(
this.Spec.Labels.version,
);
if (imageVersion.greaterThan(serviceVersion)) { if (imageVersion.greaterThan(serviceVersion)) {
console.log(`service ${this.Spec.Name} needs to be updated`); console.log(`service ${this.Spec.Name} needs to be updated`);
return true; return true;

View File

@@ -2,7 +2,7 @@ import * as plugins from './plugins.js';
export const packageDir = plugins.path.resolve( export const packageDir = plugins.path.resolve(
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
'../' '../',
); );
export const nogitDir = plugins.path.resolve(packageDir, '.nogit/'); export const nogitDir = plugins.path.resolve(packageDir, '.nogit/');

View File

@@ -6,9 +6,9 @@
"module": "NodeNext", "module": "NodeNext",
"moduleResolution": "NodeNext", "moduleResolution": "NodeNext",
"esModuleInterop": true, "esModuleInterop": true,
"verbatimModuleSyntax": true "verbatimModuleSyntax": true,
"baseUrl": ".",
"paths": {}
}, },
"exclude": [ "exclude": ["dist_*/**/*.d.ts"]
"dist_*/**/*.d.ts"
]
} }