sdk.js
  1  "use strict";
  2  Object.defineProperty(exports, "__esModule", { value: true });
  3  exports.SDK = void 0;
  4  const AWS = require("aws-sdk");
  5  const logging_1 = require("../../logging");
  6  const functions_1 = require("../../util/functions");
  7  const account_cache_1 = require("./account-cache");
  8  /**
  9   * Base functionality of SDK without credential fetching
 10   */
 11  class SDK {
 12      constructor(_credentials, region, httpOptions = {}, sdkOptions = {}) {
 13          this._credentials = _credentials;
 14          this.sdkOptions = sdkOptions;
 15          /**
 16           * Default retry options for SDK clients.
 17           */
 18          this.retryOptions = { maxRetries: 6, retryDelayOptions: { base: 300 } };
 19          /**
 20           * The more generous retry policy for CloudFormation, which has a 1 TPM limit on certain APIs,
 21           * which are abundantly used for deployment tracking, ...
 22           *
 23           * So we're allowing way more retries, but waiting a bit more.
 24           */
 25          this.cloudFormationRetryOptions = { maxRetries: 10, retryDelayOptions: { base: 1000 } };
 26          this.config = {
 27              ...httpOptions,
 28              ...this.retryOptions,
 29              credentials: _credentials,
 30              region,
 31              logger: { log: (...messages) => messages.forEach(m => logging_1.trace('%s', m)) },
 32          };
 33          this.currentRegion = region;
 34      }
 35      cloudFormation() {
 36          return this.wrapServiceErrorHandling(new AWS.CloudFormation({
 37              ...this.config,
 38              ...this.cloudFormationRetryOptions,
 39          }));
 40      }
 41      ec2() {
 42          return this.wrapServiceErrorHandling(new AWS.EC2(this.config));
 43      }
 44      ssm() {
 45          return this.wrapServiceErrorHandling(new AWS.SSM(this.config));
 46      }
 47      s3() {
 48          return this.wrapServiceErrorHandling(new AWS.S3(this.config));
 49      }
 50      route53() {
 51          return this.wrapServiceErrorHandling(new AWS.Route53(this.config));
 52      }
 53      ecr() {
 54          return this.wrapServiceErrorHandling(new AWS.ECR(this.config));
 55      }
 56      elbv2() {
 57          return this.wrapServiceErrorHandling(new AWS.ELBv2(this.config));
 58      }
 59      secretsManager() {
 60          return this.wrapServiceErrorHandling(new AWS.SecretsManager(this.config));
 61      }
 62      async currentAccount() {
 63          // Get/refresh if necessary before we can access `accessKeyId`
 64          await this.forceCredentialRetrieval();
 65          return functions_1.cached(this, CURRENT_ACCOUNT_KEY, () => SDK.accountCache.fetch(this._credentials.accessKeyId, async () => {
 66              // if we don't have one, resolve from STS and store in cache.
 67              logging_1.debug('Looking up default account ID from STS');
 68              const result = await new AWS.STS(this.config).getCallerIdentity().promise();
 69              const accountId = result.Account;
 70              const partition = result.Arn.split(':')[1];
 71              if (!accountId) {
 72                  throw new Error('STS didn\'t return an account ID');
 73              }
 74              logging_1.debug('Default account ID:', accountId);
 75              return { accountId, partition };
 76          }));
 77      }
 78      /**
 79       * Return the current credentials
 80       *
 81       * Don't use -- only used to write tests around assuming roles.
 82       */
 83      async currentCredentials() {
 84          await this.forceCredentialRetrieval();
 85          return this._credentials;
 86      }
 87      /**
 88       * Force retrieval of the current credentials
 89       *
 90       * Relevant if the current credentials are AssumeRole credentials -- do the actual
 91       * lookup, and translate any error into a useful error message (taking into
 92       * account credential provenance).
 93       */
 94      async forceCredentialRetrieval() {
 95          try {
 96              await this._credentials.getPromise();
 97          }
 98          catch (e) {
 99              logging_1.debug(`Assuming role failed: ${e.message}`);
100              throw new Error([
101                  'Could not assume role in target account',
102                  ...this.sdkOptions.assumeRoleCredentialsSourceDescription
103                      ? [`using ${this.sdkOptions.assumeRoleCredentialsSourceDescription}`]
104                      : [],
105                  e.message,
106                  '. Please make sure that this role exists in the account. If it doesn\'t exist, (re)-bootstrap the environment ' +
107                      'with the right \'--trust\', using the latest version of the CDK CLI.',
108              ].join(' '));
109          }
110      }
111      /**
112       * Return a wrapping object for the underlying service object
113       *
114       * Responds to failures in the underlying service calls, in two different
115       * ways:
116       *
117       * - When errors are encountered, log the failing call and the error that
118       *   it triggered (at debug level). This is necessary because the lack of
119       *   stack traces in NodeJS otherwise makes it very hard to suss out where
120       *   a certain AWS error occurred.
121       * - The JS SDK has a funny business of wrapping any credential-based error
122       *   in a super-generic (and in our case wrong) exception. If we then use a
123       *   'ChainableTemporaryCredentials' and the target role doesn't exist,
124       *   the error message that shows up by default is super misleading
125       *   (https://github.com/aws/aws-sdk-js/issues/3272). We can fix this because
126       *   the exception contains the "inner exception", so we unwrap and throw
127       *   the correct error ("cannot assume role").
128       *
129       * The wrapping business below is slightly more complicated than you'd think
130       * because we must hook into the `promise()` method of the object that's being
131       * returned from the methods of the object that we wrap, so there's two
132       * levels of wrapping going on, and also some exceptions to the wrapping magic.
133       */
134      wrapServiceErrorHandling(serviceObject) {
135          const classObject = serviceObject.constructor.prototype;
136          const self = this;
137          return new Proxy(serviceObject, {
138              get(obj, prop) {
139                  const real = obj[prop];
140                  // Things we don't want to intercept:
141                  // - Anything that's not a function.
142                  // - 'constructor', s3.upload() will use this to do some magic and we need the underlying constructor.
143                  // - Any method that's not on the service class (do not intercept 'makeRequest' and other helpers).
144                  if (prop === 'constructor' || !classObject.hasOwnProperty(prop) || !isFunction(real)) {
145                      return real;
146                  }
147                  // NOTE: This must be a function() and not an () => {
148                  // because I need 'this' to be dynamically bound and not statically bound.
149                  // If your linter complains don't listen to it!
150                  return function () {
151                      // Call the underlying function. If it returns an object with a promise()
152                      // method on it, wrap that 'promise' method.
153                      const args = [].slice.call(arguments, 0);
154                      const response = real.apply(this, args);
155                      // Don't intercept unless the return value is an object with a '.promise()' method.
156                      if (typeof response !== 'object' || !response) {
157                          return response;
158                      }
159                      if (!('promise' in response)) {
160                          return response;
161                      }
162                      // Return an object with the promise method replaced with a wrapper which will
163                      // do additional things to errors.
164                      return Object.assign(Object.create(response), {
165                          promise() {
166                              return response.promise().catch((e) => {
167                                  e = self.makeDetailedException(e);
168                                  logging_1.debug(`Call failed: ${prop}(${JSON.stringify(args[0])}) => ${e.message} (code=${e.code})`);
169                                  return Promise.reject(e); // Re-'throw' the new error
170                              });
171                          },
172                      });
173                  };
174              },
175          });
176      }
177      /**
178       * Extract a more detailed error out of a generic error if we can
179       *
180       * If this is an error about Assuming Roles, add in the context showing the
181       * chain of credentials we used to try to assume the role.
182       */
183      makeDetailedException(e) {
184          // This is the super-generic "something's wrong" error that the JS SDK wraps other errors in.
185          // https://github.com/aws/aws-sdk-js/blob/f0ac2e53457c7512883d0677013eacaad6cd8a19/lib/event_listeners.js#L84
186          if (typeof e.message === 'string' && e.message.startsWith('Missing credentials in config')) {
187              const original = e.originalError;
188              if (original) {
189                  // When the SDK does a 'util.copy', they lose the Error-ness of the inner error
190                  // (they copy the Error's properties into a plain object) so make it an Error object again.
191                  e = Object.assign(new Error(), original);
192              }
193          }
194          // At this point, the error might still be a generic "ChainableTemporaryCredentials failed"
195          // error which wraps the REAL error (AssumeRole failed). We're going to replace the error
196          // message with one that's more likely to help users, and tell them the most probable
197          // fix (bootstrapping). The underlying service call failure will be appended below.
198          if (e.message === 'Could not load credentials from ChainableTemporaryCredentials') {
199              e.message = [
200                  'Could not assume role in target account',
201                  ...this.sdkOptions.assumeRoleCredentialsSourceDescription
202                      ? [`using ${this.sdkOptions.assumeRoleCredentialsSourceDescription}`]
203                      : [],
204                  '(did you bootstrap the environment with the right \'--trust\'s?)',
205              ].join(' ');
206          }
207          // Replace the message on this error with a concatenation of all inner error messages.
208          // Must more clear what's going on that way.
209          e.message = allChainedExceptionMessages(e);
210          return e;
211      }
212  }
213  exports.SDK = SDK;
214  SDK.accountCache = new account_cache_1.AccountAccessKeyCache();
215  const CURRENT_ACCOUNT_KEY = Symbol('current_account_key');
216  function isFunction(x) {
217      return x && {}.toString.call(x) === '[object Function]';
218  }
219  /**
220   * Return the concatenated message of all exceptions in the AWS exception chain
221   */
222  function allChainedExceptionMessages(e) {
223      const ret = new Array();
224      while (e) {
225          ret.push(e.message);
226          e = e.originalError;
227      }
228      return ret.join(': ');
229  }
230  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2RrLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsic2RrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLCtCQUErQjtBQUUvQiwyQ0FBNkM7QUFDN0Msb0RBQThDO0FBQzlDLG1EQUF3RDtBQTBDeEQ7O0dBRUc7QUFDSCxNQUFhLEdBQUc7SUFvQmQsWUFDbUIsWUFBNkIsRUFDOUMsTUFBYyxFQUNkLGNBQW9DLEVBQUUsRUFDckIsYUFBeUIsRUFBRTtRQUgzQixpQkFBWSxHQUFaLFlBQVksQ0FBaUI7UUFHN0IsZUFBVSxHQUFWLFVBQVUsQ0FBaUI7UUFqQjlDOztXQUVHO1FBQ2MsaUJBQVksR0FBRyxFQUFFLFVBQVUsRUFBRSxDQUFDLEVBQUUsaUJBQWlCLEVBQUUsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQztRQUVwRjs7Ozs7V0FLRztRQUNjLCtCQUEwQixHQUFHLEVBQUUsVUFBVSxFQUFFLEVBQUUsRUFBRSxpQkFBaUIsRUFBRSxFQUFFLElBQUksRUFBRSxJQUFLLEVBQUUsRUFBRSxDQUFDO1FBUW5HLElBQUksQ0FBQyxNQUFNLEdBQUc7WUFDWixHQUFHLFdBQVc7WUFDZCxHQUFHLElBQUksQ0FBQyxZQUFZO1lBQ3BCLFdBQVcsRUFBRSxZQUFZO1lBQ3pCLE1BQU07WUFDTixNQUFNLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxHQUFHLFFBQVEsRUFBRSxFQUFFLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLGVBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRTtTQUN4RSxDQUFDO1FBQ0YsSUFBSSxDQUFDLGFBQWEsR0FBRyxNQUFNLENBQUM7SUFDOUIsQ0FBQztJQUVNLGNBQWM7UUFDbkIsT0FBTyxJQUFJLENBQUMsd0JBQXdCLENBQUMsSUFBSSxHQUFHLENBQUMsY0FBYyxDQUFDO1lBQzFELEdBQUcsSUFBSSxDQUFDLE1BQU07WUFDZCxHQUFHLElBQUksQ0FB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LENBQUMsR0FBSSxDQUFTLENBQUMsYUFBYSxDQUFDO0tBQzlCO0lBQ0QsT0FBTyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQ3hCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBBV1MgZnJvbSAnYXdzLXNkayc7XG5pbXBvcnQgdHlwZSB7IENvbmZpZ3VyYXRpb25PcHRpb25zIH0gZnJvbSAnYXdzLXNkay9saWIvY29uZmlnLWJhc2UnO1xuaW1wb3J0IHsgZGVidWcsIHRyYWNlIH0gZnJvbSAnLi4vLi4vbG9nZ2luZyc7XG5pbXBvcnQgeyBjYWNoZWQgfSBmcm9tICcuLi8uLi91dGlsL2Z1bmN0aW9ucyc7XG5pbXBvcnQgeyBBY2NvdW50QWNjZXNzS2V5Q2FjaGUgfSBmcm9tICcuL2FjY291bnQtY2FjaGUnO1xuaW1wb3J0IHsgQWNjb3VudCB9IGZyb20gJy4vc2RrLXByb3ZpZGVyJztcblxuZXhwb3J0IGludGVyZmFjZSBJU0RLIHtcbiAgLyoqXG4gICAqIFRoZSByZWdpb24gdGhpcyBTREsgaGFzIGJlZW4gaW5zdGFudGlhdGVkIGZvclxuICAgKlxuICAgKiAoQXMgZGlzdGluY3QgZnJvbSB0aGUgYGRlZmF1bHRSZWdpb24oKWAgb24gU2RrUHJvdmlkZXIgd2hpY2hcbiAgICogcmVwcmVzZW50cyB0aGUgcmVnaW9uIGNvbmZpZ3VyZWQgaW4gdGhlIGRlZmF1bHQgY29uZmlnKS5cbiAgICovXG4gIHJlYWRvbmx5IGN1cnJlbnRSZWdpb246IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIEFjY291bnQgdGhpcyBTREsgaGFzIGJlZW4gaW5zdGFudGlhdGVkIGZvclxuICAgKlxuICAgKiAoQXMgZGlzdGluY3QgZnJvbSB0aGUgYGRlZmF1bHRBY2NvdW50KClgIG9uIFNka1Byb3ZpZGVyIHdoaWNoXG4gICAqIHJlcHJlc2VudHMgdGhlIGFjY291bnQgYXZhaWxhYmxlIGJ5IHVzaW5nIGRlZmF1bHQgY3JlZGVudGlhbHMpLlxuICAgKi9cbiAgY3VycmVudEFjY291bnQoKTogUHJvbWlzZTxBY2NvdW50PjtcblxuICBjbG91ZEZvcm1hdGlvbigpOiBBV1MuQ2xvdWRGb3JtYXRpb247XG4gIGVjMigpOiBBV1MuRUMyO1xuICBzc20oKTogQVdTLlNTTTtcbiAgczMoKTogQVdTLlMzO1xuICByb3V0ZTUzKCk6IEFXUy5Sb3V0ZTUzO1xuICBlY3IoKTogQVdTLkVDUjtcbiAgZWxidjIoKTogQVdTLkVMQnYyO1xuICBzZWNyZXRzTWFuYWdlcigpOiBBV1MuU2VjcmV0c01hbmFnZXI7XG59XG5cbi8qKlxuICogQWRkaXRpb25hbCBTREsgY29uZmlndXJhdGlvbiBvcHRpb25zXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2RrT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBBZGRpdGlvbmFsIGRlc2NyaXB0aXZlIHN0cmluZ3MgdGhhdCBpbmRpY2F0ZSB3aGVyZSB0aGUgXCJBc3N1bWVSb2xlXCIgY3JlZGVudGlhbHMgYXJlIGNvbWluZyBmcm9tXG4gICAqXG4gICAqIFdpbGwgYmUgcHJpbnRlZCBpbiBhbiBlcnJvciBtZXNzYWdlIHRvIGhlbHAgdXNlcnMgZGlhZ25vc2UgYXV0aCBwcm9ibGVtcy5cbiAgICovXG4gIHJlYWRvbmx5IGFzc3VtZVJvbGVDcmVkZW50aWFsc1NvdXJjZURlc2NyaXB0aW9uPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIEJhc2UgZnVuY3Rpb25hbGl0eSBvZiBTREsgd2l0aG91dCBjcmVkZW50aWFsIGZldGNoaW5nXG4gKi9cbmV4cG9ydCBjbGFzcyBTREsgaW1wbGVtZW50cyBJU0RLIHtcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgYWNjb3VudENhY2hlID0gbmV3IEFjY291bnRBY2Nlc3NLZXlDYWNoZSgpO1xuXG4gIHB1YmxpYyByZWFkb25seSBjdXJyZW50UmVnaW9uOiBzdHJpbmc7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBjb25maWc6IENvbmZpZ3VyYXRpb25PcHRpb25zO1xuXG4gIC8qKlxuICAgKiBEZWZhdWx0IHJldHJ5IG9wdGlvbnMgZm9yIFNESyBjbGllbnRzLlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSByZXRyeU9wdGlvbnMgPSB7IG1heFJldHJpZXM6IDYsIHJldHJ5RGVsYXlPcHRpb25zOiB7IGJhc2U6IDMwMCB9IH07XG5cbiAgLyoqXG4gICAqIFRoZSBtb3JlIGdlbmVyb3VzIHJldHJ5IHBvbGljeSBmb3IgQ2xvdWRGb3JtYXRpb24sIHdoaWNoIGhhcyBhIDEgVFBNIGxpbWl0IG9uIGNlcnRhaW4gQVBJcyxcbiAgICogd2hpY2ggYXJlIGFidW5kYW50bHkgdXNlZCBmb3IgZGVwbG95bWVudCB0cmFja2luZywgLi4uXG4gICAqXG4gICAqIFNvIHdlJ3JlIGFsbG93aW5nIHdheSBtb3JlIHJldHJpZXMsIGJ1dCB3YWl0aW5nIGEgYml0IG1vcmUuXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGNsb3VkRm9ybWF0aW9uUmV0cnlPcHRpb25zID0geyBtYXhSZXRyaWVzOiAxMCwgcmV0cnlEZWxheU9wdGlvbnM6IHsgYmFzZTogMV8wMDAgfSB9O1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgX2NyZWRlbnRpYWxzOiBBV1MuQ3JlZGVudGlhbHMsXG4gICAgcmVnaW9uOiBzdHJpbmcsXG4gICAgaHR0cE9wdGlvbnM6IENvbmZpZ3VyYXRpb25PcHRpb25zID0ge30sXG4gICAgcHJpdmF0ZSByZWFkb25seSBzZGtPcHRpb25zOiBTZGtPcHRpb25zID0ge30pIHtcblxuICAgIHRoaXMuY29uZmlnID0ge1xuICAgICAgLi4uaHR0cE9wdGlvbnMsXG4gICAgICAuLi50aGlzLnJldHJ5T3B0aW9ucyxcbiAgICAgIGNyZWRlbnRpYWxzOiBfY3JlZGVudGlhbHMsXG4gICAgICByZWdpb24sXG4gICAgICBsb2dnZXI6IHsgbG9nOiAoLi4ubWVzc2FnZXMpID0+IG1lc3NhZ2VzLmZvckVhY2gobSA9PiB0cmFjZSgnJXMnLCBtKSkgfSxcbiAgICB9O1xuICAgIHRoaXMuY3VycmVudFJlZ2lvbiA9IHJlZ2lvbjtcbiAgfVxuXG4gIHB1YmxpYyBjbG91ZEZvcm1hdGlvbigpOiBBV1MuQ2xvdWRGb3JtYXRpb24ge1xuICAgIHJldHVybiB0aGlzLndyYXBTZXJ2aWNlRXJyb3JIYW5kbGluZyhuZXcgQVdTLkNsb3VkRm9ybWF0aW9uKHtcbiAgICAgIC4uLnRoaXMuY29uZmlnLFxuICAgICAgLi4udGhpcy5jbG91ZEZvcm1hdGlvblJldHJ5T3B0aW9ucyxcbiAgICB9KSk7XG4gIH1cblxuICBwdWJsaWMgZWMyKCk6IEFXUy5FQzIge1xuICAgIHJldHVybiB0aGlzLndyYXBTZXJ2aWNlRXJyb3JIYW5kbGluZyhuZXcgQVdTLkVDMih0aGlzLmNvbmZpZykpO1xuICB9XG5cbiAgcHVibGljIHNzbSgpOiBBV1MuU1NNIHtcbiAgICByZXR1cm4gdGhpcy53cmFwU2VydmljZUVycm9ySGFuZGxpbmcobmV3IEFXUy5TU00odGhpcy5jb25maWcpKTtcbiAgfVxuXG4gIHB1YmxpYyBzMygpOiBBV1MuUzMge1xuICAgIHJldHVybiB0aGlzLndyYXBTZXJ2aWNlRXJyb3JIYW5kbGluZyhuZXcgQVdTLlMzKHRoaXMuY29uZmlnKSk7XG4gIH1cblxuICBwdWJsaWMgcm91dGU1MygpOiBBV1MuUm91dGU1MyB7XG4gICAgcmV0dXJuIHRoaXMud3JhcFNlcnZpY2VFcnJvckhhbmRsaW5nKG5ldyBBV1MuUm91dGU1Myh0aGlzLmNvbmZpZykpO1xuICB9XG5cbiAgcHVibGljIGVjcigpOiBBV1MuRUNSIHtcbiAgICByZXR1cm4gdGhpcy53cmFwU2VydmljZUVycm9ySGFuZGxpbmcobmV3IEFXUy5FQ1IodGhpcy5jb25maWcpKTtcbiAgfVxuXG4gIHB1YmxpYyBlbGJ2MigpOiBBV1MuRUxCdjIge1xuICAgIHJldHVybiB0aGlzLndyYXBTZXJ2aWNlRXJyb3JIYW5kbGluZyhuZXcgQVdTLkVMQnYyKHRoaXMuY29uZmlnKSk7XG4gIH1cblxuICBwdWJsaWMgc2VjcmV0c01hbmFnZXIoKTogQVdTLlNlY3JldHNNYW5hZ2VyIHtcbiAgICByZXR1cm4gdGhpcy53cmFwU2VydmljZUVycm9ySGFuZGxpbmcobmV3IEFXUy5TZWNyZXRzTWFuYWdlcih0aGlzLmNvbmZpZykpO1xuICB9XG5cbiAgcHVibGljIGFzeW5jIGN1cnJlbnRBY2NvdW50KCk6IFByb21pc2U8QWNjb3VudD4ge1xuICAgIC8vIEdldC9yZWZyZXNoIGlmIG5lY2Vzc2FyeSBiZWZvcmUgd2UgY2FuIGFjY2VzcyBgYWNjZXNzS2V5SWRgXG4gICAgYXdhaXQgdGhpcy5mb3JjZUNyZWRlbnRpYWxSZXRyaWV2YWwoKTtcblxuICAgIHJldHVybiBjYWNoZWQodGhpcywgQ1VSUkVOVF9BQ0NPVU5UX0tFWSwgKCkgPT4gU0RLLmFjY291bnRDYWNoZS5mZXRjaCh0aGlzLl9jcmVkZW50aWFscy5hY2Nlc3NLZXlJZCwgYXN5bmMgKCkgPT4ge1xuICAgICAgLy8gaWYgd2UgZG9uJ3QgaGF2ZSBvbmUsIHJlc29sdmUgZnJvbSBTVFMgYW5kIHN0b3JlIGluIGNhY2hlLlxuICAgICAgZGVidWcoJ0xvb2tpbmcgdXAgZGVmYXVsdCBhY2NvdW50IElEIGZyb20gU1RTJyk7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBuZXcgQVdTLlNUUyh0aGlzLmNvbmZpZykuZ2V0Q2FsbGVySWRlbnRpdHkoKS5wcm9taXNlKCk7XG4gICAgICBjb25zdCBhY2NvdW50SWQgPSByZXN1bHQuQWNjb3VudDtcbiAgICAgIGNvbnN0IHBhcnRpdGlvbiA9IHJlc3VsdC5Bcm4hLnNwbGl0KCc6JylbMV07XG4gICAgICBpZiAoIWFjY291bnRJZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NUUyBkaWRuXFwndCByZXR1cm4gYW4gYWNjb3VudCBJRCcpO1xuICAgICAgfVxuICAgICAgZGVidWcoJ0RlZmF1bHQgYWNjb3VudCBJRDonLCBhY2NvdW50SWQpO1xuICAgICAgcmV0dXJuIHsgYWNjb3VudElkLCBwYXJ0aXRpb24gfTtcbiAgICB9KSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIHRoZSBjdXJyZW50IGNyZWRlbnRpYWxzXG4gICAqXG4gICAqIERvbid0IHVzZSAtLSBvbmx5IHVzZWQgdG8gd3JpdGUgdGVzdHMgYXJvdW5kIGFzc3VtaW5nIHJvbGVzLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIGN1cnJlbnRDcmVkZW50aWFscygpOiBQcm9taXNlPEFXUy5DcmVkZW50aWFscz4ge1xuICAgIGF3YWl0IHRoaXMuZm9yY2VDcmVkZW50aWFsUmV0cmlldmFsKCk7XG4gICAgcmV0dXJuIHRoaXMuX2NyZWRlbnRpYWxzO1xuICB9XG5cbiAgLyoqXG4gICAqIEZvcmNlIHJldHJpZXZhbCBvZiB0aGUgY3VycmVudCBjcmVkZW50aWFsc1xuICAgKlxuICAgKiBSZWxldmFudCBpZiB0aGUgY3VycmVudCBjcmVkZW50aWFscyBhcmUgQXNzdW1lUm9sZSBjcmVkZW50aWFscyAtLSBkbyB0aGUgYWN0dWFsXG4gICAqIGxvb2t1cCwgYW5kIHRyYW5zbGF0ZSBhbnkgZXJyb3IgaW50byBhIHVzZWZ1bCBlcnJvciBtZXNzYWdlICh0YWtpbmcgaW50b1xuICAgKiBhY2NvdW50IGNyZWRlbnRpYWwgcHJvdmVuYW5jZSkuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZm9yY2VDcmVkZW50aWFsUmV0cmlldmFsKCkge1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLl9jcmVkZW50aWFscy5nZXRQcm9taXNlKCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgZGVidWcoYEFzc3VtaW5nIHJvbGUgZmFpbGVkOiAke2UubWVzc2FnZX1gKTtcbiAgICAgIHRocm93IG5ldyBFcnJvcihbXG4gICAgICAgICdDb3VsZCBub3QgYXNzdW1lIHJvbGUgaW4gdGFyZ2V0IGFjY291bnQnLFxuICAgICAgICAuLi50aGlzLnNka09wdGlvbnMuYXNzdW1lUm9sZUNyZWRlbnRpYWxzU291cmNlRGVzY3JpcHRpb25cbiAgICAgICAgICA/IFtgdXNpbmcgJHt0aGlzLnNka09wdGlvbnMuYXNzdW1lUm9sZUNyZWRlbnRpYWxzU291cmNlRGVzY3JpcHRpb259YF1cbiAgICAgICAgICA6IFtdLFxuICAgICAgICBlLm1lc3NhZ2UsXG4gICAgICAgICcuIFBsZWFzZSBtYWtlIHN1cmUgdGhhdCB0aGlzIHJvbGUgZXhpc3RzIGluIHRoZSBhY2NvdW50LiBJZiBpdCBkb2VzblxcJ3QgZXhpc3QsIChyZSktYm9vdHN0cmFwIHRoZSBlbnZpcm9ubWVudCAnICtcbiAgICAgICAgJ3dpdGggdGhlIHJpZ2h0IFxcJy0tdHJ1c3RcXCcsIHVzaW5nIHRoZSBsYXRlc3QgdmVyc2lvbiBvZiB0aGUgQ0RLIENMSS4nLFxuICAgICAgXS5qb2luKCcgJykpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm4gYSB3cmFwcGluZyBvYmplY3QgZm9yIHRoZSB1bmRlcmx5aW5nIHNlcnZpY2Ugb2JqZWN0XG4gICAqXG4gICAqIFJlc3BvbmRzIHRvIGZhaWx1cmVzIGluIHRoZSB1bmRlcmx5aW5nIHNlcnZpY2UgY2FsbHMsIGluIHR3byBkaWZmZXJlbnRcbiAgICogd2F5czpcbiAgICpcbiAgICogLSBXaGVuIGVycm9ycyBhcmUgZW5jb3VudGVyZWQsIGxvZyB0aGUgZmFpbGluZyBjYWxsIGFuZCB0aGUgZXJyb3IgdGhhdFxuICAgKiAgIGl0IHRyaWdnZXJlZCAoYXQgZGVidWcgbGV2ZWwpLiBUaGlzIGlzIG5lY2Vzc2FyeSBiZWNhdXNlIHRoZSBsYWNrIG9mXG4gICAqICAgc3RhY2sgdHJhY2VzIGluIE5vZGVKUyBvdGhlcndpc2UgbWFrZXMgaXQgdmVyeSBoYXJkIHRvIHN1c3Mgb3V0IHdoZXJlXG4gICAqICAgYSBjZXJ0YWluIEFXUyBlcnJvciBvY2N1cnJlZC5cbiAgICogLSBUaGUgSlMgU0RLIGhhcyBhIGZ1bm55IGJ1c2luZXNzIG9mIHdyYXBwaW5nIGFueSBjcmVkZW50aWFsLWJhc2VkIGVycm9yXG4gICAqICAgaW4gYSBzdXBlci1nZW5lcmljIChhbmQgaW4gb3VyIGNhc2Ugd3JvbmcpIGV4Y2VwdGlvbi4gSWYgd2UgdGhlbiB1c2UgYVxuICAgKiAgICdDaGFpbmFibGVUZW1wb3JhcnlDcmVkZW50aWFscycgYW5kIHRoZSB0YXJnZXQgcm9sZSBkb2Vzbid0IGV4aXN0LFxuICAgKiAgIHRoZSBlcnJvciBtZXNzYWdlIHRoYXQgc2hvd3MgdXAgYnkgZGVmYXVsdCBpcyBzdXBlciBtaXNsZWFkaW5nXG4gICAqICAgKGh0dHBzOi8vZ2l0aHViLmNvbS9hd3MvYXdzLXNkay1qcy9pc3N1ZXMvMzI3MikuIFdlIGNhbiBmaXggdGhpcyBiZWNhdXNlXG4gICAqICAgdGhlIGV4Y2VwdGlvbiBjb250YWlucyB0aGUgXCJpbm5lciBleGNlcHRpb25cIiwgc28gd2UgdW53cmFwIGFuZCB0aHJvd1xuICAgKiAgIHRoZSBjb3JyZWN0IGVycm9yIChcImNhbm5vdCBhc3N1bWUgcm9sZVwiKS5cbiAgICpcbiAgICogVGhlIHdyYXBwaW5nIGJ1c2luZXNzIGJlbG93IGlzIHNsaWdodGx5IG1vcmUgY29tcGxpY2F0ZWQgdGhhbiB5b3UnZCB0aGlua1xuICAgKiBiZWNhdXNlIHdlIG11c3QgaG9vayBpbnRvIHRoZSBgcHJvbWlzZSgpYCBtZXRob2Qgb2YgdGhlIG9iamVjdCB0aGF0J3MgYmVpbmdcbiAgICogcmV0dXJuZWQgZnJvbSB0aGUgbWV0aG9kcyBvZiB0aGUgb2JqZWN0IHRoYXQgd2Ugd3JhcCwgc28gdGhlcmUncyB0d29cbiAgICogbGV2ZWxzIG9mIHdyYXBwaW5nIGdvaW5nIG9uLCBhbmQgYWxzbyBzb21lIGV4Y2VwdGlvbnMgdG8gdGhlIHdyYXBwaW5nIG1hZ2ljLlxuICAgKi9cbiAgcHJpdmF0ZSB3cmFwU2VydmljZUVycm9ySGFuZGxpbmc8QSBleHRlbmRzIG9iamVjdD4oc2VydmljZU9iamVjdDogQSk6IEEge1xuICAgIGNvbnN0IGNsYXNzT2JqZWN0ID0gc2VydmljZU9iamVjdC5jb25zdHJ1Y3Rvci5wcm90b3R5cGU7XG4gICAgY29uc3Qgc2VsZiA9IHRoaXM7XG5cbiAgICByZXR1cm4gbmV3IFByb3h5KHNlcnZpY2VPYmplY3QsIHtcbiAgICAgIGdldChvYmo6IEEsIHByb3A6IHN0cmluZykge1xuICAgICAgICBjb25zdCByZWFsID0gKG9iaiBhcyBhbnkpW3Byb3BdO1xuICAgICAgICAvLyBUaGluZ3Mgd2UgZG9uJ3Qgd2FudCB0byBpbnRlcmNlcHQ6XG4gICAgICAgIC8vIC0gQW55dGhpbmcgdGhhdCdzIG5vdCBhIGZ1bmN0aW9uLlxuICAgICAgICAvLyAtICdjb25zdHJ1Y3RvcicsIHMzLnVwbG9hZCgpIHdpbGwgdXNlIHRoaXMgdG8gZG8gc29tZSBtYWdpYyBhbmQgd2UgbmVlZCB0aGUgdW5kZXJseWluZyBjb25zdHJ1Y3Rvci5cbiAgICAgICAgLy8gLSBBbnkgbWV0aG9kIHRoYXQncyBub3Qgb24gdGhlIHNlcnZpY2UgY2xhc3MgKGRvIG5vdCBpbnRlcmNlcHQgJ21ha2VSZXF1ZXN0JyBhbmQgb3RoZXIgaGVscGVycykuXG4gICAgICAgIGlmIChwcm9wID09PSAnY29uc3RydWN0b3InIHx8ICFjbGFzc09iamVjdC5oYXNPd25Qcm9wZXJ0eShwcm9wKSB8fCAhaXNGdW5jdGlvbihyZWFsKSkgeyByZXR1cm4gcmVhbDsgfVxuXG4gICAgICAgIC8vIE5PVEU6IFRoaXMgbXVzdCBiZSBhIGZ1bmN0aW9uKCkgYW5kIG5vdCBhbiAoKSA9PiB7XG4gICAgICAgIC8vIGJlY2F1c2UgSSBuZWVkICd0aGlzJyB0byBiZSBkeW5hbWljYWxseSBib3VuZCBhbmQgbm90IHN0YXRpY2FsbHkgYm91bmQuXG4gICAgICAgIC8vIElmIHlvdXIgbGludGVyIGNvbXBsYWlucyBkb24ndCBsaXN0ZW4gdG8gaXQhXG4gICAgICAgIHJldHVybiBmdW5jdGlvbih0aGlzOiBhbnkpIHtcbiAgICAgICAgICAvLyBDYWxsIHRoZSB1bmRlcmx5aW5nIGZ1bmN0aW9uLiBJZiBpdCByZXR1cm5zIGFuIG9iamVjdCB3aXRoIGEgcHJvbWlzZSgpXG4gICAgICAgICAgLy8gbWV0aG9kIG9uIGl0LCB3cmFwIHRoYXQgJ3Byb21pc2UnIG1ldGhvZC5cbiAgICAgICAgICBjb25zdCBhcmdzID0gW10uc2xpY2UuY2FsbChhcmd1bWVudHMsIDApO1xuICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gcmVhbC5hcHBseSh0aGlzLCBhcmdzKTtcblxuICAgICAgICAgIC8vIERvbid0IGludGVyY2VwdCB1bmxlc3MgdGhlIHJldHVybiB2YWx1ZSBpcyBhbiBvYmplY3Qgd2l0aCBhICcucHJvbWlzZSgpJyBtZXRob2QuXG4gICAgICAgICAgaWYgKHR5cGVvZiByZXNwb25zZSAhPT0gJ29iamVjdCcgfHwgIXJlc3BvbnNlKSB7IHJldHVybiByZXNwb25zZTsgfVxuICAgICAgICAgIGlmICghKCdwcm9taXNlJyBpbiByZXNwb25zZSkpIHsgcmV0dXJuIHJlc3BvbnNlOyB9XG5cbiAgICAgICAgICAvLyBSZXR1cm4gYW4gb2JqZWN0IHdpdGggdGhlIHByb21pc2UgbWV0aG9kIHJlcGxhY2VkIHdpdGggYSB3cmFwcGVyIHdoaWNoIHdpbGxcbiAgICAgICAgICAvLyBkbyBhZGRpdGlvbmFsIHRoaW5ncyB0byBlcnJvcnMuXG4gICAgICAgICAgcmV0dXJuIE9iamVjdC5hc3NpZ24oT2JqZWN0LmNyZWF0ZShyZXNwb25zZSksIHtcbiAgICAgICAgICAgIHByb21pc2UoKSB7XG4gICAgICAgICAgICAgIHJldHVybiByZXNwb25zZS5wcm9taXNlKCkuY2F0Y2goKGU6IEVycm9yICYgeyBjb2RlPzogc3RyaW5nIH0pID0+IHtcbiAgICAgICAgICAgICAgICBlID0gc2VsZi5tYWtlRGV0YWlsZWRFeGNlcHRpb24oZSk7XG4gICAgICAgICAgICAgICAgZGVidWcoYENhbGwgZmFpbGVkOiAke3Byb3B9KCR7SlNPTi5zdHJpbmdpZnkoYXJnc1swXSl9KSA9PiAke2UubWVzc2FnZX0gKGNvZGU9JHtlLmNvZGV9KWApO1xuICAgICAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChlKTsgLy8gUmUtJ3Rocm93JyB0aGUgbmV3IGVycm9yXG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfTtcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogRXh0cmFjdCBhIG1vcmUgZGV0YWlsZWQgZXJyb3Igb3V0IG9mIGEgZ2VuZXJpYyBlcnJvciBpZiB3ZSBjYW5cbiAgICpcbiAgICogSWYgdGhpcyBpcyBhbiBlcnJvciBhYm91dCBBc3N1bWluZyBSb2xlcywgYWRkIGluIHRoZSBjb250ZXh0IHNob3dpbmcgdGhlXG4gICAqIGNoYWluIG9mIGNyZWRlbnRpYWxzIHdlIHVzZWQgdG8gdHJ5IHRvIGFzc3VtZSB0aGUgcm9sZS5cbiAgICovXG4gIHByaXZhdGUgbWFrZURldGFpbGVkRXhjZXB0aW9uKGU6IEVycm9yKTogRXJyb3Ige1xuICAgIC8vIFRoaXMgaXMgdGhlIHN1cGVyLWdlbmVyaWMgXCJzb21ldGhpbmcncyB3cm9uZ1wiIGVycm9yIHRoYXQgdGhlIEpTIFNESyB3cmFwcyBvdGhlciBlcnJvcnMgaW4uXG4gICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2F3cy9hd3Mtc2RrLWpzL2Jsb2IvZjBhYzJlNTM0NTdjNzUxMjg4M2QwNjc3MDEzZWFjYWFkNmNkOGExOS9saWIvZXZlbnRfbGlzdGVuZXJzLmpzI0w4NFxuICAgIGlmICh0eXBlb2YgZS5tZXNzYWdlID09PSAnc3RyaW5nJyAmJiBlLm1lc3NhZ2Uuc3RhcnRzV2l0aCgnTWlzc2luZyBjcmVkZW50aWFscyBpbiBjb25maWcnKSkge1xuICAgICAgY29uc3Qgb3JpZ2luYWwgPSAoZSBhcyBhbnkpLm9yaWdpbmFsRXJyb3I7XG4gICAgICBpZiAob3JpZ2luYWwpIHtcbiAgICAgICAgLy8gV2hlbiB0aGUgU0RLIGRvZXMgYSAndXRpbC5jb3B5JywgdGhleSBsb3NlIHRoZSBFcnJvci1uZXNzIG9mIHRoZSBpbm5lciBlcnJvclxuICAgICAgICAvLyAodGhleSBjb3B5IHRoZSBFcnJvcidzIHByb3BlcnRpZXMgaW50byBhIHBsYWluIG9iamVjdCkgc28gbWFrZSBpdCBhbiBFcnJvciBvYmplY3QgYWdhaW4uXG4gICAgICAgIGUgPSBPYmplY3QuYXNzaWduKG5ldyBFcnJvcigpLCBvcmlnaW5hbCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQXQgdGhpcyBwb2ludCwgdGhlIGVycm9yIG1pZ2h0IHN0aWxsIGJlIGEgZ2VuZXJpYyBcIkNoYWluYWJsZVRlbXBvcmFyeUNyZWRlbnRpYWxzIGZhaWxlZFwiXG4gICAgLy8gZXJyb3Igd2hpY2ggd3JhcHMgdGhlIFJFQUwgZXJyb3IgKEFzc3VtZVJvbGUgZmFpbGVkKS4gV2UncmUgZ29pbmcgdG8gcmVwbGFjZSB0aGUgZXJyb3JcbiAgICAvLyBtZXNzYWdlIHdpdGggb25lIHRoYXQncyBtb3JlIGxpa2VseSB0byBoZWxwIHVzZXJzLCBhbmQgdGVsbCB0aGVtIHRoZSBtb3N0IHByb2JhYmxlXG4gICAgLy8gZml4IChib290c3RyYXBwaW5nKS4gVGhlIHVuZGVybHlpbmcgc2VydmljZSBjYWxsIGZhaWx1cmUgd2lsbCBiZSBhcHBlbmRlZCBiZWxvdy5cbiAgICBpZiAoZS5tZXNzYWdlID09PSAnQ291bGQgbm90IGxvYWQgY3JlZGVudGlhbHMgZnJvbSBDaGFpbmFibGVUZW1wb3JhcnlDcmVkZW50aWFscycpIHtcbiAgICAgIGUubWVzc2FnZSA9IFtcbiAgICAgICAgJ0NvdWxkIG5vdCBhc3N1bWUgcm9sZSBpbiB0YXJnZXQgYWNjb3VudCcsXG4gICAgICAgIC4uLnRoaXMuc2RrT3B0aW9ucy5hc3N1bWVSb2xlQ3JlZGVudGlhbHNTb3VyY2VEZXNjcmlwdGlvblxuICAgICAgICAgID8gW2B1c2luZyAke3RoaXMuc2RrT3B0aW9ucy5hc3N1bWVSb2xlQ3JlZGVudGlhbHNTb3VyY2VEZXNjcmlwdGlvbn1gXVxuICAgICAgICAgIDogW10sXG4gICAgICAgICcoZGlkIHlvdSBib290c3RyYXAgdGhlIGVudmlyb25tZW50IHdpdGggdGhlIHJpZ2h0IFxcJy0tdHJ1c3RcXCdzPyknLFxuICAgICAgXS5qb2luKCcgJyk7XG4gICAgfVxuXG4gICAgLy8gUmVwbGFjZSB0aGUgbWVzc2FnZSBvbiB0aGlzIGVycm9yIHdpdGggYSBjb25jYXRlbmF0aW9uIG9mIGFsbCBpbm5lciBlcnJvciBtZXNzYWdlcy5cbiAgICAvLyBNdXN0IG1vcmUgY2xlYXIgd2hhdCdzIGdvaW5nIG9uIHRoYXQgd2F5LlxuICAgIGUubWVzc2FnZSA9IGFsbENoYWluZWRFeGNlcHRpb25NZXNzYWdlcyhlKTtcbiAgICByZXR1cm4gZTtcbiAgfVxufVxuXG5jb25zdCBDVVJSRU5UX0FDQ09VTlRfS0VZID0gU3ltYm9sKCdjdXJyZW50X2FjY291bnRfa2V5Jyk7XG5cbmZ1bmN0aW9uIGlzRnVuY3Rpb24oeDogYW55KTogeCBpcyAoLi4uYXJnczogYW55W10pID0+IGFueSB7XG4gIHJldHVybiB4ICYmIHt9LnRvU3RyaW5nLmNhbGwoeCkgPT09ICdbb2JqZWN0IEZ1bmN0aW9uXSc7XG59XG5cbi8qKlxuICogUmV0dXJuIHRoZSBjb25jYXRlbmF0ZWQgbWVzc2FnZSBvZiBhbGwgZXhjZXB0aW9ucyBpbiB0aGUgQVdTIGV4Y2VwdGlvbiBjaGFpblxuICovXG5mdW5jdGlvbiBhbGxDaGFpbmVkRXhjZXB0aW9uTWVzc2FnZXMoZTogRXJyb3IgfCB1bmRlZmluZWQpIHtcbiAgY29uc3QgcmV0ID0gbmV3IEFycmF5PHN0cmluZz4oKTtcbiAgd2hpbGUgKGUpIHtcbiAgICByZXQucHVzaChlLm1lc3NhZ2UpO1xuICAgIGUgPSAoZSBhcyBhbnkpLm9yaWdpbmFsRXJyb3I7XG4gIH1cbiAgcmV0dXJuIHJldC5qb2luKCc6ICcpO1xufVxuIl19