import docker import json import os import sys import tarfile import time import requests import subprocess DEBUG = os.environ.get('DEIS_DEBUG') in ('true', '1') registryLocation = os.getenv('DEIS_REGISTRY_LOCATION', 'on-cluster') def log_output(stream, decode): error = False for chunk in stream: if isinstance(chunk, bytes): # Convert to dict, since docker-py returns some errors as raw bytes. chunk = eval(chunk) if 'error' in chunk: error = True print(chunk['error']) elif decode: stream_chunk = chunk.get('stream') if stream_chunk: # Must handle chunks as bytes to avoid UnicodeEncodeError. encoded_chunk = stream_chunk.encode('utf-8') sys.stdout.buffer.write(encoded_chunk) elif DEBUG: print(chunk) sys.stdout.flush() if error: # HACK: delay so stderr is logged before this dockerbuilder pod exits. time.sleep(3) exit(1) def log(msg): if DEBUG: print(msg) def get_registry_name(): hostname = os.getenv('DEIS_REGISTRY_HOSTNAME', "") hostname = hostname.replace("https://", "").replace("http://", "") if registryLocation == "off-cluster": organization = os.getenv('DEIS_REGISTRY_ORGANIZATION') regName = "" # empty hostname means dockerhub and hence no need to prefix the image if hostname != "": regName = hostname + "/" # Registries may have organizations/namespaces under them which needs to # be prefixed to the image if organization != "": regName = regName + organization return regName elif registryLocation == "ecr": return hostname elif registryLocation == "gcr": return hostname + "/" + os.getenv('DEIS_REGISTRY_GCS_PROJ_ID') else: return "{}:{}".format(os.getenv("DEIS_REGISTRY_SERVICE_HOST"), os.getenv("DEIS_REGISTRY_SERVICE_PORT")) def download_file(tar_path): os.putenv('BUCKET_FILE', "/var/run/secrets/deis/objectstore/creds/builder-bucket") if os.getenv('BUILDER_STORAGE') == "minio": os.makedirs("/tmp/objectstore/minio") bucketFile = open('/tmp/objectstore/minio/builder-bucket', 'w') bucketFile.write('git') bucketFile.close() os.putenv('BUCKET_FILE', "/tmp/objectstore/minio/builder-bucket") elif os.getenv('BUILDER_STORAGE') in ["azure", "swift"]: os.putenv('CONTAINER_FILE', "/var/run/secrets/deis/objectstore/creds/builder-container") command = [ "objstorage", "--storage-type="+os.getenv('BUILDER_STORAGE'), "download", tar_path, "apptar" ] subprocess.check_call(command) tar_path = os.getenv('TAR_PATH') if tar_path: if os.path.exists("/var/run/secrets/deis/objectstore/creds/"): download_file(tar_path) else: r = requests.get(tar_path) with open("apptar", "wb") as app: app.write(r.content) log("download tar file complete") with tarfile.open("apptar", "r:gz") as tar: tar.extractall("/app/") log("extracting tar file complete") buildargs = json.loads(os.getenv('DOCKER_BUILD_ARGS', '{}')) # inject docker build args into the Dockerfile so we get around Dockerfiles that don't have things # like PORT defined. with open("/app/Dockerfile", "a") as dockerfile: # ensure we are on a new line dockerfile.write("\n") for envvar in buildargs: dockerfile.write("ARG {}\n".format(envvar)) client = docker.Client(version='auto') if registryLocation != "on-cluster": registry = os.getenv('DEIS_REGISTRY_HOSTNAME', 'https://index.docker.io/v1/') username = os.getenv('DEIS_REGISTRY_USERNAME') password = os.getenv('DEIS_REGISTRY_PASSWORD') client.login(username=username, password=password, registry=registry) registry = get_registry_name() imageName, imageTag = os.getenv('IMG_NAME').split(":", 1) repo = registry + "/" + os.getenv('IMG_NAME') stream = client.build( tag=repo, stream=True, decode=True, rm=True, pull=True, path='/app', buildargs=buildargs) log_output(stream, True) print("Pushing to registry") stream = client.push(registry+'/'+imageName, tag=imageTag, stream=True) log_output(stream, False)