diff --git a/README.md b/README.md index be2f761..5adc5e5 100644 --- a/README.md +++ b/README.md @@ -9,34 +9,25 @@

- Modern Kubernetes port-forward manager with interactive terminal UI + Kubernetes port-forward manager with interactive terminal UI

-kportal simplifies managing multiple Kubernetes port-forwards with an elegant, interactive terminal interface. Built with [Bubble Tea](https://github.com/charmbracelet/bubbletea), it provides real-time status updates, automatic reconnection, and hot-reload configuration support. +kportal manages multiple Kubernetes port-forwards with an interactive terminal interface. It provides real-time status updates, automatic reconnection, hot-reload configuration, and mDNS hostname publishing. ![kportal Screenshot](docs/kportal-screenshot.png) ## ✨ Features -- 🎯 **Interactive TUI** - Beautiful terminal interface with keyboard navigation (↑↓/jk, Space to toggle, q to quit) -- ➕ **Live Add** - Add new port-forwards on-the-fly without editing config files or restarting -- ✏️ **Live Edit** - Modify existing port-forwards (ports, resources, aliases) in real-time -- 🗑️ **Live Delete** - Remove port-forwards instantly from the running session -- 🔄 **Auto-Reconnect** - Automatic retry with exponential backoff on connection failures (max 10s) -- ⚡ **Hot-Reload** - Update configuration without restarting - changes applied automatically -- 🏥 **Advanced Health Checks** - Multiple check methods (tcp-dial, data-transfer) with stale connection detection -- 🛡️ **Goroutine Watchdog** - Detects and recovers from completely hung workers -- 🎨 **Multi-Context** - Support for multiple Kubernetes contexts and namespaces -- 📦 **Batch Management** - Manage all port-forwards from a single configuration file -- 🔌 **Toggle Forwards** - Enable/disable individual port-forwards on the fly with Space key -- 🚀 **Grace Period** - Smart 10-second grace period to avoid false "Error" status on startup -- 📊 **Status Display** - Clear visual indicators: Active (●), Starting (○), Reconnecting (◐), Error (✗) -- 🔍 **Error Reporting** - Detailed error messages displayed below the table -- 🔄 **Pod Restart Handling** - Detects and reconnects to pods when they restart -- 🏷️ **Label Selector Support** - Dynamically target pods using label selectors -- 📋 **Prefix Matching** - Automatically find and reconnect to pods with name prefixes -- 🚫 **Port Conflict Detection** - Validates port availability before starting with detailed PID info -- 🎭 **Alias Support** - Cleaner, more readable display names for your forwards +- **Interactive TUI** - Terminal interface with keyboard navigation +- **Live management** - Add, edit, and delete port-forwards without restarting +- **Auto-reconnect** - Exponential backoff retry on connection failures +- **Hot-reload** - Configuration changes applied automatically +- **Health monitoring** - Multiple check methods with stale connection detection +- **Multi-context** - Support for multiple Kubernetes contexts and namespaces +- **Pod restart handling** - Automatic reconnection when pods restart +- **Label selectors** - Dynamic pod targeting using label selectors +- **Port conflict detection** - Validates port availability with PID information +- **mDNS hostnames** - Access forwards via `.local` hostnames ## 📦 Installation @@ -46,7 +37,7 @@ kportal simplifies managing multiple Kubernetes port-forwards with an elegant, i brew install lukaszraczylo/brew-taps/kportal ``` -### Quick Install Script +### Quick Install ```bash curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash @@ -54,24 +45,19 @@ curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install. ### Manual Download -Download the latest binary for your platform from the [releases page](https://github.com/lukaszraczylo/kportal/releases): - -- **macOS**: `kportal-{version}-darwin-{amd64|arm64}.tar.gz` -- **Linux**: `kportal-{version}-linux-{amd64|arm64}.tar.gz` -- **Windows**: `kportal-{version}-windows-{amd64|arm64}.zip` +Download binaries from the [releases page](https://github.com/lukaszraczylo/kportal/releases). ### Build from Source ```bash git clone https://github.com/lukaszraczylo/kportal.git cd kportal -make build -make install +make build && make install ``` ## 🚀 Quick Start -1. **Create a configuration file** (`.kportal.yaml`): +Create `.kportal.yaml`: ```yaml contexts: @@ -85,32 +71,32 @@ contexts: localPort: 5432 alias: prod-db - - name: frontend - forwards: - resource: service/redis protocol: tcp port: 6379 - localPort: 6380 - alias: prod-redis + localPort: 6379 ``` -2. **Run kportal**: +Run: ```bash kportal ``` -3. **Navigate the interface**: - - `↑↓` or `j/k` - Navigate through forwards - - `Space` or `Enter` - Toggle forward on/off - - `a` - Add new port-forward interactively - - `e` - Edit selected port-forward - - `d` - Delete selected port-forward - - `q` - Quit application +### Keyboard Controls + +| Key | Action | +|-----|--------| +| `↑↓` / `j/k` | Navigate | +| `Space` / `Enter` | Toggle forward | +| `a` | Add forward | +| `e` | Edit forward | +| `d` | Delete forward | +| `q` | Quit | ## 📖 Configuration -### Simple Configuration +### Basic Structure ```yaml contexts: @@ -118,592 +104,207 @@ contexts: namespaces: - name: forwards: - - resource: / + - resource: / protocol: tcp port: localPort: - alias: # Optional + alias: # optional + selector: # optional ``` -### Advanced Configuration +### Forward Options -```yaml -contexts: - # Production cluster - - name: prod-us-west - namespaces: - - name: databases - forwards: - # Direct pod connection with prefix matching - - resource: pod/postgres-primary - protocol: tcp - port: 5432 - localPort: 5432 - alias: prod-postgres - - # Service connection - - resource: service/redis-master - protocol: tcp - port: 6379 - localPort: 6379 - alias: prod-redis - - # Pod with label selector - - resource: pod - selector: app=mongodb - protocol: tcp - port: 27017 - localPort: 27017 - alias: mongo - - - name: applications - forwards: - - resource: deployment/api-server - protocol: tcp - port: 8080 - localPort: 8080 - alias: api - - # Development cluster - - name: dev-local - namespaces: - - name: default - forwards: - - resource: service/grafana - protocol: tcp - port: 3000 - localPort: 3000 - alias: grafana-dashboard -``` - -### Configuration Options - -| Field | Type | Required | Description | -|-------|------|----------|-------------| -| `resource` | string | Yes | Kubernetes resource with type prefix (e.g., `service/name`, `pod/name`) | -| `protocol` | string | Yes | Connection protocol (typically `tcp`) | -| `port` | int | Yes | Remote port on the Kubernetes resource | -| `localPort` | int | Yes | Local port to forward to | -| `alias` | string | No | Friendly name for display (defaults to resource name) | -| `selector` | string | No | Label selector for dynamic pod selection (e.g., `app=nginx,env=prod`) | +| Field | Required | Description | +|-------|----------|-------------| +| `resource` | Yes | Resource type and name (e.g., `service/postgres`, `pod/my-app`) | +| `protocol` | Yes | Protocol (`tcp`) | +| `port` | Yes | Remote port | +| `localPort` | Yes | Local port | +| `alias` | No | Display name and mDNS hostname | +| `selector` | No | Label selector for pod resolution | ### Resource Formats -- **Pod by name**: `pod/pod-name` or just `pod-name` -- **Pod by prefix**: `pod/my-app` (matches `my-app-xyz789`, `my-app-abc123`, etc.) -- **Pod by selector**: Set `resource: pod` and use `selector: app=nginx` -- **Service**: `service/service-name` or `svc/service-name` -- **Deployment**: `deployment/deployment-name` or `deploy/deployment-name` +| Format | Description | +|--------|-------------| +| `service/name` | Service forwarding | +| `pod/name` | Direct pod by name | +| `pod/prefix` | Pod by prefix (matches `prefix-*`) | +| `pod` + `selector` | Pod by label selector | +| `deployment/name` | Deployment | -### Health Check & Reliability (Advanced) - -kportal includes advanced health checking to prevent stale connections during long-running operations like database dumps: +### Health Check Configuration ```yaml healthCheck: - interval: "3s" # Health check frequency (default: 3s) - timeout: "2s" # Health check timeout (default: 2s) - method: "data-transfer" # Check method: "tcp-dial" or "data-transfer" (default: data-transfer) - maxConnectionAge: "25m" # Proactive reconnect before k8s timeout (default: 25m) - maxIdleTime: "10m" # Detect hung connections (default: 10m) - -reliability: - tcpKeepalive: "30s" # TCP keepalive interval (default: 30s) - dialTimeout: "30s" # Connection dial timeout (default: 30s) - retryOnStale: true # Auto-reconnect stale connections (default: true) -``` - -**Health Check Methods:** -- **`tcp-dial`**: Fast TCP connection test - verifies local port is listening -- **`data-transfer`**: More reliable - attempts to read data to verify tunnel is functional - -**Stale Detection:** -- **Max Connection Age**: Kubernetes API typically has 30-minute timeout. kportal reconnects at 25 minutes by default to avoid hitting this limit. **Important**: Age-based reconnection only occurs when the connection is ALSO idle - active transfers (like database dumps) are never interrupted. -- **Max Idle Time**: Detects connections with no data transfer, common when intermediate firewalls drop idle TCP connections - -**Use Case Example - Database Dumps:** -```yaml -# Optimized for long-running pg_dump -healthCheck: - method: "data-transfer" - maxConnectionAge: "20m" # Only applies when idle - won't interrupt active dumps - maxIdleTime: "5m" # Detects truly stale connections + interval: "3s" # Check frequency + timeout: "2s" # Check timeout + method: "data-transfer" # tcp-dial or data-transfer + maxConnectionAge: "25m" # Reconnect before k8s timeout + maxIdleTime: "10m" # Detect idle connections reliability: tcpKeepalive: "30s" + dialTimeout: "30s" retryOnStale: true ``` -This configuration ensures multi-hour database dumps complete without interruption. The `maxConnectionAge` will only trigger reconnection if the connection has been idle for more than `maxIdleTime`, preventing interruption of active data transfers. +Health check methods: +- `tcp-dial` - Fast TCP connection test +- `data-transfer` - Verifies tunnel functionality by attempting data read -## 🎮 Usage +Connection age reconnection only triggers when the connection is also idle, preventing interruption of active transfers like database dumps. -### Interactive Mode (Default) +### mDNS Hostnames + +Enable mDNS to access forwards via `.local` hostnames: + +```yaml +mdns: + enabled: true + +contexts: + - name: production + namespaces: + - name: default + forwards: + - resource: service/postgres + port: 5432 + localPort: 5432 + alias: prod-db # Accessible via prod-db.local:5432 +``` + +- Explicit `alias` becomes `.local` +- Without alias, hostname is generated from resource name (`service/redis` → `redis.local`) +- Works on macOS (Bonjour) and Linux (avahi-daemon) + +Verify registration: +```bash +dns-sd -B _kportal._tcp local # macOS +avahi-browse -t _kportal._tcp # Linux +``` + +## Usage + +### Interactive Mode ```bash kportal ``` -Starts the interactive TUI where you can: -- View all configured port-forwards in a table -- See real-time status updates (Active, Starting, Reconnecting, Error) -- Toggle forwards on/off with Space key -- View detailed error messages at the bottom of the screen - ### Verbose Mode ```bash kportal -v ``` -Runs in verbose mode with: -- Detailed logging to stdout -- Periodic status table updates every 2 seconds -- Full error traces -- No interactive controls (for automation/debugging) - ### Validate Configuration ```bash kportal --check ``` -Validates your configuration file without starting any forwards: -- Checks YAML syntax -- Validates all required fields -- Detects duplicate local ports -- Shows validation errors with line numbers - -### Custom Configuration File +### Custom Config File ```bash kportal -c /path/to/config.yaml ``` -### Version Information +## Status Indicators + +| Indicator | Description | +|-----------|-------------| +| `● Active` | Connection healthy | +| `○ Starting` | Initial connection (10s grace period) | +| `◐ Reconnecting` | Reconnecting after failure | +| `✗ Error` | Connection failed | +| `○ Disabled` | Manually disabled | + +## Advanced Features + +### Hot-Reload + +Configuration changes are applied automatically. Manual reload: ```bash -kportal --version -# Output: kportal version 0.1.5 +kill -HUP $(pgrep kportal) ``` -## 🔄 kftray Migration +### Port Conflict Detection -Migrate from kftray JSON configuration: +kportal validates port availability at startup and during hot-reload, showing which process is using conflicting ports. + +### Retry Strategy + +Exponential backoff: 1s → 2s → 4s → 8s → 10s (max). Retries continue indefinitely until connection succeeds. + +## Migration from kftray ```bash kportal --convert configs.json --convert-output .kportal.yaml ``` -**Example conversion:** +## Signal Handling -kftray JSON: -```json -[ - { - "service": "postgres", - "namespace": "default", - "local_port": 5432, - "remote_port": 5432, - "context": "production", - "workload_type": "service", - "protocol": "tcp", - "alias": "prod-db" - } -] -``` - -Converts to kportal YAML: -```yaml -contexts: - - name: production - namespaces: - - name: default - forwards: - - resource: service/postgres - protocol: tcp - port: 5432 - localPort: 5432 - alias: prod-db -``` - -## 🎨 Status Indicators - -| Indicator | Status | Description | -|-----------|--------|-------------| -| `● Active` | 🟢 Green | Port-forward is active and healthy | -| `○ Starting` | 🟡 Yellow | Initial connection in progress (10s grace period) | -| `◐ Reconnecting` | 🟡 Yellow | Attempting to reconnect after failure | -| `✗ Error` | 🔴 Red | Connection failed - see error details below table | -| `○ Disabled` | ⚪ Gray | Port-forward manually disabled via Space key | - -## 🛠️ Advanced Features - -### Hot-Reload - -kportal automatically watches for configuration file changes and reloads: - -```bash -# Edit your config while kportal is running -vim .kportal.yaml - -# Changes are applied automatically within seconds: -# - New forwards are started -# - Removed forwards are stopped -# - Existing forwards continue running unchanged -``` - -Supports manual reload via `SIGHUP`: -```bash -kill -HUP $(pgrep kportal) -``` - -### Health Checks - -Built-in health monitoring system: -- **Check interval**: Every 5 seconds -- **Timeout**: 2 seconds per check -- **Grace period**: 10 seconds for new connections -- **Automatic updates**: Real-time status changes in UI -- **Error tracking**: Detailed error messages for failed connections - -### Error Display - -When connections fail, errors are displayed below the table: - -``` -Errors: - • prod-postgres: dial tcp 127.0.0.1:5432: connect: connection refused - • prod-redis: i/o timeout after 2.0s -``` - -Errors automatically clear when: -- Connection becomes healthy -- Forward is disabled -- Forward is removed - -### Port Conflict Detection - -kportal checks for port conflicts at multiple stages: - -**At startup:** -``` -Port conflicts detected: - Port 8080: - • Requested by: api-server (context: prod, namespace: default) - • Currently used by: PID 1234 (chrome) -``` - -**During hot-reload:** -- Only validates new ports being added -- Skips currently managed ports -- Rejects configuration if conflicts found - -### Pod Restart Handling - -When a pod restarts: -1. Port-forward connection breaks -2. kportal immediately re-resolves the resource: - - For prefix matches: Finds newest pod with matching prefix - - For selectors: Re-queries pods with matching labels -3. Reconnects to new pod -4. Logs the switch: `Switched to new pod: old-pod-abc → new-pod-xyz` - -### Retry Strategy - -Exponential backoff with maximum interval: -- **Intervals**: 1s → 2s → 4s → 8s → 10s (max) -- **Infinite retries**: Continues until connection succeeds -- **Independent**: Each forward has its own retry logic -- **Grace period**: First 10 seconds show "Starting" instead of "Error" - -## 🔧 Development - -### Prerequisites - -- Go 1.23 or higher -- Access to a Kubernetes cluster -- kubectl configured with contexts - -### Building - -```bash -# Build binary -make build - -# Run tests -make test - -# Run all checks (fmt, vet, staticcheck, test) -make all - -# Check current version -make version - -# Install locally -make install - -# Install system-wide -sudo make install-system - -# Clean build artifacts -make clean -``` - -### Project Structure - -``` -kportal/ -├── cmd/kportal/ # Main application entry point -├── internal/ -│ ├── config/ # Configuration parsing and validation -│ ├── forward/ # Port-forward manager and workers -│ │ ├── manager.go # Orchestrates all forwards -│ │ ├── worker.go # Individual forward worker -│ │ └── port_checker.go # Port conflict detection -│ ├── healthcheck/ # Health monitoring system -│ │ └── checker.go # Port health checking -│ ├── k8s/ # Kubernetes client wrapper -│ │ ├── client.go # K8s client management -│ │ ├── port_forward.go # Port-forward implementation -│ │ └── resolver.go # Resource resolution -│ ├── retry/ # Retry logic with backoff -│ │ └── backoff.go # Exponential backoff -│ ├── ui/ # Terminal UI implementations -│ │ ├── bubbletea_ui.go # Interactive TUI (Bubble Tea) -│ │ └── table_ui.go # Simple table for verbose mode -│ └── converter/ # kftray JSON converter -├── Formula/ # Homebrew formula -├── .github/workflows/ # CI/CD pipelines -│ └── release.yml # Release automation -├── install.sh # Installation script -├── semver.yaml # Semantic version config -├── Makefile # Build automation -└── README.md # This file -``` - -## 📝 Examples - -### Database Access - -```yaml -contexts: - production: - namespaces: - databases: - - resource: postgres-primary - port: 5432 - local_port: 5432 - alias: prod-db -``` - -Connect with: -```bash -kportal # Start in another terminal -psql -h localhost -p 5432 -U postgres -``` - -### Multiple Services - -```yaml -contexts: - dev: - namespaces: - default: - - resource: api - port: 8080 - local_port: 8080 - - resource: frontend - port: 3000 - local_port: 3000 - - resource: redis - port: 6379 - local_port: 6379 -``` - -Access: -- API: `http://localhost:8080` -- Frontend: `http://localhost:3000` -- Redis: `redis-cli -p 6379` - -### Cross-Context Setup - -```yaml -contexts: - prod-us: - namespaces: - backend: - - resource: api - port: 8080 - local_port: 8080 - alias: prod-us-api - - prod-eu: - namespaces: - backend: - - resource: api - port: 8080 - local_port: 8081 # Different local port - alias: prod-eu-api -``` - -Compare APIs across regions simultaneously. - -### Debug Multiple Pod Versions - -```yaml -contexts: - production: - namespaces: - default: - # Version 1 - - resource: pod - selector: app=myapp,version=v1 - port: 8080 - local_port: 8080 - alias: app-v1 - - # Version 2 - - resource: pod - selector: app=myapp,version=v2 - port: 8080 - local_port: 8081 - alias: app-v2 - - # Debug port for v2 - - resource: pod - selector: app=myapp,version=v2 - port: 6060 - local_port: 6060 - alias: app-v2-pprof -``` +- `Ctrl+C` / `SIGTERM` - Graceful shutdown +- `SIGHUP` - Reload configuration ## 🐛 Troubleshooting ### Port Already in Use -**Problem**: `Port 8080: already in use by PID 1234 (chrome)` - -**Solutions**: ```bash -# Find the process -lsof -i :8080 - -# Kill the process -kill 1234 - -# Or use a different local port in config -local_port: 8081 +lsof -i : +kill ``` ### Connection Refused -**Problem**: `dial tcp 127.0.0.1:8080: connect: connection refused` - -**Common causes**: -1. **Pod not ready yet** - Wait for status to change from "Starting" → "Active" (10s grace period) -2. **Wrong port number** - Verify the pod/service actually exposes that port -3. **Service not exposed** - Check with `kubectl get svc` and `kubectl describe svc ` - -**Debug**: -```bash -# Check pod status -kubectl get pods -n - -# Check if port is exposed -kubectl describe pod -n - -# Check service endpoints -kubectl get endpoints -n -``` +1. Verify pod is running: `kubectl get pods -n ` +2. Verify port is correct: `kubectl describe pod ` +3. Check service endpoints: `kubectl get endpoints ` ### Context Not Found -**Problem**: `context "prod" not found in kubeconfig` - -**Solution**: ```bash -# List available contexts kubectl config get-contexts - -# Verify context name matches -kubectl config current-context - -# Update your config to use the correct context name ``` -### Health Check Errors During Startup +## 🔧 Development -**Problem**: Seeing "Error" status immediately after starting +### Prerequisites -**This is normal!** kportal has a 10-second grace period. If the connection is still failing after 10 seconds, check: -- Pod is running: `kubectl get pods` -- Port is correct in config -- Network connectivity to cluster +- Go 1.23+ +- Kubernetes cluster access +- kubectl configured -### Logs Covering UI +### Build -**Problem**: Kubernetes client logs appearing over the interactive UI - -**This is fixed in v0.1.5+**. The interactive mode now completely suppresses all logs including: -- Standard Go `log` package -- Kubernetes `klog` output -- Any stderr/stdout leakage - -If you still see logs, please file an issue! - -## 🤝 Contributing - -Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. - -1. Fork the repository -2. Create a feature branch (`git checkout -b feature/amazing-feature`) -3. Make your changes and add tests -4. Run checks: `make all` -5. Commit your changes (follow [semantic commit messages](#semantic-versioning)) -6. Push to the branch (`git push origin feature/amazing-feature`) -7. Open a Pull Request - -### Semantic Versioning - -This project uses [semver-gen](https://github.com/lukaszraczylo/semver-generator) for automatic semantic version generation based on git commit messages. - -**Version Keywords:** -- **Patch** (0.0.X): `fix`, `bugfix`, `hotfix`, `patch`, `docs`, `test`, `refactor` -- **Minor** (0.X.0): `feat`, `feature`, `add`, `enhance`, `update`, `improve` -- **Major** (X.0.0): `breaking`, `major`, `BREAKING CHANGE` - -Example commits: ```bash -git commit -m "feat: add health check grace period" # Bumps minor version -git commit -m "fix: resolve port conflict detection" # Bumps patch version -git commit -m "breaking: change config file format" # Bumps major version +make build # Build binary +make test # Run tests +make all # fmt, vet, staticcheck, test +make install # Install locally ``` -## 📄 License +## Contributing -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. +See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. -## 🙏 Acknowledgments +## License -- Built with [Bubble Tea](https://github.com/charmbracelet/bubbletea) by Charm - An awesome framework for building terminal UIs -- Styled with [Lipgloss](https://github.com/charmbracelet/lipgloss) - Terminal styling library -- Inspired by [kftray](https://github.com/hcavarsan/kftray) - Original GUI port-forward manager -- Uses [client-go](https://github.com/kubernetes/client-go) for Kubernetes integration -- Version management by [semver-gen](https://github.com/lukaszraczylo/semver-generator) +MIT License - see [LICENSE](LICENSE). -## 📚 Documentation +## Acknowledgments + +- [Bubble Tea](https://github.com/charmbracelet/bubbletea) - Terminal UI framework +- [Lipgloss](https://github.com/charmbracelet/lipgloss) - Terminal styling +- [client-go](https://github.com/kubernetes/client-go) - Kubernetes client +- [kftray](https://github.com/hcavarsan/kftray) - Inspiration + +## Links - [Website](https://lukaszraczylo.github.io/kportal) -- [Issue Tracker](https://github.com/lukaszraczylo/kportal/issues) +- [Issues](https://github.com/lukaszraczylo/kportal/issues) - [Releases](https://github.com/lukaszraczylo/kportal/releases) - [Changelog](CHANGELOG.md) - -## Signal Handling - -- `Ctrl+C` / `SIGTERM`: Graceful shutdown (closes all forwards) -- `SIGHUP`: Reload configuration file - ---- - -Made with ❤️ by [Lukasz Raczylo](https://github.com/lukaszraczylo) diff --git a/RELEASE_SETUP.md b/RELEASE_SETUP.md index b19a094..7d5904f 100644 --- a/RELEASE_SETUP.md +++ b/RELEASE_SETUP.md @@ -1,320 +1,126 @@ -# Release Infrastructure Setup Summary +# Release Infrastructure -This document summarizes all the release infrastructure that has been set up for kportal. +Documentation for kportal's release automation and distribution. -## ✅ Completed Setup - -### 1. GitHub Actions CI/CD Pipeline +## 🔄 CI/CD Pipeline **File**: `.github/workflows/release.yml` -**Features**: -- Multi-platform binary builds (Linux, macOS, Windows - amd64 & arm64) -- Automatic release creation on version tags -- Binary archiving (tar.gz for Unix, zip for Windows) -- SHA256 checksum generation -- Automated Homebrew formula updates -- Release notes generation +The pipeline builds multi-platform binaries, creates GitHub releases, and updates Homebrew on version tags. + +### Trigger a Release -**How to trigger**: ```bash -# Commit with semantic versioning keywords -git commit -m "feat: add new feature" - -# Tag the release git tag -a v0.2.0 -m "Release v0.2.0" - -# Push tags git push origin v0.2.0 ``` -The pipeline will automatically: +The pipeline will: 1. Build binaries for all platforms -2. Create GitHub release with binaries +2. Create GitHub release with binaries and checksums 3. Update Homebrew tap formula -4. Generate release notes -### 2. Installation Methods +## 📦 Installation Methods -#### A. Homebrew Formula +### Homebrew **File**: `Formula/kportal.rb` -**Installation command**: ```bash brew install lukaszraczylo/tap/kportal ``` -**Note**: Formula is automatically updated by CI/CD pipeline. You'll need to create a separate tap repository: -1. Create repo: `https://github.com/lukaszraczylo/brew-taps` -2. Add Formula/kportal.rb to that repo -3. Set `HOMEBREW_TAP_TOKEN` secret in GitHub repository settings +Formula is automatically updated by CI/CD. Requires: +- Tap repository: `https://github.com/lukaszraczylo/brew-taps` +- Secret: `HOMEBREW_TAP_TOKEN` with `repo` scope -#### B. Quick Install Script +### Install Script **File**: `install.sh` -**Features**: -- Auto-detects OS and architecture -- Downloads appropriate binary -- Extracts and installs to /usr/local/bin -- Verifies installation -- Colorful output with emoji indicators - -**Installation command**: ```bash curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash ``` -#### C. Manual Download +Auto-detects OS/architecture and installs to `/usr/local/bin`. -Users can download binaries directly from GitHub releases: -``` -https://github.com/lukaszraczylo/kportal/releases -``` +### Manual Download -### 3. Documentation +Download from [releases page](https://github.com/lukaszraczylo/kportal/releases). -#### A. Comprehensive README.md +## Platform Support -**File**: `README.md` +| OS | Architecture | Format | +|----|--------------|--------| +| Linux | amd64, arm64 | tar.gz | +| macOS | amd64, arm64 | tar.gz | +| Windows | amd64, arm64 | zip | -**Contents**: -- Feature showcase with emojis -- Multiple installation methods -- Quick start guide -- Configuration examples -- Usage instructions -- Advanced features documentation -- Troubleshooting guide -- Contributing guidelines +## 🚀 Release Process -#### B. GitHub Pages Website - -**File**: `docs/index.html` - -**Features**: -- Modern, responsive design with TailwindCSS -- Hero section with clear CTA -- Feature showcase cards -- Installation guide -- Configuration examples with syntax highlighting -- Documentation links -- Mobile-friendly - -**URL** (once enabled): `https://lukaszraczylo.github.io/kportal` - -**To enable**: -1. Go to GitHub repository settings -2. Pages section -3. Source: Deploy from a branch -4. Branch: main -5. Folder: /docs - -### 4. Supporting Files - -#### CHANGELOG.md -**File**: `CHANGELOG.md` - -Tracks all changes following Keep a Changelog format. Update this file with each release. - -#### CONTRIBUTING.md -**File**: `CONTRIBUTING.md` - -Guidelines for: -- Bug reporting -- Feature requests -- Pull request process -- Commit message format -- Development setup -- Testing guidelines - -## 🚀 Release Workflow - -### Standard Release Process - -1. **Develop features** +1. **Make changes and test** ```bash - git checkout -b feature/my-feature - # Make changes - make test - make all + make test && make all ``` -2. **Commit with semantic messages** - ```bash - git commit -m "feat: add amazing feature" - git commit -m "fix: resolve bug in health check" - ``` +2. **Update CHANGELOG.md** -3. **Update CHANGELOG.md** - ```markdown - ## [0.2.0] - 2025-11-24 - - ### Added - - Amazing new feature - - ### Fixed - - Bug in health check - ``` - -4. **Tag the release** +3. **Tag and push** ```bash git tag -a v0.2.0 -m "Release v0.2.0" git push origin main git push origin v0.2.0 ``` -5. **CI/CD automatically**: - - Builds all binaries - - Creates GitHub release - - Updates Homebrew formula - - Attaches binaries and checksums +## Version Bumping -### Version Bumping (Semantic Versioning) +Version determined by commit message keywords: -Version is automatically determined by semver-gen from commit messages: +| Bump | Keywords | +|------|----------| +| Patch (0.0.X) | `fix`, `bugfix`, `docs`, `test`, `refactor` | +| Minor (0.X.0) | `feat`, `feature`, `add`, `enhance`, `update` | +| Major (X.0.0) | `breaking`, `major`, `BREAKING CHANGE` | -- **Patch** (0.0.X): `fix`, `bugfix`, `hotfix`, `patch`, `docs`, `test`, `refactor` -- **Minor** (0.X.0): `feat`, `feature`, `add`, `enhance`, `update`, `improve` -- **Major** (X.0.0): `breaking`, `major`, `BREAKING CHANGE` +## Required Secrets -## 📦 Platform Support +| Secret | Purpose | +|--------|---------| +| `GITHUB_TOKEN` | Provided by GitHub Actions | +| `HOMEBREW_TAP_TOKEN` | Personal access token with `repo` scope | -### Supported Platforms - -| OS | Architecture | Archive Format | -|---------|-------------|----------------| -| Linux | amd64 | tar.gz | -| Linux | arm64 | tar.gz | -| macOS | amd64 | tar.gz | -| macOS | arm64 | tar.gz | -| Windows | amd64 | zip | -| Windows | arm64 | zip | - -## 🔒 Required GitHub Secrets - -For full automation, set these secrets in your GitHub repository: - -1. **GITHUB_TOKEN** - Automatically provided by GitHub Actions -2. **HOMEBREW_TAP_TOKEN** - Personal access token for updating Homebrew tap - - Create at: https://github.com/settings/tokens - - Permissions needed: `repo` scope - - Add to repository secrets - -## 📝 Next Steps +## ⚙️ Initial Setup ### 1. Enable GitHub Pages -- Repository Settings → Pages → Source: main branch, /docs folder -### 2. Create Homebrew Tap Repository +Repository Settings → Pages → Source: main branch, /docs folder + +### 2. Create Homebrew Tap + ```bash -# Create new repository gh repo create lukaszraczylo/brew-taps --public - -# Clone and set up -git clone https://github.com/lukaszraczylo/brew-taps cd brew-taps -cp ../kportal/Formula/kportal.rb ./Formula/ -git add Formula/kportal.rb -git commit -m "Initial formula for kportal" -git push origin main +mkdir Formula +# Formula will be auto-updated by CI ``` -### 3. Add GitHub Token to Secrets -- Repository Settings → Secrets and variables → Actions -- New repository secret +### 3. Add Token Secret + +Repository Settings → Secrets → Actions → New secret: - Name: `HOMEBREW_TAP_TOKEN` -- Value: Your personal access token - -### 4. Create First Release -```bash -cd kportal -git add . -git commit -m "feat: initial release setup" -git push origin main -git tag -a v0.1.5 -m "Release v0.1.5" -git push origin v0.1.5 -``` - -### 5. Test Installation Methods - -After first release, test: -```bash -# Homebrew (once tap is set up) -brew install lukaszraczylo/tap/kportal - -# Quick install script -curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash - -# Manual download -# Visit releases page and download binary -``` - -## 🎨 Customization - -### Update Website Colors - -Edit `docs/index.html`: -```javascript -tailwind.config = { - theme: { - extend: { - colors: { - primary: '#3b82f6', // Blue - secondary: '#8b5cf6', // Purple - dark: '#0f172a', // Dark slate - } - } - } -} -``` - -### Update Release Notes Template - -Edit `.github/workflows/release.yml` in the "Generate release notes" step. - -## 📊 Monitoring - -After releases, monitor: -- GitHub Actions workflow runs -- GitHub Releases page -- Homebrew tap repository commits -- Download statistics on releases page +- Value: Personal access token with `repo` scope ## 🐛 Troubleshooting ### Release workflow fails - Check GitHub Actions logs -- Verify all required secrets are set -- Ensure tag follows v\d+.\d+.\d+ format +- Verify secrets are configured +- Ensure tag follows `v\d+.\d+.\d+` format -### Homebrew formula not updating -- Verify HOMEBREW_TAP_TOKEN is valid +### Homebrew not updating +- Verify `HOMEBREW_TAP_TOKEN` is valid - Check tap repository permissions -- Review release workflow logs ### Install script fails -- Test locally with different OS/arch combinations -- Check release binary naming matches script expectations -- Verify binaries are attached to release - -## ✅ Checklist for First Release - -- [ ] All code committed and pushed -- [ ] GitHub Pages enabled -- [ ] Homebrew tap repository created -- [ ] HOMEBREW_TAP_TOKEN secret set -- [ ] CHANGELOG.md updated -- [ ] Version tag created and pushed -- [ ] Release workflow completed successfully -- [ ] Binaries attached to release -- [ ] Homebrew formula updated -- [ ] Install script tested -- [ ] Documentation website live -- [ ] README.md installation links work - ---- - -**Documentation last updated**: 2025-11-23 -**Setup completed for**: kportal v0.1.5 +- Verify release binaries are attached +- Check binary naming matches script expectations diff --git a/WIZARD_USAGE.md b/WIZARD_USAGE.md index 7ab34ee..f2ee6a6 100644 --- a/WIZARD_USAGE.md +++ b/WIZARD_USAGE.md @@ -1,171 +1,104 @@ -# Interactive Add/Remove Wizards +# Interactive Wizards -kportal now includes interactive wizards for adding and removing port forwards directly from the running UI! +kportal includes wizards for adding and removing port forwards from the running UI. -## Quick Start +## ⌨️ Quick Reference -Run kportal normally: -```bash -./kportal -``` +| Key | Action | +|-----|--------| +| `a` | Add new forward | +| `d` | Delete forwards | -From the main view: -- Press **`n`** to add a new port forward -- Press **`d`** to delete existing port forwards +## ➕ Add Forward Wizard -## Add Forward Wizard (`n` key) +Press `a` from the main view to start the wizard. -The wizard guides you through 7 steps to add a new forward: +### Steps -### Step 1: Select Context -Choose from available Kubernetes contexts in your kubeconfig. +1. **Context** - Select Kubernetes context +2. **Namespace** - Select namespace +3. **Resource Type** - Choose pod (prefix), pod (selector), or service +4. **Resource** - Enter prefix, selector, or select service +5. **Remote Port** - Enter port on the resource +6. **Local Port** - Enter local port (validates availability) +7. **Confirm** - Review and optionally add an alias -### Step 2: Select Namespace -Pick the namespace where your resource lives. +### Navigation -### Step 3: Select Resource Type -Three options: -- **Pod (by name prefix)** - Forward to a specific pod by prefix matching -- **Pod (by label selector)** - Forward to pods matching labels (survives restarts) -- **Service** - Most stable, load-balanced option +| Key | Action | +|-----|--------| +| `↑↓` / `j/k` | Navigate options | +| `Enter` | Confirm and proceed | +| `Esc` | Go back / Cancel | +| `Ctrl+C` | Cancel immediately | -### Step 4: Enter Resource -- **Pod prefix**: Type a prefix like `nginx-` to match pods -- **Label selector**: Enter labels like `app=nginx,env=prod` -- **Service**: Select from a list of services +## 🗑️ Delete Forward Wizard -The wizard shows real-time validation and matching resources! +Press `d` from the main view. -### Step 5: Remote Port -Enter the port number on the remote resource. The wizard displays detected ports from running containers. +### Navigation -### Step 6: Local Port -Enter the local port to bind to. The wizard checks availability in real-time. +| Key | Action | +|-----|--------| +| `↑↓` / `j/k` | Navigate | +| `Space` | Toggle selection | +| `a` | Select all | +| `n` | Deselect all | +| `Enter` | Confirm deletion | +| `Esc` | Cancel | -### Step 7: Confirmation -Review your configuration and optionally add an alias (friendly name). Confirm to save! +## 🎯 Resource Selection -### Navigation Keys +### Pod by Prefix -- **`↑`/`↓`** or **`j`/`k`** - Navigate options -- **`Enter`** - Confirm and proceed to next step -- **`Esc`** - Go back one step (or cancel on first step) -- **`Ctrl+C`** - Hard cancel and return to main view -- **`Backspace`** - Delete characters in text fields - -## Remove Forward Wizard (`d` key) - -Multi-select interface for removing forwards: - -1. **Select forwards**: Use arrow keys to navigate, `Space` to toggle selection -2. **Confirm removal**: Press `Enter` and confirm your choice - -### Navigation Keys - -- **`↑`/`↓`** or **`j`/`k`** - Navigate forwards -- **`Space`** - Toggle selection of current forward -- **`a`** - Select all forwards -- **`n`** - Deselect all forwards -- **`Enter`** - Proceed to confirmation -- **`Esc`** - Cancel and return to main view -- **`Ctrl+C`** - Hard cancel - -## Auto Hot-Reload - -When you save a forward via the wizard: -1. The wizard writes to `.kportal.yaml` atomically -2. The file watcher detects the change (~100ms) -3. The manager reloads and starts the new forward -4. The UI updates automatically - -No restart needed! - -## Error Handling - -The wizards handle errors gracefully: - -- **Cluster unreachable**: Shows error but allows manual entry -- **Port conflicts**: Displays which process is using the port -- **Invalid selectors**: Shows validation errors in real-time -- **Duplicate ports**: Prevents adding forwards with conflicting ports - -## Tips - -### Pod Prefix Matching -When using pod prefix, you can type just the app name: +Enter app name prefix to match pods: - `nginx` matches `nginx-deployment-abc123` - `postgres` matches `postgres-statefulset-0` -### Label Selectors -Use standard Kubernetes label syntax: -- `app=nginx` - Single label -- `app=nginx,env=prod` - Multiple labels (comma-separated) -- Real-time validation shows matching pods as you type! +### Pod by Selector -### Aliases -Use aliases for cleaner UI display: -- Instead of: `production/default/pod/nginx-deployment-abc123:80→8080` -- Shows as: `my-nginx:80→8080` +Use Kubernetes label syntax: +- `app=nginx` +- `app=nginx,env=prod` -### Quick Selection -In list views, you can use `j`/`k` (Vim-style) or arrow keys for navigation. +Matching pods are shown in real-time. -## Example Workflow +### Service -Adding a forward for a PostgreSQL database: +Select from discovered services in the namespace. -1. Press `n` in main view -2. Select context: `production` (arrow keys + Enter) -3. Select namespace: `default` (arrow keys + Enter) -4. Select type: `Service` (arrow keys + Enter) -5. Select service: `postgres` (arrow keys + Enter) -6. Enter remote port: `5432` (type + Enter) -7. Enter local port: `5432` (type + Enter) -8. Add alias: `prod-db` (optional, type + Enter) -9. Confirm: Select "Add to .kportal.yaml" (Enter) +## 🔄 Auto Hot-Reload -Done! The forward starts automatically within seconds. +Changes are applied automatically: +1. Wizard writes to `.kportal.yaml` atomically +2. File watcher detects change (~100ms) +3. Manager reloads and starts forward +4. UI updates -## Architecture +## Error Handling -The wizards use: -- **Config Mutator**: Safe, atomic YAML writes (temp file + rename) -- **K8s Discovery**: Lists contexts, namespaces, pods, services -- **Modal Overlays**: Wizards appear centered over the main view -- **Async Validation**: Port checks and selector validation run in background -- **Hot-Reload Integration**: File watcher picks up changes automatically +The wizards handle: +- Cluster unreachable - allows manual entry +- Port conflicts - shows which process is using the port +- Invalid selectors - real-time validation +- Duplicate ports - prevents conflicts -## Troubleshooting +## 🐛 Troubleshooting -### Wizards not appearing? -Check that kportal can connect to your Kubernetes cluster: +### Wizard not appearing + +Verify cluster connectivity: ```bash kubectl cluster-info ``` -### Port check showing wrong status? -The port check happens asynchronously. Wait a moment after typing for validation. +### Port validation delayed -### Changes not appearing? -The file watcher triggers within 100ms. If changes aren't visible, check: +Port checks run asynchronously. Wait briefly after typing. + +### Changes not visible + +Check: 1. `.kportal.yaml` was written correctly -2. No validation errors in the file -3. kportal process is still running - ---- - -**Navigation Summary** - -Main View: -- `n` - New forward wizard -- `d` - Delete forward wizard -- `Space` - Toggle forward on/off -- `↑↓/jk` - Navigate forwards -- `q` - Quit - -Wizards: -- `Enter` - Next step / Confirm -- `Esc` - Previous step / Cancel -- `Ctrl+C` - Hard cancel -- `↑↓/jk` - Navigate -- `Space` - Toggle (in delete wizard) +2. No validation errors in file +3. kportal process is running diff --git a/cmd/kportal/main.go b/cmd/kportal/main.go index 74ee843..bfc1faa 100644 --- a/cmd/kportal/main.go +++ b/cmd/kportal/main.go @@ -19,6 +19,7 @@ import ( "github.com/nvm/kportal/internal/forward" "github.com/nvm/kportal/internal/k8s" "github.com/nvm/kportal/internal/logger" + "github.com/nvm/kportal/internal/mdns" "github.com/nvm/kportal/internal/ui" "github.com/nvm/kportal/internal/version" "k8s.io/klog/v2" @@ -209,6 +210,14 @@ func main() { os.Exit(1) } + // Create mDNS publisher if enabled in config + mdnsPublisher := mdns.NewPublisher(cfg.IsMDNSEnabled()) + manager.SetMDNSPublisher(mdnsPublisher) + + if cfg.IsMDNSEnabled() && *verbose { + log.Printf("mDNS hostname publishing enabled - aliases will be accessible via .local") + } + // Create UI (bubbletea for interactive, simple table for verbose) var bubbleTeaUI *ui.BubbleTeaUI var tableUI *ui.TableUI @@ -318,7 +327,23 @@ func main() { case os.Interrupt, syscall.SIGTERM: log.Printf("Received shutdown signal, stopping...") - manager.Stop() + + // Graceful shutdown with timeout - force exit if it takes too long + shutdownDone := make(chan struct{}) + go func() { + manager.Stop() + close(shutdownDone) + }() + + select { + case <-shutdownDone: + log.Printf("Graceful shutdown complete") + case <-time.After(5 * time.Second): + log.Printf("Shutdown timed out, forcing exit...") + case sig := <-sigChan: + // Second signal received - force exit immediately + log.Printf("Received second signal (%v), forcing exit...", sig) + } os.Exit(0) } } diff --git a/docs/index.html b/docs/index.html index ef2c18a..982b7fe 100644 --- a/docs/index.html +++ b/docs/index.html @@ -6,7 +6,7 @@ kportal - Kubernetes Port-Forward Manager - + -