Make sure the build command in the client sends the auth credentials along. Required for "FROM " lines that pull from private registries

This commit is contained in:
Joseph Schorr 2014-03-28 22:02:53 -04:00 committed by shin-
parent 2d56149831
commit 9ae3bcd2f3
3 changed files with 49 additions and 5 deletions

View File

@ -87,6 +87,10 @@ def resolve_authconfig(authconfig, registry=None):
return authconfig.get(swap_protocol(registry), None)
def encode_auth(auth_info):
return base64.b64encode(auth_info.get('username', '') + b':' +
auth_info.get('password', ''))
def decode_auth(auth):
if isinstance(auth, six.string_types):
auth = auth.encode('ascii')
@ -100,6 +104,12 @@ def encode_header(auth):
return base64.b64encode(auth_json)
def encode_full_header(auth):
""" Returns the given auth block encoded for the X-Registry-Config header.
"""
return encode_header({'configs': auth})
def load_config(root=None):
"""Loads authentication data from a Docker configuration file in the given
root directory."""

View File

@ -28,7 +28,7 @@ from .utils import utils
if not six.PY3:
import websocket
DEFAULT_DOCKER_API_VERSION = '1.8'
DEFAULT_DOCKER_API_VERSION = '1.9'
DEFAULT_TIMEOUT_SECONDS = 60
STREAM_HEADER_SIZE_BYTES = 8
@ -361,6 +361,17 @@ class Client(requests.Session):
if context is not None:
headers = {'Content-Type': 'application/tar'}
if utils.compare_version('1.9', self._version) >= 0:
# If we don't have any auth data so far, try reloading the config
# file one more time in case anything showed up in there.
if not self._auth_configs:
self._auth_configs = auth.load_config()
# Send the full auth configuration (if any exists), since the build
# could use any (or all) of the registries.
if self._auth_configs:
headers['X-Registry-Config'] = auth.encode_full_header(self._auth_configs)
response = self._post(
u,
data=context,
@ -618,7 +629,7 @@ class Client(requests.Session):
self._auth_configs = auth.load_config()
authcfg = auth.resolve_authconfig(self._auth_configs, registry)
# Do not fail here if no atuhentication exists for this specific
# Do not fail here if no authentication exists for this specific
# registry as we can have a readonly pull. Just put the header if
# we can.
if authcfg:
@ -644,7 +655,7 @@ class Client(requests.Session):
self._auth_configs = auth.load_config()
authcfg = auth.resolve_authconfig(self._auth_configs, registry)
# Do not fail here if no atuhentication exists for this specific
# Do not fail here if no authentication exists for this specific
# registry as we can have a readonly pull. Just put the header if
# we can.
if authcfg:

View File

@ -32,7 +32,7 @@ class BaseTestCase(unittest.TestCase):
tmp_containers = []
def setUp(self):
self.client = docker.Client()
self.client = docker.Client(base_url='http://localhost:4243')
self.client.pull('busybox')
self.tmp_imgs = []
self.tmp_containers = []
@ -755,6 +755,29 @@ class TestBuildFromStringIO(BaseTestCase):
self.assertNotEqual(logs, '')
class TestBuildWithAuth(BaseTestCase):
def runTest(self):
if self.client._version < 1.9:
return
key = 'K4104GON3P4Q6ZUJFZRRC2ZQTBJ5YT0UMZD7TGT7ZVIR8Y05FAH2TJQI6Y90SMIB'
self.client.login('quay+fortesting', key, registry='https://quay.io/v1/', email='')
script = io.BytesIO('\n'.join([
'FROM quay.io/quay/teststuff',
'MAINTAINER docker-py',
'RUN mkdir -p /tmp/test',
]).encode('ascii'))
stream = self.client.build(fileobj=script, stream=True)
logs = ''
for chunk in stream:
logs += chunk
self.assertNotEqual(logs, '')
self.assertEqual(logs.find('HTTP code: 403'), -1)
#######################
# PY SPECIFIC TESTS #
#######################
@ -820,7 +843,7 @@ class TestLoadJSONConfig(BaseTestCase):
class TestConnectionTimeout(unittest.TestCase):
def setUp(self):
self.timeout = 0.5
self.client = docker.client.Client(base_url='http://192.168.10.2:4243',
self.client = docker.client.Client(base_url='http://localhost:4243',
timeout=self.timeout)
def runTest(self):