gecko-dev/taskcluster/taskgraph/docker.py

203 строки
7.0 KiB
Python
Исходник Обычный вид История

# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import absolute_import, print_function, unicode_literals
import json
import os
import sys
import subprocess
import tarfile
import tempfile
import which
from subprocess import Popen, PIPE
from io import BytesIO
from taskgraph.util import docker
from taskgraph.util.taskcluster import (
find_task_id,
get_artifact_url,
)
from . import GECKO
DOCKER_INDEX = docker.INDEX_PREFIX + '.{}.{}.hash.{}'
def load_image_by_name(image_name, tag=None):
Bug 1302763 - Move docker images out of testing/docker into taskcluster/docker; r=dustin r=CuriousLearner MozReview-Commit-ID: 7v1uCDB5qoN --HG-- rename : testing/docker/README.md => taskcluster/docker/README.md rename : testing/docker/REGISTRY => taskcluster/docker/REGISTRY rename : testing/docker/android-gradle-build/Dockerfile => taskcluster/docker/android-gradle-build/Dockerfile rename : testing/docker/android-gradle-build/README.md => taskcluster/docker/android-gradle-build/README.md rename : testing/docker/android-gradle-build/REGISTRY => taskcluster/docker/android-gradle-build/REGISTRY rename : testing/docker/android-gradle-build/VERSION => taskcluster/docker/android-gradle-build/VERSION rename : testing/docker/android-gradle-build/bin/after.sh => taskcluster/docker/android-gradle-build/bin/after.sh rename : testing/docker/android-gradle-build/bin/before.sh => taskcluster/docker/android-gradle-build/bin/before.sh rename : testing/docker/android-gradle-build/bin/build.sh => taskcluster/docker/android-gradle-build/bin/build.sh rename : testing/docker/android-gradle-build/bin/checkout-script.sh => taskcluster/docker/android-gradle-build/bin/checkout-script.sh rename : testing/docker/android-gradle-build/bin/checkout-sources.sh => taskcluster/docker/android-gradle-build/bin/checkout-sources.sh rename : testing/docker/android-gradle-build/buildprops.json => taskcluster/docker/android-gradle-build/buildprops.json rename : testing/docker/android-gradle-build/dot-config/pip/pip.conf => taskcluster/docker/android-gradle-build/dot-config/pip/pip.conf rename : testing/docker/android-gradle-build/oauth.txt => taskcluster/docker/android-gradle-build/oauth.txt rename : testing/docker/base-build/Dockerfile => taskcluster/docker/base-build/Dockerfile rename : testing/docker/base-build/HASH => taskcluster/docker/base-build/HASH rename : testing/docker/base-build/VERSION => taskcluster/docker/base-build/VERSION rename : testing/docker/base-build/system-setup.sh => taskcluster/docker/base-build/system-setup.sh rename : testing/docker/base-test/Dockerfile => taskcluster/docker/base-test/Dockerfile rename : testing/docker/base-test/HASH => taskcluster/docker/base-test/HASH rename : testing/docker/base-test/REGISTRY => taskcluster/docker/base-test/REGISTRY rename : testing/docker/base-test/VERSION => taskcluster/docker/base-test/VERSION rename : testing/docker/base-test/sources.list => taskcluster/docker/base-test/sources.list rename : testing/docker/centos6-build-upd/Dockerfile => taskcluster/docker/centos6-build-upd/Dockerfile rename : testing/docker/centos6-build-upd/HASH => taskcluster/docker/centos6-build-upd/HASH rename : testing/docker/centos6-build-upd/REGISTRY => taskcluster/docker/centos6-build-upd/REGISTRY rename : testing/docker/centos6-build-upd/VERSION => taskcluster/docker/centos6-build-upd/VERSION rename : testing/docker/centos6-build/Dockerfile => taskcluster/docker/centos6-build/Dockerfile rename : testing/docker/centos6-build/HASH => taskcluster/docker/centos6-build/HASH rename : testing/docker/centos6-build/REGISTRY => taskcluster/docker/centos6-build/REGISTRY rename : testing/docker/centos6-build/VERSION => taskcluster/docker/centos6-build/VERSION rename : testing/docker/centos6-build/hgrc => taskcluster/docker/centos6-build/hgrc rename : testing/docker/centos6-build/system-setup.sh => taskcluster/docker/centos6-build/system-setup.sh rename : testing/docker/decision/Dockerfile => taskcluster/docker/decision/Dockerfile rename : testing/docker/decision/HASH => taskcluster/docker/decision/HASH rename : testing/docker/decision/README.md => taskcluster/docker/decision/README.md rename : testing/docker/decision/REGISTRY => taskcluster/docker/decision/REGISTRY rename : testing/docker/decision/VERSION => taskcluster/docker/decision/VERSION rename : testing/docker/decision/system-setup.sh => taskcluster/docker/decision/system-setup.sh rename : testing/docker/desktop-build/Dockerfile => taskcluster/docker/desktop-build/Dockerfile rename : testing/docker/desktop-build/bin/build.sh => taskcluster/docker/desktop-build/bin/build.sh rename : testing/docker/desktop-build/bin/checkout-script.sh => taskcluster/docker/desktop-build/bin/checkout-script.sh rename : testing/docker/desktop-build/bin/checkout-sources.sh => taskcluster/docker/desktop-build/bin/checkout-sources.sh rename : testing/docker/desktop-build/buildprops.json => taskcluster/docker/desktop-build/buildprops.json rename : testing/docker/desktop-build/dot-config/pip/pip.conf => taskcluster/docker/desktop-build/dot-config/pip/pip.conf rename : testing/docker/desktop-build/oauth.txt => taskcluster/docker/desktop-build/oauth.txt rename : testing/docker/desktop-test/Dockerfile => taskcluster/docker/desktop-test/Dockerfile rename : testing/docker/desktop-test/apport => taskcluster/docker/desktop-test/apport rename : testing/docker/desktop-test/buildprops.json => taskcluster/docker/desktop-test/buildprops.json rename : testing/docker/desktop-test/deja-dup-monitor.desktop => taskcluster/docker/desktop-test/deja-dup-monitor.desktop rename : testing/docker/desktop-test/dot-files/config/pip/pip.conf => taskcluster/docker/desktop-test/dot-files/config/pip/pip.conf rename : testing/docker/desktop-test/dot-files/config/user-dirs.dirs => taskcluster/docker/desktop-test/dot-files/config/user-dirs.dirs rename : testing/docker/desktop-test/dot-files/config/user-dirs.locale => taskcluster/docker/desktop-test/dot-files/config/user-dirs.locale rename : testing/docker/desktop-test/dot-files/pulse/default.pa => taskcluster/docker/desktop-test/dot-files/pulse/default.pa rename : testing/docker/desktop-test/fonts.conf => taskcluster/docker/desktop-test/fonts.conf rename : testing/docker/desktop-test/jockey-gtk.desktop => taskcluster/docker/desktop-test/jockey-gtk.desktop rename : testing/docker/desktop-test/motd => taskcluster/docker/desktop-test/motd rename : testing/docker/desktop-test/release-upgrades => taskcluster/docker/desktop-test/release-upgrades rename : testing/docker/desktop-test/taskcluster-interactive-shell => taskcluster/docker/desktop-test/taskcluster-interactive-shell rename : testing/docker/desktop-test/tc-vcs-config.yml => taskcluster/docker/desktop-test/tc-vcs-config.yml rename : testing/docker/desktop-test/tester.env => taskcluster/docker/desktop-test/tester.env rename : testing/docker/desktop1604-test/Dockerfile => taskcluster/docker/desktop1604-test/Dockerfile rename : testing/docker/desktop1604-test/apport => taskcluster/docker/desktop1604-test/apport rename : testing/docker/desktop1604-test/buildprops.json => taskcluster/docker/desktop1604-test/buildprops.json rename : testing/docker/desktop1604-test/deja-dup-monitor.desktop => taskcluster/docker/desktop1604-test/deja-dup-monitor.desktop rename : testing/docker/desktop1604-test/dot-files/config/pip/pip.conf => taskcluster/docker/desktop1604-test/dot-files/config/pip/pip.conf rename : testing/docker/desktop1604-test/dot-files/config/user-dirs.dirs => taskcluster/docker/desktop1604-test/dot-files/config/user-dirs.dirs rename : testing/docker/desktop1604-test/dot-files/config/user-dirs.locale => taskcluster/docker/desktop1604-test/dot-files/config/user-dirs.locale rename : testing/docker/desktop1604-test/dot-files/pulse/default.pa => taskcluster/docker/desktop1604-test/dot-files/pulse/default.pa rename : testing/docker/desktop1604-test/fonts.conf => taskcluster/docker/desktop1604-test/fonts.conf rename : testing/docker/desktop1604-test/jockey-gtk.desktop => taskcluster/docker/desktop1604-test/jockey-gtk.desktop rename : testing/docker/desktop1604-test/motd => taskcluster/docker/desktop1604-test/motd rename : testing/docker/desktop1604-test/release-upgrades => taskcluster/docker/desktop1604-test/release-upgrades rename : testing/docker/desktop1604-test/taskcluster-interactive-shell => taskcluster/docker/desktop1604-test/taskcluster-interactive-shell rename : testing/docker/desktop1604-test/tc-vcs-config.yml => taskcluster/docker/desktop1604-test/tc-vcs-config.yml rename : testing/docker/desktop1604-test/tester.env => taskcluster/docker/desktop1604-test/tester.env rename : testing/docker/image_builder/Dockerfile => taskcluster/docker/image_builder/Dockerfile rename : testing/docker/image_builder/HASH => taskcluster/docker/image_builder/HASH rename : testing/docker/image_builder/REGISTRY => taskcluster/docker/image_builder/REGISTRY rename : testing/docker/image_builder/VERSION => taskcluster/docker/image_builder/VERSION rename : testing/docker/image_builder/build-image.sh => taskcluster/docker/image_builder/build-image.sh rename : testing/docker/image_builder/setup.sh => taskcluster/docker/image_builder/setup.sh rename : testing/docker/lint/Dockerfile => taskcluster/docker/lint/Dockerfile rename : testing/docker/lint/system-setup.sh => taskcluster/docker/lint/system-setup.sh rename : testing/docker/recipes/centos6-build-system-setup.sh => taskcluster/docker/recipes/centos6-build-system-setup.sh rename : testing/docker/recipes/common.sh => taskcluster/docker/recipes/common.sh rename : testing/docker/recipes/install-mercurial.sh => taskcluster/docker/recipes/install-mercurial.sh rename : testing/docker/recipes/run-task => taskcluster/docker/recipes/run-task rename : testing/docker/recipes/tooltool.py => taskcluster/docker/recipes/tooltool.py rename : testing/docker/recipes/ubuntu1204-test-system-setup.sh => taskcluster/docker/recipes/ubuntu1204-test-system-setup.sh rename : testing/docker/recipes/ubuntu1604-test-system-setup.sh => taskcluster/docker/recipes/ubuntu1604-test-system-setup.sh rename : testing/docker/recipes/xvfb.sh => taskcluster/docker/recipes/xvfb.sh rename : testing/docker/rust-build/Dockerfile => taskcluster/docker/rust-build/Dockerfile rename : testing/docker/rust-build/README.md => taskcluster/docker/rust-build/README.md rename : testing/docker/rust-build/REGISTRY => taskcluster/docker/rust-build/REGISTRY rename : testing/docker/rust-build/VERSION => taskcluster/docker/rust-build/VERSION rename : testing/docker/rust-build/build_cargo.sh => taskcluster/docker/rust-build/build_cargo.sh rename : testing/docker/rust-build/build_rust.sh => taskcluster/docker/rust-build/build_rust.sh rename : testing/docker/rust-build/build_rust_mac.sh => taskcluster/docker/rust-build/build_rust_mac.sh rename : testing/docker/rust-build/fetch_cargo.sh => taskcluster/docker/rust-build/fetch_cargo.sh rename : testing/docker/rust-build/fetch_rust.sh => taskcluster/docker/rust-build/fetch_rust.sh rename : testing/docker/rust-build/package_rust.sh => taskcluster/docker/rust-build/package_rust.sh rename : testing/docker/rust-build/repack_rust.py => taskcluster/docker/rust-build/repack_rust.py rename : testing/docker/rust-build/splat_rust.py => taskcluster/docker/rust-build/splat_rust.py rename : testing/docker/rust-build/task.json => taskcluster/docker/rust-build/task.json rename : testing/docker/rust-build/tcbuild.py => taskcluster/docker/rust-build/tcbuild.py rename : testing/docker/rust-build/upload_rust.sh => taskcluster/docker/rust-build/upload_rust.sh rename : testing/docker/tester/Dockerfile => taskcluster/docker/tester/Dockerfile rename : testing/docker/tester/HASH => taskcluster/docker/tester/HASH rename : testing/docker/tester/REGISTRY => taskcluster/docker/tester/REGISTRY rename : testing/docker/tester/VERSION => taskcluster/docker/tester/VERSION rename : testing/docker/tester/bin/test.sh => taskcluster/docker/tester/bin/test.sh rename : testing/docker/tester/dot-config/pip/pip.conf => taskcluster/docker/tester/dot-config/pip/pip.conf rename : testing/docker/tester/dot-config/user-dirs.dirs => taskcluster/docker/tester/dot-config/user-dirs.dirs rename : testing/docker/tester/dot-config/user-dirs.locale => taskcluster/docker/tester/dot-config/user-dirs.locale rename : testing/docker/tester/dot-pulse/default.pa => taskcluster/docker/tester/dot-pulse/default.pa rename : testing/docker/tester/tc-vcs-config.yml => taskcluster/docker/tester/tc-vcs-config.yml rename : testing/docker/tester/tester.env => taskcluster/docker/tester/tester.env rename : testing/docker/upload-symbols/Dockerfile => taskcluster/docker/upload-symbols/Dockerfile rename : testing/docker/upload-symbols/README.md => taskcluster/docker/upload-symbols/README.md rename : testing/docker/upload-symbols/bin/checkout-script.sh => taskcluster/docker/upload-symbols/bin/checkout-script.sh rename : testing/docker/upload-symbols/bin/upload.sh => taskcluster/docker/upload-symbols/bin/upload.sh rename : testing/docker/upload-symbols/test_exports.sh => taskcluster/docker/upload-symbols/test_exports.sh extra : rebase_source : fd02b10c77de5b68476ce462b5f888475520a6fe extra : source : 50adff295bf00ea0d9d7426e745acf3635e7dba5
2016-10-20 15:55:34 +03:00
context_path = os.path.join(GECKO, 'taskcluster', 'docker', image_name)
context_hash = docker.generate_context_hash(GECKO, context_path, image_name)
index_path = DOCKER_INDEX.format('level-3', image_name, context_hash)
task_id = find_task_id(index_path)
return load_image_by_task_id(task_id, tag)
def load_image_by_task_id(task_id, tag=None):
artifact_url = get_artifact_url(task_id, 'public/image.tar.zst')
result = load_image(artifact_url, tag)
print("Found docker image: {}:{}".format(result['image'], result['tag']))
if tag:
print("Re-tagged as: {}".format(tag))
else:
tag = '{}:{}'.format(result['image'], result['tag'])
print("Try: docker run -ti --rm {} bash".format(tag))
return True
def build_context(name, outputFile):
"""Build a context.tar for image with specified name.
"""
if not name:
raise ValueError('must provide a Docker image name')
if not outputFile:
raise ValueError('must provide a outputFile')
image_dir = os.path.join(docker.IMAGE_DIR, name)
if not os.path.isdir(image_dir):
raise Exception('image directory does not exist: %s' % image_dir)
docker.create_context_tar(GECKO, image_dir, outputFile, "")
def build_image(name):
"""Build a Docker image of specified name.
Output from image building process will be printed to stdout.
"""
if not name:
raise ValueError('must provide a Docker image name')
image_dir = os.path.join(docker.IMAGE_DIR, name)
if not os.path.isdir(image_dir):
raise Exception('image directory does not exist: %s' % image_dir)
tag = docker.docker_image(name, by_tag=True)
docker_bin = which.which('docker')
# Verify that Docker is working.
try:
subprocess.check_output([docker_bin, '--version'])
except subprocess.CalledProcessError:
raise Exception('Docker server is unresponsive. Run `docker ps` and '
'check that Docker is running')
# We obtain a context archive and build from that. Going through the
# archive creation is important: it normalizes things like file owners
# and mtimes to increase the chances that image generation is
# deterministic.
fd, context_path = tempfile.mkstemp()
os.close(fd)
try:
docker.create_context_tar(GECKO, image_dir, context_path, name)
docker.build_from_context(docker_bin, context_path, name, tag)
finally:
os.unlink(context_path)
print('Successfully built %s and tagged with %s' % (name, tag))
if tag.endswith(':latest'):
print('*' * 50)
print('WARNING: no VERSION file found in image directory.')
print('Image is not suitable for deploying/pushing.')
print('Create an image suitable for deploying/pushing by creating')
print('a VERSION file in the image directory.')
print('*' * 50)
def load_image(url, imageName=None, imageTag=None):
"""
Load docker image from URL as imageName:tag, if no imageName or tag is given
it will use whatever is inside the zstd compressed tarball.
Returns an object with properties 'image', 'tag' and 'layer'.
"""
# If imageName is given and we don't have an imageTag
# we parse out the imageTag from imageName, or default it to 'latest'
# if no imageName and no imageTag is given, 'repositories' won't be rewritten
if imageName and not imageTag:
if ':' in imageName:
imageName, imageTag = imageName.split(':', 1)
else:
imageTag = 'latest'
curl, zstd, docker = None, None, None
image, tag, layer = None, None, None
error = None
try:
# Setup piping: curl | zstd | tarin
curl = Popen(['curl', '-#', '--fail', '-L', '--retry', '8', url], stdout=PIPE)
zstd = Popen(['zstd', '-d'], stdin=curl.stdout, stdout=PIPE)
tarin = tarfile.open(mode='r|', fileobj=zstd.stdout)
# Seutp piping: tarout | docker
docker = Popen(['docker', 'load'], stdin=PIPE)
tarout = tarfile.open(mode='w|', fileobj=docker.stdin, format=tarfile.GNU_FORMAT)
# Read from tarin and write to tarout
for member in tarin:
# Write non-file members directly (don't use extractfile on links)
if not member.isfile():
tarout.addfile(member)
continue
# Open reader for the member
reader = tarin.extractfile(member)
# If member is repository, we parse and possibly rewrite the image tags
if member.name == 'repositories':
# Read and parse repositories
repos = json.loads(reader.read())
reader.close()
# If there is more than one image or tag, we can't handle it here
if len(repos.keys()) > 1:
raise Exception('file contains more than one image')
image = repos.keys()[0]
if len(repos[image].keys()) > 1:
raise Exception('file contains more than one tag')
tag = repos[image].keys()[0]
layer = repos[image][tag]
# Rewrite the repositories file
data = json.dumps({imageName or image: {imageTag or tag: layer}})
reader = BytesIO(data)
member.size = len(data)
# Add member and reader
tarout.addfile(member, reader)
reader.close()
tarout.close()
except Exception:
error = sys.exc_info()[0]
finally:
def trykill(proc):
try:
proc.kill()
except:
pass
# Check that all subprocesses finished correctly
if curl and curl.wait() != 0:
trykill(zstd)
trykill(docker)
raise Exception('failed to download from url: {}'.format(url))
if zstd and zstd.wait() != 0:
trykill(docker)
raise Exception('zstd decompression failed')
if docker:
docker.stdin.close()
if docker and docker.wait() != 0:
raise Exception('loading into docker failed')
if error:
raise error
# Check that we found a repositories file
if not image or not tag or not layer:
raise Exception('No repositories file found!')
return {'image': image, 'tag': tag, 'layer': layer}