Skip to content
This repository was archived by the owner on Jul 14, 2025. It is now read-only.

Commit e4a1486

Browse files
committed
adding initial client support
1 parent c32981b commit e4a1486

File tree

8 files changed

+680
-102
lines changed

8 files changed

+680
-102
lines changed

.github/workflows/stageTestAction.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ jobs:
5151
echo "PROXY=$PROXY" >> .env
5252
echo "TOKEN=apple" >> .env
5353
echo "CI=true" >> .env
54-
54+
echo "ORG_ID_1=$STAGE_ORG_ID" >> .env
55+
echo "ACTIVATION_KEY_1=$STAGE_ACTIVATION_KEY" >> .env
5556
- name: Set up Node.js
5657
uses: actions/setup-node@v2
5758
with:

example.env

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33

44
USER1USERNAME="contentPlaywrightUserAdmin" # Required
55
USER1PASSWORD="" # Required (Ask Andrew if needed)
6+
ORG_ID_1="1234" #org id to register for registration tests
7+
ACTIVATION_KEY_1="MyKey" #activation Key used for testing
8+
9+
PROD=""
610
BASE_URL="https://stage.foo.redhat.com:1337" # Required
711
PROXY="https://something.foo.redhat.com:5432" # Required in CI only or if locally running against stage
812
CI="" # This is set to true for CI jobs, if checking for CI do !!process.env.CI
913
TOKEN="" # This is handled programmatically.
14+
15+
#DOCKER_SOCKET="/tmp/podman.sock"

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
"@types/node": "^22.9.0"
1717
},
1818
"dependencies": {
19+
"@types/dockerode": "^3.3.34",
20+
"dockerode": "^4.0.4",
1921
"dotenv": "^16.4.7",
2022
"github-actions-ctrf": "^0.0.58",
2123
"playwright-ctrf-json-reporter": "^0.0.18"

readme.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,26 @@ This stores your local credentials
1414

1515
yarn get-tests
1616

17+
# Setup container api for testing with clients
18+
19+
## Podman
20+
21+
As your user, run podman to serve the api:
22+
```
23+
podman system service -t 0 /tmp/podman.sock
24+
```
25+
26+
Uncomment the DOCKER_SOCKET option in the .env file:
27+
```
28+
DOCKER_SOCKET="/tmp/podman.sock"
29+
```
30+
31+
## Docker
32+
33+
* ensure the docker service is running
34+
* ensure your user is part of the 'docker' user group
35+
36+
1737
# Option 1 Run local:
1838

1939
For local testing, make sure your front-end/backend servers are running and accessible, then:

tests/helpers/containers.tsx

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import Dockerode, { Container } from "dockerode";
2+
import { PassThrough } from "stream";
3+
import { finished } from "stream/promises";
4+
5+
var Docker = require("dockerode");
6+
7+
const util = require("util");
8+
const exec = util.promisify(require("child_process").exec);
9+
10+
const dockerHost = () => {
11+
return process.env.DOCKER_SOCKET!;
12+
};
13+
14+
const docker = (): Dockerode => {
15+
return new Docker({ socketPath: dockerHost() });
16+
};
17+
18+
export const startContainer = async (
19+
containerName: string,
20+
imageName: string
21+
) => {
22+
console.log("starting container " + containerName);
23+
const container = await docker().createContainer({
24+
Image: imageName,
25+
name: containerName,
26+
});
27+
return container?.start();
28+
};
29+
30+
/*
31+
* starts a container and kills one if it already exists with the same name
32+
*/
33+
export const startNewContainer = async (
34+
containerName: string,
35+
imageName: string
36+
) => {
37+
await killContainer(containerName);
38+
return await startContainer(containerName, imageName);
39+
//await waitForContainer(containerName)
40+
};
41+
42+
async function sleep(ms: number): Promise<void> {
43+
return new Promise((resolve) => setTimeout(resolve, ms));
44+
}
45+
46+
const waitForContainer = async (name: string): Promise<Container | void> => {
47+
var container = await getContainer(name);
48+
var waited = 10;
49+
while (container == undefined && waited > 0) {
50+
waited -= 1;
51+
await sleep(1000);
52+
container = await getContainer(name);
53+
}
54+
return container;
55+
};
56+
57+
const getContainerInfo = async (name: string) => {
58+
const containers = await docker().listContainers({ all: true });
59+
60+
for (var contInfo of containers) {
61+
if (contInfo.Names.includes("/" + name)) {
62+
return contInfo;
63+
}
64+
}
65+
return undefined;
66+
};
67+
68+
const getContainer = async (name: string): Promise<Container | void> => {
69+
const cInfo = await getContainerInfo(name);
70+
if (cInfo !== undefined) {
71+
return docker().getContainer(cInfo.Id);
72+
}
73+
};
74+
75+
export const killContainer = async (containerName: string) => {
76+
const info = await getContainerInfo(containerName);
77+
const c = await getContainer(containerName);
78+
if (info?.State == "running") {
79+
await c?.kill();
80+
}
81+
return c?.remove();
82+
};
83+
84+
interface ExecReturn {
85+
stdout?: string;
86+
stderr?: string;
87+
exitCode?: number | null;
88+
}
89+
90+
// Runs a non-interactive command and returns stdout, stderr, and the exit code
91+
export const runCommand = async (
92+
containerName: string,
93+
command: string[]
94+
): Promise<ExecReturn | void> => {
95+
console.log("Running " + command + " on " + containerName);
96+
const c = await getContainer(containerName);
97+
const exec = await c?.exec({
98+
Cmd: command,
99+
AttachStdout: true,
100+
AttachStderr: true,
101+
});
102+
if (exec == undefined) {
103+
return undefined;
104+
}
105+
106+
const execStream = await exec?.start({});
107+
if (execStream == undefined) {
108+
return undefined;
109+
}
110+
111+
const stdoutStream = new PassThrough();
112+
const stderrStream = new PassThrough();
113+
114+
docker().modem.demuxStream(execStream, stdoutStream, stderrStream);
115+
116+
execStream.resume();
117+
await finished(execStream);
118+
119+
const stderr = stderrStream.read() as Buffer | undefined;
120+
const stdout = stdoutStream.read() as Buffer | undefined;
121+
const execInfo = await exec.inspect();
122+
123+
return {
124+
exitCode: execInfo.ExitCode,
125+
stderr: stderr?.toString(),
126+
stdout: stdout?.toString(),
127+
};
128+
};

tests/helpers/loginHelpers.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ export const throwIfMissingEnvVariables = () => {
8484
"USER1USERNAME",
8585
"USER1PASSWORD",
8686
"BASE_URL",
87-
"PROXY",
87+
"ORG_ID_1",
88+
"ACTIVATION_KEY_1",
8889
];
8990

9091
if (!process.env.PROD) ManditoryEnvVariables.push("PROXY");

tests/helpers/rhsmClient.tsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { URL } from "url";
2+
import { killContainer, runCommand, startNewContainer } from "./containers";
3+
4+
export type OSVersion = "rhel9" | "rhel8";
5+
6+
const RemoteImages = {
7+
rhel9: "quay.io/jlsherri/client-rhel9",
8+
rhel8: "localhost/client-rhel9",
9+
};
10+
11+
export class RHSMClient {
12+
name: string;
13+
constructor(name: string) {
14+
this.name = name;
15+
}
16+
17+
async Boot(version: OSVersion) {
18+
return startNewContainer(this.name, RemoteImages[version]);
19+
}
20+
21+
async ConfigureForStage() {
22+
return runCommand(this.name, stageConfigureCommand());
23+
}
24+
25+
async Register(activationKey?: string, orgId?: string) {
26+
if (!process.env.PROD) {
27+
await this.ConfigureForStage();
28+
}
29+
30+
if (activationKey == undefined) {
31+
activationKey = process.env.ACTIVATION_KEY_1 || "COULD_NOT_FIND_KEY";
32+
}
33+
if (activationKey == undefined) {
34+
orgId = process.env.ORG_ID_1 || "COULD_NOT_FIND_ORG_ID";
35+
}
36+
37+
return runCommand(this.name, [
38+
"subscription-manager",
39+
"register",
40+
"--activationkey",
41+
activationKey,
42+
"--org=" + orgId,
43+
"--name",
44+
this.name,
45+
]);
46+
}
47+
48+
async Unregister() {
49+
return runCommand(this.name, ["subscription-manager", "unregister"]);
50+
}
51+
52+
async Destroy() {
53+
const cmd = await this.Unregister();
54+
console.log(cmd?.stdout);
55+
console.log(cmd?.stderr);
56+
return killContainer(this.name);
57+
}
58+
}
59+
60+
const stageConfigureCommand = (): string[] => {
61+
var command = [
62+
"subscription-manager",
63+
"config",
64+
"--server.hostname=subscription.rhsm.stage.redhat.com",
65+
"--server.port=443",
66+
"--server.prefix=/subscription",
67+
"--server.insecure=0",
68+
];
69+
if (process.env.PROXY !== undefined) {
70+
const url = new URL(process.env.PROXY);
71+
command.push("--server.proxy_hostname=" + url.hostname);
72+
command.push("--server.proxy_port=" + url.port);
73+
}
74+
return command;
75+
};

0 commit comments

Comments
 (0)