test_handlers.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #
  2. # Copyright 2018-2022 Elyra Authors
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. import asyncio
  17. import json
  18. import os
  19. from conftest import KFP_COMPONENT_CACHE_INSTANCE
  20. import jupyter_core
  21. import pytest
  22. import requests
  23. from tornado.httpclient import HTTPClientError
  24. from elyra.metadata.metadata import Metadata
  25. from elyra.metadata.schemaspaces import ComponentCatalogs
  26. from elyra.pipeline.runtime_type import RuntimeProcessorType
  27. from elyra.pipeline.runtime_type import RuntimeTypeResources
  28. from elyra.tests.pipeline import resources
  29. from elyra.tests.util.handlers_utils import expected_http_error
  30. try:
  31. import importlib.resources as pkg_resources
  32. except ImportError:
  33. # Try backported to PY<37 `importlib_resources`.
  34. import importlib_resources as pkg_resources
  35. COMPONENT_CATALOG_DIRECTORY = os.path.join(jupyter_core.paths.ENV_JUPYTER_PATH[0], "components")
  36. TEST_CATALOG_NAME = "test_handlers_catalog"
  37. def _get_resource_path(filename):
  38. resource_path = os.path.join(os.path.dirname(__file__), "resources", "components", filename)
  39. resource_path = os.path.normpath(resource_path)
  40. return resource_path
  41. async def cli_catalog_instance(jp_fetch):
  42. # Create new registry instance with a single URL-based component
  43. # This is not a fixture because it needs to
  44. paths = [_get_resource_path("kfp_test_operator.yaml")]
  45. instance_metadata = {
  46. "description": "A test registry",
  47. "runtime_type": RuntimeProcessorType.KUBEFLOW_PIPELINES.name,
  48. "categories": ["New Components"],
  49. "paths": paths,
  50. }
  51. instance = Metadata(
  52. schema_name="local-file-catalog",
  53. name=TEST_CATALOG_NAME,
  54. display_name="New Test Catalog",
  55. metadata=instance_metadata,
  56. )
  57. body = json.dumps(instance.to_dict())
  58. r = await jp_fetch(
  59. "elyra", "metadata", ComponentCatalogs.COMPONENT_CATALOGS_SCHEMASPACE_ID, body=body, method="POST"
  60. )
  61. assert r.code == 201
  62. r = await jp_fetch("elyra", "metadata", ComponentCatalogs.COMPONENT_CATALOGS_SCHEMASPACE_ID)
  63. assert r.code == 200
  64. metadata = json.loads(r.body.decode())
  65. assert len(metadata) >= 1
  66. async def test_get_components(jp_fetch):
  67. # Ensure all valid components can be found
  68. runtime_type = RuntimeProcessorType.LOCAL
  69. response = await jp_fetch("elyra", "pipeline", "components", runtime_type.name)
  70. assert response.code == 200
  71. payload = json.loads(response.body.decode())
  72. palette = json.loads(pkg_resources.read_text(resources, "palette.json"))
  73. assert payload == palette
  74. async def test_get_component_properties_config(jp_fetch):
  75. # Ensure all valid component_entry properties can be found
  76. runtime_type = RuntimeProcessorType.LOCAL
  77. response = await jp_fetch("elyra", "pipeline", "components", runtime_type.name, "notebook", "properties")
  78. assert response.code == 200
  79. payload = json.loads(response.body.decode())
  80. properties = json.loads(pkg_resources.read_text(resources, "properties.json"))
  81. assert payload == properties
  82. @pytest.mark.parametrize("catalog_instance", [KFP_COMPONENT_CACHE_INSTANCE], indirect=True)
  83. async def test_get_component_properties_definition(catalog_instance, jp_fetch, caplog):
  84. # Ensure the definition for a component can be found
  85. component_url = (
  86. "https://raw.githubusercontent.com/elyra-ai/examples/master/component-catalog-connectors/"
  87. "kfp-example-components-connector/kfp_examples_connector/resources/download_data.yaml"
  88. )
  89. definition = requests.get(component_url)
  90. component_id = "elyra-kfp-examples-catalog:a08014f9252f" # static id for the 'Download Data' example component
  91. # Test with shorthand runtime (e.g. 'kfp', 'airflow') (support to be removed in later release)
  92. response = await jp_fetch("elyra", "pipeline", "components", "kfp", component_id)
  93. assert response.code == 200
  94. payload = json.loads(response.body.decode())
  95. assert payload["content"] == definition.text
  96. assert payload["mimeType"] == "text/x-yaml"
  97. assert "Deprecation warning" in caplog.text
  98. caplog.clear()
  99. # Test with runtime type name in endpoint
  100. runtime_type = RuntimeProcessorType.KUBEFLOW_PIPELINES
  101. response = await jp_fetch("elyra", "pipeline", "components", runtime_type.name, component_id)
  102. assert response.code == 200
  103. payload = json.loads(response.body.decode())
  104. assert payload["content"] == definition.text
  105. assert payload["mimeType"] == "text/x-yaml"
  106. assert "Deprecation warning" not in caplog.text
  107. async def test_runtime_types_resources(jp_fetch):
  108. # Ensure appropriate runtime types resources can be fetched
  109. response = await jp_fetch("elyra", "pipeline", "runtimes", "types")
  110. assert response.code == 200
  111. resources = json.loads(response.body.decode())
  112. runtime_types = resources["runtime_types"]
  113. assert len(runtime_types) >= 1 # We should have Local for sure
  114. for runtime_type_resources in runtime_types:
  115. assert runtime_type_resources.get("id") in ["LOCAL", "KUBEFLOW_PIPELINES", "APACHE_AIRFLOW", "ARGO"]
  116. # Acquire corresponding instance and compare that results are the same
  117. runtime_type = RuntimeProcessorType.get_instance_by_name(runtime_type_resources.get("id"))
  118. resources_instance = RuntimeTypeResources.get_instance_by_type(runtime_type)
  119. assert runtime_type_resources.get("display_name") == resources_instance.display_name
  120. assert runtime_type_resources.get("export_file_types") == resources_instance.export_file_types
  121. assert runtime_type_resources.get("icon") == resources_instance.icon_endpoint
  122. async def test_double_refresh(jp_fetch):
  123. # Ensure that attempts to refresh the component cache while another is in progress result in 409
  124. await cli_catalog_instance(jp_fetch)
  125. refresh = {"action": "refresh"}
  126. body = json.dumps(refresh)
  127. response = await jp_fetch("elyra", "pipeline", "components", "cache", body=body, method="PUT")
  128. assert response.code == 204
  129. with pytest.raises(HTTPClientError) as e:
  130. await jp_fetch("elyra", "pipeline", "components", "cache", body=body, method="PUT")
  131. assert expected_http_error(e, 409)
  132. # Give the first refresh attempt a chance to complete and try again to ensure it has
  133. await asyncio.sleep(2)
  134. response = await jp_fetch("elyra", "pipeline", "components", "cache", body=body, method="PUT")
  135. assert response.code == 204
  136. async def test_malformed_refresh(jp_fetch):
  137. # Ensure that providing the endpoints with a bad body generate 400 errors.
  138. refresh = {"no-action": "refresh"}
  139. body = json.dumps(refresh)
  140. with pytest.raises(HTTPClientError) as e:
  141. await jp_fetch("elyra", "pipeline", "components", "cache", body=body, method="PUT")
  142. assert expected_http_error(e, 400)
  143. refresh = {"action": "no-refresh"}
  144. body = json.dumps(refresh)
  145. with pytest.raises(HTTPClientError) as e:
  146. await jp_fetch("elyra", "pipeline", "components", "cache", body=body, method="PUT")
  147. assert expected_http_error(e, 400)
  148. async def test_get_pipeline_properties_definition(jp_fetch):
  149. runtime_list = ["kfp", "airflow", "local"]
  150. for runtime in runtime_list:
  151. response = await jp_fetch("elyra", "pipeline", runtime, "properties")
  152. assert response.code == 200
  153. payload = json.loads(response.body.decode())
  154. # Spot check
  155. assert payload["parameters"] == [
  156. {"id": "name"},
  157. {"id": "runtime"},
  158. {"id": "description"},
  159. {"id": "cos_object_prefix"},
  160. {"id": "elyra_runtime_image"},
  161. {"id": "elyra_env_vars"},
  162. {"id": "elyra_kubernetes_secrets"},
  163. {"id": "elyra_mounted_volumes"},
  164. ]