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
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,15 @@ updates:
- "python"
commit-message:
prefix: "chore(docs)"

- package-ecosystem: "docker-compose"
directory: "/tests/multi-runner-orchestration"
schedule:
interval: "weekly"
cooldown:
default-days: 7
labels:
- "dependencies"
- "docker-compose"
commit-message:
prefix: "chore(docker-compose)"
2 changes: 0 additions & 2 deletions .github/workflows/ministack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ jobs:
- default
- ephemeral
- multi-runner
- multi-runner-v2
- multi-runner-scale-set
- migration-test
- termination-watcher
services:
Expand Down
154 changes: 17 additions & 137 deletions .github/workflows/smoke-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ name: "Smoke Tests"

on:
pull_request:
paths:
- lambdas/**
paths:
- "**/*.tf"
- "**/*.hcl"
- "lambdas/**"
- "tests/ministack/**"
- "tests/multi-runner-orchestration/**"
- ".github/workflows/smoke-tests.yml"
workflow_dispatch:

Expand All @@ -16,33 +18,14 @@ concurrency:
permissions:
contents: read

env:
AWS_ACCESS_KEY_ID: "000000000000"
AWS_DEFAULT_REGION: eu-west-1
AWS_EC2_METADATA_DISABLED: "true"
AWS_ENDPOINT_URL: http://localhost:4566
AWS_REGION: eu-west-1
AWS_SECRET_ACCESS_KEY: test-only
TF_IN_AUTOMATION: "true"
TF_INPUT: "false"

jobs:
control_plane_smoke:
name: Run webhook and pool lifecycle smoke test against MiniStack
ministack_smoke:
name: Run webhook and scale-set smoke tests against MiniStack
runs-on: ubuntu-latest
timeout-minutes: 30
services:
ministack:
image: ghcr.io/ministackorg/ministack:1.5.13@sha256:ce3c906f2866ff953ce4c56f06b1fa3e453bc32e41c00de17b5f5a8672c5a42c
ports:
- 4566:4566
options: --add-host=host.docker.internal:host-gateway
env:
MINISTACK_ACCOUNT_ID: "000000000000"
MINISTACK_REGION: eu-west-1
timeout-minutes: 120
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit

Expand All @@ -51,116 +34,13 @@ jobs:
with:
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: lambdas/.nvmrc
package-manager-cache: false

- name: Setup Terraform
uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
with:
terraform_version: latest
terraform_wrapper: false

- name: Install Lambda dependencies
working-directory: lambdas
run: yarn install --frozen-lockfile

- name: Build smoke-test Lambda distributions
working-directory: lambdas
- name: Docker Compose Build and Run
run: |
yarn workspace @aws-github-runner/webhook dist
yarn workspace @aws-github-runner/control-plane dist

- name: Start MockServer
id: mockserver
uses: mock-server/setup-mockserver@24612c2ccef1f83d587f331ed77cc5cef441e0b1 # v1.0.0
with:
version: '7.6.0@sha256:80b3b1a26f3553d0c81a3f3896b5b7274c17b2a2e52f0fd2b28e246bc9efa290'
port: '1080'
startup-timeout: '60'

- name: Run webhook and pool lifecycle smoke test
env:
MINISTACK_GITHUB_MOCK_HOST: host.docker.internal
MINISTACK_GITHUB_MOCK_PORT: "1080"
MINISTACK_GITHUB_MOCK_URL: ${{ steps.mockserver.outputs.url }}
run: sh tests/ministack/run-smoke.sh

scale_set_integration_smoke:
name: Run scale-set ECS smoke test against MiniStack and MockServer
runs-on: ubuntu-latest
timeout-minutes: 30
services:
ministack:
image: ghcr.io/ministackorg/ministack:1.5.12@sha256:41fe1ce2e666c6cc410c6047a9db8bf1df69cd0028ebc0a6c6e5517c3a83d6e0
ports:
- 4566:4566
# MiniStack launches nested service containers for the integration smoke test.
# This digest-pinned, non-fork workflow is the narrowly scoped Docker-daemon exception.
options: >-
--add-host=host.docker.internal:host-gateway
--volume /var/run/docker.sock:/var/run/docker.sock
env:
MINISTACK_ACCOUNT_ID: "000000000000"
MINISTACK_REGION: eu-west-1
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
with:
egress-policy: audit

- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: lambdas/.nvmrc
package-manager-cache: false

- name: Setup Terraform
uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
with:
terraform_version: latest
terraform_wrapper: false

- name: Install Lambda dependencies
working-directory: lambdas
run: yarn install --frozen-lockfile

- name: Build smoke-test Lambda distributions
working-directory: lambdas
run: |
yarn workspace @aws-github-runner/webhook dist
yarn workspace @aws-github-runner/control-plane dist

- name: Start MockServer
id: mockserver
uses: mock-server/setup-mockserver@24612c2ccef1f83d587f331ed77cc5cef441e0b1 # v1.0.0
with:
version: '7.6.0@sha256:80b3b1a26f3553d0c81a3f3896b5b7274c17b2a2e52f0fd2b28e246bc9efa290'
port: '1080'
startup-timeout: '60'

- name: Connect MockServer to MiniStack network
shell: bash
run: |
set -euo pipefail
ministack_container="$(docker ps --format '{{.ID}} {{.Image}}' | awk '$2 ~ /ministack/ {print $1; exit}')"
network="$(docker inspect --format '{{range $name, $_ := .NetworkSettings.Networks}}{{println $name}}{{end}}' "$ministack_container" | sed -n '1p')"
docker network connect --alias mockserver "$network" mockserver

- name: Mark repository as safe
shell: sh
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Run scale-set ECS/MockServer smoke test
env:
MINISTACK_GITHUB_MOCK_HOST: mockserver
MINISTACK_GITHUB_MOCK_PORT: "1080"
MINISTACK_GITHUB_MOCK_URL: ${{ steps.mockserver.outputs.url }}
run: sh tests/ministack/run-scale-set-integration.sh
compose_file=tests/multi-runner-orchestration/docker-compose.yml
docker compose --parallel 1 -f "$compose_file" pull
docker compose --parallel 1 -f "$compose_file" build
docker compose --parallel 1 -f "$compose_file" run --no-TTY --rm smoke-tests

- name: Print MiniStack logs on failure
if: failure()
run: docker compose -f tests/multi-runner-orchestration/docker-compose.yml logs --no-color --timestamps --tail=all ministack || true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ secrets.auto.tfvars

node_modules/
site/

lambda_output
4 changes: 0 additions & 4 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,10 +373,6 @@ The scale-set lane itself defaults to `runner.group_name = "Default"`,
name, runner owner, and GitHub App SSM references in the lane; credential values
are not placed in the controller manifest.

The [multi-runner scale-set example](multi-runner-scale-set.md) shows how these
provider-specific settings coexist with webhook lanes in the same v2
`multi_runner_config` map.

### macOS Runners

This feature is in early stage and should be considered experimental. The module supports macOS-based GitHub Actions self-hosted runners on AWS EC2 Mac instances (`mac1.metal`, `mac2.metal`, `mac2-m2.metal`). macOS runners require dedicated hosts due to Apple's licensing requirements and have longer boot times (6–20 minutes). Set `runner_os = "osx"` and `use_dedicated_host = true` to enable. See the full [macOS Runners documentation](mac-runners.md) for details.
Expand Down
2 changes: 0 additions & 2 deletions docs/examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ Examples are located in the [examples](https://github.com/github-aws-runners/ter
- _[Default](default.md)_: The default example of the module
- _[Ephemeral](ephemeral.md)_: Example usages of ephemeral runners based on the default example.
- _[Multi Runner](multi-runner.md)_ : Example usage of creating a multi runner which creates multiple runners/ configurations with a single deployment. The examples including: "arm64", "windows", and "ubuntu" runners.
- _[Multi Runner v2](multi-runner-v2.md)_ : Example usage of the experimental v2 multi-runner configuration interface with shared defaults and per-lane overrides.
- _[Multi Runner scale-set](multi-runner-scale-set.md)_ : Example usage of a v2 deployment combining webhook lanes with an experimental GitHub Actions scale-set lane.
- _[Permissions boundary](permissions-boundary.md)_: Example usages of permissions boundaries.
- _[Prebuilt Images](prebuilt.md)_: Example usages of deploying runners with a custom prebuilt image.
- _[Termination watcher](termination-watcher.md)_: Example usages of termination watcher.
Expand Down
74 changes: 74 additions & 0 deletions examples/multi-runner-orchestration/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 47 additions & 0 deletions examples/multi-runner-orchestration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Multi-runner orchestration example

<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.5.6 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 6.33 |
| <a name="requirement_null"></a> [null](#requirement\_null) | ~> 3.0 |
| <a name="requirement_random"></a> [random](#requirement\_random) | ~> 3.0 |

## Providers

No providers.

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_base"></a> [base](#module\_base) | ../base | n/a |
| <a name="module_runners"></a> [runners](#module\_runners) | ../../modules/multi-runner | n/a |

## Resources

No resources.

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_aws_region"></a> [aws\_region](#input\_aws\_region) | AWS Region where the runner control plane and compute provider resources are deployed. | `string` | `"eu-west-1"` | no |
| <a name="input_compute_provider"></a> [compute\_provider](#input\_compute\_provider) | Provider-specific settings for compute providers. | <pre>object({<br/> aws = object({<br/> ec2 = object({<br/> instance_types = list(string)<br/> ami = object({<br/> filter = optional(map(list(string)), { state = ["available"] })<br/> owners = optional(list(string), ["amazon"])<br/> id_ssm_parameter = optional(object({<br/> arn = string<br/> }), null)<br/> kms_key = optional(object({<br/> arn = string<br/> }), null)<br/> })<br/> })<br/> })<br/> })</pre> | n/a | yes |
| <a name="input_environment"></a> [environment](#input\_environment) | Name prefix for the example resources. | `string` | n/a | yes |
| <a name="input_github"></a> [github](#input\_github) | Optional GitHub endpoint and scale-set ownership settings. | <pre>object({<br/> url = optional(string, null)<br/> ssl_verify = optional(bool, true)<br/> runner_owner = optional(string, null)<br/> registration_level = optional(string, "organization")<br/> })</pre> | `{}` | no |
| <a name="input_github_app"></a> [github\_app](#input\_github\_app) | GitHub App ID, base64-encoded private key, and installation ID. | <pre>object({<br/> id = string<br/> key_base64 = string<br/> installation_id = optional(string, null)<br/> webhook_secret = string<br/> })</pre> | n/a | yes |
| <a name="input_runners_lambda_zip"></a> [runners\_lambda\_zip](#input\_runners\_lambda\_zip) | Local ZIP file for the runner-control Lambda. | `string` | n/a | yes |
| <a name="input_scale_set"></a> [scale\_set](#input\_scale\_set) | GitHub Actions scale-set configuration. | <pre>object({<br/> name = string<br/> runner_group_name = optional(string, "Default")<br/> min_runners = optional(number, 0)<br/> container = optional(object({<br/> image = optional(string, null)<br/> }), {})<br/> ecs = optional(object({<br/> task = optional(object({<br/> architecture = optional(string, "x86_64")<br/> }), {})<br/> }), {})<br/> })</pre> | n/a | yes |
| <a name="input_webhook_lambda_zip"></a> [webhook\_lambda\_zip](#input\_webhook\_lambda\_zip) | Local ZIP file for the webhook Lambda. | `string` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_webhook_endpoint"></a> [webhook\_endpoint](#output\_webhook\_endpoint) | n/a |
| <a name="output_webhook_secret"></a> [webhook\_secret](#output\_webhook\_secret) | n/a |
<!-- END_TF_DOCS -->
Loading
Loading