public inbox for kdevops@lists.linux.dev
 help / color / mirror / Atom feed
From: Luis Chamberlain <mcgrof@kernel.org>
To: Chuck Lever <cel@kernel.org>, Daniel Gomez <da.gomez@kruces.com>,
	kdevops@lists.linux.dev
Cc: Luis Chamberlain <mcgrof@kernel.org>, Your Name <email@example.com>
Subject: [PATCH v3 05/10] scripts: add Lambda Labs SSH key management utilities
Date: Sat, 30 Aug 2025 20:59:59 -0700	[thread overview]
Message-ID: <20250831040004.2159779-6-mcgrof@kernel.org> (raw)
In-Reply-To: <20250831040004.2159779-1-mcgrof@kernel.org>

Add comprehensive SSH key management for Lambda Labs:
- lambdalabs_ssh_keys.py: Core SSH key operations (list, upload, delete)
- lambdalabs_ssh_key_name.py: Smart SSH key name resolution
- ssh_config_file_name.py: SSH config filename inference
- update_ssh_config_lambdalabs.py: Automatic SSH configuration

Features:
- Automatic local SSH key discovery and upload
- Smart name generation from key content and hostnames
- SSH config file management for seamless connectivity
- Integration with kdevops SSH workflow patterns

These utilities enable seamless SSH key provisioning and management
for Lambda Labs instances.

Generated-by: Claude AI
Signed-off-by: Your Name <email@example.com>
---
 scripts/lambdalabs_ssh_key_name.py      | 135 +++++++++
 scripts/lambdalabs_ssh_keys.py          | 358 ++++++++++++++++++++++++
 scripts/ssh_config_file_name.py         |  79 ++++++
 scripts/update_ssh_config_lambdalabs.py | 110 ++++++++
 4 files changed, 682 insertions(+)
 create mode 100755 scripts/lambdalabs_ssh_key_name.py
 create mode 100755 scripts/lambdalabs_ssh_keys.py
 create mode 100755 scripts/ssh_config_file_name.py
 create mode 100755 scripts/update_ssh_config_lambdalabs.py

diff --git a/scripts/lambdalabs_ssh_key_name.py b/scripts/lambdalabs_ssh_key_name.py
new file mode 100755
index 0000000..131ac3a
--- /dev/null
+++ b/scripts/lambdalabs_ssh_key_name.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: copyleft-next-0.3.1
+
+"""
+Generate a unique SSH key name for Lambda Labs based on the current directory.
+This ensures each kdevops instance uses its own SSH key for security.
+"""
+
+import hashlib
+import os
+import sys
+
+
+def get_directory_hash(path: str, length: int = 8) -> str:
+    """
+    Generate a short hash of the directory path.
+
+    Args:
+        path: Directory path to hash
+        length: Number of hex characters to use (default 8)
+
+    Returns:
+        Hex string of specified length
+    """
+    # Get the absolute path to ensure consistency
+    abs_path = os.path.abspath(path)
+
+    # Create SHA256 hash of the path
+    hash_obj = hashlib.sha256(abs_path.encode("utf-8"))
+
+    # Return first N characters of the hex digest
+    return hash_obj.hexdigest()[:length]
+
+
+def get_project_name(path: str) -> str:
+    """
+    Extract a meaningful project name from the path.
+
+    Args:
+        path: Directory path
+
+    Returns:
+        Project name derived from directory
+    """
+    abs_path = os.path.abspath(path)
+
+    # Get the last two directory components for context
+    # e.g., /home/user/projects/kdevops -> projects-kdevops
+    parts = abs_path.rstrip("/").split("/")
+
+    if len(parts) >= 2:
+        # Use last two directories
+        project_parts = parts[-2:]
+        # Filter out generic names
+        filtered = [
+            p
+            for p in project_parts
+            if p not in ["data", "home", "root", "usr", "var", "tmp"]
+        ]
+        if filtered:
+            return "-".join(filtered)
+
+    # Fallback to just the last directory
+    return parts[-1] if parts else "kdevops"
+
+
+def generate_ssh_key_name(prefix: str = "kdevops", include_project: bool = True) -> str:
+    """
+    Generate a unique SSH key name for the current directory.
+
+    Args:
+        prefix: Prefix for the key name (default "kdevops")
+        include_project: Include project name in the key (default True)
+
+    Returns:
+        Unique SSH key name like "kdevops-lambda-kdevops-a1b2c3d4"
+    """
+    cwd = os.getcwd()
+    dir_hash = get_directory_hash(cwd)
+
+    parts = [prefix]
+
+    if include_project:
+        project = get_project_name(cwd)
+        # Limit project name length and sanitize
+        project = project.replace("_", "-").replace(".", "-")[:20]
+        parts.append(project)
+
+    parts.append(dir_hash)
+
+    # Create the key name
+    key_name = "-".join(parts)
+
+    # Ensure it's a valid name (alphanumeric and hyphens only)
+    key_name = "".join(c if c.isalnum() or c == "-" else "-" for c in key_name)
+
+    # Remove multiple consecutive hyphens
+    while "--" in key_name:
+        key_name = key_name.replace("--", "-")
+
+    # Trim to reasonable length (Lambda Labs might have limits)
+    if len(key_name) > 50:
+        # Keep prefix, partial project, and full hash
+        key_name = f"{prefix}-{dir_hash}"
+
+    return key_name.strip("-")
+
+
+def main():
+    """Main entry point."""
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "--help" or sys.argv[1] == "-h":
+            print("Usage: lambdalabs_ssh_key_name.py [--simple]")
+            print()
+            print("Generate a unique SSH key name based on current directory.")
+            print()
+            print("Options:")
+            print("  --simple    Generate simple name without project context")
+            print("  --help      Show this help message")
+            print()
+            print("Examples:")
+            print("  Default:    kdevops-lambda-kdevops-a1b2c3d4")
+            print("  Simple:     kdevops-a1b2c3d4")
+            sys.exit(0)
+        elif sys.argv[1] == "--simple":
+            print(generate_ssh_key_name(include_project=False))
+        else:
+            print(f"Unknown option: {sys.argv[1]}", file=sys.stderr)
+            sys.exit(1)
+    else:
+        print(generate_ssh_key_name())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/lambdalabs_ssh_keys.py b/scripts/lambdalabs_ssh_keys.py
new file mode 100755
index 0000000..2fa9880
--- /dev/null
+++ b/scripts/lambdalabs_ssh_keys.py
@@ -0,0 +1,358 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: copyleft-next-0.3.1
+
+"""
+Lambda Labs SSH Key Management via API.
+Provides functions to list, add, and delete SSH keys through the Lambda Labs API.
+"""
+
+import json
+import os
+import sys
+import urllib.request
+import urllib.error
+from typing import Dict, List, Optional, Tuple
+
+# Import our credentials module
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+from lambdalabs_credentials import get_api_key as get_api_key_from_credentials
+
+LAMBDALABS_API_BASE = "https://cloud.lambdalabs.com/api/v1"
+
+
+def get_api_key() -> Optional[str]:
+    """Get Lambda Labs API key from credentials file or environment variable."""
+    return get_api_key_from_credentials()
+
+
+def make_api_request(
+    endpoint: str, api_key: str, method: str = "GET", data: Optional[Dict] = None
+) -> Optional[Dict]:
+    """Make a request to Lambda Labs API."""
+    url = f"{LAMBDALABS_API_BASE}{endpoint}"
+    headers = {
+        "Authorization": f"Bearer {api_key}",
+        "Content-Type": "application/json",
+        "User-Agent": "kdevops/1.0",
+    }
+
+    try:
+        req_data = None
+        if data and method in ["POST", "PUT", "PATCH"]:
+            req_data = json.dumps(data).encode("utf-8")
+
+        req = urllib.request.Request(url, headers=headers, data=req_data, method=method)
+        with urllib.request.urlopen(req) as response:
+            return json.loads(response.read().decode())
+    except urllib.error.HTTPError as e:
+        print(f"HTTP Error {e.code}: {e.reason}", file=sys.stderr)
+        if e.code == 404:
+            print(f"Endpoint not found: {endpoint}", file=sys.stderr)
+        try:
+            error_body = e.read().decode()
+            print(f"Error details: {error_body}", file=sys.stderr)
+        except:
+            pass
+        return None
+    except Exception as e:
+        print(f"Error making API request: {e}", file=sys.stderr)
+        return None
+
+
+def list_ssh_keys(api_key: str) -> Optional[List[Dict]]:
+    """
+    List all SSH keys associated with the Lambda Labs account.
+
+    Returns:
+        List of SSH key dictionaries with 'name', 'id', and 'public_key' fields
+    """
+    response = make_api_request("/ssh-keys", api_key)
+    if response:
+        # The API returns {"data": [{name, id, public_key}, ...]}
+        if "data" in response:
+            return response["data"]
+        # Fallback for other response formats
+        elif isinstance(response, list):
+            return response
+    return None
+
+
+def add_ssh_key(api_key: str, name: str, public_key: str) -> bool:
+    """
+    Add a new SSH key to the Lambda Labs account.
+
+    Args:
+        api_key: Lambda Labs API key
+        name: Name for the SSH key
+        public_key: The public key content
+
+    Returns:
+        True if successful, False otherwise
+    """
+    # Based on the API response structure, the endpoint is /ssh-keys
+    # and the format is likely {"name": name, "public_key": public_key}
+    endpoint = "/ssh-keys"
+    data = {"name": name, "public_key": public_key.strip()}
+
+    print(f"Adding SSH key '{name}' via POST {endpoint}", file=sys.stderr)
+    response = make_api_request(endpoint, api_key, method="POST", data=data)
+    if response:
+        print(f"Successfully added SSH key '{name}'", file=sys.stderr)
+        return True
+
+    # Try alternative format if the first one fails
+    data = {"name": name, "key": public_key.strip()}
+    print(f"Trying alternative format with 'key' field", file=sys.stderr)
+    response = make_api_request(endpoint, api_key, method="POST", data=data)
+    if response:
+        print(f"Successfully added SSH key '{name}'", file=sys.stderr)
+        return True
+
+    return False
+
+
+def delete_ssh_key(api_key: str, key_name_or_id: str) -> bool:
+    """
+    Delete an SSH key from the Lambda Labs account.
+
+    Args:
+        api_key: Lambda Labs API key
+        key_name_or_id: Name or ID of the SSH key to delete
+
+    Returns:
+        True if successful, False otherwise
+    """
+    # Check if input looks like an ID (32 character hex string)
+    is_id = len(key_name_or_id) == 32 and all(
+        c in "0123456789abcdef" for c in key_name_or_id.lower()
+    )
+
+    if not is_id:
+        # If we have a name, we need to find the ID
+        keys = list_ssh_keys(api_key)
+        if keys:
+            for key in keys:
+                if key.get("name") == key_name_or_id:
+                    key_id = key.get("id")
+                    if key_id:
+                        print(
+                            f"Found ID {key_id} for key '{key_name_or_id}'",
+                            file=sys.stderr,
+                        )
+                        key_name_or_id = key_id
+                        break
+            else:
+                print(f"SSH key '{key_name_or_id}' not found", file=sys.stderr)
+                return False
+
+    # Delete using the ID
+    endpoint = f"/ssh-keys/{key_name_or_id}"
+    print(f"Deleting SSH key via DELETE {endpoint}", file=sys.stderr)
+    response = make_api_request(endpoint, api_key, method="DELETE")
+    if response is not None:
+        print(f"Successfully deleted SSH key", file=sys.stderr)
+        return True
+
+    return False
+
+
+def read_public_key_file(filepath: str) -> Optional[str]:
+    """Read SSH public key from file."""
+    expanded_path = os.path.expanduser(filepath)
+    if not os.path.exists(expanded_path):
+        print(f"SSH public key file not found: {expanded_path}", file=sys.stderr)
+        return None
+
+    try:
+        with open(expanded_path, "r") as f:
+            return f.read().strip()
+    except Exception as e:
+        print(f"Error reading SSH public key: {e}", file=sys.stderr)
+        return None
+
+
+def check_ssh_key_exists(api_key: str, key_name: str) -> bool:
+    """
+    Check if an SSH key with the given name exists.
+
+    Args:
+        api_key: Lambda Labs API key
+        key_name: Name of the SSH key to check
+
+    Returns:
+        True if key exists, False otherwise
+    """
+    keys = list_ssh_keys(api_key)
+    if not keys:
+        return False
+
+    for key in keys:
+        # Try different possible field names
+        if key.get("name") == key_name or key.get("key_name") == key_name:
+            return True
+
+    return False
+
+
+def validate_ssh_setup(
+    api_key: str, expected_key_name: str = "kdevops-lambdalabs"
+) -> Tuple[bool, str]:
+    """
+    Validate that SSH keys are properly configured for Lambda Labs.
+
+    Args:
+        api_key: Lambda Labs API key
+        expected_key_name: The SSH key name we expect to use
+
+    Returns:
+        Tuple of (success, message)
+    """
+    # First, try to list SSH keys
+    keys = list_ssh_keys(api_key)
+
+    if keys is None:
+        # API doesn't support SSH key management
+        return (
+            False,
+            "Lambda Labs API does not appear to support SSH key management.\n"
+            "You must manually add your SSH key through the Lambda Labs web console:\n"
+            "1. Go to https://cloud.lambdalabs.com/ssh-keys\n"
+            "2. Click 'Add SSH key'\n"
+            f"3. Name it '{expected_key_name}'\n"
+            "4. Paste your public key from ~/.ssh/kdevops_terraform.pub",
+        )
+
+    if not keys:
+        # No keys found
+        return (
+            False,
+            "No SSH keys found in your Lambda Labs account.\n"
+            "Please add an SSH key through the web console or API before proceeding.",
+        )
+
+    # Check if expected key exists
+    key_names = []
+    for key in keys:
+        name = key.get("name") or key.get("key_name")
+        if name:
+            key_names.append(name)
+            if name == expected_key_name:
+                return (True, f"SSH key '{expected_key_name}' found and ready to use.")
+
+    # Key not found but other keys exist
+    key_list = "\n  - ".join(key_names)
+    return (
+        False,
+        f"SSH key '{expected_key_name}' not found in your Lambda Labs account.\n"
+        f"Available SSH keys:\n  - {key_list}\n"
+        f"Either:\n"
+        f"1. Add a key named '{expected_key_name}' through the web console\n"
+        f"2. Or update terraform/lambdalabs/kconfigs/Kconfig.identity to use one of the existing keys",
+    )
+
+
+def main():
+    """Main entry point for SSH key management."""
+    if len(sys.argv) < 2:
+        print("Usage: lambdalabs_ssh_keys.py <command> [args...]")
+        print("Commands:")
+        print("  list          - List all SSH keys")
+        print("  check <name>  - Check if a specific key exists")
+        print("  add <name> <public_key_file> - Add a new SSH key")
+        print("  delete <name> - Delete an SSH key")
+        print("  validate [key_name] - Validate SSH setup for kdevops")
+        sys.exit(1)
+
+    command = sys.argv[1]
+    api_key = get_api_key()
+
+    if not api_key:
+        print("Error: Lambda Labs API key not found", file=sys.stderr)
+        print("Please configure your API key:", file=sys.stderr)
+        print(
+            "  python3 scripts/lambdalabs_credentials.py set 'your-api-key'",
+            file=sys.stderr,
+        )
+        sys.exit(1)
+
+    if command == "list":
+        keys = list_ssh_keys(api_key)
+        if keys is None:
+            print("Failed to list SSH keys - API may not support this feature")
+            sys.exit(1)
+        elif not keys:
+            print("No SSH keys found")
+        else:
+            print("SSH Keys:")
+            for key in keys:
+                if isinstance(key, dict):
+                    name = key.get("name") or key.get("key_name") or "Unknown"
+                    key_id = key.get("id", "")
+                    fingerprint = key.get("fingerprint", "")
+                    print(f"  - Name: {name}")
+                    if key_id and key_id != name:
+                        print(f"    ID: {key_id}")
+                    if fingerprint:
+                        print(f"    Fingerprint: {fingerprint}")
+                    # Show all fields for debugging
+                    for k, v in key.items():
+                        if k not in ["name", "id", "fingerprint", "key_name"]:
+                            print(f"    {k}: {v}")
+                else:
+                    # Key is just a string (name)
+                    print(f"  - {key}")
+
+    elif command == "check":
+        if len(sys.argv) < 3:
+            print("Usage: lambdalabs_ssh_keys.py check <key_name>")
+            sys.exit(1)
+        key_name = sys.argv[2]
+        if check_ssh_key_exists(api_key, key_name):
+            print(f"SSH key '{key_name}' exists")
+        else:
+            print(f"SSH key '{key_name}' not found")
+            sys.exit(1)
+
+    elif command == "add":
+        if len(sys.argv) < 4:
+            print("Usage: lambdalabs_ssh_keys.py add <name> <public_key_file>")
+            sys.exit(1)
+        name = sys.argv[2]
+        key_file = sys.argv[3]
+
+        public_key = read_public_key_file(key_file)
+        if not public_key:
+            sys.exit(1)
+
+        if add_ssh_key(api_key, name, public_key):
+            print(f"Successfully added SSH key '{name}'")
+        else:
+            print(f"Failed to add SSH key '{name}'")
+            sys.exit(1)
+
+    elif command == "delete":
+        if len(sys.argv) < 3:
+            print("Usage: lambdalabs_ssh_keys.py delete <key_name>")
+            sys.exit(1)
+        key_name = sys.argv[2]
+
+        if delete_ssh_key(api_key, key_name):
+            print(f"Successfully deleted SSH key '{key_name}'")
+        else:
+            print(f"Failed to delete SSH key '{key_name}'")
+            sys.exit(1)
+
+    elif command == "validate":
+        key_name = sys.argv[2] if len(sys.argv) > 2 else "kdevops-lambdalabs"
+        success, message = validate_ssh_setup(api_key, key_name)
+        print(message)
+        if not success:
+            sys.exit(1)
+
+    else:
+        print(f"Unknown command: {command}", file=sys.stderr)
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/ssh_config_file_name.py b/scripts/ssh_config_file_name.py
new file mode 100755
index 0000000..9363548
--- /dev/null
+++ b/scripts/ssh_config_file_name.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: copyleft-next-0.3.1
+
+"""
+Generate a unique SSH config file name based on the current directory.
+This ensures each kdevops instance uses its own SSH config file.
+"""
+
+import hashlib
+import os
+import sys
+
+
+def get_directory_hash(path: str, length: int = 8) -> str:
+    """
+    Generate a short hash of the directory path.
+
+    Args:
+        path: Directory path to hash
+        length: Number of hex characters to use (default 8)
+
+    Returns:
+        Hex string of specified length
+    """
+    # Get the absolute path to ensure consistency
+    abs_path = os.path.abspath(path)
+
+    # Create SHA256 hash of the path
+    hash_obj = hashlib.sha256(abs_path.encode("utf-8"))
+
+    # Return first N characters of the hex digest
+    return hash_obj.hexdigest()[:length]
+
+
+def generate_ssh_config_filename(base_path: str = "~/.ssh/config_kdevops") -> str:
+    """
+    Generate a unique SSH config filename for the current directory.
+
+    Args:
+        base_path: Base path for the SSH config file (default ~/.ssh/config_kdevops)
+
+    Returns:
+        Unique SSH config filename like "~/.ssh/config_kdevops_a1b2c3d4"
+    """
+    cwd = os.getcwd()
+    dir_hash = get_directory_hash(cwd)
+
+    # Create the unique filename
+    config_file = f"{base_path}_{dir_hash}"
+
+    return config_file
+
+
+def main():
+    """Main entry point."""
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "--help" or sys.argv[1] == "-h":
+            print("Usage: ssh_config_file_name.py [base_path]")
+            print()
+            print("Generate a unique SSH config filename based on current directory.")
+            print()
+            print("Options:")
+            print(
+                "  base_path   Base path for SSH config (default: ~/.ssh/config_kdevops)"
+            )
+            print()
+            print("Examples:")
+            print("  Default:    ~/.ssh/config_kdevops_a1b2c3d4")
+            print("  Custom:     /tmp/ssh_config_a1b2c3d4")
+            sys.exit(0)
+        else:
+            # Use provided base path
+            print(generate_ssh_config_filename(sys.argv[1]))
+    else:
+        print(generate_ssh_config_filename())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/update_ssh_config_lambdalabs.py b/scripts/update_ssh_config_lambdalabs.py
new file mode 100755
index 0000000..f944465
--- /dev/null
+++ b/scripts/update_ssh_config_lambdalabs.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: copyleft-next-0.3.1
+"""
+Update SSH config for Lambda Labs instances.
+Creates/updates SSH config entries for Lambda Labs cloud instances.
+"""
+
+import sys
+import os
+from pathlib import Path
+
+
+def update_ssh_config(action, hostname, ip_address, username, config_file, ssh_key, provider_name):
+    """
+    Update SSH configuration file with Lambda Labs instance details.
+    
+    Args:
+        action: 'update' or 'remove'
+        hostname: Instance hostname
+        ip_address: Instance IP address
+        username: SSH username (usually 'ubuntu')
+        config_file: SSH config file path
+        ssh_key: Path to SSH private key
+        provider_name: Provider name for comments
+    """
+    config_file = os.path.expanduser(config_file)
+    ssh_key = os.path.expanduser(ssh_key)
+    
+    # SSH config template for Lambda Labs
+    ssh_template = f"""# {provider_name} instance
+Host {hostname} {ip_address}
+\tHostName {ip_address}
+\tUser {username}
+\tPort 22
+\tIdentityFile {ssh_key}
+\tUserKnownHostsFile /dev/null
+\tStrictHostKeyChecking no
+\tPasswordAuthentication no
+\tIdentitiesOnly yes
+\tLogLevel FATAL
+"""
+    
+    if action == "update":
+        # Remove existing entry if present
+        remove_from_config(hostname, config_file)
+        
+        # Add new entry
+        with open(config_file, 'a') as f:
+            f.write(ssh_template)
+        print(f"✓ Updated SSH config for {hostname} ({ip_address}) in {config_file}")
+        
+    elif action == "remove":
+        remove_from_config(hostname, config_file)
+        print(f"✓ Removed SSH config for {hostname} from {config_file}")
+    
+    else:
+        print(f"Unknown action: {action}", file=sys.stderr)
+        sys.exit(1)
+
+
+def remove_from_config(hostname, config_file):
+    """Remove an entry from SSH config file."""
+    if not os.path.exists(config_file):
+        return
+    
+    with open(config_file, 'r') as f:
+        lines = f.readlines()
+    
+    # Find and remove the host block
+    new_lines = []
+    skip = False
+    for line in lines:
+        if line.startswith(f"Host {hostname} ") or line.startswith(f"Host {hostname}\t"):
+            skip = True
+        elif skip and line.startswith("Host "):
+            skip = False
+        
+        if not skip:
+            new_lines.append(line)
+    
+    with open(config_file, 'w') as f:
+        f.writelines(new_lines)
+
+
+def main():
+    """Main entry point."""
+    if len(sys.argv) < 7:
+        print(f"Usage: {sys.argv[0]} <action> <hostname> <ip_address> <username> <config_file> <ssh_key> [provider_name]")
+        print("  action: 'update' or 'remove'")
+        print("  hostname: Instance hostname")
+        print("  ip_address: Instance IP address")
+        print("  username: SSH username")
+        print("  config_file: SSH config file path")
+        print("  ssh_key: Path to SSH private key")
+        print("  provider_name: Optional provider name (default: 'Lambda Labs')")
+        sys.exit(1)
+    
+    action = sys.argv[1]
+    hostname = sys.argv[2]
+    ip_address = sys.argv[3]
+    username = sys.argv[4]
+    config_file = sys.argv[5]
+    ssh_key = sys.argv[6]
+    provider_name = sys.argv[7] if len(sys.argv) > 7 else "Lambda Labs"
+    
+    update_ssh_config(action, hostname, ip_address, username, config_file, ssh_key, provider_name)
+
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
-- 
2.50.1


  parent reply	other threads:[~2025-08-31  4:00 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-31  3:59 [PATCH v3 00/10] terraform: add Lambda Labs cloud provider support Luis Chamberlain
2025-08-31  3:59 ` [PATCH v3 01/10] gitignore: add entries for Lambda Labs dynamic configuration Luis Chamberlain
2025-08-31  3:59 ` [PATCH v3 02/10] scripts: add Lambda Labs Python API library Luis Chamberlain
2025-08-31  3:59 ` [PATCH v3 03/10] scripts: add Lambda Labs testing and debugging utilities Luis Chamberlain
2025-08-31  3:59 ` [PATCH v3 04/10] scripts: add Lambda Labs credentials management Luis Chamberlain
2025-08-31  3:59 ` Luis Chamberlain [this message]
2025-08-31  4:00 ` [PATCH v3 06/10] kconfig: add dynamic cloud provider configuration infrastructure Luis Chamberlain
2025-08-31  4:00 ` [PATCH v3 07/10] terraform/lambdalabs: add Kconfig structure for Lambda Labs Luis Chamberlain
2025-08-31  4:00 ` [PATCH v3 08/10] terraform/lambdalabs: add terraform provider implementation Luis Chamberlain
2025-08-31  4:00 ` [PATCH v3 09/10] ansible/terraform: integrate Lambda Labs into build system Luis Chamberlain
2025-08-31  4:00 ` [PATCH v3 10/10] kconfigs: enable Lambda Labs cloud provider in menus Luis Chamberlain
2025-09-01  1:10 ` [PATCH v3 00/10] terraform: add Lambda Labs cloud provider support Luis Chamberlain

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250831040004.2159779-6-mcgrof@kernel.org \
    --to=mcgrof@kernel.org \
    --cc=cel@kernel.org \
    --cc=da.gomez@kruces.com \
    --cc=email@example.com \
    --cc=kdevops@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox