Skip to content

Commit

Permalink
Add some features (#949)
Browse files Browse the repository at this point in the history
* fix: mesos cluster metrics/overview 请求 (#939)

* fix: project perm rules with OR op (#941)

* Release 1.4.3 (#943)

* fix: mesos cluster metrics/overview 请求

* fix: mesos cluster metrics/overview 请求

* fix: 节点标签接口兼容处理 (#945)

Co-authored-by: ielgnaw <[email protected]>
Co-authored-by: jamesge <[email protected]>
Co-authored-by: hito <[email protected]>
  • Loading branch information
4 people authored May 26, 2021
1 parent 295c161 commit 9f59907
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 146 deletions.
80 changes: 52 additions & 28 deletions bcs-app/backend/components/iam/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# 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.
#
from typing import Dict, Optional

from django.conf import settings

try:
Expand All @@ -26,11 +28,6 @@


class IAMClient(Client):
def _list_policies(self, data):
path = f"/api/v1/systems/{self._app_code}/policies"
ok, message, data = self._call_iam_api(http_get, path, data)
return ok, message, data

def list_policies(self, data):
policies = []
page, page_size = 1, 100
Expand Down Expand Up @@ -76,16 +73,17 @@ def grant_resource_creator_actions(self, bk_token, bk_username, data):

return True, "success"

def _list_policies(self, data):
path = f"/api/v1/systems/{self._app_code}/policies"
ok, message, data = self._call_iam_api(http_get, path, data)
return ok, message, data


class BCSIAM(IAM):
def __init__(self, app_code, app_secret, bk_iam_host, bk_paas_host):
self._client = IAMClient(app_code, app_secret, bk_iam_host, bk_paas_host)

def make_dict_filter(self, request, key_mapping=None):
"""
仅支持{'op': 'in', 'field': 'project.id', 'value': [1, 2, 3]}
或者{'op': 'eq', 'field': 'project.id', 'value': 1}
"""
def do_policy_query(self, request) -> Optional[Dict]:
# 1. validate
if not isinstance(request, Request):
raise AuthInvalidRequest("request should be instance of iam.auth.models.Request")
Expand All @@ -99,22 +97,7 @@ def make_dict_filter(self, request, key_mapping=None):
if not policies:
return None

op = policies["op"]
if op not in [OP.IN, OP.EQ, OP.ANY]:
raise AuthInvalidRequest("make_dict_filter only support OP.IN or OP.EQ or OP.ANY")

value = policies["value"]
if op == OP.EQ:
value = [
value,
]

field = policies["field"]
if key_mapping:
k = key_mapping.get(field) or field
return {k: value, "op": op}

return {field: value, "op": op}
return policies

def _match_resource_id(self, expression, resource_type_id, resource_id):
if expression["op"] in [OP.AND, OP.OR]:
Expand Down Expand Up @@ -265,10 +248,14 @@ def can_edit(self, username, project_id, raise_exception=False):
action_id = self.actions.EDIT.value
return self._allowed_do_project_inst(username, action_id, project_id, raise_exception)

def make_view_perm_filter(self, username):
def make_view_perm_filter(self, username: str) -> Dict:
action_id = self.actions.VIEW.value
request = self._make_request_with_resources(username, action_id)
return self.iam.make_dict_filter(request, {"project.id": "project_id_list"})
policies = self.iam.do_policy_query(request)
if not policies:
return {}

return self._make_dict_filter(policies)

def op_is_any(self, filter):
if not filter:
Expand Down Expand Up @@ -302,3 +289,40 @@ def query_authorized_users(self, project_id, action_id):

def grant_related_action_perms(self, username, project_id, project_name):
return self.iam.grant_resource_creator_action(username, self.resource_type_id, project_id, project_name)

def _make_dict_filter(self, policies: Dict) -> Dict:
"""
基于策略规则, 生成 project_id 过滤器。
:params policies: 权限中心返回的策略规则,如 {'op': OP.IN, 'value': [2, 1], 'field': 'project.id'}
:returns : project_id 过滤器, 如 {'project_id_list': [1, 2], 'op': OP.IN}
"""
op = policies["op"]
if op not in [OP.IN, OP.EQ, OP.ANY, OP.OR, OP.AND]:
raise AuthInvalidRequest(f"make_dict_filter does not support op:{op}")

field, key = "project.id", "project_id_list"

if op == OP.EQ:
return {key: [policies["value"]], "op": OP.IN}

if op in [OP.IN, OP.ANY]:
return {key: policies["value"] or [], "op": op}

# 如果 op 是 OP.OR 或 OP.AND,只处理一级,不考虑嵌套的情况
value_list = []
for policy in policies["content"]:
if policy["field"] != field:
continue

op = policy["op"]
if op == OP.ANY:
return {key: policy["value"] or [], "op": op}

value = policy["value"]
if op == OP.IN:
value_list.extend(value)
elif op == OP.EQ:
value_list.append(value)

return {key: list(set(value_list)), "op": OP.IN}
6 changes: 6 additions & 0 deletions bcs-app/backend/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@ def get_logging_config(log_level, rds_hander_settings=None, log_path="app.log"):
"handlers": ["console", "logstash_redis", "file"],
"level": "DEBUG",
},
# 配置iam logger
'iam': {
'handlers': ['file'],
'level': os.getenv('IAM_LOG_LEVEL', 'ERROR'),
'propagate': False,
},
"sentry_logger": {"handlers": ["sentry"], "level": "ERROR"},
},
}
Expand Down
55 changes: 55 additions & 0 deletions bcs-app/backend/tests/components/test_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
#
# Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community Edition) available.
# Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. All rights reserved.
# Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://opensource.org/licenses/MIT
#
# 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.
#
import pytest
from iam import OP

from backend.components.iam.permissions import ProjectPermission

test_dict_filter_data = [
({'op': OP.IN, 'value': [2, 1], 'field': 'project.id'}, {'project_id_list': [1, 2], 'op': OP.IN}),
({'op': OP.EQ, 'value': 1, 'field': 'project.id'}, {'project_id_list': [1], 'op': OP.IN}),
({'op': OP.ANY, 'value': [], 'field': 'project.id'}, {'project_id_list': [], 'op': OP.ANY}),
(
{
'op': OP.OR,
'content': [
{'op': OP.IN, 'field': 'project.id', 'value': [2, 1, 5]},
{'op': OP.ANY, 'field': 'project.id', 'value': []},
{'op': OP.EQ, 'field': 'project.id', 'value': 3},
{'op': OP.IN, 'field': 'project.id', 'value': [4]},
],
},
{'project_id_list': [], 'op': OP.ANY},
),
(
{
'op': OP.OR,
'content': [
{'op': OP.IN, 'field': 'project.id', 'value': [2, 1, 5]},
{'op': OP.EQ, 'field': 'project.id', 'value': 3},
{'op': OP.IN, 'field': 'fake_project.id', 'value': [4, 6]},
],
},
{'project_id_list': [1, 2, 3, 5], 'op': OP.IN},
),
]


class TestProjectPermission:
@pytest.mark.parametrize('policies, expected_dict_filter', test_dict_filter_data)
def test_make_dict_filter(self, policies, expected_dict_filter):
project_perm = ProjectPermission()
dict_filter = project_perm._make_dict_filter(policies)
assert dict_filter['project_id_list'].sort() == expected_dict_filter['project_id_list'].sort()
assert dict_filter['op'] == expected_dict_filter['op']
76 changes: 26 additions & 50 deletions bcs-app/frontend/src/views/cluster/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -209,53 +209,6 @@
:cur-project="curProject"
:cur-cluster="cluster">
</status-progress>
<!-- <div class="biz-cluster-content" :class="curProject.kind === PROJECT_MESOS ? 'more-info' : ''">
<div class="biz-progress-box">
<div class="progress-header">
<span class="title">{{$t('CPU使用率')}}</span>
<span class="percent">
{{conversionPercent(cluster.remain_cpu, cluster.total_cpu)}}%
</span>
</div>
<div class="progress">
<div class="progress-bar primary"
:style="{ width: `${conversionPercent(cluster.remain_cpu, cluster.total_cpu)}%` }"></div>
</div>
</div>
<div class="biz-progress-box">
<div class="progress-header">
<span class="title">{{$t('内存使用率')}}</span>
<span class="percent">
{{conversionPercent(cluster.remain_mem, cluster.total_mem)}}%
</span>
</div>
<div class="progress">
<div class="progress-bar success" :style="{ width: `${conversionPercent(cluster.remain_mem, cluster.total_mem)}%` }"></div>
</div>
</div>
<div class="biz-progress-box">
<div class="progress-header">
<span class="title">{{$t('磁盘使用率')}}</span>
<span class="percent">
{{conversionPercent(cluster.remain_disk, cluster.total_disk)}}%
</span>
</div>
<div class="progress">
<div class="progress-bar warning" :style="{ width: `${conversionPercent(cluster.remain_disk, cluster.total_disk)}%` }"></div>
</div>
</div>
<div class="biz-progress-box" v-if="curProject.kind === PROJECT_MESOS">
<div class="progress-header">
<span class="title">{{$t('集群IP')}}</span>
<span class="percent">
{{cluster.allip === 0 ? 0 : `${cluster.activeip} / ${cluster.allip}(${$t('剩余')}${cluster.availableip})`}}
</span>
</div>
<div class="progress">
<div class="progress-bar warning" :style="{ width: `${cluster.allip === 0 ? 0 : conversionPercent(cluster.activeip, cluster.allip, true)}%` }"></div>
</div>
</div>
</div> -->
<div class="add-node-btn-wrapper">
<button class="bk-button bk-default add-node-btn" @click="goOverviewOrNode('clusterNode', cluster)">
<span>{{$t('添加节点')}}</span>
Expand Down Expand Up @@ -733,16 +686,23 @@
this.permissions = JSON.parse(JSON.stringify(res.permissions || {}))
const list = res.data.results || []
list.forEach((item, index) => {
item.cpu_usage = {}
item.mem_usage = {}
item.disk_usage = {}
item.activeip = 0
item.availableip = 0
item.reservedip = 0
item.allip = 0
if (this.curProject.kind === this.PROJECT_MESOS) {
this.getClusterIp(item, index)
// if (this.curProject.kind === this.PROJECT_MESOS) {
// this.getClusterIp(item, index)
// }
if (item.type === 'mesos' && item.func_wlist && item.func_wlist.indexOf('MesosResource') > -1) {
if (!notLoading) {
this.getClusterIp(item, index)
}
}
})
Expand All @@ -753,6 +713,10 @@
resListItem.cpu_usage = c.cpu_usage
resListItem.mem_usage = c.mem_usage
resListItem.disk_usage = c.disk_usage
resListItem.activeip = c.activeip
resListItem.availableip = c.availableip
resListItem.reservedip = c.reservedip
resListItem.allip = c.allip
}
})
}
Expand All @@ -766,9 +730,15 @@
this.$set(l, index, item)
this.$store.commit('cluster/forceUpdateClusterList', l)
const args = {}
if (item.type === 'mesos' && item.func_wlist && item.func_wlist.indexOf('MesosResource') > -1) {
args.dimensions = 'mesos_memory_usage,mesos_cpu_usage'
}
await this.$store.dispatch('cluster/clusterOverview', {
projectId: this.projectId,
clusterId: item.cluster_id
clusterId: item.cluster_id,
data: args
}).then(d => {
item.cpu_usage = d.data.cpu_usage
// item.cpu_usage = {
Expand All @@ -778,6 +748,12 @@
item.mem_usage = d.data.mem_usage
item.disk_usage = d.data.disk_usage
// 如果是 mesos,返回是 mesos_memory_usage 和 mesos_cpu_usage
if (item.type === 'mesos' && item.func_wlist && item.func_wlist.indexOf('MesosResource') > -1) {
item.cpu_usage = d.data.mesos_cpu_usage
item.mem_usage = d.data.mesos_memory_usage
}
const l = []
l.splice(0, 0, ...this.clusterList)
this.$set(l, index, item)
Expand Down
40 changes: 29 additions & 11 deletions bcs-app/frontend/src/views/cluster/searcher/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,16 @@
statusList: [
{ text: this.$t('正常'), value: ['normal'] },
{ text: this.$t('不可调度'), value: ['to_removed', 'removable'] }
]
],
mesosLabelMap: {}
}
},
computed: {
curProject () {
return this.$store.state.curProject
},
isMesosProject () {
return this.curProject.kind === window.PROJECT_MESOS
}
},
watch: {
Expand Down Expand Up @@ -302,12 +311,15 @@
this.showKey = true
this.tagLoading = true
try {
const res = await this.$store.dispatch('cluster/getNodeKeyList', {
const api = this.isMesosProject ? 'cluster/getMesosNodeLabels' : 'cluster/getNodeKeyList'
const res = await this.$store.dispatch(api, {
projectId: this.projectId,
clusterId: this.clusterId
})
this.keyList.splice(0, this.keyList.length, ...(res.data || []))
this.keyListTmp.splice(0, this.keyList.length, ...(res.data || []))
const keyList = this.isMesosProject ? Object.keys(res.data || {}) : res.data || []
this.isMesosProject && (this.mesosLabelMap = res.data || {})
this.keyList.splice(0, this.keyList.length, ...keyList)
this.keyListTmp.splice(0, this.keyList.length, ...keyList)
this.$nextTick(() => {
this.$refs.searchInput.focus()
this.isListeningInputKeyup = true
Expand Down Expand Up @@ -366,13 +378,19 @@
this.tagLoading = true
this.curInputValue = ''
this.inputPlaceholder = this.$t('请输入要搜索的value')
const res = await this.$store.dispatch('cluster/getNodeValueListByKey', {
projectId: this.projectId,
clusterId: this.clusterId,
keyName: k
})
this.valueList.splice(0, this.valueList.length, ...(res.data || []))
this.valueListTmp.splice(0, this.valueList.length, ...(res.data || []))
let valueList = []
if (this.isMesosProject) {
valueList = this.mesosLabelMap[k] || []
} else {
const res = await this.$store.dispatch('cluster/getNodeValueListByKey', {
projectId: this.projectId,
clusterId: this.clusterId,
keyName: k
})
valueList = res.data || []
}
this.valueList.splice(0, this.valueList.length, ...valueList)
this.valueListTmp.splice(0, this.valueList.length, ...valueList)
this.$refs.searchInput.focus()
} catch (e) {
catchErrorHandler(e, this)
Expand Down
Loading

0 comments on commit 9f59907

Please sign in to comment.