Beispiel #1
0
 def test_add_env_fromconref_has_env(self):
     pod_spec = V1PodSpec(containers=[
         V1Container(name='model',
                     image="image",
                     env=[client.V1EnvVar(name='var0', value='value0')])
     ], )
     env_vars = {'var1': {'name':'config1', 'key':'key1', 'type':'configmap'}, \
                 'var2': {'name':'secret2', 'key':'key2', 'type':'secret'}, \
                 'var3':'value3'
                 }
     k8s_utils.add_env(env_vars=env_vars)(None, pod_spec, "")
     actual = pod_spec.containers[0].env
     expected = [{
         'name': 'var0',
         'value': 'value0',
         'value_from': None
     }, {
         'name': 'var1',
         'value': 'value1',
         'value_from': None
     }, {
         'name': 'var2',
         'value': 'value2',
         'value_from': None
     }]
     # assert str(actual) == str(expected)
     print("actual:{}".format(str(actual)))
     print("expected:{}".format(str(expected)))
     print(pod_spec)
     self.assertTrue(str(actual) == str(expected))
Beispiel #2
0
 def test_resource_mutator_gpu(self):
     pod_spec = V1PodSpec(
         containers=[V1Container(name='model', image="image")], )
     k8s_utils.get_resource_mutator(gpu=1)(None, pod_spec, "")
     actual = pod_spec.containers[0].resources.limits
     expected = {'nvidia.com/gpu': 1}
     assert actual == expected
Beispiel #3
0
 def test_resource_mutator_no_mem(self):
     pod_spec = V1PodSpec(
         containers=[V1Container(name='model', image="image")], )
     k8s_utils.get_resource_mutator(cpu=1.5)(None, pod_spec, "")
     actual = pod_spec.containers[0].resources.limits
     expected = {'cpu': 1.5}
     assert actual == expected
Beispiel #4
0
 def test_resource_mutator_no_cpu(self):
     pod_spec = V1PodSpec(
         containers=[V1Container(name='model', image="image")], )
     k8s_utils.get_resource_mutator(memory=0.5)(None, pod_spec, "")
     actual = pod_spec.containers[0].resources.limits
     expected = {'memory': '0.47Gi'}
     assert actual == expected
Beispiel #5
0
def test_resource_mutator_gpu_vendor():
    pod_spec = V1PodSpec(containers=[V1Container(name='model',
                                                 image="image")], )
    k8s_utils.get_resource_mutator(gpu=2, gpu_vendor='amd')(None, pod_spec, "")
    actual = pod_spec.containers[0].resources.limits
    expected = {'amd.com/gpu': 2}
    assert actual == expected
Beispiel #6
0
 def test_add_env_no_env(self):
     pod_spec = V1PodSpec(
         containers=[V1Container(name='model', image="image")], )
     env_vars = {'var1': 'value1', 'var2': 'value2'}
     k8s_utils.add_env(env_vars=env_vars)(None, pod_spec, "")
     actual = pod_spec.containers[0].env
     expected = [{
         'name': 'var1',
         'value': 'value1',
         'value_from': None
     }, {
         'name': 'var2',
         'value': 'value2',
         'value_from': None
     }]
     assert str(actual) == str(expected)
def mock_k8s_pod(config):
    if not isinstance(config, MockK8sPodConfig):
        raise TypeError("Wrong config type")

    pod = V1Pod()

    pod.metadata = V1ObjectMeta(name=config.name,
                                labels=config.labels,
                                namespace=config.namespace)

    containers = []
    for i, requests in enumerate(config.container_requests):
        r = V1ResourceRequirements(requests=requests)
        c = V1Container(name=config.name + str(i), resources=r)
        containers.append(c)

    pod.spec = V1PodSpec(node_name=config.node_name,
                         containers=containers,
                         node_selector=config.node_selector)

    pod.status = V1PodStatus(phase=config.phase)

    return pod
Beispiel #8
0
def test_add_env_has_env():
    pod_spec = V1PodSpec(containers=[
        V1Container(name='model',
                    image="image",
                    env=[client.V1EnvVar(name='var0', value='value0')])
    ], )
    env_vars = {'var1': 'value1', 'var2': 'value2'}
    k8s_utils.add_env(env_vars=env_vars)(None, pod_spec, "")
    actual = pod_spec.containers[0].env
    expected = [{
        'name': 'var0',
        'value': 'value0',
        'value_from': None
    }, {
        'name': 'var1',
        'value': 'value1',
        'value_from': None
    }, {
        'name': 'var2',
        'value': 'value2',
        'value_from': None
    }]
    assert str(actual) == str(expected)
Beispiel #9
0
def make_pod(
    name,
    image_spec,
    image_pull_policy,
    image_pull_secret,
    port,
    cmd,
    node_selector,
    run_as_uid,
    fs_gid,
    env,
    working_dir,
    volumes,
    volume_mounts,
    labels,
    cpu_limit,
    cpu_guarantee,
    mem_limit,
    mem_guarantee,
    nvidia_gpu_limit,
    lifecycle_hooks,
    init_containers,
):
    """
    Make a k8s pod specification for running a user notebook.

    Parameters:
      - name:
        Name of pod. Must be unique within the namespace the object is
        going to be created in. Must be a valid DNS label.
      - image_spec:
        Image specification - usually a image name and tag in the form
        of image_name:tag. Same thing you would use with docker commandline
        arguments
      - image_pull_policy:
        Image pull policy - one of 'Always', 'IfNotPresent' or 'Never'. Decides
        when kubernetes will check for a newer version of image and pull it when
        running a pod.
      - image_pull_secret:
        Image pull secret - Default is None -- set to your secret name to pull
        from private docker registry.
      - port:
        Port the notebook server is going to be listening on
      - cmd:
        The command used to execute the singleuser server.
      - node_selector:
        Dictionary Selector to match nodes where to launch the Pods
      - run_as_uid:
        The UID used to run single-user pods. The default is to run as the user
        specified in the Dockerfile, if this is set to None.
      - fs_gid
        The gid that will own any fresh volumes mounted into this pod, if using
        volume types that support this (such as GCE). This should be a group that
        the uid the process is running as should be a member of, so that it can
        read / write to the volumes mounted.
      - env:
        Dictionary of environment variables.
      - volumes:
        List of dictionaries containing the volumes of various types this pod
        will be using. See k8s documentation about volumes on how to specify
        these
      - volume_mounts:
        List of dictionaries mapping paths in the container and the volume(
        specified in volumes) that should be mounted on them. See the k8s
        documentaiton for more details
      - working_dir:
        String specifying the working directory for the notebook container
      - labels:
        Labels to add to the spawned pod.
      - cpu_limit:
        Float specifying the max number of CPU cores the user's pod is
        allowed to use.
      - cpu_guarentee:
        Float specifying the max number of CPU cores the user's pod is
        guaranteed to have access to, by the scheduler.
      - mem_limit:
        String specifying the max amount of RAM the user's pod is allowed
        to use. String instead of float/int since common suffixes are allowed
      - mem_guarantee:
        String specifying the max amount of RAM the user's pod is guaranteed
        to have access to. String ins loat/int since common suffixes
        are allowed
      - nvidia_gpu_limit:
        Number of Nvidia GPUs to be allocated to user's pod.
      - lifecycle_hooks:
        Dictionary of lifecycle hooks
      - init_containers:
        List of initialization containers belonging to the pod.
    """

    pod = V1Pod()
    pod.kind = "Pod"
    pod.api_version = "v1"

    pod.metadata = V1ObjectMeta()
    pod.metadata.name = name
    pod.metadata.labels = labels.copy()

    pod.spec = V1PodSpec()

    security_context = V1PodSecurityContext()
    if fs_gid is not None:
        security_context.fs_group = int(fs_gid)
    if run_as_uid is not None:
        security_context.run_as_user = int(run_as_uid)
    pod.spec.security_context = security_context

    if image_pull_secret is not None:
        pod.spec.image_pull_secrets = []
        image_secret = V1LocalObjectReference()
        image_secret.name = image_pull_secret
        pod.spec.image_pull_secrets.append(image_secret)

    if node_selector:
        pod.spec.node_selector = node_selector

    pod.spec.containers = []
    notebook_container = V1Container()
    notebook_container.name = "notebook"
    notebook_container.image = image_spec
    notebook_container.working_dir = working_dir
    notebook_container.ports = []
    port_ = V1ContainerPort()
    port_.name = "notebook-port"
    port_.container_port = port
    notebook_container.ports.append(port_)
    notebook_container.env = [V1EnvVar(k, v) for k, v in env.items()]
    notebook_container.args = cmd
    notebook_container.image_pull_policy = image_pull_policy
    notebook_container.lifecycle = lifecycle_hooks
    notebook_container.resources = V1ResourceRequirements()

    notebook_container.resources.requests = {}

    if cpu_guarantee:
        notebook_container.resources.requests['cpu'] = float(cpu_guarantee)
    if mem_guarantee:
        notebook_container.resources.requests['memory'] = mem_guarantee

    notebook_container.resources.limits = {}
    if cpu_limit:
        notebook_container.resources.limits['cpu'] = float(cpu_limit)
    if mem_limit:
        notebook_container.resources.limits['memory'] = mem_limit
    if nvidia_gpu_limit:
        notebook_container.resources.limits[
            'alpha.kubernetes.io/nvidia-gpu'] = nvidia_gpu_limit
    notebook_container.volume_mounts = volume_mounts
    pod.spec.containers.append(notebook_container)

    pod.spec.init_containers = init_containers
    pod.spec.volumes = volumes
    return pod
Beispiel #10
0
def make_pod(name,
             cmd,
             port,
             image_spec,
             image_pull_policy,
             image_pull_secret=None,
             node_selector=None,
             run_as_uid=None,
             fs_gid=None,
             run_privileged=False,
             env={},
             working_dir=None,
             volumes=[],
             volume_mounts=[],
             labels={},
             cpu_limit=None,
             cpu_guarantee=None,
             mem_limit=None,
             mem_guarantee=None,
             lifecycle_hooks=None,
             init_containers=None,
             service_account=None,
             extra_container_config=None,
             extra_pod_config=None,
             extra_containers=None):
    """
    Make a k8s pod specification for running a user notebook.

    Parameters:
      - name:
        Name of pod. Must be unique within the namespace the object is
        going to be created in. Must be a valid DNS label.
      - image_spec:
        Image specification - usually a image name and tag in the form
        of image_name:tag. Same thing you would use with docker commandline
        arguments
      - image_pull_policy:
        Image pull policy - one of 'Always', 'IfNotPresent' or 'Never'. Decides
        when kubernetes will check for a newer version of image and pull it when
        running a pod.
      - image_pull_secret:
        Image pull secret - Default is None -- set to your secret name to pull
        from private docker registry.
      - port:
        Port the notebook server is going to be listening on
      - cmd:
        The command used to execute the singleuser server.
      - node_selector:
        Dictionary Selector to match nodes where to launch the Pods
      - run_as_uid:
        The UID used to run single-user pods. The default is to run as the user
        specified in the Dockerfile, if this is set to None.
      - fs_gid
        The gid that will own any fresh volumes mounted into this pod, if using
        volume types that support this (such as GCE). This should be a group that
        the uid the process is running as should be a member of, so that it can
        read / write to the volumes mounted.
      - run_privileged:
        Whether the container should be run in privileged mode.
      - env:
        Dictionary of environment variables.
      - volumes:
        List of dictionaries containing the volumes of various types this pod
        will be using. See k8s documentation about volumes on how to specify
        these
      - volume_mounts:
        List of dictionaries mapping paths in the container and the volume(
        specified in volumes) that should be mounted on them. See the k8s
        documentaiton for more details
      - working_dir:
        String specifying the working directory for the notebook container
      - labels:
        Labels to add to the spawned pod.
      - cpu_limit:
        Float specifying the max number of CPU cores the user's pod is
        allowed to use.
      - cpu_guarentee:
        Float specifying the max number of CPU cores the user's pod is
        guaranteed to have access to, by the scheduler.
      - mem_limit:
        String specifying the max amount of RAM the user's pod is allowed
        to use. String instead of float/int since common suffixes are allowed
      - mem_guarantee:
        String specifying the max amount of RAM the user's pod is guaranteed
        to have access to. String ins loat/int since common suffixes
        are allowed
      - lifecycle_hooks:
        Dictionary of lifecycle hooks
      - init_containers:
        List of initialization containers belonging to the pod.
      - service_account:
        Service account to mount on the pod. None disables mounting
      - extra_container_config:
        Extra configuration (e.g. envFrom) for notebook container which is not covered by parameters above.
      - extra_pod_config:
        Extra configuration (e.g. tolerations) for pod which is not covered by parameters above.
      - extra_containers:
        Extra containers besides notebook container. Used for some housekeeping jobs (e.g. crontab).
    """

    pod = V1Pod()
    pod.kind = "Pod"
    pod.api_version = "v1"

    pod.metadata = V1ObjectMeta()
    pod.metadata.name = name
    pod.metadata.labels = labels.copy()

    pod.spec = V1PodSpec()

    security_context = V1PodSecurityContext()
    if fs_gid is not None:
        security_context.fs_group = int(fs_gid)
    if run_as_uid is not None:
        security_context.run_as_user = int(run_as_uid)
    pod.spec.security_context = security_context

    if image_pull_secret is not None:
        pod.spec.image_pull_secrets = []
        image_secret = V1LocalObjectReference()
        image_secret.name = image_pull_secret
        pod.spec.image_pull_secrets.append(image_secret)

    if node_selector:
        pod.spec.node_selector = node_selector

    pod.spec.containers = []
    notebook_container = V1Container()
    notebook_container.name = "notebook"
    notebook_container.image = image_spec
    notebook_container.working_dir = working_dir
    notebook_container.ports = []
    port_ = V1ContainerPort()
    port_.name = "notebook-port"
    port_.container_port = port
    notebook_container.ports.append(port_)
    notebook_container.env = [V1EnvVar(k, v) for k, v in env.items()]
    notebook_container.args = cmd
    notebook_container.image_pull_policy = image_pull_policy
    notebook_container.lifecycle = lifecycle_hooks
    notebook_container.resources = V1ResourceRequirements()

    if service_account is None:
        # Add a hack to ensure that no service accounts are mounted in spawned pods
        # This makes sure that we don"t accidentally give access to the whole
        # kubernetes API to the users in the spawned pods.
        # Note: We don't simply use `automountServiceAccountToken` here since we wanna be compatible
        # with older kubernetes versions too for now.
        hack_volume = V1Volume()
        hack_volume.name = "no-api-access-please"
        hack_volume.empty_dir = {}
        hack_volumes = [hack_volume]

        hack_volume_mount = V1VolumeMount()
        hack_volume_mount.name = "no-api-access-please"
        hack_volume_mount.mount_path = "/var/run/secrets/kubernetes.io/serviceaccount"
        hack_volume_mount.read_only = True
        hack_volume_mounts = [hack_volume_mount]
    else:
        hack_volumes = []
        hack_volume_mounts = []

        pod.service_account_name = service_account

    if run_privileged:
        container_security_context = V1SecurityContext()
        container_security_context.privileged = True
        notebook_container.security_context = container_security_context

    notebook_container.resources.requests = {}

    if cpu_guarantee:
        notebook_container.resources.requests['cpu'] = cpu_guarantee
    if mem_guarantee:
        notebook_container.resources.requests['memory'] = mem_guarantee

    notebook_container.resources.limits = {}
    if cpu_limit:
        notebook_container.resources.limits['cpu'] = cpu_limit
    if mem_limit:
        notebook_container.resources.limits['memory'] = mem_limit
    notebook_container.volume_mounts = volume_mounts + hack_volume_mounts
    pod.spec.containers.append(notebook_container)

    if extra_container_config:
        for key, value in extra_container_config.items():
            setattr(notebook_container,
                    _map_attribute(notebook_container.attribute_map, key),
                    value)
    if extra_pod_config:
        for key, value in extra_pod_config.items():
            setattr(pod.spec, _map_attribute(pod.spec.attribute_map, key),
                    value)
    if extra_containers:
        pod.spec.containers.extend(extra_containers)

    pod.spec.init_containers = init_containers
    pod.spec.volumes = volumes + hack_volumes
    return pod