Skip to content

Commit 1999bed

Browse files
authored
Add 'AnsibleDevFileAPI' E2E test (#23456)
* Add 'AnsibleDevFileAP' E2E test * Add methods to 'utils/KubernetesCommandLineToolsExecutor.ts' pageobject
1 parent 9a5c6e1 commit 1999bed

File tree

2 files changed

+402
-1
lines changed

2 files changed

+402
-1
lines changed
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
/** *******************************************************************
2+
* copyright (c) 2025 Red Hat, Inc.
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
**********************************************************************/
10+
11+
import { BASE_TEST_CONSTANTS } from '../../constants/BASE_TEST_CONSTANTS';
12+
import { e2eContainer } from '../../configs/inversify.config';
13+
import { CLASSES } from '../../configs/inversify.types';
14+
import { DevfilesHelper } from '../../utils/DevfilesHelper';
15+
import { ContainerTerminal, KubernetesCommandLineToolsExecutor } from '../../utils/KubernetesCommandLineToolsExecutor';
16+
import { DevWorkspaceConfigurationHelper } from '../../utils/DevWorkspaceConfigurationHelper';
17+
import { DevfileContext } from '@eclipse-che/che-devworkspace-generator/lib/api/devfile-context';
18+
import { ShellString } from 'shelljs';
19+
import { expect } from 'chai';
20+
import { API_TEST_CONSTANTS } from '../../constants/API_TEST_CONSTANTS';
21+
import YAML from 'yaml';
22+
import { Logger } from '../../utils/Logger';
23+
import crypto from 'crypto';
24+
25+
suite('Ansible devfile API test', function (): void {
26+
const devfilesRegistryHelper: DevfilesHelper = e2eContainer.get(CLASSES.DevfilesRegistryHelper);
27+
const kubernetesCommandLineToolsExecutor: KubernetesCommandLineToolsExecutor = e2eContainer.get(
28+
CLASSES.KubernetesCommandLineToolsExecutor
29+
);
30+
const devfileID: string = 'ansible';
31+
const serviceNameToPortForward: string = BASE_TEST_CONSTANTS.TESTING_APPLICATION_NAME() + '-dashboard';
32+
// namespace where the application is deployed to port-forward the service
33+
const applicationNamespace: string = 'openshift-' + BASE_TEST_CONSTANTS.TESTING_APPLICATION_NAME();
34+
const containerTerminal: ContainerTerminal = e2eContainer.get(CLASSES.ContainerTerminal);
35+
let devWorkspaceConfigurationHelper: DevWorkspaceConfigurationHelper;
36+
let devfileContext: DevfileContext;
37+
let devfileContent: string = '';
38+
let devfileName: string = '';
39+
40+
suiteSetup(`Prepare login ${BASE_TEST_CONSTANTS.TEST_ENVIRONMENT}`, function (): void {
41+
kubernetesCommandLineToolsExecutor.loginToOcp();
42+
});
43+
44+
test(`Create ${devfileID} workspace`, async function (): Promise<void> {
45+
const randomPref: string = crypto.randomBytes(4).toString('hex');
46+
kubernetesCommandLineToolsExecutor.namespace = API_TEST_CONSTANTS.TS_API_TEST_NAMESPACE;
47+
devfileContent = devfilesRegistryHelper.getDevfileContent(devfileID);
48+
const editorDevfileContent: string = devfilesRegistryHelper.obtainCheDevFileEditorFromCheConfigMap('editors-definitions');
49+
devfileName = YAML.parse(devfileContent).metadata.name;
50+
const uniqueName: string = YAML.parse(devfileContent).metadata.name + randomPref;
51+
kubernetesCommandLineToolsExecutor.workspaceName = uniqueName;
52+
53+
devWorkspaceConfigurationHelper = new DevWorkspaceConfigurationHelper({
54+
editorContent: editorDevfileContent,
55+
devfileContent: devfileContent
56+
});
57+
devfileContext = await devWorkspaceConfigurationHelper.generateDevfileContext();
58+
if (devfileContext.devWorkspace.metadata) {
59+
devfileContext.devWorkspace.metadata.name = uniqueName;
60+
}
61+
const devWorkspaceConfigurationYamlString: string =
62+
devWorkspaceConfigurationHelper.getDevWorkspaceConfigurationYamlAsString(devfileContext);
63+
const output: ShellString = kubernetesCommandLineToolsExecutor.applyAndWaitDevWorkspace(devWorkspaceConfigurationYamlString);
64+
expect(output.stdout).contains('condition met');
65+
66+
Logger.info('Inject kubeconfig to workspace');
67+
kubernetesCommandLineToolsExecutor.startTcpPortForward(serviceNameToPortForward, applicationNamespace);
68+
const devworkspaceId: string = kubernetesCommandLineToolsExecutor.getDevWorkspaceId();
69+
kubernetesCommandLineToolsExecutor.injectKubeConfig(devworkspaceId);
70+
});
71+
72+
test('Check "molecule-create" command', function (): void {
73+
const containerName: string = YAML.parse(devfileContent).commands[0].exec.component;
74+
const workdir: string = YAML.parse(devfileContent).commands[0].exec.workingDir;
75+
const commandLine: string = YAML.parse(devfileContent).commands[0].exec.commandLine;
76+
Logger.info(`workdir from exec section of DevWorkspace file: ${workdir}`);
77+
Logger.info(`commandLine from exec section of DevWorkspace file: ${commandLine}`);
78+
79+
const safeCommandLine: string = commandLine.replace(
80+
'/source\s+\$HOME\/\.bashrc/',
81+
'[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc" || true'
82+
);
83+
84+
let runCommandInBash: string;
85+
if (workdir) {
86+
runCommandInBash = `cd ${workdir} && ${safeCommandLine}`;
87+
} else {
88+
runCommandInBash = `${safeCommandLine}`;
89+
}
90+
91+
const output: ShellString = containerTerminal.execInContainerCommand(runCommandInBash, containerName);
92+
expect(output.code).eqls(0);
93+
94+
const recapBlocks: string[] = output.stdout.split(/PLAY RECAP/g).slice(1);
95+
recapBlocks.forEach((block): void => {
96+
expect(block).match(/failed\s*=\s*0/);
97+
});
98+
99+
const outputText: string = output.stdout.trim();
100+
expect(outputText).to.include('was installed successfully');
101+
expect(outputText).to.not.match(/failed\s*=\s*[1-9]\d*/);
102+
});
103+
104+
test('Check "molecule-list" command', function (): void {
105+
const containerName: string = YAML.parse(devfileContent).commands[1].exec.component;
106+
const workdir: string = YAML.parse(devfileContent).commands[1].exec.workingDir;
107+
const commandLine: string = YAML.parse(devfileContent).commands[1].exec.commandLine;
108+
Logger.info(`workdir from exec section of DevWorkspace file: ${workdir}`);
109+
Logger.info(`commandLine from exec section of DevWorkspace file: ${commandLine}`);
110+
111+
const safeCommandLine: string = commandLine.replace(
112+
'/source\s+\$HOME\/\.bashrc/',
113+
'[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc" || true'
114+
);
115+
116+
let runCommandInBash: string;
117+
if (workdir) {
118+
runCommandInBash = `cd ${workdir} && ${safeCommandLine}`;
119+
} else {
120+
runCommandInBash = `${safeCommandLine}`;
121+
}
122+
123+
const output: ShellString = containerTerminal.execInContainerCommand(runCommandInBash, containerName);
124+
expect(output.code).eqls(0);
125+
126+
const outputText: string = output.stdout.trim();
127+
expect(outputText).to.include('molecule');
128+
expect(outputText).to.include('ansible');
129+
expect(outputText).to.include('default');
130+
expect(outputText).to.include('true');
131+
});
132+
133+
test('Check "molecule-converge" command', function (): void {
134+
const containerName: string = YAML.parse(devfileContent).commands[2].exec.component;
135+
const workdir: string = YAML.parse(devfileContent).commands[2].exec.workingDir;
136+
const commandLine: string = YAML.parse(devfileContent).commands[2].exec.commandLine;
137+
Logger.info(`workdir from exec section of DevWorkspace file: ${workdir}`);
138+
Logger.info(`commandLine from exec section of DevWorkspace file: ${commandLine}`);
139+
140+
const safeCommandLine: string = commandLine.replace(
141+
'/source\s+\$HOME\/\.bashrc/',
142+
'[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc" || true'
143+
);
144+
145+
let runCommandInBash: string;
146+
if (workdir) {
147+
runCommandInBash = `cd ${workdir} && ${safeCommandLine}`;
148+
} else {
149+
runCommandInBash = `${safeCommandLine}`;
150+
}
151+
152+
const output: ShellString = containerTerminal.execInContainerCommand(runCommandInBash, containerName);
153+
expect(output.code).eqls(0);
154+
155+
const recapBlocks: string[] = output.stdout.split(/PLAY RECAP/g).slice(1);
156+
recapBlocks.forEach((block): void => {
157+
expect(block).match(/failed\s*=\s*0/);
158+
});
159+
160+
const outputText: string = output.stdout.trim();
161+
expect(outputText).to.include('PLAY [Converge]');
162+
expect(outputText).to.not.match(/failed\s*=\s*[1-9]\d*/);
163+
});
164+
165+
test('Check "molecule-verify" command', function (): void {
166+
const containerName: string = YAML.parse(devfileContent).commands[3].exec.component;
167+
const workdir: string = YAML.parse(devfileContent).commands[3].exec.workingDir;
168+
const commandLine: string = YAML.parse(devfileContent).commands[3].exec.commandLine;
169+
Logger.info(`workdir from exec section of DevWorkspace file: ${workdir}`);
170+
Logger.info(`commandLine from exec section of DevWorkspace file: ${commandLine}`);
171+
172+
const safeCommandLine: string = commandLine.replace(
173+
'/source\s+\$HOME\/\.bashrc/',
174+
'[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc" || true'
175+
);
176+
177+
let runCommandInBash: string;
178+
if (workdir) {
179+
runCommandInBash = `cd ${workdir} && ${safeCommandLine}`;
180+
} else {
181+
runCommandInBash = `${safeCommandLine}`;
182+
}
183+
184+
const output: ShellString = containerTerminal.execInContainerCommand(runCommandInBash, containerName);
185+
expect(output.code).eqls(0);
186+
187+
const recapBlocks: string[] = output.stdout.split(/PLAY RECAP/g).slice(1);
188+
recapBlocks.forEach((block): void => {
189+
expect(block).match(/failed\s*=\s*0/);
190+
});
191+
192+
const outputText: string = output.stdout.trim();
193+
expect(outputText).to.include('PLAY [Verify]');
194+
expect(outputText).to.not.match(/failed\s*=\s*[1-9]\d*/);
195+
});
196+
197+
test('Check "molecule-destroy" command', function (): void {
198+
const containerName: string = YAML.parse(devfileContent).commands[4].exec.component;
199+
const workdir: string = YAML.parse(devfileContent).commands[4].exec.workingDir;
200+
const commandLine: string = YAML.parse(devfileContent).commands[4].exec.commandLine;
201+
Logger.info(`workdir from exec section of DevWorkspace file: ${workdir}`);
202+
Logger.info(`commandLine from exec section of DevWorkspace file: ${commandLine}`);
203+
204+
const safeCommandLine: string = commandLine.replace(
205+
'/source\s+\$HOME\/\.bashrc/',
206+
'[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc" || true'
207+
);
208+
209+
let runCommandInBash: string;
210+
if (workdir) {
211+
runCommandInBash = `cd ${workdir} && ${safeCommandLine}`;
212+
} else {
213+
runCommandInBash = `${safeCommandLine}`;
214+
}
215+
216+
const output: ShellString = containerTerminal.execInContainerCommand(runCommandInBash, containerName);
217+
expect(output.code).eqls(0);
218+
219+
const recapBlocks: string[] = output.stdout.split(/PLAY RECAP/g).slice(1);
220+
recapBlocks.forEach((block): void => {
221+
expect(block).match(/failed\s*=\s*0/);
222+
});
223+
224+
const outputText: string = output.stdout.trim();
225+
expect(outputText).to.include('PLAY [Destroy]');
226+
expect(outputText).to.not.match(/failed\s*=\s*[1-9]\d*/);
227+
});
228+
229+
test('Check "molecule-test" command', function (): void {
230+
const containerName: string = YAML.parse(devfileContent).commands[5].exec.component;
231+
const workdir: string = YAML.parse(devfileContent).commands[5].exec.workingDir;
232+
const commandLine: string = YAML.parse(devfileContent).commands[5].exec.commandLine;
233+
Logger.info(`workdir from exec section of DevWorkspace file: ${workdir}`);
234+
Logger.info(`commandLine from exec section of DevWorkspace file: ${commandLine}`);
235+
236+
const safeCommandLine: string = commandLine.replace(
237+
'/source\s+\$HOME\/\.bashrc/',
238+
'[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc" || true'
239+
);
240+
241+
let runCommandInBash: string;
242+
if (workdir) {
243+
runCommandInBash = `cd ${workdir} && ${safeCommandLine}`;
244+
} else {
245+
runCommandInBash = `${safeCommandLine}`;
246+
}
247+
248+
const output: ShellString = containerTerminal.execInContainerCommand(runCommandInBash, containerName);
249+
expect(output.code).eqls(0);
250+
251+
const recapBlocks: string[] = output.stdout.split(/PLAY RECAP/g).slice(1);
252+
recapBlocks.forEach((block): void => {
253+
expect(block).match(/failed\s*=\s*0/);
254+
});
255+
256+
const outputText: string = output.stdout.trim();
257+
expect(outputText).to.include('PLAY [Create]');
258+
expect(outputText).to.include('PLAY [Converge]');
259+
expect(outputText).to.include('PLAY [Verify]');
260+
expect(outputText).to.not.match(/failed\s*=\s*[1-9]\d*/);
261+
});
262+
263+
test('Check "ansible-navigator" command', function (): void {
264+
const containerName: string = YAML.parse(devfileContent).commands[6].exec.component;
265+
const workdir: string = YAML.parse(devfileContent).commands[6].exec.workingDir;
266+
const rawCommandLines: string = YAML.parse(devfileContent).commands[6].exec.commandLine;
267+
Logger.info(`workdir from exec section of DevWorkspace file: ${workdir}`);
268+
Logger.info(`commandLine from exec section of DevWorkspace file: \n${rawCommandLines}`);
269+
270+
const modifiedLines: string[] = rawCommandLines
271+
.trim()
272+
.split('\n')
273+
.map((line): string => {
274+
const trimmed: string = line.trim();
275+
if (trimmed.startsWith('if [ ! -d') || trimmed === 'fi') {
276+
return ''; // remove conditional checks
277+
}
278+
if (trimmed.startsWith('ansible-navigator')) {
279+
// '--help' with '--mode stdout' disables interactive mode and ensures stable, testable output from ansible-navigator
280+
const safeCommandLine: string = trimmed + ' --help --mode stdout';
281+
return safeCommandLine;
282+
}
283+
284+
return trimmed;
285+
})
286+
.filter(Boolean);
287+
288+
const modifiedScript: string = modifiedLines.join(' && ');
289+
const patchedCommandLine: string = `bash -c \"${modifiedScript}\"`;
290+
291+
let runCommandInBash: string;
292+
if (workdir) {
293+
runCommandInBash = `cd ${workdir} && ${patchedCommandLine}`;
294+
} else {
295+
runCommandInBash = `${patchedCommandLine}`;
296+
}
297+
298+
const output: ShellString = containerTerminal.execInContainerCommand(runCommandInBash, containerName);
299+
expect(output.code).eqls(0);
300+
301+
const outputText: string = output.stdout.trim();
302+
expect(outputText).to.include('ansible-navigator');
303+
expect(outputText).to.include('collections');
304+
expect(outputText).to.include('config');
305+
expect(outputText).to.include('settings');
306+
expect(outputText).to.include('welcome');
307+
});
308+
309+
test('Check removing molecule pod afer deleting workspace', function (): void {
310+
kubernetesCommandLineToolsExecutor.deleteDevWorkspace(devfileName);
311+
kubernetesCommandLineToolsExecutor.waitRemovingPod('molecule');
312+
});
313+
314+
suiteTeardown('Delete DevWorkspace', function (): void {
315+
kubernetesCommandLineToolsExecutor.stopTcpPortForward();
316+
kubernetesCommandLineToolsExecutor.deleteDevWorkspace(devfileName);
317+
});
318+
});

0 commit comments

Comments
 (0)