From 3b0ec220c7df6d5912a9998af5e15b3f5211e9cd Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Sun, 4 May 2025 18:20:37 +0000 Subject: [PATCH 1/2] refactor: update CoderAPI methods for workspace management - Renamed and modified the method to retrieve all templates to `_get_all_templates`, ensuring clarity in its purpose. - Added a new method `get_workspace_metadata` to fetch metadata for a specific workspace. - Implemented `is_workspace_dormant` and `set_workspace_dormancy` methods to manage workspace states effectively. - Refactored the `create_workspace` method to streamline workspace creation logic. - Updated the workspace router to print the response when starting a workspace, enhancing debugging capabilities. --- src/backend/coder.py | 155 +++++++++++++++--------- src/backend/routers/workspace_router.py | 1 + 2 files changed, 99 insertions(+), 57 deletions(-) diff --git a/src/backend/coder.py b/src/backend/coder.py index 83b31ca..691b4ed 100644 --- a/src/backend/coder.py +++ b/src/backend/coder.py @@ -1,4 +1,3 @@ -import os import requests from config import CODER_API_KEY, CODER_URL, CODER_TEMPLATE_ID, CODER_DEFAULT_ORGANIZATION, CODER_WORKSPACE_NAME @@ -24,71 +23,24 @@ def __init__(self): 'Coder-Session-Token': self.api_key } - def get_users(self): + def _get_all_templates(self): """ - Get all users from the Coder API + Get all templates from the Coder API """ - endpoint = f"{self.coder_url}/api/v2/users" + endpoint = f"{self.coder_url}/api/v2/templates" response = requests.get(endpoint, headers=self.headers) - response.raise_for_status() # Raise exception for 4XX/5XX responses - return response.json()['users'] - - - def create_workspace(self, user_id, parameter_values=None): - """ - Create a new workspace for a user using a template - - Args: - user_id (str, optional): User ID to create the workspace for. Defaults to USER_ID from .env. - name (str, optional): Name for the new workspace. Defaults to a generated name. - template_id (str, optional): Template ID to use. Defaults to TEMPLATE_ID from .env. - parameter_values (list, optional): List of template parameter values. Example: - [{"name": "param_name", "value": "param_value"}] - - Returns: - dict: Created workspace data - """ - - template_id = self.template_id - - if not template_id: - raise ValueError("template_id must be provided or TEMPLATE_ID must be set in environment variables") - - name = CODER_WORKSPACE_NAME - - # Prepare the request data - data = { - "name": name, - "template_id": template_id - } - - # Add rich_parameter_values if provided - if parameter_values: - data["rich_parameter_values"] = parameter_values - - # Set headers for JSON content - headers = self.headers.copy() - headers['Content-Type'] = 'application/json' - - # Create the workspace - print("Creating workspace for user", user_id) - endpoint = f"{self.coder_url}/api/v2/users/{user_id}/workspaces" - response = requests.post(endpoint, headers=headers, json=data) - response.raise_for_status() return response.json() + - - - - def _get_all_templates(self): + def get_users(self): """ - Get all templates from the Coder API + Get all users from the Coder API """ - endpoint = f"{self.coder_url}/api/v2/templates" + endpoint = f"{self.coder_url}/api/v2/users" response = requests.get(endpoint, headers=self.headers) - response.raise_for_status() - return response.json() + response.raise_for_status() # Raise exception for 4XX/5XX responses + return response.json()['users'] def get_user_by_email(self, email): """ @@ -186,6 +138,14 @@ def ensure_user_exists(self, user_info): new_user = self.create_user(username, email, name) return new_user, True + + def get_workspace_metadata(self, workspace_id): + """ + Get the metadata of a workspace + """ + endpoint = f"{self.coder_url}/api/v2/workspaces/{workspace_id}" + response = requests.get(endpoint, headers=self.headers) + return response.json() def get_workspace_status_for_user(self, username): """ @@ -227,6 +187,14 @@ def start_workspace(self, workspace_id): Returns: dict: Response from the API """ + + # First check if the workspace is dormant + if self.is_workspace_dormant(workspace_id): + print("Workspace is dormant, setting to not dormant") + self.set_workspace_dormancy(workspace_id, False) + else: + print("Workspace is not dormant") + # First get the workspace to get its template version workspace_endpoint = f"{self.coder_url}/api/v2/workspaces/{workspace_id}" workspace_response = requests.get(workspace_endpoint, headers=self.headers) @@ -279,3 +247,76 @@ def stop_workspace(self, workspace_id): response = requests.post(endpoint, headers=headers, json=data) response.raise_for_status() return response.json() + + def create_workspace(self, user_id, parameter_values=None): + """ + Create a new workspace for a user using a template + + Args: + user_id (str, optional): User ID to create the workspace for. Defaults to USER_ID from .env. + name (str, optional): Name for the new workspace. Defaults to a generated name. + template_id (str, optional): Template ID to use. Defaults to TEMPLATE_ID from .env. + parameter_values (list, optional): List of template parameter values. Example: + [{"name": "param_name", "value": "param_value"}] + + Returns: + dict: Created workspace data + """ + + template_id = self.template_id + + if not template_id: + raise ValueError("template_id must be provided or TEMPLATE_ID must be set in environment variables") + + name = CODER_WORKSPACE_NAME + + # Prepare the request data + data = { + "name": name, + "template_id": template_id + } + + # Add rich_parameter_values if provided + if parameter_values: + data["rich_parameter_values"] = parameter_values + + # Set headers for JSON content + headers = self.headers.copy() + headers['Content-Type'] = 'application/json' + + # Create the workspace + print("Creating workspace for user", user_id) + endpoint = f"{self.coder_url}/api/v2/users/{user_id}/workspaces" + response = requests.post(endpoint, headers=headers, json=data) + + response.raise_for_status() + return response.json() + + def is_workspace_dormant(self, workspace_id) -> bool: + """ + Check if a workspace is dormant + """ + state = self.get_workspace_metadata(workspace_id) + if state["dormant_at"]: + return True + return False + + def set_workspace_dormancy(self, workspace_id, dormant: bool): + """ + Set a workspace to be dormant or not + """ + endpoint = f"{self.coder_url}/api/v2/workspaces/{workspace_id}/dormant" + data = {"dormant": dormant} + headers = self.headers.copy() + headers['Content-Type'] = 'application/json' + response = requests.put(endpoint, headers=headers, json=data) + response.raise_for_status() + return response.json() + + +if __name__ == "__main__": + coder = CoderAPI() + workspace_id = coder.get_workspace_status_for_user("alex")["id"] + coder.set_workspace_dormancy(workspace_id, True) + state = coder.get_workspace_status_for_user("alex") + print(state) diff --git a/src/backend/routers/workspace_router.py b/src/backend/routers/workspace_router.py index c59d5e1..f024aea 100644 --- a/src/backend/routers/workspace_router.py +++ b/src/backend/routers/workspace_router.py @@ -69,6 +69,7 @@ async def start_workspace( try: response = coder_api.start_workspace(workspace.id) + print(response) return JSONResponse(content=response) except Exception as e: print(f"Error starting workspace: {str(e)}") From 5dbd9e0d96b43f3f4c28762b21231e0ef997a363 Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Sun, 4 May 2025 18:25:30 +0000 Subject: [PATCH 2/2] fix: improve workspace dormancy messages and clean up router logging - Updated the print statement in CoderAPI to clarify that the workspace was previously dormant before changing its state. - Removed the print statement in the workspace router when starting a workspace to reduce console clutter. --- src/backend/coder.py | 5 ++--- src/backend/routers/workspace_router.py | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/backend/coder.py b/src/backend/coder.py index 691b4ed..9b8d33d 100644 --- a/src/backend/coder.py +++ b/src/backend/coder.py @@ -190,10 +190,8 @@ def start_workspace(self, workspace_id): # First check if the workspace is dormant if self.is_workspace_dormant(workspace_id): - print("Workspace is dormant, setting to not dormant") + print("Workspace was dormant, setting to not dormant") self.set_workspace_dormancy(workspace_id, False) - else: - print("Workspace is not dormant") # First get the workspace to get its template version workspace_endpoint = f"{self.coder_url}/api/v2/workspaces/{workspace_id}" @@ -306,6 +304,7 @@ def set_workspace_dormancy(self, workspace_id, dormant: bool): Set a workspace to be dormant or not """ endpoint = f"{self.coder_url}/api/v2/workspaces/{workspace_id}/dormant" + data = {"dormant": dormant} headers = self.headers.copy() headers['Content-Type'] = 'application/json' diff --git a/src/backend/routers/workspace_router.py b/src/backend/routers/workspace_router.py index f024aea..c59d5e1 100644 --- a/src/backend/routers/workspace_router.py +++ b/src/backend/routers/workspace_router.py @@ -69,7 +69,6 @@ async def start_workspace( try: response = coder_api.start_workspace(workspace.id) - print(response) return JSONResponse(content=response) except Exception as e: print(f"Error starting workspace: {str(e)}")