continue refactor

Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
wang yan 2020-06-02 12:25:24 +08:00
parent de504993ad
commit c10467eb36
15 changed files with 405 additions and 109 deletions

View File

@ -1,2 +0,0 @@
ALTER TABLE blob ADD COLUMN IF NOT EXISTS update_time timestamp default CURRENT_TIMESTAMP;
ALTER TABLE blob ADD COLUMN IF NOT EXISTS status varchar(255);

View File

@ -29,3 +29,9 @@ CREATE TABLE IF NOT EXISTS task (
end_time timestamp,
FOREIGN KEY (execution_id) REFERENCES execution(id)
);
ALTER TABLE blob ADD COLUMN IF NOT EXISTS update_time timestamp default CURRENT_TIMESTAMP;
ALTER TABLE blob ADD COLUMN IF NOT EXISTS status varchar(255);
ALTER TABLE blob ADD COLUMN IF NOT EXISTS version BIGINT default 0;
CREATE INDEX IF NOT EXISTS idx_status ON blob (status);
CREATE INDEX IF NOT EXISTS idx_version ON blob (version);

View File

@ -17,7 +17,6 @@ package blob
import (
"context"
"fmt"
"github.com/docker/distribution"
"github.com/garyburd/redigo/redis"
util "github.com/goharbor/harbor/src/common/utils/redis"
@ -25,7 +24,6 @@ import (
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/blob"
"time"
)
var (
@ -76,8 +74,14 @@ type Controller interface {
// GetAcceptedBlobSize returns the accepted size of stream upload blob.
GetAcceptedBlobSize(sessionID string) (int64, error)
// ReFreshUpdateTime updates the update time for the blob.
ReFreshUpdateTime(ctx context.Context, digest string, time time.Time) (err error)
// Touch updates the blob status and increase version every time.
Touch(ctx context.Context, blob *blob.Blob) (int64, error)
// Update updates the blob, it cannot handle blob status transitions.
Update(ctx context.Context, blob *blob.Blob) error
// Delete deletes the blob by its id
Delete(ctx context.Context, id int64) error
}
// NewController creates an instance of the default repository controller
@ -263,7 +267,7 @@ func (c *controller) Sync(ctx context.Context, references []distribution.Descrip
if len(updating) > 0 {
orm.WithTransaction(func(ctx context.Context) error {
for _, blob := range updating {
if err := c.blobMgr.Update(ctx, blob); err != nil {
if err := c.Update(ctx, blob); err != nil {
log.G(ctx).Warningf("Failed to update blob %s, error: %v", blob.Digest, err)
return err
}
@ -318,6 +322,14 @@ func (c *controller) GetAcceptedBlobSize(sessionID string) (int64, error) {
return size, nil
}
func (c *controller) ReFreshUpdateTime(ctx context.Context, digest string, time time.Time) (err error) {
return c.blobMgr.ReFreshUpdateTime(ctx, digest, time)
func (c *controller) Touch(ctx context.Context, blob *blob.Blob) (int64, error) {
return c.blobMgr.UpdateBlobStatus(ctx, blob)
}
func (c *controller) Update(ctx context.Context, blob *blob.Blob) error {
return c.blobMgr.Update(ctx, blob)
}
func (c *controller) Delete(ctx context.Context, id int64) error {
return c.blobMgr.Delete(ctx, id)
}

View File

@ -17,8 +17,8 @@ package blob
import (
"context"
"fmt"
"github.com/goharbor/harbor/src/pkg/blob/models"
"testing"
"time"
"github.com/docker/distribution/manifest/schema2"
"github.com/goharbor/harbor/src/pkg/blob"
@ -268,19 +268,38 @@ func (suite *ControllerTestSuite) TestGetSetAcceptedBlobSize() {
suite.Equal(int64(100), size)
}
func (suite *ControllerTestSuite) TestReFreshUpdateTime() {
func (suite *ControllerTestSuite) TestUpdateStatus() {
ctx := suite.Context()
digest := suite.prepareBlob()
blob, err := Ctl.Get(ctx, digest)
suite.Nil(err)
now := time.Now()
suite.NotEqual(blob.UpdateTime, now)
err = Ctl.ReFreshUpdateTime(ctx, blob.Digest, now)
suite.Equal(blob.Status, models.StatusNone)
blob.Status = models.StatusDelete
count, err := Ctl.Touch(ctx, blob)
suite.Nil(err)
suite.Equal(blob.UpdateTime.Unix(), now.Unix())
suite.Equal(blob.Status, models.StatusDelete)
suite.Equal(int64(1), count)
}
func (suite *ControllerTestSuite) TestDelete() {
ctx := suite.Context()
digest := suite.DigestString()
_, err := Ctl.Ensure(ctx, digest, "application/octet-stream", 100)
suite.Nil(err)
blob, err := Ctl.Get(ctx, digest)
suite.Nil(err)
suite.Equal(digest, blob.Digest)
err = Ctl.Delete(ctx, blob.ID)
suite.Nil(err)
exist, err := Ctl.Exist(ctx, digest)
suite.Nil(err)
suite.False(exist)
}
func TestControllerTestSuite(t *testing.T) {

View File

@ -21,12 +21,11 @@ import (
"strings"
"time"
beego_orm "github.com/astaxie/beego/orm"
"github.com/docker/distribution/manifest/schema2"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/lib/q"
"github.com/goharbor/harbor/src/pkg/blob/models"
beego_orm "github.com/astaxie/beego/orm"
)
// DAO the dao for Blob, ArtifactAndBlob and ProjectBlob
@ -52,6 +51,9 @@ type DAO interface {
// UpdateBlob update blob
UpdateBlob(ctx context.Context, blob *models.Blob) error
// UpdateBlob update blob status
UpdateBlobStatus(ctx context.Context, blob *models.Blob) (int64, error)
// ListBlobs list blobs by query
ListBlobs(ctx context.Context, params models.ListParams) ([]*models.Blob, error)
@ -72,9 +74,6 @@ type DAO interface {
// DeleteBlob delete blob
DeleteBlob(ctx context.Context, id int64) (err error)
// ReFreshUpdateTime updates the blob update time
ReFreshUpdateTime(ctx context.Context, digest string, time time.Time) error
}
// New returns an instance of the default DAO
@ -171,13 +170,50 @@ func (d *dao) GetBlobByDigest(ctx context.Context, digest string) (*models.Blob,
return blob, nil
}
func (d *dao) UpdateBlobStatus(ctx context.Context, blob *models.Blob) (int64, error) {
o, err := orm.FromContext(ctx)
if err != nil {
return -1, err
}
// each update will auto increase version and update time
data := make(beego_orm.Params)
data["version"] = beego_orm.ColValue(beego_orm.ColAdd, 1)
data["update_time"] = time.Now()
data["status"] = blob.Status
qt := o.QueryTable(&models.Blob{})
cond := beego_orm.NewCondition()
var c *beego_orm.Condition
// In the multiple blob head scenario, if one request success mark the blob from StatusDelete to StatusNone, then version should increase one.
// in the meantime, the other requests tries to do the same thing, use 'where version >= blob.version' can handle it.
if blob.Status == models.StatusNone {
c = cond.And("version__gte", blob.Version)
} else {
c = cond.And("version", blob.Version)
}
/*
generated simple sql string.
UPDATE "blob" SET "version" = "version" + $1, "update_time" = $2, "status" = $3
WHERE "id" IN ( SELECT T0."id" FROM "blob" T0 WHERE T0."version" >= $4 AND T0."id" = $5 AND T0."status" IN ('delete', 'deleting') )
*/
return qt.SetCond(c).Filter("id", blob.ID).
Filter("status__in", models.StatusMap[blob.Status]).
Update(data)
}
// UpdateBlob cannot handle the status change.
func (d *dao) UpdateBlob(ctx context.Context, blob *models.Blob) error {
o, err := orm.FromContext(ctx)
if err != nil {
return err
}
_, err = o.Update(blob)
blob.Version = blob.Version + 1
blob.UpdateTime = time.Now()
_, err = o.Update(blob, "size", "content_type", "version", "update_time")
return err
}
@ -344,13 +380,3 @@ func (d *dao) DeleteBlob(ctx context.Context, id int64) error {
}
return nil
}
func (d *dao) ReFreshUpdateTime(ctx context.Context, digest string, time time.Time) error {
qs, err := orm.QuerySetter(ctx, &models.Blob{}, &q.Query{
Keywords: map[string]interface{}{
"digest": digest,
},
})
_, err = qs.Update(beego_orm.Params{"update_time": time})
return err
}

View File

@ -16,12 +16,10 @@ package dao
import (
"github.com/goharbor/harbor/src/lib/errors"
"testing"
"time"
"github.com/goharbor/harbor/src/pkg/blob/models"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/stretchr/testify/suite"
"testing"
)
type DaoTestSuite struct {
@ -131,20 +129,60 @@ func (suite *DaoTestSuite) TestUpdateBlob() {
digest := suite.DigestString()
suite.dao.CreateBlob(ctx, &models.Blob{Digest: digest})
blob, err := suite.dao.GetBlobByDigest(ctx, digest)
if suite.Nil(err) {
suite.Equal(int64(0), blob.Size)
}
blob.Size = 100
if suite.Nil(suite.dao.UpdateBlob(ctx, blob)) {
blob, err := suite.dao.GetBlobByDigest(ctx, digest)
if suite.Nil(err) {
suite.Equal(int64(100), blob.Size)
suite.Equal(int64(1), blob.Version)
}
}
blob.Status = "deleting"
suite.Nil(suite.dao.UpdateBlob(ctx, blob), "cannot be updated.")
blob, err = suite.dao.GetBlobByDigest(ctx, digest)
if suite.Nil(err) {
suite.Equal(int64(2), blob.Version)
suite.Equal(models.StatusNone, blob.Status)
}
}
func (suite *DaoTestSuite) TestUpdateBlobStatus() {
ctx := suite.Context()
digest := suite.DigestString()
suite.dao.CreateBlob(ctx, &models.Blob{Digest: digest})
blob, err := suite.dao.GetBlobByDigest(ctx, digest)
if suite.Nil(err) {
suite.Equal(int64(0), blob.Size)
}
// StatusNone cannot be updated to StatusDeleting directly
blob.Status = models.StatusDeleting
count, err := suite.dao.UpdateBlobStatus(ctx, blob)
suite.Nil(err)
suite.Equal(int64(0), count)
blob, err = suite.dao.GetBlobByDigest(ctx, digest)
if suite.Nil(err) {
suite.Equal(int64(0), blob.Version)
suite.Equal(models.StatusNone, blob.Status)
}
blob.Status = models.StatusDelete
count, err = suite.dao.UpdateBlobStatus(ctx, blob)
suite.Nil(err)
suite.Equal(int64(1), count)
blob, err = suite.dao.GetBlobByDigest(ctx, digest)
if suite.Nil(err) {
suite.Equal(int64(1), blob.Version)
suite.Equal(models.StatusDelete, blob.Status)
}
}
func (suite *DaoTestSuite) TestListBlobs() {
@ -338,25 +376,6 @@ func (suite *DaoTestSuite) TestDelete() {
suite.Require().Nil(err)
}
func (suite *DaoTestSuite) TestReFreshUpdateTime() {
ctx := suite.Context()
digest := suite.DigestString()
suite.dao.CreateBlob(ctx, &models.Blob{Digest: digest})
blob, err := suite.dao.GetBlobByDigest(ctx, digest)
suite.Require().Nil(err)
time.Sleep(1 * time.Second)
now := time.Now()
suite.NotEqual(blob.UpdateTime, now)
if suite.Nil(suite.dao.ReFreshUpdateTime(ctx, blob.Digest, now)) {
blob, err := suite.dao.GetBlobByDigest(ctx, digest)
if suite.Nil(err) {
suite.Equal(now.Unix(), blob.UpdateTime.Unix())
}
}
}
func TestDaoTestSuite(t *testing.T) {
suite.Run(t, &DaoTestSuite{})
}

View File

@ -16,10 +16,8 @@ package blob
import (
"context"
"github.com/goharbor/harbor/src/pkg/blob/dao"
"github.com/goharbor/harbor/src/pkg/blob/models"
"time"
)
// Blob alias `models.Blob` to make it natural to use the Manager
@ -59,14 +57,14 @@ type Manager interface {
// Update the blob
Update(ctx context.Context, blob *Blob) error
// Update the blob status
UpdateBlobStatus(ctx context.Context, blob *models.Blob) (int64, error)
// List returns blobs by params
List(ctx context.Context, params ListParams) ([]*Blob, error)
// DeleteBlob delete blob
Delete(ctx context.Context, id int64) (err error)
// ReFreshUpdateTime updates the blob update time
ReFreshUpdateTime(ctx context.Context, digest string, time time.Time) error
}
type manager struct {
@ -119,6 +117,10 @@ func (m *manager) Update(ctx context.Context, blob *Blob) error {
return m.dao.UpdateBlob(ctx, blob)
}
func (m *manager) UpdateBlobStatus(ctx context.Context, blob *models.Blob) (int64, error) {
return m.dao.UpdateBlobStatus(ctx, blob)
}
func (m *manager) List(ctx context.Context, params ListParams) ([]*Blob, error) {
return m.dao.ListBlobs(ctx, params)
}
@ -127,10 +129,6 @@ func (m *manager) Delete(ctx context.Context, id int64) error {
return m.dao.DeleteBlob(ctx, id)
}
func (m *manager) ReFreshUpdateTime(ctx context.Context, digest string, time time.Time) error {
return m.dao.ReFreshUpdateTime(ctx, digest, time)
}
// NewManager returns blob manager
func NewManager() Manager {
return &manager{dao: dao.New()}

View File

@ -16,11 +16,11 @@ package blob
import (
"context"
"testing"
"time"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/stretchr/testify/suite"
"testing"
"github.com/goharbor/harbor/src/pkg/blob/models"
)
type ManagerTestSuite struct {
@ -201,6 +201,7 @@ func (suite *ManagerTestSuite) TestUpdate() {
suite.Equal(digest, blob.Digest)
suite.Equal("media type", blob.ContentType)
suite.Equal(int64(1000), blob.Size)
suite.Equal(models.StatusNone, blob.Status)
}
}
}
@ -267,26 +268,24 @@ func (suite *ManagerTestSuite) TestDelete() {
suite.Nil(err)
}
func (suite *ManagerTestSuite) TestReFreshUpdateTime() {
func (suite *ManagerTestSuite) TestUpdateStatus() {
ctx := suite.Context()
digest := suite.DigestString()
_, err := Mgr.Create(ctx, digest, "media type", 100)
suite.Nil(err)
time.Sleep(1 * time.Second)
now := time.Now()
blob, err := Mgr.Get(ctx, digest)
if suite.Nil(err) {
blob.UpdateTime = now
suite.Nil(Mgr.Update(ctx, blob))
blob.Status = models.StatusDelete
_, err := Mgr.UpdateBlobStatus(ctx, blob)
suite.Nil(err)
{
blob, err := Mgr.Get(ctx, digest)
suite.Nil(err)
suite.Equal(digest, blob.Digest)
suite.Equal(now.Unix(), blob.UpdateTime.Unix())
suite.Equal(models.StatusDelete, blob.Status)
}
}
}

View File

@ -30,6 +30,37 @@ func init() {
// ArtifactAndBlob alias ArtifactAndBlob model
type ArtifactAndBlob = models.ArtifactAndBlob
/*
the status are used for Garbage Collection
StatusNone, the blob is using in Harbor as normal.
StatusDelete, the blob is marked as GC candidate.
StatusDeleting, the blob undergo a GC blob deletion.
StatusDeleteFailed, the blob is failed to delete from the backend storage.
The status transitions
StatusNone -> StatusDelete : Mark the blob as candidate.
StatusDelete -> StatusDeleting : Select the blob and call the API to delete asset in the backend storage.
StatusDeleting -> Trash : Delete success from the backend storage.
StatusDelete -> StatusNone : Client asks the existence of blob, remove it from the candidate.
StatusDelete -> StatusDeleteFailed : The storage driver returns fail when to delete the real data from the configurated file system.
StatusDeleteFailed -> StatusNone : The delete failed blobs can be pushed again, and back to normal.
StatusDeleteFailed -> StatusDelete : The delete failed blobs should be in the candidate.
*/
const (
StatusNone = ""
StatusDelete = "delete"
StatusDeleting = "deleting"
StatusDeleteFailed = "deletefailed"
)
// StatusMap key is the target status, values are the accept source status. For example, only StatusNone and StatusDeleteFailed can be convert to StatusDelete.
var StatusMap = map[string][]string{
StatusNone: {StatusNone, StatusDelete, StatusDeleteFailed},
StatusDelete: {StatusNone, StatusDeleteFailed},
StatusDeleting: {StatusDelete},
StatusDeleteFailed: {StatusDeleting},
}
// Blob holds the details of a blob.
type Blob struct {
ID int64 `orm:"pk;auto;column(id)" json:"id"`
@ -38,6 +69,7 @@ type Blob struct {
Size int64 `orm:"column(size)" json:"size"`
Status string `orm:"column(status)" json:"status"`
UpdateTime time.Time `orm:"column(update_time);auto_now_add" json:"update_time"`
Version int64 `orm:"column(version)" json:"version"`
CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"`
}

View File

@ -0,0 +1,56 @@
package blob
import (
"fmt"
"github.com/goharbor/harbor/src/controller/blob"
"github.com/goharbor/harbor/src/lib"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
blob_models "github.com/goharbor/harbor/src/pkg/blob/models"
serror "github.com/goharbor/harbor/src/server/error"
"github.com/goharbor/harbor/src/server/middleware"
"net/http"
)
// HeadBlobMiddleware intercept the head blob request
func HeadBlobMiddleware() func(http.Handler) http.Handler {
return middleware.New(func(rw http.ResponseWriter, req *http.Request, next http.Handler) {
if err := handleHead(req); err != nil {
serror.SendError(rw, err)
return
}
next.ServeHTTP(rw, req)
})
}
// handleHead ...
func handleHead(req *http.Request) error {
none := lib.ArtifactInfo{}
art := lib.GetArtifactInfo(req.Context())
if art == none {
return errors.New("cannot get the artifact information from request context").WithCode(errors.NotFoundCode)
}
bb, err := blob.Ctl.Get(req.Context(), art.Digest)
if err != nil {
return err
}
switch bb.Status {
case blob_models.StatusNone, blob_models.StatusDelete:
bb.Status = blob_models.StatusNone
count, err := blob.Ctl.Touch(req.Context(), bb)
if err != nil {
log.Errorf("failed to update blob: %s status to None, error:%v", art.Digest, err)
return err
}
if count == 0 {
return errors.New("the asking blob is in GC, mark it as non existing").WithCode(errors.NotFoundCode)
}
case blob_models.StatusDeleting, blob_models.StatusDeleteFailed:
return errors.New("the asking blob is in GC, mark it as non existing").WithCode(errors.NotFoundCode)
default:
return errors.New(nil).WithMessage(fmt.Sprintf("wrong blob status, %s", bb.Status))
}
return nil
}

View File

@ -0,0 +1,88 @@
package blob
import (
"fmt"
beego_orm "github.com/astaxie/beego/orm"
"github.com/goharbor/harbor/src/controller/blob"
"github.com/goharbor/harbor/src/lib"
"github.com/goharbor/harbor/src/lib/orm"
blob_models "github.com/goharbor/harbor/src/pkg/blob/models"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/stretchr/testify/suite"
"net/http"
"net/http/httptest"
"testing"
)
type HeadBlobUploadMiddlewareTestSuite struct {
htesting.Suite
}
func (suite *HeadBlobUploadMiddlewareTestSuite) SetupSuite() {
suite.Suite.SetupSuite()
suite.Suite.ClearTables = []string{"blob"}
}
func (suite *HeadBlobUploadMiddlewareTestSuite) makeRequest(projectName, digest string) *http.Request {
req := httptest.NewRequest("HEAD", fmt.Sprintf("/v2/%s/blobs/%s", projectName, digest), nil)
info := lib.ArtifactInfo{
Repository: fmt.Sprintf("%s/photon", projectName),
Reference: "2.0",
Tag: "2.0",
Digest: digest,
}
*req = *(req.WithContext(orm.NewContext(req.Context(), beego_orm.NewOrm())))
*req = *(req.WithContext(lib.WithArtifactInfo(req.Context(), info)))
return req
}
func (suite *HeadBlobUploadMiddlewareTestSuite) TestHeadBlobStatusNone() {
suite.WithProject(func(projectID int64, projectName string) {
digest := suite.DigestString()
_, err := blob.Ctl.Ensure(suite.Context(), digest, "application/octet-stream", 512)
suite.Nil(err)
req := suite.makeRequest(projectName, digest)
res := httptest.NewRecorder()
next := suite.NextHandler(http.StatusOK, map[string]string{"Docker-Content-Digest": digest})
HeadBlobMiddleware()(next).ServeHTTP(res, req)
suite.Equal(http.StatusOK, res.Code)
blob, err := blob.Ctl.Get(suite.Context(), digest)
suite.Nil(err)
suite.Equal(digest, blob.Digest)
suite.Equal(blob_models.StatusNone, blob.Status)
})
}
func (suite *HeadBlobUploadMiddlewareTestSuite) TestHeadBlobStatusDeleting() {
suite.WithProject(func(projectID int64, projectName string) {
digest := suite.DigestString()
id, err := blob.Ctl.Ensure(suite.Context(), digest, "application/octet-stream", 512)
suite.Nil(err)
// status-none -> status-delete -> status-deleting
_, err = blob.Ctl.Touch(suite.Context(), &blob_models.Blob{ID: id, Status: blob_models.StatusDelete})
suite.Nil(err)
_, err = blob.Ctl.Touch(suite.Context(), &blob_models.Blob{ID: id, Status: blob_models.StatusDeleting, Version: 1})
suite.Nil(err)
req := suite.NewRequest(http.MethodHead, fmt.Sprintf("/v2/%s/blobs/%s", projectName, digest), nil)
res := httptest.NewRecorder()
next := suite.NextHandler(http.StatusOK, map[string]string{"Docker-Content-Digest": digest})
HeadBlobMiddleware()(next).ServeHTTP(res, req)
suite.Equal(http.StatusNotFound, res.Code)
blob, err := blob.Ctl.Get(suite.Context(), digest)
suite.Nil(err)
suite.Equal(digest, blob.Digest)
suite.Equal(blob_models.StatusDeleting, blob.Status)
})
}
func TestHeadBlobUploadMiddlewareTestSuite(t *testing.T) {
suite.Run(t, &HeadBlobUploadMiddlewareTestSuite{})
}

View File

@ -22,7 +22,7 @@ var (
V2ManifestURLRe = regexp.MustCompile(fmt.Sprintf(`^/v2/(?P<%s>%s)/manifests/(?P<%s>%s|%s)$`, RepositorySubexp, reference.NameRegexp.String(), ReferenceSubexp, reference.TagRegexp.String(), digest.DigestRegexp.String()))
// V2TagListURLRe is the regular expression for matching request to v2 handler to list tags
V2TagListURLRe = regexp.MustCompile(fmt.Sprintf(`^/v2/(?P<%s>%s)/tags/list`, RepositorySubexp, reference.NameRegexp.String()))
// V2BlobURLRe is the regular expression for matching request to v2 handler to retrieve delete a blob
// V2BlobURLRe is the regular expression for matching request to v2 handler to retrieve head/delete a blob
V2BlobURLRe = regexp.MustCompile(fmt.Sprintf(`^/v2/(?P<%s>%s)/blobs/(?P<%s>%s)$`, RepositorySubexp, reference.NameRegexp.String(), DigestSubexp, digest.DigestRegexp.String()))
// V2BlobUploadURLRe is the regular expression for matching the request to v2 handler to upload a blob, the upload uuid currently is not put into a group
V2BlobUploadURLRe = regexp.MustCompile(fmt.Sprintf(`^/v2/(?P<%s>%s)/blobs/uploads[/a-zA-Z0-9\-_\.=]*$`, RepositorySubexp, reference.NameRegexp.String()))

View File

@ -85,6 +85,11 @@ func RegisterRoutes() {
Middleware(quota.PutBlobUploadMiddleware()).
Middleware(blob.PutBlobUploadMiddleware()).
Handler(proxy)
root.NewRoute().
Method(http.MethodHead).
Path("/*/blobs/:digest").
Middleware(blob.HeadBlobMiddleware()).
Handler(proxy)
// others
root.NewRoute().Path("/*").Handler(proxy)
}

View File

@ -12,8 +12,6 @@ import (
mock "github.com/stretchr/testify/mock"
models "github.com/goharbor/harbor/src/pkg/blob/models"
time "time"
)
// Controller is an autogenerated mock type for the Controller type
@ -84,6 +82,20 @@ func (_m *Controller) CalculateTotalSizeByProject(ctx context.Context, projectID
return r0, r1
}
// Delete provides a mock function with given fields: ctx, id
func (_m *Controller) Delete(ctx context.Context, id int64) error {
ret := _m.Called(ctx, id)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// Ensure provides a mock function with given fields: ctx, digest, contentType, size
func (_m *Controller) Ensure(ctx context.Context, digest string, contentType string, size int64) (int64, error) {
ret := _m.Called(ctx, digest, contentType, size)
@ -230,20 +242,6 @@ func (_m *Controller) List(ctx context.Context, params models.ListParams) ([]*mo
return r0, r1
}
// ReFreshUpdateTime provides a mock function with given fields: ctx, digest, _a2
func (_m *Controller) ReFreshUpdateTime(ctx context.Context, digest string, _a2 time.Time) error {
ret := _m.Called(ctx, digest, _a2)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, time.Time) error); ok {
r0 = rf(ctx, digest, _a2)
} else {
r0 = ret.Error(0)
}
return r0
}
// SetAcceptedBlobSize provides a mock function with given fields: sessionID, size
func (_m *Controller) SetAcceptedBlobSize(sessionID string, size int64) error {
ret := _m.Called(sessionID, size)
@ -271,3 +269,38 @@ func (_m *Controller) Sync(ctx context.Context, references []distribution.Descri
return r0
}
// Touch provides a mock function with given fields: ctx, _a1
func (_m *Controller) Touch(ctx context.Context, _a1 *models.Blob) (int64, error) {
ret := _m.Called(ctx, _a1)
var r0 int64
if rf, ok := ret.Get(0).(func(context.Context, *models.Blob) int64); ok {
r0 = rf(ctx, _a1)
} else {
r0 = ret.Get(0).(int64)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *models.Blob) error); ok {
r1 = rf(ctx, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Update provides a mock function with given fields: ctx, _a1
func (_m *Controller) Update(ctx context.Context, _a1 *models.Blob) error {
ret := _m.Called(ctx, _a1)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *models.Blob) error); ok {
r0 = rf(ctx, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}

View File

@ -7,8 +7,6 @@ import (
models "github.com/goharbor/harbor/src/pkg/blob/models"
mock "github.com/stretchr/testify/mock"
time "time"
)
// Manager is an autogenerated mock type for the Manager type
@ -188,20 +186,6 @@ func (_m *Manager) List(ctx context.Context, params models.ListParams) ([]*model
return r0, r1
}
// ReFreshUpdateTime provides a mock function with given fields: ctx, digest, _a2
func (_m *Manager) ReFreshUpdateTime(ctx context.Context, digest string, _a2 time.Time) error {
ret := _m.Called(ctx, digest, _a2)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, time.Time) error); ok {
r0 = rf(ctx, digest, _a2)
} else {
r0 = ret.Error(0)
}
return r0
}
// Update provides a mock function with given fields: ctx, _a1
func (_m *Manager) Update(ctx context.Context, _a1 *models.Blob) error {
ret := _m.Called(ctx, _a1)
@ -215,3 +199,24 @@ func (_m *Manager) Update(ctx context.Context, _a1 *models.Blob) error {
return r0
}
// UpdateBlobStatus provides a mock function with given fields: ctx, _a1
func (_m *Manager) UpdateBlobStatus(ctx context.Context, _a1 *models.Blob) (int64, error) {
ret := _m.Called(ctx, _a1)
var r0 int64
if rf, ok := ret.Get(0).(func(context.Context, *models.Blob) int64); ok {
r0 = rf(ctx, _a1)
} else {
r0 = ret.Get(0).(int64)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *models.Blob) error); ok {
r1 = rf(ctx, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}