mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
feature: allow delete none-default quota
fix: cannot set a quota to zero
This commit is contained in:
@@ -42,63 +42,62 @@ const (
|
||||
QUOTA_ACTION_SUB = "sub"
|
||||
QUOTA_ACTION_RESET = "reset"
|
||||
QUOTA_ACTION_REPLACE = "replace"
|
||||
QUOTA_ACTION_UPDATE = "update"
|
||||
QUOTA_ACTION_DELETE = "delete"
|
||||
)
|
||||
|
||||
func AddQuotaHandler(manager *SQuotaBaseManager, prefix string, app *appsrv.Application) {
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.getQuotaHanlder), nil, "get_quota", nil)
|
||||
auth.Authenticate(manager.getQuotaHandler), nil, "get_quota", nil)
|
||||
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/<tenantid>", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.getQuotaHanlder), nil, "get_quota_for_project", nil)
|
||||
auth.Authenticate(manager.getQuotaHandler), nil, "get_quota_for_project", nil)
|
||||
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/domains", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.listDomainQuotaHanlder), nil, "list_quotas_for_all_domains", nil)
|
||||
auth.Authenticate(manager.listDomainQuotaHandler), nil, "list_quotas_for_all_domains", nil)
|
||||
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/domains/<domainid>", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.getQuotaHanlder), nil, "get_quota_for_domain", nil)
|
||||
auth.Authenticate(manager.getQuotaHandler), nil, "get_quota_for_domain", nil)
|
||||
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/projects", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.listProjectQuotaHanlder), nil, "list_quotas_for_all_projects", nil)
|
||||
auth.Authenticate(manager.listProjectQuotaHandler), nil, "list_quotas_for_all_projects", nil)
|
||||
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/projects/<tenantid>", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.getQuotaHanlder), nil, "get_quota_for_project", nil)
|
||||
auth.Authenticate(manager.getQuotaHandler), nil, "get_quota_for_project", nil)
|
||||
|
||||
app.AddHandler2("POST",
|
||||
fmt.Sprintf("%s/%s", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.setQuotaHanlder), nil, "set_quota", nil)
|
||||
auth.Authenticate(manager.setQuotaHandler), nil, "set_quota", nil)
|
||||
|
||||
app.AddHandler2("POST",
|
||||
fmt.Sprintf("%s/%s/<tenantid>", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.setQuotaHanlder), nil, "set_quota_for_project", nil)
|
||||
auth.Authenticate(manager.setQuotaHandler), nil, "set_quota_for_project", nil)
|
||||
|
||||
app.AddHandler2("POST",
|
||||
fmt.Sprintf("%s/%s/domains/<domainid>", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.setQuotaHanlder), nil, "set_quota_for_domain", nil)
|
||||
auth.Authenticate(manager.setQuotaHandler), nil, "set_quota_for_domain", nil)
|
||||
|
||||
app.AddHandler2("POST",
|
||||
fmt.Sprintf("%s/%s/projects/<tenantid>", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.setQuotaHanlder), nil, "set_quota_for_project", nil)
|
||||
auth.Authenticate(manager.setQuotaHandler), nil, "set_quota_for_project", nil)
|
||||
|
||||
app.AddHandler2("DELETE",
|
||||
fmt.Sprintf("%s/%s", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.cleanPendingUsageHanlder), nil, "clean_pending_usage", nil)
|
||||
fmt.Sprintf("%s/%s/pending", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.cleanPendingUsageHandler), nil, "clean_pending_usage", nil)
|
||||
|
||||
app.AddHandler2("DELETE",
|
||||
fmt.Sprintf("%s/%s/domains/<domainid>", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.cleanPendingUsageHanlder), nil, "clean_pending_usage_for_domain", nil)
|
||||
fmt.Sprintf("%s/%s/domains/<domainid>/pending", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.cleanPendingUsageHandler), nil, "clean_pending_usage_for_domain", nil)
|
||||
|
||||
app.AddHandler2("DELETE",
|
||||
fmt.Sprintf("%s/%s/projects/<tenantid>", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.cleanPendingUsageHanlder), nil, "clean_pending_usage_for_project", nil)
|
||||
/*app.AddHandler2("POST",
|
||||
fmt.Sprintf("%s/%s/<tenantid>/<action>", prefix, _manager.Keyword()),
|
||||
auth.Authenticate(checkQuotaHanlder), nil, "check_quota", nil)*/
|
||||
fmt.Sprintf("%s/%s/projects/<tenantid>/pending", prefix, manager.KeywordPlural()),
|
||||
auth.Authenticate(manager.cleanPendingUsageHandler), nil, "clean_pending_usage_for_project", nil)
|
||||
}
|
||||
|
||||
func (manager *SQuotaBaseManager) queryQuota(ctx context.Context, quota IQuota, refresh bool) (*jsonutils.JSONDict, error) {
|
||||
@@ -139,7 +138,7 @@ func (manager *SQuotaBaseManager) queryQuota(ctx context.Context, quota IQuota,
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (manager *SQuotaBaseManager) getQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
func (manager *SQuotaBaseManager) getQuotaHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
params, query, _ := appsrv.FetchEnv(ctx, w, r)
|
||||
userCred := auth.FetchUserCredential(ctx, policy.FilterPolicyCredential)
|
||||
|
||||
@@ -196,14 +195,23 @@ func (manager *SQuotaBaseManager) getQuotaHanlder(ctx context.Context, w http.Re
|
||||
manager.sendQuotaList(w, quotaList)
|
||||
}
|
||||
|
||||
func (manager *SQuotaBaseManager) fetchSetQuotaScope(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject, isBaseQuotaKeys bool) (mcclient.IIdentityProvider, rbacutils.TRbacScope, rbacutils.TRbacScope, error) {
|
||||
func (manager *SQuotaBaseManager) fetchSetQuotaScope(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
data jsonutils.JSONObject,
|
||||
isBaseQuotaKeys bool,
|
||||
) (
|
||||
mcclient.IIdentityProvider,
|
||||
rbacutils.TRbacScope,
|
||||
rbacutils.TRbacScope,
|
||||
error,
|
||||
) {
|
||||
var scope rbacutils.TRbacScope
|
||||
ownerId, err := db.FetchProjectInfo(ctx, data)
|
||||
if err != nil {
|
||||
return nil, scope, scope, err
|
||||
}
|
||||
var requestScope rbacutils.TRbacScope
|
||||
ownerScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionUpdate)
|
||||
if ownerId != nil {
|
||||
if len(ownerId.GetProjectId()) > 0 {
|
||||
// project level
|
||||
@@ -235,13 +243,11 @@ func (manager *SQuotaBaseManager) fetchSetQuotaScope(ctx context.Context, userCr
|
||||
requestScope = rbacutils.ScopeProject
|
||||
}
|
||||
}
|
||||
if requestScope.HigherThan(ownerScope) {
|
||||
return nil, scope, scope, httperrors.NewForbiddenError("not enough privilleges")
|
||||
}
|
||||
return ownerId, scope, ownerScope, nil
|
||||
|
||||
return ownerId, scope, requestScope, nil
|
||||
}
|
||||
|
||||
func (manager *SQuotaBaseManager) cleanPendingUsageHanlder(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
func (manager *SQuotaBaseManager) cleanPendingUsageHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
params, query, _ := appsrv.FetchEnv(ctx, w, r)
|
||||
userCred := auth.FetchUserCredential(ctx, policy.FilterPolicyCredential)
|
||||
|
||||
@@ -284,7 +290,7 @@ func (manager *SQuotaBaseManager) cleanPendingUsageHanlder(ctx context.Context,
|
||||
appsrv.SendJSON(w, rbody)
|
||||
}
|
||||
|
||||
func (manager *SQuotaBaseManager) setQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
func (manager *SQuotaBaseManager) setQuotaHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
params, _, body := appsrv.FetchEnv(ctx, w, r)
|
||||
userCred := auth.FetchUserCredential(ctx, policy.FilterPolicyCredential)
|
||||
|
||||
@@ -305,45 +311,96 @@ func (manager *SQuotaBaseManager) setQuotaHanlder(ctx context.Context, w http.Re
|
||||
return
|
||||
}
|
||||
|
||||
ownerId, scope, _, err := manager.fetchSetQuotaScope(ctx, userCred, data, IsBaseQuotaKeys(quota.GetKeys()))
|
||||
// check is there any nonempty key other than domain_id and project_id
|
||||
isBaseQuota := IsBaseQuotaKeys(quota.GetKeys())
|
||||
ownerId, scope, requestScope, err := manager.fetchSetQuotaScope(ctx, userCred, data, isBaseQuota)
|
||||
if err != nil {
|
||||
httperrors.GeneralServerError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// fill project_id and domain_id
|
||||
baseKeys := OwnerIdQuotaKeys(scope, ownerId)
|
||||
reflectutils.FillEmbededStructValue(reflect.Indirect(reflect.ValueOf(quota)), reflect.ValueOf(baseKeys))
|
||||
|
||||
keys := quota.GetKeys()
|
||||
isNew := false
|
||||
oquota := manager.newQuota()
|
||||
err = manager.GetQuota(ctx, quota.GetKeys(), oquota)
|
||||
oquota.SetKeys(keys)
|
||||
err = manager.getQuotaByKeys(ctx, keys, oquota)
|
||||
if err != nil {
|
||||
log.Errorf("get quota fail %s", err)
|
||||
httperrors.GeneralServerError(w, err)
|
||||
return
|
||||
if errors.Cause(err) != sql.ErrNoRows {
|
||||
log.Errorf("get quota %s fail %s", QuotaKeyString(keys), err)
|
||||
httperrors.GeneralServerError(w, err)
|
||||
return
|
||||
} else {
|
||||
isNew = true
|
||||
}
|
||||
}
|
||||
|
||||
action, _ := body.GetString(manager.KeywordPlural(), "action")
|
||||
switch action {
|
||||
case QUOTA_ACTION_ADD:
|
||||
oquota.Add(quota)
|
||||
case QUOTA_ACTION_SUB:
|
||||
oquota.Sub(quota)
|
||||
case QUOTA_ACTION_RESET:
|
||||
oquota.FetchSystemQuota()
|
||||
case QUOTA_ACTION_REPLACE:
|
||||
oquota = quota
|
||||
default:
|
||||
oquota.Update(quota)
|
||||
var policyAction string
|
||||
if action == QUOTA_ACTION_DELETE {
|
||||
if isNew {
|
||||
// no need to delete
|
||||
httperrors.NotFoundError(w, "Quota %s not found", QuotaKeyString(keys))
|
||||
return
|
||||
} else if isBaseQuota {
|
||||
// base quota is not deletable
|
||||
httperrors.ForbiddenError(w, "Default quota %s not allow to delete", QuotaKeyString(keys))
|
||||
return
|
||||
}
|
||||
policyAction = policy.PolicyActionDelete
|
||||
} else {
|
||||
if isNew {
|
||||
policyAction = policy.PolicyActionCreate
|
||||
} else {
|
||||
policyAction = policy.PolicyActionUpdate
|
||||
}
|
||||
}
|
||||
|
||||
err = manager.SetQuota(ctx, userCred, oquota)
|
||||
if err != nil {
|
||||
log.Errorf("set quota fail %s", err)
|
||||
httperrors.GeneralServerError(w, err)
|
||||
log.Debugf("is_new: %v action: %s origin: %s current: %s", isNew, action, jsonutils.Marshal(oquota), jsonutils.Marshal(quota))
|
||||
|
||||
// check rbac policy
|
||||
ownerScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), manager.KeywordPlural(), policyAction)
|
||||
if requestScope.HigherThan(ownerScope) {
|
||||
httperrors.ForbiddenError(w, "not enough privilleges")
|
||||
return
|
||||
}
|
||||
|
||||
keys := OwnerIdQuotaKeys(scope, ownerId)
|
||||
quotaList, err := manager.listQuotas(ctx, userCred, keys.DomainId, keys.ProjectId, scope == rbacutils.ScopeDomain, true)
|
||||
if action == QUOTA_ACTION_DELETE {
|
||||
err = manager.DeleteQuota(ctx, userCred, keys)
|
||||
if err != nil {
|
||||
httperrors.GeneralServerError(w, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
switch action {
|
||||
case QUOTA_ACTION_ADD:
|
||||
oquota.Add(quota)
|
||||
case QUOTA_ACTION_SUB:
|
||||
oquota.Sub(quota)
|
||||
case QUOTA_ACTION_RESET:
|
||||
oquota.FetchSystemQuota()
|
||||
case QUOTA_ACTION_REPLACE:
|
||||
oquota = quota
|
||||
case QUOTA_ACTION_UPDATE:
|
||||
fallthrough
|
||||
default:
|
||||
oquota.Update(quota)
|
||||
}
|
||||
|
||||
log.Debugf("To set %s", jsonutils.Marshal(oquota))
|
||||
|
||||
err = manager.SetQuota(ctx, userCred, oquota)
|
||||
if err != nil {
|
||||
log.Errorf("set quota fail %s", err)
|
||||
httperrors.GeneralServerError(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
quotaList, err := manager.listQuotas(ctx, userCred, baseKeys.DomainId, baseKeys.ProjectId, scope == rbacutils.ScopeDomain, true)
|
||||
if err != nil {
|
||||
httperrors.GeneralServerError(w, err)
|
||||
return
|
||||
@@ -351,7 +408,7 @@ func (manager *SQuotaBaseManager) setQuotaHanlder(ctx context.Context, w http.Re
|
||||
manager.sendQuotaList(w, quotaList)
|
||||
}
|
||||
|
||||
func (manager *SQuotaBaseManager) listDomainQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
func (manager *SQuotaBaseManager) listDomainQuotaHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
_, query, _ := appsrv.FetchEnv(ctx, w, r)
|
||||
userCred := auth.FetchUserCredential(ctx, policy.FilterPolicyCredential)
|
||||
|
||||
@@ -383,7 +440,7 @@ func (manager *SQuotaBaseManager) sendQuotaList(w http.ResponseWriter, quotaList
|
||||
appsrv.SendJSON(w, rbody)
|
||||
}
|
||||
|
||||
func (manager *SQuotaBaseManager) listProjectQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
func (manager *SQuotaBaseManager) listProjectQuotaHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
userCred := auth.FetchUserCredential(ctx, policy.FilterPolicyCredential)
|
||||
_, query, _ := appsrv.FetchEnv(ctx, w, r)
|
||||
owner, err := db.FetchDomainInfo(ctx, query)
|
||||
|
||||
@@ -84,14 +84,12 @@ func (manager *SQuotaBaseManager) getQuotaByKeys(ctx context.Context, keys IQuot
|
||||
}
|
||||
|
||||
err := q.First(quota)
|
||||
if err != nil {
|
||||
if errors.Cause(err) != sql.ErrNoRows {
|
||||
return errors.Wrap(err, "q.First")
|
||||
}
|
||||
}
|
||||
if manager.nonNegative {
|
||||
quota.ResetNegative()
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "q.Query")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ package quotas
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -150,7 +151,17 @@ func (manager *SQuotaBaseManager) GetPendingUsages(ctx context.Context, keys IQu
|
||||
}
|
||||
|
||||
func (manager *SQuotaBaseManager) GetQuota(ctx context.Context, keys IQuotaKeys, quota IQuota) error {
|
||||
return manager.getQuotaByKeys(ctx, keys, quota)
|
||||
LockQuotaKeys(ctx, manager, keys)
|
||||
defer ReleaseQuotaKeys(ctx, manager, keys)
|
||||
|
||||
err := manager.getQuotaByKeys(ctx, keys, quota)
|
||||
if err != nil {
|
||||
if errors.Cause(err) != sql.ErrNoRows {
|
||||
return errors.Wrap(err, "manager.getQuotaByKeys")
|
||||
}
|
||||
// else, ignore the sql.ErrNoRows error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (manager *SQuotaBaseManager) GetChildrenQuotas(ctx context.Context, keys IQuotaKeys) ([]IQuota, error) {
|
||||
|
||||
Reference in New Issue
Block a user