Skip to content

Commit

Permalink
test
Browse files Browse the repository at this point in the history
Signed-off-by: Gustavo Lira <[email protected]>
  • Loading branch information
gustavolira committed Jan 17, 2025
1 parent 76bbff0 commit 8fefdf4
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 64 deletions.
2 changes: 1 addition & 1 deletion e2e-tests/playwright/e2e/audit-log/catalog.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { UIhelper } from "../../utils/ui-helper";
import { LogUtils } from "./log-utils";
import { CatalogImport } from "../../support/pages/catalog-import";

test.describe.skip("Audit Log check for Catalog Plugin", () => {
test.describe("Audit Log check for Catalog Plugin", () => {
let uiHelper: UIhelper;
let common: Common;
let catalogImport: CatalogImport;
Expand Down
215 changes: 152 additions & 63 deletions e2e-tests/playwright/e2e/audit-log/log-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,29 @@ import { execFile } from "child_process";
import { Log } from "./logs";

export class LogUtils {
/**
* Executes a command and returns the output as a promise.
*
* @param command The command to execute
* @param args An array of arguments for the command
* @returns A promise that resolves with the command output
*/
static executeCommand(command: string, args: string[] = []): Promise<string> {
return new Promise((resolve, reject) => {
execFile(command, args, { encoding: "utf8" }, (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);
});
});
}

/**
* Validates if the actual log matches the expected log values.
* It compares both primitive and nested object properties.
Expand All @@ -11,7 +34,6 @@ export class LogUtils {
* @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];
Expand Down Expand Up @@ -42,71 +64,116 @@ export class LogUtils {
}

/**
* Executes a command and returns the output as a promise.
* Lists all pods in the specified namespace and returns their details.
*
* @param command The command to execute
* @param args An array of arguments for the command
* @returns A promise that resolves with the command output
* @param namespace The namespace to list pods from
* @returns A promise that resolves with the pod details
*/
static executeCommand(command: string, args: string[] = []): Promise<string> {
return new Promise((resolve, reject) => {
execFile(command, args, { encoding: "utf8" }, (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);
});
});
static async listPods(namespace: string): Promise<string> {
const args = ["get", "pods", "-n", namespace, "-o", "wide"];
try {
console.log("Fetching pod list with command:", "oc", args.join(" "));
return await LogUtils.executeCommand("oc", args);
} catch (error) {
console.error("Error listing pods:", error);
throw new Error(
`Failed to list pods in namespace "${namespace}": ${error}`,
);
}
}

/**
* 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
* Fetches detailed information about a specific pod.
*
* @param podName The name of the pod to fetch details for
* @param namespace The namespace where the pod is located
* @returns A promise that resolves with the pod details in JSON format
*/
static async getPodDetails(
podName: string,
namespace: string,
): Promise<string> {
const args = ["get", "pod", podName, "-n", namespace, "-o", "json"];
try {
const output = await LogUtils.executeCommand("oc", args);
console.log(`Details for pod ${podName}:`, output);
return output;
} catch (error) {
console.error(`Error fetching details for pod ${podName}:`, error);
throw new Error(`Failed to fetch pod details: ${error}`);
}
}

/**
* Fetches logs with retry logic in case the log is not immediately available.
*
* @param filter The string to filter the logs
* @returns A promise that resolves with the filtered logs
* @param maxRetries Maximum number of retry attempts
* @param retryDelay Delay (in milliseconds) between retries
* @returns The log line matching the filter, or throws an error if not found
*/
static async getPodLogsWithGrep(filter: string): Promise<string> {
static async getPodLogsWithRetry(
filter: string,
maxRetries: number = 3,
retryDelay: number = 5000,
): Promise<string> {
const podSelector =
"app.kubernetes.io/component=backstage,app.kubernetes.io/instance=rhdh,app.kubernetes.io/name=backstage";
const tailNumber = 30;
const namespace = process.env.NAME_SPACE || "default";
const namespace = process.env.NAME_SPACE || "showcase-ci-nightly";

const args = [
"logs",
"-l",
podSelector,
`--tail=${tailNumber}`,
"-c",
"backstage-backend",
"-n",
namespace,
];
let attempt = 0;
while (attempt <= maxRetries) {
try {
console.log(
`Attempt ${attempt + 1}/${maxRetries + 1}: Fetching logs...`,
);
const args = [
"logs",
"-l",
podSelector,
`--tail=${tailNumber}`,
"-c",
"backstage-backend",
"-n",
namespace,
];

console.log("Executing command:", "oc", args.join(" "));
console.log("Executing command:", "oc", args.join(" "));
const output = await LogUtils.executeCommand("oc", args);

try {
const output = await LogUtils.executeCommand("oc", args);
console.log("Raw log output:", output);

const logLines = output.split("\n");
const logLines = output.split("\n");
const filteredLines = logLines.filter((line) => line.includes(filter));

const filteredLines = logLines.filter((line) => line.includes(filter));
if (filteredLines.length > 0) {
console.log("Matching log line found:", filteredLines[0]);
return filteredLines[0]; // Return the first matching log
}

const firstMatch = filteredLines[0] || "";
console.warn(
`No matching logs found for filter "${filter}" on attempt ${
attempt + 1
}. Retrying...`,
);
} catch (error) {
console.error(
`Error fetching logs on attempt ${attempt + 1}:`,
error.message,
);
}

return firstMatch;
} catch (error) {
console.error("Error fetching logs:", error);
throw new Error(`Failed to fetch logs: ${error}`);
attempt++;
if (attempt <= maxRetries) {
console.log(`Waiting ${retryDelay / 1000} seconds before retrying...`);
await new Promise((resolve) => setTimeout(resolve, retryDelay));
}
}

throw new Error(
`Failed to fetch logs for filter "${filter}" after ${maxRetries + 1} attempts.`,
);
}

/**
Expand All @@ -115,8 +182,8 @@ export class LogUtils {
* @returns A promise that resolves when the login is successful
*/
static async loginToOpenShift(): Promise<void> {
const token = process.env.K8S_CLUSTER_TOKEN;
const server = process.env.K8S_CLUSTER_URL;
const token = process.env.K8S_CLUSTER_TOKEN || "";
const server = process.env.K8S_CLUSTER_URL || "";

if (!token || !server) {
throw new Error(
Expand Down Expand Up @@ -155,19 +222,41 @@ export class LogUtils {
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);
try {
const actualLog = await LogUtils.getPodLogsWithRetry(eventName);
console.log("Raw log output before filtering:", actualLog);

let parsedLog: Log;
try {
parsedLog = JSON.parse(actualLog);
} catch (parseError) {
console.error("Failed to parse log JSON. Log content:", actualLog);
throw new Error(`Invalid JSON received for log: ${parseError}`);
}

const expectedLog: Partial<Log> = {
actor: {
hostname: new URL(baseURL).hostname,
},
message,
plugin,
request: {
method,
url,
},
};

console.log("Validating log with expected values:", expectedLog);
LogUtils.validateLog(parsedLog, expectedLog);
} catch (error) {
console.error("Error validating log event:", error);
console.error("Event name:", eventName);
console.error("Expected message:", message);
console.error("Expected method:", method);
console.error("Expected URL:", url);
console.error("Base URL:", baseURL);
console.error("Plugin:", plugin);
throw error;
}
}
}

0 comments on commit 8fefdf4

Please sign in to comment.