Exemplo n.º 1
0
def test_create(template: str, params: Dict, monkeypatch):
    create_from_yaml_mock = Mock()
    load_kube_config_mock = Mock()
    monkeypatch.setattr(kubernetes.utils, 'create_from_yaml',
                        create_from_yaml_mock)
    monkeypatch.setattr(kubernetes.config, 'load_kube_config',
                        load_kube_config_mock)

    # avoid deleting the config file so that we can check it
    remove_mock = Mock()
    monkeypatch.setattr(os, 'remove', remove_mock)

    create(template=template, params=params)

    # get the path to the config file
    assert remove_mock.call_count == 1
    path_to_config_file = remove_mock.call_args[0][0]

    # get the content and check that the values are present
    with open(path_to_config_file, 'r') as fh:
        content = fh.read()
    for v in params.values():
        if isinstance(v, str):
            assert v in content
        elif isinstance(v, dict):
            dict_content = json.loads(content)
            for sub_key, sub_v in v.items():
                assert dict_content['data'][sub_key] == sub_v

    monkeypatch.undo()
    os.remove(path_to_config_file)
Exemplo n.º 2
0
def test_create_deployment_with_device_plugin(template, monkeypatch):
    params = {
        'name': 'test-name',
        'namespace': 'test-namespace',
        'image': 'test-image',
        'replicas': 1,
        'command': 'test-command',
        'args': 'test-args',
        'port_expose': 1234,
        'port_in': 1234,
        'port_out': 1234,
        'port_ctrl': 1234,
        'pull_policy': 1234,
        'device_plugins': {
            'hardware-vendor.example/foo': 2,
            'nvidia.com/gpu:': 3
        },
    }

    create_from_yaml_mock = Mock()
    load_kube_config_mock = Mock()
    remove_mock = Mock()
    monkeypatch.setattr(kubernetes.utils, 'create_from_yaml',
                        create_from_yaml_mock)
    monkeypatch.setattr(kubernetes.config, 'load_kube_config',
                        load_kube_config_mock)
    monkeypatch.setattr(os, 'remove', remove_mock)

    create(template, params)

    assert remove_mock.call_count == 1
    path_to_config_file = remove_mock.call_args[0][0]

    with open(path_to_config_file, 'r') as f:
        config = yaml.safe_load(f)
        assert config['spec']['template']['spec']['containers'][0][
            'resources'] == {
                'limits': {
                    'hardware-vendor.example/foo': 2,
                    'nvidia.com/gpu:': 3
                }
            }
Exemplo n.º 3
0
def test_create(template: str, values: Dict, monkeypatch):
    create_from_yaml_mock = Mock()
    monkeypatch.setattr(kubernetes.utils, 'create_from_yaml',
                        create_from_yaml_mock)

    # avoid deleting the config file so that we can check it
    remove_mock = Mock()
    monkeypatch.setattr(os, 'remove', remove_mock)

    create(template, values)

    # get the path to the config file
    assert remove_mock.call_count == 1
    path_to_config_file = remove_mock.call_args[0][0]

    # get the content and check that the values are present
    with open(path_to_config_file, 'r') as fh:
        content = fh.read()
    for v in values.values():
        assert v in content

    monkeypatch.undo()
    os.remove(path_to_config_file)
Exemplo n.º 4
0
def deploy_service(
    name: str,
    namespace: str,
    image_name: str,
    container_cmd: str,
    container_args: str,
    logger: JinaLogger,
    replicas: int,
    pull_policy: str,
    init_container: Dict = None,
    custom_resource_dir: Optional[str] = None,
    port_expose: Optional[int] = None,
) -> str:
    """Deploy service on Kubernetes.

    :param name: name of the service and deployment
    :param namespace: k8s namespace of the service and deployment
    :param image_name: image for the k8s deployment
    :param container_cmd: command executed on the k8s pods
    :param container_args: arguments used for the k8s pod
    :param logger: used logger
    :param replicas: number of replicas
    :param pull_policy: pull policy used for fetching the Docker images from the registry.
    :param init_container: additional arguments used for the init container
    :param custom_resource_dir: Path to a folder containing the kubernetes yml template files.
        Defaults to the standard location jina.resources if not specified.
    :param port_expose: port which will be exposed by the deployed containers
    :return: dns name of the created service
    """

    # we can always assume the ports are the same for all executors since they run on different k8s pods
    # port expose can be defined by the user
    if not port_expose:
        port_expose = 8080
    port_in = 8081
    port_out = 8082
    port_ctrl = 8083

    logger.info(
        f'🔋\tCreate Service for "{name}" with exposed port "{port_expose}"')
    kubernetes_tools.create(
        'service',
        {
            'name': name,
            'target': name,
            'namespace': namespace,
            'port_expose': port_expose,
            'port_in': port_in,
            'port_out': port_out,
            'port_ctrl': port_ctrl,
            'type': 'ClusterIP',
        },
        logger=logger,
        custom_resource_dir=custom_resource_dir,
    )

    logger.info(
        f'🐳\tCreate Deployment for "{name}" with image "{image_name}", replicas {replicas} and init_container {init_container is not None}'
    )

    if init_container:
        template_name = 'deployment-init'
    else:
        template_name = 'deployment'
        init_container = {}
    kubernetes_tools.create(
        template_name,
        {
            'name': name,
            'namespace': namespace,
            'image': image_name,
            'replicas': replicas,
            'command': container_cmd,
            'args': container_args,
            'port_expose': port_expose,
            'port_in': port_in,
            'port_out': port_out,
            'port_ctrl': port_ctrl,
            'pull_policy': pull_policy,
            **init_container,
        },
        logger=logger,
        custom_resource_dir=custom_resource_dir,
    )

    logger.info(f'🔑\tCreate necessary permissions"')

    kubernetes_tools.create(
        'connection-pool-role',
        {
            'namespace': namespace,
        },
    )

    kubernetes_tools.create(
        'connection-pool-role-binding',
        {
            'namespace': namespace,
        },
    )

    return f'{name}.{namespace}.svc'
Exemplo n.º 5
0
def deploy_service(
    name: str,
    namespace: str,
    image_name: str,
    container_cmd: str,
    container_args: str,
    logger: JinaLogger,
    replicas: int,
    pull_policy: str,
    init_container: Optional[Dict] = None,
    custom_resource_dir: Optional[str] = None,
    port_expose: Optional[int] = None,
    env: Optional[Dict] = None,
    gpus: Optional[Union[int, str]] = None,
) -> str:
    """Deploy service on Kubernetes.

    :param name: name of the service and deployment
    :param namespace: k8s namespace of the service and deployment
    :param image_name: image for the k8s deployment
    :param container_cmd: command executed on the k8s pods
    :param container_args: arguments used for the k8s pod
    :param logger: used logger
    :param replicas: number of replicas
    :param pull_policy: pull policy used for fetching the Docker images from the registry.
    :param init_container: additional arguments used for the init container
    :param custom_resource_dir: Path to a folder containing the kubernetes yml template files.
        Defaults to the standard location jina.resources if not specified.
    :param port_expose: port which will be exposed by the deployed containers
    :param env: environment variables to be passed into configmap.
    :param gpus: number of gpus to use, for k8s requires you pass an int number, refers to the number of requested gpus.
    :return: dns name of the created service
    """

    # we can always assume the ports are the same for all executors since they run on different k8s pods
    # port expose can be defined by the user
    if not port_expose:
        port_expose = 8080
    port_in = 8081
    port_out = 8082
    port_ctrl = 8083

    logger.debug(
        f'🔋\tCreate Service for "{name}" with exposed port "{port_expose}"')
    kubernetes_tools.create(
        'service',
        {
            'name': name,
            'target': name,
            'namespace': namespace,
            'port_expose': port_expose,
            'port_in': port_in,
            'port_out': port_out,
            'port_ctrl': port_ctrl,
            'type': 'ClusterIP',
        },
        logger=logger,
        custom_resource_dir=custom_resource_dir,
    )

    logger.debug(f'📝\tCreate ConfigMap for deployment.')

    kubernetes_tools.create(
        'configmap',
        {
            'name': name,
            'namespace': namespace,
            'data': env,
        },
        logger=logger,
        custom_resource_dir=None,
    )

    logger.debug(
        f'🐳\tCreate Deployment for "{name}" with image "{image_name}", replicas {replicas} and init_container {init_container is not None}'
    )

    deployment_params = {
        'name': name,
        'namespace': namespace,
        'image': image_name,
        'replicas': replicas,
        'command': container_cmd,
        'args': container_args,
        'port_expose': port_expose,
        'port_in': port_in,
        'port_out': port_out,
        'port_ctrl': port_ctrl,
        'pull_policy': pull_policy,
    }

    if init_container:
        template_name = 'deployment-init'
        deployment_params = {**deployment_params, **init_container}
    else:
        template_name = 'deployment'
    if gpus:
        deployment_params['device_plugins'] = {'nvidia.com/gpu': gpus}
    kubernetes_tools.create(
        template_name,
        deployment_params,
        logger=logger,
        custom_resource_dir=custom_resource_dir,
    )

    logger.debug(f'🔑\tCreate necessary permissions"')

    kubernetes_tools.create(
        'connection-pool-role',
        {
            'namespace': namespace,
        },
    )

    kubernetes_tools.create(
        'connection-pool-role-binding',
        {
            'namespace': namespace,
        },
    )

    return f'{name}.{namespace}.svc'