Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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