blob: f2ad4139ebe9c570790e6611f98a5240e7d422e6 [file] [log] [blame]
# 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)