| # Copyright 2015 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. | |
| """This tool creates a docker image from a list of layers.""" | |
| # This is the main program to create a docker image. It expect to be run with: | |
| # join_layers --output=output_file \ | |
| # --layer=layer1 [--layer=layer2 ... --layer=layerN] \ | |
| # --id=@identifier \ | |
| # --name=myname --repository=repositoryName | |
| # See the gflags declaration about the flags argument details. | |
| import json | |
| import os.path | |
| import sys | |
| from tools.build_defs.docker import utils | |
| from tools.build_defs.pkg import archive | |
| from third_party.py import gflags | |
| gflags.DEFINE_string('output', None, 'The output file, mandatory') | |
| gflags.MarkFlagAsRequired('output') | |
| gflags.DEFINE_multistring('layer', [], 'The tar files for layers to join.') | |
| gflags.DEFINE_string( | |
| 'id', None, 'The hex identifier of the top layer (hexstring or @filename).') | |
| gflags.DEFINE_string( | |
| 'repository', None, | |
| 'The name of the repository to add this image (use with --id and --name).') | |
| gflags.DEFINE_string( | |
| 'name', None, | |
| 'The symbolic name of this image (use with --id and --repository).') | |
| FLAGS = gflags.FLAGS | |
| def _layer_filter(name): | |
| basename = os.path.basename(name) | |
| return basename not in ('manifest.json', 'top', 'repositories') | |
| def create_image(output, layers, identifier=None, | |
| name=None, repository=None): | |
| """Creates a Docker image from a list of layers. | |
| Args: | |
| output: the name of the docker image file to create. | |
| layers: the layers (tar files) to join to the image. | |
| identifier: the identifier of the top layer for this image. | |
| name: symbolic name for this docker image. | |
| repository: repository name for this docker image. | |
| """ | |
| manifest = [] | |
| tar = archive.TarFileWriter(output) | |
| for layer in layers: | |
| tar.add_tar(layer, name_filter=_layer_filter) | |
| manifest += utils.GetManifestFromTar(layer) | |
| manifest_content = json.dumps(manifest, sort_keys=True) | |
| tar.add_file('manifest.json', content=manifest_content) | |
| # In addition to N layers of the form described above, there might be | |
| # a single file at the top of the image called repositories. | |
| # This file contains a JSON blob of the form: | |
| # { | |
| # 'repo':{ | |
| # 'tag-name': 'top-most layer hex', | |
| # ... | |
| # }, | |
| # ... | |
| # } | |
| if identifier: | |
| # If the identifier is not provided, then the resulted layer will be | |
| # created without a 'top' file. Docker doesn't needs that file nor | |
| # the repository to load the image and for intermediate layer, | |
| # docker_build store the name of the layer in a separate artifact so | |
| # this 'top' file is not needed. | |
| tar.add_file('top', content=identifier) | |
| if repository and name: | |
| tar.add_file('repositories', | |
| content='\n'.join([ | |
| '{', ' "%s": {' % repository, ' "%s": "%s"' % ( | |
| name, identifier), ' }', '}' | |
| ])) | |
| def main(unused_argv): | |
| identifier = FLAGS.id | |
| if identifier and identifier.startswith('@'): | |
| with open(identifier[1:], 'r') as f: | |
| identifier = f.read() | |
| create_image(FLAGS.output, FLAGS.layer, identifier, FLAGS.name, | |
| FLAGS.repository) | |
| if __name__ == '__main__': | |
| main(FLAGS(sys.argv)) |