UNPKG

aws-logs-comptroller

Version:

Set Log Retention and prune orphaned LogGroups on a schedule using Step Functions service integrations and intrinsic functions.

234 lines 27.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getRunner = void 0; const aws_iam_1 = require("aws-cdk-lib/aws-iam"); const aws_logs_1 = require("aws-cdk-lib/aws-logs"); const aws_stepfunctions_1 = require("aws-cdk-lib/aws-stepfunctions"); const aws_stepfunctions_tasks_1 = require("aws-cdk-lib/aws-stepfunctions-tasks"); exports.getRunner = (scope, retentionInDays = aws_logs_1.RetentionDays.ONE_WEEK) => { /** * Input includes `LogGroups`, `Stats`, and `Token`. * Fan out on `LogGroups` (should be a max of 50). */ const map = new aws_stepfunctions_1.Map(scope, 'Map', { inputPath: '$.LogGroups', maxConcurrency: 10, resultPath: '$.MapResult', }); /** * Split LogGroupName into parts. We're looking for `lambda` in the second place. */ const getLGParts = new aws_stepfunctions_1.Pass(scope, 'GetLGParts', { parameters: { 'LGParts.$': 'States.StringSplit($.LogGroupName, \'/\')', }, resultPath: aws_stepfunctions_1.JsonPath.stringAt('$.Function'), }); /** * If we don't have at least two segments, `getLogType` will fail. Get the length here. * Can't seem to nest another intrinsic in `States.ArrayLength`. */ const getArrayLen = new aws_stepfunctions_1.Pass(scope, 'GetArrayLen', { parameters: { Len: aws_stepfunctions_1.JsonPath.numberAt('States.ArrayLength($.Function.LGParts)'), }, resultPath: '$.Array', }); const twoOrMoreParts = new aws_stepfunctions_1.Choice(scope, 'TwoOrMore?'); /** * Get the second part. We're looking for `lambda` to see if this is a LogGroup for a Lambda Function. */ const getLogType = new aws_stepfunctions_1.Pass(scope, 'GetLogType', { parameters: { LogType: aws_stepfunctions_1.JsonPath.numberAt('States.ArrayGetItem($.Function.LGParts, 1)'), }, resultPath: '$.Log', }); const isLambdaLog = new aws_stepfunctions_1.Choice(scope, 'IsLambdaLog?'); /** * The third part of the LogGroup name should be the name of the Lambda Function. */ const getFnName = new aws_stepfunctions_1.Pass(scope, 'GetFnName', { parameters: { FunctionName: aws_stepfunctions_1.JsonPath.stringAt('States.ArrayGetItem($.Function.LGParts, 2)'), }, resultPath: '$.Function', }); const isFnPresent = new aws_stepfunctions_1.Choice(scope, 'FunctionPresent?'); /** * Make API call to get the Lambda Function. * If this call is successful, then we have a LogGroup for an active Lambda Function. * If we get a 404 back, then the Lambda Function no longer exists and the LogGroup isn't needed. */ const getFn = new aws_stepfunctions_tasks_1.CallAwsService(scope, 'GetFunction', { action: 'getFunction', iamResources: ['*'], parameters: { 'FunctionName.$': '$.Function.FunctionName', }, resultPath: aws_stepfunctions_1.JsonPath.DISCARD, service: 'lambda', }); /** * Make an API call to delete a LogGroup. * This will end a branch of the Map State. * The resultSelector indicates the LogGroup was deleted. */ const deleteLG = new aws_stepfunctions_tasks_1.CallAwsService(scope, 'DeleteLG', { action: 'deleteLogGroup', iamAction: 'logs:DeleteLogGroup', iamResources: ['*'], parameters: { LogGroupName: aws_stepfunctions_1.JsonPath.stringAt('$.LogGroupName'), }, resultSelector: { IsDeleted: 1, IsRetained: 0, }, service: 'cloudwatchlogs', }); /** * Make an API call to set retention on the LogGroup. * This will end a branch of the Map State. * The resultSelector indicates retention was added. */ const addRetention = new aws_stepfunctions_tasks_1.CallAwsService(scope, 'AddRetention', { action: 'putRetentionPolicy', iamAction: 'logs:PutRetentionPolicy', iamResources: ['*'], parameters: { LogGroupName: aws_stepfunctions_1.JsonPath.stringAt('$.LogGroupName'), RetentionInDays: retentionInDays, }, resultSelector: { IsDeleted: 0, IsRetained: 1, }, service: 'cloudwatchlogs', }); /** * For any log group that survived the pruning above, check to see if it already has retention. * If it doesn't have retention, then set it to the `retentionInDays` prop. */ const hasRetention = new aws_stepfunctions_1.Choice(scope, 'HasRetention?') .when(aws_stepfunctions_1.Condition.isNotPresent('$.RetentionInDays'), addRetention) .otherwise(new aws_stepfunctions_1.Pass(scope, 'lgtm', { result: aws_stepfunctions_1.Result.fromObject({ IsDeleted: 0, IsRetained: 0, }), })); /** * Initialize the loop for adding up the stats. */ const initStatsLoop = new aws_stepfunctions_1.Pass(scope, 'InitStatsLoop', { parameters: { Index: 0, ResultLen: aws_stepfunctions_1.JsonPath.numberAt('States.ArrayLength($.MapResult)'), }, resultPath: '$.Iterator', }); /** * Get the next item of the array by index. */ const getNextResult = new aws_stepfunctions_1.Pass(scope, 'GetNextResult', { parameters: { 'Result.$': 'States.ArrayGetItem($.MapResult, $.Iterator.Index)', }, resultPath: '$.R', }); /** * Increment stats based on the `IsDeleted` and `IsRetained` values of the map result. */ const incrementStats = new aws_stepfunctions_1.Pass(scope, 'IncrementStats', { parameters: { 'LGsDeleted.$': 'States.MathAdd($.Stats.LGsDeleted, $.R.Result.IsDeleted)', 'LGsRetained.$': 'States.MathAdd($.Stats.LGsRetained, $.R.Result.IsRetained)', LGsSeen: aws_stepfunctions_1.JsonPath.numberAt('$.Stats.LGsSeen'), }, resultPath: '$.Stats', }); /** * Increment the counter for the next loop. */ const incrementCounter = new aws_stepfunctions_1.Pass(scope, 'IncrementCounter', { parameters: { 'Index.$': 'States.MathAdd($.Iterator.Index, 1)', ResultLen: aws_stepfunctions_1.JsonPath.numberAt('$.Iterator.ResultLen'), }, resultPath: '$.Iterator', }); /** * Return the Task Token to the parent state machine and pass stats back. */ const sendSuccess = new aws_stepfunctions_tasks_1.CallAwsService(scope, 'SendSuccess', { action: 'sendTaskSuccess', iamAction: 'states:SendTaskSuccess', iamResources: ['*'], parameters: { 'Output.$': '$.Stats', 'TaskToken.$': '$.Token', }, service: 'sfn', }); const hasNextMapResult = new aws_stepfunctions_1.Choice(scope, 'HasNextMapResult?'); /** * Iterate over LogGroups, parsing the name to determine if it's a Lambda Log or not. */ map.iterator(getLGParts); getLGParts.next(getArrayLen); getArrayLen.next(twoOrMoreParts); twoOrMoreParts.when(aws_stepfunctions_1.Condition.numberGreaterThanEquals('$.Array.Len', 2), getLogType); twoOrMoreParts.otherwise(hasRetention); getLogType.next(isLambdaLog); /** * For each Lambda log, get the function name as the 3rd part of the LogGroup name. * Call the Lambda service to see if that function still exists. * If the function doesn't exist, then delete the log. */ isLambdaLog.when(aws_stepfunctions_1.Condition.stringEquals('$.Log.LogType', 'lambda'), getFnName); isLambdaLog.otherwise(hasRetention); getFnName.next(isFnPresent); isFnPresent.when(aws_stepfunctions_1.Condition.isNotNull('$.Function.FunctionName'), getFn); isFnPresent.otherwise(hasRetention); getFn.next(hasRetention); getFn.addCatch(deleteLG, { errors: [aws_stepfunctions_1.Errors.TASKS_FAILED], resultPath: aws_stepfunctions_1.JsonPath.DISCARD, }); /** * After the map completes, loop over the results in order to track how many LogGroups were deleted * and how many had retention set. */ map.next(initStatsLoop); initStatsLoop.next(hasNextMapResult); hasNextMapResult.when(aws_stepfunctions_1.Condition.numberLessThanJsonPath('$.Iterator.Index', '$.Iterator.ResultLen'), getNextResult); getNextResult.next(incrementStats); incrementStats.next(incrementCounter); incrementCounter.next(hasNextMapResult); /** * Finally send the stats back to the parent state machine. */ hasNextMapResult.otherwise(sendSuccess); /** * These calls may be throttled, so retry many times. * We probably don't need 10 retries, but the default wasn't enough. */ addRetention.addRetry({ maxAttempts: 10, }); deleteLG.addRetry({ maxAttempts: 10, }); const sm = new aws_stepfunctions_1.StateMachine(scope, 'LogsComptrollerRunner', { definition: map, stateMachineName: 'logs-comptroller-runner', tracingEnabled: true, }); /** * Workaround for CDK throwing a circular dependency error when attempting `grantTaskResponse`. */ sm.addToRolePolicy(new aws_iam_1.PolicyStatement({ actions: ['states:SendTaskSuccess'], resources: ['*'], })); return sm; }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVubmVyLXN0YXRlLW1hY2hpbmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvcnVubmVyLXN0YXRlLW1hY2hpbmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsaURBQXNEO0FBQ3RELG1EQUFxRDtBQUNyRCxxRUFBcUg7QUFDckgsaUZBQXFFO0FBR3hELFFBQUEsU0FBUyxHQUFHLENBQ3ZCLEtBQWdCLEVBQ2hCLGtCQUFpQyx3QkFBYSxDQUFDLFFBQVEsRUFDekMsRUFBRTtJQUNoQjs7O09BR0c7SUFDSCxNQUFNLEdBQUcsR0FBRyxJQUFJLHVCQUFHLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRTtRQUNoQyxTQUFTLEVBQUUsYUFBYTtRQUN4QixjQUFjLEVBQUUsRUFBRTtRQUNsQixVQUFVLEVBQUUsYUFBYTtLQUMxQixDQUFDLENBQUM7SUFFSDs7T0FFRztJQUNILE1BQU0sVUFBVSxHQUFHLElBQUksd0JBQUksQ0FBQyxLQUFLLEVBQUUsWUFBWSxFQUFFO1FBQy9DLFVBQVUsRUFBRTtZQUNWLFdBQVcsRUFBRSwyQ0FBMkM7U0FDekQ7UUFDRCxVQUFVLEVBQUUsNEJBQVEsQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDO0tBQzVDLENBQUMsQ0FBQztJQUVIOzs7T0FHRztJQUNILE1BQU0sV0FBVyxHQUFHLElBQUksd0JBQUksQ0FBQyxLQUFLLEVBQUUsYUFBYSxFQUFFO1FBQ2pELFVBQVUsRUFBRTtZQUNWLEdBQUcsRUFBRSw0QkFBUSxDQUFDLFFBQVEsQ0FBQyx3Q0FBd0MsQ0FBQztTQUNqRTtRQUNELFVBQVUsRUFBRSxTQUFTO0tBQ3RCLENBQUMsQ0FBQztJQUVILE1BQU0sY0FBYyxHQUFHLElBQUksMEJBQU0sQ0FBQyxLQUFLLEVBQUUsWUFBWSxDQUFDLENBQUM7SUFFdkQ7O09BRUc7SUFDSCxNQUFNLFVBQVUsR0FBRyxJQUFJLHdCQUFJLENBQUMsS0FBSyxFQUFFLFlBQVksRUFBRTtRQUMvQyxVQUFVLEVBQUU7WUFD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QUM7SUFDRixhQUFhLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBRW5DLGNBQWMsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUN0QyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUV4Qzs7T0FFRztJQUNILGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUV4Qzs7O09BR0c7SUFDSCxZQUFZLENBQUMsUUFBUSxDQUFDO1FBQ3BCLFdBQVcsRUFBRSxFQUFFO0tBQ2hCLENBQUMsQ0FBQztJQUNILFFBQVEsQ0FBQyxRQUFRLENBQUM7UUFDaEIsV0FBVyxFQUFFLEVBQUU7S0FDaEIsQ0FBQyxDQUFDO0lBRUgsTUFBTSxFQUFFLEdBQUcsSUFBSSxnQ0FBWSxDQUFDLEtBQUssRUFBRSx1QkFBdUIsRUFBRTtRQUMxRCxVQUFVLEVBQUUsR0FBRztRQUNmLGdCQUFnQixFQUFFLHlCQUF5QjtRQUMzQyxjQUFjLEVBQUUsSUFBSTtLQUNyQixDQUFDLENBQUM7SUFFSDs7T0FFRztJQUNILEVBQUUsQ0FBQyxlQUFlLENBQ2hCLElBQUkseUJBQWUsQ0FBQztRQUNsQixPQUFPLEVBQUUsQ0FBQyx3QkFBd0IsQ0FBQztRQUNuQyxTQUFTLEVBQUUsQ0FBQyxHQUFHLENBQUM7S0FDakIsQ0FBQyxDQUNILENBQUM7SUFFRixPQUFPLEVBQUUsQ0FBQztBQUNaLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFBvbGljeVN0YXRlbWVudCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1pYW0nO1xuaW1wb3J0IHsgUmV0ZW50aW9uRGF5cyB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1sb2dzJztcbmltcG9ydCB7IENob2ljZSwgQ29uZGl0aW9uLCBFcnJvcnMsIEpzb25QYXRoLCBNYXAsIFBhc3MsIFJlc3VsdCwgU3RhdGVNYWNoaW5lIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXN0ZXBmdW5jdGlvbnMnO1xuaW1wb3J0IHsgQ2FsbEF3c1NlcnZpY2UgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtc3RlcGZ1bmN0aW9ucy10YXNrcyc7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcblxuZXhwb3J0IGNvbnN0IGdldFJ1bm5lciA9IChcbiAgc2NvcGU6IENvbnN0cnVjdCxcbiAgcmV0ZW50aW9uSW5EYXlzOiBSZXRlbnRpb25EYXlzID0gUmV0ZW50aW9uRGF5cy5PTkVfV0VFSyxcbik6IFN0YXRlTWFjaGluZSA9PiB7XG4gIC8qKlxuICAgKiBJbnB1dCBpbmNsdWRlcyBgTG9nR3JvdXBzYCwgYFN0YXRzYCwgYW5kIGBUb2tlbmAuXG4gICAqIEZhbiBvdXQgb24gYExvZ0dyb3Vwc2AgKHNob3VsZCBiZSBhIG1heCBvZiA1MCkuXG4gICAqL1xuICBjb25zdCBtYXAgPSBuZXcgTWFwKHNjb3BlLCAnTWFwJywge1xuICAgIGlucHV0UGF0aDogJyQuTG9nR3JvdXBzJyxcbiAgICBtYXhDb25jdXJyZW5jeTogMTAsXG4gICAgcmVzdWx0UGF0aDogJyQuTWFwUmVzdWx0JyxcbiAgfSk7XG5cbiAgLyoqXG4gICAqIFNwbGl0IExvZ0dyb3VwTmFtZSBpbnRvIHBhcnRzLiBXZSdyZSBsb29raW5nIGZvciBgbGFtYmRhYCBpbiB0aGUgc2Vjb25kIHBsYWNlLlxuICAgKi9cbiAgY29uc3QgZ2V0TEdQYXJ0cyA9IG5ldyBQYXNzKHNjb3BlLCAnR2V0TEdQYXJ0cycsIHtcbiAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAnTEdQYXJ0cy4kJzogJ1N0YXRlcy5TdHJpbmdTcGxpdCgkLkxvZ0dyb3VwTmFtZSwgXFwnL1xcJyknLFxuICAgIH0sXG4gICAgcmVzdWx0UGF0aDogSnNvblBhdGguc3RyaW5nQXQoJyQuRnVuY3Rpb24nKSxcbiAgfSk7XG5cbiAgLyoqXG4gICAqIElmIHdlIGRvbid0IGhhdmUgYXQgbGVhc3QgdHdvIHNlZ21lbnRzLCBgZ2V0TG9nVHlwZWAgd2lsbCBmYWlsLiBHZXQgdGhlIGxlbmd0aCBoZXJlLlxuICAgKiBDYW4ndCBzZWVtIHRvIG5lc3QgYW5vdGhlciBpbnRyaW5zaWMgaW4gYFN0YXRlcy5BcnJheUxlbmd0aGAuXG4gICAqL1xuICBjb25zdCBnZXRBcnJheUxlbiA9IG5ldyBQYXNzKHNjb3BlLCAnR2V0QXJyYXlMZW4nLCB7XG4gICAgcGFyYW1ldGVyczoge1xuICAgICAgTGVuOiBKc29uUGF0aC5udW1iZXJBdCgnU3RhdGVzLkFycmF5TGVuZ3RoKCQuRnVuY3Rpb24uTEdQYXJ0cyknKSxcbiAgICB9LFxuICAgIHJlc3VsdFBhdGg6ICckLkFycmF5JyxcbiAgfSk7XG5cbiAgY29uc3QgdHdvT3JNb3JlUGFydHMgPSBuZXcgQ2hvaWNlKHNjb3BlLCAnVHdvT3JNb3JlPycpO1xuXG4gIC8qKlxuICAgKiBHZXQgdGhlIHNlY29uZCBwYXJ0LiBXZSdyZSBsb29raW5nIGZvciBgbGFtYmRhYCB0byBzZWUgaWYgdGhpcyBpcyBhIExvZ0dyb3VwIGZvciBhIExhbWJkYSBGdW5jdGlvbi5cbiAgICovXG4gIGNvbnN0IGdldExvZ1R5cGUgPSBuZXcgUGFzcyhzY29wZSwgJ0dldExvZ1R5cGUnLCB7XG4gICAgcGFyYW1ldGVyczoge1xuICAgICAgTG9nVHlwZTogSnNvblBhdGgubnVtYmVyQXQoJ1N0YXRlcy5BcnJheUdldEl0ZW0oJC5GdW5jdGlvbi5MR1BhcnRzLCAxKScpLFxuICAgIH0sXG4gICAgcmVzdWx0UGF0aDogJyQuTG9nJyxcbiAgfSk7XG5cbiAgY29uc3QgaXNMYW1iZGFMb2cgPSBuZXcgQ2hvaWNlKHNjb3BlLCAnSXNMYW1iZGFMb2c/Jyk7XG5cbiAgLyoqXG4gICAqIFRoZSB0aGlyZCBwYXJ0IG9mIHRoZSBMb2dHcm91cCBuYW1lIHNob3VsZCBiZSB0aGUgbmFtZSBvZiB0aGUgTGFtYmRhIEZ1bmN0aW9uLlxuICAgKi9cbiAgY29uc3QgZ2V0Rm5OYW1lID0gbmV3IFBhc3Moc2NvcGUsICdHZXRGbk5hbWUnLCB7XG4gICAgcGFyYW1ldGVyczoge1xuICAgICAgRnVuY3Rpb25OYW1lOiBKc29uUGF0aC5zdHJpbmdBdChcbiAgICAgICAgJ1N0YXRlcy5BcnJheUdldEl0ZW0oJC5GdW5jdGlvbi5MR1BhcnRzLCAyKScsXG4gICAgICApLFxuICAgIH0sXG4gICAgcmVzdWx0UGF0aDogJyQuRnVuY3Rpb24nLFxuICB9KTtcblxuICBjb25zdCBpc0ZuUHJlc2VudCA9IG5ldyBDaG9pY2Uoc2NvcGUsICdGdW5jdGlvblByZXNlbnQ/Jyk7XG5cbiAgLyoqXG4gICAqIE1ha2UgQVBJIGNhbGwgdG8gZ2V0IHRoZSBMYW1iZGEgRnVuY3Rpb24uXG4gICAqIElmIHRoaXMgY2FsbCBpcyBzdWNjZXNzZnVsLCB0aGVuIHdlIGhhdmUgYSBMb2dHcm91cCBmb3IgYW4gYWN0aXZlIExhbWJkYSBGdW5jdGlvbi5cbiAgICogSWYgd2UgZ2V0IGEgNDA0IGJhY2ssIHRoZW4gdGhlIExhbWJkYSBGdW5jdGlvbiBubyBsb25nZXIgZXhpc3RzIGFuZCB0aGUgTG9nR3JvdXAgaXNuJ3QgbmVlZGVkLlxuICAgKi9cbiAgY29uc3QgZ2V0Rm4gPSBuZXcgQ2FsbEF3c1NlcnZpY2Uoc2NvcGUsICdHZXRGdW5jdGlvbicsIHtcbiAgICBhY3Rpb246ICdnZXRGdW5jdGlvbicsXG4gICAgaWFtUmVzb3VyY2VzOiBbJyonXSxcbiAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAnRnVuY3Rpb25OYW1lLiQnOiAnJC5GdW5jdGlvbi5GdW5jdGlvbk5hbWUnLFxuICAgIH0sXG4gICAgcmVzdWx0UGF0aDogSnNvblBhdGguRElTQ0FSRCxcbiAgICBzZXJ2aWNlOiAnbGFtYmRhJyxcbiAgfSk7XG5cbiAgLyoqXG4gICAqIE1ha2UgYW4gQVBJIGNhbGwgdG8gZGVsZXRlIGEgTG9nR3JvdXAuXG4gICAqIFRoaXMgd2lsbCBlbmQgYSBicmFuY2ggb2YgdGhlIE1hcCBTdGF0ZS5cbiAgICogVGhlIHJlc3VsdFNlbGVjdG9yIGluZGljYXRlcyB0aGUgTG9nR3JvdXAgd2FzIGRlbGV0ZWQuXG4gICAqL1xuICBjb25zdCBkZWxldGVMRyA9IG5ldyBDYWxsQXdzU2VydmljZShzY29wZSwgJ0RlbGV0ZUxHJywge1xuICAgIGFjdGlvbjogJ2RlbGV0ZUxvZ0dyb3VwJyxcbiAgICBpYW1BY3Rpb246ICdsb2dzOkRlbGV0ZUxvZ0dyb3VwJyxcbiAgICBpYW1SZXNvdXJjZXM6IFsnKiddLFxuICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgIExvZ0dyb3VwTmFtZTogSnNvblBhdGguc3RyaW5nQXQoJyQuTG9nR3JvdXBOYW1lJyksXG4gICAgfSxcbiAgICByZXN1bHRTZWxlY3Rvcjoge1xuICAgICAgSXNEZWxldGVkOiAxLCBJc1JldGFpbmVkOiAwLFxuICAgIH0sXG4gICAgc2VydmljZTogJ2Nsb3Vkd2F0Y2hsb2dzJyxcbiAgfSk7XG5cbiAgLyoqXG4gICAqIE1ha2UgYW4gQVBJIGNhbGwgdG8gc2V0IHJldGVudGlvbiBvbiB0aGUgTG9nR3JvdXAuXG4gICAqIFRoaXMgd2lsbCBlbmQgYSBicmFuY2ggb2YgdGhlIE1hcCBTdGF0ZS5cbiAgICogVGhlIHJlc3VsdFNlbGVjdG9yIGluZGljYXRlcyByZXRlbnRpb24gd2FzIGFkZGVkLlxuICAgKi9cbiAgY29uc3QgYWRkUmV0ZW50aW9uID0gbmV3IENhbGxBd3NTZXJ2aWNlKHNjb3BlLCAnQWRkUmV0ZW50aW9uJywge1xuICAgIGFjdGlvbjogJ3B1dFJldGVudGlvblBvbGljeScsXG4gICAgaWFtQWN0aW9uOiAnbG9nczpQdXRSZXRlbnRpb25Qb2xpY3knLFxuICAgIGlhbVJlc291cmNlczogWycqJ10sXG4gICAgcGFyYW1ldGVyczoge1xuICAgICAgTG9nR3JvdXBOYW1lOiBKc29uUGF0aC5zdHJpbmdBdCgnJC5Mb2dHcm91cE5hbWUnKSxcbiAgICAgIFJldGVudGlvbkluRGF5czogcmV0ZW50aW9uSW5EYXlzLFxuICAgIH0sXG4gICAgcmVzdWx0U2VsZWN0b3I6IHtcbiAgICAgIElzRGVsZXRlZDogMCwgSXNSZXRhaW5lZDogMSxcbiAgICB9LFxuICAgIHNlcnZpY2U6ICdjbG91ZHdhdGNobG9ncycsXG4gIH0pO1xuXG4gIC8qKlxuICAgKiBGb3IgYW55IGxvZyBncm91cCB0aGF0IHN1cnZpdmVkIHRoZSBwcnVuaW5nIGFib3ZlLCBjaGVjayB0byBzZWUgaWYgaXQgYWxyZWFkeSBoYXMgcmV0ZW50aW9uLlxuICAgKiBJZiBpdCBkb2Vzbid0IGhhdmUgcmV0ZW50aW9uLCB0aGVuIHNldCBpdCB0byB0aGUgYHJldGVudGlvbkluRGF5c2AgcHJvcC5cbiAgICovXG4gIGNvbnN0IGhhc1JldGVudGlvbiA9IG5ldyBDaG9pY2Uoc2NvcGUsICdIYXNSZXRlbnRpb24/JylcbiAgICAud2hlbihDb25kaXRpb24uaXNOb3RQcmVzZW50KCckLlJldGVudGlvbkluRGF5cycpLCBhZGRSZXRlbnRpb24pXG4gICAgLm90aGVyd2lzZShcbiAgICAgIG5ldyBQYXNzKHNjb3BlLCAnbGd0bScsIHtcbiAgICAgICAgcmVzdWx0OiBSZXN1bHQuZnJvbU9iamVjdCh7XG4gICAgICAgICAgSXNEZWxldGVkOiAwLCBJc1JldGFpbmVkOiAwLFxuICAgICAgICB9KSxcbiAgICAgIH0pLFxuICAgICk7XG5cbiAgLyoqXG4gICAqIEluaXRpYWxpemUgdGhlIGxvb3AgZm9yIGFkZGluZyB1cCB0aGUgc3RhdHMuXG4gICAqL1xuICBjb25zdCBpbml0U3RhdHNMb29wID0gbmV3IFBhc3Moc2NvcGUsICdJbml0U3RhdHNMb29wJywge1xuICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgIEluZGV4OiAwLFxuICAgICAgUmVzdWx0TGVuOiBKc29uUGF0aC5udW1iZXJBdCgnU3RhdGVzLkFycmF5TGVuZ3RoKCQuTWFwUmVzdWx0KScpLFxuICAgIH0sXG4gICAgcmVzdWx0UGF0aDogJyQuSXRlcmF0b3InLFxuICB9KTtcblxuICAvKipcbiAgICogR2V0IHRoZSBuZXh0IGl0ZW0gb2YgdGhlIGFycmF5IGJ5IGluZGV4LlxuICAgKi9cbiAgY29uc3QgZ2V0TmV4dFJlc3VsdCA9IG5ldyBQYXNzKHNjb3BlLCAnR2V0TmV4dFJlc3VsdCcsIHtcbiAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAnUmVzdWx0LiQnOiAnU3RhdGVzLkFycmF5R2V0SXRlbSgkLk1hcFJlc3VsdCwgJC5JdGVyYXRvci5JbmRleCknLFxuICAgIH0sXG4gICAgcmVzdWx0UGF0aDogJyQuUicsXG4gIH0pO1xuXG4gIC8qKlxuICAgKiBJbmNyZW1lbnQgc3RhdHMgYmFzZWQgb24gdGhlIGBJc0RlbGV0ZWRgIGFuZCBgSXNSZXRhaW5lZGAgdmFsdWVzIG9mIHRoZSBtYXAgcmVzdWx0LlxuICAgKi9cbiAgY29uc3QgaW5jcmVtZW50U3RhdHMgPSBuZXcgUGFzcyhzY29wZSwgJ0luY3JlbWVudFN0YXRzJywge1xuICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICdMR3NEZWxldGVkLiQnOlxuICAgICAgICAnU3RhdGVzLk1hdGhBZGQoJC5TdGF0cy5MR3NEZWxldGVkLCAkLlIuUmVzdWx0LklzRGVsZXRlZCknLFxuICAgICAgJ0xHc1JldGFpbmVkLiQnOlxuICAgICAgICAnU3RhdGVzLk1hdGhBZGQoJC5TdGF0cy5MR3NSZXRhaW5lZCwgJC5SLlJlc3VsdC5Jc1JldGFpbmVkKScsXG4gICAgICBMR3NTZWVuOiBKc29uUGF0aC5udW1iZXJBdCgnJC5TdGF0cy5MR3NTZWVuJyksXG4gICAgfSxcbiAgICByZXN1bHRQYXRoOiAnJC5TdGF0cycsXG4gIH0pO1xuXG4gIC8qKlxuICAgKiBJbmNyZW1lbnQgdGhlIGNvdW50ZXIgZm9yIHRoZSBuZXh0IGxvb3AuXG4gICAqL1xuICBjb25zdCBpbmNyZW1lbnRDb3VudGVyID0gbmV3IFBhc3Moc2NvcGUsICdJbmNyZW1lbnRDb3VudGVyJywge1xuICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICdJbmRleC4kJzogJ1N0YXRlcy5NYXRoQWRkKCQuSXRlcmF0b3IuSW5kZXgsIDEpJyxcbiAgICAgIFJlc3VsdExlbjogSnNvblBhdGgubnVtYmVyQXQoJyQuSXRlcmF0b3IuUmVzdWx0TGVuJyksXG4gICAgfSxcbiAgICByZXN1bHRQYXRoOiAnJC5JdGVyYXRvcicsXG4gIH0pO1xuXG4gIC8qKlxuICAgKiBSZXR1cm4gdGhlIFRhc2sgVG9rZW4gdG8gdGhlIHBhcmVudCBzdGF0ZSBtYWNoaW5lIGFuZCBwYXNzIHN0YXRzIGJhY2suXG4gICAqL1xuICBjb25zdCBzZW5kU3VjY2VzcyA9IG5ldyBDYWxsQXdzU2VydmljZShzY29wZSwgJ1NlbmRTdWNjZXNzJywge1xuICAgIGFjdGlvbjogJ3NlbmRUYXNrU3VjY2VzcycsXG4gICAgaWFtQWN0aW9uOiAnc3RhdGVzOlNlbmRUYXNrU3VjY2VzcycsXG4gICAgaWFtUmVzb3VyY2VzOiBbJyonXSxcbiAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAnT3V0cHV0LiQnOiAnJC5TdGF0cycsXG4gICAgICAnVGFza1Rva2VuLiQnOiAnJC5Ub2tlbicsXG4gICAgfSxcbiAgICBzZXJ2aWNlOiAnc2ZuJyxcbiAgfSk7XG5cbiAgY29uc3QgaGFzTmV4dE1hcFJlc3VsdCA9IG5ldyBDaG9pY2Uoc2NvcGUsICdIYXNOZXh0TWFwUmVzdWx0PycpO1xuXG4gIC8qKlxuICAgKiBJdGVyYXRlIG92ZXIgTG9nR3JvdXBzLCBwYXJzaW5nIHRoZSBuYW1lIHRvIGRldGVybWluZSBpZiBpdCdzIGEgTGFtYmRhIExvZyBvciBub3QuXG4gICAqL1xuICBtYXAuaXRlcmF0b3IoZ2V0TEdQYXJ0cyk7XG4gIGdldExHUGFydHMubmV4dChnZXRBcnJheUxlbik7XG4gIGdldEFycmF5TGVuLm5leHQodHdvT3JNb3JlUGFydHMpO1xuICB0d29Pck1vcmVQYXJ0cy53aGVuKFxuICAgIENvbmRpdGlvbi5udW1iZXJHcmVhdGVyVGhhbkVxdWFscygnJC5BcnJheS5MZW4nLCAyKSxcbiAgICBnZXRMb2dUeXBlLFxuICApO1xuICB0d29Pck1vcmVQYXJ0cy5vdGhlcndpc2UoaGFzUmV0ZW50aW9uKTtcbiAgZ2V0TG9nVHlwZS5uZXh0KGlzTGFtYmRhTG9nKTtcblxuICAvKipcbiAgICogRm9yIGVhY2ggTGFtYmRhIGxvZywgZ2V0IHRoZSBmdW5jdGlvbiBuYW1lIGFzIHRoZSAzcmQgcGFydCBvZiB0aGUgTG9nR3JvdXAgbmFtZS5cbiAgICogQ2FsbCB0aGUgTGFtYmRhIHNlcnZpY2UgdG8gc2VlIGlmIHRoYXQgZnVuY3Rpb24gc3RpbGwgZXhpc3RzLlxuICAgKiBJZiB0aGUgZnVuY3Rpb24gZG9lc24ndCBleGlzdCwgdGhlbiBkZWxldGUgdGhlIGxvZy5cbiAgICovXG4gIGlzTGFtYmRhTG9nLndoZW4oXG4gICAgQ29uZGl0aW9uLnN0cmluZ0VxdWFscygnJC5Mb2cuTG9nVHlwZScsICdsYW1iZGEnKSxcbiAgICBnZXRGbk5hbWUsXG4gICk7XG4gIGlzTGFtYmRhTG9nLm90aGVyd2lzZShoYXNSZXRlbnRpb24pO1xuICBnZXRGbk5hbWUubmV4dChpc0ZuUHJlc2VudCk7XG4gIGlzRm5QcmVzZW50LndoZW4oQ29uZGl0aW9uLmlzTm90TnVsbCgnJC5GdW5jdGlvbi5GdW5jdGlvbk5hbWUnKSwgZ2V0Rm4pO1xuICBpc0ZuUHJlc2VudC5vdGhlcndpc2UoaGFzUmV0ZW50aW9uKTtcbiAgZ2V0Rm4ubmV4dChoYXNSZXRlbnRpb24pO1xuICBnZXRGbi5hZGRDYXRjaChkZWxldGVMRywge1xuICAgIGVycm9yczogW0Vycm9ycy5UQVNLU19GQUlMRURdLFxuICAgIHJlc3VsdFBhdGg6IEpzb25QYXRoLkRJU0NBUkQsXG4gIH0pO1xuXG4gIC8qKlxuICAgKiBBZnRlciB0aGUgbWFwIGNvbXBsZXRlcywgbG9vcCBvdmVyIHRoZSByZXN1bHRzIGluIG9yZGVyIHRvIHRyYWNrIGhvdyBtYW55IExvZ0dyb3VwcyB3ZXJlIGRlbGV0ZWRcbiAgICogYW5kIGhvdyBtYW55IGhhZCByZXRlbnRpb24gc2V0LlxuICAgKi9cbiAgbWFwLm5leHQoaW5pdFN0YXRzTG9vcCk7XG4gIGluaXRTdGF0c0xvb3AubmV4dChoYXNOZXh0TWFwUmVzdWx0KTtcbiAgaGFzTmV4dE1hcFJlc3VsdC53aGVuKFxuICAgIENvbmRpdGlvbi5udW1iZXJMZXNzVGhhbkpzb25QYXRoKFxuICAgICAgJyQuSXRlcmF0b3IuSW5kZXgnLFxuICAgICAgJyQuSXRlcmF0b3IuUmVzdWx0TGVuJyxcbiAgICApLFxuICAgIGdldE5leHRSZXN1bHQsXG4gICk7XG4gIGdldE5leHRSZXN1bHQubmV4dChpbmNyZW1lbnRTdGF0cyk7XG5cbiAgaW5jcmVtZW50U3RhdHMubmV4dChpbmNyZW1lbnRDb3VudGVyKTtcbiAgaW5jcmVtZW50Q291bnRlci5uZXh0KGhhc05leHRNYXBSZXN1bHQpO1xuXG4gIC8qKlxuICAgKiBGaW5hbGx5IHNlbmQgdGhlIHN0YXRzIGJhY2sgdG8gdGhlIHBhcmVudCBzdGF0ZSBtYWNoaW5lLlxuICAgKi9cbiAgaGFzTmV4dE1hcFJlc3VsdC5vdGhlcndpc2Uoc2VuZFN1Y2Nlc3MpO1xuXG4gIC8qKlxuICAgKiBUaGVzZSBjYWxscyBtYXkgYmUgdGhyb3R0bGVkLCBzbyByZXRyeSBtYW55IHRpbWVzLlxuICAgKiBXZSBwcm9iYWJseSBkb24ndCBuZWVkIDEwIHJldHJpZXMsIGJ1dCB0aGUgZGVmYXVsdCB3YXNuJ3QgZW5vdWdoLlxuICAgKi9cbiAgYWRkUmV0ZW50aW9uLmFkZFJldHJ5KHtcbiAgICBtYXhBdHRlbXB0czogMTAsXG4gIH0pO1xuICBkZWxldGVMRy5hZGRSZXRyeSh7XG4gICAgbWF4QXR0ZW1wdHM6IDEwLFxuICB9KTtcblxuICBjb25zdCBzbSA9IG5ldyBTdGF0ZU1hY2hpbmUoc2NvcGUsICdMb2dzQ29tcHRyb2xsZXJSdW5uZXInLCB7XG4gICAgZGVmaW5pdGlvbjogbWFwLFxuICAgIHN0YXRlTWFjaGluZU5hbWU6ICdsb2dzLWNvbXB0cm9sbGVyLXJ1bm5lcicsXG4gICAgdHJhY2luZ0VuYWJsZWQ6IHRydWUsXG4gIH0pO1xuXG4gIC8qKlxuICAgKiBXb3JrYXJvdW5kIGZvciBDREsgdGhyb3dpbmcgYSBjaXJjdWxhciBkZXBlbmRlbmN5IGVycm9yIHdoZW4gYXR0ZW1wdGluZyBgZ3JhbnRUYXNrUmVzcG9uc2VgLlxuICAgKi9cbiAgc20uYWRkVG9Sb2xlUG9saWN5KFxuICAgIG5ldyBQb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgYWN0aW9uczogWydzdGF0ZXM6U2VuZFRhc2tTdWNjZXNzJ10sXG4gICAgICByZXNvdXJjZXM6IFsnKiddLFxuICAgIH0pLFxuICApO1xuXG4gIHJldHVybiBzbTtcbn07XG4iXX0=