Publish ITWorx Pulse source
Public source validation / validate (push) Failing after 3m8s

This commit is contained in:
ITWorx Pulse release export
2026-09-03 02:09:19 +02:00
commit bd774932d5
614 changed files with 77116 additions and 0 deletions
+180
View File
@@ -0,0 +1,180 @@
package promqlbinding
import (
"context"
"fmt"
"regexp"
"sort"
"strconv"
"strings"
"github.com/itworx/pulse/internal/auth"
"github.com/itworx/pulse/internal/datasource"
"github.com/itworx/pulse/internal/metriccatalog"
"github.com/itworx/pulse/internal/queryplan"
)
const MaxCompiledQueryLength = 4096
var placeholderPattern = regexp.MustCompile(`\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}`)
var durationPattern = regexp.MustCompile(`^(?:[0-9]+(?:ms|s|m|h|d|w|y))+$`)
var labelValuePattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$`)
var rawQueryLimit = 4096
type Error struct {
Code string
Field string
Detail string
}
func (e Error) Error() string {
if e.Field == "" {
return e.Code + ": " + e.Detail
}
return e.Code + " (" + e.Field + "): " + e.Detail
}
func Compile(definition metriccatalog.Definition, values map[string]string) (string, error) {
if err := definition.Validate(); err != nil {
return "", fmt.Errorf("invalid metric binding: %w", err)
}
matches := placeholderPattern.FindAllStringSubmatchIndex(definition.QueryTemplate, -1)
known := make(map[string]struct{}, len(matches))
for _, match := range matches {
name := definition.QueryTemplate[match[2]:match[3]]
known[name] = struct{}{}
if _, ok := values[name]; !ok {
return "", Error{Code: "PROMQL_BINDING_VALUE_REQUIRED", Field: name, Detail: "template value is required"}
}
if name == "window" {
if !durationPattern.MatchString(values[name]) {
return "", Error{Code: "PROMQL_WINDOW_INVALID", Field: name, Detail: "window must be a Prometheus duration"}
}
} else {
if !contains(definition.AllowedLabels, name) {
return "", Error{Code: "PROMQL_LABEL_NOT_ALLOWED", Field: name, Detail: "template label is not in the metric contract"}
}
if !labelValuePattern.MatchString(values[name]) {
return "", Error{Code: "PROMQL_LABEL_VALUE_INVALID", Field: name, Detail: "label value contains unsupported characters"}
}
}
}
for name := range values {
if _, ok := known[name]; !ok {
return "", Error{Code: "PROMQL_BINDING_VALUE_UNKNOWN", Field: name, Detail: "binding value is not used by the approved template"}
}
}
var builder strings.Builder
last := 0
for _, match := range matches {
builder.WriteString(definition.QueryTemplate[last:match[0]])
name := definition.QueryTemplate[match[2]:match[3]]
value := values[name]
if name != "window" {
value = strconv.Quote(value)
}
builder.WriteString(value)
last = match[1]
}
builder.WriteString(definition.QueryTemplate[last:])
query := builder.String()
if len(query) == 0 || len(query) > MaxCompiledQueryLength || strings.Contains(query, "{{") {
return "", Error{Code: "PROMQL_QUERY_LIMIT", Field: "queryTemplate", Detail: "compiled query exceeds bounds"}
}
return query, nil
}
func CompilePlan(plan queryplan.Plan) (string, error) {
values := make(map[string]string, len(plan.ResolvedScope)+1)
for label, value := range plan.ResolvedScope {
values[label] = value
}
// Gauges such as memory utilization have no range selector and therefore no
// approved {{window}} placeholder. Compile rejects every surplus binding as
// a safety boundary, so only supply the derived duration when the catalog
// template explicitly consumes it.
if strings.Contains(plan.Metric.QueryTemplate, "{{window}}") {
values["window"] = WindowForStep(plan.Request.Range.StepSeconds)
}
return Compile(plan.Metric, values)
}
func WindowForStep(stepSeconds int) string {
// A range-vector needs at least two source samples. The dashboard step is
// allowed to be 15 seconds, equal to a common Prometheus scrape interval;
// using that value verbatim makes rate()/increase() intermittently empty.
// Keep graph resolution independent and use a conservative bounded lookback.
if stepSeconds < 60 {
stepSeconds = 60
}
if stepSeconds%3600 == 0 {
return fmt.Sprintf("%dh", stepSeconds/3600)
}
if stepSeconds%60 == 0 {
return fmt.Sprintf("%dm", stepSeconds/60)
}
return fmt.Sprintf("%ds", stepSeconds)
}
type Binding struct {
Definition metriccatalog.Definition
RequiredCapabilities []string
Priority int
ID string
}
func SelectBinding(candidates []Binding, capabilities datasource.CapabilitySet) (Binding, error) {
if len(candidates) == 0 {
return Binding{}, Error{Code: "PROMQL_BINDING_UNAVAILABLE", Detail: "no approved binding exists"}
}
ordered := append([]Binding(nil), candidates...)
sort.SliceStable(ordered, func(i, j int) bool {
if ordered[i].Priority != ordered[j].Priority {
return ordered[i].Priority > ordered[j].Priority
}
if ordered[i].Definition.Version != ordered[j].Definition.Version {
return ordered[i].Definition.Version > ordered[j].Definition.Version
}
return ordered[i].ID < ordered[j].ID
})
for _, candidate := range ordered {
if candidate.Definition.Validate() != nil {
continue
}
if allCapabilitiesEnabled(candidate.RequiredCapabilities, capabilities) {
return candidate, nil
}
}
return ordered[0], Error{Code: "PROMQL_BINDING_UNAVAILABLE", Detail: "no binding has all required capabilities"}
}
func allCapabilitiesEnabled(required []string, capabilities datasource.CapabilitySet) bool {
for _, requiredID := range required {
capability, ok := capabilities.Find(requiredID)
if !ok || capability.State != datasource.CapabilityEnabled {
return false
}
}
return true
}
func ValidateRawQuery(ctx context.Context, query string) (string, error) {
if err := ctx.Err(); err != nil {
return "", err
}
principal, ok := auth.PrincipalFromContext(ctx)
if !ok || !auth.Allows(principal.Role, auth.PermissionAdmin) {
return "", Error{Code: "PROMQL_RAW_UNAUTHORIZED", Detail: "raw PromQL requires administrator permission"}
}
query = strings.TrimSpace(query)
if query == "" || len(query) > rawQueryLimit || strings.ContainsRune(query, '\x00') {
return "", Error{Code: "PROMQL_RAW_INVALID", Detail: "raw PromQL is empty or exceeds bounds"}
}
return query, nil
}
func contains(values []string, target string) bool {
for _, value := range values {
if value == target {
return true
}
}
return false
}
+143
View File
@@ -0,0 +1,143 @@
package promqlbinding
import (
"context"
"errors"
"strings"
"testing"
"time"
"github.com/itworx/pulse/internal/auth"
"github.com/itworx/pulse/internal/datasource"
"github.com/itworx/pulse/internal/metriccatalog"
"github.com/itworx/pulse/internal/queryplan"
)
func TestCompileQuotesLabelValuesAndUsesBoundedWindow(t *testing.T) {
registry, err := metriccatalog.DefaultRegistry()
if err != nil {
t.Fatal(err)
}
definition, ok := registry.Find("host.cpu.utilization")
if !ok {
t.Fatal("seed metric missing")
}
compiled, err := Compile(definition, map[string]string{"instance": "server-1", "window": "5m"})
if err != nil {
t.Fatal(err)
}
if !strings.Contains(compiled, `instance="server-1"`) || !strings.Contains(compiled, "[5m]") || strings.Contains(compiled, "{{") {
t.Fatalf("compiled=%s", compiled)
}
}
func TestCompileRejectsInjectionMissingAndUnknownValues(t *testing.T) {
registry, err := metriccatalog.DefaultRegistry()
if err != nil {
t.Fatal(err)
}
definition, _ := registry.Find("host.cpu.utilization")
cases := []map[string]string{
{"instance": `server-1" or up`, "window": "5m"},
{"instance": "server-1"},
{"instance": "server-1", "window": "5m", "groupBy": "container"},
{"instance": "server-1", "window": "5m;drop"},
}
for index, values := range cases {
if _, err := Compile(definition, values); err == nil {
t.Fatalf("case %d accepted", index)
}
}
}
func TestCompilePlanDoesNotAppendArbitraryGrouping(t *testing.T) {
registry, err := metriccatalog.DefaultRegistry()
if err != nil {
t.Fatal(err)
}
planner := queryplan.NewPlanner(registry, queryplan.Limits{})
request := queryplan.Request{Metric: "container.cpu.utilization", Scope: map[string]string{"containerId": "media_server"}, Range: queryplan.Range{From: now().Add(-3600), To: now(), StepSeconds: 60}, GroupBy: []string{"container"}}
plan, err := planner.Plan(auth.WithPrincipal(context.Background(), auth.Principal{Subject: "viewer", Role: auth.RoleViewer}), request)
if err != nil {
t.Fatal(err)
}
compiled, err := CompilePlan(plan)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(compiled, `name="media_server"`) || strings.Contains(compiled, `container="media_server"`) || strings.Contains(compiled, "groupBy") {
t.Fatalf("compiled=%s", compiled)
}
if WindowForStep(15) != "1m" || WindowForStep(60) != "1m" || WindowForStep(90) != "90s" || WindowForStep(3600) != "1h" {
t.Fatal("unexpected window formatting")
}
}
func TestCompilePlanOmitsWindowForInstantGaugeTemplate(t *testing.T) {
registry, err := metriccatalog.DefaultRegistry()
if err != nil {
t.Fatal(err)
}
planner := queryplan.NewPlanner(registry, queryplan.Limits{})
request := queryplan.Request{Metric: "host.memory.utilization", Scope: map[string]string{"serverId": "smoke-host"}, Range: queryplan.Range{From: now().Add(-5 * time.Minute), To: now(), StepSeconds: 15}}
plan, err := planner.Plan(auth.WithPrincipal(context.Background(), auth.Principal{Subject: "viewer", Role: auth.RoleViewer}), request)
if err != nil {
t.Fatal(err)
}
compiled, err := CompilePlan(plan)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(compiled, `instance="smoke-host"`) || strings.Contains(compiled, "{{") || strings.Contains(compiled, "[15s]") {
t.Fatalf("compiled=%s", compiled)
}
}
func TestBindingFallbackIsDeterministic(t *testing.T) {
registry, err := metriccatalog.DefaultRegistry()
if err != nil {
t.Fatal(err)
}
definition, _ := registry.Find("host.cpu.utilization")
first := Binding{ID: "z", Definition: definition, RequiredCapabilities: []string{"cap-z"}, Priority: 1}
second := Binding{ID: "a", Definition: definition, RequiredCapabilities: []string{"cap-a"}, Priority: 1}
second.Definition.Version = 2
capabilities := datasource.CapabilitySet{{ID: "cap-a", Version: "v1", State: datasource.CapabilityEnabled}}
selected, err := SelectBinding([]Binding{first, second}, capabilities)
if err != nil {
t.Fatal(err)
}
if selected.ID != "a" {
t.Fatalf("selected=%s", selected.ID)
}
fallback, err := SelectBinding([]Binding{second, first}, nil)
if err == nil || fallback.ID != "a" {
t.Fatalf("fallback=%+v err=%v", fallback, err)
}
}
func TestRawQueryRequiresAdminAndHonorsCancellation(t *testing.T) {
viewer := auth.WithPrincipal(context.Background(), auth.Principal{Subject: "viewer", Role: auth.RoleViewer})
if _, err := ValidateRawQuery(viewer, "up"); err == nil {
t.Fatal("viewer raw query accepted")
}
admin := auth.WithPrincipal(context.Background(), auth.Principal{Subject: "admin", Role: auth.RoleAdministrator})
query, err := ValidateRawQuery(admin, " up ")
if err != nil || query != "up" {
t.Fatalf("query=%q err=%v", query, err)
}
canceled, cancel := context.WithCancel(admin)
cancel()
if _, err := ValidateRawQuery(canceled, "up"); !errors.Is(err, context.Canceled) {
t.Fatalf("cancel=%v", err)
}
}
func TestCompileRejectsOversizedCompiledQuery(t *testing.T) {
definition := metriccatalog.Definition{SemanticName: "host.cpu.utilization", Version: 1, Description: "cpu", Unit: "percent", ValueKind: "gauge", SourceKind: "prometheus", QueryTemplate: strings.Repeat("up ", 2100) + "{{instance}}", AllowedLabels: []string{"instance"}, CardinalityBudget: 1, Limits: metriccatalog.Limits{MaxRangeSeconds: 60, MaxSeries: 1, MaxPoints: 10, TimeoutSeconds: 1}, AllowedVisualizations: []string{"stat"}, FreshnessSeconds: 1}
if _, err := Compile(definition, map[string]string{"instance": "server-1"}); err == nil {
t.Fatal("oversized query accepted")
}
}
func now() time.Time { return time.Date(2026, 8, 1, 12, 0, 0, 0, time.UTC) }