-
Notifications
You must be signed in to change notification settings - Fork 368
/
render.runner.ts
102 lines (94 loc) · 4.45 KB
/
render.runner.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import type { Runner } from './runner.js';
import { RunnerType } from './runner.js';
import { ProxyAppRouter, getRunnerClient } from '@nangohq/nango-runner';
import { NodeEnv, getEnv, getPersistAPIUrl } from '@nangohq/shared';
import { RenderAPI } from './render.api.js';
import tracer from 'dd-trace';
const jobsServiceUrl = process.env['JOBS_SERVICE_URL'] || 'http://localhost:3005';
const render: RenderAPI = new RenderAPI(process.env['RENDER_API_KEY'] || '');
export class RenderRunner implements Runner {
public client: ProxyAppRouter;
public runnerType: RunnerType = RunnerType.Render;
constructor(
public readonly id: string,
public readonly url: string,
public readonly serviceId: string
) {
this.client = getRunnerClient(this.url);
}
toJSON() {
return { runnerType: this.runnerType, id: this.id, url: this.url, serviceId: this.serviceId };
}
static fromJSON(obj: { id: string; url: string; serviceId: string }): RenderRunner {
return new RenderRunner(obj.id, obj.url, obj.serviceId);
}
async suspend(): Promise<void> {
const span = tracer.startSpan('runner.suspend').setTag('serviceId', this.serviceId).setTag('runnerId', this.id);
try {
await render.suspendService({ serviceId: this.serviceId });
} finally {
span.finish();
}
}
static async get(runnerId: string): Promise<RenderRunner | undefined> {
let svc = null;
const res = await render.getServices({ name: runnerId, type: 'private_service', limit: '1' });
if (res.data.length > 0) {
svc = res.data[0].service;
return new RenderRunner(runnerId, `http://${runnerId}`, svc.id);
}
return undefined;
}
static async getOrStart(runnerId: string): Promise<RenderRunner> {
try {
let svc = null;
// check if runner exists, if not, create it
let res = await render.getServices({ name: runnerId, type: 'private_service', limit: '1' });
if (res.data.length > 0) {
svc = res.data[0].service;
} else {
const imageTag = getEnv();
const ownerId = process.env['RUNNER_OWNER_ID'];
if (!ownerId) {
throw new Error('RUNNER_OWNER_ID is not set');
}
res = await render.createService({
type: 'private_service',
name: runnerId,
ownerId: ownerId,
image: { ownerId: ownerId, imagePath: `nangohq/nango-runner:${imageTag}` },
serviceDetails: { env: 'image' },
envVars: [
{ key: 'NODE_ENV', value: process.env['NODE_ENV'] || NodeEnv.Dev },
{ key: 'NANGO_CLOUD', value: process.env['NANGO_CLOUD'] || 'true' },
{ key: 'NODE_OPTIONS', value: '--max-old-space-size=384' },
{ key: 'RUNNER_ID', value: runnerId },
{ key: 'NOTIFY_IDLE_ENDPOINT', value: `${jobsServiceUrl}/idle` },
{ key: 'IDLE_MAX_DURATION_MS', value: `${25 * 60 * 60 * 1000}` }, // 25 hours
{ key: 'PERSIST_SERVICE_URL', value: getPersistAPIUrl() },
{ key: 'NANGO_TELEMETRY_SDK', value: process.env['NANGO_TELEMETRY_SDK'] || 'false' },
{ key: 'DD_ENV', value: process.env['DD_ENV'] || '' },
{ key: 'DD_SITE', value: process.env['DD_SITE'] || '' },
{ key: 'DD_TRACE_AGENT_URL', value: process.env['DD_TRACE_AGENT_URL'] || '' }
]
});
svc = res.data.service;
}
if (!svc) {
throw new Error(`Unable to get/create runner instance ${runnerId}`);
}
// check if runner is suspended, if so, resume it
if (svc.suspended === 'suspended') {
const span = tracer.startSpan('runner.resume').setTag('serviceId', svc.id).setTag('runnerId', runnerId);
try {
await render.resumeService({ serviceId: svc.id });
} finally {
span.finish();
}
}
return new RenderRunner(runnerId, `http://${runnerId}`, svc.id);
} catch (err) {
throw new Error(`Unable to get runner ${runnerId}: ${err}`);
}
}
}