diff --git a/binderhub/registry.py b/binderhub/registry.py index f915a78a6..eee4b8473 100644 --- a/binderhub/registry.py +++ b/binderhub/registry.py @@ -204,13 +204,19 @@ def _parse_www_authenticate_header(self, header): ) from None async def _get_token(self, client, token_url, service, scope): + # specify required query parameters + query_params = { + "scope": scope, + } + + # add optional query parameters + if service is not None: + query_params["service"] = service + auth_req = httpclient.HTTPRequest( url_concat( token_url, - { - "scope": scope, - "service": service, - }, + query_params, ), auth_username=self.username, auth_password=self.password, @@ -263,8 +269,8 @@ async def get_image_manifest(self, image, tag): token = await self._get_token( client, self.token_url, - scope=f"repository:{image}:pull", - service="container_registry", + None, # service may be pre-set in self.token_url as per Helm chart docs for specific CRs + f"repository:{image}:pull", ) req = httpclient.HTTPRequest( url, diff --git a/binderhub/tests/test_registry.py b/binderhub/tests/test_registry.py index 1ad1485c6..34ca3f0aa 100644 --- a/binderhub/tests/test_registry.py +++ b/binderhub/tests/test_registry.py @@ -169,7 +169,15 @@ async def test_get_token(): test_handle = {"username": username, "password": password} app = Application( [ - (r"/token", MockTokenHandler, {"test_handle": test_handle}), + ( + r"/token", + MockTokenHandler, + { + "test_handle": test_handle, + "service": "service.1", + "scope": "scope.2", + }, + ), ] ) ip = "127.0.0.1" @@ -196,6 +204,49 @@ async def test_get_token(): assert token == test_handle["token"] +async def test_get_token_with_config_supplied_service(): + # regression coverage for https://github.com/jupyterhub/binderhub/issues/1620 + + username = "user" + password = "pass" + test_handle = {"username": username, "password": password} + app = Application( + [ + ( + r"/token", + MockTokenHandler, + { + "test_handle": test_handle, + "service": "service.1", + "scope": "scope.2", + }, + ), + ] + ) + ip = "127.0.0.1" + port = randint(10000, 65535) + app.listen(port, ip) + + registry = DockerRegistry( + url="https://example.org", username=username, password=password + ) + + assert registry.url == "https://example.org" + assert registry.auth_config_url == "https://example.org" + # token_url should be unset, since it should be determined by the caller from a + # WWW-Authenticate header + assert registry.token_url == "" + assert registry.username == username + assert registry.password == password + token = await registry._get_token( + httpclient.AsyncHTTPClient(), + f"http://{ip}:{port}/token?service=service.1", + None, + "scope.2", + ) + assert token == test_handle["token"] + + @pytest.mark.parametrize("token_url_known", [True, False]) async def test_get_image_manifest(tmpdir, token_url_known): username = "asdf" @@ -230,7 +281,7 @@ async def test_get_image_manifest(tmpdir, token_url_known): f, ) if token_url_known: - token_url = url + "/token" + token_url = url + "/token?service=container_registry" else: token_url = "" registry = DockerRegistry( diff --git a/docs/source/zero-to-binderhub/setup-binderhub.rst b/docs/source/zero-to-binderhub/setup-binderhub.rst index e0cefd5bf..28809e422 100644 --- a/docs/source/zero-to-binderhub/setup-binderhub.rst +++ b/docs/source/zero-to-binderhub/setup-binderhub.rst @@ -174,6 +174,34 @@ where: If this is not provided, you may find BinderHub rebuilds images every launch instead of pulling them from the ACR. Suggestions for `` could be `ACR_NAME` or the name you give your BinderHub. +If you are using an authenticated (private) GitLab Container Registry +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you want your BinderHub to push and pull images from an authenticated (private) GitLab Container Registry, then your `config.yaml` file will look as follows:: + + config: + BinderHub: + use_registry: true + image_prefix: registry.gitlab.com///- + DockerRegistry: + token_url: "https://gitlab.com/jwt/auth?service=container_registry" + url: "https://registry.gitlab.com" + username: + password: + registry: + url: https://registry.gitlab.com + username: + password: + +where: + +* `` is your gitlab username, +* `` and `` are valid GitLab credentials. (Passwords may be moved to `secret.yaml`.) +* `` is an arbitrary name that is required due to BinderHub assuming that `image_prefix` contains an extra level for the project name. + See `this issue `_ for futher discussion. + If this is not provided, you may find BinderHub rebuilds images every launch instead of pulling them from the GitLab container registry. + Suggestions for `` could be the name you give your BinderHub. + If you are using OVH Container Registry ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~