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

Commit

Permalink
adding rstudio SC product
Browse files Browse the repository at this point in the history
  • Loading branch information
SanketD92 committed Sep 11, 2020
1 parent 688c1df commit cdebe7e
Show file tree
Hide file tree
Showing 8 changed files with 384 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
AWSTemplateFormatVersion: 2010-09-09

Description: Service-Workbench-on-AWS EC2-RStudio

Parameters:
Namespace:
Type: String
Description: An environment name that will be prefixed to resource names
AmiId:
Type: String
Description: Amazon Machine Image for the EC2 instance
InstanceType:
Type: String
Description: EC2 instance type to launch
Default: t3.xlarge
KeyName:
Type: String
Description: Keypair name for SSH access
AccessFromCIDRBlock:
Type: String
Description: The CIDR used to access the ec2 instances.
S3Mounts:
Type: String
Description: A JSON array of objects with name, bucket, and prefix properties used to mount data
IamPolicyDocument:
Type: String
Description: The IAM policy to be associated with the launched workstation
VPC:
Description: The VPC in which the EC2 instance will reside
Type: AWS::EC2::VPC::Id
Subnet:
Description: The VPC subnet in which the EC2 instance will reside
Type: AWS::EC2::Subnet::Id
EnvironmentInstanceFiles:
Type: String
Description: >-
An S3 URI (starting with "s3://") that specifies the location of files to be copied to
the environment instance, including any bootstrap scripts
EncryptionKeyArn:
Type: String
Description: The ARN of the KMS encryption Key used to encrypt data in the instance

Conditions:
IamPolicyEmpty: !Equals [!Ref IamPolicyDocument, '{}']

Resources:
IAMRole:
Type: 'AWS::IAM::Role'
Properties:
RoleName: !Join ['-', [Ref: Namespace, 'ec2-role']]
Path: '/'
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Service:
- 'ec2.amazonaws.com'
Action:
- 'sts:AssumeRole'
Policies:
- !If
- IamPolicyEmpty
- !Ref 'AWS::NoValue'
- PolicyName: !Join ['-', [Ref: Namespace, 's3-studydata-policy']]
PolicyDocument: !Ref IamPolicyDocument
- PolicyName: !Join ['-', [Ref: Namespace, 's3-bootstrap-script-policy']]
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Action: 's3:GetObject'
Resource: !Sub
- 'arn:aws:s3:::${S3Location}/*'
# Remove "s3://" prefix from EnvironmentInstanceFiles
- S3Location: !Select [1, !Split ['s3://', !Ref EnvironmentInstanceFiles]]
- Effect: 'Allow'
Action: 's3:ListBucket'
Resource: !Sub
- 'arn:aws:s3:::${S3Bucket}'
- S3Bucket: !Select [2, !Split ['/', !Ref EnvironmentInstanceFiles]]
Condition:
StringLike:
s3:prefix: !Sub
- '${S3Prefix}/*'
- S3Prefix: !Select [3, !Split ['/', !Ref EnvironmentInstanceFiles]]

InstanceProfile:
Type: 'AWS::IAM::InstanceProfile'
Properties:
InstanceProfileName: !Join ['-', [Ref: Namespace, 'ec2-profile']]
Path: '/'
Roles:
- Ref: IAMRole

SecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: EC2 workspace security group
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
CidrIp: 0.0.0.0/0
- IpProtocol: icmp
FromPort: -1
ToPort: -1
CidrIp: !Ref AccessFromCIDRBlock
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: !Ref AccessFromCIDRBlock
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: !Ref AccessFromCIDRBlock
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: !Ref AccessFromCIDRBlock
Tags:
- Key: Name
Value: !Join ['-', [Ref: Namespace, 'ec2-sg']]
- Key: Description
Value: EC2 workspace security group
VpcId: !Ref VPC

EC2Instance:
Type: 'AWS::EC2::Instance'
CreationPolicy:
ResourceSignal:
Timeout: 'PT20M'
Properties:
ImageId: !Ref AmiId
InstanceType: !Ref InstanceType
IamInstanceProfile: !Ref InstanceProfile
KeyName: !Ref KeyName
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: 8
Encrypted: true
KmsKeyId: !Ref EncryptionKeyArn
NetworkInterfaces:
- AssociatePublicIpAddress: 'true'
DeviceIndex: '0'
GroupSet:
- !Ref SecurityGroup
SubnetId: !Ref Subnet
Tags:
- Key: Name
Value: !Join ['-', [Ref: Namespace, 'ec2-rstudio']]
- Key: Description
Value: EC2 workspace instance
UserData:
Fn::Base64: !Sub |
#!/usr/bin/env bash
# Download and execute bootstrap script
aws s3 cp "${EnvironmentInstanceFiles}/get_bootstrap.sh" "/tmp"
chmod 500 "/tmp/get_bootstrap.sh"
/tmp/get_bootstrap.sh "${EnvironmentInstanceFiles}" '${S3Mounts}'

# Signal result to CloudFormation
/opt/aws/bin/cfn-signal -e $? --stack "${AWS::StackName}" --resource "EC2Instance" --region "${AWS::Region}"

Outputs:
Ec2WorkspaceDnsName:
Description: Public DNS name of the EC2 workspace instance
Value: !GetAtt [EC2Instance, PublicDnsName]

Ec2WorkspacePublicIp:
Description: Public IP address of the EC2 workspace instance
Value: !GetAtt [EC2Instance, PublicIp]

Ec2WorkspaceInstanceId:
Description: Instance Id for the EC2 workspace instance
Value: !Ref EC2Instance

WorkspaceInstanceRoleArn:
Description: IAM role assumed by the EC2 workspace instance
Value: !GetAtt IAMRole.Arn

# Connection related outputs. These outputs need to have prefix "MetaConnection"
# The "connections" are derived based on the CFN outputs as follows.
#
# CFN outputs with the OutputKey having format "MetaConnection<ConnectionAttrib>" or "MetaConnection<N><ConnectionAttrib>"
# are used for extracting connection information.
# - If the environment has only one connection then it can have outputs with "MetaConnection<ConnectionAttrib>" format.
# - If it has multiple connections then it can have outputs with "MetaConnection<N><ConnectionAttrib>" format.
# For example, MetaConnection1Name, MetaConnection2Name, etc.
#
# The expected CFN output variables used for capturing connections related information are as follows:
#
# - MetaConnectionName (or MetaConnection<N>Name) - Provides name for connection
#
# - MetaConnectionUrl (or MetaConnection<N>Url) - Provides connection url, if available
#
# - MetaConnectionScheme (or MetaConnection<N>Scheme) - Provides connection protocol information such as http, https, ssh, jdbc, odbc etc
#
# - MetaConnectionType (or MetaConnection<N>Type) - Provides type of the connection such as "SageMaker", "EMR", "FOO", "BAR" etc
#
# - MetaConnectionInfo (or MetaConnection<N>Info) - Provides extra information required to form connection url.
# For example, in case of MetaConnectionType = SageMaker, the MetaConnectionInfo should provide SageMaker notebook
# instance name that can be used to form pre-signed SageMaker URL.
#
# - MetaConnectionInstanceId (or MetaConnection<N>InstanceId) - Provides AWS EC2 instanceId of the instance to connect to when applicable.
# Currently this is applicable only when ConnectionScheme = 'ssh'.
# This instanceId will be used for sending user's SSH public key using AWS EC2 Instance Connect when user wants to SSH to the instance.
#
MetaConnection1Info:
Description: The name of the RStudio notebook instance.
Value: !GetAtt [EC2Instance, PublicDnsName]

MetaConnection1Type:
Description: Type of environment this connection is for
Value: RStudio

MetaConnection1Name:
Description: Name for this connection
Value: RStudio Notebook

MetaConnection1Scheme:
Description: Protocol for connection 1
Value: https

MetaConnection1InstanceId:
Description: EC2 Linux Instance Id
Value: !Ref EC2Instance
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ const productsToCreate = [
// * Product Feature 2`,
// },
// DO NOT DELETE ANY ITEMS IN THIS LIST (else, you'll lose auto-create/update functionality for them)
{
filename: 'ec2-rstudio-instance',
displayName: 'EC2 RStudio',
description: `* An EC2 RStudio instance with HTTPS access`,
},
{
filename: 'ec2-linux-instance',
displayName: 'EC2 Linux',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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 ServicesContainer = require('@aws-ee/base-services-container/lib/services-container');
const JsonSchemaValidationService = require('@aws-ee/base-services/lib/json-schema-validation-service');
const Logger = require('@aws-ee/base-services/lib/logger/logger-service');

// Mocked dependencies
jest.mock('@aws-ee/base-api-services/lib/jwt-service');
const JwtService = require('@aws-ee/base-api-services/lib/jwt-service');

jest.mock('@aws-ee/key-pair-mgmt-services/lib/key-pair/key-pair-service');
const KeyPairServiceMock = require('@aws-ee/key-pair-mgmt-services/lib/key-pair/key-pair-service');

jest.mock('@aws-ee/base-services/lib/settings/env-settings-service');
const SettingsServiceMock = require('@aws-ee/base-services/lib/settings/env-settings-service');

jest.mock('@aws-ee/base-services/lib/audit/audit-writer-service');
const AuditServiceMock = require('@aws-ee/base-services/lib/audit/audit-writer-service');

jest.mock('@aws-ee/base-services/lib/plugin-registry/plugin-registry-service');
const PluginRegistryServiceMock = require('@aws-ee/base-services/lib/plugin-registry/plugin-registry-service');

jest.mock('../../environment-dns-service.js');
const EnvironmentDnsServiceMock = require('../../environment-dns-service.js');

jest.mock('../environment-sc-service');
const EnvironmentSCServiceMock = require('../environment-sc-service');

jest.mock('../environment-sc-keypair-service');
const EnvironmentScKeyPairServiceMock = require('../environment-sc-keypair-service');

const EnvironmentScConnectionService = require('../environment-sc-connection-service');

describe('EnvironmentScConnectionService', () => {
let service = null;
beforeEach(async () => {
const container = new ServicesContainer();
container.register('jsonSchemaValidationService', new JsonSchemaValidationService());
container.register('jwtService', new JwtService());
container.register('log', new Logger());
container.register('auditWriterService', new AuditServiceMock());
container.register('pluginRegistryService', new PluginRegistryServiceMock());
container.register('settings', new SettingsServiceMock());
container.register('environmentDnsService', new EnvironmentDnsServiceMock());
container.register('keyPairService', new KeyPairServiceMock());
container.register('environmentScKeypairService', new EnvironmentScKeyPairServiceMock());
container.register('environmentScService', new EnvironmentSCServiceMock());
container.register('environmentScConnectionService', new EnvironmentScConnectionService());
await container.initServices();

// suppress expected console errors
jest.spyOn(console, 'error').mockImplementation();

// Get instance of the service we are testing
service = await container.find('environmentScConnectionService');
});

describe('create connection', () => {
it('should return connection if exists', async () => {
// BUILD
const connection = { url: 'www.example.com', info: 'An already existing connection' };
service.mustFindConnection = jest.fn(() => connection);

// OPERATE
const retConn = await service.createConnectionUrl();

// CHECK
expect(retConn).toBe(connection);
});

it('should get RStudio connection URL for RStudio connection types', async () => {
// BUILD
const connection = { type: 'RStudio' };
service.mustFindConnection = jest.fn(() => connection);
service.getRStudioUrl = jest.fn();

// OPERATE
await service.createConnectionUrl();

// CHECK
expect(service.getRStudioUrl).toHaveBeenCalled();
});

it('should NOT get RStudio connection URL for non-RStudio connection types', async () => {
// BUILD
const connection = { type: 'nonRStudio' };
service.mustFindConnection = jest.fn(() => connection);
service.getRStudioUrl = jest.fn();

// OPERATE
await service.createConnectionUrl();

// CHECK
expect(service.getRStudioUrl).not.toHaveBeenCalled();
});
});
});
Loading

0 comments on commit cdebe7e

Please sign in to comment.