Files
kubesphere/pkg/simple/client/overview/metrics.go
2025-04-30 15:53:51 +08:00

264 lines
5.9 KiB
Go

/*
* Copyright 2024 the KubeSphere Authors.
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package overview
import (
"context"
"errors"
"fmt"
"reflect"
"time"
"github.com/Masterminds/semver/v3"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
v12 "k8s.io/api/core/v1"
v1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/api/meta"
"sigs.k8s.io/controller-runtime/pkg/client"
clusterv1alpha1 "kubesphere.io/api/cluster/v1alpha1"
corev1alpha1 "kubesphere.io/api/core/v1alpha1"
iamv1beta1 "kubesphere.io/api/iam/v1beta1"
tenantv1beta1 "kubesphere.io/api/tenant/v1beta1"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
)
type Counter interface {
RegisterResource(options ...RegisterOption)
GetMetrics(metricNames []string, namespace, workspace, prefix string) (*MetricResults, error)
}
var _ Counter = &metricsCounter{}
type metricsCounter struct {
registerTypeMap map[string]reflect.Type
client client.Client
}
func New(client client.Client) Counter {
collector := &metricsCounter{
registerTypeMap: make(map[string]reflect.Type),
client: client,
}
return collector
}
func (r *metricsCounter) RegisterResource(options ...RegisterOption) {
for _, o := range options {
r.register(o)
}
}
func (r *metricsCounter) register(option RegisterOption) {
r.registerTypeMap[option.MetricsName] = reflect.TypeOf(option.Type)
}
func (r *metricsCounter) GetMetrics(metricNames []string, namespace, workspace, prefix string) (*MetricResults, error) {
result := &MetricResults{Results: make([]Metric, 0)}
for _, n := range metricNames {
metric, err := r.collect(n, prefix, namespace, workspace)
if err != nil {
return nil, err
}
result.AddMetric(metric)
}
return result, nil
}
func CustomMetric(metricName, prefix string, count int) *Metric {
return newMetric(metricName, prefix, count)
}
func newMetric(metricName, prefix string, count int) *Metric {
if prefix != "" {
metricName = fmt.Sprintf("%s_%s", prefix, metricName)
}
metric := &Metric{
MetricName: metricName,
Data: MetricData{
ResultType: "vector",
Result: make([]MetricValue, 0),
},
}
value := MetricValue{
Value: []interface{}{
time.Now().Unix(),
count,
},
}
metric.Data.Result = append(metric.Data.Result, value)
return metric
}
type RegisterOption struct {
MetricsName string
Type client.ObjectList
}
func (r *metricsCounter) collect(metricName, prefix, namespace, workspace string) (*Metric, error) {
t, exist := r.registerTypeMap[metricName]
if !exist {
return nil, errors.New("can not find metric type")
}
objVal := reflect.New(t.Elem())
objList := objVal.Interface().(client.ObjectList)
opts := make([]client.ListOption, 0)
if workspace != "" {
opt := client.MatchingLabels(map[string]string{tenantv1beta1.WorkspaceLabel: workspace})
opts = append(opts, opt)
}
if namespace != "" {
opt := client.InNamespace(namespace)
opts = append(opts, opt)
}
err := r.client.List(context.Background(), objList, opts...)
if err != nil {
return nil, err
}
return newMetric(metricName, prefix, meta.LenList(objList)), nil
}
type MetricResults struct {
Results []Metric `json:"results"`
}
type Metric struct {
MetricName string `json:"metric_name"`
Data MetricData `json:"data"`
}
type MetricData struct {
ResultType string `json:"resultType"`
Result []MetricValue `json:"result"`
}
type MetricValue struct {
Value []interface{} `json:"value"`
}
func (r *MetricResults) AddMetric(metric *Metric) {
r.Results = append(r.Results, *metric)
}
func NewDefaultRegisterOptions(k8sVersion *semver.Version) []RegisterOption {
options := []RegisterOption{
{
MetricsName: NamespaceCount,
Type: &v12.NamespaceList{},
},
{
MetricsName: WorkspaceCount,
Type: &tenantv1beta1.WorkspaceTemplateList{},
},
{
MetricsName: ClusterCount,
Type: &clusterv1alpha1.ClusterList{},
},
{
MetricsName: PodCount,
Type: &v12.PodList{},
},
{
MetricsName: DeploymentCount,
Type: &appsv1.DeploymentList{},
},
{
MetricsName: StatefulSetCount,
Type: &appsv1.StatefulSetList{},
},
{
MetricsName: DaemonSetCount,
Type: &appsv1.DaemonSetList{},
},
{
MetricsName: JobCount,
Type: &batchv1.JobList{},
},
{
MetricsName: ServiceCount,
Type: &v12.ServiceList{},
},
{
MetricsName: IngressCount,
Type: &v1.IngressList{},
},
{
MetricsName: PersistentVolumeCount,
Type: &v12.PersistentVolumeList{},
},
{
MetricsName: PersistentVolumeClaimCount,
Type: &v12.PersistentVolumeClaimList{},
},
{
MetricsName: GlobalRoleCount,
Type: &iamv1beta1.GlobalRoleList{},
},
{
MetricsName: WorkspaceRoleCount,
Type: &iamv1beta1.WorkspaceRoleList{},
},
{
MetricsName: RoleCount,
Type: &iamv1beta1.RoleList{},
},
{
MetricsName: ClusterRoleCount,
Type: &iamv1beta1.ClusterRoleList{},
},
{
MetricsName: UserCount,
Type: &iamv1beta1.UserList{},
},
{
MetricsName: GlobalRoleBindingCount,
Type: &iamv1beta1.GlobalRoleBindingList{},
},
{
MetricsName: ClusterRoleBindingCount,
Type: &iamv1beta1.ClusterRoleBindingList{},
},
{
MetricsName: RoleBindingCount,
Type: &iamv1beta1.RoleBindingList{},
},
{
MetricsName: WorkspaceRoleBindingCount,
Type: &iamv1beta1.WorkspaceRoleBindingList{},
},
{
MetricsName: ExtensionCount,
Type: &corev1alpha1.ExtensionList{},
},
}
var cronJobType client.ObjectList
if k8sutil.ServeBatchV1beta1(k8sVersion) {
cronJobType = &batchv1beta1.CronJobList{}
} else {
cronJobType = &batchv1.CronJobList{}
}
options = append(options, RegisterOption{
MetricsName: CronJobCount,
Type: cronJobType,
})
return options
}