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

Commit

Permalink
test: rstudio alb integration tests (#813)
Browse files Browse the repository at this point in the history
  • Loading branch information
nguyen102 authored Nov 18, 2021
1 parent 6a01fd5 commit 04c61a5
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 7 deletions.
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, {}));

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

0 comments on commit 04c61a5

Please sign in to comment.