Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,6 @@ export async function scaleDown(): Promise<void> {
...controlPlaneProviderRegistry.capability(computeProviderType, 'scaleDown')(),
type: computeProviderType,
};

// first runners marked to be orphan.
await terminateOrphan(environment, computeProvider);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise<stri
...controlPlaneProviderRegistry.capability(computeProviderType, 'scaleUp')(),
type: computeProviderType,
};

const { ghesApiUrl, ghesBaseUrl } = getGitHubEnterpriseApiUrl();

// Select one GitHub App for this entire invocation so every API call in the
Expand Down
9 changes: 5 additions & 4 deletions lambdas/libs/compute-providers/aws/ec2/control-plane.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { CreateStartRunnerConfig, ComputeProviderPlugin } from '../../core';
import { getTracedAWSV3Client } from '@aws-github-runner/aws-powertools-util';
import { EC2Client } from '@aws-sdk/client-ec2';
import { computeProvider } from '../../provider-types';

import type { ControlPlaneProviderCapabilities, ControlPlaneProviderModule } from '../../contracts';
import type {} from './src/environment';
Expand All @@ -11,12 +12,12 @@ import { createEc2RunnerClient } from './src/runners';

export function createEc2ControlPlanePlugin(
createStartRunnerConfig: CreateStartRunnerConfig,
): ComputeProviderPlugin<ControlPlaneProviderCapabilities, 'ec2'> {
): ComputeProviderPlugin<ControlPlaneProviderCapabilities, typeof computeProvider.ec2> {
const ec2Client = getTracedAWSV3Client(new EC2Client({ region: process.env.AWS_REGION }));
const ec2Operations = createEc2RunnerClient(ec2Client).forRequest({ signal: undefined });

return {
type: 'ec2',
type: computeProvider.ec2,
capabilities: {
pool: () => createEc2PoolCapability(ec2Operations, createStartRunnerConfig),
scaleUp: () => createEc2ScaleUpCapability(ec2Operations, createStartRunnerConfig),
Expand All @@ -26,6 +27,6 @@ export function createEc2ControlPlanePlugin(
}

export const provider = {
type: 'ec2',
type: computeProvider.ec2,
createPlugin: createEc2ControlPlanePlugin,
} satisfies ControlPlaneProviderModule<'ec2'>;
} satisfies ControlPlaneProviderModule<typeof computeProvider.ec2>;
23 changes: 23 additions & 0 deletions lambdas/libs/compute-providers/aws/ec2/logger.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { describe, expect, it, vi } from 'vitest';

import { computeProvider } from '../../provider-types';
import { createEc2ComputeProviderLogger } from './logger';

const loggerMock = vi.hoisted(() => ({
appendPersistentKeys: vi.fn(),
}));
const createChildLoggerMock = vi.hoisted(() => vi.fn(() => loggerMock));

vi.mock('@aws-github-runner/aws-powertools-util', () => ({
createChildLogger: createChildLoggerMock,
}));

describe('EC2 compute provider logger', () => {
it('adds the canonical compute provider while preserving the module name', () => {
expect(createEc2ComputeProviderLogger('runners')).toBe(loggerMock);
expect(createChildLoggerMock).toHaveBeenCalledWith('runners');
expect(loggerMock.appendPersistentKeys).toHaveBeenCalledWith({
computeProvider: computeProvider.ec2,
});
});
});
11 changes: 11 additions & 0 deletions lambdas/libs/compute-providers/aws/ec2/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createChildLogger } from '@aws-github-runner/aws-powertools-util';

import { computeProvider } from '../../provider-types';

export function createEc2ComputeProviderLogger(module: string) {
const logger = createChildLogger(module);
logger.appendPersistentKeys({
computeProvider: computeProvider.ec2,
});
return logger;
}
3 changes: 3 additions & 0 deletions lambdas/libs/compute-providers/aws/ec2/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { computeProvider } from '../../../provider-types';

export const EC2_OVERRIDE_LABEL_PREFIX = `ghr-${computeProvider.ec2}-`;
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
} from '@aws-sdk/client-ec2';

import { Ec2OverrideConfig } from '../runners.d';
import { EC2_OVERRIDE_LABEL_PREFIX } from '../constants';

const EC2_OVERRIDE_LIST_VALUE_SEPARATOR = ';';

Expand Down Expand Up @@ -129,11 +130,11 @@ export function parseEc2OverrideConfig(
labels: string[],
defaultBlockDeviceName?: string,
): Ec2OverrideConfig | undefined {
const ec2Labels = labels.filter((l) => l.startsWith('ghr-ec2-'));
const ec2Labels = labels.filter((l) => l.startsWith(EC2_OVERRIDE_LABEL_PREFIX));
const config: Ec2OverrideConfig = {};

for (const label of ec2Labels) {
const [key, ...valueParts] = label.replace('ghr-ec2-', '').split(':');
const [key, ...valueParts] = label.replace(EC2_OVERRIDE_LABEL_PREFIX, '').split(':');
const value = valueParts.join(':');

if (!value) continue;
Expand Down Expand Up @@ -348,13 +349,15 @@ function getOrCreateBlockDeviceMapping(
}

export function shouldLoadLaunchTemplateBlockDeviceName(labels: string[]): boolean {
const blockDeviceNameLabel = 'ghr-ec2-block-device-name:';
const blockDeviceNameLabel = `${EC2_OVERRIDE_LABEL_PREFIX}block-device-name:`;
let hasBlockDeviceOverride = false;
let hasBlockDeviceName = false;

for (const label of labels) {
hasBlockDeviceOverride =
hasBlockDeviceOverride || label.startsWith('ghr-ec2-ebs-') || label.startsWith('ghr-ec2-block-device-');
hasBlockDeviceOverride ||
label.startsWith(`${EC2_OVERRIDE_LABEL_PREFIX}ebs-`) ||
label.startsWith(`${EC2_OVERRIDE_LABEL_PREFIX}block-device-`);

hasBlockDeviceName =
hasBlockDeviceName || (label.startsWith(blockDeviceNameLabel) && label.slice(blockDeviceNameLabel.length) !== '');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { createChildLogger } from '@aws-github-runner/aws-powertools-util';
import { createEc2ComputeProviderLogger } from '../../logger';
import type { CreateStartRunnerConfig, PoolComputeProvider, RunnerInfo, RunnerStatus } from '../../../../core';
import { bootTimeExceeded, type Ec2RunnerResourceOperations } from '../runners';
import { createRunners, loadEc2ProviderConfig } from './runner-creation';

const logger = createChildLogger('pool');
const logger = createEc2ComputeProviderLogger('pool');

function countAvailableEc2PoolRunners(
ec2runners: RunnerInfo[],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createChildLogger } from '@aws-github-runner/aws-powertools-util';
import { createEc2ComputeProviderLogger } from '../../logger';
import type {
CreateGitHubRunnerConfig,
CreateRunnerResult,
Expand All @@ -15,7 +15,7 @@ import type { Ec2RunnerResourceOperations } from '../runners';
import type { RunnerInputParameters } from '../runners.d';
import { toControlPlaneCreateRunnerResult } from './create-result';

const logger = createChildLogger('ec2-runners');
const logger = createEc2ComputeProviderLogger('ec2-runners');
const RUNNER_LABELS_TAG_KEY = 'ghr:runner_labels';
const RUNNER_LABELS_TAG_VALUE_SEPARATOR = ',';
export const EC2_TAG_VALUE_MAX_LENGTH = 256;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { createChildLogger } from '@aws-github-runner/aws-powertools-util';
import { createEc2ComputeProviderLogger } from '../../logger';
import type { CreateStartRunnerConfig, RunnerLabelResolution, ScaleUpComputeProvider } from '../../../../core';
import yn from 'yn';

import type { Ec2RunnerProvisioningOperations } from '../runners';
import type { Ec2OverrideConfig } from '../runners.d';
import { EC2_OVERRIDE_LABEL_PREFIX } from '../constants';
import { parseEc2OverrideConfig, shouldLoadLaunchTemplateBlockDeviceName } from './dynamic-labels';
import { createRunners, loadEc2ProviderConfig } from './runner-creation';
import type { CreateEC2RunnerConfig } from './runner-creation';

const logger = createChildLogger('ec2-scale-up');
const logger = createEc2ComputeProviderLogger('ec2-scale-up');

interface Ec2ScaleUpState {
ec2OverrideConfig?: Ec2OverrideConfig;
Expand All @@ -26,9 +27,9 @@ async function resolveEc2ScaleUpRunnerLabels(
messageLabels: string[],
): Promise<RunnerLabelResolution<Ec2ScaleUpState>> {
const trimmedLabels = messageLabels.map((label) => label.trim());
const dynamicEC2Labels = trimmedLabels.filter((label) => label.startsWith('ghr-ec2-'));
const dynamicEC2Labels = trimmedLabels.filter((label) => label.startsWith(EC2_OVERRIDE_LABEL_PREFIX));
const nonEc2DynamicLabels = trimmedLabels.filter(
(label) => label.startsWith('ghr-') && !label.startsWith('ghr-ec2-'),
(label) => label.startsWith('ghr-') && !label.startsWith(EC2_OVERRIDE_LABEL_PREFIX),
);
const runnerLabels = [...nonEc2DynamicLabels, ...dynamicEC2Labels];
let ec2OverrideConfig: Ec2OverrideConfig | undefined;
Expand Down
5 changes: 3 additions & 2 deletions lambdas/libs/compute-providers/aws/ec2/src/runners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ import {
TerminateInstancesCommand,
_InstanceType,
} from '@aws-sdk/client-ec2';
import { createChildLogger, tracer } from '@aws-github-runner/aws-powertools-util';
import { tracer } from '@aws-github-runner/aws-powertools-util';
import { getParameter } from '@aws-github-runner/aws-ssm-util';
import moment from 'moment';

import type { RunnerInfo } from '../../../core';
import { getDefaultBlockDeviceNameFromLaunchTemplate } from './launch-template';
import type { Ec2RunnerCreateResult, Ec2RunnerFailureCode } from './runner-create-result';
import type { Ec2ListRunnerFilters, Ec2OverrideConfig, RunnerInputParameters } from './runners.d';
import { createEc2ComputeProviderLogger } from '../logger';

const logger = createChildLogger('runners');
const logger = createEc2ComputeProviderLogger('runners');

interface Ec2Filter {
Name: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { AwsDynamicLabelsPolicy, AwsDynamicLabelsValueRule } from '../../../../contracts';
import { violationsAgainstAwsDynamicLabelsPolicy } from '../../../dynamic-labels-policy';
import { EC2_OVERRIDE_LABEL_PREFIX } from '../constants';

export type Ec2DynamicLabelsValueRule = AwsDynamicLabelsValueRule;

Expand All @@ -19,5 +20,5 @@ export function violationsAgainstPolicy(
labels: string[],
policy: Ec2DynamicLabelsPolicy | null | undefined,
): { label: string; reason: string }[] {
return violationsAgainstAwsDynamicLabelsPolicy(labels, policy, 'ghr-ec2-');
return violationsAgainstAwsDynamicLabelsPolicy(labels, policy, EC2_OVERRIDE_LABEL_PREFIX);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { createChildLogger } from '@aws-github-runner/aws-powertools-util';
import { createEc2ComputeProviderLogger } from '../../logger';

import type { DynamicLabelProvider, RunnerMatcherConfig } from '../../../../contracts';
import { violationsAgainstPolicy } from './dynamic-labels-policy';

const logger = createChildLogger('handler');
const logger = createEc2ComputeProviderLogger('handler');

function resolveEc2DynamicLabelsPolicy(queue: RunnerMatcherConfig) {
const hasLegacyEc2DynamicLabelsPolicy = Object.prototype.hasOwnProperty.call(
Expand Down
12 changes: 8 additions & 4 deletions lambdas/libs/compute-providers/aws/ec2/webhook.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import type { ComputeProviderPlugin } from '../../core';
import { computeProvider } from '../../provider-types';

import type { WebhookProviderCapabilities, WebhookProviderModule } from '../../contracts';
import { ec2DynamicLabelProvider } from './src/webhook/dynamic-labels';

export function createEc2WebhookPlugin(): ComputeProviderPlugin<WebhookProviderCapabilities, 'ec2'> {
export function createEc2WebhookPlugin(): ComputeProviderPlugin<
WebhookProviderCapabilities,
typeof computeProvider.ec2
> {
return {
type: 'ec2',
type: computeProvider.ec2,
capabilities: { dynamicLabels: ec2DynamicLabelProvider },
};
}

export const provider = {
type: 'ec2',
type: computeProvider.ec2,
createPlugin: createEc2WebhookPlugin,
} satisfies WebhookProviderModule<'ec2'>;
} satisfies WebhookProviderModule<typeof computeProvider.ec2>;
7 changes: 4 additions & 3 deletions lambdas/libs/compute-providers/core/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { describe, expect, it } from 'vitest';

import { computeProvider } from '../provider-types';
import { createComputeProviderRegistry } from './index';

describe('compute provider registry', () => {
const plugin = {
type: 'ec2' as const,
type: computeProvider.ec2,
capabilities: {
scaleUp: () => 'scale-up',
pool: () => 'pool',
Expand All @@ -13,7 +14,7 @@ describe('compute provider registry', () => {
const registry = createComputeProviderRegistry([plugin]);

it('resolves capabilities dynamically', () => {
expect(registry.capability('ec2', 'scaleUp')()).toBe('scale-up');
expect(registry.capability('ec2', 'pool')()).toBe('pool');
expect(registry.capability(computeProvider.ec2, 'scaleUp')()).toBe('scale-up');
expect(registry.capability(computeProvider.ec2, 'pool')()).toBe('pool');
});
});
8 changes: 7 additions & 1 deletion lambdas/libs/compute-providers/provider-types.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { describe, expect, it } from 'vitest';

import { computeProviderTypes, defaultComputeProvider, resolveComputeProviderType } from './provider-types';
import {
computeProvider,
computeProviderTypes,
defaultComputeProvider,
resolveComputeProviderType,
} from './provider-types';

const defaultProviderInputs = [undefined, '', ' '] as const;
const supportedProviderCases = computeProviderTypes.flatMap(
Expand All @@ -13,6 +18,7 @@ const supportedProviderCases = computeProviderTypes.flatMap(

describe('compute provider configuration', () => {
it('defines an explicit default provider', () => {
expect(computeProvider.ec2).toBe('ec2');
expect(computeProviderTypes).toContain(defaultComputeProvider);
});
});
Expand Down
8 changes: 6 additions & 2 deletions lambdas/libs/compute-providers/provider-types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
export const computeProviderTypes = ['ec2'] as const;
export const computeProvider = {
ec2: 'ec2',
} as const;

export const computeProviderTypes = [computeProvider.ec2] as const;

export type ComputeProviderType = (typeof computeProviderTypes)[number];

export const defaultComputeProvider = 'ec2' satisfies ComputeProviderType;
export const defaultComputeProvider = computeProvider.ec2 satisfies ComputeProviderType;

export function resolveComputeProviderType(type: unknown): ComputeProviderType {
if (type === undefined) return defaultComputeProvider;
Expand Down
1 change: 0 additions & 1 deletion lambdas/libs/compute-providers/webhook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export function createDynamicLabelQueueSelector<TProvider extends string>(depend
): DynamicLabelDispatchTarget | undefined => {
for (const queue of matches) {
const { type: provider, dynamicLabels } = dependencies.resolveProvider(queue);

if (!queue.matcherConfig.enableDynamicLabels) {
logger.warn(
`Queue ${queue.id} matches non-dynamic labels but does not allow dynamic labels; trying next match`,
Expand Down
Loading