package bootstrap import ( "encoding/json" "fmt" "os" "path/filepath" "strings" ) // BootstrapConfig 预注入配置 type BootstrapConfig struct { Enabled bool `json:"enabled"` Users []UserSeed `json:"users"` Registries []RegistrySeed `json:"registries"` Clusters []ClusterSeed `json:"clusters"` } // UserSeed 用户预注入数据 type UserSeed struct { Username string `json:"username"` Password string `json:"password"` Email string `json:"email"` } // RegistrySeed Registry 预注入数据 type RegistrySeed struct { Name string `json:"name"` URL string `json:"url"` Description string `json:"description"` Username string `json:"username"` Password string `json:"password"` Insecure bool `json:"insecure"` } // ClusterSeed Cluster 预注入数据 type ClusterSeed struct { Name string `json:"name"` Host string `json:"host"` Description string `json:"description"` CAData string `json:"ca_data"` CertData string `json:"cert_data"` KeyData string `json:"key_data"` Token string `json:"token,omitempty"` } // LoadBootstrapConfig 加载预注入配置 // 支持从文件或环境变量加载 // // 加载优先级: // 1. 环境变量 BOOTSTRAP_CONFIG_JSON (最高优先级) // 2. Mock 模式: 配置文件 config/bootstrap.json // 3. 真实模式: GetDefaultBootstrapConfig() 从 .env 读取 func LoadBootstrapConfig() (*BootstrapConfig, error) { // 1. 优先从环境变量加载 if configJSON := os.Getenv("BOOTSTRAP_CONFIG_JSON"); configJSON != "" { var config BootstrapConfig if err := json.Unmarshal([]byte(configJSON), &config); err != nil { return nil, fmt.Errorf("failed to parse BOOTSTRAP_CONFIG_JSON: %w", err) } return &config, nil } // 2. 检查适配器模式 adapterMode := os.Getenv("ADAPTER_MODE") // Mock 模式: 使用配置文件(假数据) if adapterMode == "mock" { configPath := os.Getenv("BOOTSTRAP_CONFIG_FILE") if configPath == "" { configPath = filepath.Join("config", "bootstrap.json") } // 检查文件是否存在 if _, err := os.Stat(configPath); os.IsNotExist(err) { // 配置文件不存在,使用默认配置 return GetDefaultBootstrapConfig(), nil } data, err := os.ReadFile(configPath) if err != nil { return nil, fmt.Errorf("failed to read bootstrap config file %s: %w", configPath, err) } var config BootstrapConfig if err := json.Unmarshal(data, &config); err != nil { return nil, fmt.Errorf("failed to parse bootstrap config: %w", err) } return &config, nil } // 3. 真实模式 (mode 1, mode 2): 从 .env 读取 return GetDefaultBootstrapConfig(), nil } // GetDefaultBootstrapConfig 从 .env 加载 bootstrap 数据。 // 支持 BOOTSTRAP_CLUSTERS (逗号分隔的集群名) 以及每个集群的 // BOOTSTRAP_CLUSTER__HOST, _CA, _CERT, _KEY, _DESC。 // 支持 BOOTSTRAP_REGISTRY_* 环境变量。 // 支持 BOOTSTRAP_ADMIN_USER/PASS/EMAIL。 func GetDefaultBootstrapConfig() *BootstrapConfig { // Load clusters from .env (comma-separated list of cluster names) clusterStr := os.Getenv("BOOTSTRAP_CLUSTERS") var clusterSeeds []ClusterSeed if clusterStr != "" { clusterNames := strings.Split(clusterStr, ",") for _, name := range clusterNames { name = strings.TrimSpace(name) if name == "" { continue } key := sanitizeEnvKey(name) host := os.Getenv("BOOTSTRAP_CLUSTER_" + key + "_HOST") ca := os.Getenv("BOOTSTRAP_CLUSTER_" + key + "_CA") cert := os.Getenv("BOOTSTRAP_CLUSTER_" + key + "_CERT") keyData := os.Getenv("BOOTSTRAP_CLUSTER_" + key + "_KEY") desc := os.Getenv("BOOTSTRAP_CLUSTER_" + key + "_DESC") if host != "" { clusterSeeds = append(clusterSeeds, ClusterSeed{ Name: name, Host: host, Description: desc, CAData: ca, CertData: cert, KeyData: keyData, }) } } } // Load registry from .env var registrySeeds []RegistrySeed regName := strings.TrimSpace(os.Getenv("BOOTSTRAP_REGISTRY_NAME")) regURL := strings.TrimSpace(os.Getenv("BOOTSTRAP_REGISTRY_URL")) if regName != "" && regURL != "" { registrySeeds = append(registrySeeds, RegistrySeed{ Name: regName, URL: regURL, Description: strings.TrimSpace(os.Getenv("BOOTSTRAP_REGISTRY_DESC")), Username: strings.TrimSpace(os.Getenv("BOOTSTRAP_REGISTRY_USER")), Password: strings.TrimSpace(os.Getenv("BOOTSTRAP_REGISTRY_PASS")), Insecure: strings.ToLower(strings.TrimSpace(os.Getenv("BOOTSTRAP_REGISTRY_INSECURE"))) == "true", }) } // Load users from .env var userSeeds []UserSeed adminUser := strings.TrimSpace(os.Getenv("BOOTSTRAP_ADMIN_USER")) adminPass := strings.TrimSpace(os.Getenv("BOOTSTRAP_ADMIN_PASS")) if adminUser != "" { userSeeds = append(userSeeds, UserSeed{ Username: adminUser, Password: adminPass, Email: strings.TrimSpace(os.Getenv("BOOTSTRAP_ADMIN_EMAIL")), }) } return &BootstrapConfig{ Enabled: len(clusterSeeds) > 0 || len(registrySeeds) > 0 || len(userSeeds) > 0, Users: userSeeds, Registries: registrySeeds, Clusters: clusterSeeds, } } // sanitizeEnvKey converts "my-cluster" to "MY_CLUSTER" for env var names. func sanitizeEnvKey(name string) string { s := strings.Map(func(r rune) rune { if r == '-' || r == ' ' { return '_' } return r }, name) return strings.ToUpper(s) }