mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-07-01 02:24:47 +08:00
fix: clone dashboard parameters (#20589)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
@@ -15,10 +15,15 @@
|
||||
package yunionconf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/printutils"
|
||||
"yunion.io/x/pkg/util/shellutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/yunionconf"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/identity"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/yunionconf"
|
||||
@@ -241,4 +246,100 @@ func init() {
|
||||
printObject(parameter)
|
||||
return nil
|
||||
})
|
||||
|
||||
type ParameterCloneOptions struct {
|
||||
User string `help:"Clone parameter of specificated user id"`
|
||||
Service string `help:"Clone parameter of specificated service id"`
|
||||
NAME string `help:"The name of parameter"`
|
||||
DestUser string `help:"destination user id of clone action"`
|
||||
DestService string `help:"destination service id of clone action"`
|
||||
DestName string `help:"destination parameter name of clone action, may be empty"`
|
||||
}
|
||||
R(&ParameterCloneOptions{}, "parameter-clone", "clone parameter", func(s *mcclient.ClientSession, args *ParameterCloneOptions) error {
|
||||
input := api.ParameterCloneInput{}
|
||||
|
||||
input.DestName = args.DestName
|
||||
|
||||
if len(args.DestUser) > 0 {
|
||||
input.DestNs = api.NAMESPACE_USER
|
||||
input.DestNsId = args.DestUser
|
||||
} else if len(args.DestService) > 0 {
|
||||
input.DestNs = api.NAMESPACE_SERVICE
|
||||
input.DestNsId = args.DestService
|
||||
} else {
|
||||
return errors.Wrap(httperrors.ErrMissingParameter, "either dest_user or dest_service must be specified")
|
||||
}
|
||||
params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
|
||||
|
||||
var parameter jsonutils.JSONObject
|
||||
var err error
|
||||
if len(args.User) > 0 {
|
||||
parameter, err = yunionconf.Parameters.PerformActionInContext(s, args.NAME, "clone", params, &identity.UsersV3, args.User)
|
||||
} else if len(args.Service) > 0 {
|
||||
parameter, err = yunionconf.Parameters.PerformActionInContext(s, args.NAME, "clone", params, &identity.ServicesV3, args.Service)
|
||||
} else {
|
||||
parameter, err = yunionconf.Parameters.PerformAction(s, args.NAME, "clone", params)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(parameter)
|
||||
return nil
|
||||
})
|
||||
|
||||
type ParameterCloneDashboardOptions struct {
|
||||
SCOPE string `help:"dashboard scope" choices:"system|domain|project"`
|
||||
SRC string `help:"source user id"`
|
||||
DST string `help:"destination user id"`
|
||||
}
|
||||
R(&ParameterCloneDashboardOptions{}, "parameter-clone-dashboard", "clone dashboard parameter", func(s *mcclient.ClientSession, args *ParameterCloneDashboardOptions) error {
|
||||
cloneUserParams := func(srcUid, destUid, name string) (jsonutils.JSONObject, error) {
|
||||
paramId, err := yunionconf.Parameters.GetIdInContext(s, name, nil, &identity.UsersV3, srcUid)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetByName %s", name)
|
||||
}
|
||||
input := api.ParameterCloneInput{
|
||||
DestNs: api.NAMESPACE_USER,
|
||||
DestNsId: destUid,
|
||||
}
|
||||
params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
|
||||
return yunionconf.Parameters.PerformActionInContext(s, paramId, "clone", params, &identity.UsersV3, srcUid)
|
||||
}
|
||||
|
||||
rootName := fmt.Sprintf("dashboard_%s", args.SCOPE)
|
||||
|
||||
confs := []struct {
|
||||
Id string
|
||||
Name string
|
||||
}{}
|
||||
|
||||
paramObj, err := yunionconf.Parameters.GetInContext(s, rootName, nil, &identity.UsersV3, args.SRC)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetParameter")
|
||||
}
|
||||
|
||||
err = paramObj.Unmarshal(&confs, "value")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unmarshal value")
|
||||
}
|
||||
|
||||
for _, conf := range confs {
|
||||
_, err := cloneUserParams(args.SRC, args.DST, conf.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "clone %s", conf.Id)
|
||||
}
|
||||
fmt.Println("cloned", conf.Id)
|
||||
}
|
||||
|
||||
// finally copy the root
|
||||
_, err = cloneUserParams(args.SRC, args.DST, rootName)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "clone %s", rootName)
|
||||
}
|
||||
fmt.Println("cloned", rootName)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -24,3 +24,9 @@ const (
|
||||
ANY_DOMAIN_ID = "[any_domain_id]"
|
||||
ANY_PROJECT_ID = "[any_project_id]"
|
||||
)
|
||||
|
||||
const (
|
||||
NAMESPACE_USER = "user"
|
||||
NAMESPACE_SERVICE = "service"
|
||||
NAMESPACE_BUG_REPORT = "bug-report"
|
||||
)
|
||||
|
||||
@@ -44,6 +44,12 @@ type ParameterListInput struct {
|
||||
Name []string `json:"name"`
|
||||
}
|
||||
|
||||
type ParameterCloneInput struct {
|
||||
DestNs string `json:"dest_ns"`
|
||||
DestNsId string `json:"dest_ns_id"`
|
||||
DestName string `json:"dest_name"`
|
||||
}
|
||||
|
||||
type ScopedPolicyCreateInput struct {
|
||||
apis.InfrasResourceBaseCreateInput
|
||||
|
||||
|
||||
@@ -28,18 +28,20 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/yunionconf"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/identity"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
"yunion.io/x/onecloud/pkg/yunionconf/options"
|
||||
)
|
||||
|
||||
const (
|
||||
NAMESPACE_USER = "user"
|
||||
NAMESPACE_SERVICE = "service"
|
||||
NAMESPACE_BUG_REPORT = "bug-report"
|
||||
NAMESPACE_USER = api.NAMESPACE_USER
|
||||
NAMESPACE_SERVICE = api.NAMESPACE_SERVICE
|
||||
NAMESPACE_BUG_REPORT = api.NAMESPACE_BUG_REPORT
|
||||
)
|
||||
|
||||
type SParameterManager struct {
|
||||
@@ -84,8 +86,8 @@ func isAdminQuery(query jsonutils.JSONObject) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func getUserId(user string) (string, error) {
|
||||
s := auth.GetAdminSession(context.Background(), options.Options.Region)
|
||||
func getUserId(ctx context.Context, user string) (string, error) {
|
||||
s := auth.GetAdminSession(ctx, options.Options.Region)
|
||||
userObj, err := modules.UsersV3.Get(s, user, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -99,8 +101,8 @@ func getUserId(user string) (string, error) {
|
||||
return uid, nil
|
||||
}
|
||||
|
||||
func getServiceId(service string) (string, error) {
|
||||
s := auth.GetAdminSession(context.Background(), options.Options.Region)
|
||||
func getServiceId(ctx context.Context, service string) (string, error) {
|
||||
s := auth.GetAdminSession(ctx, options.Options.Region)
|
||||
serviceObj, err := modules.ServicesV3.Get(s, service, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -114,17 +116,17 @@ func getServiceId(service string) (string, error) {
|
||||
return uid, nil
|
||||
}
|
||||
|
||||
func getNamespaceInContext(userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (namespace string, namespaceId string, err error) {
|
||||
func getNamespaceInContext(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (namespace string, namespaceId string, err error) {
|
||||
// 优先匹配上线文中的参数, /users/<user_id>/parameters /services/<service_id>/parameters
|
||||
if query != nil {
|
||||
if uid := jsonutils.GetAnyString(query, []string{"user", "user_id"}); len(uid) > 0 {
|
||||
uid, err := getUserId(uid)
|
||||
uid, err := getUserId(ctx, uid)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return NAMESPACE_USER, uid, nil
|
||||
} else if sid := jsonutils.GetAnyString(query, []string{"service", "service_id"}); len(sid) > 0 {
|
||||
sid, err := getServiceId(sid)
|
||||
sid, err := getServiceId(ctx, sid)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
@@ -134,13 +136,13 @@ func getNamespaceInContext(userCred mcclient.TokenCredential, query jsonutils.JS
|
||||
|
||||
// 匹配/parameters中的参数
|
||||
if uid := jsonutils.GetAnyString(data, []string{"user", "user_id"}); len(uid) > 0 {
|
||||
uid, err := getUserId(uid)
|
||||
uid, err := getUserId(ctx, uid)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return NAMESPACE_USER, uid, nil
|
||||
} else if sid := jsonutils.GetAnyString(data, []string{"service", "service_id"}); len(sid) > 0 {
|
||||
sid, err := getServiceId(sid)
|
||||
sid, err := getServiceId(ctx, sid)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
@@ -150,10 +152,10 @@ func getNamespaceInContext(userCred mcclient.TokenCredential, query jsonutils.JS
|
||||
}
|
||||
}
|
||||
|
||||
func getNamespace(userCred mcclient.TokenCredential, resource string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (string, string, error) {
|
||||
func getNamespace(ctx context.Context, userCred mcclient.TokenCredential, resource string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (string, string, error) {
|
||||
var namespace, namespace_id string
|
||||
if policy.PolicyManager.Allow(rbacscope.ScopeSystem, userCred, consts.GetServiceType(), resource, policy.PolicyActionList).Result.IsAllow() {
|
||||
if name, nameId, e := getNamespaceInContext(userCred, query, data); e != nil {
|
||||
if name, nameId, e := getNamespaceInContext(ctx, userCred, query, data); e != nil {
|
||||
return "", "", e
|
||||
} else {
|
||||
namespace = name
|
||||
@@ -187,7 +189,7 @@ func (manager *SParameterManager) ValidateCreateData(ctx context.Context, userCr
|
||||
return nil, httperrors.NewUserNotFoundError("user not found")
|
||||
}
|
||||
|
||||
namespace, namespace_id, e := getNamespace(userCred, manager.KeywordPlural(), query, data)
|
||||
namespace, namespace_id, e := getNamespace(ctx, userCred, manager.KeywordPlural(), query, data)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -253,13 +255,13 @@ func (manager *SParameterManager) ListItemFilter(
|
||||
if id := query.NamespaceId; len(id) > 0 {
|
||||
q = q.Equals("namespace_id", id)
|
||||
} else if id := query.ServiceId; len(id) > 0 {
|
||||
if sid, err := getServiceId(id); err != nil {
|
||||
if sid, err := getServiceId(ctx, id); err != nil {
|
||||
return q, err
|
||||
} else {
|
||||
q = q.Equals("namespace_id", sid).Equals("namespace", NAMESPACE_SERVICE)
|
||||
}
|
||||
} else if id := query.UserId; len(id) > 0 {
|
||||
if uid, err := getUserId(id); err != nil {
|
||||
if uid, err := getUserId(ctx, id); err != nil {
|
||||
return q, err
|
||||
} else {
|
||||
q = q.Equals("namespace_id", uid).Equals("namespace", NAMESPACE_USER)
|
||||
@@ -313,7 +315,7 @@ func (model *SParameter) ValidateUpdateData(ctx context.Context, userCred mcclie
|
||||
return nil, httperrors.NewUserNotFoundError("user not found")
|
||||
}
|
||||
|
||||
namespace, namespace_id, e := getNamespace(userCred, model.KeywordPlural(), query, data)
|
||||
namespace, namespace_id, e := getNamespace(ctx, userCred, model.KeywordPlural(), query, data)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
@@ -411,3 +413,151 @@ func (manager *SParameterManager) DisableBugReport(ctx context.Context) error {
|
||||
bugReportEnable = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (manager *SParameterManager) FetchParameters(nsType string, nsId string, name string) ([]SParameter, error) {
|
||||
q := manager.Query()
|
||||
q = q.Equals("namespace", nsType)
|
||||
q = q.Equals("namespace_id", nsId)
|
||||
if len(name) > 0 {
|
||||
q = q.Equals("name", name)
|
||||
}
|
||||
params := make([]SParameter, 0)
|
||||
err := db.FetchModelObjects(manager, q, ¶ms)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "db.FetchModelObjects")
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func (parameter *SParameter) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
|
||||
return jsonutils.Marshal(struct {
|
||||
Id int64
|
||||
Name string
|
||||
Namespace string
|
||||
NamespaceId string
|
||||
Value jsonutils.JSONObject
|
||||
}{
|
||||
Id: parameter.Id,
|
||||
Name: parameter.Name,
|
||||
Namespace: parameter.Namespace,
|
||||
NamespaceId: parameter.NamespaceId,
|
||||
Value: parameter.Value,
|
||||
}).(*jsonutils.JSONDict)
|
||||
}
|
||||
|
||||
func (parameter *SParameter) PerformClone(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
input *api.ParameterCloneInput,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
if len(input.DestName) == 0 {
|
||||
input.DestName = parameter.Name
|
||||
}
|
||||
var nsType string
|
||||
var nsId string
|
||||
switch input.DestNs {
|
||||
case "user", "users":
|
||||
uid, err := getUserId(ctx, input.DestNsId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "getDestUserId %s", input.DestNsId)
|
||||
}
|
||||
nsType = NAMESPACE_USER
|
||||
nsId = uid
|
||||
case "service", "services":
|
||||
sid, err := getServiceId(ctx, input.DestNsId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "getDestServiceId %s", input.DestNsId)
|
||||
}
|
||||
nsType = NAMESPACE_SERVICE
|
||||
nsId = sid
|
||||
default:
|
||||
return nil, errors.Wrapf(errors.ErrNotSupported, "unsupported namespace %s/%s", input.DestNs, input.DestNsId)
|
||||
}
|
||||
|
||||
lockman.LockClass(ctx, ParameterManager, nsId)
|
||||
defer lockman.ReleaseClass(ctx, ParameterManager, nsId)
|
||||
|
||||
destParams, err := ParameterManager.FetchParameters(nsType, nsId, input.DestName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "FetchParameters")
|
||||
}
|
||||
switch len(destParams) {
|
||||
case 0:
|
||||
// create it
|
||||
if !policy.PolicyManager.Allow(rbacscope.ScopeSystem, userCred, consts.GetServiceType(), ParameterManager.KeywordPlural(), policy.PolicyActionCreate).Result.IsAllow() {
|
||||
return nil, httperrors.ErrNotSufficientPrivilege
|
||||
}
|
||||
newParam := SParameter{}
|
||||
newParam.SetModelManager(ParameterManager, &newParam)
|
||||
newParam.Namespace = nsType
|
||||
newParam.NamespaceId = nsId
|
||||
newParam.Name = input.DestName
|
||||
newParam.Value = parameter.Value
|
||||
newParam.CreatedBy = userCred.GetUserId()
|
||||
newParam.UpdatedBy = userCred.GetUserId()
|
||||
|
||||
err := ParameterManager.TableSpec().Insert(ctx, &newParam)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Insert")
|
||||
}
|
||||
logclient.AddActionLogWithContext(ctx, &newParam, logclient.ACT_CREATE, newParam.GetShortDesc(ctx), userCred, true)
|
||||
return jsonutils.Marshal(&newParam), nil
|
||||
case 1:
|
||||
// update it
|
||||
if !policy.PolicyManager.Allow(rbacscope.ScopeSystem, userCred, consts.GetServiceType(), ParameterManager.KeywordPlural(), policy.PolicyActionUpdate).Result.IsAllow() {
|
||||
return nil, httperrors.ErrNotSufficientPrivilege
|
||||
}
|
||||
destParam := destParams[0]
|
||||
lockman.LockObject(ctx, &destParam)
|
||||
defer lockman.ReleaseObject(ctx, &destParam)
|
||||
|
||||
var newValue jsonutils.JSONObject
|
||||
if parameter.Value != nil {
|
||||
switch srcVal := parameter.Value.(type) {
|
||||
case *jsonutils.JSONDict:
|
||||
if destParam.Value == nil {
|
||||
newValue = srcVal
|
||||
} else if destDict, ok := destParam.Value.(*jsonutils.JSONDict); ok {
|
||||
dest := jsonutils.NewDict()
|
||||
dest.Update(destDict)
|
||||
dest.Update(srcVal)
|
||||
newValue = dest
|
||||
} else {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidFormat, "cannot clone dictionary value to other type")
|
||||
}
|
||||
case *jsonutils.JSONArray:
|
||||
if destParam.Value == nil {
|
||||
newValue = srcVal
|
||||
} else if destArray, ok := destParam.Value.(*jsonutils.JSONArray); ok {
|
||||
dest := destArray.Copy()
|
||||
srcObjs, _ := srcVal.GetArray()
|
||||
dest.Add(srcObjs...)
|
||||
newValue = dest
|
||||
} else {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidFormat, "cannot clone array value to other type")
|
||||
}
|
||||
default:
|
||||
newValue = srcVal
|
||||
}
|
||||
} else {
|
||||
// null operation
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
diff, err := db.Update(&destParam, func() error {
|
||||
destParam.Value = newValue
|
||||
destParam.UpdatedBy = userCred.GetUserId()
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logclient.AddActionLogWithContext(ctx, &destParam, logclient.ACT_UPDATE, diff, userCred, false)
|
||||
return nil, errors.Wrap(err, "update")
|
||||
}
|
||||
logclient.AddActionLogWithContext(ctx, &destParam, logclient.ACT_UPDATE, diff, userCred, true)
|
||||
return jsonutils.Marshal(parameter), nil
|
||||
default:
|
||||
// error?
|
||||
return nil, errors.Wrapf(httperrors.ErrInternalError, "duplicate dest?")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user