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:
@ -7,6 +7,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -74,23 +75,147 @@ func (c *OCIClient) getRegistry(reg *entity.Registry) (*remote.Registry, error)
|
||||
}
|
||||
|
||||
// ListRepositories 列出 Registry 中的所有 repositories
|
||||
// 优先使用 OCI _catalog API,失败时回退到 Harbor REST API v2
|
||||
func (c *OCIClient) ListRepositories(ctx context.Context, registry *entity.Registry) ([]string, error) {
|
||||
reg, err := c.getRegistry(registry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repositories := make([]string, 0)
|
||||
|
||||
err = reg.Repositories(ctx, "", func(repos []string) error {
|
||||
repositories = append(repositories, repos...)
|
||||
return nil
|
||||
})
|
||||
// 尝试 OCI _catalog API
|
||||
reg, err := c.getRegistry(registry)
|
||||
log.Printf("[DEBUG ListRepositories] registry=%s, getRegistry err=%v", registry.URL, err)
|
||||
if err == nil {
|
||||
err = reg.Repositories(ctx, "", func(repos []string) error {
|
||||
log.Printf("[DEBUG ListRepositories] OCI got repos batch: %d", len(repos))
|
||||
repositories = append(repositories, repos...)
|
||||
return nil
|
||||
})
|
||||
log.Printf("[DEBUG ListRepositories] OCI reg.Repositories returned: err=%v, total_repos=%d", err, len(repositories))
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG ListRepositories] post-OCI check: err=%v, repos_count=%d", err, len(repositories))
|
||||
|
||||
if err == nil && len(repositories) > 0 {
|
||||
log.Printf("[DEBUG ListRepositories] OCI success, returning %d repos", len(repositories))
|
||||
return repositories, nil
|
||||
}
|
||||
|
||||
// 回退: 使用 Harbor REST API v2
|
||||
log.Printf("[Harbor Fallback] OCI failed (err=%v, repos=%d), checking if Harbor...", err, len(repositories))
|
||||
log.Printf("[Harbor Fallback] registry.URL=%s, contains 'harbor'=%v", registry.URL, strings.Contains(registry.URL, "harbor"))
|
||||
|
||||
if strings.Contains(registry.URL, "harbor") {
|
||||
log.Printf("[Harbor Fallback] Yes, this is Harbor! Calling Harbor REST API...")
|
||||
repos, fallbackErr := c.listHarborRepositories(registry)
|
||||
log.Printf("[Harbor Fallback] Got %d repos, err=%v", len(repos), fallbackErr)
|
||||
if fallbackErr == nil && len(repos) > 0 {
|
||||
log.Printf("[Harbor Fallback] Returning %d repos from Harbor API", len(repos))
|
||||
return repos, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list repositories: %w", err)
|
||||
}
|
||||
return nil, fallbackErr
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list repositories: %w", err)
|
||||
}
|
||||
return repositories, nil
|
||||
}
|
||||
|
||||
// listHarborRepositories 使用 Harbor REST API v2 获取仓库列表
|
||||
func (c *OCIClient) listHarborRepositories(registry *entity.Registry) ([]string, error) {
|
||||
// 解析 Harbor URL 基础地址
|
||||
baseURL := registry.URL
|
||||
baseURL = strings.TrimSuffix(baseURL, "/")
|
||||
baseURL = strings.TrimPrefix(baseURL, "https://")
|
||||
baseURL = strings.TrimPrefix(baseURL, "http://")
|
||||
harborHost := "https://" + baseURL
|
||||
|
||||
// 获取认证信息
|
||||
username := registry.Username
|
||||
password := registry.Password
|
||||
if username == "" || password == "" {
|
||||
username = os.Getenv("HARBOR_USERNAME")
|
||||
password = os.Getenv("HARBOR_PASSWORD")
|
||||
}
|
||||
|
||||
// 获取项目列表
|
||||
projectsURL := harborHost + "/api/v2.0/projects"
|
||||
req, err := http.NewRequest("GET", projectsURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.SetBasicAuth(username, password)
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to list projects: status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var projects []struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&projects); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repositories := make([]string, 0)
|
||||
pageSize := 100
|
||||
|
||||
for _, project := range projects {
|
||||
page := 1
|
||||
log.Printf("[listHarborRepositories] Processing project: %s", project.Name)
|
||||
for {
|
||||
reposURL := fmt.Sprintf("%s/api/v2.0/projects/%s/repositories?page=%d&page_size=%d",
|
||||
harborHost, project.Name, page, pageSize)
|
||||
req, err := http.NewRequest("GET", reposURL, nil)
|
||||
if err != nil {
|
||||
log.Printf("[listHarborRepositories] page %d: NewRequest error: %v", page, err)
|
||||
break
|
||||
}
|
||||
req.SetBasicAuth(username, password)
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
log.Printf("[listHarborRepositories] page %d: Do error: %v", page, err)
|
||||
break
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
bodyBytes, _ := io.ReadAll(io.LimitReader(resp.Body, 200))
|
||||
resp.Body.Close()
|
||||
log.Printf("[listHarborRepositories] page %d: HTTP %d, body: %s", page, resp.StatusCode, string(bodyBytes))
|
||||
break
|
||||
}
|
||||
|
||||
var repos []struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&repos); err != nil {
|
||||
resp.Body.Close()
|
||||
log.Printf("[listHarborRepositories] page %d: Decode error: %v", page, err)
|
||||
break
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
log.Printf("[listHarborRepositories] page %d: got %d repos", page, len(repos))
|
||||
if len(repos) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
for _, repo := range repos {
|
||||
repositories = append(repositories, repo.Name)
|
||||
}
|
||||
page++
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[listHarborRepositories] Total repos collected: %d", len(repositories))
|
||||
return repositories, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user