11import { logger } from "../utilities/logger.js" ;
22import { TaskRunProcess } from "../executions/taskRunProcess.js" ;
33import { env as stdEnv } from "std-env" ;
4- import { z } from "zod" ;
5- import { randomUUID } from "crypto" ;
64import { readJSONFile } from "../utilities/fileSystem.js" ;
75import {
86 type CompleteRunAttemptResult ,
@@ -26,57 +24,13 @@ import {
2624import { assertExhaustive } from "../utilities/assertExhaustive.js" ;
2725import { setTimeout as sleep } from "timers/promises" ;
2826import { io , type Socket } from "socket.io-client" ;
29-
30- const DateEnv = z
31- . string ( )
32- . transform ( ( val ) => new Date ( parseInt ( val , 10 ) ) )
33- . pipe ( z . date ( ) ) ;
34-
35- // All IDs are friendly IDs
36- const Env = z . object ( {
37- // Set at build time
38- TRIGGER_CONTENT_HASH : z . string ( ) ,
39- TRIGGER_DEPLOYMENT_ID : z . string ( ) ,
40- TRIGGER_DEPLOYMENT_VERSION : z . string ( ) ,
41- TRIGGER_PROJECT_ID : z . string ( ) ,
42- TRIGGER_PROJECT_REF : z . string ( ) ,
43- NODE_ENV : z . string ( ) . default ( "production" ) ,
44- NODE_EXTRA_CA_CERTS : z . string ( ) . optional ( ) ,
45-
46- // Set at runtime
47- TRIGGER_WORKLOAD_CONTROLLER_ID : z . string ( ) . default ( `controller_${ randomUUID ( ) } ` ) ,
48- TRIGGER_ENV_ID : z . string ( ) ,
49- TRIGGER_RUN_ID : z . string ( ) . optional ( ) , // This is only useful for cold starts
50- TRIGGER_SNAPSHOT_ID : z . string ( ) . optional ( ) , // This is only useful for cold starts
51- OTEL_EXPORTER_OTLP_ENDPOINT : z . string ( ) . url ( ) ,
52- TRIGGER_WARM_START_URL : z . string ( ) . optional ( ) ,
53- TRIGGER_WARM_START_CONNECTION_TIMEOUT_MS : z . coerce . number ( ) . default ( 30_000 ) ,
54- TRIGGER_WARM_START_KEEPALIVE_MS : z . coerce . number ( ) . default ( 300_000 ) ,
55- TRIGGER_MACHINE_CPU : z . string ( ) . default ( "0" ) ,
56- TRIGGER_MACHINE_MEMORY : z . string ( ) . default ( "0" ) ,
57- TRIGGER_RUNNER_ID : z . string ( ) ,
58- TRIGGER_METADATA_URL : z . string ( ) . optional ( ) ,
59- TRIGGER_PRE_SUSPEND_WAIT_MS : z . coerce . number ( ) . default ( 200 ) ,
60-
61- // Timeline metrics
62- TRIGGER_POD_SCHEDULED_AT_MS : DateEnv ,
63- TRIGGER_DEQUEUED_AT_MS : DateEnv ,
64-
65- // May be overridden
66- TRIGGER_SUPERVISOR_API_PROTOCOL : z . enum ( [ "http" , "https" ] ) ,
67- TRIGGER_SUPERVISOR_API_DOMAIN : z . string ( ) ,
68- TRIGGER_SUPERVISOR_API_PORT : z . coerce . number ( ) ,
69- TRIGGER_WORKER_INSTANCE_NAME : z . string ( ) ,
70- TRIGGER_HEARTBEAT_INTERVAL_SECONDS : z . coerce . number ( ) . default ( 30 ) ,
71- TRIGGER_SNAPSHOT_POLL_INTERVAL_SECONDS : z . coerce . number ( ) . default ( 5 ) ,
72- TRIGGER_SUCCESS_EXIT_CODE : z . coerce . number ( ) . default ( 0 ) ,
73- TRIGGER_FAILURE_EXIT_CODE : z . coerce . number ( ) . default ( 1 ) ,
74- } ) ;
75-
76- const env = Env . parse ( stdEnv ) ;
27+ import { RunnerEnv } from "./managed/env.js" ;
28+ import { MetadataClient } from "./managed/overrides.js" ;
7729
7830logger . loggerLevel = "debug" ;
7931
32+ const env = new RunnerEnv ( stdEnv ) ;
33+
8034type ManagedRunControllerOptions = {
8135 workerManifest : WorkerManifest ;
8236} ;
@@ -90,40 +44,10 @@ type Snapshot = {
9044 friendlyId : string ;
9145} ;
9246
93- type Metadata = {
94- TRIGGER_SUPERVISOR_API_PROTOCOL : string | undefined ;
95- TRIGGER_SUPERVISOR_API_DOMAIN : string | undefined ;
96- TRIGGER_SUPERVISOR_API_PORT : number | undefined ;
97- TRIGGER_WORKER_INSTANCE_NAME : string | undefined ;
98- TRIGGER_HEARTBEAT_INTERVAL_SECONDS : number | undefined ;
99- TRIGGER_SNAPSHOT_POLL_INTERVAL_SECONDS : number | undefined ;
100- TRIGGER_SUCCESS_EXIT_CODE : number | undefined ;
101- TRIGGER_FAILURE_EXIT_CODE : number | undefined ;
102- TRIGGER_RUNNER_ID : string | undefined ;
103- } ;
104-
105- class MetadataClient {
106- private readonly url : URL ;
107-
108- constructor ( url : string ) {
109- this . url = new URL ( url ) ;
110- }
111-
112- async getEnvOverrides ( ) : Promise < Metadata | null > {
113- try {
114- const response = await fetch ( new URL ( "/env" , this . url ) ) ;
115- return response . json ( ) ;
116- } catch ( error ) {
117- console . error ( "Failed to fetch metadata" , { error } ) ;
118- return null ;
119- }
120- }
121- }
122-
12347class ManagedRunController {
12448 private taskRunProcess ?: TaskRunProcess ;
12549
126- private workerManifest : WorkerManifest ;
50+ private readonly workerManifest : WorkerManifest ;
12751
12852 private readonly httpClient : WorkloadHttpClient ;
12953 private readonly warmStartClient : WarmStartClient | undefined ;
@@ -132,18 +56,35 @@ class ManagedRunController {
13256 private socket : Socket < WorkloadServerToClientEvents , WorkloadClientToServerEvents > ;
13357
13458 private readonly runHeartbeat : HeartbeatService ;
135- private heartbeatIntervalSeconds : number ;
136-
13759 private readonly snapshotPoller : HeartbeatService ;
138- private snapshotPollIntervalSeconds : number ;
13960
140- private workerApiUrl : string ;
141- private workerInstanceName : string ;
61+ get heartbeatIntervalSeconds ( ) {
62+ return env . TRIGGER_HEARTBEAT_INTERVAL_SECONDS ;
63+ }
64+
65+ get snapshotPollIntervalSeconds ( ) {
66+ return env . TRIGGER_SNAPSHOT_POLL_INTERVAL_SECONDS ;
67+ }
68+
69+ get runnerId ( ) {
70+ return env . TRIGGER_RUNNER_ID ;
71+ }
72+
73+ get successExitCode ( ) {
74+ return env . TRIGGER_SUCCESS_EXIT_CODE ;
75+ }
14276
143- private runnerId : string ;
77+ get failureExitCode ( ) {
78+ return env . TRIGGER_FAILURE_EXIT_CODE ;
79+ }
80+
81+ get workerApiUrl ( ) {
82+ return env . TRIGGER_SUPERVISOR_API_URL ;
83+ }
14484
145- private successExitCode = env . TRIGGER_SUCCESS_EXIT_CODE ;
146- private failureExitCode = env . TRIGGER_FAILURE_EXIT_CODE ;
85+ get workerInstanceName ( ) {
86+ return env . TRIGGER_WORKER_INSTANCE_NAME ;
87+ }
14788
14889 private state :
14990 | {
@@ -158,11 +99,6 @@ class ManagedRunController {
15899 constructor ( opts : ManagedRunControllerOptions ) {
159100 this . workerManifest = opts . workerManifest ;
160101
161- this . runnerId = env . TRIGGER_RUNNER_ID ;
162-
163- this . workerApiUrl = `${ env . TRIGGER_SUPERVISOR_API_PROTOCOL } ://${ env . TRIGGER_SUPERVISOR_API_DOMAIN } :${ env . TRIGGER_SUPERVISOR_API_PORT } ` ;
164- this . workerInstanceName = env . TRIGGER_WORKER_INSTANCE_NAME ;
165-
166102 this . httpClient = new WorkloadHttpClient ( {
167103 workerApiUrl : this . workerApiUrl ,
168104 runnerId : this . runnerId ,
@@ -172,7 +108,7 @@ class ManagedRunController {
172108 } ) ;
173109
174110 const properties = {
175- ...env ,
111+ ...env . raw ,
176112 TRIGGER_POD_SCHEDULED_AT_MS : env . TRIGGER_POD_SCHEDULED_AT_MS . toISOString ( ) ,
177113 TRIGGER_DEQUEUED_AT_MS : env . TRIGGER_DEQUEUED_AT_MS . toISOString ( ) ,
178114 } ;
@@ -183,9 +119,6 @@ class ManagedRunController {
183119 properties,
184120 } ) ;
185121
186- this . heartbeatIntervalSeconds = env . TRIGGER_HEARTBEAT_INTERVAL_SECONDS ;
187- this . snapshotPollIntervalSeconds = env . TRIGGER_SNAPSHOT_POLL_INTERVAL_SECONDS ;
188-
189122 if ( env . TRIGGER_METADATA_URL ) {
190123 this . metadataClient = new MetadataClient ( env . TRIGGER_METADATA_URL ) ;
191124 }
@@ -834,45 +767,24 @@ class ManagedRunController {
834767 properties : { ...overrides } ,
835768 } ) ;
836769
837- if ( overrides . TRIGGER_SUCCESS_EXIT_CODE ) {
838- this . successExitCode = overrides . TRIGGER_SUCCESS_EXIT_CODE ;
839- }
840-
841- if ( overrides . TRIGGER_FAILURE_EXIT_CODE ) {
842- this . failureExitCode = overrides . TRIGGER_FAILURE_EXIT_CODE ;
843- }
770+ // Override the env with the new values
771+ env . override ( overrides ) ;
844772
773+ // Update services and clients with the new values
845774 if ( overrides . TRIGGER_HEARTBEAT_INTERVAL_SECONDS ) {
846- this . heartbeatIntervalSeconds = overrides . TRIGGER_HEARTBEAT_INTERVAL_SECONDS ;
847- this . runHeartbeat . updateInterval ( this . heartbeatIntervalSeconds * 1000 ) ;
775+ this . runHeartbeat . updateInterval ( env . TRIGGER_HEARTBEAT_INTERVAL_SECONDS * 1000 ) ;
848776 }
849-
850777 if ( overrides . TRIGGER_SNAPSHOT_POLL_INTERVAL_SECONDS ) {
851- this . snapshotPollIntervalSeconds = overrides . TRIGGER_SNAPSHOT_POLL_INTERVAL_SECONDS ;
852- this . snapshotPoller . updateInterval ( this . snapshotPollIntervalSeconds * 1000 ) ;
853- }
854-
855- if ( overrides . TRIGGER_WORKER_INSTANCE_NAME ) {
856- this . workerInstanceName = overrides . TRIGGER_WORKER_INSTANCE_NAME ;
778+ this . snapshotPoller . updateInterval ( env . TRIGGER_SNAPSHOT_POLL_INTERVAL_SECONDS * 1000 ) ;
857779 }
858-
859780 if (
860781 overrides . TRIGGER_SUPERVISOR_API_PROTOCOL ||
861782 overrides . TRIGGER_SUPERVISOR_API_DOMAIN ||
862783 overrides . TRIGGER_SUPERVISOR_API_PORT
863784 ) {
864- const protocol =
865- overrides . TRIGGER_SUPERVISOR_API_PROTOCOL ?? env . TRIGGER_SUPERVISOR_API_PROTOCOL ;
866- const domain = overrides . TRIGGER_SUPERVISOR_API_DOMAIN ?? env . TRIGGER_SUPERVISOR_API_DOMAIN ;
867- const port = overrides . TRIGGER_SUPERVISOR_API_PORT ?? env . TRIGGER_SUPERVISOR_API_PORT ;
868-
869- this . workerApiUrl = `${ protocol } ://${ domain } :${ port } ` ;
870-
871785 this . httpClient . updateApiUrl ( this . workerApiUrl ) ;
872786 }
873-
874787 if ( overrides . TRIGGER_RUNNER_ID ) {
875- this . runnerId = overrides . TRIGGER_RUNNER_ID ;
876788 this . httpClient . updateRunnerId ( this . runnerId ) ;
877789 }
878790 }
@@ -1670,9 +1582,9 @@ await prodWorker.start();
16701582
16711583function gatherProcessEnv ( ) : Record < string , string > {
16721584 const $env = {
1673- NODE_ENV : env . NODE_ENV ,
1674- NODE_EXTRA_CA_CERTS : env . NODE_EXTRA_CA_CERTS ,
1675- OTEL_EXPORTER_OTLP_ENDPOINT : env . OTEL_EXPORTER_OTLP_ENDPOINT ,
1585+ NODE_ENV : stdEnv . NODE_ENV ,
1586+ NODE_EXTRA_CA_CERTS : stdEnv . NODE_EXTRA_CA_CERTS ,
1587+ OTEL_EXPORTER_OTLP_ENDPOINT : stdEnv . OTEL_EXPORTER_OTLP_ENDPOINT ,
16761588 } ;
16771589
16781590 // Filter out undefined values
0 commit comments