UNPKG

aws-cdk-neuronx-patterns

Version:
145 lines (141 loc) 22.1 kB
"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=