mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
fix(keystone): project clean (#18742)
This commit is contained in:
@@ -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"`
|
||||
|
||||
@@ -49,3 +49,6 @@ type ProjectDetails struct {
|
||||
|
||||
Organization *SProjectOrganization
|
||||
}
|
||||
|
||||
type ProjectCleanInput struct {
|
||||
}
|
||||
|
||||
@@ -282,6 +282,7 @@ type IStandaloneModel interface {
|
||||
// GetExternalId() string
|
||||
|
||||
SetName(name string)
|
||||
MarkPendingDeleted()
|
||||
|
||||
StandaloneModelManager() IStandaloneModelManager
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user