def wait_packaging_finish(timeout: int, wait: bool, mp_id: str, mp_client: ModelPackagingClient): """ Wait for packaging to finish according to command line arguments :param wait: :param timeout: :param mp_id: Model Packaging name :param mp_client: Model Packaging Client """ if not wait: return start = time.time() if timeout <= 0: raise Exception( 'Invalid --timeout argument: should be positive integer') # We create a separate client for logs because it has the different timeout settings log_mp_client = ModelPackagingClient.construct_from_other(mp_client) log_mp_client.timeout = mp_client.timeout, LOG_READ_TIMEOUT_SECONDS click.echo("Logs streaming...") while True: elapsed = time.time() - start if elapsed > timeout: raise Exception(TIMEOUT_ERROR_MESSAGE) try: mp = mp_client.get(mp_id) if mp.status.state == SUCCEEDED_STATE: click.echo( f'Model {mp_id} was packed. Packaging took {round(time.time() - start)} seconds' ) return elif mp.status.state == FAILED_STATE: raise Exception(f'Model packaging {mp_id} was failed.') elif mp.status.state == "": click.echo( f"Can't determine the state of {mp.id}. Sleeping...") else: for msg in log_mp_client.log(mp.id, follow=True): print_logs(msg) except (WrongHttpStatusCode, HTTPException, RequestException, APIConnectionException) as e: LOGGER.info( 'Callback have not confirmed completion of the operation. Exception: %s', str(e)) LOGGER.debug('Sleep before next request') time.sleep(DEFAULT_WAIT_TIMEOUT)
def run(client: ModelPackagingClient, pack_id: str, manifest_file: List[str], manifest_dir: List[str], artifact_path: str, artifact_name: str, is_target_disabled: bool): """ \b Start a packaging process locally. \b Usage example: * odahuflowctl local pack run --id wine \f """ entities: List[OdahuflowCloudResourceUpdatePair] = [] for file_path in manifest_file: entities.extend(parse_resources_file(file_path).changes) for dir_path in manifest_dir: entities.extend(parse_resources_dir(dir_path)) mp: Optional[ModelPackaging] = None packagers: Dict[str, PackagingIntegration] = {} for entity in map(lambda x: x.resource, entities): if isinstance(entity, PackagingIntegration): packagers[entity.id] = entity elif isinstance(entity, ModelPackaging) and entity.id == pack_id: mp = entity if not mp: click.echo( f'The {pack_id} packaging not found in the manifest files.' f' Trying to retrieve it from API server' ) mp = client.get(pack_id) integration_name = mp.spec.integration_name packager = packagers.get(integration_name) if not packager: click.echo( f'The {integration_name} packager not found in the manifest files.' f' Trying to retrieve it from API server' ) packager = PackagingIntegrationClient.construct_from_other(client).get(integration_name) if artifact_name: mp.spec.artifact_name = artifact_name LOGGER.debug('Override the artifact name') if is_target_disabled: mp.spec.targets = [] k8s_packager = K8sPackager( model_packaging=mp, packaging_integration=packager, targets=[], ) result = start_package(k8s_packager, artifact_path) click.echo('Packager results:') for key, value in result.items(): click.echo(f'* {key} - {value}')
def build_client(resource: OdahuflowCloudResourceUpdatePair, api_client: RemoteAPIClient) -> typing.Optional[object]: """ Build client for particular resource (e.g. it builds ModelTrainingClient for ModelTraining resource) :param resource: target resource :param api_client: base API client to extract connection options from :return: remote client or None """ if isinstance(resource.resource, ModelTraining): return ModelTrainingClient.construct_from_other(api_client) elif isinstance(resource.resource, ModelDeployment): return ModelDeploymentClient.construct_from_other(api_client) elif isinstance(resource.resource, Connection): return ConnectionClient.construct_from_other(api_client) elif isinstance(resource.resource, ToolchainIntegration): return ToolchainIntegrationClient.construct_from_other(api_client) elif isinstance(resource.resource, ModelRoute): return ModelRouteClient.construct_from_other(api_client) elif isinstance(resource.resource, ModelPackaging): return ModelPackagingClient.construct_from_other(api_client) elif isinstance(resource.resource, PackagingIntegration): return PackagingIntegrationClient.construct_from_other(api_client) else: raise InvalidResourceType('{!r} is invalid resource '.format( resource.resource))
def logs(client: ModelPackagingClient, pack_id: str, file: str, follow: bool): """ \b Stream packaging logs. For this command, you must provide a packaging ID or path to file with one packaging. The file must contain only one packaging. The command will fail if you provide both arguments. \b Usage example: * odahuflowctl pack delete --id examples-git * odahuflowctl pack delete -f pack.yaml \f :param follow: Follow logs stream :param client: Model packaging HTTP client :param pack_id: Model packaging ID :param file: Path to the file with only one packaging """ check_id_or_file_params_present(pack_id, file) if file: pack = parse_resources_file_with_one_item(file).resource if not isinstance(pack, ModelPackaging): raise ValueError( f'Model packaging expected, but {type(pack)} provided') pack_id = pack.id for msg in client.log(pack_id, follow): print_logs(msg)
def edit(client: ModelPackagingClient, pack_id: str, file: str, wait: bool, timeout: int, artifact_name: str): """ \b Update a packaging. You should specify a path to file with a packaging. The file must contain only one packaging. For now, CLI supports YAML and JSON file formats. If you want to update multiple packagings, you should use "odahuflowctl bulk apply" instead. If you provide the packaging id parameter, it will override before sending to API server. \b Usage example: * odahuflowctl pack update -f pack.yaml --id examples-git \f :param client: Model packaging HTTP client :param pack_id: Model packaging ID :param file: Path to the file with only one packaging :param timeout: timeout in seconds. for wait (if no-wait is off) :param wait: no wait until scale will be finished :param artifact_name: Override artifact name from file """ pack = parse_resources_file_with_one_item(file).resource if not isinstance(pack, ModelPackaging): raise ValueError( f'Model packaging expected, but {type(pack)} provided') if pack_id: pack.id = pack_id if artifact_name: pack.spec.artifact_name = artifact_name mp = client.edit(pack) click.echo(f"Rerun packing: {mp}") wait_packaging_finish(timeout, wait, pack.id, client)
def packaging_post(payload_file, artifact_name=None): api_object = parse_resources_file_with_one_item(payload_file).resource if artifact_name: api_object.spec.artifact_name = artifact_name return ModelPackagingClient().create(api_object)
def get(client: ModelPackagingClient, pack_id: str, output_format: str): """ \b Get packagings. The command without id argument retrieve all packagings. \b Get all packagings in json format: odahuflowctl pack get --output-format json \b Get packaging with "git-repo" id: odahuflowctl pack get --id git-repo \b Using jsonpath: odahuflowctl pack get -o 'jsonpath=[*].spec.reference' \f :param client: Model packaging HTTP client :param pack_id: Model packaging ID :param output_format: Output format :return: """ packs = [client.get(pack_id)] if pack_id else client.get_all() format_output(packs, output_format)
def create(client: ModelPackagingClient, pack_id: str, file: str, wait: bool, timeout: int, artifact_name: str, ignore_if_exists: bool): """ \b Create a packaging. You should specify a path to file with a packaging. The file must contain only one packaging. For now, CLI supports YAML and JSON file formats. If you want to create multiple packagings, you should use "odahuflowctl bulk apply" instead. If you provide the packaging id parameter, it will override before sending to API server. \b Usage example: * odahuflowctl pack create -f pack.yaml --id examples-git \f :param timeout: timeout in seconds. for wait (if no-wait is off) :param wait: no wait until scale will be finished :param client: Model packaging HTTP client :param pack_id: Model packaging ID :param file: Path to the file with only one packaging :param artifact_name: Override artifact name from file :param ignore_if_exists: Return success status code if entity is already exists """ pack = parse_resources_file_with_one_item(file).resource if not isinstance(pack, ModelPackaging): raise ValueError( f'Model packaging expected, but {type(pack)} provided') if pack_id: pack.id = pack_id if artifact_name: pack.spec.artifact_name = artifact_name try: mp = client.create(pack) except EntityAlreadyExists as e: if ignore_if_exists: LOGGER.debug( f'--ignore-if-exists was passed: {e} will be suppressed') click.echo('Packaging already exists') return raise click.echo(f"Start packing: {mp}") wait_packaging_finish(timeout, wait, pack.id, client)
def delete(client: ModelPackagingClient, pack_id: str, file: str, ignore_not_found: bool): """ \b Delete a packaging. For this command, you must provide a packaging ID or path to file with one packaging. The file must contain only one packaging. If you want to delete multiple packagings, you should use "odahuflowctl bulk delete" instead. For now, CLI supports YAML and JSON file formats. The command will fail if you provide both arguments. \b Usage example: * odahuflowctl pack delete --id examples-git * odahuflowctl pack delete -f pack.yaml \f :param client: Model packaging HTTP client :param pack_id: Model packaging ID :param file: Path to the file with only one packaging :param ignore_not_found: ignore if Model Packaging is not found """ check_id_or_file_params_present(pack_id, file) if file: pack = parse_resources_file_with_one_item(file).resource if not isinstance(pack, ModelPackaging): raise ValueError( f'Model packaging expected, but {type(pack)} provided') pack_id = pack.id try: message = client.delete(pack_id) click.echo(message) except WrongHttpStatusCode as e: if e.status_code != 404 or not ignore_not_found: raise e click.echo(f'Model packaging {pack_id} was not found. Ignore')
def packaging_group(ctx: click.core.Context, url: str, token: str): """ Local packaging process.\n Alias for the command is pack. """ ctx.obj = ModelPackagingClient(url, token)
def packaging(ctx: click.core.Context, url: str, token: str): """ Allow you to perform actions on packagings.\n Alias for the command is pack. """ ctx.obj = ModelPackagingClient(url, token)
def packaging_get(): return ModelPackagingClient().get_all()
def packaging_get_log(pack_id): log_generator = ModelPackagingClient().log(pack_id, follow=False) # logs_list will be list of log lines logs_list = list(log_generator) text = "\n".join(logs_list) return text
def packaging_delete(pack_id: str): return ModelPackagingClient().delete(pack_id)
def packaging_get_id(pack_id: str): return ModelPackagingClient().get(pack_id)
TOOLCHAIN = EntityTestData( ToolchainIntegrationClient(), ToolchainIntegration( id=ENTITY_ID, spec=ToolchainIntegrationSpec( default_image="mock-image", entrypoint="default-entrypoint", ), ), toolchain_integration.toolchain_integration, 'ToolchainIntegration', ) PACKAGING = EntityTestData( ModelPackagingClient(), ModelPackaging(id=ENTITY_ID, spec=ModelPackagingSpec(artifact_name='test-artifact-name', integration_name='test'), status=ModelPackagingStatus(state=SUCCEEDED_STATE, )), packaging.packaging, 'ModelPackaging', ) PACKAGING_INTEGRATION = EntityTestData( PackagingIntegrationClient(), PackagingIntegration( id=ENTITY_ID, spec=PackagingIntegrationSpec(default_image="odahu:image", entrypoint="some_entrypoint"), ),