Skip to content

Commit d355b81

Browse files
Merge branch 'wso2:main' into fix-json-schema-guardrail-scenarios
2 parents d790150 + bb1a41c commit d355b81

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+4133
-2047
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
name: Gateway Integration Test (Postgres)
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
branches:
7+
- main
8+
paths:
9+
- 'gateway/**'
10+
- '.github/workflows/gateway-integration-test-postgres.yml'
11+
12+
jobs:
13+
integration-test:
14+
runs-on: ubuntu-24.04
15+
steps:
16+
- name: Checkout code
17+
uses: actions/checkout@v4
18+
19+
- name: Set up Go
20+
uses: actions/setup-go@v5
21+
with:
22+
go-version: '1.25'
23+
24+
- name: Set up Docker Buildx
25+
uses: docker/setup-buildx-action@v3
26+
27+
- name: Build coverage-instrumented images
28+
run: |
29+
cd gateway
30+
make build-coverage VERSION=test
31+
32+
- name: Build mock server images
33+
run: |
34+
for mock in mock-jwks mock-azure-content-safety mock-aws-bedrock-guardrail mock-embedding-provider mock-analytics-collector; do
35+
echo "Building $mock..."
36+
docker build -t ghcr.io/wso2/api-platform/$mock:latest tests/mock-servers/$mock
37+
done
38+
39+
- name: Verify gateway-controller uses PostgreSQL
40+
run: |
41+
set -euo pipefail
42+
cd gateway/it
43+
44+
PROJECT="gateway-it-postgres-verify"
45+
cleanup() {
46+
docker compose -p "$PROJECT" -f docker-compose.test.postgres.yaml down -v --remove-orphans || true
47+
}
48+
trap cleanup EXIT
49+
50+
docker compose -p "$PROJECT" -f docker-compose.test.postgres.yaml up -d postgres gateway-controller
51+
52+
timeout 90 bash -c 'until curl -fsS http://localhost:9090/health >/dev/null; do sleep 2; done'
53+
54+
docker logs it-gateway-controller > /tmp/gateway-controller.log 2>&1 || true
55+
grep -Eq "Initializing PostgreSQL storage|PostgreSQL schema up to date" /tmp/gateway-controller.log
56+
57+
schema_count="$(docker compose -p "$PROJECT" -f docker-compose.test.postgres.yaml exec -T postgres \
58+
psql -U gateway -d gateway_test -tAc "SELECT COUNT(*) FROM schema_migrations WHERE id = 1 AND version >= 1;")"
59+
[ "$schema_count" = "1" ]
60+
61+
conn_count="$(docker compose -p "$PROJECT" -f docker-compose.test.postgres.yaml exec -T postgres \
62+
psql -U gateway -d gateway_test -tAc "SELECT COUNT(*) FROM pg_stat_activity WHERE application_name = 'gateway-controller';")"
63+
[ "${conn_count:-0}" -ge 1 ]
64+
65+
- name: Run integration tests
66+
run: |
67+
cd gateway
68+
COMPOSE_FILE=docker-compose.test.postgres.yaml make test-integration
69+
70+
- name: Upload coverage report
71+
uses: actions/upload-artifact@v4
72+
if: always()
73+
with:
74+
name: coverage-report-postgres
75+
path: gateway/it/coverage/output
76+
retention-days: 7
77+
78+
- name: Upload test reports
79+
uses: actions/upload-artifact@v4
80+
if: always()
81+
with:
82+
name: test-reports-postgres
83+
path: gateway/it/reports/
84+
retention-days: 7
85+
86+
- name: Debug on failure - Dump logs
87+
if: failure()
88+
run: |
89+
echo "=== Docker Containers ==="
90+
docker ps -a
91+
92+
echo ""
93+
echo "=== gateway/it/logs Directory Contents ==="
94+
if [ -d gateway/it/logs ]; then
95+
if [ "$(ls -A gateway/it/logs)" ]; then
96+
for f in gateway/it/logs/*; do
97+
echo ""
98+
echo "--- Contents of $f ---"
99+
cat "$f"
100+
done
101+
else
102+
echo "No log files found in gateway/it/logs."
103+
fi
104+
else
105+
echo "Directory gateway/it/logs does not exist."
106+
fi

.github/workflows/gateway-integration-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
- name: Build coverage-instrumented images
2828
run: |
2929
cd gateway
30-
make build-coverage
30+
make build-coverage VERSION=test
3131
3232
- name: Build mock server images
3333
run: |

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,4 @@ cli/src/tests/target/
126126
# Component specific patterns
127127
# Platform API
128128
platform-api/src/data
129+
gateway/gateway-runtime/target

.vscode/launch.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"-manifest", "${workspaceFolder}/gateway/build.yaml",
4242
// "-manifest", "${workspaceFolder}/gateway/sample-policies/build.yaml",
4343
"-system-manifest-lock", "${workspaceFolder}/gateway/system-policies/system-policy-manifest-lock.yaml",
44-
"-policy-engine-src", "${workspaceFolder}/gateway/policy-engine",
44+
"-policy-engine-src", "${workspaceFolder}/gateway/gateway-runtime/policy-engine",
4545
"-out-dir", "${workspaceFolder}/gateway/gateway-builder/target/output",
4646
"-log-level", "debug",
4747
],
@@ -51,7 +51,7 @@
5151
"type": "go",
5252
"request": "launch",
5353
"mode": "auto",
54-
"program": "${workspaceFolder}/gateway/policy-engine/cmd/policy-engine",
54+
"program": "${workspaceFolder}/gateway/gateway-runtime/policy-engine/cmd/policy-engine",
5555
"args": [
5656
"-config", "${workspaceFolder}/gateway/configs/config.toml" ,
5757
"-xds-server", "localhost:18001",
@@ -63,10 +63,10 @@
6363
"type": "go",
6464
"request": "launch",
6565
"mode": "auto",
66-
"program": "${workspaceFolder}/gateway/policy-engine/cmd/policy-engine",
66+
"program": "${workspaceFolder}/gateway/gateway-runtime/policy-engine/cmd/policy-engine",
6767
"args": [
6868
"-config", "${workspaceFolder}/gateway/configs/config.toml",
69-
"-policy-chains-file", "${workspaceFolder}/gateway/policy-engine/configs/policy-chains.yaml",
69+
"-policy-chains-file", "${workspaceFolder}/gateway/gateway-runtime/policy-engine/configs/policy-chains.yaml",
7070
],
7171
},
7272
]

gateway/build.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ policies:
5454
gomodule: github.com/wso2/gateway-controllers/policies/regex-guardrail@v0
5555
- name: remove-headers
5656
gomodule: github.com/wso2/gateway-controllers/policies/remove-headers@v0
57+
- name: request-rewrite
58+
gomodule: github.com/wso2/gateway-controllers/policies/request-rewrite@v0
5759
- name: respond
5860
gomodule: github.com/wso2/gateway-controllers/policies/respond@v0
5961
- name: semantic-cache

gateway/configs/config-template.toml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,28 @@ cert_file = "./certs/server.crt"
2727
key_file = "./certs/server.key"
2828

2929
[gateway_controller.storage]
30-
# Storage type: "sqlite", "postgres" (future), or "memory"
30+
# Storage type: "sqlite", "postgres", or "memory"
3131
type = "sqlite"
3232

3333
[gateway_controller.storage.sqlite]
3434
path = "./data/gateway.db"
3535

36+
[gateway_controller.storage.postgres]
37+
# Optional DSN. If set, host/port/database/user/password are ignored.
38+
# dsn = "postgres://user:password@localhost:5432/gateway?sslmode=require"
39+
host = "localhost"
40+
port = 5432
41+
database = "gateway"
42+
user = "gateway"
43+
password = ""
44+
sslmode = "require" # disable, require, verify-ca, verify-full
45+
connect_timeout = "5s"
46+
max_open_conns = 25
47+
max_idle_conns = 5
48+
conn_max_lifetime = "30m"
49+
conn_max_idle_time = "5m"
50+
application_name = "gateway-controller"
51+
3652
[gateway_controller.policies]
3753
# Directory containing policy definitions
3854
definitions_path = "./default-policies"

gateway/gateway-controller/README.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,22 @@ server:
9292

9393
# Storage configuration
9494
storage:
95-
type: sqlite # "sqlite", "postgres" (future), or "memory"
95+
type: sqlite # "sqlite", "postgres", or "memory"
9696
sqlite:
9797
path: ./data/gateway.db # SQLite database file path
98+
postgres: # Used when type=postgres
99+
host: localhost
100+
port: 5432
101+
database: gateway
102+
user: gateway
103+
password: ""
104+
sslmode: require # disable, require, verify-ca, verify-full
105+
connect_timeout: 5s
106+
max_open_conns: 25
107+
max_idle_conns: 5
108+
conn_max_lifetime: 30m
109+
conn_max_idle_time: 5m
110+
application_name: gateway-controller
98111

99112
# Router (Envoy) configuration
100113
router:
@@ -130,6 +143,16 @@ export APIP_GW_GATEWAY__CONTROLLER_STORAGE_TYPE=memory
130143
# Override SQLite database path
131144
export APIP_GW_GATEWAY__CONTROLLER_STORAGE_SQLITE_PATH=/custom/path/gateway.db
132145

146+
# Configure PostgreSQL storage
147+
export APIP_GW_GATEWAY__CONTROLLER_STORAGE_TYPE=postgres
148+
export APIP_GW_GATEWAY__CONTROLLER_STORAGE_POSTGRES_HOST=postgres.example.internal
149+
export APIP_GW_GATEWAY__CONTROLLER_STORAGE_POSTGRES_PORT=5432
150+
export APIP_GW_GATEWAY__CONTROLLER_STORAGE_POSTGRES_DATABASE=gateway
151+
export APIP_GW_GATEWAY__CONTROLLER_STORAGE_POSTGRES_USER=gateway
152+
export APIP_GW_GATEWAY__CONTROLLER_STORAGE_POSTGRES_PASSWORD=secret
153+
export APIP_GW_GATEWAY__CONTROLLER_STORAGE_POSTGRES_SSLMODE=require
154+
export APIP_GW_GATEWAY__CONTROLLER_STORAGE_POSTGRES_MAX__OPEN__CONNS=25
155+
133156
# Disable access logs
134157
export APIP_GW_GATEWAY__CONTROLLER_ROUTER_ACCESS__LOGS_ENABLED=false
135158

@@ -153,6 +176,27 @@ storage:
153176
path: ./data/gateway.db
154177
```
155178
179+
#### Persistent Mode with PostgreSQL
180+
Use an external PostgreSQL server for persistence:
181+
182+
```yaml
183+
storage:
184+
type: postgres
185+
postgres:
186+
host: postgres.example.internal
187+
port: 5432
188+
database: gateway
189+
user: gateway
190+
password: ${DB_PASSWORD}
191+
sslmode: require
192+
connect_timeout: 5s
193+
max_open_conns: 25
194+
max_idle_conns: 5
195+
conn_max_lifetime: 30m
196+
conn_max_idle_time: 5m
197+
application_name: gateway-controller
198+
```
199+
156200
#### Memory-Only Mode
157201
No persistent storage (useful for testing):
158202

gateway/gateway-controller/cmd/controller/main.go

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package main
22

33
import (
44
"context"
5+
"errors"
56
"flag"
67
"fmt"
78
"log/slog"
89
"net/http"
910
"os"
1011
"os/signal"
12+
"strings"
1113
"syscall"
1214
"time"
1315

@@ -38,6 +40,29 @@ var (
3840
BuildDate = "unknown"
3941
)
4042

43+
func toBackendConfig(cfg *config.Config) storage.BackendConfig {
44+
pg := cfg.GatewayController.Storage.Postgres
45+
return storage.BackendConfig{
46+
Type: cfg.GatewayController.Storage.Type,
47+
SQLitePath: cfg.GatewayController.Storage.SQLite.Path,
48+
Postgres: storage.PostgresConnectionConfig{
49+
DSN: pg.DSN,
50+
Host: pg.Host,
51+
Port: pg.Port,
52+
Database: pg.Database,
53+
User: pg.User,
54+
Password: pg.Password,
55+
SSLMode: pg.SSLMode,
56+
ConnectTimeout: pg.ConnectTimeout,
57+
MaxOpenConns: pg.MaxOpenConns,
58+
MaxIdleConns: pg.MaxIdleConns,
59+
ConnMaxLifetime: pg.ConnMaxLifetime,
60+
ConnMaxIdleTime: pg.ConnMaxIdleTime,
61+
ApplicationName: pg.ApplicationName,
62+
},
63+
}
64+
}
65+
4166
func main() {
4267
// Parse command-line flags
4368
configPath := flag.String("config", "", "Path to configuration file (required)")
@@ -86,29 +111,20 @@ func main() {
86111
// Initialize storage based on type
87112
var db storage.Storage
88113
if cfg.IsPersistentMode() {
89-
switch cfg.GatewayController.Storage.Type {
90-
case "sqlite":
91-
log.Info("Initializing SQLite storage", slog.String("path", cfg.GatewayController.Storage.SQLite.Path))
92-
db, err = storage.NewSQLiteStorage(cfg.GatewayController.Storage.SQLite.Path, log)
93-
if err != nil {
94-
// Check for database locked error and provide clear guidance
95-
if err.Error() == "database is locked" || err.Error() == "failed to open database: database is locked" {
96-
log.Error("Database is locked by another process",
97-
slog.String("database_path", cfg.GatewayController.Storage.SQLite.Path),
98-
slog.String("troubleshooting", "Check if another gateway-controller instance is running or remove stale WAL files"))
99-
os.Exit(1)
100-
}
101-
log.Error("Failed to initialize SQLite database", slog.Any("error", err))
114+
db, err = storage.NewStorage(toBackendConfig(cfg), log)
115+
if err != nil {
116+
if strings.EqualFold(cfg.GatewayController.Storage.Type, "sqlite") && errors.Is(err, storage.ErrDatabaseLocked) {
117+
log.Error("Database is locked by another process",
118+
slog.String("database_path", cfg.GatewayController.Storage.SQLite.Path),
119+
slog.String("troubleshooting", "Check if another gateway-controller instance is running or remove stale WAL files"))
102120
os.Exit(1)
103121
}
104-
defer db.Close()
105-
case "postgres":
106-
log.Error("PostgreSQL storage not yet implemented")
107-
os.Exit(1)
108-
default:
109-
log.Error("Unknown storage type", slog.String("type", cfg.GatewayController.Storage.Type))
122+
log.Error("Failed to initialize database storage",
123+
slog.String("type", cfg.GatewayController.Storage.Type),
124+
slog.Any("error", err))
110125
os.Exit(1)
111126
}
127+
defer db.Close()
112128
} else {
113129
log.Info("Running in memory-only mode (no persistent storage)")
114130
}

0 commit comments

Comments
 (0)