From c267aaa47408acb1dcfbc1240a8a7592e5089bf0 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Fri, 7 Feb 2020 09:11:54 +0800 Subject: [PATCH] Implement repository deletion API Implement repository deletion API based on the new design Signed-off-by: Wenkai Yin --- api/v2.0/swagger.yaml | 22 ++++++++++++ src/api/artifact/controller.go | 1 + src/api/repository/controller.go | 27 ++++++++++++++ src/api/repository/controller_test.go | 15 ++++++++ src/server/v2.0/handler/handler.go | 3 +- src/server/v2.0/handler/repository.go | 45 ++++++++++++++++++++++++ src/testing/api/repository/controller.go | 6 ++++ 7 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/server/v2.0/handler/repository.go diff --git a/api/v2.0/swagger.yaml b/api/v2.0/swagger.yaml index fd1cc8084..52fdfeee5 100644 --- a/api/v2.0/swagger.yaml +++ b/api/v2.0/swagger.yaml @@ -229,6 +229,28 @@ paths: $ref: '#/responses/404' '500': $ref: '#/responses/500' + /projects/{project_name}/repositories/{repository_name}: + delete: + summary: Delete repository + description: Delete the repository specified by name + tags: + - repository + operationId: deleteRepository + parameters: + - $ref: '#/parameters/requestId' + - $ref: '#/parameters/projectName' + - $ref: '#/parameters/repositoryName' + responses: + '200': + $ref: '#/responses/200' + '401': + $ref: '#/responses/401' + '403': + $ref: '#/responses/403' + '404': + $ref: '#/responses/404' + '500': + $ref: '#/responses/500' parameters: requestId: name: X-Request-Id diff --git a/src/api/artifact/controller.go b/src/api/artifact/controller.go index 3866a069d..7c4ad8e79 100644 --- a/src/api/artifact/controller.go +++ b/src/api/artifact/controller.go @@ -295,6 +295,7 @@ func (c *controller) Delete(ctx context.Context, id int64) error { } func (c *controller) CreateTag(ctx context.Context, tag *Tag) (int64, error) { + // TODO fire event return c.tagMgr.Create(ctx, &(tag.Tag)) } func (c *controller) ListTags(ctx context.Context, query *q.Query, option *TagOption) (int64, []*Tag, error) { diff --git a/src/api/repository/controller.go b/src/api/repository/controller.go index abb416002..2eb159d39 100644 --- a/src/api/repository/controller.go +++ b/src/api/repository/controller.go @@ -16,6 +16,7 @@ package repository import ( "context" + "github.com/goharbor/harbor/src/api/artifact" "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/utils" ierror "github.com/goharbor/harbor/src/internal/error" @@ -41,6 +42,8 @@ type Controller interface { Get(ctx context.Context, id int64) (repository *models.RepoRecord, err error) // GetByName gets the repository specified by name GetByName(ctx context.Context, name string) (repository *models.RepoRecord, err error) + // Delete the repository specified by ID + Delete(ctx context.Context, id int64) (err error) } // NewController creates an instance of the default repository controller @@ -48,12 +51,14 @@ func NewController() Controller { return &controller{ proMgr: project.Mgr, repoMgr: repository.Mgr, + artCtl: artifact.Ctl, } } type controller struct { proMgr project.Manager repoMgr repository.Manager + artCtl artifact.Controller } func (c *controller) Ensure(ctx context.Context, name string) (bool, int64, error) { @@ -108,3 +113,25 @@ func (c *controller) Get(ctx context.Context, id int64) (*models.RepoRecord, err func (c *controller) GetByName(ctx context.Context, name string) (*models.RepoRecord, error) { return c.repoMgr.GetByName(ctx, name) } + +func (c *controller) Delete(ctx context.Context, id int64) error { + // TODO auth + // TODO how to make sure the logic included by middlewares(immutable, readonly, quota, etc) + // TODO is covered when deleting the artifacts of the repository + _, artifacts, err := c.artCtl.List(ctx, &q.Query{ + Keywords: map[string]interface{}{ + "RepositoryID": id, + }, + }, nil) + if err != nil { + return err + } + for _, artifact := range artifacts { + if err = c.artCtl.Delete(ctx, artifact.ID); err != nil { + return err + } + } + return c.repoMgr.Delete(ctx, id) + + // TODO fire event +} diff --git a/src/api/repository/controller_test.go b/src/api/repository/controller_test.go index 8e7fe8f1f..fadef2d9c 100644 --- a/src/api/repository/controller_test.go +++ b/src/api/repository/controller_test.go @@ -15,7 +15,9 @@ package repository import ( + "github.com/goharbor/harbor/src/api/artifact" "github.com/goharbor/harbor/src/common/models" + artifacttesting "github.com/goharbor/harbor/src/testing/api/artifact" "github.com/goharbor/harbor/src/testing/pkg/project" "github.com/goharbor/harbor/src/testing/pkg/repository" "github.com/stretchr/testify/suite" @@ -27,14 +29,17 @@ type controllerTestSuite struct { ctl *controller proMgr *project.FakeManager repoMgr *repository.FakeManager + artCtl *artifacttesting.FakeController } func (c *controllerTestSuite) SetupTest() { c.proMgr = &project.FakeManager{} c.repoMgr = &repository.FakeManager{} + c.artCtl = &artifacttesting.FakeController{} c.ctl = &controller{ proMgr: c.proMgr, repoMgr: c.repoMgr, + artCtl: c.artCtl, } } @@ -104,6 +109,16 @@ func (c *controllerTestSuite) TestGetByName() { c.Equal(int64(1), repository.RepositoryID) } +func (c *controllerTestSuite) TestDelete() { + art := &artifact.Artifact{} + art.ID = 1 + c.artCtl.On("List").Return(1, []*artifact.Artifact{art}, nil) + c.artCtl.On("Delete").Return(nil) + c.repoMgr.On("Delete").Return(nil) + err := c.ctl.Delete(nil, 1) + c.Require().Nil(err) +} + func TestControllerTestSuite(t *testing.T) { suite.Run(t, &controllerTestSuite{}) } diff --git a/src/server/v2.0/handler/handler.go b/src/server/v2.0/handler/handler.go index d6f354eee..157f3b043 100644 --- a/src/server/v2.0/handler/handler.go +++ b/src/server/v2.0/handler/handler.go @@ -30,7 +30,8 @@ import ( // New returns http handler for API V2.0 func New() http.Handler { h, api, err := restapi.HandlerAPI(restapi.Config{ - ArtifactAPI: newArtifactAPI(), + ArtifactAPI: newArtifactAPI(), + RepositoryAPI: newRepositoryAPI(), }) if err != nil { log.Fatal(err) diff --git a/src/server/v2.0/handler/repository.go b/src/server/v2.0/handler/repository.go new file mode 100644 index 000000000..78229ebbe --- /dev/null +++ b/src/server/v2.0/handler/repository.go @@ -0,0 +1,45 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package handler + +import ( + "context" + "fmt" + "github.com/go-openapi/runtime/middleware" + "github.com/goharbor/harbor/src/api/repository" + operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/repository" +) + +func newRepositoryAPI() *repositoryAPI { + return &repositoryAPI{ + repoCtl: repository.Ctl, + } +} + +type repositoryAPI struct { + BaseAPI + repoCtl repository.Controller +} + +func (r *repositoryAPI) DeleteRepository(ctx context.Context, params operation.DeleteRepositoryParams) middleware.Responder { + repository, err := r.repoCtl.GetByName(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName)) + if err != nil { + return r.SendError(ctx, err) + } + if err := r.repoCtl.Delete(ctx, repository.RepositoryID); err != nil { + return r.SendError(ctx, err) + } + return operation.NewDeleteRepositoryOK() +} diff --git a/src/testing/api/repository/controller.go b/src/testing/api/repository/controller.go index 3ab1ecc20..0a95233f4 100644 --- a/src/testing/api/repository/controller.go +++ b/src/testing/api/repository/controller.go @@ -62,3 +62,9 @@ func (f *FakeController) GetByName(ctx context.Context, name string) (*models.Re } return repository, args.Error(1) } + +// Delete ... +func (f *FakeController) Delete(ctx context.Context, id int64) error { + args := f.Called() + return args.Error(0) +}