Files
ocdp-go/backend/internal/bootstrap/config.go
Ivan087 47849042a7 feat: complete E2E deployment flow with storage layered config and values template versioning
- Instance deployment: charts browser, deploy modal, instances list
- Values Template version management (create/history/rollback)
- Storage layered config (cluster > workspace > shared priority)
- Cluster credential decryptIfNeeded for mixed encrypted/plaintext kubeconfig
- YAML syntax validation (client-side + server-side warning)
- Frontend: charts, instances, storage, templates, admin pages
- Backend: storage service, instance service, cluster service, helm client
- Multi-Tenant Kubeconfig.md: added by user
2026-04-30 16:31:00 +08:00

176 lines
5.3 KiB
Go

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_<NAME>_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)
}