init project
This commit is contained in:
+2
@@ -0,0 +1,2 @@
|
||||
# 3rd-party files
|
||||
node_modules
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"env": {
|
||||
"mocha": true,
|
||||
"node": true
|
||||
},
|
||||
"rules": {
|
||||
/* Possible Errors */
|
||||
"no-extra-parens": 2,
|
||||
"valid-jsdoc": 2,
|
||||
/* Best Practices */
|
||||
"block-scoped-var": 2,
|
||||
"complexity": [1, 3],
|
||||
"default-case": 2,
|
||||
"dot-notation": [2],
|
||||
"guard-for-in": 2,
|
||||
"no-div-regex": 2,
|
||||
"no-else-return": 2,
|
||||
"no-eq-null": 2,
|
||||
"no-floating-decimal": 2,
|
||||
"no-process-env": 2,
|
||||
"no-self-compare": 2,
|
||||
"no-void": 2,
|
||||
"no-warning-comments": 2,
|
||||
"radix": 2,
|
||||
"vars-on-top": 2,
|
||||
"wrap-iife": 2,
|
||||
/* Variables */
|
||||
"no-catch-shadow": 2,
|
||||
/* Node.JS */
|
||||
"no-sync": 2,
|
||||
/* Stylistic Issues */
|
||||
"no-mixed-spaces-and-tabs": 2,
|
||||
"no-underscore-dangle": 2,
|
||||
"quotes": [1, "single", "avoid-escape"]
|
||||
}}
|
||||
+1
@@ -0,0 +1 @@
|
||||
github: apocas
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 550
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- question
|
||||
- bug
|
||||
- enhancement
|
||||
- "needs love"
|
||||
- "needs inspection"
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Open a new issue if needed.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build_nodejs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14.x, 16.x, 18.x, 20.x, 22.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Provisioning
|
||||
run: |
|
||||
docker --version
|
||||
node -v
|
||||
docker pull ubuntu
|
||||
|
||||
- name: NPM install
|
||||
run: npm install
|
||||
|
||||
- name: NPM test
|
||||
run: npm test
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
sudo: required
|
||||
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "8"
|
||||
|
||||
before_script:
|
||||
- docker pull ubuntu
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
before_install:
|
||||
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
|
||||
- sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
||||
- sudo apt-get update
|
||||
- sudo apt-get -y install docker-ce
|
||||
|
||||
install:
|
||||
- npm install
|
||||
|
||||
script:
|
||||
- npm test
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"args": [
|
||||
"--colors",
|
||||
"${workspaceFolder}/test",
|
||||
"-R",
|
||||
"spec",
|
||||
"--exit"
|
||||
],
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"name": "Mocha Tests",
|
||||
"program": "./node_modules/mocha/bin/mocha.js",
|
||||
"request": "launch",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node"
|
||||
}
|
||||
]
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+530
@@ -0,0 +1,530 @@
|
||||
# dockerode
|
||||
|
||||
Not another Node.js Docker Remote API module.
|
||||
|
||||
`dockerode` objectives:
|
||||
|
||||
* **streams** - `dockerode` does NOT break any stream, it passes them to you allowing for some stream voodoo.
|
||||
* **stream demux** - Supports optional [stream demultiplexing](https://github.com/apocas/dockerode#helper-functions).
|
||||
* **entities** - containers, images and execs are defined entities and not random static methods.
|
||||
* **run** - `dockerode` allow you to seamless run commands in a container aka `docker run`.
|
||||
* **tests** - `dockerode` really aims to have a good test set, allowing to follow `Docker` changes easily, quickly and painlessly.
|
||||
* **feature-rich** - There's a real effort in keeping **All** `Docker` Remote API features implemented and tested.
|
||||
* **interfaces** - Features **callback** and **promise** based interfaces, making everyone happy :)
|
||||
|
||||
## Ecosystem
|
||||
|
||||
* docker-modem [https://github.com/apocas/docker-modem](https://github.com/apocas/docker-modem) - Docker's API network stack
|
||||
* dockerode-compose [https://github.com/apocas/dockerode-compose](https://github.com/apocas/dockerode-compose) - docker-compose in Node.js
|
||||
|
||||
## Installation
|
||||
|
||||
`npm install dockerode`
|
||||
|
||||
## Usage
|
||||
|
||||
* Input options are directly passed to Docker. Check [Docker API documentation](https://docs.docker.com/engine/api/latest/) for more details.
|
||||
* Return values are unchanged from Docker, official Docker documentation will also apply to them.
|
||||
* Check the tests and examples folder for more examples.
|
||||
|
||||
### Getting started
|
||||
|
||||
To use `dockerode` first you need to instantiate it:
|
||||
|
||||
``` js
|
||||
var Docker = require('dockerode');
|
||||
var docker = new Docker({socketPath: '/var/run/docker.sock'});
|
||||
var docker1 = new Docker(); //defaults to above if env variables are not used
|
||||
var docker2 = new Docker({host: 'http://192.168.1.10', port: 3000});
|
||||
var docker3 = new Docker({protocol:'http', host: '127.0.0.1', port: 3000});
|
||||
var docker4 = new Docker({host: '127.0.0.1', port: 3000}); //defaults to http
|
||||
|
||||
//protocol http vs https is automatically detected
|
||||
var docker5 = new Docker({
|
||||
host: '192.168.1.10',
|
||||
port: process.env.DOCKER_PORT || 2375,
|
||||
ca: fs.readFileSync('ca.pem'),
|
||||
cert: fs.readFileSync('cert.pem'),
|
||||
key: fs.readFileSync('key.pem'),
|
||||
version: 'v1.25' // required when Docker >= v1.13, https://docs.docker.com/engine/api/version-history/
|
||||
});
|
||||
|
||||
var docker6 = new Docker({
|
||||
protocol: 'https', //you can enforce a protocol
|
||||
host: '192.168.1.10',
|
||||
port: process.env.DOCKER_PORT || 2375,
|
||||
ca: fs.readFileSync('ca.pem'),
|
||||
cert: fs.readFileSync('cert.pem'),
|
||||
key: fs.readFileSync('key.pem')
|
||||
});
|
||||
|
||||
//using a different promise library (default is the native one)
|
||||
var docker7 = new Docker({
|
||||
Promise: require('bluebird')
|
||||
//...
|
||||
});
|
||||
//...
|
||||
```
|
||||
|
||||
### Manipulating a container:
|
||||
|
||||
``` js
|
||||
// create a container entity. does not query API
|
||||
var container = docker.getContainer('71501a8ab0f8');
|
||||
|
||||
// query API for container info
|
||||
container.inspect(function (err, data) {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
container.start(function (err, data) {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
container.remove(function (err, data) {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
// promises are supported
|
||||
var auxContainer;
|
||||
docker.createContainer({
|
||||
Image: 'ubuntu',
|
||||
AttachStdin: false,
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
Tty: true,
|
||||
Cmd: ['/bin/bash', '-c', 'tail -f /var/log/dmesg'],
|
||||
OpenStdin: false,
|
||||
StdinOnce: false
|
||||
}).then(function(container) {
|
||||
auxContainer = container;
|
||||
return auxContainer.start();
|
||||
}).then(function(data) {
|
||||
return auxContainer.resize({
|
||||
h: process.stdout.rows,
|
||||
w: process.stdout.columns
|
||||
});
|
||||
}).then(function(data) {
|
||||
return auxContainer.stop();
|
||||
}).then(function(data) {
|
||||
return auxContainer.remove();
|
||||
}).then(function(data) {
|
||||
console.log('container removed');
|
||||
}).catch(function(err) {
|
||||
console.log(err);
|
||||
});
|
||||
```
|
||||
|
||||
You may also specify default options for each container's operations, which will always be used for the specified container and operation.
|
||||
|
||||
``` js
|
||||
container.defaultOptions.start.Binds = ["/tmp:/tmp:rw"];
|
||||
```
|
||||
|
||||
### Stopping all containers on a host
|
||||
|
||||
``` js
|
||||
docker.listContainers(function (err, containers) {
|
||||
containers.forEach(function (containerInfo) {
|
||||
docker.getContainer(containerInfo.Id).stop(cb);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Building an Image
|
||||
Context: provides the path to the Dockerfile. Additionaly files that are involved in the build *must* be explicitly mentioned in src array, since they are sent to a temp env to build. Example: file for COPY command are extracted from that temporary environment.
|
||||
|
||||
``` js
|
||||
docker.buildImage('archive.tar', {t: imageName}, function (err, response){
|
||||
//...
|
||||
});
|
||||
|
||||
docker.buildImage({
|
||||
context: __dirname,
|
||||
src: ['Dockerfile', 'file1', 'file2']
|
||||
}, {t: imageName}, function (err, response) {
|
||||
//...
|
||||
});
|
||||
```
|
||||
|
||||
`buildImage` returns a Promise of NodeJS stream. In case you want to find out when the build has finished, you must follow the progress of the build with the `modem` instance in dockerode:
|
||||
|
||||
``` js
|
||||
let dockerode = new Dockerode();
|
||||
let stream = await dockerode.buildImage(...);
|
||||
await new Promise((resolve, reject) => {
|
||||
dockerode.modem.followProgress(stream, (err, res) => err ? reject(err) : resolve(res));
|
||||
});
|
||||
// Build has finished
|
||||
```
|
||||
|
||||
|
||||
### Creating a container:
|
||||
|
||||
``` js
|
||||
docker.createContainer({Image: 'ubuntu', Cmd: ['/bin/bash'], name: 'ubuntu-test'}, function (err, container) {
|
||||
container.start(function (err, data) {
|
||||
//...
|
||||
});
|
||||
});
|
||||
//...
|
||||
```
|
||||
|
||||
### Streams goodness:
|
||||
|
||||
``` js
|
||||
//tty:true
|
||||
docker.createContainer({ /*...*/ Tty: true /*...*/ }, function(err, container) {
|
||||
|
||||
/* ... */
|
||||
|
||||
container.attach({stream: true, stdout: true, stderr: true}, function (err, stream) {
|
||||
stream.pipe(process.stdout);
|
||||
});
|
||||
|
||||
/* ... */
|
||||
});
|
||||
|
||||
//tty:false
|
||||
docker.createContainer({ /*...*/ Tty: false /*...*/ }, function(err, container) {
|
||||
|
||||
/* ... */
|
||||
|
||||
container.attach({stream: true, stdout: true, stderr: true}, function (err, stream) {
|
||||
//dockerode may demultiplex attach streams for you :)
|
||||
container.modem.demuxStream(stream, process.stdout, process.stderr);
|
||||
});
|
||||
|
||||
/* ... */
|
||||
});
|
||||
|
||||
docker.createImage({fromImage: 'ubuntu'}, function (err, stream) {
|
||||
stream.pipe(process.stdout);
|
||||
});
|
||||
|
||||
//...
|
||||
```
|
||||
|
||||
There is also support for [HTTP connection hijacking](https://docs.docker.com/engine/api/v1.45/#tag/Container/operation/ContainerAttach),
|
||||
which allows for cleaner interactions with commands that work with stdin and stdout separately.
|
||||
|
||||
```js
|
||||
docker.createContainer({Tty: false, /*... other options */}, function(err, container) {
|
||||
container.start(function(err) {
|
||||
container.exec({Cmd: ['shasum', '-'], AttachStdin: true, AttachStdout: true}, function(err, exec) {
|
||||
exec.start({hijack: true, stdin: true}, function(err, stream) {
|
||||
// shasum can't finish until after its stdin has been closed, telling it that it has
|
||||
// read all the bytes it needs to sum. Without a socket upgrade, there is no way to
|
||||
// close the write-side of the stream without also closing the read-side!
|
||||
fs.createReadStream('node-v5.1.0.tgz', 'binary').pipe(stream);
|
||||
|
||||
// Fortunately, we have a regular TCP socket now, so when the readstream finishes and closes our
|
||||
// stream, it is still open for reading and we will still get our results :-)
|
||||
docker.modem.demuxStream(stream, process.stdout, process.stderr);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Equivalent of `docker run` in `dockerode`:
|
||||
|
||||
* `image` - container image
|
||||
* `cmd` - command to be executed
|
||||
* `stream` - stream(s) which will be used for execution output.
|
||||
* `create_options` - (optional) Options used for container creation. Refer to the [DockerEngine ContainerCreate documentation](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerCreate) for the possible values
|
||||
* `start_options` - (optional) Options used for container start. Refer to the [DockerEngine ContainerStart documentation](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerStart) for the possible values
|
||||
* `callback` - callback called when execution ends (optional, promise will be returned if not used).
|
||||
|
||||
``` js
|
||||
//callback
|
||||
docker.run('ubuntu', ['bash', '-c', 'uname -a'], process.stdout, function (err, data, container) {
|
||||
console.log(data.StatusCode);
|
||||
});
|
||||
|
||||
//promise
|
||||
docker.run(testImage, ['bash', '-c', 'uname -a'], process.stdout).then(function(data) {
|
||||
var output = data[0];
|
||||
var container = data[1];
|
||||
console.log(output.StatusCode);
|
||||
return container.remove();
|
||||
}).then(function(data) {
|
||||
console.log('container removed');
|
||||
}).catch(function(err) {
|
||||
console.log(err);
|
||||
});
|
||||
```
|
||||
|
||||
or, if you want to split stdout and stderr (you must to pass `Tty:false` as an option for this to work)
|
||||
|
||||
``` js
|
||||
docker.run('ubuntu', ['bash', '-c', 'uname -a'], [process.stdout, process.stderr], {Tty:false}, function (err, data, container) {
|
||||
console.log(data.StatusCode);
|
||||
});
|
||||
```
|
||||
|
||||
If you provide a callback, `run` will return an EventEmitter supporting the following events: container, stream, data.
|
||||
If a callback isn't provided a promise will be returned.
|
||||
|
||||
``` js
|
||||
docker.run('ubuntu', ['bash', '-c', 'uname -a'], [process.stdout, process.stderr], {Tty:false}, function (err, data, container) {
|
||||
//...
|
||||
}).on('container', function (container) {
|
||||
//...
|
||||
});
|
||||
```
|
||||
|
||||
And here is one more complex example using auto-remove and Docker network.
|
||||
|
||||
``` js
|
||||
docker.run('some-python-image', ['python', 'main.py', arg], process.stdout, {name: 'my-python-container', HostConfig: { AutoRemove: true, NetworkMode: 'my_network'}}, function(err, data, container) {
|
||||
// Do stuff
|
||||
});
|
||||
```
|
||||
|
||||
### Equivalent of `docker pull` in `dockerode`:
|
||||
|
||||
* `repoTag` - container image name (optionally with tag)
|
||||
`myrepo/myname:withtag`
|
||||
* `options` - extra options passed to create image.
|
||||
* `callback` - callback called when execution ends.
|
||||
|
||||
``` js
|
||||
docker.pull('myrepo/myname:tag', function (err, stream) {
|
||||
// streaming output from pull...
|
||||
});
|
||||
```
|
||||
|
||||
#### Pull from private repos
|
||||
|
||||
`docker-modem` already base64 encodes the necessary auth object for you.
|
||||
|
||||
``` js
|
||||
var auth = {
|
||||
username: 'username',
|
||||
password: 'password',
|
||||
auth: '',
|
||||
email: 'your@email.email',
|
||||
serveraddress: 'https://index.docker.io/v1'
|
||||
};
|
||||
|
||||
docker.pull('tag', {'authconfig': auth}, function (err, stream) {
|
||||
//...
|
||||
});
|
||||
```
|
||||
|
||||
If you already have a base64 encoded auth object, you can use it directly:
|
||||
|
||||
```js
|
||||
var auth = { key: 'yJ1J2ZXJhZGRyZXNzIjoitZSI6Im4OCIsImF1dGgiOiIiLCJlbWFpbCI6ImZvbGllLmFkcmc2VybmF0iLCJzZX5jb2aHR0cHM6Ly9pbmRleC5kb2NrZXIuaW8vdZvbGllYSIsInBhc3N3b3JkIjoiRGVjZW1icmUjEvIn0=' }
|
||||
```
|
||||
|
||||
|
||||
## Helper functions
|
||||
|
||||
* `followProgress` - allows to fire a callback only in the end of a stream based process. (build, pull, ...)
|
||||
|
||||
``` js
|
||||
//followProgress(stream, onFinished, [onProgress])
|
||||
docker.pull(repoTag, function(err, stream) {
|
||||
//...
|
||||
docker.modem.followProgress(stream, onFinished, onProgress);
|
||||
|
||||
function onFinished(err, output) {
|
||||
//output is an array with output json parsed objects
|
||||
//...
|
||||
}
|
||||
function onProgress(event) {
|
||||
//...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
* `demuxStream` - demux stdout and stderr
|
||||
|
||||
``` js
|
||||
//demuxStream(stream, stdout, stderr)
|
||||
container.attach({
|
||||
stream: true,
|
||||
stdout: true,
|
||||
stderr: true
|
||||
}, function handler(err, stream) {
|
||||
//...
|
||||
container.modem.demuxStream(stream, process.stdout, process.stderr);
|
||||
//...
|
||||
});
|
||||
```
|
||||
|
||||
## Sponsors
|
||||
|
||||
Amazing entities that [sponsor](https://github.com/sponsors/apocas) my open-source work. Check them out!
|
||||
|
||||
[](https://github.com/httptoolkit)
|
||||
[](https://oomol.com)
|
||||
|
||||
## Documentation
|
||||
|
||||
### Docker
|
||||
|
||||
- docker.createContainer(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Container/operation/ContainerCreate)
|
||||
- docker.createImage([auth], options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Image/operation/ImageCreate)
|
||||
- docker.loadImage(file, options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Image/operation/ImageLoad)
|
||||
- docker.importImage(file, options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Image/operation/ImageCreate)
|
||||
- docker.buildImage(file, options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Image/operation/ImageBuild)
|
||||
- docker.checkAuth(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/System/operation/SystemAuth)
|
||||
- docker.getContainer(id) - Returns a Container object.
|
||||
- docker.getImage(name) - Returns an Image object.
|
||||
- docker.getVolume(name) - Returns a Volume object.
|
||||
- docker.getPlugin(name) - Returns a Plugin object.
|
||||
- docker.getService(id) - Returns a Service object.
|
||||
- docker.getTask(id) - Returns a Task object.
|
||||
- docker.getNode(id) - Returns a Node object.
|
||||
- docker.getNetwork(id) - Returns a Network object.
|
||||
- docker.getSecret(id) - Returns a Secret object.
|
||||
- docker.getConfig(id) - Returns a Config object.
|
||||
- docker.getExec(id) - Returns a Exec object.
|
||||
- docker.listContainers(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Container/operation/ContainerList)
|
||||
- docker.listImages(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Image/operation/ImageList)
|
||||
- docker.listServices(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Service/operation/ServiceList)
|
||||
- docker.listNodes(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Node/operation/NodeList)
|
||||
- docker.listTasks(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Task/operation/TaskList)
|
||||
- docker.listSecrets(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Secret/operation/SecretList)
|
||||
- docker.listConfigs(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Config/operation/ConfigList)
|
||||
- docker.listPlugins(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Plugin/operation/PluginList)
|
||||
- docker.listVolumes(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Volume/operation/VolumeList)
|
||||
- docker.listNetworks(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Network/operation/NetworkList)
|
||||
- docker.createSecret(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Secret/operation/SecretCreate)
|
||||
- docker.createConfig(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Config/operation/ConfigCreate)
|
||||
- docker.createPlugin(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Plugin/operation/PluginCreate)
|
||||
- docker.createVolume(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Volume/operation/VolumeCreate)
|
||||
- docker.createService(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ServiceCreate)
|
||||
- docker.createNetwork(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/NetworkCreate)
|
||||
- docker.pruneImages(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ImagePrune)
|
||||
- docker.pruneBuilder() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/BuildPrune)
|
||||
- docker.pruneContainers(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerPrune)
|
||||
- docker.pruneVolumes(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/VolumePrune)
|
||||
- docker.pruneNetworks(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/NetworkPrune)
|
||||
- docker.searchImages(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ImageSearch)
|
||||
- docker.info() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SystemInfo)
|
||||
- docker.version() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SystemVersion)
|
||||
- docker.ping() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SystemPing)
|
||||
- docker.df() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SystemDataUsage)
|
||||
- docker.getEvents(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SystemEvents)
|
||||
- docker.swarmInit(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SwarmInit)
|
||||
- docker.swarmJoin(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SwarmJoin)
|
||||
- docker.swarmLeave(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SwarmLeave)
|
||||
- docker.swarmUpdate(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SwarmUpdate)
|
||||
- docker.swarmInspect() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SwarmInspect)
|
||||
- docker.pull(repoTag, options, callback, auth) - Like Docker's CLI pull
|
||||
- docker.pullAll(repoTag, options, callback, auth) - Like Docker's CLI pull with "-a"
|
||||
- docker.run(image, cmd, stream, createOptions, startOptions) - Like Docker's CLI run
|
||||
|
||||
|
||||
### Container
|
||||
|
||||
- container.inspect(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerInspect)
|
||||
- container.rename(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerRename)
|
||||
- container.update(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerUpdate)
|
||||
- container.top(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerTop)
|
||||
- container.changes() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerChanges)
|
||||
- container.export() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerExport)
|
||||
- container.start(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerStart)
|
||||
- container.stop(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerStop)
|
||||
- container.pause(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerPause)
|
||||
- container.unpause(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerUnpause)
|
||||
- container.exec(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerExec)
|
||||
- container.commit(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ImageCommit)
|
||||
- container.restart(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerRestart)
|
||||
- container.kill(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerKill)
|
||||
- container.resize(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerResize)
|
||||
- container.attach(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerAttach)
|
||||
- container.wait(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerWait)
|
||||
- container.remove(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerDelete)
|
||||
- container.getArchive(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerArchive)
|
||||
- container.infoArchive(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerArchiveInfo)
|
||||
- container.putArchive(file, options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/PutContainerArchive)
|
||||
- container.logs(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerLogs)
|
||||
- container.stats(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ContainerStats)
|
||||
|
||||
### Exec
|
||||
|
||||
- exec.start(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ExecStart)
|
||||
- exec.resize(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ExecResize)
|
||||
- exec.inspect() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ExecInspect)
|
||||
|
||||
### Image
|
||||
|
||||
- image.inspect(options) - [Docker API Endpoint](https://docs.docker.com/engine/api/v1.48/#tag/Image/operation/ImageInspect)
|
||||
- image.history() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ImageHistory)
|
||||
- image.push(options, callback, auth) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ImagePush)
|
||||
- image.tag(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ImageTag)
|
||||
- image.remove(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ImageDelete)
|
||||
- image.get() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ImageGet)
|
||||
|
||||
### Network
|
||||
|
||||
- network.inspect() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/NetworkInspect)
|
||||
- network.remove(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/NetworkDelete)
|
||||
- network.connect(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/NetworkConnect)
|
||||
- network.disconnect(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/NetworkDisconnect)
|
||||
|
||||
### Node
|
||||
|
||||
- node.inspect() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/NodeInspect)
|
||||
- node.remove(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/NodeDelete)
|
||||
- node.update(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/NodeUpdate)
|
||||
|
||||
### Plugin
|
||||
|
||||
- plugin.privileges() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/GetPluginPrivileges)
|
||||
- plugin.pull(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/PluginPull)
|
||||
- plugin.inspect() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/PluginInspect)
|
||||
- plugin.remove(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/PluginDelete)
|
||||
- plugin.enable(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/PluginEnable)
|
||||
- plugin.disable(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/PluginDisable)
|
||||
- plugin.update([auth], options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/PluginUpgrade)
|
||||
- plugin.push(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/PluginPush)
|
||||
- plugin.configure(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/PluginSet)
|
||||
|
||||
### Secret
|
||||
|
||||
- secret.inspect() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SecretInspect)
|
||||
- secret.remove() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SecretDelete)
|
||||
- secret.update(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/SecretUpdate)
|
||||
|
||||
### Service
|
||||
|
||||
- service.inspect() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ServiceInspect)
|
||||
- service.remove(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ServiceDelete)
|
||||
- service.update(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ServiceUpdate)
|
||||
- service.logs(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/ServiceLogs)
|
||||
|
||||
### Task
|
||||
|
||||
- task.inspect() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/TaskInspect)
|
||||
- task.logs(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/Session)
|
||||
|
||||
### Volume
|
||||
|
||||
- volume.inspect() - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/VolumeInspect)
|
||||
- volume.remove(options) - [Docker API Endpoint](https://docs.docker.com/reference/api/engine/version/v1.52/#operation/VolumeDelete)
|
||||
|
||||
|
||||
## Tests
|
||||
|
||||
* `docker pull ubuntu:latest` to prepare your system for the tests.
|
||||
* Tests are implemented using `mocha` and `chai`. Run them with `npm test`.
|
||||
|
||||
## Examples
|
||||
|
||||
Check the examples folder for more specific use cases examples.
|
||||
|
||||
## License
|
||||
|
||||
Pedro Dias - [@pedromdias](https://twitter.com/pedromdias)
|
||||
|
||||
Licensed under the Apache license, version 2.0 (the "license"); You may not use this file except in compliance with the license. You may obtain a copy of the license at:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the license is distributed on an "as is" basis, without warranties or conditions of any kind, either express or implied. See the license for the specific language governing permissions and limitations under the license.
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
var protobuf = require("protobufjs");
|
||||
var path = require("path");
|
||||
|
||||
// Constants
|
||||
var BUILDKIT_TRACE_ID = "moby.buildkit.trace";
|
||||
var BUILDKIT_IMAGE_ID = "moby.image.id";
|
||||
var PROTO_TYPE = "moby.buildkit.v1.StatusResponse";
|
||||
var ENCODING_UTF8 = "utf8";
|
||||
var ENCODING_BASE64 = "base64";
|
||||
|
||||
var StatusResponse;
|
||||
|
||||
// Load the protobuf schema
|
||||
function loadProto() {
|
||||
if (StatusResponse) return StatusResponse;
|
||||
|
||||
var root = protobuf.loadSync(
|
||||
path.resolve(__dirname, "proto", "buildkit_status.proto")
|
||||
);
|
||||
StatusResponse = root.lookupType(PROTO_TYPE);
|
||||
return StatusResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a BuildKit trace message
|
||||
* @param {string} base64Data - Base64-encoded protobuf data from aux field
|
||||
* @returns {Object} Decoded status response with vertexes, logs, etc.
|
||||
*/
|
||||
function decodeBuildKitStatus(base64Data) {
|
||||
var StatusResponse = loadProto();
|
||||
|
||||
// Handle empty messages
|
||||
if (!base64Data || base64Data.length === 0) {
|
||||
return {
|
||||
vertexes: [],
|
||||
statuses: [],
|
||||
logs: [],
|
||||
warnings: []
|
||||
};
|
||||
}
|
||||
|
||||
var buffer = Buffer.from(base64Data, ENCODING_BASE64);
|
||||
var message = StatusResponse.decode(buffer);
|
||||
return StatusResponse.toObject(message, {
|
||||
longs: String,
|
||||
enums: String,
|
||||
bytes: String,
|
||||
defaults: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats BuildKit status into human-readable text
|
||||
* @param {Object} status - Decoded status response
|
||||
* @returns {string[]} Array of human-readable log lines
|
||||
*/
|
||||
function formatBuildKitStatus(status) {
|
||||
var lines = [];
|
||||
|
||||
// Process vertexes (build steps)
|
||||
if (status.vertexes && status.vertexes.length > 0) {
|
||||
status.vertexes.forEach(function(vertex) {
|
||||
if (vertex.name && vertex.started && !vertex.completed) {
|
||||
lines.push("[" + vertex.digest.substring(0, 12) + "] " + vertex.name);
|
||||
}
|
||||
if (vertex.error) {
|
||||
lines.push("ERROR: " + vertex.error);
|
||||
}
|
||||
if (vertex.completed && vertex.cached) {
|
||||
lines.push("CACHED: " + vertex.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Process logs (command output)
|
||||
if (status.logs && status.logs.length > 0) {
|
||||
status.logs.forEach(function(log) {
|
||||
var msg = Buffer.from(log.msg).toString(ENCODING_UTF8);
|
||||
if (msg.trim()) {
|
||||
lines.push(msg.trimEnd());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Process status updates (progress)
|
||||
if (status.statuses && status.statuses.length > 0) {
|
||||
status.statuses.forEach(function(s) {
|
||||
if (s.name && s.total > 0) {
|
||||
var percent = Math.floor((s.current / s.total) * 100);
|
||||
lines.push(s.name + ": " + percent + "% (" + s.current + "/" + s.total + ")");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Process warnings
|
||||
if (status.warnings && status.warnings.length > 0) {
|
||||
status.warnings.forEach(function(warning) {
|
||||
var msg = Buffer.from(warning.short).toString(ENCODING_UTF8);
|
||||
lines.push("WARNING: " + msg);
|
||||
});
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a BuildKit stream line and extract human-readable logs
|
||||
* @param {string} line - JSON line from build stream
|
||||
* @returns {Object} { isBuildKit: boolean, logs: string[], raw: Object }
|
||||
*/
|
||||
function parseBuildKitLine(line) {
|
||||
try {
|
||||
var json = JSON.parse(line);
|
||||
|
||||
// Check if it's a BuildKit trace message
|
||||
if (json.id === BUILDKIT_TRACE_ID && json.aux !== undefined) {
|
||||
var status = decodeBuildKitStatus(json.aux);
|
||||
var logs = formatBuildKitStatus(status);
|
||||
|
||||
return {
|
||||
isBuildKit: true,
|
||||
logs: logs,
|
||||
raw: status
|
||||
};
|
||||
}
|
||||
|
||||
// Check if it's the final image ID
|
||||
if (json.id === BUILDKIT_IMAGE_ID && json.aux && json.aux.ID) {
|
||||
return {
|
||||
isBuildKit: true,
|
||||
logs: ["Built image: " + json.aux.ID],
|
||||
raw: json.aux
|
||||
};
|
||||
}
|
||||
|
||||
// Not a BuildKit message
|
||||
return {
|
||||
isBuildKit: false,
|
||||
logs: [],
|
||||
raw: json
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
isBuildKit: false,
|
||||
logs: [],
|
||||
raw: null,
|
||||
error: e.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Follow progress of a stream, automatically handling both BuildKit and regular output.
|
||||
* This provides the same ergonomics as modem.followProgress but decodes BuildKit logs.
|
||||
*
|
||||
* @param {Stream} stream - Stream from buildImage(), pull(), push(), etc.
|
||||
* @param {Function} onFinished - Called when stream ends: (err, output) => void
|
||||
* @param {Function} onProgress - Called for each log event: (event) => void
|
||||
* @returns {void}
|
||||
*/
|
||||
function followProgress(stream, onFinished, onProgress) {
|
||||
var buffer = '';
|
||||
var output = [];
|
||||
var finished = false;
|
||||
|
||||
stream.on('data', onStreamEvent);
|
||||
stream.on('error', onStreamError);
|
||||
stream.on('end', onStreamEnd);
|
||||
stream.on('close', onStreamEnd);
|
||||
|
||||
function onStreamEvent(data) {
|
||||
buffer += data.toString();
|
||||
|
||||
// Process complete lines
|
||||
var lines = buffer.split('\n');
|
||||
buffer = lines.pop(); // Save incomplete line
|
||||
|
||||
lines.forEach(function(line) {
|
||||
if (!line.trim()) return;
|
||||
|
||||
processLine(line);
|
||||
});
|
||||
}
|
||||
|
||||
function processLine(line) {
|
||||
try {
|
||||
// Try to parse as BuildKit or regular Docker output
|
||||
var result = parseBuildKitLine(line);
|
||||
|
||||
if (result.isBuildKit) {
|
||||
// BuildKit message - create events from decoded logs
|
||||
result.logs.forEach(function(log) {
|
||||
var event = { stream: log + '\n' };
|
||||
output.push(event);
|
||||
if (onProgress) onProgress(event);
|
||||
});
|
||||
} else if (result.raw) {
|
||||
// Regular Docker message
|
||||
output.push(result.raw);
|
||||
if (onProgress) onProgress(result.raw);
|
||||
}
|
||||
} catch (e) {
|
||||
// If parsing fails, try plain JSON
|
||||
try {
|
||||
var json = JSON.parse(line);
|
||||
output.push(json);
|
||||
if (onProgress) onProgress(json);
|
||||
} catch (e2) {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onStreamError(err) {
|
||||
finished = true;
|
||||
stream.removeListener('data', onStreamEvent);
|
||||
stream.removeListener('error', onStreamError);
|
||||
stream.removeListener('end', onStreamEnd);
|
||||
stream.removeListener('close', onStreamEnd);
|
||||
if (onFinished) onFinished(err, output);
|
||||
}
|
||||
|
||||
function onStreamEnd() {
|
||||
if (finished) return;
|
||||
finished = true;
|
||||
|
||||
// Process any remaining data in buffer
|
||||
if (buffer.trim()) {
|
||||
processLine(buffer);
|
||||
}
|
||||
|
||||
stream.removeListener('data', onStreamEvent);
|
||||
stream.removeListener('error', onStreamError);
|
||||
stream.removeListener('end', onStreamEnd);
|
||||
stream.removeListener('close', onStreamEnd);
|
||||
if (onFinished) onFinished(null, output);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
followProgress: followProgress
|
||||
};
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* Represents a config
|
||||
* @param {Object} modem docker-modem
|
||||
* @param {String} id Config's id
|
||||
*/
|
||||
var Config = function(modem, id) {
|
||||
this.modem = modem;
|
||||
this.id = id;
|
||||
};
|
||||
|
||||
Config.prototype[require('util').inspect.custom] = function() { return this; };
|
||||
|
||||
/**
|
||||
* Inspect
|
||||
*
|
||||
* @param {Object} opts Options (optional)
|
||||
* @param {Function} callback Callback, if specified Docker will be queried.
|
||||
* @return {Object} Name only if callback isn't specified.
|
||||
*/
|
||||
Config.prototype.inspect = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/configs/' + this.id,
|
||||
method: 'GET',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'config not found',
|
||||
500: 'server error',
|
||||
503: 'node is not part of a swarm'
|
||||
}
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update a config.
|
||||
*
|
||||
* @param {object} opts
|
||||
* @param {function} callback
|
||||
*/
|
||||
Config.prototype.update = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/configs/' + this.id + '/update?',
|
||||
method: 'POST',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'config not found',
|
||||
500: 'server error',
|
||||
503: 'node is not part of a swarm'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes the config
|
||||
* @param {[Object]} opts Remove options (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Config.prototype.remove = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/configs/' + this.id,
|
||||
method: 'DELETE',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
204: true,
|
||||
404: 'config not found',
|
||||
500: 'server error',
|
||||
503: 'node is not part of a swarm'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports = Config;
|
||||
+1085
File diff suppressed because it is too large
Load Diff
+1903
File diff suppressed because it is too large
Load Diff
+139
@@ -0,0 +1,139 @@
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* Represents an Exec
|
||||
* @param {Object} modem docker-modem
|
||||
* @param {String} id Exec's ID
|
||||
*/
|
||||
var Exec = function(modem, id) {
|
||||
this.modem = modem;
|
||||
this.id = id;
|
||||
};
|
||||
|
||||
Exec.prototype[require('util').inspect.custom] = function() { return this; };
|
||||
|
||||
/**
|
||||
* Start the exec call that was setup.
|
||||
*
|
||||
* @param {object} opts
|
||||
* @param {function} callback
|
||||
*/
|
||||
Exec.prototype.start = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/exec/' + this.id + '/start',
|
||||
method: 'POST',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
isStream: true,
|
||||
allowEmpty: true,
|
||||
hijack: args.opts.hijack,
|
||||
openStdin: args.opts.stdin,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
204: true,
|
||||
404: 'no such exec',
|
||||
409: 'container stopped/paused',
|
||||
500: 'container not running'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
if (err) return args.callback(err, data);
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Resize the exec call that was setup.
|
||||
*
|
||||
* @param {object} opts
|
||||
* @param {function} callback
|
||||
*/
|
||||
Exec.prototype.resize = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/exec/' + this.id + '/resize?',
|
||||
method: 'POST',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such exec',
|
||||
500: 'container not running'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
if (err) return args.callback(err, data);
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get low-level information about the exec call.
|
||||
*
|
||||
* @param {Object} opts Options (optional)
|
||||
* @param {function} callback
|
||||
*/
|
||||
Exec.prototype.inspect = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/exec/' + this.id + '/json',
|
||||
method: 'GET',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such exec',
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
if (err) return args.callback(err, data);
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = Exec;
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* Represents an image
|
||||
* @param {Object} modem docker-modem
|
||||
* @param {String} name Image's name
|
||||
*/
|
||||
var Image = function(modem, name) {
|
||||
this.modem = modem;
|
||||
this.name = name;
|
||||
};
|
||||
|
||||
Image.prototype[require('util').inspect.custom] = function() { return this; };
|
||||
|
||||
/**
|
||||
* Inspect
|
||||
* @param {Object} opts Inspect options, only 'manifests' (optional)
|
||||
* @param {Function} callback Callback, if specified Docker will be queried.
|
||||
* @return {Object} Name only if callback isn't specified.
|
||||
*/
|
||||
Image.prototype.inspect = function(opts, callback) {
|
||||
var args = util.processArgs(opts, callback);
|
||||
var self = this;
|
||||
|
||||
var opts = {
|
||||
path: '/images/' + this.name + '/json',
|
||||
method: 'GET',
|
||||
options: args.opts,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such image',
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(opts, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(opts, function(err, data) {
|
||||
if (err) return args.callback(err, data);
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Distribution
|
||||
* @param {Object} opts
|
||||
* @param {Function} callback Callback, if specified Docker will be queried.
|
||||
* @return {Object} Name only if callback isn't specified.
|
||||
*/
|
||||
Image.prototype.distribution = function(opts, callback) {
|
||||
var args = util.processArgs(opts, callback);
|
||||
var self = this;
|
||||
|
||||
var fopts = {
|
||||
path: '/distribution/' + this.name + '/json',
|
||||
method: 'GET',
|
||||
statusCodes: {
|
||||
200: true,
|
||||
401: 'no such image',
|
||||
500: 'server error'
|
||||
},
|
||||
authconfig: (args.opts) ? args.opts.authconfig : undefined
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(fopts, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(fopts, function(err, data) {
|
||||
if (err) return args.callback(err, data);
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* History
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Image.prototype.history = function(callback) {
|
||||
var self = this;
|
||||
var opts = {
|
||||
path: '/images/' + this.name + '/history',
|
||||
method: 'GET',
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such image',
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(opts, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(opts, function(err, data) {
|
||||
if (err) return callback(err, data);
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get
|
||||
* @param {Function} callback Callback with data stream.
|
||||
*/
|
||||
Image.prototype.get = function(callback) {
|
||||
var self = this;
|
||||
var opts = {
|
||||
path: '/images/' + this.name + '/get',
|
||||
method: 'GET',
|
||||
isStream: true,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(opts, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(opts, function(err, data) {
|
||||
if (err) return callback(err, data);
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Push
|
||||
* @param {Object} opts Push options, like 'registry' (optional)
|
||||
* @param {Function} callback Callback with stream.
|
||||
* @param {Object} auth Registry authentication
|
||||
*/
|
||||
Image.prototype.push = function(opts, callback, auth) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
var isStream = true;
|
||||
if (args.opts.stream === false) {
|
||||
isStream = false;
|
||||
}
|
||||
var optsf = {
|
||||
path: '/images/' + this.name + '/push?',
|
||||
method: 'POST',
|
||||
options: args.opts,
|
||||
authconfig: args.opts.authconfig || auth,
|
||||
abortSignal: args.opts.abortSignal,
|
||||
isStream: isStream,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such image',
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
delete optsf.options.authconfig;
|
||||
|
||||
if(callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tag
|
||||
* @param {Object} opts Tag options, like 'repo' (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Image.prototype.tag = function(opts, callback) {
|
||||
var self = this;
|
||||
var optsf = {
|
||||
path: '/images/' + this.name + '/tag?',
|
||||
method: 'POST',
|
||||
options: opts,
|
||||
abortSignal: opts && opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true, // unofficial, but proxies may return it
|
||||
201: true,
|
||||
400: 'bad parameter',
|
||||
404: 'no such image',
|
||||
409: 'conflict',
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the image
|
||||
* @param {[Object]} opts Remove options (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Image.prototype.remove = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/images/' + this.name + '?',
|
||||
method: 'DELETE',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such image',
|
||||
409: 'conflict',
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Image;
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* Represents an network
|
||||
* @param {Object} modem docker-modem
|
||||
* @param {String} id Network's id
|
||||
*/
|
||||
var Network = function(modem, id) {
|
||||
this.modem = modem;
|
||||
this.id = id;
|
||||
};
|
||||
|
||||
Network.prototype[require('util').inspect.custom] = function() { return this; };
|
||||
|
||||
/**
|
||||
* Inspect
|
||||
* @param {Function} callback Callback, if specified Docker will be queried.
|
||||
* @return {Object} Id only if callback isn't specified.
|
||||
*/
|
||||
Network.prototype.inspect = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var opts = {
|
||||
path: '/networks/' + this.id + '?',
|
||||
method: 'GET',
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such network',
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(opts, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(opts, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the network
|
||||
* @param {[Object]} opts Remove options (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Network.prototype.remove = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/networks/' + this.id,
|
||||
method: 'DELETE',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
204: true,
|
||||
404: 'no such network',
|
||||
409: 'conflict',
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Connects a container to a network
|
||||
* @param {[Object]} opts Connect options (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Network.prototype.connect = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/networks/' + this.id + '/connect',
|
||||
method: 'POST',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
201: true,
|
||||
404: 'network or container is not found',
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects a container from a network
|
||||
* @param {[Object]} opts Disconnect options (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Network.prototype.disconnect = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/networks/' + this.id + '/disconnect',
|
||||
method: 'POST',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
201: true,
|
||||
404: 'network or container is not found',
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = Network;
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* Represents an Node
|
||||
* @param {Object} modem docker-modem
|
||||
* @param {String} id Node's ID
|
||||
*/
|
||||
var Node = function(modem, id) {
|
||||
this.modem = modem;
|
||||
this.id = id;
|
||||
};
|
||||
|
||||
Node.prototype[require('util').inspect.custom] = function() { return this; };
|
||||
|
||||
/**
|
||||
* Query Docker for Node details.
|
||||
*
|
||||
* @param {Object} opts Options (optional)
|
||||
* @param {function} callback
|
||||
*/
|
||||
Node.prototype.inspect = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/nodes/' + this.id,
|
||||
method: 'GET',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such node',
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Update a node.
|
||||
*
|
||||
* @param {object} opts
|
||||
* @param {function} callback
|
||||
*/
|
||||
Node.prototype.update = function(opts, callback) {
|
||||
var self = this;
|
||||
if (!callback && typeof opts === 'function') {
|
||||
callback = opts;
|
||||
}
|
||||
|
||||
var optsf = {
|
||||
path: '/nodes/' + this.id + '/update?',
|
||||
method: 'POST',
|
||||
abortSignal: opts && opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such node',
|
||||
406: 'node is not part of a swarm',
|
||||
500: 'server error'
|
||||
},
|
||||
options: opts
|
||||
};
|
||||
|
||||
if(callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Remove a Node.
|
||||
* Warning: This method is not documented in the API.
|
||||
*
|
||||
* @param {object} opts
|
||||
* @param {function} callback
|
||||
*/
|
||||
Node.prototype.remove = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/nodes/' + this.id + '?',
|
||||
method: 'DELETE',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such node',
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = Node;
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* Represents a plugin
|
||||
* @param {Object} modem docker-modem
|
||||
* @param {String} name Plugin's name
|
||||
*/
|
||||
var Plugin = function(modem, name, remote) {
|
||||
this.modem = modem;
|
||||
this.name = name;
|
||||
this.remote = remote || name;
|
||||
};
|
||||
|
||||
Plugin.prototype[require('util').inspect.custom] = function() { return this; };
|
||||
|
||||
/**
|
||||
* Inspect
|
||||
*
|
||||
* @param {Object} opts Options (optional)
|
||||
* @param {Function} callback Callback, if specified Docker will be queried.
|
||||
* @return {Object} Name only if callback isn't specified.
|
||||
*/
|
||||
Plugin.prototype.inspect = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/plugins/' + this.name + '/json',
|
||||
method: 'GET',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'plugin is not installed',
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the plugin
|
||||
* @param {[Object]} opts Remove options (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Plugin.prototype.remove = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/plugins/' + this.name + '?',
|
||||
method: 'DELETE',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'plugin is not installed',
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
if (err) return args.callback(err, data);
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* get privileges
|
||||
* @param {Object} opts Options (optional)
|
||||
* @param {Function} callback Callback
|
||||
* @return {Object} Name only if callback isn't specified.
|
||||
*/
|
||||
Plugin.prototype.privileges = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/plugins/privileges?',
|
||||
method: 'GET',
|
||||
options: {
|
||||
'remote': this.remote
|
||||
},
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Installs a new plugin
|
||||
* @param {Object} opts Create options
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Plugin.prototype.pull = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
if(args.opts._query && !args.opts._query.name) {
|
||||
args.opts._query.name = this.name;
|
||||
}
|
||||
if(args.opts._query && !args.opts._query.remote) {
|
||||
args.opts._query.remote = this.remote;
|
||||
}
|
||||
|
||||
var optsf = {
|
||||
path: '/plugins/pull?',
|
||||
method: 'POST',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
isStream: true,
|
||||
options: args.opts,
|
||||
statusCodes: {
|
||||
200: true, // unofficial, but proxies may return it
|
||||
204: true,
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enable
|
||||
* @param {Object} opts Plugin enable options (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Plugin.prototype.enable = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/plugins/' + this.name + '/enable?',
|
||||
method: 'POST',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable
|
||||
* @param {Object} opts Plugin disable options (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Plugin.prototype.disable = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/plugins/' + this.name + '/disable',
|
||||
method: 'POST',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Push
|
||||
* @param {Object} opts Plugin push options (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Plugin.prototype.push = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/plugins/' + this.name + '/push',
|
||||
method: 'POST',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'plugin not installed',
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* COnfigure
|
||||
* @param {Object} opts Plugin configure options (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Plugin.prototype.configure = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/plugins/' + this.name + '/set',
|
||||
method: 'POST',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
204: true,
|
||||
404: 'plugin not installed',
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Upgrade plugin
|
||||
*
|
||||
* @param {object} auth
|
||||
* @param {object} opts
|
||||
* @param {function} callback
|
||||
*/
|
||||
Plugin.prototype.upgrade = function(auth, opts, callback) {
|
||||
var self = this;
|
||||
if (!callback && typeof opts === 'function') {
|
||||
callback = opts;
|
||||
opts = auth;
|
||||
auth = opts.authconfig || undefined;
|
||||
}
|
||||
|
||||
var optsf = {
|
||||
path: '/plugins/' + this.name + '/upgrade?',
|
||||
method: 'POST',
|
||||
abortSignal: opts && opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
204: true,
|
||||
404: 'plugin not installed',
|
||||
500: 'server error'
|
||||
},
|
||||
authconfig: auth,
|
||||
options: opts
|
||||
};
|
||||
|
||||
if(callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = Plugin;
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package moby.filesync.v1;
|
||||
|
||||
option go_package = "auth";
|
||||
|
||||
service Auth{
|
||||
rpc Credentials(CredentialsRequest) returns (CredentialsResponse);
|
||||
rpc FetchToken(FetchTokenRequest) returns (FetchTokenResponse);
|
||||
rpc GetTokenAuthority(GetTokenAuthorityRequest) returns (GetTokenAuthorityResponse);
|
||||
rpc VerifyTokenAuthority(VerifyTokenAuthorityRequest) returns (VerifyTokenAuthorityResponse);
|
||||
}
|
||||
|
||||
message CredentialsRequest {
|
||||
string Host = 1;
|
||||
}
|
||||
|
||||
message CredentialsResponse {
|
||||
string Username = 1;
|
||||
string Secret = 2;
|
||||
}
|
||||
|
||||
message FetchTokenRequest {
|
||||
string ClientID = 1;
|
||||
string Host = 2;
|
||||
string Realm = 3;
|
||||
string Service = 4;
|
||||
repeated string Scopes = 5;
|
||||
}
|
||||
|
||||
message FetchTokenResponse {
|
||||
string Token = 1;
|
||||
int64 ExpiresIn = 2; // seconds
|
||||
int64 IssuedAt = 3; // timestamp
|
||||
}
|
||||
|
||||
message GetTokenAuthorityRequest {
|
||||
string Host = 1;
|
||||
bytes Salt = 2;
|
||||
}
|
||||
|
||||
message GetTokenAuthorityResponse {
|
||||
bytes PublicKey = 1;
|
||||
}
|
||||
|
||||
message VerifyTokenAuthorityRequest {
|
||||
string Host = 1;
|
||||
bytes Payload = 2;
|
||||
bytes Salt = 3;
|
||||
}
|
||||
|
||||
message VerifyTokenAuthorityResponse {
|
||||
bytes Signed = 1;
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package moby.buildkit.v1;
|
||||
|
||||
// Minimal definitions for decoding BuildKit status messages
|
||||
// Based on https://github.com/moby/buildkit/blob/master/api/services/control/control.proto
|
||||
// Related to https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto (vertices map to the solver op DAG)
|
||||
|
||||
message StatusResponse {
|
||||
repeated Vertex vertexes = 1;
|
||||
repeated VertexStatus statuses = 2;
|
||||
repeated VertexLog logs = 3;
|
||||
repeated VertexWarning warnings = 4;
|
||||
}
|
||||
|
||||
message Vertex {
|
||||
string digest = 1;
|
||||
repeated string inputs = 2;
|
||||
string name = 3;
|
||||
bool cached = 4;
|
||||
Timestamp started = 5;
|
||||
Timestamp completed = 6;
|
||||
string error = 7;
|
||||
ProgressGroup progressGroup = 8;
|
||||
}
|
||||
|
||||
message VertexStatus {
|
||||
string ID = 1;
|
||||
string vertex = 2;
|
||||
string name = 3;
|
||||
int64 current = 4;
|
||||
int64 total = 5;
|
||||
Timestamp timestamp = 6;
|
||||
Timestamp started = 7;
|
||||
Timestamp completed = 8;
|
||||
}
|
||||
|
||||
message VertexLog {
|
||||
string vertex = 1;
|
||||
Timestamp timestamp = 2;
|
||||
int64 stream = 3;
|
||||
bytes msg = 4;
|
||||
}
|
||||
|
||||
message VertexWarning {
|
||||
string vertex = 1;
|
||||
int64 level = 2;
|
||||
bytes short = 3;
|
||||
repeated bytes detail = 4;
|
||||
string url = 5;
|
||||
SourceInfo info = 6;
|
||||
repeated Range ranges = 7;
|
||||
}
|
||||
|
||||
message ProgressGroup {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
bool weak = 3;
|
||||
}
|
||||
|
||||
// Simplified Timestamp to match google.protobuf.Timestamp wire format
|
||||
message Timestamp {
|
||||
int64 seconds = 1;
|
||||
int32 nanos = 2;
|
||||
}
|
||||
|
||||
message SourceInfo {
|
||||
string filename = 1;
|
||||
bytes data = 2;
|
||||
// definition and language fields omitted - not needed for log decoding
|
||||
}
|
||||
|
||||
message Range {
|
||||
Position start = 1;
|
||||
Position end = 2;
|
||||
}
|
||||
|
||||
message Position {
|
||||
int32 line = 1;
|
||||
int32 character = 2;
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* Represents a secret
|
||||
* @param {Object} modem docker-modem
|
||||
* @param {String} id Secret's id
|
||||
*/
|
||||
var Secret = function(modem, id) {
|
||||
this.modem = modem;
|
||||
this.id = id;
|
||||
};
|
||||
|
||||
Secret.prototype[require('util').inspect.custom] = function() { return this; };
|
||||
|
||||
/**
|
||||
* Inspect
|
||||
* @param {Object} opts Options (optional)
|
||||
* @param {Function} callback Callback, if specified Docker will be queried.
|
||||
* @return {Object} Name only if callback isn't specified.
|
||||
*/
|
||||
Secret.prototype.inspect = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/secrets/' + this.id,
|
||||
method: 'GET',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'secret not found',
|
||||
406: 'node is not part of a swarm',
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update a secret.
|
||||
*
|
||||
* @param {object} opts
|
||||
* @param {function} callback
|
||||
*/
|
||||
Secret.prototype.update = function(opts, callback) {
|
||||
var self = this;
|
||||
if (!callback && typeof opts === 'function') {
|
||||
callback = opts;
|
||||
}
|
||||
|
||||
var optsf = {
|
||||
path: '/secrets/' + this.id + '/update?',
|
||||
method: 'POST',
|
||||
abortSignal: opts && opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'secret not found',
|
||||
500: 'server error'
|
||||
},
|
||||
options: opts
|
||||
};
|
||||
|
||||
if(callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes the secret
|
||||
* @param {[Object]} opts Remove options (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Secret.prototype.remove = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/secrets/' + this.id,
|
||||
method: 'DELETE',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
204: true,
|
||||
404: 'secret not found',
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports = Secret;
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* Represents an Service
|
||||
* @param {Object} modem docker-modem
|
||||
* @param {String} id Service's ID
|
||||
*/
|
||||
var Service = function(modem, id) {
|
||||
this.modem = modem;
|
||||
this.id = id;
|
||||
};
|
||||
|
||||
Service.prototype[require('util').inspect.custom] = function() { return this; };
|
||||
|
||||
/**
|
||||
* Query Docker for service details.
|
||||
*
|
||||
* @param {Object} opts Options (optional)
|
||||
* @param {function} callback
|
||||
*/
|
||||
Service.prototype.inspect = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/services/' + this.id,
|
||||
method: 'GET',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such service',
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete Service
|
||||
*
|
||||
* @param {Object} opts Options (optional)
|
||||
* @param {function} callback
|
||||
*/
|
||||
Service.prototype.remove = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/services/' + this.id,
|
||||
method: 'DELETE',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
204: true,
|
||||
404: 'no such service',
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update service
|
||||
*
|
||||
* @param {object} auth
|
||||
* @param {object} opts
|
||||
* @param {function} callback
|
||||
*/
|
||||
Service.prototype.update = function(auth, opts, callback) {
|
||||
var self = this;
|
||||
if (!callback) {
|
||||
var t = typeof opts;
|
||||
if(t === 'function'){
|
||||
callback = opts;
|
||||
opts = auth;
|
||||
auth = opts.authconfig || undefined;
|
||||
} else if (t === 'undefined'){
|
||||
opts = auth;
|
||||
auth = opts.authconfig || undefined;
|
||||
}
|
||||
}
|
||||
|
||||
var optsf = {
|
||||
path: '/services/' + this.id + '/update?',
|
||||
method: 'POST',
|
||||
abortSignal: opts && opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such service',
|
||||
500: 'server error'
|
||||
},
|
||||
authconfig: auth,
|
||||
options: opts
|
||||
};
|
||||
|
||||
if(callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Service logs
|
||||
* @param {Object} opts Logs options. (optional)
|
||||
* @param {Function} callback Callback with data
|
||||
*/
|
||||
Service.prototype.logs = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback, {});
|
||||
|
||||
var optsf = {
|
||||
path: '/services/' + this.id + '/logs?',
|
||||
method: 'GET',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
isStream: args.opts.follow || false,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such service',
|
||||
500: 'server error',
|
||||
503: 'node is not part of a swarm'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports = Service;
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
var grpc = require("@grpc/grpc-js"),
|
||||
protoLoader = require("@grpc/proto-loader"),
|
||||
path = require("path"),
|
||||
uuid = require("uuid").v4;
|
||||
|
||||
function withSession(docker, auth, handler) {
|
||||
const sessionId = uuid();
|
||||
|
||||
const opts = {
|
||||
method: "POST",
|
||||
path: "/session",
|
||||
hijack: true,
|
||||
headers: {
|
||||
Upgrade: "h2c",
|
||||
"X-Docker-Expose-Session-Uuid": sessionId,
|
||||
"X-Docker-Expose-Session-Name": "testcontainers",
|
||||
},
|
||||
statusCodes: {
|
||||
200: true,
|
||||
500: "server error",
|
||||
},
|
||||
};
|
||||
|
||||
docker.modem.dial(opts, function (err, socket) {
|
||||
if (err) {
|
||||
return handler(err, null, () => undefined);
|
||||
}
|
||||
|
||||
const server = new grpc.Server();
|
||||
const creds = grpc.ServerCredentials.createInsecure();
|
||||
const injector = server.createConnectionInjector(creds);
|
||||
injector.injectConnection(socket);
|
||||
|
||||
const pkg = protoLoader.loadSync(
|
||||
path.resolve(__dirname, "proto", "auth.proto")
|
||||
);
|
||||
const service = grpc.loadPackageDefinition(pkg);
|
||||
|
||||
server.addService(service.moby.filesync.v1.Auth.service, {
|
||||
Credentials({ request }, callback) {
|
||||
// We probably want to have the possibility to pass credentials per
|
||||
// hots. The correct one could be returned based on `request.Host`
|
||||
if (auth) {
|
||||
callback(null, {
|
||||
Username: auth.username,
|
||||
Secret: auth.password,
|
||||
});
|
||||
} else {
|
||||
callback(null, {});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
function done() {
|
||||
server.forceShutdown();
|
||||
socket.end();
|
||||
}
|
||||
|
||||
handler(null, sessionId, done);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = withSession;
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* Represents an Task
|
||||
* @param {Object} modem docker-modem
|
||||
* @param {String} id Task's ID
|
||||
*/
|
||||
var Task = function(modem, id) {
|
||||
this.modem = modem;
|
||||
this.id = id;
|
||||
|
||||
this.defaultOptions = {
|
||||
log: {}
|
||||
};
|
||||
};
|
||||
|
||||
Task.prototype[require('util').inspect.custom] = function() { return this; };
|
||||
|
||||
/**
|
||||
* Query Docker for Task details.
|
||||
*
|
||||
* @param {Object} opts Options (optional)
|
||||
* @param {function} callback
|
||||
*/
|
||||
Task.prototype.inspect = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/tasks/' + this.id,
|
||||
method: 'GET',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'unknown task',
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Task logs
|
||||
* @param {Object} opts Logs options. (optional)
|
||||
* @param {Function} callback Callback with data
|
||||
*/
|
||||
Task.prototype.logs = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback, this.defaultOptions.log);
|
||||
|
||||
var optsf = {
|
||||
path: '/tasks/' + this.id + '/logs?',
|
||||
method: 'GET',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
isStream: args.opts.follow || false,
|
||||
statusCodes: {
|
||||
101: true,
|
||||
200: true,
|
||||
404: 'no such container',
|
||||
500: 'server error',
|
||||
503: 'node is not part of a swarm'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = Task;
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
var DockerIgnore = require('@balena/dockerignore');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var tar = require('tar-fs');
|
||||
var zlib = require('zlib');
|
||||
|
||||
// https://github.com/HenrikJoreteg/extend-object/blob/v0.1.0/extend-object.js
|
||||
|
||||
var arr = [];
|
||||
var each = arr.forEach;
|
||||
var slice = arr.slice;
|
||||
|
||||
module.exports.extend = function(obj) {
|
||||
each.call(slice.call(arguments, 1), function(source) {
|
||||
if (source) {
|
||||
for (var prop in source) {
|
||||
obj[prop] = source[prop];
|
||||
}
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
module.exports.processArgs = function(opts, callback, defaultOpts) {
|
||||
if (!callback && typeof opts === 'function') {
|
||||
callback = opts;
|
||||
opts = null;
|
||||
}
|
||||
return {
|
||||
callback: callback,
|
||||
opts: module.exports.extend({}, defaultOpts, opts)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parse the given repo tag name (as a string) and break it out into repo/tag pair.
|
||||
* // if given the input http://localhost:8080/woot:latest
|
||||
* {
|
||||
* repository: 'http://localhost:8080/woot',
|
||||
* tag: 'latest'
|
||||
* }
|
||||
* @param {String} input Input e.g: 'repo/foo', 'ubuntu', 'ubuntu:latest'
|
||||
* @return {Object} input parsed into the repo and tag.
|
||||
*/
|
||||
module.exports.parseRepositoryTag = function(input) {
|
||||
var separatorPos;
|
||||
var digestPos = input.indexOf('@');
|
||||
var colonPos = input.lastIndexOf(':');
|
||||
// @ symbol is more important
|
||||
if (digestPos >= 0) {
|
||||
separatorPos = digestPos;
|
||||
} else if (colonPos >= 0) {
|
||||
separatorPos = colonPos;
|
||||
} else {
|
||||
// no colon nor @
|
||||
return {
|
||||
repository: input
|
||||
};
|
||||
}
|
||||
|
||||
// last colon is either the tag (or part of a port designation)
|
||||
var tag = input.slice(separatorPos + 1);
|
||||
|
||||
// if it contains a / its not a tag and is part of the url
|
||||
if (tag.indexOf('/') === -1) {
|
||||
return {
|
||||
repository: input.slice(0, separatorPos),
|
||||
tag: tag
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
repository: input
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
module.exports.prepareBuildContext = function(file, next) {
|
||||
if (file && file.context) {
|
||||
fs.readFile(path.join(file.context, '.dockerignore'), (err, data) => {
|
||||
let ignoreFn;
|
||||
let filterFn;
|
||||
|
||||
if (!err) {
|
||||
const dockerIgnore = DockerIgnore({ ignorecase: false }).add(data.toString());
|
||||
|
||||
filterFn = dockerIgnore.createFilter();
|
||||
ignoreFn = (path) => {
|
||||
return !filterFn(path);
|
||||
}
|
||||
}
|
||||
|
||||
const entries = file.src.slice() || []
|
||||
|
||||
const pack = tar.pack(file.context, {
|
||||
entries: filterFn ? entries.filter(filterFn) : entries,
|
||||
ignore: ignoreFn // Only works on directories
|
||||
});
|
||||
|
||||
next(pack.pipe(zlib.createGzip()));
|
||||
})
|
||||
} else {
|
||||
next(file);
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
var util = require('./util');
|
||||
|
||||
/**
|
||||
* Represents a volume
|
||||
* @param {Object} modem docker-modem
|
||||
* @param {String} name Volume's name
|
||||
*/
|
||||
var Volume = function(modem, name) {
|
||||
this.modem = modem;
|
||||
this.name = name;
|
||||
};
|
||||
|
||||
Volume.prototype[require('util').inspect.custom] = function() { return this; };
|
||||
|
||||
/**
|
||||
* Inspect
|
||||
* @param {Object} opts Options (optional)
|
||||
* @param {Function} callback Callback, if specified Docker will be queried.
|
||||
* @return {Object} Name only if callback isn't specified.
|
||||
*/
|
||||
Volume.prototype.inspect = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/volumes/' + this.name,
|
||||
method: 'GET',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
200: true,
|
||||
404: 'no such volume',
|
||||
500: 'server error'
|
||||
}
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the volume
|
||||
* @param {[Object]} opts Remove options (optional)
|
||||
* @param {Function} callback Callback
|
||||
*/
|
||||
Volume.prototype.remove = function(opts, callback) {
|
||||
var self = this;
|
||||
var args = util.processArgs(opts, callback);
|
||||
|
||||
var optsf = {
|
||||
path: '/volumes/' + this.name,
|
||||
method: 'DELETE',
|
||||
abortSignal: args.opts.abortSignal,
|
||||
statusCodes: {
|
||||
204: true,
|
||||
404: 'no such volume',
|
||||
409: 'conflict',
|
||||
500: 'server error'
|
||||
},
|
||||
options: args.opts
|
||||
};
|
||||
|
||||
if(args.callback === undefined) {
|
||||
return new this.modem.Promise(function(resolve, reject) {
|
||||
self.modem.dial(optsf, function(err, data) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modem.dial(optsf, function(err, data) {
|
||||
args.callback(err, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Volume;
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "dockerode",
|
||||
"description": "Docker Remote API module.",
|
||||
"version": "4.0.12",
|
||||
"author": "Pedro Dias <petermdias@gmail.com>",
|
||||
"maintainers": [
|
||||
"apocas <petermdias@gmail.com>"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/apocas/dockerode.git"
|
||||
},
|
||||
"keywords": [
|
||||
"docker",
|
||||
"docker.io"
|
||||
],
|
||||
"dependencies": {
|
||||
"@balena/dockerignore": "^1.0.2",
|
||||
"@grpc/grpc-js": "^1.11.1",
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
"docker-modem": "^5.0.7",
|
||||
"protobufjs": "^7.3.2",
|
||||
"tar-fs": "^2.1.4",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bluebird": "^3.7.0",
|
||||
"chai": "~4.2.0",
|
||||
"memorystream": "~0.3.0",
|
||||
"mocha": "^10.2.0"
|
||||
},
|
||||
"main": "./lib/docker",
|
||||
"scripts": {
|
||||
"test": "./node_modules/mocha/bin/mocha.js -R spec --exit"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">= 8.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user