@moonset/util
Version:
189 lines (177 loc) • 5.64 kB
text/typescript
import {ISDK} from './sdk-utils';
import {CommonConstants as CC} from './constants';
import * as EC2 from 'aws-sdk/clients/ec2';
import * as IAM from 'aws-sdk/clients/iam';
import * as CFN from 'aws-sdk/clients/cloudformation';
/**
* Moonset uses CDK to setup minimum required resources like VPC, role, s3 log
* bucket etc, and then uses SDK to invoke the EMR. This helper class retrieve
* the CDK generated resources for SDK's consumption.
*
*/
export class CDKResourceReader {
/**
* Constructor.
*
* Arguments:
* session: To distinguish different moonset instances in the same
* account. Usally different sessions are created by different users.
*
* sdk: The interface that represent AWS SDK. We need different sdk
* instances for different accounts such as the working account and
* reference accounts.
*
*/
constructor(private readonly session: string, private sdk: ISDK) {
}
async findSecurityGroup(tag: string): Promise<EC2.Types.SecurityGroup> {
const ec2 = this.sdk.ec2();
const params = {
Filters: [
{
Name: `tag:${CC.MOONSET_SESSION}`,
Values: [this.session],
},
{
Name: `tag:${CC.TAG_MOONSET_TYPE}`,
Values: [tag],
},
],
};
const data = await ec2.describeSecurityGroups(params).promise();
if (!data.SecurityGroups || data.SecurityGroups.length == 0) {
throw new Error(`No Security Group found for tag: ${tag}.`);
}
if (data.SecurityGroups.length > 1) {
throw new Error(`Multiple Security Group ${data.SecurityGroups} `+
`found for tag: ${tag}.`);
}
return data.SecurityGroups[0];
}
async findVpc(tag: string): Promise<EC2.Types.Vpc> {
const ec2 = this.sdk.ec2();
const params = {
Filters: [
{
Name: `tag:${CC.MOONSET_SESSION}`,
Values: [this.session],
},
{
Name: `tag:${CC.TAG_MOONSET_TYPE}`,
Values: [tag],
},
],
};
const data = await ec2.describeVpcs(params).promise();
if (!data.Vpcs || data.Vpcs.length == 0) {
throw new Error(`No Vpc found for tag: ${tag}.`);
}
if (data.Vpcs.length > 1) {
throw new Error(`Multiple Vpcs ${data.Vpcs} `+
`found for tag: ${tag}.`);
}
return data.Vpcs[0];
}
async findSubnets(tag: string): Promise<EC2.Types.SubnetList> {
const ec2 = this.sdk.ec2();
const params = {
Filters: [
{
Name: 'vpc-id',
Values: [
(await this.findVpc(tag)).VpcId!,
],
},
],
};
const data = await ec2.describeSubnets(params).promise();
if (!data.Subnets) {
throw new Error(`No Subnets found for tag: ${tag}.`);
}
return data.Subnets;
}
async findPrivateSubnet(tag: string): Promise<EC2.Types.Subnet> {
const subnets = await this.findSubnets(tag);
return subnets.filter((s) => !s.MapPublicIpOnLaunch)[0];
}
/**
* IAM Role API doesn't work well with Tag.
* Check https://github.com/boto/boto3/issues/1855
*
* ResourceGroupsTaggingAPI also doesn't work well with IAM Role Tag. Check
* the support list:
* https://docs.aws.amazon.com/resourcegroupstagging/latest/APIReference/Welcome.html
*
* So we go through all roles and check the tag for each role.
*/
async findRole(tag: string): Promise<IAM.Types.Role> {
const iam = this.sdk.iam();
const roles = [];
let data = await iam.listRoles({}).promise();
while (data.IsTruncated) {
roles.push(...data.Roles);
data = await iam.listRoles({
Marker: data.Marker,
}).promise();
}
roles.push(...data.Roles);
const matchedRoles = [];
for (const r of roles) {
const tags = [];
let data = await iam.listRoleTags({RoleName: r.RoleName}).promise();
while (data.IsTruncated) {
tags.push(...data.Tags);
data = await iam.listRoleTags({
RoleName: r.RoleName,
Marker: data.Marker,
}).promise();
}
tags.push(...data.Tags);
if ( tags.filter((t) =>
t.Key === CC.MOONSET_SESSION &&
t.Value === this.session).length > 0 &&
tags.filter((t) =>
t.Key === CC.TAG_MOONSET_TYPE &&
t.Value === tag).length > 0 ) {
matchedRoles.push(r);
}
}
if (!matchedRoles || matchedRoles.length == 0) {
throw new Error(`No matchedRoles found for: ${tag}.`);
}
if (matchedRoles.length > 1) {
throw new Error(`Multiple Roles ${matchedRoles} `+
`found for: ${tag}.`);
}
return matchedRoles[0];
}
async findS3Bucket(tag: string): Promise<string> {
const bucket = (await this.findResourceArn(tag))
.replace('arn:aws:s3:::', '');
return bucket;
}
async findResourceArn(tag: string): Promise<string> {
const tagApi = this.sdk.tagApi();
const resources = await tagApi.getResources({
TagFilters: [
{
Key: CC.MOONSET_SESSION,
Values: [this.session],
},
{
Key: CC.TAG_MOONSET_TYPE,
Values: [tag],
},
],
}).promise();
if ( !resources ||
!resources.ResourceTagMappingList ||
resources.ResourceTagMappingList.length != 1 ||
!resources.ResourceTagMappingList[0].ResourceARN
) {
throw new Error(`The resource should be uniquely identified.` +
`However we found ${JSON.stringify(resources)}.`);
}
return resources.ResourceTagMappingList[0].ResourceARN;
}
}