Compare commits

...

3 Commits

Author SHA1 Message Date
nikhilmantri0902
a899970d91 chore: initial setup 2026-03-04 12:52:55 +05:30
Nikhil Mantri
f759123b9b Merge branch 'main' into feat/infraM_revamp_backend_p1base 2026-03-02 13:07:24 +05:30
nikhilmantri0902
13a0771ced chore: added base line and a sample health check endpoint 2026-02-28 20:27:19 +05:30
14 changed files with 759 additions and 105 deletions

View File

@@ -0,0 +1,52 @@
package signozapiserver
import (
"net/http"
"github.com/SigNoz/signoz/pkg/http/handler"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/infrastructuremonitoringtypes"
"github.com/gorilla/mux"
)
func (provider *provider) addInfrastructureMonitoringRoutes(router *mux.Router) error {
if err := router.Handle("/api/v2/infra-monitoring/health", handler.New(
provider.authZ.ViewAccess(provider.infrastructureMonitoringHandler.HealthCheck),
handler.OpenAPIDef{
ID: "InfrastructureMonitoringHealth",
Tags: []string{"infrastructuremonitoring"},
Summary: "Test Health Check endpoint",
Description: "This endpoint returns a health ok message from the Infrastructure Monitoring module",
Request: nil,
RequestContentType: "",
Response: nil, // String response
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
})).Methods(http.MethodGet).GetError(); err != nil {
return err
}
if err := router.Handle("/api/v2/infra-monitoring/pods/list", handler.New(
provider.authZ.ViewAccess(provider.infrastructureMonitoringHandler.GetPodsList),
handler.OpenAPIDef{
ID: "InfrastructureMonitoringGetPodsList",
Tags: []string{"infrastructuremonitoring"},
Summary: "Get Pods List",
Description: "This endpoint returns a list of pods for infrastructure monitoring",
Request: &infrastructuremonitoringtypes.PodsListRequest{},
RequestContentType: "application/json",
Response: &infrastructuremonitoringtypes.PodsListResponse{},
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
})).Methods(http.MethodPost).GetError(); err != nil {
return err
}
return nil
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/SigNoz/signoz/pkg/modules/authdomain"
"github.com/SigNoz/signoz/pkg/modules/dashboard"
"github.com/SigNoz/signoz/pkg/modules/fields"
"github.com/SigNoz/signoz/pkg/modules/infrastructuremonitoring"
"github.com/SigNoz/signoz/pkg/modules/metricsexplorer"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/modules/preference"
@@ -28,26 +29,27 @@ import (
)
type provider struct {
config apiserver.Config
settings factory.ScopedProviderSettings
router *mux.Router
authZ *middleware.AuthZ
orgHandler organization.Handler
userHandler user.Handler
sessionHandler session.Handler
authDomainHandler authdomain.Handler
preferenceHandler preference.Handler
globalHandler global.Handler
promoteHandler promote.Handler
flaggerHandler flagger.Handler
dashboardModule dashboard.Module
dashboardHandler dashboard.Handler
metricsExplorerHandler metricsexplorer.Handler
gatewayHandler gateway.Handler
fieldsHandler fields.Handler
authzHandler authz.Handler
zeusHandler zeus.Handler
querierHandler querier.Handler
config apiserver.Config
settings factory.ScopedProviderSettings
router *mux.Router
authZ *middleware.AuthZ
orgHandler organization.Handler
userHandler user.Handler
sessionHandler session.Handler
authDomainHandler authdomain.Handler
preferenceHandler preference.Handler
globalHandler global.Handler
promoteHandler promote.Handler
flaggerHandler flagger.Handler
dashboardModule dashboard.Module
dashboardHandler dashboard.Handler
metricsExplorerHandler metricsexplorer.Handler
infrastructureMonitoringHandler infrastructuremonitoring.Handler
gatewayHandler gateway.Handler
fieldsHandler fields.Handler
authzHandler authz.Handler
zeusHandler zeus.Handler
querierHandler querier.Handler
}
func NewFactory(
@@ -64,6 +66,7 @@ func NewFactory(
dashboardModule dashboard.Module,
dashboardHandler dashboard.Handler,
metricsExplorerHandler metricsexplorer.Handler,
infrastructureMonitoringHandler infrastructuremonitoring.Handler,
gatewayHandler gateway.Handler,
fieldsHandler fields.Handler,
authzHandler authz.Handler,
@@ -88,6 +91,7 @@ func NewFactory(
dashboardModule,
dashboardHandler,
metricsExplorerHandler,
infrastructureMonitoringHandler,
gatewayHandler,
fieldsHandler,
authzHandler,
@@ -114,6 +118,7 @@ func newProvider(
dashboardModule dashboard.Module,
dashboardHandler dashboard.Handler,
metricsExplorerHandler metricsexplorer.Handler,
infrastructureMonitoringHandler infrastructuremonitoring.Handler,
gatewayHandler gateway.Handler,
fieldsHandler fields.Handler,
authzHandler authz.Handler,
@@ -124,25 +129,26 @@ func newProvider(
router := mux.NewRouter().UseEncodedPath()
provider := &provider{
config: config,
settings: settings,
router: router,
orgHandler: orgHandler,
userHandler: userHandler,
sessionHandler: sessionHandler,
authDomainHandler: authDomainHandler,
preferenceHandler: preferenceHandler,
globalHandler: globalHandler,
promoteHandler: promoteHandler,
flaggerHandler: flaggerHandler,
dashboardModule: dashboardModule,
dashboardHandler: dashboardHandler,
metricsExplorerHandler: metricsExplorerHandler,
gatewayHandler: gatewayHandler,
fieldsHandler: fieldsHandler,
authzHandler: authzHandler,
zeusHandler: zeusHandler,
querierHandler: querierHandler,
config: config,
settings: settings,
router: router,
orgHandler: orgHandler,
userHandler: userHandler,
sessionHandler: sessionHandler,
authDomainHandler: authDomainHandler,
preferenceHandler: preferenceHandler,
globalHandler: globalHandler,
promoteHandler: promoteHandler,
flaggerHandler: flaggerHandler,
dashboardModule: dashboardModule,
dashboardHandler: dashboardHandler,
metricsExplorerHandler: metricsExplorerHandler,
infrastructureMonitoringHandler: infrastructureMonitoringHandler,
gatewayHandler: gatewayHandler,
fieldsHandler: fieldsHandler,
authzHandler: authzHandler,
zeusHandler: zeusHandler,
querierHandler: querierHandler,
}
provider.authZ = middleware.NewAuthZ(settings.Logger(), orgGetter, authz)
@@ -199,6 +205,10 @@ func (provider *provider) AddToRouter(router *mux.Router) error {
return err
}
if err := provider.addInfrastructureMonitoringRoutes(router); err != nil {
return err
}
if err := provider.addGatewayRoutes(router); err != nil {
return err
}

View File

@@ -0,0 +1,35 @@
package infrastructuremonitoring
import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/factory"
)
type Config struct {
// TelemetryStore is the telemetrystore configuration
TelemetryStore TelemetryStoreConfig `mapstructure:"telemetrystore"`
}
type TelemetryStoreConfig struct {
// Threads is the number of threads to use for ClickHouse queries
Threads int `mapstructure:"threads"`
}
func NewConfigFactory() factory.ConfigFactory {
return factory.NewConfigFactory(factory.MustNewName("infrastructuremonitoring"), newConfig)
}
func newConfig() factory.Config {
return Config{
TelemetryStore: TelemetryStoreConfig{
Threads: 8, // Default value
},
}
}
func (c Config) Validate() error {
if c.TelemetryStore.Threads <= 0 {
return errors.NewInvalidInputf(errors.CodeInvalidInput, "infrastructuremonitoring.telemetrystore.threads must be positive, got %d", c.TelemetryStore.Threads)
}
return nil
}

View File

@@ -0,0 +1,57 @@
package implinfrastructuremonitoring
import (
"encoding/json"
"net/http"
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/modules/infrastructuremonitoring"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/infrastructuremonitoringtypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
type handler struct {
module infrastructuremonitoring.Module
}
// NewHandler returns a infrastructuremonitoring.Handler implementation.
func NewHandler(m infrastructuremonitoring.Module) infrastructuremonitoring.Handler {
return &handler{
module: m,
}
}
func (h *handler) HealthCheck(rw http.ResponseWriter, req *http.Request) {
msg, err := h.module.HealthCheck(req.Context())
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, msg)
}
func (h *handler) GetPodsList(rw http.ResponseWriter, req *http.Request) {
claims, err := authtypes.ClaimsFromContext(req.Context())
if err != nil {
render.Error(rw, err)
return
}
orgID := valuer.MustNewUUID(claims.OrgID)
var payload infrastructuremonitoringtypes.PodsListRequest
if err := json.NewDecoder(req.Body).Decode(&payload); err != nil {
render.Error(rw, err)
return
}
response, err := h.module.GetPodsList(req.Context(), orgID, &payload)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, response)
}

View File

@@ -0,0 +1,50 @@
package implinfrastructuremonitoring
import (
"context"
"log/slog"
"github.com/SigNoz/signoz/pkg/cache"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/modules/infrastructuremonitoring"
"github.com/SigNoz/signoz/pkg/querier"
"github.com/SigNoz/signoz/pkg/telemetrymetrics"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types/infrastructuremonitoringtypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
)
type module struct {
telemetryStore telemetrystore.TelemetryStore
querier querier.Querier
telemetryMetadataStore telemetrytypes.MetadataStore
fieldMapper qbtypes.FieldMapper
condBuilder qbtypes.ConditionBuilder
logger *slog.Logger
cache cache.Cache
config infrastructuremonitoring.Config
}
// NewModule constructs the infrastructure monitoring module with the provided dependencies.
func NewModule(ts telemetrystore.TelemetryStore, querier querier.Querier, telemetryMetadataStore telemetrytypes.MetadataStore, cache cache.Cache, providerSettings factory.ProviderSettings, cfg infrastructuremonitoring.Config) infrastructuremonitoring.Module {
fieldMapper := telemetrymetrics.NewFieldMapper()
condBuilder := telemetrymetrics.NewConditionBuilder(fieldMapper)
return &module{
telemetryStore: ts,
querier: querier,
fieldMapper: fieldMapper,
condBuilder: condBuilder,
logger: providerSettings.Logger,
telemetryMetadataStore: telemetryMetadataStore,
cache: cache,
config: cfg,
}
}
func (m *module) HealthCheck(ctx context.Context) (*infrastructuremonitoringtypes.HealthCheckResponse, error) {
return &infrastructuremonitoringtypes.HealthCheckResponse{
Status: "ok",
Message: "Infrastructure Monitoring Module is healthy",
}, nil
}

View File

@@ -0,0 +1,369 @@
package implinfrastructuremonitoring
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/SigNoz/signoz/pkg/types/infrastructuremonitoringtypes"
"github.com/SigNoz/signoz/pkg/types/metrictypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
var defaultPodsGroupByTags = []qbtypes.GroupByKey{
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "k8s.namespace.name", FieldDataType: telemetrytypes.FieldDataTypeString, FieldContext: telemetrytypes.FieldContextAttribute}},
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "k8s.node.name", FieldDataType: telemetrytypes.FieldDataTypeString, FieldContext: telemetrytypes.FieldContextAttribute}},
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "k8s.cluster.name", FieldDataType: telemetrytypes.FieldDataTypeString, FieldContext: telemetrytypes.FieldContextAttribute}},
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "k8s.deployment.name", FieldDataType: telemetrytypes.FieldDataTypeString, FieldContext: telemetrytypes.FieldContextAttribute}},
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "k8s.statefulset.name", FieldDataType: telemetrytypes.FieldDataTypeString, FieldContext: telemetrytypes.FieldContextAttribute}},
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "k8s.daemonset.name", FieldDataType: telemetrytypes.FieldDataTypeString, FieldContext: telemetrytypes.FieldContextAttribute}},
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "k8s.job.name", FieldDataType: telemetrytypes.FieldDataTypeString, FieldContext: telemetrytypes.FieldContextAttribute}},
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "k8s.cronjob.name", FieldDataType: telemetrytypes.FieldDataTypeString, FieldContext: telemetrytypes.FieldContextAttribute}},
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "k8s.pod.uid", FieldDataType: telemetrytypes.FieldDataTypeString, FieldContext: telemetrytypes.FieldContextAttribute}},
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "k8s.pod.name", FieldDataType: telemetrytypes.FieldDataTypeString, FieldContext: telemetrytypes.FieldContextAttribute}},
}
// Keep metric definitions mapped by query name (A, B, C...) to build composite queries easily.
// One metric aggregation per query.
var podQueries = map[string]qbtypes.MetricAggregation{
"A": {MetricName: "k8s.pod.cpu.usage", TimeAggregation: metrictypes.TimeAggregationAvg, SpaceAggregation: metrictypes.SpaceAggregationSum, ReduceTo: qbtypes.ReduceToAvg},
"B": {MetricName: "k8s.pod.cpu.request", TimeAggregation: metrictypes.TimeAggregationAvg, SpaceAggregation: metrictypes.SpaceAggregationAvg, ReduceTo: qbtypes.ReduceToAvg},
"C": {MetricName: "k8s.pod.cpu.limit", TimeAggregation: metrictypes.TimeAggregationAvg, SpaceAggregation: metrictypes.SpaceAggregationAvg, ReduceTo: qbtypes.ReduceToAvg},
"D": {MetricName: "k8s.pod.memory.usage", TimeAggregation: metrictypes.TimeAggregationAvg, SpaceAggregation: metrictypes.SpaceAggregationSum, ReduceTo: qbtypes.ReduceToAvg},
"E": {MetricName: "k8s.pod.memory.request", TimeAggregation: metrictypes.TimeAggregationAvg, SpaceAggregation: metrictypes.SpaceAggregationAvg, ReduceTo: qbtypes.ReduceToAvg},
"F": {MetricName: "k8s.pod.memory.limit", TimeAggregation: metrictypes.TimeAggregationAvg, SpaceAggregation: metrictypes.SpaceAggregationAvg, ReduceTo: qbtypes.ReduceToAvg},
"G": {MetricName: "k8s.pod.restarts", TimeAggregation: metrictypes.TimeAggregationLatest, SpaceAggregation: metrictypes.SpaceAggregationMax, ReduceTo: qbtypes.ReduceToSum},
}
func (m *module) GetPodsList(ctx context.Context, orgID valuer.UUID, req *infrastructuremonitoringtypes.PodsListRequest) (*infrastructuremonitoringtypes.PodsListResponse, error) {
if req.Limit <= 0 {
req.Limit = 10
}
mergedGroupBy := mergePodsGroupBy(req.GroupBy)
orderMetric, orderDir, orderQueryName := parsePodsOrderBy(req.OrderBy)
// Build Pass 1: Get Top N Pod UIDs based on Order criteria
pass1Filter := copyFilter(req.Filter)
pass1Payload := buildPass1Payload(req, mergedGroupBy, pass1Filter, orderMetric, orderDir)
pass1Result, err := m.querier.QueryRange(ctx, orgID, pass1Payload)
if err != nil {
return nil, fmt.Errorf("pass 1 querying failed: %w", err)
}
topPodUIDs := extractTopPodUIDs(pass1Result)
if len(topPodUIDs) == 0 {
return &infrastructuremonitoringtypes.PodsListResponse{
Type: "list",
Records: []infrastructuremonitoringtypes.PodsListRecord{},
Total: 0,
}, nil
}
// Build Pass 2: Get all metrics for the Top N Pod UIDs.
// Since QBV5 supports 1 MetricAggregation per Query, we build a CompositeQuery of len(podQueries)
pass2Filter := applyUIDFilter(copyFilter(req.Filter), topPodUIDs)
pass2Payload := buildPass2Payload(req, mergedGroupBy, pass2Filter, orderQueryName, orderDir)
pass2Result, err := m.querier.QueryRange(ctx, orgID, pass2Payload)
if err != nil {
return nil, fmt.Errorf("pass 2 querying failed: %w", err)
}
records := parsePass2ToRecords(pass2Result)
// Since records is returned out of order due to map merging, sort the records again in Go memory
// according to pass 1 ordering so that limit/offset are correctly portrayed in UI.
// Normally we would sort them, but given we only fetched metrics for top N, parsing guarantees all are fetched.
// We'll leave the sort to the UI or add a sorter here if needed.
return &infrastructuremonitoringtypes.PodsListResponse{
Type: "list",
Records: records,
Total: len(records), // TODO: Real count
}, nil
}
// ---------------------------------------------------------------------------------------------------------------------
// HELPER METHODS
// ---------------------------------------------------------------------------------------------------------------------
func copyFilter(f *qbtypes.Filter) *qbtypes.Filter {
if f != nil {
return f.Copy()
}
return &qbtypes.Filter{}
}
func mergePodsGroupBy(reqTags []qbtypes.GroupByKey) []qbtypes.GroupByKey {
var merged []qbtypes.GroupByKey
seen := make(map[string]bool)
for _, tag := range reqTags {
if !seen[tag.Name] {
merged = append(merged, tag)
seen[tag.Name] = true
}
}
for _, tag := range defaultPodsGroupByTags {
if !seen[tag.Name] {
merged = append(merged, tag)
seen[tag.Name] = true
}
}
return merged
}
func parsePodsOrderBy(orderBy []qbtypes.OrderBy) (metricName string, dir qbtypes.OrderDirection, queryName string) {
metricName = "k8s.pod.cpu.usage"
dir = qbtypes.OrderDirectionDesc
queryName = "A" // default correlates to CPU usage
if len(orderBy) > 0 {
name := orderBy[0].Key.Name
dir = orderBy[0].Direction
switch name {
case "cpu":
metricName = "k8s.pod.cpu.usage"
queryName = "A"
case "cpu_request":
metricName = "k8s.pod.cpu.request"
queryName = "B"
case "cpu_limit":
metricName = "k8s.pod.cpu.limit"
queryName = "C"
case "memory":
metricName = "k8s.pod.memory.usage"
queryName = "D"
case "memory_request":
metricName = "k8s.pod.memory.request"
queryName = "E"
case "memory_limit":
metricName = "k8s.pod.memory.limit"
queryName = "F"
case "restarts":
metricName = "k8s.pod.restarts"
queryName = "G"
default:
metricName = name
for qName, agg := range podQueries {
if agg.MetricName == metricName {
queryName = qName
break
}
}
}
}
return
}
func buildPass1Payload(req *infrastructuremonitoringtypes.PodsListRequest, groupBy []qbtypes.GroupByKey, filter *qbtypes.Filter, orderMetric string, orderDir qbtypes.OrderDirection) *qbtypes.QueryRangeRequest {
// Find the matching aggregation spec for Pass 1
var agg qbtypes.MetricAggregation
for _, a := range podQueries {
if a.MetricName == orderMetric {
agg = a
break
}
}
query := qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
Name: "A",
Signal: telemetrytypes.SignalMetrics,
Offset: req.Offset,
Limit: req.Limit,
GroupBy: groupBy,
Filter: filter,
Order: []qbtypes.OrderBy{{Key: qbtypes.OrderByKey{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "__result_0"}}, Direction: orderDir}},
Aggregations: []qbtypes.MetricAggregation{agg},
}
return &qbtypes.QueryRangeRequest{
SchemaVersion: "v1",
Start: req.Start,
End: req.End,
RequestType: qbtypes.RequestTypeScalar,
CompositeQuery: qbtypes.CompositeQuery{
Queries: []qbtypes.QueryEnvelope{
{Type: qbtypes.QueryTypeBuilder, Spec: query},
},
},
}
}
func extractTopPodUIDs(res *qbtypes.QueryRangeResponse) []string {
var topPodUIDs []string
if len(res.Data.Results) > 0 {
var scalarData *qbtypes.ScalarData
if p, ok := res.Data.Results[0].(*qbtypes.ScalarData); ok {
scalarData = p
} else if v, ok := res.Data.Results[0].(qbtypes.ScalarData); ok {
scalarData = &v
}
if scalarData != nil {
uidIdx := -1
for i, col := range scalarData.Columns {
if col.Name == "k8s.pod.uid" {
uidIdx = i
break
}
}
if uidIdx != -1 {
for _, row := range scalarData.Data {
if uidObj, ok := row[uidIdx].(string); ok && uidObj != "" {
topPodUIDs = append(topPodUIDs, uidObj)
}
}
}
}
}
return topPodUIDs
}
func applyUIDFilter(f *qbtypes.Filter, uids []string) *qbtypes.Filter {
inClause := fmt.Sprintf("k8s.pod.uid IN ('%s')", strings.Join(uids, "','"))
if f.Expression != "" && f.Expression != " " {
f.Expression = fmt.Sprintf("(%s) AND %s", f.Expression, inClause)
} else {
f.Expression = inClause
}
return f
}
func buildPass2Payload(req *infrastructuremonitoringtypes.PodsListRequest, groupBy []qbtypes.GroupByKey, filter *qbtypes.Filter, orderQueryName string, orderDir qbtypes.OrderDirection) *qbtypes.QueryRangeRequest {
var envelopes []qbtypes.QueryEnvelope
for queryName, agg := range podQueries {
q := qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
Name: queryName,
Signal: telemetrytypes.SignalMetrics,
GroupBy: groupBy,
Filter: filter,
Aggregations: []qbtypes.MetricAggregation{agg},
Limit: 100, // Safe fetch for the subset
}
// Apply Ordering only to the relevant subset metric query
if queryName == orderQueryName {
q.Order = []qbtypes.OrderBy{{Key: qbtypes.OrderByKey{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "__result_0"}}, Direction: orderDir}}
}
envelopes = append(envelopes, qbtypes.QueryEnvelope{
Type: qbtypes.QueryTypeBuilder,
Spec: q,
})
}
return &qbtypes.QueryRangeRequest{
SchemaVersion: "v1",
Start: req.Start,
End: req.End,
RequestType: qbtypes.RequestTypeScalar,
CompositeQuery: qbtypes.CompositeQuery{
Queries: envelopes,
},
}
}
func parsePass2ToRecords(res *qbtypes.QueryRangeResponse) []infrastructuremonitoringtypes.PodsListRecord {
var records []infrastructuremonitoringtypes.PodsListRecord
if res == nil || res.Data.Results == nil || len(res.Data.Results) == 0 {
return records
}
// We'll merge all rows across query names (A, B, C...) by k8s.pod.uid
mergedRecordsMap := make(map[string]*infrastructuremonitoringtypes.PodsListRecord)
for _, resultData := range res.Data.Results {
var scalarData *qbtypes.ScalarData
if p, ok := resultData.(*qbtypes.ScalarData); ok {
scalarData = p
} else if v, ok := resultData.(qbtypes.ScalarData); ok {
scalarData = &v
}
if scalarData == nil {
continue
}
colIdxs := make(map[string]int)
for i, col := range scalarData.Columns {
colIdxs[col.Name] = i
}
uidIdx, hasUIDColumn := colIdxs["k8s.pod.uid"]
if !hasUIDColumn {
continue
}
for _, row := range scalarData.Data {
uidStr, ok := row[uidIdx].(string)
if !ok || uidStr == "" {
continue
}
record, exists := mergedRecordsMap[uidStr]
if !exists {
record = &infrastructuremonitoringtypes.PodsListRecord{
PodUID: uidStr,
Meta: make(map[string]string),
}
mergedRecordsMap[uidStr] = record
}
// Merge columns into the record
for colName, idx := range colIdxs {
val := row[idx]
// Store grouping tags as-is with original dot-notation keys
if strings.HasPrefix(colName, "k8s.") {
if strVal, ok := val.(string); ok {
record.Meta[colName] = strVal
}
continue
}
// Apply result mapping based on the query Name (A, B, C, etc)
if colName == "__result_0" {
floatVal := 0.0
if f, ok := val.(float64); ok {
floatVal = f
} else if s, ok := val.(string); ok {
if parsed, err := strconv.ParseFloat(s, 64); err == nil {
floatVal = parsed
}
}
switch scalarData.QueryName {
case "A":
record.PodCPU = floatVal
case "B":
record.PodCPURequest = floatVal
case "C":
record.PodCPULimit = floatVal
case "D":
record.PodMemory = floatVal
case "E":
record.PodMemoryRequest = floatVal
case "F":
record.PodMemoryLimit = floatVal
case "G":
record.RestartCount = int(floatVal)
}
}
}
}
}
for _, v := range mergedRecordsMap {
records = append(records, *v)
}
return records
}

View File

@@ -0,0 +1,21 @@
package infrastructuremonitoring
import (
"context"
"net/http"
"github.com/SigNoz/signoz/pkg/types/infrastructuremonitoringtypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
// Handler exposes HTTP handlers for the infrastructuremonitoring module.
type Handler interface {
HealthCheck(http.ResponseWriter, *http.Request)
GetPodsList(http.ResponseWriter, *http.Request)
}
// Module represents the infrastructuremonitoring module interface.
type Module interface {
HealthCheck(ctx context.Context) (*infrastructuremonitoringtypes.HealthCheckResponse, error)
GetPodsList(ctx context.Context, orgID valuer.UUID, request *infrastructuremonitoringtypes.PodsListRequest) (*infrastructuremonitoringtypes.PodsListResponse, error)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/SigNoz/signoz/pkg/gateway"
"github.com/SigNoz/signoz/pkg/global"
"github.com/SigNoz/signoz/pkg/instrumentation"
"github.com/SigNoz/signoz/pkg/modules/infrastructuremonitoring"
"github.com/SigNoz/signoz/pkg/modules/metricsexplorer"
"github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/prometheus"
@@ -108,6 +109,9 @@ type Config struct {
// MetricsExplorer config
MetricsExplorer metricsexplorer.Config `mapstructure:"metricsexplorer"`
// InfrastructureMonitoring config
InfrastructureMonitoring infrastructuremonitoring.Config `mapstructure:"infrastructuremonitoring"`
// Flagger config
Flagger flagger.Config `mapstructure:"flagger"`
@@ -174,6 +178,7 @@ func NewConfig(ctx context.Context, logger *slog.Logger, resolverConfig config.R
gateway.NewConfigFactory(),
tokenizer.NewConfigFactory(),
metricsexplorer.NewConfigFactory(),
infrastructuremonitoring.NewConfigFactory(),
flagger.NewConfigFactory(),
user.NewConfigFactory(),
}

View File

@@ -16,6 +16,8 @@ import (
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
"github.com/SigNoz/signoz/pkg/modules/fields"
"github.com/SigNoz/signoz/pkg/modules/fields/implfields"
"github.com/SigNoz/signoz/pkg/modules/infrastructuremonitoring"
"github.com/SigNoz/signoz/pkg/modules/infrastructuremonitoring/implinfrastructuremonitoring"
"github.com/SigNoz/signoz/pkg/modules/metricsexplorer"
"github.com/SigNoz/signoz/pkg/modules/metricsexplorer/implmetricsexplorer"
"github.com/SigNoz/signoz/pkg/modules/quickfilter"
@@ -36,22 +38,23 @@ import (
)
type Handlers struct {
SavedView savedview.Handler
Apdex apdex.Handler
Dashboard dashboard.Handler
QuickFilter quickfilter.Handler
TraceFunnel tracefunnel.Handler
RawDataExport rawdataexport.Handler
SpanPercentile spanpercentile.Handler
Services services.Handler
MetricsExplorer metricsexplorer.Handler
Global global.Handler
FlaggerHandler flagger.Handler
GatewayHandler gateway.Handler
Fields fields.Handler
AuthzHandler authz.Handler
ZeusHandler zeus.Handler
QuerierHandler querier.Handler
SavedView savedview.Handler
Apdex apdex.Handler
Dashboard dashboard.Handler
QuickFilter quickfilter.Handler
TraceFunnel tracefunnel.Handler
RawDataExport rawdataexport.Handler
SpanPercentile spanpercentile.Handler
Services services.Handler
MetricsExplorer metricsexplorer.Handler
InfrastructureMonitoring infrastructuremonitoring.Handler
Global global.Handler
FlaggerHandler flagger.Handler
GatewayHandler gateway.Handler
Fields fields.Handler
AuthzHandler authz.Handler
ZeusHandler zeus.Handler
QuerierHandler querier.Handler
}
func NewHandlers(
@@ -68,21 +71,22 @@ func NewHandlers(
zeusService zeus.Zeus,
) Handlers {
return Handlers{
SavedView: implsavedview.NewHandler(modules.SavedView),
Apdex: implapdex.NewHandler(modules.Apdex),
Dashboard: impldashboard.NewHandler(modules.Dashboard, providerSettings),
QuickFilter: implquickfilter.NewHandler(modules.QuickFilter),
TraceFunnel: impltracefunnel.NewHandler(modules.TraceFunnel),
RawDataExport: implrawdataexport.NewHandler(modules.RawDataExport),
Services: implservices.NewHandler(modules.Services),
MetricsExplorer: implmetricsexplorer.NewHandler(modules.MetricsExplorer),
SpanPercentile: implspanpercentile.NewHandler(modules.SpanPercentile),
Global: signozglobal.NewHandler(global),
FlaggerHandler: flagger.NewHandler(flaggerService),
GatewayHandler: gateway.NewHandler(gatewayService),
Fields: implfields.NewHandler(providerSettings, telemetryMetadataStore),
AuthzHandler: signozauthzapi.NewHandler(authz),
ZeusHandler: zeus.NewHandler(zeusService, licensing),
QuerierHandler: querierHandler,
SavedView: implsavedview.NewHandler(modules.SavedView),
Apdex: implapdex.NewHandler(modules.Apdex),
Dashboard: impldashboard.NewHandler(modules.Dashboard, providerSettings),
QuickFilter: implquickfilter.NewHandler(modules.QuickFilter),
TraceFunnel: impltracefunnel.NewHandler(modules.TraceFunnel),
RawDataExport: implrawdataexport.NewHandler(modules.RawDataExport),
Services: implservices.NewHandler(modules.Services),
MetricsExplorer: implmetricsexplorer.NewHandler(modules.MetricsExplorer),
InfrastructureMonitoring: implinfrastructuremonitoring.NewHandler(modules.InfrastructureMonitoring),
SpanPercentile: implspanpercentile.NewHandler(modules.SpanPercentile),
Global: signozglobal.NewHandler(global),
FlaggerHandler: flagger.NewHandler(flaggerService),
GatewayHandler: gateway.NewHandler(gatewayService),
Fields: implfields.NewHandler(providerSettings, telemetryMetadataStore),
AuthzHandler: signozauthzapi.NewHandler(authz),
ZeusHandler: zeus.NewHandler(zeusService, licensing),
QuerierHandler: querierHandler,
}
}

View File

@@ -13,6 +13,8 @@ import (
"github.com/SigNoz/signoz/pkg/modules/authdomain"
"github.com/SigNoz/signoz/pkg/modules/authdomain/implauthdomain"
"github.com/SigNoz/signoz/pkg/modules/dashboard"
"github.com/SigNoz/signoz/pkg/modules/infrastructuremonitoring"
"github.com/SigNoz/signoz/pkg/modules/infrastructuremonitoring/implinfrastructuremonitoring"
"github.com/SigNoz/signoz/pkg/modules/metricsexplorer"
"github.com/SigNoz/signoz/pkg/modules/metricsexplorer/implmetricsexplorer"
"github.com/SigNoz/signoz/pkg/modules/organization"
@@ -49,23 +51,24 @@ import (
)
type Modules struct {
OrgGetter organization.Getter
OrgSetter organization.Setter
Preference preference.Module
User user.Module
UserGetter user.Getter
SavedView savedview.Module
Apdex apdex.Module
Dashboard dashboard.Module
QuickFilter quickfilter.Module
TraceFunnel tracefunnel.Module
RawDataExport rawdataexport.Module
AuthDomain authdomain.Module
Session session.Module
Services services.Module
SpanPercentile spanpercentile.Module
MetricsExplorer metricsexplorer.Module
Promote promote.Module
OrgGetter organization.Getter
OrgSetter organization.Setter
Preference preference.Module
User user.Module
UserGetter user.Getter
SavedView savedview.Module
Apdex apdex.Module
Dashboard dashboard.Module
QuickFilter quickfilter.Module
TraceFunnel tracefunnel.Module
RawDataExport rawdataexport.Module
AuthDomain authdomain.Module
Session session.Module
Services services.Module
SpanPercentile spanpercentile.Module
MetricsExplorer metricsexplorer.Module
InfrastructureMonitoring infrastructuremonitoring.Module
Promote promote.Module
}
func NewModules(
@@ -93,22 +96,23 @@ func NewModules(
ruleStore := sqlrulestore.NewRuleStore(sqlstore, queryParser, providerSettings)
return Modules{
OrgGetter: orgGetter,
OrgSetter: orgSetter,
Preference: implpreference.NewModule(implpreference.NewStore(sqlstore), preferencetypes.NewAvailablePreference()),
SavedView: implsavedview.NewModule(sqlstore),
Apdex: implapdex.NewModule(sqlstore),
Dashboard: dashboard,
User: user,
UserGetter: userGetter,
QuickFilter: quickfilter,
TraceFunnel: impltracefunnel.NewModule(impltracefunnel.NewStore(sqlstore)),
RawDataExport: implrawdataexport.NewModule(querier),
AuthDomain: implauthdomain.NewModule(implauthdomain.NewStore(sqlstore), authNs),
Session: implsession.NewModule(providerSettings, authNs, user, userGetter, implauthdomain.NewModule(implauthdomain.NewStore(sqlstore), authNs), tokenizer, orgGetter),
SpanPercentile: implspanpercentile.NewModule(querier, providerSettings),
Services: implservices.NewModule(querier, telemetryStore),
MetricsExplorer: implmetricsexplorer.NewModule(telemetryStore, telemetryMetadataStore, cache, ruleStore, dashboard, providerSettings, config.MetricsExplorer),
Promote: implpromote.NewModule(telemetryMetadataStore, telemetryStore),
OrgGetter: orgGetter,
OrgSetter: orgSetter,
Preference: implpreference.NewModule(implpreference.NewStore(sqlstore), preferencetypes.NewAvailablePreference()),
SavedView: implsavedview.NewModule(sqlstore),
Apdex: implapdex.NewModule(sqlstore),
Dashboard: dashboard,
User: user,
UserGetter: userGetter,
QuickFilter: quickfilter,
TraceFunnel: impltracefunnel.NewModule(impltracefunnel.NewStore(sqlstore)),
RawDataExport: implrawdataexport.NewModule(querier),
AuthDomain: implauthdomain.NewModule(implauthdomain.NewStore(sqlstore), authNs),
Session: implsession.NewModule(providerSettings, authNs, user, userGetter, implauthdomain.NewModule(implauthdomain.NewStore(sqlstore), authNs), tokenizer, orgGetter),
SpanPercentile: implspanpercentile.NewModule(querier, providerSettings),
Services: implservices.NewModule(querier, telemetryStore),
MetricsExplorer: implmetricsexplorer.NewModule(telemetryStore, telemetryMetadataStore, cache, ruleStore, dashboard, providerSettings, config.MetricsExplorer),
InfrastructureMonitoring: implinfrastructuremonitoring.NewModule(telemetryStore, querier, telemetryMetadataStore, cache, providerSettings, config.InfrastructureMonitoring),
Promote: implpromote.NewModule(telemetryMetadataStore, telemetryStore),
}
}

View File

@@ -18,6 +18,7 @@ import (
"github.com/SigNoz/signoz/pkg/modules/authdomain"
"github.com/SigNoz/signoz/pkg/modules/dashboard"
"github.com/SigNoz/signoz/pkg/modules/fields"
"github.com/SigNoz/signoz/pkg/modules/infrastructuremonitoring"
"github.com/SigNoz/signoz/pkg/modules/metricsexplorer"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/modules/preference"
@@ -54,6 +55,9 @@ func NewOpenAPI(ctx context.Context, instrumentation instrumentation.Instrumenta
struct{ dashboard.Module }{},
struct{ dashboard.Handler }{},
struct{ metricsexplorer.Handler }{},
struct {
infrastructuremonitoring.Handler
}{},
struct{ gateway.Handler }{},
struct{ fields.Handler }{},
struct{ authz.Handler }{},

View File

@@ -250,6 +250,7 @@ func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.Au
modules.Dashboard,
handlers.Dashboard,
handlers.MetricsExplorer,
handlers.InfrastructureMonitoring,
handlers.GatewayHandler,
handlers.Fields,
handlers.AuthzHandler,

View File

@@ -0,0 +1,7 @@
package infrastructuremonitoringtypes
// HealthCheckResponse represents the response structure for the infrastructure monitoring health check API.
type HealthCheckResponse struct {
Status string `json:"status"`
Message string `json:"message"`
}

View File

@@ -0,0 +1,35 @@
package infrastructuremonitoringtypes
import (
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
)
type PodsListRequest struct {
Start uint64 `json:"start"`
End uint64 `json:"end"`
Filter *qbtypes.Filter `json:"filter,omitempty"`
GroupBy []qbtypes.GroupByKey `json:"groupBy,omitempty"`
OrderBy []qbtypes.OrderBy `json:"orderBy,omitempty"`
Offset int `json:"offset"`
Limit int `json:"limit"`
}
type PodsListResponse struct {
Type string `json:"type"`
Records []PodsListRecord `json:"records"`
Total int `json:"total"`
SentAnyHostMetricsData bool `json:"sentAnyHostMetricsData"`
IsSendingK8SAgentMetrics bool `json:"isSendingK8SAgentMetrics"`
}
type PodsListRecord 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"`
}