mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-03 14:37:44 +01:00
Enhance the project deletable checking logic to include helm charts checking
add utility handler in chart controller to support getting charts by ns add ut case for utility handler update deletable checking logic in project controller refactor project deletable test case
This commit is contained in:
parent
4e4a08d0a4
commit
b501199033
@ -36,6 +36,9 @@ type Controller struct {
|
|||||||
|
|
||||||
//To cover all the manipulation requests
|
//To cover all the manipulation requests
|
||||||
manipulationHandler *ManipulationHandler
|
manipulationHandler *ManipulationHandler
|
||||||
|
|
||||||
|
//To cover the other utility requests
|
||||||
|
utilityHandler *UtilityHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewController is constructor of the chartserver.Controller
|
//NewController is constructor of the chartserver.Controller
|
||||||
@ -86,6 +89,11 @@ func NewController(backendServer *url.URL) (*Controller, error) {
|
|||||||
backendServerAddress: backendServer,
|
backendServerAddress: backendServer,
|
||||||
chartCache: cache,
|
chartCache: cache,
|
||||||
},
|
},
|
||||||
|
utilityHandler: &UtilityHandler{
|
||||||
|
apiClient: client,
|
||||||
|
backendServerAddress: backendServer,
|
||||||
|
chartOperator: operator,
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +112,11 @@ func (c *Controller) GetManipulationHandler() *ManipulationHandler {
|
|||||||
return c.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
|
//What's the cache driver if it is set
|
||||||
func parseCacheDriver() (string, bool) {
|
func parseCacheDriver() (string, bool) {
|
||||||
driver, ok := os.LookupEnv(cacheDriverENVKey)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := deletable(p.project.ProjectID)
|
result, err := p.deletable(p.project.ProjectID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.HandleInternalServerError(fmt.Sprintf(
|
p.HandleInternalServerError(fmt.Sprintf(
|
||||||
"failed to check the deletable of project %d: %v", p.project.ProjectID, err))
|
"failed to check the deletable of project %d: %v", p.project.ProjectID, err))
|
||||||
@ -258,7 +258,7 @@ func (p *ProjectAPI) Deletable() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := deletable(p.project.ProjectID)
|
result, err := p.deletable(p.project.ProjectID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.HandleInternalServerError(fmt.Sprintf(
|
p.HandleInternalServerError(fmt.Sprintf(
|
||||||
"failed to check the deletable of project %d: %v", p.project.ProjectID, err))
|
"failed to check the deletable of project %d: %v", p.project.ProjectID, err))
|
||||||
@ -269,7 +269,7 @@ func (p *ProjectAPI) Deletable() {
|
|||||||
p.ServeJSON()
|
p.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
func deletable(projectID int64) (*deletableResp, error) {
|
func (p *ProjectAPI) deletable(projectID int64) (*deletableResp, error) {
|
||||||
count, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{
|
count, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{
|
||||||
ProjectIDs: []int64{projectID},
|
ProjectIDs: []int64{projectID},
|
||||||
})
|
})
|
||||||
@ -280,7 +280,7 @@ func deletable(projectID int64) (*deletableResp, error) {
|
|||||||
if count > 0 {
|
if count > 0 {
|
||||||
return &deletableResp{
|
return &deletableResp{
|
||||||
Deletable: false,
|
Deletable: false,
|
||||||
Message: "the project contains repositories, can not be deleled",
|
Message: "the project contains repositories, can not be deleted",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,10 +292,25 @@ func deletable(projectID int64) (*deletableResp, error) {
|
|||||||
if len(policies) > 0 {
|
if len(policies) > 0 {
|
||||||
return &deletableResp{
|
return &deletableResp{
|
||||||
Deletable: false,
|
Deletable: false,
|
||||||
Message: "the project contains replication rules, can not be deleled",
|
Message: "the project contains replication rules, can not be deleted",
|
||||||
}, nil
|
}, 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{
|
return &deletableResp{
|
||||||
Deletable: true,
|
Deletable: true,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -16,10 +16,14 @@ package api
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/vmware/harbor/src/chartserver"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/vmware/harbor/src/common/dao"
|
"github.com/vmware/harbor/src/common/dao"
|
||||||
@ -362,6 +366,13 @@ func TestProjectLogsFilter(t *testing.T) {
|
|||||||
|
|
||||||
func TestDeletable(t *testing.T) {
|
func TestDeletable(t *testing.T) {
|
||||||
apiTest := newHarborAPI()
|
apiTest := newHarborAPI()
|
||||||
|
chServer, oldController, err := mockChartController()
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, chServer)
|
||||||
|
defer chServer.Close()
|
||||||
|
defer func() {
|
||||||
|
chartController = oldController
|
||||||
|
}()
|
||||||
|
|
||||||
project := models.Project{
|
project := models.Project{
|
||||||
Name: "project_for_test_deletable",
|
Name: "project_for_test_deletable",
|
||||||
@ -398,3 +409,36 @@ func TestDeletable(t *testing.T) {
|
|||||||
assert.Equal(t, http.StatusOK, code)
|
assert.Equal(t, http.StatusOK, code)
|
||||||
assert.False(t, del)
|
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