diff --git a/harbor-ctl.py b/harbor-ctl.py index 29a2dec..5cbfb90 100755 --- a/harbor-ctl.py +++ b/harbor-ctl.py @@ -2,21 +2,25 @@ """Harbor registry control utility.""" import argparse +import base64 import json import os +import ssl import sys import time -import urllib.request import urllib.error -import ssl +import urllib.request from pathlib import Path +from typing import Any + +VERSION = "0.1.0" # Default config DEFAULT_CREDS = Path("/opt/ansible/secrets/harbor/credentials.json") DEFAULT_URL = "https://harbor.mymx.me" -def load_credentials(): +def load_credentials() -> tuple[str | None, str | None, str]: """Load Harbor credentials from secrets file.""" if DEFAULT_CREDS.exists(): with open(DEFAULT_CREDS) as f: @@ -27,16 +31,32 @@ def load_credentials(): return os.environ.get("HARBOR_USER"), os.environ.get("HARBOR_PASS"), DEFAULT_URL -def api_request(url, user, password, method="GET", data=None): +def build_url(base: str, *parts: str, **params: str) -> str: + """Build API URL from parts and optional query parameters.""" + url = f"{base}/api/v2.0/{'/'.join(parts)}" + if params: + query = "&".join(f"{k}={v}" for k, v in params.items()) + url = f"{url}?{query}" + return url + + +def api_request( + url: str, + user: str, + password: str, + method: str = "GET", + data: dict[str, Any] | None = None, + verify_ssl: bool = False, +) -> dict[str, Any]: """Make authenticated API request to Harbor.""" ctx = ssl.create_default_context() - ctx.check_hostname = False - ctx.verify_mode = ssl.CERT_NONE + if not verify_ssl: + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE req = urllib.request.Request(url, method=method) # Basic auth - import base64 credentials = base64.b64encode(f"{user}:{password}".encode()).decode() req.add_header("Authorization", f"Basic {credentials}") @@ -555,9 +575,11 @@ Examples: %(prog)s config library --show Show project settings """ ) + parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {VERSION}") parser.add_argument("--url", help="Harbor URL", default=None) parser.add_argument("-u", "--user", help="Username", default=None) parser.add_argument("-p", "--password", help="Password", default=None) + parser.add_argument("--verify-ssl", action="store_true", help="Verify SSL certificates (default: skip)") subparsers = parser.add_subparsers(dest="command", help="Commands") @@ -659,6 +681,11 @@ Examples: "config": cmd_config, } + # Store connection params on args for command access + args.api_user = user + args.api_password = password + args.api_url = url + return commands[args.command](args, user, password, url)