mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-22 16:48:30 +01:00
Merge pull request #5524 from steven-zou/pro_deletable_enable
Enhance the project deletable checking logic to include helm charts checking
This commit is contained in:
commit
bc6d7b69e7
@ -36,6 +36,9 @@ type Controller struct {
|
||||
|
||||
//To cover all the manipulation requests
|
||||
manipulationHandler *ManipulationHandler
|
||||
|
||||
//To cover the other utility requests
|
||||
utilityHandler *UtilityHandler
|
||||
}
|
||||
|
||||
//NewController is constructor of the chartserver.Controller
|
||||
@ -86,6 +89,11 @@ func NewController(backendServer *url.URL) (*Controller, error) {
|
||||
backendServerAddress: backendServer,
|
||||
chartCache: cache,
|
||||
},
|
||||
utilityHandler: &UtilityHandler{
|
||||
apiClient: client,
|
||||
backendServerAddress: backendServer,
|
||||
chartOperator: operator,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -104,6 +112,11 @@ func (c *Controller) GetManipulationHandler() *ManipulationHandler {
|
||||
return c.manipulationHandler
|
||||
}
|
||||
|
||||
//GetUtilityHandler returns the reference of UtilityHandler
|
||||
func (c *Controller) GetUtilityHandler() *UtilityHandler {
|
||||
return c.utilityHandler
|
||||
}
|
||||
|
||||
//What's the cache driver if it is set
|
||||
func parseCacheDriver() (string, bool) {
|
||||
driver, ok := os.LookupEnv(cacheDriverENVKey)
|
||||
|
37
src/chartserver/utility_handler.go
Normal file
37
src/chartserver/utility_handler.go
Normal file
@ -0,0 +1,37 @@
|
||||
package chartserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//UtilityHandler provides utility methods
|
||||
type UtilityHandler struct {
|
||||
//Parse and process the chart version to provide required info data
|
||||
chartOperator *ChartOperator
|
||||
|
||||
//HTTP client used to call the realted APIs of the backend chart repositories
|
||||
apiClient *ChartClient
|
||||
|
||||
//Point to the url of the backend server
|
||||
backendServerAddress *url.URL
|
||||
}
|
||||
|
||||
//GetChartsByNs gets the chart list under the namespace
|
||||
func (uh *UtilityHandler) GetChartsByNs(namespace string) ([]*ChartInfo, error) {
|
||||
if len(strings.TrimSpace(namespace)) == 0 {
|
||||
return nil, errors.New("empty namespace when getting chart list")
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/api/%s/charts", namespace)
|
||||
url := fmt.Sprintf("%s%s", uh.backendServerAddress.String(), path)
|
||||
|
||||
content, err := uh.apiClient.GetContent(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return uh.chartOperator.GetChartList(content)
|
||||
}
|
44
src/chartserver/utility_handler_test.go
Normal file
44
src/chartserver/utility_handler_test.go
Normal file
@ -0,0 +1,44 @@
|
||||
package chartserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//TestGetChartsByNs tests GetChartsByNs method in UtilityHandler
|
||||
func TestGetChartsByNs(t *testing.T) {
|
||||
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.RequestURI {
|
||||
case "/api/repo1/charts":
|
||||
if r.Method == http.MethodGet {
|
||||
w.Write(chartListContent)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
w.Write([]byte("not supported"))
|
||||
}))
|
||||
defer mockServer.Close()
|
||||
|
||||
serverURL, err := url.Parse(mockServer.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
theController, err := NewController(serverURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
charts, err := theController.GetUtilityHandler().GetChartsByNs("repo1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(charts) != 2 {
|
||||
t.Fatalf("expect 2 items but got %d", len(charts))
|
||||
}
|
||||
}
|
@ -217,7 +217,7 @@ func (p *ProjectAPI) Delete() {
|
||||
return
|
||||
}
|
||||
|
||||
result, err := deletable(p.project.ProjectID)
|
||||
result, err := p.deletable(p.project.ProjectID)
|
||||
if err != nil {
|
||||
p.HandleInternalServerError(fmt.Sprintf(
|
||||
"failed to check the deletable of project %d: %v", p.project.ProjectID, err))
|
||||
@ -258,7 +258,7 @@ func (p *ProjectAPI) Deletable() {
|
||||
return
|
||||
}
|
||||
|
||||
result, err := deletable(p.project.ProjectID)
|
||||
result, err := p.deletable(p.project.ProjectID)
|
||||
if err != nil {
|
||||
p.HandleInternalServerError(fmt.Sprintf(
|
||||
"failed to check the deletable of project %d: %v", p.project.ProjectID, err))
|
||||
@ -269,7 +269,7 @@ func (p *ProjectAPI) Deletable() {
|
||||
p.ServeJSON()
|
||||
}
|
||||
|
||||
func deletable(projectID int64) (*deletableResp, error) {
|
||||
func (p *ProjectAPI) deletable(projectID int64) (*deletableResp, error) {
|
||||
count, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{
|
||||
ProjectIDs: []int64{projectID},
|
||||
})
|
||||
@ -280,7 +280,7 @@ func deletable(projectID int64) (*deletableResp, error) {
|
||||
if count > 0 {
|
||||
return &deletableResp{
|
||||
Deletable: false,
|
||||
Message: "the project contains repositories, can not be deleled",
|
||||
Message: "the project contains repositories, can not be deleted",
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -292,10 +292,25 @@ func deletable(projectID int64) (*deletableResp, error) {
|
||||
if len(policies) > 0 {
|
||||
return &deletableResp{
|
||||
Deletable: false,
|
||||
Message: "the project contains replication rules, can not be deleled",
|
||||
Message: "the project contains replication rules, can not be deleted",
|
||||
}, nil
|
||||
}
|
||||
|
||||
//Check helm charts number
|
||||
if config.WithChartMuseum() {
|
||||
charts, err := chartController.GetUtilityHandler().GetChartsByNs(p.project.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(charts) > 0 {
|
||||
return &deletableResp{
|
||||
Deletable: false,
|
||||
Message: "the project contains helm charts, can not be deleted",
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return &deletableResp{
|
||||
Deletable: true,
|
||||
}, nil
|
||||
|
@ -16,10 +16,14 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/harbor/src/chartserver"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
@ -362,6 +366,13 @@ func TestProjectLogsFilter(t *testing.T) {
|
||||
|
||||
func TestDeletable(t *testing.T) {
|
||||
apiTest := newHarborAPI()
|
||||
chServer, oldController, err := mockChartController()
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, chServer)
|
||||
defer chServer.Close()
|
||||
defer func() {
|
||||
chartController = oldController
|
||||
}()
|
||||
|
||||
project := models.Project{
|
||||
Name: "project_for_test_deletable",
|
||||
@ -398,3 +409,36 @@ func TestDeletable(t *testing.T) {
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.False(t, del)
|
||||
}
|
||||
|
||||
//Provides a mock chart controller for deletable test cases
|
||||
func mockChartController() (*httptest.Server, *chartserver.Controller, error) {
|
||||
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.RequestURI {
|
||||
case "/api/project_for_test_deletable/charts":
|
||||
if r.Method == http.MethodGet {
|
||||
w.Write([]byte("{}"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
w.Write([]byte("not supported"))
|
||||
}))
|
||||
|
||||
var oldController, newController *chartserver.Controller
|
||||
url, err := url.Parse(mockServer.URL)
|
||||
if err == nil {
|
||||
newController, err = chartserver.NewController(url)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
mockServer.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
//Override current controller and keep the old one for restoring
|
||||
oldController = chartController
|
||||
chartController = newController
|
||||
|
||||
return mockServer, oldController, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user