diff --git a/pkg/cloudagents/client.go b/pkg/cloudagents/client.go index 2957374f..d745c5b0 100644 --- a/pkg/cloudagents/client.go +++ b/pkg/cloudagents/client.go @@ -20,6 +20,7 @@ import ( "io" "io/fs" "net/http" + "net/url" "os" "regexp" "strings" @@ -161,6 +162,17 @@ func (c *Client) uploadAndBuild( return nil } +// getAgentsURL derives the cloud-agents service URL from the project URL. +// It replaces the project subdomain with "agents" (and optionally a region prefix) +// so that build/log requests are routed to the cloud-agents service. +// +// Examples: +// +// getAgentsURL("") -> https://agents.livekit.cloud +// getAgentsURL("osbxash1a") -> https://osbxash1a.agents.livekit.cloud +// +// When serverRegion is set, the request is pinned to a specific cloud-agents +// cluster rather than relying on GeoDNS resolution. func (c *Client) getAgentsURL(serverRegion string) string { agentsURL := c.projectURL if os.Getenv("LK_AGENTS_URL") != "" { @@ -169,13 +181,25 @@ func (c *Client) getAgentsURL(serverRegion string) string { if strings.HasPrefix(agentsURL, "ws") { agentsURL = strings.Replace(agentsURL, "ws", "http", 1) } - if !strings.Contains(agentsURL, "localhost") && !strings.Contains(agentsURL, "127.0.0.1") { - pattern := `^https://[a-zA-Z0-9\-]+\.` - re := regexp.MustCompile(pattern) - if serverRegion != "" { - serverRegion = fmt.Sprintf("%s.", serverRegion) - } - agentsURL = re.ReplaceAllString(agentsURL, fmt.Sprintf("https://%sagents.", serverRegion)) + + // skip rewrite for local development + if isLocalURL(agentsURL) { + return agentsURL + } + + pattern := `^https://[a-zA-Z0-9\-]+\.` + re := regexp.MustCompile(pattern) + if serverRegion != "" { + serverRegion = fmt.Sprintf("%s.", serverRegion) + } + return re.ReplaceAllString(agentsURL, fmt.Sprintf("https://%sagents.", serverRegion)) +} + +func isLocalURL(rawURL string) bool { + u, err := url.Parse(rawURL) + if err != nil { + return false } - return agentsURL + host := u.Hostname() + return host == "localhost" || host == "127.0.0.1" } diff --git a/pkg/cloudagents/client_test.go b/pkg/cloudagents/client_test.go new file mode 100644 index 00000000..a6e8a852 --- /dev/null +++ b/pkg/cloudagents/client_test.go @@ -0,0 +1,72 @@ +package cloudagents + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetAgentsURL(t *testing.T) { + tests := []struct { + name string + projectURL string + serverRegion string + expected string + }{ + { + name: "standard https project URL", + projectURL: "https://my-project.livekit.cloud", + expected: "https://agents.livekit.cloud", + }, + { + name: "wss project URL", + projectURL: "wss://my-project.livekit.cloud", + expected: "https://agents.livekit.cloud", + }, + { + name: "subdomain containing localhost", + projectURL: "wss://andrewnitu-localhost-project.livekit.cloud", + expected: "https://agents.livekit.cloud", + }, + { + name: "subdomain containing 127", + projectURL: "https://test-127-app.livekit.cloud", + expected: "https://agents.livekit.cloud", + }, + { + name: "with server region", + projectURL: "https://my-project.livekit.cloud", + serverRegion: "osbxash1a", + expected: "https://osbxash1a.agents.livekit.cloud", + }, + { + name: "wss with server region", + projectURL: "wss://my-project.livekit.cloud", + serverRegion: "osbxmum1a", + expected: "https://osbxmum1a.agents.livekit.cloud", + }, + { + name: "localhost development URL", + projectURL: "http://localhost:7880", + expected: "http://localhost:7880", + }, + { + name: "127.0.0.1 development URL", + projectURL: "http://127.0.0.1:7880", + expected: "http://127.0.0.1:7880", + }, + { + name: "staging project URL", + projectURL: "https://my-project.staging.livekit.cloud", + expected: "https://agents.staging.livekit.cloud", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Client{projectURL: tt.projectURL} + result := c.getAgentsURL(tt.serverRegion) + require.Equal(t, tt.expected, result) + }) + } +}