in packages/aws-rfdk/lib/deadline/lib/usage-based-licensing.ts [509:630]
constructor(scope: Construct, id: string, props: UsageBasedLicensingProps) {
super(scope, id);
const usageBasedLicenses = new Array();
props.licenses.forEach(license => {
usageBasedLicenses.push(`${license.licenseName}:${license.limit ? license.limit : UsageBasedLicense.UNLIMITED}`);
});
if (usageBasedLicenses.length < 1) {
throw new Error('Should be specified at least one license with defined limit.');
}
this.cluster = new Cluster(this, 'Cluster', { vpc: props.vpc });
if (!props.vpcSubnets && props.renderQueue.repository.secretsManagementSettings.enabled) {
Annotations.of(this).addWarning(
'Deadline Secrets Management is enabled on the Repository and VPC subnets have not been supplied. Using dedicated subnets is recommended. See https://github.com/aws/aws-rfdk/blobs/release/packages/aws-rfdk/lib/deadline/README.md#using-dedicated-subnets-for-deadline-components',
);
}
const vpcSubnets = props.vpcSubnets ?? { subnetType: SubnetType.PRIVATE_WITH_EGRESS };
this.asg = this.cluster.addCapacity('ASG', {
vpcSubnets,
instanceType: props.instanceType ? props.instanceType : InstanceType.of(InstanceClass.C5, InstanceSize.LARGE),
minCapacity: props.desiredCount ?? 1,
maxCapacity: props.desiredCount ?? 1,
blockDevices: [ {
deviceName: '/dev/xvda',
volume: BlockDeviceVolume.ebs( 30, {encrypted: true}),
}],
// addCapacity doesn't specifically take a securityGroup, but it passes on its properties to the ASG it creates,
// so this security group will get applied there
// @ts-ignore
securityGroup: props.securityGroup,
machineImage: EcsOptimizedImage.amazonLinux2023(),
});
const taskDefinition = new TaskDefinition(this, 'TaskDefinition', {
compatibility: Compatibility.EC2,
networkMode: NetworkMode.HOST,
});
this.grantPrincipal = taskDefinition.taskRole;
const containerEnv = {
UBL_CERTIFICATES_URI: '',
UBL_LIMITS: usageBasedLicenses.join(';'),
...props.renderQueue.configureClientECS({
hosts: [this.asg],
grantee: this,
}),
};
containerEnv.UBL_CERTIFICATES_URI = props.certificateSecret.secretArn;
props.certificateSecret.grantRead(taskDefinition.taskRole);
const prefix = props.logGroupProps?.logGroupPrefix ?? UsageBasedLicensing.DEFAULT_LOG_GROUP_PREFIX;
const defaultedLogGroupProps: LogGroupFactoryProps = {
...props.logGroupProps,
logGroupPrefix: prefix,
};
const logGroup = LogGroupFactory.createOrFetch(this, 'LogGroupWrapper', id, defaultedLogGroupProps);
logGroup.grantWrite(this.asg);
const container = taskDefinition.addContainer('LicenseForwarderContainer', {
image: props.images.licenseForwarder,
environment: containerEnv,
memoryReservationMiB: 1024,
logging: LogDriver.awsLogs({
logGroup,
streamPrefix: 'LicenseForwarder',
}),
});
// Increase ulimits
container.addUlimits({
name: UlimitName.NOFILE,
softLimit: 200000,
hardLimit: 200000,
}, {
name: UlimitName.NPROC,
softLimit: 64000,
hardLimit: 64000,
});
this.service = new Ec2Service(this, 'Service', {
cluster: this.cluster,
taskDefinition,
desiredCount: props.desiredCount ?? 1,
placementConstraints: [PlacementConstraint.distinctInstances()],
// This is required to right-size our host capacity and not have the ECS service block on updates. We set a memory
// reservation, but no memory limit on the container. This allows the container's memory usage to grow unbounded.
// We want 1:1 container to container instances to not over-spend, but this comes at the price of down-time during
// cloudformation updates.
minHealthyPercent: 0,
maxHealthyPercent: 100,
});
// An explicit dependency is required from the service to the ASG providing its capacity.
// See: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html
this.service.node.addDependency(this.asg);
this.node.defaultChild = this.service;
if (props.renderQueue.repository.secretsManagementSettings.enabled) {
props.renderQueue.configureSecretsManagementAutoRegistration({
dependent: this.service.node.defaultChild as CfnService,
registrationStatus: SecretsManagementRegistrationStatus.REGISTERED,
role: SecretsManagementRole.CLIENT,
vpc: props.vpc,
vpcSubnets,
});
}
// Grant the render queue the ability to connect to the license forwarder to register workers
this.asg.connections.allowFrom(props.renderQueue.backendConnections, Port.tcp(UsageBasedLicensing.LF_PORT));
// Tag deployed resources with RFDK meta-data
tagConstruct(this);
}