mirror of
https://github.com/alibaba/higress.git
synced 2026-06-24 09:45:16 +08:00
feat: add nginx-to-higress-migration skill (#3411)
This commit is contained in:
198
.claude/skills/nginx-to-higress-migration/scripts/analyze-ingress.sh
Executable file
198
.claude/skills/nginx-to-higress-migration/scripts/analyze-ingress.sh
Executable file
@@ -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)"
|
||||
210
.claude/skills/nginx-to-higress-migration/scripts/generate-migration-test.sh
Executable file
210
.claude/skills/nginx-to-higress-migration/scripts/generate-migration-test.sh
Executable file
@@ -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 <higress-gateway-ip[:port]> [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
|
||||
261
.claude/skills/nginx-to-higress-migration/scripts/generate-plugin-scaffold.sh
Executable file
261
.claude/skills/nginx-to-higress-migration/scripts/generate-plugin-scaffold.sh
Executable file
@@ -0,0 +1,261 @@
|
||||
#!/bin/bash
|
||||
# Generate WASM plugin scaffold for nginx snippet migration
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "Usage: $0 <plugin-name> [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"
|
||||
157
.claude/skills/nginx-to-higress-migration/scripts/install-harbor.sh
Executable file
157
.claude/skills/nginx-to-higress-migration/scripts/install-harbor.sh
Executable file
@@ -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 ""
|
||||
Reference in New Issue
Block a user