859640d814
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/claude-agent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
150 lines
3.3 KiB
Go
150 lines
3.3 KiB
Go
package embedded
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
|
|
"github.com/fujin/anthropic-proxy/internal/config"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type Perses struct {
|
|
cfg config.EmbeddedConfig
|
|
proxyPort int
|
|
cmd *exec.Cmd
|
|
tmpDir string
|
|
}
|
|
|
|
func NewPerses(cfg config.EmbeddedConfig, proxyPort int) *Perses {
|
|
return &Perses{cfg: cfg, proxyPort: proxyPort}
|
|
}
|
|
|
|
func (p *Perses) Start() error {
|
|
bin, err := ensureBinary("perses", p.cfg.PersesBinary, p.cfg.BinDir)
|
|
if err != nil {
|
|
return fmt.Errorf("perses: %w", err)
|
|
}
|
|
|
|
p.tmpDir, err = os.MkdirTemp("", "perses-*")
|
|
if err != nil {
|
|
return fmt.Errorf("create temp dir: %w", err)
|
|
}
|
|
|
|
if err := p.writeServerConfig(); err != nil {
|
|
return fmt.Errorf("write server config: %w", err)
|
|
}
|
|
if err := p.writeDatasourceProvision(); err != nil {
|
|
return fmt.Errorf("write datasource provision: %w", err)
|
|
}
|
|
if err := p.writeDashboardProvision(); err != nil {
|
|
return fmt.Errorf("write dashboard provision: %w", err)
|
|
}
|
|
|
|
p.cmd = exec.Command(bin,
|
|
"--config", filepath.Join(p.tmpDir, "config.yaml"),
|
|
"-web.listen-address", fmt.Sprintf(":%d", p.cfg.Port),
|
|
)
|
|
p.cmd.Dir = filepath.Dir(bin)
|
|
p.cmd.Stdout = &logWriter{level: "info", component: "perses"}
|
|
p.cmd.Stderr = &logWriter{level: "error", component: "perses"}
|
|
|
|
if err := p.cmd.Start(); err != nil {
|
|
return fmt.Errorf("start perses: %w", err)
|
|
}
|
|
|
|
log.Info().
|
|
Str("binary", bin).
|
|
Int("port", p.cfg.Port).
|
|
Str("config", p.tmpDir).
|
|
Msg("perses started")
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Perses) Stop() {
|
|
if p.cmd != nil && p.cmd.Process != nil {
|
|
_ = p.cmd.Process.Kill()
|
|
_ = p.cmd.Wait()
|
|
}
|
|
if p.tmpDir != "" {
|
|
_ = os.RemoveAll(p.tmpDir)
|
|
}
|
|
}
|
|
|
|
func (p *Perses) Running() bool {
|
|
return p.cmd != nil && p.cmd.Process != nil && p.cmd.ProcessState == nil
|
|
}
|
|
|
|
func (p *Perses) writeServerConfig() error {
|
|
provisionDir := filepath.Join(p.tmpDir, "provisions")
|
|
if err := os.MkdirAll(filepath.Join(provisionDir, "datasources"), 0o755); err != nil {
|
|
return err
|
|
}
|
|
if err := os.MkdirAll(filepath.Join(provisionDir, "dashboards"), 0o755); err != nil {
|
|
return err
|
|
}
|
|
|
|
cfg := fmt.Sprintf(`provisioning:
|
|
interval: 1m
|
|
folders:
|
|
- %s
|
|
database:
|
|
file:
|
|
folder: %s/data
|
|
extension: json
|
|
security:
|
|
readonly: false
|
|
enable_auth: false
|
|
`, provisionDir, p.tmpDir)
|
|
|
|
return os.WriteFile(filepath.Join(p.tmpDir, "config.yaml"), []byte(cfg), 0o644)
|
|
}
|
|
|
|
func (p *Perses) writeDatasourceProvision() error {
|
|
ds := fmt.Sprintf(`kind: Datasource
|
|
metadata:
|
|
name: victoria-metrics
|
|
project: anthropic-proxy
|
|
spec:
|
|
default: true
|
|
plugin:
|
|
kind: PrometheusDatasource
|
|
spec:
|
|
directUrl: http://localhost:%d
|
|
`, p.cfg.VMPort)
|
|
|
|
return os.WriteFile(
|
|
filepath.Join(p.tmpDir, "provisions", "datasources", "vm.yaml"),
|
|
[]byte(ds), 0o644,
|
|
)
|
|
}
|
|
|
|
func (p *Perses) writeDashboardProvision() error {
|
|
dashData, err := DashboardJSON()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(
|
|
filepath.Join(p.tmpDir, "provisions", "dashboards", "proxy.json"),
|
|
dashData, 0o644,
|
|
)
|
|
}
|
|
|
|
type logWriter struct {
|
|
level string
|
|
component string
|
|
}
|
|
|
|
func (w *logWriter) Write(p []byte) (n int, err error) {
|
|
msg := string(p)
|
|
switch w.level {
|
|
case "error":
|
|
log.Error().Str("component", w.component).Msg(msg)
|
|
default:
|
|
log.Debug().Str("component", w.component).Msg(msg)
|
|
}
|
|
return len(p), nil
|
|
}
|