mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
Merge pull request #2514 from lc6464/fix/issue-2488-host-binding
feat(launcher): add host overrides for launcher and gateway
This commit is contained in:
@@ -1136,6 +1136,8 @@ func LoadConfig(path string) (*Config, error) {
|
||||
|
||||
applyLegacyBindingsMigration(data, cfg)
|
||||
|
||||
gatewayHostBeforeEnv := cfg.Gateway.Host
|
||||
|
||||
if err = env.Parse(cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1144,6 +1146,10 @@ func LoadConfig(path string) (*Config, error) {
|
||||
if err = InitChannelList(cfg.Channels); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.Gateway.Host, err = resolveGatewayHostFromEnv(gatewayHostBeforeEnv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid gateway host: %w", err)
|
||||
}
|
||||
|
||||
// Expand multi-key configs into separate entries for key-level failover
|
||||
cfg.ModelList = expandMultiKeyModels(cfg.ModelList)
|
||||
|
||||
@@ -503,7 +503,7 @@ func TestDefaultConfig_Temperature(t *testing.T) {
|
||||
func TestDefaultConfig_Gateway(t *testing.T) {
|
||||
cfg := DefaultConfig()
|
||||
|
||||
if cfg.Gateway.Host != "127.0.0.1" {
|
||||
if cfg.Gateway.Host != "localhost" {
|
||||
t.Error("Gateway host should have default value")
|
||||
}
|
||||
if cfg.Gateway.Port == 0 {
|
||||
@@ -739,7 +739,7 @@ func TestConfig_Complete(t *testing.T) {
|
||||
if cfg.Agents.Defaults.MaxToolIterations == 0 {
|
||||
t.Error("MaxToolIterations should not be zero")
|
||||
}
|
||||
if cfg.Gateway.Host != "127.0.0.1" {
|
||||
if cfg.Gateway.Host != "localhost" {
|
||||
t.Error("Gateway host should have default value")
|
||||
}
|
||||
if cfg.Gateway.Port == 0 {
|
||||
|
||||
@@ -259,7 +259,7 @@ func DefaultConfig() *Config {
|
||||
},
|
||||
},
|
||||
Gateway: GatewayConfig{
|
||||
Host: "127.0.0.1",
|
||||
Host: "localhost",
|
||||
Port: 18790,
|
||||
HotReload: false,
|
||||
LogLevel: DefaultGatewayLogLevel,
|
||||
|
||||
@@ -39,7 +39,7 @@ const (
|
||||
EnvBinary = "PICOCLAW_BINARY"
|
||||
|
||||
// EnvGatewayHost overrides the host address for the gateway server.
|
||||
// Default: "127.0.0.1"
|
||||
// Default: "localhost"
|
||||
EnvGatewayHost = "PICOCLAW_GATEWAY_HOST"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ package config
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/sipeed/picoclaw/pkg/logger"
|
||||
"github.com/sipeed/picoclaw/pkg/netbind"
|
||||
)
|
||||
|
||||
const DefaultGatewayLogLevel = "warn"
|
||||
@@ -49,6 +51,31 @@ func EffectiveGatewayLogLevel(cfg *Config) string {
|
||||
return normalizeGatewayLogLevel(cfg.Gateway.LogLevel)
|
||||
}
|
||||
|
||||
func resolveGatewayHostFromEnv(baseHost string) (string, error) {
|
||||
envHost, ok := os.LookupEnv(EnvGatewayHost)
|
||||
if !ok {
|
||||
return normalizeGatewayHostInput(baseHost)
|
||||
}
|
||||
|
||||
envHost = strings.TrimSpace(envHost)
|
||||
if envHost == "" {
|
||||
return normalizeGatewayHostInput(baseHost)
|
||||
}
|
||||
|
||||
return normalizeGatewayHostInput(envHost)
|
||||
}
|
||||
|
||||
func normalizeGatewayHostInput(host string) (string, error) {
|
||||
host = strings.TrimSpace(host)
|
||||
if host == "" {
|
||||
host = strings.TrimSpace(DefaultConfig().Gateway.Host)
|
||||
}
|
||||
if host == "" {
|
||||
host = "localhost"
|
||||
}
|
||||
return netbind.NormalizeHostInput(host)
|
||||
}
|
||||
|
||||
// ResolveGatewayLogLevel reads the configured gateway log level without triggering
|
||||
// the full config loader, so startup code can apply logging before config load logs run.
|
||||
// The PICOCLAW_LOG_LEVEL environment variable overrides the file value.
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func writeGatewayHostTestConfig(t *testing.T, host string) string {
|
||||
t.Helper()
|
||||
|
||||
configPath := filepath.Join(t.TempDir(), "config.json")
|
||||
raw := fmt.Sprintf(`{"version":2,"gateway":{"host":%q,"port":18790}}`, host)
|
||||
if err := os.WriteFile(configPath, []byte(raw), 0o600); err != nil {
|
||||
t.Fatalf("WriteFile(configPath): %v", err)
|
||||
}
|
||||
return configPath
|
||||
}
|
||||
|
||||
func TestLoadConfig_GatewayHostEnvTrimmed(t *testing.T) {
|
||||
configPath := writeGatewayHostTestConfig(t, "127.0.0.1")
|
||||
t.Setenv(EnvGatewayHost, " ::1 ")
|
||||
|
||||
cfg, err := LoadConfig(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadConfig() error: %v", err)
|
||||
}
|
||||
if cfg.Gateway.Host != "::1" {
|
||||
t.Fatalf("cfg.Gateway.Host = %q, want %q", cfg.Gateway.Host, "::1")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfig_GatewayHostBlankEnvFallsBackToConfigHost(t *testing.T) {
|
||||
configPath := writeGatewayHostTestConfig(t, " localhost ")
|
||||
t.Setenv(EnvGatewayHost, " ")
|
||||
|
||||
cfg, err := LoadConfig(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadConfig() error: %v", err)
|
||||
}
|
||||
want, err := normalizeGatewayHostInput("localhost")
|
||||
if err != nil {
|
||||
t.Fatalf("normalizeGatewayHostInput() error: %v", err)
|
||||
}
|
||||
if cfg.Gateway.Host != want {
|
||||
t.Fatalf("cfg.Gateway.Host = %q, want %q", cfg.Gateway.Host, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfig_GatewayHostBlankEnvAndConfigFallsBackToDefault(t *testing.T) {
|
||||
configPath := writeGatewayHostTestConfig(t, " ")
|
||||
t.Setenv(EnvGatewayHost, " ")
|
||||
|
||||
cfg, err := LoadConfig(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadConfig() error: %v", err)
|
||||
}
|
||||
|
||||
defaultHost, err := normalizeGatewayHostInput(DefaultConfig().Gateway.Host)
|
||||
if err != nil {
|
||||
t.Fatalf("normalizeGatewayHostInput() error: %v", err)
|
||||
}
|
||||
if cfg.Gateway.Host != defaultHost {
|
||||
t.Fatalf("cfg.Gateway.Host = %q, want %q", cfg.Gateway.Host, defaultHost)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfig_GatewayHostEnvPreservesExplicitWildcardHost(t *testing.T) {
|
||||
configPath := writeGatewayHostTestConfig(t, "localhost")
|
||||
t.Setenv(EnvGatewayHost, " 0.0.0.0 ")
|
||||
|
||||
cfg, err := LoadConfig(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadConfig() error: %v", err)
|
||||
}
|
||||
|
||||
want, err := normalizeGatewayHostInput("0.0.0.0")
|
||||
if err != nil {
|
||||
t.Fatalf("normalizeGatewayHostInput() error: %v", err)
|
||||
}
|
||||
if cfg.Gateway.Host != want {
|
||||
t.Fatalf("cfg.Gateway.Host = %q, want %q", cfg.Gateway.Host, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfig_GatewayHostEnvNormalizesMultiHostInput(t *testing.T) {
|
||||
configPath := writeGatewayHostTestConfig(t, "localhost")
|
||||
t.Setenv(EnvGatewayHost, " [::1] , 127.0.0.1 , ::1 ")
|
||||
|
||||
cfg, err := LoadConfig(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadConfig() error: %v", err)
|
||||
}
|
||||
if cfg.Gateway.Host != "::1,127.0.0.1" {
|
||||
t.Fatalf("cfg.Gateway.Host = %q, want %q", cfg.Gateway.Host, "::1,127.0.0.1")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user