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