aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/build_defs/docker/bundle.bzl
blob: f2ad4139ebe9c570790e6611f98a5240e7d422e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# Copyright 2017 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Rule for bundling Docker images into a tarball."""

load(":label.bzl", _string_to_label = "string_to_label")
load(
    ":layers.bzl",
    _assemble_image = "assemble",
    _get_layers = "get_from_target",
    _incr_load = "incremental_load",
    _layer_tools = "tools",
)
load(":list.bzl", "reverse")

def _docker_bundle_impl(ctx):
    """Implementation for the docker_bundle rule."""

    # Compute the set of layers from the image_targes.
    image_target_dict = _string_to_label(
        ctx.attr.image_targets,
        ctx.attr.image_target_strings,
    )

    seen_names = []
    layers = []
    for image in ctx.attr.image_targets:
        # TODO(mattmoor): Add support for naked tarballs.
        for layer in _get_layers(ctx, image):
            if layer["name"].path in seen_names:
                continue
            seen_names.append(layer["name"].path)
            layers.append(layer)

    images = dict()
    for unresolved_tag in ctx.attr.images:
        # Allow users to put make variables into the tag name.
        tag = ctx.expand_make_variables("images", unresolved_tag, {})

        target = ctx.attr.images[unresolved_tag]
        target = image_target_dict[target]
        images[tag] = _get_layers(ctx, target)[0]

    _incr_load(ctx, layers, images, ctx.outputs.executable)

    _assemble_image(ctx, reverse(layers), {
        # Create a new dictionary with the same keyspace that
        # points to the name of the layer.
        k: images[k]["name"]
        for k in images
    }, ctx.outputs.out)

    runfiles = ctx.runfiles(
        files = ([l["name"] for l in layers] +
                 [l["id"] for l in layers] +
                 [l["layer"] for l in layers]),
    )

    return struct(
        runfiles = runfiles,
        files = depset(),
    )

docker_bundle_ = rule(
    implementation = _docker_bundle_impl,
    attrs = dict({
        "images": attr.string_dict(),
        # Implicit dependencies.
        "image_targets": attr.label_list(allow_files = True),
        "image_target_strings": attr.string_list(),
    }.items() + _layer_tools.items()),
    outputs = {
        "out": "%{name}.tar",
    },
    executable = True,
)

# Produces a new docker image tarball compatible with 'docker load', which
# contains the N listed 'images', each aliased with their key.
#
# Example:
#   docker_bundle(
#     name = "foo",
#     images = {
#       "ubuntu:latest": ":blah",
#       "foo.io/bar:canary": "//baz:asdf",
#     }
#   )
def docker_bundle(**kwargs):
    """Package several docker images into a single tarball.

    Args:
      **kwargs: See above.
    """
    for reserved in ["image_targets", "image_target_strings"]:
        if reserved in kwargs:
            fail("reserved for internal use by docker_bundle macro", attr = reserved)

    if "images" in kwargs:
        kwargs["image_targets"] = kwargs["images"].values()
        kwargs["image_target_strings"] = kwargs["images"].values()

    docker_bundle_(**kwargs)