package service import ( "os" "path/filepath" "strconv" "strings" ) // Settings configures the Metis service runtime. type Settings struct { BindAddr string InventoryPath string CacheDir string ArtifactDir string ArtifactStatePath string HistoryPath string SnapshotsPath string TargetsPath string DefaultFlashHost string FlashHosts []string LocalHost string AllowedGroups []string MaxDeviceBytes int64 Namespace string RunnerImageAMD64 string RunnerImageARM64 string HarborRegistry string HarborProject string HarborAPIBase string HarborUsername string HarborPassword string HostTmpDir string } // FromEnv builds service settings with sensible defaults for local dev and in-cluster use. func FromEnv() Settings { dataDir := getenvDefault("METIS_DATA_DIR", "/var/lib/metis") localHost := getenvDefault("METIS_LOCAL_HOST", hostnameOr("unknown")) defaultFlashHost := getenvDefault("METIS_DEFAULT_FLASH_HOST", localHost) flashHosts := splitList(getenvDefault("METIS_FLASH_HOSTS", defaultFlashHost)) return Settings{ BindAddr: getenvDefault("METIS_BIND_ADDR", ":8080"), InventoryPath: getenvDefault("METIS_INVENTORY_PATH", "inventory.titan-rpi4.yaml"), CacheDir: getenvDefault("METIS_CACHE_DIR", filepath.Join(dataDir, "cache")), ArtifactDir: getenvDefault("METIS_ARTIFACT_DIR", filepath.Join(dataDir, "artifacts")), ArtifactStatePath: getenvDefault("METIS_ARTIFACT_STATE_PATH", filepath.Join(dataDir, "artifacts.json")), HistoryPath: getenvDefault("METIS_HISTORY_PATH", filepath.Join(dataDir, "history.jsonl")), SnapshotsPath: getenvDefault("METIS_SNAPSHOTS_PATH", filepath.Join(dataDir, "snapshots.json")), TargetsPath: getenvDefault("METIS_TARGETS_PATH", filepath.Join(dataDir, "targets.json")), DefaultFlashHost: defaultFlashHost, FlashHosts: flashHosts, LocalHost: localHost, AllowedGroups: splitList(getenvDefault("METIS_ALLOWED_GROUPS", "admin,maintenance")), MaxDeviceBytes: getenvInt64("METIS_MAX_DEVICE_BYTES", 300000000000), Namespace: getenvDefault("METIS_NAMESPACE", "maintenance"), RunnerImageAMD64: getenvDefault("METIS_RUNNER_IMAGE_AMD64", ""), RunnerImageARM64: getenvDefault("METIS_RUNNER_IMAGE_ARM64", ""), HarborRegistry: getenvDefault("METIS_HARBOR_REGISTRY", "registry.bstein.dev"), HarborProject: getenvDefault("METIS_HARBOR_PROJECT", "metis"), HarborAPIBase: getenvDefault("METIS_HARBOR_API_BASE", "https://registry.bstein.dev/api/v2.0"), HarborUsername: getenvDefault("METIS_HARBOR_USERNAME", ""), HarborPassword: getenvDefault("METIS_HARBOR_PASSWORD", ""), HostTmpDir: getenvDefault("METIS_HOST_TMP_DIR", "/tmp/metis-flash-test"), } } func getenvDefault(key, fallback string) string { if value := strings.TrimSpace(os.Getenv(key)); value != "" { return value } return fallback } func getenvInt64(key string, fallback int64) int64 { raw := strings.TrimSpace(os.Getenv(key)) if raw == "" { return fallback } value, err := strconv.ParseInt(raw, 10, 64) if err != nil { return fallback } return value } func splitList(raw string) []string { if strings.TrimSpace(raw) == "" { return nil } parts := strings.Split(raw, ",") result := make([]string, 0, len(parts)) for _, part := range parts { part = strings.TrimSpace(part) if part == "" { continue } result = append(result, part) } return result } func hostnameOr(fallback string) string { name, err := os.Hostname() if err != nil || strings.TrimSpace(name) == "" { return fallback } return strings.TrimSpace(name) }