diff --git a/apps/api/main.py b/apps/api/main.py index b19b4cb..7776e7d 100644 --- a/apps/api/main.py +++ b/apps/api/main.py @@ -245,9 +245,14 @@ async def create_inference_job(job_req: InferenceJobRequest, current_user: dict cached["cached"] = True return cached + # Transform payload to match worker expectations + payload = job_req.model_dump() + payload['radius_m'] = int(payload.pop('radius_km') * 1000) + payload['model'] = payload.pop('model_name') + job = task_queue.enqueue( - 'worker.run_inference', - job_req.model_dump(), + 'worker.run_job', + payload, job_timeout='25m', result_ttl=86400, failure_ttl=86400 diff --git a/apps/musicseerr/Dockerfile b/apps/musicseerr/Dockerfile index 119f689..c118d8f 100644 --- a/apps/musicseerr/Dockerfile +++ b/apps/musicseerr/Dockerfile @@ -8,6 +8,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ && rm -rf /var/lib/apt/lists/* +# Install Deno for yt-dlp JavaScript decryption support +COPY --from=denoland/deno:bin /deno /usr/local/bin/deno + # Set working directory WORKDIR /app diff --git a/apps/musicseerr/tasks.py b/apps/musicseerr/tasks.py index 3771715..0cac9a8 100644 --- a/apps/musicseerr/tasks.py +++ b/apps/musicseerr/tasks.py @@ -4,6 +4,7 @@ import subprocess import logging import re import requests +import json from bs4 import BeautifulSoup from urllib.parse import urljoin from mutagen.mp3 import EasyMP3 @@ -229,38 +230,79 @@ def send_telegram_notification(chat_id: int, text: str): def extract_spotify_track_title(url: str, proxy_url: str) -> str: """ - Fetches the Spotify track page and extracts the track name and artists from the title tag. + Scrapes a Spotify track embed page and returns the track search string ("Artist - Title"). """ + match = re.search(r'track/([a-zA-Z0-9]+)', url) + if not match: + logger.warning(f"Could not extract track ID from URL: {url}") + return "" + track_id = match.group(1) + embed_url = f"https://open.spotify.com/embed/track/{track_id}" + proxies = {"http": proxy_url, "https": proxy_url} if proxy_url else None headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" } - logger.info(f"Extracting track title from Spotify page: {url}") + + logger.info(f"Scraping Spotify track via embed URL: {embed_url}") try: - response = requests.get(url, headers=headers, proxies=proxies, timeout=15) + response = requests.get(embed_url, headers=headers, proxies=proxies, timeout=15) if response.status_code == 200: - soup = BeautifulSoup(response.text, "html.parser") - title_tag = soup.find("title") - if title_tag: - title_text = title_tag.text - # Format is typically "Song Name - song and lyrics by Artist1, Artist2 | Spotify" - # Or "Song Name - song by Artist | Spotify" - if " - song " in title_text: - parts = title_text.split(" - song ", 1) - song_name = parts[0].strip() - artist_part = parts[1].split(" by ", 1) - if len(artist_part) > 1: - artists = artist_part[1].split(" | Spotify", 1)[0].strip() - return f"{artists} - {song_name}" - return song_name - elif " | Spotify" in title_text: - return title_text.split(" | Spotify")[0].strip() - return title_text.strip() + next_data_match = re.search(r'', response.text) + if next_data_match: + data = json.loads(next_data_match.group(1)) + entity = data['props']['pageProps']['state']['data']['entity'] + name = entity.get('name', '').strip() + artists = [a.get('name', '') for a in entity.get('artists', [])] + artists = [a for a in artists if a] + if name and artists: + return f"{', '.join(artists)} - {name}" + elif name: + return name except Exception as e: logger.warning(f"Failed to extract Spotify track title: {e}") return "" +def scrape_spotify_playlist(url: str, proxy_url: str) -> list: + """ + Scrapes a Spotify playlist embed page and returns a list of track search strings ("Artist - Title"). + """ + match = re.search(r'playlist/([a-zA-Z0-9]+)', url) + if not match: + logger.warning(f"Could not extract playlist ID from URL: {url}") + return [] + playlist_id = match.group(1) + embed_url = f"https://open.spotify.com/embed/playlist/{playlist_id}" + + proxies = {"http": proxy_url, "https": proxy_url} if proxy_url else None + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" + } + + logger.info(f"Scraping Spotify playlist via embed URL: {embed_url}") + try: + response = requests.get(embed_url, headers=headers, proxies=proxies, timeout=15) + if response.status_code == 200: + next_data_match = re.search(r'', response.text) + if next_data_match: + data = json.loads(next_data_match.group(1)) + entity = data['props']['pageProps']['state']['data']['entity'] + tracks = entity.get('trackList', []) + track_queries = [] + for t in tracks: + title = t.get('title', '').strip() + subtitle = t.get('subtitle', '').replace('\xa0', ' ').strip() + if title and subtitle: + track_queries.append(f"{subtitle} - {title}") + elif title: + track_queries.append(title) + return track_queries + except Exception as e: + logger.warning(f"Failed to scrape Spotify playlist: {e}") + return [] + + def symlink_existing_tracks(temp_dir: str): """ Finds all audio files recursively in /remote-music/music, extracts artist and title, @@ -353,143 +395,231 @@ def run_download_task(task_id: str, query: str, proxy_url: str, jobs_dict: dict, resolved_title = extract_spotify_track_title(query, resolved_proxy_url) if resolved_title: logger.info(f"[{task_id}] Pre-resolved Spotify URL to text title: '{resolved_title}'") + query = resolved_title + + is_playlist = "spotify.com/playlist/" in query.lower() + + if is_playlist: + jobs_dict[task_id]["priority_used"] = "playlist-scraper" + logger.info(f"[{task_id}] Spotify playlist detected: '{query}'") + track_queries = scrape_spotify_playlist(query, resolved_proxy_url) + if not track_queries: + raise Exception("Could not scrape any tracks from the Spotify playlist embed page.") - is_tubidy = "tubidy" in query.lower() - is_spotify = "spotify.com" in query.lower() - is_youtube = "youtube.com" in query.lower() or "youtu.be" in query.lower() - - # Priority 1: spotDL (text search or Spotify URL) - if not is_tubidy and not is_youtube: - jobs_dict[task_id]["priority_used"] = "spotDL" - logger.info(f"[{task_id}] Attempting Priority 1: spotDL for '{query}'") + logger.info(f"[{task_id}] Successfully scraped {len(track_queries)} tracks from playlist.") + download_count = 0 - env = os.environ.copy() - if resolved_proxy_url: - env["http_proxy"] = resolved_proxy_url - env["https_proxy"] = resolved_proxy_url + for idx, track_query in enumerate(track_queries): + logger.info(f"[{task_id}] [Track {idx+1}/{len(track_queries)}] Downloading '{track_query}'") + track_success = False - cmd = ["spotdl", "download", query] - if resolved_proxy_url: - cmd += ["--proxy", resolved_proxy_url] - - # Run spotDL in the temp directory - result = subprocess.run(cmd, cwd=temp_dir, env=env, capture_output=True, text=True) - if result.returncode == 0: - logger.info(f"[{task_id}] spotDL download succeeded.") - download_successful = True - else: - logger.warning(f"[{task_id}] spotDL failed: {result.stderr or result.stdout}. Falling back to yt-dlp.") - # Clear state for P2 fallback - is_spotify = False - - # Priority 2: yt-dlp (direct YouTube or fallback for failed spotDL) - if not is_tubidy and not is_spotify: - jobs_dict[task_id]["priority_used"] = "yt-dlp" - logger.info(f"[{task_id}] Attempting Priority 2: yt-dlp for '{query}'") - - env = os.environ.copy() - if resolved_proxy_url: - env["http_proxy"] = resolved_proxy_url - env["https_proxy"] = resolved_proxy_url - - # If not a link or if resolved from Spotify track URL, treat as search query - search_query = query - if resolved_title: - search_query = f"ytsearch1:{resolved_title}" - logger.info(f"[{task_id}] Using resolved Spotify track title for yt-dlp search: '{search_query}'") - elif not query.startswith("http"): - search_query = f"ytsearch1:{query}" - - cmd = [ - "yt-dlp", - "-x", - "--audio-format", "mp3", - "--embed-metadata", - "--yes-playlist", - "--output", "%(title)s.%(ext)s" - ] - if resolved_proxy_url: - cmd += ["--proxy", resolved_proxy_url] - cmd.append(search_query) - - result = subprocess.run(cmd, cwd=temp_dir, env=env, capture_output=True, text=True) - if result.returncode == 0: - logger.info(f"[{task_id}] yt-dlp download succeeded.") - download_successful = True - else: - logger.warning(f"[{task_id}] yt-dlp failed: {result.stderr or result.stdout}. Falling back to Tubidy Search.") - - # Priority 3: Custom Tubidy Link Scraper (if URL is directly passed) - if is_tubidy: - jobs_dict[task_id]["priority_used"] = "Tubidy" - logger.info(f"[{task_id}] Attempting Priority 3: Tubidy Scraper for '{query}'") - - # Scrape MP3 URL - mp3_url = scrape_tubidy_link(query, resolved_proxy_url) - logger.info(f"[{task_id}] Scraped Tubidy MP3 URL: {mp3_url}") - - # Formulate filename - filename = query.split("/")[-1] - if not filename or ".html" in filename or "?" in filename: - filename = "tubidy_download" - filename = filename.replace(".html", "").replace(".php", "").strip() - if not filename.endswith(".mp3"): - filename += ".mp3" - - dest_path = os.path.join(temp_dir, filename) - - # Download file stream - download_file_stream(mp3_url, dest_path, resolved_proxy_url) - logger.info(f"[{task_id}] Tubidy download completed.") - download_successful = True - - # Tag metadata - tag_mp3(dest_path, filename) - logger.info(f"[{task_id}] Tubidy ID3 tagging complete.") - - # Priority 4: Tubidy Search Fallback (if spotDL and yt-dlp failed and no files are present) - if not download_successful and not is_tubidy: - jobs_dict[task_id]["priority_used"] = "Tubidy-Search" - logger.info(f"[{task_id}] Attempting Fallback Priority 4: Tubidy Search Scraper for '{query}'") - - search_query = query - if resolved_title: - search_query = resolved_title - logger.info(f"[{task_id}] Using pre-resolved Spotify track title for Tubidy search: '{search_query}'") - - search_results = search_tubidy(search_query, resolved_proxy_url) - if search_results: - first_result = search_results[0] - logger.info(f"[{task_id}] Found Tubidy match: '{first_result['title']}' (ID: {first_result['id']})") - - try: - mp3_url = get_tubidy_download_link(first_result["id"], resolved_proxy_url) - logger.info(f"[{task_id}] Resolved Tubidy download URL: {mp3_url}") + # 1. spotDL + env = os.environ.copy() + if resolved_proxy_url: + env["http_proxy"] = resolved_proxy_url + env["https_proxy"] = resolved_proxy_url - filename = f"{first_result['title']}.mp3" - # Clean filename - filename = "".join([c for c in filename if c.isalnum() or c in " .-_()"]).strip() - if not filename.endswith(".mp3"): - filename += ".mp3" - if not filename or filename == ".mp3": - filename = "tubidy_fallback.mp3" + cmd = ["spotdl", "download", track_query] + if resolved_proxy_url: + cmd += ["--proxy", resolved_proxy_url] + + logger.info(f"[{task_id}] [Track {idx+1}] Trying spotDL...") + result = subprocess.run(cmd, cwd=temp_dir, env=env, capture_output=True, text=True) + if result.returncode == 0: + logger.info(f"[{task_id}] [Track {idx+1}] spotDL succeeded.") + track_success = True + else: + logger.warning(f"[{task_id}] [Track {idx+1}] spotDL failed: {result.stderr or result.stdout}. Trying yt-dlp...") + + # 2. yt-dlp + if not track_success: + search_query = f"ytsearch1:{track_query}" + cmd_ytdlp = [ + "yt-dlp", + "-x", + "--audio-format", "mp3", + "--embed-metadata", + "--yes-playlist", + "--output", "%(title)s.%(ext)s" + ] + if resolved_proxy_url: + cmd_ytdlp += ["--proxy", resolved_proxy_url] + cmd_ytdlp.append(search_query) + + logger.info(f"[{task_id}] [Track {idx+1}] Trying yt-dlp...") + result_ytdlp = subprocess.run(cmd_ytdlp, cwd=temp_dir, env=env, capture_output=True, text=True) + if result_ytdlp.returncode == 0: + logger.info(f"[{task_id}] [Track {idx+1}] yt-dlp succeeded.") + track_success = True + else: + logger.warning(f"[{task_id}] [Track {idx+1}] yt-dlp failed. Trying Tubidy...") - dest_path = os.path.join(temp_dir, filename) + # 3. Tubidy + if not track_success: + try: + logger.info(f"[{task_id}] [Track {idx+1}] Trying Tubidy...") + search_results = search_tubidy(track_query, resolved_proxy_url) + if search_results: + first_result = search_results[0] + mp3_url = get_tubidy_download_link(first_result["id"], resolved_proxy_url) + filename = f"{first_result['title']}.mp3" + filename = "".join([c for c in filename if c.isalnum() or c in " .-_()"]).strip() + if not filename.endswith(".mp3"): + filename += ".mp3" + dest_path = os.path.join(temp_dir, filename) + download_file_stream(mp3_url, dest_path, resolved_proxy_url) + tag_mp3(dest_path, filename) + track_success = True + logger.info(f"[{task_id}] [Track {idx+1}] Tubidy succeeded.") + except Exception as e: + logger.error(f"[{task_id}] [Track {idx+1}] Tubidy failed: {e}") + + if track_success: + download_count += 1 - # Download file stream - download_file_stream(mp3_url, dest_path, resolved_proxy_url) - logger.info(f"[{task_id}] Tubidy fallback download completed.") - - # Tag metadata - tag_mp3(dest_path, filename) - logger.info(f"[{task_id}] Tubidy fallback ID3 tagging complete.") - download_successful = True - except Exception as e: - logger.error(f"[{task_id}] Tubidy fallback download failed: {e}") - raise Exception(f"All download options (spotDL, yt-dlp, Tubidy) failed. Last error: {e}") + if download_count > 0: + download_successful = True + logger.info(f"[{task_id}] Playlist download finished. Successfully downloaded {download_count} tracks.") else: - logger.error(f"[{task_id}] No Tubidy search results found for query '{search_query}'") - raise Exception("All download options (spotDL, yt-dlp, Tubidy) failed. No Tubidy search results found.") + raise Exception("Failed to download any tracks from the playlist.") + + else: + is_tubidy = "tubidy" in query.lower() + is_spotify = "spotify.com" in query.lower() + is_youtube = "youtube.com" in query.lower() or "youtu.be" in query.lower() + + # Priority 1: spotDL (text search or Spotify URL) + if not is_tubidy and not is_youtube: + jobs_dict[task_id]["priority_used"] = "spotDL" + logger.info(f"[{task_id}] Attempting Priority 1: spotDL for '{query}'") + + env = os.environ.copy() + if resolved_proxy_url: + env["http_proxy"] = resolved_proxy_url + env["https_proxy"] = resolved_proxy_url + + cmd = ["spotdl", "download", query] + if resolved_proxy_url: + cmd += ["--proxy", resolved_proxy_url] + + # Run spotDL in the temp directory + result = subprocess.run(cmd, cwd=temp_dir, env=env, capture_output=True, text=True) + if result.returncode == 0: + logger.info(f"[{task_id}] spotDL download succeeded.") + download_successful = True + else: + logger.warning(f"[{task_id}] spotDL failed: {result.stderr or result.stdout}. Falling back to yt-dlp.") + # Clear state for P2 fallback + is_spotify = False + + # Priority 2: yt-dlp (direct YouTube or fallback for failed spotDL) + if not is_tubidy and not is_spotify: + jobs_dict[task_id]["priority_used"] = "yt-dlp" + logger.info(f"[{task_id}] Attempting Priority 2: yt-dlp for '{query}'") + + env = os.environ.copy() + if resolved_proxy_url: + env["http_proxy"] = resolved_proxy_url + env["https_proxy"] = resolved_proxy_url + + # If not a link or if resolved from Spotify track URL, treat as search query + search_query = query + if resolved_title: + search_query = f"ytsearch1:{resolved_title}" + logger.info(f"[{task_id}] Using resolved Spotify track title for yt-dlp search: '{search_query}'") + elif not query.startswith("http"): + search_query = f"ytsearch1:{query}" + + cmd = [ + "yt-dlp", + "-x", + "--audio-format", "mp3", + "--embed-metadata", + "--yes-playlist", + "--output", "%(title)s.%(ext)s" + ] + if resolved_proxy_url: + cmd += ["--proxy", resolved_proxy_url] + cmd.append(search_query) + + result = subprocess.run(cmd, cwd=temp_dir, env=env, capture_output=True, text=True) + if result.returncode == 0: + logger.info(f"[{task_id}] yt-dlp download succeeded.") + download_successful = True + else: + logger.warning(f"[{task_id}] yt-dlp failed: {result.stderr or result.stdout}. Falling back to Tubidy Search.") + + # Priority 3: Custom Tubidy Link Scraper (if URL is directly passed) + if is_tubidy: + jobs_dict[task_id]["priority_used"] = "Tubidy" + logger.info(f"[{task_id}] Attempting Priority 3: Tubidy Scraper for '{query}'") + + # Scrape MP3 URL + mp3_url = scrape_tubidy_link(query, resolved_proxy_url) + logger.info(f"[{task_id}] Scraped Tubidy MP3 URL: {mp3_url}") + + # Formulate filename + filename = query.split("/")[-1] + if not filename or ".html" in filename or "?" in filename: + filename = "tubidy_download" + filename = filename.replace(".html", "").replace(".php", "").strip() + if not filename.endswith(".mp3"): + filename += ".mp3" + + dest_path = os.path.join(temp_dir, filename) + + # Download file stream + download_file_stream(mp3_url, dest_path, resolved_proxy_url) + logger.info(f"[{task_id}] Tubidy download completed.") + download_successful = True + + # Tag metadata + tag_mp3(dest_path, filename) + logger.info(f"[{task_id}] Tubidy ID3 tagging complete.") + + # Priority 4: Tubidy Search Fallback (if spotDL and yt-dlp failed and no files are present) + if not download_successful and not is_tubidy: + jobs_dict[task_id]["priority_used"] = "Tubidy-Search" + logger.info(f"[{task_id}] Attempting Fallback Priority 4: Tubidy Search Scraper for '{query}'") + + search_query = query + if resolved_title: + search_query = resolved_title + logger.info(f"[{task_id}] Using pre-resolved Spotify track title for Tubidy search: '{search_query}'") + + search_results = search_tubidy(search_query, resolved_proxy_url) + if search_results: + first_result = search_results[0] + logger.info(f"[{task_id}] Found Tubidy match: '{first_result['title']}' (ID: {first_result['id']})") + + try: + mp3_url = get_tubidy_download_link(first_result["id"], resolved_proxy_url) + logger.info(f"[{task_id}] Resolved Tubidy download URL: {mp3_url}") + + filename = f"{first_result['title']}.mp3" + # Clean filename + filename = "".join([c for c in filename if c.isalnum() or c in " .-_()"]).strip() + if not filename.endswith(".mp3"): + filename += ".mp3" + if not filename or filename == ".mp3": + filename = "tubidy_fallback.mp3" + + dest_path = os.path.join(temp_dir, filename) + + # Download file stream + download_file_stream(mp3_url, dest_path, resolved_proxy_url) + logger.info(f"[{task_id}] Tubidy fallback download completed.") + + # Tag metadata + tag_mp3(dest_path, filename) + logger.info(f"[{task_id}] Tubidy fallback ID3 tagging complete.") + download_successful = True + except Exception as e: + logger.error(f"[{task_id}] Tubidy fallback download failed: {e}") + raise Exception(f"All download options (spotDL, yt-dlp, Tubidy) failed. Last error: {e}") + else: + logger.error(f"[{task_id}] No Tubidy search results found for query '{search_query}'") + raise Exception("All download options (spotDL, yt-dlp, Tubidy) failed. No Tubidy search results found.") # Verify files and move to /remote-music/music if not download_successful: diff --git a/k8s/base/supabase-basket.yaml b/k8s/base/supabase-basket.yaml index d5b5b32..37af454 100644 --- a/k8s/base/supabase-basket.yaml +++ b/k8s/base/supabase-basket.yaml @@ -1810,7 +1810,7 @@ spec: imagePullPolicy: IfNotPresent env: - name: API_EXTERNAL_URL - value: "http://supabase.local" + value: "https://basket.techarvest.co.zw" - name: DB_DRIVER value: "postgres" - name: DB_SSL @@ -1824,7 +1824,9 @@ spec: - name: GOTRUE_DISABLE_SIGNUP value: "false" - name: GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED - value: "false" + value: "true" + - name: GOTRUE_EXTERNAL_ANONYMOUS_ENABLED + value: "true" - name: GOTRUE_EXTERNAL_EMAIL_ENABLED value: "true" - name: GOTRUE_EXTERNAL_PHONE_ENABLED @@ -1848,7 +1850,7 @@ spec: - name: GOTRUE_MAILER_URLPATHS_RECOVERY value: "/auth/v1/verify" - name: GOTRUE_SITE_URL - value: "http://supabase.local" + value: "https://basket.techarvest.co.zw" - name: GOTRUE_SMS_AUTOCONFIRM value: "false" - name: GOTRUE_SMTP_ADMIN_EMAIL diff --git a/k8s/family-apps/musicseerr.yaml b/k8s/family-apps/musicseerr.yaml index 0994289..01a61b3 100644 --- a/k8s/family-apps/musicseerr.yaml +++ b/k8s/family-apps/musicseerr.yaml @@ -54,7 +54,7 @@ spec: containers: - name: musicseerr image: frankchine/geocrop-musicseerr:latest - imagePullPolicy: IfNotPresent + imagePullPolicy: Always env: - name: GLUETUN_PROXY_URL value: "http://gluetun-svc:8888" diff --git a/k8s/nextgen/manual-deployments.yaml b/k8s/nextgen/manual-deployments.yaml index 0da2827..43bda51 100644 --- a/k8s/nextgen/manual-deployments.yaml +++ b/k8s/nextgen/manual-deployments.yaml @@ -123,3 +123,44 @@ spec: name: next-gen-dev port: number: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: next-gen-main + namespace: nextgen +spec: + selector: + app: next-gen-main + ports: + - name: http + protocol: TCP + port: 80 + targetPort: http +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: next-gen-main + namespace: nextgen + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/ssl-redirect: "false" + nginx.ingress.kubernetes.io/force-ssl-redirect: "false" +spec: + ingressClassName: nginx + tls: + - hosts: + - next-gen.techarvest.co.zw + secretName: next-gen-main-tls + rules: + - host: next-gen.techarvest.co.zw + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: next-gen-main + port: + number: 80 diff --git a/terraform/main.tf b/terraform/main.tf index 3cb518f..4fc67bd 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -63,6 +63,24 @@ resource "kubernetes_namespace" "portainer" { } } +resource "kubernetes_namespace" "argo" { + metadata { + name = "argo" + } +} + +resource "kubernetes_namespace" "kubernetes_dashboard" { + metadata { + name = "kubernetes-dashboard" + } +} + +resource "kubernetes_namespace" "dressup" { + metadata { + name = "dressup" + } +} + # ========================================== # PORTAINER (kubectl deployed) # Port installed via kubectl manifest at: @@ -149,4 +167,26 @@ resource "kubernetes_namespace" "portainer" { # argo namespace: # - argo-server # - workflow-controller -# ========================================== \ No newline at end of file +# ========================================== + +# ========================================== +# PRIORITY CLASSES +# ========================================== + +resource "kubernetes_priority_class_v1" "dressup_priority" { + metadata { + name = "dressup-priority" + } + value = 800000 + global_default = false + description = "High priority scheduling for dressup app workloads during spikes." +} + +resource "kubernetes_priority_class_v1" "inference_worker_priority" { + metadata { + name = "inference-worker-priority" + } + value = 900000 + global_default = false + description = "High priority scheduling for GeoCrop inference and worker pods." +} \ No newline at end of file diff --git a/terraform/terraform.tfstate b/terraform/terraform.tfstate index 7dcee0b..d7b91a1 100644 --- a/terraform/terraform.tfstate +++ b/terraform/terraform.tfstate @@ -1,10 +1,45 @@ { "version": 4, "terraform_version": "1.14.9", - "serial": 19, + "serial": 25, "lineage": "80e41663-9b90-f349-cc6c-be6879179605", "outputs": {}, "resources": [ + { + "mode": "managed", + "type": "kubernetes_namespace", + "name": "argo", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "argo", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": {}, + "name": "argo", + "resource_version": "3086761", + "uid": "2c5f8fa2-cdb8-4ff8-9c00-f1f5f589a9f1" + } + ], + "timeouts": null, + "wait_for_default_service_account": null + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Namespace", + "name": "argo" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjozMDAwMDAwMDAwMDB9LCJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + } + ] + }, { "mode": "managed", "type": "kubernetes_namespace", @@ -110,6 +145,41 @@ } ] }, + { + "mode": "managed", + "type": "kubernetes_namespace", + "name": "dressup", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "dressup", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": {}, + "name": "dressup", + "resource_version": "1817654", + "uid": "93ccf1db-35f5-47ba-89c8-d5a186a4f9e6" + } + ], + "timeouts": null, + "wait_for_default_service_account": null + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Namespace", + "name": "dressup" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjozMDAwMDAwMDAwMDB9LCJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + } + ] + }, { "mode": "managed", "type": "kubernetes_namespace", @@ -180,6 +250,41 @@ } ] }, + { + "mode": "managed", + "type": "kubernetes_namespace", + "name": "kubernetes_dashboard", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "kubernetes-dashboard", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": {}, + "name": "kubernetes-dashboard", + "resource_version": "3212", + "uid": "05cc388b-3829-43dd-ab0f-b3acf5170423" + } + ], + "timeouts": null, + "wait_for_default_service_account": null + }, + "sensitive_attributes": [], + "identity_schema_version": 1, + "identity": { + "api_version": "v1", + "kind": "Namespace", + "name": "kubernetes-dashboard" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjozMDAwMDAwMDAwMDB9LCJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + } + ] + }, { "mode": "managed", "type": "kubernetes_namespace", @@ -284,6 +389,70 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjozMDAwMDAwMDAwMDB9LCJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" } ] + }, + { + "mode": "managed", + "type": "kubernetes_priority_class_v1", + "name": "dressup_priority", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "description": "High priority scheduling for dressup app workloads during spikes.", + "global_default": false, + "id": "dressup-priority", + "metadata": [ + { + "annotations": null, + "generate_name": "", + "generation": 1, + "labels": null, + "name": "dressup-priority", + "resource_version": "4396811", + "uid": "b4d9b507-3608-4ce4-8f53-646cca98bbe7" + } + ], + "preemption_policy": "PreemptLowerPriority", + "value": 800000 + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "bnVsbA==" + } + ] + }, + { + "mode": "managed", + "type": "kubernetes_priority_class_v1", + "name": "inference_worker_priority", + "provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "description": "High priority scheduling for GeoCrop inference and worker pods.", + "global_default": false, + "id": "inference-worker-priority", + "metadata": [ + { + "annotations": null, + "generate_name": "", + "generation": 1, + "labels": null, + "name": "inference-worker-priority", + "resource_version": "4396810", + "uid": "78d9337d-f1a5-496d-b63b-068141b1f3e3" + } + ], + "preemption_policy": "PreemptLowerPriority", + "value": 900000 + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "bnVsbA==" + } + ] } ], "check_results": null