Files
anthropic-proxy/internal/embedded/download.go
T

156 lines
3.3 KiB
Go

package embedded
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"github.com/rs/zerolog/log"
)
const cacheDir = ".cache/anthropic-proxy/bin"
var downloads = map[string]struct {
urlTemplate string
version string
extractName string
}{
"victoria-metrics": {
urlTemplate: "https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v%s/victoria-metrics-%s-v%s.tar.gz",
version: "1.118.0",
extractName: "victoria-metrics-prod",
},
"perses": {
urlTemplate: "https://github.com/perses/perses/releases/download/v%s/perses_%s_%s_%s.tar.gz",
version: "0.53.1",
},
}
func ensureBinary(name, configPath, configBinDir string) (string, error) {
if configPath != "" {
if p, err := exec.LookPath(configPath); err == nil {
return p, nil
}
}
if p, err := exec.LookPath(name); err == nil {
return p, nil
}
binDir := configBinDir
if binDir == "" {
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("get home dir: %w", err)
}
binDir = filepath.Join(home, cacheDir)
}
cachedPath := filepath.Join(binDir, name)
if _, err := os.Stat(cachedPath); err == nil {
return cachedPath, nil
}
log.Info().Str("binary", name).Msg("downloading binary (first run)")
if err := os.MkdirAll(binDir, 0o755); err != nil {
return "", fmt.Errorf("create cache dir: %w", err)
}
url, err := downloadURL(name)
if err != nil {
return "", err
}
if err := extractAll(url, binDir); err != nil {
return "", fmt.Errorf("download %s: %w", name, err)
}
d := downloads[name]
if d.extractName != "" {
oldPath := filepath.Join(binDir, d.extractName)
if _, err := os.Stat(oldPath); err == nil {
os.Rename(oldPath, cachedPath)
}
}
if _, err := os.Stat(cachedPath); err != nil {
return "", fmt.Errorf("binary %s not found after extraction", name)
}
log.Info().Str("binary", name).Str("path", cachedPath).Msg("binary downloaded")
return cachedPath, nil
}
func downloadURL(name string) (string, error) {
goarch := runtime.GOARCH
goos := runtime.GOOS
d, ok := downloads[name]
if !ok {
return "", fmt.Errorf("unknown binary: %s", name)
}
switch name {
case "victoria-metrics":
vmOS := fmt.Sprintf("%s-%s", goos, goarch)
return fmt.Sprintf(d.urlTemplate, d.version, vmOS, d.version), nil
case "perses":
return fmt.Sprintf(d.urlTemplate, d.version, d.version, goos, goarch), nil
}
return "", fmt.Errorf("unknown binary: %s", name)
}
func extractAll(url, destDir string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("download failed: HTTP %d from %s", resp.StatusCode, url)
}
gz, err := gzip.NewReader(resp.Body)
if err != nil {
return fmt.Errorf("gzip reader: %w", err)
}
defer gz.Close()
tr := tar.NewReader(gz)
for {
hdr, err := tr.Next()
if err == io.EOF {
return nil
}
if err != nil {
return fmt.Errorf("read tar: %w", err)
}
target := filepath.Join(destDir, hdr.Name)
switch hdr.Typeflag {
case tar.TypeDir:
os.MkdirAll(target, 0o755)
case tar.TypeReg:
os.MkdirAll(filepath.Dir(target), 0o755)
mode := os.FileMode(hdr.Mode)
if mode == 0 {
mode = 0o644
}
out, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
if err != nil {
return err
}
io.Copy(out, tr)
out.Close()
}
}
}