Skip to content

Commit

Permalink
Merge pull request #1724 from manics/registry-dynamic-token-2
Browse files Browse the repository at this point in the history
Support dynamic push credentials obtained from registry
  • Loading branch information
consideRatio authored Jul 8, 2023
2 parents bec74d4 + cb6d328 commit 56a9724
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 14 deletions.
64 changes: 52 additions & 12 deletions binderhub/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,21 @@ class BuildExecutor(LoggingConfigurable):

push_secret = Unicode(
"",
help="Implementation dependent secret for pushing image to a registry.",
help="Implementation dependent static secret for pushing image to a registry.",
config=True,
)

registry_credentials = Dict(
{},
help=(
"Implementation dependent credentials for pushing image to a registry. "
"For example, if push tokens are temporary this could be used to pass "
"dynamically created credentials "
'`{"registry": "docker.io", "username":"user", "password":"password"}`. '
"This will be JSON encoded and passed in the environment variable "
"CONTAINER_ENGINE_REGISTRY_CREDENTIALS` to repo2docker. "
"If provided this will be used instead of push_secret."
),
config=True,
)

Expand Down Expand Up @@ -231,7 +245,26 @@ def _default_api(self):
# Overrides the default for BuildExecutor
push_secret = Unicode(
"binder-build-docker-config",
help="Implementation dependent secret for pushing image to a registry.",
help=(
"Name of a Kubernetes secret containing static credentials for pushing "
"an image to a registry."
),
config=True,
)

registry_credentials = Dict(
{},
help=(
"Implementation dependent credentials for pushing image to a registry. "
"For example, if push tokens are temporary this could be used to pass "
"dynamically created credentials "
'`{"registry": "docker.io", "username":"user", "password":"password"}`. '
"This will be JSON encoded and passed in the environment variable "
"CONTAINER_ENGINE_REGISTRY_CREDENTIALS` to repo2docker. "
"If provided this will be used instead of push_secret. "
"Currently this is passed to the build pod as a plain text environment "
"variable, though future implementations may use a Kubernetes secret."
),
config=True,
)

Expand Down Expand Up @@ -394,7 +427,23 @@ def submit(self):
)
]

if self.push_secret:
env = [
client.V1EnvVar(name=key, value=value)
for key, value in self.extra_envs.items()
]
if self.git_credentials:
env.append(
client.V1EnvVar(name="GIT_CREDENTIAL_ENV", value=self.git_credentials)
)

if self.registry_credentials:
env.append(
client.V1EnvVar(
name="CONTAINER_ENGINE_REGISTRY_CREDENTIALS",
value=json.dumps(self.registry_credentials),
)
)
elif self.push_secret:
volume_mounts.append(
client.V1VolumeMount(mount_path="/root/.docker", name="docker-config")
)
Expand All @@ -405,15 +454,6 @@ def submit(self):
)
)

env = [
client.V1EnvVar(name=key, value=value)
for key, value in self.extra_envs.items()
]
if self.git_credentials:
env.append(
client.V1EnvVar(name="GIT_CREDENTIAL_ENV", value=self.git_credentials)
)

self.pod = client.V1Pod(
metadata=client.V1ObjectMeta(
name=self.name,
Expand Down
11 changes: 9 additions & 2 deletions binderhub/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,12 @@ async def get(self, provider_prefix, _unescaped_spec):
.lower()
)

image_without_tag, image_tag = _get_image_basename_and_tag(image_name)
if self.settings["use_registry"]:
for _ in range(3):
try:
image_manifest = await self.registry.get_image_manifest(
*_get_image_basename_and_tag(image_name)
image_without_tag, image_tag
)
image_found = bool(image_manifest)
break
Expand Down Expand Up @@ -457,7 +458,13 @@ async def get(self, provider_prefix, _unescaped_spec):
image_name=image_name,
git_credentials=provider.git_credentials,
)
if not self.settings["use_registry"]:
if self.settings["use_registry"]:
push_token = await self.registry.get_credentials(
image_without_tag, image_tag
)
if push_token:
build.registry_credentials = push_token
else:
build.push_secret = ""

self.build = build
Expand Down
8 changes: 8 additions & 0 deletions binderhub/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,14 @@ async def get_image_manifest(self, image, tag):
raise
return json.loads(resp.body.decode("utf-8"))

async def get_credentials(self, image, tag):
"""
If a dynamic token is required for pushing an image to the registry
return a dictionary of login credentials, otherwise return None
(caller should get credentials from some other source)
"""
return None


class FakeRegistry(DockerRegistry):
"""
Expand Down

0 comments on commit 56a9724

Please sign in to comment.