@gapi/gcli 1.8.248 → 1.8.249
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/README-CLI.md
CHANGED
|
@@ -33,7 +33,7 @@ Commands:
|
|
|
33
33
|
mq-trigger:create [options] Run a lambda for every message on a RabbitMQ queue
|
|
34
34
|
mq-trigger:update [options] Change a message-queue trigger (same flags as create)
|
|
35
35
|
mq-trigger:delete [options] Delete a message-queue trigger
|
|
36
|
-
cluster:create [options] Provision a private Talos cluster for a project (runs in the background
|
|
36
|
+
cluster:create [options] Provision a private Talos cluster, or with --virtual a virtual cluster, for a project (runs in the background; use --wait to block)
|
|
37
37
|
cluster:apply [options] Make a private cluster match the `cluster:` section of lambforge.yaml: creates it if missing, then applies topology, workers, control planes, load balancer and autoscaling
|
|
38
38
|
cluster:list [options] List every private cluster this project has (it may have several - dev/stage/prod, ...)
|
|
39
39
|
cluster:teardown [options] Tear down one of a project's clusters (runs in the background - use --wait to block)
|
|
@@ -47,7 +47,7 @@ Commands:
|
|
|
47
47
|
cluster:autoscale [options] Change the worker autoscaling policy - only the flags you pass change
|
|
48
48
|
cluster:multi-node [options] Convert a single-node cluster to dedicated worker nodes (required for autoscaling)
|
|
49
49
|
cluster:single-node [options] Convert a multi-node cluster back to a single node (removes the worker nodes)
|
|
50
|
-
cluster:load-balancer [options] Add or remove the cluster's load balancer (billed per hour while enabled)
|
|
50
|
+
cluster:load-balancer [options] Add or remove the cluster's load balancer (billed per hour while enabled). On a virtual cluster this is its dedicated LB - its own IP; the platform subdomain keeps working either way
|
|
51
51
|
rabbitmq:create [options] Provision a RabbitMQ broker for a project (a project may have several)
|
|
52
52
|
rabbitmq:list [options] List every RabbitMQ broker for a project
|
|
53
53
|
rabbitmq:delete [options] Delete a RabbitMQ broker
|
|
@@ -594,7 +594,7 @@ Options:
|
|
|
594
594
|
--- Subcommand: cluster:create ---[0m
|
|
595
595
|
Usage: gcli cluster:create [options]
|
|
596
596
|
|
|
597
|
-
Provision a private Talos cluster for a project (runs in the background
|
|
597
|
+
Provision a private Talos cluster, or with --virtual a virtual cluster, for a project (runs in the background; use --wait to block)
|
|
598
598
|
|
|
599
599
|
Options:
|
|
600
600
|
-p, --project <project>
|
|
@@ -605,6 +605,14 @@ Options:
|
|
|
605
605
|
-l, --location <location>
|
|
606
606
|
--workers <workers> Worker count (ignored when --singleNode)
|
|
607
607
|
--singleNode Control plane carries the workload too
|
|
608
|
+
--virtual A virtual cluster (own Kubernetes API + Fission,
|
|
609
|
+
no servers, ~2 minutes) instead of a private Talos
|
|
610
|
+
cluster
|
|
611
|
+
--host <host> With --virtual: name of a private cluster of this
|
|
612
|
+
project to run on (default: the shared cluster)
|
|
613
|
+
--dedicated-lb With --virtual: give its router its own load
|
|
614
|
+
balancer IP (billed per hour); without it lambdas
|
|
615
|
+
are served on the platform subdomain
|
|
608
616
|
--wait Block until the cluster is ready (or errors)
|
|
609
617
|
-h, --help display help for command
|
|
610
618
|
[36m
|
|
@@ -770,7 +778,7 @@ Options:
|
|
|
770
778
|
--- Subcommand: cluster:load-balancer ---[0m
|
|
771
779
|
Usage: gcli cluster:load-balancer [options]
|
|
772
780
|
|
|
773
|
-
Add or remove the cluster's load balancer (billed per hour while enabled)
|
|
781
|
+
Add or remove the cluster's load balancer (billed per hour while enabled). On a virtual cluster this is its dedicated LB - its own IP; the platform subdomain keeps working either way
|
|
774
782
|
|
|
775
783
|
Options:
|
|
776
784
|
-p, --project <project>
|
package/README.md
CHANGED
|
@@ -217,12 +217,22 @@ A virtual cluster has its own Kubernetes API and Fission but no servers: it
|
|
|
217
217
|
runs in one namespace of the shared Lambforge cluster, or of one of your
|
|
218
218
|
private clusters, and is ready in about two minutes. Everything else
|
|
219
219
|
(environments, lambdas, `--clusterName`, `cluster:kubeconfig`,
|
|
220
|
-
`cluster:teardown`) works the same as for a private cluster; scaling
|
|
221
|
-
|
|
220
|
+
`cluster:teardown`) works the same as for a private cluster; scaling
|
|
221
|
+
commands don't apply.
|
|
222
|
+
|
|
223
|
+
Its lambdas are served on a platform subdomain (`https://vc-<id>.lambforge.com`,
|
|
224
|
+
shown in the `router` column of `cluster:list`) at no extra cost. A
|
|
225
|
+
**dedicated load balancer** gives its router an IP of its own, billed per
|
|
226
|
+
started hour like any load balancer; the subdomain keeps working.
|
|
227
|
+
`cluster:kubeconfig` gives you admin access through
|
|
228
|
+
`https://k-<id>.lambforge.com` (the `api` column).
|
|
222
229
|
|
|
223
230
|
```bash
|
|
224
231
|
gcli cluster:create --virtual --name dev # on the shared cluster
|
|
225
232
|
gcli cluster:create --virtual --name stage --host prod # on your private cluster "prod"
|
|
233
|
+
gcli cluster:create --virtual --name demo --dedicated-lb # with its own IP
|
|
234
|
+
gcli cluster:load-balancer --cluster <id> --enable # add the dedicated LB later
|
|
235
|
+
gcli cluster:load-balancer --cluster <id> --disable # release it (subdomain stays)
|
|
226
236
|
```
|
|
227
237
|
|
|
228
238
|
```yaml
|
|
@@ -230,8 +240,13 @@ cluster:
|
|
|
230
240
|
name: dev
|
|
231
241
|
provider: vcluster
|
|
232
242
|
host: prod # optional - omitted = the shared cluster
|
|
243
|
+
loadBalancer: true # optional - dedicated LB; cluster:apply adds/removes it
|
|
233
244
|
```
|
|
234
245
|
|
|
246
|
+
`cluster:apply` creates the virtual cluster (with the load balancer if set)
|
|
247
|
+
and later adds or removes the dedicated load balancer when `loadBalancer`
|
|
248
|
+
changes; removing it is skipped unless you pass `--yes`.
|
|
249
|
+
|
|
235
250
|
### RabbitMQ Brokers
|
|
236
251
|
|
|
237
252
|
A project may have several RabbitMQ brokers, each optionally scoped to one of its private clusters.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## 2026-10-28 - virtual-clusters: cluster:create --dedicated-lb, loadBalancer for virtual clusters, cluster:list URLs
|
|
2
|
+
|
|
3
|
+
**Feature:** virtual-clusters · **Type:** feat
|
|
4
|
+
|
|
5
|
+
- `cluster:create --virtual --dedicated-lb` -> provisionProjectVirtualCluster
|
|
6
|
+
`dedicatedLoadBalancer`; `lambforge.yaml` `loadBalancer: true` on a
|
|
7
|
+
`provider: vcluster` cluster is now accepted (no longer in
|
|
8
|
+
VIRTUAL_UNSUPPORTED): created with it, later added/removed by
|
|
9
|
+
`cluster:apply` (removal = destructive, `--yes`).
|
|
10
|
+
- `cluster:load-balancer` help explains the virtual case (dedicated LB, the
|
|
11
|
+
subdomain stays) - the backend already handles vClusters.
|
|
12
|
+
- `cluster:list`: type, router (lambda URL), api, loadBalancer columns;
|
|
13
|
+
`ClusterFragment` + `routerUrl withLoadBalancer loadBalancerIp`.
|
|
14
|
+
- README virtual-clusters section, README-CLI regenerated (it was also
|
|
15
|
+
missing --virtual/--host), public docs gcli page + What's new.
|
|
16
|
+
- **Test:** `npx jest --coverage=false test/cluster-apply` (plan: create
|
|
17
|
+
with LB in one step, add, destructive remove; command: spec ->
|
|
18
|
+
`dedicatedLoadBalancer: true`).
|
|
19
|
+
- **Debug:** `gcli cluster:apply --dry-run` prints the planned LB step;
|
|
20
|
+
`cluster:list` shows `loadBalancer` pending while the re-apply runs.
|
|
21
|
+
- **Verification:** 119/119 tests, tsc clean, eslint clean; built CLI shows
|
|
22
|
+
`--dedicated-lb` in `cluster:create --help`. Not run against a live
|
|
23
|
+
server (gcli is logged into production).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Virtual clusters (slice)
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
3
|
+
**Status:** in production since 2026-10-28 (shared cluster host, subdomain routing, dedicated LB add-on, token kubeconfig). Feature slug `virtual-clusters`.
|
|
4
4
|
Canonical doc (how it works, test, debug, limits):
|
|
5
5
|
[`graphql-server-functions/docs/features/virtual-clusters.md`](../../../../../graphql-server-functions/docs/features/virtual-clusters.md).
|
|
6
6
|
|
|
@@ -9,4 +9,18 @@ cluster or one of the project's private clusters, `provider: 'vcluster'`.
|
|
|
9
9
|
|
|
10
10
|
## This repo
|
|
11
11
|
|
|
12
|
-
`gcli cluster:create --virtual [--host <private-cluster>]
|
|
12
|
+
- `gcli cluster:create --virtual [--host <private-cluster>] [--dedicated-lb]`;
|
|
13
|
+
in `lambforge.yaml`: `cluster: { name: dev, provider: vcluster, host: prod,
|
|
14
|
+
loadBalancer: true }` for `cluster:create` / `cluster:apply` (no host =
|
|
15
|
+
shared cluster; node settings are rejected).
|
|
16
|
+
- `loadBalancer` on a virtual cluster = its **dedicated** LB (own IP,
|
|
17
|
+
billed). `cluster:apply` creates with it in one step, later adds/removes it
|
|
18
|
+
(removal is destructive -> needs `--yes`); `cluster:load-balancer
|
|
19
|
+
--enable|--disable` does the same by hand (the backend routes
|
|
20
|
+
enable/disableLoadBalancerProjectCluster to the vCluster re-apply).
|
|
21
|
+
- `cluster:list` columns: `type`, `router` (lambda base URL, the
|
|
22
|
+
`vc-<id>` subdomain), `api` (endpoint - `k-<id>` for vClusters on the
|
|
23
|
+
shared host), `loadBalancer` (IP / pending / off). `ClusterFragment`
|
|
24
|
+
selects `routerUrl withLoadBalancer loadBalancerIp`.
|
|
25
|
+
- Code: `src/commands/cluster/helpers/virtual-cluster.ts`, `helpers/plan.ts`,
|
|
26
|
+
`create.ts`, `apply.ts`, `list.ts`. Test: `npx jest --coverage=false test/cluster-apply`.
|
package/package.json
CHANGED
package/release/index.js
CHANGED
|
@@ -36155,6 +36155,9 @@ topology {
|
|
|
36155
36155
|
singleNode
|
|
36156
36156
|
}
|
|
36157
36157
|
endpoint
|
|
36158
|
+
routerUrl
|
|
36159
|
+
withLoadBalancer
|
|
36160
|
+
loadBalancerIp
|
|
36158
36161
|
error
|
|
36159
36162
|
`;
|
|
36160
36163
|
}
|
|
@@ -36946,8 +36949,8 @@ var init_gql_client = __esm({
|
|
|
36946
36949
|
}).pipe((0, import_operators.map)((res) => res.provisionProjectCluster));
|
|
36947
36950
|
}
|
|
36948
36951
|
/** A virtual cluster (vCluster) on the shared cluster or on one of the
|
|
36949
|
-
project's private clusters.
|
|
36950
|
-
|
|
36952
|
+
project's private clusters. `dedicatedLoadBalancer` gives its router
|
|
36953
|
+
its own IP (billed); the platform subdomain works either way. */
|
|
36951
36954
|
static provisionProjectVirtualCluster(projectId, input) {
|
|
36952
36955
|
return this.query({
|
|
36953
36956
|
query: gql`
|
|
@@ -36955,14 +36958,15 @@ var init_gql_client = __esm({
|
|
|
36955
36958
|
$projectId: String!
|
|
36956
36959
|
$name: String!
|
|
36957
36960
|
$hostClusterId: String
|
|
36961
|
+
$dedicatedLoadBalancer: Boolean
|
|
36958
36962
|
) {
|
|
36959
36963
|
provisionProjectVirtualCluster(
|
|
36960
36964
|
projectId: $projectId
|
|
36961
36965
|
name: $name
|
|
36962
36966
|
hostClusterId: $hostClusterId
|
|
36967
|
+
dedicatedLoadBalancer: $dedicatedLoadBalancer
|
|
36963
36968
|
) {
|
|
36964
36969
|
${ClusterFragment}
|
|
36965
|
-
routerUrl
|
|
36966
36970
|
hostNamespace
|
|
36967
36971
|
}
|
|
36968
36972
|
}
|
|
@@ -254759,7 +254763,7 @@ var init_virtual_cluster = __esm({
|
|
|
254759
254763
|
"src/commands/cluster/helpers/virtual-cluster.ts"() {
|
|
254760
254764
|
import_rxjs8 = __toESM(require_cjs());
|
|
254761
254765
|
init_gql_client();
|
|
254762
|
-
provisionVirtualCluster = async (projectId, name2, host) => {
|
|
254766
|
+
provisionVirtualCluster = async (projectId, name2, host, dedicatedLoadBalancer) => {
|
|
254763
254767
|
let hostClusterId;
|
|
254764
254768
|
if (host) {
|
|
254765
254769
|
const clusters = await (0, import_rxjs8.lastValueFrom)(
|
|
@@ -254777,7 +254781,8 @@ var init_virtual_cluster = __esm({
|
|
|
254777
254781
|
return (0, import_rxjs8.lastValueFrom)(
|
|
254778
254782
|
GraphqlClienAPI.provisionProjectVirtualCluster(projectId, {
|
|
254779
254783
|
name: name2,
|
|
254780
|
-
hostClusterId
|
|
254784
|
+
hostClusterId,
|
|
254785
|
+
...dedicatedLoadBalancer === void 0 ? {} : { dedicatedLoadBalancer }
|
|
254781
254786
|
})
|
|
254782
254787
|
);
|
|
254783
254788
|
};
|
|
@@ -254819,7 +254824,8 @@ var init_create = __esm({
|
|
|
254819
254824
|
provisionVirtualCluster(
|
|
254820
254825
|
projectId,
|
|
254821
254826
|
cmd.name || data.name,
|
|
254822
|
-
cmd.host ?? data.host
|
|
254827
|
+
cmd.host ?? data.host,
|
|
254828
|
+
cmd.dedicatedLb ?? data.loadBalancer
|
|
254823
254829
|
)
|
|
254824
254830
|
) : GraphqlClienAPI.provisionProjectCluster(projectId, {
|
|
254825
254831
|
name: cmd.name || data.name,
|
|
@@ -254960,7 +254966,6 @@ var init_plan = __esm({
|
|
|
254960
254966
|
"singleNode",
|
|
254961
254967
|
"workers",
|
|
254962
254968
|
"controlPlanes",
|
|
254963
|
-
"loadBalancer",
|
|
254964
254969
|
"autoscale"
|
|
254965
254970
|
];
|
|
254966
254971
|
intIn = (v, min, max) => Number.isInteger(v) && v >= min && v <= max;
|
|
@@ -255022,8 +255027,21 @@ var init_plan = __esm({
|
|
|
255022
255027
|
if (!current) {
|
|
255023
255028
|
steps.push({
|
|
255024
255029
|
kind: "create",
|
|
255025
|
-
description: `create virtual cluster "${desired.name}" on ${desired.host ? `"${desired.host}"` : "the shared cluster"}`
|
|
255030
|
+
description: `create virtual cluster "${desired.name}" on ${desired.host ? `"${desired.host}"` : "the shared cluster"}${desired.loadBalancer ? " with a dedicated load balancer" : ""}`
|
|
255026
255031
|
});
|
|
255032
|
+
} else if (desired.loadBalancer !== void 0 && !!current.withLoadBalancer !== desired.loadBalancer) {
|
|
255033
|
+
steps.push(
|
|
255034
|
+
desired.loadBalancer ? {
|
|
255035
|
+
kind: "load-balancer",
|
|
255036
|
+
enabled: true,
|
|
255037
|
+
description: "add a dedicated load balancer (its own IP, billed; the subdomain stays)"
|
|
255038
|
+
} : {
|
|
255039
|
+
kind: "load-balancer",
|
|
255040
|
+
enabled: false,
|
|
255041
|
+
description: "remove the dedicated load balancer (its IP is released; the subdomain stays)",
|
|
255042
|
+
destructive: true
|
|
255043
|
+
}
|
|
255044
|
+
);
|
|
255027
255045
|
}
|
|
255028
255046
|
return { steps, notes };
|
|
255029
255047
|
}
|
|
@@ -255208,7 +255226,12 @@ var init_apply = __esm({
|
|
|
255208
255226
|
}
|
|
255209
255227
|
Logger.info(`-> ${step.description}`);
|
|
255210
255228
|
if (step.kind === "create") {
|
|
255211
|
-
const created = desired.provider === "vcluster" ? await provisionVirtualCluster(
|
|
255229
|
+
const created = desired.provider === "vcluster" ? await provisionVirtualCluster(
|
|
255230
|
+
projectId,
|
|
255231
|
+
desired.name,
|
|
255232
|
+
desired.host,
|
|
255233
|
+
desired.loadBalancer
|
|
255234
|
+
) : await (0, import_rxjs12.lastValueFrom)(
|
|
255212
255235
|
GraphqlClienAPI.provisionProjectCluster(projectId, {
|
|
255213
255236
|
name: desired.name,
|
|
255214
255237
|
imageId: desired.imageId,
|
|
@@ -255293,14 +255316,20 @@ var init_list = __esm({
|
|
|
255293
255316
|
"No private clusters for this project yet - this project runs on the shared cluster. See cluster:create."
|
|
255294
255317
|
);
|
|
255295
255318
|
} else {
|
|
255296
|
-
Logger.table(
|
|
255297
|
-
|
|
255298
|
-
|
|
255299
|
-
|
|
255300
|
-
|
|
255301
|
-
|
|
255302
|
-
|
|
255303
|
-
|
|
255319
|
+
Logger.table(
|
|
255320
|
+
clusters.map((c) => {
|
|
255321
|
+
const x2 = c;
|
|
255322
|
+
return {
|
|
255323
|
+
id: x2.id,
|
|
255324
|
+
name: x2.name,
|
|
255325
|
+
type: x2.provider === "vcluster" ? "virtual" : x2.provider,
|
|
255326
|
+
status: x2.status,
|
|
255327
|
+
router: x2.routerUrl ?? "-",
|
|
255328
|
+
api: x2.endpoint ?? "-",
|
|
255329
|
+
loadBalancer: x2.withLoadBalancer ? x2.loadBalancerIp ?? "pending" : "off"
|
|
255330
|
+
};
|
|
255331
|
+
})
|
|
255332
|
+
);
|
|
255304
255333
|
}
|
|
255305
255334
|
Logger.log("-------------------");
|
|
255306
255335
|
})
|
|
@@ -323006,6 +323035,9 @@ function registerClusterCommands(program2) {
|
|
|
323006
323035
|
).option(
|
|
323007
323036
|
"--host <host>",
|
|
323008
323037
|
"With --virtual: name of a private cluster of this project to run on (default: the shared cluster)"
|
|
323038
|
+
).option(
|
|
323039
|
+
"--dedicated-lb",
|
|
323040
|
+
"With --virtual: give its router its own load balancer IP (billed per hour); without it lambdas are served on the platform subdomain"
|
|
323009
323041
|
).option("--wait", "Block until the cluster is ready (or errors)").action(lazy(() => Promise.resolve().then(() => (init_create(), create_exports)).then((m2) => m2.default)));
|
|
323010
323042
|
program2.command("cluster:apply").description(
|
|
323011
323043
|
"Make a private cluster match the `cluster:` section of lambforge.yaml: creates it if missing, then applies topology, workers, control planes, load balancer and autoscaling"
|
|
@@ -323072,7 +323104,7 @@ function registerClusterCommands(program2) {
|
|
|
323072
323104
|
).option("--wait", "Block until the cluster settles").action(lazy(() => Promise.resolve().then(() => (init_single_node(), single_node_exports)).then((m2) => m2.default)));
|
|
323073
323105
|
target(
|
|
323074
323106
|
program2.command("cluster:load-balancer").description(
|
|
323075
|
-
"Add or remove the cluster's load balancer (billed per hour while enabled)"
|
|
323107
|
+
"Add or remove the cluster's load balancer (billed per hour while enabled). On a virtual cluster this is its dedicated LB - its own IP; the platform subdomain keeps working either way"
|
|
323076
323108
|
)
|
|
323077
323109
|
).option("--enable", "Add the load balancer").option("--disable", "Remove the load balancer").option("--wait", "Block until the cluster settles").action(lazy(() => Promise.resolve().then(() => (init_load_balancer(), load_balancer_exports)).then((m2) => m2.default)));
|
|
323078
323110
|
}
|