Skip to content

Commit

Permalink
Merge pull request #310 from helxplatform/readiness_status
Browse files Browse the repository at this point in the history
Augment App instance info with readiness
  • Loading branch information
waTeim authored Oct 10, 2023
2 parents 17e79e0 + afb359d commit 63755ca
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 26 deletions.
1 change: 1 addition & 0 deletions appstore/api/v1/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class Instance:
ephemeralStorage: str
host: InitVar[str]
username: InitVar[str]
is_ready: bool
url: str = field(init=False)
status: str = field(init=False)
protocol: InitVar[str] = os.environ.get("ACCOUNT_DEFAULT_HTTP_PROTOCOL", "http")
Expand Down
216 changes: 191 additions & 25 deletions appstore/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,34 @@ def get_social_tokens(username):

class AppViewSet(viewsets.GenericViewSet):
"""
Tycho App information.
AppViewSet - ViewSet for managing Tycho apps.
This ViewSet provides endpoints to list all available apps and retrieve details
about a specific app based on its app_id.
Endpoints:
- List All Apps:
- URL: /apps/
- HTTP Method: GET
- Method: list
- Description: Lists all available apps, parses resource specifications,
and returns them in a structured format. GPU reservations
and limits are specially handled. Any errors during the
parsing of an app's data are logged and the app is skipped.
- Retrieve App Details:
- URL: /apps/{app_id}/
- HTTP Method: GET
- Method: retrieve
- Description: Provides detailed information about a specific app based on its
app_id. Similar to the list method, it parses resource specifications
and returns them in a structured format.
Note:
- The app_id is used as a lookup field.
- The ViewSet interacts with an external system named 'tycho' to fetch app definitions
and other relevant data. There are also utility functions like 'parse_spec_resources'
and 'search_for_gpu_reservation' that are presumably defined elsewhere in the codebase.
"""

lookup_field = "app_id"
Expand Down Expand Up @@ -277,7 +304,41 @@ def retrieve(self, request, app_id=None):

class InstanceViewSet(viewsets.GenericViewSet):
"""
Active user instances.
InstanceViewSet - ViewSet for managing instances.
Endpoints:
- List Endpoint:
- URL: /instances/
- HTTP Method: GET
- Method: list
- Create Endpoint:
- URL: /instances/
- HTTP Method: POST
- Method: create
- Retrieve (Detail) Endpoint:
- URL: /instances/{sid}/
- HTTP Method: GET
- Method: retrieve
- Note: {sid} is a placeholder for the instance's ID.
- Destroy (Delete) Endpoint:
- URL: /instances/{sid}/
- HTTP Method: DELETE
- Method: destroy
- Partial Update Endpoint:
- URL: /instances/{sid}/
- HTTP Method: PATCH
- Method: partial_update
- Check Instance Readiness:
- URL: /instances/{sid}/is_ready/
- HTTP Method: GET
- Method: is_ready
- Description: Checks if a specific user instance, identified by its 'sid', is ready.
"""

lookup_field = "sid"
Expand Down Expand Up @@ -307,6 +368,31 @@ def get_queryset(self):
status = tycho.status({"username": self.request.user.username})
return status.services

def get_instance(self, sid, username, host):
active = self.get_queryset()

for instance in active:
if instance.identifier == sid:
app = tycho.apps.get(instance.app_id.rpartition("-")[0], {})
app_name = instance.app_id.replace(f"-{instance.identifier}", "")
return Instance(
app.get("name"),
app.get("docs"),
app_name,
instance.identifier,
instance.app_id,
instance.creation_time,
instance.total_util["cpu"],
instance.total_util["gpu"],
instance.total_util["memory"],
instance.total_util["ephemeralStorage"],
app.get("app_id"),
host,
username,
instance.is_ready
)
return None

def list(self, request):
"""
Provide all active instances.
Expand Down Expand Up @@ -342,6 +428,7 @@ def list(self, request):
instance.total_util["ephemeralStorage"],
host,
username,
instance.is_ready
)
instances.append(asdict(inst))
else:
Expand Down Expand Up @@ -414,35 +501,36 @@ def retrieve(self, request, sid=None):
"""
Provide active instance details.
"""
active = self.get_queryset()
principal = self.get_principal(request.user)
username = principal.username
host = get_host(request)
instance = None

for instance in active:
if instance.identifier == sid:
app = tycho.apps.get(instance.app_id.rpartition("-")[0], {})
inst = Instance(
app.get("name"),
app.get("docs"),
instance.identifier,
instance.app_id,
instance.creation_time,
instance.total_util["cpu"],
instance.total_util["gpu"],
instance.total_util["memory"],
instance.total_util["ephemeralStorage"],
app.get("app_id"),
host,
username,
)

serializer = self.get_serializer(data=asdict(inst))
if sid != None:
instance = self.get_instance(sid,username,host)
if instance != None:
serializer = self.get_serializer(data=asdict(instance))
serializer.is_valid(raise_exception=True)
return Response(serializer.validated_data)

logger.error(f"\n{sid} not found\n")
return Response(status=drf_status.HTTP_404_NOT_FOUND)

@action(detail=True, methods=['get'])
def is_ready(self, request, sid=None):
principal = self.get_principal(request.user)
username = principal.username
host = get_host(request)
instance = None

if sid != None:
instance = self.get_instance(sid,username,host)
if instance != None:
return Response({'is_ready': instance.is_ready})

logger.error(f"\n{sid} not found\n")
return Response(status=drf_status.HTTP_404_NOT_FOUND)


def destroy(self, request, sid=None):
"""
Expand Down Expand Up @@ -483,7 +571,32 @@ def partial_update(self, request, sid=None):

class UsersViewSet(viewsets.GenericViewSet):
"""
User information.
UsersViewSet - ViewSet for managing user information.
This ViewSet provides endpoints to retrieve details of the currently logged-in user
and to handle user logout.
Endpoints:
- List User Details:
- URL: /users/
- HTTP Method: GET
- Method: list
- Description: Provides details of the currently logged-in user, including their
username and access token. This endpoint is designed to support
scenarios where a reverse proxy (like nginx) performs authentication
before proxying a request.
- Logout:
- URL: /users/logout/
- HTTP Method: POST
- Method: logout
- Description: Logs out the current user and returns a success message.
Note:
- The ViewSet uses a private method '_get_access_token' to retrieve the user's
access token from the session.
- 'EmptySerializer' is used for the 'logout' action, likely to simply validate the
request without any specific data.
"""

def get_serializer_class(self):
Expand Down Expand Up @@ -520,7 +633,38 @@ def logout(self, request):

class LoginProviderViewSet(viewsets.GenericViewSet):
"""
Login provider information.
LoginProviderViewSet - ViewSet for retrieving login provider information.
This ViewSet provides information about the available social login providers
from `allauth`, Django's default login, and any product-specific providers like SSO.
It's designed to list out these available authentication providers and their
respective login URLs.
Attributes:
- permission_classes: Allow any user (authenticated or not) to access this endpoint.
- serializer_class: Uses `LoginProviderSerializer` to serialize the data.
Methods:
- get_queryset: Returns the global `settings` object.
- _get_social_providers: A private method to retrieve social login providers
from `allauth`.
- _get_django_provider: A private method to check if Django's default login
is enabled and to get its login URL.
- _get_product_providers: A private method to check for any product-specific
SSO providers and retrieve their details.
- _get_login_providers: An aggregation method that combines the results
from the above three methods to get a comprehensive
list of login providers.
- list: The main endpoint which uses `_get_login_providers` to fetch all
available login providers and returns them after serialization.
Endpoints:
- List Login Providers:
- URL: /providers/
- HTTP Method: GET
- Method: list
- Description: Lists all available authentication/login providers
and their respective login URLs.
"""

permission_classes = [AllowAny]
Expand Down Expand Up @@ -603,7 +747,29 @@ def list(self, request):

class AppContextViewSet(viewsets.GenericViewSet):
"""
Brand/Product configuration information.
AppContextViewSet - ViewSet for retrieving brand/product configuration information.
This ViewSet provides information about the brand or product's configuration settings.
It fetches the settings from the global `settings` object and serializes them using
the `AppContextSerializer`.
Attributes:
- permission_classes: Allow any user (authenticated or not) to access this endpoint.
- serializer_class: Uses `AppContextSerializer` to serialize the data.
Methods:
- get_queryset: Returns the global `settings` object.
- list: Fetches specific configuration settings from the `settings` object,
combines them with specific environment variables from `EXPORTABLE_ENV`,
and returns the aggregated data.
Endpoints:
- List Brand/Product Configuration:
- URL: /context/
- HTTP Method: GET
- Method: list
- Description: Lists specific configuration settings related to the brand or product
and certain environment variables specified in `EXPORTABLE_ENV`.
"""

permission_classes = [AllowAny]
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ python3-openid==3.1.0
requests==2.31.0
requests-oauthlib
selenium==3.141.0
tycho-api==1.15.0
tycho-api>=1.16.1
webdriver-manager==3.2.1
sqlparse==0.4.2
asgiref==3.4.1
Expand Down

0 comments on commit 63755ca

Please sign in to comment.