UNPKG

@aws-cdk/core

Version:

AWS Cloud Development Kit Core Library

567 lines 76.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const cxapi = require("@aws-cdk/cx-api"); const cx_api_1 = require("@aws-cdk/cx-api"); const fs = require("fs"); const path = require("path"); const construct_1 = require("./construct"); const context_provider_1 = require("./context-provider"); const cloudformation_lang_1 = require("./private/cloudformation-lang"); const logical_id_1 = require("./private/logical-id"); const resolve_1 = require("./private/resolve"); const uniqueid_1 = require("./private/uniqueid"); const STACK_SYMBOL = Symbol.for('@aws-cdk/core.Stack'); const VALID_STACK_NAME_REGEX = /^[A-Za-z][A-Za-z0-9-]*$/; /** * A root construct which represents a single CloudFormation stack. */ class Stack extends construct_1.Construct { /** * Creates a new stack. * * @param scope Parent of this stack, usually a Program instance. * @param name The name of the CloudFormation stack. Defaults to "Stack". * @param props Stack properties. */ constructor(scope, name, props = {}) { // For unit test convenience parents are optional, so bypass the type check when calling the parent. super(scope, name); /** * Options for CloudFormation template (like version, transform, description). */ this.templateOptions = {}; /** * Other stacks this stack depends on */ this._stackDependencies = new Set(); /** * Lists all missing contextual information. * This is returned when the stack is synthesized under the 'missing' attribute * and allows tooling to obtain the context and re-synthesize. */ this._missingContext = new Array(); Object.defineProperty(this, STACK_SYMBOL, { value: true }); this._logicalIds = new logical_id_1.LogicalIDs(); const { account, region, environment } = this.parseEnvironment(props.env); this.account = account; this.region = region; this.environment = environment; this.stackName = props.stackName !== undefined ? props.stackName : this.calculateStackName(); this.tags = new tag_manager_1.TagManager(cfn_resource_1.TagType.KEY_VALUE, 'aws:cdk:stack', props.tags); if (!VALID_STACK_NAME_REGEX.test(this.stackName)) { throw new Error(`Stack name must match the regular expression: ${VALID_STACK_NAME_REGEX.toString()}, got '${name}'`); } this.templateFile = `${this.stackName}.template.json`; } /** * Return whether the given object is a Stack. * * We do attribute detection since we can't reliably use 'instanceof'. */ static isStack(x) { return x !== null && typeof (x) === 'object' && STACK_SYMBOL in x; } /** * Looks up the first stack scope in which `construct` is defined. Fails if there is no stack up the tree. * @param construct The construct to start the search from. */ static of(construct) { return _lookup(construct); function _lookup(c) { if (Stack.isStack(c)) { return c; } if (!c.node.scope) { throw new Error(`No stack could be identified for the construct at path ${construct.node.path}`); } return _lookup(c.node.scope); } } /** * Resolve a tokenized value in the context of the current stack. */ resolve(obj) { return resolve_1.resolve(obj, { scope: this, prefix: [], resolver: cloudformation_lang_1.CLOUDFORMATION_TOKEN_RESOLVER, preparing: false }); } /** * Convert an object, potentially containing tokens, to a JSON string */ toJsonString(obj, space) { return cloudformation_lang_1.CloudFormationLang.toJSON(obj, space).toString(); } /** * Indicate that a context key was expected * * Contains instructions which will be emitted into the cloud assembly on how * the key should be supplied. * * @param report The set of parameters needed to obtain the context */ reportMissingContext(report) { this._missingContext.push(report); } /** * Rename a generated logical identities * * To modify the naming scheme strategy, extend the `Stack` class and * override the `createNamingScheme` method. */ renameLogicalId(oldId, newId) { this._logicalIds.addRename(oldId, newId); } /** * Allocates a stack-unique CloudFormation-compatible logical identity for a * specific resource. * * This method is called when a `CfnElement` is created and used to render the * initial logical identity of resources. Logical ID renames are applied at * this stage. * * This method uses the protected method `allocateLogicalId` to render the * logical ID for an element. To modify the naming scheme, extend the `Stack` * class and override this method. * * @param element The CloudFormation element for which a logical identity is * needed. */ getLogicalId(element) { const logicalId = this.allocateLogicalId(element); return this._logicalIds.applyRename(logicalId); } /** * Add a dependency between this stack and another stack */ addDependency(stack, reason) { if (stack === this) { return; } // Can ignore a dependency on self reason = reason || 'dependency added using stack.addDependency()'; const dep = stack.stackDependencyReasons(this); if (dep !== undefined) { // tslint:disable-next-line:max-line-length throw new Error(`'${stack.node.path}' depends on '${this.node.path}' (${dep.join(', ')}). Adding this dependency (${reason}) would create a cyclic reference.`); } this._stackDependencies.add({ stack, reason }); } /** * Return the stacks this stack depends on */ get dependencies() { return Array.from(this._stackDependencies.values()).map(d => d.stack); } /** * The partition in which this stack is defined */ get partition() { // Always return a non-scoped partition intrinsic. These will usually // be used to construct an ARN, but there are no cross-partition // calls anyway. return cfn_pseudo_1.Aws.PARTITION; } /** * The Amazon domain suffix for the region in which this stack is defined */ get urlSuffix() { // Since URL Suffix always follows partition, it is unscoped like partition is. return cfn_pseudo_1.Aws.URL_SUFFIX; } /** * The ID of the stack * * @example After resolving, looks like arn:aws:cloudformation:us-west-2:123456789012:stack/teststack/51af3dc0-da77-11e4-872e-1234567db123 */ get stackId() { return new cfn_pseudo_1.ScopedAws(this).stackId; } /** * Returns the list of notification Amazon Resource Names (ARNs) for the current stack. */ get notificationArns() { return new cfn_pseudo_1.ScopedAws(this).notificationArns; } /** * Creates an ARN from components. * * If `partition`, `region` or `account` are not specified, the stack's * partition, region and account will be used. * * If any component is the empty string, an empty string will be inserted * into the generated ARN at the location that component corresponds to. * * The ARN will be formatted as follows: * * arn:{partition}:{service}:{region}:{account}:{resource}{sep}}{resource-name} * * The required ARN pieces that are omitted will be taken from the stack that * the 'scope' is attached to. If all ARN pieces are supplied, the supplied scope * can be 'undefined'. */ formatArn(components) { return arn_1.Arn.format(components, this); } /** * Given an ARN, parses it and returns components. * * If the ARN is a concrete string, it will be parsed and validated. The * separator (`sep`) will be set to '/' if the 6th component includes a '/', * in which case, `resource` will be set to the value before the '/' and * `resourceName` will be the rest. In case there is no '/', `resource` will * be set to the 6th components and `resourceName` will be set to the rest * of the string. * * If the ARN includes tokens (or is a token), the ARN cannot be validated, * since we don't have the actual value yet at the time of this function * call. You will have to know the separator and the type of ARN. The * resulting `ArnComponents` object will contain tokens for the * subexpressions of the ARN, not string literals. In this case this * function cannot properly parse the complete final resourceName (path) out * of ARNs that use '/' to both separate the 'resource' from the * 'resourceName' AND to subdivide the resourceName further. For example, in * S3 ARNs: * * arn:aws:s3:::my_corporate_bucket/path/to/exampleobject.png * * After parsing the resourceName will not contain * 'path/to/exampleobject.png' but simply 'path'. This is a limitation * because there is no slicing functionality in CloudFormation templates. * * @param arn The ARN string to parse * @param sepIfToken The separator used to separate resource from resourceName * @param hasName Whether there is a name component in the ARN at all. For * example, SNS Topics ARNs have the 'resource' component contain the topic * name, and no 'resourceName' component. * * @returns an ArnComponents object which allows access to the various * components of the ARN. * * @returns an ArnComponents object which allows access to the various * components of the ARN. */ parseArn(arn, sepIfToken = '/', hasName = true) { return arn_1.Arn.parse(arn, sepIfToken, hasName); } /** * Returnst the list of AZs that are availability in the AWS environment * (account/region) associated with this stack. * * If the stack is environment-agnostic (either account and/or region are * tokens), this property will return an array with 2 tokens that will resolve * at deploy-time to the first two availability zones returned from CloudFormation's * `Fn::GetAZs` intrinsic function. * * If they are not available in the context, returns a set of dummy values and * reports them as missing, and let the CLI resolve them by calling EC2 * `DescribeAvailabilityZones` on the target environment. */ get availabilityZones() { // if account/region are tokens, we can't obtain AZs through the context // provider, so we fallback to use Fn::GetAZs. the current lowest common // denominator is 2 AZs across all AWS regions. const agnostic = token_1.Token.isUnresolved(this.account) || token_1.Token.isUnresolved(this.region); if (agnostic) { return this.node.tryGetContext(cxapi.AVAILABILITY_ZONE_FALLBACK_CONTEXT_KEY) || [ cfn_fn_1.Fn.select(0, cfn_fn_1.Fn.getAzs()), cfn_fn_1.Fn.select(1, cfn_fn_1.Fn.getAzs()) ]; } const value = context_provider_1.ContextProvider.getValue(this, { provider: cxapi.AVAILABILITY_ZONE_PROVIDER, dummyValue: ['dummy1a', 'dummy1b', 'dummy1c'], }).value; if (!Array.isArray(value)) { throw new Error(`Provider ${cxapi.AVAILABILITY_ZONE_PROVIDER} expects a list`); } return value; } /** * Returns the naming scheme used to allocate logical IDs. By default, uses * the `HashedAddressingScheme` but this method can be overridden to customize * this behavior. * * In order to make sure logical IDs are unique and stable, we hash the resource * construct tree path (i.e. toplevel/secondlevel/.../myresource) and add it as * a suffix to the path components joined without a separator (CloudFormation * IDs only allow alphanumeric characters). * * The result will be: * * <path.join('')><md5(path.join('/')> * "human" "hash" * * If the "human" part of the ID exceeds 240 characters, we simply trim it so * the total ID doesn't exceed CloudFormation's 255 character limit. * * We only take 8 characters from the md5 hash (0.000005 chance of collision). * * Special cases: * * - If the path only contains a single component (i.e. it's a top-level * resource), we won't add the hash to it. The hash is not needed for * disamiguation and also, it allows for a more straightforward migration an * existing CloudFormation template to a CDK stack without logical ID changes * (or renames). * - For aesthetic reasons, if the last components of the path are the same * (i.e. `L1/L2/Pipeline/Pipeline`), they will be de-duplicated to make the * resulting human portion of the ID more pleasing: `L1L2Pipeline<HASH>` * instead of `L1L2PipelinePipeline<HASH>` * - If a component is named "Default" it will be omitted from the path. This * allows refactoring higher level abstractions around constructs without affecting * the IDs of already deployed resources. * - If a component is named "Resource" it will be omitted from the user-visible * path, but included in the hash. This reduces visual noise in the human readable * part of the identifier. * * @param cfnElement The element for which the logical ID is allocated. */ allocateLogicalId(cfnElement) { const scopes = cfnElement.node.scopes; const stackIndex = scopes.indexOf(cfnElement.stack); const pathComponents = scopes.slice(stackIndex + 1).map(x => x.node.id); return uniqueid_1.makeUniqueId(pathComponents); } /** * Validate stack name * * CloudFormation stack names can include dashes in addition to the regular identifier * character classes, and we don't allow one of the magic markers. * * @internal */ _validateId(name) { if (name && !VALID_STACK_NAME_REGEX.test(name)) { throw new Error(`Stack name must match the regular expression: ${VALID_STACK_NAME_REGEX.toString()}, got '${name}'`); } } /** * Prepare stack * * Find all CloudFormation references and tell them we're consuming them. * * Find all dependencies as well and add the appropriate DependsOn fields. */ prepare() { // References for (const ref of this.node.references) { if (cfn_reference_1.CfnReference.isCfnReference(ref.reference)) { ref.reference.consumeFromStack(this, ref.source); } } // Resource dependencies for (const dependency of this.node.dependencies) { const theirStack = Stack.of(dependency.target); if (theirStack !== undefined && theirStack !== this) { this.addDependency(theirStack); } else { for (const target of findResources([dependency.target])) { for (const source of findResources([dependency.source])) { source.addDependsOn(target); } } } } if (this.tags.hasTags()) { this.node.addMetadata(cxapi.STACK_TAGS_METADATA_KEY, this.tags.renderTags()); } } synthesize(session) { const builder = session.assembly; // write the CloudFormation template as a JSON file const outPath = path.join(builder.outdir, this.templateFile); fs.writeFileSync(outPath, JSON.stringify(this._toCloudFormation(), undefined, 2)); const deps = this.dependencies.map(s => s.stackName); const meta = this.collectMetadata(); const properties = { templateFile: this.templateFile }; // add an artifact that represents this stack builder.addArtifact(this.stackName, { type: cxapi.ArtifactType.AWS_CLOUDFORMATION_STACK, environment: this.environment, properties, dependencies: deps.length > 0 ? deps : undefined, metadata: Object.keys(meta).length > 0 ? meta : undefined, }); for (const ctx of this._missingContext) { builder.addMissing(ctx); } } /** * Returns the CloudFormation template for this stack by traversing * the tree and invoking _toCloudFormation() on all Entity objects. * * @internal */ _toCloudFormation() { if (this.templateOptions.transform) { // tslint:disable-next-line: max-line-length this.node.addWarning('This stack is using the deprecated `templateOptions.transform` property. Consider switching to `templateOptions.transforms`.'); if (!this.templateOptions.transforms) { this.templateOptions.transforms = []; } if (this.templateOptions.transforms.indexOf(this.templateOptions.transform) === -1) { this.templateOptions.transforms.unshift(this.templateOptions.transform); } } const template = { Description: this.templateOptions.description, Transform: extractSingleValue(this.templateOptions.transforms), AWSTemplateFormatVersion: this.templateOptions.templateFormatVersion, Metadata: this.templateOptions.metadata }; const elements = cfnElements(this); const fragments = elements.map(e => this.resolve(e._toCloudFormation())); // merge in all CloudFormation fragments collected from the tree for (const fragment of fragments) { merge(template, fragment); } // resolve all tokens and remove all empties const ret = this.resolve(template) || {}; this._logicalIds.assertAllRenamesApplied(); return ret; } /** * Determine the various stack environment attributes. * */ parseEnvironment(env = {}) { // if an environment property is explicitly specified when the stack is // created, it will be used. if not, use tokens for account and region but // they do not need to be scoped, the only situation in which // export/fn::importvalue would work if { Ref: "AWS::AccountId" } is the // same for provider and consumer anyway. const account = env.account || cfn_pseudo_1.Aws.ACCOUNT_ID; const region = env.region || cfn_pseudo_1.Aws.REGION; // this is the "aws://" env specification that will be written to the cloud assembly // manifest. it will use "unknown-account" and "unknown-region" to indicate // environment-agnosticness. const envAccount = !token_1.Token.isUnresolved(account) ? account : cxapi.UNKNOWN_ACCOUNT; const envRegion = !token_1.Token.isUnresolved(region) ? region : cxapi.UNKNOWN_REGION; return { account, region, environment: cx_api_1.EnvironmentUtils.format(envAccount, envRegion) }; } /** * Check whether this stack has a (transitive) dependency on another stack * * Returns the list of reasons on the dependency path, or undefined * if there is no dependency. */ stackDependencyReasons(other) { if (this === other) { return []; } for (const dep of this._stackDependencies) { const ret = dep.stack.stackDependencyReasons(other); if (ret !== undefined) { return [dep.reason].concat(ret); } } return undefined; } collectMetadata() { const output = {}; const stack = this; visit(this); return output; function visit(node) { if (node.node.metadata.length > 0) { // Make the path absolute output[construct_1.ConstructNode.PATH_SEP + node.node.path] = node.node.metadata.map(md => stack.resolve(md)); } for (const child of node.node.children) { visit(child); } } } /** * Calculcate the stack name based on the construct path */ calculateStackName() { // In tests, it's possible for this stack to be the root object, in which case // we need to use it as part of the root path. const rootPath = this.node.scope !== undefined ? this.node.scopes.slice(1) : [this]; const ids = rootPath.map(c => c.node.id); // Special case, if rootPath is length 1 then just use ID (backwards compatibility) // otherwise use a unique stack name (including hash). This logic is already // in makeUniqueId, *however* makeUniqueId will also strip dashes from the name, // which *are* allowed and also used, so we short-circuit it. if (ids.length === 1) { // Could be empty in a unit test, so just pretend it's named "Stack" then return ids[0] || 'Stack'; } return uniqueid_1.makeUniqueId(ids); } } exports.Stack = Stack; function merge(template, part) { for (const section of Object.keys(part)) { const src = part[section]; // create top-level section if it doesn't exist let dest = template[section]; if (!dest) { template[section] = dest = src; } else { // add all entities from source section to destination section for (const id of Object.keys(src)) { if (id in dest) { throw new Error(`section '${section}' already contains '${id}'`); } dest[id] = src[id]; } } } } /** * Collect all CfnElements from a Stack * * @param node Root node to collect all CfnElements from * @param into Array to append CfnElements to * @returns The same array as is being collected into */ function cfnElements(node, into = []) { if (cfn_element_1.CfnElement.isCfnElement(node)) { into.push(node); } for (const child of node.node.children) { // Don't recurse into a substack if (Stack.isStack(child)) { continue; } cfnElements(child, into); } return into; } // These imports have to be at the end to prevent circular imports const arn_1 = require("./arn"); const cfn_element_1 = require("./cfn-element"); const cfn_fn_1 = require("./cfn-fn"); const cfn_pseudo_1 = require("./cfn-pseudo"); const cfn_resource_1 = require("./cfn-resource"); const cfn_reference_1 = require("./private/cfn-reference"); const tag_manager_1 = require("./tag-manager"); const token_1 = require("./token"); /** * Find all resources in a set of constructs */ function findResources(roots) { const ret = new Array(); for (const root of roots) { ret.push(...root.node.findAll().filter(cfn_resource_1.CfnResource.isCfnResource)); } return ret; } function extractSingleValue(array) { if (array && array.length === 1) { return array[0]; } return array; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzdGFjay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHlDQUEwQztBQUMxQyw0Q0FBbUQ7QUFDbkQseUJBQTBCO0FBQzFCLDZCQUE4QjtBQUM5QiwyQ0FBc0Y7QUFDdEYseURBQXFEO0FBRXJELHVFQUFrRztBQUNsRyxxREFBa0Q7QUFDbEQsK0NBQTRDO0FBQzVDLGlEQUFrRDtBQUVsRCxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLHFCQUFxQixDQUFDLENBQUM7QUFDdkQsTUFBTSxzQkFBc0IsR0FBRyx5QkFBeUIsQ0FBQztBQTBCekQ7O0dBRUc7QUFDSCxNQUFhLEtBQU0sU0FBUSxxQkFBUztJQTBJbEM7Ozs7OztPQU1HO0lBQ0gsWUFBbUIsS0FBaUIsRUFBRSxJQUFhLEVBQUUsUUFBb0IsRUFBRTtRQUN6RSxvR0FBb0c7UUFDcEcsS0FBSyxDQUFDLEtBQU0sRUFBRSxJQUFLLENBQUMsQ0FBQztRQWhIdkI7O1dBRUc7UUFDYSxvQkFBZSxHQUFxQixFQUFFLENBQUM7UUF3RnZEOztXQUVHO1FBQ2MsdUJBQWtCLEdBQUcsSUFBSSxHQUFHLEVBQW1CLENBQUM7UUFFakU7Ozs7V0FJRztRQUNjLG9CQUFlLEdBQUcsSUFBSSxLQUFLLEVBQXdCLENBQUM7UUFhbkUsTUFBTSxDQUFDLGNBQWMsQ0FBQyxJQUFJLEVBQUUsWUFBWSxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7UUFFM0QsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLHVCQUFVLEVBQUUsQ0FBQztRQUVwQyxNQUFNLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBRTFFLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxNQUFNLEdBQUcs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QyxFQUFFO1lBQUUsU0FBUztTQUFFO1FBRXZDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7S0FDMUI7SUFFRCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRCxrRUFBa0U7QUFDbEUsK0JBQTJDO0FBQzNDLCtDQUEyQztBQUMzQyxxQ0FBOEI7QUFDOUIsNkNBQThDO0FBQzlDLGlEQUFzRDtBQUN0RCwyREFBdUQ7QUFDdkQsK0NBQXNEO0FBQ3RELG1DQUFnQztBQUVoQzs7R0FFRztBQUNILFNBQVMsYUFBYSxDQUFDLEtBQTJCO0lBQ2hELE1BQU0sR0FBRyxHQUFHLElBQUksS0FBSyxFQUFlLENBQUM7SUFDckMsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUU7UUFDeEIsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsTUFBTSxDQUFDLDBCQUFXLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQztLQUNwRTtJQUNELE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQztBQU9ELFNBQVMsa0JBQWtCLENBQUksS0FBc0I7SUFDbkQsSUFBSSxLQUFLLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUU7UUFDL0IsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7S0FDakI7SUFDRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgY3hhcGkgPSByZXF1aXJlKCdAYXdzLWNkay9jeC1hcGknKTtcbmltcG9ydCB7IEVudmlyb25tZW50VXRpbHMgfSBmcm9tICdAYXdzLWNkay9jeC1hcGknO1xuaW1wb3J0IGZzID0gcmVxdWlyZSgnZnMnKTtcbmltcG9ydCBwYXRoID0gcmVxdWlyZSgncGF0aCcpO1xuaW1wb3J0IHsgQ29uc3RydWN0LCBDb25zdHJ1Y3ROb2RlLCBJQ29uc3RydWN0LCBJU3ludGhlc2lzU2Vzc2lvbiB9IGZyb20gJy4vY29uc3RydWN0JztcbmltcG9ydCB7IENvbnRleHRQcm92aWRlciB9IGZyb20gJy4vY29udGV4dC1wcm92aWRlcic7XG5pbXBvcnQgeyBFbnZpcm9ubWVudCB9IGZyb20gJy4vZW52aXJvbm1lbnQnO1xuaW1wb3J0IHsgQ0xPVURGT1JNQVRJT05fVE9LRU5fUkVTT0xWRVIsIENsb3VkRm9ybWF0aW9uTGFuZyB9IGZyb20gJy4vcHJpdmF0ZS9jbG91ZGZvcm1hdGlvbi1sYW5nJztcbmltcG9ydCB7IExvZ2ljYWxJRHMgfSBmcm9tICcuL3ByaXZhdGUvbG9naWNhbC1pZCc7XG5pbXBvcnQgeyByZXNvbHZlIH0gZnJvbSAnLi9wcml2YXRlL3Jlc29sdmUnO1xuaW1wb3J0IHsgbWFrZVVuaXF1ZUlkIH0gZnJvbSAnLi9wcml2YXRlL3VuaXF1ZWlkJztcblxuY29uc3QgU1RBQ0tfU1lNQk9MID0gU3ltYm9sLmZvcignQGF3cy1jZGsvY29yZS5TdGFjaycpO1xuY29uc3QgVkFMSURfU1RBQ0tfTkFNRV9SRUdFWCA9IC9eW0EtWmEtel1bQS1aYS16MC05LV0qJC87XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3RhY2tQcm9wcyB7XG4gIC8qKlxuICAgKiBUaGUgQVdTIGVudmlyb25tZW50IChhY2NvdW50L3JlZ2lvbikgd2hlcmUgdGhpcyBzdGFjayB3aWxsIGJlIGRlcGxveWVkLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIFRoZSBgZGVmYXVsdC1hY2NvdW50YCBhbmQgYGRlZmF1bHQtcmVnaW9uYCBjb250ZXh0IHBhcmFtZXRlcnMgd2lsbCBiZVxuICAgKiB1c2VkLiBJZiB0aGV5IGFyZSB1bmRlZmluZWQsIGl0IHdpbGwgbm90IGJlIHBvc3NpYmxlIHRvIGRlcGxveSB0aGUgc3RhY2suXG4gICAqL1xuICByZWFkb25seSBlbnY/OiBFbnZpcm9ubWVudDtcblxuICAvKipcbiAgICogTmFtZSB0byBkZXBsb3kgdGhlIHN0YWNrIHdpdGhcbiAgICpcbiAgICogQGRlZmF1bHQgLSBEZXJpdmVkIGZyb20gY29uc3RydWN0IHBhdGguXG4gICAqL1xuICByZWFkb25seSBzdGFja05hbWU/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFN0YWNrIHRhZ3MgdGhhdCB3aWxsIGJlIGFwcGxpZWQgdG8gYWxsIHRoZSB0YWdnYWJsZSByZXNvdXJjZXMgYW5kIHRoZSBzdGFjayBpdHNlbGYuXG4gICAqXG4gICAqIEBkZWZhdWx0IHt9XG4gICAqL1xuICByZWFkb25seSB0YWdzPzogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfTtcbn1cblxuLyoqXG4gKiBBIHJvb3QgY29uc3RydWN0IHdoaWNoIHJlcHJlc2VudHMgYSBzaW5nbGUgQ2xvdWRGb3JtYXRpb24gc3RhY2suXG4gKi9cbmV4cG9ydCBjbGFzcyBTdGFjayBleHRlbmRzIENvbnN0cnVjdCBpbXBsZW1lbnRzIElUYWdnYWJsZSB7XG4gIC8qKlxuICAgKiBSZXR1cm4gd2hldGhlciB0aGUgZ2l2ZW4gb2JqZWN0IGlzIGEgU3RhY2suXG4gICAqXG4gICAqIFdlIGRvIGF0dHJpYnV0ZSBkZXRlY3Rpb24gc2luY2Ugd2UgY2FuJ3QgcmVsaWFibHkgdXNlICdpbnN0YW5jZW9mJy5cbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgaXNTdGFjayh4OiBhbnkpOiB4IGlzIFN0YWNrIHtcbiAgICByZXR1cm4geCAhPT0gbnVsbCAmJiB0eXBlb2YoeCkgPT09ICdvYmplY3QnICYmIFNUQUNLX1NZTUJPTCBpbiB4O1xuICB9XG5cbiAgLyoqXG4gICAqIExvb2tzIHVwIHRoZSBmaXJzdCBzdGFjayBzY29wZSBpbiB3aGljaCBgY29uc3RydWN0YCBpcyBkZWZpbmVkLiBGYWlscyBpZiB0aGVyZSBpcyBubyBzdGFjayB1cCB0aGUgdHJlZS5cbiAgICogQHBhcmFtIGNvbnN0cnVjdCBUaGUgY29uc3RydWN0IHRvIHN0YXJ0IHRoZSBzZWFyY2ggZnJvbS5cbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgb2YoY29uc3RydWN0OiBJQ29uc3RydWN0KTogU3RhY2sge1xuICAgIHJldHVybiBfbG9va3VwKGNvbnN0cnVjdCk7XG5cbiAgICBmdW5jdGlvbiBfbG9va3VwKGM6IElDb25zdHJ1Y3QpOiBTdGFjayAge1xuICAgICAgaWYgKFN0YWNrLmlzU3RhY2soYykpIHtcbiAgICAgICAgcmV0dXJuIGM7XG4gICAgICB9XG5cbiAgICAgIGlmICghYy5ub2RlLnNjb3BlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgTm8gc3RhY2sgY291bGQgYmUgaWRlbnRpZmllZCBmb3IgdGhlIGNvbnN0cnVjdCBhdCBwYXRoICR7Y29uc3RydWN0Lm5vZGUucGF0aH1gKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIF9sb29rdXAoYy5ub2RlLnNjb3BlKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGFncyB0byBiZSBhcHBsaWVkIHRvIHRoZSBzdGFjay5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB0YWdzOiBUYWdNYW5hZ2VyO1xuXG4gIC8qKlxuICAgKiBPcHRpb25zIGZvciBDbG91ZEZvcm1hdGlvbiB0ZW1wbGF0ZSAobGlrZSB2ZXJzaW9uLCB0cmFuc2Zvcm0sIGRlc2NyaXB0aW9uKS5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB0ZW1wbGF0ZU9wdGlvbnM6IElUZW1wbGF0ZU9wdGlvbnMgPSB7fTtcblxuICAvKipcbiAgICogVGhlIGNvbmNyZXRlIENsb3VkRm9ybWF0aW9uIHBoeXNpY2FsIHN0YWNrIG5hbWUuXG4gICAqXG4gICAqIFRoaXMgaXMgZWl0aGVyIHRoZSBuYW1lIGRlZmluZWQgZXhwbGljaXRseSBpbiB0aGUgYHN0YWNrTmFtZWAgcHJvcCBvclxuICAgKiBhbGxvY2F0ZWQgYmFzZWQgb24gdGhlIHN0YWNrJ3MgbG9jYXRpb24gaW4gdGhlIGNvbnN0cnVjdCB0cmVlLiBTdGFja3MgdGhhdFxuICAgKiBhcmUgZGlyZWN0bHkgZGVmaW5lZCB1bmRlciB0aGUgYXBwIHVzZSB0aGVpciBjb25zdHJ1Y3QgYGlkYCBhcyB0aGVpciBzdGFja1xuICAgKiBuYW1lLiBTdGFja3MgdGhhdCBhcmUgZGVmaW5lZCBkZWVwZXIgd2l0aGluIHRoZSB0cmVlIHdpbGwgdXNlIGEgaGFzaGVkIG5hbWluZ1xuICAgKiBzY2hlbWUgYmFzZWQgb24gdGhlIGNvbnN0cnVjdCBwYXRoIHRvIGVuc3VyZSB1bmlxdWVuZXNzLlxuICAgKlxuICAgKiBJZiB5b3Ugd2lzaCB0byBvYnRhaW4gdGhlIGRlcGxveS10aW1lIEFXUzo6U3RhY2tOYW1lIGludHJpbnNpYyxcbiAgICogeW91IGNhbiB1c2UgYEF3cy5zdGFja05hbWVgIGRpcmVjdGx5LlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHN0YWNrTmFtZTogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgQVdTIHJlZ2lvbiBpbnRvIHdoaWNoIHRoaXMgc3RhY2sgd2lsbCBiZSBkZXBsb3llZCAoZS5nLiBgdXMtd2VzdC0yYCkuXG4gICAqXG4gICAqIFRoaXMgdmFsdWUgaXMgcmVzb2x2ZWQgYWNjb3JkaW5nIHRvIHRoZSBmb2xsb3dpbmcgcnVsZXM6XG4gICAqXG4gICAqIDEuIFRoZSB2YWx1ZSBwcm92aWRlZCB0byBgZW52LnJlZ2lvbmAgd2hlbiB0aGUgc3RhY2sgaXMgZGVmaW5lZC4gVGhpcyBjYW5cbiAgICogICAgZWl0aGVyIGJlIGEgY29uY2VyZXRlIHJlZ2lvbiAoZS5nLiBgdXMtd2VzdC0yYCkgb3IgdGhlIGBBd3MucmVnaW9uYFxuICAgKiAgICB0b2tlbi5cbiAgICogMy4gYEF3cy5yZWdpb25gLCB3aGljaCBpcyByZXByZXNlbnRzIHRoZSBDbG91ZEZvcm1hdGlvbiBpbnRyaW5zaWMgcmVmZXJlbmNlXG4gICAqICAgIGB7IFwiUmVmXCI6IFwiQVdTOjpSZWdpb25cIiB9YCBlbmNvZGVkIGFzIGEgc3RyaW5nIHRva2VuLlxuICAgKlxuICAgKiBQcmVmZXJhYmx5LCB5b3Ugc2hvdWxkIHVzZSB0aGUgcmV0dXJuIHZhbHVlIGFzIGFuIG9wYXF1ZSBzdHJpbmcgYW5kIG5vdFxuICAgKiBhdHRlbXB0IHRvIHBhcnNlIGl0IHRvIGltcGxlbWVudCB5b3VyIGxvZ2ljLiBJZiB5b3UgZG8sIHlvdSBtdXN0IGZpcnN0XG4gICAqIGNoZWNrIHRoYXQgaXQgaXMgYSBjb25jZXJldGUgdmFsdWUgYW4gbm90IGFuIHVucmVzb2x2ZWQgdG9rZW4uIElmIHRoaXNcbiAgICogdmFsdWUgaXMgYW4gdW5yZXNvbHZlZCB0b2tlbiAoYFRva2VuLmlzVW5yZXNvbHZlZChzdGFjay5yZWdpb24pYCByZXR1cm5zXG4gICAqIGB0cnVlYCksIHRoaXMgaW1wbGllcyB0aGF0IHRoZSB1c2VyIHdpc2hlcyB0aGF0IHRoaXMgc3RhY2sgd2lsbCBzeW50aGVzaXplXG4gICAqIGludG8gYSAqKnJlZ2lvbi1hZ25vc3RpYyB0ZW1wbGF0ZSoqLiBJbiB0aGlzIGNhc2UsIHlvdXIgY29kZSBzaG91bGQgZWl0aGVyXG4gICAqIGZhaWwgKHRocm93IGFuIGVycm9yLCBlbWl0IGEgc3ludGggZXJyb3IgdXNpbmcgYG5vZGUuYWRkRXJyb3JgKSBvclxuICAgKiBpbXBsZW1lbnQgc29tZSBvdGhlciByZWdpb24tYWdub3N0aWMgYmVoYXZpb3IuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgcmVnaW9uOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBBV1MgYWNjb3VudCBpbnRvIHdoaWNoIHRoaXMgc3RhY2sgd2lsbCBiZSBkZXBsb3llZC5cbiAgICpcbiAgICogVGhpcyB2YWx1ZSBpcyByZXNvbHZlZCBhY2NvcmRpbmcgdG8gdGhlIGZvbGxvd2luZyBydWxlczpcbiAgICpcbiAgICogMS4gVGhlIHZhbHVlIHByb3ZpZGVkIHRvIGBlbnYuYWNjb3VudGAgd2hlbiB0aGUgc3RhY2sgaXMgZGVmaW5lZC4gVGhpcyBjYW5cbiAgICogICAgZWl0aGVyIGJlIGEgY29uY2VyZXRlIGFjY291bnQgKGUuZy4gYDU4NTY5NTAzMTExMWApIG9yIHRoZVxuICAgKiAgICBgQXdzLmFjY291bnRJZGAgdG9rZW4uXG4gICAqIDMuIGBBd3MuYWNjb3VudElkYCwgd2hpY2ggcmVwcmVzZW50cyB0aGUgQ2xvdWRGb3JtYXRpb24gaW50cmluc2ljIHJlZmVyZW5jZVxuICAgKiAgICBgeyBcIlJlZlwiOiBcIkFXUzo6QWNjb3VudElkXCIgfWAgZW5jb2RlZCBhcyBhIHN0cmluZyB0b2tlbi5cbiAgICpcbiAgICogUHJlZmVyYWJseSwgeW91IHNob3VsZCB1c2UgdGhlIHJldHVybiB2YWx1ZSBhcyBhbiBvcGFxdWUgc3RyaW5nIGFuZCBub3RcbiAgICogYXR0ZW1wdCB0byBwYXJzZSBpdCB0byBpbXBsZW1lbnQgeW91ciBsb2dpYy4gSWYgeW91IGRvLCB5b3UgbXVzdCBmaXJzdFxuICAgKiBjaGVjayB0aGF0IGl0IGlzIGEgY29uY2VyZXRlIHZhbHVlIGFuIG5vdCBhbiB1bnJlc29sdmVkIHRva2VuLiBJZiB0aGlzXG4gICAqIHZhbHVlIGlzIGFuIHVucmVzb2x2ZWQgdG9rZW4gKGBUb2tlbi5pc1VucmVzb2x2ZWQoc3RhY2suYWNjb3VudClgIHJldHVybnNcbiAgICogYHRydWVgKSwgdGhpcyBpbXBsaWVzIHRoYXQgdGhlIHVzZXIgd2lzaGVzIHRoYXQgdGhpcyBzdGFjayB3aWxsIHN5bnRoZXNpemVcbiAgICogaW50byBhICoqYWNjb3VudC1hZ25vc3RpYyB0ZW1wbGF0ZSoqLiBJbiB0aGlzIGNhc2UsIHlvdXIgY29kZSBzaG91bGQgZWl0aGVyXG4gICAqIGZhaWwgKHRocm93IGFuIGVycm9yLCBlbWl0IGEgc3ludGggZXJyb3IgdXNpbmcgYG5vZGUuYWRkRXJyb3JgKSBvclxuICAgKiBpbXBsZW1lbnQgc29tZSBvdGhlciByZWdpb24tYWdub3N0aWMgYmVoYXZpb3IuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgYWNjb3VudDogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgZW52aXJvbm1lbnQgY29vcmRpbmF0ZXMgaW4gd2hpY2ggdGhpcyBzdGFjayBpcyBkZXBsb3llZC4gSW4gdGhlIGZvcm1cbiAgICogYGF3czovL2FjY291bnQvcmVnaW9uYC4gVXNlIGBzdGFjay5hY2NvdW50YCBhbmQgYHN0YWNrLnJlZ2lvbmAgdG8gb2J0YWluXG4gICAqIHRoZSBzcGVjaWZpYyB2YWx1ZXMsIG5vIG5lZWQgdG8gcGFyc2UuXG4gICAqXG4gICAqIFlvdSBjYW4gdXNlIHRoaXMgdmFsdWUgdG8gZGV0ZXJtaW5lIGlmIHR3byBzdGFja3MgYXJlIHRhcmdldGluZyB0aGUgc2FtZVxuICAgKiBlbnZpcm9ubWVudC5cbiAgICpcbiAgICogSWYgZWl0aGVyIGBzdGFjay5hY2NvdW50YCBvciBgc3RhY2sucmVnaW9uYCBhcmUgbm90IGNvbmNyZXRlIHZhbHVlcyAoZS5nLlxuICAgKiBgQXdzLmFjY291bnRgIG9yIGBBd3MucmVnaW9uYCkgdGhlIHNwZWNpYWwgc3RyaW5ncyBgdW5rbm93bi1hY2NvdW50YCBhbmQvb3JcbiAgICogYHVua25vd24tcmVnaW9uYCB3aWxsIGJlIHVzZWQgcmVzcGVjdGl2ZWx5IHRvIGluZGljYXRlIHRoaXMgc3RhY2sgaXNcbiAgICogcmVnaW9uL2FjY291bnQtYWdub3N0aWMuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgZW52aXJvbm1lbnQ6IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIG5hbWUgb2YgdGhlIENsb3VkRm9ybWF0aW9uIHRlbXBsYXRlIGZpbGUgZW1pdHRlZCB0byB0aGUgb3V0cHV0XG4gICAqIGRpcmVjdG9yeSBkdXJpbmcgc3ludGhlc2lzLlxuICAgKlxuICAgKiBAZXhhbXBsZSBNeVN0YWNrLnRlbXBsYXRlLmpzb25cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB0ZW1wbGF0ZUZpbGU6IHN0cmluZztcblxuICAvKipcbiAgICogTG9naWNhbCBJRCBnZW5lcmF0aW9uIHN0cmF0ZWd5XG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IF9sb2dpY2FsSWRzOiBMb2dpY2FsSURzO1xuXG4gIC8qKlxuICAgKiBPdGhlciBzdGFja3MgdGhpcyBzdGFjayBkZXBlbmRzIG9uXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IF9zdGFja0RlcGVuZGVuY2llcyA9IG5ldyBTZXQ8U3RhY2tEZXBlbmRlbmN5PigpO1xuXG4gIC8qKlxuICAgKiBMaXN0cyBhbGwgbWlzc2luZyBjb250ZXh0dWFsIGluZm9ybWF0aW9uLlxuICAgKiBUaGlzIGlzIHJldHVybmVkIHdoZW4gdGhlIHN0YWNrIGlzIHN5bnRoZXNpemVkIHVuZGVyIHRoZSAnbWlzc2luZycgYXR0cmlidXRlXG4gICAqIGFuZCBhbGxvd3MgdG9vbGluZyB0byBvYnRhaW4gdGhlIGNvbnRleHQgYW5kIHJlLXN5bnRoZXNpemUuXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IF9taXNzaW5nQ29udGV4dCA9IG5ldyBBcnJheTxjeGFwaS5NaXNzaW5nQ29udGV4dD4oKTtcblxuICAvKipcbiAgICogQ3JlYXRlcyBhIG5ldyBzdGFjay5cbiAgICpcbiAgICogQHBhcmFtIHNjb3BlIFBhcmVudCBvZiB0aGlzIHN0YWNrLCB1c3VhbGx5IGEgUHJvZ3JhbSBpbnN0YW5jZS5cbiAgICogQHBhcmFtIG5hbWUgVGhlIG5hbWUgb2YgdGhlIENsb3VkRm9ybWF0aW9uIHN0YWNrLiBEZWZhdWx0cyB0byBcIlN0YWNrXCIuXG4gICAqIEBwYXJhbSBwcm9wcyBTdGFjayBwcm9wZXJ0aWVzLlxuICAgKi9cbiAgcHVibGljIGNvbnN0cnVjdG9yKHNjb3BlPzogQ29uc3RydWN0LCBuYW1lPzogc3RyaW5nLCBwcm9wczogU3RhY2tQcm9wcyA9IHt9KSB7XG4gICAgLy8gRm9yIHVuaXQgdGVzdCBjb252ZW5pZW5jZSBwYXJlbnRzIGFyZSBvcHRpb25hbCwgc28gYnlwYXNzIHRoZSB0eXBlIGNoZWNrIHdoZW4gY2FsbGluZyB0aGUgcGFyZW50LlxuICAgIHN1cGVyKHNjb3BlISwgbmFtZSEpO1xuXG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRoaXMsIFNUQUNLX1NZTUJPTCwgeyB2YWx1ZTogdHJ1ZSB9KTtcblxuICAgIHRoaXMuX2xvZ2ljYWxJZHMgPSBuZXcgTG9naWNhbElEcygpO1xuXG4gICAgY29uc3QgeyBhY2NvdW50LCByZWdpb24sIGVudmlyb25tZW50IH0gPSB0aGlzLnBhcnNlRW52aXJvbm1lbnQocHJvcHMuZW52KTtcblxuICAgIHRoaXMuYWNjb3VudCA9IGFjY291bnQ7XG4gICAgdGhpcy5yZWdpb24gPSByZWdpb247XG4gICAgdGhpcy5lbnZpcm9ubWVudCA9IGVudmlyb25tZW50O1xuXG4gICAgdGhpcy5zdGFja05hbWUgPSBwcm9wcy5zdGFja05hbWUgIT09IHVuZGVmaW5lZCA/IHByb3BzLnN0YWNrTmFtZSA6IHRoaXMuY2FsY3VsYXRlU3RhY2tOYW1lKCk7XG4gICAgdGhpcy50YWdzID0gbmV3IFRhZ01hbmFnZXIoVGFnVHlwZS5LRVlfVkFMVUUsICdhd3M6Y2RrOnN0YWNrJywgcHJvcHMudGFncyk7XG5cbiAgICBpZiAoIVZBTElEX1NUQUNLX05BTUVfUkVHRVgudGVzdCh0aGlzLnN0YWNrTmFtZSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgU3RhY2sgbmFtZSBtdXN0IG1hdGNoIHRoZSByZWd1bGFyIGV4cHJlc3Npb246ICR7VkFMSURfU1RBQ0tfTkFNRV9SRUdFWC50b1N0cmluZygpfSwgZ290ICcke25hbWV9J2ApO1xuICAgIH1cblxuICAgIHRoaXMudGVtcGxhdGVGaWxlID0gYCR7dGhpcy5zdGFja05hbWV9LnRlbXBsYXRlLmpzb25gO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc29sdmUgYSB0b2tlbml6ZWQgdmFsdWUgaW4gdGhlIGNvbnRleHQgb2YgdGhlIGN1cnJlbnQgc3RhY2suXG4gICAqL1xuICBwdWJsaWMgcmVzb2x2ZShvYmo6IGFueSk6IGFueSB7XG4gICAgcmV0dXJuIHJlc29sdmUob2JqLCB7XG4gICAgICBzY29wZTogdGhpcyxcbiAgICAgIHByZWZpeDogW10sXG4gICAgICByZXNvbHZlcjogQ0xPVURGT1JNQVRJT05fVE9LRU5fUkVTT0xWRVIsXG4gICAgICBwcmVwYXJpbmc6IGZhbHNlXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQ29udmVydCBhbiBvYmplY3QsIHBvdGVudGlhbGx5IGNvbnRhaW5pbmcgdG9rZW5zLCB0byBhIEpTT04gc3RyaW5nXG4gICAqL1xuICBwdWJsaWMgdG9Kc29uU3RyaW5nKG9iajogYW55LCBzcGFjZT86IG51bWJlcik6IHN0cmluZyB7XG4