diff --git a/lambdas/functions/control-plane/src/lambda.test.ts b/lambdas/functions/control-plane/src/lambda.test.ts index f4d28ccd79..ab87a5c968 100644 --- a/lambdas/functions/control-plane/src/lambda.test.ts +++ b/lambdas/functions/control-plane/src/lambda.test.ts @@ -15,6 +15,7 @@ const body: ActionRequestMessage = { installationId: 1, repositoryName: 'name', repositoryOwner: 'owner', + repoOwnerType: 'Organization', }; const sqsRecord: SQSRecord = { diff --git a/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts b/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts index f14319a3b6..1382052baf 100644 --- a/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts +++ b/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts @@ -53,6 +53,7 @@ const TEST_DATA: scaleUpModule.ActionRequestMessage = { repositoryName: 'hello-world', repositoryOwner: 'Codertocat', installationId: 2, + repoOwnerType: 'Organization', }; // installationId 0 means no installationId is set. @@ -62,6 +63,7 @@ const TEST_DATA_WITH_ZERO_INSTALL_ID: scaleUpModule.ActionRequestMessage = { repositoryName: 'hello-world', repositoryOwner: 'Codertocat', installationId: 0, + repoOwnerType: 'Organization', }; const cleanEnv = process.env; @@ -305,6 +307,14 @@ describe('scaleUp with GHES', () => { expect(mockOctokit.paginate).toHaveBeenCalledTimes(1); }); + it('Discards event if it is a User repo and org level runners is enabled', async () => { + process.env.ENABLE_ORGANIZATION_RUNNERS = 'true'; + const USER_REPO_TEST_DATA = { ...TEST_DATA }; + USER_REPO_TEST_DATA.repoOwnerType = 'User'; + await scaleUpModule.scaleUp('aws:sqs', USER_REPO_TEST_DATA); + expect(createRunner).not.toHaveBeenCalled(); + }); + it('create SSM parameter for runner group id if it doesnt exist', async () => { mockSSMClient.on(GetParameterCommand).rejects(); await scaleUpModule.scaleUp('aws:sqs', TEST_DATA); diff --git a/lambdas/functions/control-plane/src/scale-runners/scale-up.ts b/lambdas/functions/control-plane/src/scale-runners/scale-up.ts index c4a841ab28..26cb538445 100644 --- a/lambdas/functions/control-plane/src/scale-runners/scale-up.ts +++ b/lambdas/functions/control-plane/src/scale-runners/scale-up.ts @@ -27,6 +27,7 @@ export interface ActionRequestMessage { repositoryName: string; repositoryOwner: string; installationId: number; + repoOwnerType: string; } interface CreateGitHubRunnerConfig { @@ -250,6 +251,16 @@ export async function scaleUp(eventSource: string, payload: ActionRequestMessage `Please ensure you have enabled workflow_job events.`, ); } + + if (!isValidRepoOwnerTypeIfOrgLevelEnabled(payload, enableOrgLevel)) { + logger.warn( + `Repository ${payload.repositoryOwner}/${payload.repositoryName} does not belong to a GitHub` + + `organization and organization runners are enabled. This is not supported. Not scaling up for this event.` + + `Not throwing error to prevent re-queueing and just ignoring the event.`, + ); + return; + } + const ephemeral = ephemeralEnabled && payload.eventType === 'workflow_job'; const runnerType = enableOrgLevel ? 'Org' : 'Repo'; const runnerOwner = enableOrgLevel ? payload.repositoryOwner : `${payload.repositoryOwner}/${payload.repositoryName}`; @@ -341,6 +352,10 @@ async function createStartRunnerConfig( } } +function isValidRepoOwnerTypeIfOrgLevelEnabled(payload: ActionRequestMessage, enableOrgLevel: boolean): boolean { + return !(enableOrgLevel && payload.repoOwnerType !== 'Organization'); +} + function addDelay(instances: string[]) { const delay = async (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const ssmParameterStoreMaxThroughput = 40; diff --git a/lambdas/functions/webhook/src/sqs/index.test.ts b/lambdas/functions/webhook/src/sqs/index.test.ts index c5c0839b36..0187ac433d 100644 --- a/lambdas/functions/webhook/src/sqs/index.test.ts +++ b/lambdas/functions/webhook/src/sqs/index.test.ts @@ -28,6 +28,7 @@ describe('Test sending message to SQS.', () => { repositoryOwner: 'owner', queueId: queueUrl, queueFifo: false, + repoOwnerType: 'Organization', }; afterEach(() => { diff --git a/lambdas/functions/webhook/src/sqs/index.ts b/lambdas/functions/webhook/src/sqs/index.ts index 03539ddbf6..d746bb40ff 100644 --- a/lambdas/functions/webhook/src/sqs/index.ts +++ b/lambdas/functions/webhook/src/sqs/index.ts @@ -13,6 +13,7 @@ export interface ActionRequestMessage { installationId: number; queueId: string; queueFifo: boolean; + repoOwnerType: string; } export interface MatcherConfig { diff --git a/lambdas/functions/webhook/src/webhook/index.test.ts b/lambdas/functions/webhook/src/webhook/index.test.ts index cacbde23f5..85e73e3e9b 100644 --- a/lambdas/functions/webhook/src/webhook/index.test.ts +++ b/lambdas/functions/webhook/src/webhook/index.test.ts @@ -450,6 +450,7 @@ describe('handler', () => { installationId: 0, queueId: 'ubuntu-queue-id', queueFifo: false, + repoOwnerType: 'Organization', }); }); it('Check webhook will accept jobs for latest labels if workflow labels are not specific', async () => { @@ -492,6 +493,7 @@ describe('handler', () => { installationId: 0, queueId: 'ubuntu-queue-id', queueFifo: false, + repoOwnerType: 'Organization', }); }); }); @@ -531,6 +533,7 @@ describe('handler', () => { installationId: 0, queueId: 'ubuntu-queue-id', queueFifo: false, + repoOwnerType: 'Organization', }); }); diff --git a/lambdas/functions/webhook/src/webhook/index.ts b/lambdas/functions/webhook/src/webhook/index.ts index 92daf8fea2..f9406ab7b8 100644 --- a/lambdas/functions/webhook/src/webhook/index.ts +++ b/lambdas/functions/webhook/src/webhook/index.ts @@ -53,6 +53,7 @@ async function handleWorkflowJob( installationId: installationId, queueId: queue.id, queueFifo: queue.fifo, + repoOwnerType: body.repository.owner.type, }); logger.info(`Successfully queued job for ${body.repository.full_name} to the queue ${queue.id}`); return { statusCode: 201 };