Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
43 changes: 43 additions & 0 deletions .ibm/pipelines/resources/sonarqube/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# SonarQube Installation Script

This script installs a SonarQube instance in an OpenShift cluster using the official SonarQube Helm chart.

## Prerequisites

- `kubectl` or `oc` CLI installed and configured to connect to your OpenShift cluster.
- `helm` CLI installed.

## Usage

The `install.sh` script supports the following parameters:

- `--namespace`: The namespace where SonarQube will be installed. (Default: `sonarqube`)
- `--values`: Path to a custom values file for Helm chart customization. (Default: `values.yaml` in the same directory)
- `--edition`: The SonarQube edition to install. (Default: `developer`)
- `--host`: The hostname for the OpenShift route. This is a mandatory parameter.

### Example

```shell
./install.sh --host sonar.<your-cluster-domain>
```

### Example with custom namespace

```shell
./install.sh --namespace my-sonarqube --host sonar.<your-cluster-domain>
```

## Configuration

The script uses a `values.yaml` file to configure the SonarQube Helm chart. You can modify this file to customize your SonarQube installation. For example, you can configure resource limits, persistence, and other chart values.

### External PostgreSQL

By default, the script installs a PostgreSQL database as part of the Helm release. To use an external PostgreSQL database, you can uncomment the following line in the `install.sh` script:

```shell
# HELM_ARGS="${HELM_ARGS} --set postgresql.enabled=false"
```

You will also need to provide the connection details for your external database in the `values.yaml` file.
46 changes: 46 additions & 0 deletions .ibm/pipelines/resources/sonarqube/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/bash

# Defaults
NAMESPACE="sonarqube"
VALUES_FILE="$(dirname "$0")/values.yaml"
EDITION="developer"
HOST=""

# Parse arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
--namespace) NAMESPACE="$2"; shift ;;
--values) VALUES_FILE="$2"; shift ;;
--edition) EDITION="$2"; shift ;;
--host) HOST="$2"; shift ;;
*) echo "Unknown parameter passed: $1"; exit 1 ;;
esac
shift
done

# Create namespace if it doesn't exist
kubectl get namespace "$NAMESPACE" > /dev/null || kubectl create namespace "$NAMESPACE"

helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube --force-update
helm repo update

HELM_ARGS="--install -n ${NAMESPACE} sonarqube sonarqube/sonarqube"
HELM_ARGS="${HELM_ARGS} --set edition=${EDITION}"

if [ -f "${VALUES_FILE}" ]; then
HELM_ARGS="${HELM_ARGS} -f ${VALUES_FILE}"
else
echo "Warning: Values file not found at ${VALUES_FILE}"
fi

if [ -n "${HOST}" ]; then
HELM_ARGS="${HELM_ARGS} --set OpenShift.route.host=${HOST}"
fi

HELM_ARGS="${HELM_ARGS} --set postgresql.image.repository=bitnami/postgresql"
HELM_ARGS="${HELM_ARGS} --set postgresql.image.tag=15.3.0"
# To use an external PostgreSQL, uncomment the following line
# HELM_ARGS="${HELM_ARGS} --set postgresql.enabled=false"

# shellcheck disable=SC2086
helm upgrade ${HELM_ARGS}
26 changes: 26 additions & 0 deletions .ibm/pipelines/resources/sonarqube/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
account:
adminPassword: NewAdminPassword1@
currentAdminPassword: admin

OpenShift:
enabled: true
serviceAccount:
create: true
route:
enabled: true
host: "sonarqube.<openshift-cluster-domain>"
path: "/"
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect

monitoringPasscode: "define_it"

postgresql:
image:
repository: bitnami/postgresql
tag: 15.3.0
securityContext:
enabled: false
containerSecurityContext:
enabled: false
15 changes: 15 additions & 0 deletions .ibm/pipelines/utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,19 @@ install_pipelines_operator() {
fi
}

install_sonar_qube() {
local namespace="sonarqube"
local host="sonarqube.${K8S_CLUSTER_ROUTER_BASE}"
local values_file="${DIR}/resources/sonarqube/values.yaml"

"${DIR}/resources/sonarqube/install.sh" \
--namespace "$namespace" \
--host "$host" \
--values "$values_file"
}

## todo implement uninstall_sonar_qube

# Installs the Tekton Pipelines if not already installed (alternative of OpenShift Pipelines for Kubernetes clusters)
install_tekton_pipelines() {
DISPLAY_NAME="tekton-pipelines-webhook"
Expand Down Expand Up @@ -663,13 +676,15 @@ delete_tekton_pipelines() {
}

cluster_setup_ocp_helm() {
install_sonar_qube
install_pipelines_operator
install_acm_ocp_operator
install_crunchy_postgres_ocp_operator
install_orchestrator_infra_chart
}

cluster_setup_ocp_operator() {
install_sonar_qube
install_pipelines_operator
install_acm_ocp_operator
install_crunchy_postgres_ocp_operator
Expand Down
2 changes: 2 additions & 0 deletions .ibm/pipelines/value_files/values_showcase.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ global:
# an optional `pluginConfig` with plugin-specific backstage configuration, and an optional `disabled` flag to disable/enable a plugin
# listed in `includes` files. It also includes an `integrity` field that is used to verify the plugin package [integrity](https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description).
plugins:
- package: ./dynamic-plugins/dist/backstage-community-plugin-scaffolder-backend-module-sonarqube-dynamic
disabled: false
- package: oci://quay.io/gashcrumb/example-root-http-middleware:latest!internal-backstage-plugin-middleware-header-example-dynamic
disabled: false
pluginConfig:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { test } from "@playwright/test";
import { Common, setupBrowser } from "../../../utils/common";
import { UIhelper } from "../../../utils/ui-helper";
import { CatalogImport } from "../../../support/pages/catalog-import";

test.describe("Test SonarKube Actions plugin", () => {
let common: Common;
let uiHelper: UIhelper;
let catalogImport: CatalogImport;
let project: string;
let projectKey: string;

const template =
"https://github.com/backstage/community-plugins/blob/main/workspaces/scaffolder-backend-module-sonarqube/plugins/scaffolder-backend-module-sonarqube/examples/templates/01-sonar-template.yaml";

test.beforeEach(async ({ browser, page }, testInfo) => {
page = (await setupBrowser(browser, testInfo)).page;
common = new Common(page);
uiHelper = new UIhelper(page);
catalogImport = new CatalogImport(page);

await common.loginAsGuest();
await page.goto("/create");
});

test("Creates Sonarkube project", async () => {
await uiHelper.clickButton("Import an existing Git repository");
await catalogImport.registerExistingComponent(template, false);

await uiHelper.clickLink({ ariaLabel: "Self-service" });
await common.waitForLoad();
await uiHelper.waitForTitle("Self-service");
await uiHelper.searchInputPlaceholder("Create a SonarQube project");
await uiHelper.verifyText("Create a SonarQube project");

project = `test-sonarqube-actions-${Date.now()}`;
projectKey = `any-key-${Date.now()}`;

await uiHelper.clickBtnInCard("Create a SonarQube project", "Choose");

await uiHelper.waitForTitle("Create a SonarQube project", 2);

const baseRHDHURL: string = process.env.BASE_URL;
const host: string = new URL(baseRHDHURL).hostname;
const domain = host.split(".").slice(1).join(".");
await uiHelper.fillTextInputByLabel("Base URL", `https://sonar.${domain}`);

await uiHelper.selectMuiBox(
"root_authParams__oneof_select",
"Username and Password",
);

await uiHelper.fillTextInputByLabel("Username *", "admin");
await uiHelper.fillTextInputByLabel("Password", "NewAdminPassword1@", true);

await uiHelper.fillTextInputByLabel("Name *", project, true);
await uiHelper.fillTextInputByLabel("Key *", projectKey);
await uiHelper.fillTextInputByLabel("Branch", "main");
await uiHelper.clickButton("Review");
await uiHelper.clickButton("Create");
await uiHelper.clickLinkWithNewTab(/SonarQube project URL/i);
Copy link
Member

Choose a reason for hiding this comment

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

/i is it intentional ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is regular expression. Url link contains project name with random part.


await uiHelper.isLinkVisible(project);
});
});
19 changes: 15 additions & 4 deletions e2e-tests/playwright/utils/ui-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export class UIhelper {
return this.page.getByTestId("login-button").getByText(menuItem);
}

async fillTextInputByLabel(label: string, text: string) {
await this.page.getByLabel(label).fill(text);
async fillTextInputByLabel(label: string, text: string, exact?: boolean) {
await this.page.getByLabel(label, { exact }).fill(text);
}

/**
Expand Down Expand Up @@ -51,6 +51,15 @@ export class UIhelper {
await locator.check();
}

async clickLinkWithNewTab(name: string | RegExp) {
const [newPage] = await Promise.all([
this.page.context().waitForEvent("page"),
this.page.getByRole("link", { name }).click(),
]);

await newPage.waitForLoadState();
}

async clickButton(
label: string | RegExp,
options: { exact?: boolean; force?: boolean } = {
Expand Down Expand Up @@ -332,8 +341,10 @@ export class UIhelper {
await navLink.click();
}

async selectMuiBox(label: string, value: string) {
await this.page.click(`div[aria-label="${label}"]`);
async selectMuiBox(labelOrId: string, value: string) {
await this.page.click(
`div[aria-label="${labelOrId}"], div[id="${labelOrId}"]`,
);
const optionSelector = `li[role="option"]:has-text("${value}")`;
await this.page.waitForSelector(optionSelector);
await this.page.click(optionSelector);
Expand Down
Loading