Skip to content

Commit

Permalink
Merge branch 'release/v6.7.6-1'
Browse files Browse the repository at this point in the history
  • Loading branch information
robertauer committed Mar 14, 2019
2 parents 3488a6e + a796e33 commit 5194c82
Show file tree
Hide file tree
Showing 20 changed files with 898 additions and 477 deletions.
30 changes: 17 additions & 13 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
FROM registry.cloudogu.com/official/java:8u171-1
FROM registry.cloudogu.com/official/java:8u191-1

LABEL NAME="official/sonar" \
VERSION="5.6.7-2" \
maintainer="sebastian.sdorra@cloudogu.com"
VERSION="6.7.6-1" \
maintainer="robert.auer@cloudogu.com"

ENV SONAR_VERSION=5.6.7 \
ENV SONAR_VERSION=6.7.6 \
SONARQUBE_HOME=/opt/sonar \
# mark as webapp for nginx
SERVICE_TAGS=webapp
SERVICE_TAGS=webapp \
CAS_PLUGIN_VERSION=1.0.5

RUN set -x \
&& apk add --no-cache procps postgresql-client \
&& mkdir /opt \
&& cd /tmp \
&& curl -L -O https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${SONAR_VERSION}.zip \
&& rm -rf /var/cache/apk/* \
# get SonarQube
&& curl --fail --remote-name --location https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${SONAR_VERSION}.zip \
&& unzip sonarqube-${SONAR_VERSION}.zip \
&& mv sonarqube-${SONAR_VERSION} ${SONARQUBE_HOME} \
&& rm -rf /var/cache/apk/* \
# create user
# get sonar-cas-plugin
&& curl --fail --location https://github.com/cloudogu/sonar-cas-plugin/releases/download/v${CAS_PLUGIN_VERSION}/sonar-cas-plugin-${CAS_PLUGIN_VERSION}.jar --output ${SONARQUBE_HOME}/extensions/plugins/sonar-cas-plugin-${CAS_PLUGIN_VERSION}.jar \
# create sonar user
&& addgroup -S -g 1000 sonar \
&& adduser -S -h "$SONARQUBE_HOME" -s /bin/bash -G sonar -u 1000 sonar \
&& chown -R sonar:sonar ${SONARQUBE_HOME}
&& adduser -S -h "$SONARQUBE_HOME" -s /bin/bash -G sonar -u 1000 sonar

# Copy resources
# Includes copying sonar-cas plugin (into wrong folder; correct folder would be: /var/lib/sonar/extensions/plugins/)
# is moved to right folder via startup.sh
COPY ./resources /

RUN chown -R sonar:sonar ${SONARQUBE_HOME}

EXPOSE 9000

USER sonar

CMD ["/startup.sh"]
2 changes: 1 addition & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!groovy
@Library(['github.com/cloudogu/dogu-build-lib@a9f0e688', 'github.com/cloudogu/zalenium-build-lib@d8b74327']) _
@Library(['github.com/cloudogu/dogu-build-lib@1e5e2a63', 'github.com/cloudogu/zalenium-build-lib@30923630']) _
import com.cloudogu.ces.dogubuildlib.*

node('vagrant') {
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@

**Dependencies:** postgresql, cas, nginx, postfix

## Installation Ecosystem
**Persistent Dogu Admin User:**

This SonarQube Dogu holds a persistent user called "sonarqubedoguadmin" for configuration purposes which should not be removed.

## Importing Quality Profiles

To import quality profiles into the SonarQube dogu please follow the steps described here: https://github.com/cloudogu/ecosystem/blob/develop/docs/docs/user-guide/sonar.md

## Installation in Cloudogu EcoSystem
```
cesapp install official/sonar
Expand Down
17 changes: 15 additions & 2 deletions dogu.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"Name": "official/sonar",
"Version": "5.6.7-2",
"Version": "6.7.6-1",
"DisplayName": "SonarQube",
"Description": "SonarQube is an open source quality management platform, dedicated to continuously analyze and measure source code quality",
"Category": "Development Apps",
Expand All @@ -15,7 +15,7 @@
],
"Volumes": [{
"Name": "data",
"Path": "/var/lib/sonar/",
"Path": "/opt/sonar/data/",
"Owner": "1000",
"Group": "1000",
"NeedsBackup": true
Expand All @@ -30,6 +30,13 @@
"ServiceAccounts": [{
"Type": "postgresql"
}],
"Configuration": [
{
"Name": "sonar.updatecenter.url",
"Description": "Set custom SonarQube UpdateCenter URL",
"Optional": true
}
],
"HealthChecks": [{
"Type": "tcp",
"Port": 9000
Expand All @@ -39,5 +46,11 @@
"ExposedCommands": [{
"Name": "post-upgrade",
"Command": "/post-upgrade.sh"
},{
"Name": "upgrade-notification",
"Command": "/upgrade-notification.sh"
},{
"Name": "pre-upgrade",
"Command": "/pre-upgrade.sh"
}]
}
27 changes: 20 additions & 7 deletions integrationTests/adminFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,34 @@ module.exports = class AdminFunctions{


async testUserLogin(driver) {

// getting sonar login page
await driver.get(config.baseUrl + config.sonarContextPath);
// waiting for cas login page to show up
await driver.wait(until.elementLocated(By.id('password')), 5000);
// inserting username and password
await driver.findElement(By.id('username')).sendKeys(this.testuserName);
await driver.findElement(By.id('password')).sendKeys(this.testuserPasswort);
// clicking login button
await driver.findElement(By.css('input[name="submit"]')).click();
};

async testUserLogout(driver) {
// opening user dropdown menu
await this.showUserMenu(driver);
// wait for dropdown menu
await driver.wait(until.elementLocated(By.className("dropdown-menu dropdown-menu-right")),5000);
// wait for sonar-cas-plugin to inject logout code
// timeout is set in https://github.com/cloudogu/sonar-cas-plugin/blob/develop/src/main/resources/casLogoutUrl.js
await driver.sleep(500)
// click logout link
await driver.findElement(By.className("dropdown-menu dropdown-menu-right")).findElement(By.css("a[href*='#']")).click();
};

await driver.wait(until.elementLocated(By.css("#global-navigation > div > ul.nav.navbar-nav.navbar-right > li:nth-child(1) > a")),5000);
await driver.findElement(By.css("#global-navigation > div > ul.nav.navbar-nav.navbar-right > li:nth-child(1)")).click();
await driver.wait(until.elementLocated(By.css("#global-navigation > div > ul.nav.navbar-nav.navbar-right > li:nth-child(1) > ul > li:nth-child(2) > a")),5000);
await driver.findElement(By.css("#global-navigation > div > ul.nav.navbar-nav.navbar-right > li:nth-child(1) > ul > li:nth-child(2) > a")).click();
await driver.wait(until.elementLocated(By.className('success')), 5000);
async showUserMenu(driver) {
// wait for user button
await driver.wait(until.elementLocated(By.className("dropdown-toggle navbar-avatar")),5000);
// click user button
await driver.findElement(By.className("dropdown-toggle navbar-avatar")).click();
};

async giveAdminRightsUsermgt(){
Expand Down Expand Up @@ -110,7 +123,7 @@ module.exports = class AdminFunctions{
await request(config.baseUrl)
.get(config.sonarContextPath + "/api/users/groups?login="+this.testuserName)
.auth(this.testuserName, this.testuserPasswort)
.expect('Content-Type', 'application/json;charset=utf-8')
.expect('Content-Type', 'application/json')
.type('json')
.expect(expectStatus);//403 = "Forbidden", 200 = "OK"
};
Expand Down
34 changes: 26 additions & 8 deletions integrationTests/cas-browser.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const config = require('./config');
const utils = require('./utils');
const expectations = require('./expectations');

const AdminFunctions = require('./adminFunctions');
const userName = 'testUser';

require('chromedriver');
const webdriver = require('selenium-webdriver');
Expand All @@ -11,10 +12,11 @@ const until = webdriver.until;
jest.setTimeout(30000);

let driver;

let adminFunctions;

beforeEach(async () => {
driver = utils.createDriver(webdriver);
adminFunctions = await new AdminFunctions(userName, userName, userName, userName+'@test.de', 'testuserpassword');
await driver.manage().window().maximize();
});

Expand All @@ -33,17 +35,17 @@ describe('cas browser login', () => {

test('cas authentication', async() => {
await driver.get(utils.getCasUrl(driver));
await utils.login(driver);
const username = await driver.wait(until.elementLocated(By.css("#global-navigation > div > ul.nav.navbar-nav.navbar-right > li:nth-child(1) > a"))).getText();
await utils.login(driver);
await adminFunctions.showUserMenu(driver)
const username = await driver.findElement(By.className("text-ellipsis text-muted")).getText();
expect(username).toContain(config.displayName);
});

test('logout front channel', async() => {
await driver.get(utils.getCasUrl(driver));
await utils.login(driver);
await driver.wait(until.elementLocated(By.css("#global-navigation > div > ul.nav.navbar-nav.navbar-right > li:nth-child(1) > a")),5000);
await driver.findElement(By.css("#global-navigation > div > ul.nav.navbar-nav.navbar-right > li:nth-child(1)")).click();
await driver.findElement(By.css("#global-navigation > div > ul.nav.navbar-nav.navbar-right > li:nth-child(1) > ul > li:nth-child(2) > a")).click();
await adminFunctions.testUserLogout(driver)
await driver.sleep(1000)
const url = await driver.getCurrentUrl();
expectations.expectCasLogout(url);
});
Expand All @@ -57,6 +59,22 @@ describe('cas browser login', () => {
expectations.expectCasLogin(url);
});

test('login and redirect', async() => {
// call a subpage of sonar without being logged in
const issues_subpage = config.baseUrl + config.sonarContextPath + '/issues?resolved=false'
await driver.get(issues_subpage);
// make sure you landed on cas login page
var url = await driver.getCurrentUrl();
expectations.expectCasLogin(url);
// login via cas
await utils.login(driver);
// wait for issues page to show up
await driver.wait(until.elementLocated(By.className("issues-my-issues-filter")), 5000);
// check that you have been redirected to the correct subpage
url = await driver.getCurrentUrl();
expect(url).toBe(issues_subpage);
});

});

describe('browser attributes', () => {
Expand All @@ -69,7 +87,7 @@ describe('browser attributes', () => {
const emailAddress = await driver.findElement(By.id("email")).getText();
const username = await driver.findElement(By.id("login")).getText();
expect(emailAddress).toBe(config.email);
expect(username).toBe(config.displayName);
expect(username).toBe(config.username);
});

test('front channel user administrator', async () => {
Expand Down
59 changes: 22 additions & 37 deletions integrationTests/cas-rest.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,12 @@ require('chromedriver');
const webdriver = require('selenium-webdriver');
const By = webdriver.By;
const until = webdriver.until;
const waitInterval = 3000;
jest.setTimeout(60000);

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';


let driver;

beforeEach(async () => {
driver = utils.createDriver(webdriver);
await driver.manage().window().maximize();
});

afterEach(async () => {
await driver.quit();
});



describe('cas rest basic authentication', () => {

test('authentication with username password', async () => {
Expand All @@ -35,57 +22,55 @@ describe('cas rest basic authentication', () => {
.expect(200);
});

/*login -> click on username -> configure -> show api token*/
test('authentication with API key', async () => {
//Create user Token
driver = utils.createDriver(webdriver);
// Login and go to user tokens page
await driver.get(utils.getCasUrl(driver));
await utils.login(driver);
await driver.get(config.baseUrl + config.sonarContextPath + "/account/security");
await driver.sleep(waitInterval);


await driver.wait(until.elementLocated(By.className("columns")), 5000);
await driver.findElement(By.css("form.js-generate-token-form input")).sendKeys(config.sonarqubeToken);
await driver.findElement(By.css("#content > div > div > div > div > div > form > button")).click(); //Click to create Token
await driver.sleep(waitInterval);
await driver.wait(until.elementLocated(By.className("columns")), 5000);
const apikey = await driver.findElement(By.className("text-success")).getText(); //Saving Token
await driver.wait(until.elementLocated(By.className("js-generate-token-form spacer-top panel bg-muted")), 5000);
// Create user Token
await driver.findElement(By.css("input[type='text']")).sendKeys(config.sonarqubeToken);
await driver.findElement(By.xpath("//button[contains(text(),'Generate')]")).click(); //Click to create Token
// Get token id
await driver.wait(until.elementLocated(By.className("monospaced text-success")), 5000);
const apikey = await driver.findElement(By.className("monospaced text-success")).getText(); //Saving Token
//Checking login with Token
await request(config.baseUrl)
.get(config.sonarContextPath + "/api/users/search/json")
.get(config.sonarContextPath + "/api/system/health")
.auth(apikey)
.expect(200);
//Deleting user Token
await driver.get(config.baseUrl + config.sonarContextPath + "/account/security");
await driver.wait(until.elementLocated(By.className("js-revoke-token-form")), 5000);
await driver.findElement(By.className("js-revoke-token-form")).click(); // Click to delete
await driver.findElement(By.className("js-revoke-token-form")).click(); // Click to confirm deletion
await driver.wait(until.elementLocated(By.className("js-generate-token-form spacer-top panel bg-muted")), 5000);
await driver.findElement(By.className("button-red input-small")).click(); // Click to delete
await driver.findElement(By.className("button-red active input-small")).click(); // Click to confirm deletion

await driver.quit();
});

test('rest - user attributes', async () => {
test('rest - check user attributes', async () => {
const response = await request(config.baseUrl)
.get(config.sonarContextPath + "/api/users/search/json")
.get(config.sonarContextPath + "/api/users/search")
.auth(config.username, config.password)
.expect('Content-Type', 'application/json;charset=utf-8')
.expect('Content-Type', 'application/json')
.type('json')
.send({'q': config.username})
.expect(200);

const userObject = JSON.parse(response["request"]["req"]["res"]["text"]).users[0];
const userObject = JSON.parse(response["request"]["req"]["res"]["text"]).users[1];
expectations.expectStateUser(userObject);
});

test('rest - user is administrator', async () => {
await driver.sleep(waitInterval);
const response = await request(config.baseUrl)
.get(config.sonarContextPath + "/api/users/search/json")
.get(config.sonarContextPath + "/api/users/search")
.auth(config.username, config.password)
.expect('Content-Type', 'application/json;charset=utf-8')
.expect('Content-Type', 'application/json')
.type('json')
.send({'q': config.username})
.expect(200);

const userObject = JSON.parse(response["request"]["req"]["res"]["text"]).users[0];
const userObject = JSON.parse(response["request"]["req"]["res"]["text"]).users[1];
expectations.expectStateAdmin(userObject);
});

Expand Down
2 changes: 1 addition & 1 deletion integrationTests/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module.exports = {
password: 'ecosystem2016',
firstname:'admin',
lastname: 'admin',
displayName: 'ces-admin',
displayName: 'admin',
email: '[email protected]',
webdriverType: webdriverType,
debug: true,
Expand Down
4 changes: 2 additions & 2 deletions integrationTests/expectations.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ exports.expectStateAdmin = function(user) {
}

exports.expectCasLogin = function(url) {
expect(url).toBe(config.baseUrl + '/cas/login?TARGET=https%3A%2F%2F' +config.fqdn+'%2Fsonar%2Fcas%2Fvalidate');
expect(url).toBe(config.baseUrl + '/cas/login?service=https://'+config.fqdn+'/sonar/sessions/init/cas');
}

exports.expectCasLogout = function(url) {
expect(url).toBe(config.baseUrl + '/cas/logout?service=https%3A%2F%2F'+config.fqdn+'%2Fsonar');
expect(url).toBe(config.baseUrl + '/cas/logout');
}
Loading

0 comments on commit 5194c82

Please sign in to comment.