Skip to content

Commit eb0d9ee

Browse files
committed
refactor: improve git credential handling in Kubernetes
The git credentials are now stored in a temporary secret following recommended best practice. The secret will have the same lifespan as the builder pod.
1 parent 0a7c129 commit eb0d9ee

File tree

2 files changed

+53
-21
lines changed

2 files changed

+53
-21
lines changed

binderhub/build.py

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Contains build of a docker image from a git repository.
33
"""
44

5+
import base64
56
import datetime
67
import json
78
import os
@@ -382,8 +383,24 @@ def submit(self):
382383

383384
env = []
384385
if self.git_credentials:
386+
secret_content = base64.b64encode(
387+
self.git_credentials.encode("utf-8")
388+
).decode("utf-8")
389+
data = {"credentials": secret_content}
390+
391+
secret = client.V1Secret()
392+
secret.data = data
393+
secret.metadata = {"name": self.name}
394+
secret.type = "Opaque"
395+
396+
self.api.create_namespaced_secret(self.namespace, secret)
397+
398+
secret_key_ref = client.V1SecretKeySelector(
399+
name=self.name, key="credentials", optional=False
400+
)
401+
value_from = client.V1EnvVarSource(secret_key_ref=secret_key_ref)
385402
env.append(
386-
client.V1EnvVar(name="GIT_CREDENTIAL_ENV", value=self.git_credentials)
403+
client.V1EnvVar(name="GIT_CREDENTIAL_ENV", value_from=value_from)
387404
)
388405

389406
self.pod = client.V1Pod(
@@ -515,10 +532,9 @@ def submit(self):
515532
f"Found unknown phase {phase} when building {self.name}"
516533
)
517534

518-
if self.pod.status.phase == "Succeeded":
519-
self.cleanup()
520-
elif self.pod.status.phase == "Failed":
535+
if self.pod.status.phase in ["Succeeded", "Failed"]:
521536
self.cleanup()
537+
522538
except Exception:
523539
app_log.exception("Error in watch stream for %s", self.name)
524540
raise
@@ -568,21 +584,32 @@ def stream_logs(self):
568584

569585
def cleanup(self):
570586
"""
571-
Delete the kubernetes build pod
587+
Delete the kubernetes build pod and secret if exists
572588
"""
573-
try:
574-
self.api.delete_namespaced_pod(
575-
name=self.name,
576-
namespace=self.namespace,
577-
body=client.V1DeleteOptions(grace_period_seconds=0),
578-
_request_timeout=KUBE_REQUEST_TIMEOUT,
579-
)
580-
except client.rest.ApiException as e:
581-
if e.status == 404:
582-
# Is ok, someone else has already deleted it
583-
pass
584-
else:
585-
raise
589+
590+
exceptions = []
591+
deletion_methods = [self.api.delete_namespaced_pod]
592+
593+
if self.git_credentials:
594+
deletion_methods.append(self.api.delete_namespaced_secret)
595+
596+
for deletion_method in deletion_methods:
597+
try:
598+
deletion_method(
599+
name=self.name,
600+
namespace=self.namespace,
601+
body=client.V1DeleteOptions(grace_period_seconds=0),
602+
_request_timeout=KUBE_REQUEST_TIMEOUT,
603+
)
604+
except client.rest.ApiException as e:
605+
if e.status == 404:
606+
# Is ok, someone else has already deleted it
607+
pass
608+
else:
609+
exceptions.append(str(e))
610+
611+
if exceptions:
612+
raise RuntimeError("Error(s) occurred during cleanup", exceptions)
586613

587614

588615
class KubernetesCleaner(LoggingConfigurable):

binderhub/tests/test_build.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,14 @@ def test_git_credentials_passed_to_podspec_upon_submit():
234234

235235
assert len(pod.spec.containers) == 1
236236

237-
env = {env_var.name: env_var.value for env_var in pod.spec.containers[0].env}
238-
239-
assert env["GIT_CREDENTIAL_ENV"] == git_credentials
237+
env = {env_var.name: env_var.value_from for env_var in pod.spec.containers[0].env}
238+
239+
credentials_value = env["GIT_CREDENTIAL_ENV"]
240+
assert isinstance(credentials_value, client.V1EnvVarSource)
241+
assert isinstance(credentials_value.secret_key_ref, client.V1SecretKeySelector)
242+
assert credentials_value.secret_key_ref.key == "credentials"
243+
assert credentials_value.secret_key_ref.name == "test_build"
244+
assert credentials_value.secret_key_ref.optional == False
240245

241246

242247
async def test_local_repo2docker_build():

0 commit comments

Comments
 (0)