signoz/pkg/version/deployment.go

171 lines
4.0 KiB
Go
Raw Normal View History

package version
import (
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
gotime "time"
)
var (
current = Deployment{mode: "unknown", platform: "unknown", os: "unknown", arch: "unknown"}
once = sync.Once{}
)
type Deployment struct {
// mode of deployment, e.g. kubernetes, binary, etc.
mode string
// platform of deployment, e.g. heroku, render, aws, gcp, azure, digitalocean, etc.
platform string
// os of deployment, e.g. linux, darwin, etc.
os string
// arch of deployment, e.g. amd64, arm64, etc.
arch string
}
func NewDeployment() Deployment {
once.Do(func() {
current.mode = detectMode()
current.platform = detectPlatform()
current.os = runtime.GOOS
current.arch = runtime.GOARCH
})
return current
}
func (d Deployment) Mode() string {
return d.mode
}
func (d Deployment) Platform() string {
return d.platform
}
func (d Deployment) OS() string {
return d.os
}
func (d Deployment) Arch() string {
return d.arch
}
func detectMode() string {
// Check if running in Kubernetes
if os.Getenv("KUBERNETES_SERVICE_HOST") != "" {
return "kubernetes"
}
// Check if running in a container and identify the runtime
if data, err := os.ReadFile("/proc/self/cgroup"); err == nil {
cgroupData := string(data)
switch {
case strings.Contains(cgroupData, "docker"):
return "docker"
case strings.Contains(cgroupData, "containerd"):
return "containerd"
case strings.Contains(cgroupData, "libpod") || strings.Contains(cgroupData, "podman"):
return "podman"
case strings.Contains(cgroupData, "crio"):
return "cri-o"
}
}
// Check if running as a binary
if exe, err := os.Executable(); err == nil {
// Check if the executable is in a standard binary location
exePath := filepath.Clean(exe)
if strings.HasPrefix(exePath, "/usr/local/bin/") ||
strings.HasPrefix(exePath, "/usr/bin/") ||
strings.HasPrefix(exePath, "/bin/") ||
strings.HasPrefix(exePath, "/opt/") {
return "binary"
}
// Check if the executable is in the current directory
if filepath.Dir(exePath) == "." || filepath.Dir(exePath) == filepath.Clean(os.Getenv("PWD")) {
return "binary"
}
}
return "unknown"
}
func detectPlatform() string {
// Check for PaaS platforms first as they use environment variables
switch {
case os.Getenv("DYNO") != "" || os.Getenv("HEROKU_APP_ID") != "":
return "heroku"
case os.Getenv("RENDER") != "" || os.Getenv("RENDER_SERVICE_ID") != "":
return "render"
case os.Getenv("COOLIFY_RESOURCE_UUID") != "":
return "coolify"
case os.Getenv("RAILWAY_SERVICE_ID") != "":
return "railway"
}
// Try to detect cloud provider through metadata endpoints
client := &http.Client{Timeout: 1 * gotime.Second}
// AWS metadata
if req, err := http.NewRequest(http.MethodGet, "http://169.254.169.254/latest/meta-data/", nil); err == nil {
if resp, err := client.Do(req); err == nil {
resp.Body.Close()
if resp.StatusCode == 200 {
return "aws"
}
}
}
// GCP metadata
if req, err := http.NewRequest(http.MethodGet, "http://169.254.169.254/computeMetadata/v1/", nil); err == nil {
req.Header.Add("Metadata-Flavor", "Google")
if resp, err := client.Do(req); err == nil {
resp.Body.Close()
if resp.StatusCode == 200 {
return "gcp"
}
}
}
// Azure metadata
if req, err := http.NewRequest(http.MethodGet, "http://169.254.169.254/metadata/instance", nil); err == nil {
req.Header.Add("Metadata", "true")
if resp, err := client.Do(req); err == nil {
resp.Body.Close()
if resp.StatusCode == 200 {
return "azure"
}
}
}
// Digitalocean metadata
if req, err := http.NewRequest(http.MethodGet, "http://169.254.169.254/metadata/v1/", nil); err == nil {
if resp, err := client.Do(req); err == nil {
resp.Body.Close()
if resp.StatusCode == 200 {
return "digitalocean"
}
}
}
// Hetzner metadata
if req, err := http.NewRequest(http.MethodGet, "http://169.254.169.254/hetzner/v1/metadata", nil); err == nil {
if resp, err := client.Do(req); err == nil {
resp.Body.Close()
if resp.StatusCode == 200 {
return "hetzner"
}
}
}
return "unknown"
}