This repository has been archived by the owner on Dec 6, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
384 additions
and
9 deletions.
There are no files selected for viewing
229 changes: 229 additions & 0 deletions
229
...ckages/base-raas-cfn-templates/src/templates/service-catalog/ec2-rstudio-instance.cfn.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
110 changes: 110 additions & 0 deletions
110
...vices/lib/environment/service-catalog/__tests__/environment-sc-connection-service.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.