@@ -14,9 +14,13 @@ export class DockerWorkloadManager implements WorkloadManager {
1414 private readonly docker : Docker ;
1515
1616 private readonly runnerNetworks : string [ ] ;
17+ private readonly auth ?: Docker . AuthConfig ;
18+ private readonly platform ?: string ;
1719
1820 constructor ( private opts : WorkloadManagerOptions ) {
19- this . docker = new Docker ( ) ;
21+ this . docker = new Docker ( {
22+ version : env . DOCKER_API_VERSION ,
23+ } ) ;
2024
2125 if ( opts . workloadApiDomain ) {
2226 this . logger . warn ( "⚠️ Custom workload API domain" , {
@@ -25,6 +29,29 @@ export class DockerWorkloadManager implements WorkloadManager {
2529 }
2630
2731 this . runnerNetworks = env . RUNNER_DOCKER_NETWORKS . split ( "," ) ;
32+
33+ this . platform = env . DOCKER_PLATFORM ;
34+ if ( this . platform ) {
35+ this . logger . info ( "🖥️ Platform override" , {
36+ targetPlatform : this . platform ,
37+ hostPlatform : process . arch ,
38+ } ) ;
39+ }
40+
41+ if ( env . DOCKER_REGISTRY_USERNAME && env . DOCKER_REGISTRY_PASSWORD && env . DOCKER_REGISTRY_URL ) {
42+ this . logger . info ( "🐋 Using Docker registry credentials" , {
43+ username : env . DOCKER_REGISTRY_USERNAME ,
44+ url : env . DOCKER_REGISTRY_URL ,
45+ } ) ;
46+
47+ this . auth = {
48+ username : env . DOCKER_REGISTRY_USERNAME ,
49+ password : env . DOCKER_REGISTRY_PASSWORD ,
50+ serveraddress : env . DOCKER_REGISTRY_URL ,
51+ } ;
52+ } else {
53+ this . logger . warn ( "🐋 No Docker registry credentials provided, skipping auth" ) ;
54+ }
2855 }
2956
3057 async create ( opts : WorkloadManagerCreateOptions ) {
@@ -91,7 +118,6 @@ export class DockerWorkloadManager implements WorkloadManager {
91118 }
92119
93120 const containerCreateOpts : Docker . ContainerCreateOptions = {
94- Env : envVars ,
95121 name : runnerId ,
96122 Hostname : runnerId ,
97123 HostConfig : hostConfig ,
@@ -101,30 +127,63 @@ export class DockerWorkloadManager implements WorkloadManager {
101127 AttachStdin : false ,
102128 } ;
103129
104- try {
105- // Create container
106- const container = await this . docker . createContainer ( containerCreateOpts ) ;
130+ if ( this . platform ) {
131+ containerCreateOpts . platform = this . platform ;
132+ }
133+
134+ const logger = this . logger . child ( { opts, containerCreateOpts } ) ;
107135
108- // If there are multiple networks to attach to we need to attach the remaining ones after creation
109- if ( remainingNetworks . length > 0 ) {
110- await this . attachContainerToNetworks ( {
111- containerId : container . id ,
112- networkNames : remainingNetworks ,
113- } ) ;
114- }
136+ // Ensure the image is present
137+ const [ createImageError , imageResponseReader ] = await tryCatch (
138+ this . docker . createImage ( this . auth , {
139+ fromImage : opts . image ,
140+ ...( this . platform ? { platform : this . platform } : { } ) ,
141+ } )
142+ ) ;
143+ if ( createImageError ) {
144+ logger . error ( "Failed to pull image" , { error : createImageError } ) ;
145+ return ;
146+ }
115147
116- // Start container
117- const startResult = await container . start ( ) ;
148+ const [ imageReadError , imageResponse ] = await tryCatch ( readAllChunks ( imageResponseReader ) ) ;
149+ if ( imageReadError ) {
150+ logger . error ( "failed to read image response" , { error : imageReadError } ) ;
151+ return ;
152+ }
118153
119- this . logger . debug ( "create succeeded" , {
120- opts,
121- startResult,
154+ logger . debug ( "pulled image" , { image : opts . image , imageResponse } ) ;
155+
156+ // Create container
157+ const [ createContainerError , container ] = await tryCatch (
158+ this . docker . createContainer ( {
159+ ...containerCreateOpts ,
160+ // Add env vars here so they're not logged
161+ Env : envVars ,
162+ } )
163+ ) ;
164+
165+ if ( createContainerError ) {
166+ logger . error ( "Failed to create container" , { error : createContainerError } ) ;
167+ return ;
168+ }
169+
170+ // If there are multiple networks to attach to we need to attach the remaining ones after creation
171+ if ( remainingNetworks . length > 0 ) {
172+ await this . attachContainerToNetworks ( {
122173 containerId : container . id ,
123- containerCreateOpts ,
174+ networkNames : remainingNetworks ,
124175 } ) ;
125- } catch ( error ) {
126- this . logger . error ( "create failed:" , { opts, error, containerCreateOpts } ) ;
127176 }
177+
178+ // Start container
179+ const [ startError , startResult ] = await tryCatch ( container . start ( ) ) ;
180+
181+ if ( startError ) {
182+ logger . error ( "Failed to start container" , { error : startError , containerId : container . id } ) ;
183+ return ;
184+ }
185+
186+ logger . debug ( "create succeeded" , { startResult, containerId : container . id } ) ;
128187 }
129188
130189 private async attachContainerToNetworks ( {
@@ -173,3 +232,11 @@ export class DockerWorkloadManager implements WorkloadManager {
173232 } ) ;
174233 }
175234}
235+
236+ async function readAllChunks ( reader : NodeJS . ReadableStream ) {
237+ const chunks = [ ] ;
238+ for await ( const chunk of reader ) {
239+ chunks . push ( chunk . toString ( ) ) ;
240+ }
241+ return chunks ;
242+ }
0 commit comments