Files
ocdp-go/backend/internal/domain/entity/instance.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

265 lines
7.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package entity
import (
"log"
"strings"
"time"
"unicode"
"gopkg.in/yaml.v3"
)
// InstanceStatus 实例状态
type InstanceStatus string
const (
StatusDeployed InstanceStatus = "deployed"
StatusUninstalled InstanceStatus = "uninstalled"
StatusSuperseded InstanceStatus = "superseded"
StatusFailed InstanceStatus = "failed"
StatusPending InstanceStatus = "pending-install"
StatusUpgrading InstanceStatus = "pending-upgrade"
StatusRollingBack InstanceStatus = "pending-rollback"
StatusTerminating InstanceStatus = "pending-delete"
StatusUnknown InstanceStatus = "unknown"
)
// InstanceOperation 实例操作类型
type InstanceOperation string
const (
OperationNone InstanceOperation = ""
OperationInstall InstanceOperation = "install"
OperationUpgrade InstanceOperation = "upgrade"
OperationRollback InstanceOperation = "rollback"
OperationDelete InstanceOperation = "delete"
OperationSync InstanceOperation = "sync"
)
// Instance Helm 应用实例领域实体
type Instance struct {
ID string
WorkspaceID string // 所属 workspace
OwnerID string // 创建者用户 ID
ClusterID string
RegistryID string
ChartReferenceID string // 引用的 Chart 引用
ValuesTemplateID string // 使用的 Values 模板
Name string // Helm Release Name
Namespace string
Repository string // OCI Repository (e.g., charts/app)
Chart string // Chart Name
Version string // Chart Version
Description string
Values map[string]interface{} // Helm Values (JSON)
ValuesYAML string // Helm Values (YAML format)
UserOverrideYAML string // 用户额外覆盖的配置
Status InstanceStatus
StatusReason string
LastOperation InstanceOperation
LastError string
Revision int // Helm Release Revision
// 资源使用统计Helm 安装时从集群获取并更新)
CPURequested float64 // CPU 请求量 (cores)
MemoryRequested string // 内存请求量 (e.g., "2Gi")
GPURequested float64 // GPU 请求量 (cards)
GPUMemoryRequested string // GPU 内存请求量 (e.g., "16Gi")
CreatedAt time.Time
UpdatedAt time.Time
}
// NewInstance 创建新实例
func NewInstance(workspaceID, ownerID, clusterID, registryID, chartReferenceID, valuesTemplateID, name, namespace, repository, chart, version string) *Instance {
now := time.Now()
return &Instance{
WorkspaceID: workspaceID,
OwnerID: ownerID,
ClusterID: clusterID,
RegistryID: registryID,
ChartReferenceID: chartReferenceID,
ValuesTemplateID: valuesTemplateID,
Name: name,
Namespace: namespace,
Repository: repository,
Chart: chart,
Version: version,
Status: StatusPending,
StatusReason: "Pending install",
LastOperation: OperationInstall,
Revision: 1,
CPURequested: 0,
MemoryRequested: "0Mi",
GPURequested: 0,
GPUMemoryRequested: "0Mi",
CreatedAt: now,
UpdatedAt: now,
}
}
// SetValues 设置 Helm Values
func (i *Instance) SetValues(values map[string]interface{}) {
i.Values = values
i.UpdatedAt = time.Now()
}
// SetValuesYAML 设置 YAML 格式的 Values 并解析到 Values map
func (i *Instance) SetValuesYAML(yamlStr string) {
i.ValuesYAML = yamlStr
if yamlStr == "" {
return
}
// 解析 YAML 到 map确保 Helm 客户端能正确使用
var parsed map[string]interface{}
if err := yaml.Unmarshal([]byte(yamlStr), &parsed); err != nil {
log.Printf("[SetValuesYAML] WARNING: failed to parse YAML for instance %s: %s, yaml=%q", i.Name, err, yamlStr)
return
}
if parsed == nil {
return
}
// Merge into existing Values (user-provided takes precedence)
if i.Values == nil {
i.Values = make(map[string]interface{})
}
for k, v := range parsed {
// Only set if not already present (Values map takes precedence over YAML fallback)
if _, exists := i.Values[k]; !exists {
i.Values[k] = v
}
}
i.UpdatedAt = time.Now()
}
// UpdateStatus 更新实例状态
func (i *Instance) UpdateStatus(status InstanceStatus, revision int) {
i.Status = status
i.Revision = revision
i.UpdatedAt = time.Now()
}
// BeginOperation 标记开始执行某个操作
func (i *Instance) BeginOperation(op InstanceOperation, reason string) {
i.LastOperation = op
if pendingStatus := pendingStatusForOperation(op); pendingStatus != "" {
i.Status = pendingStatus
}
if reason != "" {
i.StatusReason = reason
}
i.LastError = ""
i.UpdatedAt = time.Now()
}
// MarkSuccess 标记操作成功
func (i *Instance) MarkSuccess(status InstanceStatus, revision int, reason string) {
if status != "" {
i.Status = status
}
if revision > 0 {
i.Revision = revision
}
i.StatusReason = reason
i.LastError = ""
i.UpdatedAt = time.Now()
}
// MarkFailure 标记操作失败
func (i *Instance) MarkFailure(reason string, err error) {
i.Status = StatusFailed
if reason != "" {
i.StatusReason = reason
}
if err != nil {
i.LastError = err.Error()
}
i.UpdatedAt = time.Now()
}
func pendingStatusForOperation(op InstanceOperation) InstanceStatus {
switch op {
case OperationInstall:
return StatusPending
case OperationUpgrade:
return StatusUpgrading
case OperationRollback:
return StatusRollingBack
case OperationDelete:
return StatusTerminating
default:
return ""
}
}
// Upgrade 升级实例
func (i *Instance) Upgrade(version string, values map[string]interface{}) {
i.Version = version
if values != nil {
i.Values = values
}
i.BeginOperation(OperationUpgrade, "Pending upgrade")
}
// ValidateReleaseName 验证 Helm Release 名称是否符合 RFC 1123 DNS 子域名规范
// Helm release 名称必须:
// - 只能包含小写字母a-z、数字0-9和连字符-
// - 不能以连字符开头或结尾
// - 长度不超过 53 个字符
func ValidateReleaseName(name string) error {
if name == "" {
return ErrInvalidInstanceName
}
// 检查长度RFC 1123 DNS 子域名最大长度为 63但 Helm 限制为 53
if len(name) > 53 {
return ErrInvalidInstanceName
}
// 不能以连字符开头或结尾
if strings.HasPrefix(name, "-") || strings.HasSuffix(name, "-") {
return ErrInvalidInstanceName
}
// 只能包含小写字母、数字和连字符
for _, r := range name {
if !(unicode.IsLower(r) || unicode.IsDigit(r) || r == '-') {
return ErrInvalidInstanceName
}
}
return nil
}
// Validate 验证实例配置
func (i *Instance) Validate() error {
if i.ClusterID == "" {
return ErrInvalidClusterID
}
if err := ValidateReleaseName(i.Name); err != nil {
return err
}
if i.Namespace == "" {
return ErrInvalidNamespace
}
if i.Chart == "" {
return ErrInvalidChart
}
if i.Version == "" {
return ErrInvalidVersion
}
return nil
}
// ReleaseHistory Helm Release 历史记录
type ReleaseHistory struct {
Revision int
Updated time.Time
Status InstanceStatus
Chart string
AppVersion string
Description string
}