@aws-cdk/aws-msk-alpha
Version:
The CDK Construct Library for AWS::MSK
568 lines • 78.9 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var _a, _b, _c;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Cluster = exports.ClientAuthentication = exports.ClientBrokerEncryption = exports.ClusterMonitoringLevel = exports.StorageMode = exports.ClusterBase = void 0;
const jsiiDeprecationWarnings = require("../.warnings.jsii.js");
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const ec2 = require("aws-cdk-lib/aws-ec2");
const iam = require("aws-cdk-lib/aws-iam");
const kms = require("aws-cdk-lib/aws-kms");
const secretsmanager = require("aws-cdk-lib/aws-secretsmanager");
const core = require("aws-cdk-lib/core");
const core_1 = require("aws-cdk-lib/core");
const cr = require("aws-cdk-lib/custom-resources");
const cx_api_1 = require("aws-cdk-lib/cx-api");
const uniqueid_1 = require("constructs/lib/private/uniqueid");
const aws_msk_1 = require("aws-cdk-lib/aws-msk");
const metadata_resource_1 = require("aws-cdk-lib/core/lib/metadata-resource");
const prop_injectable_1 = require("aws-cdk-lib/core/lib/prop-injectable");
/**
* A new or imported MSK Cluster.
*/
class ClusterBase extends core.Resource {
/** Manages connections for the cluster */
get connections() {
if (!this._connections) {
throw new core.ValidationError('An imported Cluster cannot manage its security groups', this);
}
return this._connections;
}
}
exports.ClusterBase = ClusterBase;
_a = JSII_RTTI_SYMBOL_1;
ClusterBase[_a] = { fqn: "@aws-cdk/aws-msk-alpha.ClusterBase", version: "2.211.0-alpha.0" };
/**
* The storage mode for the cluster brokers.
*/
var StorageMode;
(function (StorageMode) {
/**
* Local storage mode utilizes network attached EBS storage.
*/
StorageMode["LOCAL"] = "LOCAL";
/**
* Tiered storage mode utilizes EBS storage and Tiered storage.
*/
StorageMode["TIERED"] = "TIERED";
})(StorageMode || (exports.StorageMode = StorageMode = {}));
/**
* The level of monitoring for the MSK cluster
*
* @see https://docs.aws.amazon.com/msk/latest/developerguide/monitoring.html#metrics-details
*/
var ClusterMonitoringLevel;
(function (ClusterMonitoringLevel) {
/**
* Default metrics are the essential metrics to monitor.
*/
ClusterMonitoringLevel["DEFAULT"] = "DEFAULT";
/**
* Per Broker metrics give you metrics at the broker level.
*/
ClusterMonitoringLevel["PER_BROKER"] = "PER_BROKER";
/**
* Per Topic Per Broker metrics help you understand volume at the topic level.
*/
ClusterMonitoringLevel["PER_TOPIC_PER_BROKER"] = "PER_TOPIC_PER_BROKER";
/**
* Per Topic Per Partition metrics help you understand consumer group lag at the topic partition level.
*/
ClusterMonitoringLevel["PER_TOPIC_PER_PARTITION"] = "PER_TOPIC_PER_PARTITION";
})(ClusterMonitoringLevel || (exports.ClusterMonitoringLevel = ClusterMonitoringLevel = {}));
/**
* Indicates the encryption setting for data in transit between clients and brokers.
*/
var ClientBrokerEncryption;
(function (ClientBrokerEncryption) {
/**
* TLS means that client-broker communication is enabled with TLS only.
*/
ClientBrokerEncryption["TLS"] = "TLS";
/**
* TLS_PLAINTEXT means that client-broker communication is enabled for both TLS-encrypted, as well as plaintext data.
*/
ClientBrokerEncryption["TLS_PLAINTEXT"] = "TLS_PLAINTEXT";
/**
* PLAINTEXT means that client-broker communication is enabled in plaintext only.
*/
ClientBrokerEncryption["PLAINTEXT"] = "PLAINTEXT";
})(ClientBrokerEncryption || (exports.ClientBrokerEncryption = ClientBrokerEncryption = {}));
/**
* Configuration properties for client authentication.
*/
class ClientAuthentication {
/**
* SASL authentication
*/
static sasl(props) {
try {
jsiiDeprecationWarnings._aws_cdk_aws_msk_alpha_SaslAuthProps(props);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.sasl);
}
throw error;
}
return new ClientAuthentication(props, undefined);
}
/**
* TLS authentication
*/
static tls(props) {
try {
jsiiDeprecationWarnings._aws_cdk_aws_msk_alpha_TlsAuthProps(props);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.tls);
}
throw error;
}
return new ClientAuthentication(undefined, props);
}
/**
* SASL + TLS authentication
*/
static saslTls(saslTlsProps) {
try {
jsiiDeprecationWarnings._aws_cdk_aws_msk_alpha_SaslTlsAuthProps(saslTlsProps);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.saslTls);
}
throw error;
}
return new ClientAuthentication(saslTlsProps, saslTlsProps);
}
/**
* @param saslProps - properties for SASL authentication
* @param tlsProps - properties for TLS authentication
*/
constructor(saslProps, tlsProps) {
this.saslProps = saslProps;
this.tlsProps = tlsProps;
}
}
exports.ClientAuthentication = ClientAuthentication;
_b = JSII_RTTI_SYMBOL_1;
ClientAuthentication[_b] = { fqn: "@aws-cdk/aws-msk-alpha.ClientAuthentication", version: "2.211.0-alpha.0" };
/**
* Create a MSK Cluster.
*
* @resource AWS::MSK::Cluster
*/
let Cluster = class Cluster extends ClusterBase {
/**
* Reference an existing cluster, defined outside of the CDK code, by name.
*/
static fromClusterArn(scope, id, clusterArn) {
class Import extends ClusterBase {
constructor() {
super(...arguments);
this.clusterArn = clusterArn;
this.clusterName = core.Fn.select(1, core.Fn.split('/', clusterArn)); // ['arn:partition:kafka:region:account-id', clusterName, clusterId]
}
}
return new Import(scope, id);
}
constructor(scope, id, props) {
super(scope, id, { physicalName: props.clusterName });
try {
jsiiDeprecationWarnings._aws_cdk_aws_msk_alpha_ClusterProps(props);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, Cluster);
}
throw error;
}
// Enhanced CDK Analytics Telemetry
(0, metadata_resource_1.addConstructMetadata)(this, props);
const subnetSelection = props.vpc.selectSubnets(props.vpcSubnets);
this._connections = new ec2.Connections({
securityGroups: props.securityGroups ?? [
new ec2.SecurityGroup(this, 'SecurityGroup', {
description: 'MSK security group',
vpc: props.vpc,
}),
],
});
if (subnetSelection.subnets.length < 2) {
throw new core.ValidationError(`Cluster requires at least 2 subnets, got ${subnetSelection.subnets.length}`, this);
}
if (props.encryptionInTransit?.clientBroker === ClientBrokerEncryption.PLAINTEXT && props.clientAuthentication) {
throw new core.ValidationError('To enable client authentication, you must enabled TLS-encrypted traffic between clients and brokers.', this);
}
else if (props.encryptionInTransit?.clientBroker ===
ClientBrokerEncryption.TLS_PLAINTEXT &&
(props.clientAuthentication?.saslProps?.scram ||
props.clientAuthentication?.saslProps?.iam)) {
throw new core.ValidationError('To enable SASL/SCRAM or IAM authentication, you must only allow TLS-encrypted traffic between clients and brokers.', this);
}
const volumeSize = props.ebsStorageInfo?.volumeSize ?? 1000;
// Minimum: 1 GiB, maximum: 16384 GiB
if (volumeSize < 1 || volumeSize > 16384) {
throw new core.ValidationError('EBS volume size should be in the range 1-16384', this);
}
const instanceType = props.instanceType
? this.mskInstanceType(props.instanceType)
: this.mskInstanceType(ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.LARGE));
if (props.storageMode && props.storageMode === StorageMode.TIERED) {
if (!props.kafkaVersion.isTieredStorageCompatible()) {
throw new core.ValidationError(`To deploy a tiered cluster you must select a compatible Kafka version, got ${props.kafkaVersion.version}`, this);
}
if (instanceType === this.mskInstanceType(ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.SMALL))) {
throw new core.ValidationError('Tiered storage doesn\'t support broker type t3.small', this);
}
}
const encryptionAtRest = props.ebsStorageInfo?.encryptionKey
? {
dataVolumeKmsKeyId: props.ebsStorageInfo.encryptionKey.keyId,
}
: undefined; // MSK will create the managed key
const encryptionInTransit = {
clientBroker: props.encryptionInTransit?.clientBroker ??
ClientBrokerEncryption.TLS,
inCluster: props.encryptionInTransit?.enableInCluster ?? true,
};
const openMonitoring = props.monitoring?.enablePrometheusJmxExporter ||
props.monitoring?.enablePrometheusNodeExporter
? {
prometheus: {
jmxExporter: props.monitoring?.enablePrometheusJmxExporter
? { enabledInBroker: true }
: undefined,
nodeExporter: props.monitoring
?.enablePrometheusNodeExporter
? { enabledInBroker: true }
: undefined,
},
}
: undefined;
const loggingBucket = props.logging?.s3?.bucket;
if (loggingBucket && core_1.FeatureFlags.of(this).isEnabled(cx_api_1.S3_CREATE_DEFAULT_LOGGING_POLICY)) {
const stack = core.Stack.of(this);
loggingBucket.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [
new iam.ServicePrincipal('delivery.logs.amazonaws.com'),
],
resources: [
loggingBucket.arnForObjects(`AWSLogs/${stack.account}/*`),
],
actions: ['s3:PutObject'],
conditions: {
StringEquals: {
's3:x-amz-acl': 'bucket-owner-full-control',
'aws:SourceAccount': stack.account,
},
ArnLike: {
'aws:SourceArn': stack.formatArn({
service: 'logs',
resource: '*',
}),
},
},
}));
loggingBucket.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [
new iam.ServicePrincipal('delivery.logs.amazonaws.com'),
],
resources: [loggingBucket.bucketArn],
actions: [
's3:GetBucketAcl',
's3:ListBucket',
],
conditions: {
StringEquals: {
'aws:SourceAccount': stack.account,
},
ArnLike: {
'aws:SourceArn': stack.formatArn({
service: 'logs',
resource: '*',
}),
},
},
}));
}
const loggingInfo = {
brokerLogs: {
cloudWatchLogs: {
enabled: props.logging?.cloudwatchLogGroup !== undefined,
logGroup: props.logging?.cloudwatchLogGroup?.logGroupName,
},
firehose: {
enabled: props.logging?.firehoseDeliveryStreamName !==
undefined,
deliveryStream: props.logging?.firehoseDeliveryStreamName,
},
s3: {
enabled: loggingBucket !== undefined,
bucket: loggingBucket?.bucketName,
prefix: props.logging?.s3?.prefix,
},
},
};
if (props.clientAuthentication?.saslProps?.scram && props.clientAuthentication?.saslProps?.key === undefined) {
this.saslScramAuthenticationKey = new kms.Key(this, 'SASLKey', {
description: 'Used for encrypting MSK secrets for SASL/SCRAM authentication.',
alias: `msk/${props.clusterName}/sasl/scram`,
});
// https://docs.aws.amazon.com/kms/latest/developerguide/services-secrets-manager.html#asm-policies
this.saslScramAuthenticationKey.addToResourcePolicy(new iam.PolicyStatement({
sid: 'Allow access through AWS Secrets Manager for all principals in the account that are authorized to use AWS Secrets Manager',
principals: [new iam.AnyPrincipal()],
actions: [
'kms:Encrypt',
'kms:Decrypt',
'kms:ReEncrypt*',
'kms:GenerateDataKey*',
'kms:CreateGrant',
'kms:DescribeKey',
],
resources: ['*'],
conditions: {
StringEquals: {
'kms:ViaService': `secretsmanager.${core.Stack.of(this).region}.amazonaws.com`,
'kms:CallerAccount': core.Stack.of(this).account,
},
},
}));
}
let clientAuthentication;
if (props.clientAuthentication) {
const { saslProps, tlsProps } = props.clientAuthentication;
clientAuthentication = {
sasl: saslProps ? {
iam: saslProps.iam ? { enabled: true } : undefined,
scram: saslProps.scram ? { enabled: true } : undefined,
} : undefined,
tls: tlsProps?.certificateAuthorities ? {
certificateAuthorityArnList: tlsProps.certificateAuthorities?.map((ca) => ca.certificateAuthorityArn),
} : undefined,
};
}
const resource = new aws_msk_1.CfnCluster(this, 'Resource', {
clusterName: props.clusterName,
kafkaVersion: props.kafkaVersion.version,
numberOfBrokerNodes: props.numberOfBrokerNodes !== undefined ?
subnetSelection.availabilityZones.length * props.numberOfBrokerNodes : subnetSelection.availabilityZones.length,
brokerNodeGroupInfo: {
instanceType,
clientSubnets: subnetSelection.subnetIds,
securityGroups: this.connections.securityGroups.map((group) => group.securityGroupId),
storageInfo: {
ebsStorageInfo: {
volumeSize: volumeSize,
},
},
},
encryptionInfo: {
encryptionAtRest,
encryptionInTransit,
},
configurationInfo: props.configurationInfo,
enhancedMonitoring: props.monitoring?.clusterMonitoringLevel,
openMonitoring: openMonitoring,
storageMode: props.storageMode,
loggingInfo: loggingInfo,
clientAuthentication,
});
this.clusterName = this.getResourceNameAttribute(core.Fn.select(1, core.Fn.split('/', resource.ref)));
this.clusterArn = resource.ref;
resource.applyRemovalPolicy(props.removalPolicy, {
default: core.RemovalPolicy.RETAIN,
});
}
mskInstanceType(instanceType) {
return `kafka.${instanceType.toString()}`;
}
/**
* Get the ZooKeeper Connection string
*
* Uses a Custom Resource to make an API call to `describeCluster` using the Javascript SDK
*
* @param responseField Field to return from API call. eg. ZookeeperConnectString, ZookeeperConnectStringTls
* @returns - The connection string to use to connect to the Apache ZooKeeper cluster.
*/
_zookeeperConnectionString(responseField) {
if (!this._clusterDescription) {
this._clusterDescription = new cr.AwsCustomResource(this, 'ZookeeperConnect', {
onUpdate: {
service: 'Kafka',
action: 'describeCluster',
parameters: {
ClusterArn: this.clusterArn,
},
physicalResourceId: cr.PhysicalResourceId.of('ZooKeeperConnectionString'),
// Limit the output of describeCluster that is otherwise too large
outputPaths: [
'ClusterInfo.ZookeeperConnectString',
'ClusterInfo.ZookeeperConnectStringTls',
],
},
policy: cr.AwsCustomResourcePolicy.fromSdkCalls({
resources: [this.clusterArn],
}),
installLatestAwsSdk: false,
});
}
return this._clusterDescription.getResponseField(`ClusterInfo.${responseField}`);
}
/**
* Get the ZooKeeper Connection string
*
* Uses a Custom Resource to make an API call to `describeCluster` using the Javascript SDK
*
* @returns - The connection string to use to connect to the Apache ZooKeeper cluster.
*/
get zookeeperConnectionString() {
return this._zookeeperConnectionString('ZookeeperConnectString');
}
/**
* Get the ZooKeeper Connection string for a TLS enabled cluster
*
* Uses a Custom Resource to make an API call to `describeCluster` using the Javascript SDK
*
* @returns - The connection string to use to connect to zookeeper cluster on TLS port.
*/
get zookeeperConnectionStringTls() {
return this._zookeeperConnectionString('ZookeeperConnectStringTls');
}
/**
* Get the list of brokers that a client application can use to bootstrap
*
* Uses a Custom Resource to make an API call to `getBootstrapBrokers` using the Javascript SDK
*
* @param responseField Field to return from API call. eg. BootstrapBrokerStringSaslScram, BootstrapBrokerString
* @returns - A string containing one or more hostname:port pairs.
*/
_bootstrapBrokers(responseField) {
if (!this._clusterBootstrapBrokers) {
this._clusterBootstrapBrokers = new cr.AwsCustomResource(this, `BootstrapBrokers${responseField}`, {
onUpdate: {
service: 'Kafka',
action: 'getBootstrapBrokers',
parameters: {
ClusterArn: this.clusterArn,
},
physicalResourceId: cr.PhysicalResourceId.of('BootstrapBrokers'),
},
policy: cr.AwsCustomResourcePolicy.fromSdkCalls({
resources: [this.clusterArn],
}),
// APIs are available in 2.1055.0
installLatestAwsSdk: false,
});
}
return this._clusterBootstrapBrokers.getResponseField(responseField);
}
/**
* Get the list of brokers that a client application can use to bootstrap
*
* Uses a Custom Resource to make an API call to `getBootstrapBrokers` using the Javascript SDK
*
* @returns - A string containing one or more hostname:port pairs.
*/
get bootstrapBrokers() {
return this._bootstrapBrokers('BootstrapBrokerString');
}
/**
* Get the list of brokers that a TLS authenticated client application can use to bootstrap
*
* Uses a Custom Resource to make an API call to `getBootstrapBrokers` using the Javascript SDK
*
* @returns - A string containing one or more DNS names (or IP) and TLS port pairs.
*/
get bootstrapBrokersTls() {
return this._bootstrapBrokers('BootstrapBrokerStringTls');
}
/**
* Get the list of brokers that a SASL/SCRAM authenticated client application can use to bootstrap
*
* Uses a Custom Resource to make an API call to `getBootstrapBrokers` using the Javascript SDK
*
* @returns - A string containing one or more dns name (or IP) and SASL SCRAM port pairs.
*/
get bootstrapBrokersSaslScram() {
return this._bootstrapBrokers('BootstrapBrokerStringSaslScram');
}
/**
* Get the list of brokers that a SASL/IAM authenticated client application can use to bootstrap
*
* Uses a Custom Resource to make an API call to `getBootstrapBrokers` using the Javascript SDK
*
* @returns - A string containing one or more DNS names (or IP) and TLS port pairs.
*/
get bootstrapBrokersSaslIam() {
return this._bootstrapBrokers('BootstrapBrokerStringSaslIam');
}
/**
* A list of usersnames to register with the cluster. The password will automatically be generated using Secrets
* Manager and the { username, password } JSON object stored in Secrets Manager as `AmazonMSK_username`.
*
* Must be using the SASL/SCRAM authentication mechanism.
*
* @param usernames - username(s) to register with the cluster
*/
addUser(...usernames) {
if (this.saslScramAuthenticationKey) {
const MSK_SECRET_PREFIX = 'AmazonMSK_'; // Required
const secrets = usernames.map((username) => new secretsmanager.Secret(this, `KafkaUser${username}`, {
secretName: `${MSK_SECRET_PREFIX}${this.clusterName}_${username}`,
generateSecretString: {
secretStringTemplate: JSON.stringify({ username }),
generateStringKey: 'password',
},
encryptionKey: this.saslScramAuthenticationKey,
}));
new cr.AwsCustomResource(this, `BatchAssociateScramSecrets${(0, uniqueid_1.addressOf)(usernames)}`, {
onUpdate: {
service: 'Kafka',
action: 'batchAssociateScramSecret',
parameters: {
ClusterArn: this.clusterArn,
SecretArnList: secrets.map((secret) => secret.secretArn),
},
physicalResourceId: cr.PhysicalResourceId.of('CreateUsers'),
},
policy: cr.AwsCustomResourcePolicy.fromStatements([
new iam.PolicyStatement({
actions: ['kms:CreateGrant'],
resources: [this.saslScramAuthenticationKey?.keyArn],
}),
new iam.PolicyStatement({
actions: ['kafka:BatchAssociateScramSecret'],
resources: [this.clusterArn],
}),
]),
installLatestAwsSdk: false,
});
}
else {
throw new core.ValidationError('Cannot create users if an authentication KMS key has not been created/provided.', this);
}
}
};
exports.Cluster = Cluster;
_c = JSII_RTTI_SYMBOL_1;
Cluster[_c] = { fqn: "@aws-cdk/aws-msk-alpha.Cluster", version: "2.211.0-alpha.0" };
/** Uniquely identifies this class. */
Cluster.PROPERTY_INJECTION_ID = '@aws-cdk.aws-msk-alpha.Cluster';
__decorate([
(0, metadata_resource_1.MethodMetadata)()
], Cluster.prototype, "addUser", null);
exports.Cluster = Cluster = __decorate([
prop_injectable_1.propertyInjectable
], Cluster);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2x1c3Rlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImNsdXN0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBQ0EsMkNBQTJDO0FBQzNDLDJDQUEyQztBQUMzQywyQ0FBMkM7QUFHM0MsaUVBQWlFO0FBQ2pFLHlDQUF5QztBQUN6QywyQ0FBZ0Q7QUFDaEQsbURBQW1EO0FBQ25ELCtDQUFzRTtBQUV0RSw4REFBNEQ7QUFFNUQsaURBQWlEO0FBQ2pELDhFQUE4RjtBQUM5RiwwRUFBMEU7QUFxQjFFOztHQUVHO0FBQ0gsTUFBc0IsV0FBWSxTQUFRLElBQUksQ0FBQyxRQUFRO0lBTXJELDBDQUEwQztJQUMxQyxJQUFXLFdBQVc7UUFDcEIsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztZQUN2QixNQUFNLElBQUksSUFBSSxDQUFDLGVBQWUsQ0FBQyx1REFBdUQsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUNoRyxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDO0tBQzFCOztBQVpILGtDQWFDOzs7QUFxSUQ7O0dBRUc7QUFDSCxJQUFZLFdBVVg7QUFWRCxXQUFZLFdBQVc7SUFDckI7O09BRUc7SUFDSCw4QkFBZSxDQUFBO0lBRWY7O09BRUc7SUFDSCxnQ0FBaUIsQ0FBQTtBQUNuQixDQUFDLEVBVlcsV0FBVywyQkFBWCxXQUFXLFFBVXRCO0FBbUJEOzs7O0dBSUc7QUFDSCxJQUFZLHNCQW9CWDtBQXBCRCxXQUFZLHNCQUFzQjtJQUNoQzs7T0FFRztJQUNILDZDQUFtQixDQUFBO0lBRW5COztPQUVHO0lBQ0gsbURBQXlCLENBQUE7SUFFekI7O09BRUc7SUFDSCx1RUFBNkMsQ0FBQTtJQUU3Qzs7T0FFRztJQUNILDZFQUFtRCxDQUFBO0FBQ3JELENBQUMsRUFwQlcsc0JBQXNCLHNDQUF0QixzQkFBc0IsUUFvQmpDO0FBeUVEOztHQUVHO0FBQ0gsSUFBWSxzQkFlWDtBQWZELFdBQVksc0JBQXNCO0lB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Ozs7QUFDbEIsc0NBQXNDO0FBQ2YsNkJBQXFCLEdBQVcsZ0NBQWdDLEFBQTNDLENBQTRDO0FBb1pqRjtJQUROLElBQUEsa0NBQWMsR0FBRTtzQ0F5Q2hCO2tCQTliVSxPQUFPO0lBRG5CLG9DQUFrQjtHQUNOLE9BQU8sQ0ErYm5CIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgYWNtcGNhIGZyb20gJ2F3cy1jZGstbGliL2F3cy1hY21wY2EnO1xuaW1wb3J0ICogYXMgZWMyIGZyb20gJ2F3cy1jZGstbGliL2F3cy1lYzInO1xuaW1wb3J0ICogYXMgaWFtIGZyb20gJ2F3cy1jZGstbGliL2F3cy1pYW0nO1xuaW1wb3J0ICogYXMga21zIGZyb20gJ2F3cy1jZGstbGliL2F3cy1rbXMnO1xuaW1wb3J0ICogYXMgbG9ncyBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbG9ncyc7XG5pbXBvcnQgKiBhcyBzMyBmcm9tICdhd3MtY2RrLWxpYi9hd3MtczMnO1xuaW1wb3J0ICogYXMgc2VjcmV0c21hbmFnZXIgZnJvbSAnYXdzLWNkay1saWIvYXdzLXNlY3JldHNtYW5hZ2VyJztcbmltcG9ydCAqIGFzIGNvcmUgZnJvbSAnYXdzLWNkay1saWIvY29yZSc7XG5pbXBvcnQgeyBGZWF0dXJlRmxhZ3MgfSBmcm9tICdhd3MtY2RrLWxpYi9jb3JlJztcbmltcG9ydCAqIGFzIGNyIGZyb20gJ2F3cy1jZGstbGliL2N1c3RvbS1yZXNvdXJjZXMnO1xuaW1wb3J0IHsgUzNfQ1JFQVRFX0RFRkFVTFRfTE9HR0lOR19QT0xJQ1kgfSBmcm9tICdhd3MtY2RrLWxpYi9jeC1hcGknO1xuaW1wb3J0ICogYXMgY29uc3RydWN0cyBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCB7IGFkZHJlc3NPZiB9IGZyb20gJ2NvbnN0cnVjdHMvbGliL3ByaXZhdGUvdW5pcXVlaWQnO1xuaW1wb3J0IHsgS2Fma2FWZXJzaW9uIH0gZnJvbSAnLi8nO1xuaW1wb3J0IHsgQ2ZuQ2x1c3RlciB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1tc2snO1xuaW1wb3J0IHsgYWRkQ29uc3RydWN0TWV0YWRhdGEsIE1ldGhvZE1ldGFkYXRhIH0gZnJvbSAnYXdzLWNkay1saWIvY29yZS9saWIvbWV0YWRhdGEtcmVzb3VyY2UnO1xuaW1wb3J0IHsgcHJvcGVydHlJbmplY3RhYmxlIH0gZnJvbSAnYXdzLWNkay1saWIvY29yZS9saWIvcHJvcC1pbmplY3RhYmxlJztcblxuLyoqXG4gKiBSZXByZXNlbnRzIGEgTVNLIENsdXN0ZXJcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJQ2x1c3RlciBleHRlbmRzIGNvcmUuSVJlc291cmNlLCBlYzIuSUNvbm5lY3RhYmxlIHtcbiAgLyoqXG4gICAqIFRoZSBBUk4gb2YgY2x1c3Rlci5cbiAgICpcbiAgICogQGF0dHJpYnV0ZVxuICAgKi9cbiAgcmVhZG9ubHkgY2x1c3RlckFybjogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgcGh5c2ljYWwgbmFtZSBvZiB0aGUgY2x1c3Rlci5cbiAgICpcbiAgICogQGF0dHJpYnV0ZVxuICAgKi9cbiAgcmVhZG9ubHkgY2x1c3Rlck5hbWU6IHN0cmluZztcbn1cblxuLyoqXG4gKiBBIG5ldyBvciBpbXBvcnRlZCBNU0sgQ2x1c3Rlci5cbiAqL1xuZXhwb3J0IGFic3RyYWN0IGNsYXNzIENsdXN0ZXJCYXNlIGV4dGVuZHMgY29yZS5SZXNvdXJjZSBpbXBsZW1lbnRzIElDbHVzdGVyIHtcbiAgcHVibGljIGFic3RyYWN0IHJlYWRvbmx5IGNsdXN0ZXJBcm46IHN0cmluZztcbiAgcHVibGljIGFic3RyYWN0IHJlYWRvbmx5IGNsdXN0ZXJOYW1lOiBzdHJpbmc7XG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgcHJvdGVjdGVkIF9jb25uZWN0aW9uczogZWMyLkNvbm5lY3Rpb25zIHwgdW5kZWZpbmVkO1xuXG4gIC8qKiBNYW5hZ2VzIGNvbm5lY3Rpb25zIGZvciB0aGUgY2x1c3RlciAqL1xuICBwdWJsaWMgZ2V0IGNvbm5lY3Rpb25zKCk6IGVjMi5Db25uZWN0aW9ucyB7XG4gICAgaWYgKCF0aGlzLl9jb25uZWN0aW9ucykge1xuICAgICAgdGhyb3cgbmV3IGNvcmUuVmFsaWRhdGlvbkVycm9yKCdBbiBpbXBvcnRlZCBDbHVzdGVyIGNhbm5vdCBtYW5hZ2UgaXRzIHNlY3VyaXR5IGdyb3VwcycsIHRoaXMpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fY29ubmVjdGlvbnM7XG4gIH1cbn1cblxuLyoqXG4gKiAgUHJvcGVydGllcyBmb3IgYSBNU0sgQ2x1c3RlclxuICovXG5leHBvcnQgaW50ZXJmYWNlIENsdXN0ZXJQcm9wcyB7XG4gIC8qKlxuICAgKiBUaGUgcGh5c2ljYWwgbmFtZSBvZiB0aGUgY2x1c3Rlci5cbiAgICovXG4gIHJlYWRvbmx5IGNsdXN0ZXJOYW1lOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSB2ZXJzaW9uIG9mIEFwYWNoZSBLYWZrYS5cbiAgICovXG4gIHJlYWRvbmx5IGthZmthVmVyc2lvbjogS2Fma2FWZXJzaW9uO1xuXG4gIC8qKlxuICAgKiBOdW1iZXIgb2YgQXBhY2hlIEthZmthIGJyb2tlcnMgZGVwbG95ZWQgaW4gZWFjaCBBdmFpbGFiaWxpdHkgWm9uZS5cbiAgICpcbiAgICogQGRlZmF1bHQgMVxuICAgKi9cbiAgcmVhZG9ubHkgbnVtYmVyT2ZCcm9rZXJOb2Rlcz86IG51bWJlcjtcblxuICAvKipcbiAgICogRGVmaW5lcyB0aGUgdmlydHVhbCBuZXR3b3JraW5nIGVudmlyb25tZW50IGZvciB0aGlzIGNsdXN0ZXIuXG4gICAqIE11c3QgaGF2ZSBhdCBsZWFzdCAyIHN1Ym5ldHMgaW4gdHdvIGRpZmZlcmVudCBBWnMuXG4gICAqL1xuICByZWFkb25seSB2cGM6IGVjMi5JVnBjO1xuXG4gIC8qKlxuICAgKiBXaGVyZSB0byBwbGFjZSB0aGUgbm9kZXMgd2l0aGluIHRoZSBWUEMuXG4gICAqIEFtYXpvbiBNU0sgZGlzdHJpYnV0ZXMgdGhlIGJyb2tlciBub2RlcyBldmVubHkgYWNyb3NzIHRoZSBzdWJuZXRzIHRoYXQgeW91IHNwZWNpZnkuXG4gICAqIFRoZSBzdWJuZXRzIHRoYXQgeW91IHNwZWNpZnkgbXVzdCBiZSBpbiBkaXN0aW5jdCBBdmFpbGFiaWxpdHkgWm9uZXMuXG4gICAqIENsaWVudCBzdWJuZXRzIGNhbid0IGJlIGluIEF2YWlsYWJpbGl0eSBab25lIHVzLWVhc3QtMWUuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gdGhlIFZwYyBkZWZhdWx0IHN0cmF0ZWd5IGlmIG5vdCBzcGVjaWZpZWQuXG4gICAqL1xuICByZWFkb25seSB2cGNTdWJuZXRzPzogZWMyLlN1Ym5ldFNlbGVjdGlvbjtcblxuICAvKipcbiAgICogVGhlIEVDMiBpbnN0YW5jZSB0eXBlIHRoYXQgeW91IHdhbnQgQW1hem9uIE1TSyB0byB1c2Ugd2hlbiBpdCBjcmVhdGVzIHlvdXIgYnJva2Vycy5cbiAgICpcbiAgICogQHNlZSBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vbXNrL2xhdGVzdC9kZXZlbG9wZXJndWlkZS9tc2stY3JlYXRlLWNsdXN0ZXIuaHRtbCNicm9rZXItaW5zdGFuY2UtdHlwZXNcbiAgICogQGRlZmF1bHQga2Fma2EubTUubGFyZ2VcbiAgICovXG4gIHJlYWRvbmx5IGluc3RhbmNlVHlwZT86IGVjMi5JbnN0YW5jZVR5cGU7XG5cbiAgLyoqXG4gICAqIFRoZSBBV1Mgc2VjdXJpdHkgZ3JvdXBzIHRvIGFzc29jaWF0ZSB3aXRoIHRoZSBlbGFzdGljIG5ldHdvcmsgaW50ZXJmYWNlcyBpbiBvcmRlciB0byBzcGVjaWZ5IHdobyBjYW5cbiAgICogY29ubmVjdCB0byBhbmQgY29tbXVuaWNhdGUgd2l0aCB0aGUgQW1hem9uIE1TSyBjbHVzdGVyLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIGNyZWF0ZSBuZXcgc2VjdXJpdHkgZ3JvdXBcbiAgICovXG4gIHJlYWRvbmx5IHNlY3VyaXR5R3JvdXBzPzogZWMyLklTZWN1cml0eUdyb3VwW107XG5cbiAgLyoqXG4gICAqIEluZm9ybWF0aW9uIGFib3V0IHN0b3JhZ2Ugdm9sdW1lcyBhdHRhY2hlZCB0byBNU0sgYnJva2VyIG5vZGVzLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIDEwMDAgR2lCIEVCUyB2b2x1bWVcbiAgICovXG4gIHJlYWRvbmx5IGVic1N0b3JhZ2VJbmZvPzogRWJzU3RvcmFnZUluZm87XG5cbiAgLyoqXG4gICAqIFRoaXMgY29udHJvbHMgc3RvcmFnZSBtb2RlIGZvciBzdXBwb3J0ZWQgc3RvcmFnZSB0aWVycy5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBTdG9yYWdlTW9kZS5MT0NBTFxuICAgKiBAc2VlIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9tc2svbGF0ZXN0L2RldmVsb3Blcmd1aWRlL21zay10aWVyZWQtc3RvcmFnZS5odG1sXG4gICAqL1xuICByZWFkb25seSBzdG9yYWdlTW9kZT86IFN0b3JhZ2VNb2RlO1xuXG4gIC8qKlxuICAgKiBUaGUgQW1hem9uIE1TSyBjb25maWd1cmF0aW9uIHRvIHVzZSBmb3IgdGhlIGNsdXN0ZXIuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gbm9uZVxuICAgKi9cbiAgcmVhZG9ubHkgY29uZmlndXJhdGlvbkluZm8/OiBDbHVzdGVyQ29uZmlndXJhdGlvbkluZm87XG5cbiAgLyoqXG4gICAqIENsdXN0ZXIgbW9uaXRvcmluZyBjb25maWd1cmF0aW9uLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIERFRkFVTFQgbW9uaXRvcmluZyBsZXZlbFxuICAgKi9cbiAgcmVhZG9ubHkgbW9uaXRvcmluZz86IE1vbml0b3JpbmdDb25maWd1cmF0aW9uO1xuXG4gIC8qKlxuICAgKiBDb25maWd1cmUgeW91ciBNU0sgY2x1c3RlciB0byBzZW5kIGJyb2tlciBsb2dzIHRvIGRpZmZlcmVudCBkZXN0aW5hdGlvbiB0eXBlcy5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBkaXNhYmxlZFxuICAgKi9cbiAgcmVhZG9ubHkgbG9nZ2luZz86IEJyb2tlckxvZ2dpbmc7XG5cbiAgLyoqXG4gICAqIENvbmZpZyBkZXRhaWxzIGZvciBlbmNyeXB0aW9uIGluIHRyYW5zaXQuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gZW5hYmxlZFxuICAgKi9cbiAgcmVhZG9ubHkgZW5jcnlwdGlvbkluVHJhbnNpdD86IEVuY3J5cHRpb25JblRyYW5zaXRDb25maWc7XG5cbiAgLyoqXG4gICAqIENvbmZpZ3VyYXRpb24gcHJvcGVydGllcyBmb3IgY2xpZW50IGF1dGhlbnRpY2F0aW9uLlxuICAgKiBNU0sgc3VwcG9ydHMgdXNpbmcgcHJpdmF0ZSBUTFMgY2VydGlmaWNhdGVzIG9yIFNBU0wvU0NSQU0gdG8gYXV0aGVudGljYXRlIHRoZSBpZGVudGl0eSBvZiBjbGllbnRzLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIGRpc2FibGVkXG4gICAqL1xuICByZWFkb25seSBjbGllbnRBdXRoZW50aWNhdGlvbj86IENsaWVudEF1dGhlbnRpY2F0aW9uO1xuXG4gIC8qKlxuICAgKiBXaGF0IHRvIGRvIHdoZW4gdGhpcyByZXNvdXJjZSBpcyBkZWxldGVkIGZyb20gYSBzdGFjay5cbiAgICpcbiAgICogQGRlZmF1bHQgUmVtb3ZhbFBvbGljeS5SRVRBSU5cbiAgICovXG4gIHJlYWRvbmx5IHJlbW92YWxQb2xpY3k/OiBjb3JlLlJlbW92YWxQb2xpY3k7XG59XG5cbi8qKlxuICogRUJTIHZvbHVtZSBpbmZvcm1hdGlvbi5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBFYnNTdG9yYWdlSW5mbyB7XG4gIC8qKlxuICAgKiBUaGUgc2l6ZSBpbiBHaUIgb2YgdGhlIEVCUyB2b2x1bWUgZm9yIHRoZSBkYXRhIGRyaXZlIG9uIGVhY2ggYnJva2VyIG5vZGUuXG4gICAqXG4gICAqIEBkZWZhdWx0IDEwMDBcbiAgICovXG4gIHJlYWRvbmx5IHZvbHVtZVNpemU/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIFRoZSBBV1MgS01TIGtleSBmb3IgZW5jcnlwdGluZyBkYXRhIGF0IHJlc3QuXG4gICAqXG4gICAqIEBkZWZhdWx0IFVzZXMgQVdTIG1hbmFnZWQgQ01LIChhd3Mva2Fma2EpXG4gICAqL1xuICByZWFkb25seSBlbmNyeXB0aW9uS2V5Pzoga21zLklLZXk7XG59XG5cbi8qKlxuICogVGhlIHN0b3JhZ2UgbW9kZSBmb3IgdGhlIGNsdXN0ZXIgYnJva2Vycy5cbiAqL1xuZXhwb3J0IGVudW0gU3RvcmFnZU1vZGUge1xuICAvKipcbiAgICogTG9jYWwgc3RvcmFnZSBtb2RlIHV0aWxpemVzIG5ldHdvcmsgYXR0YWNoZWQgRUJTIHN0b3JhZ2UuXG4gICAqL1xuICBMT0NBTCA9ICdMT0NBTCcsXG5cbiAgLyoqXG4gICAqIFRpZXJlZCBzdG9yYWdlIG1vZGUgdXRpbGl6ZXMgRUJTIHN0b3JhZ2UgYW5kIFRpZXJlZCBzdG9yYWdlLlxuICAgKi9cbiAgVElFUkVEID0gJ1RJRVJFRCcsXG59XG5cbi8qKlxuICogVGhlIEFtYXpvbiBNU0sgY29uZmlndXJhdGlvbiB0byB1c2UgZm9yIHRoZSBjbHVzdGVyLlxuICogTm90ZTogVGhlcmUgaXMgY3VycmVudGx5IG5vIENsb3VkZm9ybWF0aW9uIFJlc291cmNlIHRvIGNyZWF0ZSBhIENvbmZpZ3VyYXRpb25cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBDbHVzdGVyQ29uZmlndXJhdGlvbkluZm8ge1xuICAvKipcbiAgICogVGhlIEFtYXpvbiBSZXNvdXJjZSBOYW1lIChBUk4pIG9mIHRoZSBNU0sgY29uZmlndXJhdGlvbiB0byB1c2UuXG4gICAqIEZvciBleGFtcGxlLCBhcm46YXdzOmthZmthOnVzLWVhc3QtMToxMjM0NTY3ODkwMTI6Y29uZmlndXJhdGlvbi9leGFtcGxlLWNvbmZpZ3VyYXRpb24tbmFtZS9hYmNkYWJjZC0xMjM0LWFiY2QtMTIzNC1hYmNkMTIzZThlOGUtMS5cbiAgICovXG4gIHJlYWRvbmx5IGFybjogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgcmV2aXNpb24gb2YgdGhlIEFtYXpvbiBNU0sgY29uZmlndXJhdGlvbiB0byB1c2UuXG4gICAqL1xuICByZWFkb25seSByZXZpc2lvbjogbnVtYmVyO1xufVxuXG4vKipcbiAqIFRoZSBsZXZlbCBvZiBtb25pdG9yaW5nIGZvciB0aGUgTVNLIGNsdXN0ZXJcbiAqXG4gKiBAc2VlIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9tc2svbGF0ZXN0L2RldmVsb3Blcmd1aWRlL21vbml0b3JpbmcuaHRtbCNtZXRyaWNzLWRldGFpbHNcbiAqL1xuZXhwb3J0IGVudW0gQ2x1c3Rlck1vbml0b3JpbmdMZXZlbCB7XG4gIC8qKlxuICAgKiBEZWZhdWx0IG1ldHJpY3MgYXJlIHRoZSBlc3NlbnRpYWwgbWV0cmljcyB0byBtb25pdG9yLlxuICAgKi9cbiAgREVGQVVMVCA9ICdERUZBVUxUJyxcblxuICAvKipcbiAgICogUGVyIEJyb2tlciBtZXRyaWNzIGdpdmUgeW91IG1ldHJpY3MgYXQgdGhlIGJyb2tlciBsZXZlbC5cbiAgICovXG4gIFBFUl9CUk9LRVIgPSAnUEVSX0JST0tFUicsXG5cbiAgLyoqXG4gICAqIFBlciBUb3BpYyBQZXIgQnJva2VyIG1ldHJpY3MgaGVscCB5b3UgdW5kZXJzdGFuZCB2b2x1bWUgYXQgdGhlIHRvcGljIGxldmVsLlxuICAgKi9cbiAgUEVSX1RPUElDX1BFUl9CUk9LRVIgPSAnUEVSX1RPUElDX1BFUl9CUk9LRVInLFxuXG4gIC8qKlxuICAgKiBQZXIgVG9waWMgUGVyIFBhcnRpdGlvbiBtZXRyaWNzIGhlbHAgeW91IHVuZGVyc3RhbmQgY29uc3VtZXIgZ3JvdXAgbGFnIGF0IHRoZSB0b3BpYyBwYXJ0aXRpb24gbGV2ZWwuXG4gICAqL1xuICBQRVJfVE9QSUNfUEVSX1BBUlRJVElPTiA9ICdQRVJfVE9QSUNfUEVSX1BBUlRJVElPTicsXG59XG5cbi8qKlxuICogTW9uaXRvcmluZyBDb25maWd1cmF0aW9uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTW9uaXRvcmluZ0NvbmZpZ3VyYXRpb24ge1xuICAvKipcbiAgICogU3BlY2lmaWVzIHRoZSBsZXZl