Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gateway/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ services:
- APIP_GW_CONTROLLER_METRICS_PORT=9091
- APIP_GW_GATEWAY_REGISTRATION_TOKEN=${GATEWAY_REGISTRATION_TOKEN:-}
- APIP_GW_CONTROLPLANE_HOST=${GATEWAY_CONTROLPLANE_HOST:-host.docker.internal:9243}
- APIP_GW_CONTROLPLANE_ON_PREM=${GATEWAY_CONTROLPLANE_ON_PREM:-false}
volumes:
- controller-data:/app/data
- ./configs/config.toml:/etc/gateway-controller/config.toml:ro
Expand Down
8 changes: 6 additions & 2 deletions gateway/gateway-controller/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,9 @@ type LoggingConfig struct {

// ControlPlaneConfig holds control plane connection configuration
type ControlPlaneConfig struct {
Host string `koanf:"host"` // Control plane hostname
Host string `koanf:"host"` // Control plane host:port (e.g. platform-api:9243 for cloud; on-prem: apim.example.com:9443 or host.docker.internal:9443)
Token string `koanf:"token"` // Registration token (api-key)
OnPrem bool `koanf:"on_prem"` // If true, use on-prem path (/internal/data/v1/ws); else cloud path (/api/internal/v1/ws)
ReconnectInitial time.Duration `koanf:"reconnect_initial"` // Initial retry delay
ReconnectMax time.Duration `koanf:"reconnect_max"` // Maximum retry delay
PollingInterval time.Duration `koanf:"polling_interval"` // Reconciliation polling interval
Expand Down Expand Up @@ -406,6 +407,8 @@ func LoadConfig(configPath string) (*Config, error) {
switch s {
case "controlplane_host":
return "controller.controlplane.host"
case "controlplane_on_prem":
return "controller.controlplane.on_prem"
case "gateway_registration_token":
return "controller.controlplane.token"
case "reconnect_initial":
Expand Down Expand Up @@ -519,6 +522,7 @@ func defaultConfig() *Config {
ControlPlane: ControlPlaneConfig{
Host: "localhost:9243",
Token: "",
OnPrem: false,
ReconnectInitial: 1 * time.Second,
ReconnectMax: 5 * time.Minute,
PollingInterval: 15 * time.Minute,
Expand Down Expand Up @@ -934,7 +938,7 @@ func (c *Config) validateEventGatewayConfig() error {

// validateControlPlaneConfig validates the control plane configuration
func (c *Config) validateControlPlaneConfig() error {
// Host validation - required if control plane is configured
// Host is required; all URLs are built from host in code (path depends on on_prem).
if c.Controller.ControlPlane.Host == "" {
return fmt.Errorf("controlplane.host is required")
}
Expand Down
35 changes: 23 additions & 12 deletions gateway/gateway-controller/pkg/controlplane/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ func (c *Client) Start() error {

c.logger.Info("Starting control plane client",
slog.String("host", c.config.Host),
slog.String("websocket_url", c.getWebSocketURL()),
slog.String("websocket_url", c.getWebSocketConnectURL()),
slog.Bool("on_prem", c.config.OnPrem),
)

// Start connection in background
Expand Down Expand Up @@ -240,7 +241,8 @@ func (c *Client) Connect() error {
c.setState(Connecting)

c.logger.Info("Connecting to control plane",
slog.String("url", c.getWebSocketURL()),
slog.String("url", c.getWebSocketConnectURL()),
slog.Bool("on_prem", c.config.OnPrem),
slog.Int("retry_count", c.state.RetryCount),
)

Expand All @@ -261,8 +263,8 @@ func (c *Client) Connect() error {
headers := http.Header{}
headers.Add("api-key", c.config.Token)

// Dial WebSocket
wsURL := c.getWebSocketURL() + "/gateways/connect"
// Dial WebSocket: URL built from host (path depends on OnPrem)
wsURL := c.getWebSocketConnectURL()
conn, resp, err := dialer.Dial(wsURL, headers)
if err != nil {
if resp != nil {
Expand Down Expand Up @@ -1913,16 +1915,25 @@ func (c *Client) NotifyAPIDeployment(apiID string, apiConfig *models.StoredConfi
return c.apiUtilsService.NotifyAPIDeployment(apiID, apiConfig, deploymentID)
}

// getWebSocketURL constructs the base WebSocket URL from configuration
// getWebSocketURL constructs the base WebSocket URL from configuration.
// If OnPrem is true, uses on-prem path (/internal/data/v1/ws); otherwise cloud path (/api/internal/v1/ws).
func (c *Client) getWebSocketURL() string {
return fmt.Sprintf("wss://%s/api/internal/v1/ws",
c.config.Host,
)
if c.config.OnPrem {
return fmt.Sprintf("wss://%s/internal/data/v1/ws", c.config.Host)
}
return fmt.Sprintf("wss://%s/api/internal/v1/ws", c.config.Host)
}

// getWebSocketConnectURL returns the full WebSocket URL for gateway connect.
func (c *Client) getWebSocketConnectURL() string {
return c.getWebSocketURL() + "/gateways/connect"
}

// getRestAPIBaseURL constructs the base REST API URL from configuration
// getRestAPIBaseURL constructs the base REST API URL from configuration.
// If OnPrem is true, uses on-prem path (/internal/data/v1); otherwise cloud path (/api/internal/v1).
func (c *Client) getRestAPIBaseURL() string {
return fmt.Sprintf("https://%s/api/internal/v1",
c.config.Host,
)
if c.config.OnPrem {
return fmt.Sprintf("https://%s/internal/data/v1", c.config.Host)
}
return fmt.Sprintf("https://%s/api/internal/v1", c.config.Host)
}
48 changes: 48 additions & 0 deletions gateway/gateway-controller/pkg/controlplane/controlplane_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,29 @@ func createTestClient(t *testing.T) *Client {
return NewClient(cfg, logger, store, nil, nil, nil, routerConfig, nil, nil, nil, nil, nil, nil, nil)
}

func createTestClientWithOnPrem(t *testing.T, onPrem bool) *Client {
t.Helper()
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError}))
store := storage.NewConfigStore()

cfg := config.ControlPlaneConfig{
Host: "control-plane.example.com",
Token: "test-token",
OnPrem: onPrem,
ReconnectInitial: 1 * time.Second,
ReconnectMax: 30 * time.Second,
}

routerConfig := &config.RouterConfig{
VHosts: config.VHostsConfig{
Main: config.VHostEntry{Default: "api.example.com"},
Sandbox: config.VHostEntry{Default: "sandbox.example.com"},
},
}

return NewClient(cfg, logger, store, nil, nil, nil, routerConfig, nil, nil, nil, nil, nil, nil, nil)
}

func TestNewClient(t *testing.T) {
client := createTestClient(t)
if client == nil {
Expand Down Expand Up @@ -223,6 +246,21 @@ func TestClient_getWebSocketURL(t *testing.T) {
}
}

func TestClient_getWebSocketURL_OnPrem(t *testing.T) {
client := createTestClientWithOnPrem(t, true)

url := client.getWebSocketURL()
expected := "wss://control-plane.example.com/internal/data/v1/ws"
if url != expected {
t.Errorf("getWebSocketURL() OnPrem = %q, want %q", url, expected)
}
connectURL := client.getWebSocketConnectURL()
expectedConnect := expected + "/gateways/connect"
if connectURL != expectedConnect {
t.Errorf("getWebSocketConnectURL() = %q, want %q", connectURL, expectedConnect)
}
}

func TestClient_getRestAPIBaseURL(t *testing.T) {
client := createTestClient(t)

Expand All @@ -233,6 +271,16 @@ func TestClient_getRestAPIBaseURL(t *testing.T) {
}
}

func TestClient_getRestAPIBaseURL_OnPrem(t *testing.T) {
client := createTestClientWithOnPrem(t, true)

url := client.getRestAPIBaseURL()
expected := "https://control-plane.example.com/internal/data/v1"
if url != expected {
t.Errorf("getRestAPIBaseURL() OnPrem = %q, want %q", url, expected)
}
}

func TestClient_isShuttingDown(t *testing.T) {
client := createTestClient(t)

Expand Down