Skip to content

Commit 0b08864

Browse files
authored
Merge pull request #1609 from RedisInsight/e2e/feature/RI-3266_ssh-tunnel
E2e/feature/ri 3266 ssh tunnel
2 parents 2020b21 + 8fa3722 commit 0b08864

File tree

12 files changed

+293
-29
lines changed

12 files changed

+293
-29
lines changed

tests/e2e/common-actions/browser-actions.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,15 @@ export class BrowserActions {
2929
}
3030
}
3131
}
32+
33+
/**
34+
* Verify toolip contains text
35+
* @param expectedText Expected link that is compared with actual
36+
* @param contains Should this tooltip contains or not contains text
37+
*/
38+
async verifyTooltipContainsText(expectedText: string, contains: boolean): Promise<void> {
39+
contains
40+
? await t.expect(browserPage.tooltip.textContent).contains(expectedText, `"${expectedText}" Text is incorrect in tooltip`)
41+
: await t.expect(browserPage.tooltip.textContent).notContains(expectedText, `Tooltip still contains text "${expectedText}"`);
42+
}
3243
}

tests/e2e/helpers/common.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { ClientFunction, RequestMock, t } from 'testcafe';
22
import { Chance } from 'chance';
3-
import {apiUrl, commonUrl} from './conf';
3+
import { apiUrl, commonUrl } from './conf';
44

5-
const settingsApiUrl = `${commonUrl}/api/settings`;
65
const chance = new Chance();
7-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // lgtm[js/disabling-certificate-validation]
86

7+
const settingsApiUrl = `${commonUrl}/api/settings`;
8+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // lgtm[js/disabling-certificate-validation]
99
const mockedSettingsResponse = {
1010
agreements: {
1111
version: '0',
1212
eula: false,
1313
analytics: false
14-
}};
14+
}
15+
};
1516

1617
export class Common {
1718
mock = RequestMock()
@@ -27,7 +28,7 @@ export class Common {
2728
* @param length The amount of array elements
2829
*/
2930
createArrayWithKeys(length: number): string[] {
30-
return Array.from({length}, (_, i) => `key${i}`);
31+
return Array.from({ length }, (_, i) => `key${i}`);
3132
}
3233

3334
/**
@@ -36,7 +37,7 @@ export class Common {
3637
*/
3738
async createArrayWithKeyValue(length: number): Promise<string[]> {
3839
const arr: string[] = [];
39-
for(let i = 1; i <= length * 2; i++) {
40+
for (let i = 1; i <= length * 2; i++) {
4041
arr[i] = `${chance.word({ length: 10 })}-key${i}`;
4142
arr[i + 1] = `${chance.word({ length: 10 })}-value${i}`;
4243
i++;
@@ -50,7 +51,7 @@ export class Common {
5051
*/
5152
async createArrayWithKeyValueAndDelimiter(length: number): Promise<string[]> {
5253
const keyNameArray: string[] = [];
53-
for(let i = 1; i <= length; i++) {
54+
for (let i = 1; i <= length; i++) {
5455
const key = `"key${i}:test${i}"`;
5556
const value = `"value${this.generateSentence(i * 2)}"`;
5657
keyNameArray.push(key, value);
@@ -64,7 +65,7 @@ export class Common {
6465
*/
6566
async createArrayWithKeyAndDelimiter(length: number): Promise<string[]> {
6667
const keyNameArray: string[] = [];
67-
for(let i = 1; i <= length; i++) {
68+
for (let i = 1; i <= length; i++) {
6869
const key = `"key${i}:test${i}"`;
6970
keyNameArray.push(key);
7071
}
@@ -77,7 +78,7 @@ export class Common {
7778
*/
7879
async createArrayWithKeyValueForOSSCluster(length: number): Promise<string[]> {
7980
const arr: string[] = [];
80-
for(let i = 1; i <= length * 2; i++) {
81+
for (let i = 1; i <= length * 2; i++) {
8182
arr[i] = `{user1}:${chance.word({ length: 10 })}-key${i}`;
8283
arr[i + 1] = `${chance.word({ length: 10 })}-value${i}`;
8384
i++;
@@ -92,7 +93,7 @@ export class Common {
9293
*/
9394
async createArrayWithKeyValueAndKeyname(length: number, keyName: string): Promise<string[]> {
9495
const keyNameArray: string[] = [];
95-
for(let i = 1; i <= length; i++) {
96+
for (let i = 1; i <= length; i++) {
9697
const key = `${keyName}${i}`;
9798
const value = `value${i}`;
9899
keyNameArray.push(key, value);
@@ -114,7 +115,7 @@ export class Common {
114115
*/
115116
async createArray(length: number): Promise<string[]> {
116117
const arr: string[] = [];
117-
for(let i = 1; i <= length; i++) {
118+
for (let i = 1; i <= length; i++) {
118119
arr[i] = `${i}`;
119120
}
120121
return arr;
@@ -166,4 +167,13 @@ export class Common {
166167
const getPageUrl = ClientFunction(() => window.location.href);
167168
await t.expect(getPageUrl()).eql(expectedUrl, 'Opened URL is not correct');
168169
}
170+
171+
/**
172+
* Check opened URL contains text
173+
* @param expectedText Expected link that is compared with actual
174+
*/
175+
async checkURLContainsText(expectedText: string): Promise<void> {
176+
const getPageUrl = ClientFunction(() => window.location.href);
177+
await t.expect(getPageUrl()).contains(expectedText, `Opened URL not contains text ${expectedText}`);
178+
}
169179
}

tests/e2e/helpers/conf.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,11 @@ export const ossStandaloneNoPermissionsConfig = {
9595
databaseUsername: process.env.OSS_STANDALONE_USERNAME || 'noperm',
9696
databasePassword: process.env.OSS_STANDALONE_PASSWORD
9797
};
98+
99+
export const ossStandaloneForSSH = {
100+
host: process.env.OSS_STANDALONE_HOST || '172.33.100.10',
101+
port: process.env.OSS_STANDALONE_PORT || '6379',
102+
databaseName: `${process.env.OSS_STANDALONE_DATABASE_NAME || 'oss-standalone-for-ssh'}-${uniqueId}`,
103+
databaseUsername: process.env.OSS_STANDALONE_USERNAME,
104+
databasePassword: process.env.OSS_STANDALONE_PASSWORD
105+
};

tests/e2e/pageObjects/add-redis-database-page.ts

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export class AddRedisDatabasePage {
77
//*Target any element/component via data-id, if possible!
88
//*The following categories are ordered alphabetically (Alerts, Buttons, Checkboxes, etc.).
99
//-------------------------------------------------------------------------------------------
10-
//BUTTONS
10+
// BUTTONS
1111
addDatabaseButton = Selector('[data-testid=add-redis-database]');
1212
addRedisDatabaseButton = Selector('[data-testid=btn-submit]');
1313
addDatabaseManually = Selector('[data-testid=add-manual]');
@@ -29,7 +29,7 @@ export class AddRedisDatabasePage {
2929
cloneSentinelDatabaseNavigation = Selector('[data-testid=database-nav-group-clone]');
3030
cancelButton = Selector('[data-testid=btn-cancel]');
3131
showPasswordBtn = Selector('[aria-label^="Show password"]');
32-
//TEXT INPUTS (also referred to as 'Text fields')
32+
// TEXT INPUTS (also referred to as 'Text fields')
3333
hostInput = Selector('[data-testid=host]');
3434
portInput = Selector('[data-testid=port]');
3535
databaseAliasInput = Selector('[data-testid=name]');
@@ -45,13 +45,24 @@ export class AddRedisDatabasePage {
4545
masterGroupPassword = Selector('[data-testid=sentinel-master-password]');
4646
connectionType = Selector('[data-testid=connection-type]');
4747
sentinelForm = Selector('[data-testid=form]');
48-
//Links
48+
sshHostInput = Selector('[data-testid=sshHost]');
49+
sshPortInput = Selector('[data-testid=sshPort]');
50+
sshUsernameInput = Selector('[data-testid=sshUsername]');
51+
sshPasswordInput = Selector('[data-testid=sshPassword]');
52+
sshPrivateKeyInput = Selector('[data-testid=sshPrivateKey]');
53+
sshPassphraseInput = Selector('[data-testid=sshPassphrase]');
54+
// Links
4955
buildFromSource = Selector('a').withExactText('Build from source');
5056
buildFromDocker = Selector('a').withExactText('Docker');
5157
buildFromHomebrew = Selector('a').withExactText('Homebrew');
5258
// DROPDOWNS
5359
caCertField = Selector('[data-testid=select-ca-cert]', {timeout: 500});
5460
clientCertField = Selector('[data-testid=select-cert]', {timeout: 500});
61+
// CHECKBOXES
62+
useSSHCheckbox = Selector('[data-testid=use-ssh]~div', {timeout: 500});
63+
// RADIO BUTTONS
64+
sshPasswordRadioBtn = Selector('#password~div', {timeout: 500});
65+
sshPrivateKeyRadioBtn = Selector('#privateKey~div', {timeout: 500});
5566

5667
/**
5768
* Adding a new redis database
@@ -99,6 +110,50 @@ export class AddRedisDatabasePage {
99110
// Click for saving
100111
await t.click(this.addRedisDatabaseButton);
101112
}
113+
114+
/**
115+
* Adding a new standalone database with SSH
116+
* @param databaseParameters the parameters of the database
117+
* @param sshParameters the parameters of ssh
118+
*/
119+
async addStandaloneSSHDatabase(databaseParameters: AddNewDatabaseParameters, sshParameters: SSHParameters): Promise<void> {
120+
await t
121+
.click(this.addDatabaseButton)
122+
.click(this.addDatabaseManually);
123+
await t
124+
.typeText(this.hostInput, databaseParameters.host, { replace: true, paste: true })
125+
.typeText(this.portInput, databaseParameters.port, { replace: true, paste: true })
126+
.typeText(this.databaseAliasInput, databaseParameters.databaseName!, { replace: true, paste: true });
127+
if (!!databaseParameters.databaseUsername) {
128+
await t.typeText(this.usernameInput, databaseParameters.databaseUsername, { replace: true, paste: true });
129+
}
130+
if (!!databaseParameters.databasePassword) {
131+
await t.typeText(this.passwordInput, databaseParameters.databasePassword, { replace: true, paste: true });
132+
}
133+
// Select SSH Tunnel checkbox
134+
await t.click(this.useSSHCheckbox);
135+
// Enter SSH fields
136+
await t
137+
.typeText(this.sshHostInput, sshParameters.sshHost, { replace: true, paste: true })
138+
.typeText(this.sshPortInput, sshParameters.sshPort, { replace: true, paste: true })
139+
.typeText(this.sshUsernameInput, sshParameters.sshUsername, { replace: true, paste: true });
140+
if (!!sshParameters.sshPassword) {
141+
await t.typeText(this.sshPasswordInput, sshParameters.sshPassword, { replace: true, paste: true });
142+
}
143+
if (!!sshParameters.sshPrivateKey) {
144+
await t
145+
.click(this.sshPrivateKeyRadioBtn)
146+
.typeText(this.sshPrivateKeyInput, sshParameters.sshPrivateKey, { replace: true, paste: true });
147+
}
148+
if (!!sshParameters.sshPassphrase) {
149+
await t
150+
.click(this.sshPrivateKeyRadioBtn)
151+
.typeText(this.sshPrivateKeyInput, sshParameters.sshPrivateKey!, { replace: true, paste: true })
152+
.typeText(this.sshPassphraseInput, sshParameters.sshPassphrase, { replace: true, paste: true });
153+
}
154+
// Click for saving
155+
await t.click(this.addRedisDatabaseButton);
156+
}
102157

103158
/**
104159
* Auto-discover Master Groups from Sentinel
@@ -240,3 +295,21 @@ export type ClusterNodes = {
240295
host: string,
241296
port: string
242297
};
298+
299+
/**
300+
* SSH parameters
301+
* @param sshHost The hostname of ssh
302+
* @param sshPort The port of ssh
303+
* @param sshUsername The username of ssh
304+
* @param sshPassword The password of ssh
305+
* @param sshPrivateKey The private key of ssh
306+
* @param sshPassphrase The passphrase of ssh
307+
*/
308+
export type SSHParameters = {
309+
sshHost: string,
310+
sshPort: string,
311+
sshUsername: string,
312+
sshPassword?: string,
313+
sshPrivateKey?: string,
314+
sshPassphrase?: string
315+
};

tests/e2e/rte.docker-compose.yml

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
version: "3.4"
22

33
services:
4+
# ssh
5+
ssh:
6+
image: lscr.io/linuxserver/openssh-server:latest
7+
environment:
8+
- PASSWORD_ACCESS=true
9+
- USER_PASSWORD=pass
10+
- USER_NAME=u
11+
- DOCKER_MODS=linuxserver/mods:openssh-server-ssh-tunnel
12+
- PUBLIC_KEY_DIR=/keys/pub
13+
volumes:
14+
- ./rte/ssh/keys:/keys
15+
ports:
16+
- 2222:2222
17+
networks:
18+
default:
19+
ipv4_address: 172.31.100.245
20+
ssh:
21+
ipv4_address: 172.33.100.245
22+
423
# oss standalone
524
oss-standalone:
625
image: redislabs/redismod
@@ -19,7 +38,11 @@ services:
1938
image: redis:5
2039
ports:
2140
- 8101:6379
22-
41+
networks:
42+
default:
43+
ipv4_address: 172.31.100.10
44+
ssh:
45+
ipv4_address: 172.33.100.10
2346
# oss standalone redisearch
2447
oss-standalone-redisearch:
2548
image: redislabs/redismod
@@ -93,3 +116,10 @@ networks:
93116
config:
94117
- subnet: 172.31.100.0/24
95118
gateway: 172.31.100.1
119+
ssh:
120+
name: "e2e-ssh-network"
121+
ipam:
122+
driver: default
123+
config:
124+
- subnet: 172.33.100.0/24
125+
gateway: 172.33.100.1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPXS0xkxY7o+MUNBJJnf6fKh6AFFpzB0YIfifHSSseXw
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEs/ewkUXl0+uDr7hxSM2vURqdRNFHm7+x05azzW/Yzu

tests/e2e/rte/ssh/keys/test

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-----BEGIN OPENSSH PRIVATE KEY-----
2+
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
3+
QyNTUxOQAAACD10tMZMWO6PjFDQSSZ3+nyoegBRacwdGCH4nx0krHl8AAAAKBv1saEb9bG
4+
hAAAAAtzc2gtZWQyNTUxOQAAACD10tMZMWO6PjFDQSSZ3+nyoegBRacwdGCH4nx0krHl8A
5+
AAAEDyew1DnmWamAr0OrUM87FauJfFfea+pi8ctpKNnurNi/XS0xkxY7o+MUNBJJnf6fKh
6+
6AFFpzB0YIfifHSSseXwAAAAG3pvem9Aem96by1IUC1Qcm9Cb29rLTQ1MC1HNwEC
7+
-----END OPENSSH PRIVATE KEY-----

tests/e2e/rte/ssh/keys/testp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-----BEGIN OPENSSH PRIVATE KEY-----
2+
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBPcEHCGN
3+
DrMHhpQnPwc0XwAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIEs/ewkUXl0+uDr7
4+
hxSM2vURqdRNFHm7+x05azzW/YzuAAAAoEhNzctHXM6YBV0z4zzvdniQ5cLwsv8TfMZp2G
5+
WUhZU05yugvKlRu1pml5q3XGSP5wYCF4vvi4BE563PMDKZWAqFFGtiTotEn+XuD/eP+P8H
6+
xdf91tV5kE+1yvVwxUNMcijHY0uYopnG2NN3bdjOH/4YmW0WLyDu10EoMZKVnrP0qBbOrR
7+
xKIy5lqa39SrAnUnGSoTEJsEWGLiIS2rBhkVc=
8+
-----END OPENSSH PRIVATE KEY-----
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const sshPrivateKey = `
2+
-----BEGIN OPENSSH PRIVATE KEY-----
3+
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
4+
QyNTUxOQAAACD10tMZMWO6PjFDQSSZ3+nyoegBRacwdGCH4nx0krHl8AAAAKBv1saEb9bG
5+
hAAAAAtzc2gtZWQyNTUxOQAAACD10tMZMWO6PjFDQSSZ3+nyoegBRacwdGCH4nx0krHl8A
6+
AAAEDyew1DnmWamAr0OrUM87FauJfFfea+pi8ctpKNnurNi/XS0xkxY7o+MUNBJJnf6fKh
7+
6AFFpzB0YIfifHSSseXwAAAAG3pvem9Aem96by1IUC1Qcm9Cb29rLTQ1MC1HNwEC
8+
-----END OPENSSH PRIVATE KEY-----`;
9+
10+
export const sshPrivateKeyWithPasscode = `
11+
-----BEGIN OPENSSH PRIVATE KEY-----
12+
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBPcEHCGN
13+
DrMHhpQnPwc0XwAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIEs/ewkUXl0+uDr7
14+
hxSM2vURqdRNFHm7+x05azzW/YzuAAAAoEhNzctHXM6YBV0z4zzvdniQ5cLwsv8TfMZp2G
15+
WUhZU05yugvKlRu1pml5q3XGSP5wYCF4vvi4BE563PMDKZWAqFFGtiTotEn+XuD/eP+P8H
16+
xdf91tV5kE+1yvVwxUNMcijHY0uYopnG2NN3bdjOH/4YmW0WLyDu10EoMZKVnrP0qBbOrR
17+
xKIy5lqa39SrAnUnGSoTEJsEWGLiIS2rBhkVc=
18+
-----END OPENSSH PRIVATE KEY-----`;

0 commit comments

Comments
 (0)