Skip to content

Commit

Permalink
Merge branch 'main' into RHIDP-2919
Browse files Browse the repository at this point in the history
  • Loading branch information
Omar-AlJaljuli authored Sep 13, 2024
2 parents c90d7cf + e1576fe commit 5fe4b5a
Show file tree
Hide file tree
Showing 24 changed files with 504 additions and 156 deletions.
1 change: 1 addition & 0 deletions .ibm/pipelines/env_variables.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ DATA_ROUTER_URL=$(cat /tmp/secrets/DATA_ROUTER_URL)
DATA_ROUTER_USERNAME=$(cat /tmp/secrets/DATA_ROUTER_USERNAME)
DATA_ROUTER_PASSWORD=$(cat /tmp/secrets/DATA_ROUTER_PASSWORD)
DATA_ROUTER_PROJECT="main"
DATA_ROUTER_AUTO_FINALIZATION_TRESHOLD=$(cat /tmp/secrets/DATA_ROUTER_AUTO_FINALIZATION_TRESHOLD)
REPORTPORTAL_HOSTNAME=$(cat /tmp/secrets/REPORTPORTAL_HOSTNAME)
NEXUS_HOSTNAME=$(cat /tmp/secrets/NEXUS_HOSTNAME)
REDIS_TEMP_USER=temp
Expand Down
56 changes: 0 additions & 56 deletions .ibm/pipelines/openshift-ci-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -193,62 +193,6 @@ apply_yaml_files() {
fi
}

droute_send() {
# Skipping ReportPortal for nightly jobs on OCP v4.14 and v4.13 for now, as new clusters are not behind the RH VPN.
if [[ "$JOB_NAME" == *ocp-v4* ]]; then
return 0
fi

local release_name=$1
local project=$2
local droute_project="droute"
local droute_pod_name="droute-centos"
METEDATA_OUTPUT="data_router_metadata_output.json"

# Remove properties (only used for skipped test and invalidates the file if empty)
sed -i '/<properties>/,/<\/properties>/d' "${ARTIFACT_DIR}/${project}/${JUNIT_RESULTS}"

JOB_BASE_URL="https://prow.ci.openshift.org/view/gs/test-platform-results"
if [ -n "${PULL_NUMBER:-}" ]; then
JOB_URL="${JOB_BASE_URL}/pr-logs/pull/${REPO_OWNER}_${REPO_NAME}/${PULL_NUMBER}/${JOB_NAME}/${BUILD_ID}"
else
JOB_URL="${JOB_BASE_URL}/logs/${JOB_NAME}/${BUILD_ID}"
fi

jq \
--arg hostname "$REPORTPORTAL_HOSTNAME" \
--arg project "$DATA_ROUTER_PROJECT" \
--arg name "$JOB_NAME" \
--arg description "[View job run details](${JOB_URL})" \
--arg key1 "job_type" \
--arg value1 "$JOB_TYPE" \
--arg key2 "pr" \
--arg value2 "$GIT_PR_NUMBER" \
'.targets.reportportal.config.hostname = $hostname |
.targets.reportportal.config.project = $project |
.targets.reportportal.processing.launch.name = $name |
.targets.reportportal.processing.launch.description = $description |
.targets.reportportal.processing.launch.attributes += [
{"key": $key1, "value": $value1},
{"key": $key2, "value": $value2}
]' data_router/data_router_metadata_template.json > "${ARTIFACT_DIR}/${project}/${METEDATA_OUTPUT}"

oc rsync -n "${droute_project}" "${ARTIFACT_DIR}/${project}/" "${droute_project}/${droute_pod_name}:/tmp/droute"

oc exec -n "${droute_project}" "${droute_pod_name}" -- /bin/bash -c "
curl -fsSLk -o /tmp/droute-linux-amd64 'https://${NEXUS_HOSTNAME}/nexus/repository/dno-raw/droute-client/1.1/droute-linux-amd64' && chmod +x /tmp/droute-linux-amd64"

oc exec -n "${droute_project}" "${droute_pod_name}" -- /bin/bash -c "
/tmp/droute-linux-amd64 send --metadata /tmp/droute/${METEDATA_OUTPUT} \
--url '${DATA_ROUTER_URL}' \
--username '${DATA_ROUTER_USERNAME}' \
--password '${DATA_ROUTER_PASSWORD}' \
--results '/tmp/droute/${JUNIT_RESULTS}' \
--attachments '/tmp/droute/attachments' \
--verbose"

}

run_tests() {
local release_name=$1
local project=$2
Expand Down
60 changes: 60 additions & 0 deletions .ibm/pipelines/utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,63 @@ save_all_pod_logs(){
cp -a pod_logs/* "${ARTIFACT_DIR}/${namespace}/pod_logs"
set -e
}

droute_send() {
# Skipping ReportPortal for nightly jobs on OCP v4.14 and v4.13 for now, as new clusters are not behind the RH VPN.
if [[ "$JOB_NAME" == *ocp-v4* ]]; then
return 0
fi

local droute_version="1.2"
local release_name=$1
local project=$2
local droute_project="droute"
local droute_pod_name=$(oc get pods -n droute --no-headers -o custom-columns=":metadata.name" | grep ubi9-cert-rsync)
METEDATA_OUTPUT="data_router_metadata_output.json"

# Remove properties (only used for skipped test and invalidates the file if empty)
sed -i '/<properties>/,/<\/properties>/d' "${ARTIFACT_DIR}/${project}/${JUNIT_RESULTS}"

JOB_BASE_URL="https://prow.ci.openshift.org/view/gs/test-platform-results"
if [ -n "${PULL_NUMBER:-}" ]; then
JOB_URL="${JOB_BASE_URL}/pr-logs/pull/${REPO_OWNER}_${REPO_NAME}/${PULL_NUMBER}/${JOB_NAME}/${BUILD_ID}"
else
JOB_URL="${JOB_BASE_URL}/logs/${JOB_NAME}/${BUILD_ID}"
fi

jq \
--arg hostname "$REPORTPORTAL_HOSTNAME" \
--arg project "$DATA_ROUTER_PROJECT" \
--arg name "$JOB_NAME" \
--arg description "[View job run details](${JOB_URL})" \
--arg key1 "job_type" \
--arg value1 "$JOB_TYPE" \
--arg key2 "pr" \
--arg value2 "$GIT_PR_NUMBER" \
--arg auto_finalization_treshold $DATA_ROUTER_AUTO_FINALIZATION_TRESHOLD \
'.targets.reportportal.config.hostname = $hostname |
.targets.reportportal.config.project = $project |
.targets.reportportal.processing.launch.name = $name |
.targets.reportportal.processing.launch.description = $description |
.targets.reportportal.processing.launch.attributes += [
{"key": $key1, "value": $value1},
{"key": $key2, "value": $value2}
] |
.targets.reportportal.processing.tfa.auto_finalization_threshold = ($auto_finalization_treshold | tonumber)
' data_router/data_router_metadata_template.json > "${ARTIFACT_DIR}/${project}/${METEDATA_OUTPUT}"

oc rsync -n "${droute_project}" "${ARTIFACT_DIR}/${project}/" "${droute_project}/${droute_pod_name}:/tmp/droute"

oc exec -n "${droute_project}" "${droute_pod_name}" -- /bin/bash -c "
curl -fsSLk -o /tmp/droute-linux-amd64 'https://${NEXUS_HOSTNAME}/nexus/repository/dno-raw/droute-client/${droute_version}/droute-linux-amd64' && chmod +x /tmp/droute-linux-amd64"

oc exec -n "${droute_project}" "${droute_pod_name}" -- /bin/bash -c "
/tmp/droute-linux-amd64 send --metadata /tmp/droute/${METEDATA_OUTPUT} \
--url '${DATA_ROUTER_URL}' \
--username '${DATA_ROUTER_USERNAME}' \
--password '${DATA_ROUTER_PASSWORD}' \
--results '/tmp/droute/${JUNIT_RESULTS}' \
--attachments '/tmp/droute/attachments' \
--verbose"

}
2 changes: 1 addition & 1 deletion .sonarcloud.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# comma delimited path of files to exclude from copy/paste duplicate checking
sonar.cpd.exclusions=packages/app/src/components/DynamicRoot/DynamicRoot.test.tsx,packages/app/src/components/admin/AdminTabs.test.tsx,packages/app/src/components/catalog/EntityPage/defaultTabs.tsx,plugins/dynamic-plugins-info/src/components/InternalPluginsMap.tsx
sonar.cpd.exclusions=packages/app/src/components/DynamicRoot/DynamicRoot.test.tsx,packages/app/src/components/admin/AdminTabs.test.tsx,packages/app/src/components/catalog/EntityPage/defaultTabs.tsx,plugins/dynamic-plugins-info/src/components/InternalPluginsMap.tsx,e2e-tests/playwright/e2e/audit-log/LogUtils.ts
4 changes: 2 additions & 2 deletions dynamic-plugins/imports/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
"@janus-idp/backstage-plugin-quay": "1.11.3",
"@janus-idp/backstage-plugin-rbac": "1.29.4",
"@janus-idp/backstage-plugin-tekton": "3.12.3",
"@janus-idp/backstage-plugin-bulk-import": "1.4.6",
"@janus-idp/backstage-plugin-bulk-import-backend": "1.4.1",
"@janus-idp/backstage-plugin-bulk-import": "1.4.7",
"@janus-idp/backstage-plugin-bulk-import-backend": "1.5.2",
"@janus-idp/backstage-plugin-topology": "1.27.3",
"@janus-idp/backstage-scaffolder-backend-module-quay": "1.7.1",
"@janus-idp/backstage-scaffolder-backend-module-regex": "1.7.1",
Expand Down
4 changes: 4 additions & 0 deletions e2e-tests/data_router/data_router_metadata_template.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
"name": "",
"description": "",
"attributes": []
},
"tfa": {
"add_attributes": true,
"auto_finalize_defect_type": true
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
},
"devDependencies": {
"@playwright/test": "1.46.1",
"@types/node": "20.16.3",
"@types/node": "20.16.5",
"@typescript-eslint/eslint-plugin": "6.21.0",
"@typescript-eslint/parser": "6.21.0",
"eslint": "8.57.0",
Expand Down
66 changes: 66 additions & 0 deletions e2e-tests/playwright/e2e/audit-log/Log.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
class Actor {
actorId?: string;
hostname: string;
}

class LogRequest {
body?: object;
method: string;
params?: object;
query?: {
facet?: string[];
limit?: number;
offset?: number;
};
url: string;
}

class LogResponse {
status: number;
}

export class Log {
actor: Actor;
eventName: string;
isAuditLog: boolean;
level: string;
message: string;
meta: object;
plugin: string;
request: LogRequest;
response: LogResponse;
service: string;
stage: string;
status: string;
timestamp: string;

/**
* Constructor for the Log class.
* It sets default values for status and actorId, and allows other properties to be set or overridden.
*
* @param overrides Partial object to override default values in the Log class
*/
constructor(overrides: Partial<Log> = {}) {
// Default value for status
this.status = overrides.status || 'succeeded';
this.isAuditLog = overrides.isAuditLog || true;

// Default value for actorId, with other actor properties being optional
this.actor = {
actorId: overrides.actor?.actorId || 'user:development/guest', // Default actorId
hostname: overrides.actor?.hostname || '',
};

// Other properties without default values
this.eventName = overrides.eventName || '';
this.plugin = overrides.plugin || '';
this.message = overrides.message || '';
this.request = {
method: overrides.request?.method || '',
url: overrides.request?.url || '',
};
this.response = {
status: overrides.response?.status || 0,
};
}
}
144 changes: 144 additions & 0 deletions e2e-tests/playwright/e2e/audit-log/LogUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { expect } from '@playwright/test';
import { exec } from 'child_process';
import { Log } from './Log';

export class LogUtils {
/**
* Validates if the actual log matches the expected log values.
* It compares both primitive and nested object properties.
*
* @param actual The actual log returned by the system
* @param expected The expected log values to validate against
*/
public static validateLog(actual: Log, expected: Partial<Log>) {
// Loop through each key in the expected log object
Object.keys(expected).forEach(key => {
const expectedValue = expected[key as keyof Log];
const actualValue = actual[key as keyof Log];

LogUtils.compareValues(actualValue, expectedValue);
});
}

/**
* Compare the actual and expected values. Uses 'toBe' for numbers and 'toContain' for strings/arrays.
* Handles nested object comparison.
*
* @param actual The actual value to compare
* @param expected The expected value
*/
private static compareValues(actual: any, expected: any) {
if (typeof expected === 'object' && expected !== null) {
Object.keys(expected).forEach(subKey => {
const expectedSubValue = expected[subKey];
const actualSubValue = actual?.[subKey];
LogUtils.compareValues(actualSubValue, expectedSubValue);
});
} else if (typeof expected === 'number') {
expect(actual).toBe(expected);
} else {
expect(actual).toContain(expected);
}
}

/**
* Executes a shell command and returns the output as a promise.
*
* @param command The shell command to execute
* @returns A promise that resolves with the command output
*/
static executeCommand(command: string): Promise<string> {
return new Promise((resolve, reject) => {
exec(
command,
{ encoding: 'utf8', shell: '/bin/bash' },
(error, stdout, stderr) => {
if (error) {
console.error('Error executing command:', error);
reject(`Error: ${error.message}`);
return;
}
if (stderr) {
console.warn('stderr warning:', stderr);
}
resolve(stdout);
},
);
});
}

/**
* Fetches the logs from pods that match the fixed pod selector and applies a grep filter.
* The pod selector is:
* - app.kubernetes.io/component=backstage
* - app.kubernetes.io/instance=redhat-developer-hub
* - app.kubernetes.io/name=developer-hub
*
* @param grepFilter The string to filter the logs using grep
* @returns A promise that resolves with the filtered logs
*/
static async getPodLogsWithGrep(grepFilter: string): Promise<string> {
const podSelector =
'app.kubernetes.io/component=backstage,app.kubernetes.io/instance=rhdh,app.kubernetes.io/name=backstage';
const tailNumber = 30;
const command = `oc logs -l ${podSelector} --tail=${tailNumber} -c backstage-backend -n ${process.env.NAME_SPACE} | grep "${grepFilter}" | head -n 1`;
console.log(command);
try {
return await LogUtils.executeCommand(command);
} catch (error) {
console.error('Error fetching logs:', error);
throw new Error(`Failed to fetch logs: ${error}`);
}
}

/**
* Logs in to OpenShift using a token and server URL.
*
* @returns A promise that resolves when the login is successful
*/
static async loginToOpenShift(): Promise<void> {
const command = `oc login --token="${process.env.K8S_CLUSTER_TOKEN}" --server="${process.env.K8S_CLUSTER_URL}"`;
try {
const result = await LogUtils.executeCommand(command);
console.log('Login successful:', result);
} catch (error) {
console.error('Error during login:', error);
throw new Error(`Failed to login to OpenShift: ${error}`);
}
}

/**
* Validates if the actual log matches the expected log values for a specific event.
* This is a reusable method for different log validations across various tests.
*
* @param eventName The name of the event to filter in the logs
* @param message The expected log message
* @param method The HTTP method used in the log (GET, POST, etc.)
* @param url The URL endpoint that was hit in the log
* @param baseURL The base URL of the application, used to get the hostname
* @param plugin The plugin name that triggered the log event
*/
public static async validateLogEvent(
eventName: string,
message: string,
method: string,
url: string,
baseURL: string,
plugin: string,
) {
const actualLog = await LogUtils.getPodLogsWithGrep(eventName);
const expectedLog: Partial<Log> = {
actor: {
hostname: new URL(baseURL).hostname,
},
message,
plugin,
request: {
method,
url,
},
};
console.log(actualLog);
LogUtils.validateLog(JSON.parse(actualLog), expectedLog);
}
}
Loading

0 comments on commit 5fe4b5a

Please sign in to comment.