-
Notifications
You must be signed in to change notification settings - Fork 667
[Kubevirt] Test non-admin UI behaviour #2265
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { browser } from 'protractor'; | ||
| import { appHost } from '../../protractor.conf'; | ||
| import { execSync } from 'child_process'; | ||
| import { KUBEADMIN_IDP, KUBEADMIN_USERNAME, BRIDGE_KUBEADMIN_PASSWORD } from './utils/consts'; | ||
| import * as loginView from '../../views/login.view'; | ||
|
|
||
|
|
||
| describe('Authentication', () => { | ||
| beforeAll(async() => { | ||
| await browser.get(appHost); | ||
| await browser.sleep(3000); // Wait long enough for the login redirect to complete | ||
| }); | ||
|
|
||
| it('Web console logs in.', async() => { | ||
| await loginView.login(KUBEADMIN_IDP, KUBEADMIN_USERNAME, BRIDGE_KUBEADMIN_PASSWORD); | ||
| expect(loginView.userDropdown.getText()).toContain(KUBEADMIN_IDP); | ||
| execSync(`oc login -u ${KUBEADMIN_USERNAME} -p ${BRIDGE_KUBEADMIN_PASSWORD}`); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| import { $, browser, ExpectedConditions as until } from 'protractor'; | ||
|
|
||
| import { createItemButton, isLoaded } from '../../../views/crud.view'; | ||
| import { fillInput, selectDropdownOption, click } from '../utils/utils'; | ||
| import { cloudInitConfig, storageResource } from '../utils/types'; | ||
| import { PAGE_LOAD_TIMEOUT } from '../utils/consts'; | ||
| import * as wizardView from '../../../views/kubevirt/wizard.view'; | ||
|
|
||
|
|
||
| export default class Wizard { | ||
| async openWizard() { | ||
| await click(createItemButton); | ||
| await click(wizardView.createWithWizardLink); | ||
| await browser.wait(until.presenceOf(wizardView.nameInput), PAGE_LOAD_TIMEOUT); | ||
| } | ||
|
|
||
| async close() { | ||
| await click(wizardView.closeWizard, PAGE_LOAD_TIMEOUT); | ||
| await browser.wait(until.invisibilityOf(wizardView.wizardHeader), PAGE_LOAD_TIMEOUT); | ||
| // Clone VM dialog uses fade in/fade out effect, wait until it disappears | ||
| await browser.wait(until.invisibilityOf($('div.fade'))); | ||
| } | ||
|
|
||
| async fillName(name: string) { | ||
| await fillInput(wizardView.nameInput, name); | ||
| } | ||
|
|
||
| async fillDescription(description: string) { | ||
| await fillInput(wizardView.descriptionInput, description); | ||
| } | ||
|
|
||
| async selectNamespace(namespace: string) { | ||
| await selectDropdownOption(wizardView.namespaceDropdownId, namespace); | ||
| } | ||
|
|
||
| async selectTemplate(template: string) { | ||
| await selectDropdownOption(wizardView.templateDropdownId, template); | ||
| } | ||
|
|
||
| async selectOperatingSystem(operatingSystem: string) { | ||
| await selectDropdownOption(wizardView.operatingSystemDropdownId, operatingSystem); | ||
| } | ||
|
|
||
| async selectFlavor(flavor: string) { | ||
| await selectDropdownOption(wizardView.flavorDropdownId, flavor); | ||
| } | ||
|
|
||
| async selectWorkloadProfile(workloadProfile: string) { | ||
| await selectDropdownOption(wizardView.workloadProfileDropdownId, workloadProfile); | ||
| } | ||
|
|
||
| async selectProvisionSource(provisionOptions) { | ||
| await selectDropdownOption(wizardView.provisionSourceDropdownId, provisionOptions.method); | ||
| if (provisionOptions.hasOwnProperty('source')) { | ||
| await fillInput(wizardView.provisionSources[provisionOptions.method], provisionOptions.source); | ||
| } | ||
| } | ||
|
|
||
| async startOnCreation() { | ||
| await click(wizardView.startVMOnCreation); | ||
| } | ||
|
|
||
| async useCloudInit(cloudInitOptions: cloudInitConfig) { | ||
| await click(wizardView.useCloudInit); | ||
| if (cloudInitOptions.useCustomScript) { | ||
| await click(wizardView.useCustomScript); | ||
| await fillInput(wizardView.customCloudInitScript, cloudInitOptions.customScript); | ||
| } else { | ||
| await fillInput(wizardView.cloudInitHostname, cloudInitOptions.hostname); | ||
| await fillInput(wizardView.cloudInitSSH, cloudInitOptions.sshKey); | ||
| } | ||
| } | ||
|
|
||
| async next() { | ||
| await isLoaded(); | ||
| await click(wizardView.nextButton); | ||
| await isLoaded(); | ||
| } | ||
|
|
||
| async addNic(name: string, mac: string, networkDefinition: string, binding: string) { | ||
| await click(wizardView.createNIC); | ||
| const rowsCount = await this.getTableRowsCount(); | ||
| // Dropdown selection needs to be first due to https://github.com/kubevirt/web-ui-components/issues/9 | ||
| await wizardView.selectTableDropdownAttribute(rowsCount, 'network', networkDefinition); | ||
| await wizardView.selectTableDropdownAttribute(rowsCount, 'binding', binding), | ||
| await wizardView.setTableInputAttribute(rowsCount, 'name', name); | ||
| await wizardView.setTableInputAttribute(rowsCount, 'mac', mac); | ||
| await click(wizardView.apply); | ||
| } | ||
|
|
||
| async selectPxeNIC(networkDefinition: string) { | ||
| await selectDropdownOption(wizardView.pxeNICDropdownId, networkDefinition); | ||
| } | ||
|
|
||
| async getTableRowsCount() { | ||
| return await wizardView.tableRowsCount(); | ||
| } | ||
|
|
||
| async addDisk(disk: storageResource) { | ||
| await click(wizardView.createDisk); | ||
| const rowsCount = await this.getTableRowsCount(); | ||
| // Dropdown selection needs to be first due to https://github.com/kubevirt/web-ui-components/issues/9 | ||
| await wizardView.selectTableDropdownAttribute(rowsCount, 'storage', disk.storageClass); | ||
| await wizardView.setTableInputAttribute(rowsCount, 'name', disk.name); | ||
| await wizardView.setTableInputAttribute(rowsCount, 'size', disk.size); | ||
| await click(wizardView.apply); | ||
| } | ||
|
|
||
| async attachDisk(disk: storageResource) { | ||
| await click(wizardView.attachDisk); | ||
| const rowsCount = await this.getTableRowsCount(); | ||
| await wizardView.selectTableDropdownAttribute(rowsCount, 'name-attach', disk.name); | ||
| await click(wizardView.apply); | ||
| } | ||
|
|
||
| async editDiskAttribute(rowNumber: number, attribute: string, value: string) { | ||
| await wizardView.activateTableRow(rowNumber - 1); | ||
| if (attribute === 'storage') { | ||
| await wizardView.selectTableDropdownAttribute(rowNumber, attribute, value); | ||
| } else { | ||
| await wizardView.setTableInputAttribute(rowNumber, attribute, value); | ||
| } | ||
| await click(wizardView.apply); | ||
| } | ||
|
|
||
| async waitForCreation() { | ||
| await browser.wait(until.presenceOf(wizardView.provisionResultIcon)); | ||
| await browser.wait(until.elementToBeClickable(wizardView.nextButton), PAGE_LOAD_TIMEOUT); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { browser, ExpectedConditions as until } from 'protractor'; | ||
|
|
||
| import { createItemButton, errorMessage } from '../../../views/crud.view'; | ||
| import { createWithYAMLLink } from '../../../views/kubevirt/wizard.view'; | ||
| import { click } from '../utils/utils'; | ||
| import * as yamlView from '../../../views/yaml.view'; | ||
|
|
||
| export default class Yaml { | ||
| async openYamlPage() { | ||
| await click(createItemButton); | ||
| await click(createWithYAMLLink); | ||
| await yamlView.isLoaded(); | ||
| } | ||
|
|
||
| async createVMFromYaml() { | ||
| await click(yamlView.saveButton); | ||
| } | ||
|
|
||
| async cancelCreateVM() { | ||
| await click(yamlView.cancelButton); | ||
|
|
||
| } | ||
| async errorOccurOnCreateVM() { | ||
| await browser.wait(until.presenceOf(errorMessage)); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| /* eslint-disable no-undef */ | ||
| import { $, $$, browser, ExpectedConditions as until } from 'protractor'; | ||
| import { appHost, testName } from '../../protractor.conf'; | ||
| import { nonAdminSecret, nonAdminProvider } from './utils/mocks'; | ||
| import { createResources } from './utils/utils'; | ||
| import { KUBEADMIN_IDP, KUBEADMIN_USERNAME, BRIDGE_KUBEADMIN_PASSWORD, BRIDGE_HTPASSWD_IDP, BRIDGE_HTPASSWD_USERNAME, BRIDGE_HTPASSWD_PASSWORD } from './utils/consts'; | ||
| import * as loginView from '../../views/login.view'; | ||
| import * as crudView from '../../views/crud.view'; | ||
| import Wizard from './models/wizard'; | ||
| import Yaml from './models/yaml'; | ||
| import { logout } from '../../views/kubevirt/login.view'; | ||
| import { execSync } from 'child_process'; | ||
|
|
||
| describe('Test nonadmin user behaviour', () => { | ||
| const nonAdminNS = `${testName}-nonadmin`; | ||
|
|
||
| const verifyPermissions = async function(ns: string, perm: string) { | ||
| const wizard = new Wizard(); | ||
| const yaml = new Yaml(); | ||
|
|
||
| await browser.get(`${appHost}/k8s/ns/${ns}/virtualmachines`); | ||
| if (perm === 'noAccess') { | ||
| await browser.wait(until.presenceOf(crudView.errorMessage)); | ||
| expect(crudView.errorMessage.getText()).toContain('cannot list resource'); | ||
| // TODO: check it can't use vm dialog once BZ1728523 is fixed. | ||
|
||
| } | ||
| if (perm === 'view') { | ||
| await crudView.isLoaded(); // no error indicates it can view resource. | ||
| // TODO: check it can't use vm dialog once BZ1728523 is fixed. | ||
| } | ||
| if (perm === 'edit') { | ||
| await crudView.isLoaded(); | ||
| await wizard.openWizard(); | ||
| await wizard.close(); | ||
|
|
||
| await yaml.openYamlPage(); | ||
| await yaml.cancelCreateVM(); | ||
| } | ||
|
|
||
| await browser.get(`${appHost}/k8s/ns/${ns}/vmtemplates`); | ||
| if (perm === 'noAccess') { | ||
| await browser.wait(until.presenceOf(crudView.errorMessage)); | ||
| expect(crudView.errorMessage.getText()).toContain('cannot list resource'); | ||
| } | ||
| if (perm === 'view') { | ||
| await crudView.isLoaded(); // no error indicates it can view resource. | ||
| // TODO: check it can't use vm dialog once BZ1728523 is fixed. | ||
| } | ||
| if (perm === 'edit') { | ||
| await crudView.isLoaded(); | ||
| await wizard.openWizard(); | ||
| await wizard.close(); | ||
| } | ||
|
|
||
| await browser.get(`${appHost}/overview/ns/${ns}/`); | ||
| if (perm === 'noAccess') { | ||
| await browser.wait(until.presenceOf(crudView.errorMessage)); | ||
| expect(crudView.errorMessage.getText()).toContain('cannot list resource'); | ||
| } | ||
| if (perm === 'view') { | ||
| await crudView.isLoaded(); // no error indicates it can view resource. | ||
| // TODO: check it can't use vm dialog once BZ1728523 is fixed. | ||
| } | ||
| if (perm === 'edit') { | ||
| await crudView.isLoaded(); | ||
| } | ||
| }; | ||
|
|
||
| const verifyPermissionWithRoleBinding = async function(ns: string, binding: string, role: string, perm: string) { | ||
| execSync(`oc adm policy add-${binding}-to-user ${role} ${BRIDGE_HTPASSWD_USERNAME} -n ${ns}`); | ||
| await verifyPermissions(perm, ns); | ||
| execSync(`oc adm policy remove-${binding}-from-user ${role} ${BRIDGE_HTPASSWD_USERNAME} -n ${ns}`); | ||
| }; | ||
|
|
||
| beforeAll(async() => { | ||
| try { | ||
| execSync('oc get -o yaml -n openshift-config secret test-secret'); | ||
| } catch (error) { | ||
| createResources([nonAdminSecret, nonAdminProvider]); | ||
| } | ||
| }); | ||
|
|
||
| afterAll(async() => { | ||
| await logout(); | ||
| await browser.wait(until.presenceOf($('.login-pf'))); | ||
| await loginView.login(KUBEADMIN_IDP, KUBEADMIN_USERNAME, BRIDGE_KUBEADMIN_PASSWORD); | ||
| }); | ||
|
|
||
| it('Login with nonadmin', async() => { | ||
| await logout(); | ||
| // wait for logout complete | ||
| await browser.sleep(10000); | ||
gouyang marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // TODO: remove the refill appHost after bug 1721423 is fixed. | ||
| await browser.get(appHost); | ||
| await browser.wait(until.presenceOf($('.login-pf'))); | ||
| await loginView.login(BRIDGE_HTPASSWD_IDP, BRIDGE_HTPASSWD_USERNAME, BRIDGE_HTPASSWD_PASSWORD); | ||
| expect(loginView.userDropdown.getText()).toContain(BRIDGE_HTPASSWD_IDP); | ||
| }); | ||
|
|
||
| it(`Creates namespace ${nonAdminNS} for nonadmin tests`, async() => { | ||
| const resource = browser.params.openshift === 'true' ? 'projects' : 'namespaces'; | ||
| await browser.get(`${appHost}/k8s/cluster/${resource}`); | ||
| await crudView.isLoaded(); | ||
| const exists = await crudView.rowForName(nonAdminNS).isPresent(); | ||
| if (!exists) { | ||
| await crudView.createYAMLButton.click(); | ||
| await browser.sleep(3000); | ||
| await browser.wait(until.presenceOf($('.modal-body__field'))); | ||
| await $$('.modal-body__field').get(0).$('input').sendKeys(nonAdminNS); | ||
| await $$('.modal-body__field').get(1).$('input').sendKeys(`test-name=${nonAdminNS}`); | ||
| await $('.modal-content').$('#confirm-action').click(); | ||
| } | ||
| }); | ||
|
|
||
| it('Nonadmin can use vm dialog in its own namespace', async() => { | ||
| await verifyPermissions(nonAdminNS, 'edit'); | ||
| }); | ||
|
|
||
| it('Nonadmin cannot use vm dialog in other namespace', async() => { | ||
| await verifyPermissions(testName, 'noAccess'); | ||
| }); | ||
|
|
||
| it('Nonadmin with RoleBinding view can view vm objetcs in the binding NS', async() => { | ||
|
||
| await verifyPermissionWithRoleBinding(testName, 'role', 'view', 'view'); | ||
| }); | ||
|
|
||
| it('Nonadmin with RoleBinding view cannot view vm objetcs in other NS', async() => { | ||
| await verifyPermissionWithRoleBinding('default', 'role', 'view', 'noAccess'); | ||
| }); | ||
|
|
||
| it('Nonadmin with RoleBinding edit can edit vm objetcs in the binding NS', async() => { | ||
| await verifyPermissionWithRoleBinding(testName, 'role', 'edit', 'edit'); | ||
| }); | ||
|
|
||
| it('Nonadmin with RoleBinding edit cannot edit vm objetcs in other NS', async() => { | ||
| await verifyPermissionWithRoleBinding('default', 'role', 'edit', 'noAccess'); | ||
| }); | ||
|
|
||
| it('Nonadmin with ClusterRoleBinding view can view vm objetcs in the cluster', async() => { | ||
| await verifyPermissionWithRoleBinding(testName, 'cluster-role', 'view', 'view'); | ||
| await verifyPermissionWithRoleBinding('default', 'cluster-role', 'view', 'view'); | ||
| }); | ||
|
|
||
| it('Nonadmin with ClusterRoleBinding edit can edit vm objetcs in the cluster', async() => { | ||
| await verifyPermissionWithRoleBinding(testName, 'cluster-role', 'edit', 'edit'); | ||
| await verifyPermissionWithRoleBinding('default', 'cluster-role', 'edit', 'edit'); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still unclear on why we need a separate login scenario just for kubevirt. I'd rather not fork the login test code unless there is a very compelling reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It just has one more line about
oc loginas our current tests requires.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don’t need to use oc login for that. You just need the KUBECONFIG env var set. Admin console tests also run cli commands
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then we don't need another login. Could you comment on #2016?