def make_service(
    name,
    port,
    labels=None,
    annotations=None,
):
    """
    Make a k8s service specification for fronting the pod running a user notebook.

    Parameters
    ----------
    name:
        Name of the service. Must be a valid DNS label.
    port:
        The port to which the service binds.
    labels:
        Labels to add to the spawned service.
    annotations:
        Annotations to add to the spawned service.

    """
    return V1Service(
            kind='Service',
            metadata=V1ObjectMeta(name=name, labels=labels, annotations=annotations),
            spec=V1ServiceSpec(
                type='ClusterIP',
                selector={'hub.jupyter.org/server-name': name},
                ports=[V1ServicePort(port=port, target_port=port)]
            )
        )
Example #2
0
def make_ingress(name, routespec, target, data):
    """
    Returns an ingress, service, endpoint object that'll work for this service
    """
    meta = V1ObjectMeta(name=name,
                        annotations={
                            'hub.jupyter.org/proxy-data': json.dumps(data),
                            'hub.jupyter.org/proxy-routespec': routespec,
                            'hub.jupyter.org/proxy-target': target
                        },
                        labels={
                            'heritage': 'jupyterhub',
                            'component': 'singleuser-server',
                            'hub.jupyter.org/proxy-route': 'true'
                        })

    if routespec.startswith('/'):
        host = None
        path = routespec
    else:
        host, path = routespec.split('/', 1)

    target_parts = urlparse(target)

    target_ip = target_parts.hostname
    target_port = target_parts.port

    # Make endpoint object
    endpoint = V1Endpoints(kind='Endpoints',
                           metadata=meta,
                           subsets=[
                               V1EndpointSubset(
                                   addresses=[V1EndpointAddress(ip=target_ip)],
                                   ports=[V1EndpointPort(port=target_port)])
                           ])

    # Make service object
    service = V1Service(
        kind='Service',
        metadata=meta,
        spec=V1ServiceSpec(
            ports=[V1ServicePort(port=target_port, target_port=target_port)]))

    # Make Ingress object
    ingress = V1beta1Ingress(
        kind='Ingress',
        metadata=meta,
        spec=V1beta1IngressSpec(rules=[
            V1beta1IngressRule(host=host,
                               http=V1beta1HTTPIngressRuleValue(paths=[
                                   V1beta1HTTPIngressPath(
                                       path=path,
                                       backend=V1beta1IngressBackend(
                                           service_name=name,
                                           service_port=target_port))
                               ]))
        ]))

    return endpoint, service, ingress
Example #3
0
def test_get_app_service_node_port(mocker):
    test_node_port = 1234
    get_app_services_mock = mocker.patch('util.k8s.k8s_info.get_app_services')
    get_app_services_mock.return_value = [
        V1Service(spec=V1ServiceSpec(ports=[
            V1ServicePort(node_port=test_node_port, port=test_node_port)
        ]))
    ]
    assert get_app_service_node_port(
        NAUTAAppNames.DOCKER_REGISTRY) == test_node_port
Example #4
0
def make_service(
    name,
    port,
    servername,
    owner_references,
    labels=None,
    annotations=None,
):
    """
    Make a k8s service specification for using dns to communicate with the notebook.

    Parameters
    ----------
    name:
        Name of the service. Must be unique within the namespace the object is
        going to be created in.
    env:
        Dictionary of environment variables.
    labels:
        Labels to add to the service.
    annotations:
        Annotations to add to the service.

    """

    metadata = V1ObjectMeta(
        name=name,
        annotations=(annotations or {}).copy(),
        labels=(labels or {}).copy(),
        owner_references=owner_references,
    )

    service = V1Service(
        kind='Service',
        metadata=metadata,
        spec=V1ServiceSpec(
            type='ClusterIP',
            ports=[V1ServicePort(port=port, target_port=port)],
            selector={
                'component':
                'singleuser-server',
                'hub.jupyter.org/servername':
                servername,
                'hub.jupyter.org/username':
                metadata.labels['hub.jupyter.org/username'],
            },
        ),
    )

    return service
Example #5
0
def make_ingress(name, routespec, target, labels, data):
    """
    Returns an ingress, service, endpoint object that'll work for this service
    """

    # move beta imports here,
    # which are more sensitive to kubernetes version
    # and will change when they move out of beta
    # because of the API changes in 1.16, the import is tried conditionally
    # to keep compatibility with older K8S versions

    try:
        from kubernetes.client.models import (
            ExtensionsV1beta1Ingress,
            ExtensionsV1beta1IngressSpec,
            ExtensionsV1beta1IngressRule,
            ExtensionsV1beta1HTTPIngressRuleValue,
            ExtensionsV1beta1HTTPIngressPath,
            ExtensionsV1beta1IngressBackend,
        )
    except ImportError:
        from kubernetes.client.models import (
            V1beta1Ingress as ExtensionsV1beta1Ingress, V1beta1IngressSpec as
            ExtensionsV1beta1IngressSpec, V1beta1IngressRule as
            ExtensionsV1beta1IngressRule, V1beta1HTTPIngressRuleValue as
            ExtensionsV1beta1HTTPIngressRuleValue, V1beta1HTTPIngressPath as
            ExtensionsV1beta1HTTPIngressPath, V1beta1IngressBackend as
            ExtensionsV1beta1IngressBackend)

    meta = V1ObjectMeta(
        name=name,
        annotations={
            'hub.jupyter.org/proxy-data': json.dumps(data),
            'hub.jupyter.org/proxy-routespec': routespec,
            'hub.jupyter.org/proxy-target': target
        },
        labels=labels,
    )

    if routespec.startswith('/'):
        host = None
        path = routespec
    else:
        host, path = routespec.split('/', 1)

    target_parts = urlparse(target)

    target_ip = target_parts.hostname
    target_port = target_parts.port

    target_is_ip = re.match(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$',
                            target_ip) is not None

    # Make endpoint object
    if target_is_ip:
        endpoint = V1Endpoints(
            kind='Endpoints',
            metadata=meta,
            subsets=[
                V1EndpointSubset(addresses=[V1EndpointAddress(ip=target_ip)],
                                 ports=[V1EndpointPort(port=target_port)])
            ])
    else:
        endpoint = None

    # Make service object
    if target_is_ip:
        service = V1Service(kind='Service',
                            metadata=meta,
                            spec=V1ServiceSpec(type='ClusterIP',
                                               external_name='',
                                               ports=[
                                                   V1ServicePort(
                                                       port=target_port,
                                                       target_port=target_port)
                                               ]))
    else:
        service = V1Service(
            kind='Service',
            metadata=meta,
            spec=V1ServiceSpec(
                type='ExternalName',
                external_name=target_ip,
                cluster_ip='',
                ports=[
                    V1ServicePort(port=target_port, target_port=target_port)
                ],
            ),
        )

    # Make Ingress object
    ingress = ExtensionsV1beta1Ingress(
        kind='Ingress',
        metadata=meta,
        spec=ExtensionsV1beta1IngressSpec(rules=[
            ExtensionsV1beta1IngressRule(
                host=host,
                http=ExtensionsV1beta1HTTPIngressRuleValue(paths=[
                    ExtensionsV1beta1HTTPIngressPath(
                        path=path,
                        backend=ExtensionsV1beta1IngressBackend(
                            service_name=name, service_port=target_port))
                ]))
        ]))

    return endpoint, service, ingress
Example #6
0
def make_ingress(name, routespec, target, data):
    """
    Returns an ingress, service, endpoint object that'll work for this service
    """

    # move beta imports here,
    # which are more sensitive to kubernetes version
    # and will change when they move out of beta
    from kubernetes.client.models import (
        V1beta1Ingress,
        V1beta1IngressSpec,
        V1beta1IngressRule,
        V1beta1HTTPIngressRuleValue,
        V1beta1HTTPIngressPath,
        V1beta1IngressBackend,
    )

    meta = V1ObjectMeta(name=name,
                        annotations={
                            'hub.jupyter.org/proxy-data': json.dumps(data),
                            'hub.jupyter.org/proxy-routespec': routespec,
                            'hub.jupyter.org/proxy-target': target
                        },
                        labels={
                            'heritage': 'jupyterhub',
                            'component': 'singleuser-server',
                            'hub.jupyter.org/proxy-route': 'true'
                        })

    if routespec.startswith('/'):
        host = None
        path = routespec
    else:
        host, path = routespec.split('/', 1)

    target_parts = urlparse(target)

    target_ip = target_parts.hostname
    target_port = target_parts.port

    target_is_ip = re.match('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$',
                            target_ip) is not None

    # Make endpoint object
    if target_is_ip:
        endpoint = V1Endpoints(
            kind='Endpoints',
            metadata=meta,
            subsets=[
                V1EndpointSubset(addresses=[V1EndpointAddress(ip=target_ip)],
                                 ports=[V1EndpointPort(port=target_port)])
            ])
    else:
        endpoint = None

    # Make service object
    if target_is_ip:
        service = V1Service(kind='Service',
                            metadata=meta,
                            spec=V1ServiceSpec(type='ClusterIP',
                                               external_name='',
                                               ports=[
                                                   V1ServicePort(
                                                       port=target_port,
                                                       target_port=target_port)
                                               ]))
    else:
        service = V1Service(
            kind='Service',
            metadata=meta,
            spec=V1ServiceSpec(
                type='ExternalName',
                external_name=target_ip,
                cluster_ip='',
                ports=[
                    V1ServicePort(port=target_port, target_port=target_port)
                ],
            ),
        )

    # Make Ingress object
    ingress = V1beta1Ingress(
        kind='Ingress',
        metadata=meta,
        spec=V1beta1IngressSpec(rules=[
            V1beta1IngressRule(host=host,
                               http=V1beta1HTTPIngressRuleValue(paths=[
                                   V1beta1HTTPIngressPath(
                                       path=path,
                                       backend=V1beta1IngressBackend(
                                           service_name=name,
                                           service_port=target_port))
                               ]))
        ]))

    return endpoint, service, ingress
Example #7
0
    def start(self):
        """Set custom configuration during start before calling the super.start method of Dockerspawner"""

        self.saved_user_options = self.user_options

        if self.user_options.get(utils.OPTION_IMAGE):
            self.image = self.user_options.get(utils.OPTION_IMAGE)

        # Set request explicitly to 0, otherwise Kubernetes will set it to the same amount as limit
        # self.cpu_guarantee / self.mem_guarantee cannot be directly used, as they are of type ByteSpecification and, for example, 0G will be transformed to 0 which will not pass
        # the 'if cpu_guarantee' check (see https://github.com/jupyterhub/kubespawner/blob/8a6d66e04768565c0fc56c790a5fc42bfee634ec/kubespawner/objects.py#L279).
        # Hence, set it via extra_resource_guarantees.
        self.extra_resource_guarantees = {"cpu": 0, "memory": "0G"}
        if self.user_options.get(utils.OPTION_CPU_LIMIT):
            self.cpu_limit = float(
                self.user_options.get(utils.OPTION_CPU_LIMIT))

        if self.user_options.get(utils.OPTION_MEM_LIMIT):
            memory = str(self.user_options.get(utils.OPTION_MEM_LIMIT)) + "G"
            self.mem_limit = memory.upper().replace("GB", "G").replace(
                "KB", "K").replace("MB", "M").replace("TB", "T")

        #if self.user_options.get('is_mount_volume') == 'on':
        # {username} and {servername} will be automatically replaced by DockerSpawner with the right values as in template_namespace
        #    self.volumes = {'jhub-user-{username}{servername}': "/workspace"}

        # set default label 'origin' to know for sure which containers where started via the hub
        #self.extra_labels['pod_name'] = self.pod_name
        if self.user_options.get(utils.OPTION_DAYS_TO_LIVE):
            days_to_live_in_seconds = int(
                self.user_options.get(utils.OPTION_DAYS_TO_LIVE)
            ) * 24 * 60 * 60  # days * hours_per_day * minutes_per_hour * seconds_per_minute
            expiration_timestamp = time.time() + days_to_live_in_seconds
            self.extra_labels[utils.LABEL_EXPIRATION_TIMESTAMP] = str(
                expiration_timestamp)
        else:
            self.extra_labels[utils.LABEL_EXPIRATION_TIMESTAMP] = str(0)

        #if self.user_options.get('gpus'):
        #    extra_host_config['runtime'] = "nvidia"
        #    self.extra_labels[LABEL_NVIDIA_VISIBLE_DEVICES] = self.user_options.get('gpus')

        res = yield super().start()

        # Create service for pod so that it can be routed via name
        service = V1Service(
            kind='Service',
            spec=V1ServiceSpec(
                type='ClusterIP',
                ports=[V1ServicePort(port=self.port, target_port=self.port)],
                selector={
                    utils.LABEL_MLHUB_ORIGIN:
                    self.extra_labels[utils.LABEL_MLHUB_ORIGIN],
                    LABEL_POD_NAME:
                    self.extra_labels[LABEL_POD_NAME]
                }),
            metadata=V1ObjectMeta(name=self.pod_name,
                                  labels=self.extra_labels))
        try:
            yield self.asynchronize(self.api.create_namespaced_service,
                                    namespace=self.namespace,
                                    body=service)
        except client.rest.ApiException as e:
            if e.status == 409:
                self.log.info(
                    'Service {} already existed. No need to re-create.'.format(
                        self.pod_name))

        return res