| """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", | 
 |         ], | 
 |     ) | 
 |  | 
 |     # 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, 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 //...". | 
 |     ) |