예제 #1
0
def test_make_pod_with_extra_container_config():
    """
    Test specification of a pod with initContainers
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        extra_container_config={
            'envFrom': [
                {
                    'configMapRef': {
                        'name': 'special-config'
                    }
                }
            ]
        }
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            'automountServiceAccountToken': False,
            "securityContext": {},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {
                        },
                        "requests": {
                        }
                    },
                    'envFrom': [
                        {
                            'configMapRef': {
                                'name': 'special-config'
                            }
                        }
                    ]
                }
            ],
            'volumes': [],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #2
0
def test_set_pod_uid_fs_gid():
    """
    Test specification of the simplest possible pod specification
    """
    assert api_client.sanitize_for_serialization(
        make_pod(
            name='test',
            image_spec='jupyter/singleuser:latest',
            env={},
            volumes=[],
            volume_mounts=[],
            cmd=['jupyterhub-singleuser'],
            working_dir=None,
            port=8888,
            cpu_limit=None,
            cpu_guarantee=None,
            mem_limit=None,
            mem_guarantee=None,
            node_selector=None,
            run_as_uid=1000,
            fs_gid=1000,
            image_pull_policy='IfNotPresent',
            image_pull_secret=None,
            labels={},
            lifecycle_hooks=None,
            init_containers=None,
        )) == {
            "metadata": {
                "name": "test",
                "labels": {},
            },
            "spec": {
                "securityContext": {
                    "runAsUser": 1000,
                    "fsGroup": 1000
                },
                "containers": [{
                    "env": [],
                    "name":
                    "notebook",
                    "image":
                    "jupyter/singleuser:latest",
                    "imagePullPolicy":
                    "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {},
                        "requests": {}
                    }
                }],
                'volumes': [],
            },
            "kind": "Pod",
            "apiVersion": "v1"
        }
예제 #3
0
def test_make_pod_with_env():
    """
    Test specification of a pod with custom environment variables
    """
    assert api_client.sanitize_for_serialization(
        make_pod(
            name='test',
            image_spec='jupyter/singleuser:latest',
            env={'TEST_KEY': 'TEST_VALUE'},
            volumes=[],
            volume_mounts=[],
            cmd=['jupyterhub-singleuser'],
            working_dir=None,
            port=8888,
            cpu_limit=None,
            cpu_guarantee=None,
            mem_limit=None,
            mem_guarantee=None,
            image_pull_policy='IfNotPresent',
            image_pull_secret=None,
            node_selector=None,
            run_as_uid=None,
            fs_gid=None,
            labels={},
            lifecycle_hooks=None,
            init_containers=None,
        )) == {
            "metadata": {
                "name": "test",
                "labels": {},
            },
            "spec": {
                "securityContext": {},
                "containers": [{
                    "env": [{
                        'name': 'TEST_KEY',
                        'value': 'TEST_VALUE'
                    }],
                    "name":
                    "notebook",
                    "image":
                    "jupyter/singleuser:latest",
                    "imagePullPolicy":
                    "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {},
                        "requests": {}
                    }
                }],
                'volumes': [],
            },
            "kind": "Pod",
            "apiVersion": "v1"
        }
예제 #4
0
def test_make_pod_with_extra_pod_config():
    """
    Test specification of a pod with initContainers
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        extra_pod_config={
            'tolerations': [
                {
                    'key': 'dedicated',
                    'operator': 'Equal',
                    'value': 'notebook'
                }
            ]
        }
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            'automountServiceAccountToken': False,
            "securityContext": {},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [{'name': 'no-api-access-please', 'mountPath': '/var/run/secrets/kubernetes.io/serviceaccount', 'readOnly': True}],
                    "resources": {
                        "limits": {
                        },
                        "requests": {
                        }
                    }
                }
            ],
            'volumes': [{'name': 'no-api-access-please', 'emptyDir': {}}],
            'tolerations': [
                {
                    'key': 'dedicated',
                    'operator': 'Equal',
                    'value': 'notebook'
                }
            ]
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #5
0
def test_make_pod_with_node_affinity_preferred():
    """
    Test specification of the simplest possible pod specification with non-empty node_affinity_preferred
    """
    node_affinity_preferred = [{
        "weight": 1,
        "preference": {
            "matchExpressions": [{
                "key": "hub.jupyter.org/node-purpose",
                "operator": "In",
                "values": ["user"],
            }],
        }
    }]
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        node_affinity_preferred=node_affinity_preferred
    )) == {
        "metadata": {
            "name": "test",
            "labels": {},
            "annotations": {}
        },
        "spec": {
            "automountServiceAccountToken": False,
            "securityContext": {},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {},
                        "requests": {}
                    }
                }
            ],
            'restartPolicy': 'OnFailure',
            "volumes": [],
            "affinity": {
                "nodeAffinity": {
                    "preferredDuringSchedulingIgnoredDuringExecution": node_affinity_preferred
                }
            }
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #6
0
def test_make_pod_with_pod_anti_affinity_required():
    """
    Test specification of the simplest possible pod specification with non-empty pod_anti_affinity_required
    """
    pod_anti_affinity_required = [{
        "labelSelector": {
            "matchExpressions": [{
                "key": "security",
                "operator": "In",
                "values": ["S1"],
            }]
        },
        "topologyKey": "failure-domain.beta.kubernetes.io/zone"
    }]
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        pod_anti_affinity_required=pod_anti_affinity_required
    )) == {
        "metadata": {
            "name": "test",
            "labels": {},
            "annotations": {}
        },
        "spec": {
            "automountServiceAccountToken": False,
            "securityContext": {},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {},
                        "requests": {}
                    }
                }
            ],
            'restartPolicy': 'OnFailure',
            "volumes": [],
            "affinity": {
                "podAntiAffinity": {
                    "requiredDuringSchedulingIgnoredDuringExecution": pod_anti_affinity_required
                }
            }
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #7
0
def test_make_pod_with_lifecycle():
    """
    Test specification of a pod with lifecycle
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        lifecycle_hooks={
            'preStop': {
                'exec': {
                    'command': ['/bin/sh', 'test']
                }
            }
        }
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            "securityContext": {},
            'automountServiceAccountToken': False,
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {
                        },
                        "requests": {
                        }
                    },
                    "lifecycle": {
                        "preStop": {
                            "exec": {
                                "command": ["/bin/sh", "test"]
                            }
                        }
                    }
                }
            ],
            'restartPolicy': 'OnFailure',
            'volumes': [],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #8
0
def test_make_pod_with_extra_resources():
    """
    Test specification of extra resources (like GPUs)
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cpu_limit=2,
        cpu_guarantee=1,
        extra_resource_limits={"nvidia.com/gpu": "5", "k8s.io/new-resource": "1"},
        extra_resource_guarantees={"nvidia.com/gpu": "3"},
        cmd=['jupyterhub-singleuser'],
        port=8888,
        mem_limit='1Gi',
        mem_guarantee='512Mi',
        image_pull_policy='IfNotPresent',
        image_pull_secret="myregistrykey",
        node_selector={"disk": "ssd"}
    )) == {
        "metadata": {
            "name": "test",
            "labels": {},
        },
        "spec": {
            "securityContext": {},
            "imagePullSecrets": [{"name": "myregistrykey"}],
            "nodeSelector": {"disk": "ssd"},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [{'name': 'no-api-access-please', 'mountPath': '/var/run/secrets/kubernetes.io/serviceaccount', 'readOnly': True}],
                    "resources": {
                        "limits": {
                            "cpu": 2,
                            "memory": '1Gi',
                            "nvidia.com/gpu": "5",
                            "k8s.io/new-resource": "1"
                        },
                        "requests": {
                            "cpu": 1,
                            "memory": '512Mi',
                            "nvidia.com/gpu": "3"
                        }
                    }
                }
            ],
            'volumes': [{'name': 'no-api-access-please', 'emptyDir': {}}],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #9
0
def test_make_pod_with_tolerations():
    """
    Test specification of the simplest possible pod specification with non-empty tolerations
    """
    tolerations = [
        {
            'key': 'hub.jupyter.org/dedicated',
            'operator': 'Equal',
            'value': 'user',
            'effect': 'NoSchedule'
        },
        {
            'key': 'key',
            'operator': 'Exists',
            'effect': 'NoSchedule'
        }
    ]
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        tolerations=tolerations
    )) == {
        "metadata": {
            "name": "test",
            "labels": {},
            "annotations": {}
        },
        "spec": {
            "automountServiceAccountToken": False,
            "securityContext": {},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {},
                        "requests": {}
                    }
                }
            ],
            'restartPolicy': 'OnFailure',
            'volumes': [],
            'tolerations': tolerations
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #10
0
def test_make_pod_resources_all():
    """
    Test specifying all possible resource limits & guarantees
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cpu_limit=2,
        cpu_guarantee=1,
        cmd=['jupyterhub-singleuser'],
        port=8888,
        mem_limit='1Gi',
        mem_guarantee='512Mi',
        image_pull_policy='IfNotPresent',
        image_pull_secret="myregistrykey",
        node_selector={"disk": "ssd"}
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            "securityContext": {},
            'automountServiceAccountToken': False,
            "imagePullSecrets": [{"name": "myregistrykey"}],
            "nodeSelector": {"disk": "ssd"},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {
                            "cpu": 2,
                            "memory": '1Gi'
                        },
                        "requests": {
                            "cpu": 1,
                            "memory": '512Mi'
                        }
                    }
                }
            ],
            'restartPolicy': 'OnFailure',
            'volumes': [],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #11
0
def test_set_pod_supplemental_gids():
    """
    Test specification of the simplest possible pod specification
    """
    assert api_client.sanitize_for_serialization(
        make_pod(
            name='test',
            image_spec='jupyter/singleuser:latest',
            cmd=['jupyterhub-singleuser'],
            port=8888,
            run_as_uid=1000,
            supplemental_gids=[100],
            image_pull_policy='IfNotPresent')) == {
                "metadata": {
                    "name": "test",
                    "annotations": {},
                    "labels": {},
                },
                "spec": {
                    "securityContext": {
                        "runAsUser": 1000,
                        "supplementalGroups": [100]
                    },
                    'automountServiceAccountToken':
                    False,
                    "containers": [{
                        "env": [],
                        "name":
                        "notebook",
                        "image":
                        "jupyter/singleuser:latest",
                        "imagePullPolicy":
                        "IfNotPresent",
                        "args": ["jupyterhub-singleuser"],
                        "ports": [{
                            "name": "notebook-port",
                            "containerPort": 8888
                        }],
                        'volumeMounts': [{
                            'name': 'no-api-access-please',
                            'mountPath':
                            '/var/run/secrets/kubernetes.io/serviceaccount',
                            'readOnly': True
                        }],
                        "resources": {
                            "limits": {},
                            "requests": {}
                        }
                    }],
                    'volumes': [{
                        'name': 'no-api-access-please',
                        'emptyDir': {}
                    }],
                },
                "kind": "Pod",
                "apiVersion": "v1"
            }
예제 #12
0
def test_make_pod_with_extra_containers():
    """
    Test specification of a pod with initContainers
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        extra_containers=[
            {
                'name': 'crontab',
                'image': 'supercronic',
                'command': ['/usr/local/bin/supercronic', '/etc/crontab']
            }
        ]
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            'automountServiceAccountToken': False,
            "securityContext": {},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {
                        },
                        "requests": {
                        }
                    },
                },
                {
                    'name': 'crontab',
                    'image': 'supercronic',
                    'command': ['/usr/local/bin/supercronic', '/etc/crontab']
                }
            ],
            'restartPolicy': 'OnFailure',
            'volumes': [],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #13
0
    def get_pod_manifest(self):
        """
        Make a pod manifest that will spawn current user's notebook pod.
        """
        if callable(self.singleuser_uid):
            singleuser_uid = yield gen.maybe_future(self.singleuser_uid(self))
        else:
            singleuser_uid = self.singleuser_uid

        if callable(self.singleuser_fs_gid):
            singleuser_fs_gid = yield gen.maybe_future(
                self.singleuser_fs_gid(self))
        else:
            singleuser_fs_gid = self.singleuser_fs_gid

        if self.cmd:
            real_cmd = self.cmd + self.get_args()
        else:
            real_cmd = None

        # Default set of labels, picked up from
        # https://github.com/kubernetes/helm/blob/master/docs/chart_best_practices/labels.md
        labels = {
            'heritage': 'jupyterhub',
            'component': 'singleuser-server',
            'app': 'jupyterhub',
            'hub.jupyter.org/username': escapism.escape(self.user.name)
        }

        if self.name:
            # FIXME: Make sure this is dns safe?
            labels['hub.jupyter.org/servername'] = self.name

        labels.update(self._expand_all(self.singleuser_extra_labels))

        return make_pod(name=self.pod_name,
                        cmd=real_cmd,
                        port=self.port,
                        image_spec=self.singleuser_image_spec,
                        image_pull_policy=self.singleuser_image_pull_policy,
                        image_pull_secret=self.singleuser_image_pull_secrets,
                        node_selector=self.singleuser_node_selector,
                        run_as_uid=singleuser_uid,
                        fs_gid=singleuser_fs_gid,
                        run_privileged=self.singleuser_privileged,
                        env=self.get_env(),
                        volumes=self._expand_all(self.volumes),
                        volume_mounts=self._expand_all(self.volume_mounts),
                        working_dir=self.singleuser_working_dir,
                        labels=labels,
                        cpu_limit=self.cpu_limit,
                        cpu_guarantee=self.cpu_guarantee,
                        mem_limit=self.mem_limit,
                        mem_guarantee=self.mem_guarantee,
                        lifecycle_hooks=self.singleuser_lifecycle_hooks,
                        init_containers=self.singleuser_init_containers,
                        service_account=self.singleuser_service_account)
예제 #14
0
def test_make_pod_with_lifecycle():
    """
    Test specification of a pod with lifecycle
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        lifecycle_hooks={
            'preStop': {
                'exec': {
                    'command': ['/bin/sh', 'test']
                }
            }
        }
    )) == {
        "metadata": {
            "name": "test",
            "labels": {},
        },
        "spec": {
            "securityContext": {},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [{'name': 'no-api-access-please', 'mountPath': '/var/run/secrets/kubernetes.io/serviceaccount', 'readOnly': True}],
                    "resources": {
                        "limits": {
                        },
                        "requests": {
                        }
                    },
                    "lifecycle": {
                        "preStop": {
                            "exec": {
                                "command": ["/bin/sh", "test"]
                            }
                        }
                    }
                }
            ],
            'volumes': [{'name': 'no-api-access-please', 'emptyDir': {}}],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #15
0
def test_make_pod_with_extra_containers():
    """
    Test specification of a pod with initContainers
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        extra_containers=[
            {
                'name': 'crontab',
                'image': 'supercronic',
                'command': ['/usr/local/bin/supercronic', '/etc/crontab']
            }
        ]
    )) == {
        "metadata": {
            "name": "test",
            "labels": {},
        },
        "spec": {
            "securityContext": {},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [{'name': 'no-api-access-please', 'mountPath': '/var/run/secrets/kubernetes.io/serviceaccount', 'readOnly': True}],
                    "resources": {
                        "limits": {
                        },
                        "requests": {
                        }
                    },
                },
                {
                    'name': 'crontab',
                    'image': 'supercronic',
                    'command': ['/usr/local/bin/supercronic', '/etc/crontab']
                }
            ],
            'volumes': [{'name': 'no-api-access-please', 'emptyDir': {}}],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #16
0
    def get_pod_manifest(self):
        """
        Make a pod manifest that will spawn current user's notebook pod.
        """
        if callable(self.singleuser_uid):
            singleuser_uid = yield gen.maybe_future(self.singleuser_uid(self))
        else:
            singleuser_uid = self.singleuser_uid

        if callable(self.singleuser_fs_gid):
            singleuser_fs_gid = yield gen.maybe_future(
                self.singleuser_fs_gid(self))
        else:
            singleuser_fs_gid = self.singleuser_fs_gid

        if self.cmd:
            real_cmd = self.cmd + self.get_args()
        else:
            real_cmd = None

        labels = self._build_pod_labels(
            self._expand_all(self.singleuser_extra_labels))
        annotations = self._expand_all(self.singleuser_extra_annotations)

        return make_pod(
            name=self.pod_name,
            cmd=real_cmd,
            port=self.port,
            image_spec=self.singleuser_image_spec,
            image_pull_policy=self.singleuser_image_pull_policy,
            image_pull_secret=self.singleuser_image_pull_secrets,
            node_selector=self.singleuser_node_selector,
            run_as_uid=singleuser_uid,
            fs_gid=singleuser_fs_gid,
            run_privileged=self.singleuser_privileged,
            env=self.get_env(),
            volumes=self._expand_all(self.volumes),
            volume_mounts=self._expand_all(self.volume_mounts),
            working_dir=self.singleuser_working_dir,
            labels=labels,
            annotations=annotations,
            cpu_limit=self.cpu_limit,
            cpu_guarantee=self.cpu_guarantee,
            mem_limit=self.mem_limit,
            mem_guarantee=self.mem_guarantee,
            extra_resource_limits=self.extra_resource_limits,
            extra_resource_guarantees=self.extra_resource_guarantees,
            lifecycle_hooks=self.singleuser_lifecycle_hooks,
            init_containers=self.singleuser_init_containers,
            service_account=self.singleuser_service_account,
            extra_container_config=self.singleuser_extra_container_config,
            extra_pod_config=self.singleuser_extra_pod_config,
            extra_containers=self.singleuser_extra_containers)
예제 #17
0
def test_run_privileged_container():
    """
    Test specification of the container to run as privileged
    """
    assert api_client.sanitize_for_serialization(
        make_pod(
            name='test',
            image_spec='jupyter/singleuser:latest',
            cmd=['jupyterhub-singleuser'],
            port=8888,
            run_privileged=True,
            image_pull_policy='IfNotPresent')) == {
                "metadata": {
                    "name": "test",
                    "labels": {},
                },
                "spec": {
                    "securityContext": {},
                    "containers": [{
                        "env": [],
                        "name":
                        "notebook",
                        "image":
                        "jupyter/singleuser:latest",
                        "imagePullPolicy":
                        "IfNotPresent",
                        "args": ["jupyterhub-singleuser"],
                        "ports": [{
                            "name": "notebook-port",
                            "containerPort": 8888
                        }],
                        "resources": {
                            "limits": {},
                            "requests": {}
                        },
                        "securityContext": {
                            "privileged": True,
                        },
                        'volumeMounts': [{
                            'name': 'no-api-access-please',
                            'mountPath':
                            '/var/run/secrets/kubernetes.io/serviceaccount',
                            'readOnly': True
                        }],
                    }],
                    'volumes': [{
                        'name': 'no-api-access-please',
                        'emptyDir': {}
                    }],
                },
                "kind": "Pod",
                "apiVersion": "v1"
            }
예제 #18
0
def test_make_pod_with_env():
    """
    Test specification of a pod with custom environment variables
    """
    assert api_client.sanitize_for_serialization(
        make_pod(
            name='test',
            image_spec='jupyter/singleuser:latest',
            env={'TEST_KEY': 'TEST_VALUE'},
            cmd=['jupyterhub-singleuser'],
            port=8888,
            image_pull_policy='IfNotPresent')) == {
                "metadata": {
                    "name": "test",
                    "labels": {},
                },
                "spec": {
                    "securityContext": {},
                    "containers": [{
                        "env": [{
                            'name': 'TEST_KEY',
                            'value': 'TEST_VALUE'
                        }],
                        "name":
                        "notebook",
                        "image":
                        "jupyter/singleuser:latest",
                        "imagePullPolicy":
                        "IfNotPresent",
                        "args": ["jupyterhub-singleuser"],
                        "ports": [{
                            "name": "notebook-port",
                            "containerPort": 8888
                        }],
                        'volumeMounts': [{
                            'name': 'no-api-access-please',
                            'mountPath':
                            '/var/run/secrets/kubernetes.io/serviceaccount',
                            'readOnly': True
                        }],
                        "resources": {
                            "limits": {},
                            "requests": {}
                        }
                    }],
                    'volumes': [{
                        'name': 'no-api-access-please',
                        'emptyDir': {}
                    }],
                },
                "kind": "Pod",
                "apiVersion": "v1"
            }
예제 #19
0
def pod(containers):
    p = make_pod(name='test',
                 image='image',
                 cmd=['jupyterhub-singleuser'],
                 port=8888,
                 image_pull_policy='IfNotPresent'
                 )
    p.metadata.labels['primehub.io/group'] = escape_to_primehub_label(_TEST_GROUP)
    p.spec.containers = containers
    p.status = V1PodStatus()
    p.status.phase = "Pending"
    return p
예제 #20
0
 def test_override_image_url(self):
     image = {
         'spec': {
             'url': "jupyter/test-notebook",
         }
     }
     self.spawner.apply_kubespawner_override(
         self.spawner.image_to_override(image, 0))
     pod = make_pod(name='test',
                    image=self.spawner.image,
                    cmd=['jupyterhub-singleuser'],
                    port=8888,
                    image_pull_policy='IfNotPresent')
     self.assertEqual(pod.spec.containers[0].image, 'jupyter/test-notebook')
     self.assertIsNone(pod.spec.image_pull_secrets)
예제 #21
0
    def test_override_image_pull_secret(self):
        image = {
            'spec': {
                'url': "jupyter/base-notebook",
                'pullSecret': "image-test"
            }
        }
        self.spawner.apply_kubespawner_override(
            self.spawner.image_to_override(image, 0))

        pod_def = dict(name='test',
                       image='jupyter/singleuser:latest',
                       cmd=['jupyterhub-singleuser'],
                       port=8888,
                       image_pull_policy='IfNotPresent')

        try:
            pod = make_pod(image_pull_secret=self.spawner.image_pull_secrets,
                           **pod_def)
        except Exception:
            pod = make_pod(image_pull_secrets=self.spawner.image_pull_secrets,
                           **pod_def)

        self.assertEqual(pod.spec.image_pull_secrets[0].name, 'image-test')
예제 #22
0
 def test_override_image_url_for_gpu_without_url_for_gpu(self):
     image = {
         'spec': {
             'url': "jupyter/test-notebook-cpu",
         }
     }
     self.spawner.apply_kubespawner_override(
         self.spawner.image_to_override(image, 1))
     pod = make_pod(name='test',
                    image=self.spawner.image,
                    cmd=['jupyterhub-singleuser'],
                    port=8888,
                    image_pull_policy='IfNotPresent')
     self.assertEqual(pod.spec.containers[0].image,
                      'jupyter/test-notebook-cpu')
예제 #23
0
def test_make_pod_with_env():
    """
    Test specification of a pod with custom environment variables
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        env={
            'TEST_KEY': 'TEST_VALUE'
        },
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent'
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            'automountServiceAccountToken': False,
            "securityContext": {},
            "containers": [
                {
                    "env": [{'name': 'TEST_KEY', 'value': 'TEST_VALUE'}],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {
                        },
                        "requests": {
                        }
                    }
                }
            ],
            'restartPolicy': 'OnFailure',
            'volumes': [],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
def test_set_pod_supplemental_gids():
    """
    Test specification of the simplest possible pod specification
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        run_as_uid=1000,
        supplemental_gids=[100],
        image_pull_policy='IfNotPresent'
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            "securityContext": {
                "runAsUser": 1000,
                "supplementalGroups": [100]
            },
            'automountServiceAccountToken': False,
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {},
                        "requests": {}
                    }
                }
            ],
            'restartPolicy': 'OnFailure',
            'volumes': [],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #25
0
def test_set_pod_uid_fs_gid():
    """
    Test specification of the simplest possible pod specification
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        run_as_uid=1000,
        fs_gid=1000,
        image_pull_policy='IfNotPresent'
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            "securityContext": {
                "runAsUser": 1000,
                "fsGroup": 1000
            },
            'automountServiceAccountToken': False,
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {},
                        "requests": {}
                    }
                }
            ],
            'restartPolicy': 'OnFailure',
            'volumes': [],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #26
0
def test_run_privileged_container():
    """
    Test specification of the container to run as privileged
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        run_privileged=True,
        image_pull_policy='IfNotPresent'
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            "securityContext": {},
            'automountServiceAccountToken': False,
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    "resources": {
                        "limits": {},
                        "requests": {}
                    },
                    "securityContext": {
                        "privileged": True,
                    },
                    'volumeMounts': [],
                }
            ],
            'restartPolicy': 'OnFailure',
            'volumes': [],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #27
0
def test_make_pod_with_image_pull_secrets():
    """
    Test specification of the simplest possible pod specification
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        image_pull_secret='super-sekrit'
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            "securityContext": {},
            'automountServiceAccountToken': False,
            "imagePullSecrets": [
                {'name': 'super-sekrit'}
            ],
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {},
                        "requests": {}
                    }
                }
            ],
            'volumes': [],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #28
0
def test_make_pod_with_priority_class_name():
    """
    Test specification of the simplest possible pod specification with non-default priorityClassName set
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        priority_class_name='my-custom-priority-class'
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            "securityContext": {},
            'automountServiceAccountToken': False,
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {},
                        "requests": {}
                    }
                }
            ],
            'restartPolicy': 'OnFailure',
            'volumes': [],
            'priorityClassName': 'my-custom-priority-class',
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
def test_make_pod_with_scheduler_name():
    """
    Test specification of the simplest possible pod specification with non-default scheduler name
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        scheduler_name='my-custom-scheduler'
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            "securityContext": {},
            'automountServiceAccountToken': False,
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {},
                        "requests": {}
                    }
                }
            ],
            'restartPolicy': 'OnFailure',
            'volumes': [],
            'schedulerName': 'my-custom-scheduler',
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #30
0
    def get_pod_manifest(self):
        """
        Make a pod manifest that will spawn current user's notebook pod.
        """

        if callable(self.uid):
            uid = yield gen.maybe_future(self.uid(self))
        else:
            uid = self.uid

        if callable(self.gid):
            gid = yield gen.maybe_future(self.gid(self))
        else:
            gid = self.gid

        if callable(self.fs_gid):
            fs_gid = yield gen.maybe_future(self.fs_gid(self))
        else:
            fs_gid = self.fs_gid

        if callable(self.supplemental_gids):
            supplemental_gids = yield gen.maybe_future(
                self.supplemental_gids(self))
        else:
            supplemental_gids = self.supplemental_gids

        if self.cmd:
            real_cmd = self.cmd + self.get_args()
        else:
            real_cmd = None

        labels = self._build_pod_labels(self._expand_all(self.extra_labels))
        annotations = self._build_common_annotations(
            self._expand_all(self.extra_annotations))

        # The above was from the superclass.
        # This part is our custom LSST stuff.

        if os.getenv("ALLOW_DASK_SPAWN"):
            self.service_account = 'dask'

        pod_name = self.pod_name
        image = (self.image or os.getenv("LAB_IMAGE")
                 or "lsstsqre/sciplat-lab:latest")
        image_name = image
        tag = "latest"
        size = None
        image_size = None
        # First pulls can be really slow for the LSST stack containers,
        #  so let's give it a big timeout
        self.http_timeout = 60 * 15
        self.start_timeout = 60 * 15
        # We are running the Lab at the far end, not the old Notebook
        self.default_url = '/lab'
        self.image_pull_policy = 'Always'
        clear_dotlocal = False
        if self.user_options:
            self.log.debug(
                "user_options: %s" %
                json.dumps(self.user_options, sort_keys=True, indent=4))
            if self.user_options.get('kernel_image'):
                image = self.user_options.get('kernel_image')
                colon = image.find(':')
                if colon > -1:
                    imgname = image[:colon]
                    tag = image[(colon + 1):]
                    if tag == "recommended" or tag.startswith("latest"):
                        self.log.info("Resolving tag '{}'".format(tag))
                        if self._scanner:
                            qtag = self._scanner.resolve_tag(tag)
                            if qtag:
                                tag = qtag
                                image = imgname + ":" + tag
                                image_name = image
                            else:
                                self.log.warning(
                                    "Failed to resolve tag '{}'".format(tag))
                    self.log.debug("Image name: %s ; tag: %s" % (imgname, tag))
                    if tag == "__custom":
                        cit = self.user_options.get('image_tag')
                        if cit:
                            image = imgname + ":" + cit
                image_name = image
                self.log.info("Replacing image from options form: %s" % image)
                size = self.user_options.get('size')
                if size:
                    image_size = self._sizemap[size]
                clear_dotlocal = self.user_options.get('clear_dotlocal')
        mem_limit = os.getenv('LAB_MEM_LIMIT') or '2048M'
        cpu_limit = os.getenv('LAB_CPU_LIMIT') or 1.0
        if image_size:
            mem_limit = str(int(image_size["mem"])) + "M"
            cpu_limit = float(image_size["cpu"])
        if type(cpu_limit) is str:
            cpu_limit = float(cpu_limit)
        self.mem_limit = mem_limit
        self.cpu_limit = cpu_limit
        mem_guar = os.getenv('LAB_MEM_GUARANTEE') or '64K'
        cpu_guar = os.getenv('LAB_CPU_GUARANTEE') or 0.02
        if type(cpu_guar) is str:
            cpu_guar = float(cpu_guar)
        # Tiny gets the "basically nothing" above (or the explicit
        #  guarantee).  All others get 1/LAB_SIZE_RANGE times their
        #  maximum, with a default of 1/4.
        size_range = os.getenv('LAB_SIZE_RANGE') or 4.0
        if type(size_range) is str:
            size_range = float(size_range)
        if image_size and size != 'tiny':
            mem_guar = int(image_size["mem"] / size_range)
            cpu_guar = float(image_size["cpu"] / size_range)
        self.mem_guarantee = mem_guar
        self.cpu_guarantee = cpu_guar
        self.log.debug("Image: {}".format(image))
        self.image = image
        # Parse the image name + tag
        i_l = image.split("/")
        if len(i_l) == 1:
            repo_tag = i_l[0]
        else:
            repo_tag = i_l[1]
        repo = repo_tag.split(":")[0]
        rt_tag = tag.replace('_', '-')
        abbr_pn = repo
        if repo == 'sciplat-lab':
            # Saving characters because tags can be long
            abbr_pn = "nb"
        pn_template = abbr_pn + "-{username}-" + rt_tag
        pod_name = self._expand_user_properties(pn_template)
        self.pod_name = pod_name
        self.log.info("Replacing pod name from options form: %s" % pod_name)
        pod_env = self.get_env()
        idle_timeout = int(os.getenv('LAB_IDLE_TIMEOUT') or 43200)
        if idle_timeout > 0 and 'JUPYTERLAB_IDLE_TIMEOUT' not in pod_env:
            pod_env['JUPYTERLAB_IDLE_TIMEOUT'] = str(idle_timeout)
        if os.getenv('RESTRICT_DASK_NODES'):
            pod_env['RESTRICT_DASK_NODES'] = "true"
        if os.getenv('LAB_NODEJS_MAX_MEM'):
            pod_env['NODE_OPTIONS'] = ("--max-old-space-size=" +
                                       os.getenv('LAB_NODEJS_MAX_MEM'))
        external_hub_url = os.getenv('EXTERNAL_HUB_URL')
        hub_route = os.getenv('HUB_ROUTE')
        while (hub_route.endswith('/') and hub_route != "/"):
            hub_route = hub_route[:-1]
        if not external_hub_url:
            oauth_callback = os.getenv('OAUTH_CALLBACK_URL')
            endstr = "/hub/oauth_callback"
            if oauth_callback and oauth_callback.endswith(endstr):
                external_hub_url = oauth_callback[:-len(endstr)]
        # Guaranteed external endpoints
        pod_env['EXTERNAL_URL'] = external_hub_url
        pod_env['EXTERNAL_HUB_URL'] = external_hub_url
        external_instance_url = os.getenv('EXTERNAL_INSTANCE_URL')
        if not external_instance_url:
            if external_hub_url.endswith(hub_route):
                external_instance_url = external_hub_url[:-len(hub_route)]
        pod_env['EXTERNAL_INSTANCE_URL'] = external_instance_url
        if os.getenv('DEBUG'):
            pod_env['DEBUG'] = os.getenv('DEBUG')
        if clear_dotlocal:
            pod_env['CLEAR_DOTLOCAL'] = "true"
        # Add service routes
        # Check if we need trailing slash anymore for firefly
        hub_route = os.getenv('HUB_ROUTE') or "/nb"
        firefly_route = os.getenv('FIREFLY_ROUTE') or "/firefly/"
        js9_route = os.getenv('JS9_ROUTE') or "/js9"
        api_route = os.getenv('API_ROUTE') or "/api"
        tap_route = os.getenv('TAP_ROUTE') or "/api/tap"
        soda_route = os.getenv('SODA_ROUTE') or "/api/image/soda"
        pod_env['HUB_ROUTE'] = hub_route
        pod_env['FIREFLY_ROUTE'] = firefly_route
        pod_env['JS9_ROUTE'] = js9_route
        pod_env['API_ROUTE'] = api_route
        pod_env['TAP_ROUTE'] = tap_route
        pod_env['SODA_ROUTE'] = soda_route
        # Optional external endpoints
        for i in ['firefly', 'js9', 'api', 'tap', 'soda']:
            envvar = 'EXTERNAL_' + i.upper() + '_URL'
            if os.getenv(envvar):
                pod_env[envvar] = os.getenv(envvar)
        auto_repo_urls = os.getenv('AUTO_REPO_URLS')
        if auto_repo_urls:
            pod_env['AUTO_REPO_URLS'] = auto_repo_urls

        vollist = self._get_volume_list()
        self._splice_volumes(vollist)

        pod_env['DASK_VOLUME_B64'] = self._get_dask_volume_b64()
        if self._quota:
            pod_env['NAMESPACE_CPU_LIMIT'] = self._quota["limits.cpu"]
            nmlimit = self._quota["limits.memory"]
            if nmlimit[-2:] == "Mi":
                # Not technically correct, but matches mem_limit
                nmlimit = nmlimit[:-2] + "M"
            pod_env['NAMESPACE_MEM_LIMIT'] = nmlimit
        pod_env['CPU_LIMIT'] = str(cpu_limit)
        pod_env['MEM_LIMIT'] = str(mem_limit)
        self.image = image
        self.log.debug("Image: %s" %
                       json.dumps(image, indent=4, sort_keys=True))
        self.log.debug("Pod env: %s" %
                       json.dumps(pod_env, indent=4, sort_keys=True))
        if not pod_env.get("EXTERNAL_UID"):
            raise ValueError("EXTERNAL_UID is not set!")
        self.log.debug("About to run make_pod()")

        pod = make_pod(
            name=self.pod_name,
            cmd=real_cmd,
            port=self.port,
            image=self.image,
            image_pull_policy=self.image_pull_policy,
            image_pull_secret=self.image_pull_secrets,
            node_selector=self.node_selector,
            run_as_uid=uid,
            run_as_gid=gid,
            fs_gid=fs_gid,
            supplemental_gids=supplemental_gids,
            run_privileged=self.privileged,
            # env is locally-modified
            env=pod_env,
            volumes=self._expand_all(self.volumes),
            volume_mounts=self._expand_all(self.volume_mounts),
            working_dir=self.working_dir,
            labels=labels,
            annotations=annotations,
            cpu_limit=self.cpu_limit,
            cpu_guarantee=self.cpu_guarantee,
            mem_limit=self.mem_limit,
            mem_guarantee=self.mem_guarantee,
            extra_resource_limits=self.extra_resource_limits,
            extra_resource_guarantees=self.extra_resource_guarantees,
            lifecycle_hooks=self.lifecycle_hooks,
            init_containers=self._expand_all(self.init_containers),
            service_account=self.service_account,
            extra_container_config=self.extra_container_config,
            extra_pod_config=self.extra_pod_config,
            extra_containers=self.extra_containers,
            node_affinity_preferred=self.node_affinity_preferred,
            node_affinity_required=self.node_affinity_required,
            pod_affinity_preferred=self.pod_affinity_preferred,
            pod_affinity_required=self.pod_affinity_required,
            pod_anti_affinity_preferred=self.pod_anti_affinity_preferred,
            pod_anti_affinity_required=self.pod_anti_affinity_required,
            priority_class_name=self.priority_class_name,
            logger=self.log,
        )
        return pod
예제 #31
0
def test_make_pod_with_init_containers():
    """
    Test specification of a pod with initContainers
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        init_containers=[
            {
                'name': 'init-myservice',
                'image': 'busybox',
                'command': ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
            },
            {
                'name': 'init-mydb',
                'image': 'busybox',
                'command': ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
            }
        ]
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            'automountServiceAccountToken': False,
            "securityContext": {},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [{'name': 'no-api-access-please', 'mountPath': '/var/run/secrets/kubernetes.io/serviceaccount', 'readOnly': True}],
                    "resources": {
                        "limits": {
                        },
                        "requests": {
                        }
                    },
                }
            ],
            "initContainers": [
                {
                    "name": "init-myservice",
                    "image": "busybox",
                    "command": ["sh", "-c",
                                "until nslookup myservice; do echo waiting for myservice; sleep 2; done;"]
                },
                {
                    "name": "init-mydb",
                    "image": "busybox",
                    "command": ["sh", "-c", "until nslookup mydb; do echo waiting for mydb; sleep 2; done;"]
                }
            ],
            'volumes': [{'name': 'no-api-access-please', 'emptyDir': {}}],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
def test_make_pod_with_init_containers():
    """
    Test specification of a pod with initContainers
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        init_containers=[
            {
                'name': 'init-myservice',
                'image': 'busybox',
                'command': ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
            },
            {
                'name': 'init-mydb',
                'image': 'busybox',
                'command': ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
            }
        ]
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            'automountServiceAccountToken': False,
            "securityContext": {},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {
                        },
                        "requests": {
                        }
                    },
                }
            ],
            "initContainers": [
                {
                    "name": "init-myservice",
                    "image": "busybox",
                    "command": ["sh", "-c",
                                "until nslookup myservice; do echo waiting for myservice; sleep 2; done;"]
                },
                {
                    "name": "init-mydb",
                    "image": "busybox",
                    "command": ["sh", "-c", "until nslookup mydb; do echo waiting for mydb; sleep 2; done;"]
                }
            ],
            'restartPolicy': 'OnFailure',
            'volumes': [],
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
def test_make_pod_with_extra_pod_config():
    """
    Test specification of a pod with initContainers
    """
    assert api_client.sanitize_for_serialization(make_pod(
        name='test',
        image_spec='jupyter/singleuser:latest',
        cmd=['jupyterhub-singleuser'],
        port=8888,
        image_pull_policy='IfNotPresent',
        tolerations=[{
            'key': 'wrong_toleration',
            'operator': 'Equal',
            'value': 'wrong_value'
        }],
        extra_pod_config={
            'dns_policy': 'ClusterFirstWithHostNet',
            'restartPolicy': 'Always',
            'tolerations': [{
                'key': 'correct_toleration',
                'operator': 'Equal',
                'value': 'correct_value'
            }],
        },
    )) == {
        "metadata": {
            "name": "test",
            "annotations": {},
            "labels": {},
        },
        "spec": {
            'automountServiceAccountToken': False,
            "securityContext": {},
            "containers": [
                {
                    "env": [],
                    "name": "notebook",
                    "image": "jupyter/singleuser:latest",
                    "imagePullPolicy": "IfNotPresent",
                    "args": ["jupyterhub-singleuser"],
                    "ports": [{
                        "name": "notebook-port",
                        "containerPort": 8888
                    }],
                    'volumeMounts': [],
                    "resources": {
                        "limits": {
                        },
                        "requests": {
                        }
                    }
                }
            ],
            'volumes': [],
            'dnsPolicy': 'ClusterFirstWithHostNet',
            'restartPolicy': 'Always',
            'tolerations': [
                {
                    'key': 'correct_toleration',
                    'operator': 'Equal',
                    'value': 'correct_value'
                }
            ]
        },
        "kind": "Pod",
        "apiVersion": "v1"
    }
예제 #34
0
    def get_pod_manifest(self):
        """
        Make a pod manifest that will spawn current user's notebook pod.
        """
        uid = gen.maybe_future(self.uid(self)) if callable(
            self.uid) else self.uid
        fs_gid = gen.maybe_future(self.fs_gid(self)) if callable(
            self.fs_gid) else self.fs_gid
        real_cmd = self.cmd + self.get_args() if self.cmd else None

        # Default set of labels, picked up from
        # https://github.com/kubernetes/helm/blob/master/docs/chart_best_practices/labels.md
        labels = {
            'heritage': 'jupyterhub',
            'component': 'singleuser-server',
            'app': 'jupyterhub',
            'hub.jupyter.org/username': escapism.escape(self.user.name)
        }

        labels.update(self._expand_all(self.extra_labels))

        pod_name = self.pod_name
        image_spec = (self.image or os.getenv("LAB_IMAGE"))
        image_name = image_spec
        if self.user_options:
            if self.user_options.get('kernel_image'):
                image_spec = self.user_options.get('kernel_image')
                image_name = image_spec
                self.log.info("Replacing image spec from options form: %s" %
                              image_spec)
        self.image = image_spec
        s_idx = image_spec.find('/')
        c_idx = image_spec.find(':')
        tag = "latest"
        if s_idx != -1:
            image_name = image_spec[(s_idx + 1):]
            if c_idx > 0:
                image_name = image_spec[(s_idx + 1):c_idx]
                tag = image_spec[(c_idx + 1):].replace('_', '.')
        pn_template = image_name + "-{username}-" + tag
        # self.log.info('running image: %s' % (image_name,))

        auth_state = yield self.user.get_auth_state()
        # self.log.info("AuthState: %s"%(auth_state,))
        if auth_state and "id" in auth_state:
            if auth_state["id"] != self.user.id:
                self.log.info("Updating userid from %d to %d" %
                              (self.user.id, auth_state["id"]))

        pod_name = self._expand_user_properties(pn_template)
        self.pod_name = pod_name
        # self.log.info("Replacing pod name from options form: %s" % pod_name)

        # ast chance to overload some env variables for the notebox
        pod_env = self.get_env()

        idle_timeout = int(os.getenv('LAB_IDLE_TIMEOUT') or 43200)
        if idle_timeout > 0 and 'JUPYTERLAB_IDLE_TIMEOUT' not in pod_env:
            pod_env['JUPYTERLAB_IDLE_TIMEOUT'] = str(idle_timeout)

        oauth_callback = os.getenv('OAUTH_CALLBACK_URL')
        endstr = "/hub/oauth_callback"
        if oauth_callback and oauth_callback.endswith(endstr):
            pod_env['EXTERNAL_URL'] = oauth_callback[:-len(endstr)]

        # use the hub service instead of pod
        if os.getenv('HUB_SERVICE_HOST') and os.getenv('HUB_SERVICE_PORT_API'):
            pod_env['JUPYTERHUB_API_URL'] = "http://%s:%s/hub/api" % (
                os.getenv('HUB_SERVICE_HOST'),
                os.getenv('HUB_SERVICE_PORT_API'))

        # inject google drive client id
        if os.getenv('JUPYTERLAB_GOOGLE_OAUTH_CLIENTID'):
            pod_env['JUPYTERLAB_GOOGLE_OAUTH_CLIENTID'] = os.getenv(
                'JUPYTERLAB_GOOGLE_OAUTH_CLIENTID')

        # determine which labels to schedule the jupyterlab pod on
        # get names of groups
        gnames = [i.split(':')[0] for i in self.user_gids]

        spawn_on = {}
        volumes = []
        volume_mounts = []
        config = {}
        spec = {}
        with open(self.node_selector_config_file, 'r') as f:
            config = yaml.safe_load(f)
            if 'node_defaults' in config:
                this = config['node_defaults']
                if 'spawn_on' in this:
                    spawn_on = this['spawn_on']
                if 'spec' in this:
                    spec = this['spec']

        # self.log.info("node-selectors config: %s" % (config,))
        if 'node_selectors' in config:
            for idx, item in enumerate(config['node_selectors']):
                if 'filter' in item:
                    this = item['filter']
                    matching = []

                    self.log.info("checking filter: %s" % (this, ))
                    for n in ('gnames', 'images', 'uid'):
                        if n in this:
                            a = []
                            if n == 'gnames':
                                a = gnames
                            elif n == 'images':
                                a = [
                                    image_name,
                                ]
                            # TODO: hwo to get the uid?
                            self.log.info("  %s against %s" % (n, a))
                            if set(this[n]).intersection(a):
                                matching.append(True)
                            else:
                                matching.append(False)
                    self.log.info(" matching: %s" % (matching, ))
                    if False in matching:
                        continue
                    else:
                        spawn_on = item['spawn_on']
                        spec = item['spec']
                        if 'cpu' in spec:
                            self.cpu_limit = spec['cpu']
                            #self.cpu_guarantee = spec['cpu']
                        if 'memory' in spec:
                            self.mem_limit = spec['memory']
                            #self.mem_guarantee = spec['memory']
                        if 'env' in spec:
                            for k, v in spec['env'].items():
                                pod_env[k] = str(v)
                        self.log.info(" using spec: %s" % (spec, ))
                        break

        self.log.info("spawning pod %s on %s, spec %s" %
                      (pod_name, spawn_on, spec))
        return make_pod(
            name=self.pod_name,
            image=self.image,
            image_pull_policy=self.image_pull_policy,
            image_pull_secret=self.image_pull_secrets,
            port=self.port,
            cmd=real_cmd,
            node_selector=spawn_on,
            run_as_uid=uid,
            fs_gid=fs_gid,
            run_privileged=self.privileged,
            env=pod_env,
            volumes=self._expand_all(spec['volumes'])
            if 'volumes' in spec else self._expand_all([]),
            volume_mounts=self._expand_all(spec['volume_mounts'])
            if 'volume_mounts' in spec else self._expand_all([]),
            working_dir=self.working_dir,
            labels=labels,
            cpu_limit=self.cpu_limit,
            cpu_guarantee=self.cpu_guarantee,
            mem_limit=self.mem_limit,
            mem_guarantee=self.mem_guarantee,
            extra_resource_limits=spec['extra_resource_limits']
            if 'extra_resource_limits' in spec else {},
            lifecycle_hooks=self.lifecycle_hooks,
            init_containers=self.init_containers,
            service_account=None,
            extra_pod_config=spec['extra_pod_config']
            if 'extra_pod_config' in spec else {},
            # extra_container_config = { 'securityContext': { 'capabilities': { 'add': [ 'ALL',  ] }  } },
            extra_container_config=spec['extra_container_config']
            if 'extra_container_config' in spec else {},
        )