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
This commit is contained in:
Ivan087
2026-04-30 16:31:00 +08:00
parent 985369d40f
commit 47849042a7
42 changed files with 2029 additions and 255 deletions

View File

@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"fmt"
"log"
"time"
"github.com/google/uuid"
@ -124,11 +125,11 @@ func (r *ClusterRepository) GetByID(ctx context.Context, id string) (*entity.Clu
return nil, fmt.Errorf("failed to get cluster: %w", err)
}
// 解密敏感数据
cluster.CAData, _ = r.encryptor.Decrypt(encryptedCAData)
cluster.CertData, _ = r.encryptor.Decrypt(encryptedCertData)
cluster.KeyData, _ = r.encryptor.Decrypt(encryptedKeyData)
cluster.Token, _ = r.encryptor.Decrypt(encryptedToken)
// 解密敏感数据(检测 kubeconfig 格式则跳过解密)
cluster.CAData = r.decryptIfNeeded(encryptedCAData, "ca_data")
cluster.CertData = r.decryptIfNeeded(encryptedCertData, "cert_data")
cluster.KeyData = r.decryptIfNeeded(encryptedKeyData, "key_data")
cluster.Token = r.decryptIfNeeded(encryptedToken, "token")
return cluster, nil
}
@ -169,15 +170,35 @@ func (r *ClusterRepository) GetByName(ctx context.Context, name string) (*entity
return nil, fmt.Errorf("failed to get cluster: %w", err)
}
// 解密敏感数据
cluster.CAData, _ = r.encryptor.Decrypt(encryptedCAData)
cluster.CertData, _ = r.encryptor.Decrypt(encryptedCertData)
cluster.KeyData, _ = r.encryptor.Decrypt(encryptedKeyData)
cluster.Token, _ = r.encryptor.Decrypt(encryptedToken)
// 解密敏感数据(检测 kubeconfig 格式则跳过解密)
cluster.CAData = r.decryptIfNeeded(encryptedCAData, "ca_data")
cluster.CertData = r.decryptIfNeeded(encryptedCertData, "cert_data")
cluster.KeyData = r.decryptIfNeeded(encryptedKeyData, "key_data")
cluster.Token = r.decryptIfNeeded(encryptedToken, "token")
return cluster, nil
}
// decryptIfNeeded 解密数据。如果数据以 "apiVersion:" 或 "kind:" 开头kubeconfig 格式),
// 则跳过解密直接返回原值。
func (r *ClusterRepository) decryptIfNeeded(data string, fieldName string) string {
if data == "" {
return ""
}
// 检测 kubeconfig 格式(明文 YAML
if (len(data) > 10 && data[:11] == "apiVersion:") ||
(len(data) > 5 && data[:5] == "kind:") {
return data
}
// 否则尝试解密
decrypted, err := r.encryptor.Decrypt(data)
if err != nil {
log.Printf("[ClusterRepository] WARNING: failed to decrypt %s for field %s: %v (field will be empty)", data[:min(50, len(data))], fieldName, err)
return ""
}
return decrypted
}
// Update 更新集群
func (r *ClusterRepository) Update(ctx context.Context, cluster *entity.Cluster) error {
cluster.UpdatedAt = time.Now()
@ -352,18 +373,18 @@ func (r *ClusterRepository) scanClusters(rows *sql.Rows) ([]*entity.Cluster, err
cluster.OwnerID = ownerID.String
cluster.DefaultNamespace = defaultNamespace.String
// 解密敏感数据
// 解密敏感数据(检测 kubeconfig 格式则跳过解密)
if encryptedCAData.Valid {
cluster.CAData, _ = r.encryptor.Decrypt(encryptedCAData.String)
cluster.CAData = r.decryptIfNeeded(encryptedCAData.String, "ca_data")
}
if encryptedCertData.Valid {
cluster.CertData, _ = r.encryptor.Decrypt(encryptedCertData.String)
cluster.CertData = r.decryptIfNeeded(encryptedCertData.String, "cert_data")
}
if encryptedKeyData.Valid {
cluster.KeyData, _ = r.encryptor.Decrypt(encryptedKeyData.String)
cluster.KeyData = r.decryptIfNeeded(encryptedKeyData.String, "key_data")
}
if encryptedToken.Valid {
cluster.Token, _ = r.encryptor.Decrypt(encryptedToken.String)
cluster.Token = r.decryptIfNeeded(encryptedToken.String, "token")
}
clusters = append(clusters, cluster)