xref: /aosp_15_r20/external/skia/bazel/skia_app_container.bzl (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1"""This module defines the skia_app_container macro."""
2
3load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_push")
4load("@io_bazel_rules_docker//docker/util:run.bzl", "container_run_and_commit")
5load("@rules_pkg//:pkg.bzl", "pkg_tar")
6
7def skia_app_container(
8        name,
9        repository,
10        dirs,
11        entrypoint = "",
12        run_commands_root = None,
13        run_commands_skia = None,
14        base_image = "@basealpine//image",
15        env = None,
16        default_user = "skia"):
17    """Builds a Docker container for a Skia app, and generates a target to push it to GCR.
18
19    This macro produces the following:
20    * "<name>" target to build the Docker container with skia as default user.
21    * "<name>_run_root" target to execute run commands as root on the image.
22                        root will be the default user here. Will be created only
23                        if run_commands_root is specified.
24    * "<name>_run_skia" target to execute run commands as the "skia" user on the image.
25                        Will be created only if run_commands_skia is specified.
26    * "push_<name>" target to push the container to GCR.
27
28    Example:
29
30    ```
31        # //myapp/BUILD.bazel
32
33        load("//bazel:skia_app_container.bzl", "skia_app_container")
34
35        skia_app_container(
36            name = "myapp",
37            dirs = {
38                "/usr/local/bin/myapp": [
39                    ["//myapp/go:mybinary", 755"],
40                ],
41                "/usr/local/share/myapp": [
42                    ["//myapp/config:config.cfg", "644"],
43                    ["//myapp/data:data.json", "644"],
44                ],
45            },
46            entrypoint = "/usr/local/bin/myapp/mybinary",
47            repository = "skia-public/myapp",
48        )
49    ```
50
51    The above example will produce a Docker container based on gcr.io/skia-public/basealpine with
52    the following contents:
53
54      - /usr/local/bin/myapp/mybinary (mode: 755)
55      - /usr/local/share/myapp/config.cfg (mode: 644)
56      - /usr/local/share/myapp/data.json (mode: 644)
57
58    To build the container and load it into Docker:
59
60    ```
61        $ bazel run //myapp:myapp
62        ...
63        Loaded image ID: sha256:c0decafe
64        Tagging c0decafe as bazel/myapp:myapp
65    ```
66
67    To debug the container locally:
68
69    ```
70        $ docker run bazel/myapp:myapp
71        $ docker run -it --entrypoint /bin/sh bazel/myapp:myapp
72    ```
73
74    To push the container to GCR:
75
76    ```
77        $ bazel run //myapp:push_myapp
78        ...
79        Successfully pushed Docker image to gcr.io/skia-public/myapp:...
80    ```
81
82    Args:
83      name: Name of the rule.
84      repository: Name of the repository under gcr.io.
85      dirs: Contents of the container, expressed as a dictionary where the keys are directory names
86        within the container (e.g. "/usr/local/share/myapp"), and the values are an array of
87        [Bazel label, mode] tuples indicating which files should be copied into the directory (e.g.
88        ["//myapp/go:mybinary", "755"]).
89      entrypoint: The entrypoint of the container, which can be a string or an array (e.g.
90        "/usr/local/share/myapp/mybinary", or ["/usr/local/share/myapp/mybinary", "--someflag"]).
91        Optional.
92      run_commands_root: The RUN commands that should be executed on the container by the root
93        user. Optional.
94      run_commands_skia: The RUN commands that should be executed on the container by the skia
95        user. Optional.
96      base_image: The image to base the container_image on. Optional.
97      env: A {"var": "val"} dictionary with the environment variables to use when building the
98        container. Optional.
99      default_user: The user the container will be run with. Defaults to "skia" but some apps
100        like skfe requires the default user to be "root".
101    """
102
103    # According to the container_image rule's docs[1], the recommended way to place files in
104    # specific directories is via the pkg_tar rule.
105    #
106    # The below loop creates one pkg_tar rule for each file in the container.
107    #
108    # [1] https://github.com/bazelbuild/rules_docker/blob/454981e65fa100d37b19210ee85fedb2f7af9626/README.md#container_image
109    pkg_tars = []
110    i = 0
111    for dir in dirs:
112        for file, mode in dirs[dir]:
113            pkg_tar_name = name + "_pkg_tar_" + str(i)
114            i += 1
115            pkg_tars.append(pkg_tar_name)
116
117            pkg_tar(
118                name = pkg_tar_name,
119                srcs = [file],
120                package_dir = dir,
121                mode = mode,
122                tags = ["manual"],  # Exclude it from wildcard queries, e.g. "bazel build //...".
123            )
124
125    image_name = (name + "_base") if (run_commands_root or run_commands_skia) else name
126
127    container_image(
128        name = image_name,
129        base = base_image,
130
131        # We cannot use an entrypoint with the container_run_and_commit rule
132        # required when run_commands_root or run_commands_skia is specified,
133        # because the commands we want to execute do not require a specific
134        # entrypoint.
135        # We will set the entrypoint back after the container_run_and_commit
136        # rule is executed.
137        entrypoint = None if (run_commands_root or run_commands_skia) else [entrypoint],
138        tars = pkg_tars,
139        user = default_user,
140        tags = ["manual"],  # Exclude it from wildcard queries, e.g. "bazel build //...".
141        env = env,
142    )
143
144    if run_commands_root:
145        rule_name = name + "_run_root"
146        container_run_and_commit(
147            name = rule_name,
148            commands = run_commands_root,
149            docker_run_flags = ["--user", "root"],
150            image = image_name + ".tar",
151            tags = [
152                "manual",  # Exclude it from wildcard queries, e.g. "bazel build //...".
153                # container_run_and_commit requires the docker daemon to be
154                # running. This is not possible inside RBE.
155                "no-remote",
156            ],
157        )
158        image_name = ":" + rule_name + "_commit.tar"
159
160    if run_commands_skia:
161        rule_name = name + "_run_skia"
162        container_run_and_commit(
163            name = rule_name,
164            commands = run_commands_skia,
165            docker_run_flags = ["--user", "skia"],
166            # If run_commands_root was specified then the image_name already contains
167            # ".tar" suffix. Make sure we do not add a double ".tar" suffix here.
168            image = image_name if image_name.endswith(".tar") else image_name + ".tar",
169            tags = [
170                "manual",  # Exclude it from wildcard queries, e.g. "bazel build //...".
171                # container_run_and_commit requires the docker daemon to be
172                # running. This is not possible inside RBE.
173                "no-remote",
174            ],
175        )
176        image_name = ":" + rule_name + "_commit.tar"
177
178    if run_commands_root or run_commands_skia:
179        # If run_commands_root was specified then it's container_run_and_commit
180        # sets root as the default user and overrides the entrypoint.
181        # If run_commands_skia was specified then it overrides the entrypoint.
182        #
183        # Now execute container_image using the previous image as base to set
184        # back skia as the default user and to set back the original entrypoint.
185        rule_name = name
186        container_image(
187            name = rule_name,
188            base = image_name,
189            entrypoint = [entrypoint],
190            user = default_user,
191            tags = ["manual"],  # Exclude it from wildcard queries, e.g. "bazel build //...".
192            env = env,
193        )
194        image_name = ":" + rule_name
195
196    container_push(
197        name = "push_" + name,
198        format = "Docker",
199        image = image_name,
200        registry = "gcr.io",
201        repository = repository,
202        stamp = "@io_bazel_rules_docker//stamp:always",
203        tag = "{STABLE_DOCKER_TAG}",
204        tags = [
205            "manual",  # Exclude it from wildcard queries, e.g. "bazel build //...".
206            # container_push requires the docker daemon to be
207            # running. This is not possible inside RBE.
208            "no-remote",
209        ],
210    )
211