diff --git a/.claude/skills/nginx-to-higress-migration/SKILL.md b/.claude/skills/nginx-to-higress-migration/SKILL.md new file mode 100644 index 000000000..f151f3760 --- /dev/null +++ b/.claude/skills/nginx-to-higress-migration/SKILL.md @@ -0,0 +1,304 @@ +--- +name: nginx-to-higress-migration +description: "Migrate from ingress-nginx to Higress in Kubernetes environments. Use when (1) analyzing existing ingress-nginx setup (2) reading nginx Ingress resources and ConfigMaps (3) installing Higress via helm with proper ingressClass (4) identifying unsupported nginx annotations (5) generating WASM plugins for nginx snippets/advanced features (6) building and deploying custom plugins to image registry. Supports full migration workflow with compatibility analysis and plugin generation." +--- + +# Nginx to Higress Migration + +Automate migration from ingress-nginx to Higress in Kubernetes environments. + +## Prerequisites + +- kubectl configured with cluster access +- helm 3.x installed +- Go 1.24+ (for WASM plugin compilation) +- Docker (for plugin image push) + +## Migration Workflow + +### Phase 1: Discovery + +```bash +# Check for ingress-nginx installation +kubectl get pods -A | grep ingress-nginx +kubectl get ingressclass + +# List all Ingress resources using nginx class +kubectl get ingress -A -o json | jq '.items[] | select(.spec.ingressClassName=="nginx" or .metadata.annotations["kubernetes.io/ingress.class"]=="nginx")' + +# Get nginx ConfigMap +kubectl get configmap -n ingress-nginx ingress-nginx-controller -o yaml +``` + +### Phase 2: Compatibility Analysis + +Run the analysis script to identify unsupported features: + +```bash +./scripts/analyze-ingress.sh [namespace] +``` + +**Key point: No Ingress modification needed!** + +Higress natively supports `nginx.ingress.kubernetes.io/*` annotations - your existing Ingress resources work as-is. + +See [references/annotation-mapping.md](references/annotation-mapping.md) for the complete list of supported annotations. + +**Unsupported annotations** (require built-in plugin or custom WASM plugin): +- `nginx.ingress.kubernetes.io/server-snippet` +- `nginx.ingress.kubernetes.io/configuration-snippet` +- `nginx.ingress.kubernetes.io/lua-resty-waf*` +- Complex Lua logic in snippets + +For these, check [references/builtin-plugins.md](references/builtin-plugins.md) first - Higress may already have a plugin! + +### Phase 3: Higress Installation (Parallel with nginx) + +Higress natively supports `nginx.ingress.kubernetes.io/*` annotations. Install Higress **alongside** nginx for safe parallel testing. + +```bash +# 1. Get current nginx ingressClass name +INGRESS_CLASS=$(kubectl get ingressclass -o jsonpath='{.items[?(@.spec.controller=="k8s.io/ingress-nginx")].metadata.name}') +echo "Current nginx ingressClass: $INGRESS_CLASS" + +# 2. Detect timezone and select nearest registry +# China/Asia: higress-registry.cn-hangzhou.cr.aliyuncs.com (default) +# North America: higress-registry.us-west-1.cr.aliyuncs.com +# Southeast Asia: higress-registry.ap-southeast-7.cr.aliyuncs.com +TZ_OFFSET=$(date +%z) +case "$TZ_OFFSET" in + -1*|-0*) REGISTRY="higress-registry.us-west-1.cr.aliyuncs.com" ;; # Americas + +07*|+08*|+09*) REGISTRY="higress-registry.cn-hangzhou.cr.aliyuncs.com" ;; # Asia + +05*|+06*) REGISTRY="higress-registry.ap-southeast-7.cr.aliyuncs.com" ;; # Southeast Asia + *) REGISTRY="higress-registry.cn-hangzhou.cr.aliyuncs.com" ;; # Default +esac +echo "Using registry: $REGISTRY" + +# 3. Add Higress repo +helm repo add higress https://higress.io/helm-charts +helm repo update + +# 4. Install Higress with parallel-safe settings +# Note: Override ALL component hubs to use the selected registry +helm install higress higress/higress \ + -n higress-system --create-namespace \ + --set global.ingressClass=${INGRESS_CLASS:-nginx} \ + --set global.hub=${REGISTRY}/higress \ + --set global.enableStatus=false \ + --set higress-core.controller.hub=${REGISTRY}/higress \ + --set higress-core.gateway.hub=${REGISTRY}/higress \ + --set higress-core.pilot.hub=${REGISTRY}/higress \ + --set higress-core.pluginServer.hub=${REGISTRY}/higress \ + --set higress-core.gateway.replicas=2 +``` + +Key helm values: +- `global.ingressClass`: Use the **same** class as ingress-nginx +- `global.hub`: Image registry (auto-selected by timezone) +- `global.enableStatus=false`: **Disable Ingress status updates** to avoid conflicts with nginx (reduces API server pressure) +- Override all component hubs to ensure consistent registry usage +- Both nginx and Higress will watch the same Ingress resources +- Higress automatically recognizes `nginx.ingress.kubernetes.io/*` annotations +- Traffic still flows through nginx until you switch the entry point + +⚠️ **Note**: After nginx is uninstalled, you can enable status updates: +```bash +helm upgrade higress higress/higress -n higress-system \ + --reuse-values \ + --set global.enableStatus=true +``` + +### Phase 4: Generate and Run Test Script + +After Higress is running, generate a test script covering all Ingress routes: + +```bash +# Generate test script +./scripts/generate-migration-test.sh > migration-test.sh +chmod +x migration-test.sh + +# Get Higress gateway address +# Option A: If LoadBalancer is supported +HIGRESS_IP=$(kubectl get svc -n higress-system higress-gateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + +# Option B: If LoadBalancer is NOT supported, use port-forward +kubectl port-forward -n higress-system svc/higress-gateway 8080:80 & +HIGRESS_IP="127.0.0.1:8080" + +# Run tests +./migration-test.sh ${HIGRESS_IP} +``` + +The test script will: +- Extract all hosts and paths from Ingress resources +- Test each route against Higress gateway +- Verify response codes and basic functionality +- Report any failures for investigation + +### Phase 5: Traffic Cutover (User Action Required) + +⚠️ **Only proceed after all tests pass!** + +Choose your cutover method based on infrastructure: + +**Option A: DNS Switch** +```bash +# Update DNS records to point to Higress gateway IP +# Example: example.com A record -> ${HIGRESS_IP} +``` + +**Option B: Layer 4 Proxy/Load Balancer Switch** +```bash +# Update upstream in your L4 proxy (e.g., F5, HAProxy, cloud LB) +# From: nginx-ingress-controller service IP +# To: higress-gateway service IP +``` + +**Option C: Kubernetes Service Switch** (if using external traffic via Service) +```bash +# Update your external-facing Service selector or endpoints +``` + +### Phase 6: Use Built-in Plugins or Create Custom WASM Plugin (If Needed) + +Before writing custom plugins, check if Higress has a built-in plugin that meets your needs! + +#### Built-in Plugins (Recommended First) + +Higress provides many built-in plugins. Check [references/builtin-plugins.md](references/builtin-plugins.md) for the full list. + +Common replacements for nginx features: +| nginx feature | Higress built-in plugin | +|---------------|------------------------| +| Basic Auth snippet | `basic-auth` | +| IP restriction | `ip-restriction` | +| Rate limiting | `key-rate-limit`, `cluster-key-rate-limit` | +| WAF/ModSecurity | `waf` | +| Request validation | `request-validation` | +| Bot detection | `bot-detect` | +| JWT auth | `jwt-auth` | +| CORS headers | `cors` | +| Custom response | `custom-response` | +| Request/Response transform | `transformer` | + +#### Custom WASM Plugin (If No Built-in Matches) + +When nginx snippets or Lua logic has no built-in equivalent: + +1. **Analyze snippet** - Extract nginx directives/Lua code +2. **Generate Go WASM code** - Use higress-wasm-go-plugin skill +3. **Build plugin**: +```bash +cd plugin-dir +go mod tidy +GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o main.wasm ./ +``` + +4. **Push to registry**: + +If you don't have an image registry, install Harbor: +```bash +./scripts/install-harbor.sh +# Follow the prompts to install Harbor in your cluster +``` + +If you have your own registry: +```bash +# Build OCI image +docker build -t /higress-plugin-:v1 . +docker push /higress-plugin-:v1 +``` + +5. **Deploy plugin**: +```yaml +apiVersion: extensions.higress.io/v1alpha1 +kind: WasmPlugin +metadata: + name: custom-plugin + namespace: higress-system +spec: + url: oci:///higress-plugin-:v1 + phase: UNSPECIFIED_PHASE + priority: 100 +``` + +See [references/plugin-deployment.md](references/plugin-deployment.md) for detailed plugin deployment. + +## Common Snippet Conversions + +### Header Manipulation +```nginx +# nginx snippet +more_set_headers "X-Custom: value"; +``` +→ Use `headerControl` annotation or generate plugin with `proxywasm.AddHttpResponseHeader()`. + +### Request Validation +```nginx +# nginx snippet +if ($request_uri ~* "pattern") { return 403; } +``` +→ Generate WASM plugin with request header/path check. + +### Rate Limiting with Custom Logic +```nginx +# nginx snippet with Lua +access_by_lua_block { ... } +``` +→ Generate WASM plugin implementing the logic. + +See [references/snippet-patterns.md](references/snippet-patterns.md) for common patterns. + +## Validation + +Before traffic switch, use the generated test script: + +```bash +# Generate test script +./scripts/generate-migration-test.sh > migration-test.sh +chmod +x migration-test.sh + +# Get Higress gateway IP +HIGRESS_IP=$(kubectl get svc -n higress-system higress-gateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + +# Run all tests +./migration-test.sh ${HIGRESS_IP} +``` + +The test script will: +- Test every host/path combination from all Ingress resources +- Report pass/fail for each route +- Provide a summary and next steps + +**Only proceed with traffic cutover after all tests pass!** + +## Rollback + +Since nginx keeps running during migration, rollback is simply switching traffic back: + +```bash +# If traffic was switched via DNS: +# - Revert DNS records to nginx gateway IP + +# If traffic was switched via L4 proxy: +# - Revert upstream to nginx service IP + +# Nginx is still running, no action needed on k8s side +``` + +## Post-Migration Cleanup + +**Only after traffic has been fully migrated and stable:** + +```bash +# 1. Monitor Higress for a period (recommended: 24-48h) + +# 2. Backup nginx resources +kubectl get all -n ingress-nginx -o yaml > ingress-nginx-backup.yaml + +# 3. Scale down nginx (keep for emergency rollback) +kubectl scale deployment -n ingress-nginx ingress-nginx-controller --replicas=0 + +# 4. (Optional) After extended stable period, remove nginx +kubectl delete namespace ingress-nginx +``` diff --git a/.claude/skills/nginx-to-higress-migration/references/annotation-mapping.md b/.claude/skills/nginx-to-higress-migration/references/annotation-mapping.md new file mode 100644 index 000000000..40e3ecb9a --- /dev/null +++ b/.claude/skills/nginx-to-higress-migration/references/annotation-mapping.md @@ -0,0 +1,158 @@ +# Nginx to Higress Annotation Compatibility + +## ⚠️ Important: Do NOT Modify Your Ingress Resources! + +**Higress natively supports `nginx.ingress.kubernetes.io/*` annotations** - no conversion or modification needed! + +The Higress controller uses `ParseStringASAP()` which first tries `nginx.ingress.kubernetes.io/*` prefix, then falls back to `higress.io/*`. Your existing Ingress resources work as-is with Higress. + +## Fully Compatible Annotations (Work As-Is) + +These nginx annotations work directly with Higress without any changes: + +| nginx annotation (keep as-is) | Higress also accepts | Notes | +|-------------------------------|---------------------|-------| +| `nginx.ingress.kubernetes.io/rewrite-target` | `higress.io/rewrite-target` | Supports capture groups | +| `nginx.ingress.kubernetes.io/use-regex` | `higress.io/use-regex` | Enable regex path matching | +| `nginx.ingress.kubernetes.io/ssl-redirect` | `higress.io/ssl-redirect` | Force HTTPS | +| `nginx.ingress.kubernetes.io/force-ssl-redirect` | `higress.io/force-ssl-redirect` | Same behavior | +| `nginx.ingress.kubernetes.io/backend-protocol` | `higress.io/backend-protocol` | HTTP/HTTPS/GRPC | +| `nginx.ingress.kubernetes.io/proxy-body-size` | `higress.io/proxy-body-size` | Max body size | + +### CORS + +| nginx annotation | Higress annotation | +|------------------|-------------------| +| `nginx.ingress.kubernetes.io/enable-cors` | `higress.io/enable-cors` | +| `nginx.ingress.kubernetes.io/cors-allow-origin` | `higress.io/cors-allow-origin` | +| `nginx.ingress.kubernetes.io/cors-allow-methods` | `higress.io/cors-allow-methods` | +| `nginx.ingress.kubernetes.io/cors-allow-headers` | `higress.io/cors-allow-headers` | +| `nginx.ingress.kubernetes.io/cors-expose-headers` | `higress.io/cors-expose-headers` | +| `nginx.ingress.kubernetes.io/cors-allow-credentials` | `higress.io/cors-allow-credentials` | +| `nginx.ingress.kubernetes.io/cors-max-age` | `higress.io/cors-max-age` | + +### Timeout & Retry + +| nginx annotation | Higress annotation | +|------------------|-------------------| +| `nginx.ingress.kubernetes.io/proxy-connect-timeout` | `higress.io/proxy-connect-timeout` | +| `nginx.ingress.kubernetes.io/proxy-send-timeout` | `higress.io/proxy-send-timeout` | +| `nginx.ingress.kubernetes.io/proxy-read-timeout` | `higress.io/proxy-read-timeout` | +| `nginx.ingress.kubernetes.io/proxy-next-upstream-tries` | `higress.io/proxy-next-upstream-tries` | + +### Canary (Grayscale) + +| nginx annotation | Higress annotation | +|------------------|-------------------| +| `nginx.ingress.kubernetes.io/canary` | `higress.io/canary` | +| `nginx.ingress.kubernetes.io/canary-weight` | `higress.io/canary-weight` | +| `nginx.ingress.kubernetes.io/canary-header` | `higress.io/canary-header` | +| `nginx.ingress.kubernetes.io/canary-header-value` | `higress.io/canary-header-value` | +| `nginx.ingress.kubernetes.io/canary-header-pattern` | `higress.io/canary-header-pattern` | +| `nginx.ingress.kubernetes.io/canary-by-cookie` | `higress.io/canary-by-cookie` | + +### Authentication + +| nginx annotation | Higress annotation | +|------------------|-------------------| +| `nginx.ingress.kubernetes.io/auth-type` | `higress.io/auth-type` | +| `nginx.ingress.kubernetes.io/auth-secret` | `higress.io/auth-secret` | +| `nginx.ingress.kubernetes.io/auth-realm` | `higress.io/auth-realm` | + +### Load Balancing + +| nginx annotation | Higress annotation | +|------------------|-------------------| +| `nginx.ingress.kubernetes.io/load-balance` | `higress.io/load-balance` | +| `nginx.ingress.kubernetes.io/upstream-hash-by` | `higress.io/upstream-hash-by` | + +### IP Access Control + +| nginx annotation | Higress annotation | +|------------------|-------------------| +| `nginx.ingress.kubernetes.io/whitelist-source-range` | `higress.io/whitelist-source-range` | +| `nginx.ingress.kubernetes.io/denylist-source-range` | `higress.io/denylist-source-range` | + +### Redirect + +| nginx annotation | Higress annotation | +|------------------|-------------------| +| `nginx.ingress.kubernetes.io/permanent-redirect` | `higress.io/permanent-redirect` | +| `nginx.ingress.kubernetes.io/temporal-redirect` | `higress.io/temporal-redirect` | +| `nginx.ingress.kubernetes.io/permanent-redirect-code` | `higress.io/permanent-redirect-code` | + +### Header Control + +| nginx annotation | Higress annotation | +|------------------|-------------------| +| `nginx.ingress.kubernetes.io/proxy-set-headers` | `higress.io/proxy-set-headers` | +| `nginx.ingress.kubernetes.io/proxy-hide-headers` | `higress.io/proxy-hide-headers` | +| `nginx.ingress.kubernetes.io/proxy-pass-headers` | `higress.io/proxy-pass-headers` | + +### Upstream TLS + +| nginx annotation | Higress annotation | +|------------------|-------------------| +| `nginx.ingress.kubernetes.io/proxy-ssl-secret` | `higress.io/proxy-ssl-secret` | +| `nginx.ingress.kubernetes.io/proxy-ssl-verify` | `higress.io/proxy-ssl-verify` | + +## Unsupported Annotations (Require WASM Plugin) + +These annotations have no direct Higress equivalent and require custom WASM plugins: + +### Configuration Snippets +```yaml +# NOT supported - requires WASM plugin +nginx.ingress.kubernetes.io/server-snippet: | + location /custom { ... } +nginx.ingress.kubernetes.io/configuration-snippet: | + more_set_headers "X-Custom: value"; +nginx.ingress.kubernetes.io/stream-snippet: | + # TCP/UDP snippets +``` + +### Lua Scripting +```yaml +# NOT supported - convert to WASM plugin +nginx.ingress.kubernetes.io/lua-resty-waf: "active" +nginx.ingress.kubernetes.io/lua-resty-waf-score-threshold: "10" +``` + +### ModSecurity +```yaml +# NOT supported - use Higress WAF plugin or custom WASM +nginx.ingress.kubernetes.io/enable-modsecurity: "true" +nginx.ingress.kubernetes.io/modsecurity-snippet: | + SecRule ... +``` + +### Rate Limiting (Complex) +```yaml +# Basic rate limiting supported via plugin +# Complex Lua-based rate limiting requires WASM +nginx.ingress.kubernetes.io/limit-rps: "10" +nginx.ingress.kubernetes.io/limit-connections: "5" +``` + +### Other Unsupported +```yaml +# NOT directly supported +nginx.ingress.kubernetes.io/client-body-buffer-size +nginx.ingress.kubernetes.io/proxy-buffering +nginx.ingress.kubernetes.io/proxy-buffers-number +nginx.ingress.kubernetes.io/proxy-buffer-size +nginx.ingress.kubernetes.io/mirror-uri +nginx.ingress.kubernetes.io/mirror-request-body +nginx.ingress.kubernetes.io/grpc-backend +nginx.ingress.kubernetes.io/custom-http-errors +nginx.ingress.kubernetes.io/default-backend +``` + +## Migration Script + +Use this script to analyze Ingress annotations: + +```bash +# scripts/analyze-ingress.sh in this skill +./scripts/analyze-ingress.sh +``` diff --git a/.claude/skills/nginx-to-higress-migration/references/builtin-plugins.md b/.claude/skills/nginx-to-higress-migration/references/builtin-plugins.md new file mode 100644 index 000000000..228498109 --- /dev/null +++ b/.claude/skills/nginx-to-higress-migration/references/builtin-plugins.md @@ -0,0 +1,115 @@ +# Higress Built-in Plugins + +Before writing custom WASM plugins, check if Higress has a built-in plugin that meets your needs. + +**Plugin docs and images**: https://github.com/higress-group/higress-console/tree/main/backend/sdk/src/main/resources/plugins + +## Authentication & Authorization + +| Plugin | Description | Replaces nginx feature | +|--------|-------------|----------------------| +| `basic-auth` | HTTP Basic Authentication | `auth_basic` directive | +| `jwt-auth` | JWT token validation | JWT Lua scripts | +| `key-auth` | API Key authentication | Custom auth headers | +| `hmac-auth` | HMAC signature authentication | Signature validation | +| `oauth` | OAuth 2.0 authentication | OAuth Lua scripts | +| `oidc` | OpenID Connect | OIDC integration | +| `ext-auth` | External authorization service | `auth_request` directive | +| `opa` | Open Policy Agent integration | Complex auth logic | + +## Traffic Control + +| Plugin | Description | Replaces nginx feature | +|--------|-------------|----------------------| +| `key-rate-limit` | Rate limiting by key | `limit_req` directive | +| `cluster-key-rate-limit` | Distributed rate limiting | `limit_req` with shared state | +| `ip-restriction` | IP whitelist/blacklist | `allow`/`deny` directives | +| `request-block` | Block requests by pattern | `if` + `return 403` | +| `traffic-tag` | Traffic tagging | Custom headers for routing | +| `bot-detect` | Bot detection & blocking | Bot detection Lua scripts | + +## Request/Response Modification + +| Plugin | Description | Replaces nginx feature | +|--------|-------------|----------------------| +| `transformer` | Transform request/response | `proxy_set_header`, `more_set_headers` | +| `cors` | CORS headers | `add_header` CORS headers | +| `custom-response` | Custom static response | `return` directive | +| `request-validation` | Request parameter validation | Validation Lua scripts | +| `de-graphql` | GraphQL to REST conversion | GraphQL handling | + +## Security + +| Plugin | Description | Replaces nginx feature | +|--------|-------------|----------------------| +| `waf` | Web Application Firewall | ModSecurity module | +| `geo-ip` | GeoIP-based access control | `geoip` module | + +## Caching & Performance + +| Plugin | Description | Replaces nginx feature | +|--------|-------------|----------------------| +| `cache-control` | Cache control headers | `expires`, `add_header Cache-Control` | + +## AI Features (Higress-specific) + +| Plugin | Description | +|--------|-------------| +| `ai-proxy` | AI model proxy | +| `ai-cache` | AI response caching | +| `ai-quota` | AI token quota | +| `ai-token-ratelimit` | AI token rate limiting | +| `ai-transformer` | AI request/response transform | +| `ai-security-guard` | AI content security | +| `ai-statistics` | AI usage statistics | +| `mcp-server` | Model Context Protocol server | + +## Using Built-in Plugins + +### Via WasmPlugin CRD + +```yaml +apiVersion: extensions.higress.io/v1alpha1 +kind: WasmPlugin +metadata: + name: basic-auth-plugin + namespace: higress-system +spec: + # Use built-in plugin image + url: oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/basic-auth:1.0.0 + phase: AUTHN + priority: 320 + defaultConfig: + consumers: + - name: user1 + credential: "admin:123456" +``` + +### Via Higress Console + +1. Navigate to **Plugins** → **Plugin Market** +2. Find the desired plugin +3. Click **Enable** and configure + +## Image Registry Locations + +Select the nearest registry based on your location: + +| Region | Registry | +|--------|----------| +| China/Default | `higress-registry.cn-hangzhou.cr.aliyuncs.com` | +| North America | `higress-registry.us-west-1.cr.aliyuncs.com` | +| Southeast Asia | `higress-registry.ap-southeast-7.cr.aliyuncs.com` | + +Example with regional registry: +```yaml +spec: + url: oci://higress-registry.us-west-1.cr.aliyuncs.com/plugins/basic-auth:1.0.0 +``` + +## Plugin Configuration Reference + +Each plugin has its own configuration schema. View the spec.yaml in the plugin directory: +https://github.com/higress-group/higress-console/tree/main/backend/sdk/src/main/resources/plugins//spec.yaml + +Or check the README files for detailed documentation. diff --git a/.claude/skills/nginx-to-higress-migration/references/plugin-deployment.md b/.claude/skills/nginx-to-higress-migration/references/plugin-deployment.md new file mode 100644 index 000000000..f6307b682 --- /dev/null +++ b/.claude/skills/nginx-to-higress-migration/references/plugin-deployment.md @@ -0,0 +1,245 @@ +# WASM Plugin Build and Deployment + +## Plugin Project Structure + +``` +my-plugin/ +├── main.go # Plugin entry point +├── go.mod # Go module +├── go.sum # Dependencies +├── Dockerfile # OCI image build +└── wasmplugin.yaml # K8s deployment manifest +``` + +## Build Process + +### 1. Initialize Project + +```bash +mkdir my-plugin && cd my-plugin +go mod init my-plugin + +# Set proxy (only needed in China due to network restrictions) +# Skip this step if you're outside China or have direct access to GitHub +go env -w GOPROXY=https://proxy.golang.com.cn,direct + +# Get dependencies +go get github.com/higress-group/proxy-wasm-go-sdk@go-1.24 +go get github.com/higress-group/wasm-go@main +go get github.com/tidwall/gjson +``` + +### 2. Write Plugin Code + +See the higress-wasm-go-plugin skill for detailed API reference. Basic template: + +```go +package main + +import ( + "github.com/higress-group/wasm-go/pkg/wrapper" + "github.com/higress-group/proxy-wasm-go-sdk/proxywasm" + "github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types" + "github.com/tidwall/gjson" +) + +func main() {} + +func init() { + wrapper.SetCtx( + "my-plugin", + wrapper.ParseConfig(parseConfig), + wrapper.ProcessRequestHeaders(onHttpRequestHeaders), + ) +} + +type MyConfig struct { + // Config fields +} + +func parseConfig(json gjson.Result, config *MyConfig) error { + // Parse YAML config (converted to JSON) + return nil +} + +func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action { + // Process request + return types.HeaderContinue +} +``` + +### 3. Compile to WASM + +```bash +go mod tidy +GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o main.wasm ./ +``` + +### 4. Create Dockerfile + +```dockerfile +FROM scratch +COPY main.wasm /plugin.wasm +``` + +### 5. Build and Push Image + +#### Option A: Use Your Own Registry + +```bash +# User provides registry +REGISTRY=your-registry.com/higress-plugins + +# Build +docker build -t ${REGISTRY}/my-plugin:v1 . + +# Push +docker push ${REGISTRY}/my-plugin:v1 +``` + +#### Option B: Install Harbor (If No Registry Available) + +If you don't have an image registry, we can install Harbor for you: + +```bash +# Prerequisites +# - Kubernetes cluster with LoadBalancer or Ingress support +# - Persistent storage (PVC) +# - At least 4GB RAM and 2 CPU cores available + +# Install Harbor via Helm +helm repo add harbor https://helm.goharbor.io +helm repo update + +# Install with minimal configuration +helm install harbor harbor/harbor \ + --namespace harbor-system --create-namespace \ + --set expose.type=nodePort \ + --set expose.tls.enabled=false \ + --set persistence.enabled=true \ + --set harborAdminPassword=Harbor12345 + +# Get Harbor access info +export NODE_PORT=$(kubectl get svc -n harbor-system harbor-core -o jsonpath='{.spec.ports[0].nodePort}') +export NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}') +echo "Harbor URL: http://${NODE_IP}:${NODE_PORT}" +echo "Username: admin" +echo "Password: Harbor12345" + +# Login to Harbor +docker login ${NODE_IP}:${NODE_PORT} -u admin -p Harbor12345 + +# Create project in Harbor UI (http://${NODE_IP}:${NODE_PORT}) +# - Project Name: higress-plugins +# - Access Level: Public + +# Build and push plugin +docker build -t ${NODE_IP}:${NODE_PORT}/higress-plugins/my-plugin:v1 . +docker push ${NODE_IP}:${NODE_PORT}/higress-plugins/my-plugin:v1 +``` + +**Note**: For production use, enable TLS and use proper persistent storage. + +## Deployment + +### WasmPlugin CRD + +```yaml +apiVersion: extensions.higress.io/v1alpha1 +kind: WasmPlugin +metadata: + name: my-plugin + namespace: higress-system +spec: + # OCI image URL + url: oci://your-registry.com/higress-plugins/my-plugin:v1 + + # Plugin phase (when to execute) + # UNSPECIFIED_PHASE | AUTHN | AUTHZ | STATS + phase: UNSPECIFIED_PHASE + + # Priority (higher = earlier execution) + priority: 100 + + # Plugin configuration + defaultConfig: + key: value + + # Optional: specific routes/domains + matchRules: + - domain: + - "*.example.com" + config: + key: domain-specific-value + - ingress: + - default/my-ingress + config: + key: ingress-specific-value +``` + +### Apply to Cluster + +```bash +kubectl apply -f wasmplugin.yaml +``` + +### Verify Deployment + +```bash +# Check plugin status +kubectl get wasmplugin -n higress-system + +# Check gateway logs +kubectl logs -n higress-system -l app=higress-gateway | grep -i plugin + +# Test endpoint +curl -v http:///test-path +``` + +## Troubleshooting + +### Plugin Not Loading + +```bash +# Check image accessibility +kubectl run test --rm -it --image=your-registry.com/higress-plugins/my-plugin:v1 -- ls + +# Check gateway events +kubectl describe pod -n higress-system -l app=higress-gateway +``` + +### Plugin Errors + +```bash +# Enable debug logging +kubectl set env deployment/higress-gateway -n higress-system LOG_LEVEL=debug + +# View plugin logs +kubectl logs -n higress-system -l app=higress-gateway -f +``` + +### Image Pull Issues + +```bash +# Create image pull secret if needed +kubectl create secret docker-registry regcred \ + --docker-server=your-registry.com \ + --docker-username=user \ + --docker-password=pass \ + -n higress-system + +# Reference in WasmPlugin +spec: + imagePullSecrets: + - name: regcred +``` + +## Plugin Configuration via Console + +If using Higress Console: + +1. Navigate to **Plugins** → **Custom Plugins** +2. Click **Add Plugin** +3. Enter OCI URL: `oci://your-registry.com/higress-plugins/my-plugin:v1` +4. Configure plugin settings +5. Apply to routes/domains as needed diff --git a/.claude/skills/nginx-to-higress-migration/references/snippet-patterns.md b/.claude/skills/nginx-to-higress-migration/references/snippet-patterns.md new file mode 100644 index 000000000..845e4e6bd --- /dev/null +++ b/.claude/skills/nginx-to-higress-migration/references/snippet-patterns.md @@ -0,0 +1,331 @@ +# Common Nginx Snippet to WASM Plugin Patterns + +## Header Manipulation + +### Add Response Header + +**Nginx snippet:** +```nginx +more_set_headers "X-Custom-Header: custom-value"; +more_set_headers "X-Request-ID: $request_id"; +``` + +**WASM plugin:** +```go +func onHttpResponseHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action { + proxywasm.AddHttpResponseHeader("X-Custom-Header", "custom-value") + + // For request ID, get from request context + if reqId, err := proxywasm.GetHttpRequestHeader("x-request-id"); err == nil { + proxywasm.AddHttpResponseHeader("X-Request-ID", reqId) + } + return types.HeaderContinue +} +``` + +### Remove Headers + +**Nginx snippet:** +```nginx +more_clear_headers "Server"; +more_clear_headers "X-Powered-By"; +``` + +**WASM plugin:** +```go +func onHttpResponseHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action { + proxywasm.RemoveHttpResponseHeader("Server") + proxywasm.RemoveHttpResponseHeader("X-Powered-By") + return types.HeaderContinue +} +``` + +### Conditional Header + +**Nginx snippet:** +```nginx +if ($http_x_custom_flag = "enabled") { + more_set_headers "X-Feature: active"; +} +``` + +**WASM plugin:** +```go +func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action { + flag, _ := proxywasm.GetHttpRequestHeader("x-custom-flag") + if flag == "enabled" { + proxywasm.AddHttpRequestHeader("X-Feature", "active") + } + return types.HeaderContinue +} +``` + +## Request Validation + +### Block by Path Pattern + +**Nginx snippet:** +```nginx +if ($request_uri ~* "(\.php|\.asp|\.aspx)$") { + return 403; +} +``` + +**WASM plugin:** +```go +import "regexp" + +type MyConfig struct { + BlockPattern *regexp.Regexp +} + +func parseConfig(json gjson.Result, config *MyConfig) error { + pattern := json.Get("blockPattern").String() + if pattern == "" { + pattern = `\.(php|asp|aspx)$` + } + config.BlockPattern = regexp.MustCompile(pattern) + return nil +} + +func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action { + path := ctx.Path() + if config.BlockPattern.MatchString(path) { + proxywasm.SendHttpResponse(403, nil, []byte("Forbidden"), -1) + return types.HeaderStopAllIterationAndWatermark + } + return types.HeaderContinue +} +``` + +### Block by User Agent + +**Nginx snippet:** +```nginx +if ($http_user_agent ~* "(bot|crawler|spider)") { + return 403; +} +``` + +**WASM plugin:** +```go +func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action { + ua, _ := proxywasm.GetHttpRequestHeader("user-agent") + ua = strings.ToLower(ua) + + blockedPatterns := []string{"bot", "crawler", "spider"} + for _, pattern := range blockedPatterns { + if strings.Contains(ua, pattern) { + proxywasm.SendHttpResponse(403, nil, []byte("Blocked"), -1) + return types.HeaderStopAllIterationAndWatermark + } + } + return types.HeaderContinue +} +``` + +### Request Size Validation + +**Nginx snippet:** +```nginx +if ($content_length > 10485760) { + return 413; +} +``` + +**WASM plugin:** +```go +func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action { + clStr, _ := proxywasm.GetHttpRequestHeader("content-length") + if cl, err := strconv.ParseInt(clStr, 10, 64); err == nil { + if cl > 10*1024*1024 { // 10MB + proxywasm.SendHttpResponse(413, nil, []byte("Request too large"), -1) + return types.HeaderStopAllIterationAndWatermark + } + } + return types.HeaderContinue +} +``` + +## Request Modification + +### URL Rewrite with Logic + +**Nginx snippet:** +```nginx +set $backend "default"; +if ($http_x_version = "v2") { + set $backend "v2"; +} +rewrite ^/api/(.*)$ /api/$backend/$1 break; +``` + +**WASM plugin:** +```go +func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action { + version, _ := proxywasm.GetHttpRequestHeader("x-version") + backend := "default" + if version == "v2" { + backend = "v2" + } + + path := ctx.Path() + if strings.HasPrefix(path, "/api/") { + newPath := "/api/" + backend + path[4:] + proxywasm.ReplaceHttpRequestHeader(":path", newPath) + } + return types.HeaderContinue +} +``` + +### Add Query Parameter + +**Nginx snippet:** +```nginx +if ($args !~ "source=") { + set $args "${args}&source=gateway"; +} +``` + +**WASM plugin:** +```go +func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action { + path := ctx.Path() + if !strings.Contains(path, "source=") { + separator := "?" + if strings.Contains(path, "?") { + separator = "&" + } + newPath := path + separator + "source=gateway" + proxywasm.ReplaceHttpRequestHeader(":path", newPath) + } + return types.HeaderContinue +} +``` + +## Lua Script Conversion + +### Simple Lua Access Check + +**Nginx Lua:** +```lua +access_by_lua_block { + local token = ngx.var.http_authorization + if not token or token == "" then + ngx.exit(401) + end +} +``` + +**WASM plugin:** +```go +func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action { + token, _ := proxywasm.GetHttpRequestHeader("authorization") + if token == "" { + proxywasm.SendHttpResponse(401, [][2]string{ + {"WWW-Authenticate", "Bearer"}, + }, []byte("Unauthorized"), -1) + return types.HeaderStopAllIterationAndWatermark + } + return types.HeaderContinue +} +``` + +### Lua with Redis + +**Nginx Lua:** +```lua +access_by_lua_block { + local redis = require "resty.redis" + local red = redis:new() + red:connect("127.0.0.1", 6379) + + local ip = ngx.var.remote_addr + local count = red:incr("rate:" .. ip) + if count > 100 then + ngx.exit(429) + end + red:expire("rate:" .. ip, 60) +} +``` + +**WASM plugin:** +```go +// See references/redis-client.md in higress-wasm-go-plugin skill +func parseConfig(json gjson.Result, config *MyConfig) error { + config.redis = wrapper.NewRedisClusterClient(wrapper.FQDNCluster{ + FQDN: json.Get("redisService").String(), + Port: json.Get("redisPort").Int(), + }) + return config.redis.Init("", json.Get("redisPassword").String(), 1000) +} + +func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action { + ip, _ := proxywasm.GetHttpRequestHeader("x-real-ip") + if ip == "" { + ip, _ = proxywasm.GetHttpRequestHeader("x-forwarded-for") + } + + key := "rate:" + ip + err := config.redis.Incr(key, func(val int) { + if val > 100 { + proxywasm.SendHttpResponse(429, nil, []byte("Rate limited"), -1) + return + } + config.redis.Expire(key, 60, nil) + proxywasm.ResumeHttpRequest() + }) + + if err != nil { + return types.HeaderContinue // Fallback on Redis error + } + return types.HeaderStopAllIterationAndWatermark +} +``` + +## Response Modification + +### Inject Script/Content + +**Nginx snippet:** +```nginx +sub_filter '' ''; +sub_filter_once on; +``` + +**WASM plugin:** +```go +func init() { + wrapper.SetCtx( + "inject-script", + wrapper.ParseConfig(parseConfig), + wrapper.ProcessResponseHeaders(onHttpResponseHeaders), + wrapper.ProcessResponseBody(onHttpResponseBody), + ) +} + +func onHttpResponseHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action { + contentType, _ := proxywasm.GetHttpResponseHeader("content-type") + if strings.Contains(contentType, "text/html") { + ctx.BufferResponseBody() + proxywasm.RemoveHttpResponseHeader("content-length") + } + return types.HeaderContinue +} + +func onHttpResponseBody(ctx wrapper.HttpContext, config MyConfig, body []byte) types.Action { + bodyStr := string(body) + injection := `` + newBody := strings.Replace(bodyStr, "", injection, 1) + proxywasm.ReplaceHttpResponseBody([]byte(newBody)) + return types.BodyContinue +} +``` + +## Best Practices + +1. **Error Handling**: Always handle external call failures gracefully +2. **Performance**: Cache regex patterns in config, avoid recompiling +3. **Timeout**: Set appropriate timeouts for external calls (default 500ms) +4. **Logging**: Use `proxywasm.LogInfo/Warn/Error` for debugging +5. **Testing**: Test locally with Docker Compose before deploying diff --git a/.claude/skills/nginx-to-higress-migration/scripts/analyze-ingress.sh b/.claude/skills/nginx-to-higress-migration/scripts/analyze-ingress.sh new file mode 100755 index 000000000..c3bce48a8 --- /dev/null +++ b/.claude/skills/nginx-to-higress-migration/scripts/analyze-ingress.sh @@ -0,0 +1,198 @@ +#!/bin/bash +# Analyze nginx Ingress resources and identify migration requirements + +set -e + +NAMESPACE="${1:-}" +OUTPUT_FORMAT="${2:-text}" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# Supported nginx annotations that map to Higress +SUPPORTED_ANNOTATIONS=( + "rewrite-target" + "use-regex" + "ssl-redirect" + "force-ssl-redirect" + "backend-protocol" + "proxy-body-size" + "enable-cors" + "cors-allow-origin" + "cors-allow-methods" + "cors-allow-headers" + "cors-expose-headers" + "cors-allow-credentials" + "cors-max-age" + "proxy-connect-timeout" + "proxy-send-timeout" + "proxy-read-timeout" + "proxy-next-upstream-tries" + "canary" + "canary-weight" + "canary-header" + "canary-header-value" + "canary-header-pattern" + "canary-by-cookie" + "auth-type" + "auth-secret" + "auth-realm" + "load-balance" + "upstream-hash-by" + "whitelist-source-range" + "denylist-source-range" + "permanent-redirect" + "temporal-redirect" + "permanent-redirect-code" + "proxy-set-headers" + "proxy-hide-headers" + "proxy-pass-headers" + "proxy-ssl-secret" + "proxy-ssl-verify" +) + +# Unsupported annotations requiring WASM plugins +UNSUPPORTED_ANNOTATIONS=( + "server-snippet" + "configuration-snippet" + "stream-snippet" + "lua-resty-waf" + "lua-resty-waf-score-threshold" + "enable-modsecurity" + "modsecurity-snippet" + "limit-rps" + "limit-connections" + "limit-rate" + "limit-rate-after" + "client-body-buffer-size" + "proxy-buffering" + "proxy-buffers-number" + "proxy-buffer-size" + "custom-http-errors" + "default-backend" +) + +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Nginx to Higress Migration Analysis${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" + +# Check for ingress-nginx +echo -e "${YELLOW}Checking for ingress-nginx...${NC}" +if kubectl get pods -A 2>/dev/null | grep -q ingress-nginx; then + echo -e "${GREEN}✓ ingress-nginx found${NC}" + kubectl get pods -A | grep ingress-nginx | head -5 +else + echo -e "${RED}✗ ingress-nginx not found${NC}" +fi +echo "" + +# Check IngressClass +echo -e "${YELLOW}IngressClass resources:${NC}" +kubectl get ingressclass 2>/dev/null || echo "No IngressClass resources found" +echo "" + +# Get Ingress resources +if [ -n "$NAMESPACE" ]; then + INGRESS_LIST=$(kubectl get ingress -n "$NAMESPACE" -o json 2>/dev/null) +else + INGRESS_LIST=$(kubectl get ingress -A -o json 2>/dev/null) +fi + +if [ -z "$INGRESS_LIST" ] || [ "$(echo "$INGRESS_LIST" | jq '.items | length')" -eq 0 ]; then + echo -e "${RED}No Ingress resources found${NC}" + exit 0 +fi + +TOTAL_INGRESS=$(echo "$INGRESS_LIST" | jq '.items | length') +echo -e "${YELLOW}Found ${TOTAL_INGRESS} Ingress resources${NC}" +echo "" + +# Analyze each Ingress +COMPATIBLE_COUNT=0 +NEEDS_PLUGIN_COUNT=0 +UNSUPPORTED_FOUND=() + +echo "$INGRESS_LIST" | jq -c '.items[]' | while read -r ingress; do + NAME=$(echo "$ingress" | jq -r '.metadata.name') + NS=$(echo "$ingress" | jq -r '.metadata.namespace') + INGRESS_CLASS=$(echo "$ingress" | jq -r '.spec.ingressClassName // .metadata.annotations["kubernetes.io/ingress.class"] // "unknown"') + + # Skip non-nginx ingresses + if [[ "$INGRESS_CLASS" != "nginx" && "$INGRESS_CLASS" != "unknown" ]]; then + continue + fi + + echo -e "${BLUE}-------------------------------------------${NC}" + echo -e "${BLUE}Ingress: ${NS}/${NAME}${NC}" + echo -e "IngressClass: ${INGRESS_CLASS}" + + # Get annotations + ANNOTATIONS=$(echo "$ingress" | jq -r '.metadata.annotations // {}') + + HAS_UNSUPPORTED=false + SUPPORTED_LIST=() + UNSUPPORTED_LIST=() + + # Check each annotation + echo "$ANNOTATIONS" | jq -r 'keys[]' | while read -r key; do + # Extract annotation name (remove prefix) + ANNO_NAME=$(echo "$key" | sed 's/nginx.ingress.kubernetes.io\///' | sed 's/higress.io\///') + + if [[ "$key" == nginx.ingress.kubernetes.io/* ]]; then + # Check if supported + IS_SUPPORTED=false + for supported in "${SUPPORTED_ANNOTATIONS[@]}"; do + if [[ "$ANNO_NAME" == "$supported" ]]; then + IS_SUPPORTED=true + break + fi + done + + # Check if explicitly unsupported + for unsupported in "${UNSUPPORTED_ANNOTATIONS[@]}"; do + if [[ "$ANNO_NAME" == "$unsupported" ]]; then + IS_SUPPORTED=false + HAS_UNSUPPORTED=true + VALUE=$(echo "$ANNOTATIONS" | jq -r --arg k "$key" '.[$k]') + echo -e " ${RED}✗ $ANNO_NAME${NC} (requires WASM plugin)" + if [[ "$ANNO_NAME" == *"snippet"* ]]; then + echo -e " Value preview: $(echo "$VALUE" | head -1)" + fi + break + fi + done + + if [ "$IS_SUPPORTED" = true ]; then + echo -e " ${GREEN}✓ $ANNO_NAME${NC}" + fi + fi + done + + if [ "$HAS_UNSUPPORTED" = true ]; then + echo -e "\n ${YELLOW}Status: Requires WASM plugin for full compatibility${NC}" + else + echo -e "\n ${GREEN}Status: Fully compatible${NC}" + fi + echo "" +done + +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Summary${NC}" +echo -e "${BLUE}========================================${NC}" +echo -e "Total Ingress resources: ${TOTAL_INGRESS}" +echo "" +echo -e "${GREEN}✓ No Ingress modification needed!${NC}" +echo " Higress natively supports nginx.ingress.kubernetes.io/* annotations." +echo "" +echo -e "${YELLOW}Next Steps:${NC}" +echo "1. Install Higress with the SAME ingressClass as nginx" +echo " (set global.enableStatus=false to disable Ingress status updates)" +echo "2. For snippets/Lua: check Higress built-in plugins first, then generate custom WASM if needed" +echo "3. Generate and run migration test script" +echo "4. Switch traffic via DNS or L4 proxy after tests pass" +echo "5. After stable period, uninstall nginx and enable status updates (global.enableStatus=true)" diff --git a/.claude/skills/nginx-to-higress-migration/scripts/generate-migration-test.sh b/.claude/skills/nginx-to-higress-migration/scripts/generate-migration-test.sh new file mode 100755 index 000000000..b1c2e2665 --- /dev/null +++ b/.claude/skills/nginx-to-higress-migration/scripts/generate-migration-test.sh @@ -0,0 +1,210 @@ +#!/bin/bash +# Generate test script for all Ingress routes +# Tests each route against Higress gateway to validate migration + +set -e + +NAMESPACE="${1:-}" + +# Colors for output script +cat << 'HEADER' +#!/bin/bash +# Higress Migration Test Script +# Auto-generated - tests all Ingress routes against Higress gateway + +set -e + +GATEWAY_IP="${1:-}" +TIMEOUT="${2:-5}" +VERBOSE="${3:-false}" + +if [ -z "$GATEWAY_IP" ]; then + echo "Usage: $0 [timeout] [verbose]" + echo "" + echo "Examples:" + echo " # With LoadBalancer IP" + echo " $0 10.0.0.100 5 true" + echo "" + echo " # With port-forward (run this first: kubectl port-forward -n higress-system svc/higress-gateway 8080:80 &)" + echo " $0 127.0.0.1:8080 5 true" + exit 1 +fi + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +TOTAL=0 +PASSED=0 +FAILED=0 +FAILED_TESTS=() + +test_route() { + local host="$1" + local path="$2" + local expected_code="${3:-200}" + local description="$4" + + TOTAL=$((TOTAL + 1)) + + # Build URL + local url="http://${GATEWAY_IP}${path}" + + # Make request + local response + response=$(curl -s -o /dev/null -w "%{http_code}" \ + -H "Host: ${host}" \ + --connect-timeout "${TIMEOUT}" \ + --max-time $((TIMEOUT * 2)) \ + "${url}" 2>/dev/null) || response="000" + + # Check result + if [ "$response" = "$expected_code" ] || [ "$expected_code" = "*" ]; then + PASSED=$((PASSED + 1)) + echo -e "${GREEN}✓${NC} [${response}] ${host}${path}" + if [ "$VERBOSE" = "true" ]; then + echo " Expected: ${expected_code}, Got: ${response}" + fi + else + FAILED=$((FAILED + 1)) + FAILED_TESTS+=("${host}${path} (expected ${expected_code}, got ${response})") + echo -e "${RED}✗${NC} [${response}] ${host}${path}" + echo " Expected: ${expected_code}, Got: ${response}" + fi +} + +echo "========================================" +echo "Higress Migration Test" +echo "========================================" +echo "Gateway IP: ${GATEWAY_IP}" +echo "Timeout: ${TIMEOUT}s" +echo "" +echo "Testing routes..." +echo "" + +HEADER + +# Get Ingress resources +if [ -n "$NAMESPACE" ]; then + INGRESS_JSON=$(kubectl get ingress -n "$NAMESPACE" -o json 2>/dev/null) +else + INGRESS_JSON=$(kubectl get ingress -A -o json 2>/dev/null) +fi + +if [ -z "$INGRESS_JSON" ] || [ "$(echo "$INGRESS_JSON" | jq '.items | length')" -eq 0 ]; then + echo "# No Ingress resources found" + echo "echo 'No Ingress resources found to test'" + echo "exit 0" + exit 0 +fi + +# Generate test cases for each Ingress +echo "$INGRESS_JSON" | jq -c '.items[]' | while read -r ingress; do + NAME=$(echo "$ingress" | jq -r '.metadata.name') + NS=$(echo "$ingress" | jq -r '.metadata.namespace') + + echo "" + echo "# ================================================" + echo "# Ingress: ${NS}/${NAME}" + echo "# ================================================" + + # Check for TLS hosts + TLS_HOSTS=$(echo "$ingress" | jq -r '.spec.tls[]?.hosts[]?' 2>/dev/null | sort -u) + + # Process each rule + echo "$ingress" | jq -c '.spec.rules[]?' | while read -r rule; do + HOST=$(echo "$rule" | jq -r '.host // "*"') + + # Process each path + echo "$rule" | jq -c '.http.paths[]?' | while read -r path_item; do + PATH=$(echo "$path_item" | jq -r '.path // "/"') + PATH_TYPE=$(echo "$path_item" | jq -r '.pathType // "Prefix"') + SERVICE=$(echo "$path_item" | jq -r '.backend.service.name // .backend.serviceName // "unknown"') + PORT=$(echo "$path_item" | jq -r '.backend.service.port.number // .backend.service.port.name // .backend.servicePort // "80"') + + # Generate test + # For Prefix paths, test the exact path + # For Exact paths, test exactly + # Add a simple 200 or * expectation (can be customized) + + echo "" + echo "# Path: ${PATH} (${PATH_TYPE}) -> ${SERVICE}:${PORT}" + + # Test the path + if [ "$PATH_TYPE" = "Exact" ]; then + echo "test_route \"${HOST}\" \"${PATH}\" \"*\" \"Exact path\"" + else + # For Prefix, test base path and a subpath + echo "test_route \"${HOST}\" \"${PATH}\" \"*\" \"Prefix path\"" + + # If path doesn't end with /, add a subpath test + if [[ ! "$PATH" =~ /$ ]] && [ "$PATH" != "/" ]; then + echo "test_route \"${HOST}\" \"${PATH}/\" \"*\" \"Prefix path with trailing slash\"" + fi + fi + done + done + + # Check for specific annotations that might need special testing + REWRITE=$(echo "$ingress" | jq -r '.metadata.annotations["nginx.ingress.kubernetes.io/rewrite-target"] // .metadata.annotations["higress.io/rewrite-target"] // ""') + if [ -n "$REWRITE" ] && [ "$REWRITE" != "null" ]; then + echo "" + echo "# Note: This Ingress has rewrite-target: ${REWRITE}" + echo "# Verify the rewritten path manually if needed" + fi + + CANARY=$(echo "$ingress" | jq -r '.metadata.annotations["nginx.ingress.kubernetes.io/canary"] // .metadata.annotations["higress.io/canary"] // ""') + if [ "$CANARY" = "true" ]; then + echo "" + echo "# Note: This is a canary Ingress - test with appropriate headers/cookies" + CANARY_HEADER=$(echo "$ingress" | jq -r '.metadata.annotations["nginx.ingress.kubernetes.io/canary-header"] // .metadata.annotations["higress.io/canary-header"] // ""') + CANARY_VALUE=$(echo "$ingress" | jq -r '.metadata.annotations["nginx.ingress.kubernetes.io/canary-header-value"] // .metadata.annotations["higress.io/canary-header-value"] // ""') + if [ -n "$CANARY_HEADER" ] && [ "$CANARY_HEADER" != "null" ]; then + echo "# Canary header: ${CANARY_HEADER}=${CANARY_VALUE}" + fi + fi +done + +# Generate summary section +cat << 'FOOTER' + +# ================================================ +# Summary +# ================================================ +echo "" +echo "========================================" +echo "Test Summary" +echo "========================================" +echo -e "Total: ${TOTAL}" +echo -e "Passed: ${GREEN}${PASSED}${NC}" +echo -e "Failed: ${RED}${FAILED}${NC}" +echo "" + +if [ ${FAILED} -gt 0 ]; then + echo -e "${YELLOW}Failed tests:${NC}" + for test in "${FAILED_TESTS[@]}"; do + echo -e " ${RED}•${NC} $test" + done + echo "" + echo -e "${YELLOW}⚠ Some tests failed. Please investigate before switching traffic.${NC}" + exit 1 +else + echo -e "${GREEN}✓ All tests passed!${NC}" + echo "" + echo "========================================" + echo -e "${GREEN}Ready for Traffic Cutover${NC}" + echo "========================================" + echo "" + echo "Next steps:" + echo "1. Switch traffic to Higress gateway:" + echo " - DNS: Update A/CNAME records to ${GATEWAY_IP}" + echo " - L4 Proxy: Update upstream to ${GATEWAY_IP}" + echo "" + echo "2. Monitor for errors after switch" + echo "" + echo "3. Once stable, scale down nginx:" + echo " kubectl scale deployment -n ingress-nginx ingress-nginx-controller --replicas=0" + echo "" +fi +FOOTER diff --git a/.claude/skills/nginx-to-higress-migration/scripts/generate-plugin-scaffold.sh b/.claude/skills/nginx-to-higress-migration/scripts/generate-plugin-scaffold.sh new file mode 100755 index 000000000..c82698101 --- /dev/null +++ b/.claude/skills/nginx-to-higress-migration/scripts/generate-plugin-scaffold.sh @@ -0,0 +1,261 @@ +#!/bin/bash +# Generate WASM plugin scaffold for nginx snippet migration + +set -e + +if [ "$#" -lt 1 ]; then + echo "Usage: $0 [output-dir]" + echo "" + echo "Example: $0 custom-headers ./plugins" + exit 1 +fi + +PLUGIN_NAME="$1" +OUTPUT_DIR="${2:-.}" +PLUGIN_DIR="${OUTPUT_DIR}/${PLUGIN_NAME}" + +# Colors +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +echo -e "${YELLOW}Generating WASM plugin scaffold: ${PLUGIN_NAME}${NC}" + +# Create directory +mkdir -p "$PLUGIN_DIR" + +# Generate go.mod +cat > "${PLUGIN_DIR}/go.mod" << EOF +module ${PLUGIN_NAME} + +go 1.24 + +require ( + github.com/higress-group/proxy-wasm-go-sdk v1.0.1-0.20241230091623-edc7227eb588 + github.com/higress-group/wasm-go v1.0.1-0.20250107151137-19a0ab53cfec + github.com/tidwall/gjson v1.18.0 +) +EOF + +# Generate main.go +cat > "${PLUGIN_DIR}/main.go" << 'EOF' +package main + +import ( + "github.com/higress-group/proxy-wasm-go-sdk/proxywasm" + "github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types" + "github.com/higress-group/wasm-go/pkg/wrapper" + "github.com/tidwall/gjson" +) + +func main() {} + +func init() { + wrapper.SetCtx( + "PLUGIN_NAME_PLACEHOLDER", + wrapper.ParseConfig(parseConfig), + wrapper.ProcessRequestHeaders(onHttpRequestHeaders), + wrapper.ProcessRequestBody(onHttpRequestBody), + wrapper.ProcessResponseHeaders(onHttpResponseHeaders), + wrapper.ProcessResponseBody(onHttpResponseBody), + ) +} + +// PluginConfig holds the plugin configuration +type PluginConfig struct { + // TODO: Add configuration fields + // Example: + // HeaderName string + // HeaderValue string + Enabled bool +} + +// parseConfig parses the plugin configuration from YAML (converted to JSON) +func parseConfig(json gjson.Result, config *PluginConfig) error { + // TODO: Parse configuration + // Example: + // config.HeaderName = json.Get("headerName").String() + // config.HeaderValue = json.Get("headerValue").String() + config.Enabled = json.Get("enabled").Bool() + + proxywasm.LogInfof("Plugin config loaded: enabled=%v", config.Enabled) + return nil +} + +// onHttpRequestHeaders is called when request headers are received +func onHttpRequestHeaders(ctx wrapper.HttpContext, config PluginConfig) types.Action { + if !config.Enabled { + return types.HeaderContinue + } + + // TODO: Implement request header processing + // Example: Add custom header + // proxywasm.AddHttpRequestHeader(config.HeaderName, config.HeaderValue) + + // Example: Check path and block + // path := ctx.Path() + // if strings.Contains(path, "/blocked") { + // proxywasm.SendHttpResponse(403, nil, []byte("Forbidden"), -1) + // return types.HeaderStopAllIterationAndWatermark + // } + + return types.HeaderContinue +} + +// onHttpRequestBody is called when request body is received +// Remove this function from init() if not needed +func onHttpRequestBody(ctx wrapper.HttpContext, config PluginConfig, body []byte) types.Action { + if !config.Enabled { + return types.BodyContinue + } + + // TODO: Implement request body processing + // Example: Log body size + // proxywasm.LogInfof("Request body size: %d", len(body)) + + return types.BodyContinue +} + +// onHttpResponseHeaders is called when response headers are received +func onHttpResponseHeaders(ctx wrapper.HttpContext, config PluginConfig) types.Action { + if !config.Enabled { + return types.HeaderContinue + } + + // TODO: Implement response header processing + // Example: Add security headers + // proxywasm.AddHttpResponseHeader("X-Content-Type-Options", "nosniff") + // proxywasm.AddHttpResponseHeader("X-Frame-Options", "DENY") + + return types.HeaderContinue +} + +// onHttpResponseBody is called when response body is received +// Remove this function from init() if not needed +func onHttpResponseBody(ctx wrapper.HttpContext, config PluginConfig, body []byte) types.Action { + if !config.Enabled { + return types.BodyContinue + } + + // TODO: Implement response body processing + // Example: Modify response body + // newBody := strings.Replace(string(body), "old", "new", -1) + // proxywasm.ReplaceHttpResponseBody([]byte(newBody)) + + return types.BodyContinue +} +EOF + +# Replace plugin name placeholder +sed -i "s/PLUGIN_NAME_PLACEHOLDER/${PLUGIN_NAME}/g" "${PLUGIN_DIR}/main.go" + +# Generate Dockerfile +cat > "${PLUGIN_DIR}/Dockerfile" << 'EOF' +FROM scratch +COPY main.wasm /plugin.wasm +EOF + +# Generate build script +cat > "${PLUGIN_DIR}/build.sh" << 'EOF' +#!/bin/bash +set -e + +echo "Downloading dependencies..." +go mod tidy + +echo "Building WASM plugin..." +GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o main.wasm ./ + +echo "Build complete: main.wasm" +ls -lh main.wasm +EOF +chmod +x "${PLUGIN_DIR}/build.sh" + +# Generate WasmPlugin manifest +cat > "${PLUGIN_DIR}/wasmplugin.yaml" << EOF +apiVersion: extensions.higress.io/v1alpha1 +kind: WasmPlugin +metadata: + name: ${PLUGIN_NAME} + namespace: higress-system +spec: + # TODO: Replace with your registry + url: oci://YOUR_REGISTRY/${PLUGIN_NAME}:v1 + phase: UNSPECIFIED_PHASE + priority: 100 + defaultConfig: + enabled: true + # TODO: Add your configuration + # Optional: Apply to specific routes/domains + # matchRules: + # - domain: + # - "*.example.com" + # config: + # enabled: true +EOF + +# Generate README +cat > "${PLUGIN_DIR}/README.md" << EOF +# ${PLUGIN_NAME} + +A Higress WASM plugin migrated from nginx configuration. + +## Build + +\`\`\`bash +./build.sh +\`\`\` + +## Push to Registry + +\`\`\`bash +# Set your registry +REGISTRY=your-registry.com/higress-plugins + +# Build Docker image +docker build -t \${REGISTRY}/${PLUGIN_NAME}:v1 . + +# Push +docker push \${REGISTRY}/${PLUGIN_NAME}:v1 +\`\`\` + +## Deploy + +1. Update \`wasmplugin.yaml\` with your registry URL +2. Apply to cluster: + \`\`\`bash + kubectl apply -f wasmplugin.yaml + \`\`\` + +## Configuration + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| enabled | bool | true | Enable/disable plugin | + +## TODO + +- [ ] Implement plugin logic in main.go +- [ ] Add configuration fields +- [ ] Test locally +- [ ] Push to registry +- [ ] Deploy to cluster +EOF + +echo -e "\n${GREEN}✓ Plugin scaffold generated at: ${PLUGIN_DIR}${NC}" +echo "" +echo "Files created:" +echo " - ${PLUGIN_DIR}/main.go (plugin source)" +echo " - ${PLUGIN_DIR}/go.mod (Go module)" +echo " - ${PLUGIN_DIR}/Dockerfile (OCI image)" +echo " - ${PLUGIN_DIR}/build.sh (build script)" +echo " - ${PLUGIN_DIR}/wasmplugin.yaml (K8s manifest)" +echo " - ${PLUGIN_DIR}/README.md (documentation)" +echo "" +echo -e "${YELLOW}Next steps:${NC}" +echo "1. cd ${PLUGIN_DIR}" +echo "2. Edit main.go to implement your logic" +echo "3. Run: ./build.sh" +echo "4. Push image to your registry" +echo "5. Update wasmplugin.yaml with registry URL" +echo "6. Deploy: kubectl apply -f wasmplugin.yaml" diff --git a/.claude/skills/nginx-to-higress-migration/scripts/install-harbor.sh b/.claude/skills/nginx-to-higress-migration/scripts/install-harbor.sh new file mode 100755 index 000000000..66fea5e41 --- /dev/null +++ b/.claude/skills/nginx-to-higress-migration/scripts/install-harbor.sh @@ -0,0 +1,157 @@ +#!/bin/bash +# Install Harbor registry for WASM plugin images +# Only use this if you don't have an existing image registry + +set -e + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +HARBOR_NAMESPACE="${1:-harbor-system}" +HARBOR_PASSWORD="${2:-Harbor12345}" + +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Harbor Registry Installation${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" +echo -e "${YELLOW}This will install Harbor in your cluster.${NC}" +echo "" +echo "Configuration:" +echo " Namespace: ${HARBOR_NAMESPACE}" +echo " Admin Password: ${HARBOR_PASSWORD}" +echo " Exposure: NodePort (no TLS)" +echo " Persistence: Enabled (default StorageClass)" +echo "" +read -p "Continue? (y/N): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 1 +fi + +# Check prerequisites +echo -e "\n${YELLOW}Checking prerequisites...${NC}" + +# Check for helm +if ! command -v helm &> /dev/null; then + echo -e "${RED}✗ helm not found. Please install helm 3.x${NC}" + exit 1 +fi +echo -e "${GREEN}✓ helm found${NC}" + +# Check for kubectl +if ! command -v kubectl &> /dev/null; then + echo -e "${RED}✗ kubectl not found${NC}" + exit 1 +fi +echo -e "${GREEN}✓ kubectl found${NC}" + +# Check cluster access +if ! kubectl get nodes &> /dev/null; then + echo -e "${RED}✗ Cannot access cluster${NC}" + exit 1 +fi +echo -e "${GREEN}✓ Cluster access OK${NC}" + +# Check for default StorageClass +if ! kubectl get storageclass -o name | grep -q .; then + echo -e "${YELLOW}⚠ No StorageClass found. Harbor needs persistent storage.${NC}" + echo " You may need to install a storage provisioner first." + read -p "Continue anyway? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + exit 1 + fi +fi + +# Add Harbor helm repo +echo -e "\n${YELLOW}Adding Harbor helm repository...${NC}" +helm repo add harbor https://helm.goharbor.io +helm repo update +echo -e "${GREEN}✓ Repository added${NC}" + +# Install Harbor +echo -e "\n${YELLOW}Installing Harbor...${NC}" +helm install harbor harbor/harbor \ + --namespace "${HARBOR_NAMESPACE}" --create-namespace \ + --set expose.type=nodePort \ + --set expose.tls.enabled=false \ + --set persistence.enabled=true \ + --set harborAdminPassword="${HARBOR_PASSWORD}" \ + --wait --timeout 10m + +if [ $? -ne 0 ]; then + echo -e "${RED}✗ Harbor installation failed${NC}" + exit 1 +fi + +echo -e "${GREEN}✓ Harbor installed successfully${NC}" + +# Wait for Harbor to be ready +echo -e "\n${YELLOW}Waiting for Harbor to be ready...${NC}" +kubectl wait --for=condition=ready pod -l app=harbor -n "${HARBOR_NAMESPACE}" --timeout=300s + +# Get access information +echo -e "\n${BLUE}========================================${NC}" +echo -e "${BLUE}Harbor Access Information${NC}" +echo -e "${BLUE}========================================${NC}" + +NODE_PORT=$(kubectl get svc -n "${HARBOR_NAMESPACE}" harbor-core -o jsonpath='{.spec.ports[0].nodePort}') +NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}') +if [ -z "$NODE_IP" ]; then + NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}') +fi + +HARBOR_URL="${NODE_IP}:${NODE_PORT}" + +echo "" +echo -e "Harbor URL: ${GREEN}http://${HARBOR_URL}${NC}" +echo -e "Username: ${GREEN}admin${NC}" +echo -e "Password: ${GREEN}${HARBOR_PASSWORD}${NC}" +echo "" + +# Test Docker login +echo -e "${YELLOW}Testing Docker login...${NC}" +if docker login "${HARBOR_URL}" -u admin -p "${HARBOR_PASSWORD}" &> /dev/null; then + echo -e "${GREEN}✓ Docker login successful${NC}" +else + echo -e "${YELLOW}⚠ Docker login failed. You may need to:${NC}" + echo " 1. Add '${HARBOR_URL}' to Docker's insecure registries" + echo " 2. Restart Docker daemon" + echo "" + echo " Edit /etc/docker/daemon.json (Linux) or Docker Desktop settings (Mac/Windows):" + echo " {" + echo " \"insecure-registries\": [\"${HARBOR_URL}\"]" + echo " }" +fi + +echo "" +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Next Steps${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" +echo "1. Open Harbor UI: http://${HARBOR_URL}" +echo "2. Login with admin/${HARBOR_PASSWORD}" +echo "3. Create a new project:" +echo " - Click 'Projects' → 'New Project'" +echo " - Name: higress-plugins" +echo " - Access Level: Public" +echo "" +echo "4. Build and push your plugin:" +echo " docker build -t ${HARBOR_URL}/higress-plugins/my-plugin:v1 ." +echo " docker push ${HARBOR_URL}/higress-plugins/my-plugin:v1" +echo "" +echo "5. Use in WasmPlugin:" +echo " url: oci://${HARBOR_URL}/higress-plugins/my-plugin:v1" +echo "" +echo -e "${YELLOW}⚠ Note: This is a basic installation for testing.${NC}" +echo " For production use:" +echo " - Enable TLS (set expose.tls.enabled=true)" +echo " - Use LoadBalancer or Ingress instead of NodePort" +echo " - Configure proper persistent storage" +echo " - Set strong admin password" +echo "" diff --git a/helm/core/charts/redis/values.yaml b/helm/core/charts/redis/values.yaml index d1ca05b5a..05741b6d9 100644 --- a/helm/core/charts/redis/values.yaml +++ b/helm/core/charts/redis/values.yaml @@ -3,7 +3,8 @@ # Declare variables to be passed into your templates. global: # -- Specify the image registry and pull policy - hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress + # Will inherit from parent chart's global.hub if not set + hub: "" # -- Specify image pull policy if default behavior isn't desired. # Default behavior: latest images will be Always else IfNotPresent. imagePullPolicy: "" diff --git a/helm/core/templates/_pod.tpl b/helm/core/templates/_pod.tpl index a5e680384..419a5af6d 100644 --- a/helm/core/templates/_pod.tpl +++ b/helm/core/templates/_pod.tpl @@ -203,7 +203,7 @@ template: {{- if $o11y.enabled }} {{- $config := $o11y.promtail }} - name: promtail - image: {{ $config.image.repository }}:{{ $config.image.tag }} + image: {{ $config.image.repository | default (printf "%s/promtail" .Values.global.hub) }}:{{ $config.image.tag }} imagePullPolicy: IfNotPresent args: - -config.file=/etc/promtail/promtail.yaml diff --git a/helm/core/values.yaml b/helm/core/values.yaml index d4f7360f2..56864f139 100644 --- a/helm/core/values.yaml +++ b/helm/core/values.yaml @@ -364,7 +364,7 @@ global: enabled: false promtail: image: - repository: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/promtail + repository: "" # Will use global.hub if not set tag: 2.9.4 port: 3101 resources: @@ -379,7 +379,7 @@ global: # The default value is "" and when caName="", the CA will be configured by other # mechanisms (e.g., environmental variable CA_PROVIDER). caName: "" -hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress +hub: "" # Will use global.hub if not set clusterName: "" # -- meshConfig defines runtime configuration of components, including Istiod and istio-agent behavior @@ -435,7 +435,7 @@ gateway: # -- The readiness timeout seconds readinessTimeoutSeconds: 3 - hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress + hub: "" # Will use global.hub if not set tag: "" # -- revision declares which revision this gateway is a part of revision: "" @@ -557,7 +557,7 @@ controller: replicas: 1 image: higress - hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress + hub: "" # Will use global.hub if not set tag: "" env: {} @@ -653,7 +653,7 @@ pilot: rollingMaxSurge: 100% rollingMaxUnavailable: 25% - hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress + hub: "" # Will use global.hub if not set tag: "" # -- Can be a full hub/image:tag @@ -806,7 +806,7 @@ pluginServer: replicas: 2 image: plugin-server - hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress + hub: "" # Will use global.hub if not set tag: "" imagePullSecrets: [] diff --git a/helm/higress/README.md b/helm/higress/README.md index e854da204..3452f1282 100644 --- a/helm/higress/README.md +++ b/helm/higress/README.md @@ -44,7 +44,7 @@ The command removes all the Kubernetes components associated with the chart and | controller.autoscaling.minReplicas | int | `1` | | | controller.autoscaling.targetCPUUtilizationPercentage | int | `80` | | | controller.env | object | `{}` | | -| controller.hub | string | `"higress-registry.cn-hangzhou.cr.aliyuncs.com/higress"` | | +| controller.hub | string | `""` | | | controller.image | string | `"higress"` | | | controller.imagePullSecrets | list | `[]` | | | controller.labels | object | `{}` | | @@ -96,7 +96,7 @@ The command removes all the Kubernetes components associated with the chart and | gateway.hostNetwork | bool | `false` | | | gateway.httpPort | int | `80` | | | gateway.httpsPort | int | `443` | | -| gateway.hub | string | `"higress-registry.cn-hangzhou.cr.aliyuncs.com/higress"` | | +| gateway.hub | string | `""` | | | gateway.image | string | `"gateway"` | | | gateway.kind | string | `"Deployment"` | Use a `DaemonSet` or `Deployment` | | gateway.labels | object | `{}` | Labels to apply to all resources | @@ -195,7 +195,7 @@ The command removes all the Kubernetes components associated with the chart and | global.multiCluster.clusterName | string | `""` | Should be set to the name of the cluster this installation will run in. This is required for sidecar injection to properly label proxies | | global.multiCluster.enabled | bool | `true` | Set to true to connect two kubernetes clusters via their respective ingressgateway services when pods in each cluster cannot directly talk to one another. All clusters should be using Istio mTLS and must have a shared root CA for this model to work. | | global.network | string | `""` | Network defines the network this cluster belong to. This name corresponds to the networks in the map of mesh networks. | -| global.o11y | object | `{"enabled":false,"promtail":{"image":{"repository":"higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/promtail","tag":"2.9.4"},"port":3101,"resources":{"limits":{"cpu":"500m","memory":"2Gi"}},"securityContext":{}}}` | Observability (o11y) configurations | +| global.o11y | object | `{"enabled":false,"promtail":{"image":{"repository":"","tag":"2.9.4"},"port":3101,"resources":{"limits":{"cpu":"500m","memory":"2Gi"}},"securityContext":{}}}` | Observability (o11y) configurations | | global.omitSidecarInjectorConfigMap | bool | `false` | | | global.onDemandRDS | bool | `false` | | | global.oneNamespace | bool | `false` | Whether to restrict the applications namespace the controller manages; If not set, controller watches all namespaces | @@ -247,7 +247,7 @@ The command removes all the Kubernetes components associated with the chart and | global.watchNamespace | string | `""` | If not empty, Higress Controller will only watch resources in the specified namespace. When isolating different business systems using K8s namespace, if each namespace requires a standalone gateway instance, this parameter can be used to confine the Ingress watching of Higress within the given namespace. | | global.xdsMaxRecvMsgSize | string | `"104857600"` | | | gzip | object | `{"chunkSize":4096,"compressionLevel":"BEST_COMPRESSION","compressionStrategy":"DEFAULT_STRATEGY","contentType":["text/html","text/css","text/plain","text/xml","application/json","application/javascript","application/xhtml+xml","image/svg+xml"],"disableOnEtagHeader":true,"enable":true,"memoryLevel":5,"minContentLength":1024,"windowBits":12}` | Gzip compression settings | -| hub | string | `"higress-registry.cn-hangzhou.cr.aliyuncs.com/higress"` | | +| hub | string | `""` | | | meshConfig | object | `{"enablePrometheusMerge":true,"rootNamespace":null,"trustDomain":"cluster.local"}` | meshConfig defines runtime configuration of components, including Istiod and istio-agent behavior See https://istio.io/docs/reference/config/istio.mesh.v1alpha1/ for all available options | | meshConfig.rootNamespace | string | `nil` | The namespace to treat as the administrative root namespace for Istio configuration. When processing a leaf namespace Istio will search for declarations in that namespace first and if none are found it will search in the root namespace. Any matching declaration found in the root namespace is processed as if it were declared in the leaf namespace. | | meshConfig.trustDomain | string | `"cluster.local"` | The trust domain corresponds to the trust root of a system Refer to https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md#21-trust-domain | @@ -264,7 +264,7 @@ The command removes all the Kubernetes components associated with the chart and | pilot.env.PILOT_ENABLE_METADATA_EXCHANGE | string | `"false"` | | | pilot.env.PILOT_SCOPE_GATEWAY_TO_NAMESPACE | string | `"false"` | | | pilot.env.VALIDATION_ENABLED | string | `"false"` | | -| pilot.hub | string | `"higress-registry.cn-hangzhou.cr.aliyuncs.com/higress"` | | +| pilot.hub | string | `""` | | | pilot.image | string | `"pilot"` | Can be a full hub/image:tag | | pilot.jwksResolverExtraRootCA | string | `""` | You can use jwksResolverExtraRootCA to provide a root certificate in PEM format. This will then be trusted by pilot when resolving JWKS URIs. | | pilot.keepaliveMaxServerConnectionAge | string | `"30m"` | The following is used to limit how long a sidecar can be connected to a pilot. It balances out load across pilot instances at the cost of increasing system churn. | @@ -279,7 +279,7 @@ The command removes all the Kubernetes components associated with the chart and | pilot.serviceAnnotations | object | `{}` | | | pilot.tag | string | `""` | | | pilot.traceSampling | float | `1` | | -| pluginServer.hub | string | `"higress-registry.cn-hangzhou.cr.aliyuncs.com/higress"` | | +| pluginServer.hub | string | `""` | | | pluginServer.image | string | `"plugin-server"` | | | pluginServer.imagePullSecrets | list | `[]` | | | pluginServer.labels | object | `{}` | |