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
JavaScript
"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=