@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.
@@ -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":[]}
@@ -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 Schema from "effect/Schema";
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 fs = yield* FileSystem.FileSystem;
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)?.isReady ?? false), Effect.catchAllCause(() => Effect.succeed(true)))
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":["FileSystem","HttpClient","HttpClientRequest","HttpClientResponse","Context","Effect","identity","Layer","Schedule","Schema","Runners","RunnerHealth","Tag","layerNoop","succeed","isAlive","makePing","gen","runners","schedule","spaced","address","ping","pipe","timeout","retry","times","isSuccess","of","layerPing","scoped","makeK8s","fnUntraced","options","fs","token","readFileString","option","client","filterStatusOk","baseRequest","get","_tag","bearerToken","value","trim","getPods","appendUrl","namespace","setUrlParam","labelSelector","allPods","execute","flatMap","schemaBodyJson","PodList","map","list","pods","Map","i","items","length","pod","set","status","podIP","tapErrorCause","cause","logWarning","cachedWithTTL","host","isReady","catchAllCause","Pod","Class","Struct","phase","String","conditions","Array","type","lastTransitionTime","initializedAt","readyAt","condition","layerK8s","effect"],"sources":["../../src/RunnerHealth.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
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
@@ -58,6 +58,10 @@ export * as Envelope from "./Envelope.js";
58
58
  * @since 1.0.0
59
59
  */
60
60
  export * as HttpRunner from "./HttpRunner.js";
61
+ /**
62
+ * @since 1.0.0
63
+ */
64
+ export * as K8sHttpClient from "./K8sHttpClient.js";
61
65
  /**
62
66
  * @since 1.0.0
63
67
  */
@@ -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",
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
  ],
@@ -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
+ })