@@ -393,24 +393,37 @@ module Docker = struct
393393 | [item] -> Ok (`Copy_item (item, dst))
394394 | _ -> Fmt. error_msg " When copying multiple items, the destination must end with '/'"
395395
396+ let tarball_from_context ~src_dir op user ~to_untar =
397+ Log. debug (fun f -> f " tarball_from_context" );
398+ (* If the sending thread finishes (or fails), close the writing
399+ socket immediately so that the tar process finishes too. *)
400+ (* Lwt.finalize *)
401+ (* (fun () -> *)
402+ match op with
403+ | `Copy_items (src_manifest , dst_dir ) ->
404+ Tar_transfer. send_files ~src_dir ~src_manifest ~dst_dir ~to_untar ~user
405+ | `Copy_item (src_manifest , dst ) ->
406+ Tar_transfer. send_file ~src_dir ~src_manifest ~dst ~to_untar ~user (* ) *)
407+ (* (fun () -> Lwt_unix.close to_untar) *)
408+
396409 let mount_point_inside_unix = if Sys. win32 then " /cygdrive/c" else " /var/lib/obuilder"
397410 let mount_point_inside_native = if Sys. win32 then {| C :\| } else mount_point_inside_unix
398411
399- let docker_manifest_from_build ~base ~exclude src user =
412+ let manifest_from_build ~base ~exclude src workdir user =
400413 let obuilder_volume = Docker. obuilder_volume () in
401414 let docker_argv = [
402415 " --mount" ; Printf. sprintf " type=volume,src=%s,dst=%s,readonly" obuilder_volume (mount_point_inside_native / obuilder_volume);
403416 " --entrypoint" ; if Sys. win32 then mount_point_inside_native / obuilder_volume / " bash.exe" else " bash" ;
404417 " --env" ; Printf. sprintf " PATH=%s" (if Sys. win32 then mount_point_inside_unix // obuilder_volume else " /bin:/usr/bin" );
405- " --workdir" ; if Sys. win32 then { | C :\ | } else " / " ;
418+ " --workdir" ; workdir ;
406419 " --user" ; match user with `Unix { Obuilder_spec. uid; gid } -> Printf. sprintf " %d:%d" uid gid | `Windows { Obuilder_spec. name } -> name
407420 (* FIXME: we don't have access to isolation type here. *)
408421 ] in
409422 let manifest_bash =
410423 (* FIXME: does Filename.quote always use Bash quoting rules? *)
411424 Printf. sprintf " exec %s %S %S %d %s %d %s"
412425 (mount_point_inside_unix // obuilder_volume // " manifest.bash" )
413- " . "
426+ workdir
414427 " /"
415428 (List. length exclude)
416429 (String. concat " " (List. map Filename. quote exclude))
@@ -421,96 +434,79 @@ module Docker = struct
421434 let pp f = Os. pp_cmd f [" Generating source manifest" ] in
422435 Docker. run_pread_result ~pp ~rm: true docker_argv (Docker. docker_image base) argv >>!= fun manifests ->
423436 match Parsexp.Many. parse_string manifests with
424- | Ok ts -> List. rev_map Manifest. t_of_sexp ts |> Lwt. return |> Lwt_result. ok
437+ | Ok ts -> List. rev_map Manifest. t_of_sexp ts |> Lwt_result. return
425438 | Error e -> Lwt_result. fail (`Msg (Parsexp.Parse_error. message e))
426439
427- let docker_tarball_from_context ~src_dir mount_point op user =
428- let name = if Sys. win32 then mount_point / " archive.tar" else Filename. temp_file " obuilder-" " " in
429- Log. debug (fun f -> f " docker_tarball_from_context name:%s" name);
430- Lwt_unix. openfile name [O_WRONLY ; O_CREAT ] 0o600 >> = fun to_untar ->
431- (* If the sending thread finishes (or fails), close the writing socket
432- immediately so that the tar process finishes too. *)
433- Lwt. finalize
434- (fun () ->
435- Lwt. try_bind
436- (fun () ->
437- match op with
438- | `Copy_items (src_manifest , dst_dir ) ->
439- Tar_transfer. send_files ~src_dir ~src_manifest ~dst_dir ~to_untar ~user
440- | `Copy_item (src_manifest , dst ) ->
441- Tar_transfer. send_file ~src_dir ~src_manifest ~dst ~to_untar ~user )
442- (fun () ->
443- Lwt_unix. close to_untar >> = fun () ->
444- if not (Sys. win32) then Os. sudo [" cp" ; " --" ; name; mount_point / " archive.tar" ]
445- else Lwt. return_unit)
446- (fun exn -> Lwt_unix. close to_untar >> = fun () -> Lwt. fail exn ))
447- (fun () -> if not (Sys. win32) then Lwt_unix. unlink name else Lwt. return_unit)
448-
449- let docker_tar_transfer ~dst op ch =
450- let copy_root dst acc t =
451- (* FIXME: escape dst, src, path for regexp? *)
452- begin match t with
453- | `File (path , _ ) | `Symlink (path , _ ) ->
454- Lwt_io. fprint ch path >> = fun () ->
455- Lwt. return (" --transform" :: (Printf. sprintf " s,^%s$,%s," path dst) :: acc)
456- | `Dir (src , _ ) as dir ->
457- Lwt_io. fprint ch (String. concat " \n " (Manifest. to_list dir)) >> = fun () ->
458- Lwt. return (" --transform" :: (Printf. sprintf " s,^%s,%s," (Filename. dirname src) dst) :: acc)
459- end >> = fun tar_argv -> Lwt_io. fprint ch " \n " >> = fun () -> Lwt. return tar_argv
440+ let manifest_files_from op fd =
441+ let copy_root manifest =
442+ let list = Manifest. to_from_files ~null: true manifest in
443+ Os. write_all_string fd list 0 (String. length list )
460444 in
461445 match op with
462- | `Copy_items (src_manifest , _ ) -> begin Lwt_list. fold_left_s ( copy_root dst) [] src_manifest end
463- | `Copy_item (src_manifest , _ ) -> begin copy_root dst [] ( src_manifest: Manifest.t ) end
446+ | `Copy_items (src_manifest , _ ) -> Lwt_list. iter_s copy_root src_manifest
447+ | `Copy_item (src_manifest , _ ) -> copy_root src_manifest
464448
465- let docker_tarball_from_build t ~log ~ dst ~ from (`Docker_volume volume ) mount_point op user =
449+ let tarball_from_build t ~files_from ~ to_untar workdir user id =
466450 Log. debug (fun f -> f " docker_tarball_from_build" );
467- begin
468- let name = mount_point / " manifest" in
469- if Sys. win32 then Lwt_io. with_file ~mode: Output name (docker_tar_transfer ~dst op)
470- else Lwt_io. with_temp_file (fun (tmp , ch ) ->
471- docker_tar_transfer ~dst op ch >> = fun tar_argv ->
472- Os. sudo [" cp" ; " -a" ; " --" ; tmp; name] >> = fun () ->
473- Lwt. return tar_argv)
474- end >> = fun tar_argv ->
475451 let argv =
476452 [" --login" ; " -c" ;
477453 String. concat " "
478454 ([ if Sys. win32 then " tar.exe" else " tar" ;
479- " -cf " ; mount_point_inside_unix // volume // " archive.tar " ;
480- " --absolute-names " ; " --no-recursion " ;
481- " --show-transformed-names " ; " -v " ; (* for debugging *)
482- " --files-from" ; mount_point_inside_unix // volume // " manifest " ]
483- @ tar_argv )]
455+ " -c " ; " --format=ustar " ;
456+ " --directory " ; workdir ;
457+ (* Beware, the order is meaningfull: --files-from should come last. *)
458+ " --verbatim- files-from" ; " --null " ; " --absolute-names " ; " --files-from=- " ;
459+ " -f- " ] )]
484460 in
485461 let config =
486462 let obuilder_volume = Docker. obuilder_volume () in
487463 Config. v
488- ~cwd: ( if Sys. win32 then { | C :\ | } else " / " )
464+ ~cwd: workdir
489465 ~argv
490466 ~hostname
491467 ~user
492468 ~env: [" PATH" , if Sys. win32 then mount_point_inside_unix // obuilder_volume else " /bin:/usr/bin" ]
493469 ~mount_secrets: []
494470 ~mounts: Config.Mount. [
495- {src = volume; dst = mount_point_inside_native / volume; readonly = false };
496471 {src = obuilder_volume; dst = mount_point_inside_native / obuilder_volume; readonly = true }]
497472 ~network: []
498473 ~entrypoint: (if Sys. win32 then mount_point_inside_native / obuilder_volume / " bash.exe" else " /bin/bash" )
499474 ()
500475 in
501- let from_tmp = Docker. docker_image ~tmp: true from in
502- Docker. tag (Docker. docker_image from) from_tmp >> = fun () ->
503- (* Bypass db_store. *)
504- let cancelled, set_cancelled = Lwt. wait () in
505- Lwt. finalize
506- (fun () ->
507- Lwt_result. bind_lwt
508- (Docker_sandbox. run ~cancelled ~log t.sandbox config from)
509- (fun () -> docker_teardown_sandbox from ~commit: false ))
510- (fun () ->
511- Docker. image " rm" from_tmp >> = fun () ->
512- Lwt. wakeup_later set_cancelled () ;
513- Lwt. return_unit)
476+ let docker_args, args = Docker_sandbox.Docker_config. make config ~config_dir: " " (* unused *) t in
477+ Docker. run ~stdin: (`FD_move_safely files_from) ~stdout: (`FD_move_safely to_untar)
478+ ~rm: true docker_args (Docker. docker_image id) args
479+
480+ let untar t ~cancelled ~stdin ~log workdir id =
481+ let obuilder_volume = Docker. obuilder_volume () in
482+ let mounts =
483+ if Sys. win32 then [Config.Mount. {
484+ src = obuilder_volume;
485+ dst = mount_point_inside_native / obuilder_volume;
486+ readonly = true ; }]
487+ else []
488+ in
489+ let entrypoint =
490+ if Sys. win32 then Printf. sprintf {| C :\% s\tar.exe| } obuilder_volume
491+ else " /usr/bin/env" in
492+ let argv = [" -xf" ; " -" ; " --absolute-names" ; ]
493+ |> if not Sys. win32 then List. cons " tar" else Fun. id
494+ in
495+ let config = Config. v
496+ ~cwd: workdir
497+ ~argv
498+ ~hostname
499+ ~user: Obuilder_spec. root
500+ ~env: []
501+ ~mount_secrets: []
502+ ~mounts
503+ ~network: []
504+ ~entrypoint
505+ ()
506+ in
507+ Lwt_result. bind_lwt
508+ (Docker_sandbox. run ~cancelled ~stdin ~log t.sandbox config id)
509+ (fun () -> docker_teardown_sandbox id ~commit: true )
514510
515511 let copy t ~context ~base { Obuilder_spec. from; src; dst; exclude } =
516512 let { Context. switch; src_dir; workdir; user; log; shell = _; env = _; scope; secrets = _ } = context in
@@ -526,11 +522,11 @@ module Docker = struct
526522 | None ->
527523 Lwt_result. fail (`Msg (Fmt. str " Build result %S not found" id))
528524 | Some dir ->
529- Lwt_result. return (`Build (id, dir / " rootfs " ))
525+ Lwt_result. return (`Build (id, dir))
530526 end >>!= fun src_dir ->
531527 begin match src_dir with
532528 | `Context src_dir -> sequence (List. map (Manifest. generate ~exclude ~src_dir ) src) |> Lwt. return
533- | `Build (id , _ ) -> docker_manifest_from_build ~base: id ~exclude src user
529+ | `Build (id , _ ) -> manifest_from_build ~base: id ~exclude src workdir user
534530 end >> = fun src_manifest ->
535531 match Result. bind src_manifest (to_copy_op ~dst ) with
536532 | Error _ as e -> Lwt. return e
@@ -543,53 +539,37 @@ module Docker = struct
543539 Log. debug (fun f -> f " COPY: %a@." Sexplib.Sexp. pp_hum (sexp_of_copy_details details));
544540 let id = Sha256. to_hex (Sha256. string (Sexplib.Sexp. to_string (sexp_of_copy_details details))) in
545541 Store. build t.store ?switch ~base ~id ~log (fun ~cancelled ~log _ ->
546- let volume = Docker. docker_volume_copy id in
547- Docker. volume [" create" ] volume >> = fun _ ->
548- Lwt. finalize
549- (fun () ->
550- Docker. mount_point volume >> = fun mount_point ->
551- Log. debug (fun f -> f " Generating tarball" );
552- begin match src_dir with
553- | `Context src_dir -> docker_tarball_from_context ~src_dir mount_point op user |> Lwt_result. ok
554- | `Build (from , _ ) -> docker_tarball_from_build t ~log ~dst ~from volume mount_point op user
555- end >>!= fun () ->
556- let argv = [" -xf" ; mount_point_inside_unix // id // " archive.tar" ; " --absolute-names" ;
557- " -v" (* for debugging *) ]
558- |> if not Sys. win32 then List. cons " tar" else Fun. id
559- in
560- let config =
561- let obuilder_volume = Docker. obuilder_volume () in
562- let mounts =
563- [ Config.Mount. {
564- src = Docker. volume_copy_name id;
565- dst = mount_point_inside_native / id;
566- readonly = false }]
567- |> if Sys. win32 then List. cons Config.Mount. {
568- src = obuilder_volume;
569- dst = mount_point_inside_native / obuilder_volume;
570- readonly = true ; }
571- else Fun. id
572- in
573- let entrypoint =
574- if Sys. win32 then Printf. sprintf {| C :\% s\tar.exe| } obuilder_volume
575- else " /usr/bin/env" in
576- Config. v
577- ~cwd: (if Sys. win32 then {| C :\| } else " /" )
578- ~argv
579- ~hostname
580- ~user: Obuilder_spec. root
581- ~env: []
582- ~mount_secrets: []
583- ~mounts
584- ~network: []
585- ~entrypoint
586- ()
587- in
588- Log. debug (fun f -> f " Docker_sandbox is running" );
589- Lwt_result. bind_lwt
590- (Docker_sandbox. run ~cancelled ~log t.sandbox config id)
591- (fun () -> docker_teardown_sandbox id ~commit: true ))
592- (fun () -> Docker. volume [" rm" ] volume >> = fun _ -> Lwt. return_unit))
542+ Log. debug (fun f -> f " Produce the tar stream" );
543+ (* let dbgout = Os.{raw = Unix.openfile "/tmp/ocaml.tar" [O_WRONLY;O_CREAT;O_TRUNC] 0o666; needs_close = false} in *)
544+ (* Lwt_unix.openfile "/tmp/ocaml.tar" [O_WRONLY;O_CREAT;O_TRUNC] 0o666 >>= fun dbgout -> *)
545+ Os. with_pipe_between_children @@ fun ~r :from_tar ~w :to_untar -> begin
546+ begin match src_dir with
547+ | `Context src_dir ->
548+ Log. debug (fun f -> f " … from build context" );
549+ tarball_from_context ~src_dir op user ~to_untar: (Lwt_unix. of_unix_file_descr to_untar.raw)
550+ | `Build (id , _dir ) ->
551+ Log. debug (fun f -> f " … from build %s" id );
552+ Os. with_pipe_to_child @@ fun ~r :files_from_in ~w :files_from_out -> begin
553+ manifest_files_from op files_from_out >> = fun () ->
554+ tarball_from_build t.sandbox ~files_from: files_from_in ~to_untar workdir user id
555+ end
556+ end >> = fun () -> begin
557+ Log. debug (fun f -> f " Rename the files in tar" );
558+ Os. with_pipe_between_children @@ fun ~r :from_tar' ~w :to_untar -> begin
559+ begin
560+ let from_tar = Lwt_unix. of_unix_file_descr from_tar.raw in
561+ let to_untar = Lwt_unix. of_unix_file_descr to_untar.raw in
562+ match op with
563+ | `Copy_items (_ , dst_dir ) ->
564+ Tar_transfer. transform_files ~dst_dir ~user ~from_tar ~to_untar
565+ | `Copy_item (_ , dst ) ->
566+ Tar_transfer. transform_file ~dst ~user ~from_tar ~to_untar
567+ end >> = fun () ->
568+ Log. debug (fun f -> f " Untar" );
569+ untar t ~cancelled ~log ~stdin: from_tar' workdir id
570+ end
571+ end
572+ end)
593573
594574 let pp_op ~(context :Context.t ) f op =
595575 Fmt. pf f " @[<v2>%s: %a@]" context.workdir Obuilder_spec. pp_op op
0 commit comments