Skip to content
This repository has been archived by the owner on Dec 6, 2024. It is now read-only.

test: rstudio alb integration tests #813

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ class ProvisionAccount extends StepBase {
/* response example:
data = {
CreateAccountStatus: {
AccountId: "333333333333",
AccountId: "333333333333",
Id: "car-exampleaccountcreationrequestid",
State: "SUCCEEDED"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

const { sleep } = require('@aws-ee/base-services/lib/helpers/utils');
const axios = require('axios').default;
const { runSetup } = require('../../../support/setup');
const { deleteWorkspaceServiceCatalog } = require('../../../support/complex/delete-workspace-service-catalog');

describe('Launch and terminate RStudio instance', () => {
let setup;
let adminSession;

beforeAll(async () => {
setup = await runSetup();
adminSession = await setup.defaultAdminSession();
});

afterAll(async () => {
// await setup.cleanup();
});

async function checkAllRstudioWorkspaceIsTerminated() {
const response = await adminSession.resources.workspaceServiceCatalogs.get();
const workspaces = response.filter(workspace => {
return (
workspace.envTypeId === setup.defaults.envTypes.rstudio.envTypeId &&
!['TERMINATED', 'FAILED'].includes(workspace.status)
);
});
if (workspaces.length > 0) {
throw new Error('All RStudio workspaces should be terminated or failed');
}
}

// eslint-disable-next-line jest/expect-expect
it('should launch a RStudio instance', async () => {
// Putting checkAllRstudioWorkspaceIsTerminated check here, because putting this check in `beforeAll` will not stop executing the test if the check does fail
// https://github.com/facebook/jest/issues/2713
await checkAllRstudioWorkspaceIsTerminated();

const envId = await launchRStudioWorkspace();
// const envId = 'f7cb0f78-3fd2-4351-bea0-b6a05096c2c5';

// For installations without AppStream enabled, check that workspace CIDR can be changed
if (!setup.defaults.isAppStreamEnabled) {
await checkCIDR(envId);
}

const rstudioServerUrlResponse = await checkConnectionUrlCanBeCreated(envId);
await checkConnectionUrlNetworkConnectivity(rstudioServerUrlResponse);
await checkWorkspaceCanBeTerminatedCorrectly(envId);
});

async function launchRStudioWorkspace() {
const workspaceName = setup.gen.string({ prefix: 'launch-studio-workspace-test' });
const createWorkspaceBody = {
name: workspaceName,
envTypeId: setup.defaults.envTypes.rstudio.envTypeId,
envTypeConfigId: setup.defaults.envTypes.rstudio.envTypeConfigId,
studyIds: [],
description: 'test',
projectId: setup.defaults.project.id,
};
if (!setup.defaults.isAppStreamEnabled) {
createWorkspaceBody.cidr = '0.0.0.0/24';
}
const env = await adminSession.resources.workspaceServiceCatalogs.create(createWorkspaceBody);
await sleep(2000);
await adminSession.resources.workflows
.versions('wf-provision-environment-sc')
.version(1)
.findAndPollWorkflow(env.id, 10000, 90);
return env.id;
}

async function checkCIDR(envId) {
const cidrs = {
cidr: [
{
protocol: 'tcp',
fromPort: 22,
toPort: 22,
cidrBlocks: ['0.0.0.0/32'],
},
{
protocol: 'tcp',
fromPort: 80,
toPort: 80,
cidrBlocks: ['0.0.0.0/32'],
},
{
protocol: 'tcp',
fromPort: 443,
toPort: 443,
cidrBlocks: ['0.0.0.0/32'],
},
],
};
await expect(
adminSession.resources.workspaceServiceCatalogs.workspaceServiceCatalog(envId).cidr(cidrs),
).resolves.toBeDefined();
}

async function checkWorkspaceCanBeTerminatedCorrectly(envId) {
await adminSession.resources.workspaceServiceCatalogs.workspaceServiceCatalog(envId).delete();
await sleep(2000);
await adminSession.resources.workflows
.versions('wf-terminate-environment-sc')
.version(1)
.findAndPollWorkflow(envId, 10000, 35);

const envState = await adminSession.resources.workspaceServiceCatalogs.workspaceServiceCatalog(envId).get();

// Check that workspace terminated correctly
expect(envState.status).toEqual('TERMINATED');
await deleteWorkspaceServiceCatalog({ aws: setup.aws, id: envId });
}

async function checkConnectionUrlCanBeCreated(envId) {
const rstudioServerUrlResponse = await adminSession.resources.workspaceServiceCatalogs
.workspaceServiceCatalog(envId)
.connections()
.connection('id-1')
.createUrl();
expect(rstudioServerUrlResponse.url).toBeDefined();
if (setup.defaults.isAppStreamEnabled) {
expect(rstudioServerUrlResponse.appstreamDestinationUrl).toBeDefined();
}

return rstudioServerUrlResponse;
}

async function checkConnectionUrlNetworkConnectivity(rstudioServerUrlResponse) {
if (!setup.defaults.isAppStreamEnabled) {
// VERIFY active workspaces are associated with unexpired TLSv1.2 certs
// By default Axios verify that the domain's SSL cert is valid. If we're able to get a response from the domain it means the domain's cert is valid
// Getting a 403 response code is expected because our client's IP address is not whitelisted to access the RStudio server
const url = rstudioServerUrlResponse.url;
await expect(axios.get(url)).rejects.toThrow('Request failed with status code 403');
} else {
// If AppStream is enabled, we should not be able to access the RStudio url from the internet
const url = rstudioServerUrlResponse.appstreamDestinationUrl;
await expect(axios.get(url)).rejects.toThrow(/getaddrinfo ENOTFOUND .*/);
}
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,28 @@ describe('Cidr workspace-service-catalog scenarios', () => {
});

describe('Cidr workspace-service-catalog', () => {
const cidrs = {
cidr: [
{
protocol: 'tcp',
fromPort: 22,
toPort: 22,
cidrBlocks: ['0.0.0.0/32'],
},
{
protocol: 'tcp',
fromPort: 80,
toPort: 80,
cidrBlocks: ['0.0.0.0/32'],
},
{
protocol: 'tcp',
fromPort: 443,
toPort: 443,
cidrBlocks: ['0.0.0.0/32'],
},
],
};
it('should fail if user is inactive', async () => {
const adminSession2 = await setup.createAdminSession();
const workspaceName = setup.gen.string({ prefix: 'workspace-service-catalog-test' });
Expand All @@ -58,8 +80,6 @@ describe('Cidr workspace-service-catalog scenarios', () => {
envTypeConfigId: configurationId,
});

const cidrs = [{ fromPort: 10, toPort: 20, protocol: 'http', cidrBlocks: ['0.0.0.0/32'] }];

await expect(
adminSession2.resources.workspaceServiceCatalogs.workspaceServiceCatalog(response.id).cidr(cidrs),
).rejects.toMatchObject({
Expand All @@ -82,8 +102,6 @@ describe('Cidr workspace-service-catalog scenarios', () => {
envTypeConfigId: configurationId,
});

const cidrs = [{ fromPort: 10, toPort: 20, protocol: 'http', cidrBlocks: ['0.0.0.0/32'] }];

await expect(
anonymousSession.resources.workspaceServiceCatalogs.workspaceServiceCatalog(response.id).cidr(cidrs),
).rejects.toMatchObject({
Expand Down
3 changes: 3 additions & 0 deletions main/integration-tests/config/settings/example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ emrEnvTypeId: "prod-sampleEMR-pa-sampleEMR"
# Provide the id of a configuration for an imported EMR environment
emrConfigId: "sampleEMRConfigName"

rstudioServerId: "prod-sampleRStudio-pa-sampleRstudio"
rstudioServerConfigId: "sampleRStudioConfigName"

# Provide the id of the external BYOB Data Source study
byobStudy: "sampleByobStudyName"
# ------- CONFIG VALUES BELOW ONLY REQUIRED FOR TESTS IN "appstream-egress-enabled" FOLDER ------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class WorkspaceServiceCatalog extends Resource {

async cidr(body) {
const api = `${this.api}/cidr`;
const response = await this.doCall(async () => this.axiosClient.put(api, body, {}));
const response = await this.doCall(async () => this.axiosClient.post(api, body, {}));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making updates to CIDR is actually a POST method, not a PUT method. (Link here]


await sleep(this.deflakeDelay());
return response;
Expand Down
6 changes: 5 additions & 1 deletion main/integration-tests/support/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class Setup {
// so it has to stay active throughout the test suite duration.
// Therefore the buffer time (in minutes) should be the longest time taken by any single test suite
// If the current token has less than the buffer minutes remaining, we create a new one.
const bufferInMinutes = 10;
const bufferInMinutes = 25;
const tokenExpired = (expiresAt - Date.now()) / 60 / 1000 < bufferInMinutes;

// Only create a new client session if we haven't done that already or if the token has expired
Expand Down Expand Up @@ -153,6 +153,10 @@ class Setup {
envTypeId: this.settings.get('emrEnvTypeId'),
envTypeConfigId: this.settings.get('emrConfigId'),
},
rstudio: {
envTypeId: this.settings.get('rstudioServerId'),
envTypeConfigId: this.settings.get('rstudioServerConfigId'),
},
};

const byobStudy = await this.settings.get('byobStudy');
Expand Down