@effect/cluster 0.53.3 → 0.53.4
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/K8sHttpClient/package.json +6 -0
- package/dist/cjs/EntityResource.js +16 -1
- package/dist/cjs/EntityResource.js.map +1 -1
- package/dist/cjs/K8sHttpClient.js +164 -0
- package/dist/cjs/K8sHttpClient.js.map +1 -0
- package/dist/cjs/RunnerHealth.js +3 -63
- package/dist/cjs/RunnerHealth.js.map +1 -1
- package/dist/cjs/index.js +3 -1
- package/dist/dts/EntityResource.d.ts +9 -0
- package/dist/dts/EntityResource.d.ts.map +1 -1
- package/dist/dts/K8sHttpClient.d.ts +91 -0
- package/dist/dts/K8sHttpClient.d.ts.map +1 -0
- package/dist/dts/RunnerHealth.d.ts +3 -4
- package/dist/dts/RunnerHealth.d.ts.map +1 -1
- package/dist/dts/index.d.ts +4 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/esm/EntityResource.js +15 -0
- package/dist/esm/EntityResource.js.map +1 -1
- package/dist/esm/K8sHttpClient.js +153 -0
- package/dist/esm/K8sHttpClient.js.map +1 -0
- package/dist/esm/RunnerHealth.js +3 -63
- package/dist/esm/RunnerHealth.js.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/package.json +14 -3
- package/src/EntityResource.ts +30 -0
- package/src/K8sHttpClient.ts +240 -0
- package/src/RunnerHealth.ts +4 -79
- package/src/index.ts +5 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as FileSystem from "@effect/platform/FileSystem";
|
|
5
|
+
import * as HttpClient from "@effect/platform/HttpClient";
|
|
6
|
+
import * as HttpClientRequest from "@effect/platform/HttpClientRequest";
|
|
7
|
+
import * as HttpClientResponse from "@effect/platform/HttpClientResponse";
|
|
8
|
+
import * as Context from "effect/Context";
|
|
9
|
+
import * as Effect from "effect/Effect";
|
|
10
|
+
import { identity } from "effect/Function";
|
|
11
|
+
import * as Layer from "effect/Layer";
|
|
12
|
+
import * as Option from "effect/Option";
|
|
13
|
+
import * as Schedule from "effect/Schedule";
|
|
14
|
+
import * as Schema from "effect/Schema";
|
|
15
|
+
/**
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
* @category Tags
|
|
18
|
+
*/
|
|
19
|
+
export class K8sHttpClient extends /*#__PURE__*/Context.Tag("@effect/cluster/K8sHttpClient")() {}
|
|
20
|
+
/**
|
|
21
|
+
* @since 1.0.0
|
|
22
|
+
* @category Layers
|
|
23
|
+
*/
|
|
24
|
+
export const layer = /*#__PURE__*/Layer.effect(K8sHttpClient, /*#__PURE__*/Effect.gen(function* () {
|
|
25
|
+
const fs = yield* FileSystem.FileSystem;
|
|
26
|
+
const token = yield* fs.readFileString("/var/run/secrets/kubernetes.io/serviceaccount/token").pipe(Effect.option);
|
|
27
|
+
return (yield* HttpClient.HttpClient).pipe(HttpClient.mapRequest(HttpClientRequest.prependUrl("https://kubernetes.default.svc/api")), token._tag === "Some" ? HttpClient.mapRequest(HttpClientRequest.bearerToken(token.value.trim())) : identity, HttpClient.filterStatusOk, HttpClient.retryTransient({
|
|
28
|
+
schedule: Schedule.spaced(5000)
|
|
29
|
+
}));
|
|
30
|
+
}));
|
|
31
|
+
/**
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
* @category Constructors
|
|
34
|
+
*/
|
|
35
|
+
export const makeGetPods = /*#__PURE__*/Effect.fnUntraced(function* (options) {
|
|
36
|
+
const client = yield* K8sHttpClient;
|
|
37
|
+
const getPods = HttpClientRequest.get(options?.namespace ? `/v1/namespaces/${options.namespace}/pods` : "/v1/pods").pipe(HttpClientRequest.setUrlParam("fieldSelector", "status.phase=Running"), options?.labelSelector ? HttpClientRequest.setUrlParam("labelSelector", options.labelSelector) : identity);
|
|
38
|
+
return yield* client.execute(getPods).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(PodList)), Effect.map(list => {
|
|
39
|
+
const pods = new Map();
|
|
40
|
+
for (let i = 0; i < list.items.length; i++) {
|
|
41
|
+
const pod = list.items[i];
|
|
42
|
+
pods.set(pod.status.podIP, pod);
|
|
43
|
+
}
|
|
44
|
+
return pods;
|
|
45
|
+
}), Effect.tapErrorCause(cause => Effect.logWarning("Failed to fetch pods from Kubernetes API", cause)), Effect.cachedWithTTL("10 seconds"));
|
|
46
|
+
});
|
|
47
|
+
/**
|
|
48
|
+
* @since 1.0.0
|
|
49
|
+
* @category Constructors
|
|
50
|
+
*/
|
|
51
|
+
export const makeCreatePod = /*#__PURE__*/Effect.gen(function* () {
|
|
52
|
+
const client = yield* K8sHttpClient;
|
|
53
|
+
return Effect.fnUntraced(function* (spec) {
|
|
54
|
+
spec = {
|
|
55
|
+
apiVersion: "v1",
|
|
56
|
+
kind: "Pod",
|
|
57
|
+
metadata: {
|
|
58
|
+
namespace: "default",
|
|
59
|
+
...spec.metadata
|
|
60
|
+
},
|
|
61
|
+
...spec
|
|
62
|
+
};
|
|
63
|
+
const namespace = spec.metadata?.namespace ?? "default";
|
|
64
|
+
const name = spec.metadata.name;
|
|
65
|
+
const readPodRaw = HttpClientRequest.get(`/v1/namespaces/${namespace}/pods/${name}`).pipe(client.execute);
|
|
66
|
+
const readPod = readPodRaw.pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(Pod)), Effect.asSome, Effect.retry({
|
|
67
|
+
while: e => e._tag === "ParseError",
|
|
68
|
+
schedule: Schedule.spaced("1 seconds")
|
|
69
|
+
}), Effect.catchIf(err => err._tag === "ResponseError" && err.response.status === 404, () => Effect.succeedNone), Effect.orDie);
|
|
70
|
+
const isPodFound = readPodRaw.pipe(Effect.as(true), Effect.catchIf(err => err._tag === "ResponseError" && err.response.status === 404, () => Effect.succeed(false)));
|
|
71
|
+
const createPod = HttpClientRequest.post(`/v1/namespaces/${namespace}/pods`).pipe(HttpClientRequest.bodyUnsafeJson(spec), client.execute, Effect.catchIf(err => err._tag === "ResponseError" && err.response.status === 409, () => readPod), Effect.tapErrorCause(Effect.logInfo), Effect.orDie);
|
|
72
|
+
const deletePod = HttpClientRequest.del(`/v1/namespaces/${namespace}/pods/${name}`).pipe(client.execute, Effect.flatMap(res => res.json), Effect.catchIf(err => err._tag === "ResponseError" && err.response.status === 404, () => Effect.void), Effect.tapErrorCause(Effect.logInfo), Effect.orDie, Effect.asVoid);
|
|
73
|
+
yield* Effect.addFinalizer(Effect.fnUntraced(function* () {
|
|
74
|
+
yield* deletePod;
|
|
75
|
+
yield* isPodFound.pipe(Effect.repeat({
|
|
76
|
+
until: found => !found,
|
|
77
|
+
schedule: Schedule.spaced("3 seconds")
|
|
78
|
+
}), Effect.orDie);
|
|
79
|
+
}));
|
|
80
|
+
let opod = Option.none();
|
|
81
|
+
while (Option.isNone(opod) || !opod.value.isReady) {
|
|
82
|
+
if (Option.isNone(opod)) {
|
|
83
|
+
yield* createPod;
|
|
84
|
+
}
|
|
85
|
+
yield* Effect.sleep("3 seconds");
|
|
86
|
+
opod = yield* readPod;
|
|
87
|
+
}
|
|
88
|
+
return opod.value.status;
|
|
89
|
+
}, Effect.withSpan("K8sHttpClient.createPod"));
|
|
90
|
+
});
|
|
91
|
+
/**
|
|
92
|
+
* @since 1.0.0
|
|
93
|
+
* @category Schemas
|
|
94
|
+
*/
|
|
95
|
+
export class PodStatus extends /*#__PURE__*/Schema.Class("@effect/cluster/K8sHttpClient/PodStatus")({
|
|
96
|
+
phase: Schema.String,
|
|
97
|
+
conditions: /*#__PURE__*/Schema.Array(/*#__PURE__*/Schema.Struct({
|
|
98
|
+
type: Schema.String,
|
|
99
|
+
status: Schema.String,
|
|
100
|
+
lastTransitionTime: Schema.String
|
|
101
|
+
})),
|
|
102
|
+
podIP: Schema.String,
|
|
103
|
+
hostIP: Schema.String
|
|
104
|
+
}) {}
|
|
105
|
+
/**
|
|
106
|
+
* @since 1.0.0
|
|
107
|
+
* @category Schemas
|
|
108
|
+
*/
|
|
109
|
+
export class Pod extends /*#__PURE__*/Schema.Class("@effect/cluster/K8sHttpClient/Pod")({
|
|
110
|
+
status: PodStatus
|
|
111
|
+
}) {
|
|
112
|
+
get isReady() {
|
|
113
|
+
for (let i = 0; i < this.status.conditions.length; i++) {
|
|
114
|
+
const condition = this.status.conditions[i];
|
|
115
|
+
if (condition.type === "Ready") {
|
|
116
|
+
return condition.status === "True";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
get isReadyOrInitializing() {
|
|
122
|
+
let initializedAt;
|
|
123
|
+
let readyAt;
|
|
124
|
+
for (let i = 0; i < this.status.conditions.length; i++) {
|
|
125
|
+
const condition = this.status.conditions[i];
|
|
126
|
+
switch (condition.type) {
|
|
127
|
+
case "Initialized":
|
|
128
|
+
{
|
|
129
|
+
if (condition.status !== "True") {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
initializedAt = condition.lastTransitionTime;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
case "Ready":
|
|
136
|
+
{
|
|
137
|
+
if (condition.status === "True") {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
readyAt = condition.lastTransitionTime;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// if the pod is still booting up, consider it ready as it would have
|
|
146
|
+
// already registered itself with RunnerStorage by now
|
|
147
|
+
return initializedAt === readyAt;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const PodList = /*#__PURE__*/Schema.Struct({
|
|
151
|
+
items: /*#__PURE__*/Schema.Array(Pod)
|
|
152
|
+
});
|
|
153
|
+
//# sourceMappingURL=K8sHttpClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"K8sHttpClient.js","names":["FileSystem","HttpClient","HttpClientRequest","HttpClientResponse","Context","Effect","identity","Layer","Option","Schedule","Schema","K8sHttpClient","Tag","layer","effect","gen","fs","token","readFileString","pipe","option","mapRequest","prependUrl","_tag","bearerToken","value","trim","filterStatusOk","retryTransient","schedule","spaced","makeGetPods","fnUntraced","options","client","getPods","get","namespace","setUrlParam","labelSelector","execute","flatMap","schemaBodyJson","PodList","map","list","pods","Map","i","items","length","pod","set","status","podIP","tapErrorCause","cause","logWarning","cachedWithTTL","makeCreatePod","spec","apiVersion","kind","metadata","name","readPodRaw","readPod","Pod","asSome","retry","while","e","catchIf","err","response","succeedNone","orDie","isPodFound","as","succeed","createPod","post","bodyUnsafeJson","logInfo","deletePod","del","res","json","void","asVoid","addFinalizer","repeat","until","found","opod","none","isNone","isReady","sleep","withSpan","PodStatus","Class","phase","String","conditions","Array","Struct","type","lastTransitionTime","hostIP","condition","isReadyOrInitializing","initializedAt","readyAt"],"sources":["../../src/K8sHttpClient.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
package/dist/esm/RunnerHealth.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
|
-
import * as FileSystem from "@effect/platform/FileSystem";
|
|
5
|
-
import * as HttpClient from "@effect/platform/HttpClient";
|
|
6
|
-
import * as HttpClientRequest from "@effect/platform/HttpClientRequest";
|
|
7
|
-
import * as HttpClientResponse from "@effect/platform/HttpClientResponse";
|
|
8
4
|
import * as Context from "effect/Context";
|
|
9
5
|
import * as Effect from "effect/Effect";
|
|
10
|
-
import { identity } from "effect/Function";
|
|
11
6
|
import * as Layer from "effect/Layer";
|
|
12
7
|
import * as Schedule from "effect/Schedule";
|
|
13
|
-
import * as
|
|
8
|
+
import * as K8s from "./K8sHttpClient.js";
|
|
14
9
|
import * as Runners from "./Runners.js";
|
|
15
10
|
/**
|
|
16
11
|
* Represents the service used to check if a Runner is healthy.
|
|
@@ -63,66 +58,11 @@ export const layerPing = /*#__PURE__*/Layer.scoped(RunnerHealth, makePing);
|
|
|
63
58
|
* @category Constructors
|
|
64
59
|
*/
|
|
65
60
|
export const makeK8s = /*#__PURE__*/Effect.fnUntraced(function* (options) {
|
|
66
|
-
const
|
|
67
|
-
const token = yield* fs.readFileString("/var/run/secrets/kubernetes.io/serviceaccount/token").pipe(Effect.option);
|
|
68
|
-
const client = (yield* HttpClient.HttpClient).pipe(HttpClient.filterStatusOk);
|
|
69
|
-
const baseRequest = HttpClientRequest.get("https://kubernetes.default.svc/api").pipe(token._tag === "Some" ? HttpClientRequest.bearerToken(token.value.trim()) : identity);
|
|
70
|
-
const getPods = baseRequest.pipe(HttpClientRequest.appendUrl(options?.namespace ? `/v1/namespaces/${options.namespace}/pods` : "/v1/pods"), HttpClientRequest.setUrlParam("fieldSelector", "status.phase=Running"), options?.labelSelector ? HttpClientRequest.setUrlParam("labelSelector", options.labelSelector) : identity);
|
|
71
|
-
const allPods = yield* client.execute(getPods).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(PodList)), Effect.map(list => {
|
|
72
|
-
const pods = new Map();
|
|
73
|
-
for (let i = 0; i < list.items.length; i++) {
|
|
74
|
-
const pod = list.items[i];
|
|
75
|
-
pods.set(pod.status.podIP, pod);
|
|
76
|
-
}
|
|
77
|
-
return pods;
|
|
78
|
-
}), Effect.tapErrorCause(cause => Effect.logWarning("Failed to fetch pods from Kubernetes API", cause)), Effect.cachedWithTTL("10 seconds"));
|
|
61
|
+
const allPods = yield* K8s.makeGetPods(options);
|
|
79
62
|
return RunnerHealth.of({
|
|
80
|
-
isAlive: address => allPods.pipe(Effect.map(pods => pods.get(address.host)?.
|
|
63
|
+
isAlive: address => allPods.pipe(Effect.map(pods => pods.get(address.host)?.isReadyOrInitializing ?? false), Effect.catchAllCause(() => Effect.succeed(true)))
|
|
81
64
|
});
|
|
82
65
|
});
|
|
83
|
-
class Pod extends /*#__PURE__*/Schema.Class("effect/cluster/RunnerHealth/Pod")({
|
|
84
|
-
status: /*#__PURE__*/Schema.Struct({
|
|
85
|
-
phase: Schema.String,
|
|
86
|
-
conditions: /*#__PURE__*/Schema.Array(/*#__PURE__*/Schema.Struct({
|
|
87
|
-
type: Schema.String,
|
|
88
|
-
status: Schema.String,
|
|
89
|
-
lastTransitionTime: Schema.String
|
|
90
|
-
})),
|
|
91
|
-
podIP: Schema.String
|
|
92
|
-
})
|
|
93
|
-
}) {
|
|
94
|
-
get isReady() {
|
|
95
|
-
let initializedAt;
|
|
96
|
-
let readyAt;
|
|
97
|
-
for (let i = 0; i < this.status.conditions.length; i++) {
|
|
98
|
-
const condition = this.status.conditions[i];
|
|
99
|
-
switch (condition.type) {
|
|
100
|
-
case "Initialized":
|
|
101
|
-
{
|
|
102
|
-
if (condition.status !== "True") {
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
initializedAt = condition.lastTransitionTime;
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
case "Ready":
|
|
109
|
-
{
|
|
110
|
-
if (condition.status === "True") {
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
readyAt = condition.lastTransitionTime;
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// if the pod is still booting up, consider it ready as it would have
|
|
119
|
-
// already registered itself with RunnerStorage by now
|
|
120
|
-
return initializedAt === readyAt;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
const PodList = /*#__PURE__*/Schema.Struct({
|
|
124
|
-
items: /*#__PURE__*/Schema.Array(Pod)
|
|
125
|
-
});
|
|
126
66
|
/**
|
|
127
67
|
* A layer which will check the Kubernetes API to see if a Runner is healthy.
|
|
128
68
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RunnerHealth.js","names":["
|
|
1
|
+
{"version":3,"file":"RunnerHealth.js","names":["Context","Effect","Layer","Schedule","K8s","Runners","RunnerHealth","Tag","layerNoop","succeed","isAlive","makePing","gen","runners","schedule","spaced","address","ping","pipe","timeout","retry","times","isSuccess","of","layerPing","scoped","makeK8s","fnUntraced","options","allPods","makeGetPods","map","pods","get","host","isReadyOrInitializing","catchAllCause","layerK8s","effect"],"sources":["../../src/RunnerHealth.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAGA,OAAO,KAAKA,OAAO,MAAM,gBAAgB;AACzC,OAAO,KAAKC,MAAM,MAAM,eAAe;AACvC,OAAO,KAAKC,KAAK,MAAM,cAAc;AACrC,OAAO,KAAKC,QAAQ,MAAM,iBAAiB;AAE3C,OAAO,KAAKC,GAAG,MAAM,oBAAoB;AAEzC,OAAO,KAAKC,OAAO,MAAM,cAAc;AAEvC;;;;;;;;;;AAUA,OAAM,MAAOC,YAAa,sBAAQN,OAAO,CAACO,GAAG,CAAC,8BAA8B,CAAC,EAK1E;AAEH;;;;;;;;AAQA,OAAO,MAAMC,SAAS,gBAAGN,KAAK,CAACO,OAAO,CAACH,YAAY,EAAE;EACnDI,OAAO,EAAEA,CAAA,KAAMT,MAAM,CAACQ,OAAO,CAAC,IAAI;CACnC,CAAC;AAEF;;;;AAIA,OAAO,MAAME,QAAQ,gBAIjBV,MAAM,CAACW,GAAG,CAAC,aAAS;EACtB,MAAMC,OAAO,GAAG,OAAOR,OAAO,CAACA,OAAO;EACtC,MAAMS,QAAQ,GAAGX,QAAQ,CAACY,MAAM,CAAC,GAAG,CAAC;EAErC,SAASL,OAAOA,CAACM,OAAsB;IACrC,OAAOH,OAAO,CAACI,IAAI,CAACD,OAAO,CAAC,CAACE,IAAI,CAC/BjB,MAAM,CAACkB,OAAO,CAAC,MAAM,CAAC,EACtBlB,MAAM,CAACmB,KAAK,CAAC;MAAEC,KAAK,EAAE,CAAC;MAAEP;IAAQ,CAAE,CAAC,EACpCb,MAAM,CAACqB,SAAS,CACjB;EACH;EAEA,OAAOhB,YAAY,CAACiB,EAAE,CAAC;IAAEb;EAAO,CAAE,CAAC;AACrC,CAAC,CAAC;AAEF;;;;;;AAMA,OAAO,MAAMc,SAAS,gBAIlBtB,KAAK,CAACuB,MAAM,CAACnB,YAAY,EAAEK,QAAQ,CAAC;AAExC;;;;AAIA,OAAO,MAAMe,OAAO,gBAAGzB,MAAM,CAAC0B,UAAU,CAAC,WAAUC,OAGlD;EACC,MAAMC,OAAO,GAAG,OAAOzB,GAAG,CAAC0B,WAAW,CAACF,OAAO,CAAC;EAE/C,OAAOtB,YAAY,CAACiB,EAAE,CAAC;IACrBb,OAAO,EAAGM,OAAO,IACfa,OAAO,CAACX,IAAI,CACVjB,MAAM,CAAC8B,GAAG,CAAEC,IAAI,IAAKA,IAAI,CAACC,GAAG,CAACjB,OAAO,CAACkB,IAAI,CAAC,EAAEC,qBAAqB,IAAI,KAAK,CAAC,EAC5ElC,MAAM,CAACmC,aAAa,CAAC,MAAMnC,MAAM,CAACQ,OAAO,CAAC,IAAI,CAAC,CAAC;GAErD,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;;;;;AAYA,OAAO,MAAM4B,QAAQ,GACnBT,OAGa,IAKV1B,KAAK,CAACoC,MAAM,CAAChC,YAAY,EAAEoB,OAAO,CAACE,OAAO,CAAC,CAAC","ignoreList":[]}
|
package/dist/esm/index.js
CHANGED
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["ClusterCron","ClusterError","ClusterMetrics","ClusterSchema","ClusterWorkflowEngine","DeliverAt","Entity","EntityAddress","EntityId","EntityProxy","EntityProxyServer","EntityResource","EntityType","Envelope","HttpRunner","MachineId","Message","MessageStorage","Reply","Runner","RunnerAddress","RunnerHealth","RunnerServer","RunnerStorage","Runners","ShardId","Sharding","ShardingConfig","ShardingRegistrationEvent","Singleton","SingletonAddress","Snowflake","SocketRunner","SqlMessageStorage","SqlRunnerStorage"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAGA,OAAO,KAAKA,WAAW,MAAM,kBAAkB;AAE/C;;;AAGA,OAAO,KAAKC,YAAY,MAAM,mBAAmB;AAEjD;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,qBAAqB,MAAM,4BAA4B;AAEnE;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,MAAM,MAAM,aAAa;AAErC;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,QAAQ,MAAM,eAAe;AAEzC;;;AAGA,OAAO,KAAKC,WAAW,MAAM,kBAAkB;AAE/C;;;AAGA,OAAO,KAAKC,iBAAiB,MAAM,wBAAwB;AAE3D;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,UAAU,MAAM,iBAAiB;AAE7C;;;AAGA,OAAO,KAAKC,QAAQ,MAAM,eAAe;AAEzC;;;AAGA,OAAO,KAAKC,UAAU,MAAM,iBAAiB;AAE7C;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,OAAO,MAAM,cAAc;AAEvC;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,KAAK,MAAM,YAAY;AAEnC;;;AAGA,OAAO,KAAKC,MAAM,MAAM,aAAa;AAErC;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,YAAY,MAAM,mBAAmB;AAEjD;;;AAGA,OAAO,KAAKC,YAAY,MAAM,mBAAmB;AAEjD;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,OAAO,MAAM,cAAc;AAEvC;;;AAGA,OAAO,KAAKC,OAAO,MAAM,cAAc;AAEvC;;;AAGA,OAAO,KAAKC,QAAQ,MAAM,eAAe;AAEzC;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,yBAAyB,MAAM,gCAAgC;AAE3E;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,gBAAgB,MAAM,uBAAuB;AAEzD;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,YAAY,MAAM,mBAAmB;AAEjD;;;AAGA,OAAO,KAAKC,iBAAiB,MAAM,wBAAwB;AAE3D;;;AAGA,OAAO,KAAKC,gBAAgB,MAAM,uBAAuB","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"index.js","names":["ClusterCron","ClusterError","ClusterMetrics","ClusterSchema","ClusterWorkflowEngine","DeliverAt","Entity","EntityAddress","EntityId","EntityProxy","EntityProxyServer","EntityResource","EntityType","Envelope","HttpRunner","K8sHttpClient","MachineId","Message","MessageStorage","Reply","Runner","RunnerAddress","RunnerHealth","RunnerServer","RunnerStorage","Runners","ShardId","Sharding","ShardingConfig","ShardingRegistrationEvent","Singleton","SingletonAddress","Snowflake","SocketRunner","SqlMessageStorage","SqlRunnerStorage"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAGA,OAAO,KAAKA,WAAW,MAAM,kBAAkB;AAE/C;;;AAGA,OAAO,KAAKC,YAAY,MAAM,mBAAmB;AAEjD;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,qBAAqB,MAAM,4BAA4B;AAEnE;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,MAAM,MAAM,aAAa;AAErC;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,QAAQ,MAAM,eAAe;AAEzC;;;AAGA,OAAO,KAAKC,WAAW,MAAM,kBAAkB;AAE/C;;;AAGA,OAAO,KAAKC,iBAAiB,MAAM,wBAAwB;AAE3D;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,UAAU,MAAM,iBAAiB;AAE7C;;;AAGA,OAAO,KAAKC,QAAQ,MAAM,eAAe;AAEzC;;;AAGA,OAAO,KAAKC,UAAU,MAAM,iBAAiB;AAE7C;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,OAAO,MAAM,cAAc;AAEvC;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,KAAK,MAAM,YAAY;AAEnC;;;AAGA,OAAO,KAAKC,MAAM,MAAM,aAAa;AAErC;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,YAAY,MAAM,mBAAmB;AAEjD;;;AAGA,OAAO,KAAKC,YAAY,MAAM,mBAAmB;AAEjD;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,OAAO,MAAM,cAAc;AAEvC;;;AAGA,OAAO,KAAKC,OAAO,MAAM,cAAc;AAEvC;;;AAGA,OAAO,KAAKC,QAAQ,MAAM,eAAe;AAEzC;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,yBAAyB,MAAM,gCAAgC;AAE3E;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,gBAAgB,MAAM,uBAAuB;AAEzD;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,YAAY,MAAM,mBAAmB;AAEjD;;;AAGA,OAAO,KAAKC,iBAAiB,MAAM,wBAAwB;AAE3D;;;AAGA,OAAO,KAAKC,gBAAgB,MAAM,uBAAuB","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect/cluster",
|
|
3
|
-
"version": "0.53.
|
|
3
|
+
"version": "0.53.4",
|
|
4
4
|
"description": "Unified interfaces for common cluster-specific services",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -10,12 +10,15 @@
|
|
|
10
10
|
},
|
|
11
11
|
"sideEffects": [],
|
|
12
12
|
"homepage": "https://effect.website",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"kubernetes-types": "^1.30.0"
|
|
15
|
+
},
|
|
13
16
|
"peerDependencies": {
|
|
14
17
|
"@effect/platform": "^0.93.3",
|
|
15
18
|
"@effect/sql": "^0.48.0",
|
|
16
|
-
"@effect/rpc": "^0.72.2",
|
|
17
19
|
"@effect/workflow": "^0.13.0",
|
|
18
|
-
"effect": "^3.19.6"
|
|
20
|
+
"effect": "^3.19.6",
|
|
21
|
+
"@effect/rpc": "^0.72.2"
|
|
19
22
|
},
|
|
20
23
|
"publishConfig": {
|
|
21
24
|
"provenance": true
|
|
@@ -105,6 +108,11 @@
|
|
|
105
108
|
"import": "./dist/esm/HttpRunner.js",
|
|
106
109
|
"default": "./dist/cjs/HttpRunner.js"
|
|
107
110
|
},
|
|
111
|
+
"./K8sHttpClient": {
|
|
112
|
+
"types": "./dist/dts/K8sHttpClient.d.ts",
|
|
113
|
+
"import": "./dist/esm/K8sHttpClient.js",
|
|
114
|
+
"default": "./dist/cjs/K8sHttpClient.js"
|
|
115
|
+
},
|
|
108
116
|
"./MachineId": {
|
|
109
117
|
"types": "./dist/dts/MachineId.d.ts",
|
|
110
118
|
"import": "./dist/esm/MachineId.js",
|
|
@@ -258,6 +266,9 @@
|
|
|
258
266
|
"HttpRunner": [
|
|
259
267
|
"./dist/dts/HttpRunner.d.ts"
|
|
260
268
|
],
|
|
269
|
+
"K8sHttpClient": [
|
|
270
|
+
"./dist/dts/K8sHttpClient.d.ts"
|
|
271
|
+
],
|
|
261
272
|
"MachineId": [
|
|
262
273
|
"./dist/dts/MachineId.d.ts"
|
|
263
274
|
],
|
package/src/EntityResource.ts
CHANGED
|
@@ -7,7 +7,9 @@ import * as Effect from "effect/Effect"
|
|
|
7
7
|
import { identity } from "effect/Function"
|
|
8
8
|
import * as RcRef from "effect/RcRef"
|
|
9
9
|
import * as Scope from "effect/Scope"
|
|
10
|
+
import type * as v1 from "kubernetes-types/core/v1.d.ts"
|
|
10
11
|
import * as Entity from "./Entity.js"
|
|
12
|
+
import * as K8sHttpClient from "./K8sHttpClient.js"
|
|
11
13
|
import type { Sharding } from "./Sharding.js"
|
|
12
14
|
|
|
13
15
|
/**
|
|
@@ -106,3 +108,31 @@ export const make: <A, E, R>(options: {
|
|
|
106
108
|
close: RcRef.invalidate(ref)
|
|
107
109
|
})
|
|
108
110
|
})
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @since 1.0.0
|
|
114
|
+
* @category Kubernetes
|
|
115
|
+
*/
|
|
116
|
+
export const makeK8sPod: (
|
|
117
|
+
spec: v1.Pod,
|
|
118
|
+
options?: {
|
|
119
|
+
readonly idleTimeToLive?: Duration.DurationInput | undefined
|
|
120
|
+
} | undefined
|
|
121
|
+
) => Effect.Effect<
|
|
122
|
+
EntityResource<K8sHttpClient.PodStatus>,
|
|
123
|
+
never,
|
|
124
|
+
Scope.Scope | Sharding | Entity.CurrentAddress | K8sHttpClient.K8sHttpClient
|
|
125
|
+
> = Effect.fnUntraced(function*(spec: v1.Pod, options?: {
|
|
126
|
+
readonly idleTimeToLive?: Duration.DurationInput | undefined
|
|
127
|
+
}) {
|
|
128
|
+
const createPod = yield* K8sHttpClient.makeCreatePod
|
|
129
|
+
return yield* make({
|
|
130
|
+
...options,
|
|
131
|
+
acquire: Effect.gen(function*() {
|
|
132
|
+
const scope = yield* CloseScope
|
|
133
|
+
return yield* createPod(spec).pipe(
|
|
134
|
+
Scope.extend(scope)
|
|
135
|
+
)
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
})
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as FileSystem from "@effect/platform/FileSystem"
|
|
5
|
+
import * as HttpClient from "@effect/platform/HttpClient"
|
|
6
|
+
import type * as HttpClientError from "@effect/platform/HttpClientError"
|
|
7
|
+
import * as HttpClientRequest from "@effect/platform/HttpClientRequest"
|
|
8
|
+
import * as HttpClientResponse from "@effect/platform/HttpClientResponse"
|
|
9
|
+
import * as Context from "effect/Context"
|
|
10
|
+
import * as Effect from "effect/Effect"
|
|
11
|
+
import { identity } from "effect/Function"
|
|
12
|
+
import * as Layer from "effect/Layer"
|
|
13
|
+
import * as Option from "effect/Option"
|
|
14
|
+
import type * as ParseResult from "effect/ParseResult"
|
|
15
|
+
import * as Schedule from "effect/Schedule"
|
|
16
|
+
import * as Schema from "effect/Schema"
|
|
17
|
+
import type * as v1 from "kubernetes-types/core/v1.d.ts"
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @since 1.0.0
|
|
21
|
+
* @category Tags
|
|
22
|
+
*/
|
|
23
|
+
export class K8sHttpClient extends Context.Tag("@effect/cluster/K8sHttpClient")<
|
|
24
|
+
K8sHttpClient,
|
|
25
|
+
HttpClient.HttpClient
|
|
26
|
+
>() {}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @since 1.0.0
|
|
30
|
+
* @category Layers
|
|
31
|
+
*/
|
|
32
|
+
export const layer: Layer.Layer<
|
|
33
|
+
K8sHttpClient,
|
|
34
|
+
never,
|
|
35
|
+
HttpClient.HttpClient | FileSystem.FileSystem
|
|
36
|
+
> = Layer.effect(
|
|
37
|
+
K8sHttpClient,
|
|
38
|
+
Effect.gen(function*() {
|
|
39
|
+
const fs = yield* FileSystem.FileSystem
|
|
40
|
+
const token = yield* fs.readFileString("/var/run/secrets/kubernetes.io/serviceaccount/token").pipe(
|
|
41
|
+
Effect.option
|
|
42
|
+
)
|
|
43
|
+
return (yield* HttpClient.HttpClient).pipe(
|
|
44
|
+
HttpClient.mapRequest(HttpClientRequest.prependUrl("https://kubernetes.default.svc/api")),
|
|
45
|
+
token._tag === "Some" ? HttpClient.mapRequest(HttpClientRequest.bearerToken(token.value.trim())) : identity,
|
|
46
|
+
HttpClient.filterStatusOk,
|
|
47
|
+
HttpClient.retryTransient({
|
|
48
|
+
schedule: Schedule.spaced(5000)
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
})
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @since 1.0.0
|
|
56
|
+
* @category Constructors
|
|
57
|
+
*/
|
|
58
|
+
export const makeGetPods: (
|
|
59
|
+
options?: {
|
|
60
|
+
readonly namespace?: string | undefined
|
|
61
|
+
readonly labelSelector?: string | undefined
|
|
62
|
+
} | undefined
|
|
63
|
+
) => Effect.Effect<
|
|
64
|
+
Effect.Effect<Map<string, Pod>, HttpClientError.HttpClientError | ParseResult.ParseError, never>,
|
|
65
|
+
never,
|
|
66
|
+
K8sHttpClient
|
|
67
|
+
> = Effect.fnUntraced(function*(options?: {
|
|
68
|
+
readonly namespace?: string | undefined
|
|
69
|
+
readonly labelSelector?: string | undefined
|
|
70
|
+
}) {
|
|
71
|
+
const client = yield* K8sHttpClient
|
|
72
|
+
|
|
73
|
+
const getPods = HttpClientRequest.get(
|
|
74
|
+
options?.namespace ? `/v1/namespaces/${options.namespace}/pods` : "/v1/pods"
|
|
75
|
+
).pipe(
|
|
76
|
+
HttpClientRequest.setUrlParam("fieldSelector", "status.phase=Running"),
|
|
77
|
+
options?.labelSelector ? HttpClientRequest.setUrlParam("labelSelector", options.labelSelector) : identity
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return yield* client.execute(getPods).pipe(
|
|
81
|
+
Effect.flatMap(HttpClientResponse.schemaBodyJson(PodList)),
|
|
82
|
+
Effect.map((list) => {
|
|
83
|
+
const pods = new Map<string, Pod>()
|
|
84
|
+
for (let i = 0; i < list.items.length; i++) {
|
|
85
|
+
const pod = list.items[i]
|
|
86
|
+
pods.set(pod.status.podIP, pod)
|
|
87
|
+
}
|
|
88
|
+
return pods
|
|
89
|
+
}),
|
|
90
|
+
Effect.tapErrorCause((cause) => Effect.logWarning("Failed to fetch pods from Kubernetes API", cause)),
|
|
91
|
+
Effect.cachedWithTTL("10 seconds")
|
|
92
|
+
)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @since 1.0.0
|
|
97
|
+
* @category Constructors
|
|
98
|
+
*/
|
|
99
|
+
export const makeCreatePod = Effect.gen(function*() {
|
|
100
|
+
const client = yield* K8sHttpClient
|
|
101
|
+
|
|
102
|
+
return Effect.fnUntraced(function*(spec: v1.Pod) {
|
|
103
|
+
spec = {
|
|
104
|
+
apiVersion: "v1",
|
|
105
|
+
kind: "Pod",
|
|
106
|
+
metadata: {
|
|
107
|
+
namespace: "default",
|
|
108
|
+
...spec.metadata
|
|
109
|
+
},
|
|
110
|
+
...spec
|
|
111
|
+
}
|
|
112
|
+
const namespace = spec.metadata?.namespace ?? "default"
|
|
113
|
+
const name = spec.metadata!.name!
|
|
114
|
+
const readPodRaw = HttpClientRequest.get(`/v1/namespaces/${namespace}/pods/${name}`).pipe(
|
|
115
|
+
client.execute
|
|
116
|
+
)
|
|
117
|
+
const readPod = readPodRaw.pipe(
|
|
118
|
+
Effect.flatMap(HttpClientResponse.schemaBodyJson(Pod)),
|
|
119
|
+
Effect.asSome,
|
|
120
|
+
Effect.retry({
|
|
121
|
+
while: (e) => e._tag === "ParseError",
|
|
122
|
+
schedule: Schedule.spaced("1 seconds")
|
|
123
|
+
}),
|
|
124
|
+
Effect.catchIf((err) => err._tag === "ResponseError" && err.response.status === 404, () => Effect.succeedNone),
|
|
125
|
+
Effect.orDie
|
|
126
|
+
)
|
|
127
|
+
const isPodFound = readPodRaw.pipe(
|
|
128
|
+
Effect.as(true),
|
|
129
|
+
Effect.catchIf(
|
|
130
|
+
(err) => err._tag === "ResponseError" && err.response.status === 404,
|
|
131
|
+
() => Effect.succeed(false)
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
const createPod = HttpClientRequest.post(`/v1/namespaces/${namespace}/pods`).pipe(
|
|
135
|
+
HttpClientRequest.bodyUnsafeJson(spec),
|
|
136
|
+
client.execute,
|
|
137
|
+
Effect.catchIf(
|
|
138
|
+
(err) => err._tag === "ResponseError" && err.response.status === 409,
|
|
139
|
+
() => readPod
|
|
140
|
+
),
|
|
141
|
+
Effect.tapErrorCause(Effect.logInfo),
|
|
142
|
+
Effect.orDie
|
|
143
|
+
)
|
|
144
|
+
const deletePod = HttpClientRequest.del(`/v1/namespaces/${namespace}/pods/${name}`).pipe(
|
|
145
|
+
client.execute,
|
|
146
|
+
Effect.flatMap((res) => res.json),
|
|
147
|
+
Effect.catchIf(
|
|
148
|
+
(err) => err._tag === "ResponseError" && err.response.status === 404,
|
|
149
|
+
() => Effect.void
|
|
150
|
+
),
|
|
151
|
+
Effect.tapErrorCause(Effect.logInfo),
|
|
152
|
+
Effect.orDie,
|
|
153
|
+
Effect.asVoid
|
|
154
|
+
)
|
|
155
|
+
yield* Effect.addFinalizer(Effect.fnUntraced(function*() {
|
|
156
|
+
yield* deletePod
|
|
157
|
+
yield* isPodFound.pipe(
|
|
158
|
+
Effect.repeat({
|
|
159
|
+
until: (found) => !found,
|
|
160
|
+
schedule: Schedule.spaced("3 seconds")
|
|
161
|
+
}),
|
|
162
|
+
Effect.orDie
|
|
163
|
+
)
|
|
164
|
+
}))
|
|
165
|
+
|
|
166
|
+
let opod = Option.none<Pod>()
|
|
167
|
+
while (Option.isNone(opod) || !opod.value.isReady) {
|
|
168
|
+
if (Option.isNone(opod)) {
|
|
169
|
+
yield* createPod
|
|
170
|
+
}
|
|
171
|
+
yield* Effect.sleep("3 seconds")
|
|
172
|
+
opod = yield* readPod
|
|
173
|
+
}
|
|
174
|
+
return opod.value.status
|
|
175
|
+
}, Effect.withSpan("K8sHttpClient.createPod"))
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @since 1.0.0
|
|
180
|
+
* @category Schemas
|
|
181
|
+
*/
|
|
182
|
+
export class PodStatus extends Schema.Class<PodStatus>("@effect/cluster/K8sHttpClient/PodStatus")({
|
|
183
|
+
phase: Schema.String,
|
|
184
|
+
conditions: Schema.Array(Schema.Struct({
|
|
185
|
+
type: Schema.String,
|
|
186
|
+
status: Schema.String,
|
|
187
|
+
lastTransitionTime: Schema.String
|
|
188
|
+
})),
|
|
189
|
+
podIP: Schema.String,
|
|
190
|
+
hostIP: Schema.String
|
|
191
|
+
}) {}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* @since 1.0.0
|
|
195
|
+
* @category Schemas
|
|
196
|
+
*/
|
|
197
|
+
export class Pod extends Schema.Class<Pod>("@effect/cluster/K8sHttpClient/Pod")({
|
|
198
|
+
status: PodStatus
|
|
199
|
+
}) {
|
|
200
|
+
get isReady(): boolean {
|
|
201
|
+
for (let i = 0; i < this.status.conditions.length; i++) {
|
|
202
|
+
const condition = this.status.conditions[i]
|
|
203
|
+
if (condition.type === "Ready") {
|
|
204
|
+
return condition.status === "True"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return false
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
get isReadyOrInitializing(): boolean {
|
|
211
|
+
let initializedAt: string | undefined
|
|
212
|
+
let readyAt: string | undefined
|
|
213
|
+
for (let i = 0; i < this.status.conditions.length; i++) {
|
|
214
|
+
const condition = this.status.conditions[i]
|
|
215
|
+
switch (condition.type) {
|
|
216
|
+
case "Initialized": {
|
|
217
|
+
if (condition.status !== "True") {
|
|
218
|
+
return true
|
|
219
|
+
}
|
|
220
|
+
initializedAt = condition.lastTransitionTime
|
|
221
|
+
break
|
|
222
|
+
}
|
|
223
|
+
case "Ready": {
|
|
224
|
+
if (condition.status === "True") {
|
|
225
|
+
return true
|
|
226
|
+
}
|
|
227
|
+
readyAt = condition.lastTransitionTime
|
|
228
|
+
break
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// if the pod is still booting up, consider it ready as it would have
|
|
233
|
+
// already registered itself with RunnerStorage by now
|
|
234
|
+
return initializedAt === readyAt
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const PodList = Schema.Struct({
|
|
239
|
+
items: Schema.Array(Pod)
|
|
240
|
+
})
|