aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/build_defs/docker/join_layers.py
blob: 8ed26c8d34599ad9308e224731b08dfa3105f7e3 (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
# 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))