@hatchet-dev/typescript-sdk 1.9.7 → 1.10.0
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/clients/hatchet-client/client-config.d.ts +18 -0
- package/clients/hatchet-client/client-config.js +5 -0
- package/package.json +5 -3
- package/util/config-loader/config-loader.js +13 -8
- package/v1/client/worker/health-server.d.ts +30 -0
- package/v1/client/worker/health-server.js +161 -0
- package/v1/client/worker/worker-internal.d.ts +11 -0
- package/v1/client/worker/worker-internal.js +66 -4
- package/version.d.ts +1 -1
- package/version.js +1 -1
|
@@ -41,6 +41,16 @@ export declare const ClientConfigSchema: z.ZodObject<{
|
|
|
41
41
|
key_file?: string | undefined;
|
|
42
42
|
server_name?: string | undefined;
|
|
43
43
|
}>;
|
|
44
|
+
healthcheck: z.ZodOptional<z.ZodObject<{
|
|
45
|
+
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
46
|
+
port: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
47
|
+
}, "strip", z.ZodTypeAny, {
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
port: number;
|
|
50
|
+
}, {
|
|
51
|
+
enabled?: boolean | undefined;
|
|
52
|
+
port?: number | undefined;
|
|
53
|
+
}>>;
|
|
44
54
|
host_port: z.ZodString;
|
|
45
55
|
api_url: z.ZodString;
|
|
46
56
|
log_level: z.ZodOptional<z.ZodEnum<["OFF", "DEBUG", "INFO", "WARN", "ERROR"]>>;
|
|
@@ -58,6 +68,10 @@ export declare const ClientConfigSchema: z.ZodObject<{
|
|
|
58
68
|
host_port: string;
|
|
59
69
|
api_url: string;
|
|
60
70
|
tenant_id: string;
|
|
71
|
+
healthcheck?: {
|
|
72
|
+
enabled: boolean;
|
|
73
|
+
port: number;
|
|
74
|
+
} | undefined;
|
|
61
75
|
log_level?: "OFF" | "DEBUG" | "INFO" | "WARN" | "ERROR" | undefined;
|
|
62
76
|
namespace?: string | undefined;
|
|
63
77
|
}, {
|
|
@@ -72,6 +86,10 @@ export declare const ClientConfigSchema: z.ZodObject<{
|
|
|
72
86
|
host_port: string;
|
|
73
87
|
api_url: string;
|
|
74
88
|
tenant_id: string;
|
|
89
|
+
healthcheck?: {
|
|
90
|
+
enabled?: boolean | undefined;
|
|
91
|
+
port?: number | undefined;
|
|
92
|
+
} | undefined;
|
|
75
93
|
log_level?: "OFF" | "DEBUG" | "INFO" | "WARN" | "ERROR" | undefined;
|
|
76
94
|
namespace?: string | undefined;
|
|
77
95
|
}>;
|
|
@@ -9,9 +9,14 @@ const ClientTLSConfigSchema = zod_1.z.object({
|
|
|
9
9
|
key_file: zod_1.z.string().optional(),
|
|
10
10
|
server_name: zod_1.z.string().optional(),
|
|
11
11
|
});
|
|
12
|
+
const HealthcheckConfigSchema = zod_1.z.object({
|
|
13
|
+
enabled: zod_1.z.boolean().optional().default(false),
|
|
14
|
+
port: zod_1.z.number().optional().default(8001),
|
|
15
|
+
});
|
|
12
16
|
exports.ClientConfigSchema = zod_1.z.object({
|
|
13
17
|
token: zod_1.z.string(),
|
|
14
18
|
tls_config: ClientTLSConfigSchema,
|
|
19
|
+
healthcheck: HealthcheckConfigSchema.optional(),
|
|
15
20
|
host_port: zod_1.z.string(),
|
|
16
21
|
api_url: zod_1.z.string(),
|
|
17
22
|
log_level: zod_1.z.enum(['OFF', 'DEBUG', 'INFO', 'WARN', 'ERROR']).optional(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hatchet-dev/typescript-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Background task orchestration & visibility for developers",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"grpc-tools": "^1.13.0",
|
|
44
44
|
"jest": "^29.7.0",
|
|
45
45
|
"jest-tsd": "^0.2.2",
|
|
46
|
-
"pino": "^9.
|
|
46
|
+
"pino": "^9.12.0",
|
|
47
47
|
"prettier": "^3.5.3",
|
|
48
48
|
"resolve-tspaths": "^0.8.23",
|
|
49
49
|
"ts-jest": "^29.3.1",
|
|
@@ -67,7 +67,9 @@
|
|
|
67
67
|
"yaml": "^2.7.1",
|
|
68
68
|
"zod": "^3.24.2"
|
|
69
69
|
},
|
|
70
|
-
"
|
|
70
|
+
"optionalDependencies": {
|
|
71
|
+
"prom-client": "^15.1.3"
|
|
72
|
+
},
|
|
71
73
|
"scripts": {
|
|
72
74
|
"build": "echo 'build hatchet sdk with `npm run tsc:build` to ensure it is not build during the publish step' && exit 0",
|
|
73
75
|
"postinstall": "node scripts/version-check.js",
|
|
@@ -44,7 +44,7 @@ const token_1 = require("./token");
|
|
|
44
44
|
const DEFAULT_CONFIG_FILE = '.hatchet.yaml';
|
|
45
45
|
class ConfigLoader {
|
|
46
46
|
static loadClientConfig(override, config) {
|
|
47
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6;
|
|
47
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8;
|
|
48
48
|
const yaml = this.loadYamlConfig(config === null || config === void 0 ? void 0 : config.path);
|
|
49
49
|
const tlsConfig = (_a = override === null || override === void 0 ? void 0 : override.tls_config) !== null && _a !== void 0 ? _a : {
|
|
50
50
|
tls_strategy: (_d = (_c = (_b = yaml === null || yaml === void 0 ? void 0 : yaml.tls_config) === null || _b === void 0 ? void 0 : _b.tls_strategy) !== null && _c !== void 0 ? _c : this.env('HATCHET_CLIENT_TLS_STRATEGY')) !== null && _d !== void 0 ? _d : 'tls',
|
|
@@ -54,6 +54,10 @@ class ConfigLoader {
|
|
|
54
54
|
server_name: (_m = (_l = yaml === null || yaml === void 0 ? void 0 : yaml.tls_config) === null || _l === void 0 ? void 0 : _l.server_name) !== null && _m !== void 0 ? _m : this.env('HATCHET_CLIENT_TLS_SERVER_NAME'),
|
|
55
55
|
};
|
|
56
56
|
const token = (_p = (_o = override === null || override === void 0 ? void 0 : override.token) !== null && _o !== void 0 ? _o : yaml === null || yaml === void 0 ? void 0 : yaml.token) !== null && _p !== void 0 ? _p : this.env('HATCHET_CLIENT_TOKEN');
|
|
57
|
+
const healthCheckConfig = (_r = (_q = override === null || override === void 0 ? void 0 : override.healthcheck) !== null && _q !== void 0 ? _q : yaml === null || yaml === void 0 ? void 0 : yaml.healthcheck) !== null && _r !== void 0 ? _r : {
|
|
58
|
+
enabled: this.env('HATCHET_CLIENT_WORKER_HEALTHCHECK_ENABLED') === 'true',
|
|
59
|
+
port: parseInt(this.env('HATCHET_CLIENT_WORKER_HEALTHCHECK_PORT') || '8001', 10),
|
|
60
|
+
};
|
|
57
61
|
if (!token) {
|
|
58
62
|
throw new Error('No token provided. Provide it by setting the HATCHET_CLIENT_TOKEN environment variable.');
|
|
59
63
|
}
|
|
@@ -66,25 +70,26 @@ class ConfigLoader {
|
|
|
66
70
|
try {
|
|
67
71
|
const addresses = (0, token_1.getAddressesFromJWT)(token);
|
|
68
72
|
grpcBroadcastAddress =
|
|
69
|
-
(
|
|
73
|
+
(_u = (_t = (_s = override === null || override === void 0 ? void 0 : override.host_port) !== null && _s !== void 0 ? _s : yaml === null || yaml === void 0 ? void 0 : yaml.host_port) !== null && _t !== void 0 ? _t : this.env('HATCHET_CLIENT_HOST_PORT')) !== null && _u !== void 0 ? _u : addresses.grpcBroadcastAddress;
|
|
70
74
|
apiUrl =
|
|
71
|
-
(
|
|
75
|
+
(_x = (_w = (_v = override === null || override === void 0 ? void 0 : override.api_url) !== null && _v !== void 0 ? _v : yaml === null || yaml === void 0 ? void 0 : yaml.api_url) !== null && _w !== void 0 ? _w : this.env('HATCHET_CLIENT_API_URL')) !== null && _x !== void 0 ? _x : addresses.serverUrl;
|
|
72
76
|
}
|
|
73
77
|
catch (e) {
|
|
74
78
|
grpcBroadcastAddress =
|
|
75
|
-
(
|
|
76
|
-
apiUrl = (
|
|
79
|
+
(_z = (_y = override === null || override === void 0 ? void 0 : override.host_port) !== null && _y !== void 0 ? _y : yaml === null || yaml === void 0 ? void 0 : yaml.host_port) !== null && _z !== void 0 ? _z : this.env('HATCHET_CLIENT_HOST_PORT');
|
|
80
|
+
apiUrl = (_1 = (_0 = override === null || override === void 0 ? void 0 : override.api_url) !== null && _0 !== void 0 ? _0 : yaml === null || yaml === void 0 ? void 0 : yaml.api_url) !== null && _1 !== void 0 ? _1 : this.env('HATCHET_CLIENT_API_URL');
|
|
77
81
|
}
|
|
78
|
-
let namespace = (
|
|
82
|
+
let namespace = (_3 = (_2 = override === null || override === void 0 ? void 0 : override.namespace) !== null && _2 !== void 0 ? _2 : yaml === null || yaml === void 0 ? void 0 : yaml.namespace) !== null && _3 !== void 0 ? _3 : this.env('HATCHET_CLIENT_NAMESPACE');
|
|
79
83
|
if (namespace && !(namespace === null || namespace === void 0 ? void 0 : namespace.endsWith('_'))) {
|
|
80
84
|
namespace = `${namespace}_`;
|
|
81
85
|
}
|
|
82
86
|
return {
|
|
83
|
-
token: (
|
|
87
|
+
token: (_5 = (_4 = override === null || override === void 0 ? void 0 : override.token) !== null && _4 !== void 0 ? _4 : yaml === null || yaml === void 0 ? void 0 : yaml.token) !== null && _5 !== void 0 ? _5 : this.env('HATCHET_CLIENT_TOKEN'),
|
|
84
88
|
host_port: grpcBroadcastAddress,
|
|
85
89
|
api_url: apiUrl,
|
|
86
90
|
tls_config: tlsConfig,
|
|
87
|
-
|
|
91
|
+
healthcheck: healthCheckConfig,
|
|
92
|
+
log_level: (_8 = (_7 = (_6 = override === null || override === void 0 ? void 0 : override.log_level) !== null && _6 !== void 0 ? _6 : yaml === null || yaml === void 0 ? void 0 : yaml.log_level) !== null && _7 !== void 0 ? _7 : this.env('HATCHET_CLIENT_LOG_LEVEL')) !== null && _8 !== void 0 ? _8 : 'INFO',
|
|
88
93
|
tenant_id: tenantId,
|
|
89
94
|
namespace: namespace ? `${namespace}`.toLowerCase() : '',
|
|
90
95
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Logger } from '../../../util/logger';
|
|
2
|
+
export declare const workerStatus: {
|
|
3
|
+
readonly INITIALIZED: "INITIALIZED";
|
|
4
|
+
readonly STARTING: "STARTING";
|
|
5
|
+
readonly HEALTHY: "HEALTHY";
|
|
6
|
+
readonly UNHEALTHY: "UNHEALTHY";
|
|
7
|
+
};
|
|
8
|
+
export type WorkerStatus = (typeof workerStatus)[keyof typeof workerStatus];
|
|
9
|
+
export declare class HealthServer {
|
|
10
|
+
private port;
|
|
11
|
+
private getStatus;
|
|
12
|
+
private workerName;
|
|
13
|
+
private getSlots;
|
|
14
|
+
private getActions;
|
|
15
|
+
private getLabels;
|
|
16
|
+
private logger;
|
|
17
|
+
private server;
|
|
18
|
+
private register;
|
|
19
|
+
private workerStatusGauge;
|
|
20
|
+
private workerSlotsGauge;
|
|
21
|
+
private workerActionsGauge;
|
|
22
|
+
private metricsInitialized;
|
|
23
|
+
constructor(port: number, getStatus: () => WorkerStatus, workerName: string, getSlots: () => number, getActions: () => string[], getLabels: () => Record<string, string | number>, logger: Logger);
|
|
24
|
+
private handleRequest;
|
|
25
|
+
private handleHealth;
|
|
26
|
+
private initializeMetrics;
|
|
27
|
+
private handleMetrics;
|
|
28
|
+
start(): Promise<void>;
|
|
29
|
+
stop(): Promise<void>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.HealthServer = exports.workerStatus = void 0;
|
|
13
|
+
const node_http_1 = require("node:http");
|
|
14
|
+
exports.workerStatus = {
|
|
15
|
+
INITIALIZED: 'INITIALIZED',
|
|
16
|
+
STARTING: 'STARTING',
|
|
17
|
+
HEALTHY: 'HEALTHY',
|
|
18
|
+
UNHEALTHY: 'UNHEALTHY',
|
|
19
|
+
};
|
|
20
|
+
class HealthServer {
|
|
21
|
+
constructor(port, getStatus, workerName, getSlots, getActions, getLabels, logger) {
|
|
22
|
+
this.port = port;
|
|
23
|
+
this.getStatus = getStatus;
|
|
24
|
+
this.workerName = workerName;
|
|
25
|
+
this.getSlots = getSlots;
|
|
26
|
+
this.getActions = getActions;
|
|
27
|
+
this.getLabels = getLabels;
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
this.server = null;
|
|
30
|
+
this.register = null;
|
|
31
|
+
this.workerStatusGauge = null;
|
|
32
|
+
this.workerSlotsGauge = null;
|
|
33
|
+
this.workerActionsGauge = null;
|
|
34
|
+
this.metricsInitialized = false;
|
|
35
|
+
this.initializeMetrics();
|
|
36
|
+
}
|
|
37
|
+
handleRequest(req, res) {
|
|
38
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
39
|
+
const url = req.url || '';
|
|
40
|
+
if (url === '/health' && req.method === 'GET') {
|
|
41
|
+
yield this.handleHealth(res);
|
|
42
|
+
}
|
|
43
|
+
else if (url === '/metrics' && req.method === 'GET') {
|
|
44
|
+
yield this.handleMetrics(res);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
48
|
+
res.end('Not Found');
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
handleHealth(res) {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
const response = {
|
|
55
|
+
status: this.getStatus(),
|
|
56
|
+
name: this.workerName,
|
|
57
|
+
slots: this.getSlots(),
|
|
58
|
+
actions: this.getActions(),
|
|
59
|
+
labels: this.getLabels(),
|
|
60
|
+
nodeVersion: process.version,
|
|
61
|
+
};
|
|
62
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
63
|
+
yield res.end(JSON.stringify(response));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
initializeMetrics() {
|
|
67
|
+
try {
|
|
68
|
+
// @ts-ignore - prom-client is an optional dependency
|
|
69
|
+
// eslint-disable-next-line
|
|
70
|
+
const { Registry, Gauge, collectDefaultMetrics } = require('prom-client');
|
|
71
|
+
this.register = new Registry();
|
|
72
|
+
collectDefaultMetrics({ register: this.register });
|
|
73
|
+
this.workerStatusGauge = new Gauge({
|
|
74
|
+
name: 'hatchet_worker_status',
|
|
75
|
+
help: 'Current status of the Hatchet worker',
|
|
76
|
+
registers: [this.register],
|
|
77
|
+
collect: () => {
|
|
78
|
+
this.workerStatusGauge.set(this.getStatus() === exports.workerStatus.HEALTHY ? 1 : 0);
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
this.workerSlotsGauge = new Gauge({
|
|
82
|
+
name: 'hatchet_worker_slots',
|
|
83
|
+
help: 'Total slots available on the worker',
|
|
84
|
+
registers: [this.register],
|
|
85
|
+
collect: () => {
|
|
86
|
+
this.workerSlotsGauge.set(this.getSlots());
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
this.workerActionsGauge = new Gauge({
|
|
90
|
+
name: 'hatchet_worker_actions',
|
|
91
|
+
help: 'Number of registered actions on the worker',
|
|
92
|
+
registers: [this.register],
|
|
93
|
+
collect: () => {
|
|
94
|
+
this.workerActionsGauge.set(this.getActions().length);
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
this.metricsInitialized = true;
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
this.metricsInitialized = false;
|
|
101
|
+
this.logger.error('Metrics initialization failed - prom-client dependency not installed');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
handleMetrics(res) {
|
|
105
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
106
|
+
if (!this.metricsInitialized || !this.register) {
|
|
107
|
+
this.logger.error('Metrics initialization failed - prom-client dependency not installed');
|
|
108
|
+
res.writeHead(503, { 'Content-Type': 'text/plain' });
|
|
109
|
+
res.end('Metrics initialization failed');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const metrics = yield this.register.metrics();
|
|
114
|
+
res.writeHead(200, { 'Content-Type': this.register.contentType });
|
|
115
|
+
res.end(metrics);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
this.logger.error(`Error generating metrics: ${error}`);
|
|
119
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
120
|
+
res.end('Error generating metrics');
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
start() {
|
|
125
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
try {
|
|
128
|
+
this.server = (0, node_http_1.createServer)((req, res) => {
|
|
129
|
+
this.handleRequest(req, res);
|
|
130
|
+
});
|
|
131
|
+
this.server.listen(this.port, '0.0.0.0', () => {
|
|
132
|
+
this.logger.info(`Health check server running on port ${this.port}`);
|
|
133
|
+
resolve();
|
|
134
|
+
});
|
|
135
|
+
this.server.on('error', (error) => {
|
|
136
|
+
this.logger.error(`Failed to start health check server: ${error.message}`);
|
|
137
|
+
reject(error);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
this.logger.error(`Failed to start health check server: ${error}`);
|
|
142
|
+
reject(error);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
stop() {
|
|
148
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
149
|
+
if (this.server) {
|
|
150
|
+
return new Promise((resolve) => {
|
|
151
|
+
this.server.close(() => {
|
|
152
|
+
this.logger.info('Health check server stopped!');
|
|
153
|
+
resolve();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return Promise.resolve();
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.HealthServer = HealthServer;
|
|
@@ -14,6 +14,8 @@ export interface WorkerOpts {
|
|
|
14
14
|
handleKill?: boolean;
|
|
15
15
|
maxRuns?: number;
|
|
16
16
|
labels?: WorkerLabels;
|
|
17
|
+
healthPort?: number;
|
|
18
|
+
enableHealthServer?: boolean;
|
|
17
19
|
}
|
|
18
20
|
export declare class V1Worker {
|
|
19
21
|
client: HatchetClient;
|
|
@@ -30,12 +32,21 @@ export declare class V1Worker {
|
|
|
30
32
|
logger: Logger;
|
|
31
33
|
registeredWorkflowPromises: Array<Promise<any>>;
|
|
32
34
|
labels: WorkerLabels;
|
|
35
|
+
healthPort: number;
|
|
36
|
+
enableHealthServer: boolean;
|
|
37
|
+
private healthServer;
|
|
38
|
+
private status;
|
|
33
39
|
constructor(client: HatchetClient, options: {
|
|
34
40
|
name: string;
|
|
35
41
|
handleKill?: boolean;
|
|
36
42
|
maxRuns?: number;
|
|
37
43
|
labels?: WorkerLabels;
|
|
38
44
|
});
|
|
45
|
+
private initializeHealthServer;
|
|
46
|
+
private getAvailableSlots;
|
|
47
|
+
private getRegisteredActions;
|
|
48
|
+
private getFilteredLabels;
|
|
49
|
+
private setStatus;
|
|
39
50
|
private registerActions;
|
|
40
51
|
getHandler(workflows: Workflow[]): void;
|
|
41
52
|
registerWebhook(webhook: WebhookWorkerCreateRequest): Promise<import("axios").AxiosResponse<import("../../../clients/rest/generated/data-contracts").WebhookWorkerCreated, any, {}>>;
|
|
@@ -32,23 +32,61 @@ const step_1 = require("../../../step");
|
|
|
32
32
|
const apply_namespace_1 = require("../../../util/apply-namespace");
|
|
33
33
|
const context_1 = require("./context");
|
|
34
34
|
const parent_run_context_vars_1 = require("../../parent-run-context-vars");
|
|
35
|
+
const health_server_1 = require("./health-server");
|
|
35
36
|
class V1Worker {
|
|
36
37
|
constructor(client, options) {
|
|
38
|
+
var _a, _b, _c, _d;
|
|
37
39
|
this.workflow_registry = [];
|
|
38
40
|
this.futures = {};
|
|
39
41
|
this.contexts = {};
|
|
40
42
|
this.registeredWorkflowPromises = [];
|
|
41
43
|
this.labels = {};
|
|
44
|
+
this.status = health_server_1.workerStatus.INITIALIZED;
|
|
42
45
|
this.client = client;
|
|
43
46
|
this.name = (0, apply_namespace_1.applyNamespace)(options.name, this.client.config.namespace);
|
|
44
47
|
this.action_registry = {};
|
|
45
48
|
this.maxRuns = options.maxRuns;
|
|
46
49
|
this.labels = options.labels || {};
|
|
50
|
+
this.enableHealthServer = (_b = (_a = client.config.healthcheck) === null || _a === void 0 ? void 0 : _a.enabled) !== null && _b !== void 0 ? _b : false;
|
|
51
|
+
this.healthPort = (_d = (_c = client.config.healthcheck) === null || _c === void 0 ? void 0 : _c.port) !== null && _d !== void 0 ? _d : 8001;
|
|
47
52
|
process.on('SIGTERM', () => this.exitGracefully(true));
|
|
48
53
|
process.on('SIGINT', () => this.exitGracefully(true));
|
|
49
54
|
this.killing = false;
|
|
50
55
|
this.handle_kill = options.handleKill === undefined ? true : options.handleKill;
|
|
51
56
|
this.logger = client.config.logger(`Worker/${this.name}`, this.client.config.log_level);
|
|
57
|
+
if (this.enableHealthServer && this.healthPort) {
|
|
58
|
+
this.initializeHealthServer();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
initializeHealthServer() {
|
|
62
|
+
if (!this.healthPort) {
|
|
63
|
+
this.logger.warn('Health server enabled but no port specified');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this.healthServer = new health_server_1.HealthServer(this.healthPort, () => this.status, this.name, () => this.getAvailableSlots(), () => this.getRegisteredActions(), () => this.getFilteredLabels(), this.logger);
|
|
67
|
+
}
|
|
68
|
+
getAvailableSlots() {
|
|
69
|
+
if (!this.maxRuns) {
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
const currentRuns = Object.keys(this.futures).length;
|
|
73
|
+
return Math.max(0, this.maxRuns - currentRuns);
|
|
74
|
+
}
|
|
75
|
+
getRegisteredActions() {
|
|
76
|
+
return Object.keys(this.action_registry);
|
|
77
|
+
}
|
|
78
|
+
getFilteredLabels() {
|
|
79
|
+
const filtered = {};
|
|
80
|
+
for (const [key, value] of Object.entries(this.labels)) {
|
|
81
|
+
if (value !== undefined) {
|
|
82
|
+
filtered[key] = value;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return filtered;
|
|
86
|
+
}
|
|
87
|
+
setStatus(status) {
|
|
88
|
+
this.status = status;
|
|
89
|
+
this.logger.debug(`Worker status changed to: ${status}`);
|
|
52
90
|
}
|
|
53
91
|
registerActions(workflow) {
|
|
54
92
|
var _a;
|
|
@@ -127,7 +165,7 @@ class V1Worker {
|
|
|
127
165
|
try {
|
|
128
166
|
const { concurrency } = workflow;
|
|
129
167
|
let onFailureTask;
|
|
130
|
-
if (
|
|
168
|
+
if (workflow.onFailure && typeof workflow.onFailure === 'function') {
|
|
131
169
|
onFailureTask = {
|
|
132
170
|
readableId: 'on-failure-task',
|
|
133
171
|
action: onFailureTaskName(workflow),
|
|
@@ -140,7 +178,7 @@ class V1Worker {
|
|
|
140
178
|
concurrency: [],
|
|
141
179
|
};
|
|
142
180
|
}
|
|
143
|
-
if (
|
|
181
|
+
if (workflow.onFailure && typeof workflow.onFailure === 'object') {
|
|
144
182
|
const onFailure = workflow.onFailure;
|
|
145
183
|
onFailureTask = {
|
|
146
184
|
readableId: 'on-failure-task',
|
|
@@ -195,8 +233,10 @@ class V1Worker {
|
|
|
195
233
|
this.logger.warn(`\`on\` for event and cron triggers is deprecated and will be removed soon, use \`onEvents\` and \`onCrons\` instead for ${workflow.name}`);
|
|
196
234
|
}
|
|
197
235
|
const eventTriggers = [
|
|
198
|
-
...(workflow.onEvents || []),
|
|
199
|
-
...(((_u = workflow.on) === null || _u === void 0 ? void 0 : _u.event)
|
|
236
|
+
...(workflow.onEvents || []).map((event) => (0, apply_namespace_1.applyNamespace)(event, this.client.config.namespace)),
|
|
237
|
+
...(((_u = workflow.on) === null || _u === void 0 ? void 0 : _u.event)
|
|
238
|
+
? [(0, apply_namespace_1.applyNamespace)(workflow.on.event, this.client.config.namespace)]
|
|
239
|
+
: []),
|
|
200
240
|
];
|
|
201
241
|
const cronTriggers = [
|
|
202
242
|
...(workflow.onCrons || []),
|
|
@@ -582,6 +622,7 @@ class V1Worker {
|
|
|
582
622
|
return __awaiter(this, void 0, void 0, function* () {
|
|
583
623
|
var _a;
|
|
584
624
|
this.killing = true;
|
|
625
|
+
this.setStatus(health_server_1.workerStatus.UNHEALTHY);
|
|
585
626
|
this.logger.info('Starting to exit...');
|
|
586
627
|
try {
|
|
587
628
|
yield ((_a = this.listener) === null || _a === void 0 ? void 0 : _a.unregister());
|
|
@@ -593,6 +634,14 @@ class V1Worker {
|
|
|
593
634
|
// attempt to wait for futures to finish
|
|
594
635
|
yield Promise.all(Object.values(this.futures).map(({ promise }) => promise));
|
|
595
636
|
this.logger.info('Successfully finished pending tasks.');
|
|
637
|
+
if (this.healthServer) {
|
|
638
|
+
try {
|
|
639
|
+
yield this.healthServer.stop();
|
|
640
|
+
}
|
|
641
|
+
catch (e) {
|
|
642
|
+
this.logger.error(`Could not stop health server: ${e.message}`);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
596
645
|
if (handleKill) {
|
|
597
646
|
this.logger.info('Exiting hatchet worker...');
|
|
598
647
|
process.exit(0);
|
|
@@ -602,6 +651,17 @@ class V1Worker {
|
|
|
602
651
|
start() {
|
|
603
652
|
return __awaiter(this, void 0, void 0, function* () {
|
|
604
653
|
var _a, e_1, _b, _c;
|
|
654
|
+
this.setStatus(health_server_1.workerStatus.STARTING);
|
|
655
|
+
if (this.healthServer) {
|
|
656
|
+
try {
|
|
657
|
+
yield this.healthServer.start();
|
|
658
|
+
}
|
|
659
|
+
catch (e) {
|
|
660
|
+
this.logger.error(`Could not start health server: ${e.message}`);
|
|
661
|
+
this.setStatus(health_server_1.workerStatus.UNHEALTHY);
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
605
665
|
// ensure all workflows are registered
|
|
606
666
|
yield Promise.all(this.registeredWorkflowPromises);
|
|
607
667
|
if (Object.keys(this.action_registry).length === 0) {
|
|
@@ -616,6 +676,7 @@ class V1Worker {
|
|
|
616
676
|
labels: this.labels,
|
|
617
677
|
});
|
|
618
678
|
this.workerId = this.listener.workerId;
|
|
679
|
+
this.setStatus(health_server_1.workerStatus.HEALTHY);
|
|
619
680
|
const generator = this.listener.actions();
|
|
620
681
|
this.logger.info(`Worker ${this.name} listening for actions`);
|
|
621
682
|
try {
|
|
@@ -636,6 +697,7 @@ class V1Worker {
|
|
|
636
697
|
}
|
|
637
698
|
}
|
|
638
699
|
catch (e) {
|
|
700
|
+
this.setStatus(health_server_1.workerStatus.UNHEALTHY);
|
|
639
701
|
if (this.killing) {
|
|
640
702
|
this.logger.info(`Exiting worker, ignoring error: ${e.message}`);
|
|
641
703
|
return;
|
package/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const HATCHET_VERSION = "1.
|
|
1
|
+
export declare const HATCHET_VERSION = "1.10.0";
|
package/version.js
CHANGED