Skip to content

Instantly share code, notes, and snippets.

@alloydwhitlock
Created August 19, 2024 20:11
Show Gist options
  • Save alloydwhitlock/ebf8a4eae1e39e5c5e66ae21dd7bb458 to your computer and use it in GitHub Desktop.
Save alloydwhitlock/ebf8a4eae1e39e5c5e66ae21dd7bb458 to your computer and use it in GitHub Desktop.
Retrieve information on long-lived Coder workspaces which are turned off, but still present.
#!/usr/bin/env python3
import requests
from datetime import datetime, timedelta, timezone
# Replace these with your actual Coder API URL and API token
# To obtain these values, log in to your Coder account and navigate to your profile settings.
# The API URL can typically be found in the documentation or your organization's Coder instance.
# The API token (Coder-Session-Token) can be generated from your profile settings.
CODER_API_URL = "https://coder.yourdomain.com/api/v2"
API_TOKEN = "XXXXXXXX-XXXXXXXXXXXXX"
def get_workspaces():
"""
Retrieves the list of workspaces from the Coder API.
Returns:
dict: The JSON response from the API containing the list of workspaces.
Raises:
HTTPError: If the request to the API fails.
"""
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Coder-Session-Token": API_TOKEN,
}
response = requests.get(f"{CODER_API_URL}/workspaces", headers=headers)
# Check if the request was successful
if response.status_code != 200:
print(f"Failed to retrieve workspaces. Status code: {response.status_code}")
print(f"Response text: {response.text}")
response.raise_for_status()
# Attempt to parse the JSON response
try:
return response.json()
except requests.exceptions.JSONDecodeError:
print("Failed to parse JSON response.")
print(f"Response text: {response.text}")
raise
def filter_inactive_workspaces(workspaces, days=7):
"""
Filters the workspaces that have been inactive for a specified number of days.
Args:
workspaces (dict): The JSON response containing the list of workspaces.
days (int): The number of days to consider a workspace inactive. Default is 7 days.
Returns:
list: A list of inactive workspaces.
"""
inactive_workspaces = []
threshold_date = datetime.now(timezone.utc) - timedelta(days=days)
for workspace in workspaces.get("workspaces", []):
# Check last_used_at and status
last_used_str = workspace.get("last_used_at")
status = workspace.get("status", "")
if last_used_str:
try:
last_used_date = datetime.strptime(
last_used_str, "%Y-%m-%dT%H:%M:%S.%fZ"
).replace(tzinfo=timezone.utc)
# Check if last used date is older than the threshold and if the instance is stopped
if last_used_date < threshold_date:
inactive_workspaces.append(workspace)
except ValueError as e:
print(f"Error parsing date for workspace {workspace.get('name')}: {e}")
continue
elif status == "stopped":
# If no last_used_at but the instance is stopped, consider it inactive
inactive_workspaces.append(workspace)
return inactive_workspaces
def main():
"""
Main function to retrieve and filter inactive workspaces, then print the results.
"""
workspaces_data = get_workspaces()
if isinstance(workspaces_data, dict):
inactive_workspaces = filter_inactive_workspaces(workspaces_data)
if inactive_workspaces:
print("Workspaces not used in over a week:")
for workspace in inactive_workspaces:
print(
f"- {workspace['owner_name']} (Workspace Name: {workspace['name']} (Workspace ID: {workspace['id']}) - Last used: {workspace.get('last_used_at', 'N/A')}"
)
else:
print("No inactive workspaces found.")
else:
print("Unexpected data structure received from the API.")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment