Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Add Che update e2e test #1117

Merged
merged 3 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
29 changes: 29 additions & 0 deletions .github/workflows/e2e-minikube-operator-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#
# Copyright (c) 2021 Red Hat, Inc.
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# Red Hat, Inc. - initial API and implementation
name: Minikube E2E Che update
mmorhun marked this conversation as resolved.
Show resolved Hide resolved
on: pull_request
jobs:
minikube-e2e-update:
name: Operator installer update
mmorhun marked this conversation as resolved.
Show resolved Hide resolved
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v1
- name: Provision minikube cluster
run: |
minikube start --memory=6000
minikube addons enable ingress
- name: Install chectl dependencies
run: yarn
- name: Run e2e update tests minikube
run: |
export PLATFORM=minikube
export INSTALLER=operator
yarn test --coverage=false --forceExit --testRegex=test/e2e/e2e-upgrade.test.ts
167 changes: 167 additions & 0 deletions test/e2e/e2e-upgrade.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*********************************************************************
* Copyright (c) 2020 Red Hat, Inc.
mmorhun marked this conversation as resolved.
Show resolved Hide resolved
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/

// tslint:disable: no-console

import { expect } from '@oclif/test'
import * as execa from 'execa'

import { CheGithubClient } from '../../src/api/github-client'
import { KubeHelper } from '../../src/api/kube'
import { isKubernetesPlatformFamily } from '../../src/util'

import { DEVFILE_URL, E2eHelper } from './util'

const kube = new KubeHelper()
const helper = new E2eHelper()
jest.setTimeout(1000000)

const binChectl = `${process.cwd()}/bin/run`

const PLATFORM = process.env.PLATFORM || 'minikube'

const INSTALLER = 'operator'
const NAMESPACE = 'eclipse-che'

const NIGHTLY = 'nightly'

const UPDATE_CHE_TIMEOUT_MS = 5 * 60 * 1000
const WORKSPACE_START_TIMEOUT_MS = 5 * 60 * 1000
const CHE_VERSION_TIMEOUT_MS = 50 * 1000

async function runCommand(command: string, args?: string[]): Promise<string> {
console.log(`Running command: ${command} ${args ? args.join(' ') : ''}`)
const { exitCode, stdout, stderr } = await execa(command, args, { shell: true })
console.log(stdout)
if (exitCode !== 0) {
console.log(stderr)
}

expect(exitCode).equal(0)

return stdout
}

async function waitForVersionInCheCR(version: string, timeoutMs: number): Promise<boolean> {
mmorhun marked this conversation as resolved.
Show resolved Hide resolved
const delayMs = 5 * 1000

let totalTimeMs = 0
while (totalTimeMs < timeoutMs) {
const cheCR = await kube.getCheCluster(NAMESPACE)
if (cheCR.status.cheVersion === version) {
mmorhun marked this conversation as resolved.
Show resolved Hide resolved
return true
}
await helper.sleep(delayMs)
totalTimeMs += delayMs
}
return false
}

async function waitForCheServerImageTag(tag: string, timeoutMs: number): Promise<boolean> {
mmorhun marked this conversation as resolved.
Show resolved Hide resolved
const delayMs = 5 * 1000
const chePodNameRegExp = new RegExp('che-[0-9a-f]+-.*')

let totalTimeMs = 0
while (totalTimeMs < timeoutMs) {
const pods = (await kube.listNamespacedPod(NAMESPACE)).items
const pod = pods.find((pod => pod.metadata && pod.metadata.name && pod.metadata.name.match(chePodNameRegExp)))
if (pod && pod.status && pod.status.containerStatuses && pod.status.containerStatuses[0].image) {
const imageTag = pod.status.containerStatuses[0].image.split(':')[1]
if (imageTag === tag) {
return true
}
}
await helper.sleep(delayMs)
totalTimeMs += delayMs
}
return false
}

describe('Test Che upgrade', () => {
let cheVersion: string

describe('Prepare latest stable Che', () => {
it(`Deploy Che using ${INSTALLER} installer and self signed certificates`, async () => {
// Retrieve latest stable Che version
const githubClient = new CheGithubClient()
const latestStableCheTag = (await githubClient.getTemplatesTagInfo(INSTALLER, 'latest'))!
cheVersion = latestStableCheTag.name

const deployCommand = `${binChectl} server:deploy --platform=${PLATFORM} --installer=${INSTALLER} --version=${cheVersion} --chenamespace=${NAMESPACE} --telemetry=off --che-operator-cr-patch-yaml=test/e2e/resources/cr-patch.yaml`
await runCommand(deployCommand)

expect(await waitForVersionInCheCR(cheVersion, CHE_VERSION_TIMEOUT_MS)).equal(true)
})

it('Prepare test workspace', async () => {
await loginTest()

// Create
await runCommand(binChectl, ['workspace:create', `--devfile=${DEVFILE_URL}`, '--telemetry=off', `-n ${NAMESPACE}`])
const workspaceId = await helper.getWorkspaceId()

// Start
await runCommand(binChectl, ['workspace:start', workspaceId, `-n ${NAMESPACE}`, '--telemetry=off'])
expect(await helper.waitWorkspaceStatus('RUNNING', WORKSPACE_START_TIMEOUT_MS)).to.equal(true)

// Stop
await runCommand(binChectl, ['workspace:stop', workspaceId, `-n ${NAMESPACE}`, '--telemetry=off'])
const workspaceStatus = await helper.getWorkspaceStatus()
// The status could be STOPPING or STOPPED
expect(workspaceStatus).to.contain('STOP')
})
})

describe('Test Che update', () => {
it('Update Che to nightly version', async () => {
await runCommand(binChectl, ['server:update', '-y', `-n ${NAMESPACE}`, '--telemetry=off'])
expect(await waitForCheServerImageTag(NIGHTLY, UPDATE_CHE_TIMEOUT_MS)).equal(true)
})

it('Check updated Che version', async () => {
expect(await waitForVersionInCheCR(NIGHTLY, CHE_VERSION_TIMEOUT_MS)).equal(true)
})
})

describe('Test updated Che', () => {
it('Start existing workspace after update', async () => {
// Relogin
await loginTest()

const workspaceId = await helper.getWorkspaceId()
await runCommand(binChectl, ['workspace:start', workspaceId, `-n ${NAMESPACE}`, '--telemetry=off'])
expect(await helper.waitWorkspaceStatus('RUNNING', WORKSPACE_START_TIMEOUT_MS)).to.equal(true)
})
})

describe('Test Che downgrade', () => {
it('Downgrade Che', async () => {
await runCommand(binChectl, ['server:update', '-y', `--version=${cheVersion}`, `-n ${NAMESPACE}`, '--telemetry=off'])
expect(await waitForCheServerImageTag(cheVersion, UPDATE_CHE_TIMEOUT_MS)).equal(true)
})

it('Check downgraded Che version', async () => {
expect(await waitForVersionInCheCR(cheVersion, CHE_VERSION_TIMEOUT_MS)).equal(true)
})
})

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should check that workspace starts after downgrade.... But up to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use-case in the tests, at least my thoughts about it, is that downgrade is used if a user have completed upgrade, but something stopped to work due to a bug, but the user need the feature, so a step back is taken temporally and right after upgrade... So no need in such check.
If a used needs specific version of Che, it is possible to install it via version flag, no need to downgrade.

})

async function loginTest() {
mmorhun marked this conversation as resolved.
Show resolved Hide resolved
let cheApiEndpoint: string
if (isKubernetesPlatformFamily(PLATFORM)) {
cheApiEndpoint = await helper.K8SHostname('che', NAMESPACE) + '/api'
} else {
cheApiEndpoint = await helper.OCHostname('che', NAMESPACE) + '/api'
}

const stdout = await runCommand(binChectl, ['auth:login', cheApiEndpoint, '-u', 'admin', '-p', 'admin', '-n', `${NAMESPACE}`, '--telemetry=off'])
expect(stdout).to.contain('Successfully logged into')
}
16 changes: 16 additions & 0 deletions test/e2e/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ interface WorkspaceInfo {

const binChectl = `${process.cwd()}/bin/run`

export const DEVFILE_URL = 'https://raw.githubusercontent.com/eclipse/che-devfile-registry/master/devfiles/quarkus/devfile.yaml'

//Utilities to help e2e tests
export class E2eHelper {
protected kubeHelper: KubeHelper
Expand Down Expand Up @@ -85,6 +87,20 @@ export class E2eHelper {
return workspaceStatus
}

async waitWorkspaceStatus(status: string, timeoutMs: number): Promise<boolean> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually wait methods return void or throw an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to wait for an event and then decide what to do: fail or continue. But maybe it is not needed for tests...

const delayMs = 1000 * 5

let totalTimeMs = 0
while (totalTimeMs < timeoutMs) {
if (await this.getWorkspaceStatus() === status) {
return true
}
await this.sleep(delayMs)
totalTimeMs += delayMs
}
return false
}

//Return a route from Openshift adding protocol
async OCHostname(ingressName: string, namespace: string): Promise<string> {
if (await this.oc.routeExist(ingressName, namespace)) {
Expand Down