UNPKG

@moonset/util

Version:
189 lines (177 loc) 5.64 kB
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; } }