Skip to content

Commit

Permalink
feature: decouple metadata resource and standalone resource
Browse files Browse the repository at this point in the history
  • Loading branch information
Qiu Jian committed May 6, 2020
1 parent d39d146 commit 722498b
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 114 deletions.
26 changes: 26 additions & 0 deletions cmd/climc/shell/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -436,4 +436,30 @@ func init() {
printObject(result)
return nil
})

R(&options.ResourceMetadataOptions{}, "project-add-tag", "Set tag of a project", func(s *mcclient.ClientSession, opts *options.ResourceMetadataOptions) error {
params, err := opts.Params()
if err != nil {
return err
}
result, err := modules.Projects.PerformAction(s, opts.ID, "user-metadata", params)
if err != nil {
return err
}
printObject(result)
return nil
})

R(&options.ResourceMetadataOptions{}, "project-set-tag", "Replace all tags of a project", func(s *mcclient.ClientSession, opts *options.ResourceMetadataOptions) error {
params, err := opts.Params()
if err != nil {
return err
}
result, err := modules.Projects.PerformAction(s, opts.ID, "set-user-metadata", params)
if err != nil {
return err
}
printObject(result)
return nil
})
}
4 changes: 2 additions & 2 deletions cmd/climc/shell/servers.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func init() {
return nil
})

R(&options.ServerMetadataOptions{}, "server-add-tag", "Set tag of a server", func(s *mcclient.ClientSession, opts *options.ServerMetadataOptions) error {
R(&options.ResourceMetadataOptions{}, "server-add-tag", "Set tag of a server", func(s *mcclient.ClientSession, opts *options.ResourceMetadataOptions) error {
params, err := opts.Params()
if err != nil {
return err
Expand All @@ -147,7 +147,7 @@ func init() {
return nil
})

R(&options.ServerMetadataOptions{}, "server-set-tag", "Set tag of a server", func(s *mcclient.ClientSession, opts *options.ServerMetadataOptions) error {
R(&options.ResourceMetadataOptions{}, "server-set-tag", "Set tag of a server", func(s *mcclient.ClientSession, opts *options.ResourceMetadataOptions) error {
params, err := opts.Params()
if err != nil {
return err
Expand Down
10 changes: 7 additions & 3 deletions pkg/apis/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,7 @@ type STag struct {
Value string
}

type StandaloneResourceListInput struct {
ResourceBaseListInput

type MetadataResourceListInput struct {
// 通过标签过滤
Tags []STag `json:"tags"`

Expand All @@ -176,6 +174,12 @@ type StandaloneResourceListInput struct {
WithoutUserMeta bool `json:"without_user_meta"`
// 返回列表数据中包含资源的标签数据(Metadata)
WithMeta *bool `json:"with_meta"`
}

type StandaloneResourceListInput struct {
ResourceBaseListInput

MetadataResourceListInput

// 显示所有的资源,包括模拟的资源
ShowEmulated *bool `json:"show_emulated"`
Expand Down
8 changes: 6 additions & 2 deletions pkg/apis/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,15 @@ type StatusStandaloneResourceDetails struct {
StandaloneResourceDetails
}

type MetadataResourceInfo struct {
// 标签
Metadata map[string]string `json:"metadata"`
}

type StandaloneResourceDetails struct {
ResourceBaseDetails

// 标签
Metadata map[string]string `json:"metadata"`
MetadataResourceInfo
}

type DomainizedResourceInfo struct {
Expand Down
6 changes: 5 additions & 1 deletion pkg/cloudcommon/db/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@ type IJointModel interface {
AllowUpdateJointItem(ctx context.Context, userCred mcclient.TokenCredential, item IJointModel) bool
}

type IMetadataBaseModelManager interface {
GetMetadataHiddenKeys() []string
}

type IStandaloneModelManager interface {
IResourceModelManager

Expand All @@ -253,7 +257,7 @@ type IStandaloneModelManager interface {

// FetchByExternalId(idStr string) (IStandaloneModel, error)

GetMetadataHiddenKeys() []string
IMetadataBaseModelManager
}

type IStandaloneModel interface {
Expand Down
2 changes: 1 addition & 1 deletion pkg/cloudcommon/db/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ func GetVisiableMetadata(model IStandaloneModel, userCred mcclient.TokenCredenti
return metaData, nil
}

func metaList2Map(manager IStandaloneModelManager, userCred mcclient.TokenCredential, metaList []SMetadata) map[string]string {
func metaList2Map(manager IMetadataBaseModelManager, userCred mcclient.TokenCredential, metaList []SMetadata) map[string]string {
metaMap := make(map[string]string)

hiddenKeys := manager.GetMetadataHiddenKeys()
Expand Down
174 changes: 174 additions & 0 deletions pkg/cloudcommon/db/metadataresource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// 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 db

import (
"strings"

"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"

"yunion.io/x/onecloud/pkg/apis"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)

type SMetadataResourceBaseModelManager struct{}

func (meta *SMetadataResourceBaseModelManager) ListItemFilter(
manager IModelManager,
q *sqlchemy.SQuery,
input apis.MetadataResourceListInput,
) *sqlchemy.SQuery {
tags := map[string][]string{}
for _, tag := range input.Tags {
if _, ok := tags[tag.Key]; !ok {
tags[tag.Key] = []string{}
}
if len(tag.Value) > 0 && !utils.IsInStringArray(tag.Value, tags[tag.Key]) {
tags[tag.Key] = append(tags[tag.Key], tag.Value)
}
}

if len(tags) > 0 {
metadataResQ := Metadata.Query().Equals("obj_type", manager.Keyword()).SubQuery()
metadataView := metadataResQ.Query()
idx := 0
for key, values := range tags {
if idx == 0 {
metadataView = metadataView.Equals("key", key)
if len(values) > 0 {
metadataView = metadataView.In("value", values)
}
} else {
subMetataView := metadataResQ.Query().Equals("key", key)
if len(values) > 0 {
subMetataView = subMetataView.In("value", values)
}
sq := subMetataView.SubQuery()
metadataView.Join(sq, sqlchemy.Equals(metadataView.Field("id"), sq.Field("id")))
}
idx++
}
metadatas := metadataView.SubQuery()
sq := metadatas.Query(metadatas.Field("obj_id")).Distinct().SubQuery()
q = q.Filter(sqlchemy.In(q.Field("id"), sq))
}

if input.WithoutUserMeta {
metadatas := Metadata.Query().Equals("obj_type", manager.Keyword()).SubQuery()
sq := metadatas.Query(metadatas.Field("obj_id")).Startswith("key", USER_TAG_PREFIX).Distinct().SubQuery()
q.Filter(sqlchemy.NotIn(q.Field("id"), sq))
}

return q
}

func (meta *SMetadataResourceBaseModelManager) QueryDistinctExtraField(manager IModelManager, q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
if strings.HasPrefix(field, "tag:") {
tagKey := field[4:]
metaQ := Metadata.Query("obj_id", "value").Equals("obj_type", manager.Keyword()).Equals("key", tagKey).SubQuery()
q = q.AppendField(metaQ.Field("value", field)).Distinct()
q = q.LeftJoin(metaQ, sqlchemy.Equals(q.Field("id"), metaQ.Field("obj_id")))
q = q.Asc(metaQ.Field("value"))
return q, nil
}
return q, httperrors.ErrNotFound
}

func (meta *SMetadataResourceBaseModelManager) OrderByExtraFields(
manager IModelManager,
q *sqlchemy.SQuery,
input apis.MetadataResourceListInput,
) *sqlchemy.SQuery {
if len(input.OrderByTag) > 0 {
order := sqlchemy.SQL_ORDER_ASC
tagKey := input.OrderByTag
if stringutils2.HasSuffixIgnoreCase(input.OrderByTag, string(sqlchemy.SQL_ORDER_ASC)) {
tagKey = tagKey[0 : len(tagKey)-len(sqlchemy.SQL_ORDER_ASC)-1]
} else if stringutils2.HasSuffixIgnoreCase(input.OrderByTag, string(sqlchemy.SQL_ORDER_DESC)) {
tagKey = tagKey[0 : len(tagKey)-len(sqlchemy.SQL_ORDER_DESC)-1]
order = sqlchemy.SQL_ORDER_DESC
}
metaQ := Metadata.Query("obj_id", "value").Equals("obj_type", manager.Keyword()).Equals("key", tagKey).SubQuery()
q = q.LeftJoin(metaQ, sqlchemy.Equals(q.Field("id"), metaQ.Field("obj_id")))
if order == sqlchemy.SQL_ORDER_ASC {
q = q.Asc(metaQ.Field("value"))
} else {
q = q.Desc(metaQ.Field("value"))
}
}
return q
}

func (meta *SMetadataResourceBaseModelManager) FetchCustomizeColumns(
manager IModelManager,
userCred mcclient.TokenCredential,
objs []interface{},
fields stringutils2.SSortedStrings,
) []apis.MetadataResourceInfo {
ret := make([]apis.MetadataResourceInfo, len(objs))
resIds := make([]string, len(objs))
for i := range objs {
resIds[i] = GetObjectIdstr(objs[i].(IModel))
}

if fields == nil || fields.Contains("__meta__") {
q := Metadata.Query("id", "key", "value")
metaKeyValues := make(map[string][]SMetadata)
err := FetchQueryObjectsByIds(q, "id", resIds, &metaKeyValues)
if err != nil {
log.Errorf("FetchQueryObjectsByIds metadata fail %s", err)
return ret
}

for i := range objs {
if metaList, ok := metaKeyValues[resIds[i]]; ok {
ret[i].Metadata = metaList2Map(manager.(IMetadataBaseModelManager), userCred, metaList)
}
}
}

return ret
}

const (
TAG_EXPORT_KEY_PREFIX = "tag:"
)

func (meta *SMetadataResourceBaseModelManager) GetExportExtraKeys(keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict {
res := jsonutils.NewDict()
for _, key := range keys {
if strings.HasPrefix(key, TAG_EXPORT_KEY_PREFIX) {
res.Add(jsonutils.NewString(rowMap[key]), key)
}
}
return res
}

func (meta *SMetadataResourceBaseModelManager) ListItemExportKeys(manager IModelManager, q *sqlchemy.SQuery, keys stringutils2.SSortedStrings) *sqlchemy.SQuery {
for _, key := range keys {
if strings.HasPrefix(key, TAG_EXPORT_KEY_PREFIX) {
tagKey := key[len(TAG_EXPORT_KEY_PREFIX):]
metaQ := Metadata.Query("obj_id", "value").Equals("obj_type", manager.Keyword()).Equals("key", tagKey).SubQuery()
q = q.LeftJoin(metaQ, sqlchemy.Equals(q.Field("id"), metaQ.Field("obj_id")))
q = q.AppendField(metaQ.Field("value", key))
}
}
return q
}

0 comments on commit 722498b

Please sign in to comment.