mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-12 15:31:32 +08:00
chore: add k8s pods list (#6229)
This commit is contained in:
parent
6448fb17e7
commit
1e78786cae
@ -114,6 +114,7 @@ type APIHandler struct {
|
|||||||
|
|
||||||
hostsRepo *inframetrics.HostsRepo
|
hostsRepo *inframetrics.HostsRepo
|
||||||
processesRepo *inframetrics.ProcessesRepo
|
processesRepo *inframetrics.ProcessesRepo
|
||||||
|
podsRepo *inframetrics.PodsRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
type APIHandlerOpts struct {
|
type APIHandlerOpts struct {
|
||||||
@ -185,6 +186,7 @@ func NewAPIHandler(opts APIHandlerOpts) (*APIHandler, error) {
|
|||||||
|
|
||||||
hostsRepo := inframetrics.NewHostsRepo(opts.Reader, querierv2)
|
hostsRepo := inframetrics.NewHostsRepo(opts.Reader, querierv2)
|
||||||
processesRepo := inframetrics.NewProcessesRepo(opts.Reader, querierv2)
|
processesRepo := inframetrics.NewProcessesRepo(opts.Reader, querierv2)
|
||||||
|
podsRepo := inframetrics.NewPodsRepo(opts.Reader, querierv2)
|
||||||
|
|
||||||
aH := &APIHandler{
|
aH := &APIHandler{
|
||||||
reader: opts.Reader,
|
reader: opts.Reader,
|
||||||
@ -205,6 +207,7 @@ func NewAPIHandler(opts APIHandlerOpts) (*APIHandler, error) {
|
|||||||
UseLogsNewSchema: opts.UseLogsNewSchema,
|
UseLogsNewSchema: opts.UseLogsNewSchema,
|
||||||
hostsRepo: hostsRepo,
|
hostsRepo: hostsRepo,
|
||||||
processesRepo: processesRepo,
|
processesRepo: processesRepo,
|
||||||
|
podsRepo: podsRepo,
|
||||||
}
|
}
|
||||||
|
|
||||||
logsQueryBuilder := logsv3.PrepareLogsQuery
|
logsQueryBuilder := logsv3.PrepareLogsQuery
|
||||||
@ -363,6 +366,11 @@ func (aH *APIHandler) RegisterInfraMetricsRoutes(router *mux.Router, am *AuthMid
|
|||||||
processesSubRouter.HandleFunc("/attribute_keys", am.ViewAccess(aH.getProcessAttributeKeys)).Methods(http.MethodGet)
|
processesSubRouter.HandleFunc("/attribute_keys", am.ViewAccess(aH.getProcessAttributeKeys)).Methods(http.MethodGet)
|
||||||
processesSubRouter.HandleFunc("/attribute_values", am.ViewAccess(aH.getProcessAttributeValues)).Methods(http.MethodGet)
|
processesSubRouter.HandleFunc("/attribute_values", am.ViewAccess(aH.getProcessAttributeValues)).Methods(http.MethodGet)
|
||||||
processesSubRouter.HandleFunc("/list", am.ViewAccess(aH.getProcessList)).Methods(http.MethodPost)
|
processesSubRouter.HandleFunc("/list", am.ViewAccess(aH.getProcessList)).Methods(http.MethodPost)
|
||||||
|
|
||||||
|
podsSubRouter := router.PathPrefix("/api/v1/pods").Subrouter()
|
||||||
|
podsSubRouter.HandleFunc("/attribute_keys", am.ViewAccess(aH.getPodAttributeKeys)).Methods(http.MethodGet)
|
||||||
|
podsSubRouter.HandleFunc("/attribute_values", am.ViewAccess(aH.getPodAttributeValues)).Methods(http.MethodGet)
|
||||||
|
podsSubRouter.HandleFunc("/list", am.ViewAccess(aH.getPodList)).Methods(http.MethodPost)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) RegisterWebSocketPaths(router *mux.Router, am *AuthMiddleware) {
|
func (aH *APIHandler) RegisterWebSocketPaths(router *mux.Router, am *AuthMiddleware) {
|
||||||
|
@ -122,3 +122,56 @@ func (aH *APIHandler) getProcessList(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
aH.Respond(w, hostList)
|
aH.Respond(w, hostList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (aH *APIHandler) getPodAttributeKeys(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
req, err := parseFilterAttributeKeyRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, err := aH.podsRepo.GetPodAttributeKeys(ctx, *req)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
aH.Respond(w, keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aH *APIHandler) getPodAttributeValues(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
req, err := parseFilterAttributeValueRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
values, err := aH.podsRepo.GetPodAttributeValues(ctx, *req)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
aH.Respond(w, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aH *APIHandler) getPodList(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
req := model.PodListRequest{}
|
||||||
|
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
podList, err := aH.podsRepo.GetPodList(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
aH.Respond(w, podList)
|
||||||
|
}
|
||||||
|
@ -53,6 +53,10 @@ func getParamsForTopHosts(req model.HostListRequest) (int64, string, string) {
|
|||||||
return getParamsForTopItems(req.Start, req.End)
|
return getParamsForTopItems(req.Start, req.End)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getParamsForTopPods(req model.PodListRequest) (int64, string, string) {
|
||||||
|
return getParamsForTopItems(req.Start, req.End)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(srikanthccv): remove this
|
// TODO(srikanthccv): remove this
|
||||||
// What is happening here?
|
// What is happening here?
|
||||||
// The `PrepareTimeseriesFilterQuery` uses the local time series table for sub-query because each fingerprint
|
// The `PrepareTimeseriesFilterQuery` uses the local time series table for sub-query because each fingerprint
|
||||||
|
386
pkg/query-service/app/inframetrics/pods.go
Normal file
386
pkg/query-service/app/inframetrics/pods.go
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
package inframetrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/app/metrics/v4/helpers"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/common"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/interfaces"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/model"
|
||||||
|
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/postprocess"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
metricToUseForPods = "k8s_pod_cpu_utilization"
|
||||||
|
|
||||||
|
podAttrsToEnrich = []string{
|
||||||
|
"k8s_pod_uid",
|
||||||
|
"k8s_pod_name",
|
||||||
|
"k8s_namespace_name",
|
||||||
|
"k8s_node_name",
|
||||||
|
"k8s_deployment_name",
|
||||||
|
"k8s_statefulset_name",
|
||||||
|
"k8s_daemonset_name",
|
||||||
|
"k8s_job_name",
|
||||||
|
"k8s_cronjob_name",
|
||||||
|
}
|
||||||
|
|
||||||
|
k8sPodUIDAttrKey = "k8s_pod_uid"
|
||||||
|
|
||||||
|
queryNamesForPods = map[string][]string{
|
||||||
|
"cpu": {"A"},
|
||||||
|
"cpu_request": {"B", "A"},
|
||||||
|
"cpu_limit": {"C", "A"},
|
||||||
|
"memory": {"D"},
|
||||||
|
"memory_request": {"E", "D"},
|
||||||
|
"memory_limit": {"F", "D"},
|
||||||
|
"restarts": {"G", "A"},
|
||||||
|
}
|
||||||
|
podQueryNames = []string{"A", "B", "C", "D", "E", "F", "G"}
|
||||||
|
|
||||||
|
metricNamesForPods = map[string]string{
|
||||||
|
"cpu": "k8s_pod_cpu_utilization",
|
||||||
|
"cpu_request": "k8s_pod_cpu_request_utilization",
|
||||||
|
"cpu_limit": "k8s_pod_cpu_limit_utilization",
|
||||||
|
"memory": "k8s_pod_memory_usage",
|
||||||
|
"memory_request": "k8s_pod_memory_request_utilization",
|
||||||
|
"memory_limit": "k8s_pod_memory_limit_utilization",
|
||||||
|
"restarts": "k8s_container_restarts",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type PodsRepo struct {
|
||||||
|
reader interfaces.Reader
|
||||||
|
querierV2 interfaces.Querier
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPodsRepo(reader interfaces.Reader, querierV2 interfaces.Querier) *PodsRepo {
|
||||||
|
return &PodsRepo{reader: reader, querierV2: querierV2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PodsRepo) GetPodAttributeKeys(ctx context.Context, req v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
|
||||||
|
// TODO(srikanthccv): remove hardcoded metric name and support keys from any pod metric
|
||||||
|
req.DataSource = v3.DataSourceMetrics
|
||||||
|
req.AggregateAttribute = metricToUseForPods
|
||||||
|
if req.Limit == 0 {
|
||||||
|
req.Limit = 50
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeKeysResponse, err := p.reader.GetMetricAttributeKeys(ctx, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(srikanthccv): only return resource attributes when we have a way to
|
||||||
|
// distinguish between resource attributes and other attributes.
|
||||||
|
filteredKeys := []v3.AttributeKey{}
|
||||||
|
for _, key := range attributeKeysResponse.AttributeKeys {
|
||||||
|
if slices.Contains(pointAttrsToIgnore, key.Key) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filteredKeys = append(filteredKeys, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &v3.FilterAttributeKeyResponse{AttributeKeys: filteredKeys}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PodsRepo) GetPodAttributeValues(ctx context.Context, req v3.FilterAttributeValueRequest) (*v3.FilterAttributeValueResponse, error) {
|
||||||
|
req.DataSource = v3.DataSourceMetrics
|
||||||
|
req.AggregateAttribute = metricToUseForPods
|
||||||
|
if req.Limit == 0 {
|
||||||
|
req.Limit = 50
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeValuesResponse, err := p.reader.GetMetricAttributeValues(ctx, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return attributeValuesResponse, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PodsRepo) getMetadataAttributes(ctx context.Context, req model.PodListRequest) (map[string]map[string]string, error) {
|
||||||
|
podAttrs := map[string]map[string]string{}
|
||||||
|
|
||||||
|
for _, key := range podAttrsToEnrich {
|
||||||
|
hasKey := false
|
||||||
|
for _, groupByKey := range req.GroupBy {
|
||||||
|
if groupByKey.Key == key {
|
||||||
|
hasKey = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasKey {
|
||||||
|
req.GroupBy = append(req.GroupBy, v3.AttributeKey{Key: key})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mq := v3.BuilderQuery{
|
||||||
|
DataSource: v3.DataSourceMetrics,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: metricToUseForPods,
|
||||||
|
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||||
|
},
|
||||||
|
Temporality: v3.Unspecified,
|
||||||
|
GroupBy: req.GroupBy,
|
||||||
|
}
|
||||||
|
|
||||||
|
query, err := helpers.PrepareTimeseriesFilterQuery(req.Start, req.End, &mq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
query = localQueryToDistributedQuery(query)
|
||||||
|
|
||||||
|
attrsListResponse, err := p.reader.GetListResultV3(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range attrsListResponse {
|
||||||
|
stringData := map[string]string{}
|
||||||
|
for key, value := range row.Data {
|
||||||
|
if str, ok := value.(string); ok {
|
||||||
|
stringData[key] = str
|
||||||
|
} else if strPtr, ok := value.(*string); ok {
|
||||||
|
stringData[key] = *strPtr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
podName := stringData[k8sPodUIDAttrKey]
|
||||||
|
if _, ok := podAttrs[podName]; !ok {
|
||||||
|
podAttrs[podName] = map[string]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range req.GroupBy {
|
||||||
|
podAttrs[podName][key.Key] = stringData[key.Key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return podAttrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PodsRepo) getTopPodGroups(ctx context.Context, req model.PodListRequest, q *v3.QueryRangeParamsV3) ([]map[string]string, []map[string]string, error) {
|
||||||
|
step, timeSeriesTableName, samplesTableName := getParamsForTopPods(req)
|
||||||
|
|
||||||
|
queryNames := queryNamesForPods[req.OrderBy.ColumnName]
|
||||||
|
topPodGroupsQueryRangeParams := &v3.QueryRangeParamsV3{
|
||||||
|
Start: req.Start,
|
||||||
|
End: req.End,
|
||||||
|
Step: step,
|
||||||
|
CompositeQuery: &v3.CompositeQuery{
|
||||||
|
BuilderQueries: map[string]*v3.BuilderQuery{},
|
||||||
|
QueryType: v3.QueryTypeBuilder,
|
||||||
|
PanelType: v3.PanelTypeTable,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, queryName := range queryNames {
|
||||||
|
query := q.CompositeQuery.BuilderQueries[queryName].Clone()
|
||||||
|
query.StepInterval = step
|
||||||
|
query.MetricTableHints = &v3.MetricTableHints{
|
||||||
|
TimeSeriesTableName: timeSeriesTableName,
|
||||||
|
SamplesTableName: samplesTableName,
|
||||||
|
}
|
||||||
|
if req.Filters != nil && len(req.Filters.Items) > 0 {
|
||||||
|
if query.Filters == nil {
|
||||||
|
query.Filters = &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}}
|
||||||
|
}
|
||||||
|
query.Filters.Items = append(query.Filters.Items, req.Filters.Items...)
|
||||||
|
}
|
||||||
|
topPodGroupsQueryRangeParams.CompositeQuery.BuilderQueries[queryName] = query
|
||||||
|
}
|
||||||
|
|
||||||
|
queryResponse, _, err := p.querierV2.QueryRange(ctx, topPodGroupsQueryRangeParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
formattedResponse, err := postprocess.PostProcessResult(queryResponse, topPodGroupsQueryRangeParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(formattedResponse) == 0 || len(formattedResponse[0].Series) == 0 {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.OrderBy.Order == v3.DirectionDesc {
|
||||||
|
sort.Slice(formattedResponse[0].Series, func(i, j int) bool {
|
||||||
|
return formattedResponse[0].Series[i].Points[0].Value > formattedResponse[0].Series[j].Points[0].Value
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
sort.Slice(formattedResponse[0].Series, func(i, j int) bool {
|
||||||
|
return formattedResponse[0].Series[i].Points[0].Value < formattedResponse[0].Series[j].Points[0].Value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
paginatedTopPodGroupsSeries := formattedResponse[0].Series[req.Offset : req.Offset+req.Limit]
|
||||||
|
|
||||||
|
topPodGroups := []map[string]string{}
|
||||||
|
for _, series := range paginatedTopPodGroupsSeries {
|
||||||
|
topPodGroups = append(topPodGroups, series.Labels)
|
||||||
|
}
|
||||||
|
allPodGroups := []map[string]string{}
|
||||||
|
for _, series := range formattedResponse[0].Series {
|
||||||
|
allPodGroups = append(allPodGroups, series.Labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
return topPodGroups, allPodGroups, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PodsRepo) GetPodList(ctx context.Context, req model.PodListRequest) (model.PodListResponse, error) {
|
||||||
|
resp := model.PodListResponse{}
|
||||||
|
|
||||||
|
if req.Limit == 0 {
|
||||||
|
req.Limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.OrderBy == nil {
|
||||||
|
req.OrderBy = &v3.OrderBy{ColumnName: "cpu", Order: v3.DirectionDesc}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.GroupBy == nil {
|
||||||
|
req.GroupBy = []v3.AttributeKey{{Key: k8sPodUIDAttrKey}}
|
||||||
|
resp.Type = model.ResponseTypeList
|
||||||
|
} else {
|
||||||
|
resp.Type = model.ResponseTypeGroupedList
|
||||||
|
}
|
||||||
|
|
||||||
|
step := common.MinAllowedStepInterval(req.Start, req.End)
|
||||||
|
|
||||||
|
query := PodsTableListQuery.Clone()
|
||||||
|
|
||||||
|
query.Start = req.Start
|
||||||
|
query.End = req.End
|
||||||
|
query.Step = step
|
||||||
|
|
||||||
|
for _, query := range query.CompositeQuery.BuilderQueries {
|
||||||
|
query.StepInterval = step
|
||||||
|
if req.Filters != nil && len(req.Filters.Items) > 0 {
|
||||||
|
if query.Filters == nil {
|
||||||
|
query.Filters = &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}}
|
||||||
|
}
|
||||||
|
query.Filters.Items = append(query.Filters.Items, req.Filters.Items...)
|
||||||
|
}
|
||||||
|
query.GroupBy = req.GroupBy
|
||||||
|
}
|
||||||
|
|
||||||
|
podAttrs, err := p.getMetadataAttributes(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
topPodGroups, allPodGroups, err := p.getTopPodGroups(ctx, req, query)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
groupFilters := map[string][]string{}
|
||||||
|
for _, topPodGroup := range topPodGroups {
|
||||||
|
for k, v := range topPodGroup {
|
||||||
|
groupFilters[k] = append(groupFilters[k], v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for groupKey, groupValues := range groupFilters {
|
||||||
|
hasGroupFilter := false
|
||||||
|
if req.Filters != nil && len(req.Filters.Items) > 0 {
|
||||||
|
for _, filter := range req.Filters.Items {
|
||||||
|
if filter.Key.Key == groupKey {
|
||||||
|
hasGroupFilter = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasGroupFilter {
|
||||||
|
for _, query := range query.CompositeQuery.BuilderQueries {
|
||||||
|
query.Filters.Items = append(query.Filters.Items, v3.FilterItem{
|
||||||
|
Key: v3.AttributeKey{Key: groupKey},
|
||||||
|
Value: groupValues,
|
||||||
|
Operator: v3.FilterOperatorIn,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queryResponse, _, err := p.querierV2.QueryRange(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
formattedResponse, err := postprocess.PostProcessResult(queryResponse, query)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
records := []model.PodListRecord{}
|
||||||
|
|
||||||
|
for _, result := range formattedResponse {
|
||||||
|
for _, row := range result.Table.Rows {
|
||||||
|
|
||||||
|
record := model.PodListRecord{
|
||||||
|
PodCPU: -1,
|
||||||
|
PodCPURequest: -1,
|
||||||
|
PodCPULimit: -1,
|
||||||
|
PodMemory: -1,
|
||||||
|
PodMemoryRequest: -1,
|
||||||
|
PodMemoryLimit: -1,
|
||||||
|
RestartCount: -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
if podUID, ok := row.Data[k8sPodUIDAttrKey].(string); ok {
|
||||||
|
record.PodUID = podUID
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpu, ok := row.Data["A"].(float64); ok {
|
||||||
|
record.PodCPU = cpu
|
||||||
|
}
|
||||||
|
if cpuRequest, ok := row.Data["B"].(float64); ok {
|
||||||
|
record.PodCPURequest = cpuRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpuLimit, ok := row.Data["C"].(float64); ok {
|
||||||
|
record.PodCPULimit = cpuLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory, ok := row.Data["D"].(float64); ok {
|
||||||
|
record.PodMemory = memory
|
||||||
|
}
|
||||||
|
|
||||||
|
if memoryRequest, ok := row.Data["E"].(float64); ok {
|
||||||
|
record.PodMemoryRequest = memoryRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
if memoryLimit, ok := row.Data["F"].(float64); ok {
|
||||||
|
record.PodMemoryLimit = memoryLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
if restarts, ok := row.Data["G"].(float64); ok {
|
||||||
|
record.RestartCount = int(restarts)
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Meta = map[string]string{}
|
||||||
|
if _, ok := podAttrs[record.PodUID]; ok {
|
||||||
|
record.Meta = podAttrs[record.PodUID]
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range row.Data {
|
||||||
|
if slices.Contains(podQueryNames, k) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if labelValue, ok := v.(string); ok {
|
||||||
|
record.Meta[k] = labelValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
records = append(records, record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp.Total = len(allPodGroups)
|
||||||
|
resp.Records = records
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
196
pkg/query-service/app/inframetrics/pods_query.go
Normal file
196
pkg/query-service/app/inframetrics/pods_query.go
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
package inframetrics
|
||||||
|
|
||||||
|
import v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||||
|
|
||||||
|
var PodsTableListQuery = v3.QueryRangeParamsV3{
|
||||||
|
CompositeQuery: &v3.CompositeQuery{
|
||||||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||||||
|
// pod cpu utilization
|
||||||
|
"A": {
|
||||||
|
QueryName: "A",
|
||||||
|
DataSource: v3.DataSourceMetrics,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: metricNamesForPods["cpu"],
|
||||||
|
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||||
|
},
|
||||||
|
Temporality: v3.Unspecified,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: []v3.FilterItem{},
|
||||||
|
},
|
||||||
|
GroupBy: []v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: k8sPodUIDAttrKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeResource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Expression: "A",
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
TimeAggregation: v3.TimeAggregationAvg,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
// pod cpu request utilization
|
||||||
|
"B": {
|
||||||
|
QueryName: "B",
|
||||||
|
DataSource: v3.DataSourceMetrics,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: metricNamesForPods["cpu_request"],
|
||||||
|
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||||
|
},
|
||||||
|
Temporality: v3.Unspecified,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: []v3.FilterItem{},
|
||||||
|
},
|
||||||
|
GroupBy: []v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: k8sPodUIDAttrKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeResource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Expression: "B",
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
TimeAggregation: v3.TimeAggregationAvg,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
// pod cpu limit utilization
|
||||||
|
"C": {
|
||||||
|
QueryName: "C",
|
||||||
|
DataSource: v3.DataSourceMetrics,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: metricNamesForPods["cpu_limit"],
|
||||||
|
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||||
|
},
|
||||||
|
Temporality: v3.Unspecified,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: []v3.FilterItem{},
|
||||||
|
},
|
||||||
|
GroupBy: []v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: k8sPodUIDAttrKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeResource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Expression: "C",
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
TimeAggregation: v3.TimeAggregationAvg,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
// pod memory utilization
|
||||||
|
"D": {
|
||||||
|
QueryName: "D",
|
||||||
|
DataSource: v3.DataSourceMetrics,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: metricNamesForPods["memory"],
|
||||||
|
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||||
|
},
|
||||||
|
Temporality: v3.Unspecified,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: []v3.FilterItem{},
|
||||||
|
},
|
||||||
|
GroupBy: []v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: k8sPodUIDAttrKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeResource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Expression: "D",
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
TimeAggregation: v3.TimeAggregationAvg,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
// pod memory request utilization
|
||||||
|
"E": {
|
||||||
|
QueryName: "E",
|
||||||
|
DataSource: v3.DataSourceMetrics,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: metricNamesForPods["memory_request"],
|
||||||
|
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||||
|
},
|
||||||
|
Temporality: v3.Unspecified,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: []v3.FilterItem{},
|
||||||
|
},
|
||||||
|
GroupBy: []v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: k8sPodUIDAttrKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeResource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Expression: "E",
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
TimeAggregation: v3.TimeAggregationAvg,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
// pod memory limit utilization
|
||||||
|
"F": {
|
||||||
|
QueryName: "F",
|
||||||
|
DataSource: v3.DataSourceMetrics,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: metricNamesForPods["memory_limit"],
|
||||||
|
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||||
|
},
|
||||||
|
Temporality: v3.Unspecified,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: []v3.FilterItem{},
|
||||||
|
},
|
||||||
|
GroupBy: []v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: k8sPodUIDAttrKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeResource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Expression: "F",
|
||||||
|
ReduceTo: v3.ReduceToOperatorAvg,
|
||||||
|
TimeAggregation: v3.TimeAggregationAvg,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationSum,
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
"G": {
|
||||||
|
QueryName: "G",
|
||||||
|
DataSource: v3.DataSourceMetrics,
|
||||||
|
AggregateAttribute: v3.AttributeKey{
|
||||||
|
Key: metricNamesForPods["restarts"],
|
||||||
|
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||||
|
},
|
||||||
|
Temporality: v3.Unspecified,
|
||||||
|
Filters: &v3.FilterSet{
|
||||||
|
Operator: "AND",
|
||||||
|
Items: []v3.FilterItem{},
|
||||||
|
},
|
||||||
|
GroupBy: []v3.AttributeKey{
|
||||||
|
{
|
||||||
|
Key: k8sPodUIDAttrKey,
|
||||||
|
DataType: v3.AttributeKeyDataTypeString,
|
||||||
|
Type: v3.AttributeKeyTypeResource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Expression: "G",
|
||||||
|
ReduceTo: v3.ReduceToOperatorSum,
|
||||||
|
TimeAggregation: v3.TimeAggregationAnyLast,
|
||||||
|
SpaceAggregation: v3.SpaceAggregationMax,
|
||||||
|
Functions: []v3.Function{{Name: v3.FunctionNameRunningDiff}},
|
||||||
|
Disabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PanelType: v3.PanelTypeTable,
|
||||||
|
QueryType: v3.QueryTypeBuilder,
|
||||||
|
},
|
||||||
|
Version: "v4",
|
||||||
|
FormatForWeb: true,
|
||||||
|
}
|
@ -2,6 +2,15 @@ package model
|
|||||||
|
|
||||||
import v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
import v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||||
|
|
||||||
|
type (
|
||||||
|
ResponseType string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ResponseTypeList ResponseType = "list"
|
||||||
|
ResponseTypeGroupedList ResponseType = "grouped_list"
|
||||||
|
)
|
||||||
|
|
||||||
type HostListRequest struct {
|
type HostListRequest struct {
|
||||||
Start int64 `json:"start"` // epoch time in ms
|
Start int64 `json:"start"` // epoch time in ms
|
||||||
End int64 `json:"end"` // epoch time in ms
|
End int64 `json:"end"` // epoch time in ms
|
||||||
@ -80,3 +89,40 @@ type ProcessListGroup struct {
|
|||||||
GroupMemoryAvg float64 `json:"groupMemoryAvg"`
|
GroupMemoryAvg float64 `json:"groupMemoryAvg"`
|
||||||
ProcessNames []string `json:"processNames"`
|
ProcessNames []string `json:"processNames"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PodListRequest struct {
|
||||||
|
Start int64 `json:"start"` // epoch time in ms
|
||||||
|
End int64 `json:"end"` // epoch time in ms
|
||||||
|
Filters *v3.FilterSet `json:"filters"`
|
||||||
|
GroupBy []v3.AttributeKey `json:"groupBy"`
|
||||||
|
OrderBy *v3.OrderBy `json:"orderBy"`
|
||||||
|
Offset int `json:"offset"`
|
||||||
|
Limit int `json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PodListResponse struct {
|
||||||
|
Type ResponseType `json:"type"`
|
||||||
|
Records []PodListRecord `json:"records"`
|
||||||
|
Total int `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PodListRecord struct {
|
||||||
|
PodUID string `json:"podUID,omitempty"`
|
||||||
|
PodCPU float64 `json:"podCPU"`
|
||||||
|
PodCPURequest float64 `json:"podCPURequest"`
|
||||||
|
PodCPULimit float64 `json:"podCPULimit"`
|
||||||
|
PodMemory float64 `json:"podMemory"`
|
||||||
|
PodMemoryRequest float64 `json:"podMemoryRequest"`
|
||||||
|
PodMemoryLimit float64 `json:"podMemoryLimit"`
|
||||||
|
RestartCount int `json:"restartCount"`
|
||||||
|
Meta map[string]string `json:"meta"`
|
||||||
|
CountByPhase PodCountByPhase `json:"countByPhase"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PodCountByPhase struct {
|
||||||
|
Pending int `json:"pending"`
|
||||||
|
Running int `json:"running"`
|
||||||
|
Succeeded int `json:"succeeded"`
|
||||||
|
Failed int `json:"failed"`
|
||||||
|
Unknown int `json:"unknown"`
|
||||||
|
}
|
||||||
|
@ -28,12 +28,12 @@ func PostProcessResult(result []*v3.Result, queryRangeParams *v3.QueryRangeParam
|
|||||||
// The function is named applyMetricLimit because it only applies to metrics data source
|
// The function is named applyMetricLimit because it only applies to metrics data source
|
||||||
// In traces and logs, the limit is achieved using subqueries
|
// In traces and logs, the limit is achieved using subqueries
|
||||||
ApplyMetricLimit(result, queryRangeParams)
|
ApplyMetricLimit(result, queryRangeParams)
|
||||||
|
// We apply the functions here it's easier to add new functions
|
||||||
|
ApplyFunctions(result, queryRangeParams)
|
||||||
// Each series in the result produces N number of points, where N is (end - start) / step
|
// Each series in the result produces N number of points, where N is (end - start) / step
|
||||||
// For the panel type table, we need to show one point for each series in the row
|
// For the panel type table, we need to show one point for each series in the row
|
||||||
// We do that by applying a reduce function to each series
|
// We do that by applying a reduce function to each series
|
||||||
applyReduceTo(result, queryRangeParams)
|
applyReduceTo(result, queryRangeParams)
|
||||||
// We apply the functions here it's easier to add new functions
|
|
||||||
ApplyFunctions(result, queryRangeParams)
|
|
||||||
|
|
||||||
// expressions are executed at query serivce so the value of time.now in the invdividual
|
// expressions are executed at query serivce so the value of time.now in the invdividual
|
||||||
// queries will be different so for table panel we are making it same.
|
// queries will be different so for table panel we are making it same.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user