diff --git a/web/cypress/fixtures/flowcollector/fc_lokiWithoutLokiStack.yaml b/web/cypress/fixtures/flowcollector/fc_lokiWithoutLokiStack.yaml new file mode 100644 index 000000000..a77f4a042 --- /dev/null +++ b/web/cypress/fixtures/flowcollector/fc_lokiWithoutLokiStack.yaml @@ -0,0 +1,14 @@ +kind: FlowCollector +apiVersion: flows.netobserv.io/v1beta2 +metadata: + name: cluster +spec: + agent: + ebpf: + sampling: 1 + type: eBPF + loki: + enable: true + mode: LokiStack + lokiStack: + name: loki diff --git a/web/cypress/integration-tests/flowcollector_status.cy.ts b/web/cypress/integration-tests/flowcollector_status.cy.ts new file mode 100644 index 000000000..fb7e1074d --- /dev/null +++ b/web/cypress/integration-tests/flowcollector_status.cy.ts @@ -0,0 +1,61 @@ +import { Operator } from "@views/netobserv" +import { flowcollectorStatusPage, flowcollectorStatusSelectors } from "@views/flowcollector-status" + +describe('Network_Observability FlowCollector status error scenario', { tags: ['Network_Observability'] }, function () { + + before('setup', function () { + cy.adminCLI(`oc adm policy add-cluster-role-to-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) + cy.uiLogin(Cypress.env('LOGIN_IDP'), Cypress.env('LOGIN_USERNAME'), Cypress.env('LOGIN_PASSWORD')) + + Operator.install() + cy.checkStorageClass(this) + Operator.createFlowcollector("LokiWithoutLokiStack") + }) + + it("(OCP-88744, kapjain, Network_Observability) Verify error status when Loki enabled without LokiStack", function () { + // Visit status page and wait for Ready condition to show False (error state) + flowcollectorStatusPage.visit() + cy.get(flowcollectorStatusSelectors.readyRow, { timeout: 120000 }).should('exist') + .should('have.attr', 'data-test-status', 'False') + cy.get(flowcollectorStatusSelectors.readyRow) + .should('have.attr', 'data-test-reason') + .and('not.equal', 'Pending') + .and('not.equal', 'Valid') + + // Verify WaitingFLPMonolith condition shows error about loki-gateway-ca-bundle + cy.get(flowcollectorStatusSelectors.flpMonolithRow).should('exist') + .should('have.attr', 'data-test-status', 'True') + cy.get(flowcollectorStatusSelectors.flpMonolithRow).parent() + .should('contain.text', 'loki-gateway-ca-bundle') + + // Verify Flowlogs Pipeline component shows error about loki-gateway-ca-bundle + cy.contains('td', 'Flowlogs Pipeline').parent('tr') + .should('contain.text', 'loki-gateway-ca-bundle') + + // Verify WaitingLokiStack condition shows LokiStack not found error + cy.get(flowcollectorStatusSelectors.lokiStackRow).should('exist') + .should('have.attr', 'data-test-status', 'True') + cy.get(flowcollectorStatusSelectors.lokiStackRow) + .should('contain.text', 'Loki is configured in LokiStack mode, but LokiStack API is missing') + + // Verify WaitingFLPParent condition shows FLP error + cy.get(flowcollectorStatusSelectors.flpParentRow).should('exist') + .should('have.attr', 'data-test-status', 'True') + .and('have.attr', 'data-test-reason', 'FLPError') + + // Verify status icon tooltip shows error + cy.get(flowcollectorStatusSelectors.statusButton) + .find('span span').trigger('mouseenter', { force: true }) + cy.get(flowcollectorStatusSelectors.statusTooltip, { timeout: 10000 }) + .should('contain.text', 'FlowCollector has errors') + + // Verify "Open Network Traffic page" button is disabled + cy.byLegacyTestID('open-network-traffic').should('exist') + .should('have.attr', 'aria-disabled', 'true') + }) + + after("all tests", function () { + Operator.deleteFlowCollector() + cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) + }) +}) diff --git a/web/cypress/integration-tests/static_plugin.cy.ts b/web/cypress/integration-tests/static_plugin.cy.ts index 1edbc34f7..4b6bf357b 100644 --- a/web/cypress/integration-tests/static_plugin.cy.ts +++ b/web/cypress/integration-tests/static_plugin.cy.ts @@ -1,7 +1,9 @@ import { netflowPage, overviewSelectors, pluginSelectors } from "@views/netflow-page" import { Operator } from "@views/netobserv" +import {flowcollectorStatusPage, flowcollectorStatusSelectors} from "@views/flowcollector-status"; +import {searchPage} from "@views/search"; -describe('(OCP-84156 Network_Observability) StaticPlugin test', { tags: ['Network_Observability'] }, function () { +describe('(OCP-84156 OCP-88744 Network_Observability) StaticPlugin test with Status Check', { tags: ['Network_Observability'] }, function () { before('any test', function () { cy.adminCLI(`oc adm policy add-cluster-role-to-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) @@ -12,35 +14,113 @@ describe('(OCP-84156 Network_Observability) StaticPlugin test', { tags: ['Networ Operator.createFlowcollector("StaticPlugin") }) - it("(OCP-84156, aramesha, Network_Observability) Edit flowcollector form view", function () { + it("(OCP-84156, OCP-88744 aramesha, Network_Observability) Edit flowcollector form view with Status Check", function () { // Edit flowcollector form view to update sampling to 1 - cy.visit('k8s/cluster/flows.netobserv.io~v1beta2~FlowCollector/status') + flowcollectorStatusPage.visit() + + // Verify status page title with status icon and tooltip on hover + cy.contains('Network Observability FlowCollector status').should('exist') + cy.get(flowcollectorStatusSelectors.statusButton).should('exist') + .find('span span').trigger('mouseenter', { force: true }) + cy.get(flowcollectorStatusSelectors.statusTooltip, { timeout: 10000 }) + .should('contain.text', 'FlowCollector is ready') + + // Verify component statuses table headers + cy.contains('Component statuses').should('exist') + cy.contains('th', 'Component').should('exist') + cy.contains('th', 'State').should('exist') + cy.contains('th', 'Replicas').should('exist') + cy.contains('th', 'Details').should('exist') + + // Verify component rows + cy.contains('eBPF Agent').should('exist') + cy.contains('Flowlogs Pipeline').should('exist') + cy.contains('Console Plugin').should('exist') + cy.contains('Loki').should('exist') + cy.contains('Monitoring').should('exist') + + // Verify "Open Network Traffic page" button is enabled when FC is ready + cy.byLegacyTestID('open-network-traffic').should('exist') + .should('not.have.attr', 'aria-disabled', 'true') + + // Verify demoloki install warning alert at top of status page + cy.get(flowcollectorStatusSelectors.configIssueRow).should('exist') + .should('have.attr', 'data-test-status', 'True') + .should('have.attr', 'data-test-reason', 'Warnings') + cy.contains('Configuration warnings').should('exist') + + // Verify Conditions + cy.contains('Conditions').should('exist') + cy.get(flowcollectorStatusSelectors.readyRow) + .should('have.attr', 'data-test-status', 'True') + cy.get(flowcollectorStatusSelectors.agentReadyRow).should('exist') + cy.get(flowcollectorStatusSelectors.pluginReadyRow).should('exist') + cy.get(flowcollectorStatusSelectors.monitoringReadyRow).should('exist') + + // Updating ebpf Sampling to 1 cy.get(pluginSelectors.editFlowcollector).click() cy.get('#root_spec_agent_accordion-toggle').click() cy.get('#root_spec_agent_ebpf_sampling').clear().type('1') cy.get(pluginSelectors.update).click() + // Wait for flowcollector to get ready cy.wait(20000) - cy.get('[id=Ready-row]', { timeout: 60000 }).each($td => { - cy.wrap($td).should('have.attr', 'data-test-status', 'True') - cy.wrap($td).should('have.attr', "data-test-reason", 'Ready') - }) - + cy.get(flowcollectorStatusSelectors.readyRow,{ timeout: 60000 }).should('exist') + .should('have.attr', 'data-test-status', 'True') + .should('have.attr', 'data-test-reason', 'Ready') cy.get(pluginSelectors.openNetworkTraffic).click() + // Verify PacketDrop data is seen cy.get('li.overviewTabButton').trigger('click') netflowPage.clearAllFilters() netflowPage.setAutoRefresh() cy.checkPanel(overviewSelectors.defaultPacketDropPanels) cy.checkPanelsNum(6); - cy.checkNetflowTraffic() - }) - - afterEach("test", function () { netflowPage.resetClearFilters() }) + it("(OCP-88744, kapjain, Network_Observability) Verify status indicator on Network Health page", function () { + cy.visit('/network-health') + + // cy.get('#content-scrollable', { timeout: 30000 }).should('exist') + cy.get(flowcollectorStatusSelectors.statusIndicator).should('exist') + .find('span span').trigger('mouseenter', { force: true }) + cy.get(flowcollectorStatusSelectors.statusTooltip, { timeout: 10000 }) + .should('contain.text', 'FlowCollector is ready') + cy.get(flowcollectorStatusSelectors.statusIndicator).click() + cy.contains('Network Observability FlowCollector status', { timeout: 30000 }).should('exist') + }) + + it("(OCP-88744, kapjain, Network_Observability) Verify status indicator on Network Traffic page", function () { + cy.visit('/netflow-traffic') + + // cy.get('#overview-container', { timeout: 60000 }).should('exist') + cy.get(flowcollectorStatusSelectors.statusIndicator).should('exist') + .find('span span').trigger('mouseenter', { force: true }) + cy.get(flowcollectorStatusSelectors.statusTooltip, { timeout: 10000 }) + .should('contain.text', 'FlowCollector is ready') + cy.get(flowcollectorStatusSelectors.statusIndicator).click() + cy.contains('Network Observability FlowCollector status', { timeout: 30000 }).should('exist') + }) + + it("(OCP-88744, kapjain, Network_Observability) Verify FlowCollector status via search and cluster columns", function () { + // Search for FlowCollector via search page + searchPage.navToSearchPage() + searchPage.chooseResourceType('FlowCollector') + cy.byTestID('data-view-table', { timeout: 30000 }).should('exist') + cy.byTestID('data-view-cell-cluster-name').should('exist') + + // Verify additionalPrinterColumn headers + cy.byTestID('additional-printer-column-header-Agent').should('exist') + cy.byTestID('additional-printer-column-header-Processor').should('exist') + cy.byTestID('additional-printer-column-header-Plugin').should('exist') + cy.byTestID('additional-printer-column-header-Status').should('exist') + + // Verify status column shows Ready + cy.byTestID('additional-printer-column-data-Status').should('contain.text', 'Ready') + }) + after("after all tests", function () { Operator.deleteFlowCollector() cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`) diff --git a/web/cypress/views/flowcollector-status.ts b/web/cypress/views/flowcollector-status.ts new file mode 100644 index 000000000..6a8f0eb5c --- /dev/null +++ b/web/cypress/views/flowcollector-status.ts @@ -0,0 +1,20 @@ +export namespace flowcollectorStatusSelectors { + export const statusIndicator = '#flowcollector-status-indicator' + export const statusButton = 'button[aria-label="FlowCollector status"]' + export const statusTooltip = '#flowcollector-status-tooltip' + export const readyRow = '[id=Ready-row]' + export const agentReadyRow = '[id=WaitingEBPFAgents-row]' + export const pluginReadyRow = '[id=WaitingWebConsole-row]' + export const monitoringReadyRow = '[id=WaitingMonitoring-row]' + export const configIssueRow = '[id=ConfigurationIssue-row]' + export const flpMonolithRow = '[id=WaitingFLPMonolith-row]' + export const lokiStackRow = '[id=WaitingLokiStack-row]' + export const flpParentRow = '[id=WaitingFLPParent-row]' +} + +export const flowcollectorStatusPage = { + visit: () => { + cy.visit('k8s/cluster/flows.netobserv.io~v1beta2~FlowCollector/status') + cy.get(flowcollectorStatusSelectors.readyRow, { timeout: 30000 }).should('exist') + } +} diff --git a/web/cypress/views/netobserv.ts b/web/cypress/views/netobserv.ts index e66be5432..1fa005129 100644 --- a/web/cypress/views/netobserv.ts +++ b/web/cypress/views/netobserv.ts @@ -19,6 +19,7 @@ type FlowCollectorParameter = | 'DNSTracking' | 'UDNMapping' | 'LokiDisabled' + | 'LokiWithoutLokiStack' | 'Conversations' | 'ZonesAndMultiCluster' | 'BytesMetrics' @@ -48,6 +49,7 @@ const FIXTURE_PATHS = { flowRTT: './cypress/fixtures/flowcollector/fc_flowRTT.yaml', udnMapping: './cypress/fixtures/flowcollector/fc_UDN.yaml', lokiDisabled: './cypress/fixtures/flowcollector/fc_lokiDisabled.yaml', + lokiWithoutLokiStack: './cypress/fixtures/flowcollector/fc_lokiWithoutLokiStack.yaml', conversations: './cypress/fixtures/flowcollector/fc_conversations.yaml', subnetLabels: './cypress/fixtures/flowcollector/fc_subnetLabel.yaml', zonesMultiCluster: './cypress/fixtures/flowcollector/fc_zoneMulticluster.yaml', @@ -165,6 +167,9 @@ export const Operator = { case "LokiDisabled": cy.deployFlowcollectorFromFixture(FIXTURE_PATHS.lokiDisabled) break; + case "LokiWithoutLokiStack": + cy.deployFlowcollectorFromFixture(FIXTURE_PATHS.lokiWithoutLokiStack) + break; case "Conversations": cy.deployFlowcollectorFromFixture(FIXTURE_PATHS.conversations) break; @@ -199,11 +204,13 @@ export const Operator = { // wait for all window refresh cy.wait('@reload', { timeout: 100000 }) cy.log("Console refreshed successfully") - if (parameters !== "LokiDisabled") { + if (parameters !== "LokiDisabled" && parameters !== "LokiWithoutLokiStack") { cy.adminCLI(`oc wait --for=condition=Ready pod -l app=loki -n ${project} --timeout=180s`) } - Operator.visitFlowcollector() - cy.byTestID('status-text', { timeout: 120000 }).should('exist').should('contain.text', 'Ready') + if (parameters !== "LokiWithoutLokiStack") { + Operator.visitFlowcollector() + cy.byTestID('status-text', { timeout: 120000 }).should('exist').should('contain.text', 'Ready') + } } }) }, diff --git a/web/cypress/views/search.ts b/web/cypress/views/search.ts new file mode 100644 index 000000000..25af40594 --- /dev/null +++ b/web/cypress/views/search.ts @@ -0,0 +1,10 @@ +export const searchPage = { + navToSearchPage: () => cy.visit('/search/all-namespaces'), + chooseResourceType: (resource_type) => { + cy.get('input[placeholder="Resources"]').clear().type(`${resource_type}`); + cy.get(`label[id$="~${resource_type}"]`).click(); + }, + clearAllFilters: () => { + cy.byButtonText('Clear all filters').click({force: true}); + }, +}