mirror of
https://github.com/goharbor/harbor.git
synced 2025-03-11 06:04:11 +01:00
Update codes per review comments
Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
parent
7a41d89ac8
commit
6e11ecc6fc
@ -2409,6 +2409,20 @@ paths:
|
|||||||
$ref: '#/responses/UnsupportedMediaType'
|
$ref: '#/responses/UnsupportedMediaType'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
|
/internal/syncquota:
|
||||||
|
post:
|
||||||
|
summary: Sync quota from registry/chart to DB.
|
||||||
|
description: |
|
||||||
|
This endpoint is for syncing quota usage of registry/chart with database.
|
||||||
|
tags:
|
||||||
|
- Products
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Sync repositories successfully.
|
||||||
|
'401':
|
||||||
|
description: User need to log in first.
|
||||||
|
'403':
|
||||||
|
description: User does not have permission of system admin role.
|
||||||
/systeminfo:
|
/systeminfo:
|
||||||
get:
|
get:
|
||||||
summary: Get general system info
|
summary: Get general system info
|
||||||
|
@ -86,6 +86,7 @@ CREATE TABLE quota_usage
|
|||||||
UNIQUE (reference, reference_id)
|
UNIQUE (reference, reference_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* only set quota and usage for 'library', and let the sync quota handling others. */
|
||||||
INSERT INTO quota (reference, reference_id, hard, creation_time, update_time)
|
INSERT INTO quota (reference, reference_id, hard, creation_time, update_time)
|
||||||
SELECT 'project',
|
SELECT 'project',
|
||||||
CAST(project_id AS VARCHAR),
|
CAST(project_id AS VARCHAR),
|
||||||
@ -93,7 +94,7 @@ SELECT 'project',
|
|||||||
NOW(),
|
NOW(),
|
||||||
NOW()
|
NOW()
|
||||||
FROM project
|
FROM project
|
||||||
WHERE deleted = 'f';
|
WHERE name = 'library' and deleted = 'f';
|
||||||
|
|
||||||
INSERT INTO quota_usage (id, reference, reference_id, used, creation_time, update_time)
|
INSERT INTO quota_usage (id, reference, reference_id, used, creation_time, update_time)
|
||||||
SELECT id,
|
SELECT id,
|
||||||
|
@ -35,6 +35,7 @@ import (
|
|||||||
testutils "github.com/goharbor/harbor/src/common/utils/test"
|
testutils "github.com/goharbor/harbor/src/common/utils/test"
|
||||||
api_models "github.com/goharbor/harbor/src/core/api/models"
|
api_models "github.com/goharbor/harbor/src/core/api/models"
|
||||||
apimodels "github.com/goharbor/harbor/src/core/api/models"
|
apimodels "github.com/goharbor/harbor/src/core/api/models"
|
||||||
|
quota "github.com/goharbor/harbor/src/core/api/quota"
|
||||||
_ "github.com/goharbor/harbor/src/core/auth/db"
|
_ "github.com/goharbor/harbor/src/core/auth/db"
|
||||||
_ "github.com/goharbor/harbor/src/core/auth/ldap"
|
_ "github.com/goharbor/harbor/src/core/auth/ldap"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
@ -203,12 +204,17 @@ func init() {
|
|||||||
beego.Router("/api/quotas/:id([0-9]+)", quotaAPIType, "get:Get;put:Put")
|
beego.Router("/api/quotas/:id([0-9]+)", quotaAPIType, "get:Get;put:Put")
|
||||||
|
|
||||||
beego.Router("/api/internal/switchquota", &InternalAPI{}, "put:SwitchQuota")
|
beego.Router("/api/internal/switchquota", &InternalAPI{}, "put:SwitchQuota")
|
||||||
|
beego.Router("/api/internal/syncquota", &InternalAPI{}, "post:SyncQuota")
|
||||||
|
|
||||||
// syncRegistry
|
// syncRegistry
|
||||||
if err := SyncRegistry(config.GlobalProjectMgr); err != nil {
|
if err := SyncRegistry(config.GlobalProjectMgr); err != nil {
|
||||||
log.Fatalf("failed to sync repositories from registry: %v", err)
|
log.Fatalf("failed to sync repositories from registry: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := quota.Sync(config.GlobalProjectMgr, false); err != nil {
|
||||||
|
log.Fatalf("failed to sync quota from backend: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Init user Info
|
// Init user Info
|
||||||
admin = &usrInfo{adminName, adminPwd}
|
admin = &usrInfo{adminName, adminPwd}
|
||||||
unknownUsr = &usrInfo{"unknown", "unknown"}
|
unknownUsr = &usrInfo{"unknown", "unknown"}
|
||||||
|
@ -28,6 +28,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
quota "github.com/goharbor/harbor/src/core/api/quota"
|
quota "github.com/goharbor/harbor/src/core/api/quota"
|
||||||
|
|
||||||
|
comcfg "github.com/goharbor/harbor/src/common/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InternalAPI handles request of harbor admin...
|
// InternalAPI handles request of harbor admin...
|
||||||
@ -154,9 +156,25 @@ func (ia *InternalAPI) ensureQuota() error {
|
|||||||
|
|
||||||
// SyncQuota ...
|
// SyncQuota ...
|
||||||
func (ia *InternalAPI) SyncQuota() {
|
func (ia *InternalAPI) SyncQuota() {
|
||||||
|
cur := config.ReadOnly()
|
||||||
|
cfgMgr := comcfg.NewDBCfgManager()
|
||||||
|
if cur != true {
|
||||||
|
cfgMgr.Set(common.ReadOnly, true)
|
||||||
|
}
|
||||||
|
// For api call, to avoid the timeout, it should be asynchronous
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if cur != true {
|
||||||
|
cfgMgr.Set(common.ReadOnly, false)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
log.Info("start to sync quota(API), the system will be set to ReadOnly and back it normal once it done.")
|
||||||
err := quota.Sync(ia.ProjectMgr, false)
|
err := quota.Sync(ia.ProjectMgr, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ia.SendInternalServerError(err)
|
log.Errorf("fail to sync quota(API), but with error: %v, please try to do it again.", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Info("success to sync quota(API).")
|
||||||
|
}()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
@ -54,3 +54,36 @@ func TestSwitchQuota(t *testing.T) {
|
|||||||
}
|
}
|
||||||
runCodeCheckingCases(t, cases...)
|
runCodeCheckingCases(t, cases...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cannot verify the real scenario here
|
||||||
|
func TestSyncQuota(t *testing.T) {
|
||||||
|
cases := []*codeCheckingCase{
|
||||||
|
// 401
|
||||||
|
{
|
||||||
|
request: &testingRequest{
|
||||||
|
method: http.MethodPost,
|
||||||
|
url: "/api/internal/syncquota",
|
||||||
|
},
|
||||||
|
code: http.StatusUnauthorized,
|
||||||
|
},
|
||||||
|
// 200
|
||||||
|
{
|
||||||
|
request: &testingRequest{
|
||||||
|
method: http.MethodPost,
|
||||||
|
url: "/api/internal/syncquota",
|
||||||
|
credential: sysAdmin,
|
||||||
|
},
|
||||||
|
code: http.StatusOK,
|
||||||
|
},
|
||||||
|
// 403
|
||||||
|
{
|
||||||
|
request: &testingRequest{
|
||||||
|
url: "/api/internal/syncquota",
|
||||||
|
method: http.MethodPost,
|
||||||
|
credential: nonSysAdmin,
|
||||||
|
},
|
||||||
|
code: http.StatusForbidden,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
runCodeCheckingCases(t, cases...)
|
||||||
|
}
|
||||||
|
@ -103,19 +103,22 @@ func (rm *Migrator) Dump() ([]quota.ProjectInfo, error) {
|
|||||||
|
|
||||||
ctr, err := chartController()
|
ctr, err := chartController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
errChan <- err
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chartInfo, err := ctr.ListCharts(project.Name)
|
chartInfo, err := ctr.ListCharts(project.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
errChan <- err
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// repo
|
// repo
|
||||||
for _, chart := range chartInfo {
|
for _, chart := range chartInfo {
|
||||||
chartVersions, err := ctr.GetChart(project.Name, chart.Name)
|
chartVersions, err := ctr.GetChart(project.Name, chart.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
errChan <- err
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
for _, chart := range chartVersions {
|
for _, chart := range chartVersions {
|
||||||
af := &models.Artifact{
|
af := &models.Artifact{
|
||||||
@ -143,7 +146,7 @@ func (rm *Migrator) Dump() ([]quota.ProjectInfo, error) {
|
|||||||
}(project)
|
}(project)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Done()
|
wg.Wait()
|
||||||
close(infoChan)
|
close(infoChan)
|
||||||
|
|
||||||
<-done
|
<-done
|
||||||
|
@ -12,10 +12,9 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package models
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/quota"
|
"github.com/goharbor/harbor/src/common/quota"
|
||||||
@ -75,23 +74,21 @@ func Register(name string, adapter Instance) {
|
|||||||
|
|
||||||
// Sync ...
|
// Sync ...
|
||||||
func Sync(pm promgr.ProjectManager, populate bool) error {
|
func Sync(pm promgr.ProjectManager, populate bool) error {
|
||||||
for name := range adapters {
|
for name, instanceFunc := range adapters {
|
||||||
if !config.WithChartMuseum() {
|
if !config.WithChartMuseum() {
|
||||||
if name == "chart" {
|
if name == "chart" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
instanceFunc, ok := adapters[name]
|
|
||||||
if !ok {
|
|
||||||
err := fmt.Errorf("quota migtator: unknown adapter name %q", name)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
adapter := instanceFunc(pm)
|
adapter := instanceFunc(pm)
|
||||||
data, err := adapter.Dump()
|
data, err := adapter.Dump()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
usage, err := adapter.Usage(data)
|
usage, err := adapter.Usage(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := ensureQuota(usage); err != nil {
|
if err := ensureQuota(usage); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -383,6 +383,14 @@ func infoOfRepo(pid int64, repo string) (quota.RepoData, error) {
|
|||||||
DigestBlob: desc.Digest.String(),
|
DigestBlob: desc.Digest.String(),
|
||||||
}
|
}
|
||||||
afnbs = append(afnbs, afnb)
|
afnbs = append(afnbs, afnb)
|
||||||
|
// add manifest as a blob.
|
||||||
|
blob := &models.Blob{
|
||||||
|
Digest: desc.Digest.String(),
|
||||||
|
ContentType: desc.MediaType,
|
||||||
|
Size: desc.Size,
|
||||||
|
CreationTime: time.Now(),
|
||||||
|
}
|
||||||
|
blobs = append(blobs, blob)
|
||||||
for _, layer := range manifest.References() {
|
for _, layer := range manifest.References() {
|
||||||
afnb := &models.ArtifactAndBlob{
|
afnb := &models.ArtifactAndBlob{
|
||||||
DigestAF: desc.Digest.String(),
|
DigestAF: desc.Digest.String(),
|
||||||
|
@ -17,16 +17,12 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"strconv"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
_ "github.com/astaxie/beego/session/redis"
|
_ "github.com/astaxie/beego/session/redis"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/job"
|
"github.com/goharbor/harbor/src/common/job"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
|
common_quota "github.com/goharbor/harbor/src/common/quota"
|
||||||
"github.com/goharbor/harbor/src/common/utils"
|
"github.com/goharbor/harbor/src/common/utils"
|
||||||
"github.com/goharbor/harbor/src/common/utils/log"
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
"github.com/goharbor/harbor/src/core/api"
|
"github.com/goharbor/harbor/src/core/api"
|
||||||
@ -34,6 +30,10 @@ import (
|
|||||||
_ "github.com/goharbor/harbor/src/core/auth/db"
|
_ "github.com/goharbor/harbor/src/core/auth/db"
|
||||||
_ "github.com/goharbor/harbor/src/core/auth/ldap"
|
_ "github.com/goharbor/harbor/src/core/auth/ldap"
|
||||||
_ "github.com/goharbor/harbor/src/core/auth/uaa"
|
_ "github.com/goharbor/harbor/src/core/auth/uaa"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
quota "github.com/goharbor/harbor/src/core/api/quota"
|
quota "github.com/goharbor/harbor/src/core/api/quota"
|
||||||
_ "github.com/goharbor/harbor/src/core/api/quota/chart"
|
_ "github.com/goharbor/harbor/src/core/api/quota/chart"
|
||||||
@ -46,6 +46,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/core/service/token"
|
"github.com/goharbor/harbor/src/core/service/token"
|
||||||
"github.com/goharbor/harbor/src/pkg/notification"
|
"github.com/goharbor/harbor/src/pkg/notification"
|
||||||
"github.com/goharbor/harbor/src/pkg/scheduler"
|
"github.com/goharbor/harbor/src/pkg/scheduler"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/types"
|
||||||
"github.com/goharbor/harbor/src/replication"
|
"github.com/goharbor/harbor/src/replication"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -92,16 +93,48 @@ func quotaSync() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// upgrade from old version
|
// The condition handles these two cases:
|
||||||
if len(projects) > 1 && len(usages) == 1 {
|
// 1, len(project) > 1 && len(usages) == 1. existing projects without usage, as we do always has 'library' usage in DB.
|
||||||
|
// 2, migration fails at the phase of inserting usage into DB, and parts of them are inserted successfully.
|
||||||
|
if len(projects) != len(usages) {
|
||||||
log.Info("Start to sync quota data .....")
|
log.Info("Start to sync quota data .....")
|
||||||
if err := quota.Sync(config.GlobalProjectMgr, true); err != nil {
|
if err := quota.Sync(config.GlobalProjectMgr, true); err != nil {
|
||||||
log.Errorf("Error happened when syncing quota usage data, %v", err)
|
log.Errorf("Fail to sync quota data, %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Info("Success to sync quota data .....")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only has one project without usage
|
||||||
|
zero := common_quota.ResourceList{
|
||||||
|
common_quota.ResourceCount: 0,
|
||||||
|
common_quota.ResourceStorage: 0,
|
||||||
|
}
|
||||||
|
if len(projects) == 1 && len(usages) == 1 {
|
||||||
|
totalRepo, err := dao.GetTotalOfRepositories()
|
||||||
|
if totalRepo == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
refID, err := strconv.ParseInt(usages[0].ReferenceID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
usedRes, err := types.NewResourceList(usages[0].Used)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if types.Equals(usedRes, zero) && refID == projects[0].ProjectID {
|
||||||
|
log.Info("Start to sync quota data .....")
|
||||||
|
if err := quota.Sync(config.GlobalProjectMgr, true); err != nil {
|
||||||
|
log.Errorf("Fail to sync quota data, %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info("Success to sync quota data .....")
|
log.Info("Success to sync quota data .....")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user