airfoil 0.3.0 → 0.4.1
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/dist/index.js +843 -99650
- package/package.json +21 -22
- package/src/commands/cluster/index.ts +15 -42
- package/src/commands/cluster/namespace/create.ts +280 -73
- package/src/commands/cluster/namespace/delete.ts +12 -20
- package/src/commands/cluster/namespace/get.ts +15 -21
- package/src/commands/cluster/namespace/list.ts +17 -44
- package/src/commands/cluster/table/create.ts +279 -0
- package/src/commands/cluster/table/delete.ts +54 -0
- package/src/commands/cluster/table/get.ts +62 -0
- package/src/commands/cluster/table/list.ts +61 -0
- package/src/commands/dev.ts +73 -172
- package/src/commands/sql.ts +31 -62
- package/src/index.ts +13 -6
- package/src/utils/client.ts +3 -3
- package/src/utils/docker.ts +137 -0
- package/src/utils/options.ts +2 -4
- package/src/utils/wings.ts +72 -64
- package/src/commands/cluster/data-lake/create-iceberg.ts +0 -61
- package/src/commands/cluster/data-lake/create-parquet.ts +0 -61
- package/src/commands/cluster/data-lake/create.ts +0 -16
- package/src/commands/cluster/data-lake/delete.ts +0 -58
- package/src/commands/cluster/data-lake/get.ts +0 -46
- package/src/commands/cluster/data-lake/list.ts +0 -72
- package/src/commands/cluster/object-store/create-aws.ts +0 -106
- package/src/commands/cluster/object-store/create-azure.ts +0 -100
- package/src/commands/cluster/object-store/create-google.ts +0 -102
- package/src/commands/cluster/object-store/create-s3.ts +0 -126
- package/src/commands/cluster/object-store/create.ts +0 -20
- package/src/commands/cluster/object-store/delete.ts +0 -58
- package/src/commands/cluster/object-store/get.ts +0 -51
- package/src/commands/cluster/object-store/list.ts +0 -72
- package/src/commands/cluster/tenant/create.ts +0 -46
- package/src/commands/cluster/tenant/delete.ts +0 -62
- package/src/commands/cluster/tenant/get.ts +0 -44
- package/src/commands/cluster/tenant/list.ts +0 -60
- package/src/commands/cluster/topic/create.ts +0 -317
- package/src/commands/cluster/topic/delete.ts +0 -67
- package/src/commands/cluster/topic/get.ts +0 -51
- package/src/commands/cluster/topic/list.ts +0 -70
- package/src/scripts/compile-all.ts +0 -21
- package/src/utils/effect.ts +0 -11
package/src/commands/dev.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import {
|
|
1
|
+
import { Effect, FileSystem } from "effect";
|
|
2
|
+
import { Command, Flag, Prompt } from "effect/unstable/cli";
|
|
3
|
+
import { ChildProcess } from "effect/unstable/process";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
checkDockerVersion,
|
|
7
|
+
createDockerVolume,
|
|
8
|
+
pullDockerImage,
|
|
9
|
+
runDockerContainer,
|
|
10
|
+
} from "../utils/docker";
|
|
11
|
+
import { downloadWings, getWingsPath } from "../utils/wings";
|
|
6
12
|
|
|
7
13
|
const dockerOption = Flag.boolean("docker").pipe(
|
|
8
14
|
Flag.withDescription("Run Wings using Docker (recommended)"),
|
|
@@ -10,9 +16,7 @@ const dockerOption = Flag.boolean("docker").pipe(
|
|
|
10
16
|
);
|
|
11
17
|
|
|
12
18
|
const versionOption = Flag.string("version").pipe(
|
|
13
|
-
Flag.withDescription(
|
|
14
|
-
"Specify Wings version (e.g., v0.1.0-alpha.11 or 'latest')",
|
|
15
|
-
),
|
|
19
|
+
Flag.withDescription("Specify Wings version (e.g., v0.1.0-alpha.14 or 'latest')"),
|
|
16
20
|
Flag.withDefault("latest"),
|
|
17
21
|
);
|
|
18
22
|
|
|
@@ -47,189 +51,86 @@ export const devCommand = Command.make(
|
|
|
47
51
|
stress: stressOption,
|
|
48
52
|
yes: yesOption,
|
|
49
53
|
},
|
|
50
|
-
(options) =>
|
|
51
|
-
Effect.tryPromise({
|
|
52
|
-
try: () =>
|
|
53
|
-
options.docker ? runWithDocker(options) : runWithBinary(options),
|
|
54
|
-
catch: (error) =>
|
|
55
|
-
error instanceof Error ? error : new Error("Operation failed"),
|
|
56
|
-
}).pipe(Effect.catch(handleCliError("Operation failed"))),
|
|
54
|
+
(options) => (options.docker ? runWithDocker(options) : runWithBinary(options)),
|
|
57
55
|
).pipe(
|
|
58
56
|
Command.withDescription(
|
|
59
57
|
"Download and run Wings dev server locally (Docker recommended for portability)",
|
|
60
58
|
),
|
|
61
59
|
);
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
stress: boolean;
|
|
67
|
-
}) {
|
|
68
|
-
p.intro("🪽 Airfoil Dev");
|
|
69
|
-
|
|
70
|
-
const version = options.version;
|
|
71
|
-
const isStress = options.stress;
|
|
72
|
-
const wingsPath = getWingsPath(version, isStress);
|
|
73
|
-
const fileExists = await Bun.file(wingsPath).exists();
|
|
74
|
-
|
|
75
|
-
if (!fileExists) {
|
|
76
|
-
p.log.warn(
|
|
77
|
-
`Wings${isStress ? " stress" : ""} binary not found for version ${version}`,
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
if (!options.yes) {
|
|
81
|
-
const confirm = await p.confirm({
|
|
82
|
-
message: `Download Wings${isStress ? " stress" : ""} ${version} binary? (~100MB)`,
|
|
83
|
-
initialValue: true,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
if (p.isCancel(confirm) || !confirm) {
|
|
87
|
-
p.cancel("Download cancelled");
|
|
88
|
-
process.exit(0);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
61
|
+
const runWithBinary = (options: { version: string; yes: boolean; stress: boolean }) =>
|
|
62
|
+
Effect.gen(function* () {
|
|
63
|
+
const fs = yield* FileSystem.FileSystem;
|
|
91
64
|
|
|
92
|
-
|
|
93
|
-
s.start(`Downloading Wings${isStress ? " stress" : ""} ${version}...`);
|
|
65
|
+
yield* Effect.logInfo("🪽 Airfoil Dev");
|
|
94
66
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
s.stop("Download failed");
|
|
100
|
-
throw error;
|
|
101
|
-
}
|
|
67
|
+
const version = options.version;
|
|
68
|
+
const isStress = options.stress;
|
|
69
|
+
const wingsPath = yield* getWingsPath(version, isStress);
|
|
70
|
+
const exists = yield* fs.exists(wingsPath);
|
|
102
71
|
|
|
103
|
-
|
|
104
|
-
|
|
72
|
+
if (!exists) {
|
|
73
|
+
yield* Effect.logWarning(
|
|
74
|
+
`Wings${isStress ? " stress" : ""} binary not found for version ${version}`,
|
|
75
|
+
);
|
|
105
76
|
|
|
106
|
-
|
|
107
|
-
|
|
77
|
+
if (!options.yes) {
|
|
78
|
+
const confirm = yield* Prompt.confirm({
|
|
79
|
+
message: `Download Wings${isStress ? " stress" : ""} ${version} binary? (~100MB)`,
|
|
80
|
+
initial: true,
|
|
81
|
+
});
|
|
108
82
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
"Binary checksum does not match expected value. This could indicate a corrupted download or security issue.",
|
|
113
|
-
);
|
|
83
|
+
if (!confirm) {
|
|
84
|
+
return yield* Effect.fail(new Error("Download cancelled"));
|
|
85
|
+
}
|
|
114
86
|
}
|
|
115
87
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
88
|
+
yield* Effect.logInfo(`Downloading Wings${isStress ? " stress" : ""} ${version}...`);
|
|
89
|
+
|
|
90
|
+
yield* downloadWings(version, wingsPath, isStress);
|
|
91
|
+
} else {
|
|
92
|
+
yield* Effect.logInfo(`Using cached Wings${isStress ? " stress" : ""} binary (${version})`);
|
|
120
93
|
}
|
|
121
94
|
|
|
122
|
-
|
|
123
|
-
} else {
|
|
124
|
-
p.log.success(
|
|
125
|
-
`Using cached Wings${isStress ? " stress" : ""} binary (${version})`,
|
|
126
|
-
);
|
|
127
|
-
}
|
|
95
|
+
yield* Effect.logInfo(`Starting Wings dev server...`);
|
|
128
96
|
|
|
129
|
-
|
|
97
|
+
const handle = yield* ChildProcess.make(wingsPath, ["dev"], {
|
|
98
|
+
stdin: "inherit",
|
|
99
|
+
stdout: "inherit",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const exitCode = yield* handle.exitCode;
|
|
130
103
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
stdin: "inherit",
|
|
104
|
+
if (exitCode !== 0) {
|
|
105
|
+
return yield* Effect.fail(new Error(`Process exited with code ${exitCode}`));
|
|
106
|
+
}
|
|
135
107
|
});
|
|
136
108
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (tag !== "latest" && !tag.includes("aarch64") && !tag.includes("x86_64")) {
|
|
148
|
-
const arch = process.arch === "arm64" ? "aarch64" : "x86_64";
|
|
149
|
-
// Remove 'v' prefix if present for docker tags
|
|
150
|
-
const cleanTag = tag.startsWith("v") ? tag.substring(1) : tag;
|
|
151
|
-
tag = `${cleanTag}-${arch}`;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const image = `docker.useairfoil.com/airfoil/wings:${tag}`;
|
|
155
|
-
|
|
156
|
-
p.log.info(`Using Docker image: ${image}`);
|
|
157
|
-
p.log.info("Ports: 7777 (metadata), 7780 (http)");
|
|
158
|
-
|
|
159
|
-
try {
|
|
160
|
-
await Bun.$`docker --version`.quiet();
|
|
161
|
-
} catch {
|
|
162
|
-
p.cancel(
|
|
163
|
-
"Docker is not installed or not running. Please install Docker first.",
|
|
164
|
-
);
|
|
165
|
-
process.exit(1);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
let imageExists = false;
|
|
169
|
-
if (!options.forcePull) {
|
|
170
|
-
try {
|
|
171
|
-
const result = await Bun.$`docker images -q ${image}`.text();
|
|
172
|
-
imageExists = result.trim().length > 0;
|
|
173
|
-
} catch {
|
|
174
|
-
imageExists = false;
|
|
109
|
+
const runWithDocker = (options: { tag: string; forcePull: boolean }) =>
|
|
110
|
+
Effect.gen(function* () {
|
|
111
|
+
yield* Effect.logInfo("🐳 Airfoil Dev (Docker)");
|
|
112
|
+
|
|
113
|
+
let tag = options.tag;
|
|
114
|
+
|
|
115
|
+
if (tag !== "latest" && !tag.includes("aarch64") && !tag.includes("x86_64")) {
|
|
116
|
+
const arch = process.arch === "arm64" ? "aarch64" : "x86_64";
|
|
117
|
+
const cleanTag = tag.startsWith("v") ? tag.substring(1) : tag;
|
|
118
|
+
tag = `${cleanTag}-${arch}`;
|
|
175
119
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
await Bun.$`docker pull ${image}`.quiet();
|
|
188
|
-
s.stop("Docker image ready");
|
|
189
|
-
} catch {
|
|
190
|
-
s.stop("Failed to pull image");
|
|
191
|
-
p.cancel(
|
|
192
|
-
"Could not download Docker image. Please check your internet connection and registry access.",
|
|
193
|
-
);
|
|
194
|
-
process.exit(1);
|
|
120
|
+
|
|
121
|
+
const image = `docker.useairfoil.com/airfoil/wings:${tag}`;
|
|
122
|
+
|
|
123
|
+
yield* Effect.logInfo(`Using Docker image: ${image}`);
|
|
124
|
+
yield* Effect.logInfo("Ports: 7777 (gRPC)");
|
|
125
|
+
|
|
126
|
+
yield* checkDockerVersion();
|
|
127
|
+
|
|
128
|
+
if (options.forcePull) {
|
|
129
|
+
yield* Effect.logInfo(`Pulling Docker image: ${image}`);
|
|
130
|
+
yield* pullDockerImage(image);
|
|
195
131
|
}
|
|
196
|
-
} else {
|
|
197
|
-
p.log.success("Using cached Docker image");
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
try {
|
|
201
|
-
await Bun.$`docker volume create wings-data`.quiet();
|
|
202
|
-
} catch {
|
|
203
|
-
// volume may already exist, that's fine
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
p.log.info("Starting Wings dev server...");
|
|
207
|
-
|
|
208
|
-
const proc = Bun.spawn(
|
|
209
|
-
[
|
|
210
|
-
"docker",
|
|
211
|
-
"run",
|
|
212
|
-
"-it",
|
|
213
|
-
"--rm",
|
|
214
|
-
"--name",
|
|
215
|
-
"wings-dev",
|
|
216
|
-
"-v",
|
|
217
|
-
"wings-data:/tmp",
|
|
218
|
-
"-p",
|
|
219
|
-
"7777:7777",
|
|
220
|
-
"-p",
|
|
221
|
-
"7780:7780",
|
|
222
|
-
image,
|
|
223
|
-
"dev",
|
|
224
|
-
"--http.address=0.0.0.0:7780",
|
|
225
|
-
"--metadata.address=0.0.0.0:7777",
|
|
226
|
-
],
|
|
227
|
-
{
|
|
228
|
-
stdout: "inherit",
|
|
229
|
-
stderr: "inherit",
|
|
230
|
-
stdin: "inherit",
|
|
231
|
-
},
|
|
232
|
-
);
|
|
233
132
|
|
|
234
|
-
|
|
235
|
-
|
|
133
|
+
yield* createDockerVolume("wings-data");
|
|
134
|
+
|
|
135
|
+
yield* runDockerContainer(image, "wings-data");
|
|
136
|
+
});
|
package/src/commands/sql.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import * as p from "@clack/prompts";
|
|
3
1
|
import { ArrowFlightSqlClient } from "@useairfoil/flight";
|
|
4
2
|
import { printTable } from "console-table-printer";
|
|
5
|
-
import { Effect, Option } from "effect";
|
|
3
|
+
import { Effect, FileSystem, Option, Stream } from "effect";
|
|
6
4
|
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
7
5
|
import { Metadata } from "nice-grpc-common";
|
|
8
|
-
|
|
9
|
-
import { hostOption, portOption } from "../utils/options
|
|
6
|
+
|
|
7
|
+
import { hostOption, portOption } from "../utils/options";
|
|
10
8
|
|
|
11
9
|
const queryArg = Argument.string("query").pipe(
|
|
12
10
|
Argument.withDescription("SQL query to execute"),
|
|
@@ -22,7 +20,7 @@ const fileOption = Flag.string("file").pipe(
|
|
|
22
20
|
const namespaceOption = Flag.string("namespace").pipe(
|
|
23
21
|
Flag.withAlias("n"),
|
|
24
22
|
Flag.withDescription("Wings namespace"),
|
|
25
|
-
Flag.withDefault("
|
|
23
|
+
Flag.withDefault("namespaces/default"),
|
|
26
24
|
);
|
|
27
25
|
|
|
28
26
|
const jsonOption = Flag.boolean("json").pipe(
|
|
@@ -42,80 +40,51 @@ export const sqlCommand = Command.make(
|
|
|
42
40
|
},
|
|
43
41
|
({ query, host, port, file, namespace, json }) =>
|
|
44
42
|
Effect.gen(function* () {
|
|
43
|
+
const fs = yield* FileSystem.FileSystem;
|
|
45
44
|
let sqlQuery = Option.getOrUndefined(query);
|
|
46
45
|
const filePath = Option.getOrUndefined(file);
|
|
47
46
|
|
|
48
47
|
if (filePath) {
|
|
49
|
-
sqlQuery = yield*
|
|
50
|
-
try: () => readFileSync(filePath, "utf-8"),
|
|
51
|
-
catch: (error) =>
|
|
52
|
-
new Error(
|
|
53
|
-
`Failed to read file: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
54
|
-
),
|
|
55
|
-
});
|
|
48
|
+
sqlQuery = yield* fs.readFileString(filePath);
|
|
56
49
|
}
|
|
57
50
|
|
|
58
51
|
if (!sqlQuery) {
|
|
59
52
|
return yield* Effect.fail(
|
|
60
|
-
new Error(
|
|
61
|
-
"No query provided. Use a query argument or --file option.",
|
|
62
|
-
),
|
|
53
|
+
new Error("No query provided. Use a query argument or --file option."),
|
|
63
54
|
);
|
|
64
55
|
}
|
|
65
56
|
|
|
66
57
|
const hostPort = `${host}:${port}`;
|
|
67
58
|
|
|
68
|
-
|
|
69
|
-
p.log.info(`Connecting to: ${hostPort}`);
|
|
70
|
-
p.log.info(`Namespace: ${namespace}`);
|
|
59
|
+
yield* Effect.logInfo(`Namespace: ${namespace}`);
|
|
71
60
|
|
|
72
|
-
const client =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
metadata: Metadata({
|
|
80
|
-
"x-wings-namespace": namespace,
|
|
81
|
-
}),
|
|
82
|
-
},
|
|
61
|
+
const client = yield* ArrowFlightSqlClient.make({
|
|
62
|
+
host: hostPort,
|
|
63
|
+
defaultCallOptions: {
|
|
64
|
+
"*": {
|
|
65
|
+
metadata: Metadata({
|
|
66
|
+
"x-wings-namespace": namespace,
|
|
67
|
+
}),
|
|
83
68
|
},
|
|
84
69
|
},
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
const s = p.spinner();
|
|
88
|
-
s.start("Executing query...");
|
|
89
|
-
|
|
90
|
-
const flightInfo = yield* Effect.tryPromise({
|
|
91
|
-
try: () => client.executeQuery({ query: sqlQuery }),
|
|
92
|
-
catch: (error) =>
|
|
93
|
-
error instanceof Error ? error : new Error("Query failed"),
|
|
94
|
-
}).pipe(Effect.tapError(() => Effect.sync(() => s.stop("Query failed"))));
|
|
70
|
+
});
|
|
95
71
|
|
|
96
|
-
const
|
|
97
|
-
try: () => Array.fromAsync(client.executeFlightInfo(flightInfo)),
|
|
98
|
-
catch: (error) =>
|
|
99
|
-
error instanceof Error ? error : new Error("Query failed"),
|
|
100
|
-
}).pipe(Effect.tapError(() => Effect.sync(() => s.stop("Query failed"))));
|
|
72
|
+
const flightInfo = yield* client.executeQuery({ query: sqlQuery });
|
|
101
73
|
|
|
102
|
-
const
|
|
74
|
+
const batches = yield* client.executeFlightInfo(flightInfo).pipe(Stream.runCollect);
|
|
103
75
|
|
|
104
|
-
|
|
76
|
+
const data = Array.from(batches).flatMap(({ batch }) => batch.toArray());
|
|
105
77
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
p.outro("✓ Done");
|
|
119
|
-
});
|
|
120
|
-
}).pipe(Effect.catch(handleCliError("Command failed"))),
|
|
78
|
+
if (json) {
|
|
79
|
+
yield* Effect.log(JSON.stringify(data, null, 2));
|
|
80
|
+
} else if (data.length === 0) {
|
|
81
|
+
yield* Effect.logWarning("No results returned");
|
|
82
|
+
} else {
|
|
83
|
+
printTable(data, {
|
|
84
|
+
defaultColumnOptions: {
|
|
85
|
+
alignment: "left",
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}),
|
|
121
90
|
).pipe(Command.withDescription("Execute SQL queries using Arrow Flight SQL"));
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
3
|
-
import { Effect } from "effect";
|
|
3
|
+
import { Effect, Layer } from "effect";
|
|
4
4
|
import { Command } from "effect/unstable/cli";
|
|
5
|
+
import { FetchHttpClient } from "effect/unstable/http";
|
|
6
|
+
|
|
5
7
|
import packageJson from "../package.json";
|
|
6
|
-
import { clusterCommand } from "./commands/cluster/index
|
|
7
|
-
import { devCommand } from "./commands/dev
|
|
8
|
-
import { sqlCommand } from "./commands/sql
|
|
8
|
+
import { clusterCommand } from "./commands/cluster/index";
|
|
9
|
+
import { devCommand } from "./commands/dev";
|
|
10
|
+
import { sqlCommand } from "./commands/sql";
|
|
9
11
|
|
|
10
12
|
const version = packageJson.version;
|
|
11
13
|
|
|
@@ -17,4 +19,9 @@ const cli = Command.run(program, {
|
|
|
17
19
|
version,
|
|
18
20
|
});
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
NodeRuntime.runMain(
|
|
23
|
+
cli.pipe(
|
|
24
|
+
Effect.provide(Layer.mergeAll(FetchHttpClient.layer, NodeServices.layer)),
|
|
25
|
+
Effect.scoped,
|
|
26
|
+
),
|
|
27
|
+
);
|
package/src/utils/client.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClusterClient } from "@useairfoil/wings";
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
|
|
3
|
+
export const makeClusterClientLayer = (host: string, port: number) =>
|
|
4
|
+
ClusterClient.layer({
|
|
5
5
|
host: `${host}:${port}`,
|
|
6
6
|
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Effect, String } from "effect";
|
|
2
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
3
|
+
|
|
4
|
+
export const checkDockerVersion = () =>
|
|
5
|
+
Effect.gen(function* () {
|
|
6
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
7
|
+
const docker = ChildProcess.make("docker", ["--version"]);
|
|
8
|
+
|
|
9
|
+
return yield* spawner.string(docker).pipe(Effect.map(String.trim));
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export const createDockerVolume = (volumeName: string) =>
|
|
13
|
+
Effect.gen(function* () {
|
|
14
|
+
const docker = yield* ChildProcess.make("docker", ["volume", "create", volumeName]);
|
|
15
|
+
const exitCode = yield* docker.exitCode;
|
|
16
|
+
if (exitCode !== 0) {
|
|
17
|
+
return yield* Effect.fail(new Error(`Docker volume create failed with code ${exitCode}`));
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const createDockerNetwork = (networkName: string) =>
|
|
22
|
+
Effect.gen(function* () {
|
|
23
|
+
const docker = yield* ChildProcess.make("docker", ["network", "create", networkName]);
|
|
24
|
+
const exitCode = yield* docker.exitCode;
|
|
25
|
+
if (exitCode !== 0) {
|
|
26
|
+
return yield* Effect.fail(new Error(`Docker network create failed with code ${exitCode}`));
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const removeDockerContainer = (containerName: string) =>
|
|
31
|
+
Effect.gen(function* () {
|
|
32
|
+
const docker = yield* ChildProcess.make("docker", ["rm", "-f", containerName], {
|
|
33
|
+
stdout: "ignore",
|
|
34
|
+
stderr: "ignore",
|
|
35
|
+
});
|
|
36
|
+
yield* docker.exitCode;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export const removeDockerNetwork = (networkName: string) =>
|
|
40
|
+
Effect.gen(function* () {
|
|
41
|
+
const docker = yield* ChildProcess.make("docker", ["network", "rm", networkName], {
|
|
42
|
+
stdout: "ignore",
|
|
43
|
+
stderr: "ignore",
|
|
44
|
+
});
|
|
45
|
+
yield* docker.exitCode;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const pullDockerImage = (image: string) =>
|
|
49
|
+
Effect.gen(function* () {
|
|
50
|
+
const docker = yield* ChildProcess.make("docker", ["pull", image], {
|
|
51
|
+
stdout: "inherit",
|
|
52
|
+
stderr: "inherit",
|
|
53
|
+
});
|
|
54
|
+
const exitCode = yield* docker.exitCode;
|
|
55
|
+
if (exitCode !== 0) {
|
|
56
|
+
return yield* Effect.fail(new Error(`Docker pull failed with code ${exitCode}`));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export const runDockerContainer = (image: string, volumeName: string) =>
|
|
61
|
+
Effect.gen(function* () {
|
|
62
|
+
const networkName = "airfoil-dev";
|
|
63
|
+
const seaweedContainerName = "airfoil-seaweedfs";
|
|
64
|
+
|
|
65
|
+
yield* removeDockerContainer(seaweedContainerName);
|
|
66
|
+
yield* removeDockerNetwork(networkName);
|
|
67
|
+
yield* createDockerNetwork(networkName);
|
|
68
|
+
yield* Effect.addFinalizer(() =>
|
|
69
|
+
removeDockerNetwork(networkName).pipe(Effect.catchCause(() => Effect.void)),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
73
|
+
const seaweed = ChildProcess.make("docker", [
|
|
74
|
+
"run",
|
|
75
|
+
"-d",
|
|
76
|
+
"--rm",
|
|
77
|
+
"--name",
|
|
78
|
+
seaweedContainerName,
|
|
79
|
+
"--network",
|
|
80
|
+
networkName,
|
|
81
|
+
"--network-alias",
|
|
82
|
+
"seaweedfs",
|
|
83
|
+
"-e",
|
|
84
|
+
"AWS_ACCESS_KEY_ID=wingsdevaccesskey",
|
|
85
|
+
"-e",
|
|
86
|
+
"AWS_SECRET_ACCESS_KEY=wingsdevsecretkey",
|
|
87
|
+
"-e",
|
|
88
|
+
"S3_BUCKET=default-bucket",
|
|
89
|
+
"-v",
|
|
90
|
+
`${volumeName}:/data`,
|
|
91
|
+
"-p",
|
|
92
|
+
"8333:8333",
|
|
93
|
+
"chrislusf/seaweedfs:4.29",
|
|
94
|
+
"weed",
|
|
95
|
+
"mini",
|
|
96
|
+
"-s3",
|
|
97
|
+
"-dir=/data",
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
yield* spawner.string(seaweed).pipe(Effect.map(String.trim));
|
|
101
|
+
yield* Effect.addFinalizer(() =>
|
|
102
|
+
removeDockerContainer(seaweedContainerName).pipe(Effect.catchCause(() => Effect.void)),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const docker = yield* ChildProcess.make("docker", [
|
|
106
|
+
"run",
|
|
107
|
+
"-it",
|
|
108
|
+
"--rm",
|
|
109
|
+
"--name",
|
|
110
|
+
"wings-dev",
|
|
111
|
+
"--network",
|
|
112
|
+
networkName,
|
|
113
|
+
"-e",
|
|
114
|
+
"AWS_ACCESS_KEY_ID=wingsdevaccesskey",
|
|
115
|
+
"-e",
|
|
116
|
+
"AWS_SECRET_ACCESS_KEY=wingsdevsecretkey",
|
|
117
|
+
"-e",
|
|
118
|
+
"AWS_ENDPOINT=http://seaweedfs:8333",
|
|
119
|
+
"-e",
|
|
120
|
+
"AWS_BUCKET_NAME=default-bucket",
|
|
121
|
+
"-e",
|
|
122
|
+
"AWS_DEFAULT_REGION=us-east-1",
|
|
123
|
+
"-e",
|
|
124
|
+
"AWS_ALLOW_HTTP=true",
|
|
125
|
+
"-p",
|
|
126
|
+
"7777:7777",
|
|
127
|
+
image,
|
|
128
|
+
"dev",
|
|
129
|
+
"--grpc.address=0.0.0.0:7777",
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
const exitCode = yield* docker.exitCode;
|
|
133
|
+
|
|
134
|
+
if (exitCode !== 0) {
|
|
135
|
+
return yield* Effect.fail(new Error(`Docker process exited with code ${exitCode}`));
|
|
136
|
+
}
|
|
137
|
+
});
|
package/src/utils/options.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Flag } from "effect/unstable/cli";
|
|
|
5
5
|
*/
|
|
6
6
|
export const hostOption = Flag.string("host").pipe(
|
|
7
7
|
Flag.withDescription("Server host"),
|
|
8
|
-
Flag.withDefault("
|
|
8
|
+
Flag.withDefault("127.0.0.1"),
|
|
9
9
|
);
|
|
10
10
|
|
|
11
11
|
export const portOption = Flag.integer("port").pipe(
|
|
@@ -23,9 +23,7 @@ export const pageSizeOption = Flag.integer("page-size").pipe(
|
|
|
23
23
|
);
|
|
24
24
|
|
|
25
25
|
export const pageTokenOption = Flag.optional(
|
|
26
|
-
Flag.string("page-token").pipe(
|
|
27
|
-
Flag.withDescription("Continuation token for pagination"),
|
|
28
|
-
),
|
|
26
|
+
Flag.string("page-token").pipe(Flag.withDescription("Continuation token for pagination")),
|
|
29
27
|
);
|
|
30
28
|
|
|
31
29
|
/**
|