Skip to content

Commit

Permalink
Merge pull request #506 from kravciak/tracing
Browse files Browse the repository at this point in the history
Add audit scanner test
  • Loading branch information
kravciak authored Oct 12, 2023
2 parents 7d2c7d8 + e80f8b9 commit 4785f06
Show file tree
Hide file tree
Showing 14 changed files with 287 additions and 148 deletions.
31 changes: 11 additions & 20 deletions tests/e2e/00-installation.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { test, expect } from './rancher-test';
import { RancherCommonPage } from './pages/rancher-common.page';
import { RancherExtensionsPage } from './pages/rancher-extensions.page';
import { OverviewPage } from './pages/overview.page';
import { KubewardenPage } from './pages/kubewarden.page';
import { PolicyServersPage } from './pages/policyservers.page';
import { policyTitles } from './pages/basepolicypage';
import { RancherAppsPage } from './pages/rancher-apps.page';

// source (yarn dev) | rc (add github repo) | released (just install)
const ORIGIN = process.env.ORIGIN || (process.env.API ? 'source' : 'rc');
Expand Down Expand Up @@ -42,14 +43,12 @@ test('01 Enable extension support', async({ page, ui }) => {
test('02 Install extension', async({ page }) => {
// Add UI charts repository
if (ORIGIN === 'rc') {
const rancher = new RancherCommonPage(page);

await rancher.addRepository('kubewarden-extension-rc', 'https://rancher.github.io/kubewarden-ui/');
const apps = new RancherAppsPage(page);
await apps.addRepository('kubewarden-extension-rc', 'https://rancher.github.io/kubewarden-ui/');
}

// Install or developer load extension
const extensions = new RancherExtensionsPage(page);

await extensions.goto();
if (ORIGIN === 'source') {
await extensions.developerLoad('http://127.0.0.1:4500/kubewarden-0.0.1/kubewarden-0.0.1.umd.min.js');
Expand All @@ -59,8 +58,7 @@ test('02 Install extension', async({ page }) => {
});

test('03 Install kubewarden', async({ page, ui }) => {
const kwPage = new OverviewPage(page);

const kwPage = new KubewardenPage(page);
await kwPage.installKubewarden();

// Check UI is active
Expand All @@ -72,31 +70,24 @@ test('03 Install kubewarden', async({ page, ui }) => {

test('04 Install default policyserver', async({ page, ui }) => {
const psPage = new PolicyServersPage(page);
const kwPage = new KubewardenPage(page);

// Check banner is also visible on overview page
const kwPage = new OverviewPage(page);

// Banner is visible on Overview page
await kwPage.goto();
await expect(psPage.noDefaultPsBanner).toBeVisible();

// Banner is visible on Policy Servers page
await psPage.goto();
await expect(psPage.noDefaultPsBanner).toBeVisible();

await page.getByRole('button', { name: 'Install Chart', exact: true }).click();
await expect(page).toHaveURL(/.*\/apps\/charts\/install.*chart=kubewarden-defaults/);

// Handle PolicyServer Installer Dialog
await expect(page.getByRole('heading', { name: 'Install: Step 1' })).toBeVisible();
await page.getByRole('button', { name: 'Next' }).click();

await expect(page.getByRole('heading', { name: 'Install: Step 2' })).toBeVisible();
await psPage.installDefaultDialog({ enable: true, mode: 'monitor' });

await page.getByRole('button', { name: 'Install' }).click();
await expect(ui.helmPassRegex('rancher-kubewarden-defaults')).toBeVisible({ timeout: 40_000 });
await psPage.installDefault({recommended: true, mode: 'monitor'})
});

test('05 Whitelist artifacthub', async({ page }) => {
const kwPage = new OverviewPage(page);
const kwPage = new KubewardenPage(page);

await page.goto('/dashboard/c/local/kubewarden/policies.kubewarden.io.clusteradmissionpolicy/create');
await expect(page.getByRole('heading', { name: 'Custom Policy' })).toBeVisible();
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/10-landing.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { test, expect } from './rancher-test';
import { OverviewPage } from './pages/overview.page';
import { KubewardenPage } from './pages/kubewarden.page';
import { PolicyServersPage } from './pages/policyservers.page';
import { AdmissionPoliciesPage } from './pages/admissionpolicies.page';
import { ClusterAdmissionPoliciesPage } from './pages/clusteradmissionpolicies.page';

test('Kubewarden Landing page', async({ page, ui }) => {
const kwPage = new OverviewPage(page);
const kwPage = new KubewardenPage(page);
await kwPage.goto();
await expect(page.getByRole('heading', { name: 'Welcome to Kubewarden' })).toBeVisible()

Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/50-policyreports.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ test('PolicyReports', async({ page, ui }) => {
await den.getByRole('textbox').fill('unsafelbl')

// Customize because audit for "*" rules is skipped
await capPage.editYaml()
await ui.editYaml(ui.page, d => {
await ui.openYamlEditor()
await ui.editYaml(d => {
d.spec.rules[0].apiGroups[0] = ""
d.spec.rules[0].apiVersions[0] = "v1"
d.spec.rules[0].resources[0] = "namespaces"
Expand Down
66 changes: 66 additions & 0 deletions tests/e2e/60-tracing.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { test, expect } from './rancher-test';
import { Chart, RancherAppsPage } from './pages/rancher-apps.page';
import { PolicyServersPage } from './pages/policyservers.page';

/**
* Expect timeout has to be increased after telemetry installation on local cluster
*
*/
test('Install opentelemetry & jaeger', async ({ page }) => {
const apps = new RancherAppsPage(page)
const otelChart: Chart = { title: 'opentelemetry-operator', name: 'opentelemetry-operator', namespace: 'open-telemetry', version: '0.38.0', check: 'opentelemetry-operator' }
const jaegerChart: Chart = { title: 'Jaeger Operator', namespace: 'jaeger', check: "jaeger-operator" }

// Install OpenTelemetry
await apps.addRepository('open-telemetry', 'https://open-telemetry.github.io/opentelemetry-helm-charts')
await apps.installChart(otelChart)

// Install Jaeger
await apps.installChart(jaegerChart, d => {
d.jaeger.create = "true"
d.rbac.clusterRole = "true"
})
});

test('Enable tracing in Kubewarden', async ({ page, ui }) => {
const apps = new RancherAppsPage(page)
await apps.updateApp('rancher-kubewarden-controller', d => {
d.telemetry.enabled = true
d.telemetry.tracing.jaeger.endpoint = "jaeger-operator-jaeger-collector.jaeger.svc.cluster.local:14250"
d.telemetry.tracing.jaeger.tls = {}
d.telemetry.tracing.jaeger.tls.insecure = true
})

// Wait until kubewarden controller and policyserver are restarted, it takes around 1m
await ui.shell(`for i in $(seq 60); do
echo "Retry #$i"; k logs -l app=kubewarden-policy-server-default -n cattle-kubewarden-system -c otc-container | grep -F 'Everything is ready.' && break || sleep 5;
done`)
});

test('Check traces are visible', async ({ page, ui }) => {
const tracingTab = page.getByRole('tablist').locator('li#policy-tracing')
const policiesTab = page.getByRole('tablist').locator('li#related-policies')
const logline = ui.getRow('tracing-privpod').row.first()

const psPage = new PolicyServersPage(page)
await psPage.goto()

// Create trace log line
await ui.shell('k run tracing-privpod --image=nginx:alpine --privileged')
console.warn('Workaround: Opentelemetry not installed warning before I generate traces')
await page.reload()

// Check logs on policy server
await ui.getRow('default').open()
await tracingTab.click()
await expect(logline).toBeVisible()

// Check logs on the policy
await policiesTab.click()
await ui.getRow('no-privileged-pod').open()
await tracingTab.click()
await expect(logline).toBeVisible()

// Cleanup
await ui.shell('k delete pod tracing-privpod')
})
27 changes: 20 additions & 7 deletions tests/e2e/components/table-row.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import { expect, Locator } from '@playwright/test';
import { RancherUI } from '../pages/rancher-ui';

/**
* Builds xpath expression to get column index by it's name
* @param name Name of the table column
* @returns xpath expression to get index of the column
*/
function xpath_colIndex(...names: string[]):string {
// Transform: names > normalize-space(.)="names[0]" [or ...]
const selector = names.map(str => `normalize-space(.)="${str}"`).join(" or ")
// Find thead > th with requested name and count how many th was before it
return `count(ancestor::table[1]/thead/tr/th[${selector}]/preceding-sibling::th)+1`
}

export class TableRow {

private readonly ui: RancherUI
Expand All @@ -10,8 +22,8 @@ export class TableRow {

/**
*
* @param page required by actions menu since it's not child of the table
* @param name of the row, has to be a link to the resource
* @param page is required by row actions menu since it's not child of the table
* @param name of the row, it is searched under "Name" column by default
* @param group When there are multiple tbodies filter by group-tab
*
*/
Expand All @@ -22,7 +34,7 @@ export class TableRow {
}

this.ui = ui
this.row = tbody.locator('tr.main-row').filter({has: ui.page.getByRole('link', {name: name, exact: true})})
this.row = tbody.locator(`xpath=tr[td[${xpath_colIndex("Name")}][normalize-space(.)="${name}"]]`)
this.name = this.column('Name')
this.status = this.column('Status', 'State')
}
Expand All @@ -37,11 +49,12 @@ export class TableRow {
await expect(this.status).toHaveText('Active', {timeout: timeout})
}

/**
* @param names header of the column, you can provide alternative names (State|Status)
* @returns table cell (td) that is under requested column. Returns first cell if no match was found
*/
column(...names: string[]) {
// Transform: names > normalize-space(.)="names[0]" [or ...]
const selector = names.map(str => `normalize-space(.)="${str}"`).join(" or ")
// https://stackoverflow.com/questions/14745478/how-to-select-table-column-by-column-header-name-with-xpath
return this.row.locator(`xpath=/td[count(ancestor::table[1]/thead/tr/th[${selector}]/preceding-sibling::th)+1]`)
return this.row.locator(`xpath=td[${xpath_colIndex(...names)}]`)
}

async action(name: string) {
Expand Down
18 changes: 9 additions & 9 deletions tests/e2e/exec.spec.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { test } from './rancher-test'
import { type Chart, RancherCommonPage } from './pages/rancher-common.page';
import { type Chart, RancherAppsPage } from './pages/rancher-apps.page';

const charts: Chart[] = [
{title: 'Jaeger Operator', namespace: 'jaeger', name: "jaeger-operator"},
{title: 'Monitoring', project: '(None)', name: "rancher-monitoring"},
{title: 'Kubewarden', project: 'Default', name: "rancher-kubewarden-controller"},
{title: 'Jaeger Operator', namespace: 'jaeger', check: "jaeger-operator"},
{title: 'Monitoring', project: '(None)', check: "rancher-monitoring"},
{title: 'Kubewarden', project: 'Default', check: "rancher-kubewarden-controller"},
]

// Install chart from apps menu
// app=Monitoring pw test exec -g 'appInstall' --headed
test('appInstall', async({ page}) => {
const chart = charts.find(o => o.title === process.env.app) || charts[0]

const rancher = new RancherCommonPage(page)
await rancher.installApp(chart)
const apps = new RancherAppsPage(page)
await apps.installChart(chart)
});

// Upgrade without any changes will reload app yaml (patched by kubectl)
// app='Jaeger Operator' pw test exec -g 'appUpdate' --headed
test('appUpdate', async({ page }) => {
const chart = charts.find(o => o.title === process.env.app) || charts[0]
const app = process.env.app || 'jaeger-operator'

const rancher = new RancherCommonPage(page)
await rancher.updateApp(chart)
const apps = new RancherAppsPage(page)
await apps.updateApp(app)
});
10 changes: 1 addition & 9 deletions tests/e2e/pages/basepolicypage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,6 @@ export class BasePolicyPage extends BasePage {
await this.ui.checkbox('Ignore Rancher Namespaces').setChecked(checked)
}

async editYaml() {
// Give generated fields time to get registered
await this.page.waitForTimeout(200)
// Show yaml with edited settings
await this.page.getByRole('button', { name: 'Edit YAML' }).click()
await expect(this.page.getByTestId('kw-policy-config-yaml-editor')).toBeVisible()
}

async open(p: Policy) {
// Open list of policies
await this.ui.createBtn.click()
Expand Down Expand Up @@ -91,7 +83,7 @@ export class BasePolicyPage extends BasePage {
// Extra policy settings
if (p.settings) {
await p.settings(this.ui)
await this.editYaml()
await this.ui.openYamlEditor()
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { expect, Locator, Page } from '@playwright/test';
import { BasePage } from './basepage';
import { RancherAppsPage } from './rancher-apps.page';

export class OverviewPage extends BasePage {
export class KubewardenPage extends BasePage {
readonly createPsBtn: Locator;
readonly createApBtn: Locator;
readonly createCapBtn: Locator;
Expand Down Expand Up @@ -58,21 +59,21 @@ export class OverviewPage extends BasePage {

// ==================================================================================================
// Rancher Application Metadata
await this.page.getByRole('button', { name: 'Next' }).click();
const apps = new RancherAppsPage(this.page)
await expect(apps.step1).toBeVisible()
await apps.nextBtn.click()
await expect(apps.step2).toBeVisible()

// Rancher Application Values
const schedule = this.ui.input('Schedule')
await expect(schedule).toHaveValue('*/60 * * * *')
await schedule.fill('*/1 * * * *')
await this.ui.checkbox('Enable Policy Reporter').check()

// Enable telemetry
// await page.getByRole('button', { name: 'Edit YAML' }).click()
// await editYaml(page, d => d.telemetry.enabled = true )
// await page.getByRole('button', { name: 'Compare Changes' }).click()

await this.page.getByRole('button', { name: 'Install' }).click();
await expect(this.ui.helmPassRegex('rancher-kubewarden-crds')).toBeVisible({ timeout: 30_000 });
await expect(this.ui.helmPassRegex('rancher-kubewarden-controller')).toBeVisible({ timeout: 60_000 });
// Start installation
await apps.installBtn.click()
await apps.waitHelmSuccess('rancher-kubewarden-crds')
await apps.waitHelmSuccess('rancher-kubewarden-controller')
}

async whitelistArtifacthub() {
Expand Down
26 changes: 20 additions & 6 deletions tests/e2e/pages/policyservers.page.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Locator, Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { expect } from '@playwright/test';
import { BasePage } from './basepage';
import { RancherAppsPage } from './rancher-apps.page';

export class PolicyServersPage extends BasePage {
readonly noDefaultPsBanner: Locator;
Expand Down Expand Up @@ -33,12 +35,24 @@ export class PolicyServersPage extends BasePage {
await this.ui.getRow(name).delete()
}

async installDefaultDialog(recommended: {enable: boolean, mode?: 'monitor' | 'protect'}) {
await this.ui.checkbox('Enable recommended policies').check()
async installDefault(options?: {recommended?: boolean, mode?: 'monitor' | 'protect'}) {
const apps = new RancherAppsPage(this.page)
// Skip metadata
await expect(apps.step1).toBeVisible()
await apps.nextBtn.click()
await expect(apps.step2).toBeVisible()

if (recommended.enable && recommended.mode) {
await this.ui.select('Execution mode', recommended.mode)
// Handle questions
if (options?.recommended) {
await this.ui.checkbox('Enable recommended policies').setChecked(options.recommended)
}
};
if (options?.mode) {
await this.ui.select('Execution mode', options.mode)
}

// Install
await apps.installBtn.click();
await apps.waitHelmSuccess('rancher-kubewarden-defaults')
}

}
Loading

0 comments on commit 4785f06

Please sign in to comment.