Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use user credentials for pulling images #327

Merged
merged 5 commits into from
Jun 10, 2020
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
6 changes: 3 additions & 3 deletions helm-chart/renku-notebooks/requirements.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
dependencies:
- name: jupyterhub
repository: https://jupyterhub.github.io/helm-chart
version: 0.9-e120fda
digest: sha256:4d545e7a3c7d7c50eae0416fffc95c0aa7a00df1528073a3b6f731077df8fff9
generated: 2019-03-06T00:48:01.519542+01:00
version: 0.9.0-beta.4
digest: sha256:6b20d99bb549be425df3b96e943f08574a252034378814d993d750bf4e589b99
generated: "2020-06-08T11:36:32.854872+02:00"
4 changes: 0 additions & 4 deletions helm-chart/renku-notebooks/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ spec:
value: {{ template "notebooks.http" . }}://{{ .Values.global.renku.domain }}
- name: JUPYTERHUB_CLIENT_ID
value: {{ .Values.jupyterhub.hub.services.notebooks.oauth_client_id }}
{{ if and .Values.gitlab.registry.username .Values.gitlab.registry.token }}
- name: GITLAB_REGISTRY_SECRET
value: {{ template "notebooks.fullname" . }}-registry
{{ end }}
- name: GITLAB_URL
{{ if .Values.gitlab.url }}
value: {{ .Values.gitlab.url }}
Expand Down
17 changes: 0 additions & 17 deletions helm-chart/renku-notebooks/templates/registry-secret.yaml

This file was deleted.

8 changes: 8 additions & 0 deletions helm-chart/renku-notebooks/templates/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ rules:
- pods/log
- services
- endpoints
- secrets
verbs:
- get
- list
Expand All @@ -24,6 +25,13 @@ rules:
- ""
resources:
- pods
- secrets
verbs:
- delete
- apiGroups:
- ""
resources:
- secrets
verbs:
- create
{{- end -}}
4 changes: 0 additions & 4 deletions helm-chart/renku-notebooks/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ gitlab:
registry:
## Set the default image registry
host:
## Username and token are used for creating a secret for pulling private docker images.
## This combination therefore must give access to private projects, so it should be an admin.
# username:
# token:

## For sending exceptions to Sentry, specify the DSN to use
# sentryDsn:
Expand Down
5 changes: 4 additions & 1 deletion jupyterhub/spawners.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ def get_pod_manifest(self):
),
client.V1EnvVar(name="BRANCH", value=options.get("branch", "master")),
client.V1EnvVar(name="JUPYTERHUB_USER", value=self.user.name),
client.V1EnvVar(name="GITLAB_AUTOSAVE", value=gitlab_autosave)
client.V1EnvVar(name="GITLAB_AUTOSAVE", value=gitlab_autosave),
],
image=options.get("git_clone_image"),
volume_mounts=[volume_mount],
Expand Down Expand Up @@ -294,6 +294,9 @@ def get_pod_manifest(self):
self.gid = 100
self.supplemental_gids = [1000]

# set the image pull policy
self.image_pull_policy = "Always"

pod = yield super().get_pod_manifest()

# Because repository comes from a coroutine, we can't put it simply in `get_env()`
Expand Down
9 changes: 6 additions & 3 deletions renku_notebooks/api/notebooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
get_user_server,
get_user_servers,
delete_user_pod,
create_or_replace_registry_secret,
)
from .auth import authenticated

Expand Down Expand Up @@ -143,9 +144,11 @@ def launch_notebook(user):

current_app.logger.debug(f"Creating server {server_name} with {payload}")

if os.environ.get("GITLAB_REGISTRY_SECRET"):
payload["image_pull_secrets"] = payload.get("image_pull_secrets", [])
payload["image_pull_secrets"].append(os.environ["GITLAB_REGISTRY_SECRET"])
# only create a pull secret if the project has limited visibility and a token is available
if config.GITLAB_AUTH and gl_project.visibility in {"private", "internal"}:
secret_name = f"{user.get('name')}-registry"
create_or_replace_registry_secret(user, namespace, secret_name)
payload["image_pull_secrets"] = [secret_name]

r = create_named_server(user, server_name, payload)

Expand Down
60 changes: 60 additions & 0 deletions renku_notebooks/util/kubernetes_.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
)

from .. import config
from .gitlab_ import _get_oauth_token


# adjust k8s service account paths if running inside telepresence
Expand Down Expand Up @@ -212,3 +213,62 @@ def read_namespaced_pod_log(pod_name, max_log_lines=0):
pod_name, kubernetes_namespace, tail_lines=max_log_lines
)
return logs


def create_or_replace_registry_secret(user, namespace, secret_name):
"""Read or replace a registry secret for a user."""
import base64
import json

token = _get_oauth_token(user)
payload = {
"auths": {
current_app.config.get("IMAGE_REGISTRY"): {
"Username": "oauth2",
"Password": token,
"Email": user.get("email"),
}
}
}

data = {
".dockerconfigjson": base64.b64encode(json.dumps(payload).encode()).decode()
}

secret = client.V1Secret(
api_version="v1",
data=data,
kind="Secret",
metadata={
"name": secret_name,
"namespace": kubernetes_namespace,
"annotations": {
current_app.config.get("RENKU_ANNOTATION_PREFIX")
+ "username": user.get("name")
},
"labels": {
"component": "singleuser-server",
current_app.config.get("RENKU_ANNOTATION_PREFIX")
+ "username": user.get("name"),
},
},
type="kubernetes.io/dockerconfigjson",
)

if _secret_exists(secret_name, kubernetes_namespace):
v1.replace_namespaced_secret(secret_name, kubernetes_namespace, secret)
else:
v1.create_namespaced_secret(kubernetes_namespace, body=secret)

return secret


def _secret_exists(name, namespace):
"""Check if the secret exists."""

try:
v1.read_namespaced_secret(name, namespace)
return True
except client.rest.ApiException:
pass
return False
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ def items(self):
{
f"{namespace}/{project_name}": {
"id": 42,
"visibility": "public",
"path_with_namespace": f"{namespace}/{project_name}",
"attributes": {
"permissions": List([{}, {"access_level": access_level}])
Expand Down