package originclient import ( "net" "os" "strings" ) // Hostname returns the OS hostname, or "" if it cannot be determined. func Hostname() string { h, err := os.Hostname() if err != nil { return "" } return h } // OutboundIP returns the first non-loopback IPv4 address bound to this // host. It is a LAN-facing address, not the host's public internet // address, and is not part of Origin's wire contract — it exists so // callers can populate BaseURL/InternalURL/Description themselves. func OutboundIP() string { addrs, err := net.InterfaceAddrs() if err != nil { return "" } for _, addr := range addrs { ipNet, ok := addr.(*net.IPNet) if !ok || ipNet.IP.IsLoopback() { continue } if ip4 := ipNet.IP.To4(); ip4 != nil { return ip4.String() } } return "" } // ContainerID best-effort detects the Docker/Kubernetes container ID this // process runs in. It returns "" outside a container or when detection // fails; an empty container ID is treated as "not containerized" rather // than an error. func ContainerID() string { if data, err := os.ReadFile("/proc/self/cgroup"); err == nil { if id := parseCgroupContainerID(data); id != "" { return id } } if _, err := os.Stat("/.dockerenv"); err == nil { if h := Hostname(); isContainerIDLike(h) { return h } } return "" } // Orchestrator best-effort detects the orchestration environment: // "kubernetes" under Kubernetes, "docker" in a plain Docker container, "" // otherwise. func Orchestrator() string { if os.Getenv("KUBERNETES_SERVICE_HOST") != "" { return "kubernetes" } if _, err := os.Stat("/.dockerenv"); err == nil { return "docker" } return "" } // parseCgroupContainerID extracts a container ID from /proc/self/cgroup // content, handling both cgroup v1 (".../docker/") and cgroup v2 // systemd-scope (".../docker-.scope") layouts. func parseCgroupContainerID(data []byte) string { for _, line := range strings.Split(string(data), "\n") { line = strings.TrimSpace(line) idx := strings.LastIndex(line, "/") if idx == -1 { continue } segment := line[idx+1:] segment = strings.TrimSuffix(segment, ".scope") segment = strings.TrimPrefix(segment, "docker-") if isContainerIDLike(segment) { return segment } } return "" } func isContainerIDLike(s string) bool { if len(s) < 12 || len(s) > 64 { return false } for _, r := range s { if (r < '0' || r > '9') && (r < 'a' || r > 'f') { return false } } return true }