Skip to content
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

feat: PR to Support GitHub Enterprise Cloud with Data Residency #4367

Merged
merged 11 commits into from
Jan 28, 2025
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ Join our discord community via [this invite link](https://discord.gg/bxgXW8jJGh)
| <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_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, true)<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 |
| <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 - github.com. However if you are using Github Eneterprise cloud with data-residency (ghe.com), set the endpoint here. Example - https://comaonyname.ghe.com | `string` | `null` | no |
neethu-p marked this conversation as resolved.
Show resolved Hide resolved
| <a name="input_github_app"></a> [github\_app](#input\_github\_app) | GitHub app parameters, see your github app. Ensure the key is the base64-encoded `.pem` file (the output of `base64 app.private-key.pem`, not the content of `private-key.pem`). | <pre>object({<br/> key_base64 = string<br/> id = string<br/> webhook_secret = string<br/> })</pre> | n/a | yes |
| <a name="input_idle_config"></a> [idle\_config](#input\_idle\_config) | List of time periods, defined as a cron expression, to keep a minimum amount of runners active instead of scaling down to 0. By defining this list you can ensure that in time periods that match the cron expression within 5 seconds a runner is kept idle. | <pre>list(object({<br/> cron = string<br/> timeZone = string<br/> idleCount = number<br/> evictionStrategy = optional(string, "oldest_first")<br/> }))</pre> | `[]` | no |
| <a name="input_instance_allocation_strategy"></a> [instance\_allocation\_strategy](#input\_instance\_allocation\_strategy) | The allocation strategy for spot instances. AWS recommends using `price-capacity-optimized` however the AWS default is `lowest-price`. | `string` | `"lowest-price"` | no |
Expand Down
16 changes: 16 additions & 0 deletions lambdas/functions/control-plane/src/pool/pool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,22 @@ describe('Test simple pool.', () => {
});
});

describe('With Github Data Residency', () => {
beforeEach(() => {
process.env.GHES_URL = 'https://companyname.ghe.com';
});

it('Top up if the pool size is set to 5', async () => {
await expect(await adjust({ poolSize: 5 })).resolves;
// 2 idle, top up with 3 to match a pool of 5
expect(createRunners).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({ numberOfRunners: 3 }),
expect.anything(),
);
});
});

describe('With Runner Name Prefix', () => {
beforeEach(() => {
process.env.RUNNER_NAME_PREFIX = 'runner-prefix_';
Expand Down
8 changes: 2 additions & 6 deletions lambdas/functions/control-plane/src/pool/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import yn from 'yn';
import { bootTimeExceeded, listEC2Runners } from '../aws/runners';
import { RunnerList } from '../aws/runners.d';
import { createGithubAppAuth, createGithubInstallationAuth, createOctokitClient } from '../github/auth';
import { createRunners } from '../scale-runners/scale-up';
import { createRunners, getGitHubEnterpriseApiUrl } from '../scale-runners/scale-up';

const logger = createChildLogger('pool');

Expand All @@ -24,7 +24,6 @@ export async function adjust(event: PoolEvent): Promise<void> {
const runnerGroup = process.env.RUNNER_GROUP_NAME || '';
const runnerNamePrefix = process.env.RUNNER_NAME_PREFIX || '';
const environment = process.env.ENVIRONMENT;
const ghesBaseUrl = process.env.GHES_URL;
const ssmTokenPath = process.env.SSM_TOKEN_PATH;
const ssmConfigPath = process.env.SSM_CONFIG_PATH || '';
const subnets = process.env.SUBNET_IDS.split(',');
Expand All @@ -43,10 +42,7 @@ export async function adjust(event: PoolEvent): Promise<void> {
? (JSON.parse(process.env.ENABLE_ON_DEMAND_FAILOVER_FOR_ERRORS) as [string])
: [];

let ghesApiUrl = '';
if (ghesBaseUrl) {
ghesApiUrl = `${ghesBaseUrl}/api/v3`;
}
const { ghesApiUrl, ghesBaseUrl } = getGitHubEnterpriseApiUrl();

const installationId = await getInstallationId(ghesApiUrl, runnerOwner);
const ghAuth = await createGithubInstallationAuth(installationId, ghesApiUrl);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { addPersistentContextToChildLogger, createSingleMetric, logger } from '@aws-github-runner/aws-powertools-util';
import { publishMessage } from '../aws/sqs';
import { ActionRequestMessage, ActionRequestMessageRetry, getGitHubEnterpriseApiUrl, isJobQueued } from './scale-up';
import { ActionRequestMessage, ActionRequestMessageRetry, isJobQueued ,getGitHubEnterpriseApiUrl} from './scale-up';
import { getOctokit } from '../github/octokit';
import { MetricUnit } from '@aws-lambda-powertools/metrics';
import yn from 'yn';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,11 @@ describe('Scale down runners', () => {
mockCreateClient.mockResolvedValue(new mocktokit());
});

const endpoints = ['https://api.github.com', 'https://github.enterprise.something'];
const endpoints = ['https://api.github.com', 'https://github.enterprise.something', 'https://companyname.ghe.com'];

describe.each(endpoints)('for %s', (endpoint) => {
beforeEach(() => {
if (endpoint.includes('enterprise')) {
if (endpoint.includes('enterprise') || endpoint.includes('ghe.com')) {
neethu-p marked this conversation as resolved.
Show resolved Hide resolved
process.env.GHES_URL = endpoint;
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { RunnerInfo, RunnerList } from './../aws/runners.d';
import { GhRunners, githubCache } from './cache';
import { ScalingDownConfig, getEvictionStrategy, getIdleRunnerCount } from './scale-down-config';
import { metricGitHubAppRateLimit } from '../github/rate-limit';
import {getGitHubEnterpriseApiUrl} from './scale-up';

const logger = createChildLogger('scale-down');

Expand All @@ -21,11 +22,7 @@ async function getOrCreateOctokit(runner: RunnerInfo): Promise<Octokit> {
}

logger.debug(`[createGitHubClientForRunner] Cache miss for ${key}`);
const ghesBaseUrl = process.env.GHES_URL;
let ghesApiUrl = '';
if (ghesBaseUrl) {
ghesApiUrl = `${ghesBaseUrl}/api/v3`;
}
const { ghesApiUrl} = getGitHubEnterpriseApiUrl();
const ghAuthPre = await createGithubAppAuth(undefined, ghesApiUrl);
const githubClientPre = await createOctokitClient(ghAuthPre.token, ghesApiUrl);

Expand Down
Loading
Loading