fix(keystone): project clean (#18742)

This commit is contained in:
屈轩
2023-11-21 21:10:23 +08:00
committed by GitHub
parent ffb8ff3df8
commit 7504c2eb5b
10 changed files with 142 additions and 3 deletions
+1
View File
@@ -40,6 +40,7 @@ func init() {
cmd.GetProperty(&identity_options.ProjectGetPropertyTagValueTreeOptions{})
cmd.GetProperty(&identity_options.ProjectGetPropertyDomainTagValuePairOptions{})
cmd.GetProperty(&identity_options.ProjectGetPropertyDomainTagValueTreeOptions{})
cmd.PerformClass("clean", &identity_options.ProjectCleanOptions{})
type ProjectShowOptions struct {
ID string `help:"ID or Name of project"`
+3
View File
@@ -49,3 +49,6 @@ type ProjectDetails struct {
Organization *SProjectOrganization
}
type ProjectCleanInput struct {
}
+1
View File
@@ -282,6 +282,7 @@ type IStandaloneModel interface {
// GetExternalId() string
SetName(name string)
MarkPendingDeleted()
StandaloneModelManager() IStandaloneModelManager
+4
View File
@@ -686,6 +686,10 @@ func (model *SModelBase) MarkDelete() error {
return nil
}
func (model *SModelBase) MarkPendingDeleted() {
return
}
func (model *SModelBase) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
return nil
}
+6 -2
View File
@@ -48,6 +48,11 @@ func (base *SPendingDeletedBase) GetPendingDeleted() bool {
return base.PendingDeleted
}
func (base *SPendingDeletedBase) MarkPendingDeleted() {
base.PendingDeleted = true
base.PendingDeletedAt = timeutils.UtcNow()
}
// GetPendingDeletedAt implements IPendingDeltable
func (base *SPendingDeletedBase) GetPendingDeletedAt() time.Time {
return base.PendingDeletedAt
@@ -85,8 +90,7 @@ func (base *SPendingDeletedBase) MarkPendingDelete(model IStandaloneModel, ctx c
if len(newName) > 0 {
model.SetName(newName)
}
base.PendingDeleted = true
base.PendingDeletedAt = timeutils.UtcNow()
model.MarkPendingDeleted()
return nil
})
if err != nil {
+5 -1
View File
@@ -17,6 +17,7 @@ package models
import (
"context"
"fmt"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
@@ -425,7 +426,10 @@ func (manager *SIdentityBaseResourceManager) GetPropertyDomainTagValueTree(
func (model *SIdentityBaseResource) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
if !model.PendingDeleted {
newName := fmt.Sprintf("%s-deleted-%s", model.Name, timeutils.ShortDate(timeutils.UtcNow()))
newName := model.Name
if !strings.Contains(model.Name, "-deleted-") {
newName = fmt.Sprintf("%s-deleted-%s", model.Name, timeutils.ShortDate(timeutils.UtcNow()))
}
err := model.SPendingDeletedBase.MarkPendingDelete(model.GetIStandaloneModel(), ctx, userCred, newName)
if err != nil {
return errors.Wrap(err, "MarkPendingDelete")
+51
View File
@@ -33,6 +33,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/keystone/options"
"yunion.io/x/onecloud/pkg/mcclient"
@@ -901,3 +902,53 @@ func (manager *SProjectManager) FilterByOwner(q *sqlchemy.SQuery, man db.FilterB
}
return manager.SIdentityBaseResourceManager.FilterByOwner(q, man, userCred, owner, scope)
}
func (manager *SProjectManager) GetSystemProject() (*SProject, error) {
q := manager.Query().Equals("name", api.SystemAdminProject)
ret := &SProject{}
ret.SetModelManager(manager, ret)
err := q.First(ret)
if err != nil {
return nil, err
}
return ret, nil
}
func (self *SProject) StartProjectCleanTask(ctx context.Context, userCred mcclient.TokenCredential) error {
task, err := taskman.TaskManager.NewTask(ctx, "ProjectCleanTask", self, userCred, nil, "", "", nil)
if err != nil {
return err
}
return task.ScheduleRun(nil)
}
func (self *SProject) GetEmptyProjects() ([]SProject, error) {
q := ProjectManager.Query().IsFalse("pending_deleted").NotEquals("name", api.SystemAdminProject)
scopes := []SScopeResource{}
ScopeResourceManager.Query().GT("count", 0).All(&scopes)
ids := []string{}
for _, scope := range scopes {
ids = append(ids, scope.ProjectId)
}
projects := []SProject{}
if len(ids) == 0 {
return projects, nil
}
q = q.Filter(sqlchemy.NotIn(q.Field("id"), ids))
err := db.FetchModelObjects(ProjectManager, q, &projects)
if err != nil {
return nil, err
}
return projects, nil
}
func (manager *SProjectManager) PerformClean(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ProjectCleanInput) (jsonutils.JSONObject, error) {
if !userCred.HasSystemAdminPrivilege() {
return nil, httperrors.NewForbiddenError("not allow clean projects")
}
system, err := manager.GetSystemProject()
if err != nil {
return nil, err
}
return nil, system.StartProjectCleanTask(ctx, userCred)
}
+62
View File
@@ -0,0 +1,62 @@
// Copyright 2019 Yunion
//
// 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 tasks
import (
"context"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/keystone/models"
"yunion.io/x/onecloud/pkg/util/logclient"
)
type ProjectCleanTask struct {
taskman.STask
}
func init() {
taskman.RegisterTask(ProjectCleanTask{})
}
func (task *ProjectCleanTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
project := obj.(*models.SProject)
empty, err := project.GetEmptyProjects()
if err != nil {
logclient.AddActionLogWithStartable(task, project, logclient.ACT_CLEAN_PROJECT, errors.Wrapf(err, "GetEmptyProjects"), task.UserCred, false)
task.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
return
}
cnt, failed := 0, []error{}
for i := range empty {
lockman.LockObject(ctx, &empty[i])
defer lockman.ReleaseObject(ctx, &empty[i])
err = empty[i].Delete(ctx, task.UserCred)
if err != nil {
failed = append(failed, err)
continue
}
cnt++
}
logclient.AddActionLogWithStartable(task, project, logclient.ACT_CLEAN_PROJECT, map[string]interface{}{"clean": cnt, "failed": errors.NewAggregate(failed).Error()}, task.UserCred, true)
task.SetStageComplete(ctx, nil)
}
+7
View File
@@ -113,3 +113,10 @@ type ProjectSetAdminOptions struct {
func (opts *ProjectSetAdminOptions) Params() (jsonutils.JSONObject, error) {
return jsonutils.Marshal(opts), nil
}
type ProjectCleanOptions struct {
}
func (opts *ProjectCleanOptions) Params() (jsonutils.JSONObject, error) {
return jsonutils.Marshal(opts), nil
}
+2
View File
@@ -274,4 +274,6 @@ const (
ACT_CREATE_SECURITY_GROUP_RULE = "create_security_group_rule"
ACT_DELETE_SECURITY_GROUP_RULE = "delete_security_group_rule"
ACT_CLEAN_PROJECT = "clean_project"
)