From 33b0560319d4a93c9a975fc81e5621394dc6242b Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Fri, 22 Oct 2021 16:27:15 -0700 Subject: [PATCH 01/13] Add initial set of artifacts --- component-catalog-connectors/README.md | 2 + .../mlx-connector/README.md | 14 ++ .../mlx-connector/src/mlx-catalog.json | 79 ++++++++ .../src/mlx_component_catalog_connector.py | 168 ++++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 component-catalog-connectors/README.md create mode 100644 component-catalog-connectors/mlx-connector/README.md create mode 100644 component-catalog-connectors/mlx-connector/src/mlx-catalog.json create mode 100644 component-catalog-connectors/mlx-connector/src/mlx_component_catalog_connector.py diff --git a/component-catalog-connectors/README.md b/component-catalog-connectors/README.md new file mode 100644 index 0000000..b77b39a --- /dev/null +++ b/component-catalog-connectors/README.md @@ -0,0 +1,2 @@ +## Lorem Ipsum + diff --git a/component-catalog-connectors/mlx-connector/README.md b/component-catalog-connectors/mlx-connector/README.md new file mode 100644 index 0000000..bed1e54 --- /dev/null +++ b/component-catalog-connectors/mlx-connector/README.md @@ -0,0 +1,14 @@ +## Lorem Ipsum + +1. Git clone the catalog connector PR. +1. From this repository copy `src/mlx-catalog.json` to the `elyra/elyra/metadata/schemas` directory. +1. From this repository copy `src/mlx_component_catalog_connector.py` to the `elyra/elyra/pipeline` directory. +1. In `setup.py` add the last line shown below + ``` + 'elyra.component.catalog_types': [ + 'url-catalog = elyra.pipeline.component_reader:UrlComponentCatalogConnector', + 'local-file-catalog = elyra.pipeline.component_reader:FilesystemComponentCatalogConnector', + 'local-directory-catalog = elyra.pipeline.component_reader:DirectoryComponentCatalogConnector', + 'mlx-catalog = elyra.pipeline.mlx_component_catalog_connector:MLXComponentCatalogConnector' + ``` +1. Rebuild Elyra. \ No newline at end of file diff --git a/component-catalog-connectors/mlx-connector/src/mlx-catalog.json b/component-catalog-connectors/mlx-connector/src/mlx-catalog.json new file mode 100644 index 0000000..f9a0134 --- /dev/null +++ b/component-catalog-connectors/mlx-connector/src/mlx-catalog.json @@ -0,0 +1,79 @@ +{ + "$schema": "https://raw.githubusercontent.com/elyra-ai/elyra/master/elyra/metadata/schemas/meta-schema.json", + "$id": "https://raw.githubusercontent.com/kiersten-stokes/elyra/registry-updates/elyra/metadata/schemas/url-catalog.json", + "title": "Machine Learning Exchange Component Catalog", + "name": "mlx-catalog", + "display_name": "Machine Learning Exchange Component Catalog", + "schemaspace": "component-registries", + "schemaspace_id": "ae79159a-489d-4656-83a6-1adfbc567c70", + "uihints": { + "title": "Machine Learning Exchange Component Catalog", + "icon": "", + "reference_url": "https://elyra.readthedocs.io/en/stable/user_guide/pipeline-components.html" + }, + "properties": { + "schema_name": { + "title": "Schema Name", + "description": "The schema associated with this instance", + "type": "string", + "const": "mlx-catalog" + }, + "display_name": { + "title": "Display Name", + "description": "Display name of this Component Catalog", + "type": "string", + "minLength": 1 + }, + "version": { + "title": "Version", + "description": "The version associated with this instance", + "type": "integer", + "const": 1 + }, + "metadata": { + "description": "Additional data specific to this metadata", + "type": "object", + "properties": { + "description": { + "title": "Description", + "description": "Description of this Component Catalog", + "type": "string" + }, + "runtime": { + "title": "Runtime", + "description": "The runtime associated with this Component Catalog", + "type": "string", + "$comment": "This enum is dynamically generated to contain the available runtime values.", + "enum": ["{currently-configured-runtimes}"], + "uihints": { + "field_type": "dropdown" + } + }, + "categories": { + "title": "Category Names", + "description": "Category names associated with this Component Catalog (the components defined in this registry will be organized in the component palette according to these categories)", + "type": "array", + "items": { + "type": "string", + "maxLength": 18 + }, + "uihints": { + "field_type": "array", + "category": "Component Categories" + } + }, + "mlx_api_url": { + "title": "MLX API URL", + "description": "API endpoint URL for the Machine Learning Exchange server", + "type": "string", + "format": "uri", + "uihints": { + "category": "Source" + } + } + }, + "required": ["runtime", "mlx_api_url"] + } + }, + "required": ["schema_name", "display_name", "version", "metadata"] +} diff --git a/component-catalog-connectors/mlx-connector/src/mlx_component_catalog_connector.py b/component-catalog-connectors/mlx-connector/src/mlx_component_catalog_connector.py new file mode 100644 index 0000000..aaa9b28 --- /dev/null +++ b/component-catalog-connectors/mlx-connector/src/mlx_component_catalog_connector.py @@ -0,0 +1,168 @@ +# +# Copyright 2018-2021 Elyra Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from http import HTTPStatus +from io import BytesIO +import tarfile +from tempfile import TemporaryFile +from typing import Any +from typing import Dict +from typing import List +from typing import Optional +from urllib.parse import urlparse + +import requests + +from elyra.pipeline.component_reader import ComponentCatalogConnector + + +class MLXComponentCatalogConnector(ComponentCatalogConnector): + """ + Read component definitions from a Machine Learning Exchange catalog + https://github.com/machine-learning-exchange + """ + + def get_catalog_entries(self, catalog_metadata: Dict[str, Any]) -> List[Dict[str, Any]]: + + """ + Returns a list of component_metadata instances, one per component found in the given registry. + The form that component_metadata takes is determined by requirements of the reader class. + + :param registry_metadata: the dictionary-form of the Metadata instance for a single registry + """ + component_list = [] + + # verify that the required inputs were provided + mlx_api_url = catalog_metadata.get('mlx_api_url') + + if mlx_api_url is None: + self.log.error('Cannot connect to MLX catalog: An API endpoint URL must be provided.') + # return empty component specification list + return component_list + + # TODO: if runtime type is not kfp + if catalog_metadata.get('runtime') != 'kfp': + self.log.error(f'MLX catalog {mlx_api_url} only supports Kubeflow Pipelines.') + # return empty component specification list + return component_list + + try: + # invoke endpoint to retrieve list of component specifications from + # the catalog + self.log.debug(f'Retrieving component list from MLX catalog \'{mlx_api_url}\'.') + u = urlparse(mlx_api_url) + # assemble MLX component list endpoint URL + endpoint = u._replace(path='/apis/v1alpha1/components').geturl() + + # Query MLX catalog components endpoint + res = requests.get(endpoint) + if res.status_code != HTTPStatus.OK: + self.log.warning(f'Error fetching component list from MLX catalog {mlx_api_url}: ' + f'Request: {endpoint} HTTP code: {res.status_code}.') + return component_list + + if res.headers['Content-Type'] != 'application/json': + self.log.warning(f'Error fetching component list from MLX catalog {mlx_api_url}: ' + f'Unexpected content type: {res.headers["Content-Type"]}.' + f'Content: {res.content}') + return component_list + + # the response is JSON formatted: + # "components": [ + # { + # "id": "component-id-used-for-retrieval", + # ... + # } + # ] + for component in res.json().get('components', []): + # TODO apply filtering, if an expression was provided + component_list.append({'mlx_component_id': component.get('id')}) + + except Exception as ex: + self.log.warning(f'Error fetching component list from MLX catalog {mlx_api_url}: {ex}') + + return component_list + + def read_catalog_entry(self, + catalog_entry_data: Dict[str, Any], + catalog_metadata: Dict[str, Any]) -> Optional[str]: + """ + Read a component definition for a single catalog entry using the its data (as returned from + get_catalog_entries()) and the catalog metadata, if needed + + :param catalog_entry_data: a dictionary that contains the information needed to read the content + of the component definition + :param catalog_metadata: the metadata associated with the catalog in which this catalog entry is + stored; in addition to catalog_entry_data, catalog_metadata may also be + needed to read the component definition for certain types of catalogs + + :returns: the content of the given catalog entry's definition in string form + """ + + # verify that the required inputs were provided + mlx_api_url = catalog_metadata.get('mlx_api_url') + if mlx_api_url is None: + self.log.error('Cannot connect to MLX catalog: An API endpoint URL must be provided.') + return None + + mlx_component_id = catalog_entry_data['mlx_component_id'] + if mlx_component_id is None: + self.log.error(f'Cannot retrieve component specification from MLX catalog {mlx_api_url}: ' + 'A component id must be provided.') + return None + + try: + u = urlparse(mlx_api_url) + # assemble MLX component list endpoint URL + endpoint = u._replace(path=f'apis/v1alpha1/components/{mlx_component_id}/download').geturl() + res = requests.get(endpoint) + except Exception as e: + self.log.error(f'Failed to download component specification {mlx_component_id} ' + f'from {mlx_api_url}: {str(e)}') + return None + + if res.status_code != HTTPStatus.OK: + self.log.error(f'Error fetching component specification {mlx_component_id} ' + f'from MLX catalog {mlx_api_url}: ' + f'Request: {endpoint} HTTP code: {res.status_code}.') + return None + + # response type should be 'application/gzip' + # Content-Disposition: attachment; filename=model-fairness-check.tgz + with TemporaryFile() as fp: + fp.write(res.content) + fp.seek(0) + try: + tar = tarfile.open(fileobj=BytesIO(fp.read()), + mode='r:gz') + if len(tar.getnames()) > 1: + self.log.error(f'The response archive contains more than one member: {tar.getnames()}') + + return tar.extractfile(tar.getnames()[0]).read() + except Exception as ex: + # the response is not a tgz file + self.log.error(f'The MLX catalog response could not be processed: {ex}') + + return None + + def get_hash_keys(self) -> List[Any]: + """ + Provides a list of keys available in the 'catalog_entry_data' dictionary whose values + will be used to construct a unique hash id for each entry with the given catalog type + + :returns: a list of keys + """ + return ['mlx_component_id'] From 23327fe8e78874e51f3db42df01de484707d6604 Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Mon, 25 Oct 2021 14:19:20 -0700 Subject: [PATCH 02/13] BYO and connector enhancements --- .../mlx-connector/Makefile | 29 +++++++++ .../mlx-connector/setup.py | 64 +++++++++++++++++++ .../mlx-connector/src/_version.py | 16 +++++ .../src/catalog_connector/__init__.py | 15 +++++ .../mlx_component_catalog_connector.py | 22 +++++-- .../catalog_connector/mlx_schema_provider.py | 44 +++++++++++++ .../src/{ => schema}/mlx-catalog.json | 10 ++- .../mlx-connector/test_requirements.txt | 1 + 8 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 component-catalog-connectors/mlx-connector/Makefile create mode 100644 component-catalog-connectors/mlx-connector/setup.py create mode 100644 component-catalog-connectors/mlx-connector/src/_version.py create mode 100644 component-catalog-connectors/mlx-connector/src/catalog_connector/__init__.py rename component-catalog-connectors/mlx-connector/src/{ => catalog_connector}/mlx_component_catalog_connector.py (88%) create mode 100644 component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_schema_provider.py rename component-catalog-connectors/mlx-connector/src/{ => schema}/mlx-catalog.json (89%) create mode 100644 component-catalog-connectors/mlx-connector/test_requirements.txt diff --git a/component-catalog-connectors/mlx-connector/Makefile b/component-catalog-connectors/mlx-connector/Makefile new file mode 100644 index 0000000..b7c3b91 --- /dev/null +++ b/component-catalog-connectors/mlx-connector/Makefile @@ -0,0 +1,29 @@ +# +# Copyright 2021-2021 Elyra Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +.PHONY: help lint package-connector + +SHELL:=/bin/bash + +help: +# http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html + @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +test-dependencies: + @pip install -q -r test_requirements.txt + +lint: test-dependencies + flake8 src/. diff --git a/component-catalog-connectors/mlx-connector/setup.py b/component-catalog-connectors/mlx-connector/setup.py new file mode 100644 index 0000000..8a7c316 --- /dev/null +++ b/component-catalog-connectors/mlx-connector/setup.py @@ -0,0 +1,64 @@ +# +# Copyright 2018-2021 Elyra Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import os + +from setuptools import find_packages, setup + +long_desc = """ + Elyra component catalog connector for the Machine Learning Exchange + """ + +here = os.path.abspath(os.path.dirname(__file__)) + +version_ns = {} +with open(os.path.join(here, 'src', '_version.py')) as f: + exec(f.read(), {}, version_ns) + + +setup_args = dict( + name="mlx-component-catalog-connector", + version=version_ns['__version__'], + url="https://github.com/elyra-ai/examples", + description="Elyra component catalog connector for the Machine Learning Exchange", + long_description=long_desc, + author="Elyra Maintainers", + license="Apache License Version 2.0", + data_files=[('schema', 'src/schema/mlx-catalog.json')], + packages=find_packages(), + install_requires=[ + 'elyra', + 'requests' + ], + include_package_data=True, + classifiers=( + 'Development Status :: 4 - Beta', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: OS Independent', + 'Topic :: Scientific/Engineering', + 'Topic :: Scientific/Engineering :: Artificial Intelligence', + 'Topic :: Software Development', + 'Topic :: Software Development :: Libraries', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + ) +) + + +if __name__ == '__main__': + setup(**setup_args) diff --git a/component-catalog-connectors/mlx-connector/src/_version.py b/component-catalog-connectors/mlx-connector/src/_version.py new file mode 100644 index 0000000..4abcc16 --- /dev/null +++ b/component-catalog-connectors/mlx-connector/src/_version.py @@ -0,0 +1,16 @@ +# +# Copyright 2018-2021 Elyra Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +__version__ = '0.0.1' diff --git a/component-catalog-connectors/mlx-connector/src/catalog_connector/__init__.py b/component-catalog-connectors/mlx-connector/src/catalog_connector/__init__.py new file mode 100644 index 0000000..febc0a2 --- /dev/null +++ b/component-catalog-connectors/mlx-connector/src/catalog_connector/__init__.py @@ -0,0 +1,15 @@ +# +# Copyright 2018-2021 Elyra Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/component-catalog-connectors/mlx-connector/src/mlx_component_catalog_connector.py b/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_component_catalog_connector.py similarity index 88% rename from component-catalog-connectors/mlx-connector/src/mlx_component_catalog_connector.py rename to component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_component_catalog_connector.py index aaa9b28..12a1b97 100644 --- a/component-catalog-connectors/mlx-connector/src/mlx_component_catalog_connector.py +++ b/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_component_catalog_connector.py @@ -16,6 +16,7 @@ from http import HTTPStatus from io import BytesIO +import re import tarfile from tempfile import TemporaryFile from typing import Any @@ -26,7 +27,7 @@ import requests -from elyra.pipeline.component_reader import ComponentCatalogConnector +from elyra.pipeline.catalog_connector import ComponentCatalogConnector class MLXComponentCatalogConnector(ComponentCatalogConnector): @@ -53,7 +54,6 @@ def get_catalog_entries(self, catalog_metadata: Dict[str, Any]) -> List[Dict[str # return empty component specification list return component_list - # TODO: if runtime type is not kfp if catalog_metadata.get('runtime') != 'kfp': self.log.error(f'MLX catalog {mlx_api_url} only supports Kubeflow Pipelines.') # return empty component specification list @@ -84,12 +84,26 @@ def get_catalog_entries(self, catalog_metadata: Dict[str, Any]) -> List[Dict[str # "components": [ # { # "id": "component-id-used-for-retrieval", + # "name": "user friendly component name" # ... # } # ] + + # create component filter regex if a filter condition was + # specified by the user + filter_expression = catalog_metadata.get('filter', '').strip() + regex = None + if len(filter_expression) > 0: + regex = filter_expression.replace('*', '.*').replace('?', '.?') + + # post-process the component list by applying the filter regex, if + # one was specified for component in res.json().get('components', []): - # TODO apply filtering, if an expression was provided - component_list.append({'mlx_component_id': component.get('id')}) + if regex: + if re.fullmatch(regex, component.get('name', ''), flags=re.IGNORECASE): + component_list.append({'mlx_component_id': component.get('id')}) + else: + component_list.append({'mlx_component_id': component.get('id')}) except Exception as ex: self.log.warning(f'Error fetching component list from MLX catalog {mlx_api_url}: {ex}') diff --git a/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_schema_provider.py b/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_schema_provider.py new file mode 100644 index 0000000..abaaad9 --- /dev/null +++ b/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_schema_provider.py @@ -0,0 +1,44 @@ +# +# Copyright 2018-2021 Elyra Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import json +from pathlib import Path +from typing import Dict +from typing import List + +from elyra.metadata.schema import SchemasProvider + + +class MLXSchemasProvider(SchemasProvider): + """ + Enables BYO catalog connector for the Machine Learning Exchange + """ + + def get_schemas(self) -> List[Dict]: + """ + Return the MLX catalog connector schema + """ + mlx_catalog_schema_defs = [] + try: + mlx_catalog_connector_schema_file = Path(__file__) / 'schema' / 'mlx-catalog.json' + print(f'Reading MLX catalog connector schema from {mlx_catalog_connector_schema_file}') + with open(mlx_catalog_connector_schema_file, 'r') as fp: + mlx_catalog_connector_schema = json.load(fp) + mlx_catalog_schema_defs.append(mlx_catalog_connector_schema) + except Exception as ex: + print(f'Error reading MLX catalog connector schema {mlx_catalog_connector_schema_file}: {ex}') + + return mlx_catalog_schema_defs diff --git a/component-catalog-connectors/mlx-connector/src/mlx-catalog.json b/component-catalog-connectors/mlx-connector/src/schema/mlx-catalog.json similarity index 89% rename from component-catalog-connectors/mlx-connector/src/mlx-catalog.json rename to component-catalog-connectors/mlx-connector/src/schema/mlx-catalog.json index f9a0134..640dcaa 100644 --- a/component-catalog-connectors/mlx-connector/src/mlx-catalog.json +++ b/component-catalog-connectors/mlx-connector/src/schema/mlx-catalog.json @@ -70,7 +70,15 @@ "uihints": { "category": "Source" } - } + }, + "filter": { + "title": "Component name filter", + "description": "Only return components that match the specified name filter expression. * and ? are valid wildcards.", + "type": "string", + "uihints": { + "category": "Source" + } + } }, "required": ["runtime", "mlx_api_url"] } diff --git a/component-catalog-connectors/mlx-connector/test_requirements.txt b/component-catalog-connectors/mlx-connector/test_requirements.txt new file mode 100644 index 0000000..28f28b5 --- /dev/null +++ b/component-catalog-connectors/mlx-connector/test_requirements.txt @@ -0,0 +1 @@ +flake8>=3.5.0,<3.9.0 From dc61888a1961d5efe619c0ff20aa36062d929dbf Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Mon, 25 Oct 2021 14:34:01 -0700 Subject: [PATCH 03/13] More prep work --- component-catalog-connectors/mlx-connector/Makefile | 5 ++++- component-catalog-connectors/mlx-connector/setup.py | 2 +- .../catalog_connector/mlx_component_catalog_connector.py | 8 ++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/component-catalog-connectors/mlx-connector/Makefile b/component-catalog-connectors/mlx-connector/Makefile index b7c3b91..b37f168 100644 --- a/component-catalog-connectors/mlx-connector/Makefile +++ b/component-catalog-connectors/mlx-connector/Makefile @@ -14,7 +14,7 @@ # limitations under the License. # -.PHONY: help lint package-connector +.PHONY: help lint test-dependencies test-install SHELL:=/bin/bash @@ -27,3 +27,6 @@ test-dependencies: lint: test-dependencies flake8 src/. + +test-install: lint ## install MLX component connector package from source + pip install -e . diff --git a/component-catalog-connectors/mlx-connector/setup.py b/component-catalog-connectors/mlx-connector/setup.py index 8a7c316..5e17062 100644 --- a/component-catalog-connectors/mlx-connector/setup.py +++ b/component-catalog-connectors/mlx-connector/setup.py @@ -39,7 +39,7 @@ data_files=[('schema', 'src/schema/mlx-catalog.json')], packages=find_packages(), install_requires=[ - 'elyra', + 'elyra==3.3.0.dev0', 'requests' ], include_package_data=True, diff --git a/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_component_catalog_connector.py b/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_component_catalog_connector.py index 12a1b97..910a47e 100644 --- a/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_component_catalog_connector.py +++ b/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_component_catalog_connector.py @@ -14,10 +14,10 @@ # limitations under the License. # -from http import HTTPStatus -from io import BytesIO import re import tarfile +from http import HTTPStatus +from io import BytesIO from tempfile import TemporaryFile from typing import Any from typing import Dict @@ -25,10 +25,10 @@ from typing import Optional from urllib.parse import urlparse -import requests - from elyra.pipeline.catalog_connector import ComponentCatalogConnector +import requests + class MLXComponentCatalogConnector(ComponentCatalogConnector): """ From a8b32102e777344a81ebd55bb8fae34ad75f122f Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Mon, 25 Oct 2021 15:16:30 -0700 Subject: [PATCH 04/13] Fix setup.py entrypoints --- component-catalog-connectors/mlx-connector/setup.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/component-catalog-connectors/mlx-connector/setup.py b/component-catalog-connectors/mlx-connector/setup.py index 5e17062..d118aa0 100644 --- a/component-catalog-connectors/mlx-connector/setup.py +++ b/component-catalog-connectors/mlx-connector/setup.py @@ -43,6 +43,14 @@ 'requests' ], include_package_data=True, + entry_points={ + 'metadata.schemas_providers': [ + 'component-registries = catalog_connector.mlx_component_catalog_connector:MLXSchemasProvider' + ], + 'elyra.component.catalog_types': [ + 'mlx-catalog = catalog_connector.mlx_component_catalog_connector:MLXComponentCatalogConnector' + ], + }, classifiers=( 'Development Status :: 4 - Beta', 'License :: OSI Approved :: Apache Software License', From 3000b3c312245461a48845d026b5b98341e2d238 Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Mon, 25 Oct 2021 15:37:04 -0700 Subject: [PATCH 05/13] More fixes for obvious issues --- component-catalog-connectors/mlx-connector/setup.py | 2 +- .../mlx-connector/src/catalog_connector/mlx_schema_provider.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/component-catalog-connectors/mlx-connector/setup.py b/component-catalog-connectors/mlx-connector/setup.py index d118aa0..649a55a 100644 --- a/component-catalog-connectors/mlx-connector/setup.py +++ b/component-catalog-connectors/mlx-connector/setup.py @@ -45,7 +45,7 @@ include_package_data=True, entry_points={ 'metadata.schemas_providers': [ - 'component-registries = catalog_connector.mlx_component_catalog_connector:MLXSchemasProvider' + 'component-registries = catalog_connector.mlx_schema_provider:MLXSchemasProvider' ], 'elyra.component.catalog_types': [ 'mlx-catalog = catalog_connector.mlx_component_catalog_connector:MLXComponentCatalogConnector' diff --git a/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_schema_provider.py b/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_schema_provider.py index abaaad9..aaa8c56 100644 --- a/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_schema_provider.py +++ b/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_schema_provider.py @@ -33,7 +33,7 @@ def get_schemas(self) -> List[Dict]: """ mlx_catalog_schema_defs = [] try: - mlx_catalog_connector_schema_file = Path(__file__) / 'schema' / 'mlx-catalog.json' + mlx_catalog_connector_schema_file = Path(__file__) / '..' / 'schema' / 'mlx-catalog.json' print(f'Reading MLX catalog connector schema from {mlx_catalog_connector_schema_file}') with open(mlx_catalog_connector_schema_file, 'r') as fp: mlx_catalog_connector_schema = json.load(fp) From 0c0cd433d2206241da4c3a8f9c2413852f6ca4c7 Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Mon, 25 Oct 2021 17:09:40 -0700 Subject: [PATCH 06/13] First working BYO version --- .../mlx-connector/MANIFEST.in | 37 +++++++++++++ .../mlx-connector/Makefile | 12 ++++- .../__init__.py | 0 .../_version.py | 0 .../mlx-catalog.json | 0 .../mlx_component_catalog_connector.py | 6 +-- .../mlx_schema_provider.py | 2 +- .../mlx-connector/setup.cfg | 53 +++++++++++++++++++ .../mlx-connector/setup.py | 13 +++-- 9 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 component-catalog-connectors/mlx-connector/MANIFEST.in rename component-catalog-connectors/mlx-connector/{src/catalog_connector => mlx_catalog_connector}/__init__.py (100%) rename component-catalog-connectors/mlx-connector/{src => mlx_catalog_connector}/_version.py (100%) rename component-catalog-connectors/mlx-connector/{src/schema => mlx_catalog_connector}/mlx-catalog.json (100%) rename component-catalog-connectors/mlx-connector/{src/catalog_connector => mlx_catalog_connector}/mlx_component_catalog_connector.py (100%) rename component-catalog-connectors/mlx-connector/{src/catalog_connector => mlx_catalog_connector}/mlx_schema_provider.py (93%) create mode 100644 component-catalog-connectors/mlx-connector/setup.cfg diff --git a/component-catalog-connectors/mlx-connector/MANIFEST.in b/component-catalog-connectors/mlx-connector/MANIFEST.in new file mode 100644 index 0000000..4bd1206 --- /dev/null +++ b/component-catalog-connectors/mlx-connector/MANIFEST.in @@ -0,0 +1,37 @@ +# +# Copyright 2018-2021 Elyra Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# exclude from ANY directory +global-exclude *.ipynb +global-exclude *.py[cod] +global-exclude __pycache__ +global-exclude .git +global-exclude .ipynb_checkpoints +global-exclude .DS_Store +global-exclude *.sh +global-exclude docs +global-exclude tests + +# explicit includes +include CONTRIBUTING.md +include README.md +include LICENSE +include dist/*.tgz + +recursive-exclude * __pycache__ +recursive-exclude * *.py[co] + +recursive-include mlx_catalog_connector mlx-catalog.json diff --git a/component-catalog-connectors/mlx-connector/Makefile b/component-catalog-connectors/mlx-connector/Makefile index b37f168..0f54e6f 100644 --- a/component-catalog-connectors/mlx-connector/Makefile +++ b/component-catalog-connectors/mlx-connector/Makefile @@ -14,7 +14,7 @@ # limitations under the License. # -.PHONY: help lint test-dependencies test-install +.PHONY: help clean lint test-dependencies test-install dist SHELL:=/bin/bash @@ -22,11 +22,19 @@ help: # http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +clean: + rm -rf dist/ + rm -rf build/ + rm -rf *.egg-info + test-dependencies: @pip install -q -r test_requirements.txt lint: test-dependencies - flake8 src/. + flake8 catalog_connector + +dist: clean lint ## Build distribution + python setup.py bdist_wheel sdist test-install: lint ## install MLX component connector package from source pip install -e . diff --git a/component-catalog-connectors/mlx-connector/src/catalog_connector/__init__.py b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/__init__.py similarity index 100% rename from component-catalog-connectors/mlx-connector/src/catalog_connector/__init__.py rename to component-catalog-connectors/mlx-connector/mlx_catalog_connector/__init__.py diff --git a/component-catalog-connectors/mlx-connector/src/_version.py b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/_version.py similarity index 100% rename from component-catalog-connectors/mlx-connector/src/_version.py rename to component-catalog-connectors/mlx-connector/mlx_catalog_connector/_version.py diff --git a/component-catalog-connectors/mlx-connector/src/schema/mlx-catalog.json b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json similarity index 100% rename from component-catalog-connectors/mlx-connector/src/schema/mlx-catalog.json rename to component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json diff --git a/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_component_catalog_connector.py b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_component_catalog_connector.py similarity index 100% rename from component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_component_catalog_connector.py rename to component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_component_catalog_connector.py index 910a47e..a680574 100644 --- a/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_component_catalog_connector.py +++ b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_component_catalog_connector.py @@ -14,10 +14,11 @@ # limitations under the License. # -import re -import tarfile + from http import HTTPStatus from io import BytesIO +import re +import tarfile from tempfile import TemporaryFile from typing import Any from typing import Dict @@ -26,7 +27,6 @@ from urllib.parse import urlparse from elyra.pipeline.catalog_connector import ComponentCatalogConnector - import requests diff --git a/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_schema_provider.py b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_schema_provider.py similarity index 93% rename from component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_schema_provider.py rename to component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_schema_provider.py index aaa8c56..4c0fc0d 100644 --- a/component-catalog-connectors/mlx-connector/src/catalog_connector/mlx_schema_provider.py +++ b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_schema_provider.py @@ -33,7 +33,7 @@ def get_schemas(self) -> List[Dict]: """ mlx_catalog_schema_defs = [] try: - mlx_catalog_connector_schema_file = Path(__file__) / '..' / 'schema' / 'mlx-catalog.json' + mlx_catalog_connector_schema_file = Path(__file__).parent / 'mlx-catalog.json' print(f'Reading MLX catalog connector schema from {mlx_catalog_connector_schema_file}') with open(mlx_catalog_connector_schema_file, 'r') as fp: mlx_catalog_connector_schema = json.load(fp) diff --git a/component-catalog-connectors/mlx-connector/setup.cfg b/component-catalog-connectors/mlx-connector/setup.cfg new file mode 100644 index 0000000..e35c831 --- /dev/null +++ b/component-catalog-connectors/mlx-connector/setup.cfg @@ -0,0 +1,53 @@ +# +# Copyright 2018-2021 Elyra Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +[bdist_wheel] +universal=0 + +[metadata] +description_file=README.md + +[flake8] +application-import-names = catalog_connector +application-package-names = catalog_connector +enable-extensions = G +# References: +# https://flake8.readthedocs.io/en/latest/user/configuration.html +# https://flake8.readthedocs.io/en/latest/user/error-codes.html +# https://docs.openstack.org/hacking/latest/user/hacking.html +ignore = + # Import formatting + E4, + # Comparing types instead of isinstance + E721, + # Assigning lambda expression + E731, + # Ambiguous variable names + E741, + # File contains nothing but comments + H104, + # Include name with TODOs as in # TODO(yourname) + H101, + # Enable mocking + H216, + # Multi line docstrings should start without a leading new line + H404, + # Multi line docstrings should start with a one line summary followed by an empty line + H405, + # Allow breaks after binary operators + W504 +import-order-style = google +max-line-length = 120 diff --git a/component-catalog-connectors/mlx-connector/setup.py b/component-catalog-connectors/mlx-connector/setup.py index 649a55a..04f6a4c 100644 --- a/component-catalog-connectors/mlx-connector/setup.py +++ b/component-catalog-connectors/mlx-connector/setup.py @@ -24,10 +24,9 @@ here = os.path.abspath(os.path.dirname(__file__)) version_ns = {} -with open(os.path.join(here, 'src', '_version.py')) as f: +with open(os.path.join(here, 'mlx_catalog_connector', '_version.py')) as f: exec(f.read(), {}, version_ns) - setup_args = dict( name="mlx-component-catalog-connector", version=version_ns['__version__'], @@ -36,22 +35,22 @@ long_description=long_desc, author="Elyra Maintainers", license="Apache License Version 2.0", - data_files=[('schema', 'src/schema/mlx-catalog.json')], packages=find_packages(), install_requires=[ 'elyra==3.3.0.dev0', 'requests' ], + setup_requires=['flake8'], include_package_data=True, entry_points={ 'metadata.schemas_providers': [ - 'component-registries = catalog_connector.mlx_schema_provider:MLXSchemasProvider' + 'component-registries = mlx_catalog_connector.mlx_schema_provider:MLXSchemasProvider' ], 'elyra.component.catalog_types': [ - 'mlx-catalog = catalog_connector.mlx_component_catalog_connector:MLXComponentCatalogConnector' + 'mlx-catalog = mlx_catalog_connector.mlx_component_catalog_connector:MLXComponentCatalogConnector' ], }, - classifiers=( + classifiers=[ 'Development Status :: 4 - Beta', 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent', @@ -64,7 +63,7 @@ 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', - ) + ] ) From 6efdc43dce968f4560cfd895c66372f679d713df Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Mon, 25 Oct 2021 17:12:34 -0700 Subject: [PATCH 07/13] Update Makefile --- component-catalog-connectors/mlx-connector/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/component-catalog-connectors/mlx-connector/Makefile b/component-catalog-connectors/mlx-connector/Makefile index 0f54e6f..9476a88 100644 --- a/component-catalog-connectors/mlx-connector/Makefile +++ b/component-catalog-connectors/mlx-connector/Makefile @@ -31,10 +31,10 @@ test-dependencies: @pip install -q -r test_requirements.txt lint: test-dependencies - flake8 catalog_connector + flake8 mlx_catalog_connector dist: clean lint ## Build distribution python setup.py bdist_wheel sdist test-install: lint ## install MLX component connector package from source - pip install -e . + pip install . From 0aad70f3ffcda422cd11b78e6000d5390c3012f5 Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Tue, 26 Oct 2021 09:29:04 -0700 Subject: [PATCH 08/13] Incorporate review feedback --- component-catalog-connectors/README.md | 3 ++- .../mlx-connector/README.md | 23 ++++++++----------- .../mlx_catalog_connector/mlx-catalog.json | 4 ++-- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/component-catalog-connectors/README.md b/component-catalog-connectors/README.md index b77b39a..f3d5c55 100644 --- a/component-catalog-connectors/README.md +++ b/component-catalog-connectors/README.md @@ -1,2 +1,3 @@ -## Lorem Ipsum +## Bring your own component catalog connector +Lorem Ipsum diff --git a/component-catalog-connectors/mlx-connector/README.md b/component-catalog-connectors/mlx-connector/README.md index bed1e54..3b99822 100644 --- a/component-catalog-connectors/mlx-connector/README.md +++ b/component-catalog-connectors/mlx-connector/README.md @@ -1,14 +1,9 @@ -## Lorem Ipsum - -1. Git clone the catalog connector PR. -1. From this repository copy `src/mlx-catalog.json` to the `elyra/elyra/metadata/schemas` directory. -1. From this repository copy `src/mlx_component_catalog_connector.py` to the `elyra/elyra/pipeline` directory. -1. In `setup.py` add the last line shown below - ``` - 'elyra.component.catalog_types': [ - 'url-catalog = elyra.pipeline.component_reader:UrlComponentCatalogConnector', - 'local-file-catalog = elyra.pipeline.component_reader:FilesystemComponentCatalogConnector', - 'local-directory-catalog = elyra.pipeline.component_reader:DirectoryComponentCatalogConnector', - 'mlx-catalog = elyra.pipeline.mlx_component_catalog_connector:MLXComponentCatalogConnector' - ``` -1. Rebuild Elyra. \ No newline at end of file +## Machine Learning Exchange catalog connector + +### Using the connector in Elyra + +Lorem Ipsum (pip install + configure connector) + +### How to build a catalog connector + +Lorem Ipsum (discusses the implementation artifacts in more details) \ No newline at end of file diff --git a/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json index 640dcaa..7cb4589 100644 --- a/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json +++ b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/elyra-ai/elyra/master/elyra/metadata/schemas/meta-schema.json", - "$id": "https://raw.githubusercontent.com/kiersten-stokes/elyra/registry-updates/elyra/metadata/schemas/url-catalog.json", + "$id": "https://raw.githubusercontent.com/elyra-ai/examples/master/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json", "title": "Machine Learning Exchange Component Catalog", "name": "mlx-catalog", "display_name": "Machine Learning Exchange Component Catalog", @@ -44,7 +44,7 @@ "description": "The runtime associated with this Component Catalog", "type": "string", "$comment": "This enum is dynamically generated to contain the available runtime values.", - "enum": ["{currently-configured-runtimes}"], + "enum": ["kfp"], "uihints": { "field_type": "dropdown" } From edb8a6428c7e30f17e8264f31a61938747a8ff33 Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Wed, 27 Oct 2021 10:02:55 -0700 Subject: [PATCH 09/13] Fix metadata.schemas_providers entry based on review feedback --- component-catalog-connectors/mlx-connector/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/component-catalog-connectors/mlx-connector/setup.py b/component-catalog-connectors/mlx-connector/setup.py index 04f6a4c..59646c0 100644 --- a/component-catalog-connectors/mlx-connector/setup.py +++ b/component-catalog-connectors/mlx-connector/setup.py @@ -44,7 +44,7 @@ include_package_data=True, entry_points={ 'metadata.schemas_providers': [ - 'component-registries = mlx_catalog_connector.mlx_schema_provider:MLXSchemasProvider' + 'mlx-catalog-schema = mlx_catalog_connector.mlx_schema_provider:MLXSchemasProvider' ], 'elyra.component.catalog_types': [ 'mlx-catalog = mlx_catalog_connector.mlx_component_catalog_connector:MLXComponentCatalogConnector' From 1f9c81663144e595f31f81a525d75aee3c493ed6 Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Fri, 29 Oct 2021 07:15:10 -0700 Subject: [PATCH 10/13] Finalize instructions for MLX connector --- component-catalog-connectors/README.md | 4 +- .../mlx-connector/Makefile | 7 ++- .../mlx-connector/README.md | 50 +++++++++++++++++-- .../mlx_catalog_connector/mlx-catalog.json | 1 + 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/component-catalog-connectors/README.md b/component-catalog-connectors/README.md index f3d5c55..b52ea15 100644 --- a/component-catalog-connectors/README.md +++ b/component-catalog-connectors/README.md @@ -1,3 +1,3 @@ -## Bring your own component catalog connector +## Component catalog connectors -Lorem Ipsum +This content is under development. diff --git a/component-catalog-connectors/mlx-connector/Makefile b/component-catalog-connectors/mlx-connector/Makefile index 9476a88..05264d0 100644 --- a/component-catalog-connectors/mlx-connector/Makefile +++ b/component-catalog-connectors/mlx-connector/Makefile @@ -14,7 +14,7 @@ # limitations under the License. # -.PHONY: help clean lint test-dependencies test-install dist +.PHONY: help clean lint test-dependencies source-install install dist SHELL:=/bin/bash @@ -36,5 +36,8 @@ lint: test-dependencies dist: clean lint ## Build distribution python setup.py bdist_wheel sdist -test-install: lint ## install MLX component connector package from source +source-install: dist ## Install MLX component connector package from source pip install . + +install: ## Install MLX component connector package from PyPI + pip install mlx-component-catalog-connector diff --git a/component-catalog-connectors/mlx-connector/README.md b/component-catalog-connectors/mlx-connector/README.md index 3b99822..3fb06c2 100644 --- a/component-catalog-connectors/mlx-connector/README.md +++ b/component-catalog-connectors/mlx-connector/README.md @@ -1,9 +1,51 @@ ## Machine Learning Exchange catalog connector -### Using the connector in Elyra +This catalog connector enables Elyra to load Kubeflow Pipelines components from [Machine Learning Exchange](https://github.com/machine-learning-exchange) (MLX) deployments. -Lorem Ipsum (pip install + configure connector) +### Install the connector -### How to build a catalog connector +1. [Install Elyra](https://elyra.readthedocs.io/en/stable/getting_started/installation.html) (version 3.3 and above). +1. Install the MLX catalog connector from source code + ``` + $ git clone https://github.com/elyra-ai/examples.git + $ cd examples/component-catalog-connectors/mlx-connector/ + $ make source-install + ``` +> Note: A rebuild of JupyterLab is not required! + +### Use the connector + +1. Launch JupyterLab. +1. [Open the `Manage Components` panel]( +https://elyra.readthedocs.io/en/stable/user_guide/pipeline-components.html#managing-custom-components-using-the-jupyterlab-ui). +1. Add a new MLX component catalog ('`+`' > '`New Machine Learning Exchange Component Catalog`'). +1. Specify a catalog name, e.g. '`MLX dev catalog`'. +1. Select Kubeflow Pipelines as runtime. +1. (Optional) Specify a category under which the catalog's component will be organized in the palette. +1. Configure the `MLX API URL`, e.g. '`http://my-mlx-server.mydomain:8080/`'. + > Note: The Machine Learning Exchange API URL is different from the GUI URL! +1. Apply an optional filter expression to the component names. The `*` (zero or more characters) and `?` (zero or one character) wildcards are supported. +1. Save the catalog entry. +1. Open the Kubeflow Pipelines Visual Pipeline Editor and expand the palette. The components that were loaded from the specified MLX URL are displayed. + +### Uninstall the connector + +1. Remove all MLX catalog entries from the '`Manage Components`' panel. +1. Stop JupyterLab. +1. Uninstall the `mlx-component-catalog-connector` package. + ``` + $ pip uninstall -y mlx-component-catalog-connector + ``` + +### Troubleshooting + +**Problem: The palette does not diplay any components from the configured catalog.** + +**Solution:** If the the Elyra GUI does not display any error message indicating that a problem was encountered, inspect the JupyterLab log file. + +Example error message (The specified MLX URL is invalid): + +``` +Error fetching component list from MLX catalog http://localhost:8080: ... Failed to establish a new connection: [Errno 61] Connection refused' +``` -Lorem Ipsum (discusses the implementation artifacts in more details) \ No newline at end of file diff --git a/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json index 7cb4589..de2b248 100644 --- a/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json +++ b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json @@ -45,6 +45,7 @@ "type": "string", "$comment": "This enum is dynamically generated to contain the available runtime values.", "enum": ["kfp"], + "default": "kfp", "uihints": { "field_type": "dropdown" } From b1b361d4c44f26039fa1fbe93d1dbf589ba36f80 Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Fri, 29 Oct 2021 09:50:00 -0700 Subject: [PATCH 11/13] Update READMEs --- README.md | 6 ++++++ .../build-a-custom-connector.md | 3 +++ .../connector-directory.md | 11 +++++++++++ .../mlx-connector/README.md | 18 +++++++++++++++--- 4 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 component-catalog-connectors/build-a-custom-connector.md create mode 100644 component-catalog-connectors/connector-directory.md diff --git a/README.md b/README.md index becccd8..0f015c0 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,12 @@ Tutorials to get started with generic pipelines in Elyra: - [Run generic pipelines on Apache Airflow](pipelines/run-generic-pipelines-on-apache-airflow) - [Run runtime-specific pipelines on Kubeflow Pipelines](pipelines/run-pipelines-on-kubeflow-pipelines) +### Pipeline component catalog connectors + +Elyra loads [custom components](https://elyra.readthedocs.io/en/stable/user_guide/pipeline-components.html) from component catalogs: +- [Find catalog connectors](component-catalog-connectors/connector-directory.md) +- [Build a custom catalog connector](component-catalog-connectors/build-a-custom-connector.md) + ### Custom pipeline component examples Pipeline nodes are implemented using diff --git a/component-catalog-connectors/build-a-custom-connector.md b/component-catalog-connectors/build-a-custom-connector.md new file mode 100644 index 0000000..e816b62 --- /dev/null +++ b/component-catalog-connectors/build-a-custom-connector.md @@ -0,0 +1,3 @@ +## How to build a component catalog connector + +This content is under development. \ No newline at end of file diff --git a/component-catalog-connectors/connector-directory.md b/component-catalog-connectors/connector-directory.md new file mode 100644 index 0000000..2a3e389 --- /dev/null +++ b/component-catalog-connectors/connector-directory.md @@ -0,0 +1,11 @@ +## Component catalog connectors + +The following third-party catalog connectors should work with Elyra. Connectors are provided as is and, unless specified otherwise, are not maintained by the Elyra core committers. + +- To add your connector to the list [create a pull request](https://github.com/elyra-ai/examples/pulls). +- Learn [how to build your own catalog connector](build-a-custom-connector.md). + +| Connector | Description | +| ----------- | ----------- | +| [Machine Learning Exchange](mlx-connector/) | Provides access to [Machine Learning Exchange](https://github.com/machine-learning-exchange) catalogs | + diff --git a/component-catalog-connectors/mlx-connector/README.md b/component-catalog-connectors/mlx-connector/README.md index 3fb06c2..79320a4 100644 --- a/component-catalog-connectors/mlx-connector/README.md +++ b/component-catalog-connectors/mlx-connector/README.md @@ -4,14 +4,26 @@ This catalog connector enables Elyra to load Kubeflow Pipelines components from ### Install the connector -1. [Install Elyra](https://elyra.readthedocs.io/en/stable/getting_started/installation.html) (version 3.3 and above). -1. Install the MLX catalog connector from source code +You can install the MLX catalog connector from PyPI or source code. Note that a **rebuild of JupyterLab is not required**. + +**Prerequisites** + +- [Install Elyra](https://elyra.readthedocs.io/en/stable/getting_started/installation.html) (version 3.3 and above). +- [Machine Learning Exchange deployment](https://github.com/machine-learning-exchange/mlx) ([quickstart guide](https://github.com/machine-learning-exchange/mlx/tree/main/quickstart)) + +**Install from PyPI** + + ``` + $ pip install mlx-component-catalog-connector + ``` + +**Install from source code** + ``` $ git clone https://github.com/elyra-ai/examples.git $ cd examples/component-catalog-connectors/mlx-connector/ $ make source-install ``` -> Note: A rebuild of JupyterLab is not required! ### Use the connector From 68f529384c1f7e64dda8bb714dc1dc89f0ef2950 Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Tue, 2 Nov 2021 07:10:53 -0700 Subject: [PATCH 12/13] Update schema and add logging --- .../mlx-connector/mlx_catalog_connector/mlx-catalog.json | 1 + .../mlx_catalog_connector/mlx_schema_provider.py | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json index de2b248..172edde 100644 --- a/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json +++ b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json @@ -6,6 +6,7 @@ "display_name": "Machine Learning Exchange Component Catalog", "schemaspace": "component-registries", "schemaspace_id": "ae79159a-489d-4656-83a6-1adfbc567c70", + "metadata_class_name": "elyra.pipeline.component_metadata.ComponentCatalogMetadata", "uihints": { "title": "Machine Learning Exchange Component Catalog", "icon": "", diff --git a/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_schema_provider.py b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_schema_provider.py index 4c0fc0d..043ac34 100644 --- a/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_schema_provider.py +++ b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_schema_provider.py @@ -15,6 +15,7 @@ # import json +import logging from pathlib import Path from typing import Dict from typing import List @@ -31,14 +32,17 @@ def get_schemas(self) -> List[Dict]: """ Return the MLX catalog connector schema """ + # use Elyra logger + log = logging.getLogger('ElyraApp') mlx_catalog_schema_defs = [] try: + # load MLX catalog schema definition mlx_catalog_connector_schema_file = Path(__file__).parent / 'mlx-catalog.json' - print(f'Reading MLX catalog connector schema from {mlx_catalog_connector_schema_file}') + log.debug(f'Reading MLX catalog connector schema from {mlx_catalog_connector_schema_file}') with open(mlx_catalog_connector_schema_file, 'r') as fp: mlx_catalog_connector_schema = json.load(fp) mlx_catalog_schema_defs.append(mlx_catalog_connector_schema) except Exception as ex: - print(f'Error reading MLX catalog connector schema {mlx_catalog_connector_schema_file}: {ex}') + log.error(f'Error reading MLX catalog connector schema {mlx_catalog_connector_schema_file}: {ex}') return mlx_catalog_schema_defs From af0db2ec0df5fd5c6c4a9c7603d458a8aaf5c55e Mon Sep 17 00:00:00 2001 From: Patrick Titzler Date: Fri, 5 Nov 2021 15:29:47 -0700 Subject: [PATCH 13/13] Minor improvements --- .../mlx-connector/Makefile | 1 + .../mlx-connector/README.md | 5 ++--- .../mlx_catalog_connector/mlx-catalog.json | 10 +++++----- .../mlx_component_catalog_connector.py | 18 +++++++++--------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/component-catalog-connectors/mlx-connector/Makefile b/component-catalog-connectors/mlx-connector/Makefile index 05264d0..00bdac2 100644 --- a/component-catalog-connectors/mlx-connector/Makefile +++ b/component-catalog-connectors/mlx-connector/Makefile @@ -26,6 +26,7 @@ clean: rm -rf dist/ rm -rf build/ rm -rf *.egg-info + - pip uninstall -y mlx-component-catalog-connector test-dependencies: @pip install -q -r test_requirements.txt diff --git a/component-catalog-connectors/mlx-connector/README.md b/component-catalog-connectors/mlx-connector/README.md index 79320a4..04b0d3c 100644 --- a/component-catalog-connectors/mlx-connector/README.md +++ b/component-catalog-connectors/mlx-connector/README.md @@ -28,7 +28,7 @@ You can install the MLX catalog connector from PyPI or source code. Note that a ### Use the connector 1. Launch JupyterLab. -1. [Open the `Manage Components` panel]( +1. [Open the '`Manage Components`' panel]( https://elyra.readthedocs.io/en/stable/user_guide/pipeline-components.html#managing-custom-components-using-the-jupyterlab-ui). 1. Add a new MLX component catalog ('`+`' > '`New Machine Learning Exchange Component Catalog`'). 1. Specify a catalog name, e.g. '`MLX dev catalog`'. @@ -51,7 +51,7 @@ https://elyra.readthedocs.io/en/stable/user_guide/pipeline-components.html#manag ### Troubleshooting -**Problem: The palette does not diplay any components from the configured catalog.** +**Problem: The palette does not display any components from the configured catalog.** **Solution:** If the the Elyra GUI does not display any error message indicating that a problem was encountered, inspect the JupyterLab log file. @@ -60,4 +60,3 @@ Example error message (The specified MLX URL is invalid): ``` Error fetching component list from MLX catalog http://localhost:8080: ... Failed to establish a new connection: [Errno 61] Connection refused' ``` - diff --git a/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json index 172edde..c8128d0 100644 --- a/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json +++ b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx-catalog.json @@ -10,7 +10,7 @@ "uihints": { "title": "Machine Learning Exchange Component Catalog", "icon": "", - "reference_url": "https://elyra.readthedocs.io/en/stable/user_guide/pipeline-components.html" + "reference_url": "https://github.com/elyra-ai/examples/tree/master/component-catalog-connectors/mlx-connector" }, "properties": { "schema_name": { @@ -38,13 +38,13 @@ "description": { "title": "Description", "description": "Description of this Component Catalog", - "type": "string" + "type": "string", + "default": "Kubeflow Pipelines component catalog" }, "runtime": { "title": "Runtime", - "description": "The runtime associated with this Component Catalog", + "description": "The Machine Learning Exchange only supports Kubeflow Pipeline components.", "type": "string", - "$comment": "This enum is dynamically generated to contain the available runtime values.", "enum": ["kfp"], "default": "kfp", "uihints": { @@ -53,7 +53,7 @@ }, "categories": { "title": "Category Names", - "description": "Category names associated with this Component Catalog (the components defined in this registry will be organized in the component palette according to these categories)", + "description": "Assign the components in the catalog to one or more categories, to group them in the visual pipeline editor palette.", "type": "array", "items": { "type": "string", diff --git a/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_component_catalog_connector.py b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_component_catalog_connector.py index a680574..145bbc9 100644 --- a/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_component_catalog_connector.py +++ b/component-catalog-connectors/mlx-connector/mlx_catalog_connector/mlx_component_catalog_connector.py @@ -51,17 +51,16 @@ def get_catalog_entries(self, catalog_metadata: Dict[str, Any]) -> List[Dict[str if mlx_api_url is None: self.log.error('Cannot connect to MLX catalog: An API endpoint URL must be provided.') - # return empty component specification list + # return empty component list return component_list if catalog_metadata.get('runtime') != 'kfp': self.log.error(f'MLX catalog {mlx_api_url} only supports Kubeflow Pipelines.') - # return empty component specification list + # return empty component list return component_list try: - # invoke endpoint to retrieve list of component specifications from - # the catalog + # invoke endpoint to retrieve component list from the catalog self.log.debug(f'Retrieving component list from MLX catalog \'{mlx_api_url}\'.') u = urlparse(mlx_api_url) # assemble MLX component list endpoint URL @@ -114,8 +113,8 @@ def read_catalog_entry(self, catalog_entry_data: Dict[str, Any], catalog_metadata: Dict[str, Any]) -> Optional[str]: """ - Read a component definition for a single catalog entry using the its data (as returned from - get_catalog_entries()) and the catalog metadata, if needed + Fetch the component that is identified by catalog_entry_data from + the MLX catalog. :param catalog_entry_data: a dictionary that contains the information needed to read the content of the component definition @@ -174,9 +173,10 @@ def read_catalog_entry(self, def get_hash_keys(self) -> List[Any]: """ - Provides a list of keys available in the 'catalog_entry_data' dictionary whose values - will be used to construct a unique hash id for each entry with the given catalog type + Identifies the unique MLX catalog key that read_catalog_entry can use + to fetch an entry from the catalog. Method get_catalog_entries retrieves + the list of available key values. - :returns: a list of keys + :returns: a list of keys, which is for MLX the component id """ return ['mlx_component_id']