Skip to content

Commit

Permalink
Introduce a Docker backend for OBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
MisterDA committed Sep 23, 2021
1 parent 64bf212 commit 3f0fc09
Show file tree
Hide file tree
Showing 36 changed files with 1,870 additions and 180 deletions.
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.cmd text eol=crlf
*.bash text eol=lf
*.sh text eol=lf
37 changes: 22 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

OBuilder takes a build script (similar to a Dockerfile) and performs the steps in it in a sandboxed environment.

After each step, OBuild uses the snapshot feature of the filesystem (ZFS or Btrfs) to store the state of the build.
Repeating a build will reuse the cached results where possible.
After each step, OBuilder uses the snapshot feature of the filesystem (ZFS or
Btrfs) to store the state of the build. On Linux, it uses `runc` to sandbox the
build steps, but any system that can run a command safely in a chroot could be
used. Repeating a build will reuse the cached results where possible.

OBuilder aims to be portable, although currently only Linux support is present.
On Linux, it uses `runc` to sandbox the build steps, but any system that can run a command safely in a chroot could be used.
OBuilder can also use Docker as a backend (fully replacing of `runc` and the
snapshotting filesystem) on any system supported by Docker (Linux, Windows, …).

OBuilder stores the log output of each build step.
This is useful for CI, where you may still want to see the output even if the result was cached from some other build.
Expand Down Expand Up @@ -70,7 +72,7 @@ runc then sync operations will instead fail with `EPERM`.

## The build specification language

The spec files are loosly based on the [Dockerfile][] format.
The spec files are loosely based on the [Dockerfile][] format.
The main difference is that the format uses S-expressions rather than a custom format,
which should make it easier to generate and consume it automatically.

Expand Down Expand Up @@ -99,9 +101,9 @@ The initial filesystem snapshot is `BASE`. `run` and `copy` operations create ne
The initial context is supplied by the user (see [build.mli](lib/build.mli) for details).
By default:
- The environment is taken from the Docker configuration of `BASE`.
- The user is `(uid 0) (gid 0)`.
- The workdir is `/`.
- The shell is `/bin/bash -c`.
- The user is `(uid 0) (gid 0)` on Linux, `(name ContainerAdministrator)` on Windows.
- The workdir is `/`, `C:/` on Windows.
- The shell is `/bin/bash -c`, `C:/Windows/System32/cmd.exe` on Windows.

### Multi-stage builds

Expand All @@ -125,7 +127,6 @@ For example:

At the moment, the `(build ...)` items must appear before the `(from ...)` line.


### workdir

```sexp
Expand Down Expand Up @@ -164,7 +165,6 @@ The command run will be this list of arguments followed by the single argument `
(network NETWORK...)?
(secrets SECRET...)?
(shell COMMAND))
```

Examples:
Expand Down Expand Up @@ -205,9 +205,9 @@ the image. Each `SECRET` entry is under the form `(ID (target PATH))`, where `ID
`PATH` is the location of the mounted secret file within the container.
The sandbox context API contains a `secrets` parameter to provide values to the runtime.
If a requested secret isn't provided with a value, the runtime fails.
With the command line interface `obuilder`, use the `--secret ID:PATH` option to provide the path of the file
containing the secret for `ID`.
When used with Docker, make sure to use the **buildkit** syntax, as only buildkit supports a `--secret` option.
Use the `--secret ID:PATH` option to provide the path of the file containing the
secret for `ID`.
When used with Docker, make sure to use the **BuildKit** syntax, as only BuildKit supports a `--secret` option.
(See https://docs.docker.com/develop/develop-images/build_enhancements/#new-docker-build-secret-information)

### copy
Expand Down Expand Up @@ -256,13 +256,20 @@ Notes:

- Both `SRC` and `DST` use `/` as the directory separator on all platforms.

- The copy is currently done by running `tar` inside the container to receive the files.
Therefore, the filesystem must have a working `tar` binary.
- The copy is currently done by running `tar` inside the container to receive
the files. Therefore, the filesystem must have a working `tar` binary. On
Windows when using the Docker backend, OBuilder provides a `tar` binary.

- On Windows, copying from a build step image based on [Nano Server][nanoserver]
isn't supported.

[nanoserver]: https://hub.docker.com/_/microsoft-windows-nanoserver

### user

```sexp
(user (uid UID) (gid GID))
(user (name NAME)) ; on Windows
```

Example:
Expand Down
6 changes: 4 additions & 2 deletions dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@
(fmt (>= 0.8.9))
logs
cmdliner
tar-unix
(tar (>= 1.2))
(tar-unix (>= 1.2))
yojson
sexplib
ppx_deriving
ppx_sexp_conv
sha
sqlite3
(crunch :build)
(obuilder-spec (= :version))
(ocaml (>= 4.10.0))
(ocaml (>= 4.13.0))
(alcotest-lwt :with-test)))
(package
(name obuilder-spec)
Expand Down
16 changes: 8 additions & 8 deletions example.spec
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; This script builds OBuilder itself using a snapshot of the ocaml/opam:debian-10-4.12 base image.
; This script builds OBuilder itself using a snapshot of the ocaml/opam:debian-10-4.13 base image.
;
; Run it from the top-level of the OBuilder source tree, e.g.
;
Expand All @@ -7,11 +7,11 @@
; The result can then be found in /tank/HASH/rootfs/ (where HASH is displayed at the end of the build).

((build dev
((from ocaml/opam@sha256:116c960addbbda19190d47b49e42310916cf42fe432fa5e37eb6104c488218d6)
(workdir /src)
((from ocaml/opam@sha256:236175bf94d96d8b93d8ead26f0f7dc942ec7dcd4a5ed53b9c37982c85f24f61)
(workdir /project)
(user (uid 1000) (gid 1000)) ; Build as the "opam" user
(run (shell "sudo chown opam /src"))
(env OPAM_HASH "de818e3f460f5fd30db492fc65de68d0957dfdd1") ; Fix the version of opam-repository we want
(run (shell "sudo chown opam /project"))
(env OPAM_HASH "bf04be33f42bfc33931df57adea016f1fa259d7a") ; Fix the version of opam-repository we want
(run
(network host)
(shell
Expand All @@ -35,15 +35,15 @@
(shell "opam install --deps-only -t obuilder"))
(copy ; Copy the rest of the source code
(src .)
(dst /src/)
(exclude .git _build))
(dst /project/)
(exclude .git _build _opam))
(run (shell "opam exec -- dune build @install @runtest")))) ; Build and test
; Now generate a small runtime image with just the resulting binary:
(from debian:10)
(run
(network host)
(shell "apt-get update && apt-get install -y libsqlite3-0 --no-install-recommends"))
(copy (from (build dev))
(src /src/_build/default/main.exe)
(src /project/_build/default/main.exe)
(dst /usr/local/bin/obuilder))
(run (shell "obuilder --help")))
7 changes: 5 additions & 2 deletions lib/btrfs_store.ml
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,11 @@ let cache ~user t name : (string * (unit -> unit Lwt.t)) Lwt.t =
(* Create writeable clone. *)
let gen = cache.gen in
Btrfs.subvolume_snapshot `RW ~src:snapshot tmp >>= fun () ->
let { Obuilder_spec.uid; gid } = user in
Os.sudo ["chown"; Printf.sprintf "%d:%d" uid gid; tmp] >>= fun () ->
begin match user with
| `Unix { Obuilder_spec.uid; gid } ->
Os.sudo ["chown"; Printf.sprintf "%d:%d" uid gid; tmp]
| _ -> assert false
end >>= fun () ->
let release () =
Lwt_mutex.with_lock cache.lock @@ fun () ->
begin
Expand Down
Loading

0 comments on commit 3f0fc09

Please sign in to comment.