180 lines
4.3 KiB
Go
180 lines
4.3 KiB
Go
package windsurf
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
type LSConnector interface {
|
|
Mode() string
|
|
Acquire(ctx context.Context, proxyURL string) (*LSLease, error)
|
|
Health(ctx context.Context) error
|
|
Status() *LSConnectorStatus
|
|
// Shutdown releases any resources owned by the connector. Connectors
|
|
// that only dial external endpoints implement this as a no-op; the
|
|
// embedded mode terminates its spawned LS processes here.
|
|
Shutdown()
|
|
}
|
|
|
|
type LSLease struct {
|
|
Mode string
|
|
Endpoint string
|
|
Client *LocalLSClient
|
|
Release func()
|
|
}
|
|
|
|
type LSConnectorStatus struct {
|
|
Mode string `json:"mode"`
|
|
Healthy bool `json:"healthy"`
|
|
Instances int `json:"instances"`
|
|
Endpoint string `json:"endpoint,omitempty"`
|
|
}
|
|
|
|
type DockerConnector struct {
|
|
host string
|
|
port int
|
|
csrfToken string
|
|
client *LocalLSClient
|
|
once sync.Once
|
|
}
|
|
|
|
func NewDockerConnector(host string, port int, csrfToken string) *DockerConnector {
|
|
return &DockerConnector{host: host, port: port, csrfToken: csrfToken}
|
|
}
|
|
|
|
func (d *DockerConnector) Mode() string { return "docker" }
|
|
|
|
func (d *DockerConnector) Acquire(_ context.Context, _ string) (*LSLease, error) {
|
|
d.once.Do(func() {
|
|
d.client = NewLocalLSClient(d.port, d.csrfToken)
|
|
d.client.BaseURL = fmt.Sprintf("http://%s:%d", d.host, d.port)
|
|
})
|
|
return &LSLease{
|
|
Mode: "docker",
|
|
Endpoint: fmt.Sprintf("%s:%d", d.host, d.port),
|
|
Client: d.client,
|
|
Release: func() {},
|
|
}, nil
|
|
}
|
|
|
|
func (d *DockerConnector) Health(ctx context.Context) error {
|
|
_, err := d.Acquire(ctx, "")
|
|
return err
|
|
}
|
|
|
|
func (d *DockerConnector) Status() *LSConnectorStatus {
|
|
return &LSConnectorStatus{
|
|
Mode: "docker",
|
|
Healthy: d.client != nil,
|
|
Instances: 1,
|
|
Endpoint: fmt.Sprintf("%s:%d", d.host, d.port),
|
|
}
|
|
}
|
|
|
|
// Shutdown is a no-op: DockerConnector only dials a remote endpoint and
|
|
// owns no long-lived goroutines or child processes.
|
|
func (d *DockerConnector) Shutdown() {}
|
|
|
|
type EmbeddedConnector struct {
|
|
pool *LSPool
|
|
}
|
|
|
|
func NewEmbeddedConnector(pool *LSPool) *EmbeddedConnector {
|
|
return &EmbeddedConnector{pool: pool}
|
|
}
|
|
|
|
func (e *EmbeddedConnector) Mode() string { return "embedded" }
|
|
|
|
func (e *EmbeddedConnector) Acquire(ctx context.Context, proxyURL string) (*LSLease, error) {
|
|
entry, err := e.pool.Ensure(ctx, proxyURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &LSLease{
|
|
Mode: "embedded",
|
|
Endpoint: fmt.Sprintf("localhost:%d", entry.Port),
|
|
Client: entry.Client,
|
|
Release: func() {},
|
|
}, nil
|
|
}
|
|
|
|
func (e *EmbeddedConnector) Health(_ context.Context) error {
|
|
status := e.pool.Status()
|
|
if !status.Running {
|
|
return fmt.Errorf("no LS instances running")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *EmbeddedConnector) Status() *LSConnectorStatus {
|
|
status := e.pool.Status()
|
|
readyCount := 0
|
|
for _, inst := range status.Instances {
|
|
if inst.Ready {
|
|
readyCount++
|
|
}
|
|
}
|
|
return &LSConnectorStatus{
|
|
Mode: "embedded",
|
|
Healthy: readyCount > 0,
|
|
Instances: len(status.Instances),
|
|
}
|
|
}
|
|
|
|
// Shutdown terminates every LS process in the pool. Must be called on
|
|
// application teardown; otherwise child processes leak until the OS
|
|
// reaps them at exit.
|
|
func (e *EmbeddedConnector) Shutdown() {
|
|
if e == nil || e.pool == nil {
|
|
return
|
|
}
|
|
e.pool.Shutdown()
|
|
}
|
|
|
|
type ExternalConnector struct {
|
|
baseURL string
|
|
port int
|
|
csrfToken string
|
|
client *LocalLSClient
|
|
once sync.Once
|
|
}
|
|
|
|
func NewExternalConnector(baseURL string, port int, csrfToken string) *ExternalConnector {
|
|
return &ExternalConnector{baseURL: baseURL, port: port, csrfToken: csrfToken}
|
|
}
|
|
|
|
func (x *ExternalConnector) Mode() string { return "external" }
|
|
|
|
func (x *ExternalConnector) Acquire(_ context.Context, _ string) (*LSLease, error) {
|
|
x.once.Do(func() {
|
|
x.client = NewLocalLSClient(x.port, x.csrfToken)
|
|
if x.baseURL != "" {
|
|
x.client.BaseURL = x.baseURL
|
|
}
|
|
})
|
|
return &LSLease{
|
|
Mode: "external",
|
|
Endpoint: x.baseURL,
|
|
Client: x.client,
|
|
Release: func() {},
|
|
}, nil
|
|
}
|
|
|
|
func (x *ExternalConnector) Health(ctx context.Context) error {
|
|
_, err := x.Acquire(ctx, "")
|
|
return err
|
|
}
|
|
|
|
func (x *ExternalConnector) Status() *LSConnectorStatus {
|
|
return &LSConnectorStatus{
|
|
Mode: "external",
|
|
Healthy: x.client != nil,
|
|
Instances: 1,
|
|
Endpoint: x.baseURL,
|
|
}
|
|
}
|
|
|
|
// Shutdown is a no-op: ExternalConnector only dials a remote endpoint.
|
|
func (x *ExternalConnector) Shutdown() {}
|