77from django .conf import settings
88from rest_framework .exceptions import PermissionDenied
99from simpleflock import SimpleFlock
10+
1011import docker
1112import docker .constants
13+ from docker .errors import APIError
1214
1315logger = logging .getLogger (__name__ )
1416
1517
18+ class RegistryException (Exception ):
19+ pass
20+
21+
1622class DockerClient (object ):
1723 """Use the Docker API to pull, tag, build, and push images to deis-registry."""
1824
@@ -40,35 +46,41 @@ def publish_release(self, source, target, deis_registry):
4046 repo = "{}/{}" .format (self .registry , src_name )
4147 else :
4248 repo = src_name
43- self .pull (repo , src_tag )
4449
45- # tag the image locally without the repository URL
46- image = "{}:{}" .format (src_name , src_tag )
47- self .tag (image , "{}/{}" .format (self .registry , name ), tag = tag )
50+ try :
51+ self .pull (repo , src_tag )
52+
53+ # tag the image locally without the repository URL
54+ image = "{}:{}" .format (src_name , src_tag )
55+ self .tag (image , "{}/{}" .format (self .registry , name ), tag = tag )
4856
49- # push the image to deis-registry
50- self .push ("{}/{}" .format (self .registry , name ), tag )
57+ # push the image to deis-registry
58+ self .push ("{}/{}" .format (self .registry , name ), tag )
59+ except APIError as e :
60+ raise RegistryException (str (e ))
5161
5262 def pull (self , repo , tag ):
5363 """Pull a Docker image into the local storage graph."""
5464 check_blacklist (repo )
5565 logger .info ("Pulling Docker image {}:{}" .format (repo , tag ))
5666 with SimpleFlock (self .FLOCKFILE , timeout = 1200 ):
57- stream = self .client .pull (repo , tag = tag , stream = True , insecure_registry = True )
58- log_output (stream )
67+ stream = self .client .pull (repo , tag = tag , stream = True ,
68+ decode = True , insecure_registry = True )
69+ log_output (stream , 'pull' , repo , tag )
5970
6071 def push (self , repo , tag ):
6172 """Push a local Docker image to a registry."""
6273 logger .info ("Pushing Docker image {}:{}" .format (repo , tag ))
63- stream = self .client .push (repo , tag = tag , stream = True , insecure_registry = True )
64- log_output (stream )
74+ stream = self .client .push (repo , tag = tag , stream = True , decode = True ,
75+ insecure_registry = True )
76+ log_output (stream , 'push' , repo , tag )
6577
6678 def tag (self , image , repo , tag ):
6779 """Tag a local Docker image with a new name and tag."""
6880 check_blacklist (repo )
6981 logger .info ("Tagging Docker image {} as {}:{}" .format (image , repo , tag ))
7082 if not self .client .tag (image , repo , tag = tag , force = True ):
71- raise docker . errors . DockerException ( "tagging failed" )
83+ raise RegistryException ( 'Tagging {} as {}:{} failed' . format ( image , repo , tag ) )
7284
7385
7486def check_blacklist (repo ):
@@ -81,13 +93,26 @@ def check_blacklist(repo):
8193 raise PermissionDenied ("Repository name {} is not allowed" .format (repo ))
8294
8395
84- def log_output (stream ):
85- """Log a stream at DEBUG level, and raise DockerException if it contains " error". """
96+ def log_output (stream , operation , repo , tag ):
97+ """Log a stream at DEBUG level, and raise RegistryException if it contains an error"""
8698 for chunk in stream :
87- logger .debug (chunk )
8899 # error handling requires looking at the response body
89- if b'"error"' in chunk .lower ():
90- raise docker .errors .DockerException (chunk )
100+ if 'error' in chunk :
101+ stream_error (chunk , operation , repo , tag )
102+
103+
104+ def stream_error (chunk , operation , repo , tag ):
105+ """Translate docker stream errors into a more digestable format"""
106+ # not all errors provide the code
107+ if 'code' in chunk ['errorDetail' ]:
108+ # permission denied on the repo
109+ if chunk ['errorDetail' ]['code' ] == 403 :
110+ message = 'Permission Denied attempting to {} image {}:{}' .format (operation , repo , tag )
111+ else :
112+ # grab the generic error and strip the useless Error: portion
113+ message = chunk ['error' ].replace ('Error: ' , '' )
114+
115+ raise RegistryException (message )
91116
92117
93118def strip_prefix (name ):
@@ -97,5 +122,4 @@ def strip_prefix(name):
97122
98123
99124def publish_release (source , target , deis_registry ):
100- client = DockerClient ()
101- return client .publish_release (source , target , deis_registry )
125+ return DockerClient ().publish_release (source , target , deis_registry )
0 commit comments