@zukhruf/docker 0.0.0-stage → 0.3.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +902 -2
- package/dist/api-version.d.ts +7 -0
- package/dist/api-version.d.ts.map +1 -0
- package/dist/api-version.js +22 -0
- package/dist/api-version.js.map +1 -0
- package/dist/container-process.d.ts +35 -0
- package/dist/container-process.d.ts.map +1 -0
- package/dist/container-process.js +91 -0
- package/dist/container-process.js.map +1 -0
- package/dist/containers.d.ts +160 -0
- package/dist/containers.d.ts.map +1 -0
- package/dist/containers.js +700 -0
- package/dist/containers.js.map +1 -0
- package/dist/context-store.d.ts +22 -0
- package/dist/context-store.d.ts.map +1 -0
- package/dist/context-store.js +128 -0
- package/dist/context-store.js.map +1 -0
- package/dist/docker.d.ts +57 -0
- package/dist/docker.d.ts.map +1 -0
- package/dist/docker.js +96 -0
- package/dist/docker.js.map +1 -0
- package/dist/engine-api.d.ts +390 -0
- package/dist/engine-api.d.ts.map +1 -0
- package/dist/engine-api.js +4 -0
- package/dist/engine-api.js.map +1 -0
- package/dist/engine-client.d.ts +74 -0
- package/dist/engine-client.d.ts.map +1 -0
- package/dist/engine-client.js +253 -0
- package/dist/engine-client.js.map +1 -0
- package/dist/errors.d.ts +133 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +214 -0
- package/dist/errors.js.map +1 -0
- package/dist/host.d.ts +13 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +22 -0
- package/dist/host.js.map +1 -0
- package/dist/images.d.ts +40 -0
- package/dist/images.d.ts.map +1 -0
- package/dist/images.js +173 -0
- package/dist/images.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/multiplexed-stream.d.ts +11 -0
- package/dist/multiplexed-stream.d.ts.map +1 -0
- package/dist/multiplexed-stream.js +81 -0
- package/dist/multiplexed-stream.js.map +1 -0
- package/dist/ndjson.d.ts +7 -0
- package/dist/ndjson.d.ts.map +1 -0
- package/dist/ndjson.js +21 -0
- package/dist/ndjson.js.map +1 -0
- package/dist/networks.d.ts +46 -0
- package/dist/networks.d.ts.map +1 -0
- package/dist/networks.js +170 -0
- package/dist/networks.js.map +1 -0
- package/dist/options.d.ts +7 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +2 -0
- package/dist/options.js.map +1 -0
- package/dist/output.d.ts +18 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +21 -0
- package/dist/output.js.map +1 -0
- package/dist/progress.d.ts +9 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +20 -0
- package/dist/progress.js.map +1 -0
- package/dist/ssh-connection.d.ts +23 -0
- package/dist/ssh-connection.d.ts.map +1 -0
- package/dist/ssh-connection.js +225 -0
- package/dist/ssh-connection.js.map +1 -0
- package/dist/tar-reader.d.ts +22 -0
- package/dist/tar-reader.d.ts.map +1 -0
- package/dist/tar-reader.js +163 -0
- package/dist/tar-reader.js.map +1 -0
- package/dist/tar-writer.d.ts +16 -0
- package/dist/tar-writer.d.ts.map +1 -0
- package/dist/tar-writer.js +114 -0
- package/dist/tar-writer.js.map +1 -0
- package/dist/transport.d.ts +29 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +56 -0
- package/dist/transport.js.map +1 -0
- package/dist/volumes.d.ts +41 -0
- package/dist/volumes.d.ts.map +1 -0
- package/dist/volumes.js +119 -0
- package/dist/volumes.js.map +1 -0
- package/package.json +43 -4
package/dist/networks.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { isSuccess, parseBody } from "./engine-client.js";
|
|
2
|
+
import { ContainerNotFoundError, NameConflictError, NetworkInUseError, NetworkNotFoundError, } from "./errors.js";
|
|
3
|
+
/** The networks that the engine defines and never removes: runconfig.IsPreDefinedNetwork of Moby. */
|
|
4
|
+
const PREDEFINED_NETWORKS = new Set(['bridge', 'host', 'none']);
|
|
5
|
+
/** A network, by the ID that the engine gave it. It has no disposal. */
|
|
6
|
+
export class Network {
|
|
7
|
+
id;
|
|
8
|
+
#client;
|
|
9
|
+
constructor(client, id) {
|
|
10
|
+
this.#client = client;
|
|
11
|
+
this.id = id;
|
|
12
|
+
}
|
|
13
|
+
get #path() {
|
|
14
|
+
return `/networks/${encodeURIComponent(this.id)}`;
|
|
15
|
+
}
|
|
16
|
+
async inspect(options = {}) {
|
|
17
|
+
const method = 'GET';
|
|
18
|
+
const path = this.#path;
|
|
19
|
+
const response = await this.#client.exchange(method, path, options);
|
|
20
|
+
if (isSuccess(response)) {
|
|
21
|
+
return parseBody(response);
|
|
22
|
+
}
|
|
23
|
+
const error = this.#client.error(response, method, path);
|
|
24
|
+
if (response.status !== 404)
|
|
25
|
+
throw error;
|
|
26
|
+
throw new NetworkNotFoundError(error.message, error, this.id, {
|
|
27
|
+
cause: error,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async connect(container, endpoint, options = {}) {
|
|
31
|
+
const body = { Container: container.id, EndpointConfig: endpoint };
|
|
32
|
+
await this.#endpoint('connect', container, body, options);
|
|
33
|
+
}
|
|
34
|
+
async disconnect(container, options = {}) {
|
|
35
|
+
const body = { Container: container.id, Force: options.force };
|
|
36
|
+
await this.#endpoint('disconnect', container, body, options);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolves for 404. A 403 is a pre-defined network or a network with
|
|
40
|
+
* connected containers: an inspect tells which. A pre-defined network can
|
|
41
|
+
* have connected containers too, so its name decides first. Only a running
|
|
42
|
+
* container has an endpoint, so only a running one keeps the network.
|
|
43
|
+
*/
|
|
44
|
+
async remove(options = {}) {
|
|
45
|
+
const method = 'DELETE';
|
|
46
|
+
const path = this.#path;
|
|
47
|
+
const response = await this.#client.exchange(method, path, options);
|
|
48
|
+
if (isSuccess(response) || response.status === 404)
|
|
49
|
+
return;
|
|
50
|
+
const error = this.#client.error(response, method, path);
|
|
51
|
+
if (response.status !== 403)
|
|
52
|
+
throw error;
|
|
53
|
+
const inspect = await this.#client.exchange('GET', path, options);
|
|
54
|
+
if (inspect.status === 404)
|
|
55
|
+
return;
|
|
56
|
+
if (!isSuccess(inspect))
|
|
57
|
+
throw error;
|
|
58
|
+
const network = parseBody(inspect);
|
|
59
|
+
if (PREDEFINED_NETWORKS.has(network.Name))
|
|
60
|
+
throw error;
|
|
61
|
+
const containers = Object.keys(network.Containers ?? {});
|
|
62
|
+
if (containers.length === 0)
|
|
63
|
+
throw error;
|
|
64
|
+
throw new NetworkInUseError(error.message, error, this.id, containers, {
|
|
65
|
+
cause: error,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A 404 of a connect or a disconnect is a missing network or a missing
|
|
70
|
+
* container: an inspect of the network tells which. An engine in swarm mode
|
|
71
|
+
* answers a missing network with 500 ("Could not attach to network", probed
|
|
72
|
+
* at API 1.51), so a 500 gets the same inspect.
|
|
73
|
+
*/
|
|
74
|
+
async #endpoint(action, container, body, options) {
|
|
75
|
+
const method = 'POST';
|
|
76
|
+
const path = `${this.#path}/${action}`;
|
|
77
|
+
const response = await this.#client.exchange(method, path, {
|
|
78
|
+
body,
|
|
79
|
+
signal: options.signal,
|
|
80
|
+
});
|
|
81
|
+
if (isSuccess(response))
|
|
82
|
+
return;
|
|
83
|
+
const error = this.#client.error(response, method, path);
|
|
84
|
+
if (response.status !== 404 && response.status !== 500)
|
|
85
|
+
throw error;
|
|
86
|
+
const inspect = await this.#client.exchange('GET', this.#path, options);
|
|
87
|
+
if (inspect.status === 404) {
|
|
88
|
+
throw new NetworkNotFoundError(error.message, error, this.id, {
|
|
89
|
+
cause: error,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (response.status === 500)
|
|
93
|
+
throw error;
|
|
94
|
+
throw new ContainerNotFoundError(error.message, error, container.id, {
|
|
95
|
+
cause: error,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** A network that this handle created. Disposal removes it. */
|
|
100
|
+
export class OwnedNetwork extends Network {
|
|
101
|
+
/** remove() */
|
|
102
|
+
async [Symbol.asyncDispose]() {
|
|
103
|
+
await this.remove();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** The networks of one engine. */
|
|
107
|
+
export class Networks {
|
|
108
|
+
#client;
|
|
109
|
+
constructor(client) {
|
|
110
|
+
this.#client = client;
|
|
111
|
+
}
|
|
112
|
+
/** From API 1.44, the engine refuses a taken name with 409: it rejects with NameConflictError. */
|
|
113
|
+
async create(body, options = {}) {
|
|
114
|
+
const method = 'POST';
|
|
115
|
+
const path = '/networks/create';
|
|
116
|
+
const response = await this.#client.exchange(method, path, {
|
|
117
|
+
body,
|
|
118
|
+
signal: options.signal,
|
|
119
|
+
});
|
|
120
|
+
if (isSuccess(response)) {
|
|
121
|
+
const { Id } = parseBody(response);
|
|
122
|
+
return new OwnedNetwork(this.#client, Id);
|
|
123
|
+
}
|
|
124
|
+
const error = this.#client.error(response, method, path);
|
|
125
|
+
if (response.status === 409) {
|
|
126
|
+
throw new NameConflictError(error.message, error, 'network', body.Name, {
|
|
127
|
+
cause: error,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Creates the network, or gives the one that has the name. The network that
|
|
134
|
+
* exists wins. When that network is removed between the 409 and the
|
|
135
|
+
* inspect, the create runs again.
|
|
136
|
+
*/
|
|
137
|
+
async createOrGet(body, options = {}) {
|
|
138
|
+
for (;;) {
|
|
139
|
+
try {
|
|
140
|
+
return { created: true, network: await this.create(body, options) };
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (!(error instanceof NameConflictError))
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
const network = await this.find(body.Name, options);
|
|
147
|
+
if (network)
|
|
148
|
+
return { created: false, network };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** One inspect: a handle with the ID of the engine, or undefined for 404. */
|
|
152
|
+
async find(nameOrId, options = {}) {
|
|
153
|
+
const method = 'GET';
|
|
154
|
+
const path = `/networks/${encodeURIComponent(nameOrId)}`;
|
|
155
|
+
const response = await this.#client.exchange(method, path, options);
|
|
156
|
+
if (response.status === 404)
|
|
157
|
+
return undefined;
|
|
158
|
+
if (!isSuccess(response))
|
|
159
|
+
throw this.#client.error(response, method, path);
|
|
160
|
+
const { Id } = parseBody(response);
|
|
161
|
+
return new Network(this.#client, Id);
|
|
162
|
+
}
|
|
163
|
+
async list(options = {}) {
|
|
164
|
+
return this.#client.json('GET', '/networks', {
|
|
165
|
+
query: { filters: options.filters },
|
|
166
|
+
signal: options.signal,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=networks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"networks.js","sourceRoot":"","sources":["../src/networks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface CancelOptions {
|
|
2
|
+
/** Cancels the wait for the engine. */
|
|
3
|
+
signal?: AbortSignal;
|
|
4
|
+
}
|
|
5
|
+
/** The filters of a list or of the events, as the Engine API takes them. */
|
|
6
|
+
export type Filters = Record<string, string[]>;
|
|
7
|
+
//# sourceMappingURL=options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,uCAAuC;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,4EAA4E;AAC5E,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC"}
|
package/dist/options.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":""}
|
package/dist/output.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { OutputChunk } from './multiplexed-stream.ts';
|
|
2
|
+
export interface Output {
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
/** Standard output and standard error, in the order of the engine. */
|
|
6
|
+
output: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ExecResult extends Output {
|
|
9
|
+
exitCode: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Reads each chunk and decodes the text of each stream. A chunk can end inside
|
|
13
|
+
* a UTF-8 character: the character waits for the next chunk of the same
|
|
14
|
+
* stream, and `output` gets it then, so a chunk of the other stream between
|
|
15
|
+
* the two parts never breaks it. The text is not trimmed.
|
|
16
|
+
*/
|
|
17
|
+
export declare function readOutput(chunks: AsyncIterable<OutputChunk>): Promise<Output>;
|
|
18
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAW,SAAQ,MAAM;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,GACjC,OAAO,CAAC,MAAM,CAAC,CAajB"}
|
package/dist/output.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads each chunk and decodes the text of each stream. A chunk can end inside
|
|
3
|
+
* a UTF-8 character: the character waits for the next chunk of the same
|
|
4
|
+
* stream, and `output` gets it then, so a chunk of the other stream between
|
|
5
|
+
* the two parts never breaks it. The text is not trimmed.
|
|
6
|
+
*/
|
|
7
|
+
export async function readOutput(chunks) {
|
|
8
|
+
const decoders = { stdout: new TextDecoder(), stderr: new TextDecoder() };
|
|
9
|
+
const text = { stdout: '', stderr: '', output: '' };
|
|
10
|
+
const append = (stream, piece) => {
|
|
11
|
+
text[stream] += piece;
|
|
12
|
+
text.output += piece;
|
|
13
|
+
};
|
|
14
|
+
for await (const { stream, data } of chunks) {
|
|
15
|
+
append(stream, decoders[stream].decode(data, { stream: true }));
|
|
16
|
+
}
|
|
17
|
+
append('stdout', decoders.stdout.decode());
|
|
18
|
+
append('stderr', decoders.stderr.decode());
|
|
19
|
+
return text;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAaA;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAkC;IAElC,MAAM,QAAQ,GAAG,EAAE,MAAM,EAAE,IAAI,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,WAAW,EAAE,EAAE,CAAC;IAC1E,MAAM,IAAI,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACpD,MAAM,MAAM,GAAG,CAAC,MAA6B,EAAE,KAAa,EAAE,EAAE;QAC9D,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC;QACtB,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC;IACvB,CAAC,CAAC;IACF,IAAI,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,MAAM,EAAE,CAAC;QAC5C,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads the progress of the engine to its end. The engine reports an error in
|
|
3
|
+
* the progress after the status 200, so the DockerEngineError has no status.
|
|
4
|
+
*/
|
|
5
|
+
export declare function readProgress(chunks: AsyncIterable<Uint8Array>, request: {
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
}): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../src/progress.ts"],"names":[],"mappings":"AAcA;;;GAGG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,EACjC,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACxC,OAAO,CAAC,IAAI,CAAC,CAQf"}
|
package/dist/progress.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DockerEngineError } from "./errors.js";
|
|
2
|
+
import { ndjson } from "./ndjson.js";
|
|
3
|
+
function isProgressMessage(value) {
|
|
4
|
+
return typeof value === 'object' && value !== null;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Reads the progress of the engine to its end. The engine reports an error in
|
|
8
|
+
* the progress after the status 200, so the DockerEngineError has no status.
|
|
9
|
+
*/
|
|
10
|
+
export async function readProgress(chunks, request) {
|
|
11
|
+
for await (const message of ndjson(chunks)) {
|
|
12
|
+
if (!isProgressMessage(message))
|
|
13
|
+
continue;
|
|
14
|
+
const text = message.errorDetail?.message ?? message.error;
|
|
15
|
+
if (text !== undefined) {
|
|
16
|
+
throw new DockerEngineError(text, { status: undefined, request });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../src/progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AASrC,SAAS,iBAAiB,CAAC,KAAc;IACvC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAiC,EACjC,OAAyC;IAEzC,IAAI,KAAK,EAAE,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC;YAAE,SAAS;QAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,EAAE,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC;QAC3D,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM,IAAI,iBAAiB,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ClientRequest, type RequestOptions } from 'node:http';
|
|
2
|
+
import { Duplex } from 'node:stream';
|
|
3
|
+
import type { Transport } from './transport.ts';
|
|
4
|
+
/** `ssh` exited before the engine answered. It is the cause of a ConnectionFailedError. */
|
|
5
|
+
export declare class SshExitError extends Error {
|
|
6
|
+
readonly exitCode: number | null;
|
|
7
|
+
readonly signal: NodeJS.Signals | null;
|
|
8
|
+
/** The last 4 KB of the standard error of `ssh`, such as "Permission denied (publickey)." */
|
|
9
|
+
readonly stderr: string;
|
|
10
|
+
constructor(exitCode: number | null, signal: NodeJS.Signals | null, stderr: string);
|
|
11
|
+
}
|
|
12
|
+
/** The arguments of the connection helper, as the connhelper of docker/cli gives them. */
|
|
13
|
+
export declare function sshArguments(url: URL): string[];
|
|
14
|
+
/** The connections to an engine at an ssh:// host. Each connection is one `ssh` process. */
|
|
15
|
+
export declare class SshTransport implements Transport {
|
|
16
|
+
#private;
|
|
17
|
+
constructor(url: URL);
|
|
18
|
+
request(options: RequestOptions): ClientRequest;
|
|
19
|
+
/** Ends the standard input of `ssh`: `docker system dial-stdio` then closes the write side to the engine. */
|
|
20
|
+
endInput(socket: Duplex): void;
|
|
21
|
+
close(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=ssh-connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh-connection.d.ts","sourceRoot":"","sources":["../src/ssh-connection.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,cAAc,EAEpB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAKhD,2FAA2F;AAC3F,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IACvC,6FAA6F;IAC7F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAGtB,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,EAC7B,MAAM,EAAE,MAAM;CAUjB;AAED,0FAA0F;AAC1F,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,CAc/C;AA4LD,4FAA4F;AAC5F,qBAAa,YAAa,YAAW,SAAS;;gBAGhC,GAAG,EAAE,GAAG;IAIpB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa;IAI/C,6GAA6G;IAC7G,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { Agent, request, } from 'node:http';
|
|
3
|
+
import { Socket } from 'node:net';
|
|
4
|
+
import { Duplex } from 'node:stream';
|
|
5
|
+
import { finished } from 'node:stream/promises';
|
|
6
|
+
/** The part of the standard error of `ssh` that an SshExitError keeps: the last 4 KB. */
|
|
7
|
+
const STDERR_LIMIT = 4096;
|
|
8
|
+
/** `ssh` exited before the engine answered. It is the cause of a ConnectionFailedError. */
|
|
9
|
+
export class SshExitError extends Error {
|
|
10
|
+
exitCode;
|
|
11
|
+
signal;
|
|
12
|
+
/** The last 4 KB of the standard error of `ssh`, such as "Permission denied (publickey)." */
|
|
13
|
+
stderr;
|
|
14
|
+
constructor(exitCode, signal, stderr) {
|
|
15
|
+
super(`ssh exited with ${exitCode === null ? `signal ${signal}` : `code ${exitCode}`}${stderr.trim() ? `: ${stderr.trim()}` : '.'}`);
|
|
16
|
+
this.name = 'SshExitError';
|
|
17
|
+
this.exitCode = exitCode;
|
|
18
|
+
this.signal = signal;
|
|
19
|
+
this.stderr = stderr;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** The arguments of the connection helper, as the connhelper of docker/cli gives them. */
|
|
23
|
+
export function sshArguments(url) {
|
|
24
|
+
return [
|
|
25
|
+
'-T',
|
|
26
|
+
'-o',
|
|
27
|
+
'BatchMode=yes',
|
|
28
|
+
...(url.username ? ['-l', decodeURIComponent(url.username)] : []),
|
|
29
|
+
...(url.port ? ['-p', url.port] : []),
|
|
30
|
+
'--',
|
|
31
|
+
// An IPv6 address keeps its brackets in a URL; ssh takes it without them, as URL.Hostname() of Go gives it.
|
|
32
|
+
url.hostname.replace(/^\[(.*)\]$/, '$1'),
|
|
33
|
+
'docker',
|
|
34
|
+
'system',
|
|
35
|
+
'dial-stdio',
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* One connection to an SSH engine: one `ssh` process that runs
|
|
40
|
+
* `docker system dial-stdio`. Its readable side is the standard output of
|
|
41
|
+
* `ssh`, and its writable side is the standard input of `ssh`.
|
|
42
|
+
*
|
|
43
|
+
* The end of the writable side ends the standard input of `ssh`, and
|
|
44
|
+
* `docker system dial-stdio` then closes the write side of its connection to
|
|
45
|
+
* the engine. This is the end of the input of an attach or an exec.
|
|
46
|
+
*/
|
|
47
|
+
class SshConnection extends Duplex {
|
|
48
|
+
#child;
|
|
49
|
+
#stderr = '';
|
|
50
|
+
/** Resolves when `ssh` exited and its pipes are closed. */
|
|
51
|
+
exited;
|
|
52
|
+
constructor(url) {
|
|
53
|
+
super({ allowHalfOpen: true });
|
|
54
|
+
const child = spawn('ssh', sshArguments(url), {
|
|
55
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
56
|
+
});
|
|
57
|
+
this.#child = child;
|
|
58
|
+
const closed = new Promise((resolve) => {
|
|
59
|
+
child.once('close', () => resolve());
|
|
60
|
+
});
|
|
61
|
+
this.exited = () => closed;
|
|
62
|
+
child.on('error', (error) => this.destroy(error));
|
|
63
|
+
child.stderr.setEncoding('utf8');
|
|
64
|
+
child.stderr.on('data', (chunk) => {
|
|
65
|
+
this.#stderr = (this.#stderr + chunk).slice(-STDERR_LIMIT);
|
|
66
|
+
});
|
|
67
|
+
child.stdout.on('data', (chunk) => {
|
|
68
|
+
if (!this.push(chunk))
|
|
69
|
+
child.stdout.pause();
|
|
70
|
+
});
|
|
71
|
+
child.stdout.once('end', () => {
|
|
72
|
+
void closed.then(() => {
|
|
73
|
+
const error = this.#exitError();
|
|
74
|
+
if (error)
|
|
75
|
+
this.destroy(error);
|
|
76
|
+
else
|
|
77
|
+
this.push(null);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
// A write to the standard input of an `ssh` that exited fails with EPIPE.
|
|
81
|
+
// The write callback reports it, after the exit of `ssh` gives the reason.
|
|
82
|
+
child.stdin.on('error', () => undefined);
|
|
83
|
+
}
|
|
84
|
+
/** True until `ssh` exits. */
|
|
85
|
+
get running() {
|
|
86
|
+
return this.#child.exitCode === null && this.#child.signalCode === null;
|
|
87
|
+
}
|
|
88
|
+
/** The exit of `ssh` as an error, or undefined while it runs or when it exited with 0. */
|
|
89
|
+
#exitError() {
|
|
90
|
+
const { exitCode, signalCode } = this.#child;
|
|
91
|
+
if (exitCode === 0 || (exitCode === null && signalCode === null)) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
return new SshExitError(exitCode, signalCode, this.#stderr);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Gives the exit of `ssh` in place of an error of its pipes, when `ssh`
|
|
98
|
+
* exited. When `ssh` exited with 0, its output is complete: the reader gets
|
|
99
|
+
* the end of the output before the error of the write, so a command that
|
|
100
|
+
* does not read all of its input still gives its output.
|
|
101
|
+
*/
|
|
102
|
+
#fail(error, callback) {
|
|
103
|
+
void this.exited().then(async () => {
|
|
104
|
+
const exit = this.#exitError();
|
|
105
|
+
if (exit === undefined) {
|
|
106
|
+
await finished(this, { writable: false }).catch(() => undefined);
|
|
107
|
+
}
|
|
108
|
+
callback(exit ?? error);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
_read() {
|
|
112
|
+
this.#child.stdout.resume();
|
|
113
|
+
}
|
|
114
|
+
_write(chunk, _encoding, callback) {
|
|
115
|
+
this.#child.stdin.write(chunk, (error) => {
|
|
116
|
+
if (error)
|
|
117
|
+
this.#fail(error, callback);
|
|
118
|
+
else
|
|
119
|
+
callback();
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
_final(callback) {
|
|
123
|
+
this.#child.stdin.end((error) => {
|
|
124
|
+
if (error)
|
|
125
|
+
this.#fail(error, callback);
|
|
126
|
+
else
|
|
127
|
+
callback();
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
_destroy(error, callback) {
|
|
131
|
+
const { exitCode, signalCode } = this.#child;
|
|
132
|
+
if (exitCode === null && signalCode === null) {
|
|
133
|
+
// The disposal waits for the exit, so the exit must keep the process running.
|
|
134
|
+
this.#child.ref();
|
|
135
|
+
this.#child.kill();
|
|
136
|
+
}
|
|
137
|
+
this.#child.stdin.destroy();
|
|
138
|
+
this.#child.stdout.destroy();
|
|
139
|
+
this.#child.stderr.destroy();
|
|
140
|
+
callback(error);
|
|
141
|
+
}
|
|
142
|
+
/** An idle connection does not keep the process running. */
|
|
143
|
+
unref() {
|
|
144
|
+
this.#child.unref();
|
|
145
|
+
for (const pipe of this.#pipes())
|
|
146
|
+
pipe.unref();
|
|
147
|
+
}
|
|
148
|
+
/** A connection in use keeps the process running. */
|
|
149
|
+
ref() {
|
|
150
|
+
this.#child.ref();
|
|
151
|
+
for (const pipe of this.#pipes())
|
|
152
|
+
pipe.ref();
|
|
153
|
+
}
|
|
154
|
+
#pipes() {
|
|
155
|
+
return [this.#child.stdin, this.#child.stdout, this.#child.stderr].filter((pipe) => pipe instanceof Socket);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Keeps the idle connections to an SSH engine for the next request, and
|
|
160
|
+
* starts a new `ssh` for each request that finds no idle connection.
|
|
161
|
+
*/
|
|
162
|
+
class SshAgent extends Agent {
|
|
163
|
+
#url;
|
|
164
|
+
#connections = new Set();
|
|
165
|
+
constructor(url) {
|
|
166
|
+
super({ keepAlive: true });
|
|
167
|
+
this.#url = url;
|
|
168
|
+
}
|
|
169
|
+
createConnection() {
|
|
170
|
+
const connection = new SshConnection(this.#url);
|
|
171
|
+
this.#connections.add(connection);
|
|
172
|
+
void connection.exited().then(() => {
|
|
173
|
+
this.#connections.delete(connection);
|
|
174
|
+
// An idle connection whose `ssh` exited, such as after a restart of the
|
|
175
|
+
// engine, cannot take the next request. Its close removes it from the
|
|
176
|
+
// free connections of the agent.
|
|
177
|
+
if (this.#isIdle(connection))
|
|
178
|
+
connection.destroy();
|
|
179
|
+
});
|
|
180
|
+
return connection;
|
|
181
|
+
}
|
|
182
|
+
#isIdle(connection) {
|
|
183
|
+
return Object.values(this.freeSockets).some((sockets) => sockets?.some((socket) => Object.is(socket, connection)));
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* The keepSocketAlive of Node sets a timeout and TCP keep-alive, which a
|
|
187
|
+
* connection through `ssh` does not have. The reuseSocket of Node stays: it
|
|
188
|
+
* removes the error listener of the idle connection and calls ref(). A
|
|
189
|
+
* connection whose `ssh` exited is not kept.
|
|
190
|
+
*/
|
|
191
|
+
keepSocketAlive(socket) {
|
|
192
|
+
if (!(socket instanceof SshConnection))
|
|
193
|
+
return true;
|
|
194
|
+
if (!socket.running)
|
|
195
|
+
return false;
|
|
196
|
+
socket.unref();
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
/** Ends each `ssh` process and resolves when each one exited. */
|
|
200
|
+
async close() {
|
|
201
|
+
const connections = [...this.#connections];
|
|
202
|
+
this.destroy();
|
|
203
|
+
for (const connection of connections)
|
|
204
|
+
connection.destroy();
|
|
205
|
+
await Promise.all(connections.map((connection) => connection.exited()));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/** The connections to an engine at an ssh:// host. Each connection is one `ssh` process. */
|
|
209
|
+
export class SshTransport {
|
|
210
|
+
#agent;
|
|
211
|
+
constructor(url) {
|
|
212
|
+
this.#agent = new SshAgent(url);
|
|
213
|
+
}
|
|
214
|
+
request(options) {
|
|
215
|
+
return request({ ...options, agent: this.#agent });
|
|
216
|
+
}
|
|
217
|
+
/** Ends the standard input of `ssh`: `docker system dial-stdio` then closes the write side to the engine. */
|
|
218
|
+
endInput(socket) {
|
|
219
|
+
socket.end();
|
|
220
|
+
}
|
|
221
|
+
async close() {
|
|
222
|
+
await this.#agent.close();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=ssh-connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh-connection.js","sourceRoot":"","sources":["../src/ssh-connection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** One entry of a tar archive, with its bytes. */
|
|
2
|
+
export interface TarEntry {
|
|
3
|
+
/** The name in the archive, without a trailing '/'. */
|
|
4
|
+
path: string;
|
|
5
|
+
/** 'link' is a hard link; 'other' is a device, a FIFO or another kind. */
|
|
6
|
+
type: 'file' | 'directory' | 'symlink' | 'link' | 'other';
|
|
7
|
+
mode: number;
|
|
8
|
+
uid: number;
|
|
9
|
+
gid: number;
|
|
10
|
+
/** Seconds since the Unix epoch. */
|
|
11
|
+
mtime: number;
|
|
12
|
+
/** The target of a symlink or of a hard link; '' for each other entry. */
|
|
13
|
+
linkTarget: string;
|
|
14
|
+
data: Uint8Array;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The entries of a tar archive, as the chunks come. It takes ustar, PAX
|
|
18
|
+
* headers for one entry ('x'), and the GNU long name ('L') and long link
|
|
19
|
+
* ('K'). It skips a global PAX header ('g'). Each entry comes with its bytes.
|
|
20
|
+
*/
|
|
21
|
+
export declare function tarEntries(chunks: AsyncIterable<Uint8Array>): AsyncGenerator<TarEntry>;
|
|
22
|
+
//# sourceMappingURL=tar-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tar-reader.d.ts","sourceRoot":"","sources":["../src/tar-reader.ts"],"names":[],"mappings":"AAEA,kDAAkD;AAClD,MAAM,WAAW,QAAQ;IACvB,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,UAAU,CAAC;CAClB;AAwGD;;;;GAIG;AACH,wBAAuB,UAAU,CAC/B,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,GAChC,cAAc,CAAC,QAAQ,CAAC,CA4D1B"}
|