| # 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 os.path |
| import sys |
| |
| from tools.build_defs.docker 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 --repsoitory).') |
| |
| FLAGS = gflags.FLAGS |
| |
| |
| def _layer_filter(name): |
| """Ignore files 'top' and 'repositories' when merging layers.""" |
| basename = os.path.basename(name) |
| return basename not in ('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. |
| """ |
| tar = archive.TarFileWriter(output) |
| for layer in layers: |
| tar.add_tar(layer, name_filter=_layer_filter) |
| # 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)) |