@@ -20,7 +20,7 @@ import { nodePath } from "./mounts";
2020import { isValidUUID } from "@cocalc/util/misc" ;
2121import { ensureConfFilesExists , setupDataPath , writeSecretToken } from "./util" ;
2222import { getEnvironment } from "./env" ;
23- import { mkdir , readFile } from "node:fs/promises" ;
23+ import { mkdir , readFile , rm } from "node:fs/promises" ;
2424import { spawn } from "node:child_process" ;
2525import { getCoCalcMounts , COCALC_SRC } from "./mounts" ;
2626import { setQuota } from "./filesystem" ;
@@ -36,8 +36,8 @@ import {
3636 type SshServersFunction ,
3737 type LocalPathFunction ,
3838} from "@cocalc/conat/project/runner/types" ;
39- import { write as writeMutagenConfig } from "@cocalc/backend/mutagen/config" ;
4039import { initSshKeys } from "@cocalc/backend/mutagen/ssh-keys" ;
40+ import { PROJECT_IMAGE_PATH } from "@cocalc/util/db-schema/defaults" ;
4141
4242const logger = getLogger ( "project-runner:podman" ) ;
4343const children : { [ project_id : string ] : any } = { } ;
@@ -78,15 +78,24 @@ export async function start({
7878 logger . debug ( "start: got home" , { project_id, home } ) ;
7979 const mounts = getCoCalcMounts ( ) ;
8080 const image = getImage ( config ) ;
81- await initSshKeys ( { home, sshServers : await sshServers ?.( { project_id } ) } ) ;
81+ const servers = await sshServers ?.( { project_id } ) ;
82+ await initSshKeys ( { home, sshServers : servers } ) ;
8283
8384 const env = await getEnvironment ( {
8485 project_id,
8586 env : config ?. env ,
8687 HOME : "/root" ,
8788 image,
8889 } ) ;
89- await startSidecar ( { project_id, home, mounts, env, pod } ) ;
90+ const initMutagen = await startSidecar ( {
91+ image,
92+ project_id,
93+ home,
94+ mounts,
95+ env,
96+ pod,
97+ servers,
98+ } ) ;
9099
91100 const rootfs = await rootFilesystem . mount ( { project_id, home, config } ) ;
92101 logger . debug ( "start: got rootfs" , { project_id, rootfs } ) ;
@@ -95,11 +104,11 @@ export async function start({
95104 await ensureConfFilesExists ( home ) ;
96105 logger . debug ( "start: created conf files" , { project_id } ) ;
97106
98- await writeMutagenConfig ( {
99- home,
100- sync : config ?. sync ,
101- forward : config ?. forward ,
102- } ) ;
107+ // await writeMutagenConfig({
108+ // home,
109+ // sync: config?.sync,
110+ // forward: config?.forward,
111+ // });
103112
104113 await setupDataPath ( home ) ;
105114 logger . debug ( "start: setup data path" , { project_id } ) ;
@@ -119,6 +128,7 @@ export async function start({
119128 const args : string [ ] = [ ] ;
120129 args . push ( "run" ) ;
121130 args . push ( "--rm" ) ;
131+ args . push ( "--replace" ) ;
122132 args . push ( "--user=0:0" ) ;
123133 args . push ( "--pod" , pod ) ;
124134
@@ -157,16 +167,27 @@ export async function start({
157167 child . stderr . on ( "data" , ( chunk : Buffer ) => {
158168 logger . debug ( `project_id=${ project_id } .stderr: ` , chunk . toString ( ) ) ;
159169 } ) ;
170+
171+ await initMutagen ?.( ) ;
160172}
161173
162- async function startSidecar ( { project_id, mounts, env, pod, home } ) {
174+ async function startSidecar ( {
175+ image,
176+ project_id,
177+ mounts,
178+ env,
179+ pod,
180+ home,
181+ servers,
182+ } ) {
163183 // sidecar: refactor
164184 const sidecarPodName = `sidecar-${ project_id } ` ;
165185 const args2 = [
166186 "run" ,
167187 `--name=${ sidecarPodName } ` ,
168188 "--detach" ,
169189 "--rm" ,
190+ "--replace" ,
170191 "--pod" ,
171192 pod ,
172193 "--init" ,
@@ -178,22 +199,72 @@ async function startSidecar({ project_id, mounts, env, pod, home }) {
178199 for ( const name in env ) {
179200 args2 . push ( "-e" , `${ name } =${ env [ name ] } ` ) ;
180201 }
202+
181203 args2 . push ( sidecarImageName , "mutagen" , "daemon" , "run" ) ;
204+
205+ // always start with fresh .mutagen
206+ await rm ( join ( home , ".mutagen-dev" ) , { force : true , recursive : true } ) ;
207+
182208 await podman ( args2 ) ;
209+
210+ if ( servers . length == 0 ) {
211+ return ;
212+ }
213+
214+ const upperdir = join ( PROJECT_IMAGE_PATH , image , "upperdir" ) ;
183215 try {
216+ await podman (
217+ [
218+ "exec" ,
219+ sidecarPodName ,
220+ "rsync" ,
221+ "--relative" ,
222+ "-axH" ,
223+ `${ servers [ 0 ] . name } :${ upperdir } /` ,
224+ "/root/" ,
225+ ] ,
226+ 10 * 60 ,
227+ ) ;
228+ } catch ( err ) {
229+ console . log ( err ) ;
230+ }
231+
232+ return async ( ) => {
184233 await podman ( [
185234 "exec" ,
186235 sidecarPodName ,
187- "rsync" ,
188- "-axH" ,
189- "--exclude=.mutagen" ,
190- "--delete" ,
191- "file-server:/root/" ,
192- "/root/" ,
236+ "mutagen" ,
237+ "sync" ,
238+ "create" ,
239+ "--name=upperdir" ,
240+ "--mode=one-way-replica" ,
241+ "--symlink-mode=posix-raw" ,
242+ join ( "/root" , upperdir ) ,
243+ `${ servers [ 0 ] . name } :${ upperdir } ` ,
193244 ] ) ;
194- } catch ( err ) {
195- console . log ( err ) ;
196- }
245+
246+ await podman ( [
247+ "exec" ,
248+ sidecarPodName ,
249+ "mutagen" ,
250+ "sync" ,
251+ "create" ,
252+ "--name=root" ,
253+ "--symlink-mode=posix-raw" ,
254+ "--ignore" ,
255+ ".local/share/overlay/**" ,
256+ "--ignore" ,
257+ ".cache/cocalc/**" ,
258+ "--ignore" ,
259+ ".mutagen-dev/**" ,
260+ "--ignore" ,
261+ ".ssh/**" ,
262+ "--ignore" ,
263+ ".snapshots/**" ,
264+ "/root" ,
265+ `${ servers [ 0 ] . name } :/root` ,
266+ ] ) ;
267+ } ;
197268}
198269
199270export async function stop ( {
@@ -249,13 +320,14 @@ export async function stop({
249320 }
250321}
251322
252- async function podman ( args : string [ ] ) {
323+ async function podman ( args : string [ ] , timeout ? ) {
253324 logger . debug ( "podman " , args . join ( " " ) ) ;
254325 return await executeCode ( {
255326 verbose : true ,
256327 command : "podman" ,
257328 args,
258329 err_on_exit : true ,
330+ timeout,
259331 } ) ;
260332}
261333
0 commit comments