aws-cdk-neuronx-patterns
Version:
> [!WARNING] > This library is experimental module.
145 lines (141 loc) • 22.1 kB
JavaScript
"use strict";
var _a, _b;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApplicationLoadBalancedNeuronxService = exports.NeuronxTaskDefinition = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const aws_autoscaling_1 = require("aws-cdk-lib/aws-autoscaling");
const aws_ec2_1 = require("aws-cdk-lib/aws-ec2");
const ecs = require("aws-cdk-lib/aws-ecs");
const aws_ecs_patterns_1 = require("aws-cdk-lib/aws-ecs-patterns");
const neuronx_1 = require("../neuronx");
class NeuronxTaskDefinition extends ecs.Ec2TaskDefinition {
constructor(scope, id, props) {
const neuronxInstanceType = props.neuronxInstanceType ?? props.compiledModel.compileTimeInstanceType;
const tensorParallelSize = props.tensorParallelSize ?? 1;
const tasksPerInstance = Math.floor(neuronxInstanceType.acceleratorChips.neuronxCores / tensorParallelSize);
const placementConstraints = tasksPerInstance >= 2
? [
ecs.PlacementConstraint.memberOf(`runningTasksCount<${tasksPerInstance}`),
]
: [];
super(scope, id, {
...props,
placementConstraints: [
...(props.placementConstraints ?? []),
...placementConstraints,
],
});
this.linuxParameters = new ecs.LinuxParameters(this, "LinuxParameters");
this.linuxParameters.addDevices(...Array.from({
length: neuronxInstanceType?.acceleratorChips.chips ?? 1,
}).map((_, index) => ({
hostPath: `/dev/neuron${index}`,
containerPath: `/dev/neuron${index}`,
permissions: [ecs.DevicePermission.READ, ecs.DevicePermission.WRITE],
})));
if (this.executionRole) {
props.compiledModel.bucket.grantRead(this.executionRole);
}
props.compiledModel.bucket.grantRead(this.taskRole);
this.compiledModel = props.compiledModel;
this.neuronxInstanceType = neuronxInstanceType;
this.tensorParallelSize = tensorParallelSize;
this.tasksPerInstance = tasksPerInstance;
}
addContainerWithDefault(id, props) {
const container = super.addContainer(id, {
logging: ecs.LogDriver.awsLogs({
streamPrefix: id,
}),
linuxParameters: this.linuxParameters,
memoryReservationMiB: Math.ceil((this.neuronxInstanceType.memory.toMebibytes() * 0.8) /
this.tasksPerInstance),
...props,
});
container.addMountPoints({
sourceVolume: "model",
containerPath: "/opt/ml/model",
readOnly: false,
});
return container;
}
}
exports.NeuronxTaskDefinition = NeuronxTaskDefinition;
_a = JSII_RTTI_SYMBOL_1;
NeuronxTaskDefinition[_a] = { fqn: "aws-cdk-neuronx-patterns.NeuronxTaskDefinition", version: "0.0.23" };
class ApplicationLoadBalancedNeuronxService extends aws_ecs_patterns_1.ApplicationLoadBalancedEc2Service {
constructor(scope, id, props) {
const neuronxTaskDefinition = props.taskDefinition;
if (!neuronxTaskDefinition ||
!(neuronxTaskDefinition instanceof NeuronxTaskDefinition)) {
throw new Error("taskDefinition must extend NeuronxTaskDefinition");
}
super(scope, id, {
healthCheckGracePeriod: props.healthCheckGracePeriod ?? aws_cdk_lib_1.Duration.minutes(15),
...props,
});
const cluster = this.getDefaultCluster(this, props.vpc);
// install and mount mountpoint for s3
const userData = aws_ec2_1.UserData.forLinux();
userData.addCommands(`
#!/bin/bash -e
# Install Mountpoint
MP_RPM=$(mktemp --suffix=.rpm)
curl https://s3.amazonaws.com/mountpoint-s3-release/latest/x86_64/mount-s3.rpm > $MP_RPM
# cloud-init installs conflict with SSM agent: https://github.com/amazonlinux/amazon-linux-2023/issues/397
attempt=0
max_attempts=5
until dnf install -y $MP_RPM; do
attempt=$((attempt + 1))
if [ $attempt -ge $max_attempts ]; then
echo "Failed to install mount-s3 after $max_attempts attempts. Exiting."
exit 1
fi
echo "dnf install mount-s3 failed (attempt $attempt/$max_attempts), retrying in 3 seconds..."
sleep 3
done
rm $MP_RPM
# Setup the fstab file and create the mount
mkdir --parents /mnt/${neuronxTaskDefinition.compiledModel.bucket.bucketName}
mount-s3 ${neuronxTaskDefinition.compiledModel.bucket.bucketName} /mnt/${neuronxTaskDefinition.compiledModel.bucket.bucketName} --allow-other --file-mode=644 --dir-mode=755`);
neuronxTaskDefinition.addVolume({
name: "model",
host: {
sourcePath: `/mnt/${neuronxTaskDefinition.compiledModel.bucket.bucketName}`,
},
});
const volumeSize = Math.ceil(neuronx_1.PytorchTrainingNeuronxImage.size.toGibibytes() +
neuronx_1.NeuronOptimizedMachineImage.size.toGibibytes());
const desiredCapacity = Math.ceil((props.desiredCount ?? 1) / neuronxTaskDefinition.tasksPerInstance);
const autoScalingGroup = new aws_autoscaling_1.AutoScalingGroup(this, "AutoScalingGroup", {
vpc: cluster.vpc,
instanceType: neuronxTaskDefinition.neuronxInstanceType.instanceType,
machineImage: new neuronx_1.NeuronOptimizedMachineImage(this, "NeuronOptimizedMachineImage"),
blockDevices: [
{
deviceName: "/dev/xvda",
volume: aws_autoscaling_1.BlockDeviceVolume.ebs(volumeSize, {
volumeType: aws_autoscaling_1.EbsDeviceVolumeType.GP3,
encrypted: true,
}),
},
],
userData,
desiredCapacity,
maxCapacity: neuronxTaskDefinition.tasksPerInstance === 1
? desiredCapacity + 1
: undefined,
});
autoScalingGroup.connections.allowFrom(this.loadBalancer, aws_ec2_1.Port.tcpRange(32768, 60999));
neuronxTaskDefinition.compiledModel.bucket.grantRead(autoScalingGroup);
const provider = new ecs.AsgCapacityProvider(this, "AsgCapacityProvider", {
autoScalingGroup,
});
cluster.addAsgCapacityProvider(provider);
}
}
exports.ApplicationLoadBalancedNeuronxService = ApplicationLoadBalancedNeuronxService;
_b = JSII_RTTI_SYMBOL_1;
ApplicationLoadBalancedNeuronxService[_b] = { fqn: "aws-cdk-neuronx-patterns.ApplicationLoadBalancedNeuronxService", version: "0.0.23" };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwbGljYXRpb24tbG9hZC1iYWxhbmNlZC1uZXVyb254LXNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYmFzZS9hd3MtZWNzLXBhdHRlcm5zL2FwcGxpY2F0aW9uLWxvYWQtYmFsYW5jZWQtbmV1cm9ueC1zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsNkNBQXVDO0FBQ3ZDLGlFQUlxQztBQUNyQyxpREFBcUQ7QUFDckQsMkNBQTJDO0FBQzNDLG1FQUdzQztBQUV0Qyx3Q0FJb0I7QUE4QnBCLE1BQWEscUJBQ1gsU0FBUSxHQUFHLENBQUMsaUJBQWlCO0lBUTdCLFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBaUM7UUFDekUsTUFBTSxtQkFBbUIsR0FDdkIsS0FBSyxDQUFDLG1CQUFtQixJQUFJLEtBQUssQ0FBQyxhQUFhLENBQUMsdUJBQXVCLENBQUM7UUFDM0UsTUFBTSxrQkFBa0IsR0FBRyxLQUFLLENBQUMsa0JBQWtCLElBQUksQ0FBQyxDQUFDO1FBQ3pELE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FDakMsbUJBQW1CLENBQUMsZ0JBQWdCLENBQUMsWUFBWSxHQUFHLGtCQUFrQixDQUN2RSxDQUFDO1FBQ0YsTUFBTSxvQkFBb0IsR0FDeEIsZ0JBQWdCLElBQUksQ0FBQztZQUNuQixDQUFDLENBQUM7Z0JBQ0UsR0FBRyxDQUFDLG1CQUFtQixDQUFDLFFBQVEsQ0FDOUIscUJBQXFCLGdCQUFnQixFQUFFLENBQ3hDO2FBQ0Y7WUFDSCxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ1QsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLEVBQUU7WUFDZixHQUFHLEtBQUs7WUFDUixvQkFBb0IsRUFBRTtnQkFDcEIsR0FBRyxDQUFDLEtBQUssQ0FBQyxvQkFBb0IsSUFBSSxFQUFFLENBQUM7Z0JBQ3JDLEdBQUcsb0JBQW9CO2FBQ3hCO1NBQ0YsQ0FBQyxDQUFDO1FBQ0gs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QUc7d0JBQ25DLFNBQVMsRUFBRSxJQUFJO3FCQUNoQixDQUFDO2lCQUNIO2FBQ0Y7WUFDRCxRQUFRO1lBQ1IsZUFBZTtZQUNmLFdBQVcsRUFDVCxxQkFBcUIsQ0FBQyxnQkFBZ0IsS0FBSyxDQUFDO2dCQUMxQyxDQUFDLENBQUMsZUFBZSxHQUFHLENBQUM7Z0JBQ3JCLENBQUMsQ0FBQyxTQUFTO1NBQ2hCLENBQUMsQ0FBQztRQUNILGdCQUFnQixDQUFDLFdBQVcsQ0FBQyxTQUFTLENBQ3BDLElBQUksQ0FBQyxZQUFZLEVBQ2pCLGNBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUM1QixDQUFDO1FBQ0YscUJBQXFCLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUN2RSxNQUFNLFFBQVEsR0FBRyxJQUFJLEdBQUcsQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLEVBQUUscUJBQXFCLEVBQUU7WUFDeEUsZ0JBQWdCO1NBQ2pCLENBQUMsQ0FBQztRQUNILE9BQU8sQ0FBQyxzQkFBc0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMzQyxDQUFDOztBQS9GSCxzRkFnR0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBEdXJhdGlvbiB9IGZyb20gXCJhd3MtY2RrLWxpYlwiO1xuaW1wb3J0IHtcbiAgQXV0b1NjYWxpbmdHcm91cCxcbiAgQmxvY2tEZXZpY2VWb2x1bWUsXG4gIEVic0RldmljZVZvbHVtZVR5cGUsXG59IGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtYXV0b3NjYWxpbmdcIjtcbmltcG9ydCB7IFBvcnQsIFVzZXJEYXRhIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1lYzJcIjtcbmltcG9ydCAqIGFzIGVjcyBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWVjc1wiO1xuaW1wb3J0IHtcbiAgQXBwbGljYXRpb25Mb2FkQmFsYW5jZWRFYzJTZXJ2aWNlLFxuICBBcHBsaWNhdGlvbkxvYWRCYWxhbmNlZEVjMlNlcnZpY2VQcm9wcyxcbn0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1lY3MtcGF0dGVybnNcIjtcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gXCJjb25zdHJ1Y3RzXCI7XG5pbXBvcnQge1xuICBJTmV1cm9ueEluc3RhbmNlVHlwZSxcbiAgTmV1cm9uT3B0aW1pemVkTWFjaGluZUltYWdlLFxuICBQeXRvcmNoVHJhaW5pbmdOZXVyb254SW1hZ2UsXG59IGZyb20gXCIuLi9uZXVyb254XCI7XG5pbXBvcnQgeyBOZXVyb254Q29tcGlsZWRNb2RlbCB9IGZyb20gXCIuLi9uZXVyb254LWNvbXBpbGVyXCI7XG5cbmV4cG9ydCBpbnRlcmZhY2UgTmV1cm9ueFRhc2tEZWZpbml0aW9uUHJvcHNCYXNlXG4gIGV4dGVuZHMgZWNzLkVjMlRhc2tEZWZpbml0aW9uUHJvcHMge1xuICAvKipcbiAgICogVGhlIGluc3RhbmNlIHR5cGUgb2YgY29tcGlsZSB3b3JrZXIgaW5zdGFuY2UuXG4gICAqL1xuICByZWFkb25seSBuZXVyb254SW5zdGFuY2VUeXBlPzogSU5ldXJvbnhJbnN0YW5jZVR5cGU7XG4gIC8qKlxuICAgKiBUaGUgbnVtYmVyIG9mIHRlbnNvciBwYXJhbGxlbCBzaXplLlxuICAgKiBAZGVmYXVsdCAtIDFcbiAgICovXG4gIHJlYWRvbmx5IHRlbnNvclBhcmFsbGVsU2l6ZT86IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOZXVyb254VGFza0RlZmluaXRpb25Qcm9wc1xuICBleHRlbmRzIE5ldXJvbnhUYXNrRGVmaW5pdGlvblByb3BzQmFzZSB7XG4gIC8qKlxuICAgKiBUaGUgbW9kZWwgdG8gYmUgY29tcGlsZWQuXG4gICAqL1xuICByZWFkb25seSBjb21waWxlZE1vZGVsOiBOZXVyb254Q29tcGlsZWRNb2RlbDtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBJTmV1cm9ueFRhc2tEZWZpbml0aW9uIGV4dGVuZHMgZWNzLklFYzJUYXNrRGVmaW5pdGlvbiB7XG4gIHJlYWRvbmx5IG5ldXJvbnhJbnN0YW5jZVR5cGU6IElOZXVyb254SW5zdGFuY2VUeXBlO1xuICByZWFkb25seSB0ZW5zb3JQYXJhbGxlbFNpemU6IG51bWJlcjtcbiAgcmVhZG9ubHkgdGFza3NQZXJJbnN0YW5jZTogbnVtYmVyO1xuICByZWFkb25seSBjb21waWxlZE1vZGVsOiBOZXVyb254Q29tcGlsZWRNb2RlbDtcbn1cbmV4cG9ydCBjbGFzcyBOZXVyb254VGFza0RlZmluaXRpb25cbiAgZXh0ZW5kcyBlY3MuRWMyVGFza0RlZmluaXRpb25cbiAgaW1wbGVtZW50cyBJTmV1cm9ueFRhc2tEZWZpbml0aW9uXG57XG4gIHJlYWRvbmx5IG5ldXJvbnhJbnN0YW5jZVR5cGU6IElOZXVyb254SW5zdGFuY2VUeXBlO1xuICByZWFkb25seSB0ZW5zb3JQYXJhbGxlbFNpemU6IG51bWJlcjtcbiAgcmVhZG9ubHkgdGFza3NQZXJJbnN0YW5jZTogbnVtYmVyO1xuICByZWFkb25seSBjb21waWxlZE1vZGVsOiBOZXVyb254Q29tcGlsZWRNb2RlbDtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGxpbnV4UGFyYW1ldGVyczogZWNzLkxpbnV4UGFyYW1ldGVycztcbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IE5ldXJvbnhUYXNrRGVmaW5pdGlvblByb3BzKSB7XG4gICAgY29uc3QgbmV1cm9ueEluc3RhbmNlVHlwZSA9XG4gICAgICBwcm9wcy5uZXVyb254SW5zdGFuY2VUeXBlID8/IHByb3BzLmNvbXBpbGVkTW9kZWwuY29tcGlsZVRpbWVJbnN0YW5jZVR5cGU7XG4gICAgY29uc3QgdGVuc29yUGFyYWxsZWxTaXplID0gcHJvcHMudGVuc29yUGFyYWxsZWxTaXplID8/IDE7XG4gICAgY29uc3QgdGFza3NQZXJJbnN0YW5jZSA9IE1hdGguZmxvb3IoXG4gICAgICBuZXVyb254SW5zdGFuY2VUeXBlLmFjY2VsZXJhdG9yQ2hpcHMubmV1cm9ueENvcmVzIC8gdGVuc29yUGFyYWxsZWxTaXplLFxuICAgICk7XG4gICAgY29uc3QgcGxhY2VtZW50Q29uc3RyYWludHMgPVxuICAgICAgdGFza3NQZXJJbnN0YW5jZSA+PSAyXG4gICAgICAgID8gW1xuICAgICAgICAgICAgZWNzLlBsYWNlbWVudENvbnN0cmFpbnQubWVtYmVyT2YoXG4gICAgICAgICAgICAgIGBydW5uaW5nVGFza3NDb3VudDwke3Rhc2tzUGVySW5zdGFuY2V9YCxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgXVxuICAgICAgICA6IFtdO1xuICAgIHN1cGVyKHNjb3BlLCBpZCwge1xuICAgICAgLi4ucHJvcHMsXG4gICAgICBwbGFjZW1lbnRDb25zdHJhaW50czogW1xuICAgICAgICAuLi4ocHJvcHMucGxhY2VtZW50Q29uc3RyYWludHMgPz8gW10pLFxuICAgICAgICAuLi5wbGFjZW1lbnRDb25zdHJhaW50cyxcbiAgICAgIF0sXG4gICAgfSk7XG4gICAgdGhpcy5saW51eFBhcmFtZXRlcnMgPSBuZXcgZWNzLkxpbnV4UGFyYW1ldGVycyh0aGlzLCBcIkxpbnV4UGFyYW1ldGVyc1wiKTtcbiAgICB0aGlzLmxpbnV4UGFyYW1ldGVycy5hZGREZXZpY2VzKFxuICAgICAgLi4uQXJyYXkuZnJvbSh7XG4gICAgICAgIGxlbmd0aDogbmV1cm9ueEluc3RhbmNlVHlwZT8uYWNjZWxlcmF0b3JDaGlwcy5jaGlwcyA/PyAxLFxuICAgICAgfSkubWFwPGVjcy5EZXZpY2U+KChfLCBpbmRleCkgPT4gKHtcbiAgICAgICAgaG9zdFBhdGg6IGAvZGV2L25ldXJvbiR7aW5kZXh9YCxcbiAgICAgICAgY29udGFpbmVyUGF0aDogYC9kZXYvbmV1cm9uJHtpbmRleH1gLFxuICAgICAgICBwZXJtaXNzaW9uczogW2Vjcy5EZXZpY2VQZXJtaXNzaW9uLlJFQUQsIGVjcy5EZXZpY2VQZXJtaXNzaW9uLldSSVRFXSxcbiAgICAgIH0pKSxcbiAgICApO1xuICAgIGlmICh0aGlzLmV4ZWN1dGlvblJvbGUpIHtcbiAgICAgIHByb3BzLmNvbXBpbGVkTW9kZWwuYnVja2V0LmdyYW50UmVhZCh0aGlzLmV4ZWN1dGlvblJvbGUpO1xuICAgIH1cbiAgICBwcm9wcy5jb21waWxlZE1vZGVsLmJ1Y2tldC5ncmFudFJlYWQodGhpcy50YXNrUm9sZSk7XG4gICAgdGhpcy5jb21waWxlZE1vZGVsID0gcHJvcHMuY29tcGlsZWRNb2RlbDtcbiAgICB0aGlzLm5ldXJvbnhJbnN0YW5jZVR5cGUgPSBuZXVyb254SW5zdGFuY2VUeXBlO1xuICAgIHRoaXMudGVuc29yUGFyYWxsZWxTaXplID0gdGVuc29yUGFyYWxsZWxTaXplO1xuICAgIHRoaXMudGFza3NQZXJJbnN0YW5jZSA9IHRhc2tzUGVySW5zdGFuY2U7XG4gIH1cblxuICBhZGRDb250YWluZXJXaXRoRGVmYXVsdChcbiAgICBpZDogc3RyaW5nLFxuICAgIHByb3BzOiBlY3MuQ29udGFpbmVyRGVmaW5pdGlvbk9wdGlvbnMsXG4gICk6IGVjcy5Db250YWluZXJEZWZpbml0aW9uIHtcbiAgICBjb25zdCBjb250YWluZXIgPSBzdXBlci5hZGRDb250YWluZXIoaWQsIHtcbiAgICAgIGxvZ2dpbmc6IGVjcy5Mb2dEcml2ZXIuYXdzTG9ncyh7XG4gICAgICAgIHN0cmVhbVByZWZpeDogaWQsXG4gICAgICB9KSxcbiAgICAgIGxpbnV4UGFyYW1ldGVyczogdGhpcy5saW51eFBhcmFtZXRlcnMsXG4gICAgICBtZW1vcnlSZXNlcnZhdGlvbk1pQjogTWF0aC5jZWlsKFxuICAgICAgICAodGhpcy5uZXVyb254SW5zdGFuY2VUeXBlLm1lbW9yeS50b01lYmlieXRlcygpICogMC44KSAvXG4gICAgICAgICAgdGhpcy50YXNrc1Blckluc3RhbmNlLFxuICAgICAgKSxcbiAgICAgIC4uLnByb3BzLFxuICAgIH0pO1xuICAgIGNvbnRhaW5lci5hZGRNb3VudFBvaW50cyh7XG4gICAgICBzb3VyY2VWb2x1bWU6IFwibW9kZWxcIixcbiAgICAgIGNvbnRhaW5lclBhdGg6IFwiL29wdC9tbC9tb2RlbFwiLFxuICAgICAgcmVhZE9ubHk6IGZhbHNlLFxuICAgIH0pO1xuICAgIHJldHVybiBjb250YWluZXI7XG4gIH1cbn1cblxuZXhwb3J0IGludGVyZmFjZSBBcHBsaWNhdGlvbkxvYWRCYWxhbmNlZE5ldXJvbnhTZXJ2aWNlUHJvcHNcbiAgZXh0ZW5kcyBBcHBsaWNhdGlvbkxvYWRCYWxhbmNlZEVjMlNlcnZpY2VQcm9wcyB7fVxuZXhwb3J0IGNsYXNzIEFwcGxpY2F0aW9uTG9hZEJhbGFuY2VkTmV1cm9ueFNlcnZpY2UgZXh0ZW5kcyBBcHBsaWNhdGlvbkxvYWRCYWxhbmNlZEVjMlNlcnZpY2Uge1xuICBjb25zdHJ1Y3RvcihcbiAgICBzY29wZTogQ29uc3RydWN0LFxuICAgIGlkOiBzdHJpbmcsXG4gICAgcHJvcHM6IEFwcGxpY2F0aW9uTG9hZEJhbGFuY2VkTmV1cm9ueFNlcnZpY2VQcm9wcyxcbiAgKSB7XG4gICAgY29uc3QgbmV1cm9ueFRhc2tEZWZpbml0aW9uID0gcHJvcHMudGFza0RlZmluaXRpb247XG4gICAgaWYgKFxuICAgICAgIW5ldXJvbnhUYXNrRGVmaW5pdGlvbiB8fFxuICAgICAgIShuZXVyb254VGFza0RlZmluaXRpb24gaW5zdGFuY2VvZiBOZXVyb254VGFza0RlZmluaXRpb24pXG4gICAgKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJ0YXNrRGVmaW5pdGlvbiBtdXN0IGV4dGVuZCBOZXVyb254VGFza0RlZmluaXRpb25cIik7XG4gICAgfVxuICAgIHN1cGVyKHNjb3BlLCBpZCwge1xuICAgICAgaGVhbHRoQ2hlY2tHcmFjZVBlcmlvZDpcbiAgICAgICAgcHJvcHMuaGVhbHRoQ2hlY2tHcmFjZVBlcmlvZCA/PyBEdXJhdGlvbi5taW51dGVzKDE1KSxcbiAgICAgIC4uLnByb3BzLFxuICAgIH0pO1xuXG4gICAgY29uc3QgY2x1c3RlciA9IHRoaXMuZ2V0RGVmYXVsdENsdXN0ZXIodGhpcywgcHJvcHMudnBjKTtcblxuICAgIC8vIGluc3RhbGwgYW5kIG1vdW50IG1vdW50cG9pbnQgZm9yIHMzXG4gICAgY29uc3QgdXNlckRhdGEgPSBVc2VyRGF0YS5mb3JMaW51eCgpO1xuICAgIHVzZXJEYXRhLmFkZENvbW1hbmRzKGBcbiMhL2Jpbi9iYXNoIC1lXG5cbiMgSW5zdGFsbCBNb3VudHBvaW50XG5NUF9SUE09JChta3RlbXAgLS1zdWZmaXg9LnJwbSlcbmN1cmwgaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL21vdW50cG9pbnQtczMtcmVsZWFzZS9sYXRlc3QveDg2XzY0L21vdW50LXMzLnJwbSA+ICRNUF9SUE1cblxuIyBjbG91ZC1pbml0IGluc3RhbGxzIGNvbmZsaWN0IHdpdGggU1NNIGFnZW50OiBodHRwczovL2dpdGh1Yi5jb20vYW1hem9ubGludXgvYW1hem9uLWxpbnV4LTIwMjMvaXNzdWVzLzM5N1xuYXR0ZW1wdD0wXG5tYXhfYXR0ZW1wdHM9NVxudW50aWwgZG5mIGluc3RhbGwgLXkgJE1QX1JQTTsgZG9cbiAgICBhdHRlbXB0PSQoKGF0dGVtcHQgKyAxKSlcbiAgICBpZiBbICRhdHRlbXB0IC1nZSAkbWF4X2F0dGVtcHRzIF07IHRoZW5cbiAgICAgICAgZWNobyBcIkZhaWxlZCB0byBpbnN0YWxsIG1vdW50LXMzIGFmdGVyICRtYXhfYXR0ZW1wdHMgYXR0ZW1wdHMuIEV4aXRpbmcuXCJcbiAgICAgICAgZXhpdCAxXG4gICAgZmlcbiAgICBlY2hvIFwiZG5mIGluc3RhbGwgbW91bnQtczMgZmFpbGVkIChhdHRlbXB0ICRhdHRlbXB0LyRtYXhfYXR0ZW1wdHMpLCByZXRyeWluZyBpbiAzIHNlY29uZHMuLi5cIlxuICAgIHNsZWVwIDNcbmRvbmVcblxucm0gJE1QX1JQTVxuXG4jIFNldHVwIHRoZSBmc3RhYiBmaWxlIGFuZCBjcmVhdGUgdGhlIG1vdW50XG5ta2RpciAtLXBhcmVudHMgL21udC8ke25ldXJvbnhUYXNrRGVmaW5pdGlvbi5jb21waWxlZE1vZGVsLmJ1Y2tldC5idWNrZXROYW1lfVxubW91bnQtczMgJHtuZXVyb254VGFza0RlZmluaXRpb24uY29tcGlsZWRNb2RlbC5idWNrZXQuYnVja2V0TmFtZX0gL21udC8ke25ldXJvbnhUYXNrRGVmaW5pdGlvbi5jb21waWxlZE1vZGVsLmJ1Y2tldC5idWNrZXROYW1lfSAtLWFsbG93LW90aGVyIC0tZmlsZS1tb2RlPTY0NCAtLWRpci1tb2RlPTc1NWApO1xuXG4gICAgbmV1cm9ueFRhc2tEZWZpbml0aW9uLmFkZFZvbHVtZSh7XG4gICAgICBuYW1lOiBcIm1vZGVsXCIsXG4gICAgICBob3N0OiB7XG4gICAgICAgIHNvdXJjZVBhdGg6IGAvbW50LyR7bmV1cm9ueFRhc2tEZWZpbml0aW9uLmNvbXBpbGVkTW9kZWwuYnVja2V0LmJ1Y2tldE5hbWV9YCxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zdCB2b2x1bWVTaXplID0gTWF0aC5jZWlsKFxuICAgICAgUHl0b3JjaFRyYWluaW5nTmV1cm9ueEltYWdlLnNpemUudG9HaWJpYnl0ZXMoKSArXG4gICAgICAgIE5ldXJvbk9wdGltaXplZE1hY2hpbmVJbWFnZS5zaXplLnRvR2liaWJ5dGVzKCksXG4gICAgKTtcbiAgICBjb25zdCBkZXNpcmVkQ2FwYWNpdHkgPSBNYXRoLmNlaWwoXG4gICAgICAocHJvcHMuZGVzaXJlZENvdW50ID8/IDEpIC8gbmV1cm9ueFRhc2tEZWZpbml0aW9uLnRhc2tzUGVySW5zdGFuY2UsXG4gICAgKTtcbiAgICBjb25zdCBhdXRvU2NhbGluZ0dyb3VwID0gbmV3IEF1dG9TY2FsaW5nR3JvdXAodGhpcywgXCJBdXRvU2NhbGluZ0dyb3VwXCIsIHtcbiAgICAgIHZwYzogY2x1c3Rlci52cGMsXG4gICAgICBpbnN0YW5jZVR5cGU6IG5ldXJvbnhUYXNrRGVmaW5pdGlvbi5uZXVyb254SW5zdGFuY2VUeXBlLmluc3RhbmNlVHlwZSxcbiAgICAgIG1hY2hpbmVJbWFnZTogbmV3IE5ldXJvbk9wdGltaXplZE1hY2hpbmVJbWFnZShcbiAgICAgICAgdGhpcyxcbiAgICAgICAgXCJOZXVyb25PcHRpbWl6ZWRNYWNoaW5lSW1hZ2VcIixcbiAgICAgICksXG4gICAgICBibG9ja0RldmljZXM6IFtcbiAgICAgICAge1xuICAgICAgICAgIGRldmljZU5hbWU6IFwiL2Rldi94dmRhXCIsXG4gICAgICAgICAgdm9sdW1lOiBCbG9ja0RldmljZVZvbHVtZS5lYnModm9sdW1lU2l6ZSwge1xuICAgICAgICAgICAgdm9sdW1lVHlwZTogRWJzRGV2aWNlVm9sdW1lVHlwZS5HUDMsXG4gICAgICAgICAgICBlbmNyeXB0ZWQ6IHRydWUsXG4gICAgICAgICAgfSksXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgICAgdXNlckRhdGEsXG4gICAgICBkZXNpcmVkQ2FwYWNpdHksXG4gICAgICBtYXhDYXBhY2l0eTpcbiAgICAgICAgbmV1cm9ueFRhc2tEZWZpbml0aW9uLnRhc2tzUGVySW5zdGFuY2UgPT09IDFcbiAgICAgICAgICA/IGRlc2lyZWRDYXBhY2l0eSArIDFcbiAgICAgICAgICA6IHVuZGVmaW5lZCxcbiAgICB9KTtcbiAgICBhdXRvU2NhbGluZ0dyb3VwLmNvbm5lY3Rpb25zLmFsbG93RnJvbShcbiAgICAgIHRoaXMubG9hZEJhbGFuY2VyLFxuICAgICAgUG9ydC50Y3BSYW5nZSgzMjc2OCwgNjA5OTkpLFxuICAgICk7XG4gICAgbmV1cm9ueFRhc2tEZWZpbml0aW9uLmNvbXBpbGVkTW9kZWwuYnVja2V0LmdyYW50UmVhZChhdXRvU2NhbGluZ0dyb3VwKTtcbiAgICBjb25zdCBwcm92aWRlciA9IG5ldyBlY3MuQXNnQ2FwYWNpdHlQcm92aWRlcih0aGlzLCBcIkFzZ0NhcGFjaXR5UHJvdmlkZXJcIiwge1xuICAgICAgYXV0b1NjYWxpbmdHcm91cCxcbiAgICB9KTtcbiAgICBjbHVzdGVyLmFkZEFzZ0NhcGFjaXR5UHJvdmlkZXIocHJvdmlkZXIpO1xuICB9XG59XG4iXX0=