943d2c00e9
This reverts commit a253e50609
.
This is a prime suspect in our gate being terrible lately - newer CoreOS
does much more on boot, as well as runs more services. The timeline
roughly matches up.
Change-Id: I58e6ba9d7ac360df98d74746d47b3a76da6f99db
292 lines
13 KiB
Python
Executable File
292 lines
13 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# 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.
|
|
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
|
|
import requests
|
|
|
|
COREOS_VERSION = "367.1.0"
|
|
|
|
COREOS_ARCH = "amd64-usr"
|
|
COREOS_BASE_URL = ("http://storage.core-os.net/coreos/{arch}/{ver}"
|
|
.format(arch=COREOS_ARCH, ver=COREOS_VERSION))
|
|
COREOS_PXE_DIGESTS = "coreos_production_pxe_image.cpio.gz.DIGESTS.asc"
|
|
COREOS_PXE_KERNEL = "coreos_production_pxe.vmlinuz"
|
|
COREOS_PXE_IMAGE = "coreos_production_pxe_image.cpio.gz"
|
|
COREOS_PXE_IMAGE_URL = "{url}/{img}".format(url=COREOS_BASE_URL,
|
|
img=COREOS_PXE_IMAGE)
|
|
COREOS_PXE_KERNEL_URL = "{url}/{kernel}".format(url=COREOS_BASE_URL,
|
|
kernel=COREOS_PXE_KERNEL)
|
|
COREOS_PXE_DIGESTS_URL = "{url}/{digests}".format(url=COREOS_BASE_URL,
|
|
digests=COREOS_PXE_DIGESTS)
|
|
|
|
|
|
def get_etag(cache_name):
|
|
etag_file = "{}.etag".format(cache_name)
|
|
if not os.path.exists(etag_file):
|
|
return None
|
|
with open(etag_file, 'rb') as fp:
|
|
etag = fp.read()
|
|
etag.strip()
|
|
return etag
|
|
|
|
|
|
def save_etag(cache_name, etag):
|
|
etag_file = "{}.etag".format(cache_name)
|
|
with open(etag_file, 'w+b') as fp:
|
|
fp.write(etag)
|
|
|
|
|
|
def cache_file(cache_name, remote_url):
|
|
print("{cname} <- {url}".format(cname=cache_name, url=remote_url))
|
|
etag = get_etag(cache_name)
|
|
headers = {}
|
|
if etag:
|
|
headers['If-None-Match'] = etag
|
|
|
|
start = time.time()
|
|
r = requests.get(remote_url, headers=headers)
|
|
|
|
if r.status_code == 304:
|
|
print("[etag-match]")
|
|
return
|
|
|
|
if r.status_code != 200:
|
|
raise RuntimeError('Failed to download {url}, got HTTP {code} Status '
|
|
'Code.'.format(url=remote_url, code=r.status_code))
|
|
|
|
with open(cache_name, 'w+b') as fp:
|
|
fp.write(r.content)
|
|
|
|
print("{length} bytes in {timespan} seconds"
|
|
.format(length=len(r.content), timespan=time.time() - start))
|
|
save_etag(cache_name, r.headers['etag'])
|
|
|
|
|
|
def inject_oem(archive, oem_dir, output_file):
|
|
d = tempfile.mkdtemp(prefix="oem-inject")
|
|
try:
|
|
dest_oem_dir = os.path.join(d, 'usr', 'share', 'oem')
|
|
cmd_chain = 'gunzip -c {} | cpio -iv'.format(archive)
|
|
execute(cmd_chain, shell=True, cwd=d)
|
|
|
|
shutil.copytree(oem_dir, dest_oem_dir)
|
|
|
|
cmd_chain = 'find . -depth -print | sort | cpio -o -H newc | ' \
|
|
'gzip > {}'.format(output_file)
|
|
execute(cmd_chain, shell=True, cwd=d)
|
|
finally:
|
|
shutil.rmtree(d)
|
|
return output_file
|
|
|
|
|
|
def validate_digests(digests, target, hash_type='sha1'):
|
|
cmd_chain = 'grep -i -A1 "^# {htype} HASH$" {digests} | grep {tgt} | ' \
|
|
'{htype}sum -c /dev/stdin'.format(htype=hash_type,
|
|
digests=digests,
|
|
tgt=os.path.basename(target))
|
|
execute(cmd_chain, shell=True, cwd=os.path.dirname(digests))
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) != 3:
|
|
print("usage: {} [oem-directory-to-inject] [output-directory]"
|
|
.format(os.path.basename(__file__)))
|
|
return
|
|
|
|
oem_dir = os.path.abspath(os.path.expanduser(sys.argv[1]))
|
|
output_dir = os.path.abspath(os.path.expanduser(sys.argv[2]))
|
|
|
|
if not os.path.exists(oem_dir):
|
|
print("Error: {} doesn't exist.".format(oem_dir))
|
|
return
|
|
|
|
if not os.path.exists(os.path.join(oem_dir, 'cloud-config.yml')):
|
|
print("Error: {} is missing cloud-config.yml".format(oem_dir))
|
|
return
|
|
|
|
here = os.path.abspath(os.path.dirname(__file__))
|
|
|
|
top_cache_dir = os.path.join(os.path.dirname(here), ".image_cache")
|
|
cache_dir = os.path.join(top_cache_dir, COREOS_ARCH, COREOS_VERSION)
|
|
|
|
if not os.path.exists(cache_dir):
|
|
os.makedirs(cache_dir)
|
|
|
|
orig_cpio = os.path.join(cache_dir, COREOS_PXE_IMAGE)
|
|
digests = os.path.join(cache_dir, COREOS_PXE_DIGESTS)
|
|
kernel = os.path.join(cache_dir, COREOS_PXE_KERNEL)
|
|
|
|
cache_file(digests, COREOS_PXE_DIGESTS_URL)
|
|
gpg_verify_file(digests)
|
|
cache_file(kernel, COREOS_PXE_KERNEL_URL)
|
|
validate_digests(digests, kernel)
|
|
cache_file(orig_cpio, COREOS_PXE_IMAGE_URL)
|
|
validate_digests(digests, orig_cpio)
|
|
|
|
if not os.path.exists(output_dir):
|
|
os.makedirs(output_dir)
|
|
|
|
output_kernel = os.path.join(output_dir, os.path.basename(kernel))
|
|
output_cpio = os.path.join(
|
|
output_dir,
|
|
os.path.basename(orig_cpio).replace('.cpio.gz', '-oem.cpio.gz'))
|
|
inject_oem(orig_cpio, oem_dir, output_cpio)
|
|
shutil.copy(kernel, output_kernel)
|
|
|
|
|
|
def gpg_verify_file(ascfile):
|
|
d = tempfile.mkdtemp(prefix="oem-gpg-validate")
|
|
try:
|
|
tmpring = os.path.join(d, 'tmp.gpg')
|
|
key = os.path.join(d, 'coreos.key')
|
|
with open(key, 'w+b') as fp:
|
|
fp.write(GPG_KEY)
|
|
|
|
execute(['gpg', '--batch', '--no-default-keyring',
|
|
'--keyring', tmpring, '--import', key])
|
|
|
|
execute(['gpg', '--batch', '--no-default-keyring',
|
|
'--keyring', tmpring, '--verify', ascfile])
|
|
|
|
finally:
|
|
shutil.rmtree(d)
|
|
|
|
|
|
def execute(cmd, shell=False, cwd=None):
|
|
popen_obj = subprocess.Popen(cmd, shell=shell, cwd=cwd)
|
|
popen_obj.communicate()
|
|
if popen_obj.returncode != 0:
|
|
raise subprocess.CalledProcessError(returncode=popen_obj.returncode,
|
|
cmd=cmd)
|
|
|
|
GPG_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
Version: GnuPG v2
|
|
|
|
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
|
|
Pg==
|
|
=LY4G
|
|
-----END PGP PUBLIC KEY BLOCK-----
|
|
"""
|
|
|
|
if __name__ == "__main__":
|
|
main()
|