Skip to content

Commit

Permalink
Merge pull request #789 from kravciak/application-collection
Browse files Browse the repository at this point in the history
Set up tests for using ApplicationCollection
  • Loading branch information
kravciak authored Jul 15, 2024
2 parents 3d0ec1d + 49c8ba3 commit b515ba7
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 37 deletions.
8 changes: 6 additions & 2 deletions tests/e2e/00-installation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { PolicyServersPage } from './pages/policyservers.page'
import { apList, capList, ClusterAdmissionPoliciesPage } from './pages/policies.page'
import { RancherAppsPage } from './rancher/rancher-apps.page'
import { RancherFleetPage } from './rancher/rancher-fleet.page'
import { RancherUI } from './components/rancher-ui'

// source (yarn dev) | rc (add github repo) | released (just install)
const ORIGIN = process.env.ORIGIN || (process.env.API ? 'source' : 'rc')
Expand Down Expand Up @@ -52,9 +53,12 @@ test('Initial rancher setup', async({ page, ui, nav }) => {

test('Install UI extension', async({ page, ui }) => {
const extensions = new RancherExtensionsPage(page)
await extensions.goto()

await test.step('Enable extension support', async() => {
await extensions.enable({ rancherRepo: ORIGIN === 'released' })
if (RancherUI.isVersion('<2.9')) {
await extensions.enable({ rancherRepo: ORIGIN === 'released' })
}
// Wait for default list of extensions
if (ORIGIN === 'released') {
await ui.retry(async() => {
Expand All @@ -69,7 +73,7 @@ test('Install UI extension', async({ page, ui }) => {
const apps = new RancherAppsPage(page)
await page.getByTestId('extensions-page-menu').click()
await page.getByText('Manage Repositories', { exact: true }).click()
await apps.addRepository('kubewarden-extension-rc', 'https://rancher.github.io/kubewarden-ui/')
await apps.addRepository({ name: 'kubewarden-extension-rc', url: 'https://rancher.github.io/kubewarden-ui/' })
})
}

Expand Down
45 changes: 31 additions & 14 deletions tests/e2e/60-telemetry.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { test, expect } from './rancher/rancher-test'
import { Chart, RancherAppsPage } from './rancher/rancher-apps.page'
import { Chart, ChartRepo, RancherAppsPage } from './rancher/rancher-apps.page'
import { TelemetryPage } from './pages/telemetry.page'
import { RancherUI } from './components/rancher-ui'

// OpenTelemetry
const otelRepo: ChartRepo = { name: 'open-telemetry', url: 'https://open-telemetry.github.io/opentelemetry-helm-charts' }
const otelChart: Chart = { title: 'opentelemetry-operator', name: 'opentelemetry-operator', namespace: 'open-telemetry', check: 'opentelemetry-operator' }
const jaegerChart: Chart = { title: 'jaeger-operator', name: 'jaeger-operator', namespace: 'jaeger', check: 'jaeger-operator', version: '2.53.0' }
// Jaeger Tracing
const jaegerRepo: ChartRepo = { name: 'jaegertracing', url: 'https://jaegertracing.github.io/helm-charts' }
const jaegerChart: Chart = { title: 'jaeger-operator', name: 'jaeger-operator', namespace: 'jaeger', check: 'jaeger-operator' }
// Monitoring
const monitoringChart: Chart = { title: 'Monitoring', check: 'rancher-monitoring' }

test.skip(process.env.MODE === 'fleet')
Expand All @@ -22,7 +28,7 @@ test('Install OpenTelemetry', async({ page, nav }) => {
await expect(telPage.configBtn).toBeDisabled()
}
// Install OpenTelemetry
await apps.addRepository('open-telemetry', 'https://open-telemetry.github.io/opentelemetry-helm-charts')
await apps.addRepository(otelRepo)
await apps.installChart(otelChart,
{ yamlPatch: (y) => { y.manager.collectorImage.repository = 'otel/opentelemetry-collector-contrib' } })

Expand All @@ -43,18 +49,25 @@ test.describe('Tracing', () => {
await nav.pserver('default', 'Tracing')
})

test('Install Jaeger', async({ nav }) => {
test('Install Jaeger', async({ nav, shell }) => {
// Jaeger is not installed
await telPage.toBeIncomplete('jaeger')
await expect(telPage.configBtn).toBeDisabled()
// Install Jaeger
await apps.addRepository('jaegertracing', 'https://jaegertracing.github.io/helm-charts')
await apps.installChart(jaegerChart, {
yamlPatch: (y) => {
y.jaeger.create = true
y.rbac.clusterRole = true
}
})
if (RancherUI.hasAppCollection) {
await apps.installFromAppCollection(jaegerChart)
} else {
await apps.addRepository(jaegerRepo)
await apps.installChart(jaegerChart, {
yamlPatch: {
'jaeger.create' : true,
'rbac.clusterRole': true,
}
})
// Workaround for https://github.com/jaegertracing/helm-charts/issues/581
await shell.run('kubectl get clusterrole jaeger-operator -o json | jq \'.rules[] |= (select(.apiGroups | index("networking.k8s.io")).resources += ["ingressclasses"])\' | kubectl apply -f -')
}

// Jaeger is installed
await nav.pserver('default', 'Tracing')
await telPage.toBeComplete('jaeger')
Expand Down Expand Up @@ -91,7 +104,7 @@ test.describe('Tracing', () => {
await expect(logline).toBeVisible()
})

test('Uninstall tracing', async({ ui, nav }) => {
test('Uninstall tracing', async({ ui, nav, shell }) => {
// Clean up
await apps.updateApp('rancher-kubewarden-controller', {
questions: async() => {
Expand All @@ -100,7 +113,11 @@ test.describe('Tracing', () => {
}
})
await apps.deleteApp('jaeger-operator')
await apps.deleteRepository('jaegertracing')
await shell.run('kubectl delete ns jaeger')
if (!RancherUI.hasAppCollection) {
await apps.deleteRepository(jaegerRepo)
}

// Check
await nav.pserver('default', 'Tracing')
await telPage.toBeIncomplete('config')
Expand Down Expand Up @@ -201,5 +218,5 @@ test.describe('Metrics', () => {
test('Uninstall OpenTelemetry', async({ page }) => {
const apps = new RancherAppsPage(page)
await apps.deleteApp('opentelemetry-operator')
await apps.deleteRepository('open-telemetry')
await apps.deleteRepository(otelRepo)
})
8 changes: 5 additions & 3 deletions tests/e2e/components/kubectl-shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ export class Shell {
}

if (!options?.inPlace) await this.close()
else await this.cursor.press('Control+l')

return statusCode
}

Expand Down Expand Up @@ -174,12 +176,12 @@ export class Shell {
* @param options.timeout time in seconds, guess short for delay * tries
*/
@step
async retry(cmd: string, options?: { delay?: number, tries?: number, timeout?: number, runner?: Runner}) {
async retry(cmd: string, options?: { delay?: number, tries?: number, timeout?: number, runner?: Runner, inPlace?: boolean}) {
const delay = options?.delay || (options?.timeout ? Math.sqrt(options.timeout) : 10)
const tries = options?.tries || (options?.timeout ? options.timeout / delay : 5 * 6)
const runner = options?.runner || 'nodejs'

if (runner === 'rancher') await this.open()
if (runner === 'rancher' && !options?.inPlace) await this.open()
for (let i = 1; i < tries; i++) {
// If command passed break retry loop
if (await this.run(cmd, { status: NaN, inPlace: true, runner }) === 0) break
Expand All @@ -188,7 +190,7 @@ export class Shell {
// Final try
if (i === tries - 1) await this.run(cmd, { inPlace: true, runner })
}
if (runner === 'rancher') await this.close()
if (runner === 'rancher' && !options?.inPlace) await this.close()
}

@step
Expand Down
19 changes: 14 additions & 5 deletions tests/e2e/components/rancher-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,23 @@ export class RancherUI {
}
}

static isPrime(): boolean {
if (!process.env.RANCHER_PRIME) throw new Error('RANCHER_PRIME not set')
return process.env.RANCHER_PRIME === 'true'
static requireEnv(name: string): string {
const value = process.env[name]
if (!value) throw new Error(`Environment variable ${name} not set`)
return value
}

static get isPrime(): boolean {
return this.requireEnv('RANCHER_PRIME') === 'true'
}

static isVersion(query: string|Range): boolean {
if (!process.env.RANCHER_VERSION) throw new Error('RANCHER_VERSION not set')
if (!semver.validRange(query)) throw new Error(`Invalid range: ${query}`)
return semver.satisfies(process.env.RANCHER_VERSION, query, { includePrerelease: true })
return semver.satisfies(this.requireEnv('RANCHER_VERSION'), query, { includePrerelease: true })
}

static get hasAppCollection(): boolean {
// OCI repository support was added in 2.9
return this.isPrime && this.isVersion('>=2.9')
}
}
88 changes: 76 additions & 12 deletions tests/e2e/rancher/rancher-apps.page.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
import type { YAMLPatch } from '../components/rancher-ui'
import { RancherUI, type YAMLPatch } from '../components/rancher-ui'
import { Shell } from '../components/kubectl-shell'
import { step } from './rancher-test'
import { BasePage } from './basepage'

export interface ChartRepo {
// type: 'http'|'git'|'oci'
name: string
url: string
httpAuth?: {
username: string
password: string
}
// Git specific
branch?: string
}

export interface Chart {
title: string, // Exact chart title displayed in Rancher
check: string, // Used to check for helm success, chart name or tgz
Expand All @@ -13,6 +26,18 @@ export interface Chart {
project?: string,
}

export const appColRepo: ChartRepo = {
name : 'application-collection',
url : 'oci://dp.apps.rancher.io/charts',
httpAuth: (() => {
const auth = process.env.APP_COLLECTION_AUTH
if (auth) {
const [username, password] = auth.split(':')
return { username, password }
}
})()
}

export class RancherAppsPage extends BasePage {
readonly step1: Locator
readonly step2: Locator
Expand Down Expand Up @@ -48,35 +73,49 @@ export class RancherAppsPage extends BasePage {
* @param url Git or http(s) url of the repository
*/
@step
async addRepository(name: string, url: string) {
async addRepository(repo: ChartRepo) {
await this.nav.explorer('Apps', 'Repositories')
await this.ui.button('Create').click()

await this.ui.input('Name *').fill(name)
if (url.endsWith('.git')) {
await this.ui.input('Name *').fill(repo.name)
if (repo.url.endsWith('.git')) {
// Git repository
await this.page.getByRole('radio', { name: 'Git repository' }).check()
await this.ui.input('Git Repo URL *').fill(url)
await this.ui.input('Git Repo URL *').fill(repo.url)
} else if (repo.url.startsWith('oci://')) {
// OCI repository
await this.page.getByRole('radio', { name: 'OCI repository' }).check()
await this.ui.input('OCI Repository Host URL *').fill(repo.url)
} else {
// HTTP repository
await this.page.getByRole('radio', { name: 'http(s) URL' }).check()
await this.ui.input('Index URL *').fill(url)
await this.ui.input('Index URL *').fill(repo.url)
}
if (repo.httpAuth) {
// Auth takes a moment to load values
await expect(this.ui.select('Authentication')).toContainText('None')
await this.ui.selectOption('Authentication', 'Create a HTTP Basic Auth Secret')
await this.ui.input('Username').fill(repo.httpAuth.username)
await this.ui.input('Password').fill(repo.httpAuth.password)
}
await this.ui.button('Create').click()

await this.ui.button('Create').click()
// Transitions: Active ?> In Progress ?> [Active|InProgress] - https://github.com/rancher/dashboard/issues/10079
const repo = await this.ui.tableRow(name).waitFor()
const repoRow = await this.ui.tableRow(repo.name).waitFor()
// Wait out first Active state
await this.page.waitForTimeout(1000)
// Refresh for occasional freeze In Progress
await repo.action('Refresh')
await repoRow.action('Refresh')
// Prevent matching Active before refresh is processed
await this.page.waitForTimeout(1000)
await repo.toBeActive()
await repoRow.toBeActive()
}

@step
async deleteRepository(name: string) {
async deleteRepository(repo: string|ChartRepo) {
await this.nav.explorer('Apps', 'Repositories')
await this.ui.tableRow(name).delete()
const repoName = typeof repo === 'string' ? repo : repo.name
await this.ui.tableRow(repoName).delete()
}

/**
Expand Down Expand Up @@ -112,6 +151,31 @@ export class RancherAppsPage extends BasePage {
}
}

@step
async installFromAppCollection(chart: Chart) {
const shell = new Shell(this.page)
const shellOpts = { inPlace: true, runner: 'rancher' } as const
const ns = chart.namespace || 'default'

// Configure authentification
await shell.open()
// Secret to pull images from app collection
if (ns !== 'default') await shell.run(`kubectl create ns ${ns}`, shellOpts)
await shell.run(`kubectl create secret docker-registry application-collection -n ${ns} --docker-server=dp.apps.rancher.io --docker-username=${appColRepo.httpAuth?.username} --docker-password=${appColRepo.httpAuth?.password}`, shellOpts)
// Login to app collection to access helm chart
await shell.run(`helm registry login ${appColRepo.url.split('://')[1]} -u ${appColRepo.httpAuth?.username} -p ${appColRepo.httpAuth?.password}`, shellOpts)

// Chart-specific setup
if (chart.name === 'jaeger-operator') {
await shell.run(`helm install ${chart.name} ${appColRepo.url}/jaeger-operator -n ${ns} --set jaeger.create=true --set rbac.clusterRole=true --set image.imagePullSecrets[0]=application-collection`, shellOpts)
// Workaround for jaeger issue https://github.com/jaegertracing/helm-charts/issues/581
await shell.run('kubectl get clusterrole jaeger-operator -o json | jq \'.rules[] |= (select(.apiGroups | index("networking.k8s.io")).resources += ["ingressclasses"])\' | kubectl apply -f -', shellOpts)
// Patch SA to use pull secret for jaeger creation (retry waits for SA creation)
await shell.retry(`kubectl patch serviceaccount jaeger-operator-jaeger -n ${ns} -p '{"imagePullSecrets": [{"name": "application-collection"}]}'`, shellOpts)
}
await shell.close()
}

@step
async installChart(chart: Chart, options?: { questions?: () => Promise<void>, yamlPatch?: YAMLPatch, timeout?: number, navigate?: boolean }) {
// Select chart by title
Expand Down
1 change: 0 additions & 1 deletion tests/e2e/rancher/rancher-extensions.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export class RancherExtensionsPage extends BasePage {
const rancherRepo = this.ui.checkbox('Rancher Extension')
const partnersRepo = this.ui.checkbox('Partners Extension')

await this.goto()
await expect(this.page.getByRole('heading', { name: 'Extension support is not enabled' })).toBeVisible()

// Enable extensions
Expand Down

0 comments on commit b515ba7

Please sign in to comment.