Source code for registry.private

import cStringIO
import hashlib
import json
import requests
import tarfile
import urlparse
import uuid

from django.conf import settings
from docker.utils import utils

from api.utils import encode


[docs]def publish_release(source, config, target): """ Publish a new release as a Docker image Given a source image and dictionary of last-mile configuration, create a target Docker image on the registry. For example:: publish_release('registry.local:5000/gabrtv/myapp:v22', {'ENVVAR': 'values'}, 'registry.local:5000/gabrtv/myapp:v23') results in a new Docker image at 'registry.local:5000/gabrtv/myapp:v23' which contains the new configuration as ENV entries. """ try: repo, tag = utils.parse_repository_tag(source) src_image = repo src_tag = tag if tag is not None else 'latest' nameparts = repo.rsplit('/', 1) if len(nameparts) == 2: if '/' in nameparts[0]: # strip the hostname and just use the app name src_image = '{}/{}'.format(nameparts[0].rsplit('/', 1)[1], nameparts[1]) elif '.' in nameparts[0]: # we got a name like registry.local:5000/registry src_image = nameparts[1] target_image = target.rsplit(':', 1)[0] target_tag = target.rsplit(':', 1)[1] image_id = _get_tag(src_image, src_tag) except RuntimeError: if src_tag == 'latest': # no image exists yet, so let's build one! _put_first_image(src_image) image_id = _get_tag(src_image, src_tag) else: raise image = _get_image(image_id) # construct the new image image['parent'] = image['id'] image['id'] = _new_id() config['DEIS_APP'] = target_image config['DEIS_RELEASE'] = target_tag image['config']['Env'] = _construct_env(image['config']['Env'], config) # update and tag the new image _commit(target_image, image, _empty_tar_archive(), target_tag) # registry access
def _commit(repository_path, image, layer, tag): _put_image(image) cookies = _put_layer(image['id'], layer) _put_checksum(image, layer, cookies) _put_tag(image['id'], repository_path, tag) def _put_first_image(repository_path): image = { 'id': _new_id(), 'parent': '', 'config': { 'Env': [] } } # tag as v0 in the registry _commit(repository_path, image, _empty_tar_archive(), 'v0') def _api_call(endpoint, data=None, headers={}, cookies=None, request_type='GET'): base_headers = {'user-agent': 'docker/1.0.0'} r = None if len(headers) > 0: for header, value in headers.viewitems(): base_headers[header] = value if request_type == 'GET': r = requests.get(endpoint, headers=base_headers) elif request_type == 'PUT': r = requests.put(endpoint, data=data, headers=base_headers, cookies=cookies) else: raise AttributeError("request type not supported: {}".format(request_type)) return r def _get_tag(repository, tag): path = "/v1/repositories/{repository}/tags/{tag}".format(**locals()) url = urlparse.urljoin(settings.REGISTRY_URL, path) r = _api_call(url) if not r.status_code == 200: raise RuntimeError("GET Image Error ({}: {})".format(r.status_code, r.text)) return r.json() def _get_image(image_id): path = "/v1/images/{image_id}/json".format(**locals()) url = urlparse.urljoin(settings.REGISTRY_URL, path) r = _api_call(url) if not r.status_code == 200: raise RuntimeError("GET Image Error ({}: {})".format(r.status_code, r.text)) return r.json() def _put_image(image): path = "/v1/images/{id}/json".format(**image) url = urlparse.urljoin(settings.REGISTRY_URL, path) r = _api_call(url, data=json.dumps(image), request_type='PUT') if not r.status_code == 200: raise RuntimeError("PUT Image Error ({}: {})".format(r.status_code, r.text)) return r.json() def _put_layer(image_id, layer_fileobj): path = "/v1/images/{image_id}/layer".format(**locals()) url = urlparse.urljoin(settings.REGISTRY_URL, path) r = _api_call(url, data=layer_fileobj.read(), request_type='PUT') if not r.status_code == 200: raise RuntimeError("PUT Layer Error ({}: {})".format(r.status_code, r.text)) return r.cookies def _put_checksum(image, layer, cookies): path = "/v1/images/{id}/checksum".format(**image) url = urlparse.urljoin(settings.REGISTRY_URL, path) h = hashlib.sha256(json.dumps(image) + '\n') h.update(layer.getvalue()) layer_checksum = "sha256:{0}".format(h.hexdigest()) headers = {'X-Docker-Checksum-Payload': layer_checksum} r = _api_call(url, headers=headers, cookies=cookies, request_type='PUT') if not r.status_code == 200: raise RuntimeError("PUT Checksum Error ({}: {})".format(r.status_code, r.text)) def _put_tag(image_id, repository_path, tag): path = "/v1/repositories/{repository_path}/tags/{tag}".format(**locals()) url = urlparse.urljoin(settings.REGISTRY_URL, path) r = _api_call(url, data=json.dumps(image_id), request_type='PUT') if not r.status_code == 200: raise RuntimeError("PUT Tag Error ({}: {})".format(r.status_code, r.text)) # utility functions def _construct_env(env, config): "Update current environment with latest config" new_env = [] # see if we need to update existing ENV vars for e in env: k, v = e.split('=', 1) if k in config: # update values defined by config v = config.pop(k) new_env.append("{}={}".format(encode(k), encode(v))) # add other config ENV items for k, v in config.viewitems(): new_env.append("{}={}".format(encode(k), encode(v))) return new_env def _new_id(): "Return 64-char UUID for use as Image ID" return ''.join(uuid.uuid4().hex * 2) def _empty_tar_archive(): "Return an empty tar archive (in memory)" data = cStringIO.StringIO() tar = tarfile.open(mode="w", fileobj=data) tar.close() data.seek(0) return data