mirror of
https://github.com/SigNoz/signoz.git
synced 2026-07-31 18:20:36 +01:00
Compare commits
2 Commits
issue_5015
...
fix/rule-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98745423d2 | ||
|
|
57e2d25a6c |
6
.github/workflows/e2eci.yaml
vendored
6
.github/workflows/e2eci.yaml
vendored
@@ -45,15 +45,9 @@ jobs:
|
||||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))) && contains(github.event.pull_request.labels.*.name, 'safe-to-e2e')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
SIGNOZ_BUILDX_GHA_SCOPE: signoz-e2e
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: setup-buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: expose-gha-runtime
|
||||
uses: crazy-max/ghaction-github-runtime@04d248b84655b509d8c44dc1d6f990c879747487
|
||||
- name: python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
|
||||
6
.github/workflows/integrationci.yaml
vendored
6
.github/workflows/integrationci.yaml
vendored
@@ -80,15 +80,9 @@ jobs:
|
||||
((github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
|
||||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))) && contains(github.event.pull_request.labels.*.name, 'safe-to-integrate')
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SIGNOZ_BUILDX_GHA_SCOPE: signoz-integration
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: setup-buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: expose-gha-runtime
|
||||
uses: crazy-max/ghaction-github-runtime@04d248b84655b509d8c44dc1d6f990c879747487
|
||||
- name: python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
|
||||
19
Makefile
19
Makefile
@@ -34,8 +34,6 @@ DOCKER_BUILD_ARCHS_ENTERPRISE = $(addprefix docker-build-enterprise-,$(ARCHS))
|
||||
DOCKERFILE_ENTERPRISE = $(SRC)/cmd/enterprise/Dockerfile
|
||||
DOCKER_REGISTRY_ENTERPRISE ?= docker.io/signoz/signoz
|
||||
JS_BUILD_CONTEXT = $(SRC)/frontend
|
||||
DOCKER_BUILDX_PRUNE_FLAGS ?= --force
|
||||
SIGNOZ_INTEGRATION_BUILD_CACHE_DIR ?= /tmp/signoz-integration-buildx-cache
|
||||
|
||||
##############################################################
|
||||
# directories
|
||||
@@ -212,7 +210,7 @@ py-lint: ## Run ruff check across the shared tests project
|
||||
|
||||
.PHONY: py-test-setup
|
||||
py-test-setup: ## Bring up the shared SigNoz backend used by integration and e2e tests
|
||||
@cd tests && SIGNOZ_INTEGRATION_BUILD_CACHE_DIR=$(SIGNOZ_INTEGRATION_BUILD_CACHE_DIR) uv run pytest --basetemp=./tmp/ -vv --reuse --capture=no integration/bootstrap/setup.py::test_setup
|
||||
@cd tests && uv run pytest --basetemp=./tmp/ -vv --reuse --capture=no integration/bootstrap/setup.py::test_setup
|
||||
|
||||
.PHONY: py-test-teardown
|
||||
py-test-teardown: ## Tear down the shared SigNoz backend
|
||||
@@ -231,21 +229,6 @@ py-clean: ## Clear all pycache and pytest cache from tests directory recursively
|
||||
@find tests -type f -name "*.pyo" -delete 2>/dev/null || true
|
||||
@echo ">> python cache cleaned"
|
||||
|
||||
.PHONY: py-docker-clean
|
||||
py-docker-clean: ## Remove Docker image and build caches used by python integration tests
|
||||
@echo ">> removing SigNoz integration test image"
|
||||
@docker image rm -f signoz:integration 2>/dev/null || true
|
||||
@echo ">> removing local integration buildx cache directories"
|
||||
@rm -rf $(SIGNOZ_INTEGRATION_BUILD_CACHE_DIR) $(SIGNOZ_INTEGRATION_BUILD_CACHE_DIR)-next
|
||||
@echo ">> pruning docker buildx cache with flags: $(DOCKER_BUILDX_PRUNE_FLAGS)"
|
||||
@docker buildx prune $(DOCKER_BUILDX_PRUNE_FLAGS)
|
||||
|
||||
.PHONY: py-test-clean
|
||||
py-test-clean: ## Tear down python test stack and remove python/Docker test caches
|
||||
@$(MAKE) py-test-teardown || true
|
||||
@$(MAKE) py-clean
|
||||
@$(MAKE) py-docker-clean
|
||||
|
||||
|
||||
##############################################################
|
||||
# generate commands
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# syntax=docker/dockerfile:1.13
|
||||
|
||||
FROM golang:1.25-bookworm
|
||||
|
||||
ARG OS="linux"
|
||||
@@ -23,8 +21,7 @@ RUN set -eux; \
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
go mod download
|
||||
RUN go mod download
|
||||
|
||||
COPY ./cmd/ ./cmd/
|
||||
COPY ./ee/ ./ee/
|
||||
@@ -32,9 +29,7 @@ COPY ./pkg/ ./pkg/
|
||||
COPY ./templates /root/templates
|
||||
|
||||
COPY Makefile Makefile
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
TARGET_DIR=/root ARCHS=${TARGETARCH} ZEUS_URL=${ZEUSURL} LICENSE_URL=${ZEUSURL}/api/v1 make go-build-enterprise-race
|
||||
RUN TARGET_DIR=/root ARCHS=${TARGETARCH} ZEUS_URL=${ZEUSURL} LICENSE_URL=${ZEUSURL}/api/v1 make go-build-enterprise-race
|
||||
RUN mv /root/linux-${TARGETARCH}/signoz /root/signoz
|
||||
|
||||
RUN chmod 755 /root /root/signoz
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# syntax=docker/dockerfile:1.13
|
||||
|
||||
FROM node:22-bookworm AS build
|
||||
|
||||
WORKDIR /opt/
|
||||
@@ -32,8 +30,7 @@ RUN set -eux; \
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
go mod download
|
||||
RUN go mod download
|
||||
|
||||
COPY ./cmd/ ./cmd/
|
||||
COPY ./ee/ ./ee/
|
||||
@@ -41,9 +38,7 @@ COPY ./pkg/ ./pkg/
|
||||
COPY ./templates /root/templates
|
||||
|
||||
COPY Makefile Makefile
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
TARGET_DIR=/root ARCHS=${TARGETARCH} ZEUS_URL=${ZEUSURL} LICENSE_URL=${ZEUSURL}/api/v1 make go-build-enterprise-race
|
||||
RUN TARGET_DIR=/root ARCHS=${TARGETARCH} ZEUS_URL=${ZEUSURL} LICENSE_URL=${ZEUSURL}/api/v1 make go-build-enterprise-race
|
||||
RUN mv /root/linux-${TARGETARCH}/signoz /root/signoz
|
||||
|
||||
COPY --from=build /opt/build ./web/
|
||||
|
||||
@@ -7430,6 +7430,8 @@ components:
|
||||
- below
|
||||
- equal
|
||||
- not_equal
|
||||
- above_or_equal
|
||||
- below_or_equal
|
||||
- outside_bounds
|
||||
type: string
|
||||
RuletypesCumulativeSchedule:
|
||||
|
||||
@@ -8479,6 +8479,8 @@ export enum RuletypesCompareOperatorDTO {
|
||||
below = 'below',
|
||||
equal = 'equal',
|
||||
not_equal = 'not_equal',
|
||||
above_or_equal = 'above_or_equal',
|
||||
below_or_equal = 'below_or_equal',
|
||||
outside_bounds = 'outside_bounds',
|
||||
}
|
||||
export interface RuletypesBasicRuleThresholdDTO {
|
||||
|
||||
@@ -66,6 +66,10 @@ function ThresholdItem({
|
||||
return '=';
|
||||
case AlertThresholdOperator.IS_NOT_EQUAL_TO:
|
||||
return '!=';
|
||||
case AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO:
|
||||
return '>=';
|
||||
case AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO:
|
||||
return '<=';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -83,6 +83,10 @@ const getOperatorWord = (op: AlertThresholdOperator): string => {
|
||||
return 'equal';
|
||||
case AlertThresholdOperator.IS_NOT_EQUAL_TO:
|
||||
return 'not equal';
|
||||
case AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO:
|
||||
return 'equal or exceed';
|
||||
case AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO:
|
||||
return 'equal or fall below';
|
||||
default:
|
||||
return 'exceed';
|
||||
}
|
||||
@@ -98,6 +102,10 @@ const getThresholdValue = (op: AlertThresholdOperator): number => {
|
||||
return 100;
|
||||
case AlertThresholdOperator.IS_NOT_EQUAL_TO:
|
||||
return 0;
|
||||
case AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO:
|
||||
return 80;
|
||||
case AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO:
|
||||
return 50;
|
||||
default:
|
||||
return 80;
|
||||
}
|
||||
@@ -116,6 +124,8 @@ const getDataPoints = (
|
||||
[AlertThresholdOperator.IS_EQUAL_TO]: [95, 100, 105, 90, 100],
|
||||
[AlertThresholdOperator.IS_NOT_EQUAL_TO]: [5, 0, 10, 15, 0],
|
||||
[AlertThresholdOperator.IS_ABOVE]: [75, 85, 90, 78, 95],
|
||||
[AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO]: [75, 80, 90, 78, 95],
|
||||
[AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO]: [60, 50, 40, 55, 35],
|
||||
[AlertThresholdOperator.ABOVE_BELOW]: [75, 85, 90, 78, 95],
|
||||
},
|
||||
[AlertThresholdMatchType.ALL_THE_TIME]: {
|
||||
@@ -123,6 +133,8 @@ const getDataPoints = (
|
||||
[AlertThresholdOperator.IS_EQUAL_TO]: [100, 100, 100, 100, 100],
|
||||
[AlertThresholdOperator.IS_NOT_EQUAL_TO]: [5, 10, 15, 8, 12],
|
||||
[AlertThresholdOperator.IS_ABOVE]: [85, 87, 90, 88, 95],
|
||||
[AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO]: [80, 87, 90, 88, 95],
|
||||
[AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO]: [50, 40, 35, 42, 38],
|
||||
[AlertThresholdOperator.ABOVE_BELOW]: [85, 87, 90, 88, 95],
|
||||
},
|
||||
[AlertThresholdMatchType.ON_AVERAGE]: {
|
||||
@@ -130,6 +142,8 @@ const getDataPoints = (
|
||||
[AlertThresholdOperator.IS_EQUAL_TO]: [95, 105, 100, 95, 105],
|
||||
[AlertThresholdOperator.IS_NOT_EQUAL_TO]: [5, 10, 15, 8, 12],
|
||||
[AlertThresholdOperator.IS_ABOVE]: [75, 85, 90, 78, 95],
|
||||
[AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO]: [70, 85, 90, 75, 80],
|
||||
[AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO]: [60, 40, 55, 45, 50],
|
||||
[AlertThresholdOperator.ABOVE_BELOW]: [75, 85, 90, 78, 95],
|
||||
},
|
||||
[AlertThresholdMatchType.IN_TOTAL]: {
|
||||
@@ -137,6 +151,8 @@ const getDataPoints = (
|
||||
[AlertThresholdOperator.IS_EQUAL_TO]: [20, 20, 20, 20, 20],
|
||||
[AlertThresholdOperator.IS_NOT_EQUAL_TO]: [10, 15, 25, 5, 30],
|
||||
[AlertThresholdOperator.IS_ABOVE]: [10, 15, 25, 5, 30],
|
||||
[AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO]: [10, 15, 25, 5, 25],
|
||||
[AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO]: [8, 5, 10, 12, 15],
|
||||
[AlertThresholdOperator.ABOVE_BELOW]: [10, 15, 25, 5, 30],
|
||||
},
|
||||
[AlertThresholdMatchType.LAST]: {
|
||||
@@ -144,6 +160,8 @@ const getDataPoints = (
|
||||
[AlertThresholdOperator.IS_EQUAL_TO]: [75, 85, 90, 78, 100],
|
||||
[AlertThresholdOperator.IS_NOT_EQUAL_TO]: [75, 85, 90, 78, 25],
|
||||
[AlertThresholdOperator.IS_ABOVE]: [75, 85, 90, 78, 95],
|
||||
[AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO]: [75, 85, 90, 78, 80],
|
||||
[AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO]: [75, 85, 90, 78, 50],
|
||||
[AlertThresholdOperator.ABOVE_BELOW]: [75, 85, 90, 78, 95],
|
||||
},
|
||||
};
|
||||
@@ -157,6 +175,8 @@ const getTooltipOperatorSymbol = (op: AlertThresholdOperator): string => {
|
||||
[AlertThresholdOperator.IS_BELOW]: '<',
|
||||
[AlertThresholdOperator.IS_EQUAL_TO]: '=',
|
||||
[AlertThresholdOperator.IS_NOT_EQUAL_TO]: '!=',
|
||||
[AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO]: '>=',
|
||||
[AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO]: '<=',
|
||||
[AlertThresholdOperator.ABOVE_BELOW]: '>',
|
||||
};
|
||||
return symbolMap[op] || '>';
|
||||
@@ -252,6 +272,10 @@ export const getMatchTypeTooltip = (
|
||||
return p === thresholdValue;
|
||||
case AlertThresholdOperator.IS_NOT_EQUAL_TO:
|
||||
return p !== thresholdValue;
|
||||
case AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO:
|
||||
return p >= thresholdValue;
|
||||
case AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO:
|
||||
return p <= thresholdValue;
|
||||
default:
|
||||
return p > thresholdValue;
|
||||
}
|
||||
@@ -294,7 +318,8 @@ export const getMatchTypeTooltip = (
|
||||
matchType={matchType}
|
||||
>
|
||||
Alert triggers (all points {operatorWord} {thresholdValue})<br />
|
||||
If any point was {thresholdValue}, no alert would fire
|
||||
If any point didn't {operatorWord} {thresholdValue}, no alert would
|
||||
fire
|
||||
</TooltipExample>
|
||||
<TooltipLink />
|
||||
</TooltipContent>
|
||||
|
||||
@@ -532,7 +532,7 @@ describe('Footer utils', () => {
|
||||
['symbol', '>', 'at_least_once'],
|
||||
['literal', 'above', 'at_least_once'],
|
||||
['short', 'eq', 'avg'],
|
||||
['UI-unexposed', 'above_or_equal', 'at_least_once'],
|
||||
['inclusive', 'above_or_equal', 'at_least_once'],
|
||||
])(
|
||||
'round-trips %s op/matchType unchanged through the submit payload (%s / %s)',
|
||||
(_desc, op, matchType) => {
|
||||
|
||||
@@ -332,25 +332,20 @@ describe('CreateAlertV2 utils', () => {
|
||||
['not_equal', AlertThresholdOperator.IS_NOT_EQUAL_TO],
|
||||
['not_eq', AlertThresholdOperator.IS_NOT_EQUAL_TO],
|
||||
['!=', AlertThresholdOperator.IS_NOT_EQUAL_TO],
|
||||
['5', AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO],
|
||||
['above_or_equal', AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO],
|
||||
['above_or_eq', AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO],
|
||||
['>=', AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO],
|
||||
['6', AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO],
|
||||
['below_or_equal', AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO],
|
||||
['below_or_eq', AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO],
|
||||
['<=', AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO],
|
||||
['7', AlertThresholdOperator.ABOVE_BELOW],
|
||||
['outside_bounds', AlertThresholdOperator.ABOVE_BELOW],
|
||||
])('maps backend alias %s to canonical enum', (alias, expected) => {
|
||||
expect(normalizeOperator(alias)).toBe(expected);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['5', 'above_or_equal'],
|
||||
['above_or_equal', 'above_or_equal'],
|
||||
['above_or_eq', 'above_or_equal'],
|
||||
['>=', 'above_or_equal'],
|
||||
['6', 'below_or_equal'],
|
||||
['below_or_equal', 'below_or_equal'],
|
||||
['below_or_eq', 'below_or_equal'],
|
||||
['<=', 'below_or_equal'],
|
||||
])('returns undefined for UI-unexposed alias %s (%s family)', (alias) => {
|
||||
expect(normalizeOperator(alias)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns undefined for unknown values', () => {
|
||||
expect(normalizeOperator('gibberish')).toBeUndefined();
|
||||
expect(normalizeOperator(undefined)).toBeUndefined();
|
||||
@@ -413,8 +408,8 @@ describe('CreateAlertV2 utils', () => {
|
||||
['symbol', '>', 'at_least_once'],
|
||||
['short form', 'eq', 'avg'],
|
||||
['mixed numeric and literal', '7', 'last'],
|
||||
['UI-unexposed operator', 'above_or_equal', 'at_least_once'],
|
||||
['UI-unexposed numeric operator', '5', 'at_least_once'],
|
||||
['inclusive literal operator', 'above_or_equal', 'at_least_once'],
|
||||
['inclusive numeric operator', '5', 'at_least_once'],
|
||||
])('preserves %s op/matchType verbatim (%s / %s)', (_desc, op, matchType) => {
|
||||
const state = getThresholdStateFromAlertDef(buildDef(op, matchType));
|
||||
expect(state.operator).toBe(op);
|
||||
|
||||
@@ -2,9 +2,8 @@ import { AlertThresholdMatchType, AlertThresholdOperator } from './types';
|
||||
|
||||
// Mirrors the backend's CompareOperator.Normalize() in
|
||||
// pkg/types/ruletypes/compare.go. Maps any accepted alias to the enum value
|
||||
// the dropdown understands. Returns undefined for aliases the UI does not
|
||||
// expose (e.g. above_or_equal, below_or_equal) so callers can keep the raw
|
||||
// value on screen instead of silently rewriting it.
|
||||
// the dropdown understands. Returns undefined for unknown values so callers
|
||||
// can keep the raw value on screen instead of silently rewriting it.
|
||||
export function normalizeOperator(
|
||||
raw: string | undefined,
|
||||
): AlertThresholdOperator | undefined {
|
||||
@@ -27,6 +26,16 @@ export function normalizeOperator(
|
||||
case 'not_eq':
|
||||
case '!=':
|
||||
return AlertThresholdOperator.IS_NOT_EQUAL_TO;
|
||||
case '5':
|
||||
case 'above_or_equal':
|
||||
case 'above_or_eq':
|
||||
case '>=':
|
||||
return AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO;
|
||||
case '6':
|
||||
case 'below_or_equal':
|
||||
case 'below_or_eq':
|
||||
case '<=':
|
||||
return AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO;
|
||||
case '7':
|
||||
case 'outside_bounds':
|
||||
return AlertThresholdOperator.ABOVE_BELOW;
|
||||
|
||||
@@ -125,6 +125,14 @@ export const THRESHOLD_OPERATOR_OPTIONS = [
|
||||
{ value: AlertThresholdOperator.IS_BELOW, label: 'BELOW' },
|
||||
{ value: AlertThresholdOperator.IS_EQUAL_TO, label: 'EQUAL TO' },
|
||||
{ value: AlertThresholdOperator.IS_NOT_EQUAL_TO, label: 'NOT EQUAL TO' },
|
||||
{
|
||||
value: AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO,
|
||||
label: 'ABOVE OR EQUAL TO',
|
||||
},
|
||||
{
|
||||
value: AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO,
|
||||
label: 'BELOW OR EQUAL TO',
|
||||
},
|
||||
];
|
||||
|
||||
export const ANOMALY_THRESHOLD_OPERATOR_OPTIONS = [
|
||||
|
||||
@@ -99,6 +99,8 @@ export enum AlertThresholdOperator {
|
||||
IS_BELOW = 'below',
|
||||
IS_EQUAL_TO = 'equal',
|
||||
IS_NOT_EQUAL_TO = 'not_equal',
|
||||
IS_ABOVE_OR_EQUAL_TO = 'above_or_equal',
|
||||
IS_BELOW_OR_EQUAL_TO = 'below_or_equal',
|
||||
ABOVE_BELOW = 'outside_bounds',
|
||||
}
|
||||
|
||||
|
||||
@@ -167,9 +167,9 @@ describe('deriveAlertPrefill', () => {
|
||||
|
||||
it.each([
|
||||
['above', AlertThresholdOperator.IS_ABOVE],
|
||||
['above_or_equal', AlertThresholdOperator.IS_ABOVE],
|
||||
['above_or_equal', AlertThresholdOperator.IS_ABOVE_OR_EQUAL_TO],
|
||||
['below', AlertThresholdOperator.IS_BELOW],
|
||||
['below_or_equal', AlertThresholdOperator.IS_BELOW],
|
||||
['below_or_equal', AlertThresholdOperator.IS_BELOW_OR_EQUAL_TO],
|
||||
['equal', AlertThresholdOperator.IS_EQUAL_TO],
|
||||
['not_equal', AlertThresholdOperator.IS_NOT_EQUAL_TO],
|
||||
])('maps panel operator %s → %s', (op, expected) => {
|
||||
|
||||
@@ -104,20 +104,6 @@ function pickHighestDanger(
|
||||
)[0];
|
||||
}
|
||||
|
||||
// The alert UI has no inclusive operator; collapse "or equal" onto its strict variant.
|
||||
function panelOperatorToAlertOperator(
|
||||
operator: DashboardtypesComparisonOperatorDTO | undefined,
|
||||
): AlertThresholdOperator | undefined {
|
||||
switch (operator) {
|
||||
case 'above_or_equal':
|
||||
return normalizeOperator('above');
|
||||
case 'below_or_equal':
|
||||
return normalizeOperator('below');
|
||||
default:
|
||||
return normalizeOperator(operator);
|
||||
}
|
||||
}
|
||||
|
||||
export function deriveAlertPrefill(
|
||||
panel: DashboardtypesPanelDTO,
|
||||
query: Query,
|
||||
@@ -135,7 +121,7 @@ export function deriveAlertPrefill(
|
||||
|
||||
const top = pickHighestDanger(readPanelThresholds(panel.spec.plugin));
|
||||
if (top) {
|
||||
prefill.operator = panelOperatorToAlertOperator(top.operator);
|
||||
prefill.operator = normalizeOperator(top.operator);
|
||||
prefill.threshold = {
|
||||
id: uuid(),
|
||||
label: 'critical',
|
||||
|
||||
@@ -48,8 +48,8 @@ func (CompareOperator) Enum() []any {
|
||||
ValueIsBelowLiteral,
|
||||
ValueIsEqLiteral,
|
||||
ValueIsNotEqLiteral,
|
||||
// ValueAboveOrEqLiteral,
|
||||
// ValueBelowOrEqLiteral,
|
||||
ValueAboveOrEqLiteral,
|
||||
ValueBelowOrEqLiteral,
|
||||
ValueOutsideBoundsLiteral,
|
||||
}
|
||||
}
|
||||
|
||||
5
tests/fixtures/http.py
vendored
5
tests/fixtures/http.py
vendored
@@ -17,8 +17,6 @@ from fixtures.logger import setup_logger
|
||||
|
||||
logger = setup_logger(__name__)
|
||||
|
||||
ZEUS_NETWORK_ALIAS = "signoz-zeus"
|
||||
|
||||
|
||||
@pytest.fixture(name="zeus", scope="package")
|
||||
def zeus(
|
||||
@@ -33,7 +31,6 @@ def zeus(
|
||||
def create() -> types.TestContainerDocker:
|
||||
container = WireMockContainer(image="wiremock/wiremock:2.35.1-1", secure=False)
|
||||
container.with_network(network)
|
||||
container.with_network_aliases(ZEUS_NETWORK_ALIAS)
|
||||
container.start()
|
||||
|
||||
return types.TestContainerDocker(
|
||||
@@ -45,7 +42,7 @@ def zeus(
|
||||
container.get_exposed_port(8080),
|
||||
)
|
||||
},
|
||||
container_configs={"8080": types.TestContainerUrlConfig("http", ZEUS_NETWORK_ALIAS, 8080)},
|
||||
container_configs={"8080": types.TestContainerUrlConfig("http", container.get_wrapped_container().name, 8080)},
|
||||
)
|
||||
|
||||
def delete(container: types.TestContainerDocker):
|
||||
|
||||
136
tests/fixtures/signoz.py
vendored
136
tests/fixtures/signoz.py
vendored
@@ -1,19 +1,14 @@
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from http import HTTPStatus
|
||||
from os import path
|
||||
from pathlib import Path
|
||||
|
||||
import docker
|
||||
import docker.errors
|
||||
import pytest
|
||||
import requests
|
||||
from testcontainers.core.container import DockerContainer, Network
|
||||
from testcontainers.core.image import DockerImage
|
||||
|
||||
from fixtures import reuse, types
|
||||
from fixtures.logger import setup_logger
|
||||
@@ -21,110 +16,6 @@ from fixtures.logger import setup_logger
|
||||
logger = setup_logger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SigNozImageBuild:
|
||||
process: subprocess.Popen
|
||||
command: list[str]
|
||||
cache_path: Path | None = None
|
||||
next_cache_path: Path | None = None
|
||||
reader: threading.Thread | None = None
|
||||
|
||||
|
||||
def _stream_build_output(pipe, log) -> None:
|
||||
try:
|
||||
for line in iter(pipe.readline, ""):
|
||||
log.info("buildx: %s", line.rstrip())
|
||||
finally:
|
||||
pipe.close()
|
||||
|
||||
|
||||
def start_signoz_image_build(pytestconfig: pytest.Config, dockerfile_path: str, arch: str, zeus_url: str) -> SigNozImageBuild:
|
||||
root = pytestconfig.rootpath.parent
|
||||
command = [
|
||||
"docker",
|
||||
"buildx",
|
||||
"build",
|
||||
"--load",
|
||||
"--progress",
|
||||
"plain",
|
||||
"--tag",
|
||||
"signoz:integration",
|
||||
"--file",
|
||||
dockerfile_path,
|
||||
"--build-arg",
|
||||
f"TARGETARCH={arch}",
|
||||
"--build-arg",
|
||||
f"ZEUSURL={zeus_url}",
|
||||
str(root),
|
||||
]
|
||||
|
||||
cache_path = None
|
||||
next_cache_path = None
|
||||
if os.environ.get("ACTIONS_RUNTIME_TOKEN"):
|
||||
# Running in GitHub Actions — use BuildKit's native GHA cache backend.
|
||||
# Avoids the local-write races and partial exports seen with type=local.
|
||||
scope = os.environ.get("SIGNOZ_BUILDX_GHA_SCOPE", "signoz-integration")
|
||||
command.extend(["--cache-from", f"type=gha,scope={scope}"])
|
||||
command.extend(["--cache-to", f"type=gha,scope={scope},mode=max"])
|
||||
elif build_cache_dir := os.environ.get("SIGNOZ_INTEGRATION_BUILD_CACHE_DIR"):
|
||||
# Local cache for developer machines / non-GHA CI.
|
||||
cache_path = Path(build_cache_dir)
|
||||
next_cache_path = Path(f"{build_cache_dir}-next")
|
||||
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.rmtree(next_cache_path, ignore_errors=True)
|
||||
|
||||
if cache_path.exists():
|
||||
command.extend(["--cache-from", f"type=local,src={cache_path}"])
|
||||
command.extend(["--cache-to", f"type=local,dest={next_cache_path},mode=max"])
|
||||
|
||||
logger.info("Building SigNoz integration image with %s", " ".join(command))
|
||||
process = subprocess.Popen(
|
||||
command,
|
||||
cwd=root,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
)
|
||||
reader = threading.Thread(target=_stream_build_output, args=(process.stdout, logger), daemon=True)
|
||||
reader.start()
|
||||
|
||||
return SigNozImageBuild(
|
||||
process=process,
|
||||
command=command,
|
||||
cache_path=cache_path,
|
||||
next_cache_path=next_cache_path,
|
||||
reader=reader,
|
||||
)
|
||||
|
||||
|
||||
def wait_for_signoz_image_build(build: SigNozImageBuild) -> None:
|
||||
returncode = build.process.wait()
|
||||
if build.reader is not None:
|
||||
build.reader.join(timeout=5)
|
||||
if returncode != 0:
|
||||
raise subprocess.CalledProcessError(returncode, build.command)
|
||||
|
||||
if build.cache_path and build.next_cache_path and build.next_cache_path.exists():
|
||||
shutil.rmtree(build.cache_path, ignore_errors=True)
|
||||
shutil.move(build.next_cache_path, build.cache_path)
|
||||
|
||||
|
||||
def stop_signoz_image_build(build: SigNozImageBuild) -> None:
|
||||
if build.process.poll() is not None:
|
||||
return
|
||||
|
||||
build.process.terminate()
|
||||
try:
|
||||
build.process.wait(timeout=10)
|
||||
except subprocess.TimeoutExpired:
|
||||
build.process.kill()
|
||||
build.process.wait()
|
||||
|
||||
if build.reader is not None:
|
||||
build.reader.join(timeout=5)
|
||||
|
||||
|
||||
def create_signoz(
|
||||
network: Network,
|
||||
zeus: types.TestContainerDocker,
|
||||
@@ -142,6 +33,9 @@ def create_signoz(
|
||||
"""
|
||||
|
||||
def create() -> types.SigNoz:
|
||||
# Run the migrations for clickhouse
|
||||
request.getfixturevalue("migrator")
|
||||
|
||||
# Get the no-web flag
|
||||
with_web = pytestconfig.getoption("--with-web")
|
||||
|
||||
@@ -154,15 +48,19 @@ def create_signoz(
|
||||
if with_web:
|
||||
dockerfile_path = "cmd/enterprise/Dockerfile.with-web.integration"
|
||||
|
||||
# The SigNoz image build does not depend on ClickHouse migrations, so
|
||||
# build it while the migrator container runs.
|
||||
image_build = start_signoz_image_build(pytestconfig, dockerfile_path, arch, zeus.container_configs["8080"].base())
|
||||
try:
|
||||
request.getfixturevalue("migrator")
|
||||
wait_for_signoz_image_build(image_build)
|
||||
except Exception: # pylint: disable=broad-exception-caught
|
||||
stop_signoz_image_build(image_build)
|
||||
raise
|
||||
# Docker build context is the repo root — one up from pytest's
|
||||
# rootdir (tests/).
|
||||
self = DockerImage(
|
||||
path=str(pytestconfig.rootpath.parent),
|
||||
dockerfile_path=dockerfile_path,
|
||||
tag="signoz:integration",
|
||||
buildargs={
|
||||
"TARGETARCH": arch,
|
||||
"ZEUSURL": zeus.container_configs["8080"].base(),
|
||||
},
|
||||
)
|
||||
|
||||
self.build()
|
||||
|
||||
env = (
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user