@@ -4,7 +4,7 @@ Runner based on podman.
4
4
5
5
import getLogger from "@cocalc/backend/logger" ;
6
6
import { nodePath } from "./mounts" ;
7
- import { isValidUUID } from "@cocalc/util/misc" ;
7
+ import { isValidUUID , replace_all } from "@cocalc/util/misc" ;
8
8
import { ensureConfFilesExists , setupDataPath , writeSecretToken } from "./util" ;
9
9
import { getEnvironment } from "./env" ;
10
10
import { mkdir } from "fs/promises" ;
@@ -23,6 +23,8 @@ const children: { [project_id: string]: any } = {};
23
23
24
24
const GRACE_PERIOD = 2000 ;
25
25
26
+ const info : { [ project_id : string ] : { home : string } } = { } ;
27
+
26
28
export async function start ( {
27
29
project_id,
28
30
config,
@@ -39,7 +41,10 @@ export async function start({
39
41
return ;
40
42
}
41
43
44
+ const image = await getImage ( { project_id, config } ) ;
42
45
const home = await mountHome ( project_id ) ;
46
+ info [ project_id ] = { home } ;
47
+
43
48
await mkdir ( home , { recursive : true } ) ;
44
49
await ensureConfFilesExists ( home ) ;
45
50
const env = getEnvironment ( {
@@ -76,7 +81,7 @@ export async function start({
76
81
args . push ( "-e" , `${ name } =${ env [ name ] } ` ) ;
77
82
}
78
83
79
- args . push ( config ?. image ?? DEFAULT_IMAGE ) ;
84
+ args . push ( image ) ;
80
85
args . push ( nodePath ) ;
81
86
args . push ( script , "--init" , "project_init.sh" ) ;
82
87
@@ -148,6 +153,59 @@ export async function close() {
148
153
await Promise . all ( v ) ;
149
154
}
150
155
156
+ export async function getImage ( { project_id, config } ) : Promise < string > {
157
+ const { stdout } = await executeCode ( {
158
+ err_on_exit : true ,
159
+ command : "podman" ,
160
+ args : [
161
+ "images" ,
162
+ "--filter" ,
163
+ `reference=localhost/project/${ project_id } ` ,
164
+ "--format=json" ,
165
+ ] ,
166
+ } ) ;
167
+ const v = JSON . parse ( stdout ) ;
168
+ return v [ 0 ] ?. Names ?. [ 0 ] ?? config ?. image ?? DEFAULT_IMAGE ;
169
+ }
170
+
171
+ function nowTag ( ) {
172
+ let s = new Date ( ) . toISOString ( ) ;
173
+ const i = s . lastIndexOf ( ":" ) ;
174
+ s = s . slice ( 0 , i ) ;
175
+ return replace_all ( s , ":" , "-" ) ;
176
+ }
177
+
178
+ export async function save ( {
179
+ project_id,
180
+ tag,
181
+ } : {
182
+ project_id : string ;
183
+ tag ?: string ;
184
+ } ) {
185
+ const home = info [ project_id ] ?. home ?? ( await mountHome ( project_id ) ) ;
186
+ const oci = join ( home , ".oci" ) ;
187
+ await mkdir ( oci , { recursive : true } ) ;
188
+ tag ??= nowTag ( ) ;
189
+ const name = `localhost/project/${ project_id } :${ tag } ` ;
190
+ await executeCode ( {
191
+ err_on_exit : true ,
192
+ command : "podman" ,
193
+ // in seconds; can be long if lots of files/data
194
+ timeout : 60 * 10 ,
195
+ args : [ "commit" , `project-${ project_id } ` , name ] ,
196
+ } ) ;
197
+ await executeCode ( {
198
+ err_on_exit : true ,
199
+ command : "skopeo" ,
200
+ timeout : 60 * 10 ,
201
+ args : [
202
+ "copy" ,
203
+ `containers-storage:${ name } ` ,
204
+ `oci:${ oci } :project-${ project_id } -${ tag } ` ,
205
+ ] ,
206
+ } ) ;
207
+ }
208
+
151
209
// important because it kills all
152
210
// the processes that were spawned
153
211
process . once ( "exit" , close ) ;
0 commit comments