Private
Public Access
CI / rust (push) Successful in 3m6s
CI / go (push) Successful in 33s
CI / js (push) Successful in 34s
Standalone clients that resolve a machine unique ID (OS machine ID first, falling back to a generated UUID persisted to disk), detect hostname/IP/ container/orchestrator, and register+re-ping origin.warky.dev's public service-checkin endpoint on a daily interval. Ported from icy2's internal origincheckin package but extended to the full check-in contract and made dependency-light for external reuse. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
101 lines
2.4 KiB
Go
101 lines
2.4 KiB
Go
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/<id>") and cgroup v2
|
|
// systemd-scope (".../docker-<id>.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
|
|
}
|