Skip to content

feat: remove deprecated bata feature workflow job queue #4249

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .tflint.hcl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
config {
format = "compact"
module = true
call_module_type = "local"
}

plugin "aws" {
Expand Down
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,8 @@ Talk to the forestkeepers in the `runners-channel` on Slack.
|------|------|
| [aws_sqs_queue.queued_builds](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue) | resource |
| [aws_sqs_queue.queued_builds_dlq](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue) | resource |
| [aws_sqs_queue.webhook_events_workflow_job_queue](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue) | resource |
| [aws_sqs_queue_policy.build_queue_dlq_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue_policy) | resource |
| [aws_sqs_queue_policy.build_queue_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue_policy) | resource |
| [aws_sqs_queue_policy.webhook_events_workflow_job_queue_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue_policy) | resource |
| [random_string.random](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource |
| [aws_iam_policy_document.deny_unsecure_transport](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |

Expand Down Expand Up @@ -156,7 +154,6 @@ Talk to the forestkeepers in the `runners-channel` on Slack.
| <a name="input_enable_ssm_on_runners"></a> [enable\_ssm\_on\_runners](#input\_enable\_ssm\_on\_runners) | Enable to allow access to the runner instances for debugging purposes via SSM. Note that this adds additional permissions to the runner instances. | `bool` | `false` | no |
| <a name="input_enable_user_data_debug_logging_runner"></a> [enable\_user\_data\_debug\_logging\_runner](#input\_enable\_user\_data\_debug\_logging\_runner) | Option to enable debug logging for user-data, this logs all secrets as well. | `bool` | `false` | no |
| <a name="input_enable_userdata"></a> [enable\_userdata](#input\_enable\_userdata) | Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI. | `bool` | `true` | no |
| <a name="input_enable_workflow_job_events_queue"></a> [enable\_workflow\_job\_events\_queue](#input\_enable\_workflow\_job\_events\_queue) | Enabling this experimental feature will create a secondary SQS queue to which a copy of the workflow\_job event will be delivered. | `bool` | `false` | no |
| <a name="input_eventbridge"></a> [eventbridge](#input\_eventbridge) | Enable the use of EventBridge by the module. By enabling this feature events will be put on the EventBridge by the webhook instead of directly dispatching to queues for scaling.<br/><br/> `enable`: Enable the EventBridge feature.<br/> `accept_events`: List can be used to only allow specific events to be putted on the EventBridge. By default all events, empty list will be be interpreted as all events. | <pre>object({<br/> enable = optional(bool, false)<br/> accept_events = optional(list(string), null)<br/> })</pre> | `{}` | no |
| <a name="input_ghes_ssl_verify"></a> [ghes\_ssl\_verify](#input\_ghes\_ssl\_verify) | GitHub Enterprise SSL verification. Set to 'false' when custom certificate (chains) is used for GitHub Enterprise Server (insecure). | `bool` | `true` | no |
| <a name="input_ghes_url"></a> [ghes\_url](#input\_ghes\_url) | GitHub Enterprise Server URL. Example: https://github.internal.co - DO NOT SET IF USING PUBLIC GITHUB | `string` | `null` | no |
Expand Down Expand Up @@ -251,7 +248,6 @@ Talk to the forestkeepers in the `runners-channel` on Slack.
| <a name="input_webhook_lambda_s3_object_version"></a> [webhook\_lambda\_s3\_object\_version](#input\_webhook\_lambda\_s3\_object\_version) | S3 object version for webhook lambda function. Useful if S3 versioning is enabled on source bucket. | `string` | `null` | no |
| <a name="input_webhook_lambda_timeout"></a> [webhook\_lambda\_timeout](#input\_webhook\_lambda\_timeout) | Time out of the webhook lambda in seconds. | `number` | `10` | no |
| <a name="input_webhook_lambda_zip"></a> [webhook\_lambda\_zip](#input\_webhook\_lambda\_zip) | File location of the webhook lambda zip file. | `string` | `null` | no |
| <a name="input_workflow_job_queue_configuration"></a> [workflow\_job\_queue\_configuration](#input\_workflow\_job\_queue\_configuration) | Configuration options for workflow job queue which is only applicable if the flag enable\_workflow\_job\_events\_queue is set to true. | <pre>object({<br/> delay_seconds = number<br/> visibility_timeout_seconds = number<br/> message_retention_seconds = number<br/> })</pre> | <pre>{<br/> "delay_seconds": null,<br/> "message_retention_seconds": null,<br/> "visibility_timeout_seconds": null<br/>}</pre> | no |

## Outputs

Expand Down
56 changes: 46 additions & 10 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,23 +332,59 @@ resource "aws_iam_role_policy" "event_rule_firehose_role" {

### Queue to publish workflow job events

!!! warning "Deprecated
!!! warning "Removed

This fearure will be removed since we introducing the EventBridge. Same functinallity can be implemented by adding a rule to the EventBridge to forward `workflow_job` events to the SQS queue.
This feaTure will be removed since we introducing the EventBridge. Same functionality can be implemented by adding a rule to the EventBridge to forward `workflow_job` events to the SQS queue.

This queue is an experimental feature to allow you to receive a copy of the wokflow_jobs events sent by the GitHub App. This can be used to calculate a matrix or monitor the system.
Below an example how you can sent all `workflow_job` with action `in_progress` to a SQS queue.

To enable the feature set `enable_workflow_job_events_queue = true`. Be aware though, this feature is experimental!
```hcl

Messages received on the queue are using the same format as published by GitHub wrapped in a property `workflowJobEvent`.
resource "aws_cloudwatch_event_rule" "workflow_job_in_progress" {
name = "workflow-job-in-progress"
event_bus_name = modules.runners.webhook.eventbridge.name # The name of the event bus output by the module

```
export interface GithubWorkflowEvent {
workflowJobEvent: WorkflowJobEvent;
event_pattern = <<EOF
{
"detail-type": ["workflow_job"],
"detail": {
"action": ["in_progress"]
}
}
EOF
}

resource "aws_sqs_queue" "workflow_job_in_progress" {
name = "workflow_job_in_progress
}

resource "aws_sqs_queue_policy" "workflow_job_in_progress" {
queue_url = aws_sqs_queue.workflow_job_in_progress.id
policy = data.aws_iam_policy_document.sqs_policy.json
}

data "aws_iam_policy_document" "sqs_policy" {
statement {
sid = "AllowFromEventBridge"
actions = ["sqs:SendMessage"]

principals {
type = "Service"
identifiers = ["events.amazonaws.com"]
}

resources = [aws_sqs_queue.workflow_job_in_progress.arn]

condition {
test = "ArnEquals"
variable = "aws:SourceArn"
values = [aws_cloudwatch_event_rule.workflow_job_in_progress.arn]
}
}
}

```

This extensible format allows more fields to be added if needed.
You can configure the queue by setting properties to `workflow_job_events_queue_config`


NOTE: By default, a runner AMI update requires a re-apply of this terraform config (the runner AMI ID is looked up by a terraform data source). To avoid this, you can use `ami_id_ssm_parameter_name` to have the scale-up lambda dynamically lookup the runner AMI ID from an SSM parameter at instance launch time. Said SSM parameter is managed outside of this module (e.g. by a runner AMI build workflow).
2 changes: 0 additions & 2 deletions examples/default/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,6 @@ module "runners" {

# override scaling down
scale_down_schedule_expression = "cron(* * * * ? *)"
# enable this flag to publish webhook events to workflow job queue
# enable_workflow_job_events_queue = true

enable_user_data_debug_logging_runner = true

Expand Down
3 changes: 0 additions & 3 deletions examples/multi-runner/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,6 @@ module "runners" {
# runner_binaries_syncer_lambda_zip = "../lambdas-download/runner-binaries-syncer.zip"
# runners_lambda_zip = "../lambdas-download/runners.zip"

# enable_workflow_job_events_queue = true
# override delay of events in seconds

# Enable debug logging for the lambda functions
# log_level = "debug"

Expand Down
5 changes: 2 additions & 3 deletions lambdas/functions/webhook/jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import type { Config } from 'jest';

import defaultConfig from '../../jest.base.config';

const config: Config = {
...defaultConfig,
coverageThreshold: {
global: {
statements: 99.58,
statements: 100,
branches: 100,
functions: 100,
lines: 99.57,
lines: 100,
},
},
};
Expand Down
4 changes: 1 addition & 3 deletions lambdas/functions/webhook/src/ConfigLoader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,8 @@ describe('ConfigLoader Tests', () => {
describe('ConfigWebhook', () => {
it('should load config successfully', async () => {
process.env.REPOSITORY_ALLOW_LIST = '["repo1", "repo2"]';
process.env.SQS_WORKFLOW_JOB_QUEUE = 'secondary-queue';
process.env.PARAMETER_RUNNER_MATCHER_CONFIG_PATH = '/path/to/matcher/config';
process.env.PARAMETER_GITHUB_APP_WEBHOOK_SECRET = '/path/to/webhook/secret';
process.env.PARAMETER_RUNNER_MATCHER_CONFIG_PATH = '/path/to/matcher/config';
const matcherConfig = [
{
id: '1',
Expand All @@ -121,7 +120,6 @@ describe('ConfigLoader Tests', () => {
const config: ConfigWebhook = await ConfigWebhook.load();

expect(config.repositoryAllowList).toEqual(['repo1', 'repo2']);
expect(config.workflowJobEventSecondaryQueue).toBe('secondary-queue');
expect(config.matcherConfig).toEqual(matcherConfig);
expect(config.webhookSecret).toBe('secret');
});
Expand Down
2 changes: 0 additions & 2 deletions lambdas/functions/webhook/src/ConfigLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ export class ConfigWebhook extends BaseConfig {

async loadConfig(): Promise<void> {
this.loadEnvVar(process.env.REPOSITORY_ALLOW_LIST, 'repositoryAllowList', []);
this.loadEnvVar(process.env.SQS_WORKFLOW_JOB_QUEUE, 'workflowJobEventSecondaryQueue', '');

await Promise.all([
this.loadParameter(process.env.PARAMETER_RUNNER_MATCHER_CONFIG_PATH, 'matcherConfig'),
Expand Down Expand Up @@ -129,7 +128,6 @@ export class ConfigDispatcher extends BaseConfig {

async loadConfig(): Promise<void> {
this.loadEnvVar(process.env.REPOSITORY_ALLOW_LIST, 'repositoryAllowList', []);
this.loadEnvVar(process.env.SQS_WORKFLOW_JOB_QUEUE, 'workflowJobEventSecondaryQueue', '');
await this.loadParameter(process.env.PARAMETER_RUNNER_MATCHER_CONFIG_PATH, 'matcherConfig');

validateRunnerMatcherConfig(this);
Expand Down
1 change: 0 additions & 1 deletion lambdas/functions/webhook/src/modules.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ declare namespace NodeJS {
REPOSITORY_ALLOW_LIST: string;
RUNNER_LABELS: string;
ACCEPT_EVENTS: string;
SQS_WORKFLOW_JOB_QUEUE: string;
}
}
7 changes: 0 additions & 7 deletions lambdas/functions/webhook/src/runners/dispatch.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getParameter } from '@aws-github-runner/aws-ssm-util';

Check notice on line 1 in lambdas/functions/webhook/src/runners/dispatch.test.ts

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

✅ No longer an issue: Code Duplication

The module no longer contains too many functions with similar structure
import { mocked } from 'jest-mock';
import nock from 'nock';
import { WorkflowJobEvent } from '@octokit/webhooks-types';
Expand All @@ -14,7 +14,6 @@
jest.mock('../sqs');
jest.mock('@aws-github-runner/aws-ssm-util');

const sendWebhookEventToWorkflowJobQueueMock = jest.mocked(sendActionRequest);
const GITHUB_APP_WEBHOOK_SECRET = 'TEST_SECRET';

const cleanEnv = process.env;
Expand Down Expand Up @@ -56,7 +55,6 @@
statusCode: 403,
});
expect(sendActionRequest).not.toHaveBeenCalled();
expect(sendWebhookEventToWorkflowJobQueueMock).not.toHaveBeenCalled();
});

it('should handle workflow_job events without installation id', async () => {
Expand All @@ -65,7 +63,6 @@
const resp = await dispatch(event, 'workflow_job', config);
expect(resp.statusCode).toBe(201);
expect(sendActionRequest).toHaveBeenCalled();
expect(sendWebhookEventToWorkflowJobQueueMock).toHaveBeenCalled();
});

it('should handle workflow_job events from allow listed repositories', async () => {
Expand All @@ -74,7 +71,6 @@
const resp = await dispatch(event, 'workflow_job', config);
expect(resp.statusCode).toBe(201);
expect(sendActionRequest).toHaveBeenCalled();
expect(sendWebhookEventToWorkflowJobQueueMock).toHaveBeenCalled();
});

it('should match labels', async () => {
Expand Down Expand Up @@ -108,7 +104,6 @@
queueFifo: false,
repoOwnerType: 'Organization',
});
expect(sendWebhookEventToWorkflowJobQueueMock).toHaveBeenCalled();
});

it('should sort matcher with exact first.', async () => {
Expand Down Expand Up @@ -157,7 +152,6 @@
queueFifo: false,
repoOwnerType: 'Organization',
});
expect(sendWebhookEventToWorkflowJobQueueMock).toHaveBeenCalled();
});

it('should not accept jobs where not all labels are supported (single matcher).', async () => {
Expand All @@ -181,7 +175,6 @@
const resp = await dispatch(event, 'workflow_job', config);
expect(resp.statusCode).toBe(202);
expect(sendActionRequest).not.toHaveBeenCalled();
expect(sendWebhookEventToWorkflowJobQueueMock).not.toHaveBeenCalled();
});
});

Expand Down
7 changes: 2 additions & 5 deletions lambdas/functions/webhook/src/runners/dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createChildLogger } from '@aws-github-runner/aws-powertools-util';
import { WorkflowJobEvent } from '@octokit/webhooks-types';

import { Response } from '../lambda';
import { RunnerMatcherConfig, sendActionRequest, sendWebhookEventToWorkflowJobQueue } from '../sqs';
import { RunnerMatcherConfig, sendActionRequest } from '../sqs';
import ValidationError from '../ValidationError';
import { ConfigDispatcher, ConfigWebhook } from '../ConfigLoader';

Expand All @@ -15,10 +15,7 @@ export async function dispatch(
): Promise<Response> {
validateRepoInAllowList(event, config);

const result = await handleWorkflowJob(event, eventType, config.matcherConfig!);
await sendWebhookEventToWorkflowJobQueue({ workflowJobEvent: event }, config);

return result;
return await handleWorkflowJob(event, eventType, config.matcherConfig!);
}

function validateRepoInAllowList(event: WorkflowJobEvent, config: ConfigDispatcher) {
Expand Down
68 changes: 1 addition & 67 deletions lambdas/functions/webhook/src/sqs/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { SendMessageCommandInput } from '@aws-sdk/client-sqs';

Check notice on line 1 in lambdas/functions/webhook/src/sqs/index.test.ts

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

✅ No longer an issue: Code Duplication

The module no longer contains too many functions with similar structure

import { ActionRequestMessage, GithubWorkflowEvent, sendActionRequest, sendWebhookEventToWorkflowJobQueue } from '.';
import workflowjob_event from '../../test/resources/github_workflowjob_event.json';
import { getParameter } from '@aws-github-runner/aws-ssm-util';
import { mocked } from 'jest-mock';
import { ActionRequestMessage, sendActionRequest } from '.';

const mockSQS = {
sendMessage: jest.fn(() => {
Expand All @@ -15,9 +11,6 @@
}));
jest.mock('@aws-github-runner/aws-ssm-util');

import { SQS } from '@aws-sdk/client-sqs';
import { ConfigDispatcher, ConfigWebhook } from '../ConfigLoader';

describe('Test sending message to SQS.', () => {
const queueUrl = 'https://sqs.eu-west-1.amazonaws.com/123456789/queued-builds';
const message = {
Expand Down Expand Up @@ -72,62 +65,3 @@
await expect(result).resolves.not.toThrow();
});
});

describe('Test sending message to SQS.', () => {
const message: GithubWorkflowEvent = {
workflowJobEvent: JSON.parse(JSON.stringify(workflowjob_event)),
};
const sqsMessage: SendMessageCommandInput = {
QueueUrl: 'https://sqs.eu-west-1.amazonaws.com/123456789/webhook_events_workflow_job_queue',
MessageBody: JSON.stringify(message),
};
beforeEach(() => {
ConfigDispatcher.reset();
const mockedGet = mocked(getParameter);
mockedGet.mockResolvedValue('["abc"]');
});
afterEach(() => {
jest.clearAllMocks();
});

it('sends webhook events to workflow job queue', async () => {
// Arrange
process.env.SQS_WORKFLOW_JOB_QUEUE = sqsMessage.QueueUrl || '';
const config: ConfigWebhook = await ConfigWebhook.load();

// Act
const result = sendWebhookEventToWorkflowJobQueue(message, config);

// Assert
expect(mockSQS.sendMessage).toHaveBeenCalledWith(sqsMessage);
await expect(result).resolves.not.toThrow();
});

it('Does not send webhook events to workflow job event copy queue when job queue is not in environment', async () => {
// Arrange
process.env.SQS_WORKFLOW_JOB_QUEUE = '';
const config: ConfigDispatcher = await ConfigDispatcher.load();

// Act
await sendWebhookEventToWorkflowJobQueue(message, config);

// Assert
expect(SQS).not.toHaveBeenCalled();
});

it('Catch the exception when even copy queue throws exception', async () => {
// Arrange
process.env.SQS_WORKFLOW_JOB_QUEUE = sqsMessage.QueueUrl || '';
const config: ConfigDispatcher = await ConfigDispatcher.load();

const mockSQS = {
sendMessage: jest.fn(() => {
throw new Error();
}),
};
jest.mock('aws-sdk', () => ({
SQS: jest.fn().mockImplementation(() => mockSQS),
}));
await expect(sendWebhookEventToWorkflowJobQueue(message, config)).resolves.not.toThrow();
});
});
24 changes: 0 additions & 24 deletions lambdas/functions/webhook/src/sqs/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { SQS, SendMessageCommandInput } from '@aws-sdk/client-sqs';
import { WorkflowJobEvent } from '@octokit/webhooks-types';
import { createChildLogger, getTracedAWSV3Client } from '@aws-github-runner/aws-powertools-util';
import { ConfigDispatcher } from '../ConfigLoader';

const logger = createChildLogger('sqs');

Expand Down Expand Up @@ -49,26 +48,3 @@ export const sendActionRequest = async (message: ActionRequestMessage): Promise<

await sqs.sendMessage(sqsMessage);
};

export async function sendWebhookEventToWorkflowJobQueue(
message: GithubWorkflowEvent,
config: ConfigDispatcher,
): Promise<void> {
if (!config.workflowJobEventSecondaryQueue) {
return;
}

const sqs = new SQS({ region: process.env.AWS_REGION });
const sqsMessage: SendMessageCommandInput = {
QueueUrl: String(config.workflowJobEventSecondaryQueue),
MessageBody: JSON.stringify(message),
};

logger.info(`Sending event to the workflow job queue: ${config.workflowJobEventSecondaryQueue}`);

try {
await sqs.sendMessage(sqsMessage);
} catch (e) {
logger.warn(`Error in sending webhook events to workflow job queue: ${(e as Error).message}`);
}
}
Loading