cdk8s-plus-25
Version:
cdk8s+ is a software development framework that provides high level abstractions for authoring Kubernetes applications. cdk8s-plus-25 synthesizes Kubernetes manifests for Kubernetes 1.25.0
569 lines • 66.9 kB
JavaScript
"use strict";
var _a, _b, _c, _d;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Replicas = exports.ScalingStrategy = exports.MetricTarget = exports.Metric = exports.HorizontalPodAutoscaler = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const cdk8s_1 = require("cdk8s");
const base_1 = require("./base");
const k8s = require("./imports/k8s");
/**
* A HorizontalPodAutoscaler scales a workload up or down in response to a metric
* change. This allows your services to scale up when demand is high and scale down
* when they are no longer needed.
*
*
* Typical use cases for HorizontalPodAutoscaler:
*
* * When Memory usage is above 70%, scale up the number of replicas to meet the demand.
* * When CPU usage is below 30%, scale down the number of replicas to save resources.
* * When a service is experiencing a spike in traffic, scale up the number of replicas
* to meet the demand. Then, when the traffic subsides, scale down the number of
* replicas to save resources.
*
* The autoscaler uses the following algorithm to determine the number of replicas to scale:
*
* `desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]`
*
* HorizontalPodAutoscaler's can be used to with any `Scalable` workload:
* * Deployment
* * StatefulSet
*
* **Targets that already have a replica count defined:**
*
* Remove any replica counts from the target resource before associating with a
* HorizontalPodAutoscaler. If this isn't done, then any time a change to that object is applied,
* Kubernetes will scale the current number of Pods to the value of the target.replicas key. This
* may not be desired and could lead to unexpected behavior.
*
* @see https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#implicit-maintenance-mode-deactivation
*
* @example
* const backend = new kplus.Deployment(this, 'Backend', ...);
*
* const hpa = new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
* target: backend,
* maxReplicas: 10,
* scaleUp: {
* policies: [
* {
* replicas: kplus.Replicas.absolute(3),
* duration: Duration.minutes(5),
* },
* ],
* },
* });
*/
class HorizontalPodAutoscaler extends base_1.Resource {
constructor(scope, id, props) {
super(scope, id);
this.resourceType = 'horizontalpodautoscaler';
this._defaultScalingDuration = cdk8s_1.Duration.seconds(15);
this.apiObject = new k8s.KubeHorizontalPodAutoscalerV2(this, 'Resource', {
metadata: props.metadata,
spec: cdk8s_1.Lazy.any({ produce: () => this._toKube() }),
});
if (props?.minReplicas && props.minReplicas > props.maxReplicas) {
throw new Error(`'minReplicas' (${props.minReplicas}) must be less than or equal to 'maxReplicas' (${props.maxReplicas}) in order for HorizontalPodAutoscaler to scale.`);
}
if (props?.scaleUp?.stabilizationWindow !== undefined) {
this._validateStabilizationWindow('scaleUp', props.scaleUp.stabilizationWindow);
}
if (props?.scaleDown?.stabilizationWindow !== undefined) {
this._validateStabilizationWindow('scaleDown', props.scaleDown.stabilizationWindow);
}
if (props?.scaleUp?.policies?.length) {
this._validateScalingPolicies('scaleUp', props.scaleUp.policies);
}
if (props?.scaleDown?.policies?.length) {
this._validateScalingPolicies('scaleDown', props.scaleDown.policies);
}
this.target = props.target;
this.target.markHasAutoscaler();
this.maxReplicas = props.maxReplicas;
this.minReplicas = props.minReplicas ?? 1;
this.metrics = props.metrics;
this.scaleUp = {
strategy: ScalingStrategy.MAX_CHANGE,
stabilizationWindow: cdk8s_1.Duration.seconds(0),
...props.scaleUp,
policies: props.scaleUp?.policies?.map((p) => ({ duration: this._defaultScalingDuration, ...p })) ?? [
{
replicas: Replicas.absolute(4),
duration: cdk8s_1.Duration.minutes(1),
},
{
replicas: Replicas.percent(200),
duration: cdk8s_1.Duration.minutes(1),
},
],
};
if (props?.scaleUp?.policies?.length) {
this._validateScalingPolicies('scaleUp', props.scaleUp.policies);
}
this.scaleDown = {
strategy: ScalingStrategy.MAX_CHANGE,
stabilizationWindow: cdk8s_1.Duration.minutes(5),
...props.scaleDown,
policies: props.scaleDown?.policies?.map((p) => ({ duration: this._defaultScalingDuration, ...p })) ?? [
{
replicas: Replicas.absolute(this.minReplicas),
duration: cdk8s_1.Duration.minutes(5),
},
],
};
this.node.addValidation({ validate: () => this._validateTargetReplicas() });
this.node.addValidation({ validate: () => this._validateTargetContainers() });
}
/**
* Validate a list of scaling policies.
* @internal
*/
_validateScalingPolicies(direction, policies) {
policies.forEach((p) => {
if (p.duration !== undefined) {
this._validateScalingPolicyDuration(direction, p.duration);
}
});
}
/**
* Validate `ScalingPolicy.duration` is within the allowed range.
*
* `duration` range: 1 second - 30 min
*
* Kubernetes name: `ScalingPolicy.periodSeconds`.
* @see https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/horizontal-pod-autoscaler-v2/#HorizontalPodAutoscalerSpec
* @internal
*/
_validateScalingPolicyDuration(direction, duration) {
const periodSeconds = duration.toSeconds() ?? 15;
const isWithinRange = Boolean(0 < periodSeconds && periodSeconds <= 1800);
if (!isWithinRange) {
throw new Error(`'${direction}.policies' duration (${duration.toHumanString()}) is outside of the allowed range. Must be at least 1 second long and no longer than 30 minutes.`);
}
}
/**
* Validate `ScalingRules.stabilizationWindow` is within the allowed range.
*
* `stabilizationWindow` range: 0 seconds - 1 hour
*
* @see https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/horizontal-pod-autoscaler-v2/#HorizontalPodAutoscalerSpec
* @internal
*/
_validateStabilizationWindow(direction, window) {
const windowSeconds = window.toSeconds();
const isWithinRange = Boolean(0 <= windowSeconds && windowSeconds <= 3600);
if (!isWithinRange) {
throw new Error(`'${direction}.stabilizationWindow' (${window.toHumanString()}) must be 0 seconds or more with a max of 1 hour.`);
}
}
/**
* Guarantee the HPA has a metric to scale on.
* Verify that metrics are configured, if not check every pod container has a resource limit or
* request defined.
* @internal
*/
_validateTargetContainers() {
const containers = this.target.toScalingTarget().containers;
const hasResourceConstraints = containers.some((c) => this._hasRequestsOrLimits(c));
if (!hasResourceConstraints && !this.metrics) {
return ['If HorizontalPodAutoscaler does not have metrics defined, then every container in the target must have a CPU or memory resource constraint defined.'];
}
return [];
}
/**
* Prevent the HPA from scaling a target with a replica count defined.
* @see https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#implicit-maintenance-mode-deactivation
* @internal
*/
_validateTargetReplicas() {
const replicas = this.target.toScalingTarget().replicas;
if (replicas) {
return [
`HorizontalPodAutoscaler target cannot have a fixed number of replicas (${replicas}).`,
];
}
return [];
}
/**
* Validate that the container has at least one CPU/memory request/limit defined.
* @internal
*/
_hasRequestsOrLimits(c) {
const hasRequests = c.resources?.cpu?.request || c.resources?.memory?.request;
const hasLimits = c.resources?.cpu?.limit || c.resources?.memory?.limit;
return Boolean(hasRequests || hasLimits);
}
/**
* @internal
*/
_toKube() {
const scalingTarget = this.target.toScalingTarget();
return {
maxReplicas: this.maxReplicas,
minReplicas: this.minReplicas,
scaleTargetRef: {
apiVersion: scalingTarget.apiVersion,
name: scalingTarget.name,
kind: scalingTarget.kind,
},
metrics: this.metrics?.map(m => m._toKube()),
behavior: {
scaleUp: {
policies: this.scaleUp.policies?.map((p) => ({
...p.replicas._toKube(),
periodSeconds: p.duration?.toSeconds() ?? this._defaultScalingDuration.toSeconds(),
})),
selectPolicy: this.scaleUp.strategy,
stabilizationWindowSeconds: this.scaleUp.stabilizationWindow?.toSeconds(),
},
scaleDown: {
policies: this.scaleDown.policies?.map((p) => ({
...p.replicas._toKube(),
periodSeconds: p.duration?.toSeconds() ?? this._defaultScalingDuration.toSeconds(),
})),
selectPolicy: this.scaleDown.strategy,
stabilizationWindowSeconds: this.scaleDown.stabilizationWindow?.toSeconds(),
},
},
};
}
}
exports.HorizontalPodAutoscaler = HorizontalPodAutoscaler;
_a = JSII_RTTI_SYMBOL_1;
HorizontalPodAutoscaler[_a] = { fqn: "cdk8s-plus-25.HorizontalPodAutoscaler", version: "2.22.79" };
/**
* A metric condition that HorizontalPodAutoscaler's scale on.
*/
class Metric {
constructor(metric) {
this.metric = metric;
this.type = metric.type;
}
/**
* Metric that tracks the CPU of a container. This metric
* will be tracked across all pods of the current scale target.
*
*/
static containerCpu(options) {
return new Metric({
type: 'ContainerResource',
containerResource: {
name: 'cpu',
container: options.container.name,
target: options.target._toKube(),
},
});
}
/**
* Metric that tracks the Memory of a container. This metric
* will be tracked across all pods of the current scale target.
*
*/
static containerMemory(options) {
return new Metric({
type: 'ContainerResource',
containerResource: {
name: 'memory',
container: options.container.name,
target: options.target._toKube(),
},
});
}
/**
* Metric that tracks the volume size of a container. This metric
* will be tracked across all pods of the current scale target.
*
*/
static containerStorage(options) {
return new Metric({
type: 'ContainerResource',
containerResource: {
name: 'storage',
container: options.container.name,
target: options.target._toKube(),
},
});
}
/**
* Metric that tracks the local ephemeral storage of a container. This metric
* will be tracked across all pods of the current scale target.
*
*/
static containerEphemeralStorage(options) {
return new Metric({
type: 'ContainerResource',
containerResource: {
name: 'ephemeral-storage',
container: options.container.name,
target: options.target._toKube(),
},
});
}
/**
* A global metric that is not associated with any Kubernetes object.
* Allows for autoscaling based on information coming from components running outside of
* the cluster.
*
* Use case:
* * Scale up when the length of an SQS queue is greater than 10 messages.
* * Scale down when an outside load balancer's queries are less than 10000 per second.
*/
static external(options) {
return new Metric({
type: 'External',
external: {
metric: {
name: options.name,
selector: options.labelSelector?._toKube(),
},
target: options.target._toKube(),
},
});
}
/**
* Metric that describes a metric of a kubernetes object
*
* Use case:
* * Scale on a Kubernetes Ingress's hits-per-second metric.
*/
static object(options) {
return new Metric({
type: 'Object',
object: {
describedObject: {
apiVersion: options.object.apiVersion,
kind: options.object.kind,
name: options.object.name,
},
metric: {
name: options.name,
selector: options.labelSelector?._toKube(),
},
target: options.target._toKube(),
},
});
}
/**
* A pod metric that will be averaged across all pods of the current scale target.
*
* Use case:
* * Average CPU utilization across all pods
* * Transactions processed per second across all pods
*/
static pods(options) {
return new Metric({
type: 'Pods',
pods: {
metric: {
name: options.name,
selector: options.labelSelector?._toKube(),
},
target: options.target._toKube(),
},
});
}
/**
* Tracks the available CPU of the pods in a target.
*
* Note: Since the resource usages of all the containers are summed up the total
* pod utilization may not accurately represent the individual container resource
* usage. This could lead to situations where a single container might be running
* with high usage and the HPA will not scale out because the overall pod usage
* is still within acceptable limits.
*
* Use case:
* * Scale up when CPU is above 40%.
*/
static resourceCpu(target) {
return new Metric({
type: 'Resource',
resource: {
name: 'cpu',
target: target._toKube(),
},
});
}
/**
* Tracks the available Memory of the pods in a target.
*
* Note: Since the resource usages of all the containers are summed up the total
* pod utilization may not accurately represent the individual container resource
* usage. This could lead to situations where a single container might be running
* with high usage and the HPA will not scale out because the overall pod usage
* is still within acceptable limits.
*
* Use case:
* * Scale up when Memory is above 512MB.
*/
static resourceMemory(target) {
return new Metric({
type: 'Resource',
resource: {
name: 'memory',
target: target._toKube(),
},
});
}
/**
* Tracks the available Storage of the pods in a target.
*
* Note: Since the resource usages of all the containers are summed up the total
* pod utilization may not accurately represent the individual container resource
* usage. This could lead to situations where a single container might be running
* with high usage and the HPA will not scale out because the overall pod usage
* is still within acceptable limits.
*
*/
static resourceStorage(target) {
return new Metric({
type: 'Resource',
resource: {
name: 'storage',
target: target._toKube(),
},
});
}
/**
* Tracks the available Ephemeral Storage of the pods in a target.
*
* Note: Since the resource usages of all the containers are summed up the total
* pod utilization may not accurately represent the individual container resource
* usage. This could lead to situations where a single container might be running
* with high usage and the HPA will not scale out because the overall pod usage
* is still within acceptable limits.
*
*/
static resourceEphemeralStorage(target) {
return new Metric({
type: 'Resource',
resource: {
name: 'ephemeral-storage',
target: target._toKube(),
},
});
}
/**
* @internal
*/
_toKube() {
return this.metric;
}
}
exports.Metric = Metric;
_b = JSII_RTTI_SYMBOL_1;
Metric[_b] = { fqn: "cdk8s-plus-25.Metric", version: "2.22.79" };
/**
* A metric condition that will trigger scaling behavior when satisfied.
*
* @example
*
* MetricTarget.averageUtilization(70); // 70% average utilization
*
*/
class MetricTarget {
constructor(metric) {
this.metric = metric;
}
/**
* Target a specific target value.
*
* @param value The target value.
*/
static value(value) {
return new MetricTarget({
type: 'Value',
value: k8s.Quantity.fromNumber(value),
});
}
/**
* Target the average value across all relevant pods.
*
* @param averageValue The average metric value.
*/
static averageValue(averageValue) {
return new MetricTarget({
type: 'AverageValue',
averageValue: k8s.Quantity.fromNumber(averageValue),
});
}
/**
* Target a percentage value across all relevant pods.
*
* @param averageUtilization The percentage of the utilization metric. e.g. `50` for 50%.
*/
static averageUtilization(averageUtilization) {
return new MetricTarget({
type: 'Utilization',
averageUtilization,
});
}
/**
* @internal
*/
_toKube() {
return this.metric;
}
}
exports.MetricTarget = MetricTarget;
_c = JSII_RTTI_SYMBOL_1;
MetricTarget[_c] = { fqn: "cdk8s-plus-25.MetricTarget", version: "2.22.79" };
var ScalingStrategy;
(function (ScalingStrategy) {
/**
* Use the policy that provisions the most changes.
*/
ScalingStrategy["MAX_CHANGE"] = "Max";
/**
* Use the policy that provisions the least amount of changes.
*/
ScalingStrategy["MIN_CHANGE"] = "Min";
/**
* Disables scaling in this direction.
*
* @deprecated - Omit the ScalingRule instead
*/
ScalingStrategy["DISABLED"] = "Disabled";
})(ScalingStrategy = exports.ScalingStrategy || (exports.ScalingStrategy = {}));
/**
* The amount of replicas that will change.
*/
class Replicas {
constructor(replicas) {
this.replicas = replicas;
}
/**
* Changes the pods by a percentage of the it's current value.
*
* @param value The percentage of change to apply. Must be greater than 0.
*/
static percent(value) {
return new Replicas({
type: 'Percent',
value,
});
}
/**
* Changes the pods by a percentage of the it's current value.
*
* @param value The amount of change to apply. Must be greater than 0.
*/
static absolute(value) {
return new Replicas({
type: 'Pods',
value,
});
}
/**
* @internal
*/
_toKube() {
return {
type: this.replicas.type,
value: this.replicas.value,
};
}
}
exports.Replicas = Replicas;
_d = JSII_RTTI_SYMBOL_1;
Replicas[_d] = { fqn: "cdk8s-plus-25.Replicas", version: "2.22.79" };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaG9yaXpvbnRhbC1wb2QtYXV0b3NjYWxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9ob3Jpem9udGFsLXBvZC1hdXRvc2NhbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsaUNBQWtEO0FBRWxELGlDQUE0RDtBQUU1RCxxQ0FBcUM7QUFvR3JDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBOENHO0FBQ0gsTUFBYSx1QkFBd0IsU0FBUSxlQUFRO0lBa0NuRCxZQUFZLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEtBQW1DO1FBQzNFLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUE5QkgsaUJBQVksR0FBRyx5QkFBeUIsQ0FBQztRQTBCeEMsNEJBQXVCLEdBQUcsZ0JBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUM7UUFNOUQsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLEdBQUcsQ0FBQyw2QkFBNkIsQ0FBQyxJQUFJLEVBQUUsVUFBVSxFQUFFO1lBQ3ZFLFFBQVEsRUFBRSxLQUFLLENBQUMsUUFBUTtZQUN4QixJQUFJLEVBQUUsWUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLE9BQU8sRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztTQUNsRCxDQUFDLENBQUM7UUFFSCxJQUFJLEtBQUssRUFBRSxXQUFXLElBQUksS0FBSyxDQUFDLFdBQVcsR0FBRyxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQy9ELE1BQU0sSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssQ0FBQyxXQUFXLGtEQUFrRCxLQUFLLENBQUMsV0FBVyxrREFBa0QsQ0FBQyxDQUFDO1NBQzNLO1FBQ0QsSUFBSSxLQUFLLEVBQUUsT0FBTyxFQUFFLG1CQUFtQixLQUFLLFNBQVMsRUFBRTtZQUNyRCxJQUFJLENBQUMsNEJBQTRCLENBQUMsU0FBUyxFQUFF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QUMsS0FBSztTQUMzQixDQUFDO0lBQ0osQ0FBQzs7QUFwQ0gsNEJBcUNDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQXBpT2JqZWN0LCBEdXJhdGlvbiwgTGF6eSB9IGZyb20gJ2NkazhzJztcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuaW1wb3J0IHsgUmVzb3VyY2UsIFJlc291cmNlUHJvcHMsIElSZXNvdXJjZSB9IGZyb20gJy4vYmFzZSc7XG5pbXBvcnQgKiBhcyBjb250YWluZXIgZnJvbSAnLi9jb250YWluZXInO1xuaW1wb3J0ICogYXMgazhzIGZyb20gJy4vaW1wb3J0cy9rOHMnO1xuaW1wb3J0ICogYXMgcG9kIGZyb20gJy4vcG9kJztcblxuXG4vKipcbiAqIFByb3BlcnRpZXMgdXNlZCB0byBjb25maWd1cmUgdGhlIHRhcmdldCBvZiBhbiBBdXRvc2NhbGVyLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFNjYWxpbmdUYXJnZXQge1xuICAvKipcbiAgICogVGhlIG9iamVjdCBraW5kIChlLmcuIFwiRGVwbG95bWVudFwiKS5cbiAgICovXG4gIHJlYWRvbmx5IGtpbmQ6IHN0cmluZztcbiAgLyoqXG4gICAqIFRoZSBvYmplY3QncyBBUEkgdmVyc2lvbiAoZS5nLiBcImF1dGhvcml6YXRpb24uazhzLmlvL3YxXCIpXG4gICAqL1xuICByZWFkb25seSBhcGlWZXJzaW9uOiBzdHJpbmc7XG4gIC8qKlxuICAgKiBUaGUgS3ViZXJuZXRlcyBuYW1lIG9mIHRoaXMgcmVzb3VyY2UuXG4gICAqL1xuICByZWFkb25seSBuYW1lOiBzdHJpbmc7XG4gIC8qKlxuICAgKiBDb250YWluZXIgZGVmaW5pdGlvbnMgYXNzb2NpYXRlZCB3aXRoIHRoZSB0YXJnZXQuXG4gICAqL1xuICByZWFkb25seSBjb250YWluZXJzOiBjb250YWluZXIuQ29udGFpbmVyW107XG4gIC8qKlxuICAgKiBUaGUgZml4ZWQgbnVtYmVyIG9mIHJlcGxpY2FzIGRlZmluZWQgb24gdGhlIHRhcmdldC4gVGhpcyBpcyB1c2VkXG4gICAqIGZvciB2YWxpZGF0aW9uIHB1cnBvc2VzIGFzIFNjYWxhYmxlIHRhcmdldHMgc2hvdWxkIG5vdCBoYXZlIGFcbiAgICogZml4ZWQgbnVtYmVyIG9mIHJlcGxpY2FzLlxuICAgKi9cbiAgcmVhZG9ubHkgcmVwbGljYXM/OiBudW1iZXI7XG5cblxufVxuXG4vKipcbiAqIFJlcHJlc2VudHMgYSBzY2FsYWJsZSB3b3JrbG9hZC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJU2NhbGFibGUge1xuICAvKipcbiAgICogSWYgdGhpcyBpcyBhIHRhcmdldCBvZiBhbiBhdXRvc2NhbGVyLlxuICAgKi9cbiAgaGFzQXV0b3NjYWxlcjogYm9vbGVhbjtcbiAgLyoqXG4gICAqIENhbGxlZCBvbiBhbGwgSVNjYWxhYmxlIHRhcmdldHMgd2hlbiB0aGV5IGFyZSBhc3NvY2lhdGVkIHdpdGggYW4gYXV0b3NjYWxlci5cbiAgICovXG4gIG1hcmtIYXNBdXRvc2NhbGVyKCk6IHZvaWQ7XG4gIC8qKlxuICAgKiBSZXR1cm4gdGhlIHRhcmdldCBzcGVjIHByb3BlcnRpZXMgb2YgdGhpcyBTY2FsYWJsZS5cbiAgICovXG4gIHRvU2NhbGluZ1RhcmdldCgpOiBTY2FsaW5nVGFyZ2V0O1xufVxuXG4vKipcbiAqIFByb3BlcnRpZXMgZm9yIEhvcml6b250YWxQb2RBdXRvc2NhbGVyXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSG9yaXpvbnRhbFBvZEF1dG9zY2FsZXJQcm9wcyBleHRlbmRzIFJlc291cmNlUHJvcHMge1xuICAvKipcbiAgICogVGhlIHdvcmtsb2FkIHRvIHNjYWxlIHVwIG9yIGRvd24uXG4gICAqXG4gICAqIFNjYWxhYmxlIHdvcmtsb2FkIHR5cGVzOlxuICAgKiAqIERlcGxveW1lbnRcbiAgICogKiBTdGF0ZWZ1bFNldFxuICAgKi9cbiAgcmVhZG9ubHkgdGFyZ2V0OiBJU2NhbGFibGU7XG4gIC8qKlxuICAgKiBUaGUgbWF4aW11bSBudW1iZXIgb2YgcmVwbGljYXMgdGhhdCBjYW4gYmUgc2NhbGVkIHVwIHRvLlxuICAgKi9cbiAgcmVhZG9ubHkgbWF4UmVwbGljYXM6IG51bWJlcjtcbiAgLyoqXG4gICAqIFRoZSBtaW5pbXVtIG51bWJlciBvZiByZXBsaWNhcyB0aGF0IGNhbiBiZSBzY2FsZWQgZG93biB0by5cbiAgICpcbiAgICogQ2FuIGJlIHNldCB0byAwIGlmIHRoZSBhbHBoYSBmZWF0dXJlIGdhdGUgYEhQQVNjYWxlVG9aZXJvYCBpcyBlbmFibGVkIGFuZFxuICAgKiBhdCBsZWFzdCBvbmUgT2JqZWN0IG9yIEV4dGVybmFsIG1ldHJpYyBpcyBjb25maWd1cmVkLlxuICAgKlxuICAgKiBAZGVmYXVsdCAxXG4gICAqL1xuICByZWFkb25seSBtaW5SZXBsaWNhcz86IG51bWJlcjtcbiAgLyoqXG4gICAqIFRoZSBtZXRyaWMgY29uZGl0aW9ucyB0aGF0IHRyaWdnZXIgYSBzY2FsZSB1cCBvciBzY2FsZSBkb3duLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIElmIG1ldHJpY3MgYXJlIG5vdCBwcm92aWRlZCwgdGhlbiB0aGUgdGFyZ2V0IHJlc291cmNlXG4gICAqIGNvbnN0cmFpbnRzIChlLmcuIGNwdSBsaW1pdCkgd2lsbCBiZSB1c2VkIGFzIHNjYWxpbmcgbWV0cmljcy5cbiAgICovXG4gIHJlYWRvbmx5IG1ldHJpY3M/OiBNZXRyaWNbXTtcbiAgLyoqXG4gICAqIFRoZSBzY2FsaW5nIGJlaGF2aW9yIHdoZW4gc2NhbGluZyB1cC5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBJcyB0aGUgaGlnaGVyIG9mOlxuICAgKiAqIEluY3JlYXNlIG5vIG1vcmUgdGhhbiA0IHBvZHMgcGVyIDYwIHNlY29uZHNcbiAgICogKiBEb3VibGUgdGhlIG51bWJlciBvZiBwb2RzIHBlciA2MCBzZWNvbmRzXG4gICAqL1xuICByZWFkb25seSBzY2FsZVVwPzogU2NhbGluZ1J1bGVzO1xuICAvKipcbiAgICogVGhlIHNjYWxpbmcgYmVoYXZpb3Igd2hlbiBzY2FsaW5nIGRvd24uXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gU2NhbGUgZG93biB0byBtaW5SZXBsaWNhIGNvdW50IHdpdGggYSA1IG1pbnV0ZSBzdGFiaWxpemF0aW9uIHdpbmRvdy5cbiAgICovXG4gIHJlYWRvbmx5IHNjYWxlRG93bj86IFNjYWxpbmdSdWxlcztcbn1cblxuLyoqXG4gKiBBIEhvcml6b250YWxQb2RBdXRvc2NhbGVyIHNjYWxlcyBhIHdvcmtsb2FkIHVwIG9yIGRvd24gaW4gcmVzcG9uc2UgdG8gYSBtZXRyaWNcbiAqIGNoYW5nZS4gVGhpcyBhbGxvd3MgeW91ciBzZXJ2aWNlcyB0byBzY2FsZSB1cCB3aGVuIGRlbWFuZCBpcyBoaWdoIGFuZCBzY2FsZSBkb3duXG4gKiB3aGVuIHRoZXkgYXJlIG5vIGxvbmdlciBuZWVkZWQuXG4gKlxuICpcbiAqIFR5cGljYWwgdXNlIGNhc2VzIGZvciBIb3Jpem9udGFsUG9kQXV0b3NjYWxlcjpcbiAqXG4gKiAqIFdoZW4gTWVtb3J5IHVzYWdlIGlzIGFib3ZlIDcwJSwgc2NhbGUgdXAgdGhlIG51bWJlciBvZiByZXBsaWNhcyB0byBtZWV0IHRoZSBkZW1hbmQuXG4gKiAqIFdoZW4gQ1BVIHVzYWdlIGlzIGJlbG93IDMwJSwgc2NhbGUgZG93biB0aGUgbnVtYmVyIG9mIHJlcGxpY2FzIHRvIHNhdmUgcmVzb3VyY2VzLlxuICogKiBXaGVuIGEgc2VydmljZSBpcyBleHBlcmllbmNpbmcgYSBzcGlrZSBpbiB0cmFmZmljLCBzY2FsZSB1cCB0aGUgbnVtYmVyIG9mIHJlcGxpY2FzXG4gKiAgIHRvIG1lZXQgdGhlIGRlbWFuZC4gVGhlbiwgd2hlbiB0aGUgdHJhZmZpYyBzdWJzaWRlcywgc2NhbGUgZG93biB0aGUgbnVtYmVyIG9mXG4gKiAgIHJlcGxpY2FzIHRvIHNhdmUgcmVzb3VyY2VzLlxuICpcbiAqIFRoZSBhdXRvc2NhbGVyIHVzZXMgdGhlIGZvbGxvd2luZyBhbGdvcml0aG0gdG8gZGV0ZXJtaW5lIHRoZSBudW1iZXIgb2YgcmVwbGljYXMgdG8gc2NhbGU6XG4gKlxuICogYGRlc2lyZWRSZXBsaWNhcyA9IGNlaWxbY3VycmVudFJlcGxpY2FzICogKCBjdXJyZW50TWV0cmljVmFsdWUgLyBkZXNpcmVkTWV0cmljVmFsdWUgKV1gXG4gKlxuICogSG9yaXpvbnRhbFBvZEF1dG9zY2FsZXIncyBjYW4gYmUgdXNlZCB0byB3aXRoIGFueSBgU2NhbGFibGVgIHdvcmtsb2FkOlxuICogKiBEZXBsb3ltZW50XG4gKiAqIFN0YXRlZnVsU2V0XG4gKlxuICogKipUYXJnZXRzIHRoYXQgYWxyZWFkeSBoYXZlIGEgcmVwbGljYSBjb3VudCBkZWZpbmVkOioqXG4gKlxuICogUmVtb3ZlIGFueSByZXBsaWNhIGNvdW50cyBmcm9tIHRoZSB0YXJnZXQgcmVzb3VyY2UgYmVmb3JlIGFzc29jaWF0aW5nIHdpdGggYVxuICogSG9yaXpvbnRhbFBvZEF1dG9zY2FsZXIuIElmIHRoaXMgaXNuJ3QgZG9uZSwgdGhlbiBhbnkgdGltZSBhIGNoYW5nZSB0byB0aGF0IG9iamVjdCBpcyBhcHBsaWVkLFxuICogS3ViZXJuZXRlcyB3aWxsIHNjYWxlIHRoZSBjdXJyZW50IG51bWJlciBvZiBQb2RzIHRvIHRoZSB2YWx1ZSBvZiB0aGUgdGFyZ2V0LnJlcGxpY2FzIGtleS4gVGhpc1xuICogbWF5IG5vdCBiZSBkZXNpcmVkIGFuZCBjb3VsZCBsZWFkIHRvIHVuZXhwZWN0ZWQgYmVoYXZpb3IuXG4gKlxuICogQHNlZSBodHRwczovL2t1YmVybmV0ZXMuaW8vZG9jcy90YXNrcy9ydW4tYXBwbGljYXRpb24vaG9yaXpvbnRhbC1wb2QtYXV0b3NjYWxlLyNpbXBsaWNpdC1tYWludGVuYW5jZS1tb2RlLWRlYWN0aXZhdGlvblxuICpcbiAqIEBleGFtcGxlXG4gKiBjb25zdCBiYWNrZW5kID0gbmV3IGtwbHVzLkRlcGxveW1lbnQodGhpcywgJ0JhY2tlbmQnLCAuLi4pO1xuICpcbiAqIGNvbnN0IGhwYSA9IG5ldyBrcGx1cy5Ib3Jpem9udGFsUG9kQXV0b3NjYWxlcihjaGFydCwgJ0hwYScsIHtcbiAqICB0YXJnZXQ6IGJhY2tlbmQsXG4gKiAgbWF4UmVwbGljYXM6IDEwLFxuICogIHNjYWxlVXA6IHtcbiAqICAgIHBvbGljaWVzOiBbXG4gKiAgICAgIHtcbiAqICAgICAgICByZXBsaWNhczoga3BsdXMuUmVwbGljYXMuYWJzb2x1dGUoMyksXG4gKiAgICAgICAgZHVyYXRpb246IER1cmF0aW9uLm1pbnV0ZXMoNSksXG4gKiAgICAgIH0sXG4gKiAgICBdLFxuICogIH0sXG4gKiB9KTtcbiAqL1xuZXhwb3J0IGNsYXNzIEhvcml6b250YWxQb2RBdXRvc2NhbGVyIGV4dGVuZHMgUmVzb3VyY2Uge1xuICAvKipcbiAqIEBzZWUgYmFzZS5SZXNvdXJjZS5hcGlPYmplY3RcbiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgYXBpT2JqZWN0OiBBcGlPYmplY3Q7XG4gIHB1YmxpYyByZWFkb25seSByZXNvdXJjZVR5cGUgPSAnaG9yaXpvbnRhbHBvZGF1dG9zY2FsZXInO1xuICAvKipcbiAgICogVGhlIHdvcmtsb2FkIHRvIHNjYWxlIHVwIG9yIGRvd24uXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgdGFyZ2V0OiBJU2NhbGFibGU7XG4gIC8qKlxuICAgKiBUaGUgbWF4aW11bSBudW1iZXIgb2YgcmVwbGljYXMgdGhhdCBjYW4gYmUgc2NhbGVkIHVwIHRvLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IG1heFJlcGxpY2FzOiBudW1iZXI7XG4gIC8qKlxuICAgKiBUaGUgbWluaW11bSBudW1iZXIgb2YgcmVwbGljYXMgdGhhdCBjYW4gYmUgc2NhbGVkIGRvd24gdG8uXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgbWluUmVwbGljYXM6IG51bWJlcjtcbiAgLyoqXG4gICAqIFRoZSBtZXRyaWMgY29uZGl0aW9ucyB0aGF0IHRyaWdnZXIgYSBzY2FsZSB1cCBvciBzY2FsZSBkb3duLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IG1ldHJpY3M/OiBNZXRyaWNbXTtcbiAgLyoqXG4gICAqIFRoZSBzY2FsaW5nIGJlaGF2aW9yIHdoZW4gc2NhbGluZyB1cC5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBzY2FsZVVwOiBTY2FsaW5nUnVsZXM7XG4gIC8qKlxuICAgKiBUaGUgc2NhbGluZyBiZWhhdmlvciB3aGVuIHNjYWxpbmcgZG93bi5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBzY2FsZURvd246IFNjYWxpbmdSdWxlcztcblxuICBwcml2YXRlIHJlYWRvbmx5IF9kZWZhdWx0U2NhbGluZ0R1cmF0aW9uID0gRHVyYXRpb24uc2Vjb25kcygxNSk7XG5cblxuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogSG9yaXpvbnRhbFBvZEF1dG9zY2FsZXJQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG5cbiAgICB0aGlzLmFwaU9iamVjdCA9IG5ldyBrOHMuS3ViZUhvcml6b250YWxQb2RBdXRvc2NhbGVyVjIodGhpcywgJ1Jlc291cmNlJywge1xuICAgICAgbWV0YWRhdGE6IHByb3BzLm1ldGFkYXRhLFxuICAgICAgc3BlYzogTGF6eS5hbnkoeyBwcm9kdWNlOiAoKSA9PiB0aGlzLl90b0t1YmUoKSB9KSxcbiAgICB9KTtcblxuICAgIGlmIChwcm9wcz8ubWluUmVwbGljYXMgJiYgcHJvcHMubWluUmVwbGljYXMgPiBwcm9wcy5tYXhSZXBsaWNhcykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGAnbWluUmVwbGljYXMnICgke3Byb3BzLm1pblJlcGxpY2FzfSkgbXVzdCBiZSBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gJ21heFJlcGxpY2FzJyAoJHtwcm9wcy5tYXhSZXBsaWNhc30pIGluIG9yZGVyIGZvciBIb3Jpem9udGFsUG9kQXV0b3NjYWxlciB0byBzY2FsZS5gKTtcbiAgICB9XG4gICAgaWYgKHByb3BzPy5zY2FsZVVwPy5zdGFiaWxpemF0aW9uV2luZG93ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHRoaXMuX3ZhbGlkYXRlU3RhYmlsaXphdGlvbldpbmRvdygnc2NhbGVVcCcsIHByb3BzLnNjYWxlVXAuc3RhYmlsaXphdGlvbldpbmRvdyk7XG4gICAgfVxuICAgIGlmIChwcm9wcz8uc2NhbGVEb3duPy5zdGFiaWxpemF0aW9uV2luZG93ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHRoaXMuX3ZhbGlkYXRlU3RhYmlsaXphdGlvbldpbmRvdygnc2NhbGVEb3duJywgcHJvcHMuc2NhbGVEb3duLnN0YWJpbGl6YXRpb25XaW5kb3cpO1xuICAgIH1cbiAgICBpZiAocHJvcHM/LnNjYWxlVXA/LnBvbGljaWVzPy5sZW5ndGgpIHtcbiAgICAgIHRoaXMuX3ZhbGlkYXRlU2NhbGluZ1BvbGljaWVzKCdzY2FsZVVwJywgcHJvcHMuc2NhbGVVcC5wb2xpY2llcyk7XG4gICAgfVxuICAgIGlmIChwcm9wcz8uc2NhbGVEb3duPy5wb2xpY2llcz8ubGVuZ3RoKSB7XG4gICAgICB0aGlzLl92YWxpZGF0ZVNjYWxpbmdQb2xpY2llcygnc2NhbGVEb3duJywgcHJvcHMuc2NhbGVEb3duLnBvbGljaWVzKTtcbiAgICB9XG5cbiAgICB0aGlzLnRhcmdldCA9IHByb3BzLnRhcmdldDtcbiAgICB0aGlzLnRhcmdldC5tYXJrSGFzQXV0b3NjYWxlcigpO1xuICAgIHRoaXMubWF4UmVwbGljYXMgPSBwcm9wcy5tYXhSZXBsaWNhcztcbiAgICB0aGlzLm1pblJlcGxpY2FzID0gcHJvcHMubWluUmVwbGljYXMgPz8gMTtcbiAgICB0aGlzLm1ldHJpY3MgPSBwcm9wcy5tZXRyaWNzO1xuICAgIHRoaXMuc2NhbGVVcCA9IHtcbiAgICAgIHN0cmF0ZWd5OiBTY2FsaW5nU3RyYXRlZ3kuTUFYX0NIQU5HRSxcbiAgICAgIHN0YWJpbGl6YXRpb25XaW5kb3c6IER1cmF0aW9uLnNlY29uZHMoMCksXG4gICAgICAuLi5wcm9wcy5zY2FsZVVwLFxuICAgICAgcG9saWNpZXM6IHByb3BzLnNjYWxlVXA/LnBvbGljaWVzPy5tYXAoKHApID0+ICh7IGR1cmF0aW9uOiB0aGlzLl9kZWZhdWx0U2NhbGluZ0R1cmF0aW9uLCAuLi5wIH0pKSA/PyBbXG4gICAgICAgIHtcbiAgICAgICAgICByZXBsaWNhczogUmVwbGljYXMuYWJzb2x1dGUoNCksXG4gICAgICAgICAgZHVyYXRpb246IER1cmF0aW9uLm1pbnV0ZXMoMSksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICByZXBsaWNhczogUmVwbGljYXMucGVyY2VudCgyMDApLFxuICAgICAgICAgIGR1cmF0aW9uOiBEdXJhdGlvbi5taW51dGVzKDEpLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9O1xuICAgIGlmIChwcm9wcz8uc2NhbGVVcD8ucG9saWNpZXM/Lmxlbmd0aCkge1xuICAgICAgdGhpcy5fdmFsaWRhdGVTY2FsaW5nUG9saWNpZXMoJ3NjYWxlVXAnLCBwcm9wcy5zY2FsZVVwLnBvbGljaWVzKTtcbiAgICB9XG4gICAgdGhpcy5zY2FsZURvd24gPSB7XG4gICAgICBzdHJhdGVneTogU2NhbGluZ1N0cmF0ZWd5Lk1BWF9DSEFOR0UsXG4gICAgICBzdGFiaWxpemF0aW9uV2luZG93OiBEdXJhdGlvbi5taW51dGVzKDUpLFxuICAgICAgLi4ucHJvcHMuc2NhbGVEb3duLFxuICAgICAgcG9saWNpZXM6IHByb3BzLnNjYWxlRG93bj8ucG9saWNpZXM/Lm1hcCgocCkgPT4gKHsgZHVyYXRpb246IHRoaXMuX2RlZmF1bHRTY2FsaW5nRHVyYXRpb24sIC4uLnAgfSkpID8/IFtcbiAgICAgICAge1xuICAgICAgICAgIHJlcGxpY2FzOiBSZXBsaWNhcy5hYnNvbHV0ZSh0aGlzLm1pblJlcGxpY2FzKSxcbiAgICAgICAgICBkdXJhdGlvbjogRHVyYXRpb24ubWludXRlcyg1KSxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfTtcblxuICAgIHRoaXMubm9kZS5hZGRWYWxpZGF0aW9uKHsgdmFsaWRhdGU6ICgpID0+IHRoaXMuX3ZhbGlkYXRlVGFyZ2V0UmVwbGljYXMoKSB9KTtcbiAgICB0aGlzLm5vZGUuYWRkVmFsaWRhdGlvbih7IHZhbGlkYXRlOiAoKSA9PiB0aGlzLl92YWxpZGF0ZVRhcmdldENvbnRhaW5lcnMoKSB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSBhIGxpc3Qgb2Ygc2NhbGluZyBwb2xpY2llcy5cbiAgICogQGludGVybmFsXG4gICAqL1xuICBwcml2YXRlIF92YWxpZGF0ZVNjYWxpbmdQb2xpY2llcyhkaXJlY3Rpb246ICdzY2FsZVVwJyB8ICdzY2FsZURvd24nLCBwb2xpY2llczogU2NhbGluZ1BvbGljeVtdKSB7XG4gICAgcG9saWNpZXMuZm9yRWFjaCgocCkgPT4ge1xuICAgICAgaWYgKHAuZHVyYXRpb24gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICB0aGlzLl92YWxpZGF0ZVNjYWxpbmdQb2xpY3lEdXJhdGlvbihkaXJlY3Rpb24sIHAuZHVyYXRpb24pO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIGBTY2FsaW5nUG9saWN5LmR1cmF0aW9uYCBpcyB3aXRoaW4gdGhlIGFsbG93ZWQgcmFuZ2UuXG4gICAqXG4gICAqIGBkdXJhdGlvbmAgcmFuZ2U6IDEgc2Vjb25kIC0gMzAgbWluXG4gICAqXG4gICAqIEt1YmVybmV0ZXMgbmFtZTogYFNjYWxpbmdQb2xpY3kucGVyaW9kU2Vjb25kc2AuXG4gICAqIEBzZWUgaHR0cHM6Ly9rdWJlcm5ldGVzLmlvL2RvY3MvcmVmZXJlbmNlL2t1YmVybmV0ZXMtYXBpL3dvcmtsb2FkLXJlc291cmNlcy9ob3Jpem9udGFsLXBvZC1hdXRvc2NhbGVyLXYyLyNIb3Jpem9udGFsUG9kQXV0b3NjYWxlclNwZWNcbiAgICogQGludGVybmFsXG4gICAqL1xuICBwcml2YXRlIF92YWxpZGF0ZVNjYWxpbmdQb2xpY3lEdXJhdGlvbihkaXJlY3Rpb246ICdzY2FsZVVwJyB8ICdzY2FsZURvd24nLCBkdXJhdGlvbjogRHVyYXRpb24pIHtcbiAgICBjb25zdCBwZXJpb2RTZWNvbmRzID0gZHVyYXRpb24udG9TZWNvbmRzKCkgPz8gMTU7XG4gICAgY29uc3QgaXNXaXRoaW5SYW5nZSA9IEJvb2xlYW4oMCA8IHBlcmlvZFNlY29uZHMgJiYgcGVyaW9kU2Vjb25kcyA8PSAxODAwKTtcbiAgICBpZiAoIWlzV2l0aGluUmFuZ2UpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgJyR7ZGlyZWN0aW9ufS5wb2xpY2llcycgZHVyYXRpb24gKCR7ZHVyYXRpb24udG9IdW1hblN0cmluZygpfSkgaXMgb3V0c2lkZSBvZiB0aGUgYWxsb3dlZCByYW5nZS4gTXVzdCBiZSBhdCBsZWFzdCAxIHNlY29uZCBsb25nIGFuZCBubyBsb25nZXIgdGhhbiAzMCBtaW51dGVzLmApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSBgU2NhbGluZ1J1bGVzLnN0YWJpbGl6YXRpb25XaW5kb3dgIGlzIHdpdGhpbiB0aGUgYWxsb3dlZCByYW5nZS5cbiAgICpcbiAgICogYHN0YWJpbGl6YXRpb25XaW5kb3dgIHJhbmdlOiAwIHNlY29uZHMgLSAxIGhvdXJcbiAgICpcbiAgICogQHNlZSBodHRwczovL2t1YmVybmV0ZXMuaW8vZG9jcy9yZWZlcmVuY2Uva3ViZXJuZXRlcy1hcGkvd29ya2xvYWQtcmVzb3VyY2VzL2hvcml6b250YWwtcG9kLWF1dG9zY2FsZXItdjIvI0hvcml6b250YWxQb2RBdXRvc2NhbGVyU3BlY1xuICAgKiBAaW50ZXJuYWxcbiAgICovXG4gIHByaXZhdGUgX3ZhbGlkYXRlU3RhYmlsaXphdGlvbldpbmRvdyhkaXJlY3Rpb246ICdzY2FsZVVwJyB8ICdzY2FsZURvd24nLCB3aW5kb3c6IER1cmF0aW9uKSB7XG4gICAgY29uc3Qgd2luZG93U2Vjb25kcyA9IHdpbmRvdy50b1NlY29uZHMoKTtcbiAgICBjb25zdCBpc1dpdGhpblJhbmdlID0gQm9vbGVhbigwIDw9IHdpbmRvd1NlY29uZHMgJiYgd2luZG93U2Vjb25kcyA8PSAzNjAwKTtcbiAgICBpZiAoIWlzV2l0aGluUmFuZ2UpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgJyR7ZGlyZWN0aW9ufS5zdGFiaWxpemF0aW9uV2luZG93JyAoJHt3aW5kb3cudG9IdW1hblN0cmluZygpfSkgbXVzdCBiZSAwIHNlY29uZHMgb3IgbW9yZSB3aXRoIGEgbWF4IG9mIDEgaG91ci5gKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR3VhcmFudGVlIHRoZSBIUEEgaGFzIGEgbWV0cmljIHRvIHNjYWxlIG9uLlxuICAgKiBWZXJpZnkgdGhhdCBtZXRyaWNzIGFyZSBjb25maWd1cmVkLCBpZiBub3QgY2hlY2sgZXZlcnkgcG9kIGNvbnRhaW5lciBoYXMgYSByZXNvdXJjZSBsaW1pdCBvclxuICAgKiByZXF1ZXN0IGRlZmluZWQuXG4gICAqIEBpbnRlcm5hbFxuICAgKi9cbiAgcHJpdmF0ZSBfdmFsaWRhdGVUYXJnZXRDb250YWluZXJzKCkge1xuICAgIGNvbnN0IGNvbnRhaW5lcnMgPSB0aGlzLnRhcmdldC50b1NjYWxpbmdUYXJnZXQoKS5jb250YWluZXJzO1xuICAgIGNvbnN0IGhhc1Jlc291cmNlQ29uc3RyYWludHMgPSBjb250YWluZXJzLnNvbWUoKGMpID0+IHRoaXMuX2hhc1JlcXVlc3RzT3JMaW1pdHMoYykpO1xuICAgIGlmICghaGFzUmVzb3VyY2VDb25zdHJhaW50cyAmJiAhdGhpcy5tZXRyaWNzKSB7XG4gICAgICByZXR1cm4gWydJZiBIb3Jpem9udGFsUG9kQXV0b3NjYWxlciBkb2VzIG5vdCBoYXZlIG1ldHJpY3MgZGVmaW5lZCwgdGhlbiBldmVyeSBjb250YWluZXIgaW4gdGhlIHRhcmdldCBtdXN0IGhhdmUgYSBDUFUgb3IgbWVtb3J5IHJlc291cmNlIGNvbnN0cmFpbnQgZGVmaW5lZC4nXTtcbiAgICB9XG4gICAgcmV0dXJuIFtdO1xuICB9XG5cbiAgLyoqXG4gICAqIFByZXZlbnQgdGhlIEhQQSBmcm9tIHNjYWxpbmcgYSB0YXJnZXQgd2l0aCBhIHJlcGxpY2EgY291bnQgZGVmaW5lZC5cbiAgICogQHNlZSBodHRwczovL2t1YmVybmV0ZXMuaW8vZG9jcy90YXNrcy9ydW4tYXBwbGljYXRpb24vaG9yaXpvbnRhbC1wb2QtYXV0b3NjYWxlLyNpbXBsaWNpdC1tYWludGVuYW5jZS1tb2RlLWRlYWN0aXZhdGlvblxuICAgKiBAaW50ZXJuYWxcbiAgICovXG4gIHByaXZhdGUgX3ZhbGlkYXRlVGFyZ2V0UmVwbGljYXMoKSB7XG4gICAgY29uc3QgcmVwbGljYXMgPSB0aGlzLnRhcmdldC50b1NjYWxpbmdUYXJnZXQoKS5yZXBsaWNhcztcbiAgICBpZiAocmVwbGljYXMpIHtcbiAgICAgIHJldHVybiBbXG4gICAgICAgIGBIb3Jpem9udGFsUG9kQXV0b3NjYWxlciB0YXJnZXQgY2Fubm90IGhhdmUgYSBmaXhlZCBudW1iZXIgb2YgcmVwbGljYXMgKCR7cmVwbGljYXN9KS5gLFxuICAgICAgXTtcbiAgICB9XG4gICAgcmV0dXJuIFtdO1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIHRoYXQgdGhlIGNvbnRhaW5lciBoYXMgYXQgbGVhc3Qgb25lIENQVS9tZW1vcnkgcmVxdWVzdC9saW1pdCBkZWZpbmVkLlxuICAgKiBAaW50ZXJuYWxcbiAgICovXG4gIHByaXZhdGUgX2hhc1JlcXVlc3RzT3JMaW1pdHMoYzogY29udGFpbmVyLkNvbnRhaW5lcik6IEJvb2xlYW4ge1xuICAgIGNvbnN0IGhhc1JlcXVlc3RzID0gYy5yZXNvdXJjZXM/LmNwdT8ucmVxdWVzdCB8fCBjLnJlc291cmNlcz8ubWVtb3J5Py5yZXF1ZXN0O1xuICAgIGNvbnN0IGhhc0xpbWl0cyA9IGMucmVzb3VyY2VzPy5jcHU/LmxpbWl0IHx8IGMucmVzb3VyY2VzPy5tZW1vcnk/LmxpbWl0O1xuICAgIHJldHVybiBCb29sZWFuKGhhc1JlcXVlc3RzIHx8IGhhc0xpbWl0cyk7XG4gIH1cblxuXG4gIC8qKlxuICAgKiBAaW50ZXJuYWxcbiAgICovXG4gIHB1YmxpYyBfdG9LdWJlKCk6IGs4cy5Ib3Jpem9udGFsUG9kQXV0b3NjYWxlclNwZWNWMiB7XG4gICAgY29uc3Qgc2NhbGluZ1RhcmdldCA9IHRoaXMudGFyZ2V0LnRvU2NhbGluZ1RhcmdldCgpO1xuICAgIHJldHVybiB7XG4gICAgICBtYXhSZXBsaWNhczogdGhpcy5tYXhSZXBsaWNhcyxcbiAgICAgIG1pblJlcGxpY2FzOiB0aGlzLm1pblJlcGxpY2FzLFxuICAgICAgc2NhbGVUYXJnZXRSZWY6IHtcbiAgICAgICAgYXBpVmVyc2lvbjogc2NhbGluZ1RhcmdldC5hcGlWZXJzaW9uLFxuICAgICAgICBuYW1lOiBzY2FsaW5nVGFyZ2V0Lm5hbWUsXG4gICAgICAgIGtpbmQ6IHNjYWxpbmdUYXJnZXQua2luZCxcbiAgICAgIH0sXG4gICAgICBtZXRyaWNzOiB0aGlzLm1ldHJpY3M/Lm1hcChtID0+IG0uX3RvS3ViZSgpKSxcbiAgICAgIGJlaGF2aW9yOiB7XG4gICAgICAgIHNjYWxlVXA6IHtcbiAgICAgICAgICBwb2xpY2llczogdGhpcy5zY2FsZVVwLnBvbGljaWVzPy5tYXAoKHApID0+ICh7XG4gICAgICAgICAgICAuLi5wLnJlcGxpY2FzLl90b0t1YmUoKSxcbiAgICAgICAgICAgIHBlcmlvZFNlY29uZHM6IHAuZHVyYXRpb24/LnRvU2Vjb25kcygpID8/IHRoaXMuX2RlZmF1bHRTY2FsaW5nRHVyYXRpb24udG9TZWNvbmRzKCksXG4gICAgICAgICAgfSkpLFxuICAgICAgICAgIHNlbGVjdFBvbGljeTogdGhpcy5zY2FsZVVwLnN0cmF0ZWd5LFxuICAgICAgICAgIHN0YWJpbGl6YXRpb25XaW5kb3dTZWNvbmRzOiB0aGlzLnNjYWxlVXAuc3R