This repository was archived by the owner on Jun 4, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 115
/
Copy pathfast_pusher_.py
executable file
·162 lines (125 loc) · 5.79 KB
/
fast_pusher_.py
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# Copyright 2017 Google Inc. 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 package pushes images to a Docker Registry.
The format this tool *expects* to deal with is (unlike docker_pusher)
proprietary, however, unlike {fast,docker}_puller the signature of this tool is
compatible with docker_pusher.
"""
import argparse
import logging
from containerregistry.client import docker_creds
from containerregistry.client import docker_name
from containerregistry.client.v2_2 import docker_image as v2_2_image
from containerregistry.client.v2_2 import docker_session
from containerregistry.client.v2_2 import oci_compat
from containerregistry.tools import logging_setup
from containerregistry.tools import patched
from containerregistry.transport import transport_pool
import httplib2
parser = argparse.ArgumentParser(
description='Push images to a Docker Registry, faaaaaast.')
parser.add_argument('--name', action='store',
help=('The name of the docker image to push.'))
# The name of this flag was chosen for compatibility with docker_pusher.py
parser.add_argument('--tarball', action='store',
help='An optional legacy base image tarball.')
parser.add_argument('--config', action='store',
help='The path to the file storing the image config.')
parser.add_argument('--digest', action='append',
help='The list of layer digest filenames in order.')
parser.add_argument('--layer', action='append',
help='The list of layer filenames in order.')
parser.add_argument('--stamp-info-file', action='append', required=False,
help=('A list of files from which to read substitutions '
'to make in the provided --name, e.g. {BUILD_USER}'))
parser.add_argument('--oci', action='store_true',
help='Push the image with an OCI Manifest.')
parser.add_argument('--certificates', nargs='*', help='A comma separated ' +
'tuple of key file, cert, and domain. (From httplib2 ' +
'docs) Add a key and cert that will be used for an SSL ' +
'connection to the specified domain. keyfile is the name ' +
'of a PEM formatted file that contains your private key. ' +
'certfile is a PEM formatted certificate chain file.')
_THREADS = 8
def Tag(name, files):
"""Perform substitutions in the provided tag name."""
format_args = {}
for infofile in files or []:
with open(infofile) as info:
for line in info:
line = line.strip('\n')
key, value = line.split(' ', 1)
if key in format_args:
print ('WARNING: Duplicate value for key "%s": '
'using "%s"' % (key, value))
format_args[key] = value
formatted_name = name.format(**format_args)
if files:
print('{name} was resolved to {fname}'.format(
name=name, fname=formatted_name))
return docker_name.Tag(formatted_name)
def main():
logging_setup.DefineCommandLineArgs(parser)
args = parser.parse_args()
logging_setup.Init(args=args)
if not args.name:
raise Exception('--name is a required arguments.')
# This library can support push-by-digest, but the likelihood of a user
# correctly providing us with the digest without using this library
# directly is essentially nil.
name = Tag(args.name, args.stamp_info_file)
if not args.config and (args.layer or args.digest):
raise Exception(
'Using --layer or --digest requires --config to be specified.')
if not args.config and not args.tarball:
raise Exception('Either --config or --tarball must be specified.')
# If config is specified, use that. Otherwise, fallback on reading
# the config from the tarball.
config = args.config
if args.config:
logging.info('Reading config from %r', args.config)
with open(args.config, 'r') as reader:
config = reader.read()
elif args.tarball:
logging.info('Reading config from tarball %r', args.tarball)
with v2_2_image.FromTarball(args.tarball) as base:
config = base.config_file()
if len(args.digest or []) != len(args.layer or []):
raise Exception('--digest and --layer must have matching lengths.')
transport = transport_pool.Http(httplib2.Http, size=_THREADS)
if args.certificates:
for item in args.certificates:
logging.info('Adding certificate %s', item)
key, cert, domain = item.split(',')
transport.add_certificate(key, cert, domain)
# Resolve the appropriate credential to use based on the standard Docker
# client logic.
creds = docker_creds.DefaultKeychain.Resolve(name)
with docker_session.Push(name, creds, transport, threads=_THREADS) as session:
logging.info('Loading v2.2 image from disk ...')
with v2_2_image.FromDisk(config, zip(args.digest or [], args.layer or []),
legacy_base=args.tarball) as v2_2_img:
logging.info('Starting upload ...')
if args.oci:
with oci_compat.OCIFromV22(v2_2_img) as oci_img:
session.upload(oci_img)
digest = oci_img.digest()
else:
session.upload(v2_2_img)
digest = v2_2_img.digest()
print('{name} was published with digest: {digest}'.format(
name=name, digest=digest))
if __name__ == '__main__':
with patched.Httplib2():
main()