diff --git a/registry/Excellencedev/README.md b/registry/Excellencedev/README.md new file mode 100644 index 000000000..411dc5274 --- /dev/null +++ b/registry/Excellencedev/README.md @@ -0,0 +1,10 @@ +--- +display_name: Excellence Ademiluyi +bio: Excellence Dev - Crafting exceptional software solutions with passion and precision. +github: Excellencedev +status: community +--- + +# Excellencedev + +Excellence Dev - Crafting exceptional software solutions with passion and precision. diff --git a/registry/Excellencedev/modules/parsec/README.md b/registry/Excellencedev/modules/parsec/README.md new file mode 100644 index 000000000..9df265dce --- /dev/null +++ b/registry/Excellencedev/modules/parsec/README.md @@ -0,0 +1,169 @@ +--- +display_name: Parsec +description: Low-latency cloud gaming remote desktop access for workspaces +icon: ../../../../.icons/desktop.svg +verified: true +tags: [remote-desktop, gaming, parsec, desktop, low-latency] +--- + +# Parsec + +Add low-latency remote desktop access to your Coder workspaces using [Parsec](https://parsec.app/), the cloud gaming platform. Parsec provides near-zero latency remote desktop streaming, making it ideal for graphics-intensive applications, gaming, and high-performance computing workloads. + +```tf +module "parsec" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/parsec/coder" + version = "1.0.0" + agent_id = coder_agent.main.id +} +``` + +## Features + +- **Low-latency streaming**: Sub-1ms latency for responsive remote desktop access +- **Cross-platform support**: Works with Windows, macOS, Linux, and web clients +- **Automatic installation**: Handles Parsec server installation and configuration +- **Headless operation**: Optimized for server environments +- **Customizable IDs**: Set custom server and peer IDs for identification +- **Systemd integration**: Automatic service management on systemd systems + +## Requirements + +- **Supported OS**: Ubuntu/Debian, CentOS/RHEL/Fedora +- **Architecture**: x86_64, ARM64 +- **Network**: Stable internet connection for low-latency streaming +- **Permissions**: sudo access required for installation + +## Examples + +### Basic Setup + +```tf +module "parsec" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/parsec/coder" + version = "1.0.0" + agent_id = coder_agent.main.id +} +``` + +### Custom Configuration + +```tf +module "parsec" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/parsec/coder" + version = "1.0.0" + agent_id = coder_agent.main.id + parsec_version = "150_39b" # Specific Parsec version + server_id = "my-workspace-server" + peer_id = "workspace-peer-001" + share = "authenticated" # Allow authenticated users to access + order = 1 +} +``` + +### With AWS + +```tf +module "parsec" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/parsec/coder" + version = "1.0.0" + agent_id = coder_agent.main.id +} + +# AWS EC2 instance with GPU support (recommended for gaming/graphics) +resource "aws_instance" "workspace" { + ami = "ami-0abcdef1234567890" # Ubuntu with NVIDIA drivers + instance_type = "g4dn.xlarge" # GPU instance + # ... other configuration +} +``` + +### With Google Cloud + +```tf +module "parsec" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/parsec/coder" + version = "1.0.0" + agent_id = coder_agent.main.id +} + +# GCP instance with GPU +resource "google_compute_instance" "workspace" { + name = "coder-workspace" + machine_type = "n1-standard-8" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "ubuntu-os-cloud/ubuntu-2204-lts" + } + } + + # GPU configuration + guest_accelerator { + type = "nvidia-tesla-t4" + count = 1 + } + # ... other configuration +} +``` + +## Configuration Options + +| Variable | Type | Default | Description | +| ---------------- | -------- | ---------- | ------------------------------------------------- | +| `agent_id` | `string` | - | **Required.** The ID of a Coder agent | +| `order` | `number` | `null` | UI position order | +| `group` | `string` | `null` | App group name | +| `share` | `string` | `"owner"` | Sharing level: `owner`, `authenticated`, `public` | +| `parsec_version` | `string` | `"latest"` | Parsec version to install | +| `server_id` | `string` | `""` | Custom server ID | +| `peer_id` | `string` | `""` | Custom peer ID | + +## Usage + +1. **Install the Parsec client** on your local machine from [parsec.app](https://parsec.app/) +2. **Add the module** to your Coder template +3. **Start your workspace** - Parsec will be automatically installed and configured +4. **Connect using the Parsec app**: + - Open the Parsec client + - Look for your workspace in the host list + - Click to connect with ultra-low latency + +## Troubleshooting + +### Connection Issues + +- Ensure your workspace has a stable internet connection +- Check that Parsec is running: `systemctl status parsec` (on systemd systems) +- Verify firewall settings allow Parsec traffic + +### Performance Issues + +- For best performance, use instances with GPU support +- Ensure adequate bandwidth (minimum 10 Mbps recommended) +- Close unnecessary applications on the workspace + +### Installation Issues + +- The module requires sudo access for Parsec installation +- Supported distributions: Ubuntu/Debian, CentOS/RHEL/Fedora +- Check system logs: `journalctl -u parsec` (systemd) or `/tmp/parsec.log` + +## Security Considerations + +- By default, Parsec access is restricted to the workspace owner +- Use `share = "authenticated"` to allow all authenticated Coder users +- Consider network security groups/firewalls to restrict access +- Parsec uses end-to-end encryption for all connections + +## Advanced Configuration + +The module creates a Parsec configuration file at `~/.parsec/config.txt` with optimized settings for headless operation. You can modify this file after installation for advanced tuning. + +For more information about Parsec configuration options, visit the [Parsec documentation](https://support.parsec.app/hc/en-us). diff --git a/registry/Excellencedev/modules/parsec/main.tf b/registry/Excellencedev/modules/parsec/main.tf new file mode 100644 index 000000000..1b424951b --- /dev/null +++ b/registry/Excellencedev/modules/parsec/main.tf @@ -0,0 +1,87 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 2.5" + } + } +} + +variable "agent_id" { + type = string + description = "The ID of a Coder agent." +} + +variable "order" { + type = number + description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)." + default = null +} + +variable "group" { + type = string + description = "The name of a group that this app belongs to." + default = null +} + +variable "share" { + type = string + description = "The sharing level for the Parsec app." + default = "owner" + validation { + condition = var.share == "owner" || var.share == "authenticated" || var.share == "public" + error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'." + } +} + +variable "parsec_version" { + type = string + description = "Version of Parsec to install." + default = "latest" +} + +variable "server_id" { + type = string + description = "Custom server ID for Parsec. If not provided, a random ID will be generated." + default = "" +} + +variable "peer_id" { + type = string + description = "Custom peer ID for Parsec. If not provided, a random ID will be generated." + default = "" +} + +data "coder_workspace" "me" {} + +resource "coder_script" "parsec" { + agent_id = var.agent_id + display_name = "Parsec" + icon = "/icon/desktop.svg" + run_on_start = true + script = templatefile("${path.module}/run.sh", { + PARSEC_VERSION = var.parsec_version + SERVER_ID = var.server_id + PEER_ID = var.peer_id + }) +} + +resource "coder_app" "parsec" { + agent_id = var.agent_id + slug = "parsec" + display_name = "Parsec" + url = "http://localhost:8000" + icon = "/icon/desktop.svg" + subdomain = true + share = var.share + order = var.order + group = var.group + + healthcheck { + url = "http://localhost:8000" + interval = 5 + threshold = 15 + } +} diff --git a/registry/Excellencedev/modules/parsec/parsec.tftest.hcl b/registry/Excellencedev/modules/parsec/parsec.tftest.hcl new file mode 100644 index 000000000..bd16ea978 --- /dev/null +++ b/registry/Excellencedev/modules/parsec/parsec.tftest.hcl @@ -0,0 +1,90 @@ +run "plan_with_required_vars" { + command = plan + + variables { + agent_id = "example-agent-id" + } + + assert { + condition = resource.coder_app.parsec.url == "http://localhost:8000" + error_message = "Expected Parsec app URL to be http://localhost:8000" + } + + assert { + condition = resource.coder_app.parsec.display_name == "Parsec" + error_message = "Expected Parsec app display name to be 'Parsec'" + } + + assert { + condition = resource.coder_app.parsec.slug == "parsec" + error_message = "Expected Parsec app slug to be 'parsec'" + } +} + +run "plan_with_custom_config" { + command = plan + + variables { + agent_id = "example-agent-id" + parsec_version = "150_39b" + server_id = "test-server" + peer_id = "test-peer" + share = "authenticated" + order = 5 + group = "Remote Desktop" + } + + assert { + condition = resource.coder_app.parsec.share == "authenticated" + error_message = "Expected Parsec app share to be 'authenticated'" + } + + assert { + condition = resource.coder_app.parsec.order == 5 + error_message = "Expected Parsec app order to be 5" + } + + assert { + condition = resource.coder_app.parsec.group == "Remote Desktop" + error_message = "Expected Parsec app group to be 'Remote Desktop'" + } +} + +run "plan_with_invalid_share" { + command = plan + + variables { + agent_id = "example-agent-id" + share = "invalid" + } + + expect_failures = [ + var.share + ] +} + +run "validate_script_template" { + command = plan + + variables { + agent_id = "example-agent-id" + parsec_version = "150_39b" + server_id = "custom-server" + peer_id = "custom-peer" + } + + assert { + condition = strcontains(resource.coder_script.parsec.script, "PARSEC_VERSION=150_39b") + error_message = "Expected script to contain PARSEC_VERSION=150_39b" + } + + assert { + condition = strcontains(resource.coder_script.parsec.script, "SERVER_ID=custom-server") + error_message = "Expected script to contain SERVER_ID=custom-server" + } + + assert { + condition = strcontains(resource.coder_script.parsec.script, "PEER_ID=custom-peer") + error_message = "Expected script to contain PEER_ID=custom-peer" + } +} diff --git a/registry/Excellencedev/modules/parsec/run.sh b/registry/Excellencedev/modules/parsec/run.sh new file mode 100644 index 000000000..1f1e23498 --- /dev/null +++ b/registry/Excellencedev/modules/parsec/run.sh @@ -0,0 +1,237 @@ +#!/usr/bin/env bash + +# Exit on error, undefined variables, and pipe failures +set -euo pipefail + +# Convert templated variables to shell variables +PARSEC_VERSION=${PARSEC_VERSION} +SERVER_ID=${SERVER_ID} +PEER_ID=${PEER_ID} + +# Colors for output +BOLD='\033[0;1m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +error() { + printf '\033[0;31m💀 ERROR: %s\033[0m\n' "$@" + exit 1 +} + +info() { + printf '\033[0;32mℹ️ INFO: %s\033[0m\n' "$@" +} + +warn() { + printf '\033[1;33m⚠️ WARNING: %s\033[0m\n' "$@" +} + +# Function to check if Parsec is already installed +check_installed() { + if command -v parsecd &> /dev/null; then + info "Parsec is already installed." + return 0 + else + return 1 + fi +} + +# Function to download a file using wget, curl, or busybox as a fallback +download_file() { + local url="$1" + local output="$2" + local download_tool + + if command -v curl &> /dev/null; then + download_tool=(curl -fsSL) + elif command -v wget &> /dev/null; then + download_tool=(wget -q -O-) + elif command -v busybox &> /dev/null; then + download_tool=(busybox wget -O-) + else + error "No download tool available (curl, wget, or busybox required)" + fi + + # shellcheck disable=SC2288 + "$${download_tool[@]}" "$url" > "$output" || { + error "Failed to download $url" + } +} + +# Function to install Parsec for Ubuntu/Debian +install_deb() { + local url="$1" + local parsecdeb="/tmp/parsec.deb" + + download_file "$url" "$parsecdeb" + + # Update package cache if needed + if ! dpkg -l | grep -q "parsec"; then + info "Installing Parsec package..." + sudo dpkg -i "$parsecdeb" || { + warn "dpkg failed, trying to fix dependencies..." + sudo apt-get update + sudo apt-get install -f -y + sudo dpkg -i "$parsecdeb" + } + fi + + rm "$parsecdeb" +} + +# Function to install Parsec for CentOS/RHEL/Fedora +install_rpm() { + local url="$1" + local parsecrpm="/tmp/parsec.rpm" + + download_file "$url" "$parsecrpm" + + if command -v dnf &> /dev/null; then + sudo dnf install -y "$parsecrpm" + elif command -v yum &> /dev/null; then + sudo yum localinstall -y "$parsecrpm" + else + error "No supported package manager available (dnf or yum required)" + fi + + rm "$parsecrpm" +} + +# Detect system information +if [[ ! -f /etc/os-release ]]; then + error "Cannot detect OS: /etc/os-release not found" +fi + +# shellcheck disable=SC1091 +source /etc/os-release +distro="$ID" +distro_version="$VERSION_ID" +arch="$(uname -m)" + +# Map architecture +case "$arch" in + x86_64) + arch="amd64" + ;; + aarch64) + arch="arm64" + ;; + *) + error "Unsupported architecture: $arch" + ;; +esac + +info "Detected Distribution: $distro" +info "Detected Version: $distro_version" +info "Detected Architecture: $arch" + +# Check if Parsec is installed, and install if not +if ! check_installed; then + # Check for sudo access + if ! command -v sudo &> /dev/null || ! sudo -n true 2> /dev/null; then + error "sudo access required for Parsec installation!" + fi + + printf '\033[0;1m🚀 Installing Parsec...\n\n\033[0m' + + # Determine download URL based on version + if [[ "$PARSEC_VERSION" == "latest" ]]; then + base_url="https://builds.parsec.app/package" + else + base_url="https://builds.parsec.app/package/v${PARSEC_VERSION}" + fi + + case $distro in + ubuntu | debian | kali | linuxmint | pop) + bin_name="parsec-linux.deb" + install_deb "$base_url/$bin_name" + ;; + centos | rhel | fedora | ol) + bin_name="parsec-linux.rpm" + install_rpm "$base_url/$bin_name" + ;; + *) + error "Unsupported distribution: $distro. Parsec supports Ubuntu/Debian and CentOS/RHEL/Fedora." + ;; + esac +else + info "Parsec already installed. Skipping installation." +fi + +printf '\033[0;1m⚙️ Configuring Parsec...\n\n\033[0m' + +# Create Parsec configuration directory +mkdir -p "$HOME/.parsec" + +# Configure Parsec for headless operation +cat > "$HOME/.parsec/config.txt" << EOF +app_run_level = 1 +server_bind_port = 8000 +server_bind_ip = 127.0.0.1 +encoder_bitrate = 100 +encoder_min_bitrate = 10 +encoder_max_bitrate = 100 +client_port = 0 +host_port = 0 +EOF + +# Set custom IDs if provided +if [[ -n "$SERVER_ID" ]]; then + echo "server_id = $SERVER_ID" >> "$HOME/.parsec/config.txt" +fi + +if [[ -n "$PEER_ID" ]]; then + echo "peer_id = $PEER_ID" >> "$HOME/.parsec/config.txt" +fi + +# Create systemd service for Parsec (if systemd is available) +if command -v systemctl &> /dev/null; then + info "Setting up Parsec systemd service..." + + sudo tee /etc/systemd/system/parsec.service > /dev/null << EOF +[Unit] +Description=Parsec Cloud Gaming +After=network.target + +[Service] +Type=simple +User=$USER +ExecStart=/usr/bin/parsecd +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF + + sudo systemctl daemon-reload + sudo systemctl enable parsec + sudo systemctl start parsec + + info "Parsec service started and enabled." +else + warn "systemd not available, starting Parsec manually..." + + # Start Parsec in background + nohup /usr/bin/parsecd > /tmp/parsec.log 2>&1 & + PARSEC_PID=$! + + # Save PID for potential cleanup + echo $PARSEC_PID > /tmp/parsec.pid + + info "Parsec started with PID: $PARSEC_PID" +fi + +# Wait a moment for Parsec to start +sleep 3 + +# Check if Parsec is running +if pgrep -f parsecd > /dev/null; then + printf '\033[0;32m✅ Parsec installation and configuration complete!\033[0m\n\n' + info "Parsec web interface should be available at http://localhost:8000" + info "You can now connect to this workspace using the Parsec client." +else + error "Parsec failed to start. Check logs for details." +fi