def test_parse_env_map(): a = set_deployment_parser().parse_args( ['--env', 'key1=value1', '--env', 'key2=value2']) assert a.env == {'key1': 'value1', 'key2': 'value2'} a = set_deployment_parser().parse_args( ['--env', 'key1=value1', 'key2=value2', 'key3=3']) assert a.env == {'key1': 'value1', 'key2': 'value2', 'key3': 3}
def test_dynamic_polling_overwrite_default_config(polling): endpoint_polling = {'/search': PollingType.ANY, '*': polling} args = set_deployment_parser().parse_args([ '--uses', 'DynamicPollingExecutorDefaultNames', '--shards', str(2), '--polling', json.dumps(endpoint_polling), ]) pod = Deployment(args) with pod: response = GrpcConnectionPool.send_request_sync( _create_test_data_message(endpoint='/search'), f'{pod.head_args.host}:{pod.head_args.port_in}', endpoint='/search', ) assert (len(response.docs) == 1 + 1 ) # 1 source doc + 1 doc added by the one shard response = GrpcConnectionPool.send_request_sync( _create_test_data_message(endpoint='/index'), f'{pod.head_args.host}:{pod.head_args.port_in}', endpoint='/index', ) assert (len(response.docs) == 1 + 1 ) # 1 source doc + 1 doc added by the one shard
def test_pod_naming_with_replica(): args = set_deployment_parser().parse_args( ['--name', 'pod', '--replicas', '2']) with Deployment(args) as bp: assert bp.head_pod.name == 'pod/head' assert bp.shards[0]._pods[0].name == 'pod/rep-0' assert bp.shards[0]._pods[1].name == 'pod/rep-1'
def test_use_from_local_hub_deployment_level(test_envs, mocker, monkeypatch, local_hub_executor): from jina.hubble.hubio import HubIO, HubExecutor mock = mocker.Mock() def _mock_fetch(name, tag=None, secret=None, force=False): mock(name=name) return ( HubExecutor( uuid='hello', name='alias_dummy', tag='v0', image_name='jinahub/pod.dummy_mwu_encoder', md5sum=None, visibility=True, archive_url=None, ), False, ) monkeypatch.setattr(HubIO, 'fetch_meta', _mock_fetch) a = set_deployment_parser().parse_args(['--uses', 'jinahub://hello']) with Deployment(a): pass
async def test_scale_remote_pod_async( deployment_params, async_jinad_client, slow_down_tests ): num_replicas, scale_to, shards = deployment_params args = set_deployment_parser().parse_args( ['--replicas', str(num_replicas), '--shards', str(shards)] ) payload = replace_enum_to_str(ArgNamespace.flatten_to_dict(args)) workspace_id = await async_jinad_client.workspaces.create( paths=[os.path.join(cur_dir, cur_dir)] ) success, pod_id = await async_jinad_client.deployments.create( workspace_id=workspace_id, payload=payload ) assert success resp = await async_jinad_client.deployments.get(pod_id) remote_pod_args = resp['arguments']['object']['arguments'] assert remote_pod_args['replicas'] == num_replicas assert remote_pod_args['shards'] == shards await async_jinad_client.deployments.scale(id=pod_id, replicas=scale_to) resp = await async_jinad_client.deployments.get(pod_id) remote_pod_args = resp['arguments']['object']['arguments'] assert remote_pod_args['replicas'] == scale_to assert remote_pod_args['shards'] == shards assert await async_jinad_client.deployments.delete(pod_id)
def test_pod_rolling_update(shards): args_list = ['--replicas', '7'] args_list.extend(['--shards', str(shards)]) args = set_deployment_parser().parse_args(args_list) args.uses = 'AppendParamExecutor' args.uses_with = {'param': 10} with Deployment(args) as pod: async def run_async_test(): response_texts = await _send_requests(pod) assert 2 == len(response_texts) assert all(text in response_texts for text in ['10', 'client']) await pod.rolling_update(uses_with={'param': 20}) response_texts = await _send_requests(pod) assert 2 == len(response_texts) assert all(text in response_texts for text in ['20', 'client']) assert '10' not in response_texts p = Process(target=run_async_test) p.start() p.join() assert p.exitcode == 0 Deployment(args).start().close()
def test_pod_upload_files( replicas, upload_files, uses, uses_before, uses_after, py_modules, expected, ): args = set_deployment_parser().parse_args([ '--uses', uses, '--uses-before', uses_before, '--uses-after', uses_after, '--py-modules', *py_modules, '--upload-files', *upload_files, '--replicas', str(replicas), ]) pod = Deployment(args) for k, v in pod.pod_args.items(): if k in ['head', 'tail']: if v: pass # assert sorted(v.upload_files) == sorted(expected) elif v is not None and k == 'pods': for shard_id in v: for pod in v[shard_id]: print(sorted(pod.upload_files)) print(sorted(expected)) assert sorted(pod.upload_files) == sorted(expected)
def external_deployment_args(): args = ['--port-in', str(45678)] args = vars(set_deployment_parser().parse_args(args)) del args['external'] del args['deployment_role'] del args['host'] return args
def _create_regular_deployment( port, name='', executor=None, uses_before=False, uses_after=False, polling=PollingType.ANY, shards=None, replicas=None, ): args = set_deployment_parser().parse_args([]) args.port = port args.name = name if shards: args.shards = shards if replicas: args.replicas = replicas args.polling = polling if executor: args.uses = executor if executor else 'NameChangeExecutor' if uses_after: args.uses_after = executor if executor else 'NameChangeExecutor' if uses_before: args.uses_before = executor if executor else 'NameChangeExecutor' return Deployment(args)
def test_pod_context_replicas(replicas): args_list = ['--replicas', str(replicas)] args = set_deployment_parser().parse_args(args_list) with Deployment(args) as bp: assert bp.num_pods == replicas Deployment(args).start().close()
def test_dynamic_polling_default_config(polling): args = set_deployment_parser().parse_args([ '--uses', 'DynamicPollingExecutorDefaultNames', '--shards', str(2), '--polling', polling, ]) pod = Deployment(args) with pod: response = GrpcConnectionPool.send_request_sync( _create_test_data_message(endpoint='/search'), f'{pod.head_args.host}:{pod.head_args.port_in}', endpoint='/search', ) assert len(response.docs) == 1 + 2 response = GrpcConnectionPool.send_request_sync( _create_test_data_message(endpoint='/index'), f'{pod.head_args.host}:{pod.head_args.port_in}', endpoint='/index', ) assert len(response.docs) == 1 + 1
def test_pod_context_shards_replicas(shards): args_list = ['--replicas', str(3)] args_list.extend(['--shards', str(shards)]) args = set_deployment_parser().parse_args(args_list) with Deployment(args) as bp: assert bp.num_pods == shards * 3 + 1 Deployment(args).start().close()
def pod_args(): args = [ '--name', 'test', '--replicas', '2', '--host', __default_host__, ] return set_deployment_parser().parse_args(args)
async def _create(deployment: 'DeploymentModel', envs: Optional[Dict] = {}): """ .. #noqa: DAR101 .. #noqa: DAR201""" try: args = ArgNamespace.kwargs2namespace(deployment.dict(), set_deployment_parser()) return store.add(args, envs) except Exception as ex: raise PartialDaemon400Exception from ex
def pod_args_singleton(): args = [ '--name', 'test2', '--uses-before', __default_executor__, '--replicas', '1', '--host', __default_host__, ] return set_deployment_parser().parse_args(args)
def test_pod_args_remove_uses_ba(): args = set_deployment_parser().parse_args([]) with Deployment(args) as p: assert p.num_pods == 2 args = set_deployment_parser().parse_args([ '--uses-before', __default_executor__, '--uses-after', __default_executor__ ]) with Deployment(args) as p: assert p.num_pods == 2 args = set_deployment_parser().parse_args([ '--uses-before', __default_executor__, '--uses-after', __default_executor__, '--replicas', '2', ]) with Deployment(args) as p: assert p.num_pods == 3
def external_deployment_pre_shards_args(num_shards): args = [ '--uses', 'MyExternalExecutor', '--name', 'external_real', '--port', str(random_port()), '--shards', str(num_shards), '--polling', 'all', ] return set_deployment_parser().parse_args(args)
def dump(self, data: 'Flow') -> Dict: """ :param data: versioned flow object :return: the dictionary given a versioned flow object """ r = {} if data._version: r['version'] = data._version # to maintain order - version -> with -> executors r['with'] = {} if data._kwargs: r['with'].update(data._kwargs) if data._common_kwargs: r['with'].update(data._common_kwargs) if data._deployment_nodes: r['executors'] = [] last_name = 'gateway' for k, v in data._deployment_nodes.items(): kwargs = {} # only add "needs" when the value is not the last deployment name if list(v.needs) != [last_name]: kwargs = {'needs': list(v.needs)} # get nondefault kwargs parser = set_deployment_parser() if v.role == DeploymentRoleType.GATEWAY: parser = set_gateway_parser() non_default_kw = ArgNamespace.get_non_defaults_args(v.args, parser) kwargs.update(non_default_kw) for t in _get_taboo(parser): if t in kwargs: kwargs.pop(t) if k == 'gateway': if 'JINA_FULL_CLI' in os.environ: r['with'].update(kwargs) else: continue else: last_name = kwargs['name'] r['executors'].append(kwargs) return r
async def test_flow_with_external_native_deployment(logger, docker_images, tmpdir): class DocReplaceExecutor(Executor): @requests def add(self, **kwargs): return DocumentArray( [Document(text='executor was here') for _ in range(100)]) args = set_deployment_parser().parse_args(['--uses', 'DocReplaceExecutor']) with Deployment(args) as external_deployment: ports = [args.port for args in external_deployment.pod_args['pods'][0]] flow = Flow(name='k8s_flow-with_external_deployment', port=9090).add( name='external_executor', external=True, host=f'172.17.0.1', port=ports[0], ) namespace = 'test-flow-with-external-deployment' dump_path = os.path.join(str(tmpdir), namespace) flow.to_k8s_yaml(dump_path, k8s_namespace=namespace) from kubernetes import client api_client = client.ApiClient() core_client = client.CoreV1Api(api_client=api_client) app_client = client.AppsV1Api(api_client=api_client) await create_all_flow_deployments_and_wait_ready( dump_path, namespace=namespace, api_client=api_client, app_client=app_client, core_client=core_client, deployment_replicas_expected={ 'gateway': 1, }, logger=logger, ) resp = await run_test( flow=flow, namespace=namespace, core_client=core_client, endpoint='/', ) docs = resp[0].docs assert len(docs) == 100 for doc in docs: assert doc.text == 'executor was here' core_client.delete_namespace(namespace)
def test_worker_services(name: str, shards: str): args = set_deployment_parser().parse_args( ['--name', name, '--shards', shards]) deployment_config = DockerComposeConfig(args) actual_services = deployment_config.worker_services assert len(actual_services) == int(shards) for i, deploy in enumerate(actual_services): if int(shards) > 1: assert deploy.name == f'{name}-{i}' else: assert deploy.name == name assert deploy.jina_deployment_name == name assert deploy.shard_id == i
def test_deployments(name: str, shards: str, k8s_connection_pool_call): args = set_deployment_parser().parse_args(['--name', name, '--shards', shards]) deployment_config = K8sDeploymentConfig(args, 'ns', k8s_connection_pool_call) actual_deployments = deployment_config.worker_deployments assert len(actual_deployments) == int(shards) for i, deploy in enumerate(actual_deployments): if int(shards) > 1: assert deploy.name == f'{name}-{i}' else: assert deploy.name == name assert deploy.jina_deployment_name == name assert deploy.shard_id == i assert deploy.k8s_connection_pool is k8s_connection_pool_call
async def _create_external_deployment(api_client, app_client, docker_images, tmpdir): namespace = 'external-deployment-ns' args = set_deployment_parser().parse_args([ '--uses', f'docker://{docker_images[0]}', '--name', 'external-deployment' ]) external_deployment_config = K8sDeploymentConfig(args=args, k8s_namespace=namespace) configs = external_deployment_config.to_k8s_yaml() deployment_base = os.path.join(tmpdir, 'external-deployment') filenames = [] for name, k8s_objects in configs: filename = os.path.join(deployment_base, f'{name}.yml') os.makedirs(deployment_base, exist_ok=True) with open(filename, 'w+') as fp: filenames.append(filename) for i, k8s_object in enumerate(k8s_objects): yaml.dump(k8s_object, fp) if i < len(k8s_objects) - 1: fp.write('---\n') from kubernetes import utils namespace_object = { 'apiVersion': 'v1', 'kind': 'Namespace', 'metadata': { 'name': f'{namespace}' }, } try: utils.create_from_dict(api_client, namespace_object) except: pass for filename in filenames: try: utils.create_from_yaml( api_client, yaml_file=filename, namespace=namespace, ) except: pass await asyncio.sleep(1.0)
def test_pod_remote_pod_replicas_host(num_shards, num_replicas): args = set_deployment_parser().parse_args([ '--shards', str(num_shards), '--replicas', str(num_replicas), '--host', __default_host__, ]) assert args.host == __default_host__ with Deployment(args) as pod: assert pod.num_pods == num_shards * num_replicas + 1 pod_args = dict(pod.pod_args['pods']) for k, replica_args in pod_args.items(): assert len(replica_args) == num_replicas for replica_arg in replica_args: assert replica_arg.host == __default_host__
def test_dynamic_polling_with_config(polling): endpoint_polling = { '/any': PollingType.ANY, '/all': PollingType.ALL, '*': polling } args = set_deployment_parser().parse_args([ '--uses', 'DynamicPollingExecutor', '--shards', str(2), '--polling', json.dumps(endpoint_polling), ]) pod = Deployment(args) with pod: response = GrpcConnectionPool.send_request_sync( _create_test_data_message(endpoint='/all'), f'{pod.head_args.host}:{pod.head_args.port_in}', endpoint='/all', ) assert len(response.docs ) == 1 + 2 # 1 source doc + 2 docs added by each shard response = GrpcConnectionPool.send_request_sync( _create_test_data_message(endpoint='/any'), f'{pod.head_args.host}:{pod.head_args.port_in}', endpoint='/any', ) assert (len(response.docs) == 1 + 1 ) # 1 source doc + 1 doc added by the one shard response = GrpcConnectionPool.send_request_sync( _create_test_data_message(endpoint='/no_polling'), f'{pod.head_args.host}:{pod.head_args.port_in}', endpoint='/no_polling', ) if polling == 'any': assert (len(response.docs) == 1 + 1 ) # 1 source doc + 1 doc added by the one shard else: assert (len(response.docs) == 1 + 2 ) # 1 source doc + 1 doc added by the two shards
def test_pod_activates_replicas(): args_list = ['--replicas', '3', '--shards', '2', '--disable-reduce'] args = set_deployment_parser().parse_args(args_list) args.uses = 'AppendNameExecutor' with Deployment(args) as pod: assert pod.num_pods == 7 response_texts = set() # replicas are used in a round robin fashion, so sending 3 requests should hit each one time for _ in range(6): response = GrpcConnectionPool.send_request_sync( _create_test_data_message(), f'{pod.head_args.host}:{pod.head_args.port}', ) response_texts.update(response.response.docs.texts) assert 4 == len(response_texts) assert all(text in response_texts for text in ['0', '1', '2', 'client']) Deployment(args).start().close()
def external_deployment_args(num_replicas, num_shards): args = [ '--uses', 'MyExternalExecutor', '--name', 'external_real', '--port-in', str(random_port()), '--host-in', '0.0.0.0', '--shards', str(num_shards), '--replicas', str(num_replicas), '--polling', 'all', ] return set_deployment_parser().parse_args(args)
def test_pod_naming_with_shards(): args = set_deployment_parser().parse_args([ '--name', 'pod', '--shards', '2', '--replicas', '3', ]) with Deployment(args) as pod: assert pod.head_pod.name == 'pod/head' assert pod.shards[0].args[0].name == 'pod/shard-0/rep-0' assert pod.shards[0].args[1].name == 'pod/shard-0/rep-1' assert pod.shards[0].args[2].name == 'pod/shard-0/rep-2' assert pod.shards[1].args[0].name == 'pod/shard-1/rep-0' assert pod.shards[1].args[1].name == 'pod/shard-1/rep-1' assert pod.shards[1].args[2].name == 'pod/shard-1/rep-2'
def test_deployments(name: str, shards: str, gpus): args = set_deployment_parser().parse_args( ['--name', name, '--shards', shards, '--gpus', gpus] ) deployment_config = K8sDeploymentConfig(args, 'ns') if name != 'gateway' and int(shards) > int(1): head_deployment = deployment_config.head_deployment assert head_deployment.deployment_args.gpus is None actual_deployments = deployment_config.worker_deployments assert len(actual_deployments) == int(shards) for i, deploy in enumerate(actual_deployments): assert deploy.deployment_args.gpus == gpus if int(shards) > 1: assert deploy.name == f'{name}-{i}' else: assert deploy.name == name assert deploy.jina_deployment_name == name assert deploy.shard_id == i
def test_pod_activates_shards(): args_list = ['--replicas', '3'] args_list.extend(['--shards', '3']) args = set_deployment_parser().parse_args(args_list) args.uses = 'AppendShardExecutor' args.polling = PollingType.ALL with Deployment(args) as pod: assert pod.num_pods == 3 * 3 + 1 response_texts = set() # replicas are used in a round robin fashion, so sending 3 requests should hit each one time response = GrpcConnectionPool.send_request_sync( _create_test_data_message(), f'{pod.head_args.host}:{pod.head_args.port_in}', ) response_texts.update(response.response.docs.texts) assert 4 == len(response.response.docs.texts) assert 4 == len(response_texts) assert all(text in response_texts for text in ['0', '1', '2', 'client']) Deployment(args).start().close()
def test_use_from_local_dir_deployment_level(): a = set_deployment_parser().parse_args(['--uses', 'dummyhub/config.yml']) with Deployment(a): pass