Skip to content
Open
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
4 changes: 2 additions & 2 deletions lambdas/functions/control-plane/src/pool/pool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ describe('Test simple pool.', () => {
});

it('Rejects unsupported pool provider types.', async () => {
await expect(adjust({ poolSize: 10, type: 'microvm' })).rejects.toThrow(
"Unsupported compute provider type 'microvm'",
await expect(adjust({ poolSize: 10, type: 'unsupported-provider' })).rejects.toThrow(
"Unsupported compute provider type 'unsupported-provider'",
);
expect(mockListRunners).not.toHaveBeenCalled();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2157,9 +2157,11 @@ describe('compute provider selection', () => {
});

it('rejects unsupported scale-up provider types', async () => {
process.env.COMPUTE_PROVIDER_TYPE = 'microvm';
process.env.COMPUTE_PROVIDER_TYPE = 'unsupported-provider';

await expect(scaleUpModule.scaleUp(TEST_DATA)).rejects.toThrow("Unsupported compute provider type 'microvm'");
await expect(scaleUpModule.scaleUp(TEST_DATA)).rejects.toThrow(
"Unsupported compute provider type 'unsupported-provider'",
);
expect(mockedAppAuth).not.toHaveBeenCalled();
});
});
Expand Down

This file was deleted.

This file was deleted.

29 changes: 0 additions & 29 deletions lambdas/functions/webhook/src/runners/aws-dynamic-labels.ts

This file was deleted.

113 changes: 34 additions & 79 deletions lambdas/functions/webhook/src/runners/dispatch.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getParameter } from '@aws-github-runner/aws-ssm-util';
import { selectDynamicLabelQueue } from '@aws-github-runner/compute-providers/webhook';

import nock from 'nock';
import { WorkflowJobEvent } from '@octokit/webhooks-types';
Expand All @@ -14,6 +15,9 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';

vi.mock('../sqs');
vi.mock('@aws-github-runner/aws-ssm-util');
vi.mock('@aws-github-runner/compute-providers/webhook', () => ({
selectDynamicLabelQueue: vi.fn(),
}));

const GITHUB_APP_WEBHOOK_SECRET = 'TEST_SECRET';

Expand Down Expand Up @@ -246,7 +250,14 @@ describe('Dispatcher', () => {
describe('per-matcher dynamic labels handling', () => {
const baseRunner = runnerConfig[0];

it('strips invalid ghr- labels (too long, bad chars) before policy and dispatch', async () => {
beforeEach(() => {
vi.mocked(selectDynamicLabelQueue).mockImplementation((matches, nonGhrLabels, sanitizedGhrLabels) => ({
queue: matches[0],
labels: [...nonGhrLabels, ...sanitizedGhrLabels],
}));
});

it('strips invalid ghr- labels before provider selection and dispatch', async () => {
const longLabel = 'ghr-' + 'a'.repeat(125); // 129 chars
config = await createConfig(undefined, [
{
Expand Down Expand Up @@ -276,140 +287,84 @@ describe('Dispatcher', () => {
} as unknown as WorkflowJobEvent;
const resp = await dispatch(event, 'workflow_job', config);
expect(resp.statusCode).toBe(201);
expect(selectDynamicLabelQueue).toHaveBeenCalledWith(
[expect.objectContaining({ id: baseRunner.id })],
['self-hosted', 'linux'],
['ghr-valid:value', 'ghr-list:value;another'],
);
expect(sendActionRequest).toHaveBeenCalledWith(
expect.objectContaining({ labels: ['self-hosted', 'linux', 'ghr-valid:value', 'ghr-list:value;another'] }),
);
});

it('rejects the job (202) when the only matching runner has enableDynamicLabels=false', async () => {
it('rejects the job when no provider accepts the dynamic labels', async () => {
vi.mocked(selectDynamicLabelQueue).mockReturnValue(undefined);
config = await createConfig(undefined, [
{
...baseRunner,
matcherConfig: {
labelMatchers: [['self-hosted', 'linux']],
exactMatch: true,
enableDynamicLabels: false,
enableDynamicLabels: true,
},
},
]);
const event = {
...workFlowJobEvent,
workflow_job: {
...workFlowJobEvent.workflow_job,
labels: ['self-hosted', 'linux', 'ghr-ec2-instance-type:t3.large'],
labels: ['self-hosted', 'linux', 'ghr-provider-setting:value'],
},
} as unknown as WorkflowJobEvent;
const resp = await dispatch(event, 'workflow_job', config);
expect(resp.statusCode).toBe(202);
expect(sendActionRequest).not.toHaveBeenCalled();
});

it('keeps dynamic labels when the matched runner enables them and has no policy', async () => {
it('dispatches to the queue and labels returned by the provider selector', async () => {
config = await createConfig(undefined, [
{
...baseRunner,
id: 'first',
matcherConfig: {
labelMatchers: [['self-hosted', 'linux']],
exactMatch: true,
enableDynamicLabels: true,
},
},
]);
const event = {
...workFlowJobEvent,
workflow_job: {
...workFlowJobEvent.workflow_job,
labels: ['self-hosted', 'linux', 'ghr-ec2-instance-type:t3.large'],
},
} as unknown as WorkflowJobEvent;
const resp = await dispatch(event, 'workflow_job', config);
expect(resp.statusCode).toBe(201);
expect(sendActionRequest).toHaveBeenCalledWith(
expect.objectContaining({ labels: ['self-hosted', 'linux', 'ghr-ec2-instance-type:t3.large'] }),
);
});

it('skips a matching runner whose policy rejects the dynamic labels and uses the next compliant one', async () => {
config = await createConfig(undefined, [
{
...baseRunner,
id: 'strict',
matcherConfig: {
labelMatchers: [['self-hosted', 'linux']],
exactMatch: true,
enableDynamicLabels: true,
awsDynamicLabelsPolicy: {
restricted_keys: {
'instance-type': { allowed: ['m5.*'] },
},
},
},
},
{
...baseRunner,
id: 'permissive',
id: 'selected',
matcherConfig: {
labelMatchers: [['self-hosted', 'linux']],
exactMatch: true,
enableDynamicLabels: true,
},
},
]);

vi.mocked(selectDynamicLabelQueue).mockImplementation((matches) => ({
queue: matches[1],
labels: ['self-hosted', 'linux', 'ghr-provider-setting:normalized'],
}));

const event = {
...workFlowJobEvent,
workflow_job: {
...workFlowJobEvent.workflow_job,
labels: ['self-hosted', 'linux', 'ghr-ec2-instance-type:t3.large'],
labels: ['self-hosted', 'linux', 'ghr-provider-setting:requested'],
},
} as unknown as WorkflowJobEvent;
const resp = await dispatch(event, 'workflow_job', config);
expect(resp.statusCode).toBe(201);
expect(sendActionRequest).toHaveBeenCalledWith(
expect.objectContaining({
queueId: 'permissive',
labels: ['self-hosted', 'linux', 'ghr-ec2-instance-type:t3.large'],
queueId: 'selected',
labels: ['self-hosted', 'linux', 'ghr-provider-setting:normalized'],
}),
);
});

it('rejects the job (202) when no runner accepts the policy', async () => {
config = await createConfig(undefined, [
{
...baseRunner,
id: 'first',
matcherConfig: {
labelMatchers: [['self-hosted', 'linux']],
exactMatch: true,
enableDynamicLabels: true,
awsDynamicLabelsPolicy: {
restricted_keys: {
'instance-type': { allowed: ['m5.*'] },
},
},
},
},
{
...baseRunner,
id: 'second',
matcherConfig: {
labelMatchers: [['self-hosted', 'linux']],
exactMatch: true,
enableDynamicLabels: false,
},
},
]);
const event = {
...workFlowJobEvent,
workflow_job: {
...workFlowJobEvent.workflow_job,
labels: ['self-hosted', 'linux', 'ghr-ec2-instance-type:t3.large'],
},
} as unknown as WorkflowJobEvent;
const resp = await dispatch(event, 'workflow_job', config);
expect(resp.statusCode).toBe(202);
expect(sendActionRequest).not.toHaveBeenCalled();
});

it('forwards non-dynamic jobs as-is to the first match', async () => {
config = await createConfig(undefined, [
{
Expand All @@ -419,7 +374,6 @@ describe('Dispatcher', () => {
labelMatchers: [['self-hosted', 'linux']],
exactMatch: true,
enableDynamicLabels: true,
awsDynamicLabelsPolicy: {},
},
},
]);
Expand All @@ -435,6 +389,7 @@ describe('Dispatcher', () => {
expect(sendActionRequest).toHaveBeenCalledWith(
expect.objectContaining({ queueId: 'first', labels: ['self-hosted', 'linux'] }),
);
expect(selectDynamicLabelQueue).not.toHaveBeenCalled();
});
});
});
Expand Down
4 changes: 2 additions & 2 deletions lambdas/functions/webhook/src/runners/dispatch.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { createChildLogger } from '@aws-github-runner/aws-powertools-util';
import { selectDynamicLabelQueue } from '@aws-github-runner/compute-providers/webhook';
import { WorkflowJobEvent } from '@octokit/webhooks-types';

import { Response } from '../lambda';
import { RunnerMatcherConfig, sendActionRequest } from '../sqs';
import ValidationError from '../ValidationError';
import { ConfigDispatcher, ConfigWebhook, QueueSelectionStrategy } from '../ConfigLoader';
import { selectAwsDynamicLabelQueue } from './aws-dynamic-labels';
import { canRunJob, splitWorkflowJobLabels } from './labels';

const logger = createChildLogger('handler');
Expand Down Expand Up @@ -84,7 +84,7 @@ async function handleWorkflowJob(
// Dynamic labels present: prefer the first provider-compliant queue. The
// queue selection strategy applies to standard jobs only; dynamic-label jobs
// always use the first compliant queue.
const dynamicTarget = selectAwsDynamicLabelQueue(matches, nonGhrLabels, sanitizedGhrLabels);
const dynamicTarget = selectDynamicLabelQueue(matches, nonGhrLabels, sanitizedGhrLabels);

if (dynamicTarget) {
targets = [dynamicTarget.queue];
Expand Down
Loading