Skip to content

Commit

Permalink
Reindex GitHub Events into maintainer inactivity data
Browse files Browse the repository at this point in the history
Signed-off-by: Brandon Shien <[email protected]>
  • Loading branch information
bshien committed Nov 1, 2024
1 parent 436acb9 commit 526989d
Show file tree
Hide file tree
Showing 19 changed files with 1,327 additions and 22 deletions.
1 change: 1 addition & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ So you want to contribute code to this project? Excellent! We're glad you're her
- `cdk deploy OpenSearchMetrics-GitHubAutomationApp`: Create the resources which launches the [GitHub Automation App](https://github.com/opensearch-project/automation-app). Listens to GitHub events and index the data to Metrics cluster.
- `cdk deploy OpenSearchMetrics-GitHubAutomationAppEvents-S3`: Creates the S3 Bucket for the [GitHub Automation App](https://github.com/opensearch-project/automation-app) to store OpenSearch Project GitHub Events.
- `cdk deploy OpenSearchS3EventIndex-Workflow`: Creates the Lambda and Step Function to index the GitHub Events stored in the S3 Bucket to the Metrics cluster.
- `cdk deploy OpenSearchMaintainerInactivity-Workflow`: Creates the Lambda and Step Function to index Maintainer Inactivity to the Metrics cluster.

### Forking and Cloning

Expand Down
16 changes: 13 additions & 3 deletions infrastructure/lib/infrastructure-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@ import { Construct } from 'constructs';
import { OpenSearchMetricsNginxCognito } from "./constructs/opensearchNginxProxyCognito";
import Project from './enums/project';
import { GitHubAutomationApp } from "./stacks/gitHubAutomationApp";
import { GitHubWorkflowMonitorAlarms } from "./stacks/gitHubWorkflowMonitorAlarms";
import { OpenSearchMetricsWorkflowStack } from "./stacks/metricsWorkflow";
import { OpenSearchMetricsMonitoringStack } from "./stacks/monitoringDashboard";
import { OpenSearchDomainStack } from "./stacks/opensearch";
import { OpenSearchMetricsNginxReadonly } from "./stacks/opensearchNginxProxyReadonly";
import { OpenSearchHealthRoute53 } from "./stacks/route53";
import { OpenSearchS3 } from "./stacks/s3";
import { OpenSearchS3EventIndexWorkflowStack } from "./stacks/s3EventIndexWorkflow";
import { OpenSearchMetricsSecretsStack } from "./stacks/secrets";
import { VpcStack } from "./stacks/vpc";
import { OpenSearchWAF } from "./stacks/waf";
import { GitHubWorkflowMonitorAlarms } from "./stacks/gitHubWorkflowMonitorAlarms";
import { OpenSearchS3EventIndexWorkflowStack } from "./stacks/s3EventIndexWorkflow";
import { OpenSearchMaintainerInactivityWorkflowStack } from "./stacks/maintainerInactivityWorkflow";

export class InfrastructureStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
Expand Down Expand Up @@ -99,6 +100,14 @@ export class InfrastructureStack extends Stack {
})
openSearchS3EventIndexWorkflowStack.node.addDependency(vpcStack, openSearchDomainStack);

// Create OpenSearch Maintainer Inactivity Lambda setup
const openSearchMaintainerInactivityWorkflowStack = new OpenSearchMaintainerInactivityWorkflowStack(app, 'OpenSearchMaintainerInactivity-Workflow', {
opensearchDomainStack: openSearchDomainStack,
vpcStack: vpcStack,
lambdaPackage: Project.LAMBDA_PACKAGE,
})
openSearchMaintainerInactivityWorkflowStack.node.addDependency(vpcStack, openSearchDomainStack);

// Create Secret Manager for the metrics project
const openSearchMetricsSecretsStack = new OpenSearchMetricsSecretsStack(app, "OpenSearchMetrics-Secrets", {
secretName: 'metrics-creds'
Expand All @@ -111,7 +120,8 @@ export class InfrastructureStack extends Stack {
account: Project.AWS_ACCOUNT,
workflowComponent: {
opensearchMetricsWorkflowStateMachineName: openSearchMetricsWorkflowStack.workflowComponent.opensearchMetricsWorkflowStateMachineName,
opensearchS3EventIndexWorkflowStateMachineName: openSearchS3EventIndexWorkflowStack.workflowComponent.opensearchS3EventIndexWorkflowStateMachineName
opensearchMaintainerInactivityWorkflowStateMachineName: openSearchMaintainerInactivityWorkflowStack.workflowComponent.opensearchMaintainerInactivityWorkflowStateMachineName,
opensearchS3EventIndexWorkflowStateMachineName: openSearchS3EventIndexWorkflowStack.workflowComponent.opensearchS3EventIndexWorkflowStateMachineName,
},
lambdaPackage: Project.LAMBDA_PACKAGE,
secrets: openSearchMetricsSecretsStack.secret,
Expand Down
78 changes: 78 additions & 0 deletions infrastructure/lib/stacks/maintainerInactivityWorkflow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

import { Duration, Stack, StackProps } from "aws-cdk-lib";
import { Rule, Schedule } from "aws-cdk-lib/aws-events";
import { SfnStateMachine } from "aws-cdk-lib/aws-events-targets";
import { JsonPath, StateMachine } from "aws-cdk-lib/aws-stepfunctions";
import { LambdaInvoke } from "aws-cdk-lib/aws-stepfunctions-tasks";
import { Construct } from 'constructs';
import { OpenSearchLambda } from "../constructs/lambda";
import { OpenSearchDomainStack } from "./opensearch";
import { VpcStack } from "./vpc";

export interface OpenSearchMaintainerInactivityWorkflowStackProps extends StackProps {
readonly opensearchDomainStack: OpenSearchDomainStack;
readonly vpcStack: VpcStack;
readonly lambdaPackage: string
}

export interface WorkflowComponent {
opensearchMaintainerInactivityWorkflowStateMachineName: string
}

export class OpenSearchMaintainerInactivityWorkflowStack extends Stack {
public readonly workflowComponent: WorkflowComponent;
constructor(scope: Construct, id: string, props: OpenSearchMaintainerInactivityWorkflowStackProps) {
super(scope, id, props);

const maintainerInactivityTask = this.createMaintainerInactivityTask(
this,
props.opensearchDomainStack,
props.vpcStack,
props.lambdaPackage
);
const opensearchMaintainerInactivityWorkflow = new StateMachine(this, 'OpenSearchMaintainerInactivityWorkflow', {
definition: maintainerInactivityTask,
timeout: Duration.minutes(15),
stateMachineName: 'OpenSearchMaintainerInactivityWorkflow'
})

new Rule(this, 'MaintainerInactivityWorkflow-Every-Day', {
schedule: Schedule.expression('cron(15 0 * * ? *)'),
targets: [new SfnStateMachine(opensearchMaintainerInactivityWorkflow)],
});

this.workflowComponent = {
opensearchMaintainerInactivityWorkflowStateMachineName: opensearchMaintainerInactivityWorkflow.stateMachineName
}
}

private createMaintainerInactivityTask(scope: Construct, opensearchDomainStack: OpenSearchDomainStack,
vpcStack: VpcStack, lambdaPackage: string) {
const openSearchDomain = opensearchDomainStack.domain;
const maintainerInactivityLambda = new OpenSearchLambda(scope, "OpenSearchMetricsMaintainerInactivityLambdaFunction", {
lambdaNameBase: "OpenSearchMetricsMaintainerInactivity",
handler: "org.opensearchmetrics.lambda.MaintainerInactivityLambda",
lambdaZipPath: `../../../build/distributions/${lambdaPackage}`,
vpc: vpcStack.vpc,
securityGroup: vpcStack.securityGroup,
role: opensearchDomainStack.openSearchMetricsLambdaRole,
environment: {
OPENSEARCH_DOMAIN_ENDPOINT: openSearchDomain.domainEndpoint,
OPENSEARCH_DOMAIN_REGION: openSearchDomain.env.region,
OPENSEARCH_DOMAIN_ROLE: opensearchDomainStack.fullAccessRole.roleArn,
},
}).lambda;
return new LambdaInvoke(scope, 'Maintainer Inactivity Lambda', {
lambdaFunction: maintainerInactivityLambda,
resultPath: JsonPath.DISCARD,
timeout: Duration.minutes(15)
}).addRetry();
}
}
1 change: 1 addition & 0 deletions infrastructure/lib/stacks/monitoringDashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export class OpenSearchMetricsMonitoringStack extends Stack {
private snsMonitorStepFunctionExecutionsFailed(): void {
const stepFunctionSnsAlarms = [
{ alertName: 'StepFunction_execution_errors_MetricsWorkflow', stateMachineName: this.props.workflowComponent.opensearchMetricsWorkflowStateMachineName },
{ alertName: 'StepFunction_execution_errors_MaintainerInactivityWorkflow', stateMachineName: this.props.workflowComponent.opensearchMaintainerInactivityWorkflowStateMachineName },
{ alertName: 'StepFunction_execution_errors_S3EventIndexWorkflow', stateMachineName: this.props.workflowComponent.opensearchS3EventIndexWorkflowStateMachineName },
];

Expand Down
78 changes: 78 additions & 0 deletions infrastructure/test/maintainer-inactivity-workflow-stack.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

import { App } from "aws-cdk-lib";
import { Template } from "aws-cdk-lib/assertions";
import { OpenSearchMetricsWorkflowStack } from "../lib/stacks/metricsWorkflow";
import Project from "../lib/enums/project";
import { OpenSearchDomainStack } from "../lib/stacks/opensearch";
import { VpcStack } from "../lib/stacks/vpc";
import { ArnPrincipal } from "aws-cdk-lib/aws-iam";
import {OpenSearchS3} from "../lib/stacks/s3";
import {OpenSearchMaintainerInactivityWorkflowStack} from "../lib/stacks/maintainerInactivityWorkflow";

test('Maintainer Inactivity Workflow Stack Test', () => {
const app = new App();
const vpcStack = new VpcStack(app, 'Test-OpenSearchHealth-VPC', {});
const s3Stack = new OpenSearchS3(app, "Test-OpenSearchMetrics-GitHubAutomationAppEvents-S3");
const openSearchDomainStack = new OpenSearchDomainStack(app, 'OpenSearchHealth-OpenSearch', {
region: "us-east-1",
account: "test-account",
vpcStack: new VpcStack(app, 'OpenSearchHealth-VPC', {}),
enableNginxCognito: true,
jenkinsAccess: {
jenkinsAccountRoles: [
new ArnPrincipal(Project.JENKINS_MASTER_ROLE),
new ArnPrincipal(Project.JENKINS_AGENT_ROLE)
]
},
githubAutomationAppAccess: "sample-role-arn",
githubEventsBucket: s3Stack.bucket,
});
const openSearchMaintainerInactivityWorkflowStack = new OpenSearchMaintainerInactivityWorkflowStack(app, 'Test-OpenSearchMaintainerInactivity-Workflow', {
opensearchDomainStack: openSearchDomainStack,
vpcStack: vpcStack,
lambdaPackage: Project.LAMBDA_PACKAGE,
});
const template = Template.fromStack(openSearchMaintainerInactivityWorkflowStack);
template.resourceCountIs('AWS::IAM::Role', 2);
template.resourceCountIs('AWS::Lambda::Function', 1);
template.hasResourceProperties('AWS::Lambda::Function', {
"FunctionName": "OpenSearchMetricsMaintainerInactivityLambda",
"Handler": "org.opensearchmetrics.lambda.MaintainerInactivityLambda"
});
template.resourceCountIs('AWS::StepFunctions::StateMachine', 1);
template.hasResourceProperties('AWS::StepFunctions::StateMachine', {
"DefinitionString": {
"Fn::Join": [
"",
[
"{\"StartAt\":\"Maintainer Inactivity Lambda\",\"States\":{\"Maintainer Inactivity Lambda\":{\"End\":true,\"Retry\":[{\"ErrorEquals\":[\"Lambda.ClientExecutionTimeoutException\",\"Lambda.ServiceException\",\"Lambda.AWSLambdaException\",\"Lambda.SdkClientException\"],\"IntervalSeconds\":2,\"MaxAttempts\":6,\"BackoffRate\":2},{\"ErrorEquals\":[\"States.ALL\"]}],\"Type\":\"Task\",\"TimeoutSeconds\":900,\"ResultPath\":null,\"Resource\":\"arn:",
{
"Ref": "AWS::Partition"
},
":states:::lambda:invoke\",\"Parameters\":{\"FunctionName\":\"",
{
"Fn::GetAtt": [
"OpenSearchMetricsMaintainerInactivityLambdaCB6D4475",
"Arn"
]
},
"\",\"Payload.$\":\"$\"}}},\"TimeoutSeconds\":900}"
]
]
},
"RoleArn": {
"Fn::GetAtt": [
"OpenSearchMaintainerInactivityWorkflowRoleF9A5E625",
"Arn"
]
},
"StateMachineName": "OpenSearchMaintainerInactivityWorkflow"
});
});
49 changes: 46 additions & 3 deletions infrastructure/test/monitoring-stack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import Project from "../lib/enums/project";
import { OpenSearchMetricsWorkflowStack } from "../lib/stacks/metricsWorkflow";
import { OpenSearchMetricsMonitoringStack } from "../lib/stacks/monitoringDashboard";
import { OpenSearchDomainStack } from "../lib/stacks/opensearch";
import { VpcStack } from "../lib/stacks/vpc";
import { OpenSearchMetricsSecretsStack } from "../lib/stacks/secrets";
import { OpenSearchS3 } from "../lib/stacks/s3";
import { OpenSearchS3EventIndexWorkflowStack } from "../lib/stacks/s3EventIndexWorkflow";
import { OpenSearchMetricsSecretsStack } from "../lib/stacks/secrets";
import { VpcStack } from "../lib/stacks/vpc";
import { OpenSearchMaintainerInactivityWorkflowStack } from "../lib/stacks/maintainerInactivityWorkflow";

test('Monitoring Stack Test', () => {
const app = new App();
Expand All @@ -41,6 +42,11 @@ test('Monitoring Stack Test', () => {
vpcStack: vpcStack,
lambdaPackage: Project.LAMBDA_PACKAGE,
});
const openSearchMaintainerInactivityWorkflowStack = new OpenSearchMaintainerInactivityWorkflowStack(app, 'Test-OpenSearchMaintainerInactivity-Workflow', {
opensearchDomainStack: opensearchDomainStack,
vpcStack: vpcStack,
lambdaPackage: Project.LAMBDA_PACKAGE,
});
const openSearchS3EventIndexWorkflowStack = new OpenSearchS3EventIndexWorkflowStack(app, 'Test-OpenSearchS3EventIndex-Workflow', {
region: Project.REGION,
opensearchDomainStack: opensearchDomainStack,
Expand All @@ -56,6 +62,7 @@ test('Monitoring Stack Test', () => {
account: Project.AWS_ACCOUNT,
workflowComponent: {
opensearchMetricsWorkflowStateMachineName: openSearchMetricsWorkflowStack.workflowComponent.opensearchMetricsWorkflowStateMachineName,
opensearchMaintainerInactivityWorkflowStateMachineName: openSearchMaintainerInactivityWorkflowStack.workflowComponent.opensearchMaintainerInactivityWorkflowStateMachineName,
opensearchS3EventIndexWorkflowStateMachineName: openSearchS3EventIndexWorkflowStack.workflowComponent.opensearchS3EventIndexWorkflowStateMachineName
},
lambdaPackage: Project.LAMBDA_PACKAGE,
Expand All @@ -65,7 +72,7 @@ test('Monitoring Stack Test', () => {
const template = Template.fromStack(openSearchMetricsMonitoringStack);
template.resourceCountIs('AWS::IAM::Role', 2);
template.resourceCountIs('AWS::IAM::Policy', 1);
template.resourceCountIs('AWS::CloudWatch::Alarm', 3);
template.resourceCountIs('AWS::CloudWatch::Alarm', 4);
template.resourceCountIs('AWS::SNS::Topic', 2);
template.resourceCountIs('AWS::Synthetics::Canary', 1);
template.hasResourceProperties('AWS::IAM::Role', {
Expand Down Expand Up @@ -172,6 +179,42 @@ test('Monitoring Stack Test', () => {
"Threshold": 1,
"TreatMissingData": "notBreaching"
});

template.hasResourceProperties('AWS::CloudWatch::Alarm', {
"AlarmActions": [
{
"Ref": "SnsMonitorsStepFunctionExecutionsFailedOpenSearchMetricsAlarmStepFunctionExecutionsFailed0B259DBC"
}
],
"AlarmDescription": "Detect SF execution failure",
"AlarmName": "StepFunction_execution_errors_MaintainerInactivityWorkflow",
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
"DatapointsToAlarm": 1,
"Dimensions": [
{
"Name": "StateMachineArn",
"Value": {
"Fn::Join": [
"",
[
"arn:aws:states:::stateMachine:",
{
"Fn::ImportValue": "Test-OpenSearchMaintainerInactivity-Workflow:ExportsOutputFnGetAttOpenSearchMaintainerInactivityWorkflowE07E380BName0C54300B"
}
]
]
}
}
],
"EvaluationPeriods": 1,
"MetricName": "ExecutionsFailed",
"Namespace": "AWS/States",
"Period": 300,
"Statistic": "Sum",
"Threshold": 1,
"TreatMissingData": "notBreaching"
});

template.hasResourceProperties('AWS::CloudWatch::Alarm', {
"AlarmActions": [
{
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/org/opensearchmetrics/dagger/CommonModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.opensearchmetrics.metrics.MetricsCalculation;
import org.opensearchmetrics.metrics.general.*;
import org.opensearchmetrics.metrics.label.LabelMetrics;
import org.opensearchmetrics.metrics.maintainer.MaintainerMetrics;
import org.opensearchmetrics.metrics.release.ReleaseMetrics;
import org.opensearchmetrics.util.OpenSearchUtil;
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
Expand Down Expand Up @@ -116,15 +117,15 @@ public MetricsCalculation getMetricsCalculation(OpenSearchUtil openSearchUtil, O
CreatedIssues createdIssues, IssueComments issueComments,
PullComments pullComments, IssuePositiveReactions issuePositiveReactions,
IssueNegativeReactions issueNegativeReactions, LabelMetrics labelMetrics,
ReleaseMetrics releaseMetrics) {
ReleaseMetrics releaseMetrics, MaintainerMetrics maintainerMetrics) {
return new MetricsCalculation(openSearchUtil, objectMapper,
untriagedIssues, uncommentedPullRequests,
unlabelledPullRequests, unlabelledIssues,
mergedPullRequests, openPullRequests,
openIssues, closedIssues, createdIssues,
issueComments, pullComments,
issuePositiveReactions, issueNegativeReactions,
labelMetrics, releaseMetrics);
labelMetrics, releaseMetrics, maintainerMetrics);
}

@Provides
Expand Down
Loading

0 comments on commit 526989d

Please sign in to comment.