fix(core): Stabilize CI/workflows and runtime: update CI images/metadata, improve streaming requests and image handling, and fix tests & package metadata
This commit is contained in:
@@ -6,8 +6,8 @@ on:
|
||||
- '**'
|
||||
|
||||
env:
|
||||
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
|
||||
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
|
||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Install pnpm and npmci
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
pnpm install -g @ship.zone/npmci
|
||||
|
||||
- name: Run npm prepare
|
||||
run: npmci npm prepare
|
||||
|
@@ -6,8 +6,8 @@ on:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
|
||||
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
|
||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
pnpm install -g @ship.zone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Audit production dependencies
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
pnpm install -g @ship.zone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Test stable
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
pnpm install -g @ship.zone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Release
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
pnpm install -g @ship.zone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Code quality
|
||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -3,7 +3,6 @@
|
||||
# artifacts
|
||||
coverage/
|
||||
public/
|
||||
pages/
|
||||
|
||||
# installs
|
||||
node_modules/
|
||||
@@ -17,4 +16,8 @@ node_modules/
|
||||
dist/
|
||||
dist_*/
|
||||
|
||||
# custom
|
||||
# AI
|
||||
.claude/
|
||||
.serena/
|
||||
|
||||
#------# custom
|
51
changelog.md
51
changelog.md
@@ -1,6 +1,18 @@
|
||||
# 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.
|
||||
@@ -9,6 +21,7 @@ Increase test timeout, enable DockerImageStore test, update test image name, bum
|
||||
- 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)
|
||||
|
||||
Adjust requestStreaming timeout and autoDrain; stabilize tests
|
||||
|
||||
- Reduced requestStreaming timeout from 10 minutes to 30 seconds to avoid long-running hanging requests.
|
||||
@@ -17,6 +30,7 @@ Adjust requestStreaming timeout and autoDrain; stabilize tests
|
||||
- Added local tool settings file (.claude/settings.local.json) with local permissions (development-only).
|
||||
|
||||
## 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: correct 'tineout' -> 'timeout' for npm test command and set timeout to 120s
|
||||
@@ -26,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
|
||||
|
||||
## 2025-08-18 - 1.3.1 - fix(test)
|
||||
|
||||
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
|
||||
@@ -35,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
|
||||
|
||||
## 2024-12-23 - 1.3.0 - feat(core)
|
||||
|
||||
Initial release of Docker client with TypeScript support
|
||||
|
||||
- Provides easy communication with Docker's remote API from Node.js
|
||||
- Includes implementations for managing Docker services, networks, secrets, containers, and images
|
||||
|
||||
## 2024-12-23 - 1.2.8 - fix(core)
|
||||
|
||||
Improved the image creation process from tar stream in DockerImage class.
|
||||
|
||||
- Enhanced `DockerImage.createFromTarStream` method to handle streamed response and parse imported image details.
|
||||
- Fixed the dependency version for `@push.rocks/smartarchive` in package.json.
|
||||
|
||||
## 2024-10-13 - 1.2.7 - fix(core)
|
||||
|
||||
Prepare patch release with minor fixes and improvements
|
||||
|
||||
|
||||
## 2024-10-13 - 1.2.6 - fix(core)
|
||||
|
||||
Minor refactoring and code quality improvements.
|
||||
|
||||
|
||||
## 2024-10-13 - 1.2.5 - fix(dependencies)
|
||||
|
||||
Update dependencies for stability improvements
|
||||
|
||||
- Updated @push.rocks/smartstream to version ^3.0.46
|
||||
@@ -62,137 +80,160 @@ Update dependencies for stability improvements
|
||||
- Updated @types/node to version 22.7.5
|
||||
|
||||
## 2024-10-13 - 1.2.4 - fix(core)
|
||||
|
||||
Refactored DockerImageStore constructor to remove DockerHost dependency
|
||||
|
||||
- Adjusted DockerImageStore constructor to remove dependency on DockerHost
|
||||
- Updated ts/classes.host.ts to align with DockerImageStore's new constructor signature
|
||||
|
||||
## 2024-08-21 - 1.2.3 - fix(dependencies)
|
||||
|
||||
Update dependencies to the latest versions and fix image export test
|
||||
|
||||
- Updated several dependencies to their latest versions in package.json.
|
||||
- Enabled the previously skipped 'should export images' test.
|
||||
|
||||
## 2024-06-10 - 1.2.1-1.2.2 - Core/General
|
||||
|
||||
General updates and fixes.
|
||||
|
||||
- Fix core update
|
||||
|
||||
## 2024-06-10 - 1.2.0 - Core
|
||||
|
||||
Core updates and bug fixes.
|
||||
|
||||
- Fix core update
|
||||
|
||||
## 2024-06-08 - 1.2.0 - General/Core
|
||||
|
||||
Major release with core enhancements.
|
||||
|
||||
- Processing images with extraction, retagging, repackaging, and long-term storage
|
||||
|
||||
## 2024-06-06 - 1.1.4 - General/Imagestore
|
||||
|
||||
Significant feature addition.
|
||||
|
||||
- Add feature to process images with extraction, retagging, repackaging, and long-term storage
|
||||
|
||||
## 2024-05-08 - 1.0.112 - Images
|
||||
|
||||
Add new functionality for image handling.
|
||||
|
||||
- Can now import and export images
|
||||
- Start work on local 100% JS OCI image registry
|
||||
|
||||
## 2024-06-05 - 1.1.0-1.1.3 - Core
|
||||
|
||||
Regular updates and fixes.
|
||||
|
||||
- Fix core update
|
||||
|
||||
## 2024-02-02 - 1.0.105-1.0.110 - Core
|
||||
|
||||
Routine core updates and fixes.
|
||||
|
||||
- Fix core update
|
||||
|
||||
## 2022-10-17 - 1.0.103-1.0.104 - Core
|
||||
|
||||
Routine core updates.
|
||||
|
||||
- Fix core update
|
||||
|
||||
## 2020-10-01 - 1.0.99-1.0.102 - Core
|
||||
|
||||
Routine core updates.
|
||||
|
||||
- Fix core update
|
||||
|
||||
## 2019-09-22 - 1.0.73-1.0.78 - Core
|
||||
|
||||
Routine updates and core fixes.
|
||||
|
||||
- Fix core update
|
||||
|
||||
## 2019-09-13 - 1.0.60-1.0.72 - Core
|
||||
|
||||
Routine updates and core fixes.
|
||||
|
||||
- Fix core update
|
||||
|
||||
## 2019-08-16 - 1.0.43-1.0.59 - Core
|
||||
|
||||
Routine updates and core fixes.
|
||||
|
||||
- Fix core update
|
||||
|
||||
## 2019-08-15 - 1.0.37-1.0.42 - Core
|
||||
|
||||
Routine updates and core fixes.
|
||||
|
||||
- Fix core update
|
||||
|
||||
## 2019-08-14 - 1.0.31-1.0.36 - Core
|
||||
|
||||
Routine updates and core fixes.
|
||||
|
||||
- Fix core update
|
||||
|
||||
## 2019-01-10 - 1.0.27-1.0.30 - Core
|
||||
|
||||
Routine updates and core fixes.
|
||||
|
||||
- Fix core update
|
||||
|
||||
## 2018-07-16 - 1.0.23-1.0.24 - Core
|
||||
|
||||
Routine updates and core fixes.
|
||||
|
||||
- Fix core shift to new style
|
||||
|
||||
## 2017-07-16 - 1.0.20-1.0.22 - General
|
||||
|
||||
Routine updates and fixes.
|
||||
|
||||
- Update node_modules within npmdocker
|
||||
|
||||
## 2017-04-02 - 1.0.18-1.0.19 - General
|
||||
|
||||
Routine updates and fixes.
|
||||
|
||||
- Work with npmdocker and npmts 7.x.x
|
||||
- CI updates
|
||||
|
||||
## 2016-07-31 - 1.0.17 - General
|
||||
|
||||
Enhancements and fixes.
|
||||
|
||||
- Now waiting for response to be stored before ending streaming request
|
||||
- Cosmetic fix
|
||||
|
||||
## 2016-07-29 - 1.0.14-1.0.16 - General
|
||||
|
||||
Multiple updates and features added.
|
||||
|
||||
- Fix request for change observable and add npmdocker
|
||||
- Add request typings
|
||||
|
||||
## 2016-07-28 - 1.0.13 - Core
|
||||
|
||||
Fixes and preparations.
|
||||
|
||||
- Fixed request for newer docker
|
||||
- Prepare for npmdocker
|
||||
|
||||
|
||||
## 2016-06-16 - 1.0.0-1.0.2 - General
|
||||
|
||||
Initial sequence of releases, significant feature additions and CI setups.
|
||||
|
||||
- Implement container start and stop
|
||||
- Implement list containers and related functions
|
||||
- Add tests with in docker environment
|
||||
- Add tests with in docker environment
|
||||
|
||||
## 2016-04-12 - unknown - Initial Commit
|
||||
|
||||
Initial project setup.
|
||||
|
||||
- Initial commit
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"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)",
|
||||
"dockerSock": true
|
||||
},
|
||||
@@ -12,11 +12,11 @@
|
||||
"gitzone": {
|
||||
"projectType": "npm",
|
||||
"module": {
|
||||
"githost": "gitlab.com",
|
||||
"gitscope": "mojoio",
|
||||
"githost": "code.foss.global",
|
||||
"gitscope": "apiclient.xyz",
|
||||
"gitrepo": "docker",
|
||||
"description": "Provides easy communication with Docker remote API from Node.js, with TypeScript support.",
|
||||
"npmPackagename": "@mojoio/docker",
|
||||
"npmPackagename": "@apiclient.xyz/docker",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"Docker",
|
||||
@@ -34,4 +34,4 @@
|
||||
"tsdoc": {
|
||||
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
package.json
11
package.json
@@ -13,7 +13,7 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://gitlab.com/mojoio/docker.git"
|
||||
"url": "https://code.foss.global/apiclient.xyz/docker.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Docker",
|
||||
@@ -29,9 +29,9 @@
|
||||
"author": "Lossless GmbH",
|
||||
"license": "MIT",
|
||||
"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": {
|
||||
"@push.rocks/lik": "^6.2.2",
|
||||
"@push.rocks/smartarchive": "^4.2.2",
|
||||
@@ -72,5 +72,8 @@
|
||||
"browserslist": [
|
||||
"last 1 chrome versions"
|
||||
],
|
||||
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748"
|
||||
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748",
|
||||
"pnpm": {
|
||||
"overrides": {}
|
||||
}
|
||||
}
|
||||
|
137
readme.md
137
readme.md
@@ -38,7 +38,7 @@ const docker = new DockerHost();
|
||||
|
||||
// Or connect to remote Docker host
|
||||
const remoteDocker = new DockerHost({
|
||||
socketPath: 'tcp://remote-docker-host:2375'
|
||||
socketPath: 'tcp://remote-docker-host:2375',
|
||||
});
|
||||
```
|
||||
|
||||
@@ -56,9 +56,9 @@ const docker = new DockerHost();
|
||||
|
||||
// Custom initialization options
|
||||
const customDocker = new DockerHost({
|
||||
socketPath: '/var/run/docker.sock', // Unix socket path
|
||||
socketPath: '/var/run/docker.sock', // Unix socket path
|
||||
// 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)
|
||||
@@ -76,7 +76,7 @@ await docker.stop();
|
||||
const allContainers = await docker.getContainers();
|
||||
|
||||
// Each container includes detailed information
|
||||
allContainers.forEach(container => {
|
||||
allContainers.forEach((container) => {
|
||||
console.log(`Container: ${container.Names[0]}`);
|
||||
console.log(` ID: ${container.Id}`);
|
||||
console.log(` Status: ${container.Status}`);
|
||||
@@ -96,21 +96,18 @@ const container = await DockerContainer.create(docker, {
|
||||
name: 'my-nginx-server',
|
||||
HostConfig: {
|
||||
PortBindings: {
|
||||
'80/tcp': [{ HostPort: '8080' }]
|
||||
'80/tcp': [{ HostPort: '8080' }],
|
||||
},
|
||||
RestartPolicy: {
|
||||
Name: 'unless-stopped'
|
||||
Name: 'unless-stopped',
|
||||
},
|
||||
Memory: 512 * 1024 * 1024, // 512MB memory limit
|
||||
},
|
||||
Env: [
|
||||
'NODE_ENV=production',
|
||||
'LOG_LEVEL=info'
|
||||
],
|
||||
Env: ['NODE_ENV=production', 'LOG_LEVEL=info'],
|
||||
Labels: {
|
||||
'app': 'web-server',
|
||||
'environment': 'production'
|
||||
}
|
||||
app: 'web-server',
|
||||
environment: 'production',
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`Container created: ${container.Id}`);
|
||||
@@ -124,7 +121,10 @@ console.log(`Container created: ${container.Id}`);
|
||||
#### Get Container by ID
|
||||
|
||||
```typescript
|
||||
const container = await DockerContainer.getContainerById(docker, 'container-id-here');
|
||||
const container = await DockerContainer.getContainerById(
|
||||
docker,
|
||||
'container-id-here',
|
||||
);
|
||||
if (container) {
|
||||
console.log(`Found container: ${container.Names[0]}`);
|
||||
}
|
||||
@@ -142,7 +142,7 @@ const image = await DockerImage.createFromRegistry(docker, {
|
||||
imageName: 'node',
|
||||
imageTag: '18-alpine',
|
||||
// Optional: provide registry authentication
|
||||
authToken: 'your-registry-auth-token'
|
||||
authToken: 'your-registry-auth-token',
|
||||
});
|
||||
|
||||
console.log(`Image pulled: ${image.RepoTags[0]}`);
|
||||
@@ -159,7 +159,7 @@ const tarStream = fs.createReadStream('./my-image.tar');
|
||||
const importedImage = await DockerImage.createFromTarStream(docker, {
|
||||
tarStream,
|
||||
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', {
|
||||
registry: 'myregistry.com',
|
||||
imageName: 'my-node-app',
|
||||
imageTag: 'v2.0.0'
|
||||
imageTag: 'v2.0.0',
|
||||
});
|
||||
// Result: myregistry.com/my-node-app:v2.0.0
|
||||
```
|
||||
@@ -201,15 +201,17 @@ const network = await DockerNetwork.createNetwork(docker, {
|
||||
EnableIPv6: false,
|
||||
IPAM: {
|
||||
Driver: 'default',
|
||||
Config: [{
|
||||
Subnet: '172.28.0.0/16',
|
||||
Gateway: '172.28.0.1'
|
||||
}]
|
||||
Config: [
|
||||
{
|
||||
Subnet: '172.28.0.0/16',
|
||||
Gateway: '172.28.0.1',
|
||||
},
|
||||
],
|
||||
},
|
||||
Labels: {
|
||||
'project': 'my-app',
|
||||
'environment': 'production'
|
||||
}
|
||||
project: 'my-app',
|
||||
environment: 'production',
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`Network created: ${network.Id}`);
|
||||
@@ -220,14 +222,17 @@ console.log(`Network created: ${network.Id}`);
|
||||
```typescript
|
||||
// Get all networks
|
||||
const networks = await docker.getNetworks();
|
||||
networks.forEach(net => {
|
||||
networks.forEach((net) => {
|
||||
console.log(`Network: ${net.Name} (${net.Driver})`);
|
||||
console.log(` Scope: ${net.Scope}`);
|
||||
console.log(` Internal: ${net.Internal}`);
|
||||
});
|
||||
|
||||
// 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
|
||||
const containers = await appNetwork.getContainersOnNetwork();
|
||||
@@ -246,28 +251,32 @@ const service = await DockerService.createService(docker, {
|
||||
name: 'web-api',
|
||||
image: 'my-api:latest',
|
||||
replicas: 3,
|
||||
ports: [{
|
||||
Protocol: 'tcp',
|
||||
PublishedPort: 80,
|
||||
TargetPort: 3000
|
||||
}],
|
||||
ports: [
|
||||
{
|
||||
Protocol: 'tcp',
|
||||
PublishedPort: 80,
|
||||
TargetPort: 3000,
|
||||
},
|
||||
],
|
||||
networks: ['my-app-network'],
|
||||
labels: {
|
||||
'app': 'api',
|
||||
'version': '2.0.0'
|
||||
app: 'api',
|
||||
version: '2.0.0',
|
||||
},
|
||||
resources: {
|
||||
limits: {
|
||||
Memory: 256 * 1024 * 1024, // 256MB
|
||||
CPUs: 0.5
|
||||
}
|
||||
CPUs: 0.5,
|
||||
},
|
||||
},
|
||||
secrets: ['api-key', 'db-password'],
|
||||
mounts: [{
|
||||
Target: '/data',
|
||||
Source: 'app-data',
|
||||
Type: 'volume'
|
||||
}]
|
||||
mounts: [
|
||||
{
|
||||
Target: '/data',
|
||||
Source: 'app-data',
|
||||
Type: 'volume',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log(`Service deployed: ${service.ID}`);
|
||||
@@ -278,7 +287,7 @@ console.log(`Service deployed: ${service.ID}`);
|
||||
```typescript
|
||||
// List all services
|
||||
const services = await docker.getServices();
|
||||
services.forEach(service => {
|
||||
services.forEach((service) => {
|
||||
console.log(`Service: ${service.Spec.Name}`);
|
||||
console.log(` Replicas: ${service.Spec.Mode.Replicated.Replicas}`);
|
||||
console.log(` Image: ${service.Spec.TaskTemplate.ContainerSpec.Image}`);
|
||||
@@ -307,16 +316,16 @@ const secret = await DockerSecret.createSecret(docker, {
|
||||
name: 'api-key',
|
||||
data: Buffer.from('super-secret-key-123').toString('base64'),
|
||||
labels: {
|
||||
'app': 'my-app',
|
||||
'type': 'api-key'
|
||||
}
|
||||
app: 'my-app',
|
||||
type: 'api-key',
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`Secret created: ${secret.ID}`);
|
||||
|
||||
// List secrets
|
||||
const secrets = await DockerSecret.getSecrets(docker);
|
||||
secrets.forEach(secret => {
|
||||
secrets.forEach((secret) => {
|
||||
console.log(`Secret: ${secret.Spec.Name}`);
|
||||
});
|
||||
|
||||
@@ -325,7 +334,7 @@ const apiKeySecret = await DockerSecret.getSecretByName(docker, 'api-key');
|
||||
|
||||
// Update secret
|
||||
await apiKeySecret.update({
|
||||
data: Buffer.from('new-secret-key-456').toString('base64')
|
||||
data: Buffer.from('new-secret-key-456').toString('base64'),
|
||||
});
|
||||
|
||||
// Remove secret
|
||||
@@ -342,7 +351,7 @@ await docker.addS3Storage({
|
||||
endpoint: 's3.amazonaws.com',
|
||||
accessKeyId: 'your-access-key',
|
||||
secretAccessKey: 'your-secret-key',
|
||||
bucket: 'docker-images'
|
||||
bucket: 'docker-images',
|
||||
});
|
||||
|
||||
// Store an image to S3
|
||||
@@ -368,7 +377,7 @@ const subscription = eventStream.subscribe({
|
||||
console.log(`Time: ${new Date(event.time * 1000).toISOString()}`);
|
||||
},
|
||||
error: (err) => console.error('Event stream error:', err),
|
||||
complete: () => console.log('Event stream completed')
|
||||
complete: () => console.log('Event stream completed'),
|
||||
});
|
||||
|
||||
// Unsubscribe when done
|
||||
@@ -384,7 +393,7 @@ Authenticate with Docker registries for private images:
|
||||
await docker.auth({
|
||||
username: 'your-username',
|
||||
password: 'your-password',
|
||||
serveraddress: 'https://index.docker.io/v1/'
|
||||
serveraddress: 'https://index.docker.io/v1/',
|
||||
});
|
||||
|
||||
// Or use existing Docker config
|
||||
@@ -394,7 +403,7 @@ const authToken = await docker.getAuthTokenFromDockerConfig('myregistry.com');
|
||||
const privateImage = await DockerImage.createFromRegistry(docker, {
|
||||
imageName: 'myregistry.com/private/image',
|
||||
imageTag: 'latest',
|
||||
authToken
|
||||
authToken,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -407,14 +416,14 @@ Initialize and manage Docker Swarm:
|
||||
await docker.activateSwarm({
|
||||
ListenAddr: '0.0.0.0:2377',
|
||||
AdvertiseAddr: '192.168.1.100:2377',
|
||||
ForceNewCluster: false
|
||||
ForceNewCluster: false,
|
||||
});
|
||||
|
||||
// Now you can create services, secrets, and use swarm features
|
||||
const service = await DockerService.createService(docker, {
|
||||
name: 'my-swarm-service',
|
||||
image: 'nginx:latest',
|
||||
replicas: 5
|
||||
replicas: 5,
|
||||
// ... more service config
|
||||
});
|
||||
```
|
||||
@@ -426,37 +435,37 @@ const service = await DockerService.createService(docker, {
|
||||
```typescript
|
||||
async function deployStack() {
|
||||
const docker = new DockerHost();
|
||||
|
||||
|
||||
// Create network
|
||||
const network = await DockerNetwork.createNetwork(docker, {
|
||||
Name: 'app-network',
|
||||
Driver: 'overlay' // for swarm mode
|
||||
Driver: 'overlay', // for swarm mode
|
||||
});
|
||||
|
||||
|
||||
// Create secrets
|
||||
const dbPassword = await DockerSecret.createSecret(docker, {
|
||||
name: 'db-password',
|
||||
data: Buffer.from('strong-password').toString('base64')
|
||||
data: Buffer.from('strong-password').toString('base64'),
|
||||
});
|
||||
|
||||
|
||||
// Deploy database service
|
||||
const dbService = await DockerService.createService(docker, {
|
||||
name: 'postgres',
|
||||
image: 'postgres:14',
|
||||
networks: ['app-network'],
|
||||
secrets: ['db-password'],
|
||||
env: ['POSTGRES_PASSWORD_FILE=/run/secrets/db-password']
|
||||
env: ['POSTGRES_PASSWORD_FILE=/run/secrets/db-password'],
|
||||
});
|
||||
|
||||
|
||||
// Deploy application service
|
||||
const appService = await DockerService.createService(docker, {
|
||||
name: 'web-app',
|
||||
image: 'my-app:latest',
|
||||
replicas: 3,
|
||||
networks: ['app-network'],
|
||||
ports: [{ Protocol: 'tcp', PublishedPort: 80, TargetPort: 3000 }]
|
||||
ports: [{ Protocol: 'tcp', PublishedPort: 80, TargetPort: 3000 }],
|
||||
});
|
||||
|
||||
|
||||
console.log('Stack deployed successfully!');
|
||||
}
|
||||
```
|
||||
@@ -471,7 +480,7 @@ import type {
|
||||
IServiceCreationDescriptor,
|
||||
INetworkCreationDescriptor,
|
||||
IImageCreationDescriptor,
|
||||
ISecretCreationDescriptor
|
||||
ISecretCreationDescriptor,
|
||||
} from '@apiclient.xyz/docker';
|
||||
|
||||
// Full IntelliSense support for all configuration options
|
||||
@@ -493,7 +502,7 @@ For Docker Remote API reference, see [Docker Engine API Documentation](https://d
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||
|
||||
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
||||
|
||||
@@ -508,4 +517,4 @@ Registered at District court Bremen HRB 35230 HB, Germany
|
||||
|
||||
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
||||
|
||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
||||
|
@@ -41,7 +41,10 @@ tap.test('should create 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();
|
||||
});
|
||||
|
||||
@@ -78,7 +81,10 @@ tap.test('should create a secret', 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();
|
||||
});
|
||||
|
||||
@@ -102,11 +108,14 @@ tap.test('should create a service', async () => {
|
||||
labels: {},
|
||||
contentArg: '{"hi": "wow"}',
|
||||
});
|
||||
const testImage = await docker.DockerImage.createFromRegistry(testDockerHost, {
|
||||
creationObject: {
|
||||
imageUrl: 'code.foss.global/host.today/ht-docker-node:latest',
|
||||
}
|
||||
});
|
||||
const testImage = await docker.DockerImage.createFromRegistry(
|
||||
testDockerHost,
|
||||
{
|
||||
creationObject: {
|
||||
imageUrl: 'code.foss.global/host.today/ht-docker-node:latest',
|
||||
},
|
||||
},
|
||||
);
|
||||
const testService = await docker.DockerService.createService(testDockerHost, {
|
||||
image: testImage,
|
||||
labels: {},
|
||||
@@ -124,13 +133,16 @@ tap.test('should create a service', async () => {
|
||||
|
||||
tap.test('should export images', async (toolsArg) => {
|
||||
const done = toolsArg.defer();
|
||||
const testImage = await docker.DockerImage.createFromRegistry(testDockerHost, {
|
||||
creationObject: {
|
||||
imageUrl: 'code.foss.global/host.today/ht-docker-node:latest',
|
||||
}
|
||||
});
|
||||
const testImage = await docker.DockerImage.createFromRegistry(
|
||||
testDockerHost,
|
||||
{
|
||||
creationObject: {
|
||||
imageUrl: 'code.foss.global/host.today/ht-docker-node:latest',
|
||||
},
|
||||
},
|
||||
);
|
||||
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();
|
||||
exportStream.pipe(fsWriteStream).on('finish', () => {
|
||||
@@ -141,14 +153,17 @@ tap.test('should export images', async (toolsArg) => {
|
||||
|
||||
tap.test('should import images', async () => {
|
||||
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,
|
||||
{
|
||||
tarStream: fsReadStream,
|
||||
creationObject: {
|
||||
imageUrl: 'code.foss.global/host.today/ht-docker-node:latest',
|
||||
},
|
||||
},
|
||||
);
|
||||
const importedImage = await docker.DockerImage.createFromTarStream(testDockerHost, {
|
||||
tarStream: fsReadStream,
|
||||
creationObject: {
|
||||
imageUrl: 'code.foss.global/host.today/ht-docker-node:latest',
|
||||
}
|
||||
});
|
||||
expect(importedImage).toBeInstanceOf(docker.DockerImage);
|
||||
});
|
||||
|
||||
@@ -163,11 +178,16 @@ tap.test('should expose a working DockerImageStore', async () => {
|
||||
await testDockerHost.addS3Storage(s3Descriptor);
|
||||
|
||||
//
|
||||
await testDockerHost.imageStore.storeImage('hello2', 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 () => {
|
||||
await testDockerHost.stop();
|
||||
})
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@apiclient.xyz/docker',
|
||||
version: '1.3.4',
|
||||
version: '1.3.5',
|
||||
description: 'Provides easy communication with Docker remote API from Node.js, with TypeScript support.'
|
||||
}
|
||||
|
@@ -10,7 +10,9 @@ export class DockerContainer {
|
||||
/**
|
||||
* get all containers
|
||||
*/
|
||||
public static async getContainers(dockerHostArg: DockerHost): Promise<DockerContainer[]> {
|
||||
public static async getContainers(
|
||||
dockerHostArg: DockerHost,
|
||||
): Promise<DockerContainer[]> {
|
||||
const result: DockerContainer[] = [];
|
||||
const response = await dockerHostArg.request('GET', '/containers/json');
|
||||
|
||||
@@ -34,7 +36,7 @@ export class DockerContainer {
|
||||
*/
|
||||
public static async create(
|
||||
dockerHost: DockerHost,
|
||||
containerCreationDescriptor: interfaces.IContainerCreationDescriptor
|
||||
containerCreationDescriptor: interfaces.IContainerCreationDescriptor,
|
||||
) {
|
||||
// check for unique hostname
|
||||
const existingContainers = await DockerContainer.getContainers(dockerHost);
|
||||
@@ -50,7 +52,10 @@ export class DockerContainer {
|
||||
if (response.statusCode < 300) {
|
||||
logger.log('info', 'Container created successfully');
|
||||
} 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',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -37,10 +37,13 @@ export class DockerHost {
|
||||
constructor(optionsArg: IDockerHostConstructorOptions) {
|
||||
this.options = {
|
||||
...{
|
||||
imageStoreDir: plugins.path.join(paths.nogitDir, 'temp-docker-image-store'),
|
||||
imageStoreDir: plugins.path.join(
|
||||
paths.nogitDir,
|
||||
'temp-docker-image-store',
|
||||
),
|
||||
},
|
||||
...optionsArg,
|
||||
}
|
||||
};
|
||||
let pathToUse: string;
|
||||
if (optionsArg.dockerSockPath) {
|
||||
pathToUse = optionsArg.dockerSockPath;
|
||||
@@ -62,7 +65,7 @@ export class DockerHost {
|
||||
this.imageStore = new DockerImageStore({
|
||||
bucketDir: null,
|
||||
localDirPath: this.options.imageStoreDir,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
public async start() {
|
||||
@@ -84,17 +87,22 @@ export class DockerHost {
|
||||
throw new Error(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
|
||||
*/
|
||||
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 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(':');
|
||||
await this.auth({
|
||||
username: gitlabAuthArray[0],
|
||||
@@ -116,7 +124,9 @@ export class DockerHost {
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -127,7 +137,6 @@ export class DockerHost {
|
||||
return await DockerNetwork.getNetworkByName(this, networkNameArg);
|
||||
}
|
||||
|
||||
|
||||
// ==============
|
||||
// CONTAINERS
|
||||
// ==============
|
||||
@@ -226,7 +235,7 @@ export class DockerHost {
|
||||
*/
|
||||
public async request(methodArg: string, routeArg: string, dataArg = {}) {
|
||||
const requestUrl = `${this.socketPath}${routeArg}`;
|
||||
|
||||
|
||||
// Build the request using the fluent API
|
||||
const smartRequest = plugins.smartrequest.SmartRequest.create()
|
||||
.url(requestUrl)
|
||||
@@ -234,12 +243,12 @@ export class DockerHost {
|
||||
.header('X-Registry-Auth', this.registryToken)
|
||||
.header('Host', 'docker.sock')
|
||||
.options({ keepAlive: false });
|
||||
|
||||
|
||||
// Add body for methods that support it
|
||||
if (dataArg && Object.keys(dataArg).length > 0) {
|
||||
smartRequest.json(dataArg);
|
||||
}
|
||||
|
||||
|
||||
// Execute the request based on method
|
||||
let response;
|
||||
switch (methodArg.toUpperCase()) {
|
||||
@@ -258,23 +267,28 @@ export class DockerHost {
|
||||
default:
|
||||
throw new Error(`Unsupported HTTP method: ${methodArg}`);
|
||||
}
|
||||
|
||||
|
||||
// Parse the response body based on content type
|
||||
let body;
|
||||
const contentType = response.headers['content-type'] || '';
|
||||
|
||||
|
||||
// Docker's streaming endpoints (like /images/create) return newline-delimited JSON
|
||||
// which can't be parsed as a single JSON object
|
||||
const isStreamingEndpoint = routeArg.includes('/images/create') ||
|
||||
routeArg.includes('/images/load') ||
|
||||
routeArg.includes('/build');
|
||||
|
||||
const isStreamingEndpoint =
|
||||
routeArg.includes('/images/create') ||
|
||||
routeArg.includes('/images/load') ||
|
||||
routeArg.includes('/build');
|
||||
|
||||
if (contentType.includes('application/json') && !isStreamingEndpoint) {
|
||||
body = await response.json();
|
||||
} else {
|
||||
body = await response.text();
|
||||
// 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 {
|
||||
body = JSON.parse(body);
|
||||
} catch {
|
||||
@@ -282,24 +296,28 @@ export class DockerHost {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create a response object compatible with existing code
|
||||
const legacyResponse = {
|
||||
statusCode: response.status,
|
||||
body: body,
|
||||
headers: response.headers
|
||||
headers: response.headers,
|
||||
};
|
||||
|
||||
|
||||
if (response.status !== 200) {
|
||||
console.log(body);
|
||||
}
|
||||
|
||||
|
||||
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}`;
|
||||
|
||||
|
||||
// Build the request using the fluent API
|
||||
const smartRequest = plugins.smartrequest.SmartRequest.create()
|
||||
.url(requestUrl)
|
||||
@@ -308,7 +326,7 @@ export class DockerHost {
|
||||
.header('Host', 'docker.sock')
|
||||
.timeout(30000)
|
||||
.options({ keepAlive: false, autoDrain: true }); // Disable auto-drain for streaming
|
||||
|
||||
|
||||
// If we have a readStream, use the new stream method with logging
|
||||
if (readStream) {
|
||||
let counter = 0;
|
||||
@@ -319,16 +337,16 @@ export class DockerHost {
|
||||
}
|
||||
counter++;
|
||||
return chunkArg;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
// Pipe through the logging duplex stream
|
||||
const loggedStream = readStream.pipe(smartduplex);
|
||||
|
||||
|
||||
// Use the new stream method to stream the data
|
||||
smartRequest.stream(loggedStream, 'application/octet-stream');
|
||||
}
|
||||
|
||||
|
||||
// Execute the request based on method
|
||||
let response;
|
||||
switch (methodArg.toUpperCase()) {
|
||||
@@ -347,29 +365,29 @@ export class DockerHost {
|
||||
default:
|
||||
throw new Error(`Unsupported HTTP method: ${methodArg}`);
|
||||
}
|
||||
|
||||
|
||||
console.log(response.status);
|
||||
|
||||
|
||||
// For streaming responses, get the Node.js stream
|
||||
const nodeStream = response.streamNode();
|
||||
|
||||
|
||||
if (!nodeStream) {
|
||||
// If no stream is available, consume the body as text
|
||||
const body = await response.text();
|
||||
console.log(body);
|
||||
|
||||
|
||||
// Return a compatible response object
|
||||
return {
|
||||
statusCode: response.status,
|
||||
body: body,
|
||||
headers: response.headers
|
||||
headers: response.headers,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// For streaming responses, return the stream with added properties
|
||||
(nodeStream as any).statusCode = response.status;
|
||||
(nodeStream as any).body = ''; // For compatibility
|
||||
|
||||
|
||||
return nodeStream;
|
||||
}
|
||||
|
||||
@@ -382,10 +400,14 @@ export class DockerHost {
|
||||
if (!optionsArg.bucketName) {
|
||||
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();
|
||||
if (optionsArg.directoryPath) {
|
||||
wantedDirectory = await wantedDirectory.getSubDirectoryByName(optionsArg.directoryPath);
|
||||
wantedDirectory = await wantedDirectory.getSubDirectoryByName(
|
||||
optionsArg.directoryPath,
|
||||
);
|
||||
}
|
||||
this.imageStore.options.bucketDir = wantedDirectory;
|
||||
}
|
||||
|
@@ -17,7 +17,10 @@ export class DockerImage {
|
||||
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 result = images.find((image) => {
|
||||
if (image.RepoTags) {
|
||||
@@ -32,8 +35,8 @@ export class DockerImage {
|
||||
public static async createFromRegistry(
|
||||
dockerHostArg: DockerHost,
|
||||
optionsArg: {
|
||||
creationObject: interfaces.IImageCreationDescriptor
|
||||
}
|
||||
creationObject: interfaces.IImageCreationDescriptor;
|
||||
},
|
||||
): Promise<DockerImage> {
|
||||
// lets create a sanatized imageUrlObject
|
||||
const imageUrlObject: {
|
||||
@@ -50,7 +53,7 @@ export class DockerImage {
|
||||
const imageTag = imageUrlObject.imageUrl.split(':')[1];
|
||||
if (imageUrlObject.imageTag) {
|
||||
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 {
|
||||
imageUrlObject.imageUrl = imageUrl;
|
||||
@@ -65,12 +68,18 @@ export class DockerImage {
|
||||
const response = await dockerHostArg.request(
|
||||
'POST',
|
||||
`/images/create?fromImage=${encodeURIComponent(
|
||||
imageUrlObject.imageUrl
|
||||
)}&tag=${encodeURIComponent(imageUrlObject.imageTag)}`
|
||||
imageUrlObject.imageUrl,
|
||||
)}&tag=${encodeURIComponent(imageUrlObject.imageTag)}`,
|
||||
);
|
||||
if (response.statusCode < 300) {
|
||||
logger.log('info', `Successfully pulled image ${imageUrlObject.imageUrl} from the registry`);
|
||||
const image = await DockerImage.getImageByName(dockerHostArg, imageUrlObject.imageOriginTag);
|
||||
logger.log(
|
||||
'info',
|
||||
`Successfully pulled image ${imageUrlObject.imageUrl} from the registry`,
|
||||
);
|
||||
const image = await DockerImage.getImageByName(
|
||||
dockerHostArg,
|
||||
imageUrlObject.imageOriginTag,
|
||||
);
|
||||
return image;
|
||||
} else {
|
||||
logger.log('error', `Failed at the attempt of creating a new image`);
|
||||
@@ -78,7 +87,7 @@ export class DockerImage {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param dockerHostArg
|
||||
* @param tarStreamArg
|
||||
*/
|
||||
@@ -87,13 +96,13 @@ export class DockerImage {
|
||||
optionsArg: {
|
||||
creationObject: interfaces.IImageCreationDescriptor;
|
||||
tarStream: plugins.smartstream.stream.Readable;
|
||||
}
|
||||
},
|
||||
): Promise<DockerImage> {
|
||||
// Start the request for importing an image
|
||||
const response = await dockerHostArg.requestStreaming(
|
||||
'POST',
|
||||
'/images/load',
|
||||
optionsArg.tarStream
|
||||
optionsArg.tarStream,
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -144,7 +153,7 @@ export class DockerImage {
|
||||
|
||||
if (!loadedImageTag) {
|
||||
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..."
|
||||
// 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.
|
||||
const newlyImportedImage = await DockerImage.getImageByName(dockerHostArg, loadedImageTag);
|
||||
const newlyImportedImage = await DockerImage.getImageByName(
|
||||
dockerHostArg,
|
||||
loadedImageTag,
|
||||
);
|
||||
|
||||
if (!newlyImportedImage) {
|
||||
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(
|
||||
'info',
|
||||
`Successfully imported image "${loadedImageTag}".`
|
||||
);
|
||||
logger.log('info', `Successfully imported image "${loadedImageTag}".`);
|
||||
|
||||
return newlyImportedImage;
|
||||
}
|
||||
|
||||
|
||||
public static async tagImageByIdOrName(
|
||||
dockerHost: DockerHost,
|
||||
idOrNameArg: string,
|
||||
newTagArg: string
|
||||
newTagArg: string,
|
||||
) {
|
||||
const response = await dockerHost.request(
|
||||
'POST',
|
||||
`/images/${encodeURIComponent(idOrNameArg)}/${encodeURIComponent(newTagArg)}`
|
||||
`/images/${encodeURIComponent(idOrNameArg)}/${encodeURIComponent(newTagArg)}`,
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static async buildImage(dockerHostArg: DockerHost, dockerImageTag) {
|
||||
@@ -249,23 +255,25 @@ export class DockerImage {
|
||||
*/
|
||||
public async exportToTarStream(): Promise<plugins.smartstream.stream.Readable> {
|
||||
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
|
||||
if (!response || typeof response.on !== 'function') {
|
||||
throw new Error('Failed to get streaming response for image export');
|
||||
}
|
||||
|
||||
|
||||
let counter = 0;
|
||||
const webduplexStream = new plugins.smartstream.SmartDuplex({
|
||||
writeFunction: async (chunk, tools) => {
|
||||
if (counter % 1000 === 0)
|
||||
console.log(`Got chunk: ${counter}`);
|
||||
if (counter % 1000 === 0) console.log(`Got chunk: ${counter}`);
|
||||
counter++;
|
||||
return chunk;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
response.on('data', (chunk) => {
|
||||
if (!webduplexStream.write(chunk)) {
|
||||
response.pause();
|
||||
@@ -274,16 +282,16 @@ export class DockerImage {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
response.on('end', () => {
|
||||
webduplexStream.end();
|
||||
});
|
||||
|
||||
|
||||
response.on('error', (error) => {
|
||||
logger.log('error', `Error during image export: ${error.message}`);
|
||||
webduplexStream.destroy(error);
|
||||
});
|
||||
|
||||
|
||||
return webduplexStream;
|
||||
}
|
||||
}
|
||||
|
@@ -22,14 +22,25 @@ export class DockerImageStore {
|
||||
}
|
||||
|
||||
// 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}...`);
|
||||
const uniqueProcessingId = plugins.smartunique.shortId();
|
||||
|
||||
const initialTarDownloadPath = plugins.path.join(this.options.localDirPath, `${uniqueProcessingId}.tar`);
|
||||
const extractionDir = plugins.path.join(this.options.localDirPath, uniqueProcessingId);
|
||||
const initialTarDownloadPath = plugins.path.join(
|
||||
this.options.localDirPath,
|
||||
`${uniqueProcessingId}.tar`,
|
||||
);
|
||||
const extractionDir = plugins.path.join(
|
||||
this.options.localDirPath,
|
||||
uniqueProcessingId,
|
||||
);
|
||||
// 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
|
||||
await new Promise((resolve, reject) => {
|
||||
@@ -37,23 +48,43 @@ export class DockerImageStore {
|
||||
writeStream.on('finish', resolve);
|
||||
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
|
||||
const tarArchive = await plugins.smartarchive.SmartArchive.fromArchiveFile(initialTarDownloadPath);
|
||||
const tarArchive = await plugins.smartarchive.SmartArchive.fromArchiveFile(
|
||||
initialTarDownloadPath,
|
||||
);
|
||||
await tarArchive.exportToFs(extractionDir);
|
||||
logger.log('info', `Image ${imageName} extracted.`);
|
||||
await plugins.smartfile.fs.remove(initialTarDownloadPath);
|
||||
logger.log('info', `deleted original tar to save space.`);
|
||||
logger.log('info', `now repackaging for s3...`);
|
||||
const smartfileIndexJson = await plugins.smartfile.SmartFile.fromFilePath(plugins.path.join(extractionDir, 'index.json'));
|
||||
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 smartfileIndexJson = await plugins.smartfile.SmartFile.fromFilePath(
|
||||
plugins.path.join(extractionDir, 'index.json'),
|
||||
);
|
||||
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 manifestJson = JSON.parse(smartfileManifestJson.contents.toString());
|
||||
const ociLayoutJson = JSON.parse(smartfileOciLayoutJson.contents.toString());
|
||||
const repositoriesJson = JSON.parse(smartfileRepositoriesJson.contents.toString());
|
||||
const ociLayoutJson = JSON.parse(
|
||||
smartfileOciLayoutJson.contents.toString(),
|
||||
);
|
||||
const repositoriesJson = JSON.parse(
|
||||
smartfileRepositoriesJson.contents.toString(),
|
||||
);
|
||||
|
||||
indexJson.manifests[0].annotations['io.containerd.image.name'] = imageName;
|
||||
manifestJson[0].RepoTags[0] = imageName;
|
||||
@@ -62,10 +93,18 @@ export class DockerImageStore {
|
||||
repositoriesJson[imageName] = repoFirstValue;
|
||||
delete repositoriesJson[repoFirstKey];
|
||||
|
||||
smartfileIndexJson.contents = Buffer.from(JSON.stringify(indexJson, 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));
|
||||
smartfileIndexJson.contents = Buffer.from(
|
||||
JSON.stringify(indexJson, 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([
|
||||
smartfileIndexJson.write(),
|
||||
smartfileManifestJson.write(),
|
||||
@@ -77,8 +116,12 @@ export class DockerImageStore {
|
||||
const tartools = new plugins.smartarchive.TarTools();
|
||||
const newTarPack = await tartools.packDirectory(extractionDir);
|
||||
const finalTarName = `${uniqueProcessingId}.processed.tar`;
|
||||
const finalTarPath = plugins.path.join(this.options.localDirPath, finalTarName);
|
||||
const finalWriteStream = plugins.smartfile.fsStream.createWriteStream(finalTarPath);
|
||||
const finalTarPath = plugins.path.join(
|
||||
this.options.localDirPath,
|
||||
finalTarName,
|
||||
);
|
||||
const finalWriteStream =
|
||||
plugins.smartfile.fsStream.createWriteStream(finalTarPath);
|
||||
await new Promise((resolve, reject) => {
|
||||
newTarPack.finalize();
|
||||
newTarPack.pipe(finalWriteStream);
|
||||
@@ -87,7 +130,8 @@ export class DockerImageStore {
|
||||
});
|
||||
logger.log('ok', `Repackaged image ${imageName} for s3.`);
|
||||
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({
|
||||
stream: finalTarReadStream,
|
||||
path: `${imageName}.tar`,
|
||||
@@ -102,8 +146,13 @@ export class DockerImageStore {
|
||||
public async stop() {}
|
||||
|
||||
// Method to retrieve tar stream
|
||||
public async getImage(imageName: string): Promise<plugins.smartstream.stream.Readable> {
|
||||
const imagePath = plugins.path.join(this.options.localDirPath, `${imageName}.tar`);
|
||||
public async getImage(
|
||||
imageName: string,
|
||||
): Promise<plugins.smartstream.stream.Readable> {
|
||||
const imagePath = plugins.path.join(
|
||||
this.options.localDirPath,
|
||||
`${imageName}.tar`,
|
||||
);
|
||||
|
||||
if (!(await plugins.smartfile.fs.fileExists(imagePath))) {
|
||||
throw new Error(`Image ${imageName} does not exist.`);
|
||||
|
@@ -6,7 +6,9 @@ import { DockerService } from './classes.service.js';
|
||||
import { logger } from './logger.js';
|
||||
|
||||
export class DockerNetwork {
|
||||
public static async getNetworks(dockerHost: DockerHost): Promise<DockerNetwork[]> {
|
||||
public static async getNetworks(
|
||||
dockerHost: DockerHost,
|
||||
): Promise<DockerNetwork[]> {
|
||||
const dockerNetworks: DockerNetwork[] = [];
|
||||
const response = await dockerHost.request('GET', '/networks');
|
||||
for (const networkObject of response.body) {
|
||||
@@ -17,14 +19,19 @@ export class DockerNetwork {
|
||||
return dockerNetworks;
|
||||
}
|
||||
|
||||
public static async getNetworkByName(dockerHost: DockerHost, dockerNetworkNameArg: string) {
|
||||
public static async getNetworkByName(
|
||||
dockerHost: DockerHost,
|
||||
dockerNetworkNameArg: string,
|
||||
) {
|
||||
const networks = await DockerNetwork.getNetworks(dockerHost);
|
||||
return networks.find((dockerNetwork) => dockerNetwork.Name === dockerNetworkNameArg);
|
||||
return networks.find(
|
||||
(dockerNetwork) => dockerNetwork.Name === dockerNetworkNameArg,
|
||||
);
|
||||
}
|
||||
|
||||
public static async createNetwork(
|
||||
dockerHost: DockerHost,
|
||||
networkCreationDescriptor: interfaces.INetworkCreationDescriptor
|
||||
networkCreationDescriptor: interfaces.INetworkCreationDescriptor,
|
||||
): Promise<DockerNetwork> {
|
||||
const response = await dockerHost.request('POST', '/networks/create', {
|
||||
Name: networkCreationDescriptor.Name,
|
||||
@@ -47,9 +54,15 @@ export class DockerNetwork {
|
||||
});
|
||||
if (response.statusCode < 300) {
|
||||
logger.log('info', 'Created network successfully');
|
||||
return await DockerNetwork.getNetworkByName(dockerHost, networkCreationDescriptor.Name);
|
||||
return await DockerNetwork.getNetworkByName(
|
||||
dockerHost,
|
||||
networkCreationDescriptor.Name,
|
||||
);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
@@ -75,7 +88,7 @@ export class DockerNetwork {
|
||||
Subnet: string;
|
||||
IPRange: string;
|
||||
Gateway: string;
|
||||
}
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@@ -87,7 +100,10 @@ export class DockerNetwork {
|
||||
* removes the network
|
||||
*/
|
||||
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<
|
||||
@@ -100,7 +116,10 @@ export class DockerNetwork {
|
||||
}>
|
||||
> {
|
||||
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)) {
|
||||
returnArray.push(response.body.Containers[key]);
|
||||
}
|
||||
|
@@ -22,14 +22,17 @@ export class DockerSecret {
|
||||
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);
|
||||
return secrets.find((secret) => secret.Spec.Name === nameArg);
|
||||
}
|
||||
|
||||
public static async createSecret(
|
||||
dockerHostArg: DockerHost,
|
||||
secretDescriptor: interfaces.ISecretCreationDescriptor
|
||||
secretDescriptor: interfaces.ISecretCreationDescriptor,
|
||||
) {
|
||||
const labels: interfaces.TLabels = {
|
||||
...secretDescriptor.labels,
|
||||
@@ -45,7 +48,7 @@ export class DockerSecret {
|
||||
Object.assign(newSecretInstance, response.body);
|
||||
Object.assign(
|
||||
newSecretInstance,
|
||||
await DockerSecret.getSecretByID(dockerHostArg, newSecretInstance.ID)
|
||||
await DockerSecret.getSecretByID(dockerHostArg, newSecretInstance.ID),
|
||||
);
|
||||
return newSecretInstance;
|
||||
}
|
||||
@@ -77,7 +80,7 @@ export class DockerSecret {
|
||||
Name: this.Spec.Name,
|
||||
Labels: this.Spec.Labels,
|
||||
Data: plugins.smartstring.base64.encode(contentArg),
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -21,7 +21,7 @@ export class DockerService {
|
||||
|
||||
public static async getServiceByName(
|
||||
dockerHost: DockerHost,
|
||||
networkName: string
|
||||
networkName: string,
|
||||
): Promise<DockerService> {
|
||||
const allServices = await DockerService.getServices(dockerHost);
|
||||
const wantedService = allServices.find((service) => {
|
||||
@@ -35,10 +35,13 @@ export class DockerService {
|
||||
*/
|
||||
public static async createService(
|
||||
dockerHost: DockerHost,
|
||||
serviceCreationDescriptor: interfaces.IServiceCreationDescriptor
|
||||
serviceCreationDescriptor: interfaces.IServiceCreationDescriptor,
|
||||
): Promise<DockerService> {
|
||||
// lets get the image
|
||||
logger.log('info', `now creating service ${serviceCreationDescriptor.name}`);
|
||||
logger.log(
|
||||
'info',
|
||||
`now creating service ${serviceCreationDescriptor.name}`,
|
||||
);
|
||||
|
||||
// await serviceCreationDescriptor.image.pullLatestImageFromRegistry();
|
||||
const serviceVersion = await serviceCreationDescriptor.image.getVersion();
|
||||
@@ -71,8 +74,12 @@ export class DockerService {
|
||||
});
|
||||
}
|
||||
|
||||
if (serviceCreationDescriptor.resources && serviceCreationDescriptor.resources.volumeMounts) {
|
||||
for (const volumeMount of serviceCreationDescriptor.resources.volumeMounts) {
|
||||
if (
|
||||
serviceCreationDescriptor.resources &&
|
||||
serviceCreationDescriptor.resources.volumeMounts
|
||||
) {
|
||||
for (const volumeMount of serviceCreationDescriptor.resources
|
||||
.volumeMounts) {
|
||||
mounts.push({
|
||||
Target: volumeMount.containerFsPath,
|
||||
Source: volumeMount.hostFsPath,
|
||||
@@ -130,7 +137,8 @@ export class DockerService {
|
||||
// lets configure limits
|
||||
|
||||
const memoryLimitMB =
|
||||
serviceCreationDescriptor.resources && serviceCreationDescriptor.resources.memorySizeMB
|
||||
serviceCreationDescriptor.resources &&
|
||||
serviceCreationDescriptor.resources.memorySizeMB
|
||||
? serviceCreationDescriptor.resources.memorySizeMB
|
||||
: 1000;
|
||||
|
||||
@@ -139,7 +147,8 @@ export class DockerService {
|
||||
};
|
||||
|
||||
if (serviceCreationDescriptor.resources) {
|
||||
limits.MemoryBytes = serviceCreationDescriptor.resources.memorySizeMB * 1000000;
|
||||
limits.MemoryBytes =
|
||||
serviceCreationDescriptor.resources.memorySizeMB * 1000000;
|
||||
}
|
||||
|
||||
const response = await dockerHost.request('POST', '/services/create', {
|
||||
@@ -182,7 +191,7 @@ export class DockerService {
|
||||
|
||||
const createdService = await DockerService.getServiceByName(
|
||||
dockerHost,
|
||||
serviceCreationDescriptor.name
|
||||
serviceCreationDescriptor.name,
|
||||
);
|
||||
return createdService;
|
||||
}
|
||||
@@ -228,7 +237,10 @@ export class DockerService {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -236,14 +248,21 @@ export class DockerService {
|
||||
// TODO: implement digest based update recognition
|
||||
|
||||
await this.reReadFromDockerEngine();
|
||||
const dockerImage = await DockerImage.createFromRegistry(this.dockerHostRef, {
|
||||
creationObject: {
|
||||
imageUrl: this.Spec.TaskTemplate.ContainerSpec.Image,
|
||||
}
|
||||
});
|
||||
const dockerImage = await DockerImage.createFromRegistry(
|
||||
this.dockerHostRef,
|
||||
{
|
||||
creationObject: {
|
||||
imageUrl: this.Spec.TaskTemplate.ContainerSpec.Image,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const imageVersion = new plugins.smartversion.SmartVersion(dockerImage.Labels.version);
|
||||
const serviceVersion = new plugins.smartversion.SmartVersion(this.Spec.Labels.version);
|
||||
const imageVersion = new plugins.smartversion.SmartVersion(
|
||||
dockerImage.Labels.version,
|
||||
);
|
||||
const serviceVersion = new plugins.smartversion.SmartVersion(
|
||||
this.Spec.Labels.version,
|
||||
);
|
||||
if (imageVersion.greaterThan(serviceVersion)) {
|
||||
console.log(`service ${this.Spec.Name} needs to be updated`);
|
||||
return true;
|
||||
|
@@ -2,4 +2,4 @@ import * as plugins from './plugins.js';
|
||||
import { commitinfo } from './00_commitinfo_data.js';
|
||||
|
||||
export const logger = plugins.smartlog.Smartlog.createForCommitinfo(commitinfo);
|
||||
logger.enableConsole();
|
||||
logger.enableConsole();
|
||||
|
@@ -2,7 +2,7 @@ import * as plugins from './plugins.js';
|
||||
|
||||
export const packageDir = plugins.path.resolve(
|
||||
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
|
||||
'../'
|
||||
'../',
|
||||
);
|
||||
|
||||
export const nogitDir = plugins.path.resolve(packageDir, '.nogit/');
|
||||
|
@@ -6,9 +6,9 @@
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"esModuleInterop": true,
|
||||
"verbatimModuleSyntax": true
|
||||
"verbatimModuleSyntax": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {}
|
||||
},
|
||||
"exclude": [
|
||||
"dist_*/**/*.d.ts"
|
||||
]
|
||||
"exclude": ["dist_*/**/*.d.ts"]
|
||||
}
|
||||
|
Reference in New Issue
Block a user