Skip to content

Commit

Permalink
Merge branch 'release/6.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
GrahamDumpleton committed Sep 5, 2019
2 parents da6933c + 05a64aa commit 8d94344
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 149 deletions.
68 changes: 36 additions & 32 deletions jupyterhub/src/configs/hosted-workshop.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,7 @@
# deployment mode. In this mode authentication for JupyterHub is done
# against the OpenShift cluster using OAuth.

# Work out the public server address for the OpenShift OAuth endpoint.
# Make sure the request is done in a session so the connection is closed
# and later calls against the REST API don't attempt to reuse it. This
# is just to avoid potential for any problems with connection reuse.

from fnmatch import fnmatch

from tornado import web, gen

oauth_metadata_url = '%s/.well-known/oauth-authorization-server' % kubernetes_server_url

with requests.Session() as session:
response = session.get(oauth_metadata_url, verify=False)
data = json.loads(response.content.decode('UTF-8'))
oauth_issuer_address = data['issuer']
from tornado import web

# Enable the OpenShift authenticator. Environments variables have
# already been set from the hosted-workshop.sh script file.
Expand Down Expand Up @@ -70,12 +56,6 @@
# don't loose their work. This is mounted on /opt/app-root, so we need
# to copy the contents from the image into the persistent volume the
# first time using an init container.
#
# Note that if a profiles list is used, there must still be a default
# terminal image setup we can use to run the init container. The image
# is what contains the script which copies the file into the persistent
# volume. Perhaps should use the JupyterHub image for the init container
# and add the script which performs the copy to this image.

volume_size = os.environ.get('VOLUME_SIZE')

Expand Down Expand Up @@ -199,23 +179,47 @@

@gen.coroutine
def modify_pod_hook(spawner, pod):
# Set the session access token from the OpenShift login in
# both the terminal and console containers. We still mount
# the service account token still as well because the console
# needs the SSL certificate contained in it when accessing
# the cluster REST API.
hub = '%s-%s' % (application_name, namespace)
short_name = spawner.user.name
user_account_name = '%s-%s' % (hub, short_name)
hub_account_name = '%s-hub' % hub

pod.spec.service_account_name = user_account_name
pod.spec.automount_service_account_token = True

# Grab the OpenShift user access token from the login state.

auth_state = yield spawner.user.get_auth_state()
access_token = auth_state['access_token']

# Ensure that a service account exists corresponding to the user.
# Need to do this as it may have been cleaned up if the session had
# expired and user wasn't logged out in the browser.

owner_uid = yield create_service_account(spawner, pod)

# If there are any exposed ports defined for the session, create
# a service object mapping to the pod for the ports, and create
# routes for each port.

yield expose_service_ports(spawner, pod, owner_uid)

# Before can continue, need to poll looking to see if the secret for
# the api token has been added to the service account. If don't do
# this then pod creation will fail immediately. To do this, must get
# the secrets from the service account and make sure they in turn
# exist.

yield wait_on_service_account(user_account_name)

# Set the session access token from the OpenShift login in
# both the terminal and console containers.

pod.spec.containers[0].env.append(
dict(name='OPENSHIFT_TOKEN', value=auth_state['access_token']))
dict(name='OPENSHIFT_TOKEN', value=access_token))

pod.spec.containers[-1].env.append(
dict(name='BRIDGE_K8S_AUTH_BEARER_TOKEN',
value=auth_state['access_token']))

pod.spec.service_account_name = '%s-%s-user' % (application_name, namespace)
pod.spec.automount_service_account_token = True
dict(name='BRIDGE_K8S_AUTH_BEARER_TOKEN', value=access_token))

# See if a template for the project name has been specified.
# Try expanding the name, substituting the username. If the
Expand Down
11 changes: 3 additions & 8 deletions jupyterhub/src/configs/learning-portal.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,16 @@
# '/restart' URL handler will cause any session to be restarted and they
# will be given a new instance.

import time
import functools
import random
import weakref

from tornado import gen, web
from tornado import web

from jupyterhub.auth import Authenticator
from jupyterhub.handlers import BaseHandler
from jupyterhub.utils import url_path_join

from kubernetes.client.rest import ApiException

class AnonymousUser(object):

def __init__(self, name):
Expand Down Expand Up @@ -244,15 +241,13 @@ def authenticate(self, handler, data):

@gen.coroutine
def modify_pod_hook(spawner, pod):
# Create the service account. We know the user name is a UUID, but
# it is too long to use as is in project name, so we want to shorten.

hub = '%s-%s' % (application_name, namespace)
short_name = spawner.user.name
project_name = '%s-%s' % (hub, short_name)
user_account_name = '%s-%s' % (hub, short_name)
hub_account_name = '%s-hub' % hub

project_name = '%s-%s' % (hub, short_name)

pod.spec.automount_service_account_token = True
pod.spec.service_account_name = user_account_name

Expand Down
64 changes: 35 additions & 29 deletions jupyterhub/src/configs/terminal-server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,7 @@
# deployment mode. In this mode authentication for JupyterHub is done
# against the OpenShift cluster using OAuth.

# Work out the public server address for the OpenShift OAuth endpoint.
# Make sure the request is done in a session so the connection is closed
# and later calls against the REST API don't attempt to reuse it. This
# is just to avoid potential for any problems with connection reuse.

from fnmatch import fnmatch

from tornado import web, gen

oauth_metadata_url = '%s/.well-known/oauth-authorization-server' % kubernetes_server_url

with requests.Session() as session:
response = session.get(oauth_metadata_url, verify=False)
data = json.loads(response.content.decode('UTF-8'))
oauth_issuer_address = data['issuer']
from tornado import web

# Enable the OpenShift authenticator. Environments variables have
# already been set from the terminal-server.sh script file.
Expand Down Expand Up @@ -70,12 +56,6 @@
# don't loose their work. This is mounted on /opt/app-root, so we need
# to copy the contents from the image into the persistent volume the
# first time using an init container.
#
# Note that if a profiles list is used, there must still be a default
# terminal image setup we can use to run the init container. The image
# is what contains the script which copies the file into the persistent
# volume. Perhaps should use the JupyterHub image for the init container
# and add the script which performs the copy to this image.

volume_size = os.environ.get('VOLUME_SIZE')

Expand Down Expand Up @@ -140,18 +120,44 @@

@gen.coroutine
def modify_pod_hook(spawner, pod):
# Set the session access token from the OpenShift login in
# both the terminal and console containers. We still mount
# the service account token still as well because the console
# needs the SSL certificate contained in it when accessing
# the cluster REST API.
hub = '%s-%s' % (application_name, namespace)
short_name = spawner.user.name
user_account_name = '%s-%s' % (hub, short_name)
hub_account_name = '%s-hub' % hub

pod.spec.service_account_name = user_account_name
pod.spec.automount_service_account_token = True

# Grab the OpenShift user access token from the login state.

auth_state = yield spawner.user.get_auth_state()
access_token = auth_state['access_token']

pod.spec.containers[0].env.append(
dict(name='OPENSHIFT_TOKEN', value=auth_state['access_token']))
# Ensure that a service account exists corresponding to the user.
# Need to do this as it may have been cleaned up if the session had
# expired and user wasn't logged out in the browser.

pod.spec.service_account_name = '%s-%s-user' % (application_name, namespace)
owner_uid = yield create_service_account(spawner, pod)

# If there are any exposed ports defined for the session, create
# a service object mapping to the pod for the ports, and create
# routes for each port.

yield expose_service_ports(spawner, pod, owner_uid)

# Before can continue, need to poll looking to see if the secret for
# the api token has been added to the service account. If don't do
# this then pod creation will fail immediately. To do this, must get
# the secrets from the service account and make sure they in turn
# exist.

yield wait_on_service_account(user_account_name)

# Set the session access token from the OpenShift login in
# both the terminal and console containers.

pod.spec.containers[0].env.append(
dict(name='OPENSHIFT_TOKEN', value=access_token))

# See if a template for the project name has been specified.
# Try expanding the name, substituting the username. If the
Expand Down
13 changes: 3 additions & 10 deletions jupyterhub/src/configs/user-workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@
# deployment mode. In this mode authentication for JupyterHub is done
# against a KeyCloak authentication server.

import string
import yaml

from tornado import web, gen

from kubernetes.client.rest import ApiException
from tornado import web

# Configure standalone KeyCloak as the authentication provider for
# users. Environments variables have already been set from the
Expand Down Expand Up @@ -188,15 +183,13 @@

@gen.coroutine
def modify_pod_hook(spawner, pod):
# Create the service account. We know the user name is a UUID, but
# it is too long to use as is in project name, so we want to shorten.

hub = '%s-%s' % (application_name, namespace)
short_name = spawner.user.name
project_name = '%s-%s' % (hub, short_name)
user_account_name = '%s-%s' % (hub, short_name)
hub_account_name = '%s-hub' % hub

project_name = '%s-%s' % (hub, short_name)

pod.spec.automount_service_account_token = True
pod.spec.service_account_name = user_account_name

Expand Down
19 changes: 8 additions & 11 deletions jupyterhub/src/jupyterhub_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1268,6 +1268,11 @@ def create_project_namespace(spawner, pod, project_name):

@gen.coroutine
def setup_project_namespace(spawner, pod, project_name, role, budget):
hub = '%s-%s' % (application_name, namespace)
short_name = spawner.user.name
user_account_name = '%s-%s' % (hub, short_name)
hub_account_name = '%s-hub' % hub

# Wait for project to exist before continuing.

for _ in range(30):
Expand Down Expand Up @@ -1298,11 +1303,6 @@ def setup_project_namespace(spawner, pod, project_name, role, budget):
# delete project when done. Will fail if the project hasn't actually
# been created yet.

hub = '%s-%s' % (application_name, namespace)
short_name = spawner.user.name
user_account_name = '%s-%s' % (hub, short_name)
hub_account_name = '%s-hub' % hub

try:
text = role_binding_template.safe_substitute(
configuration=configuration_type, namespace=namespace,
Expand All @@ -1322,10 +1322,7 @@ def setup_project_namespace(spawner, pod, project_name, role, budget):
raise

# Create role binding in the project so the users service account
# can create resources in it. Need to give it 'admin' role and not
# just 'edit' so that can grant roles to service accounts in the
# project. This means it could though delete the project itself, and
# if do that can't create a new one as has no rights to do that.
# can create resources in it.

try:
text = role_binding_template.safe_substitute(
Expand Down Expand Up @@ -1491,7 +1488,7 @@ def setup_project_namespace(spawner, pod, project_name, role, budget):
extra_resources_loader = json.loads

@gen.coroutine
def create_extra_resources(spawner, pod, project_name, project_uid,
def create_extra_resources(spawner, pod, project_name, owner_uid,
user_account_name, short_name):

if not extra_resources:
Expand Down Expand Up @@ -1520,7 +1517,7 @@ def create_extra_resources(spawner, pod, project_name, project_uid,
'clusterrolebinding', 'namespace'):
body['metadata']['ownerReferences'] = [dict(
apiVersion='v1', kind='Namespace', blockOwnerDeletion=False,
controller=True, name=project_name, uid=project_uid)]
controller=True, name=project_name, uid=owner_uid)]

if kind.lower() == 'namespace':
service_account_name = 'system:serviceaccount:%s:%s-%s-hub' % (
Expand Down
13 changes: 0 additions & 13 deletions templates/hosted-workshop-development.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,19 +151,6 @@
}
}
},
{
"kind": "ServiceAccount",
"apiVersion": "v1",
"metadata": {
"name": "${APPLICATION_NAME}-${PROJECT_NAME}-user",
"namespace": "${PROJECT_NAME}",
"labels": {
"app": "${APPLICATION_NAME}-${PROJECT_NAME}",
"spawner": "hosted-workshop",
"class": "spawner"
}
}
},
{
"kind": "OAuthClient",
"apiVersion": "oauth.openshift.io/v1",
Expand Down
15 changes: 1 addition & 14 deletions templates/hosted-workshop-production.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
},
{
"name": "SPAWNER_IMAGE",
"value": "quay.io/openshifthomeroom/workshop-spawner:6.0.1",
"value": "quay.io/openshifthomeroom/workshop-spawner:6.1.0",
"required": true
},
{
Expand Down Expand Up @@ -146,19 +146,6 @@
}
}
},
{
"kind": "ServiceAccount",
"apiVersion": "v1",
"metadata": {
"name": "${APPLICATION_NAME}-${PROJECT_NAME}-user",
"namespace": "${PROJECT_NAME}",
"labels": {
"app": "${APPLICATION_NAME}-${PROJECT_NAME}",
"spawner": "hosted-workshop",
"class": "spawner"
}
}
},
{
"kind": "OAuthClient",
"apiVersion": "oauth.openshift.io/v1",
Expand Down
4 changes: 2 additions & 2 deletions templates/jumpbox-server-production.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@
},
{
"name": "SPAWNER_IMAGE",
"value": "quay.io/openshifthomeroom/workshop-spawner:6.0.1",
"value": "quay.io/openshifthomeroom/workshop-spawner:6.1.0",
"required": true
},
{
"name": "KEYCLOAK_IMAGE",
"value": "quay.io/openshifthomeroom/workshop-keycloak:6.0.1",
"value": "quay.io/openshifthomeroom/workshop-keycloak:6.1.0",
"required": true
},
{
Expand Down
2 changes: 1 addition & 1 deletion templates/learning-portal-production.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
},
{
"name": "SPAWNER_IMAGE",
"value": "quay.io/openshifthomeroom/workshop-spawner:6.0.1",
"value": "quay.io/openshifthomeroom/workshop-spawner:6.1.0",
"required": true
},
{
Expand Down
Loading

0 comments on commit 8d94344

Please sign in to comment.