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

Commit

Permalink
test: restore advanced integ tests (#722)
Browse files Browse the repository at this point in the history
* test: restore advanced integ tests

* testing workflow trigger

* revert workflow changes

* chore: add logic for new admin token integ tests

* code cleanup

* fix: method call correction integ test

* code cleanup
  • Loading branch information
SanketD92 authored Sep 28, 2021
1 parent f831100 commit 4d6f972
Show file tree
Hide file tree
Showing 7 changed files with 324 additions and 1,236 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* 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 { NodeSSH } = require('node-ssh');
const { mountStudies, readWrite } = require('../../../support/complex/run-shell-command');
const { runSetup } = require('../../../support/setup');

describe('EC2 Linux scenarios', () => {
let setup;
let ssh;
async function testSetup() {
const adminSession = await setup.createAdminSession();
const admin2Session = await setup.createAdminSession();
const keyPair = await admin2Session.resources.keyPairs.create();
return { adminSession, admin2Session, keyPair };
}

beforeAll(async () => {
setup = await runSetup();
ssh = new NodeSSH();
});
afterAll(async () => {
await setup.cleanup();
});

describe('Updates to mounted study permissions', () => {
it('should propagate for Org Study', async () => {
const { adminSession, admin2Session, keyPair } = await testSetup();
const studyId = setup.gen.string({ prefix: `create-org-study-test` });
await adminSession.resources.studies.create({ id: studyId, name: studyId, category: 'Organization' });
await adminSession.resources.studies
.study(studyId)
.propagatePermission(admin2Session, ['admin', 'readwrite'], []);

const workspaceName = setup.gen.string({ prefix: 'workspace-sc-test' });

const env = await admin2Session.resources.workspaceServiceCatalogs.create({
name: workspaceName,
envTypeId: setup.defaults.envTypes.ec2Linux.envTypeId,
envTypeConfigId: setup.defaults.envTypes.ec2Linux.envTypeConfigId,
studyIds: [studyId],
description: 'test',
projectId: setup.defaults.project.id,
cidr: '0.0.0.0/0',
});
// Poll until workspace is provisioned
await sleep(2000);
await adminSession.resources.workflows
.versions('wf-provision-environment-sc')
.version(1)
.findAndPollWorkflow(env.id, 10000, 60);

// Connect to workspace
const networkInfo = await admin2Session.resources.workspaceServiceCatalogs
.workspaceServiceCatalog(env.id) // env.id
.connections()
.connection('id-1')
.sendSshPublicKey({ keyPairId: keyPair.id });

await ssh.connect({
host: networkInfo.networkInterfaces[0].publicDnsName,
username: 'ec2-user',
privateKey: keyPair.privateKey,
});

// Mount studies
let output;
output = await mountStudies(ssh, studyId);

// Readwrite permission level
const numberOfBytes = 20;
output = await readWrite(ssh, studyId, numberOfBytes);
expect(output.stdout).toContain(`ec2-user ${numberOfBytes}`);

// Admin permission level
await adminSession.resources.studies.study(studyId).propagatePermission(admin2Session, ['admin'], ['readwrite']);
output = await readWrite(ssh, studyId);
expect(output.stderr).toEqual(expect.stringMatching(/write error: Permission denied/));

// Readonly permission level
await adminSession.resources.studies.study(studyId).propagatePermission(admin2Session, ['readonly'], ['admin']);
output = await readWrite(ssh, studyId);
expect(output.stderr).toEqual(expect.stringMatching(/write error: Permission denied/));

// None permission level
await adminSession.resources.studies.study(studyId).propagatePermission(admin2Session, [], ['readonly']);
output = await readWrite(ssh, studyId);
expect(output.stderr).toEqual(expect.stringMatching(/reading directory .: Permission denied/));

await ssh.dispose();
await setup.cleanup();
});

it('should propagate for BYOB Study', async () => {
const { adminSession, admin2Session, keyPair } = await testSetup();
const externalStudy = setup.defaults.byobStudy;
const workspaceName = setup.gen.string({ prefix: 'workspace-sc-test' });
await adminSession.resources.studies.study(externalStudy).propagatePermission(admin2Session, ['readwrite'], []);

const env = await admin2Session.resources.workspaceServiceCatalogs.create({
name: workspaceName,
envTypeId: setup.defaults.envTypes.ec2Linux.envTypeId,
envTypeConfigId: setup.defaults.envTypes.ec2Linux.envTypeConfigId,
studyIds: [externalStudy],
description: 'test',
projectId: setup.defaults.project.id,
cidr: '0.0.0.0/0',
});
// Poll until workspace is provisioned
await sleep(2000);
await adminSession.resources.workflows
.versions('wf-provision-environment-sc')
.version(1)
.findAndPollWorkflow(env.id, 10000, 60);
// Connect to workspace
const networkInfo = await admin2Session.resources.workspaceServiceCatalogs
.workspaceServiceCatalog(env.id) // env.id
.connections()
.connection('id-1')
.sendSshPublicKey({ keyPairId: keyPair.id });

await ssh.connect({
host: networkInfo.networkInterfaces[0].publicDnsName,
username: 'ec2-user',
privateKey: keyPair.privateKey,
});

// Mount studies
let output;
output = await mountStudies(ssh, externalStudy);

// Readwrite permission level
const numberOfBytes = 20;
output = await readWrite(ssh, externalStudy, numberOfBytes);
expect(output.stdout).toContain(`ec2-user ${numberOfBytes}`);

// Readonly permission level
await adminSession.resources.studies
.study(externalStudy)
.propagatePermission(admin2Session, ['readonly'], ['readwrite']);
output = await readWrite(ssh, externalStudy);
expect(output.stderr).toEqual(expect.stringMatching(/reading directory .: Permission denied/));

await ssh.dispose();
// Removes user permission
await adminSession.resources.studies.study(externalStudy).propagatePermission(admin2Session, [], ['readonly']);
await setup.cleanup();
});
});

describe('Confirm study permissions', () => {
it('should pass for My Study', async () => {
const { adminSession, admin2Session, keyPair } = await testSetup();
const studyId = setup.gen.string({ prefix: `create-my-study-test` });
await admin2Session.resources.studies.create({ id: studyId, name: studyId, category: 'My Studies' });

const workspaceName = setup.gen.string({ prefix: 'workspace-sc-test' });
const env = await admin2Session.resources.workspaceServiceCatalogs.create({
name: workspaceName,
envTypeId: setup.defaults.envTypes.ec2Linux.envTypeId,
envTypeConfigId: setup.defaults.envTypes.ec2Linux.envTypeConfigId,
studyIds: [studyId],
description: 'test',
projectId: setup.defaults.project.id,
cidr: '0.0.0.0/0',
});

// Poll until workspace is provisioned
await sleep(2000);
await adminSession.resources.workflows
.versions('wf-provision-environment-sc')
.version(1)
.findAndPollWorkflow(env.id, 10000, 60);

// Connect to workspace
const networkInfo = await admin2Session.resources.workspaceServiceCatalogs
.workspaceServiceCatalog(env.id) // env.id
.connections()
.connection('id-1')
.sendSshPublicKey({ keyPairId: keyPair.id });

await ssh.connect({
host: networkInfo.networkInterfaces[0].publicDnsName,
username: 'ec2-user',
privateKey: keyPair.privateKey,
});

const output = await mountStudies(ssh, studyId);
expect(output.stdout).toEqual(expect.stringMatching(/output.txt/));
await setup.cleanup();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* permissions and limitations under the License.
*/

const { sleep } = require('@aws-ee/base-services/lib/helpers/utils');
const { runSetup } = require('../../../support/setup');
const {
createWorkspaceTypeAndConfiguration,
Expand Down Expand Up @@ -167,4 +168,51 @@ describe('Create workspace-service-catalog scenarios', () => {
});
});
});
describe('Workspace SC env with studies', () => {
it('for EC2Linux should provision correctly', async () => {
const admin1Session = await setup.createAdminSession();

const studyIds = [];
let studyId = setup.gen.string({ prefix: `create-study-ray-my-study` });
await expect(
admin1Session.resources.studies.create({ id: studyId, name: studyId, category: 'My Studies' }),
).resolves.toMatchObject({
id: studyId,
});
studyIds.push(studyId);

studyId = setup.gen.string({ prefix: `create-study-ray-org-study` });
await expect(
admin1Session.resources.studies.create({ id: studyId, name: studyId, category: 'Organization' }),
).resolves.toMatchObject({
id: studyId,
});
studyIds.push(studyId);

const workspaceName = setup.gen.string({ prefix: 'workspace-sc-test' });
const env = await admin1Session.resources.workspaceServiceCatalogs.create({
name: workspaceName,
envTypeId: setup.defaults.envTypes.ec2Linux.envTypeId,
envTypeConfigId: setup.defaults.envTypes.ec2Linux.envTypeConfigId,
studyIds,
description: 'assignment',
projectId: setup.defaults.project.id,
cidr: '123.123.123.123/12',
});
expect(env).toMatchObject({
name: workspaceName,
envTypeId: setup.defaults.envTypes.ec2Linux.envTypeId,
envTypeConfigId: setup.defaults.envTypes.ec2Linux.envTypeConfigId,
studyIds,
});

// Poll until workspace is provisioned
await sleep(2000);
await admin1Session.resources.workflows
.versions('wf-provision-environment-sc')
.version(1)
.findAndPollWorkflow(env.id, 10000, 60);
await setup.cleanup();
});
});
});
18 changes: 9 additions & 9 deletions main/integration-tests/config/settings/example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,24 @@ isLocal: false
# Set this to the API endpoint if different than the following
localApiEndpoint: http://localhost:4000
# Provide the id of the available EC2-Linux Service Catalog product
# ec2LinuxEnvTypeId: "prod-sampleEC2Linux-pa-sampleEC2Linux"
ec2LinuxEnvTypeId: "prod-sampleEC2Linux-pa-sampleEC2Linux"
# Provide the id of a configuration for an imported EC2-Linux environment
# ec2LinuxConfigId: ""
ec2LinuxConfigId: "sampleLinuxConfigName"

# Provide the id of the available EC2-Windows Service Catalog product
# ec2WindowsEnvTypeId: "prod-sampleEC2Windows-pa-sampleEC2Windows"
ec2WindowsEnvTypeId: "prod-sampleEC2Windows-pa-sampleEC2Windows"
# Provide the id of a configuration for an imported EC2-Windows environment
# ec2WindowsConfigId: ""
ec2WindowsConfigId: "sampleWindowsConfigName"

# Provide the id of the available SageMaker Service Catalog product
# sagemakerEnvTypeId: "prod-sampleSageMaker-pa-sampleSageMaker"
sagemakerEnvTypeId: "prod-sampleSageMaker-pa-sampleSageMaker"
# Provide the id of a configuration for an imported SageMaker environment
# sagemakerConfigId: ""
sagemakerConfigId: "sampleSageMakerConfigName"

# Provide the id of the available EMR Service Catalog product
# emrEnvTypeId: "prod-sampleEMR-pa-sampleEMR"
emrEnvTypeId: "prod-sampleEMR-pa-sampleEMR"
# Provide the id of a configuration for an imported EMR environment
# emrConfigId: ""
emrConfigId: "sampleEMRConfigName"

# Provide the id of the external BYOB Data Source study
# byobStudy: ""
byobStudy: "sampleByobStudyName"
1 change: 1 addition & 0 deletions main/integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"chance": "^1.1.7",
"fs-extra": "^9.1.0",
"js-yaml": "^4.1.0",
"jwt-decode": "^2.2.0",
"lodash": "^4.17.21",
"node-ssh": "^11.1.1",
"services": "workspace:*",
Expand Down
9 changes: 7 additions & 2 deletions main/integration-tests/support/complex/run-shell-command.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ async function mountStudies(ssh, studyId) {
const output = await ssh.execCommand(`source ~/.bash_profile && cd ~/studies/${studyId} && touch output.txt && ls`);
return output;
}
async function readWrite(ssh, studyId) {

// This method aides in the advanced integration test to check study permission levels on workspaces
// by performing the following operations:
// 1. Reads the contents of the study folder (verifies read priveleges)
// 2. Writes random content into a new file in that study folder, and lists to confirm file can be viewed (verifies write priveleges)
async function readWrite(ssh, studyId, numberOfBytes = 20) {
const output = await ssh.execCommand(
`cd ~/studies/${studyId} && ls -l && head -c 20 </dev/urandom >output.txt && ls -l`,
`cd ~/studies/${studyId} && ls -l && head -c ${numberOfBytes} </dev/urandom >output.txt && ls -l`,
);
return output;
}
Expand Down
52 changes: 48 additions & 4 deletions main/integration-tests/support/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

const _ = require('lodash');
const jwtDecode = require('jwt-decode');

const Settings = require('./utils/settings');
const { getIdToken } = require('./utils/id-token');
Expand Down Expand Up @@ -67,12 +68,55 @@ class Setup {
jest.retryTimes(3);
}

async getNewAdminIdToken() {
let apiEndpoint;

// If isLocal = false, we get the api endpoint from the backend stack outputs
if (this.settings.get('isLocal')) {
apiEndpoint = this.settings.get('localApiEndpoint');
} else {
const cloudformation = await this.aws.services.cloudFormation();
const stackName = this.aws.settings.get('backendStackName');
apiEndpoint = await cloudformation.getStackOutputValue(stackName, 'ServiceEndpoint');
if (_.isEmpty(apiEndpoint)) throw new Error(`No API Endpoint value defined in stack ${stackName}`);
}

// Get the admin password from parameter store
const ssm = await this.aws.services.parameterStore();
const passwordPath = this.settings.get('passwordPath');
const password = await ssm.getParameter(passwordPath);

const adminIdToken = await getIdToken({
username: this.settings.get('username'),
password,
apiEndpoint,
authenticationProviderId: this.settings.get('authenticationProviderId'),
});

return adminIdToken;
}

async defaultAdminSession() {
// Only create a new client session if we haven't done that already
if (this.defaultAdminSessionInstance) return this.defaultAdminSessionInstance;
let idToken = this.settings.get('adminIdToken');
const decodedIdToken = jwtDecode(idToken);
const expiresAt = _.get(decodedIdToken, 'exp', 0) * 1000;

// Assume the default admin session is shared between all test cases in a given test suite (ie. test file),
// 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 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
if (this.defaultAdminSessionInstance && !tokenExpired) return this.defaultAdminSessionInstance;

// If previous token expired, we need to create a new id token for the default admin
if (tokenExpired) {
idToken = await this.getNewAdminIdToken();
this.settings.set('adminIdToken', idToken);
}

const idToken = this.settings.get('adminIdToken');
// In the future, we can check if the token expired and if so, we can create a new one
const session = await getClientSession({ idToken, setup: this });
this.sessions.push(session);
this.defaultAdminSessionInstance = session;
Expand Down
Loading

0 comments on commit 4d6f972

Please sign in to comment.