Skip to content

Commit

Permalink
optimize: add api for list cuberesourcequota
Browse files Browse the repository at this point in the history
  • Loading branch information
weilaaa committed Sep 27, 2023
1 parent 9ecaeaa commit 43e439a
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 20 deletions.
19 changes: 1 addition & 18 deletions pkg/apiserver/cubeapi/authorization/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,29 +538,12 @@ func (h *handler) getTenantByUser(c *gin.Context) {
userName = c.GetString(constants.UserName)
}

user := user.User{}
err := h.Client.Cache().Get(ctx, types.NamespacedName{Name: userName}, &user)
if err != nil {
response.FailReturn(c, errcode.CustomReturn(http.StatusNotFound, err.Error()))
return
}

tenants := tenantv1.TenantList{}
err = h.Client.Cache().List(ctx, &tenants)
res, err := GetVisibleTenants(ctx, h.Client, userName)
if err != nil {
response.FailReturn(c, errcode.CustomReturn(http.StatusNotFound, err.Error()))
return
}

tenantSet := sets.NewString(user.Status.BelongTenants...)
res := []tenantv1.Tenant{}
for _, t := range tenants.Items {
if !user.Status.PlatformAdmin && !tenantSet.Has(t.Name) {
continue
}
res = append(res, t)
}

sort.SliceStable(res, func(i, j int) bool {
return res[i].CreationTimestamp.Time.After(res[j].CreationTimestamp.Time)
})
Expand Down
27 changes: 27 additions & 0 deletions pkg/apiserver/cubeapi/authorization/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"strings"

"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
userinfo "k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -296,3 +298,28 @@ func isPlatformRole(labels map[string]string) bool {
}
return labels[constants.RoleLabel] == constants.ClusterRolePlatform
}

func GetVisibleTenants(ctx context.Context, cli mgrclient.Client, username string) ([]tenantv1.Tenant, error) {
user := userv1.User{}
err := cli.Cache().Get(ctx, types.NamespacedName{Name: username}, &user)
if err != nil {
return nil, err
}

tenants := tenantv1.TenantList{}
err = cli.Cache().List(ctx, &tenants)
if err != nil {
return nil, err
}

tenantSet := sets.NewString(user.Status.BelongTenants...)
res := []tenantv1.Tenant{}
for _, t := range tenants.Items {
if !user.Status.PlatformAdmin && !tenantSet.Has(t.Name) {
continue
}
res = append(res, t)
}

return res, nil
}
51 changes: 51 additions & 0 deletions pkg/apiserver/cubeapi/cluster/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"sigs.k8s.io/hierarchical-namespaces/api/v1alpha2"

clusterv1 "github.com/kubecube-io/kubecube/pkg/apis/cluster/v1"
quotav1 "github.com/kubecube-io/kubecube/pkg/apis/quota/v1"
"github.com/kubecube-io/kubecube/pkg/authorizer/rbac"
"github.com/kubecube-io/kubecube/pkg/clients"
"github.com/kubecube-io/kubecube/pkg/clog"
Expand All @@ -64,6 +65,7 @@ func (h *handler) AddApisTo(root *gin.Engine) {
r.POST("register", h.registerCluster)
r.POST("add", h.addCluster)
r.POST("nsquota", h.createNsAndQuota)
r.GET("cuberesourcequotas", h.getCubeResourceQuota)
}

type result struct {
Expand Down Expand Up @@ -744,3 +746,52 @@ func (h *handler) createNsAndQuota(c *gin.Context) {

response.SuccessJsonReturn(c, "success")
}

type getCubeResourceQuotaResp struct {
Total int `json:"total"`
Items []cubeResourceQuotaData `json:"items"`
}

type cubeResourceQuotaData struct {
ClusterName string `json:"clusterName"`
ClusterIdentity string `json:"clusterIdentity"`
Tenant string `json:"tenant"`
CubeResourceQuota *quotav1.CubeResourceQuota `json:"cubeResourceQuota"`
ExclusiveNodeHard map[string]v1.ResourceList `json:"exclusiveNodeHard"`
}

func (h *handler) getCubeResourceQuota(c *gin.Context) {
ts := c.Query("tenants")
cs := c.Query("clusters")
userName := c.Query("user")

tenants, clusters := strings.Split(ts, ";"), strings.Split(cs, ";")

if len(userName) == 0 {
userName = c.GetString(constants.UserName)
}

if len(cs) == 0 {
clusters = multicluster.Interface().ListClustersNameByType(multicluster.AllCluster)
}

if len(ts) == 0 {
tenants = nil
}

visibleTenants, err := getVisibleTenants(h.Client, userName, tenants)
if err != nil {
response.FailReturn(c, errcode.CustomReturn(http.StatusBadRequest, err.Error()))
return
}

res, err := listCubeResourceQuota(h.Client, visibleTenants, clusters)
if err != nil {
response.FailReturn(c, errcode.CustomReturn(http.StatusBadRequest, err.Error()))
return
}

sortCubeResourceQuotas(res)

response.SuccessReturn(c, getCubeResourceQuotaResp{Total: len(res), Items: res})
}
134 changes: 134 additions & 0 deletions pkg/apiserver/cubeapi/cluster/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,22 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"

clusterv1 "github.com/kubecube-io/kubecube/pkg/apis/cluster/v1"
v1 "github.com/kubecube-io/kubecube/pkg/apis/quota/v1"
"github.com/kubecube-io/kubecube/pkg/apiserver/cubeapi/authorization"
"github.com/kubecube-io/kubecube/pkg/clients"
"github.com/kubecube-io/kubecube/pkg/clog"
"github.com/kubecube-io/kubecube/pkg/multicluster"
mgrclient "github.com/kubecube-io/kubecube/pkg/multicluster/client"
"github.com/kubecube-io/kubecube/pkg/quota"
"github.com/kubecube-io/kubecube/pkg/utils/constants"
"github.com/kubecube-io/kubecube/pkg/utils/meta"
"github.com/kubecube-io/kubecube/pkg/utils/strproc"
)

Expand Down Expand Up @@ -543,3 +546,134 @@ func listHncNsByTenantsFunc(ctx context.Context, tenantList []string) func(cli m
return nsLIst, nil
}
}

func getVisibleTenants(cli mgrclient.Client, userName string, tenants []string) ([]string, error) {
visibleTenants, err := authorization.GetVisibleTenants(context.Background(), cli, userName)
if err != nil {
return nil, err
}
visibleTenantsSet := sets.New[string]()
for _, t := range visibleTenants {
visibleTenantsSet.Insert(t.Name)
}
if len(tenants) == 0 {
return visibleTenantsSet.UnsortedList(), nil
}
queryTenantSet := sets.New[string](tenants...)
if !visibleTenantsSet.IsSuperset(queryTenantSet) {
return nil, fmt.Errorf("query tenants (%v) is not visible for user (%v)", queryTenantSet.UnsortedList(), userName)
}
return queryTenantSet.UnsortedList(), nil
}

func listCubeResourceQuota(cli mgrclient.Client, tenants []string, clusters []string) ([]cubeResourceQuotaData, error) {
ls := labels.NewSelector()
r1, err := labels.NewRequirement(constants.ClusterLabel, selection.In, clusters)
if err != nil {
return nil, err
}
r2, err := labels.NewRequirement(constants.TenantLabel, selection.In, tenants)
if err != nil {
return nil, err
}
ls = ls.Add(*r1)
ls = ls.Add(*r2)

list := v1.CubeResourceQuotaList{}
err = cli.Cache().List(context.Background(), &list, &client.ListOptions{LabelSelector: ls})
if err != nil {
return nil, err
}

// construct cube resource quota map
quotaMap := make(map[string]v1.CubeResourceQuota, len(list.Items))
for _, v := range list.Items {
meta.TrimObjectMeta(&v)
quotaMap[v.Name] = v
}

// construct cluster cn name map
clusterMap := make(map[string]string, len(clusters))
clusterList := clusterv1.ClusterList{}
err = cli.Cache().List(context.Background(), &clusterList)
if err != nil {
return nil, err
}
for _, v := range clusterList.Items {
if v.Annotations != nil {
cnName, ok := v.Annotations[constants.CubeCnAnnotation]
if ok {
clusterMap[v.Name] = cnName
}
}
}

res := make([]cubeResourceQuotaData, 0, len(tenants)*len(clusters))
for _, tenant := range tenants {
for _, cluster := range clusters {
v := cubeResourceQuotaData{
ClusterIdentity: cluster,
ClusterName: cluster,
Tenant: tenant,
CubeResourceQuota: nil,
ExclusiveNodeHard: nil,
}
quotaName := strings.Join([]string{cluster, tenant}, ".")
q, ok := quotaMap[quotaName]
if ok {
v.CubeResourceQuota = &q
}
cnName, ok := clusterMap[cluster]
if ok {
v.ClusterName = cnName
}
clusterCli := clients.Interface().Kubernetes(cluster)
if clusterCli == nil {
return nil, fmt.Errorf("cluster %v not found", cluster)
}
v.ExclusiveNodeHard, err = getExclusiveNodeHard(clusterCli, tenant)
if err != nil {
return nil, err
}
res = append(res, v)
}
}
return res, nil
}

func getExclusiveNodeHard(cli mgrclient.Client, tenant string) (map[string]corev1.ResourceList, error) {
ls, err := labels.Parse(fmt.Sprintf("%v=%v", constants.LabelNodeTenant, tenant))
if err != nil {
return nil, err
}

nodeList := corev1.NodeList{}
err = cli.Cache().List(context.Background(), &nodeList, &client.ListOptions{LabelSelector: ls})
if err != nil {
return nil, err
}
ex := make(map[string]corev1.ResourceList, len(nodeList.Items))
for _, v := range nodeList.Items {
ex[v.Name] = v.Status.Capacity
}
return ex, nil
}

func sortCubeResourceQuotas(qs []cubeResourceQuotaData) []cubeResourceQuotaData {
sort.SliceStable(qs, func(i, j int) bool {
return qs[i].Tenant+qs[i].ClusterName < qs[j].Tenant+qs[j].ClusterName
})

res := []cubeResourceQuotaData{}
unsetted := []cubeResourceQuotaData{}

for _, v := range qs {
if len(v.ExclusiveNodeHard) == 0 || v.CubeResourceQuota == nil {
unsetted = append(unsetted, v)
} else {
res = append(res, v)
}
}
res = append(res, unsetted...)
return res
}
5 changes: 3 additions & 2 deletions pkg/multicluster/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const (
LocalCluster clusterType = iota
PivotCluster
MemberCluster
AllCluster
)

// ManagerImpl instance implement interface,
Expand Down Expand Up @@ -278,7 +279,7 @@ func (m *MultiClustersMgr) ListClustersByType(t clusterType) []*InternalCluster

var clusters []*InternalCluster
for _, v := range m.Clusters {
if v.Type == t {
if v.Type == t || (t == AllCluster && v.Type != LocalCluster) {
clusters = append(clusters, v)
}
}
Expand All @@ -294,7 +295,7 @@ func (m *MultiClustersMgr) ListClustersNameByType(t clusterType) []string {

var clusterNames []string
for _, v := range m.Clusters {
if v.Type == t {
if v.Type == t || (t == AllCluster && v.Type != LocalCluster) {
clusterNames = append(clusterNames, v.Name)
}
}
Expand Down
14 changes: 14 additions & 0 deletions pkg/utils/meta/meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package meta

import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
)

func TrimObjectMeta(obj runtime.Object) {
objMeta, _ := meta.Accessor(obj)
objMeta.SetFinalizers(nil)
objMeta.SetDeletionGracePeriodSeconds(nil)
objMeta.SetManagedFields(nil)
objMeta.SetOwnerReferences(nil)
}

0 comments on commit 43e439a

Please sign in to comment.