Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions tests/Dockerfile-cypress
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ RUN wget -O chainsaw.tar.gz https://github.com/kyverno/chainsaw/releases/downloa
&& mv chainsaw /usr/local/bin/ \
&& rm -rf chainsaw.tar.gz

# Install apache2-utils for htpasswd required by OCP CI
RUN apt update && apt install -y apache2-utils \
# Install apache2-utils for htpasswd required by OCP CI, jq and curl for metrics validation
RUN apt update && apt install -y apache2-utils jq curl \
&& rm -rf /var/lib/apt/lists/*
6 changes: 3 additions & 3 deletions tests/PATTERNFLY_COMMANDS_EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ cy.findByTestId('span-duration-bar').first().click(); // By test ID (multiple sp

```typescript
// Single attribute validation
cy.muiTraceAttribute('net.peer.ip', '1.2.3.4');
cy.muiTraceAttribute('network.peer.address', '1.2.3.4');
cy.muiTraceAttribute('peer.service', 'telemetrygen-client');
cy.muiTraceAttribute('k8s.container.name', 'telemetrygen', true); // Optional attribute

Expand All @@ -122,7 +122,7 @@ cy.muiTraceAttribute('service.name', (text) => {

// Bulk attribute validation (recommended for multiple attributes)
cy.muiTraceAttributes({
'net.peer.ip': { value: '1.2.3.4' },
'network.peer.address': { value: '1.2.3.4' },
'peer.service': { value: 'telemetrygen-client' },
'k8s.container.name': {
value: 'telemetrygen',
Expand Down Expand Up @@ -168,7 +168,7 @@ describe('Trace inspection', () => {

// Validate trace attributes efficiently
cy.muiTraceAttributes({
'net.peer.ip': { value: '1.2.3.4' },
'network.peer.address': { value: '1.2.3.4' },
'peer.service': { value: 'telemetrygen-client' },
'service.name': {
value: (text) => text.includes('rbac')
Expand Down
2 changes: 1 addition & 1 deletion tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ cy.muiSpanBar('grpc-service').click()
// Attribute validation (bulk)
cy.muiTraceAttributes({
'service.name': { value: 'my-service' },
'net.peer.ip': { value: '1.2.3.4' },
'network.peer.address': { value: '1.2.3.4' },
'k8s.namespace': { value: 'test-ns', optional: true }
})
```
Expand Down
10 changes: 5 additions & 5 deletions tests/SELECTOR_BEST_PRACTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ cy.muiSpanBar('http-rbac-2') // Click specific span bar
cy.muiFirstSpanBar() // Click first span bar

// Trace Attribute Validation
cy.muiTraceAttribute('net.peer.ip', '1.2.3.4') // Single attribute check
cy.muiTraceAttribute('network.peer.address', '1.2.3.4') // Single attribute check
cy.muiTraceAttributes({ // Bulk attribute validation
'net.peer.ip': { value: '1.2.3.4' },
'network.peer.address': { value: '1.2.3.4' },
'service.name': { value: (text) => text.includes('rbac') }
})
```
Expand Down Expand Up @@ -219,7 +219,7 @@ cy.findByTestId('span-duration-bar').first().click() // By test ID
### Trace Attribute Validation
```typescript
// Old approach (48+ lines of repetitive code):
cy.contains('.MuiTypography-h5', 'net.peer.ip').next('.MuiTypography-body1').should('have.text', '1.2.3.4')
cy.contains('.MuiTypography-h5', 'network.peer.address').next('.MuiTypography-body1').should('have.text', '1.2.3.4')
cy.contains('.MuiTypography-h5', 'peer.service').next('.MuiTypography-body1').should('have.text', 'telemetrygen-client')
cy.get('body').then(($body) => {
if ($body.find('.MuiTypography-h5:contains("k8s.container.name")').length > 0) {
Expand All @@ -230,7 +230,7 @@ cy.get('body').then(($body) => {

// New approach (12 lines, 75% reduction):
cy.muiTraceAttributes({
'net.peer.ip': { value: '1.2.3.4' },
'network.peer.address': { value: '1.2.3.4' },
'peer.service': { value: 'telemetrygen-client' },
'k8s.container.name': { value: 'telemetrygen', optional: true },
'k8s.namespace.name': {
Expand All @@ -243,7 +243,7 @@ cy.muiTraceAttributes({
}, 'TempoStack')

// Or individual attribute checks:
cy.muiTraceAttribute('net.peer.ip', '1.2.3.4')
cy.muiTraceAttribute('network.peer.address', '1.2.3.4')
cy.muiTraceAttribute('service.name', (text) => text.includes('rbac'), false, 'TempoStack')
```

Expand Down
133 changes: 125 additions & 8 deletions tests/e2e/dt-plugin-tests.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,24 +350,25 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => {
.and('have.text', 'Create a TempoMonolithic instance');
});

it('Test Distributed Tracing UI plugin with Tempo instances and verify traces using user having cluster-admin role', function () {
it('Test Distributed Tracing UI plugin with Tempo instances and verify traces, span links using user having cluster-admin role', function () {
cy.log('Create TempoStack and TempoMonolithic instances');
cy.exec(
'chainsaw test --config ./fixtures/.chainsaw.yaml --skip-delete ./fixtures/chainsaw-tests',
{
env: {
KUBECONFIG: Cypress.env('KUBECONFIG_PATH'),
},
timeout: 1800000,
timeout: 1200000,
failOnNonZeroExit: true
}
) .then((result) => {
expect(result.code).to.eq(0);
cy.log(`Chainsaw test ran successfully: ${result.stdout}`);
});
cy.log('Navigate to the observe/traces page');
cy.visit('/observe/traces');

cy.log('Navigate to the /observe/traces page');
cy.visit('/observe/traces');
cy.url().should('include', '/observe/traces');
cy.log('Assert traces in TempoStack instance.');
cy.pfTypeahead('Select a Tempo instance').click();
cy.pfSelectMenuItem('chainsaw-rbac / simplst').click();
Expand All @@ -384,8 +385,93 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => {
cy.muiFirstTraceLink().click();
cy.findByTestId('span-duration-bar').eq(1).click();
cy.muiTraceAttributes({
'net.peer.ip': { value: '1.2.3.4' },
'peer.service': { value: 'telemetrygen-client' },
'network.peer.address': { value: '1.2.3.4' },
'peer.service': { value: (text) => ['telemetrygen-server', 'telemetrygen-client'].includes(text) },
'k8s.container.name': { value: 'telemetrygen', optional: true },
'k8s.namespace.name': {
value: (text) => ['chainsaw-test-rbac-1', 'chainsaw-test-rbac-2', 'chainsaw-mono-rbac-1', 'chainsaw-mono-rbac-2'].includes(text),
optional: true
},
'service.name': {
value: (text) => ['http-rbac-1', 'http-rbac-2', 'grpc-rbac-1', 'grpc-rbac-2'].includes(text)
}
}, 'TempoStack');
cy.log('Click on the Links tab');
cy.get('button.MuiTab-root').contains('Links').click();
cy.log('Verify link details are present');
// Verify first link (index 0)
cy.muiTraceAttribute('link.index', '0', false, 'Links');
cy.muiTraceAttribute('link.type', 'random', false, 'Links');
// Verify trace ID and span ID have valid format for first link (they will be different each time)
cy.contains('.MuiTypography-h5', 'trace ID').first().next('.MuiTypography-body1').invoke('text').then((traceId) => {
cy.log(`First link trace ID: ${traceId.trim()}`);
expect(traceId.trim()).to.match(/^[A-F0-9]{32}$/);
});
cy.contains('.MuiTypography-h5', 'span ID').first().next('.MuiTypography-body1').invoke('text').then((spanId) => {
cy.log(`First link span ID: ${spanId.trim()}`);
expect(spanId.trim()).to.match(/^[A-F0-9]{16}$/);
});
cy.log('Click on the first trace ID link to navigate to that trace');
cy.contains('.MuiTypography-h5', 'trace ID').first().next('.MuiTypography-body1').invoke('text').then((traceId) => {
const cleanTraceId = traceId.trim();
cy.get('a.MuiLink-root[href*="/observe/traces/"]').first().click();

cy.log('Verify URL contains the correct trace ID');
cy.url().should('include', `/observe/traces/${cleanTraceId}`);
cy.log(`✓ Successfully navigated to trace: ${cleanTraceId}`);
});
cy.log('Verify navigation by checking trace attributes');
cy.findByTestId('span-duration-bar').eq(1).click();
cy.muiTraceAttributes({
'network.peer.address': { value: '1.2.3.4' },
'peer.service': { value: (text) => ['telemetrygen-server', 'telemetrygen-client'].includes(text) },
'k8s.container.name': { value: 'telemetrygen', optional: true },
'k8s.namespace.name': {
value: (text) => ['chainsaw-test-rbac-1', 'chainsaw-test-rbac-2', 'chainsaw-mono-rbac-1', 'chainsaw-mono-rbac-2'].includes(text),
optional: true
},
'service.name': {
value: (text) => ['http-rbac-1', 'http-rbac-2', 'grpc-rbac-1', 'grpc-rbac-2'].includes(text)
}
}, 'TempoStack');

cy.log('Rerun the steps and select span ID from links');
cy.pfBreadcrumb('Traces').click();
cy.pfCloseButtonIfExists('Close chip group');
cy.pfTypeahead('Select a Tempo instance').click();
cy.pfSelectMenuItem('chainsaw-rbac / simplst').click();
cy.pfTypeahead('Select a tenant').click();
cy.pfSelectMenuItem('dev').click();
cy.muiSelect('Select time range').click();
cy.muiSelectOption('Last 1 hour').click();
cy.pfMenuToggle('Service Name').click();
cy.pfMenuToggleByLabel('Multi typeahead checkbox').click();
cy.pfCheckMenuItem('http-rbac-1');
cy.pfCheckMenuItem('http-rbac-2');
cy.pfCheckMenuItem('grpc-rbac-1');
cy.pfCheckMenuItem('grpc-rbac-2');
cy.muiFirstTraceLink().click();
cy.findByTestId('span-duration-bar').eq(1).click();
cy.log('Click on the Links tab again');
cy.get('button.MuiTab-root').contains('Links').click();
cy.log('Click on the first span ID link to navigate to that span');
cy.contains('.MuiTypography-h5', 'trace ID').first().next('.MuiTypography-body1').invoke('text').then((traceId) => {
const cleanTraceId = traceId.trim();
cy.contains('.MuiTypography-h5', 'span ID').first().next('.MuiTypography-body1').invoke('text').then((spanId) => {
const cleanSpanId = spanId.trim();
cy.contains('.MuiTypography-h5', 'span ID').first().next('.MuiTypography-body1').find('a').first().click();

cy.log('Verify URL contains the correct trace ID and span ID');
cy.url().should('include', `/observe/traces/${cleanTraceId}`);
cy.url().should('include', `selectSpan=${cleanSpanId}`);
cy.log(`✓ Successfully navigated to trace: ${cleanTraceId} with selected span: ${cleanSpanId}`);
});
});
cy.log('Verify navigation by checking trace attributes');
cy.findByTestId('span-duration-bar').eq(1).click();
cy.muiTraceAttributes({
'network.peer.address': { value: '1.2.3.4' },
'peer.service': { value: (text) => ['telemetrygen-server', 'telemetrygen-client'].includes(text) },
'k8s.container.name': { value: 'telemetrygen', optional: true },
'k8s.namespace.name': {
value: (text) => ['chainsaw-test-rbac-1', 'chainsaw-test-rbac-2', 'chainsaw-mono-rbac-1', 'chainsaw-mono-rbac-2'].includes(text),
Expand All @@ -396,6 +482,37 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => {
}
}, 'TempoStack');

cy.log('Rerun the steps and select span links from the Traces page');
cy.pfBreadcrumb('Traces').click();
cy.pfCloseButtonIfExists('Close chip group');
cy.pfTypeahead('Select a Tempo instance').click();
cy.pfSelectMenuItem('chainsaw-rbac / simplst').click();
cy.pfTypeahead('Select a tenant').click();
cy.pfSelectMenuItem('dev').click();
cy.muiSelect('Select time range').click();
cy.muiSelectOption('Last 1 hour').click();
cy.pfMenuToggle('Service Name').click();
cy.pfMenuToggleByLabel('Multi typeahead checkbox').click();
cy.pfCheckMenuItem('http-rbac-1');
cy.pfCheckMenuItem('http-rbac-2');
cy.pfCheckMenuItem('grpc-rbac-1');
cy.pfCheckMenuItem('grpc-rbac-2');
cy.muiFirstTraceLink().click();
cy.get('[data-testid="LaunchIcon"]').first().click();
cy.get('a[role="menuitem"]').contains('Open linked span').first().click();
cy.muiTraceAttributes({
'network.peer.address': { value: '1.2.3.4' },
'peer.service': { value: (text) => ['telemetrygen-server', 'telemetrygen-client'].includes(text) },
'k8s.container.name': { value: 'telemetrygen', optional: true },
'k8s.namespace.name': {
value: (text) => ['chainsaw-test-rbac-1', 'chainsaw-test-rbac-2', 'chainsaw-mono-rbac-1', 'chainsaw-mono-rbac-2'].includes(text),
optional: true
},
'service.name': {
value: (text) => ['http-rbac-1', 'http-rbac-2', 'grpc-rbac-1', 'grpc-rbac-2'].includes(text)
}
}, 'TempoMonolithic');

cy.log('Assert traces in TempoMonolithic instance.');
cy.pfBreadcrumb('Traces').click();
cy.pfCloseButtonIfExists('Close chip group');
Expand All @@ -414,8 +531,8 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => {
cy.muiFirstTraceLink().click();
cy.findByTestId('span-duration-bar').eq(1).click();
cy.muiTraceAttributes({
'net.peer.ip': { value: '1.2.3.4' },
'peer.service': { value: 'telemetrygen-client' },
'network.peer.address': { value: '1.2.3.4' },
'peer.service': { value: (text) => ['telemetrygen-server', 'telemetrygen-client'].includes(text) },
'k8s.container.name': { value: 'telemetrygen', optional: true },
'k8s.namespace.name': {
value: (text) => ['chainsaw-test-rbac-1', 'chainsaw-test-rbac-2', 'chainsaw-mono-rbac-1', 'chainsaw-mono-rbac-2'].includes(text),
Expand Down
14 changes: 7 additions & 7 deletions tests/fixtures/.chainsaw.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ kind: Configuration
metadata:
name: configuration
spec:
parallel: 4
parallel: 2
timeouts:
assert: 6m0s
cleanup: 5m0s
delete: 5m0s
error: 5m0s
apply: 10s
exec: 15s
assert: 15m0s
cleanup: 10m0s
delete: 10m0s
error: 10m0s
apply: 2m0s
exec: 5m0s
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ spec:
port: 4318
protocol: TCP
targetPort: 4318
- name: prometheus
port: 8889
protocol: TCP
targetPort: 8889
selector:
app.kubernetes.io/component: opentelemetry-collector
app.kubernetes.io/instance: chainsaw-mmo-rbac.dev
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,13 @@ apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
name: dev
namespace: chainsaw-mmo-rbac
spec:
serviceAccount: otel-collector-deployment
mode: deployment
observability:
metrics:
enableMetrics: true
config: |
extensions:
bearertokenauth:
Expand All @@ -54,6 +59,19 @@ spec:
protocols:
http:

connectors:
spanmetrics:
histogram:
explicit:
buckets: [2ms, 4ms, 6ms, 8ms, 10ms, 50ms, 100ms, 200ms, 400ms, 800ms, 1s, 1400ms, 2s, 5s, 10s, 15s]
dimensions:
- name: http.method
default: GET
- name: http.status_code
dimensions_cache_size: 1000
aggregation_temporality: "AGGREGATION_TEMPORALITY_CUMULATIVE"
metrics_flush_interval: 5s

processors:
k8sattributes:
extract:
Expand Down Expand Up @@ -89,6 +107,11 @@ spec:
authenticator: bearertokenauth
headers:
X-Scope-OrgID: dev # tenantName
prometheus:
add_metric_suffixes: false
endpoint: 0.0.0.0:8889
resource_to_telemetry_conversion:
enabled: true

service:
telemetry:
Expand All @@ -100,11 +123,14 @@ spec:
traces/grpc:
receivers: [otlp/grpc]
processors: [k8sattributes]
exporters: [otlp,debug]
exporters: [otlp,debug,spanmetrics]
traces/http:
receivers: [otlp/http]
processors: [k8sattributes]
exporters: [otlphttp,debug]
exporters: [otlphttp,debug,spanmetrics]
metrics:
receivers: [spanmetrics]
exporters: [prometheus,debug]

---
apiVersion: rbac.authorization.k8s.io/v1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus-operator
namespace: openshift-user-workload-monitoring
(status.replicas == spec.replicas): true
spec:
(replicas >= `1`): true

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: prometheus-user-workload
namespace: openshift-user-workload-monitoring
(status.replicas == spec.replicas): true
spec:
(replicas >= `1`): true

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: thanos-ruler-user-workload
namespace: openshift-user-workload-monitoring
(status.replicas == spec.replicas): true
spec:
(replicas >= `1`): true
Loading