-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathdockerclient.py
More file actions
98 lines (76 loc) · 3.84 KB
/
dockerclient.py
File metadata and controls
98 lines (76 loc) · 3.84 KB
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
# -*- coding: utf-8 -*-
"""Support the Deis workflow by manipulating and publishing Docker images."""
import logging
from django.conf import settings
from rest_framework.exceptions import PermissionDenied
from simpleflock import SimpleFlock
import docker
logger = logging.getLogger(__name__)
class DockerClient(object):
"""Use the Docker API to pull, tag, build, and push images to deis-registry."""
FLOCKFILE = '/tmp/controller-pull'
def __init__(self):
self.client = docker.Client(version='auto')
self.registry = settings.REGISTRY_HOST + ':' + str(settings.REGISTRY_PORT)
def publish_release(self, source, target, deis_registry):
"""Update a source Docker image with environment config and publish it to deis-registry."""
# get the source repository name and tag
src_name, src_tag = docker.utils.parse_repository_tag(source)
# get the target repository name and tag
name, tag = docker.utils.parse_repository_tag(target)
# strip any "http://host.domain:port" prefix from the target repository name,
# since we always publish to the Deis registry
name = strip_prefix(name)
# pull the source image from the registry
# NOTE: this relies on an implementation detail of deis-builder, that
# the image has been uploaded already to deis-registry
if deis_registry:
repo = "{}/{}".format(self.registry, src_name)
else:
repo = src_name
self.pull(repo, src_tag)
# tag the image locally without the repository URL
image = "{}:{}".format(src_name, src_tag)
self.tag(image, "{}/{}".format(self.registry, name), tag=tag)
# push the image to deis-registry
self.push("{}/{}".format(self.registry, name), tag)
def pull(self, repo, tag):
"""Pull a Docker image into the local storage graph."""
check_blacklist(repo)
logger.info("Pulling Docker image {}:{}".format(repo, tag))
with SimpleFlock(self.FLOCKFILE, timeout=1200):
stream = self.client.pull(repo, tag=tag, stream=True, insecure_registry=True)
log_output(stream)
def push(self, repo, tag):
"""Push a local Docker image to a registry."""
logger.info("Pushing Docker image {}:{}".format(repo, tag))
stream = self.client.push(repo, tag=tag, stream=True, insecure_registry=True)
log_output(stream)
def tag(self, image, repo, tag):
"""Tag a local Docker image with a new name and tag."""
check_blacklist(repo)
logger.info("Tagging Docker image {} as {}:{}".format(image, repo, tag))
if not self.client.tag(image, repo, tag=tag, force=True):
raise docker.errors.DockerException("tagging failed")
def check_blacklist(repo):
"""Check a Docker repository name for collision with deis/* components."""
blacklisted = [ # NOTE: keep this list up to date!
'builder', 'controller', 'database', 'dockerbuilder', 'etcd', 'minio', 'registry',
'router', 'slugbuilder', 'slugrunner', 'workflow',
]
if any("deis/{}".format(c) in repo for c in blacklisted):
raise PermissionDenied("Repository name {} is not allowed".format(repo))
def log_output(stream):
"""Log a stream at DEBUG level, and raise DockerException if it contains "error"."""
for chunk in stream:
logger.debug(chunk)
# error handling requires looking at the response body
if b'"error"' in chunk.lower():
raise docker.errors.DockerException(chunk)
def strip_prefix(name):
"""Strip the schema and host:port from a Docker repository name."""
paths = name.split('/')
return '/'.join(p for p in paths if p and '.' not in p and ':' not in p)
def publish_release(source, target, deis_registry):
client = DockerClient()
return client.publish_release(source, target, deis_registry)