Skip to content

Commit

Permalink
Adding Basic Support for Windows Docker Images (#493)
Browse files Browse the repository at this point in the history
container_import
Added a parameter called “manifest”. This allows Bazel to import a Windows base image without checking in GBs of tar files. All that is needed is the config and manifest files from the base Windows image. The layers parameter should be an empty list.

container_import(
name = "windowsservercore_1803",
config = "windowsservercore.1803.config.json",
manifest = "windowsservercore.1803.manifest.json",
layers = []
)

container_image
Added an optional parameter to docker_build called “operating_system.” While most of the changes are foreign layer specific, two changes were required to address Windows specifically.

There is no support for adding registry diffs, but diffs in existing layers will be respected and continue to work.

container_image(
name = "basic_windows_image",
base = ":import_windows_base_image",
cmd = ['bar.exe'],
files = [":bar.exe"]
operating_system = "windows",
)

* Adding basic support for Windows Docker images.
Does not support adding new registry delta's, but will work with
base images that already have registry deltas.

* Changing container_import to use
application/vnd.docker.distribution.manifest.v2+json instead of
application/vnd.docker.distribution.manifest.list.v2+json as the input.

* Using archive.py from http_archive.  To be removed when bazel 0.17,0 is released
  • Loading branch information
nowens03 authored and nlopezgi committed Aug 16, 2018
1 parent a25094d commit e9e2809
Show file tree
Hide file tree
Showing 16 changed files with 435 additions and 71 deletions.
2 changes: 1 addition & 1 deletion container/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ py_binary(
srcs_version = "PY2AND3",
visibility = ["//visibility:public"],
deps = [
"@bazel_source//tools/build_defs/pkg:archive",
"@bazel_tools//third_party/py/gflags",
"@bazel_tools//tools/build_defs/pkg:archive",
],
)

Expand Down
43 changes: 39 additions & 4 deletions container/build_tar.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import tarfile
import tempfile

from tools.build_defs.pkg import archive
from bazel_source.tools.build_defs.pkg import archive
from third_party.py import gflags

gflags.DEFINE_string('output', None, 'The output file, mandatory')
Expand All @@ -39,6 +39,13 @@
gflags.DEFINE_string(
'mode', None, 'Force the mode on the added files (in octal).')

gflags.DEFINE_multistring(
'empty_root_dir',
[],
'An empty root directory to add to the layer. This will create a directory that'
'is a peer of "root_directory". "empty_dir" creates an empty directory inside of'
'"root_directory"')

gflags.DEFINE_multistring('tar', [], 'A tar file to add to the layer')

gflags.DEFINE_multistring('deb', [], 'A debian package to add to the layer')
Expand Down Expand Up @@ -77,6 +84,10 @@
'Specify the owner names of individual files, e.g. '
'path/to/file=root.root.')

gflags.DEFINE_string(
'root_directory', './', 'Default root directory is named "."'
'Windows docker images require this be named "Files" instead of "."')

FLAGS = gflags.FLAGS


Expand All @@ -98,13 +109,18 @@ def parse_pkg_name(metadata, filename):
else:
return os.path.basename(os.path.splitext(filename)[0])

def __init__(self, output, directory, compression):
def __init__(self, output, directory, compression, root_directory):
self.directory = directory
self.output = output
self.compression = compression
self.root_directory = root_directory

def __enter__(self):
self.tarfile = archive.TarFileWriter(self.output, self.compression)
self.tarfile = archive.TarFileWriter(
self.output,
self.compression,
self.root_directory
)
return self

def __exit__(self, t, v, traceback):
Expand Down Expand Up @@ -188,6 +204,23 @@ def add_empty_dir(self, destpath, mode=None, ids=None, names=None):
self.add_empty_file(destpath, mode=mode, ids=ids, names=names,
kind=tarfile.DIRTYPE)

def add_empty_root_dir(self, destpath, mode=None, ids=None, names=None):
"""Add a directory to the root of the tar file.
Args:
destpath: the name of the directory in the layer
mode: force to set the specified mode, defaults to 644
ids: (uid, gid) for the file to set ownership
names: (username, groupname) for the file to set ownership.
An empty directory will be created as `destfile` in the root layer.
"""
original_root_directory = self.tarfile.root_directory
self.tarfile.root_directory = destpath
self.add_empty_dir(
destpath, mode=mode, ids=ids, names=names)
self.tarfile.root_directory = original_root_directory

def add_tar(self, tar):
"""Merge a tar file into the destination tar file.
Expand Down Expand Up @@ -358,7 +391,7 @@ def main(unused_argv):
ids_map[f] = (int(user), int(group))

# Add objects to the tar file
with TarFile(FLAGS.output, FLAGS.directory, FLAGS.compression) as output:
with TarFile(FLAGS.output, FLAGS.directory, FLAGS.compression, FLAGS.root_directory) as output:
def file_attributes(filename):
if filename[0] == '/':
filename = filename[1:]
Expand All @@ -375,6 +408,8 @@ def file_attributes(filename):
output.add_empty_file(f, **file_attributes(f))
for f in FLAGS.empty_dir:
output.add_empty_dir(f, **file_attributes(f))
for f in FLAGS.empty_root_dir:
output.add_empty_root_dir(f, **file_attributes(f))
for tar in FLAGS.tar:
output.add_tar(tar)
for deb in FLAGS.deb:
Expand Down
16 changes: 12 additions & 4 deletions container/container.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ container = struct(
)

# The release of the github.com/google/containerregistry to consume.
CONTAINERREGISTRY_RELEASE = "v0.0.28"
CONTAINERREGISTRY_RELEASE = "v0.0.30"

_local_tool_build_template = """
sh_binary(
Expand Down Expand Up @@ -69,7 +69,7 @@ def repositories():
name = "puller",
urls = [("https://storage.googleapis.com/containerregistry-releases/" +
CONTAINERREGISTRY_RELEASE + "/puller.par")],
sha256 = "c834a311a1d2ade959c38c262dfead3b180ba022d196c4a96453d4bfa01e83da",
sha256 = "89a7c48df0fd5fb839d452599cc054a6550c18563394d4401428ab2e094d4f0b",
executable = True,
)

Expand All @@ -78,7 +78,7 @@ def repositories():
name = "importer",
urls = [("https://storage.googleapis.com/containerregistry-releases/" +
CONTAINERREGISTRY_RELEASE + "/importer.par")],
sha256 = "19643df59bb1dc750e97991e7071c601aa2debe94f6ad72e5f23ab8ae77da46f",
sha256 = "3c1f299df498b0712386c52e1eb5499e00d58143ae10fc4b5c12bf0deffb55b6",
executable = True,
)

Expand All @@ -87,10 +87,18 @@ def repositories():
name = "containerregistry",
urls = [("https://github.com/google/containerregistry/archive/" +
CONTAINERREGISTRY_RELEASE + ".tar.gz")],
sha256 = "07b9d06e46a9838bef712116bbda7e094ede37be010c1f8c0a3f32f2eeca6384",
sha256 = "10fb9ffa1dde14c81f5c12593666bf1d9e9f53727b8cda9abeb0012d08e57fd1",
strip_prefix = "containerregistry-" + CONTAINERREGISTRY_RELEASE[1:],
)

# TODO(nichow): Remove after bazel 0.17.0 is released
if "bazel_source" not in excludes:
http_archive(
name = "bazel_source",
urls = [("https://releases.bazel.build/0.17.0/rc1/bazel-0.17.0rc1-dist.zip")],
sha256 = "a9afd2b16a21085bd6c0a70a23acce30b105a8af3a7b3c92a4b83bea6b623fd8",
)

# TODO(mattmoor): Remove all of this (copied from google/containerregistry)
# once transitive workspace instantiation lands.

Expand Down
25 changes: 21 additions & 4 deletions container/create_image_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,15 @@
parser.add_argument('--base', action='store',
help='The parent image.')

parser.add_argument('--basemanifest', action='store',
help='The parent image manifest.')

parser.add_argument('--output', action='store', required=True,
help='The output file to generate.')

parser.add_argument('--manifestoutput', action='store', required=False,
help='The manifest output file to generate.')

parser.add_argument('--layer', action='append', default=[],
help='Layer sha256 hashes that make up this image')

Expand Down Expand Up @@ -76,10 +82,11 @@
parser.add_argument('--null_cmd', action='store', default=False,
help='If True, "Cmd" will be set to null.')

_PROCESSOR_ARCHITECTURE = 'amd64'

_OPERATING_SYSTEM = 'linux'
parser.add_argument('--operating_system', action='store', default='linux',
choices=['linux', 'windows'],
help=('Operating system to create docker image for, e.g. {linux}'))

_PROCESSOR_ARCHITECTURE = 'amd64'

def KeyValueToDict(pair):
"""Converts an iterable object of key=value pairs to dictionary."""
Expand Down Expand Up @@ -123,6 +130,12 @@ def Stamp(inp):
base_json = r.read()
data = json.loads(base_json)

base_manifest_json = '{}'
if args.basemanifest:
with open(args.basemanifest, 'r') as r:
base_manifest_json = r.read()
manifestdata = json.loads(base_manifest_json)

layers = []
for layer in args.layer:
layers.append(utils.ExtractValue(layer))
Expand Down Expand Up @@ -172,7 +185,7 @@ def Stamp(inp):
},
ports=args.ports, volumes=args.volumes, workdir=Stamp(args.workdir)),
architecture=_PROCESSOR_ARCHITECTURE,
operating_system=_OPERATING_SYSTEM)
operating_system=args.operating_system)

if ('config' in output and 'Cmd' in output['config'] and
args.null_cmd == "True"):
Expand All @@ -186,6 +199,10 @@ def Stamp(inp):
json.dump(output, fp, sort_keys=True)
fp.write('\n')

if (args.manifestoutput):
with open(args.manifestoutput, 'w') as fp:
json.dump(manifestdata, fp, sort_keys=False)
fp.write('\n')

if __name__ == '__main__':
main()
4 changes: 4 additions & 0 deletions container/extract_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
parser.add_argument('--output', action='store', required=True,
help='The output file to which we write the config.')

parser.add_argument('--manifestoutput', action='store', required=True,
help='The output file to which we write the manifest.')

# Main program to create a docker image. It expect to be run with:
# extract_config --tarball=image.tar \
Expand All @@ -37,6 +39,8 @@ def main():
with docker_image.FromTarball(args.tarball) as img:
with open(args.output, 'w') as f:
f.write(img.config_file())
with open(args.manifestoutput, 'w') as f:
f.write(img.manifest())


if __name__ == '__main__':
Expand Down
Loading

0 comments on commit e9e2809

Please sign in to comment.