diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 18d3e1c7..82c0c59d 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -20,6 +20,19 @@ "category": "sre", "agents": "./agents/remediator.md", "skills": "./skills" + }, + { + "name": "rh-developer", + "description": "Developer Agentic Collection", + "version": "1.0.0", + "author": { + "name": "Red Hat Ecosystem Engineering", + "email": "eco-engineering@redhat.com" + }, + "source": "./rh-developer", + "category": "developer", + "agents": ["./agents/error-handling.md", "./agents/s2i-builder-images.md"], + "skills": "./skills" } ] } \ No newline at end of file diff --git a/rh-developer/.claude-plugin/plugin.json b/rh-developer/.claude-plugin/plugin.json new file mode 100644 index 00000000..e4a41bc8 --- /dev/null +++ b/rh-developer/.claude-plugin/plugin.json @@ -0,0 +1,13 @@ +{ + "name": "rh-developer", + "version": "1.0.0", + "description": "Developer Agentic Collection", + "author": { + "name": "Red Hat Ecosystem Engineering", + "email": "eco-engineering@redhat.com" + }, + "homepage": "https://github.com/dmartinol/ai5-marketplaces", + "repository": "https://github.com/dmartinol/ai5-marketplaces", + "license": "Apache-2.0", + "keywords": ["developer", "openshift", "rhel", "s2i", "containerization", "deployment", "helm", "podman"] +} diff --git a/rh-developer/.mcp.json b/rh-developer/.mcp.json new file mode 100644 index 00000000..d5767d1a --- /dev/null +++ b/rh-developer/.mcp.json @@ -0,0 +1,45 @@ +{ + "mcpServers": { + "kubernetes": { + "command": "npx", + "args": ["-y", "kubernetes-mcp-server@latest"], + "env": { + "KUBECONFIG": "${KUBECONFIG}" + }, + "description": "Kubernetes/OpenShift MCP server for cluster management, resource operations, and Helm deployments", + "security": { + "isolation": "process", + "network": "local", + "credentials": "env-only" + } + }, + "podman": { + "command": "npx", + "args": ["-y", "podman-mcp-server@latest"], + "env": {}, + "description": "Podman MCP server for container image management and local builds", + "security": { + "isolation": "process", + "network": "local", + "credentials": "none" + } + }, + "github": { + "command": "podman", + "args": [ + "run", "-i", "--rm", + "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PERSONAL_ACCESS_TOKEN}" + }, + "description": "GitHub MCP server for repository browsing and code analysis", + "security": { + "isolation": "container", + "network": "local", + "credentials": "env-only" + } + } + } +} diff --git a/rh-developer/agents/error-handling.md b/rh-developer/agents/error-handling.md new file mode 100644 index 00000000..76aa9e06 --- /dev/null +++ b/rh-developer/agents/error-handling.md @@ -0,0 +1,400 @@ +--- +name: error-handling +description: | + Reference guide for common error patterns and recovery strategies in OpenShift S2I deployments. Covers authentication errors (401/403), resource conflicts (409), build failures (git clone, S2I assemble, image push), deployment errors (ImagePullBackOff, CrashLoopBackOff, Pending pods), and route issues. Provides templated responses for each error type with troubleshooting steps and user options. Used by /s2i-build, /deploy, and /containerize-deploy skills. +--- + +# Error Handling Patterns + +## Philosophy + +1. **Detect Early** - Check prerequisites before attempting actions +2. **Explain Clearly** - Tell user exactly what went wrong +3. **Offer Options** - Provide multiple paths forward +4. **Never Leave Hanging** - Always end with actionable next step + +## Authentication Errors + +### Not logged into cluster + +**Detection:** kubernetes MCP tools fail with 401/403 or connection refused + +**Response:** +``` +I cannot connect to the OpenShift cluster. + +Please ensure you are logged in: +1. Run: oc login +2. Or set KUBECONFIG environment variable to your kubeconfig file + +After logging in, try again. +``` + +### Insufficient permissions + +**Detection:** 403 Forbidden on resource creation + +**Response:** +``` +You don't have permission to create [resource-type] in namespace [namespace]. + +Current user: [username] +Required role: edit or admin + +Options: +1. Contact your cluster admin to grant permissions +2. Try a different namespace where you have edit access +3. List available namespaces: oc projects +``` + +## Resource Conflicts + +### Resource already exists + +**Detection:** 409 Conflict on create + +**Response:** +``` +A [resource-type] named '[name]' already exists in namespace '[namespace]'. + +Current [resource-type] details: +[show key details] + +Options: +1. Update existing resource with new configuration +2. Delete existing and recreate +3. Use a different name + +What would you like to do? +``` + +## Build Errors + +### Git clone failed + +**Detection:** Build fails in git-clone phase + +**Causes:** +- Invalid URL +- Private repo without credentials +- Network issues + +**Response:** +``` +Failed to clone Git repository: [url] + +Error: [git error message] + +Troubleshooting: +1. Verify URL is correct and accessible from the cluster +2. For private repos, create a Git secret: + oc create secret generic git-creds \ + --from-literal=username= \ + --from-literal=password= \ + --type=kubernetes.io/basic-auth + oc set build-secret --source bc/[name] git-creds +3. Check if the branch '[branch]' exists + +Would you like me to help set up Git credentials? +``` + +### S2I assemble failed + +**Detection:** Build fails during assemble phase + +**Response:** +``` +S2I build failed during the assemble phase. + +Error summary: +[last 10 error lines from log] + +Common causes: +- Missing dependencies in [package.json/requirements.txt/pom.xml] +- Incompatible language version (e.g., using Node 20 features with Node 18 image) +- Build script errors + +Would you like me to: +1. Show full build logs +2. Suggest a different S2I builder image +3. Help troubleshoot the specific error +``` + +### Image push failed + +**Detection:** Build fails during push phase + +**Response:** +``` +Failed to push image to internal registry. + +Error: [push error] + +Common causes: +- Image registry storage is full +- Registry is not accessible +- ImageStream not found + +This is typically a cluster configuration issue. Contact your cluster admin. +``` + +## Deployment Errors + +### ImagePullBackOff + +**Detection:** Pod in ImagePullBackOff state + +**Response:** +``` +Pods cannot pull the container image. + +Image: [image-reference] +Error: [pull error] + +For ImageStream images: +1. Verify build completed: oc get builds +2. Check ImageStream: oc get is [name] -o yaml +3. Ensure image tag exists: oc get istag [name]:latest + +For external images: +1. Verify image exists in registry +2. Create image pull secret if needed: + oc create secret docker-registry my-pull-secret \ + --docker-server= \ + --docker-username= \ + --docker-password= + oc secrets link default my-pull-secret --for=pull +``` + +### CrashLoopBackOff + +**Detection:** Pod in CrashLoopBackOff state + +**Response:** +``` +Application is crashing on startup. + +Pod: [pod-name] +Restart count: [count] + +Recent logs: +[last 20 lines of logs] + +Common causes: +- Missing environment variables +- Database/service connection failures +- Port binding issues (app not listening on expected port) +- Application startup errors + +Would you like me to: +1. Show full pod logs +2. Check pod events for more details +3. Describe the pod configuration +4. Help set up environment variables +``` + +### Pods stuck Pending + +**Detection:** Pod stuck in Pending state for >30 seconds + +**Response:** +``` +Pods are stuck in Pending state. + +Events: +[relevant events] + +Common causes: +- Insufficient cluster resources (CPU/memory) +- No nodes match pod requirements +- PersistentVolumeClaim not bound + +Try: +1. Reduce resource requests in deployment +2. Check cluster capacity: oc describe nodes +3. Contact cluster admin if resources are exhausted +``` + +## Route Errors + +### Route not admitted + +**Detection:** Route status shows not admitted + +**Response:** +``` +Route was created but not admitted by the router. + +Route: [route-name] +Status: [route status] + +Common causes: +- Hostname conflicts with existing route +- TLS certificate issues +- Router capacity exceeded + +Check details: oc describe route [name] + +Would you like me to: +1. Try a different hostname +2. Remove TLS configuration +3. Show conflicting routes +``` + +## Helm Deployment Errors + +### Chart not found + +**Detection:** `helm install` fails with chart not found error + +**Response:** +``` +Helm chart not found at specified path. + +Path checked: [chart-path] + +Verify: +1. Chart.yaml exists in the directory +2. Path is correct (check for typos) +3. If using a repository, run: helm repo update + +Would you like me to: +1. Search for Chart.yaml in common locations +2. Create a new Helm chart for this project +3. Specify a different chart path +``` + +### Release already exists + +**Detection:** `helm install` fails with "cannot re-use a name that is still in use" + +**Response:** +``` +A Helm release named '[release-name]' already exists in namespace '[namespace]'. + +Current release: +- Status: [status] +- Chart: [chart-name]-[version] +- Updated: [timestamp] + +Options: +1. Upgrade the existing release with new values +2. Uninstall the existing release and reinstall +3. Use a different release name + +What would you like to do? +``` + +### Helm values validation failed + +**Detection:** Template rendering fails due to invalid values + +**Response:** +``` +Helm chart validation failed. + +Error: [error message] + +Common causes: +- Missing required values +- Invalid value types (string vs number) +- Template syntax errors + +Would you like me to: +1. Show the default values.yaml for reference +2. Validate your custom values file +3. Render templates locally to debug +``` + +### Helm release failed + +**Detection:** Release status shows "failed" + +**Response:** +``` +Helm release '[name]' failed to deploy. + +Release Status: failed +Revision: [revision] + +**Events:** +[relevant events from pods] + +**Pod Status:** +[pod status table] + +Options: +1. View detailed release status +2. Check pod logs for errors +3. Rollback to previous revision +4. Uninstall and retry + +What would you like to do? +``` + +### Chart dependency error + +**Detection:** Dependencies not satisfied + +**Response:** +``` +Chart has unresolved dependencies. + +Missing dependencies: +[list of missing deps] + +Run these commands to resolve: +1. cd [chart-directory] +2. helm dependency update + +Would you like me to update dependencies? +``` + +## Recovery Actions + +### Retry pattern + +For transient errors: +``` +This might be a temporary issue. + +Would you like me to retry? (yes/no) +``` + +### Partial rollback + +When later steps fail: +``` +The deployment failed, but earlier steps succeeded. + +Completed: +- [x] ImageStream created +- [x] BuildConfig created +- [x] Build completed successfully + +Failed: +- [ ] Deployment creation + +The image is available at: [image] + +Options: +1. Retry deployment only +2. Investigate the issue +3. Rollback all changes +``` + +### Full rollback + +``` +Rolling back all changes... + +Deleting resources: +- [ ] Route: [name] +- [ ] Service: [name] +- [ ] Deployment: [name] +- [ ] BuildConfig: [name] +- [ ] ImageStream: [name] + +[After deletion] +All resources cleaned up. Your namespace is back to its original state. +``` diff --git a/rh-developer/agents/s2i-builder-images.md b/rh-developer/agents/s2i-builder-images.md new file mode 100644 index 00000000..80685325 --- /dev/null +++ b/rh-developer/agents/s2i-builder-images.md @@ -0,0 +1,178 @@ +--- +name: s2i-builder-images +description: | + Reference table of Red Hat UBI-based S2I builder images for Node.js, Python, Java, Go, Ruby, .NET, PHP, and Perl. Includes dynamic lookup methods (Red Hat Catalog API, skopeo), version extraction patterns from project files, framework-specific recommendations (Quarkus, Spring Boot, Next.js, Django), and OpenShift built-in ImageStreams. Used by /detect-project skill to recommend appropriate builder images. Note - verify image availability before recommending as versions may change. +--- + +# S2I Builder Image Reference + +Use this reference when recommending S2I builder images to users. + +## Important: Dynamic Lookup First + +**This reference may be outdated.** Always verify image availability before recommending. + +### Verify with Skopeo (Recommended) + +```bash +# Check if an image exists and get metadata +skopeo inspect docker://registry.access.redhat.com/ubi9/nodejs-20 + +# Get specific fields +skopeo inspect docker://registry.access.redhat.com/ubi9/nodejs-20 --format '{{.Created}}' +skopeo inspect docker://registry.access.redhat.com/ubi9/nodejs-20 --format '{{.Architecture}}' + +# List all available tags +skopeo list-tags docker://registry.access.redhat.com/ubi9/nodejs-20 +``` + +**If skopeo is not installed**, prompt the user: +``` +Install with: sudo dnf install skopeo (Fedora/RHEL) + sudo apt install skopeo (Ubuntu/Debian) + brew install skopeo (macOS) +``` + +### Check Security Status (Red Hat Security Data API) + +Query CVE information (no authentication required): + +```bash +# Check for critical CVEs affecting UBI9 +curl -s "https://access.redhat.com/hydra/rest/securitydata/cve.json?product=Red%20Hat%20Universal%20Base%20Image%209&severity=critical" | jq 'length' + +# Get CVE details +curl -s "https://access.redhat.com/hydra/rest/securitydata/cve.json?product=Red%20Hat%20Universal%20Base%20Image%209&severity=critical" | jq '.[] | {cve: .CVE, severity: .severity}' +``` + +### Verify with Red Hat Catalog API (Alternative) + +```bash +# Search for available Node.js images +curl -s "https://catalog.redhat.com/api/containers/v1/repositories?filter=repository=like=ubi9/nodejs" | jq '.data[].repository' + +# Search for available Python images +curl -s "https://catalog.redhat.com/api/containers/v1/repositories?filter=repository=like=ubi9/python" | jq '.data[].repository' +``` + +### Extract Version from Project Files + +Before recommending an image, check the project's version requirements: + +| Project File | How to Extract Version | +|--------------|------------------------| +| `package.json` | `.engines.node` field | +| `requirements.txt` | `python_requires` or comments | +| `pyproject.toml` | `[project].requires-python` | +| `pom.xml` | `` or `` | +| `go.mod` | `go` directive (e.g., `go 1.21`) | +| `*.csproj` | `` (e.g., `net8.0`) | + +--- + +## Image Reference Tables + +**Quick lookup pattern:** `ubi9/{language}-{version}` (e.g., `ubi9/nodejs-20`, `ubi9/python-311`) + +--- + +## Project Detection Mapping + +### Step 1: Detect Language from Files + +| Indicator File(s) | Language | Framework | Version Source | +|-------------------|----------|-----------|----------------| +| `package.json` | Node.js | - | `.engines.node` | +| `package.json` + `next.config.js` | Node.js | Next.js | `.engines.node` | +| `package.json` + `angular.json` | Node.js | Angular | `.engines.node` | +| `pom.xml` | Java | Maven | `` or `` | +| `pom.xml` + quarkus dep | Java | Quarkus | `` (prefer 21+) | +| `pom.xml` + spring-boot dep | Java | Spring Boot | `` | +| `build.gradle` / `build.gradle.kts` | Java | Gradle | `sourceCompatibility` or `java.toolchain` | +| `requirements.txt` | Python | - | `python_requires` or shebang | +| `Pipfile` | Python | Pipenv | `[requires].python_version` | +| `pyproject.toml` | Python | Poetry/Modern | `[project].requires-python` | +| `go.mod` | Go | - | `go` directive line | +| `Gemfile` | Ruby | - | `ruby` directive or `.ruby-version` | +| `*.csproj` / `*.sln` | .NET | - | `` (e.g., net8.0 → 80) | +| `composer.json` | PHP | - | `require.php` field | +| `Cargo.toml` | Rust | - | Custom (no official S2I) | + +### Step 2: Map Version to Image + +| Language | Version Mapping | Image Pattern | +|----------|-----------------|---------------| +| Node.js | 18.x → 18, 20.x → 20, 22.x → 22 | `ubi9/nodejs-{major}` | +| Python | 3.9 → 39, 3.11 → 311, 3.12 → 312 | `ubi9/python-{majmin}` | +| Java | 11, 17, 21 (use nearest LTS) | `ubi9/openjdk-{version}` | +| Go | 1.21 → 1.21, 1.22 → 1.22 | `ubi9/go-toolset:{version}` | +| Ruby | 3.1 → 31, 3.3 → 33 | `ubi9/ruby-{majmin}` | +| .NET | net6.0 → 60, net8.0 → 80 | `ubi9/dotnet-{version}` | +| PHP | 8.0 → 80, 8.1 → 81 | `ubi9/php-{majmin}` | + +### Step 3: Verify and Fallback + +1. **Verify image exists**: `skopeo inspect docker://registry.access.redhat.com/ubi9/{image}` +2. **If version not found**: Use nearest available LTS version +3. **If no version in project**: Use current LTS (check catalog API) + +--- + +## Use-Case Aware Selection + +For advanced image selection based on use case, see the `/recommend-image` skill. + +### Quick Use-Case Matrix + +| Use Case | Variant | Priority | Example | +|----------|---------|----------|---------| +| Production | Minimal/Runtime | Security, Size | `nodejs-20-minimal` | +| Development | Full | Tools, Debug | `nodejs-20` | +| Serverless | Minimal | Startup Time | `openjdk-21-runtime` | +| Edge/IoT | Minimal | Size | `nodejs-20-minimal` | + +### Image Variants + +| Variant | Description | Has Build Tools | Size | +|---------|-------------|-----------------|------| +| Full | Complete development environment | Yes | Largest | +| Minimal | Essential packages only | Limited | Medium | +| Runtime | Runtime only, no build tools | No | Smallest | + +**Availability by language:** + +| Language | Full | Minimal | Runtime | +|----------|------|---------|---------| +| Node.js | `nodejs-{ver}` | `nodejs-{ver}-minimal` | - | +| Python | `python-{ver}` | - | - | +| Java | `openjdk-{ver}` | - | `openjdk-{ver}-runtime` | +| Go | `go-toolset:{ver}` | - | (produces static binary) | +| .NET | `dotnet-{ver}` | - | `dotnet-{ver}-runtime` | +| Ruby | `ruby-{ver}` | - | - | +| PHP | `php-{ver}` | - | - | + +### When to Recommend Each Variant + +**Full variant:** +- User needs to compile native extensions +- Development/debugging environment +- CI/CD build stages + +**Minimal variant:** +- Production deployments +- Security-focused environments +- When size matters but some tools needed + +**Runtime variant:** +- Pre-compiled applications (JARs, .NET assemblies) +- Maximum security posture +- Smallest possible footprint + +--- + +## Python S2I Entry Point Requirements + +**Quick reference:** +- Default entry point: `app.py` (works without configuration) +- Custom entry points require: `gunicorn` + `APP_MODULE` environment variable +- Format: `APP_MODULE=module:variable` (e.g., `main:app`) diff --git a/rh-developer/docs/builder-images.md b/rh-developer/docs/builder-images.md new file mode 100644 index 00000000..e36c2287 --- /dev/null +++ b/rh-developer/docs/builder-images.md @@ -0,0 +1,117 @@ +# S2I Builder Image Reference Tables + +> **Note:** Versions marked "Recommended" may change. Always verify with `skopeo inspect` before use. Prefer matching the project's version requirements over these defaults. + +For use-case-aware image selection, use the `/recommend-image` skill. + +## Red Hat UBI-based Images + +### Node.js + +| Version | Full Image | Minimal Image | Use Case | +|---------|------------|---------------|----------| +| 18 LTS | `registry.access.redhat.com/ubi9/nodejs-18` | `registry.access.redhat.com/ubi9/nodejs-18-minimal` | Long-term support | +| 20 LTS | `registry.access.redhat.com/ubi9/nodejs-20` | `registry.access.redhat.com/ubi9/nodejs-20-minimal` | **Recommended** | +| 22 | `registry.access.redhat.com/ubi9/nodejs-22` | `registry.access.redhat.com/ubi9/nodejs-22-minimal` | Current | + +**Choose minimal for:** Production, security-focused, smaller image size +**Choose full for:** Development, native module compilation + +### Python + +| Version | Image | Notes | +|---------|-------|-------| +| 3.9 | `registry.access.redhat.com/ubi9/python-39` | | +| 3.11 | `registry.access.redhat.com/ubi9/python-311` | **Recommended** | +| 3.12 | `registry.access.redhat.com/ubi9/python-312` | Latest | + +### Java / OpenJDK + +| Version | Build Image | Runtime Image | Notes | +|---------|-------------|---------------|-------| +| 11 LTS | `registry.access.redhat.com/ubi8/openjdk-11` | `registry.access.redhat.com/ubi8/openjdk-11-runtime` | LTS | +| 17 LTS | `registry.access.redhat.com/ubi9/openjdk-17` | `registry.access.redhat.com/ubi9/openjdk-17-runtime` | **Recommended** | +| 21 LTS | `registry.access.redhat.com/ubi9/openjdk-21` | `registry.access.redhat.com/ubi9/openjdk-21-runtime` | Latest LTS | + +**Choose runtime for:** Production with pre-built JARs, smallest footprint +**Choose build for:** S2I builds, Maven/Gradle compilation needed + +### Go + +| Version | Image | Notes | +|---------|-------|-------| +| 1.20 | `registry.access.redhat.com/ubi9/go-toolset:1.20` | | +| 1.21 | `registry.access.redhat.com/ubi9/go-toolset:1.21` | **Recommended** | + +### Ruby + +| Version | Image | Notes | +|---------|-------|-------| +| 3.1 | `registry.access.redhat.com/ubi9/ruby-31` | | +| 3.3 | `registry.access.redhat.com/ubi9/ruby-33` | **Recommended** | + +### .NET + +| Version | Build Image | Runtime Image | Notes | +|---------|-------------|---------------|-------| +| 6.0 LTS | `registry.access.redhat.com/ubi8/dotnet-60` | `registry.access.redhat.com/ubi8/dotnet-60-runtime` | LTS | +| 7.0 | `registry.access.redhat.com/ubi8/dotnet-70` | `registry.access.redhat.com/ubi8/dotnet-70-runtime` | | +| 8.0 LTS | `registry.access.redhat.com/ubi9/dotnet-80` | `registry.access.redhat.com/ubi9/dotnet-80-runtime` | **Recommended** | + +**Choose runtime for:** Production with pre-built assemblies +**Choose build for:** S2I builds, dotnet build/publish needed + +### PHP + +| Version | Image | Notes | +|---------|-------|-------| +| 8.0 | `registry.access.redhat.com/ubi9/php-80` | | +| 8.1 | `registry.access.redhat.com/ubi9/php-81` | **Recommended** | + +### Perl + +| Version | Image | Notes | +|---------|-------|-------| +| 5.32 | `registry.access.redhat.com/ubi9/perl-532` | | + +## OpenShift Built-in ImageStreams + +These are often pre-configured in OpenShift clusters under the `openshift` namespace: + +| ImageStream | Usage | +|-------------|-------| +| `nodejs:20-ubi9` | Node.js 20 on UBI 9 | +| `python:3.11-ubi9` | Python 3.11 on UBI 9 | +| `openjdk-17-ubi8` | Java 17 on UBI 8 | +| `ruby:3.1-ubi9` | Ruby 3.1 on UBI 9 | +| `php:8.0-ubi9` | PHP 8.0 on UBI 9 | + +When using OpenShift ImageStreams, reference them as: +```yaml +from: + kind: ImageStreamTag + namespace: openshift + name: nodejs:20-ubi9 +``` + +## Framework-Specific Recommendations + +### Quarkus (Java) +- **Native build**: `quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21` +- **JVM build**: `registry.access.redhat.com/ubi9/openjdk-21` + +### Spring Boot (Java) +- Use: `registry.access.redhat.com/ubi9/openjdk-17` or `openjdk-21` +- Ensure `spring-boot-maven-plugin` is configured for packaging + +### Next.js / React (Node.js) +- Use: `registry.access.redhat.com/ubi9/nodejs-20` +- Ensure build outputs to `build/` or `.next/` + +### Django / Flask (Python) +- Use: `registry.access.redhat.com/ubi9/python-311` +- Ensure `requirements.txt` or `Pipfile` exists at root + +### Express.js (Node.js) +- Use: `registry.access.redhat.com/ubi9/nodejs-18` or higher +- Ensure `npm start` script is defined in `package.json` diff --git a/rh-developer/docs/dynamic-validation.md b/rh-developer/docs/dynamic-validation.md new file mode 100644 index 00000000..a1b754c5 --- /dev/null +++ b/rh-developer/docs/dynamic-validation.md @@ -0,0 +1,245 @@ +# Dynamic Image Validation Reference + +This document provides detailed patterns for validating container images using Skopeo and the Red Hat Security Data API. + +## Skopeo Commands + +Skopeo inspects container images without downloading them, providing real-time metadata. + +### Prerequisites + +**Check if skopeo is installed:** +```bash +which skopeo +# or +skopeo --version +``` + +**Installation:** +| OS | Command | +|----|---------| +| Fedora/RHEL/CentOS | `sudo dnf install skopeo` | +| Ubuntu/Debian | `sudo apt install skopeo` | +| macOS (Homebrew) | `brew install skopeo` | + +### Basic Inspection + +```bash +# Inspect an image (full JSON output) +skopeo inspect docker://registry.access.redhat.com/ubi9/nodejs-20 + +# The docker:// transport is OCI-standard and works with all registries +# (Docker Hub, Red Hat, Quay, Podman registries, etc.) +``` + +### Extracting Specific Fields + +```bash +# Get creation date +skopeo inspect docker://registry.access.redhat.com/ubi9/nodejs-20 --format '{{.Created}}' + +# Get architecture +skopeo inspect docker://registry.access.redhat.com/ubi9/nodejs-20 --format '{{.Architecture}}' + +# Get all labels +skopeo inspect docker://registry.access.redhat.com/ubi9/nodejs-20 --format '{{.Labels}}' + +# Get specific label (e.g., version) +skopeo inspect docker://registry.access.redhat.com/ubi9/nodejs-20 --format '{{index .Labels "version"}}' + +# Get layer count +skopeo inspect docker://registry.access.redhat.com/ubi9/nodejs-20 --format '{{len .Layers}}' +``` + +### Listing Available Tags + +```bash +# List all tags for an image +skopeo list-tags docker://registry.access.redhat.com/ubi9/nodejs-20 + +# Output includes all available versions/tags +``` + +### Image Transport Options + +```bash +# Remote registry (most common) +skopeo inspect docker://registry.access.redhat.com/ubi9/nodejs-20 + +# Local Podman storage +skopeo inspect containers-storage:localhost/myimage:latest + +# OCI layout directory +skopeo inspect oci:/path/to/oci-layout:tag + +# Docker archive +skopeo inspect docker-archive:/path/to/image.tar +``` + +### Useful Metadata Fields + +| Field | Description | Use Case | +|-------|-------------|----------| +| `Created` | Image build timestamp | Freshness indicator | +| `Architecture` | CPU architecture | Verify ARM64/x86_64 support | +| `Os` | Operating system | Should be "linux" for UBI | +| `Labels` | Image labels (version, maintainer, etc.) | Verify language version | +| `Layers` | Layer digests | Calculate approximate size | +| `Digest` | Immutable image hash | Pin exact version | + +### Error Handling + +**Image not found:** +``` +Error: Error reading manifest: ... 404 Not Found +``` +→ Image does not exist at specified tag + +**Authentication required:** +``` +Error: Error reading manifest: unauthorized +``` +→ Private registry, need `skopeo login` first + +**Network error:** +``` +Error: Error initializing source: pinging container registry +``` +→ Network connectivity issue + +--- + +## Red Hat Security Data API + +The Security Data API provides CVE information without authentication. + +### Base Endpoint + +``` +https://access.redhat.com/hydra/rest/securitydata/ +``` + +### Query CVEs + +```bash +# Get all CVEs for UBI 9 (may return many results) +curl -s "https://access.redhat.com/hydra/rest/securitydata/cve.json?product=Red%20Hat%20Universal%20Base%20Image%209" + +# Filter by severity (critical, important, moderate, low) +curl -s "https://access.redhat.com/hydra/rest/securitydata/cve.json?product=Red%20Hat%20Universal%20Base%20Image%209&severity=critical" + +# Filter by date (CVEs after a specific date) +curl -s "https://access.redhat.com/hydra/rest/securitydata/cve.json?product=Red%20Hat%20Universal%20Base%20Image%209&after=2025-01-01" + +# Count critical CVEs +curl -s "https://access.redhat.com/hydra/rest/securitydata/cve.json?product=Red%20Hat%20Universal%20Base%20Image%209&severity=critical" | jq 'length' +``` + +### Product Names for Queries + +| Image Base | Product Name (URL-encoded) | +|------------|---------------------------| +| UBI 9 | `Red%20Hat%20Universal%20Base%20Image%209` | +| UBI 8 | `Red%20Hat%20Universal%20Base%20Image%208` | +| RHEL 9 | `Red%20Hat%20Enterprise%20Linux%209` | +| RHEL 8 | `Red%20Hat%20Enterprise%20Linux%208` | + +### Response Fields + +Each CVE object contains: + +| Field | Description | +|-------|-------------| +| `CVE` | CVE identifier (e.g., CVE-2024-1234) | +| `severity` | critical, important, moderate, low | +| `public_date` | When CVE was disclosed | +| `advisories` | Related Red Hat advisories | +| `bugzilla` | Bugzilla tracking URL | +| `affected_packages` | Packages affected by CVE | + +### Parsing Examples + +```bash +# Get CVE IDs and severities +curl -s "https://access.redhat.com/hydra/rest/securitydata/cve.json?product=Red%20Hat%20Universal%20Base%20Image%209&severity=critical" | jq '.[] | {cve: .CVE, severity: .severity}' + +# Get most recent CVE date +curl -s "https://access.redhat.com/hydra/rest/securitydata/cve.json?product=Red%20Hat%20Universal%20Base%20Image%209&severity=critical" | jq '.[0].public_date' + +# Check if any critical CVEs exist +CRITICAL_COUNT=$(curl -s "https://access.redhat.com/hydra/rest/securitydata/cve.json?product=Red%20Hat%20Universal%20Base%20Image%209&severity=critical" | jq 'length') +if [ "$CRITICAL_COUNT" -gt 0 ]; then + echo "Warning: $CRITICAL_COUNT critical CVEs found" +fi +``` + +--- + +## Validation Workflow + +### Complete Validation Sequence + +``` +1. Check if skopeo is installed + ├── Yes → Continue to step 2 + └── No → Prompt user to install, offer to continue with static data + +2. For each candidate image: + a. Run: skopeo inspect docker://registry.access.redhat.com/ubi9/[image] + b. If fails → Remove from candidates, try next + c. If succeeds → Extract: Created, Architecture, Labels + +3. Query Security Data API for UBI base: + a. Run: curl CVE query for critical severity + b. Parse count of critical CVEs + c. If count > 0 → Add warning to recommendation + +4. Compile results: + - Image metadata (from skopeo) + - Security status (from API) + - Static scoring data (from reference tables) + +5. Present recommendation with sources indicated +``` + +### Fallback Behavior + +| Scenario | Action | +|----------|--------| +| Skopeo not installed | Prompt installation, offer static-only mode | +| Skopeo command fails | Note "unable to verify", use static data | +| Security API unavailable | Note "security not verified", proceed | +| Image not found | Remove from candidates, suggest alternatives | +| Network offline | Use static data only, note limitations | + +--- + +## Integration with Recommendation Output + +### When Dynamic Data Available + +```markdown +| Property | Value | Source | +|----------|-------|--------| +| Size | 147 MB | Skopeo | +| Built | 2026-01-28 | Skopeo | +| Architecture | amd64, arm64 | Skopeo | + +**Security Status:** No critical CVEs +- Last checked: 2026-02-03 +- Source: Red Hat Security Data API +``` + +### When Dynamic Data Unavailable + +```markdown +| Property | Value | Source | +|----------|-------|--------| +| Size | ~150 MB (estimate) | Static | +| Built | Unknown | - | +| Architecture | Assumed amd64 | Static | + +**Security Status:** Not verified (warning) +- Skopeo not installed - install for accurate metadata +- Run: `sudo dnf install skopeo` +``` diff --git a/rh-developer/docs/image-selection-criteria.md b/rh-developer/docs/image-selection-criteria.md new file mode 100644 index 00000000..6c4cb1a0 --- /dev/null +++ b/rh-developer/docs/image-selection-criteria.md @@ -0,0 +1,207 @@ +# Image Selection Criteria Reference + +This document provides detailed criteria for selecting the optimal container image based on use case requirements. + +## Scoring Matrix + +Use this matrix to score image options based on user requirements. + +### Criteria Weights by Environment + +| Criteria | Production | Development | Edge/IoT | Serverless | +|----------|------------|-------------|----------|------------| +| Image Size | 3 | 1 | 5 | 4 | +| Security Posture | 5 | 2 | 4 | 3 | +| Build Tools | 1 | 5 | 1 | 1 | +| Startup Time | 3 | 1 | 3 | 5 | +| LTS Status | 5 | 2 | 4 | 3 | +| Debug Tools | 1 | 5 | 1 | 1 | + +**Scale:** 1 (low importance) to 5 (high importance) + +### Image Variant Scores + +| Variant | Size | Security | Build Tools | Startup | Debug | +|---------|------|----------|-------------|---------|-------| +| Full | 2 | 2 | 5 | 2 | 5 | +| Minimal | 4 | 4 | 2 | 4 | 2 | +| Runtime | 5 | 5 | 1 | 5 | 1 | + +**Scale:** 1 (poor) to 5 (excellent) + +## Image Size Reference + +Approximate compressed image sizes: + +### Node.js +| Image | Size | +|-------|------| +| `ubi9/nodejs-20` | ~250MB | +| `ubi9/nodejs-20-minimal` | ~150MB | + +### Python +| Image | Size | +|-------|------| +| `ubi9/python-311` | ~280MB | + +### Java +| Image | Size | +|-------|------| +| `ubi9/openjdk-17` | ~400MB | +| `ubi9/openjdk-17-runtime` | ~200MB | + +### Go +| Image | Size | +|-------|------| +| `ubi9/go-toolset:1.21` | ~500MB | +| Final binary | ~10-50MB | + +### .NET +| Image | Size | +|-------|------| +| `ubi9/dotnet-80` | ~350MB | +| `ubi9/dotnet-80-runtime` | ~150MB | + +## LTS Support Timeline + +### Node.js +| Version | Status | End of Life | +|---------|--------|-------------| +| 18 LTS | Active | April 2025 | +| 20 LTS | Active | April 2026 | +| 22 LTS | Active | April 2027 | + +### Python +| Version | Status | End of Life | +|---------|--------|-------------| +| 3.9 | Security | October 2025 | +| 3.11 | Active | October 2027 | +| 3.12 | Active | October 2028 | + +### Java (OpenJDK) +| Version | Status | Extended Support | +|---------|--------|------------------| +| 11 LTS | Active | Red Hat until 2027 | +| 17 LTS | Active | Red Hat until 2029 | +| 21 LTS | Active | Red Hat until 2031 | + +### .NET +| Version | Status | End of Life | +|---------|--------|-------------| +| 6.0 LTS | Active | November 2024 | +| 8.0 LTS | Active | November 2026 | + +## Security Considerations + +### Minimal Images - When to Use +- Fewer installed packages = smaller attack surface +- Recommended for production workloads +- May lack debugging tools when issues occur + +### Full Images - When to Use +- Include development tools (gcc, make, etc.) +- Needed for native extensions (Python C extensions, Node native modules) +- Better for development and debugging + +### Runtime Images - When to Use +- No build tools at all +- Smallest possible footprint +- Requires pre-compiled application (JAR, static binary) + +## Framework-Specific Considerations + +### Quarkus (Java) +**For JVM mode:** +- Use `ubi9/openjdk-21` for build +- Use `ubi9/openjdk-21-runtime` for production + +**For Native mode:** +- Build: `quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21` +- Run: `quay.io/quarkus/quarkus-micro-image:2.0` +- Dramatically faster startup (~50ms vs ~2s) + +### Spring Boot (Java) +**Standard:** +- Build and run: `ubi9/openjdk-17` + +**Optimized production:** +- Build with layered JAR: `spring-boot-maven-plugin` with layers +- Run on: `ubi9/openjdk-17-runtime` + +### Next.js (Node.js) +**Development:** +- Use `ubi9/nodejs-20` + +**Production (multi-stage recommended):** +1. Build stage: `ubi9/nodejs-20` +2. Run stage: `ubi9/nodejs-20-minimal` with `.next` output + +### Django/Flask (Python) +- Always use full image (may need compilation for dependencies) +- `ubi9/python-311` recommended +- Consider `gunicorn` for production + +## Decision Tree + +``` +START + | + v +Is this production? + | + +-- YES --> Need native compilation? + | | + | +-- YES --> Use FULL variant + | | + | +-- NO --> Is app pre-compiled? + | | + | +-- YES --> Use RUNTIME variant + | | + | +-- NO --> Use MINIMAL variant + | + +-- NO (Development) --> Use FULL variant +``` + +## Multi-Stage Build Recommendations + +For optimal production images, consider multi-stage builds: + +### Node.js Example +```dockerfile +# Build stage +FROM registry.access.redhat.com/ubi9/nodejs-20 AS builder +COPY . . +RUN npm ci && npm run build + +# Production stage +FROM registry.access.redhat.com/ubi9/nodejs-20-minimal +COPY --from=builder /app/dist /app +CMD ["node", "/app/index.js"] +``` + +### Java Example +```dockerfile +# Build stage +FROM registry.access.redhat.com/ubi9/openjdk-21 AS builder +COPY . . +RUN mvn package -DskipTests + +# Production stage +FROM registry.access.redhat.com/ubi9/openjdk-21-runtime +COPY --from=builder /app/target/*.jar /app/app.jar +CMD ["java", "-jar", "/app/app.jar"] +``` + +### Go Example +Go produces static binaries, so minimal base is ideal: +```dockerfile +# Build stage +FROM registry.access.redhat.com/ubi9/go-toolset:1.21 AS builder +COPY . . +RUN go build -o /app/server + +# Production stage +FROM registry.access.redhat.com/ubi9/ubi-micro +COPY --from=builder /app/server /server +CMD ["/server"] +``` diff --git a/rh-developer/docs/python-s2i-entrypoints.md b/rh-developer/docs/python-s2i-entrypoints.md new file mode 100644 index 00000000..015bb58f --- /dev/null +++ b/rh-developer/docs/python-s2i-entrypoints.md @@ -0,0 +1,56 @@ +# Python S2I Entry Point Requirements + +The UBI Python S2I builder has specific startup logic that must be understood to avoid deployment failures. + +## How the S2I Python Run Script Works + +The S2I Python builder uses this startup logic (in order): + +1. If `app.sh` exists → Execute it directly +2. If `gunicorn` is installed AND `APP_MODULE` is set → Start with gunicorn +3. If `app.py` exists → Run with Python directly +4. Otherwise → **ERROR: No start command found** + +## Entry Point Configuration Matrix + +| Entry Point File | gunicorn in requirements | Configuration Needed | Result | +|------------------|--------------------------|----------------------|--------| +| `app.py` | No | None | Works (Python direct) | +| `app.py` | Yes | None (optional APP_MODULE) | Works | +| `main.py` | **No** | - | **FAILS** | +| `main.py` | Yes | `APP_MODULE=main:app` | Works | +| `wsgi.py` | Yes | `APP_MODULE=wsgi` or `APP_MODULE=wsgi:application` | Works | +| Custom file | Yes | `APP_MODULE=[module]:[variable]` | Works | + +## APP_MODULE Format + +- **Format:** `[python_module]:[flask_app_variable]` +- **Example:** `main:app` → imports `app` from `main.py` +- **Requires:** `gunicorn` in `requirements.txt` + +### Common Patterns + +| File | Typical APP_MODULE | +|------|-------------------| +| `main.py` with `app = Flask(__name__)` | `main:app` | +| `main.py` with `application = Flask(__name__)` | `main:application` | +| `wsgi.py` with `application` | `wsgi:application` or just `wsgi` | +| `src/app.py` with `app` | `src.app:app` | + +## Alternative: APP_FILE + +- Set `APP_FILE=main.py` to run with Python directly (development mode) +- **Not recommended for production** (no WSGI server, no worker management) +- Use only if gunicorn is not an option + +## Critical Warning + +**If the entry point is NOT `app.py` and `gunicorn` is NOT installed:** +- The S2I build will succeed (dependencies install) +- The container will **fail to start** with "No start command found" +- This is a **runtime failure**, not a build failure + +**Always verify:** +1. Entry point file name +2. `gunicorn` in requirements.txt +3. `APP_MODULE` environment variable in BuildConfig diff --git a/rh-developer/docs/rhel-deployment.md b/rh-developer/docs/rhel-deployment.md new file mode 100644 index 00000000..36f98486 --- /dev/null +++ b/rh-developer/docs/rhel-deployment.md @@ -0,0 +1,562 @@ +# RHEL Deployment Reference + +Reference material for deploying applications to standalone RHEL systems. + +## Table of Contents + +1. [RHEL Version Compatibility](#rhel-version-compatibility) +2. [Systemd Unit Templates](#systemd-unit-templates) +3. [SELinux Configuration](#selinux-configuration) +4. [Firewall Commands](#firewall-commands) +5. [SSH Connection Patterns](#ssh-connection-patterns) +6. [Runtime Package Mapping](#runtime-package-mapping) + +--- + +## RHEL Version Compatibility + +| Distribution | Version | Podman | Recommended | +|--------------|---------|--------|-------------| +| RHEL | 8.x | 4.0+ | Production ready | +| RHEL | 9.x | 4.4+ | **Recommended** | +| CentOS Stream | 8 | 4.0+ | Development | +| CentOS Stream | 9 | 4.4+ | Development | +| Rocky Linux | 8.x | 4.0+ | Production ready | +| Rocky Linux | 9.x | 4.4+ | Production ready | +| AlmaLinux | 8.x | 4.0+ | Production ready | +| AlmaLinux | 9.x | 4.4+ | Production ready | +| Fedora | 38+ | 4.6+ | Latest features | + +### Version Detection Commands + +```bash +# Get RHEL/CentOS version +cat /etc/redhat-release + +# Get detailed OS info +cat /etc/os-release + +# Check architecture +uname -m + +# Check kernel version +uname -r +``` + +--- + +## Systemd Unit Templates + +### Podman Container Service (Rootful) + +```ini +[Unit] +Description=${APP_NAME} Container +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +Restart=always +RestartSec=5 +TimeoutStartSec=300 +TimeoutStopSec=70 + +# Pre-start: ensure clean state +ExecStartPre=-/usr/bin/podman stop -t 10 ${APP_NAME} +ExecStartPre=-/usr/bin/podman rm ${APP_NAME} + +# Main container run +ExecStart=/usr/bin/podman run \ + --name ${APP_NAME} \ + -p ${HOST_PORT}:${CONTAINER_PORT} \ + --rm \ + ${IMAGE} + +# Stop container gracefully +ExecStop=/usr/bin/podman stop -t 10 ${APP_NAME} + +[Install] +WantedBy=multi-user.target +``` + +### Podman Container Service (Rootless) + +```ini +[Unit] +Description=${APP_NAME} Container (Rootless) +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +Restart=always +RestartSec=5 +TimeoutStartSec=300 +TimeoutStopSec=70 + +ExecStartPre=-/usr/bin/podman stop -t 10 ${APP_NAME} +ExecStartPre=-/usr/bin/podman rm ${APP_NAME} +ExecStart=/usr/bin/podman run \ + --name ${APP_NAME} \ + -p ${HOST_PORT}:${CONTAINER_PORT} \ + --rm \ + ${IMAGE} +ExecStop=/usr/bin/podman stop -t 10 ${APP_NAME} + +[Install] +WantedBy=default.target +``` + +**Rootless setup commands:** +```bash +# Create user systemd directory +mkdir -p ~/.config/systemd/user + +# Place unit file +cp ${APP_NAME}.service ~/.config/systemd/user/ + +# Reload and enable +systemctl --user daemon-reload +systemctl --user enable --now ${APP_NAME} + +# Keep services running after logout +loginctl enable-linger ${USER} +``` + +### Podman Container with Volumes + +```ini +[Unit] +Description=${APP_NAME} Container with Persistent Data +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +Restart=always +RestartSec=5 + +ExecStartPre=-/usr/bin/podman stop -t 10 ${APP_NAME} +ExecStartPre=-/usr/bin/podman rm ${APP_NAME} +ExecStart=/usr/bin/podman run \ + --name ${APP_NAME} \ + -p ${HOST_PORT}:${CONTAINER_PORT} \ + -v /var/lib/${APP_NAME}/data:/app/data:z \ + -e DATABASE_URL=${DATABASE_URL} \ + --rm \ + ${IMAGE} +ExecStop=/usr/bin/podman stop -t 10 ${APP_NAME} + +[Install] +WantedBy=multi-user.target +``` + +### Native Node.js Application + +```ini +[Unit] +Description=${APP_NAME} Node.js Service +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=${SERVICE_USER} +WorkingDirectory=/opt/${APP_NAME} +Environment=NODE_ENV=production +Environment=PORT=${PORT} +ExecStart=/usr/bin/node /opt/${APP_NAME}/server.js +Restart=always +RestartSec=5 + +# Security hardening +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +PrivateTmp=true +ReadWritePaths=/opt/${APP_NAME} + +[Install] +WantedBy=multi-user.target +``` + +### Native Python Application + +```ini +[Unit] +Description=${APP_NAME} Python Service +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=${SERVICE_USER} +WorkingDirectory=/opt/${APP_NAME} +Environment=PYTHONUNBUFFERED=1 +Environment=PORT=${PORT} +ExecStart=/usr/bin/python3 /opt/${APP_NAME}/app.py +Restart=always +RestartSec=5 + +# Security hardening +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +PrivateTmp=true +ReadWritePaths=/opt/${APP_NAME} + +[Install] +WantedBy=multi-user.target +``` + +### Native Java Application + +```ini +[Unit] +Description=${APP_NAME} Java Service +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=${SERVICE_USER} +WorkingDirectory=/opt/${APP_NAME} +Environment=JAVA_OPTS=-Xmx512m +ExecStart=/usr/bin/java -jar /opt/${APP_NAME}/app.jar --server.port=${PORT} +Restart=always +RestartSec=5 +SuccessExitStatus=143 + +# Security hardening +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +PrivateTmp=true +ReadWritePaths=/opt/${APP_NAME} + +[Install] +WantedBy=multi-user.target +``` + +### Native Go Application + +```ini +[Unit] +Description=${APP_NAME} Go Service +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=${SERVICE_USER} +WorkingDirectory=/opt/${APP_NAME} +Environment=PORT=${PORT} +ExecStart=/opt/${APP_NAME}/${BINARY_NAME} +Restart=always +RestartSec=5 + +# Security hardening +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +PrivateTmp=true + +[Install] +WantedBy=multi-user.target +``` + +--- + +## SELinux Configuration + +### Common SELinux Contexts + +| Context Type | Use Case | +|--------------|----------| +| `container_t` | Standard Podman container processes | +| `container_file_t` | Container data files | +| `bin_t` | Executable binaries | +| `httpd_sys_content_t` | Web application content (read-only) | +| `httpd_sys_rw_content_t` | Web application content (read-write) | +| `var_lib_t` | Application data in /var/lib | + +### Volume Label Options for Podman + +| Option | Description | Use Case | +|--------|-------------|----------| +| `:z` | Shared volume label | Volume accessed by multiple containers | +| `:Z` | Private volume label | Volume accessed by single container only | + +Example: +```bash +podman run -v /data/shared:/app/shared:z myimage # Shared +podman run -v /data/private:/app/data:Z myimage # Private +``` + +### SELinux Commands + +```bash +# Check current SELinux mode +getenforce + +# View file context +ls -Z /path/to/file + +# Set context for application directory +sudo semanage fcontext -a -t bin_t "/opt/myapp(/.*)?" +sudo restorecon -Rv /opt/myapp + +# Set context for web content +sudo semanage fcontext -a -t httpd_sys_content_t "/opt/myapp/public(/.*)?" +sudo restorecon -Rv /opt/myapp/public + +# Allow non-standard port for HTTP +sudo semanage port -a -t http_port_t -p tcp 8080 + +# View port contexts +sudo semanage port -l | grep http + +# Check for SELinux denials +sudo ausearch -m AVC -ts recent + +# Generate policy from denials (troubleshooting) +sudo ausearch -m AVC -ts recent | audit2allow -M mypolicy +sudo semodule -i mypolicy.pp + +# Temporarily set permissive (for debugging only) +sudo setenforce 0 +``` + +### Common SELinux Booleans + +```bash +# Allow HTTP to connect to network (for proxy/API calls) +sudo setsebool -P httpd_can_network_connect 1 + +# Allow HTTP to connect to databases +sudo setsebool -P httpd_can_network_connect_db 1 + +# List all HTTP-related booleans +getsebool -a | grep httpd +``` + +--- + +## Firewall Commands + +### Basic Port Management + +```bash +# Check firewall status +sudo firewall-cmd --state + +# List all open ports +sudo firewall-cmd --list-ports + +# List all services +sudo firewall-cmd --list-services + +# Open port permanently +sudo firewall-cmd --permanent --add-port=8080/tcp + +# Open port temporarily (until reload) +sudo firewall-cmd --add-port=8080/tcp + +# Reload firewall to apply permanent changes +sudo firewall-cmd --reload + +# Remove port +sudo firewall-cmd --permanent --remove-port=8080/tcp +sudo firewall-cmd --reload +``` + +### Service-Based Management + +```bash +# Add HTTP service +sudo firewall-cmd --permanent --add-service=http + +# Add HTTPS service +sudo firewall-cmd --permanent --add-service=https + +# Remove service +sudo firewall-cmd --permanent --remove-service=http + +# Apply changes +sudo firewall-cmd --reload +``` + +### Zone Management + +```bash +# List zones +sudo firewall-cmd --get-zones + +# Get active zone +sudo firewall-cmd --get-active-zones + +# Add port to specific zone +sudo firewall-cmd --zone=public --permanent --add-port=8080/tcp + +# Set default zone +sudo firewall-cmd --set-default-zone=public +``` + +### Rich Rules (Advanced) + +```bash +# Allow specific IP to access port +sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="8080" accept' + +# Rate limiting +sudo firewall-cmd --permanent --add-rich-rule='rule service name="http" limit value="10/m" accept' + +# Apply changes +sudo firewall-cmd --reload +``` + +--- + +## SSH Connection Patterns + +### Test Connection + +```bash +# Basic connection test +ssh -o BatchMode=yes -o ConnectTimeout=10 user@host "echo 'OK'" + +# Verbose output for debugging +ssh -v user@host + +# Test with specific key +ssh -i ~/.ssh/mykey user@host "echo 'OK'" +``` + +### Execute Remote Commands + +```bash +# Single command +ssh user@host "command" + +# Multiple commands +ssh user@host "cmd1 && cmd2 && cmd3" + +# With sudo +ssh user@host "sudo command" + +# Preserve environment +ssh user@host 'bash -l -c "command"' +``` + +### File Transfer + +```bash +# Copy file to remote +scp local_file user@host:/remote/path/ + +# Copy directory recursively +scp -r local_dir user@host:/remote/path/ + +# Using rsync (preferred for large transfers) +rsync -avz --progress local_dir/ user@host:/remote/path/ + +# Exclude patterns +rsync -avz --exclude 'node_modules' --exclude '.git' ./ user@host:/remote/path/ +``` + +### SSH Config for Convenience + +``` +# ~/.ssh/config +Host myrhel + HostName 192.168.1.100 + User deploy + Port 22 + IdentityFile ~/.ssh/id_rsa + StrictHostKeyChecking accept-new +``` + +Usage: `ssh myrhel "command"` + +--- + +## Runtime Package Mapping + +### Node.js + +| Version | RHEL 8 | RHEL 9 | +|---------|--------|--------| +| 18 | `dnf module enable nodejs:18 && dnf install -y nodejs npm` | `dnf install -y nodejs npm` | +| 20 | `dnf module enable nodejs:20 && dnf install -y nodejs npm` | `dnf module enable nodejs:20 && dnf install -y nodejs npm` | + +### Python + +| Version | RHEL 8 | RHEL 9 | +|---------|--------|--------| +| 3.8 | `dnf install -y python38 python38-pip` | N/A | +| 3.9 | `dnf install -y python39 python39-pip` | `dnf install -y python3 python3-pip` | +| 3.11 | N/A | `dnf install -y python3.11 python3.11-pip` | +| 3.12 | N/A | `dnf install -y python3.12 python3.12-pip` | + +### Java + +| Version | RHEL 8 | RHEL 9 | +|---------|--------|--------| +| 11 | `dnf install -y java-11-openjdk java-11-openjdk-devel` | `dnf install -y java-11-openjdk java-11-openjdk-devel` | +| 17 | `dnf install -y java-17-openjdk java-17-openjdk-devel` | `dnf install -y java-17-openjdk java-17-openjdk-devel` | +| 21 | N/A | `dnf install -y java-21-openjdk java-21-openjdk-devel` | + +### Go + +| Version | RHEL 8 | RHEL 9 | +|---------|--------|--------| +| 1.20+ | `dnf install -y go-toolset` | `dnf install -y golang` | + +### Ruby + +| Version | RHEL 8 | RHEL 9 | +|---------|--------|--------| +| 3.0 | `dnf module enable ruby:3.0 && dnf install -y ruby ruby-devel` | `dnf install -y ruby ruby-devel` | +| 3.1 | `dnf module enable ruby:3.1 && dnf install -y ruby ruby-devel` | `dnf module enable ruby:3.1 && dnf install -y ruby ruby-devel` | + +### PHP + +| Version | RHEL 8 | RHEL 9 | +|---------|--------|--------| +| 7.4 | `dnf module enable php:7.4 && dnf install -y php php-cli php-fpm` | N/A | +| 8.0 | `dnf module enable php:8.0 && dnf install -y php php-cli php-fpm` | `dnf install -y php php-cli php-fpm` | +| 8.1 | N/A | `dnf module enable php:8.1 && dnf install -y php php-cli php-fpm` | + +### Module Stream Commands + +```bash +# List available streams for a module +dnf module list nodejs + +# Enable specific stream +sudo dnf module enable nodejs:20 + +# Reset module (to switch streams) +sudo dnf module reset nodejs + +# Install from enabled stream +sudo dnf install -y nodejs npm +``` + +--- + +## Service User Creation + +For running applications as non-root: + +```bash +# Create system user for the application +sudo useradd -r -s /sbin/nologin -d /opt/myapp myapp + +# Set ownership +sudo chown -R myapp:myapp /opt/myapp + +# Allow user to bind to privileged port (if needed) +sudo setcap 'cap_net_bind_service=+ep' /opt/myapp/binary +``` diff --git a/rh-developer/docs/session-state-patterns.md b/rh-developer/docs/session-state-patterns.md new file mode 100644 index 00000000..28fee1be --- /dev/null +++ b/rh-developer/docs/session-state-patterns.md @@ -0,0 +1,162 @@ +# Session State Patterns + +This reference defines the state management patterns used across deployment skills. + +## Common State Properties + +All deployment workflows share these core state properties: + +``` +COMMON_STATE = { + phase: string, // Current workflow phase + + // Project detection + app_name: string, + language: string, + framework: string, + version: string, + container_port: number, + + // Build configuration + builder_image: string, + build_strategy: "Source" | "Podman", + + // Tracking + created_resources: [ + { type: string, name: string, path?: string, status?: string } + ] +} +``` + +## OpenShift Deployment State + +For `/containerize-deploy` and `/s2i-build` workflows: + +``` +OPENSHIFT_STATE = { + ...COMMON_STATE, + + phase: "intro" | "detect" | "target" | "strategy" | "image-select" | + "connect" | "helm" | "git" | "pre-build" | "build" | + "pre-deploy" | "deploy" | "complete", + + // Target + deployment_target: "openshift" | "rhel", + deployment_strategy: "S2I" | "Podman" | "Helm", + + // Cluster connection + cluster: string, + namespace: string, + user: string, + + // Git source + git_url: string, + git_branch: string, + + // Image selection + image_variant: "full" | "minimal" | "runtime", + selection_rationale: string, + + // Helm (if applicable) + helm_chart_detected: boolean, + helm_chart_path: string, + helm_chart_name: string, + helm_chart_version: string, + helm_release_name: string, + helm_release_revision: number, + + // Deployment config + replicas: number, + create_route: boolean, + + // Results + build_name: string, + route_host: string +} +``` + +## RHEL Deployment State + +For `/rhel-deploy` workflows: + +``` +RHEL_STATE = { + ...COMMON_STATE, + + phase: "intro" | "ssh" | "analyze" | "strategy" | + "container-*" | "native-*" | "complete", + + // SSH connection + rhel_host: string, + rhel_user: string, + rhel_port: number, // default: 22 + + // Target analysis + rhel_version: string, // e.g., "RHEL 9.3" + rhel_arch: string, // e.g., "x86_64" + podman_available: boolean, + podman_version: string, + selinux_status: "enforcing" | "permissive" | "disabled", + firewall_status: "active" | "inactive", + + // Deployment strategy + deployment_strategy: "container" | "native", + + // Container-specific + container_mode: "rootless" | "rootful", + container_name: string, + container_image: string, + + // Native-specific + app_install_path: string, // e.g., "/opt/[app-name]" + service_user: string, + + // Common service + systemd_unit_name: string, + exposed_port: number +} +``` + +## State Transitions + +### OpenShift Path +``` +intro → detect → target → strategy → image-select → connect → + ├── (S2I/Podman) → git → pre-build → build → pre-deploy → deploy → complete + └── (Helm) → helm → complete +``` + +### RHEL Path +``` +intro → ssh → analyze → strategy → + ├── (Container) → container-image → container-config → container-systemd → firewall → complete + └── (Native) → native-deps → native-deploy → native-systemd → firewall → complete +``` + +## Resource Tracking + +Track created resources for rollback support: + +```javascript +created_resources: [ + { type: "file", path: "/etc/systemd/system/app.service" }, + { type: "service", name: "app.service" }, + { type: "firewall_rule", port: 8080 }, + { type: "selinux_context", path: "/opt/app" }, + { kind: "Deployment", name: "app", namespace: "default" }, + { kind: "Service", name: "app", namespace: "default" }, + { kind: "Route", name: "app", namespace: "default" } +] +``` + +## Passing State Between Skills + +When delegating between skills, pass these values: + +| From | To | Values Passed | +|------|-----|---------------| +| `/containerize-deploy` | `/detect-project` | (none - detect provides values) | +| `/detect-project` | `/recommend-image` | `LANGUAGE`, `FRAMEWORK`, `VERSION` | +| `/recommend-image` | caller | `BUILDER_IMAGE`, `IMAGE_VARIANT`, `SELECTION_RATIONALE` | +| `/containerize-deploy` | `/rhel-deploy` | `APP_NAME`, `LANGUAGE`, `FRAMEWORK`, `VERSION`, `BUILDER_IMAGE`, `CONTAINER_PORT` | +| `/containerize-deploy` | `/helm-deploy` | `APP_NAME`, `NAMESPACE`, `HELM_CHART_PATH` | diff --git a/rh-developer/skills/containerize-deploy/SKILL.md b/rh-developer/skills/containerize-deploy/SKILL.md new file mode 100644 index 00000000..68af1b63 --- /dev/null +++ b/rh-developer/skills/containerize-deploy/SKILL.md @@ -0,0 +1,487 @@ +--- +name: containerize-deploy +description: | + Complete end-to-end workflow for containerizing and deploying applications to OpenShift or standalone RHEL systems. Orchestrates /detect-project, /s2i-build, /deploy, /helm-deploy, and /rhel-deploy skills with user confirmation checkpoints at each phase. Supports S2I, Podman, Helm deployment strategies for OpenShift, and Podman/native deployments for RHEL hosts. Use this skill when user wants to go from source code to running application in one guided workflow. Supports resume after interruption and rollback on failure. Triggers on /containerize-deploy command. +--- + +# /containerize-deploy Skill + +Provide a complete, guided workflow from local source code to running application on OpenShift or standalone RHEL systems. This skill orchestrates `/detect-project`, `/s2i-build`, `/deploy`, `/helm-deploy`, and `/rhel-deploy` with clear user checkpoints at each phase. + +## Overview + +``` +[Intro] → [Detect] → [Target] → [Strategy] ──┬─→ [OpenShift Path] ─────┬─→ [Complete] + │ │ [S2I/Podman/Helm] │ + │ │ │ + │ └─→ [RHEL Path] ───────────┘ + │ (/rhel-deploy) + │ + User chooses target: + - OpenShift → Strategy selection (S2I/Podman/Helm) + - RHEL → Delegate to /rhel-deploy skill +``` + +**Deployment Targets:** +- **OpenShift** - Deploy to OpenShift/Kubernetes cluster (S2I, Podman, or Helm strategies) +- **RHEL Host** - Deploy to standalone RHEL system via SSH (delegates to /rhel-deploy) + +**OpenShift Deployment Strategies (if OpenShift target selected):** +- **S2I** - Source-to-Image build on OpenShift, then deploy (Phases 3-7) +- **Podman** - Build from Containerfile/Dockerfile on OpenShift, then deploy (Phases 3-7) +- **Helm** - Deploy using Helm chart (Phase 2-H) + +## Workflow + +### Phase 0: Introduction + +```markdown +# Containerize & Deploy + +I'll help you containerize and deploy your application to OpenShift or a standalone RHEL system. + +**What this workflow does:** +1. **Detect** - Analyze your project and determine the best deployment strategy +2. **Choose Target** - Deploy to OpenShift cluster or RHEL host +3. **Build** - Build container image (S2I or Podman) or skip if using Helm with existing image +4. **Deploy** - Deploy using Kubernetes resources, Helm chart, or systemd services + +**Deployment Targets:** +- **OpenShift** - Deploy to OpenShift/Kubernetes cluster +- **RHEL Host** - Deploy directly to a RHEL system via SSH + +**OpenShift Strategies:** +- **S2I (Source-to-Image)** - Build and deploy from source code +- **Podman** - Build from existing Containerfile/Dockerfile +- **Helm** - Deploy using Helm chart + +**RHEL Strategies:** +- **Container** - Run with Podman + systemd +- **Native** - Install with dnf + systemd + +**What I need from you:** +- Confirmation at each step before I make changes +- Access to an OpenShift cluster OR SSH access to a RHEL host +- Your project source code + +**Ready to begin?** (yes/no) +``` + +Wait for user confirmation before proceeding. + +### Phase 1: Project Detection + +Execute the `/detect-project` workflow. + +**If Remote URL provided:** +Follow the "Remote Repository Strategy" path in `/detect-project`. +- Ask user to choose: Remote S2I, Remote Podman, or Clone. + +**If Local Files:** +Proceed with standard detection. + +```markdown +## Phase 1: Analyzing Your Project + +[If Local] +Scanning project directory for language indicators... + +[If Remote] +Analyzing remote repository options... + +... +``` + +Store confirmed values in session state, including `BUILD_STRATEGY` and `HELM_CHART_DETECTED`. + +### Phase 1.4: Deployment Target Selection + +```markdown +## Deployment Target + +Where would you like to deploy this application? + +| Target | Description | Requirements | +|--------|-------------|--------------| +| **OpenShift** | Deploy to OpenShift/Kubernetes cluster | `oc login` access | +| **RHEL Host** | Deploy directly to a standalone RHEL system | SSH access to RHEL 8+ | + +**Which target would you like to use?** +1. OpenShift - Deploy to current cluster +2. RHEL - Deploy to a RHEL host via SSH +``` + +Store `DEPLOYMENT_TARGET` in session state. + +**If user selects "RHEL":** +- Store `DEPLOYMENT_TARGET = "rhel"` in session state +- Delegate to `/rhel-deploy` skill with detected project info +- Pass: `APP_NAME`, `LANGUAGE`, `FRAMEWORK`, `VERSION`, `BUILDER_IMAGE`, `CONTAINER_PORT` +- The `/rhel-deploy` skill handles SSH connection, deployment strategy, and service creation +- After `/rhel-deploy` completes → Go to **Phase 8 (Completion)** + +**If user selects "OpenShift":** +- Store `DEPLOYMENT_TARGET = "openshift"` in session state +- Continue to Phase 1.5 (Strategy Selection) + +### Phase 1.5: Strategy Selection + +If multiple deployment options are available (Helm chart detected, Dockerfile present, or standard project): + +```markdown +## Deployment Strategy + +Based on my analysis, you have these options: + +| Strategy | Use When | Detected | +|----------|----------|----------| +| **S2I** | Standard apps, no Dockerfile needed | [Yes/No] | +| **Podman** | Custom Containerfile/Dockerfile exists | [Yes/No] | +| **Helm** | Helm chart exists or complex deployments | [Yes/No] | + +**Detected in your project:** +[List what was found: language indicators, Dockerfile, Helm chart at ./chart] + +**Which deployment strategy would you like to use?** +1. S2I - Build with Source-to-Image +2. Podman - Build from Containerfile/Dockerfile +3. Helm - Use existing Helm chart +4. Create Helm chart - Generate a new Helm chart for your project (if no chart exists) +``` + +Store `DEPLOYMENT_STRATEGY` in session state. + +### Phase 1.6: Image Selection (S2I/Podman only) + +If user selected S2I or Podman deployment strategy, offer image selection options: + +```markdown +## Image Selection + +**Current recommendation:** `[builder-image]` +(Based on: [language] [version]) + +**Image Selection Options:** +- **quick** - Use the recommended image (good for most cases) +- **smart** - Run `/recommend-image` for tailored selection (production vs dev, security, performance) + +Which option would you prefer? +``` + +**If user selects "smart":** +- Invoke `/recommend-image` skill with detected `LANGUAGE`, `FRAMEWORK`, `VERSION` +- Store the result in `BUILDER_IMAGE` and `IMAGE_VARIANT` session state +- Continue to Phase 2 + +**If user selects "quick":** +- Use the already-detected `BUILDER_IMAGE` +- Continue to Phase 2 + +**BRANCHING LOGIC:** +- If `DEPLOYMENT_STRATEGY` is **"S2I"** or **"Podman"** → After Phase 2, continue to **Phase 3 (S2I/Podman Path)** +- If `DEPLOYMENT_STRATEGY` is **"Helm"** → After Phase 2, go to **Phase 2-H (Helm Path)** + +### Phase 2: OpenShift Connection + +```markdown +## Phase 2: Connecting to OpenShift + +Checking cluster connection... + +**Current Context:** +| Setting | Value | +|---|---| +| Cluster | [cluster-api-url] | +| User | [username] | +| Namespace | [current-namespace] | + +**Is this the correct cluster and namespace?** +- yes - Continue to build +- no - I need to change this + +[If no] +**To change context:** +1. Run `oc login ` in your terminal +2. Or run `oc project ` to switch namespace +3. Then tell me to continue + +**Available namespaces you have access to:** +[List first 10 namespaces/projects] + +Which namespace should I deploy to? +``` + +Store confirmed `NAMESPACE` in session state. + +--- + +## S2I/PODMAN PATH (If DEPLOYMENT_STRATEGY is "S2I" or "Podman") + +### Phase 3: Git Repository Check + +```markdown +## Git Repository + +I need a Git URL for the S2I build. + +**Detected from .git/config:** +- Remote: `[git-url]` +- Branch: `[current-branch]` + +**Is this correct?** (yes/no) + +[If no git config found] +**Please provide:** +1. Git repository URL (e.g., https://github.com/user/repo.git) +2. Branch name (default: main) +``` + +Store `GIT_URL` and `GIT_BRANCH` in session state. + +### Phase 4: Pre-Build Summary + +```markdown +## Phase 3: Build Configuration + +Here's what I'll create on OpenShift: + +**Target:** +- Cluster: [cluster] +- Namespace: [namespace] + +**Resources to Create:** + +1. **ImageStream** `[app-name]` + - Stores built container images + +2. **BuildConfig** `[app-name]` + - Source: [git-url] (branch: [branch]) + - Builder: [builder-image] + - Output: [app-name]:latest + +--- + +**Would you like to see the full YAML?** (yes/no) + +[If yes, show both YAML manifests] + +--- + +**Proceed with creating these resources and starting the build?** +- yes - Create resources and start build +- modify - I need to change something +- cancel - Stop here +``` + +### Phase 5: Execute Build + +```markdown +## Creating Build Resources... + +[x] ImageStream created: [app-name] +[x] BuildConfig created: [app-name] + +## Starting Build... + +**Build:** [app-name]-1 +**Status:** Running + +--- +**Build Logs:** +``` +[Stream S2I build output] +``` +--- + +[When complete] + +## Build Successful! + +**Build:** [app-name]-1 +**Duration:** [X]m [Y]s +**Image:** [image-reference] + +**CRITICAL: Wait for the build to reach 'Complete' status before proceeding.** + +Continue to deployment? (yes/no) +``` + +If build fails, follow error handling from error-handling-agent. + +### Phase 6: Pre-Deploy Summary + +```markdown +## Phase 4: Deployment Configuration + +**Image ready!** Now let's deploy it. + +**Resources to Create:** + +1. **Deployment** `[app-name]` + - Image: [app-name]:latest + - Replicas: 1 + - Port: [detected-port] + +2. **Service** `[app-name]` + - Internal load balancer + - Port: [port] + +3. **Route** `[app-name]` + - External HTTPS access + - URL: https://[app-name]-[namespace].[domain] + +--- + +**Would you like to see the full YAML?** (yes/no) + +[If yes, show all three YAML manifests] + +--- + +**Proceed with deployment?** +- yes - Deploy the application +- modify - I need to change something +- cancel - Stop here (build artifacts preserved) +``` + +### Phase 7: Execute Deployment + +```markdown +## Deploying Application... + +[x] Deployment created: [app-name] +[x] Service created: [app-name] +[x] Route created: [app-name] + +## Waiting for Rollout... + +**Pod Status:** +| Pod | Status | Ready | +|-----|-----|---| +| [app-name]-xxx-yyy | Running | 1/1 | + +Rollout complete! +``` + +If deployment fails, follow error handling from error-handling-agent. + +--- + +## HELM PATH (If DEPLOYMENT_STRATEGY is "Helm") + +### Phase 2-H: Helm Deployment + +If user selected Helm in Phase 1.5, execute this path instead of Phases 3-7. + +```markdown +## Helm Deployment + +Switching to Helm deployment workflow... + +The `/helm-deploy` skill will handle: +1. Validate the Helm chart +2. Review and customize values +3. Install/upgrade the release +4. Monitor deployment +5. Present results + +Proceeding with Helm deployment... +``` + +**Delegate to `/helm-deploy` skill:** +- Pass `APP_NAME`, `NAMESPACE`, `HELM_CHART_PATH` from session state +- The helm-deploy skill handles chart detection, values review, and installation +- After helm-deploy completes → Go to **Phase 8 (Completion)** + +**If user chose "Create Helm chart":** +- Generate chart using templates from templates/helm/ +- Replace `${APP_NAME}` placeholders with detected app name +- Set `${CONTAINER_PORT}` based on detected port +- Then proceed with helm-deploy workflow + +--- + +## COMPLETION (Both paths converge here) + +### Phase 8: Completion + +```markdown +## Deployment Complete! + +Your application is now running on OpenShift. + +--- + +**Application Summary:** + +| Setting | Value | +|---|---| +| Name | [app-name] | +| Namespace | [namespace] | +| Language | [language] | +| Framework | [framework] | + +--- + +**Access URLs:** + +| Type | URL | +|---|-----| +| **External** | https://[route-host] | +| Internal | http://[app-name].[namespace].svc.cluster.local:[port] | + +--- + +**Resources Created:** + +| Resource | Name | Status | +|----|---|-----| +| ImageStream | [app-name] | Ready | +| BuildConfig | [app-name] | Ready | +| Deployment | [app-name] | 1/1 Running | +| Service | [app-name] | Active | +| Route | [app-name] | Admitted | + +--- + +**Quick Commands:** + +```bash +# View logs +oc logs -f deployment/[app-name] -n [namespace] + +# Scale up +oc scale deployment/[app-name] --replicas=3 -n [namespace] + +# Trigger rebuild (after code changes) +oc start-build [app-name] -n [namespace] + +# Delete everything +oc delete all -l app=[app-name] -n [namespace] +``` + +--- + +**Next Steps:** +- Open your app: [route-url] +- Set up Git webhooks for automatic builds +- Add environment variables: `oc set env deployment/[app-name] KEY=value` +- Configure autoscaling: `oc autoscale deployment/[app-name] --min=1 --max=5` + +--- + +Congratulations! Your application is live. +``` + +## MCP Tools Used + +All tools from child skills: + +| Phase | Tools | +|---|---| +| Detect | `run_terminal_cmd` (optional clone) | +| Connect | `resources_list` (namespaces) | +| Build | `resources_create_or_update`, `pod_logs`, `events_list` | +| Deploy | `resources_create_or_update`, `pod_list`, `pod_logs` | +| Helm | `helm_install`, `helm_upgrade`, `helm_status`, `helm_list`, `pod_list` | +| Rollback | `resources_delete`, `helm_uninstall`, `helm_rollback` | diff --git a/rh-developer/skills/deploy/SKILL.md b/rh-developer/skills/deploy/SKILL.md new file mode 100644 index 00000000..32cecedd --- /dev/null +++ b/rh-developer/skills/deploy/SKILL.md @@ -0,0 +1,333 @@ +--- +name: deploy +description: | + Create Kubernetes Deployment, Service, and Route resources on OpenShift to deploy and expose an application. Use this skill after /s2i-build to make the built image accessible. Handles port detection, replica configuration, HTTPS route creation, rollout monitoring, and rollback on failure. Triggers on /deploy command when user wants to deploy a container image to OpenShift. +--- + +# /deploy Skill + +Create Kubernetes/OpenShift resources (Deployment, Service, Route) to deploy and expose an application from a container image. + +## Prerequisites + +Before running this skill: +1. User is logged into OpenShift cluster +2. Container image exists (from ImageStream or external registry) +3. Target namespace exists + +## Critical: Human-in-the-Loop Requirements + +**IMPORTANT:** This skill requires explicit user confirmation at each step. You MUST: +1. **Wait for user confirmation** before executing any actions +2. **Do NOT proceed** to the next step until the user explicitly approves +3. **Present options clearly** (yes/no/modify) and wait for response +4. **Never auto-execute** resource creation or deployments + +If the user says "no" or wants modifications, address their concerns before proceeding. + +## Workflow + +### Step 1: Gather Deployment Information + +```markdown +## Deployment Configuration + +**Current OpenShift Context:** +- Cluster: [cluster] +- Namespace: [namespace] + +**Please confirm deployment settings:** + +| Setting | Value | Source | +|---------|-------|--------| +| App Name | `[name]` | [from s2i-build / input] | +| Image | `[image-ref]` | [from ImageStream / input] | +| Container Port | `[port]` | [detected / needs input] | +| Replicas | `1` | [default] | +| Expose Route | `yes` | [default] | + +Confirm these settings or tell me what to change. +``` + +**WAIT for user confirmation before proceeding.** Do NOT continue until user explicitly confirms these settings or provides corrections. + +### Step 2: Detect Container Port + +Try to detect port from project files: + +1. **Dockerfile:** Look for `EXPOSE ` (Most accurate for container builds) +2. **Web Server Config:** Look for `listen ` in `nginx.conf`, `httpd.conf`, etc. +3. **Framework Defaults:** + - **Node.js:** Look for `PORT` env var usage, common: 3000 (dev), 8080 (prod/S2I) + - **Python:** Flask default 5000, FastAPI/Uvicorn 8000 + - **Java:** Spring Boot 8080, Quarkus 8080 + - **Go:** Common 8080 + - **Ruby Rails:** 3000 + +```markdown +## Port Detection + +I detected port **[port]** based on: +- [reason - e.g., "PORT environment variable in package.json scripts"] + +Is this correct? +- yes - Use port [port] +- no - Specify the correct port +``` + +**WAIT for user confirmation.** Do NOT proceed until user confirms the port or provides the correct value. + +If unable to detect: +```markdown +## Port Required + +I couldn't automatically detect the container port. + +Common ports by framework: +- Node.js/Express: 3000 or 8080 +- Python Flask: 5000 +- Python FastAPI: 8000 +- Java Spring Boot: 8080 +- Go: 8080 + +**What port does your application listen on?** +``` + +### Step 3: Create Deployment + +Show the Deployment manifest: + +```markdown +## Step 1 of 3: Create Deployment + +A Deployment manages your application pods. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: [app-name] + namespace: [namespace] + labels: + app: [app-name] + app.kubernetes.io/name: [app-name] + annotations: + image.openshift.io/triggers: | + [{"from":{"kind":"ImageStreamTag","name":"[app-name]:latest"},"fieldPath":"spec.template.spec.containers[0].image"}] +spec: + replicas: [replicas] + selector: + matchLabels: + app: [app-name] + template: + metadata: + labels: + app: [app-name] + spec: + containers: + - name: [app-name] + image: image-registry.openshift-image-registry.svc:5000/[namespace]/[app-name]:latest + ports: + - containerPort: [port] + protocol: TCP + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: / + port: [port] + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: [port] + initialDelaySeconds: 5 + periodSeconds: 5 +``` + +**This Deployment will:** +- Run [replicas] replica(s) of your application +- Use image from ImageStream: `[app-name]:latest` +- Expose container port: [port] +- Auto-update when new images are pushed + +**Proceed with creating this Deployment?** (yes/no) +``` + +**WAIT for user confirmation.** Do NOT create the Deployment until user explicitly says "yes". + +- If user says "yes" → Use kubernetes MCP `resources_create_or_update` to apply +- If user says "no" → Ask what they would like to change +- If user wants modifications → Update the YAML and show again for confirmation + +### Step 4: Create Service + +```markdown +## Step 2 of 3: Create Service + +A Service provides internal load balancing to your pods. + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: [app-name] + namespace: [namespace] + labels: + app: [app-name] +spec: + selector: + app: [app-name] + ports: + - name: http + port: [port] + targetPort: [port] + protocol: TCP + type: ClusterIP +``` + +**This Service will:** +- Create internal DNS: `[app-name].[namespace].svc.cluster.local` +- Load balance traffic to pods on port [port] + +**Proceed with creating this Service?** (yes/no) +``` + +**WAIT for user confirmation.** Do NOT create the Service until user explicitly says "yes". + +- If user says "yes" → Use kubernetes MCP `resources_create_or_update` to apply +- If user says "no" → Ask what they would like to change +- If user wants modifications → Update the YAML and show again for confirmation + +### Step 5: Create Route (Optional) + +If user wants external exposure: + +```markdown +## Step 3 of 3: Create Route + +A Route exposes your application externally with HTTPS. + +```yaml +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: [app-name] + namespace: [namespace] + labels: + app: [app-name] +spec: + to: + kind: Service + name: [app-name] + weight: 100 + port: + targetPort: http + tls: + termination: edge + insecureEdgeTerminationPolicy: Redirect + wildcardPolicy: None +``` + +**This Route will:** +- Expose app at: `https://[app-name]-[namespace].[cluster-domain]` +- Enable TLS with edge termination +- Redirect HTTP to HTTPS + +**Proceed with creating this Route?** (yes/no/skip) +``` + +**WAIT for user confirmation.** Do NOT create the Route until user explicitly responds. + +- If user says "yes" → Use kubernetes MCP `resources_create_or_update` to apply +- If user says "skip" → Skip Route creation and proceed to rollout monitoring +- If user says "no" → Ask what they would like to change +- If user wants modifications → Update the YAML and show again for confirmation + +### Step 6: Wait for Rollout + +Monitor deployment status: + +```markdown +## Deployment Rollout + +Waiting for pods to be ready... + +**Deployment:** [app-name] +**Desired:** [replicas] +**Ready:** [current]/[replicas] + +**Pod Status:** +| Pod | Status | Ready | Restarts | +|-----|--------|-------|----------| +| [app-name]-xxx-yyy | Running | 1/1 | 0 | + +[Poll until ready or timeout after 5 minutes] +``` + +### Step 7: Deployment Complete + +```markdown +## Deployment Complete! + +**Application:** [app-name] +**Namespace:** [namespace] + +**Access URLs:** +| Type | URL | +|------|-----| +| External | https://[route-host] | +| Internal | http://[app-name].[namespace].svc.cluster.local:[port] | + +**Resources Created:** +| Resource | Name | Status | +|----------|------|--------| +| Deployment | [app-name] | [replicas]/[replicas] Ready | +| Service | [app-name] | Active | +| Route | [app-name] | Admitted | + +**Quick Commands:** +```bash +# View logs +oc logs -f deployment/[app-name] -n [namespace] + +# Scale replicas +oc scale deployment/[app-name] --replicas=3 -n [namespace] + +# Restart pods +oc rollout restart deployment/[app-name] -n [namespace] + +# Delete all +oc delete all -l app=[app-name] -n [namespace] +``` + +Your application is now live! +``` + +## MCP Tools Used + +| Tool | Purpose | +|------|---------| +| `resources_list` | Check existing deployments/services/routes | +| `resources_get` | Get resource details, route host | +| `resources_create_or_update` | Create Deployment, Service, Route | +| `pod_list` | Check pod status during rollout | +| `pod_logs` | Debug pod issues | +| `events_list` | Check events for errors | + +## Required Inputs + +| Input | Auto-detected | Must Confirm | +|-------|---------------|--------------| +| App name | Yes (from build) | Yes | +| Image | Yes (from ImageStream) | Yes | +| Port | Yes (from project files) | Yes | +| Replicas | Yes (default: 1) | Optional | +| Create Route | Yes (default: yes) | Yes | +| Namespace | Yes (from kubeconfig) | Yes | diff --git a/rh-developer/skills/detect-project/SKILL.md b/rh-developer/skills/detect-project/SKILL.md new file mode 100644 index 00000000..9210604a --- /dev/null +++ b/rh-developer/skills/detect-project/SKILL.md @@ -0,0 +1,299 @@ +--- +name: detect-project +description: | + Analyze a project folder or GitHub repository to detect programming language, framework, and version requirements. Use this skill when containerizing an application, selecting an S2I builder image, deploying to OpenShift or RHEL, or determining a project's tech stack. Supports Node.js, Python, Java, Go, Ruby, .NET, PHP, and Perl. Triggers on /detect-project command or when user needs build strategy recommendations. Run before /s2i-build or /rhel-deploy. +--- + +# /detect-project Skill + +## Critical Restrictions +- **DO NOT CLONE** remote repositories unless the user explicitly selects the "Clone & Inspect" option. +- **ALWAYS** use `github-mcp-server` tools (`list_directory`, `get_file_contents`) for initial analysis of remote URLs. +- **NEVER** assume you have permission to write to the local filesystem for analysis purposes. + +Analyze the project to detect language/framework and recommend a build strategy. This skill handles both local project directories and remote Git repositories. + +## Critical: Human-in-the-Loop Requirements + +**IMPORTANT:** This skill requires user confirmation before proceeding. You MUST: +1. **Wait for user confirmation** on detected values before saving to session state +2. **Do NOT assume** detection is correct - always present findings and ask for confirmation +3. **Present options clearly** when multiple choices exist and wait for selection +4. **Never auto-select** deployment strategies or images without user approval + +## Workflow + +### Step 1: Context Analysis + +**Scenario A: Local Files Available** +If you are in a project directory with source code: +1. Proceed to **Step 2: Scan Project Files**. + +**Scenario B: Remote Git URL Provided** +If the user provided a Git URL (e.g., `https://github.com/...`): + +Use the **github-mcp-server** to analyze the repository directly without cloning: + +1. Use GitHub MCP to list repository contents (look for indicator files) +2. Use GitHub MCP to read key files (`package.json`, `pom.xml`, `requirements.txt`, `Dockerfile`, etc.) +3. Proceed with analysis as if local files + +```markdown +## Analyzing Remote Repository + +I'm inspecting the repository: `[git-url]` + +Using GitHub API to analyze the project structure... + +[Use github MCP to get_file_contents for indicator files] + +**Files Found:** +- [list files from repo root] + +[Continue with Step 2: Scan Project Files using the remote file contents] +``` + +If GitHub MCP is unavailable or repo is private without access: + +```markdown +## Remote Repository Access + +I see you want to deploy from: `[git-url]` + +I couldn't access the repository directly. Options: + +1. **Remote S2I Build** (Recommended for standard apps) + - OpenShift will clone and build the code directly. + - I need you to confirm the language/framework. + +2. **Remote Podman Build** (Recommended if Containerfile/Dockerfile exists) + - OpenShift will use the Containerfile/Dockerfile in the repo. + - Best if you already have a custom build process. + +3. **Clone & Inspect** + - I will clone the repo locally to analyze it first. + - This helps if you're unsure about the project details. + +**Which approach do you prefer?** +``` + +**WAIT for user to select an option.** Do NOT proceed until user makes a choice. + +**Scenario C: No Context** +If no files and no URL: +1. Ask the user for the Git URL or to navigate to a project folder. + +### Step 2: Scan Project Files (Local Only) + +Look for these indicator files in the project root: + +| File | Language | Framework Hint | +|---|----|----| +| `Chart.yaml` | Helm Chart | Existing Helm deployment available | +| `package.json` | Node.js | Check for next, angular, vue, react | +| `pom.xml` | Java | Check for spring-boot, quarkus deps | +| `build.gradle` / `build.gradle.kts` | Java | Check for spring, quarkus plugins | +| `requirements.txt` | Python | - | +| `Pipfile` | Python | Pipenv | +| `pyproject.toml` | Python | Poetry or modern Python | +| `go.mod` | Go | - | +| `Gemfile` | Ruby | Check for rails | +| `composer.json` | PHP | Check for laravel, symfony | +| `*.csproj` / `*.sln` | .NET | - | +| `Cargo.toml` | Rust | No official S2I | +| `Dockerfile` / `Containerfile` | Pre-containerized | May not need S2I | + +### Helm Chart Detection + +Also check for Helm charts in these locations (in order): + +| Priority | Path | Description | +|----------|------|-------------| +| 1 | `./Chart.yaml` | Root directory | +| 2 | `./chart/Chart.yaml` | Chart subdirectory | +| 3 | `./charts/*/Chart.yaml` | Charts directory | +| 4 | `./helm/Chart.yaml` | Helm subdirectory | +| 5 | `./deploy/helm/Chart.yaml` | Deploy directory | + +If Chart.yaml is found, parse it to extract: +- `name`: Chart name +- `version`: Chart version (SemVer) +- `appVersion`: Application version +- `description`: Chart description + +Also check for: +- `values.yaml`: Default configuration +- `templates/`: Template files + +### Step 3: Detect Version Requirements + +For each detected language, extract version info: + +**Node.js:** +- Check `engines.node` in package.json +- Example: `"engines": { "node": ">=18" }` + +**Python:** +- Check `python_requires` in pyproject.toml +- Check `runtime.txt` for version +- Check `.python-version` file + +**Java:** +- Check `` or `` in pom.xml +- Check `sourceCompatibility` in build.gradle + +**Go:** +- Check `go` directive in go.mod +- Example: `go 1.21` + +### Step 4: Detect Framework + +Look for framework-specific indicators: + +**Node.js frameworks:** +- `next.config.js` or `next.config.mjs` → Next.js +- `angular.json` → Angular +- `vue.config.js` or `vite.config.ts` with vue → Vue.js +- `remix.config.js` → Remix + +**Java frameworks:** +- `quarkus` in dependencies → Quarkus +- `spring-boot` in dependencies → Spring Boot +- `micronaut` in dependencies → Micronaut + +**Python frameworks:** +- `django` in requirements → Django +- `flask` in requirements → Flask +- `fastapi` in requirements → FastAPI + +### Step 4.5: Detect Python Entry Point (Python projects only) + +For Python projects, detect the application entry point to ensure proper S2I configuration: + +**Check for entry point files (in order of S2I preference):** +1. `app.py` - Default S2I Python entry point (no config needed) +2. `application.py` - Alternative default +3. `wsgi.py` - WSGI module +4. `main.py` - Common alternative (requires APP_MODULE config) +5. Any file with `if __name__ == "__main__"` and Flask/FastAPI app + +**Check requirements.txt/Pipfile/pyproject.toml for WSGI server:** +- `gunicorn` - Required for APP_MODULE to work with S2I Python +- `uwsgi` - Alternative WSGI server + +### Step 5: Present Findings + +Format your response: + +```markdown +## Project Analysis Results + +**Detected Language:** [Language] +**Framework:** [Framework or "None detected"] +**Version:** [Version or "Not specified"] + +**Detection Confidence:** [High/Medium/Low] +- High: Clear indicator file with version info +- Medium: Indicator file found but no version specified +- Low: Multiple conflicting indicators or unusual setup + +**Indicator Files Found:** +- [list of files] + +--- + +**Recommended S2I Builder Image:** +`registry.access.redhat.com/ubi9/[image-name]` + +**Why this image:** +- [Brief explanation] + +**Alternative Options:** +1. `[alternative-1]` - [when to choose] +2. `[alternative-2]` - [when to choose] + +--- + +**Suggested App Name:** `[derived-name]` +(based on [folder name / package.json name / pom artifactId]) + +--- + +**Image Selection Options:** +- **quick** - Use the recommended image above (good for most cases) +- **smart** - Run `/recommend-image` for use-case aware selection (production vs dev, security, performance) + +Please confirm: +1. Is the detected language/framework correct? +2. Image selection: quick or smart? +3. Is the app name acceptable? + +Type 'yes' to confirm all with quick image selection, 'smart' for tailored recommendation, or tell me what to change. +``` + +**WAIT for user confirmation.** Do NOT save configuration or proceed until user explicitly confirms or provides corrections. + +- If user says "yes" → Save configuration with quick image selection +- If user says "smart" → Invoke `/recommend-image` skill +- If user provides corrections → Update values and show again for confirmation + +**Note:** If the user selects "smart", invoke the `/recommend-image` skill with the detected `LANGUAGE`, `FRAMEWORK`, and `VERSION` values. + +## Output Variables + +After successful detection, these values should be available for other skills: + +| Variable | Description | Example | +|----|----|---| +| `APP_NAME` | Application name | `my-nodejs-app` | +| `LANGUAGE` | Detected language | `nodejs` | +| `FRAMEWORK` | Detected framework | `express` | +| `VERSION` | Language version | `20` | +| `BUILDER_IMAGE` | Full S2I image reference | `registry.access.redhat.com/ubi9/nodejs-20` | +| `BUILD_STRATEGY` | Build strategy | `Source` (S2I) or `Podman` | +| `DEPLOYMENT_TARGET` | Deployment target | `openshift` or `rhel` | +| `DEPLOYMENT_STRATEGY` | Deployment method | `S2I`, `Podman`, `Helm` (OpenShift) or `container`, `native` (RHEL) | +| `HELM_CHART_PATH` | Path to Helm chart | `./chart` | +| `HELM_CHART_NAME` | Helm chart name | `my-app` | +| `HELM_CHART_VERSION` | Helm chart version | `0.1.0` | +| `HELM_CHART_DETECTED` | Whether Helm chart was found | `true` or `false` | +| `RHEL_HOST` | SSH target for RHEL deployment | `user@192.168.1.100` | +| `PYTHON_ENTRY_FILE` | Python entry point file (Python only) | `main.py` | +| `PYTHON_APP_MODULE` | APP_MODULE value for S2I (Python only) | `main:app` | +| `PYTHON_HAS_GUNICORN` | Whether gunicorn is in requirements (Python only) | `true` or `false` | + +## MCP Tools Used + +This skill uses: + +### github-mcp-server (for remote repositories) + +**IMPORTANT: GitHub MCP has two different patterns for browsing vs reading:** + +#### Browsing Repository Structure +Use `mcp_github_get_file_contents` to **list directories**: +``` +mcp_github_get_file_contents(owner="org", repo="repo", path="/") → root listing +mcp_github_get_file_contents(owner="org", repo="repo", path="src") → src/ contents +``` +Returns: JSON array with file metadata (name, path, sha, type, download_url) + +#### Reading File Contents +Use `fetch_mcp_resource` with GitHub resource URI to **read actual file content**: +``` +fetch_mcp_resource(server="github", uri="repo://owner/repo/contents/path/to/file") +``` +Returns: Actual file content as text + +**URI Format:** `repo://{owner}/{repo}/contents/{file-path}` + +**Examples:** +- `repo://RHEcosystemAppEng/sast-ai-frontend/contents/package.json` +- `repo://facebook/react/contents/README.md` +- `repo://myorg/myrepo/contents/src/index.ts` + +### Local file reading (for local projects) +- Standard file reading capabilities (`read_file` tool) + +### Terminal (fallback) +- `run_terminal_cmd` - Only if user selects "Clone & Inspect" for private repos diff --git a/rh-developer/skills/helm-deploy/SKILL.md b/rh-developer/skills/helm-deploy/SKILL.md new file mode 100644 index 00000000..a5a28bb8 --- /dev/null +++ b/rh-developer/skills/helm-deploy/SKILL.md @@ -0,0 +1,377 @@ +--- +name: helm-deploy +description: | + Deploy applications to OpenShift using Helm charts. Use this skill when user wants to deploy with Helm, when a Helm chart is detected in the project, or when /helm-deploy command is invoked. Supports both existing charts and chart creation. Handles chart detection, values customization, install/upgrade operations, and rollback. Requires kubernetes MCP Helm tools. +--- + +# /helm-deploy Skill + +Deploy applications to OpenShift using Helm charts. Supports existing charts or creates new ones. + +## Prerequisites + +1. User logged into OpenShift cluster +2. Helm chart exists OR user wants to create one +3. Container image available (from registry or will be built) + +## Critical: Human-in-the-Loop Requirements + +**IMPORTANT:** This skill requires explicit user confirmation at each step. You MUST: +1. **Wait for user confirmation** before executing any actions +2. **Do NOT proceed** to the next step until the user explicitly approves +3. **Present options clearly** and wait for response +4. **Never auto-execute** chart creation, Helm installations, or upgrades + +If the user says "no" or wants modifications, address their concerns before proceeding. + +## Workflow + +### Step 1: Check OpenShift Connection + +Use kubernetes MCP to verify cluster connection: + +```markdown +## Checking OpenShift Connection... + +**Cluster:** [cluster-url] +**User:** [username] +**Namespace:** [namespace] + +Is this the correct cluster and namespace? (yes/no) +``` + +**WAIT for user confirmation before proceeding.** Do NOT continue until user explicitly confirms. + +If user says "no", wait for them to switch context and tell you to continue. + +If connection fails, refer to error-handling-agent for authentication error patterns. + +### Step 2: Detect Helm Chart + +Search for Helm charts in this order: + +| Priority | Path | Description | +|----------|------|-------------| +| 1 | `./Chart.yaml` | Root directory | +| 2 | `./chart/Chart.yaml` | Chart subdirectory | +| 3 | `./charts/*/Chart.yaml` | Charts directory | +| 4 | `./helm/Chart.yaml` | Helm subdirectory | +| 5 | `./deploy/helm/Chart.yaml` | Deploy directory | + +**If chart found:** + +```markdown +## Helm Chart Detected + +**Location:** [chart-path] + +| Field | Value | +|-------|-------| +| Name | [chart-name] | +| Version | [chart-version] | +| App Version | [app-version] | +| Description | [description] | + +**Templates found:** +- [list of template files] + +**Values file:** [values.yaml path] + +Would you like to: +1. Deploy using this chart (recommended) +2. Customize values before deploying +3. Use a different chart location +``` + +**WAIT for user to select an option.** Do NOT proceed until user makes a choice. + +**If no chart found:** + +```markdown +## No Helm Chart Found + +I searched these locations but found no Helm chart: +- ./Chart.yaml +- ./chart/Chart.yaml +- ./charts/*/Chart.yaml +- ./helm/Chart.yaml +- ./deploy/helm/Chart.yaml + +**Options:** +1. **Create a new Helm chart** - I'll generate one based on your project +2. **Specify chart path** - Point me to your chart location +3. **Use a different deployment method** - Try /deploy or /containerize-deploy + +Which would you prefer? +``` + +**WAIT for user to select an option.** Do NOT proceed until user makes a choice. + +### Step 3: Create Helm Chart (if needed) + +If user chooses to create a chart: + +```markdown +## Creating Helm Chart + +I'll create a Helm chart based on your project. + +**Detected Project Info:** +| Setting | Value | +|---------|-------| +| App Name | [app-name] | +| Language | [language] | +| Framework | [framework] | +| Port | [port] | + +**Chart will include:** +- Chart.yaml with project metadata +- values.yaml with configurable options +- Deployment template +- Service template +- Route template (OpenShift) +- Helper templates + +**Target directory:** ./chart/ + +Proceed with creating the Helm chart? (yes/no) +``` + +**WAIT for user confirmation.** Do NOT create the chart until user explicitly says "yes". + +- If user says "yes" → Create chart files +- If user says "no" → Ask what they would like to change or use a different approach + +Use templates from templates/helm/ to generate: +1. Chart.yaml +2. values.yaml +3. templates/deployment.yaml +4. templates/service.yaml +5. templates/route.yaml +6. templates/_helpers.tpl +7. templates/NOTES.txt + +Replace `${APP_NAME}` placeholders with actual app name in all template files. + +### Step 4: Check for Existing Release + +Before installing, check if a release with the same name exists: + +```markdown +## Checking for Existing Release... + +[Use mcp_kubernetes_helm_list to check] +``` + +**If release exists:** + +```markdown +## Existing Release Found + +A release named '[name]' already exists. + +| Field | Value | +|-------|-------| +| Status | [status] | +| Revision | [revision] | +| Chart | [chart-name] v[version] | +| Updated | [timestamp] | + +**Options:** +1. Upgrade the release with new configuration +2. Rollback to a previous revision +3. Uninstall and reinstall +4. Cancel + +Which would you like to do? +``` + +**WAIT for user to select an option.** Do NOT proceed until user makes a choice. + +### Step 5: Review Values + +```markdown +## Chart Values Configuration + +**Current values.yaml:** + +```yaml +replicaCount: [value] +image: + repository: [value] + tag: [value] +service: + port: [value] +route: + enabled: [value] +resources: + limits: + memory: [value] +``` + +**Common customizations:** + +| Value | Current | Description | +|-------|---------|-------------| +| `replicaCount` | 1 | Number of pods | +| `image.repository` | [repo] | Container image | +| `image.tag` | [tag] | Image version | +| `service.port` | [port] | Service port | +| `resources.limits.memory` | 512Mi | Memory limit | + +**Options:** +1. Deploy with current values +2. Modify values interactively +3. Use a custom values file + +Which would you prefer? +``` + +**WAIT for user to select an option.** Do NOT proceed until user makes a choice. + +### Step 6: Pre-Deploy Summary + +```markdown +## Helm Deployment Summary + +**Release Configuration:** + +| Setting | Value | +|---------|-------| +| Release Name | [release-name] | +| Namespace | [namespace] | +| Chart | [chart-path] | +| Chart Version | [version] | + +**Resources to be created:** + +| Resource | Name | +|----------|------| +| Deployment | [name] | +| Service | [name] | +| Route | [name] (if enabled) | + +**Values to apply:** +```yaml +[show customized values or "Using defaults"] +``` + +**Helm command equivalent:** +```bash +helm install [release-name] [chart-path] -n [namespace] [--set options] +``` + +**Proceed with Helm deployment?** (yes/no) +``` + +**WAIT for user confirmation.** Do NOT execute the Helm install/upgrade until user explicitly says "yes". + +- If user says "yes" → Proceed with deployment +- If user says "no" → Ask what they would like to change + +### Step 7: Execute Deployment + +Use kubernetes MCP `mcp_kubernetes_helm_install` or `mcp_kubernetes_helm_upgrade`: + +```markdown +## Deploying with Helm... + +**Release:** [release-name] +**Chart:** [chart-name] v[version] + +[x] Chart validated +[x] Templates rendered +[ ] Installing release... + +--- + +**Installation Progress:** + +Waiting for resources to be ready... + +| Resource | Status | +|----------|--------| +| Deployment/[name] | [status] | +| Service/[name] | [status] | +| Route/[name] | [status] | + +--- +``` + +Monitor pod status using `mcp_kubernetes_pods_list_in_namespace` until pods are ready or timeout. + +### Step 8: Deployment Complete + +```markdown +## Helm Deployment Complete! + +**Release:** [release-name] +**Status:** deployed +**Revision:** 1 +**Namespace:** [namespace] + +--- + +**Resources Created:** + +| Resource | Name | Status | +|----------|------|--------| +| Deployment | [name] | [replicas] Ready | +| Service | [name] | Active | +| Route | [name] | Admitted | + +**Access URL:** https://[route-host] + +--- + +**Quick Commands:** + +```bash +# Check release status +helm status [release-name] -n [namespace] + +# View release history +helm history [release-name] -n [namespace] + +# Upgrade with new values +helm upgrade [release-name] [chart-path] -n [namespace] -f new-values.yaml + +# Rollback to previous version +helm rollback [release-name] 1 -n [namespace] + +# Uninstall release +helm uninstall [release-name] -n [namespace] + +# View logs +oc logs -l app.kubernetes.io/instance=[release-name] -n [namespace] -f +``` + +--- + +Your application is live! +``` + +## MCP Tools Used + +| Tool | Purpose | +|------|---------| +| `mcp_kubernetes_helm_list` | Check existing releases | +| `mcp_kubernetes_helm_install` | Install new release | +| `mcp_kubernetes_helm_upgrade` | Upgrade existing release | +| `mcp_kubernetes_helm_status` | Get release status | +| `mcp_kubernetes_helm_history` | Get release history | +| `mcp_kubernetes_helm_rollback` | Rollback to revision | +| `mcp_kubernetes_helm_uninstall` | Remove release | +| `mcp_kubernetes_pods_list_in_namespace` | Monitor pod status | +| `mcp_kubernetes_pods_log` | View pod logs | +| `mcp_kubernetes_events_list` | Check for errors | + +## Output Variables + +| Variable | Description | Example | +|----------|-------------|---------| +| `RELEASE_NAME` | Helm release name | `my-app` | +| `CHART_PATH` | Path to chart | `./chart` | +| `CHART_VERSION` | Chart version | `0.1.0` | +| `RELEASE_REVISION` | Current revision | `1` | +| `ROUTE_HOST` | External URL | `my-app-ns.apps.cluster.com` | diff --git a/rh-developer/skills/recommend-image/SKILL.md b/rh-developer/skills/recommend-image/SKILL.md new file mode 100644 index 00000000..573379ce --- /dev/null +++ b/rh-developer/skills/recommend-image/SKILL.md @@ -0,0 +1,303 @@ +--- +name: recommend-image +description: | + Intelligently recommend the optimal S2I builder image or container base image for a project based on detected language/framework, use-case requirements, security posture, and deployment target. Supports GitHub URLs for remote project analysis (delegates to /detect-project). Use this skill when the user needs a container image recommendation, wants to compare image options, or asks about production vs development images. Triggers on /recommend-image command, or when advanced image selection beyond basic version matching is needed. Supports Node.js, Python, Java, Go, Ruby, .NET, PHP, and Perl on Red Hat UBI. +--- + +# /recommend-image Skill + +Provide intelligent, use-case-aware container image recommendations that go beyond simple language-to-image mapping. + +## When to Use This Skill + +- User asks for the "best" image for their use case +- User needs to choose between production vs development images +- User wants to compare image options (minimal vs full-featured) +- `/detect-project` completed and user wants a tailored recommendation +- User asks about image size, security, or performance trade-offs + +## Critical: Human-in-the-Loop Requirements + +**IMPORTANT:** This skill requires user input and confirmation. You MUST: +1. **Wait for user responses** to all questions before proceeding +2. **Do NOT assume** user preferences - always ask +3. **Present options clearly** and wait for selection +4. **Confirm final recommendation** before saving to session state + +## Workflow + +### Step 1: Gather Context + +**If invoked after `/detect-project`:** +Use the already-detected values: +- `LANGUAGE` - Programming language +- `FRAMEWORK` - Framework (if detected) +- `VERSION` - Language version + +**If invoked with a GitHub URL:** + +Example: `/recommend-image for https://github.com/RHEcosystemAppEng/sast-ai-frontend` + +When a GitHub URL is provided: + +```markdown +## Analyzing Remote Repository + +I'll analyze the repository to detect the project type first. + +Invoking `/detect-project` for: `[github-url]` +``` + +**Delegate to `/detect-project`:** +- Pass the GitHub URL to `/detect-project` +- `/detect-project` will use GitHub MCP to analyze the repository +- Receive back: `LANGUAGE`, `FRAMEWORK`, `VERSION`, `APP_NAME` +- Continue to Step 2 (Use-Case Assessment) + +**If invoked standalone (no URL, no prior detection):** +Ask the user: + +```markdown +## Image Recommendation + +To recommend the best image, I need some information: + +**Option 1:** Provide a GitHub URL +- Example: `/recommend-image for https://github.com/user/repo` + +**Option 2:** Tell me about your project +1. **What language/framework is your project?** + (e.g., Python 3.11, Node.js 20, Java 17 with Spring Boot) + +2. **What version do you need?** + (or say "latest LTS" if unsure) +``` + +### Step 2: Use-Case Assessment + +Present use-case questions: + +```markdown +## Use-Case Assessment + +To recommend the optimal image, please tell me about your requirements: + +**1. Deployment Environment:** +- **Production** - Stability, security, long-term support critical +- **Development** - Tooling, debugging features preferred +- **Edge/IoT** - Minimal footprint essential + +**2. Security Priority:** +- **Standard** - Red Hat UBI with regular updates +- **Hardened** - Minimal attack surface, fewer packages +- **Compliance** - FIPS or specific compliance requirements + +**3. Performance Priority:** +- **Fast startup** - Serverless, scale-to-zero workloads +- **Low memory** - High-density deployments +- **Balanced** - General purpose applications + +**4. Build Requirements:** +- **Need build tools** - Native extensions, compilation during build +- **Runtime only** - Pre-compiled, no build tools needed + +Please describe your use case or select from the options above. +``` + +**WAIT for user to provide their requirements.** Do NOT proceed until user describes their use case or selects options. + +### Step 3: Evaluate Image Options + +For each language, evaluate available variants: + +**Image Variant Types:** + +| Variant | Description | Best For | +|---------|-------------|----------| +| Full | Includes build tools, dev utilities | Development, native extensions | +| Minimal | Smaller base, essential packages only | Production, security-focused | +| Runtime | No build tools, runtime only | Pre-compiled apps, smallest size | + +**Scoring Criteria:** + +| Criteria | Weight (Production) | Weight (Development) | +|----------|---------------------|----------------------| +| Image Size | High | Low | +| Security (fewer packages) | High | Medium | +| Build Tools | Low | High | +| Startup Time | Medium | Low | +| LTS Status | High | Medium | + +### Step 3.5: Dynamic Image Validation + +Before presenting recommendations, validate with dynamic sources to provide accurate, real-time data. + +#### Check if Skopeo is Available + +First, verify skopeo is installed: + +```bash +which skopeo +``` + +**If skopeo is NOT installed**, present: + +```markdown +## Skopeo Required for Image Validation + +To provide accurate image recommendations, I need `skopeo` to inspect container images. + +**Skopeo is not installed.** This tool allows me to: +- Verify the image exists before recommending it +- Get exact image size (not estimates) +- Check architecture support (amd64, arm64) +- Show when the image was last built + +**Install skopeo:** + +| OS | Command | +|----|---------| +| Fedora/RHEL/CentOS | `sudo dnf install skopeo` | +| Ubuntu/Debian | `sudo apt install skopeo` | +| macOS (Homebrew) | `brew install skopeo` | + +After installing, run `/recommend-image` again for enhanced recommendations. + +**Continue without skopeo?** +- **yes** - Use static reference data only (less accurate) +- **install** - I'll install skopeo first +``` + +**WAIT for user to select an option.** Do NOT proceed until user chooses. + +If user continues without skopeo, proceed with static data and note: "Image metadata from static reference (not verified)". + +#### Skopeo Verification + +For each candidate image, verify availability and get metadata: + +```bash +# Verify image exists and get metadata +skopeo inspect docker://registry.access.redhat.com/ubi9/[candidate-image] +``` + +**Note:** The `docker://` transport is OCI-standard and works with Podman registries - it's not Docker-specific. + +### Step 4: Present Recommendation + +Format your recommendation: + +```markdown +## Image Recommendation + +Based on your requirements: + +| Factor | Your Input | +|--------|------------| +| Language | [language] [version] | +| Framework | [framework or "None"] | +| Environment | [Production/Development/Edge] | +| Security | [Standard/Hardened/Compliance] | +| Priority | [startup/memory/balanced] | +| Build Tools | [needed/not needed] | + +--- + +### Recommended Image + +`registry.access.redhat.com/ubi9/[image-name]` + +**Why this image:** +- [Reason 1 - matches primary requirement] +- [Reason 2 - matches secondary requirement] +- [Reason 3 - version/LTS consideration] + +**Image Details:** +| Property | Value | Source | +|----------|-------|--------| +| Base | UBI 9 | Static | +| Variant | [Full/Minimal/Runtime] | Static | +| Size | [exact-size]MB | Skopeo | +| Built | [build-date] | Skopeo | +| Architecture | amd64, arm64 | Skopeo | +| LTS | [Yes/No - EOL date] | Static | + +**Security Status:** [status-icon] [status-message] +- Last checked: [timestamp] +- Source: Red Hat Security Data API + +*(If skopeo unavailable: "Image metadata from static reference - install skopeo for verified data")* + +**Trade-offs:** +- [What you give up with this choice] +- [When you might choose differently] + +--- + +### Alternative Options + +| Image | Best For | Trade-off | +|-------|----------|-----------| +| `[alternative-1]` | [use case] | [trade-off] | +| `[alternative-2]` | [use case] | [trade-off] | + +--- + +**Confirm this recommendation?** +- Type **yes** to use `[recommended-image]` +- Type **alternative N** to use an alternative +- Tell me if you have different requirements +``` + +**WAIT for user to confirm or select an alternative.** Do NOT save configuration until user explicitly confirms. + +### Step 5: Handle Confirmation + +**If user confirms:** + +```markdown +## Image Selected + +| Setting | Value | +|---------|-------| +| Builder Image | `[full-image-reference]` | +| Variant | [variant] | +| Rationale | [brief reason] | + +Configuration saved. You can now: +- Run `/s2i-build` to build with this image +- Run `/containerize-deploy` for the full workflow +``` + +**If user selects alternative:** +Update the selection and confirm. + +**If user has different requirements:** +Return to Step 2 with new inputs. + +## Image Reference + +### Quick Use-Case Matrix + +| Use Case | Variant | Pattern | +|----------|---------|---------| +| Production | Minimal/Runtime | `ubi9/{lang}-{ver}-minimal` or `-runtime` | +| Development | Full | `ubi9/{lang}-{ver}` | +| Serverless | Smallest | Minimal variants or native builds | + +**Framework-Specific (common):** +- **Quarkus**: `openjdk-21` (JVM) or Mandrel builder (native) +- **Spring Boot**: `openjdk-17-runtime` for production +- **Next.js/React**: `nodejs-20` with multi-stage build +- **Django/Flask**: `python-311` with `requirements.txt` + +## Output Variables + +After successful recommendation: + +| Variable | Description | Example | +|----------|-------------|---------| +| `BUILDER_IMAGE` | Full image reference | `registry.access.redhat.com/ubi9/nodejs-20-minimal` | +| `IMAGE_VARIANT` | Variant type | `minimal` | +| `SELECTION_RATIONALE` | Why this image | "Minimal variant for production security" | +| `ALTERNATIVES` | Fallback options | `["ubi9/nodejs-20", "ubi9/nodejs-22-minimal"]` | diff --git a/rh-developer/skills/rhel-deploy/SKILL.md b/rh-developer/skills/rhel-deploy/SKILL.md new file mode 100644 index 00000000..91a00409 --- /dev/null +++ b/rh-developer/skills/rhel-deploy/SKILL.md @@ -0,0 +1,518 @@ +--- +name: rhel-deploy +description: | + CRITICAL: When user types /rhel-deploy, use THIS skill immediately. This skill deploys applications to standalone RHEL/Fedora/CentOS systems (NOT OpenShift) using Podman containers with systemd, or native dnf builds. Handles SSH connectivity, SELinux, firewall-cmd, and systemd unit creation. Triggers: /rhel-deploy command, 'deploy to RHEL', 'deploy to Fedora', 'deploy to my server via SSH'. +--- + +# /rhel-deploy Skill + +**IMPORTANT:** This skill is for deploying to standalone RHEL/Fedora/CentOS systems via SSH. If user invoked `/rhel-deploy`, skip any OpenShift-related steps and proceed directly with SSH-based deployment. + +Deploy applications to standalone RHEL systems using Podman containers or native builds with systemd service management. + +## Overview + +``` +[Intro] → [SSH Connect] → [Analyze] → [Strategy] ──┬─→ [Container Path] ──→ [Complete] + │ (Podman + systemd) + │ + └─→ [Native Path] ─────→ [Complete] + (dnf + systemd) +``` + +**Deployment Strategies (user chooses one):** +- **Container** - Build/pull container image, run with Podman, manage with systemd +- **Native** - Install dependencies with dnf, run application directly, manage with systemd + +## Prerequisites + +1. SSH access to target RHEL host with sudo privileges +2. RHEL 8+, CentOS Stream, Rocky Linux, or Fedora +3. For container deployments: Podman installed on target +4. For native deployments: Required development tools available via dnf + +## Critical: Human-in-the-Loop Requirements + +**IMPORTANT:** This skill requires explicit user confirmation at each phase. You MUST: +1. **Wait for user confirmation** before executing any actions +2. **Do NOT proceed** to the next phase until the user explicitly approves +3. **Present options clearly** (yes/no/modify) and wait for response +4. **Never auto-execute** SSH commands, file transfers, or service creation + +If the user says "no" or wants modifications, address their concerns before proceeding. + +## Workflow + +### Phase 0: Introduction + +```markdown +# Deploy to RHEL Host + +I'll help you deploy your application to a RHEL system. + +**What this workflow does:** +1. **Connect** - Establish SSH connection to your RHEL host +2. **Analyze** - Check target system capabilities (Podman, SELinux, firewall) +3. **Build** - Build container image or prepare native deployment +4. **Deploy** - Configure systemd service and networking +5. **Verify** - Ensure application is running and accessible + +**Deployment Strategies:** +- **Container (Podman)** - Run your app in a container managed by systemd +- **Native** - Install and run your app directly on the host + +**What I need from you:** +- SSH access to the target RHEL host (user@host) +- sudo privileges on the target host +- Your application source code + +**Ready to begin?** (yes/no) +``` + +Wait for user confirmation before proceeding. + +### Phase 1: SSH Connection + +```markdown +## Phase 1: Connecting to RHEL Host + +**SSH Target Configuration:** + +Please provide your RHEL host details: + +| Setting | Value | Default | +|---------|-------|---------| +| Host | [required] | - | +| User | [current user] | $USER | +| Port | 22 | 22 | + +Example: `user@192.168.1.100` or `deploy@myserver.example.com` + +**Enter your SSH target:** +``` + +**Connection verification:** + +```bash +# Test SSH connection +ssh -o BatchMode=yes -o ConnectTimeout=10 [user]@[host] "echo 'Connection successful'" + +# If connection fails, provide troubleshooting: +# - Check host is reachable: ping [host] +# - Verify SSH key is configured +# - Check firewall allows SSH (port 22) +``` + +Store `RHEL_HOST`, `RHEL_USER`, `RHEL_PORT` in session state. + +### Phase 2: Target Host Analysis + +```markdown +## Phase 2: Analyzing Target Host + +Checking capabilities of [host]... + +**System Information:** +| Setting | Value | +|---------|-------| +| OS | [cat /etc/redhat-release] | +| Kernel | [uname -r] | +| Architecture | [uname -m] | + +**Container Runtime:** +| Setting | Status | +|---------|--------| +| Podman | [Installed v4.x / Not installed] | +| Container Storage | [/var/lib/containers] | + +**System Services:** +| Setting | Status | +|---------|--------| +| SELinux | [Enforcing / Permissive / Disabled] | +| Firewall | [Active / Inactive] | +| systemd | Running | + +Is this the correct target host? (yes/no) +``` + +**WAIT for user confirmation before proceeding.** Do NOT continue to Phase 3 until user confirms. + +If user says "no", ask what needs to be changed or allow them to specify a different host. + +**Commands to gather information:** + +```bash +# RHEL version +ssh [target] "cat /etc/redhat-release" + +# Podman check +ssh [target] "podman --version 2>/dev/null || echo 'Not installed'" + +# SELinux status +ssh [target] "getenforce" + +# Firewall status +ssh [target] "firewall-cmd --state 2>/dev/null || echo 'Not running'" +``` + +Store `RHEL_VERSION`, `PODMAN_AVAILABLE`, `SELINUX_STATUS`, `FIREWALL_STATUS` in session state. + +### Phase 3: Strategy Selection + +```markdown +## Deployment Strategy + +Based on your project ([language]/[framework]) and target capabilities: + +| Strategy | Description | Requirements | +|----------|-------------|--------------| +| **Container** | Build image, run with Podman + systemd | Podman installed | +| **Native** | Install with dnf, run directly + systemd | Runtime packages available | + +**Recommendation:** [Container/Native] because [reason] + +**Which deployment strategy would you like to use?** +1. Container - Deploy using Podman +2. Native - Deploy directly on host +``` + +**WAIT for user to select a strategy.** Do NOT proceed until user makes a choice. + +**If Podman not installed and user selects Container:** +```markdown +Podman is not installed on the target. Would you like me to install it? + +```bash +sudo dnf install -y podman +``` + +Proceed with Podman installation? (yes/no) +``` + +**WAIT for user confirmation.** Only install Podman if user explicitly says "yes". + +Store `DEPLOYMENT_STRATEGY` in session state. + +--- + +## CONTAINER PATH (If DEPLOYMENT_STRATEGY is "Container") + +### Phase 4a-1: Image Selection + +```markdown +## Container Image + +**Options:** + +1. **Build on target** - Transfer source, build with Podman on RHEL host +2. **Build locally and transfer** - Build here, push to registry or transfer +3. **Use existing image** - Pull from registry (e.g., quay.io, docker.io) + +Which approach would you prefer? +``` + +**WAIT for user to select an option.** Do NOT proceed until user makes a choice. + +**For options 1 and 2 (building an image):** + +If no Containerfile/Dockerfile exists in the project, delegate to `/recommend-image`: + +```markdown +## Selecting Base Image + +To build your container, I need to select an appropriate base image. + +Invoking `/recommend-image` to get the optimal UBI image for your [language]/[framework] project... +``` + +Use the `BUILDER_IMAGE` output from `/recommend-image` as the base image in the Containerfile. + +**For build on target:** +```bash +# Transfer source +rsync -avz --exclude node_modules --exclude .git ./ [target]:/tmp/[app-name]-build/ + +# If no Containerfile exists, create one with recommended base image +ssh [target] "test -f /tmp/[app-name]-build/Containerfile -o -f /tmp/[app-name]-build/Dockerfile" || \ +ssh [target] "cat > /tmp/[app-name]-build/Containerfile << 'EOF' +FROM [BUILDER_IMAGE] +# Containerfile generated using /recommend-image +WORKDIR /app +COPY . . +# Add language-specific build and run commands +EOF" + +# Build on target +ssh [target] "cd /tmp/[app-name]-build && podman build -t [app-name]:latest ." +``` + +**For existing image:** +```bash +# Pull image on target +ssh [target] "podman pull [image-reference]" +``` + +### Phase 4a-2: Container Configuration + +```markdown +## Container Configuration + +**Container Settings:** +| Setting | Value | +|---------|-------| +| Name | [app-name] | +| Image | [image-ref] | +| Port Mapping | [host-port]:[container-port] | +| Volume Mounts | [list any persistent data paths] | +| Environment | [list env vars] | +| Run Mode | [rootless / rootful] | + +**SELinux Volume Labels:** +- Use `:z` for shared volumes (multiple containers) +- Use `:Z` for private volumes (single container) + +Proceed with this configuration? (yes/modify/cancel) +``` + +**WAIT for user confirmation.** Do NOT proceed until user explicitly approves. + +- If user says "yes" → Continue to systemd unit creation +- If user says "modify" → Ask what they would like to change +- If user says "cancel" → Stop and preserve current state + +### Phase 4a-3: Systemd Unit Creation + +```markdown +## Systemd Service Configuration + +Creating systemd unit for Podman container. + +**Locations:** +- Rootless: `~/.config/systemd/user/[app-name].service` +- Rootful: `/etc/systemd/system/[app-name].service` + +Proceed with creating this service? (yes/no) +``` + +**WAIT for user confirmation.** Do NOT create the systemd unit until user explicitly says "yes". + +- If user says "yes" → Create the service unit file using the template +- If user says "no" → Ask what they would like to change + +**Commands to execute:** + +```bash +# For rootful: +ssh [target] "sudo tee /etc/systemd/system/[app-name].service" < [unit-file] +ssh [target] "sudo systemctl daemon-reload" +ssh [target] "sudo systemctl enable --now [app-name]" + +# For rootless: +ssh [target] "mkdir -p ~/.config/systemd/user" +ssh [target] "tee ~/.config/systemd/user/[app-name].service" < [unit-file] +ssh [target] "systemctl --user daemon-reload" +ssh [target] "systemctl --user enable --now [app-name]" +ssh [target] "loginctl enable-linger [user]" # Keep user services running +``` + +### Phase 4a-4: Firewall Configuration + +```markdown +## Firewall Configuration + +Opening port [port] for application access. + +**Commands to execute:** +```bash +# Open port permanently +sudo firewall-cmd --permanent --add-port=[port]/tcp + +# Reload firewall +sudo firewall-cmd --reload + +# Verify +sudo firewall-cmd --list-ports +``` + +Proceed with firewall configuration? (yes/skip) +``` + +**WAIT for user confirmation.** Do NOT modify firewall until user explicitly responds. + +- If user says "yes" → Configure firewall +- If user says "skip" → Skip firewall configuration and continue + +--- + +## NATIVE PATH (If DEPLOYMENT_STRATEGY is "Native") + +### Phase 4b-1: Dependency Installation + +```markdown +## Installing Dependencies + +**Runtime packages for [language]:** + +| Language | RHEL 8 | RHEL 9 | +|----------|--------|--------| +| Node.js 18 | `dnf module enable nodejs:18 && dnf install nodejs` | `dnf install nodejs` | +| Node.js 20 | `dnf module enable nodejs:20 && dnf install nodejs` | `dnf module enable nodejs:20 && dnf install nodejs` | +| Python 3.9 | `dnf install python39 python39-pip` | `dnf install python3 python3-pip` | +| Python 3.11 | N/A | `dnf install python3.11 python3.11-pip` | +| Java 17 | `dnf install java-17-openjdk` | `dnf install java-17-openjdk` | +| Java 21 | N/A | `dnf install java-21-openjdk` | +| Go | `dnf install go-toolset` | `dnf install golang` | + +**Commands to execute:** +```bash +ssh [target] "sudo dnf install -y [packages]" +``` + +Proceed with installation? (yes/no) +``` + +**WAIT for user confirmation.** Do NOT install packages until user explicitly says "yes". + +### Phase 4b-2: Application Deployment + +```markdown +## Deploying Application + +**Deployment location:** `/opt/[app-name]` + +**Steps:** +1. Create application directory +2. Transfer source code via rsync +3. Install application dependencies +4. Set ownership and permissions +5. Configure SELinux context + +```bash +# Create directory +ssh [target] "sudo mkdir -p /opt/[app-name]" + +# Transfer files +rsync -avz --exclude node_modules --exclude .git --exclude __pycache__ \ + ./ [target]:/tmp/[app-name]-deploy/ +ssh [target] "sudo cp -r /tmp/[app-name]-deploy/* /opt/[app-name]/" + +# Install dependencies (example for Node.js) +ssh [target] "cd /opt/[app-name] && npm install --production" + +# Set permissions +ssh [target] "sudo chown -R [service-user]:[service-user] /opt/[app-name]" + +# SELinux context +ssh [target] "sudo semanage fcontext -a -t bin_t '/opt/[app-name](/.*)?'" +ssh [target] "sudo restorecon -Rv /opt/[app-name]" +``` + +Proceed with deployment? (yes/no) +``` + +**WAIT for user confirmation.** Do NOT transfer files or deploy until user explicitly says "yes". + +### Phase 4b-3: Native Systemd Unit + +```markdown +## Systemd Service Configuration + +**Location:** `/etc/systemd/system/[app-name].service` + +**Start commands by language:** +| Language | ExecStart Example | +|----------|-------------------| +| Node.js | `/usr/bin/node /opt/[app-name]/server.js` | +| Python | `/usr/bin/python3 /opt/[app-name]/app.py` | +| Java | `/usr/bin/java -jar /opt/[app-name]/app.jar` | +| Go | `/opt/[app-name]/[binary-name]` | + +Proceed with creating this service? (yes/no) +``` + +**WAIT for user confirmation.** Do NOT create the systemd unit until user explicitly says "yes". + +### Phase 4b-4: Firewall Configuration + +Same as container path - open required port with firewall-cmd. + +--- + +## COMPLETION (Both paths converge here) + +### Phase 5: Completion + +```markdown +## Deployment Complete! + +Your application is now running on [host]. + +--- + +**Application Summary:** +| Setting | Value | +|---------|-------| +| Name | [app-name] | +| Host | [host] | +| Strategy | [Container/Native] | +| Service | [app-name].service | + +--- + +**Access URLs:** +| Type | URL | +|------|-----| +| HTTP | http://[host]:[port] | +| SSH | ssh [user]@[host] | + +--- + +**Service Status:** +``` +[systemctl status output] +``` + +--- + +**Quick Commands:** + +```bash +# View logs +sudo journalctl -u [app-name] -f + +# Restart service +sudo systemctl restart [app-name] + +# Stop service +sudo systemctl stop [app-name] + +# Check status +sudo systemctl status [app-name] + +# View container logs (if container deployment) +podman logs -f [app-name] + +# Remove deployment +sudo systemctl disable --now [app-name] +sudo rm /etc/systemd/system/[app-name].service +# For container: podman rm [app-name] +# For native: sudo rm -rf /opt/[app-name] +``` + +--- + +Your application is live! +``` + +## Delegated Skills + +This skill delegates to other skills when needed: + +| Scenario | Delegated Skill | Purpose | +|----------|-----------------|---------| +| Building container image (no Containerfile) | `/recommend-image` | Get optimal UBI base image for the detected language/framework | + +When delegating to `/recommend-image`: +1. Provide detected `LANGUAGE`, `FRAMEWORK`, and `VERSION` from project analysis +2. Receive back: `BUILDER_IMAGE`, `IMAGE_VARIANT`, `SELECTION_RATIONALE` +3. Use `BUILDER_IMAGE` as the FROM image in generated Containerfile diff --git a/rh-developer/skills/s2i-build/SKILL.md b/rh-developer/skills/s2i-build/SKILL.md new file mode 100644 index 00000000..6cf3018e --- /dev/null +++ b/rh-developer/skills/s2i-build/SKILL.md @@ -0,0 +1,416 @@ +--- +name: s2i-build +description: | + Create BuildConfig and ImageStream resources on OpenShift and trigger a Source-to-Image (S2I) build. Use this skill after /detect-project to build container images from source code on the cluster. Handles namespace verification, resource creation with user confirmation, build monitoring with log streaming, and failure recovery. Triggers on /s2i-build command. Run before /deploy. +--- + +# /s2i-build Skill + +Create the necessary OpenShift resources (BuildConfig, ImageStream) and trigger a Source-to-Image build on the cluster. + +## Prerequisites + +Before running this skill, ensure: +1. User is logged into OpenShift cluster +2. Target namespace/project exists or can be created +3. Git repository URL is available (or will use binary build) + +## Critical: Human-in-the-Loop Requirements + +**IMPORTANT:** This skill requires explicit user confirmation at each step. You MUST: +1. **Wait for user confirmation** before executing any actions +2. **Do NOT proceed** to the next step until the user explicitly approves +3. **Present options clearly** (yes/no/modify) and wait for response +4. **Never auto-execute** resource creation, builds, or deployments + +If the user says "no" or wants modifications, address their concerns before proceeding. + +## Workflow + +### Step 1: Check OpenShift Connection + +Use kubernetes MCP to verify connection: + +```markdown +## Checking OpenShift Connection... + +**Cluster:** [cluster-url from kubeconfig] +**User:** [current user] +**Current Namespace:** [current namespace] + +Is this the correct cluster and namespace for the build? +- yes - Continue +- no - Let me switch context +``` + +**WAIT for user confirmation before proceeding.** Do NOT continue to Step 2 until user confirms. + +If user says "no", wait for them to switch context and tell you to continue. + +If connection fails, show error from error-handling-agent. + +### Step 2: Gather Build Information + +Collect required information (from /detect-project or ask user): + +```markdown +## S2I Build Configuration + +I need the following information: + +| Setting | Current Value | Source | +|---------|---------------|--------| +| App Name | `[name]` | [from detect-project / folder name] | +| Git URL | `[url]` | [from .git/config / needs input] | +| Git Branch | `main` | [default] | +| S2I Builder | `[image]` | [from detect-project / needs input] | +| Namespace | `[ns]` | [from current context] | + +[For Python projects only - include these rows if PYTHON_ENTRY_FILE is set] +| Entry Point | `[PYTHON_ENTRY_FILE]` | [from detect-project] | +| APP_MODULE | `[PYTHON_APP_MODULE]` | [Python only - required if entry point != app.py] | +| gunicorn | [Found / Missing] | [from detect-project] | + +Please confirm these values or tell me what to change. +``` + +**Python Entry Point Warning:** + +If `PYTHON_ENTRY_FILE` is NOT `app.py` AND `PYTHON_HAS_GUNICORN` is `false`: + +```markdown +## Python Configuration Issue + +Your application uses `[PYTHON_ENTRY_FILE]` as entry point, but `gunicorn` is not in your requirements. + +**This build will FAIL** because: +- The S2I Python builder requires `gunicorn` to use `APP_MODULE` +- Without gunicorn, it looks for `app.py` (which doesn't exist) + +**Please choose:** +1. **Add gunicorn** - Add `gunicorn` to requirements.txt and retry +2. **Rename entry point** - Rename `[main.py]` to `app.py` +3. **Continue anyway** - Proceed (build will likely fail) +``` + +**WAIT for user to resolve the issue before proceeding.** + +**WAIT for user confirmation before proceeding.** Do NOT continue until user explicitly confirms these values or provides corrections. + +**To detect Git URL:** +- Read `.git/config` and extract `[remote "origin"]` url + +### Step 3: Verify Namespace + +Use kubernetes MCP `resources_list` to check if namespace exists: + +```markdown +## Namespace Check + +Checking if namespace `[namespace]` exists... + +[If exists] +Namespace `[namespace]` exists and you have access. + +[If not exists] +Namespace `[namespace]` does not exist. + +Would you like me to create it? (yes/no) +``` + +**WAIT for user confirmation.** Only create the namespace if user explicitly says "yes". + +If creating namespace, use `resources_create_or_update`: +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: [namespace] +``` + +### Step 4: Create ImageStream + +Show the ImageStream that will be created: + +```markdown +## Step 1 of 3: Create ImageStream + +An ImageStream stores references to your built container images. + +```yaml +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: [app-name] + namespace: [namespace] + labels: + app: [app-name] + app.kubernetes.io/name: [app-name] +spec: + lookupPolicy: + local: false +``` + +**Proceed with creating this ImageStream?** (yes/no) +``` + +**WAIT for user confirmation.** Do NOT create the ImageStream until user explicitly says "yes". + +- If user says "yes" → Use kubernetes MCP `resources_create_or_update` to apply +- If user says "no" → Ask what they would like to change +- If user wants modifications → Update the YAML and show again for confirmation + +### Step 5: Create BuildConfig + +Show the BuildConfig: + +**For non-Python projects OR Python with app.py entry point:** + +```markdown +## Step 2 of 3: Create BuildConfig + +A BuildConfig defines how to build your application using S2I. + +```yaml +apiVersion: build.openshift.io/v1 +kind: BuildConfig +metadata: + name: [app-name] + namespace: [namespace] + labels: + app: [app-name] + app.kubernetes.io/name: [app-name] +spec: + source: + type: Git + git: + uri: [git-url] + ref: [git-branch] + strategy: + type: Source + sourceStrategy: + from: + kind: DockerImage + name: [builder-image] + output: + to: + kind: ImageStreamTag + name: [app-name]:latest + triggers: + - type: ConfigChange + - type: ImageChange + runPolicy: Serial +``` + +**This BuildConfig will:** +- Pull source from: `[git-url]` (branch: `[git-branch]`) +- Build using S2I with: `[builder-image]` +- Push result to: `[app-name]:latest` ImageStream + +**Proceed with creating this BuildConfig?** (yes/no) +``` + +**For Python projects with non-default entry point (e.g., main.py):** + +```markdown +## Step 2 of 3: Create BuildConfig + +A BuildConfig defines how to build your application using S2I. + +```yaml +apiVersion: build.openshift.io/v1 +kind: BuildConfig +metadata: + name: [app-name] + namespace: [namespace] + labels: + app: [app-name] + app.kubernetes.io/name: [app-name] +spec: + source: + type: Git + git: + uri: [git-url] + ref: [git-branch] + strategy: + type: Source + sourceStrategy: + from: + kind: DockerImage + name: [builder-image] + # Python S2I: Required when entry point is not app.py + env: + - name: APP_MODULE + value: "[PYTHON_APP_MODULE]" # e.g., "main:app" + output: + to: + kind: ImageStreamTag + name: [app-name]:latest + triggers: + - type: ConfigChange + - type: ImageChange + runPolicy: Serial +``` + +**This BuildConfig will:** +- Pull source from: `[git-url]` (branch: `[git-branch]`) +- Build using S2I with: `[builder-image]` +- Push result to: `[app-name]:latest` ImageStream + +**Python Entry Point Configuration:** +- Entry point file: `[PYTHON_ENTRY_FILE]` +- APP_MODULE: `[PYTHON_APP_MODULE]` +- This tells the S2I Python builder how to start your application with gunicorn. + +**Proceed with creating this BuildConfig?** (yes/no) +``` + +**WAIT for user confirmation.** Do NOT create the BuildConfig until user explicitly says "yes". + +- If user says "yes" → Use kubernetes MCP `resources_create_or_update` to apply +- If user says "no" → Ask what they would like to change +- If user wants modifications → Update the YAML and show again for confirmation + +### Step 6: Start Build + +```markdown +## Step 3 of 3: Start Build + +Resources created successfully! + +| Resource | Name | Status | +|----------|------|--------| +| ImageStream | [app-name] | Created | +| BuildConfig | [app-name] | Created | + +**Would you like me to start a build now?** (yes/no) + +(You can also trigger builds later with: oc start-build [app-name]) +``` + +**WAIT for user confirmation.** Do NOT start the build until user explicitly says "yes". + +- If user says "yes" → Create a Build resource as shown below +- If user says "no" → Complete this step and inform user how to start build manually later + +If yes, create a Build resource: +```yaml +apiVersion: build.openshift.io/v1 +kind: Build +metadata: + generateName: [app-name]- + namespace: [namespace] + labels: + app: [app-name] + buildconfig: [app-name] + annotations: + openshift.io/build-config.name: [app-name] +spec: + serviceAccount: builder + source: + type: Git + git: + uri: [git-url] + ref: [git-branch] + strategy: + type: Source + sourceStrategy: + from: + kind: DockerImage + name: [builder-image] + output: + to: + kind: ImageStreamTag + name: [app-name]:latest + triggeredBy: + - message: Manually triggered +``` + +### Step 7: Monitor Build + +Stream build logs using kubernetes MCP `pod_logs`: + +```markdown +## Build Progress + +**Build:** [app-name]-1 +**Status:** Running +**Phase:** [current phase] + +--- +[Streaming build logs here] +--- + +[When complete] + +## Build Complete! + +**Build:** [app-name]-1 +**Status:** Complete +**Duration:** [X]m [Y]s +**Image:** image-registry.openshift-image-registry.svc:5000/[namespace]/[app-name]:latest + +**CRITICAL: Ensure the build status is 'Complete' before proceeding to deployment.** + +The image is ready for deployment. +Run `/deploy` to create Deployment, Service, and Route. +``` + +### Step 8: Handle Build Failure + +If build fails: + +```markdown +## Build Failed + +**Build:** [app-name]-1 +**Status:** Failed +**Phase:** [phase where it failed] + +**Error:** +``` +[Last 20 lines of build log] +``` + +**Common causes for [phase] failure:** +- [relevant troubleshooting tips] + +**Options:** +1. View full build logs +2. Delete failed build and retry +3. Update BuildConfig and retry +4. Cancel and troubleshoot + +What would you like to do? +``` + +## MCP Tools Used + +| Tool | Purpose | +|------|---------| +| `resources_list` | Check namespaces, existing ImageStreams/BuildConfigs | +| `resources_get` | Get specific resource details | +| `resources_create_or_update` | Create ImageStream, BuildConfig, Build | +| `pod_logs` | Stream build logs (builds run as pods) | +| `pod_list` | Find builder pod | +| `events_list` | Check for build events/errors | + +## Required Inputs + +| Input | Auto-detected | Must Confirm | +|-------|---------------|--------------| +| App name | Yes (from detect-project) | Yes | +| Git URL | Yes (from .git/config) | Yes | +| Git branch | Yes (default: main) | Optional | +| S2I image | Yes (from detect-project) | Yes | +| Namespace | Yes (from kubeconfig) | Yes | + +## Output + +On success, these values are available for `/deploy`: + +| Variable | Value | +|----------|-------| +| `IMAGE_REF` | `image-registry.openshift-image-registry.svc:5000/[ns]/[app]:latest` | +| `IMAGESTREAM_TAG` | `[app]:latest` | +| `BUILD_NAME` | `[app]-1` | diff --git a/rh-developer/templates/buildconfig.yaml.template b/rh-developer/templates/buildconfig.yaml.template new file mode 100644 index 00000000..b3294eb2 --- /dev/null +++ b/rh-developer/templates/buildconfig.yaml.template @@ -0,0 +1,38 @@ +apiVersion: build.openshift.io/v1 +kind: BuildConfig +metadata: + name: ${APP_NAME} + namespace: ${NAMESPACE} + labels: + app: ${APP_NAME} + app.kubernetes.io/name: ${APP_NAME} + app.kubernetes.io/component: build + app.kubernetes.io/part-of: ${APP_NAME} +spec: + source: + type: Git + git: + uri: ${GIT_URL} + ref: ${GIT_BRANCH} + strategy: + type: Source + sourceStrategy: + from: + kind: DockerImage + name: ${BUILDER_IMAGE} + env: [] + output: + to: + kind: ImageStreamTag + name: ${APP_NAME}:latest + triggers: + - type: ConfigChange + - type: ImageChange + runPolicy: Serial + resources: + limits: + memory: "1Gi" + cpu: "1" + requests: + memory: "512Mi" + cpu: "500m" diff --git a/rh-developer/templates/deployment.yaml.template b/rh-developer/templates/deployment.yaml.template new file mode 100644 index 00000000..eb3b481a --- /dev/null +++ b/rh-developer/templates/deployment.yaml.template @@ -0,0 +1,61 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ${APP_NAME} + namespace: ${NAMESPACE} + labels: + app: ${APP_NAME} + app.kubernetes.io/name: ${APP_NAME} + app.kubernetes.io/component: application + app.kubernetes.io/part-of: ${APP_NAME} + annotations: + image.openshift.io/triggers: | + [{"from":{"kind":"ImageStreamTag","name":"${APP_NAME}:latest"},"fieldPath":"spec.template.spec.containers[0].image"}] +spec: + replicas: ${REPLICAS} + selector: + matchLabels: + app: ${APP_NAME} + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + template: + metadata: + labels: + app: ${APP_NAME} + app.kubernetes.io/name: ${APP_NAME} + spec: + containers: + - name: ${APP_NAME} + image: image-registry.openshift-image-registry.svc:5000/${NAMESPACE}/${APP_NAME}:latest + ports: + - containerPort: ${CONTAINER_PORT} + protocol: TCP + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: / + port: ${CONTAINER_PORT} + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + readinessProbe: + httpGet: + path: / + port: ${CONTAINER_PORT} + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + env: [] + restartPolicy: Always + terminationGracePeriodSeconds: 30 diff --git a/rh-developer/templates/helm/Chart.yaml.template b/rh-developer/templates/helm/Chart.yaml.template new file mode 100644 index 00000000..1aa22dd1 --- /dev/null +++ b/rh-developer/templates/helm/Chart.yaml.template @@ -0,0 +1,13 @@ +apiVersion: v2 +name: ${APP_NAME} +description: ${APP_DESCRIPTION} +type: application +version: 0.1.0 +appVersion: "${APP_VERSION}" +keywords: + - ${LANGUAGE} + - ${FRAMEWORK} + - openshift +maintainers: + - name: ${MAINTAINER_NAME} + email: ${MAINTAINER_EMAIL} diff --git a/rh-developer/templates/helm/templates/NOTES.txt.template b/rh-developer/templates/helm/templates/NOTES.txt.template new file mode 100644 index 00000000..154e628d --- /dev/null +++ b/rh-developer/templates/helm/templates/NOTES.txt.template @@ -0,0 +1,32 @@ +Congratulations! Your application {{ include "${APP_NAME}.fullname" . }} has been deployed. + +{{- if .Values.route.enabled }} + +Access your application at: +{{- if .Values.route.host }} + https://{{ .Values.route.host }} +{{- else }} + Run: oc get route {{ include "${APP_NAME}.fullname" . }} -o jsonpath='{.spec.host}' +{{- end }} + +{{- else }} + +Your application is available internally at: + {{ include "${APP_NAME}.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.port }} + +To expose it externally, create a Route or set route.enabled=true. + +{{- end }} + +Useful commands: + # View pods + oc get pods -l app.kubernetes.io/name={{ include "${APP_NAME}.name" . }} + + # View logs + oc logs -l app.kubernetes.io/name={{ include "${APP_NAME}.name" . }} -f + + # Upgrade release + helm upgrade {{ .Release.Name }} ./{{ .Chart.Name }} -f values.yaml + + # Uninstall release + helm uninstall {{ .Release.Name }} diff --git a/rh-developer/templates/helm/templates/_helpers.tpl.template b/rh-developer/templates/helm/templates/_helpers.tpl.template new file mode 100644 index 00000000..15873b10 --- /dev/null +++ b/rh-developer/templates/helm/templates/_helpers.tpl.template @@ -0,0 +1,60 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "${APP_NAME}.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "${APP_NAME}.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "${APP_NAME}.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "${APP_NAME}.labels" -}} +helm.sh/chart: {{ include "${APP_NAME}.chart" . }} +{{ include "${APP_NAME}.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "${APP_NAME}.selectorLabels" -}} +app.kubernetes.io/name: {{ include "${APP_NAME}.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "${APP_NAME}.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "${APP_NAME}.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/rh-developer/templates/helm/templates/deployment.yaml.template b/rh-developer/templates/helm/templates/deployment.yaml.template new file mode 100644 index 00000000..a6cbd868 --- /dev/null +++ b/rh-developer/templates/helm/templates/deployment.yaml.template @@ -0,0 +1,61 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "${APP_NAME}.fullname" . }} + labels: + {{- include "${APP_NAME}.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "${APP_NAME}.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "${APP_NAME}.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "${APP_NAME}.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.env }} + env: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/rh-developer/templates/helm/templates/route.yaml.template b/rh-developer/templates/helm/templates/route.yaml.template new file mode 100644 index 00000000..e2bab29a --- /dev/null +++ b/rh-developer/templates/helm/templates/route.yaml.template @@ -0,0 +1,24 @@ +{{- if .Values.route.enabled }} +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: {{ include "${APP_NAME}.fullname" . }} + labels: + {{- include "${APP_NAME}.labels" . | nindent 4 }} +spec: + {{- if .Values.route.host }} + host: {{ .Values.route.host }} + {{- end }} + to: + kind: Service + name: {{ include "${APP_NAME}.fullname" . }} + weight: 100 + port: + targetPort: http + {{- with .Values.route.tls }} + tls: + termination: {{ .termination }} + insecureEdgeTerminationPolicy: {{ .insecureEdgeTerminationPolicy }} + {{- end }} + wildcardPolicy: None +{{- end }} diff --git a/rh-developer/templates/helm/templates/service.yaml.template b/rh-developer/templates/helm/templates/service.yaml.template new file mode 100644 index 00000000..837bc888 --- /dev/null +++ b/rh-developer/templates/helm/templates/service.yaml.template @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "${APP_NAME}.fullname" . }} + labels: + {{- include "${APP_NAME}.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "${APP_NAME}.selectorLabels" . | nindent 4 }} diff --git a/rh-developer/templates/helm/values.yaml.template b/rh-developer/templates/helm/values.yaml.template new file mode 100644 index 00000000..1cca6017 --- /dev/null +++ b/rh-developer/templates/helm/values.yaml.template @@ -0,0 +1,67 @@ +# Default values for ${APP_NAME} +replicaCount: 1 + +image: + repository: ${IMAGE_REPOSITORY} + pullPolicy: IfNotPresent + tag: "${IMAGE_TAG}" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + create: true + annotations: {} + name: "" + +podAnnotations: {} +podSecurityContext: {} +securityContext: {} + +service: + type: ClusterIP + port: ${CONTAINER_PORT} + +route: + enabled: true + host: "" + tls: + termination: edge + insecureEdgeTerminationPolicy: Redirect + +resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" + +livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + +readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 5 + periodSeconds: 5 + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + +nodeSelector: {} +tolerations: [] +affinity: {} + +env: [] +# - name: MY_VAR +# value: "my-value" diff --git a/rh-developer/templates/imagestream.yaml.template b/rh-developer/templates/imagestream.yaml.template new file mode 100644 index 00000000..46572193 --- /dev/null +++ b/rh-developer/templates/imagestream.yaml.template @@ -0,0 +1,13 @@ +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: ${APP_NAME} + namespace: ${NAMESPACE} + labels: + app: ${APP_NAME} + app.kubernetes.io/name: ${APP_NAME} + app.kubernetes.io/component: image + app.kubernetes.io/part-of: ${APP_NAME} +spec: + lookupPolicy: + local: false diff --git a/rh-developer/templates/route.yaml.template b/rh-developer/templates/route.yaml.template new file mode 100644 index 00000000..7c53d2e7 --- /dev/null +++ b/rh-developer/templates/route.yaml.template @@ -0,0 +1,21 @@ +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: ${APP_NAME} + namespace: ${NAMESPACE} + labels: + app: ${APP_NAME} + app.kubernetes.io/name: ${APP_NAME} + app.kubernetes.io/component: route + app.kubernetes.io/part-of: ${APP_NAME} +spec: + to: + kind: Service + name: ${APP_NAME} + weight: 100 + port: + targetPort: http + tls: + termination: edge + insecureEdgeTerminationPolicy: Redirect + wildcardPolicy: None diff --git a/rh-developer/templates/service.yaml.template b/rh-developer/templates/service.yaml.template new file mode 100644 index 00000000..7e1cf371 --- /dev/null +++ b/rh-developer/templates/service.yaml.template @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + name: ${APP_NAME} + namespace: ${NAMESPACE} + labels: + app: ${APP_NAME} + app.kubernetes.io/name: ${APP_NAME} + app.kubernetes.io/component: service + app.kubernetes.io/part-of: ${APP_NAME} +spec: + selector: + app: ${APP_NAME} + ports: + - name: http + port: ${CONTAINER_PORT} + targetPort: ${CONTAINER_PORT} + protocol: TCP + type: ClusterIP + sessionAffinity: None diff --git a/rh-developer/templates/systemd/systemd-container-rootful.service b/rh-developer/templates/systemd/systemd-container-rootful.service new file mode 100644 index 00000000..75aed8b4 --- /dev/null +++ b/rh-developer/templates/systemd/systemd-container-rootful.service @@ -0,0 +1,27 @@ +# Rootful Podman container managed by systemd (system service) +# Location: /etc/systemd/system/${APP_NAME}.service +# +# Variables to replace: +# ${APP_NAME} - Application name +# ${PORT} - Container port mapping (host:container) +# ${IMAGE} - Container image reference + +[Unit] +Description=${APP_NAME} Container +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +Restart=always +RestartSec=5 +ExecStartPre=-/usr/bin/podman stop -t 10 ${APP_NAME} +ExecStartPre=-/usr/bin/podman rm ${APP_NAME} +ExecStart=/usr/bin/podman run --name ${APP_NAME} \ + -p ${PORT}:${PORT} \ + --rm \ + ${IMAGE} +ExecStop=/usr/bin/podman stop -t 10 ${APP_NAME} + +[Install] +WantedBy=multi-user.target diff --git a/rh-developer/templates/systemd/systemd-container-rootless.service b/rh-developer/templates/systemd/systemd-container-rootless.service new file mode 100644 index 00000000..e4185e21 --- /dev/null +++ b/rh-developer/templates/systemd/systemd-container-rootless.service @@ -0,0 +1,27 @@ +# Rootless Podman container managed by systemd (user service) +# Location: ~/.config/systemd/user/${APP_NAME}.service +# +# Variables to replace: +# ${APP_NAME} - Application name +# ${PORT} - Container port mapping (host:container) +# ${IMAGE} - Container image reference + +[Unit] +Description=${APP_NAME} Container +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +Restart=always +RestartSec=5 +ExecStartPre=-/usr/bin/podman stop -t 10 ${APP_NAME} +ExecStartPre=-/usr/bin/podman rm ${APP_NAME} +ExecStart=/usr/bin/podman run --name ${APP_NAME} \ + -p ${PORT}:${PORT} \ + --rm \ + ${IMAGE} +ExecStop=/usr/bin/podman stop -t 10 ${APP_NAME} + +[Install] +WantedBy=default.target diff --git a/rh-developer/templates/systemd/systemd-native.service b/rh-developer/templates/systemd/systemd-native.service new file mode 100644 index 00000000..9e3eadc7 --- /dev/null +++ b/rh-developer/templates/systemd/systemd-native.service @@ -0,0 +1,40 @@ +# Native application managed by systemd (system service) +# Location: /etc/systemd/system/${APP_NAME}.service +# +# Variables to replace: +# ${APP_NAME} - Application name +# ${SERVICE_USER} - User to run the service as +# ${APP_PATH} - Application install path (e.g., /opt/app-name) +# ${PORT} - Application listen port +# ${START_COMMAND} - Application start command +# +# Start command examples by language: +# Node.js: /usr/bin/node ${APP_PATH}/server.js +# Python: /usr/bin/python3 ${APP_PATH}/app.py +# Java: /usr/bin/java -jar ${APP_PATH}/app.jar +# Go: ${APP_PATH}/binary-name + +[Unit] +Description=${APP_NAME} Service +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=${SERVICE_USER} +WorkingDirectory=${APP_PATH} +Environment=NODE_ENV=production +Environment=PORT=${PORT} +ExecStart=${START_COMMAND} +Restart=always +RestartSec=5 + +# Security hardening +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +PrivateTmp=true +ReadWritePaths=${APP_PATH} + +[Install] +WantedBy=multi-user.target