Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added handler to list private endpoint #981

Merged
merged 3 commits into from
Oct 28, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ads/aqua/constants.py
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
LIFECYCLE_DETAILS_MISSING_JOBRUN = "The associated JobRun resource has been deleted."
READY_TO_DEPLOY_STATUS = "ACTIVE"
READY_TO_FINE_TUNE_STATUS = "TRUE"
PRIVATE_ENDPOINT_TYPE = "MODEL_DEPLOYMENT"
AQUA_GA_LIST = ["id19sfcrra6z"]
AQUA_MODEL_TYPE_SERVICE = "service"
AQUA_MODEL_TYPE_CUSTOM = "custom"
14 changes: 14 additions & 0 deletions ads/aqua/extension/ui_handler.py
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@

from ads.aqua.common.decorator import handle_exceptions
from ads.aqua.common.enums import Tags
from ads.aqua.constants import PRIVATE_ENDPOINT_TYPE
from ads.aqua.extension.errors import Errors
from ads.aqua.extension.base_handler import AquaAPIhandler
from ads.aqua.extension.utils import validate_function_parameters
@@ -72,6 +73,8 @@ def get(self, id=""):
return self.list_vcn()
elif paths.startswith("aqua/subnets"):
return self.list_subnets()
elif paths.startswith("aqua/privateendpoints"):
return self.list_private_endpoints()
elif paths.startswith("aqua/shapes/limit"):
return self.get_shape_availability()
elif paths.startswith("aqua/bucket/versioning"):
@@ -175,6 +178,16 @@ def list_subnets(self, **kwargs):
)
)

def list_private_endpoints(self, **kwargs):
"""Lists the private endpoints in the specified compartment."""
compartment_id = self.get_argument("compartment_id", default=COMPARTMENT_OCID)
resource_type = self.get_argument("resource_type", default=PRIVATE_ENDPOINT_TYPE)
return self.finish(
AquaUIApp().list_private_endpoints(
compartment_id=compartment_id, resource_type=resource_type, **kwargs
)
)

def get_shape_availability(self, **kwargs):
"""For a given compartmentId, resource limit name, and scope, returns the number of available resources associated
with the given limit."""
@@ -244,6 +257,7 @@ def post(self, *args, **kwargs):
("job/shapes/?([^/]*)", AquaUIHandler),
("vcn/?([^/]*)", AquaUIHandler),
("subnets/?([^/]*)", AquaUIHandler),
("privateendpoints/?([^/]*)", AquaUIHandler),
("shapes/limit/?([^/]*)", AquaUIHandler),
("bucket/versioning/?([^/]*)", AquaUIHandler),
("containers/?([^/]*)", AquaUIHandler),
29 changes: 29 additions & 0 deletions ads/aqua/ui.py
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@
from ads.aqua.common.enums import Tags
from ads.aqua.common.errors import AquaResourceAccessError, AquaValueError
from ads.aqua.common.utils import get_container_config, load_config, sanitize_response
from ads.aqua.constants import PRIVATE_ENDPOINT_TYPE
from ads.common import oci_client as oc
from ads.common.auth import default_signer
from ads.common.object_storage_details import ObjectStorageDetails
@@ -549,6 +550,34 @@ def list_subnets(self, **kwargs) -> list:

return sanitize_response(oci_client=vcn_client, response=res)

@telemetry(entry_point="plugin=ui&action=list_private_endpoints", name="aqua")
def list_private_endpoints(self, **kwargs) -> list:
"""Lists the private endpoints in the specified compartment.
Data seicne private endpoints have two types: `NOTEBOOK_SESSION` and `MODEL_DEPLOYMENT`.
This api will by default list `MODEL_DEPLOYMENT` type as needed by AQUA model deployment.

Parameters
----------
**kwargs
Addtional arguments, such as `compartment_id`,
for `list_data_science_private_endpoints <https://docs.oracle.com/en-us/iaas/tools/python/latest/api/data_science/client/oci.data_science.DataScienceClient.html#oci.data_science.DataScienceClient.list_data_science_private_endpoints>`_

Returns
-------
json representation of `oci.data_science.models.DataSciencePrivateEndpointSummary`.
"""
compartment_id = kwargs.pop("compartment_id", COMPARTMENT_OCID)
resource_type = kwargs.pop("resource_type", PRIVATE_ENDPOINT_TYPE)
logger.info(
f"Loading private endpoints from compartment: {compartment_id}"
)

res = self.ds_client.list_data_science_private_endpoints(
compartment_id=compartment_id, data_science_resource_type=resource_type
).data

return sanitize_response(oci_client=self.ds_client, response=res)

@telemetry(entry_point="plugin=ui&action=get_shape_availability", name="aqua")
def get_shape_availability(self, **kwargs):
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[
{
"compartment_id": "ocid1.compartment.oc1..<OCID>",
"defined_tags": {},
"display_name": "datascienceprivateendpoint20241024145113",
"freeform_tags": {},
"system_tags": {},
"id": "ocid1.datascienceprivateendpointint.oc1.iad.<OCID>",
"lifecycle_state": "ACTIVE",
"data_science_resource_type": "MODEL_DEPLOYMENT",
"created_by": "ocid1.datasciencenotebooksessionint.oc1.iad.<OCID>",
"subnet_id": "ocid1.subnet.oc1.iad.<OCID>",
"fqdn": "<OCID>.md2-pe.modeldeployment-int.us-ashburn-1.oci.oc-test.com",
"time_created": "2024-10-24T14:51:13.883000Z",
"time_updated": "2024-10-24T14:51:13.883000Z"
},
{
"compartment_id": "ocid1.compartment.oc1..<OCID>",
"defined_tags": {},
"display_name": "datascienceprivateendpoint20241023220645",
"freeform_tags": {},
"system_tags": {},
"id": "ocid1.datascienceprivateendpointint.oc1.iad.<OCID>",
"lifecycle_state": "ACTIVE",
"data_science_resource_type": "MODEL_DEPLOYMENT",
"created_by": "ocid1.datasciencenotebooksessionint.oc1.iad.<OCID>",
"subnet_id": "ocid1.subnet.oc1.iad.<OCID>",
"fqdn": "<OCID>.md2-pe.modeldeployment-int.us-ashburn-1.oci.oc-test.com",
"time_created": "2024-10-23T22:06:45.224000Z",
"time_updated": "2024-10-23T22:06:45.224000Z"
}
]
42 changes: 42 additions & 0 deletions tests/unitary/with_extras/aqua/test_ui.py
Original file line number Diff line number Diff line change
@@ -398,6 +398,48 @@ def test_list_subnets(self, mock_list_subnets):
)
assert len(results) == len(subnets)

def test_list_private_endpoints(self):
"""Test to list the private endpoints in the specified compartment."""
private_endpoints_list_json = os.path.join(
self.curr_dir, f"test_data/ui/private_endpoints_list.json"
)
with open(private_endpoints_list_json, "r") as _file:
private_endpoints = json.load(_file)

self.app.ds_client.list_data_science_private_endpoints = MagicMock(
return_value=oci.response.Response(
status=200,
request=MagicMock(),
headers=MagicMock(),
data=[
oci.data_science.models.DataSciencePrivateEndpointSummary(**pe)
for pe in private_endpoints
],
)
)

results = self.app.list_private_endpoints()
expected_attributes = {
"compartmentId",
"definedTags",
"displayName",
"freeformTags",
"systemTags",
"id",
"lifecycleState",
"dataScienceResourceType",
"createdBy",
"subnetId",
"fqdn",
"timeCreated",
"timeUpdated"
}
for result in results:
self.assertTrue(
expected_attributes.issuperset(set(result)), "Attributes mismatch"
)
assert len(results) == len(private_endpoints)

@patch.object(oci.limits.limits_client.LimitsClient, "get_resource_availability")
def test_get_shape_availability(self, mock_get_resource_availability):
"""Test whether the function returns the number of available resources associated with the given shape."""
11 changes: 11 additions & 0 deletions tests/unitary/with_extras/aqua/test_ui_handler.py
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
class TestDataset:
USER_COMPARTMENT_ID = "ocid1.compartment.oc1..<USER_COMPARTMENT_OCID>"
USER_PROJECT_ID = "ocid1.datascienceproject.oc1.iad.<USER_PROJECT_OCID>"
PRIVATE_ENDPOINT_RESOURCE_TYPE = "MODEL_DEPLOYMENT"
DEPLOYMENT_SHAPE_NAME = "VM.GPU.A10.1"


@@ -149,6 +150,16 @@ def test_list_subnets(self, mock_list_subnets):
compartment_id=TestDataset.USER_COMPARTMENT_ID, vcn_id="mock-vcn-id"
)

@patch("ads.aqua.ui.AquaUIApp.list_private_endpoints")
def test_list_private_endpoints(self, mock_list_private_endpoints):
"""Test the get method to fetch list of private endpoints."""
self.ui_handler.request.path = "aqua/privateendpoints"
self.ui_handler.get()
mock_list_private_endpoints.assert_called_with(
compartment_id=TestDataset.USER_COMPARTMENT_ID,
resource_type=TestDataset.PRIVATE_ENDPOINT_RESOURCE_TYPE
)

@patch("ads.aqua.ui.AquaUIApp.get_shape_availability")
def test_get_shape_availability(self, mock_get_shape_availability):
"""Test get shape availability."""
Loading