|  | """This module defines the skia_app_container macro.""" | 
|  |  | 
|  | load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_push") | 
|  | load("@io_bazel_rules_docker//docker/util:run.bzl", "container_run_and_commit") | 
|  | load("@rules_pkg//:pkg.bzl", "pkg_tar") | 
|  |  | 
|  | def skia_app_container( | 
|  | name, | 
|  | repository, | 
|  | dirs, | 
|  | entrypoint = "", | 
|  | run_commands_root = None, | 
|  | run_commands_skia = None, | 
|  | base_image = "@basealpine//image", | 
|  | env = None, | 
|  | default_user = "skia"): | 
|  | """Builds a Docker container for a Skia app, and generates a target to push it to GCR. | 
|  |  | 
|  | This macro produces the following: | 
|  | * "<name>" target to build the Docker container with skia as default user. | 
|  | * "<name>_run_root" target to execute run commands as root on the image. | 
|  | root will be the default user here. Will be created only | 
|  | if run_commands_root is specified. | 
|  | * "<name>_run_skia" target to execute run commands as the "skia" user on the image. | 
|  | Will be created only if run_commands_skia is specified. | 
|  | * "push_<name>" target to push the container to GCR. | 
|  | * "pushk_<name>" target to push the container to GCR, and deploy <name> to production via pushk. | 
|  |  | 
|  | Example: | 
|  |  | 
|  | ``` | 
|  | # //myapp/BUILD.bazel | 
|  |  | 
|  | load("//bazel:skia_app_container.bzl", "skia_app_container") | 
|  |  | 
|  | skia_app_container( | 
|  | name = "myapp", | 
|  | dirs = { | 
|  | "/usr/local/bin/myapp": [ | 
|  | ["//myapp/go:mybinary", 755"], | 
|  | ], | 
|  | "/usr/local/share/myapp": [ | 
|  | ["//myapp/config:config.cfg", "644"], | 
|  | ["//myapp/data:data.json", "644"], | 
|  | ], | 
|  | }, | 
|  | entrypoint = "/usr/local/bin/myapp/mybinary", | 
|  | repository = "skia-public/myapp", | 
|  | ) | 
|  | ``` | 
|  |  | 
|  | The above example will produce a Docker container based on gcr.io/skia-public/basealpine with | 
|  | the following contents: | 
|  |  | 
|  | - /usr/local/bin/myapp/mybinary (mode: 755) | 
|  | - /usr/local/share/myapp/config.cfg (mode: 644) | 
|  | - /usr/local/share/myapp/data.json (mode: 644) | 
|  |  | 
|  | To build the container and load it into Docker: | 
|  |  | 
|  | ``` | 
|  | $ bazel run //myapp:myapp | 
|  | ... | 
|  | Loaded image ID: sha256:c0decafe | 
|  | Tagging c0decafe as bazel/myapp:myapp | 
|  | ``` | 
|  |  | 
|  | To debug the container locally: | 
|  |  | 
|  | ``` | 
|  | $ docker run bazel/myapp:myapp | 
|  | $ docker run -it --entrypoint /bin/sh bazel/myapp:myapp | 
|  | ``` | 
|  |  | 
|  | To push the container to GCR: | 
|  |  | 
|  | ``` | 
|  | $ bazel run //myapp:push_myapp | 
|  | ... | 
|  | Successfully pushed Docker image to gcr.io/skia-public/myapp:... | 
|  | ``` | 
|  |  | 
|  | To push the app to production (assuming the app is compatible with pushk): | 
|  |  | 
|  | ``` | 
|  | $ bazel run //myapp:pushk_myapp | 
|  | ``` | 
|  |  | 
|  | Which is equivalent to: | 
|  |  | 
|  | ``` | 
|  | $ bazel run //myapp:push_myapp | 
|  | $ pushk myapp | 
|  | ``` | 
|  |  | 
|  | Args: | 
|  | name: Name of the rule. | 
|  | repository: Name of the repository under gcr.io. | 
|  | dirs: Contents of the container, expressed as a dictionary where the keys are directory names | 
|  | within the container (e.g. "/usr/local/share/myapp"), and the values are an array of | 
|  | [Bazel label, mode] tuples indicating which files should be copied into the directory (e.g. | 
|  | ["//myapp/go:mybinary", "755"]). | 
|  | entrypoint: The entrypoint of the container, which can be a string or an array (e.g. | 
|  | "/usr/local/share/myapp/mybinary", or ["/usr/local/share/myapp/mybinary", "--someflag"]). | 
|  | Optional. | 
|  | run_commands_root: The RUN commands that should be executed on the container by the root | 
|  | user. Optional. | 
|  | run_commands_skia: The RUN commands that should be executed on the container by the skia | 
|  | user. Optional. | 
|  | base_image: The image to base the container_image on. Optional. | 
|  | env: A {"var": "val"} dictionary with the environment variables to use when building the | 
|  | container. Optional. | 
|  | default_user: The user the container will be run with. Defaults to "skia" but some apps | 
|  | like skfe requires the default user to be "root". | 
|  | """ | 
|  |  | 
|  | # According to the container_image rule's docs[1], the recommended way to place files in | 
|  | # specific directories is via the pkg_tar rule. | 
|  | # | 
|  | # The below loop creates one pkg_tar rule for each file in the container. | 
|  | # | 
|  | # [1] https://github.com/bazelbuild/rules_docker/blob/454981e65fa100d37b19210ee85fedb2f7af9626/README.md#container_image | 
|  | pkg_tars = [] | 
|  | i = 0 | 
|  | for dir in dirs: | 
|  | for file, mode in dirs[dir]: | 
|  | pkg_tar_name = name + "_pkg_tar_" + str(i) | 
|  | i += 1 | 
|  | pkg_tars.append(pkg_tar_name) | 
|  |  | 
|  | pkg_tar( | 
|  | name = pkg_tar_name, | 
|  | srcs = [file], | 
|  | package_dir = dir, | 
|  | mode = mode, | 
|  | tags = ["manual"],  # Exclude it from wildcard queries, e.g. "bazel build //...". | 
|  | ) | 
|  |  | 
|  | image_name = (name + "_base") if (run_commands_root or run_commands_skia) else name | 
|  |  | 
|  | container_image( | 
|  | name = image_name, | 
|  | base = base_image, | 
|  |  | 
|  | # We cannot use an entrypoint with the container_run_and_commit rule | 
|  | # required when run_commands_root or run_commands_skia is specified, | 
|  | # because the commands we want to execute do not require a specific | 
|  | # entrypoint. | 
|  | # We will set the entrypoint back after the container_run_and_commit | 
|  | # rule is executed. | 
|  | entrypoint = None if (run_commands_root or run_commands_skia) else [entrypoint], | 
|  | tars = pkg_tars, | 
|  | user = default_user, | 
|  | tags = ["manual"],  # Exclude it from wildcard queries, e.g. "bazel build //...". | 
|  | env = env, | 
|  | ) | 
|  |  | 
|  | if run_commands_root: | 
|  | rule_name = name + "_run_root" | 
|  | container_run_and_commit( | 
|  | name = rule_name, | 
|  | commands = run_commands_root, | 
|  | docker_run_flags = ["--user", "root"], | 
|  | image = image_name + ".tar", | 
|  | tags = [ | 
|  | "manual",  # Exclude it from wildcard queries, e.g. "bazel build //...". | 
|  | # container_run_and_commit requires the docker daemon to be | 
|  | # running. This is not possible inside RBE. | 
|  | "no-remote", | 
|  | ], | 
|  | ) | 
|  | image_name = ":" + rule_name + "_commit.tar" | 
|  |  | 
|  | if run_commands_skia: | 
|  | rule_name = name + "_run_skia" | 
|  | container_run_and_commit( | 
|  | name = rule_name, | 
|  | commands = run_commands_skia, | 
|  | docker_run_flags = ["--user", "skia"], | 
|  | # If run_commands_root was specified then the image_name already contains | 
|  | # ".tar" suffix. Make sure we do not add a double ".tar" suffix here. | 
|  | image = image_name if image_name.endswith(".tar") else image_name + ".tar", | 
|  | tags = [ | 
|  | "manual",  # Exclude it from wildcard queries, e.g. "bazel build //...". | 
|  | # container_run_and_commit requires the docker daemon to be | 
|  | # running. This is not possible inside RBE. | 
|  | "no-remote", | 
|  | ], | 
|  | ) | 
|  | image_name = ":" + rule_name + "_commit.tar" | 
|  |  | 
|  | if run_commands_root or run_commands_skia: | 
|  | # If run_commands_root was specified then it's container_run_and_commit | 
|  | # sets root as the default user and overrides the entrypoint. | 
|  | # If run_commands_skia was specified then it overrides the entrypoint. | 
|  | # | 
|  | # Now execute container_image using the previous image as base to set | 
|  | # back skia as the default user and to set back the original entrypoint. | 
|  | rule_name = name | 
|  | container_image( | 
|  | name = rule_name, | 
|  | base = image_name, | 
|  | entrypoint = [entrypoint], | 
|  | user = default_user, | 
|  | tags = ["manual"],  # Exclude it from wildcard queries, e.g. "bazel build //...". | 
|  | env = env, | 
|  | ) | 
|  | image_name = ":" + rule_name | 
|  |  | 
|  | container_push( | 
|  | name = "push_" + name, | 
|  | format = "Docker", | 
|  | image = image_name, | 
|  | registry = "gcr.io", | 
|  | repository = repository, | 
|  | stamp = "@io_bazel_rules_docker//stamp:always", | 
|  | tag = "{STABLE_DOCKER_TAG}", | 
|  | tags = [ | 
|  | "manual",  # Exclude it from wildcard queries, e.g. "bazel build //...". | 
|  | # container_push requires the docker daemon to be | 
|  | # running. This is not possible inside RBE. | 
|  | "no-remote", | 
|  | ], | 
|  | ) | 
|  |  | 
|  | # pushk expects the second half of the repository name as an argument. | 
|  | pushk_image_name = repository.split("/")[1] | 
|  |  | 
|  | # The container_push rule outputs two files: <name>, which is a script that uploads the | 
|  | # container to GCR, and <name>.digest, which contains the SHA256 digest of the container. | 
|  | # | 
|  | # Because the container_push rule outputs multiple files, we cannot use $$(rootpath push_<name>) | 
|  | # to get the path to <name>, so we use $$(rootpaths push_<name>), which returns the list of all | 
|  | # output files, then take the base directory of an arbitrary file, and append <name> to it to | 
|  | # get the path to the desired script. | 
|  | pushk_script = "\n".join([ | 
|  | "container_push_outputs=($(rootpaths push_%s))", | 
|  | "container_push_base_dir=$$(dirname $${container_push_outputs[0]})", | 
|  | "container_push_script=$${container_push_base_dir}/push_%s", | 
|  | "", | 
|  | "$$container_push_script && $(rootpath //kube/go/pushk) --use-temp-checkout %s", | 
|  | ]) % (name, name, pushk_image_name) | 
|  |  | 
|  | native.genrule( | 
|  | name = "gen_pushk_" + name, | 
|  | srcs = [ | 
|  | "push_" + name, | 
|  | "//kube/go/pushk", | 
|  | ], | 
|  | outs = ["pushk_%s.sh" % name], | 
|  | cmd = "echo '%s' > $@" % pushk_script, | 
|  | tags = ["manual"],  # Exclude it from wildcard queries, e.g. "bazel build //...". | 
|  | ) | 
|  |  | 
|  | native.sh_binary( | 
|  | name = "pushk_" + name, | 
|  | srcs = ["gen_pushk_" + name], | 
|  | data = [ | 
|  | "push_" + name, | 
|  | "//kube/go/pushk", | 
|  | ], | 
|  | tags = ["manual"],  # Exclude it from wildcard queries, e.g. "bazel build //...". | 
|  | ) |