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:
@ -10,6 +10,14 @@ import (
|
||||
"github.com/ocdp/cluster-service/internal/domain/service"
|
||||
)
|
||||
|
||||
// StorageResolutionResponse 分层存储解析响应
|
||||
type StorageResolutionResponse struct {
|
||||
Storage *dto.StorageResponse `json:"storage,omitempty"`
|
||||
ValuesYAML string `json:"values_yaml,omitempty"`
|
||||
Source string `json:"source,omitempty"` // workspace, cluster, shared
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// StorageHandler Storage Backend Handler
|
||||
type StorageHandler struct {
|
||||
storageService *service.StorageService
|
||||
@ -77,6 +85,7 @@ func (h *StorageHandler) CreateStorage(w http.ResponseWriter, r *http.Request) {
|
||||
req.Description,
|
||||
req.IsDefault,
|
||||
req.IsShared,
|
||||
req.ClusterID,
|
||||
)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusBadRequest, "Failed to create storage backend", err.Error())
|
||||
@ -252,6 +261,45 @@ func (h *StorageHandler) DeleteStorage(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// ResolveStorage 预览分层存储解析结果
|
||||
// @Summary 预览分层存储解析结果
|
||||
// @Description 根据 cluster_id 和 workspace_id 解析出最终生效的存储配置
|
||||
// @Tags Storage
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param cluster_id query string false "Cluster ID"
|
||||
// @Param workspace_id query string false "Workspace ID"
|
||||
// @Success 200 {object} StorageResolutionResponse
|
||||
// @Router /storage-backends/resolve [get]
|
||||
func (h *StorageHandler) ResolveStorage(w http.ResponseWriter, r *http.Request) {
|
||||
clusterID := r.URL.Query().Get("cluster_id")
|
||||
workspaceID := r.URL.Query().Get("workspace_id")
|
||||
if workspaceID == "" {
|
||||
workspaceID = r.Header.Get("X-Workspace-ID")
|
||||
}
|
||||
|
||||
resolution, err := h.storageService.ResolveStorageConfig(r.Context(), clusterID, workspaceID)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, "Failed to resolve storage config", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if resolution == nil || resolution.Storage == nil {
|
||||
respondJSON(w, http.StatusOK, &StorageResolutionResponse{
|
||||
Message: "No default storage configured",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
response := &StorageResolutionResponse{
|
||||
Storage: toStorageResponse(resolution.Storage),
|
||||
ValuesYAML: resolution.ValuesYAML,
|
||||
Source: resolution.Source,
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusOK, response)
|
||||
}
|
||||
|
||||
// toStorageResponse 转换为响应 DTO
|
||||
func toStorageResponse(storage *entity.StorageBackend) *dto.StorageResponse {
|
||||
config := dto.StorageConfigDTO{}
|
||||
@ -278,6 +326,7 @@ func toStorageResponse(storage *entity.StorageBackend) *dto.StorageResponse {
|
||||
return &dto.StorageResponse{
|
||||
ID: storage.ID,
|
||||
WorkspaceID: storage.WorkspaceID,
|
||||
ClusterID: storage.ClusterID,
|
||||
OwnerID: storage.OwnerID,
|
||||
Name: storage.Name,
|
||||
Type: string(storage.Type),
|
||||
|
||||
Reference in New Issue
Block a user