cdk-github-microvm-runners 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3989 @@
1
+ r'''
2
+ # cdk-github-microvm-runners
3
+
4
+ [![npm](https://img.shields.io/npm/v/cdk-github-microvm-runners?logo=npm&label=npm)](https://www.npmjs.com/package/cdk-github-microvm-runners) [![PyPI](https://img.shields.io/pypi/v/cdk-github-microvm-runners?logo=pypi&logoColor=white&label=PyPI)](https://pypi.org/project/cdk-github-microvm-runners/) [![CI](https://img.shields.io/github/actions/workflow/status/schuettc/cdk-github-microvm-runners/ci.yml?branch=main&logo=githubactions&logoColor=white&label=CI)](https://github.com/schuettc/cdk-github-microvm-runners/actions/workflows/ci.yml) [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
5
+
6
+ A CDK construct library that runs GitHub Actions jobs on
7
+ [AWS Lambda MicroVMs](https://docs.aws.amazon.com/lambda/latest/dg/lambda-microvms-guide.html).
8
+ Each job runs on its own MicroVM in your AWS account, created when the job
9
+ starts and removed once it finishes.
10
+
11
+ ## Why this exists
12
+
13
+ AWS Lambda MicroVMs provides the ability to launch a MicroVM, but nothing that
14
+ connects one to GitHub Actions: there is no runner registration, no path from
15
+ an incoming job to a running VM, and no cleanup when a VM or a runner is left
16
+ behind. AWS CDK provides the individual building blocks — Lambda functions, a
17
+ queue, a table, IAM roles — but not an assembled system. This construct is that
18
+ system. It receives GitHub's `workflow_job` events, launches a MicroVM for each
19
+ job, registers a single-use runner, and reconciles the result afterward, so
20
+ that adopting per-job MicroVM runners is a matter of configuring a construct
21
+ rather than building and operating the surrounding machinery yourself.
22
+
23
+ Two design decisions shape the rest of the library. The runner VMs hold no AWS
24
+ credentials by default, because a MicroVM's instance metadata service would
25
+ otherwise return any attached role's credentials to the code running in the
26
+ job; a job that needs AWS obtains its own short-lived credentials through GitHub
27
+ OIDC instead. Routing is per job: a workflow job opts in by naming the runner's
28
+ label in its `runs-on`, and every other job continues to run on GitHub-hosted
29
+ runners, so a repository can be moved across one job at a time.
30
+
31
+ ## How it works
32
+
33
+ A single runner set — one `GithubMicrovmRunners` in a stack — serves a GitHub
34
+ organization or a set of repositories. It is configured with two required
35
+ properties: how it authenticates to GitHub, and which organization or
36
+ repositories it serves.
37
+
38
+ A runner class pairs a `runs-on` label with the MicroVM size and image that
39
+ jobs carrying that label run on, and a runner set can define more than one.
40
+ Each class builds its own image and runs at its own size, so a workflow can
41
+ send small jobs to one class and memory-heavy jobs to a larger one by choosing
42
+ the matching label. The runner VMs are ARM64, so the tools and container images
43
+ a job pulls need arm64 builds.
44
+
45
+ <!-- sync:begin lifecycle-diagram -->
46
+
47
+ ```mermaid
48
+ flowchart TD
49
+ GH["GitHub Actions — workflow_job event"] --> WH["Webhook Lambda — verifies the signature"]
50
+ WH --> Q["SQS job queue — launch and terminate intents"]
51
+ Q --> LN["Launcher Lambda — claims the job, obtains a MicroVM"]
52
+ LN --> VM["MicroVM — runs one job"]
53
+ LN -.->|registers a single-use runner| GH
54
+ LN <--> DB[("DynamoDB runner table")]
55
+ JAN["Janitor Lambda — scheduled sweep"] -.->|reaps stranded VMs, reconciles state| VM
56
+ JAN <--> DB
57
+ ```
58
+
59
+ <!-- sync:end lifecycle-diagram -->
60
+
61
+ When GitHub sends a `workflow_job` event, the webhook handler verifies its
62
+ signature and places the job on an SQS queue. The launcher reads the queue,
63
+ records a claim so that a given job is only ever launched once, starts a
64
+ MicroVM (or resumes one from a warm pool, if the runner class keeps one), and
65
+ registers a single-use runner with GitHub. The VM runs that one job and is then
66
+ removed. A janitor runs on a schedule to reconcile the runner set against the
67
+ running VMs and GitHub's view of its runners, terminating anything that was
68
+ stranded and cleaning up records that are no longer needed. The
69
+ [architecture guide](docs/architecture.md) describes the lifecycle, the
70
+ idempotency model, and the checks that keep the janitor from terminating a
71
+ runner that is still working.
72
+
73
+ ## Install
74
+
75
+ One source, compiled by jsii into two packages. The API is the same in each,
76
+ with names rendered in the target language's conventions.
77
+
78
+ ```bash
79
+ npm install cdk-github-microvm-runners
80
+ ```
81
+
82
+ ```bash
83
+ pip install cdk-github-microvm-runners
84
+ ```
85
+
86
+ ## Getting started
87
+
88
+ To deploy, you need an AWS account, a GitHub App connected to the runner set,
89
+ and the App's private key and webhook secret stored in AWS Secrets Manager. The
90
+ [getting-started guide](docs/getting-started.md) walks through all three, and
91
+ includes a helper that performs the GitHub App setup for you.
92
+
93
+ The example below is TypeScript; [API.md](API.md) carries the same surface for
94
+ every language.
95
+
96
+ ```python
97
+ from aws_cdk import Environment
98
+ from aws_cdk import App, CfnOutput, Stack
99
+ from aws_cdk.aws_secretsmanager import Secret
100
+ from cdk_github_microvm_runners import GithubMicrovmRunners, GithubAppId, GithubAppKey, GithubAuth, RunnerScope, MicrovmSize
101
+
102
+ app = App()
103
+ stack = Stack(app, "Runners", env=Environment(region="us-east-1"))
104
+
105
+ # The GitHub App's ID, private key, and webhook secret are read from Secrets
106
+ # Manager at run time, so this stack deploys before the App is created.
107
+ app_id = Secret.from_secret_name_v2(stack, "AppId", "microvm-runner/dev/app-id")
108
+ private_key = Secret.from_secret_name_v2(stack, "AppKey", "microvm-runner/dev/app-private-key")
109
+ webhook_secret = Secret.from_secret_name_v2(stack, "WebhookSecret", "microvm-runner/dev/webhook-secret")
110
+
111
+ runners = GithubMicrovmRunners(stack, "Runners",
112
+ github=GithubAuth.app(
113
+ app_id=GithubAppId.from_secret(app_id),
114
+ private_key=GithubAppKey.from_secret(private_key),
115
+ webhook_secret=webhook_secret
116
+ ),
117
+ scope=RunnerScope.org("my-org")
118
+ )
119
+
120
+ # A runner class pairs a label with the MicroVM size it runs on. Workflows
121
+ # reach it through `runs-on: [self-hosted, microvm]`.
122
+ runners.add_runner_class("microvm", size=MicrovmSize.GB4)
123
+
124
+ CfnOutput(stack, "WebhookUrl", value=runners.webhook_url)
125
+ ```
126
+
127
+ Once the stack is deployed, point the GitHub App's webhook at the stack's
128
+ `WebhookUrl` output — the setup helper reads it from the stack and wires it for
129
+ you, or you can set it by hand — and change a workflow job's `runs-on` to
130
+ `[self-hosted, microvm]`.
131
+
132
+ ## Examples
133
+
134
+ The samples below extend the runner set above, and each shows only the part it
135
+ adds.
136
+
137
+ ### A runner class with its own image
138
+
139
+ A runner class that names no image builds from the base image, which carries
140
+ the operating system, the Actions runner, Docker, and the AWS CLI.
141
+ `RunnerImage.fromOptions` describes what to add to that base: system packages,
142
+ setup commands, environment variables, and files.
143
+
144
+ ```python
145
+ runners.add_runner_class("build",
146
+ size=MicrovmSize.GB8,
147
+ image=RunnerImage.from_options(
148
+ system_packages=["jq", "ripgrep"],
149
+ setup_commands=["npm install -g pnpm@10"]
150
+ )
151
+ )
152
+ ```
153
+
154
+ Images are built when the stack deploys, so a job that arrives later boots an
155
+ image that already exists. [Runner images](docs/images.md) covers the three
156
+ ways to describe one, including supplying a Dockerfile of your own.
157
+
158
+ ### Language versions baked into the image
159
+
160
+ `actions/setup-python` and `actions/setup-node` resolve the version a job asks
161
+ for from the image's tool cache. A toolchain is a language runtime placed in
162
+ that cache when the image is built.
163
+
164
+ ```python
165
+ runners.add_runner_class("test",
166
+ size=MicrovmSize.GB4,
167
+ image=RunnerImage.from_options(
168
+ toolchains=[
169
+ RunnerToolchain.python("3.12.7"),
170
+ RunnerToolchain.node("22.11.0")
171
+ ]
172
+ )
173
+ )
174
+ ```
175
+
176
+ Each toolchain pins a full three-part version, because that version names the
177
+ cache directory it is baked into. A workflow targeting the class then writes
178
+ `actions/setup-python` as it would on a GitHub-hosted runner, and
179
+ [Toolchains](docs/toolchains.md) covers the rest.
180
+
181
+ ### Runners in your own VPC
182
+
183
+ Network egress belongs to the runner set rather than to a class, and every
184
+ runner class shares it. `RunnerNetwork.vpc` has the construct build a Lambda
185
+ runtime connector from a VPC you pass, so the VMs follow that VPC's subnets,
186
+ security groups, and route tables.
187
+
188
+ ```python
189
+ GithubMicrovmRunners(stack, "Runners",
190
+ github=github,
191
+ scope=RunnerScope.org("my-org"),
192
+ network=RunnerNetwork.vpc(vpc,
193
+ subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS)
194
+ )
195
+ )
196
+ ```
197
+
198
+ The connector's network interfaces land in the VPC's private-with-egress
199
+ subnets and get a new security group unless you pass your own. The
200
+ [security guide](docs/security.md) covers attaching a connector you already
201
+ manage, and what the VPC's routing then decides for a job.
202
+
203
+ ## Security
204
+
205
+ The runner VMs carry no AWS identity by default, so a compromised or malicious
206
+ workflow step has no credentials to read from the instance metadata service. A
207
+ job that needs AWS assumes a role scoped to that job through GitHub OIDC. The
208
+ [security guide](docs/security.md) covers the whole model, including the OIDC
209
+ trust-policy setup and the details that are easy to get wrong.
210
+
211
+ The webhook is served from a public Lambda Function URL, because GitHub cannot
212
+ sign its webhook deliveries with SigV4. The handler verifies GitHub's
213
+ HMAC-SHA256 signature on every request.
214
+
215
+ A single property attaches an execution role to the VMs — `vmExecutionRole` —
216
+ for a runner set whose jobs need a standing AWS identity. Because that role's
217
+ credentials become readable from inside the job, it is off by default, and the
218
+ construct never creates a VM identity on your behalf. Runtime console capture
219
+ (`consoleLogs: ConsoleLogs.enabled()`) writes its logs through that same role,
220
+ so it too requires `vmExecutionRole`, and you grant the role the log-write
221
+ actions yourself.
222
+
223
+ ## Documentation
224
+
225
+ | Guide | What it covers |
226
+ | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
227
+ | [Getting started](docs/getting-started.md) | Prerequisites, GitHub App setup, deploying, and running a first job |
228
+ | [Onboarding a repo](docs/onboarding.md) | Moving an existing repository's CI onto the runners |
229
+ | [Runner images](docs/images.md) | What an image contains, when it is built, how it changes, and the disk each size allows |
230
+ | [Toolchains](docs/toolchains.md) | Baking the Python and Node versions your workflows use into the image |
231
+ | [Security](docs/security.md) | The security model, per-job AWS access through GitHub OIDC, and the control plane |
232
+ | [Service quotas](docs/service-quotas.md) | Sizing for concurrency, and what to do when a quota cannot be raised |
233
+ | [Monitoring](docs/monitoring.md) | Opt-in CloudWatch metrics and the ready-made alarms |
234
+ | [Logging](docs/logging.md) | Image build logs and runtime VM console output, and the tradeoff console capture involves |
235
+ | [Architecture](docs/architecture.md) | The lifecycle, the idempotency model, and the checks that prevent terminating an active runner |
236
+ | [API reference](API.md) | Every construct, property, and default, generated from the source |
237
+ | [Security policy](SECURITY.md) · [Contributing](CONTRIBUTING.md) · [Changelog](CHANGELOG.md) | Reporting and project information |
238
+
239
+ ## Configuration
240
+
241
+ Beyond the two required properties, every option is optional and has a
242
+ conservative default, so the example above deploys as written. The available
243
+ options include a customer-managed KMS key applied to the
244
+ DynamoDB table, the SQS queues, and the log groups (`encryptionKey`); a
245
+ permissions boundary applied to every role the construct creates
246
+ (`permissionsBoundary`); the removal policy, DynamoDB point-in-time recovery,
247
+ log retention, dead-letter retention, dead-letter redrive count, and Lambda
248
+ memory; and control over the VMs' network egress through `RunnerNetwork`.
249
+
250
+ ## Development
251
+
252
+ Quality checks run through [`just`](https://github.com/casey/just) and are
253
+ enforced by [lefthook](https://github.com/evilmartians/lefthook) git hooks; CI
254
+ runs the same `just verify`.
255
+
256
+ ```bash
257
+ brew install just lefthook
258
+ pnpm install
259
+ lefthook install
260
+ just verify
261
+ ```
262
+
263
+ <!-- sync:begin branch-model -->
264
+
265
+ Feature pull requests target `dev`, which is promoted to `main` by pull
266
+ request.
267
+
268
+ <!-- sync:end branch-model -->
269
+
270
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup and the full workflow.
271
+
272
+ ## License
273
+
274
+ Released under the [Apache-2.0](LICENSE) license.
275
+ '''
276
+ from __future__ import annotations
277
+
278
+ from pkgutil import extend_path
279
+ __path__ = extend_path(__path__, __name__)
280
+
281
+ import abc
282
+ import builtins
283
+ import datetime
284
+ import enum
285
+ import typing
286
+
287
+ import jsii
288
+ import publication
289
+ import typing_extensions
290
+
291
+ from jsii._type_checking import cached_type_hints, check_type
292
+
293
+
294
+ from ._jsii import *
295
+
296
+ class _LazyImport:
297
+ def __init__(self, module_name: str) -> None:
298
+ self._module_name = module_name
299
+ self._module: typing.Any = None
300
+ def __getattr__(self, name: str) -> typing.Any:
301
+ if self._module is None:
302
+ import importlib
303
+ self._module = importlib.import_module(self._module_name)
304
+ return getattr(self._module, name)
305
+
306
+ if typing.TYPE_CHECKING:
307
+
308
+ import aws_cdk as _aws_cdk_ceddda9d
309
+ import aws_cdk.aws_cloudwatch as _aws_cdk_aws_cloudwatch_ceddda9d
310
+ import aws_cdk.aws_dynamodb as _aws_cdk_aws_dynamodb_ceddda9d
311
+ import aws_cdk.aws_ec2 as _aws_cdk_aws_ec2_ceddda9d
312
+ import aws_cdk.aws_iam as _aws_cdk_aws_iam_ceddda9d
313
+ import aws_cdk.aws_kms as _aws_cdk_aws_kms_ceddda9d
314
+ import aws_cdk.aws_lambda as _aws_cdk_aws_lambda_ceddda9d
315
+ import aws_cdk.aws_logs as _aws_cdk_aws_logs_ceddda9d
316
+ import aws_cdk.aws_secretsmanager as _aws_cdk_aws_secretsmanager_ceddda9d
317
+ import aws_cdk.aws_sqs as _aws_cdk_aws_sqs_ceddda9d
318
+ import constructs as _constructs_77d1e7e8
319
+ else:
320
+
321
+ _aws_cdk_aws_cloudwatch_ceddda9d = _LazyImport("aws_cdk.aws_cloudwatch")
322
+ _aws_cdk_aws_dynamodb_ceddda9d = _LazyImport("aws_cdk.aws_dynamodb")
323
+ _aws_cdk_aws_ec2_ceddda9d = _LazyImport("aws_cdk.aws_ec2")
324
+ _aws_cdk_aws_iam_ceddda9d = _LazyImport("aws_cdk.aws_iam")
325
+ _aws_cdk_aws_kms_ceddda9d = _LazyImport("aws_cdk.aws_kms")
326
+ _aws_cdk_aws_lambda_ceddda9d = _LazyImport("aws_cdk.aws_lambda")
327
+ _aws_cdk_aws_logs_ceddda9d = _LazyImport("aws_cdk.aws_logs")
328
+ _aws_cdk_aws_secretsmanager_ceddda9d = _LazyImport("aws_cdk.aws_secretsmanager")
329
+ _aws_cdk_aws_sqs_ceddda9d = _LazyImport("aws_cdk.aws_sqs")
330
+ _aws_cdk_ceddda9d = _LazyImport("aws_cdk")
331
+ _constructs_77d1e7e8 = _LazyImport("constructs")
332
+
333
+
334
+ class ConsoleLogs(
335
+ metaclass=jsii.JSIIMeta,
336
+ jsii_type="cdk-github-microvm-runners.ConsoleLogs",
337
+ ):
338
+ '''Runtime console capture for a runner set: everything a VM prints while it boots, runs the runner agent, and runs the job.
339
+
340
+ Off unless you add it.
341
+
342
+ Console capture needs a VM execution role. The platform writes these logs
343
+ with the VM's own role, and the construct never creates a VM identity on
344
+ your behalf, so a runner set that turns console capture on without
345
+ ``vmExecutionRole`` fails at synth. The two console-write actions on the group
346
+ are granted by you as well, since the construct does not add policy to a
347
+ role it did not create::
348
+
349
+ runners.vm_console_log_group.grant(role, "logs:CreateLogStream", "logs:PutLogEvents")
350
+
351
+ A MicroVM's instance metadata service serves the execution role's
352
+ credentials to arbitrary job code, so whatever that role can do, every job
353
+ running on this runner set can do. Console capture on its own needs nothing
354
+ beyond the two log-write actions above. Job code can also write whatever it
355
+ likes into the console group, so the contents are as trustworthy as the jobs
356
+ that produced them.
357
+
358
+ ``ImageLogs`` covers the build-time counterpart and needs no role. The two are
359
+ independent and can both be on.
360
+
361
+ Example::
362
+
363
+ GithubMicrovmRunners(stack, "Runners",
364
+ github=github,
365
+ scope=scope,
366
+ vm_execution_role=role,
367
+ console_logs=ConsoleLogs.enabled()
368
+ )
369
+ '''
370
+
371
+ @jsii.member(jsii_name="enabled")
372
+ @builtins.classmethod
373
+ def enabled(
374
+ cls,
375
+ log_group: typing.Optional["_aws_cdk_aws_logs_ceddda9d.ILogGroup"] = None,
376
+ ) -> "ConsoleLogs":
377
+ '''Capture the runtime console.
378
+
379
+ With no argument the construct creates a log
380
+ group and exposes it as ``runners.vmConsoleLogGroup``. Pass an ``ILogGroup``
381
+ to use one whose retention and KMS key you control. Either way the runner
382
+ set needs ``vmExecutionRole``, and that role needs the two console-write
383
+ actions on the group.
384
+
385
+ :param log_group: destination group. Omitted, the construct creates one with the runner set's ``logRetention`` (two weeks by default).
386
+
387
+ Example::
388
+
389
+ console_capture = ConsoleLogs.enabled(my_console_log_group)
390
+ '''
391
+ if __debug__:
392
+ type_hints = cached_type_hints(_typecheckingstub__0548cfa81769608b199a2a959830e9d47b13bb916b5f4181e977c1f03bfa81c7)
393
+ check_type(argname="argument log_group", value=log_group, expected_type=type_hints["log_group"])
394
+ return typing.cast("ConsoleLogs", jsii.sinvoke(cls, "enabled", [log_group]))
395
+
396
+ @builtins.property
397
+ @jsii.member(jsii_name="logGroup")
398
+ def log_group(self) -> typing.Optional["_aws_cdk_aws_logs_ceddda9d.ILogGroup"]:
399
+ '''The group console output goes to, when one was passed to ``ConsoleLogs.enabled()``. ``undefined`` means the construct creates one.'''
400
+ return typing.cast(typing.Optional["_aws_cdk_aws_logs_ceddda9d.ILogGroup"], jsii.get(self, "logGroup"))
401
+
402
+
403
+ @jsii.data_type(
404
+ jsii_type="cdk-github-microvm-runners.GithubAppAuthProps",
405
+ jsii_struct_bases=[],
406
+ name_mapping={
407
+ "app_id": "appId",
408
+ "private_key": "privateKey",
409
+ "webhook_secret": "webhookSecret",
410
+ },
411
+ )
412
+ class GithubAppAuthProps:
413
+ def __init__(
414
+ self,
415
+ *,
416
+ app_id: "GithubAppId",
417
+ private_key: "GithubAppKey",
418
+ webhook_secret: "_aws_cdk_aws_secretsmanager_ceddda9d.ISecret",
419
+ ) -> None:
420
+ '''Props for ``GithubAuth.app``.
421
+
422
+ :param app_id: The GitHub App's numeric ID, as a literal or a Secrets Manager reference.
423
+ :param private_key: The App's private key, backed by a secret or a KMS key.
424
+ :param webhook_secret: Secret holding the webhook secret used to validate inbound deliveries.
425
+ '''
426
+ if __debug__:
427
+ type_hints = cached_type_hints(_typecheckingstub__e6e950b235861eb6ceb59c62bebf431836fac157088b1334fedf915b19dec8a0)
428
+ check_type(argname="argument app_id", value=app_id, expected_type=type_hints["app_id"])
429
+ check_type(argname="argument private_key", value=private_key, expected_type=type_hints["private_key"])
430
+ check_type(argname="argument webhook_secret", value=webhook_secret, expected_type=type_hints["webhook_secret"])
431
+ self._values: typing.Dict[builtins.str, typing.Any] = {
432
+ "app_id": app_id,
433
+ "private_key": private_key,
434
+ "webhook_secret": webhook_secret,
435
+ }
436
+
437
+ @builtins.property
438
+ def app_id(self) -> "GithubAppId":
439
+ '''The GitHub App's numeric ID, as a literal or a Secrets Manager reference.'''
440
+ result = self._values.get("app_id")
441
+ assert result is not None, "Required property 'app_id' is missing"
442
+ return typing.cast("GithubAppId", result)
443
+
444
+ @builtins.property
445
+ def private_key(self) -> "GithubAppKey":
446
+ '''The App's private key, backed by a secret or a KMS key.'''
447
+ result = self._values.get("private_key")
448
+ assert result is not None, "Required property 'private_key' is missing"
449
+ return typing.cast("GithubAppKey", result)
450
+
451
+ @builtins.property
452
+ def webhook_secret(self) -> "_aws_cdk_aws_secretsmanager_ceddda9d.ISecret":
453
+ '''Secret holding the webhook secret used to validate inbound deliveries.'''
454
+ result = self._values.get("webhook_secret")
455
+ assert result is not None, "Required property 'webhook_secret' is missing"
456
+ return typing.cast("_aws_cdk_aws_secretsmanager_ceddda9d.ISecret", result)
457
+
458
+ def __eq__(self, rhs: typing.Any) -> builtins.bool:
459
+ return isinstance(rhs, self.__class__) and rhs._values == self._values
460
+
461
+ def __ne__(self, rhs: typing.Any) -> builtins.bool:
462
+ return not (rhs == self)
463
+
464
+ def __repr__(self) -> str:
465
+ return "GithubAppAuthProps(%s)" % ", ".join(
466
+ k + "=" + repr(v) for k, v in self._values.items()
467
+ )
468
+
469
+
470
+ class GithubAppId(
471
+ metaclass=jsii.JSIIMeta,
472
+ jsii_type="cdk-github-microvm-runners.GithubAppId",
473
+ ):
474
+ '''Where a GitHub App's numeric ID comes from: a literal known at synth time, or a Secrets Manager secret read at runtime.
475
+
476
+ The secret form makes setup single-pass. A GitHub App can only be created
477
+ once the runner set's webhook URL exists, so an App ID that has to be known
478
+ at synth means deploying twice. Referencing the ID by secret, the way the
479
+ private key and webhook secret already are, lets you deploy, then create the
480
+ App and write its ID into the secret, with no redeploy.
481
+
482
+ Build one with the static factories below; the constructor is private.
483
+
484
+ Example::
485
+
486
+ app_id = GithubAppId.from_secret(
487
+ Secret.from_secret_name_v2(stack, "AppId", "microvm-runner/dev/app-id"))
488
+ '''
489
+
490
+ @jsii.member(jsii_name="fromSecret")
491
+ @builtins.classmethod
492
+ def from_secret(
493
+ cls,
494
+ secret: "_aws_cdk_aws_secretsmanager_ceddda9d.ISecret",
495
+ ) -> "GithubAppId":
496
+ '''The App ID is read at runtime from a Secrets Manager secret whose value is the numeric ID.
497
+
498
+ The secret need not exist at deploy time.
499
+
500
+ :param secret: -
501
+
502
+ Example::
503
+
504
+ app_id = GithubAppId.from_secret(
505
+ Secret.from_secret_name_v2(stack, "AppId", "microvm-runner/dev/app-id"))
506
+ '''
507
+ if __debug__:
508
+ type_hints = cached_type_hints(_typecheckingstub__aba0941864c428347812aa10a096b18df3ae77616773316bd134e2764ae2657c)
509
+ check_type(argname="argument secret", value=secret, expected_type=type_hints["secret"])
510
+ return typing.cast("GithubAppId", jsii.sinvoke(cls, "fromSecret", [secret]))
511
+
512
+ @jsii.member(jsii_name="fromValue")
513
+ @builtins.classmethod
514
+ def from_value(cls, value: builtins.str) -> "GithubAppId":
515
+ '''The App ID is a literal string known at synth time.
516
+
517
+ :param value: -
518
+
519
+ Example::
520
+
521
+ app_id = GithubAppId.from_value("123456")
522
+ '''
523
+ if __debug__:
524
+ type_hints = cached_type_hints(_typecheckingstub__6b22d9ed14d289886f4ed34dc8cfd25d58e6bbda42419d5097a7a73ea48bd10f)
525
+ check_type(argname="argument value", value=value, expected_type=type_hints["value"])
526
+ return typing.cast("GithubAppId", jsii.sinvoke(cls, "fromValue", [value]))
527
+
528
+ @builtins.property
529
+ @jsii.member(jsii_name="secret")
530
+ def secret(self) -> typing.Optional["_aws_cdk_aws_secretsmanager_ceddda9d.ISecret"]:
531
+ '''The secret holding the ID, for an ID built with ``fromSecret()``.'''
532
+ return typing.cast(typing.Optional["_aws_cdk_aws_secretsmanager_ceddda9d.ISecret"], jsii.get(self, "secret"))
533
+
534
+ @builtins.property
535
+ @jsii.member(jsii_name="value")
536
+ def value(self) -> typing.Optional[builtins.str]:
537
+ '''The literal ID, for an ID built with ``fromValue()``.'''
538
+ return typing.cast(typing.Optional[builtins.str], jsii.get(self, "value"))
539
+
540
+
541
+ class GithubAppKey(
542
+ metaclass=jsii.JSIIMeta,
543
+ jsii_type="cdk-github-microvm-runners.GithubAppKey",
544
+ ):
545
+ '''Where a GitHub App's private key lives: a Secrets Manager secret holding the PEM, or a KMS key that signs the App's JWTs directly.
546
+
547
+ Build one with the static factories below; the constructor is private.
548
+
549
+ Example::
550
+
551
+ private_key = GithubAppKey.from_secret(
552
+ Secret.from_secret_name_v2(stack, "AppKey", "microvm-runner/dev/app-private-key"))
553
+ '''
554
+
555
+ @jsii.member(jsii_name="fromKmsKey")
556
+ @builtins.classmethod
557
+ def from_kms_key(cls, key: "_aws_cdk_aws_kms_ceddda9d.IKey") -> "GithubAppKey":
558
+ '''The App's private key lives in KMS and is used via ``kms:Sign``.
559
+
560
+ :param key: -
561
+
562
+ Example::
563
+
564
+ from aws_cdk.aws_kms import Key
565
+
566
+
567
+ private_key = GithubAppKey.from_kms_key(
568
+ Key.from_key_arn(stack, "AppKey", "arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"))
569
+ '''
570
+ if __debug__:
571
+ type_hints = cached_type_hints(_typecheckingstub__cc9961f681c63592cc8d260fe3e536a71239765b00a83bdec348eff483581f2b)
572
+ check_type(argname="argument key", value=key, expected_type=type_hints["key"])
573
+ return typing.cast("GithubAppKey", jsii.sinvoke(cls, "fromKmsKey", [key]))
574
+
575
+ @jsii.member(jsii_name="fromSecret")
576
+ @builtins.classmethod
577
+ def from_secret(
578
+ cls,
579
+ secret: "_aws_cdk_aws_secretsmanager_ceddda9d.ISecret",
580
+ ) -> "GithubAppKey":
581
+ '''The App's private key is stored as a PEM in Secrets Manager.
582
+
583
+ :param secret: -
584
+
585
+ Example::
586
+
587
+ private_key = GithubAppKey.from_secret(
588
+ Secret.from_secret_name_v2(stack, "AppKey", "microvm-runner/dev/app-private-key"))
589
+ '''
590
+ if __debug__:
591
+ type_hints = cached_type_hints(_typecheckingstub__b690547df91198ce68555c0eef4906781d6764b68601422750750169edd60220)
592
+ check_type(argname="argument secret", value=secret, expected_type=type_hints["secret"])
593
+ return typing.cast("GithubAppKey", jsii.sinvoke(cls, "fromSecret", [secret]))
594
+
595
+ @builtins.property
596
+ @jsii.member(jsii_name="kmsKey")
597
+ def kms_key(self) -> typing.Optional["_aws_cdk_aws_kms_ceddda9d.IKey"]:
598
+ '''The signing key, for a key built with ``fromKmsKey()``.'''
599
+ return typing.cast(typing.Optional["_aws_cdk_aws_kms_ceddda9d.IKey"], jsii.get(self, "kmsKey"))
600
+
601
+ @builtins.property
602
+ @jsii.member(jsii_name="secret")
603
+ def secret(self) -> typing.Optional["_aws_cdk_aws_secretsmanager_ceddda9d.ISecret"]:
604
+ '''The secret holding the PEM, for a key built with ``fromSecret()``.'''
605
+ return typing.cast(typing.Optional["_aws_cdk_aws_secretsmanager_ceddda9d.ISecret"], jsii.get(self, "secret"))
606
+
607
+
608
+ class GithubAuth(
609
+ metaclass=jsii.JSIIMeta,
610
+ jsii_type="cdk-github-microvm-runners.GithubAuth",
611
+ ):
612
+ '''How a runner set authenticates to GitHub: as a GitHub App, whose private key is backed by a secret or a KMS key, or with a personal access token.
613
+
614
+ Either
615
+ form also carries the webhook secret that inbound GitHub deliveries are
616
+ validated against.
617
+
618
+ Build one with the static factories below; the constructor is private.
619
+
620
+ Example::
621
+
622
+ auth = GithubAuth.app(
623
+ app_id=GithubAppId.from_secret(
624
+ Secret.from_secret_name_v2(stack, "AppId", "microvm-runner/dev/app-id")),
625
+ private_key=GithubAppKey.from_secret(
626
+ Secret.from_secret_name_v2(stack, "AppKey", "microvm-runner/dev/app-private-key")),
627
+ webhook_secret=Secret.from_secret_name_v2(stack, "WebhookSecret", "microvm-runner/dev/webhook-secret")
628
+ )
629
+ '''
630
+
631
+ @jsii.member(jsii_name="app")
632
+ @builtins.classmethod
633
+ def app(
634
+ cls,
635
+ *,
636
+ app_id: "GithubAppId",
637
+ private_key: "GithubAppKey",
638
+ webhook_secret: "_aws_cdk_aws_secretsmanager_ceddda9d.ISecret",
639
+ ) -> "GithubAuth":
640
+ '''Authenticate as a GitHub App.
641
+
642
+ :param app_id: The GitHub App's numeric ID, as a literal or a Secrets Manager reference.
643
+ :param private_key: The App's private key, backed by a secret or a KMS key.
644
+ :param webhook_secret: Secret holding the webhook secret used to validate inbound deliveries.
645
+
646
+ Example::
647
+
648
+ auth = GithubAuth.app(
649
+ app_id=GithubAppId.from_secret(
650
+ Secret.from_secret_name_v2(stack, "AppId", "microvm-runner/dev/app-id")),
651
+ private_key=GithubAppKey.from_secret(
652
+ Secret.from_secret_name_v2(stack, "AppKey", "microvm-runner/dev/app-private-key")),
653
+ webhook_secret=Secret.from_secret_name_v2(stack, "WebhookSecret", "microvm-runner/dev/webhook-secret")
654
+ )
655
+ '''
656
+ props = GithubAppAuthProps(
657
+ app_id=app_id, private_key=private_key, webhook_secret=webhook_secret
658
+ )
659
+
660
+ return typing.cast("GithubAuth", jsii.sinvoke(cls, "app", [props]))
661
+
662
+ @jsii.member(jsii_name="pat")
663
+ @builtins.classmethod
664
+ def pat(
665
+ cls,
666
+ *,
667
+ token: "_aws_cdk_aws_secretsmanager_ceddda9d.ISecret",
668
+ webhook_secret: "_aws_cdk_aws_secretsmanager_ceddda9d.ISecret",
669
+ ) -> "GithubAuth":
670
+ '''Authenticate with a personal access token.
671
+
672
+ :param token: Secret holding a GitHub personal access token.
673
+ :param webhook_secret: Secret holding the webhook secret used to validate inbound deliveries.
674
+
675
+ Example::
676
+
677
+ auth = GithubAuth.pat(
678
+ token=Secret.from_secret_name_v2(stack, "Pat", "microvm-runner/dev/token"),
679
+ webhook_secret=Secret.from_secret_name_v2(stack, "WebhookSecret", "microvm-runner/dev/webhook-secret")
680
+ )
681
+ '''
682
+ props = GithubPatAuthProps(token=token, webhook_secret=webhook_secret)
683
+
684
+ return typing.cast("GithubAuth", jsii.sinvoke(cls, "pat", [props]))
685
+
686
+ @jsii.member(jsii_name="bindEnv")
687
+ def bind_env(self) -> typing.Mapping[builtins.str, builtins.str]:
688
+ '''Serialize to the environment variables the runner set's handlers read: ``GH_AUTH_KIND``, ``GH_APP_ID`` or ``GH_APP_ID_SECRET_ARN``, ``GH_KEY_SECRET_ARN`` or ``GH_KEY_KMS_ARN``, ``GH_PAT_SECRET_ARN``, and ``GH_WEBHOOK_SECRET_ARN``.
689
+
690
+ Entries that do not apply are left out.
691
+ '''
692
+ return typing.cast(typing.Mapping[builtins.str, builtins.str], jsii.invoke(self, "bindEnv", []))
693
+
694
+ @jsii.member(jsii_name="grantRead")
695
+ def grant_read(self, grantee: "_aws_cdk_aws_iam_ceddda9d.IGrantable") -> None:
696
+ '''Grant ``grantee`` read access to whichever credentials this auth carries: the App's secret-backed key and/or ``kms:Sign`` on its KMS key, plus its secret-backed App ID when one is used, or the PAT secret — plus, in every case, read access to the webhook secret.
697
+
698
+ This is the full set, for a handler that has to act as the App. A handler
699
+ that only verifies signatures wants {@link grantReadWebhookSecret}.
700
+
701
+ :param grantee: -
702
+ '''
703
+ if __debug__:
704
+ type_hints = cached_type_hints(_typecheckingstub__970e66a646e2fc86c035066057cff7cd247b431b227ac9f2d5a8c86ed710c988)
705
+ check_type(argname="argument grantee", value=grantee, expected_type=type_hints["grantee"])
706
+ return typing.cast(None, jsii.invoke(self, "grantRead", [grantee]))
707
+
708
+ @jsii.member(jsii_name="grantReadWebhookSecret")
709
+ def grant_read_webhook_secret(
710
+ self,
711
+ grantee: "_aws_cdk_aws_iam_ceddda9d.IGrantable",
712
+ ) -> None:
713
+ '''Grant ``grantee`` read access to the webhook secret, and nothing else.
714
+
715
+ This is all a handler needs to verify the HMAC signature GitHub sends with
716
+ every delivery. It is deliberately separate from {@link grantRead}, which
717
+ also hands over the credentials that can act AS the App — minting
718
+ installation tokens, registering runners. A component that only checks
719
+ signatures and enqueues has no use for those, and the webhook handler is
720
+ the one component reachable from the public internet.
721
+
722
+ :param grantee: -
723
+
724
+ Example::
725
+
726
+ github.grant_read_webhook_secret(role)
727
+ '''
728
+ if __debug__:
729
+ type_hints = cached_type_hints(_typecheckingstub__80b1bb7036f50148e74587e4f8bac689760f9f314afddfa588fbc2a86d5062dd)
730
+ check_type(argname="argument grantee", value=grantee, expected_type=type_hints["grantee"])
731
+ return typing.cast(None, jsii.invoke(self, "grantReadWebhookSecret", [grantee]))
732
+
733
+ @builtins.property
734
+ @jsii.member(jsii_name="kind")
735
+ def kind(self) -> "GithubAuthKind":
736
+ '''Whether this is App or personal-access-token authentication.'''
737
+ return typing.cast("GithubAuthKind", jsii.get(self, "kind"))
738
+
739
+ @builtins.property
740
+ @jsii.member(jsii_name="webhookSecret")
741
+ def webhook_secret(self) -> "_aws_cdk_aws_secretsmanager_ceddda9d.ISecret":
742
+ '''Secret holding the webhook secret inbound deliveries are validated against.'''
743
+ return typing.cast("_aws_cdk_aws_secretsmanager_ceddda9d.ISecret", jsii.get(self, "webhookSecret"))
744
+
745
+ @builtins.property
746
+ @jsii.member(jsii_name="appId")
747
+ def app_id(self) -> typing.Optional["GithubAppId"]:
748
+ '''The App's ID, for App authentication.'''
749
+ return typing.cast(typing.Optional["GithubAppId"], jsii.get(self, "appId"))
750
+
751
+ @builtins.property
752
+ @jsii.member(jsii_name="privateKey")
753
+ def private_key(self) -> typing.Optional["GithubAppKey"]:
754
+ '''The App's private key, for App authentication.'''
755
+ return typing.cast(typing.Optional["GithubAppKey"], jsii.get(self, "privateKey"))
756
+
757
+ @builtins.property
758
+ @jsii.member(jsii_name="token")
759
+ def token(self) -> typing.Optional["_aws_cdk_aws_secretsmanager_ceddda9d.ISecret"]:
760
+ '''The personal access token secret, for token authentication.'''
761
+ return typing.cast(typing.Optional["_aws_cdk_aws_secretsmanager_ceddda9d.ISecret"], jsii.get(self, "token"))
762
+
763
+
764
+ @jsii.enum(jsii_type="cdk-github-microvm-runners.GithubAuthKind")
765
+ class GithubAuthKind(enum.Enum):
766
+ '''Which credential flow a ``GithubAuth`` represents.'''
767
+
768
+ APP = "APP"
769
+ '''A GitHub App.'''
770
+ PAT = "PAT"
771
+ '''A personal access token.'''
772
+
773
+
774
+ class GithubMicrovmRunners(
775
+ _constructs_77d1e7e8.Construct,
776
+ metaclass=jsii.JSIIMeta,
777
+ jsii_type="cdk-github-microvm-runners.GithubMicrovmRunners",
778
+ ):
779
+ '''A runner set: one deployment of GitHub Actions runners that run on AWS Lambda MicroVMs, with a fresh VM per job that is thrown away when the job ends.
780
+
781
+ The construct deploys a webhook handler for GitHub's ``workflow_job``
782
+ deliveries, a queue those deliveries become launch and terminate intents on,
783
+ a launcher that starts a VM and registers it with GitHub for each queued
784
+ job, and a janitor that sweeps on a schedule for VMs and runners that
785
+ outlived their job. Every runner class registered through
786
+ ``addRunnerClass`` adds an image build of its own, and a runner set needs
787
+ at least one class to synthesize.
788
+
789
+ Deploying it takes two props: how to authenticate to GitHub, and which
790
+ GitHub scope the runners register into. The VMs themselves carry no AWS
791
+ identity unless ``GithubMicrovmRunnersProps.vmExecutionRole`` gives them
792
+ one.
793
+
794
+ Example::
795
+
796
+ runner_set = GithubMicrovmRunners(stack, "Runners",
797
+ github=GithubAuth.app(
798
+ app_id=GithubAppId.from_secret(
799
+ Secret.from_secret_name_v2(stack, "AppId", "microvm-runner/dev/app-id")),
800
+ private_key=GithubAppKey.from_secret(
801
+ Secret.from_secret_name_v2(stack, "AppKey", "microvm-runner/dev/app-private-key")),
802
+ webhook_secret=Secret.from_secret_name_v2(stack, "WebhookSecret", "microvm-runner/dev/webhook-secret")
803
+ ),
804
+ scope=RunnerScope.org("my-org")
805
+ )
806
+
807
+ runner_set.add_runner_class("microvm", size=MicrovmSize.GB4)
808
+ '''
809
+
810
+ def __init__(
811
+ self,
812
+ scope_: "_constructs_77d1e7e8.Construct",
813
+ id: builtins.str,
814
+ *,
815
+ github: "GithubAuth",
816
+ scope: "RunnerScope",
817
+ additional_regions: typing.Optional[typing.Sequence[builtins.str]] = None,
818
+ console_logs: typing.Optional["ConsoleLogs"] = None,
819
+ dead_letter_retention: typing.Optional["_aws_cdk_ceddda9d.Duration"] = None,
820
+ emit_metrics: typing.Optional[builtins.bool] = None,
821
+ encryption_key: typing.Optional["_aws_cdk_aws_kms_ceddda9d.IKey"] = None,
822
+ idle_runner_grace_seconds: typing.Optional[jsii.Number] = None,
823
+ image_logs: typing.Optional["ImageLogs"] = None,
824
+ janitor_interval: typing.Optional["_aws_cdk_ceddda9d.Duration"] = None,
825
+ keep_image_versions: typing.Optional[jsii.Number] = None,
826
+ lambda_memory_size: typing.Optional[jsii.Number] = None,
827
+ log_retention: typing.Optional["_aws_cdk_aws_logs_ceddda9d.RetentionDays"] = None,
828
+ max_concurrent_vms: typing.Optional[jsii.Number] = None,
829
+ max_job_duration: typing.Optional["_aws_cdk_ceddda9d.Duration"] = None,
830
+ max_receive_count: typing.Optional[jsii.Number] = None,
831
+ network: typing.Optional["RunnerNetwork"] = None,
832
+ permissions_boundary: typing.Optional["_aws_cdk_aws_iam_ceddda9d.IManagedPolicy"] = None,
833
+ point_in_time_recovery: typing.Optional[builtins.bool] = None,
834
+ recover_stuck_launches: typing.Optional[builtins.bool] = None,
835
+ removal_policy: typing.Optional["_aws_cdk_ceddda9d.RemovalPolicy"] = None,
836
+ vm_execution_role: typing.Optional["_aws_cdk_aws_iam_ceddda9d.IRole"] = None,
837
+ warm_pool_interval: typing.Optional["_aws_cdk_ceddda9d.Duration"] = None,
838
+ webhook: typing.Optional["WebhookEndpoint"] = None,
839
+ webhook_reserved_concurrency: typing.Optional[jsii.Number] = None,
840
+ ) -> None:
841
+ '''
842
+ :param scope_: -
843
+ :param id: -
844
+ :param github: How the runner set authenticates to GitHub, as an App or with a personal access token. This also carries the webhook secret.
845
+ :param scope: Which GitHub scope, an organization or a list of repositories, registered runners are visible to.
846
+ :param additional_regions: Additional regions to accept as Lambda MicroVMs regions, beyond the ones this library already knows about. Deploying into a region on neither list fails at synth. Default: [] (only the regions this library knows about)
847
+ :param console_logs: Where a VM's runtime console goes: everything it prints while it boots, runs the runner agent, and runs the job. ``ConsoleLogs.enabled()`` has the construct create the group and expose it as ``vmConsoleLogGroup``, and ``ConsoleLogs.enabled(logGroup)`` uses one you control. The platform writes these logs with the VM's own role, so this requires ``vmExecutionRole``; see ``ConsoleLogs`` for what that role means for job code. Independent of ``imageLogs``. Default: undefined (no runtime console capture)
848
+ :param dead_letter_retention: How long the dead-letter queue retains a failed launch or terminate intent. SQS allows up to 14 days, which is also how long ``recoverStuckLaunches`` has to re-drive a message before SQS drops it. Default: Duration.days(4) (SQS default)
849
+ :param emit_metrics: Report this runner set's CloudWatch custom metrics. Those are the janitor's per-sweep counters, the launcher's per-launch outcomes and spin-up timings, and the warm pool's fill numbers — everything ``GithubMicrovmRunnersMetrics`` names. With this off the handlers report none of them. CloudWatch bills custom metrics per metric per month, and this runner set's bill is not a fixed number: the launcher and warm-pool metrics carry a runner-class dimension, so each one becomes a separate billable metric per registered runner class. The two alarms backed by these metrics, ``sweepErrorsAlarm`` and ``stuckLaunchesRecoveredAlarm``, throw at synth unless this is on, since the metric they watch would never report. ``deadLetterQueueNotEmptyAlarm`` watches an SQS metric and works either way. The metric accessors on ``GithubMicrovmRunnersMetrics`` return a ``Metric`` regardless, so a dashboard can be built ahead of turning metrics on. Default: false (no metrics emitted)
850
+ :param encryption_key: Customer-managed KMS key for this runner set's data at rest: the DynamoDB runner table, the SQS job queue and dead-letter queue, and any log group this construct creates. Log groups you bring yourself, and the GitHub secrets you pass in, keep their own keys. Default: undefined (AWS-managed keys)
851
+ :param idle_runner_grace_seconds: How many seconds a registered runner may sit idle before the janitor's two-strike sweep treats it as stuck. Default: 600
852
+ :param image_logs: Where build-time image logs go: the Docker build layers and the ready-probe banner from each image build. ``ImageLogs.enabled()`` sends them to the platform's own CloudWatch group, and ``ImageLogs.enabled(logGroup)`` to a group whose retention and KMS key you control. These are written by the image build role rather than by a VM, so they need no VM execution role. Independent of ``consoleLogs``. Default: undefined (no image logs)
853
+ :param janitor_interval: How often the janitor sweep runs. Default: Duration.minutes(5)
854
+ :param keep_image_versions: How many MicroVM image versions to keep per runner class. The janitor prunes inactive versions past this count. Default: 5
855
+ :param lambda_memory_size: Memory, in MiB, for the handler Lambdas: the webhook, the launcher, the janitor, and the warm pool. The janitor's sweep scans the runner table and reconciles every running VM, so it is the handler most sensitive to this on a busy runner set. Default: 128
856
+ :param log_retention: Retention for the CloudWatch log groups this construct creates: the handler Lambda log groups and, when console capture is on, the VM console group. Default: logs.RetentionDays.TWO_WEEKS
857
+ :param max_concurrent_vms: Maximum number of MicroVMs this runner set runs at once. Default: 10
858
+ :param max_job_duration: How long a job may run before its MicroVM is terminated. The VM is killed five minutes after this value, not at it. The runner set asks the platform for ``maxJobDuration + 5 minutes``, so that a job which reaches its own limit is stopped by the runner — which reports the timeout to GitHub and lets the VM come down cleanly — rather than by the platform removing the machine underneath it. Treat the five minutes as headroom for that shutdown rather than as extra running time. Default: Duration.hours(6)
859
+ :param max_receive_count: How many times a launch intent is redriven before it dead-letters. A runner set already at ``maxConcurrentVms`` redrives capacity-rejected launches through this same budget, so on a runner set that regularly runs at capacity this count is how long a queued job waits before its launch is dropped. See docs/service-quotas.md. Default: 20
860
+ :param network: How launched MicroVMs and image builds reach the network. Default: RunnerNetwork.internetEgress()
861
+ :param permissions_boundary: Permissions boundary applied to every IAM role this construct creates: the handler execution roles, the per-class image build roles, and the network-connector operator role. It is applied once at construct scope, so roles created later also carry it — the warm-pool handler's role, and the build role of any runner class registered after construction. Default: undefined (no boundary)
862
+ :param point_in_time_recovery: Turn on DynamoDB point-in-time recovery for the runner table. Default: false
863
+ :param recover_stuck_launches: Recover launches that dead-lettered while GitHub Actions was down. Each janitor sweep re-drives dead-lettered launch messages back onto the job queue, but only for jobs GitHub still reports as queued; a launch whose job has since completed or been cancelled is discarded rather than booting a VM for work nobody is waiting on. Recovery happens once an outage ends, since GitHub dispatches no jobs while it is down. The janitor counts each recovered launch under the ``stuckLaunchesRecovered`` metric, which reports when ``emitMetrics`` is on. Default: false
864
+ :param removal_policy: Removal policy for this runner set's stateful resources: the runner table and any log group the construct creates. The table holds correlation data for VMs that are currently running, all of which the janitor can rebuild from the MicroVM and GitHub APIs. Default: RemovalPolicy.DESTROY
865
+ :param vm_execution_role: An AWS identity for this runner set's runner VMs. By default the VMs carry no AWS identity at all: the runner agent talks outbound to GitHub, the just-in-time registration is pushed to the VM over a platform-authenticated channel, and a job that needs AWS assumes its own role through GitHub OIDC. With a role attached, the MicroVM's instance metadata service serves that role's credentials to arbitrary job code, so every job running on this runner set can do whatever the role can do. ``consoleLogs`` requires a role, because the platform writes a VM's console output using it. Default: undefined (the VMs carry no AWS identity)
866
+ :param warm_pool_interval: How often the warm-pool sweep refills pre-booted VMs. It applies only to runner classes that set ``RunnerClassProps.warmPoolSize``, and the warm-pool handler and its schedule are only created once such a class is registered. A runner set with no warm class never runs this sweep, and never reads this value. Default: Duration.minutes(2)
867
+ :param webhook: How the webhook handler is exposed to GitHub. Default: WebhookEndpoint.functionUrl()
868
+ :param webhook_reserved_concurrency: Reserved concurrency for the webhook Lambda, which caps how many webhook deliveries the runner set processes at once. Reserved concurrency is carved out of the account's shared pool of unreserved concurrency, so a runner set that sets it takes that capacity away from every other function in the account. Must be a positive integer when set, since ``0`` would disable the webhook entirely. Default: undefined (no reservation; the webhook draws from the shared pool)
869
+ '''
870
+ if __debug__:
871
+ type_hints = cached_type_hints(_typecheckingstub__f9ccf90cd58a24eaea8e05c002c729a7c0b56c6afdd04d8cd485938eed8facc5)
872
+ check_type(argname="argument scope_", value=scope_, expected_type=type_hints["scope_"])
873
+ check_type(argname="argument id", value=id, expected_type=type_hints["id"])
874
+ props = GithubMicrovmRunnersProps(
875
+ github=github,
876
+ scope=scope,
877
+ additional_regions=additional_regions,
878
+ console_logs=console_logs,
879
+ dead_letter_retention=dead_letter_retention,
880
+ emit_metrics=emit_metrics,
881
+ encryption_key=encryption_key,
882
+ idle_runner_grace_seconds=idle_runner_grace_seconds,
883
+ image_logs=image_logs,
884
+ janitor_interval=janitor_interval,
885
+ keep_image_versions=keep_image_versions,
886
+ lambda_memory_size=lambda_memory_size,
887
+ log_retention=log_retention,
888
+ max_concurrent_vms=max_concurrent_vms,
889
+ max_job_duration=max_job_duration,
890
+ max_receive_count=max_receive_count,
891
+ network=network,
892
+ permissions_boundary=permissions_boundary,
893
+ point_in_time_recovery=point_in_time_recovery,
894
+ recover_stuck_launches=recover_stuck_launches,
895
+ removal_policy=removal_policy,
896
+ vm_execution_role=vm_execution_role,
897
+ warm_pool_interval=warm_pool_interval,
898
+ webhook=webhook,
899
+ webhook_reserved_concurrency=webhook_reserved_concurrency,
900
+ )
901
+
902
+ jsii.create(self.__class__, self, [scope_, id, props])
903
+
904
+ @jsii.member(jsii_name="addRunnerClass")
905
+ def add_runner_class(
906
+ self,
907
+ label: builtins.str,
908
+ *,
909
+ size: "MicrovmSize",
910
+ idle_policy: typing.Optional[typing.Union["MicrovmIdlePolicy", typing.Dict[builtins.str, typing.Any]]] = None,
911
+ image: typing.Optional["RunnerImage"] = None,
912
+ warm_pool_size: typing.Optional[jsii.Number] = None,
913
+ ) -> "RunnerClass":
914
+ '''Register a runner class: the ``runs-on`` label a workflow targets, paired with the VM size, and optionally the image, that jobs carrying that label run on.
915
+
916
+ Each class builds its own image. A runner set needs at least one
917
+ class, and one that reaches synth with none fails.
918
+
919
+ Classes can be registered at any point before synth. Everything that
920
+ depends on the full set of them — which labels the webhook accepts, which
921
+ image each label launches, and the janitor's access to each class's image
922
+ — is resolved once, after the last call.
923
+
924
+ Setting ``warmPoolSize`` on a class keeps that many pre-booted VMs ready for
925
+ it. The first class to do so creates the warm-pool handler and its
926
+ schedule, which every later warm class then shares.
927
+
928
+ :param label: the ``runs-on`` label workflows use to target this class.
929
+ :param size: VM memory floor for this class.
930
+ :param idle_policy: Auto-suspend and auto-resume policy for this class's cold-launched VMs. Mutually exclusive with ``warmPoolSize`` on the same class, since both drive the VM's suspended state; setting both throws at ``addRunnerClass`` time. Default: undefined (no idle policy; the platform never auto-suspends)
931
+ :param image: Image this class builds from. Default: RunnerImage.fromOptions()
932
+ :param warm_pool_size: How many pre-booted, suspended VMs to keep ready for this class. A job that matches this class resumes one of them instead of cold-launching a new VM, and falls back to a cold launch when none is available. This is a count, not a flag: ``warmPoolSize: 3`` keeps three VMs ready. The runner set refills the pool on the ``warmPoolInterval`` schedule. Default: undefined (no warm pool; every job cold-launches)
933
+
934
+ :return:
935
+
936
+ the registered ``RunnerClass``, carrying its label, size,
937
+ image pipeline, and image ARN.
938
+ '''
939
+ if __debug__:
940
+ type_hints = cached_type_hints(_typecheckingstub__48e77fc4d43555e4d6ac3d9e5b13de1cbbb1ef807cfc4ce1e0688ac170b863d1)
941
+ check_type(argname="argument label", value=label, expected_type=type_hints["label"])
942
+ props = RunnerClassProps(
943
+ size=size,
944
+ idle_policy=idle_policy,
945
+ image=image,
946
+ warm_pool_size=warm_pool_size,
947
+ )
948
+
949
+ return typing.cast("RunnerClass", jsii.invoke(self, "addRunnerClass", [label, props]))
950
+
951
+ @jsii.member(jsii_name="runnerClass")
952
+ def runner_class(self, label: builtins.str) -> "RunnerClass":
953
+ '''The registered ``RunnerClass`` carrying ``label``.
954
+
955
+ Throws when no class
956
+ with that label has been registered.
957
+
958
+ :param label: the ``runs-on`` label the class was registered under.
959
+ '''
960
+ if __debug__:
961
+ type_hints = cached_type_hints(_typecheckingstub__e74a92121e3eaec69c4009cc027a299a12257a68eccc613318213d92545265f4)
962
+ check_type(argname="argument label", value=label, expected_type=type_hints["label"])
963
+ return typing.cast("RunnerClass", jsii.invoke(self, "runnerClass", [label]))
964
+
965
+ @builtins.property
966
+ @jsii.member(jsii_name="deadLetterQueue")
967
+ def dead_letter_queue(self) -> "_aws_cdk_aws_sqs_ceddda9d.IQueue":
968
+ '''Dead-letter queue holding job-queue messages that ran out of redrives.'''
969
+ return typing.cast("_aws_cdk_aws_sqs_ceddda9d.IQueue", jsii.get(self, "deadLetterQueue"))
970
+
971
+ @builtins.property
972
+ @jsii.member(jsii_name="defaultImageArn")
973
+ def default_image_arn(self) -> builtins.str:
974
+ '''The image a job whose labels match no registered runner class launches on: the class labelled ``microvm`` if one is registered, otherwise the first class registered.
975
+
976
+ Runner classes can be added right up until synth, so this
977
+ is a token that resolves once the set of them is final.
978
+ '''
979
+ return typing.cast(builtins.str, jsii.get(self, "defaultImageArn"))
980
+
981
+ @builtins.property
982
+ @jsii.member(jsii_name="janitorFunction")
983
+ def janitor_function(self) -> "_aws_cdk_aws_lambda_ceddda9d.IFunction":
984
+ '''The janitor Lambda, which runs the scheduled sweep.'''
985
+ return typing.cast("_aws_cdk_aws_lambda_ceddda9d.IFunction", jsii.get(self, "janitorFunction"))
986
+
987
+ @builtins.property
988
+ @jsii.member(jsii_name="jobQueue")
989
+ def job_queue(self) -> "_aws_cdk_aws_sqs_ceddda9d.IQueue":
990
+ '''Queue carrying launch and terminate intents from the webhook handler to the launcher.'''
991
+ return typing.cast("_aws_cdk_aws_sqs_ceddda9d.IQueue", jsii.get(self, "jobQueue"))
992
+
993
+ @builtins.property
994
+ @jsii.member(jsii_name="launcherFunction")
995
+ def launcher_function(self) -> "_aws_cdk_aws_lambda_ceddda9d.IFunction":
996
+ '''The launcher Lambda, which reads the job queue and starts and terminates MicroVMs.'''
997
+ return typing.cast("_aws_cdk_aws_lambda_ceddda9d.IFunction", jsii.get(self, "launcherFunction"))
998
+
999
+ @builtins.property
1000
+ @jsii.member(jsii_name="metrics")
1001
+ def metrics(self) -> "GithubMicrovmRunnersMetrics":
1002
+ '''This runner set's CloudWatch metrics and the ready-made alarms over them.'''
1003
+ return typing.cast("GithubMicrovmRunnersMetrics", jsii.get(self, "metrics"))
1004
+
1005
+ @builtins.property
1006
+ @jsii.member(jsii_name="runnerClasses")
1007
+ def runner_classes(self) -> typing.List["RunnerClass"]:
1008
+ '''Every runner class registered through ``addRunnerClass``, in the order they were registered.
1009
+
1010
+ It is empty until the first class is added, and a
1011
+ runner set that reaches synth with none fails. Each call returns a copy, so
1012
+ changing the returned array does not change the runner set.
1013
+ '''
1014
+ return typing.cast(typing.List["RunnerClass"], jsii.get(self, "runnerClasses"))
1015
+
1016
+ @builtins.property
1017
+ @jsii.member(jsii_name="runnerTable")
1018
+ def runner_table(self) -> "_aws_cdk_aws_dynamodb_ceddda9d.ITable":
1019
+ '''DynamoDB table mapping each runner's name to its MicroVM, and holding the janitor's record of which VMs it already suspects.'''
1020
+ return typing.cast("_aws_cdk_aws_dynamodb_ceddda9d.ITable", jsii.get(self, "runnerTable"))
1021
+
1022
+ @builtins.property
1023
+ @jsii.member(jsii_name="setupCommand")
1024
+ def setup_command(self) -> builtins.str:
1025
+ '''The command that creates this runner set's GitHub App and writes its three secrets.
1026
+
1027
+ It carries the scope, the stack name, and the region this runner
1028
+ set was built with, and is pinned to the version of this library that
1029
+ produced it, so the helper and the construct agree about secret names and
1030
+ stack outputs.
1031
+
1032
+ Surface it as a stack output and the deploy ends by printing the line to
1033
+ paste. On a stack built without an explicit ``env``, the region is a token
1034
+ that reads as ``${Token[AWS.Region.N]}`` here and resolves to the real region
1035
+ in the deployed output.
1036
+
1037
+ Example::
1038
+
1039
+ cdk.CfnOutput(stack, "SetupCommand", value=runners.setup_command)
1040
+ '''
1041
+ return typing.cast(builtins.str, jsii.get(self, "setupCommand"))
1042
+
1043
+ @builtins.property
1044
+ @jsii.member(jsii_name="webhookFunction")
1045
+ def webhook_function(self) -> "_aws_cdk_aws_lambda_ceddda9d.IFunction":
1046
+ '''The webhook Lambda, which GitHub's deliveries reach through ``webhookUrl``.'''
1047
+ return typing.cast("_aws_cdk_aws_lambda_ceddda9d.IFunction", jsii.get(self, "webhookFunction"))
1048
+
1049
+ @builtins.property
1050
+ @jsii.member(jsii_name="webhookUrl")
1051
+ def webhook_url(self) -> builtins.str:
1052
+ '''The webhook handler's public Function URL, which is the payload URL to configure on the GitHub App or webhook.'''
1053
+ return typing.cast(builtins.str, jsii.get(self, "webhookUrl"))
1054
+
1055
+ @builtins.property
1056
+ @jsii.member(jsii_name="vmConsoleLogGroup")
1057
+ def vm_console_log_group(
1058
+ self,
1059
+ ) -> typing.Optional["_aws_cdk_aws_logs_ceddda9d.ILogGroup"]:
1060
+ '''Where a VM's runtime console goes when console capture is on: the group the construct created, or the one you supplied.
1061
+
1062
+ ``undefined`` when console capture is off.
1063
+ '''
1064
+ return typing.cast(typing.Optional["_aws_cdk_aws_logs_ceddda9d.ILogGroup"], jsii.get(self, "vmConsoleLogGroup"))
1065
+
1066
+ @builtins.property
1067
+ @jsii.member(jsii_name="vmExecutionRole")
1068
+ def vm_execution_role(self) -> typing.Optional["_aws_cdk_aws_iam_ceddda9d.IRole"]:
1069
+ '''The AWS identity launched MicroVMs run with, passed in as ``GithubMicrovmRunnersProps.vmExecutionRole``, or ``undefined`` when the VMs carry no AWS identity. Console capture runs on this role and requires it. A runner set identifies its own VMs by the image they booted from, not by this role.'''
1070
+ return typing.cast(typing.Optional["_aws_cdk_aws_iam_ceddda9d.IRole"], jsii.get(self, "vmExecutionRole"))
1071
+
1072
+ @builtins.property
1073
+ @jsii.member(jsii_name="warmPoolFunction")
1074
+ def warm_pool_function(
1075
+ self,
1076
+ ) -> typing.Optional["_aws_cdk_aws_lambda_ceddda9d.IFunction"]:
1077
+ '''The Lambda that refills the warm pool, or ``undefined`` when no registered runner class sets ``RunnerClassProps.warmPoolSize``. It is created by the first class that does, so a runner set with no warm class deploys neither this function nor its schedule.'''
1078
+ return typing.cast(typing.Optional["_aws_cdk_aws_lambda_ceddda9d.IFunction"], jsii.get(self, "warmPoolFunction"))
1079
+
1080
+
1081
+ class GithubMicrovmRunnersMetrics(
1082
+ metaclass=jsii.JSIIMeta,
1083
+ jsii_type="cdk-github-microvm-runners.GithubMicrovmRunnersMetrics",
1084
+ ):
1085
+ '''The CloudWatch metrics a runner set reports, and ready-made alarms over them. A ``GithubMicrovmRunners`` exposes its own as ``runners.metrics``.
1086
+
1087
+ The metrics live in the ``MicrovmRunners`` namespace, tagged with the runner
1088
+ set's id. The janitor reports one set of counters per sweep, the launcher
1089
+ one per launch, and the warm pool one per refill sweep. Launcher and
1090
+ warm-pool metrics are also tagged with the runner class the launch belongs
1091
+ to, which is why those accessors take a runner-class label. Every method
1092
+ here names one of those metrics, or the dead-letter queue's own SQS metric;
1093
+ the class carries no data of its own.
1094
+
1095
+ Everything except ``deadLetterQueueDepth`` reports only when
1096
+ ``GithubMicrovmRunnersProps.emitMetrics`` is on. The accessors return a
1097
+ ``Metric`` either way, so a dashboard can be built ahead of turning metrics
1098
+ on. The two alarms over those metrics throw at synth instead, rather than
1099
+ synthesizing an alarm that could never fire.
1100
+
1101
+ Example::
1102
+
1103
+ cw.Alarm(stack, "SweepErrors",
1104
+ metric=runners.metrics.errors(),
1105
+ threshold=1,
1106
+ evaluation_periods=1
1107
+ )
1108
+ '''
1109
+
1110
+ def __init__(
1111
+ self,
1112
+ runner_set_id: builtins.str,
1113
+ dead_letter_queue: "_aws_cdk_aws_sqs_ceddda9d.IQueue",
1114
+ emit_metrics: typing.Optional[builtins.bool] = None,
1115
+ ) -> None:
1116
+ '''
1117
+ :param runner_set_id: -
1118
+ :param dead_letter_queue: -
1119
+ :param emit_metrics: Whether the runner set reports the metrics this class names, which is ``GithubMicrovmRunnersProps.emitMetrics``. Every accessor except ``deadLetterQueueDepth`` depends on it, though they all return a ``Metric`` either way; only the two alarms over those metrics refuse to synthesize.
1120
+ '''
1121
+ if __debug__:
1122
+ type_hints = cached_type_hints(_typecheckingstub__8c3f47f3bf65ddc91d8c105e318179909d2860c56e34cc7b699066e3f7952170)
1123
+ check_type(argname="argument runner_set_id", value=runner_set_id, expected_type=type_hints["runner_set_id"])
1124
+ check_type(argname="argument dead_letter_queue", value=dead_letter_queue, expected_type=type_hints["dead_letter_queue"])
1125
+ check_type(argname="argument emit_metrics", value=emit_metrics, expected_type=type_hints["emit_metrics"])
1126
+ jsii.create(self.__class__, self, [runner_set_id, dead_letter_queue, emit_metrics])
1127
+
1128
+ @jsii.member(jsii_name="cancelledBeforeLaunch")
1129
+ def cancelled_before_launch(
1130
+ self,
1131
+ runner_class_label: builtins.str,
1132
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1133
+ '''Launches skipped because the job had already stopped waiting for a runner by the time the launch was processed — cancelled, or its run deleted.
1134
+
1135
+ No VM is booted for these, so a rising count is work avoided rather than
1136
+ work lost. It tracks how often jobs are cancelled while still queued,
1137
+ which is routine on a repository using concurrency groups: every re-push
1138
+ cancels the run it superseded. A count that dwarfs ``ColdBoot`` suggests the
1139
+ workflows feeding this runner set are cancelled more often than they
1140
+ finish, which is usually a question about their triggers rather than
1141
+ about the runner set.
1142
+
1143
+ :param runner_class_label: -
1144
+
1145
+ Example::
1146
+
1147
+ cw.Alarm(stack, "MostlyCancelled",
1148
+ metric=runners.metrics.cancelled_before_launch("microvm"),
1149
+ threshold=50,
1150
+ evaluation_periods=3
1151
+ )
1152
+ '''
1153
+ if __debug__:
1154
+ type_hints = cached_type_hints(_typecheckingstub__5f2f67ec6dfaab44c26a53e7bc3669af5ba74fc2c75f8dcd8afb795779d8ddfa)
1155
+ check_type(argname="argument runner_class_label", value=runner_class_label, expected_type=type_hints["runner_class_label"])
1156
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "cancelledBeforeLaunch", [runner_class_label]))
1157
+
1158
+ @jsii.member(jsii_name="capacityRejected")
1159
+ def capacity_rejected(
1160
+ self,
1161
+ runner_class_label: builtins.str,
1162
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1163
+ '''Launches the MicroVM service rejected for capacity.
1164
+
1165
+ This is the runner
1166
+ set's quota signal: a value that stays above zero means jobs are queueing
1167
+ behind a MicroVM quota, or behind ``maxConcurrentVms``, rather than running,
1168
+ and each rejected launch spends one of its ``maxReceiveCount`` redrives on
1169
+ the way to the dead-letter queue. See docs/service-quotas.md.
1170
+
1171
+ :param runner_class_label: -
1172
+ '''
1173
+ if __debug__:
1174
+ type_hints = cached_type_hints(_typecheckingstub__36d476c29eaf47f8fca7a5205fb5caa5a9749233fb462a093763a6a3200429b4)
1175
+ check_type(argname="argument runner_class_label", value=runner_class_label, expected_type=type_hints["runner_class_label"])
1176
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "capacityRejected", [runner_class_label]))
1177
+
1178
+ @jsii.member(jsii_name="coldBoot")
1179
+ def cold_boot(
1180
+ self,
1181
+ runner_class_label: builtins.str,
1182
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1183
+ '''Launches served by booting a new VM, because no warm VM was available or the class keeps no warm pool.
1184
+
1185
+ :param runner_class_label: -
1186
+ '''
1187
+ if __debug__:
1188
+ type_hints = cached_type_hints(_typecheckingstub__baf385f806ff6b0b2ae70b9d811654097d3297503bd8bdbafe09f5d9ed879629)
1189
+ check_type(argname="argument runner_class_label", value=runner_class_label, expected_type=type_hints["runner_class_label"])
1190
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "coldBoot", [runner_class_label]))
1191
+
1192
+ @jsii.member(jsii_name="coldSpinUpMs")
1193
+ def cold_spin_up_ms(
1194
+ self,
1195
+ runner_class_label: builtins.str,
1196
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1197
+ '''Milliseconds to spin up a cold launch: starting the VM, waiting for it to boot, and pushing the runner's registration.
1198
+
1199
+ Reported as an average rather than a sum.
1200
+
1201
+ :param runner_class_label: -
1202
+ '''
1203
+ if __debug__:
1204
+ type_hints = cached_type_hints(_typecheckingstub__75224a1d56fc14f38e94a3639da5e4ae455f069c6e477bb8fa010333e5c6ae4f)
1205
+ check_type(argname="argument runner_class_label", value=runner_class_label, expected_type=type_hints["runner_class_label"])
1206
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "coldSpinUpMs", [runner_class_label]))
1207
+
1208
+ @jsii.member(jsii_name="deadLetterQueueDepth")
1209
+ def dead_letter_queue_depth(self) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1210
+ '''Messages sitting in the dead-letter queue: a launch or terminate intent SQS gave up redriving.'''
1211
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "deadLetterQueueDepth", []))
1212
+
1213
+ @jsii.member(jsii_name="deadLetterQueueNotEmptyAlarm")
1214
+ def dead_letter_queue_not_empty_alarm(
1215
+ self,
1216
+ scope: "_constructs_77d1e7e8.Construct",
1217
+ *,
1218
+ evaluation_periods: typing.Optional[jsii.Number] = None,
1219
+ period: typing.Optional["_aws_cdk_ceddda9d.Duration"] = None,
1220
+ threshold: typing.Optional[jsii.Number] = None,
1221
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Alarm":
1222
+ '''Alarm when the dead-letter queue is not empty, meaning SQS gave up redriving a launch or terminate intent.
1223
+
1224
+ A runner set that is keeping up
1225
+ holds this at 0, so any sustained depth means jobs are being dropped,
1226
+ unless ``recoverStuckLaunches`` is draining them. It fires on one message
1227
+ over a single 5-minute period; pass ``RunnerAlarmOptions`` to change
1228
+ that, and ``alarm.addAlarmAction()`` to route it.
1229
+
1230
+ This is the one alarm here that works without
1231
+ ``GithubMicrovmRunnersProps.emitMetrics``, because it watches the
1232
+ dead-letter queue's own SQS metric rather than one the handlers report.
1233
+
1234
+ :param scope: -
1235
+ :param evaluation_periods: Consecutive breaching periods before the alarm fires. Default: 1 (3 for the stuck-launch alarm)
1236
+ :param period: Aggregation period for the metric. Default: Duration.minutes(5)
1237
+ :param threshold: Value at or above which the alarm fires. Default: 1
1238
+
1239
+ :default: threshold 1, 1 evaluation period, 5-minute period
1240
+ '''
1241
+ if __debug__:
1242
+ type_hints = cached_type_hints(_typecheckingstub__aca740a7276ae0a556b6b45300b0252ac7d8074b4dd0ab26c6b38fba7adb7eaf)
1243
+ check_type(argname="argument scope", value=scope, expected_type=type_hints["scope"])
1244
+ options = RunnerAlarmOptions(
1245
+ evaluation_periods=evaluation_periods, period=period, threshold=threshold
1246
+ )
1247
+
1248
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Alarm", jsii.invoke(self, "deadLetterQueueNotEmptyAlarm", [scope, options]))
1249
+
1250
+ @jsii.member(jsii_name="errors")
1251
+ def errors(self) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1252
+ '''Janitor sweep count: failures on individual VMs, rows, or image versions during a sweep.
1253
+
1254
+ The sweep isolates each one and still completes.
1255
+ '''
1256
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "errors", []))
1257
+
1258
+ @jsii.member(jsii_name="imageVersionsPruned")
1259
+ def image_versions_pruned(self) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1260
+ '''Janitor sweep count: inactive MicroVM image versions pruned past ``keepImageVersions``.'''
1261
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "imageVersionsPruned", []))
1262
+
1263
+ @jsii.member(jsii_name="lifetimeKills")
1264
+ def lifetime_kills(self) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1265
+ '''Janitor sweep count: VMs terminated for having run longer than ``maxJobDuration`` plus the platform's own grace.'''
1266
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "lifetimeKills", []))
1267
+
1268
+ @jsii.member(jsii_name="orphansReaped")
1269
+ def orphans_reaped(self) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1270
+ '''Janitor sweep count: running VMs that belong to this runner set but have no row in the runner table, reaped once a second sweep has seen the same VM unaccounted for.'''
1271
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "orphansReaped", []))
1272
+
1273
+ @jsii.member(jsii_name="poolCurrent")
1274
+ def pool_current(
1275
+ self,
1276
+ runner_class_label: builtins.str,
1277
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1278
+ '''Warm VMs suspended and available for this class as of the last warm-pool sweep.
1279
+
1280
+ :param runner_class_label: -
1281
+ '''
1282
+ if __debug__:
1283
+ type_hints = cached_type_hints(_typecheckingstub__6153c9a068c30f370bb352b26cc502516866238676a7d8139fc2dd0736dff46a)
1284
+ check_type(argname="argument runner_class_label", value=runner_class_label, expected_type=type_hints["runner_class_label"])
1285
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "poolCurrent", [runner_class_label]))
1286
+
1287
+ @jsii.member(jsii_name="poolLaunched")
1288
+ def pool_launched(
1289
+ self,
1290
+ runner_class_label: builtins.str,
1291
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1292
+ '''Warm VMs the last warm-pool sweep launched to reach ``warmPoolSize``.
1293
+
1294
+ :param runner_class_label: -
1295
+ '''
1296
+ if __debug__:
1297
+ type_hints = cached_type_hints(_typecheckingstub__767e219d8c25f069fd8964ea100816a20869884628a2606dfd2fb3e63b857a8e)
1298
+ check_type(argname="argument runner_class_label", value=runner_class_label, expected_type=type_hints["runner_class_label"])
1299
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "poolLaunched", [runner_class_label]))
1300
+
1301
+ @jsii.member(jsii_name="poolLaunchFailed")
1302
+ def pool_launch_failed(
1303
+ self,
1304
+ runner_class_label: builtins.str,
1305
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1306
+ '''Warm-VM launches a warm-pool sweep attempted and failed.
1307
+
1308
+ A value that stays above zero means the pool is not reaching ``warmPoolSize``, so jobs keep booting new VMs instead of resuming warm ones.
1309
+
1310
+ :param runner_class_label: -
1311
+ '''
1312
+ if __debug__:
1313
+ type_hints = cached_type_hints(_typecheckingstub__6513962ff7d13ce627041f61f3e7a3b13688b30e0cb4c5bfbc63f79b8a32b595)
1314
+ check_type(argname="argument runner_class_label", value=runner_class_label, expected_type=type_hints["runner_class_label"])
1315
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "poolLaunchFailed", [runner_class_label]))
1316
+
1317
+ @jsii.member(jsii_name="poolTarget")
1318
+ def pool_target(
1319
+ self,
1320
+ runner_class_label: builtins.str,
1321
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1322
+ '''This class's ``warmPoolSize``, as the last warm-pool sweep read it.
1323
+
1324
+ :param runner_class_label: -
1325
+ '''
1326
+ if __debug__:
1327
+ type_hints = cached_type_hints(_typecheckingstub__1ba8a40c62efe6c3a58896ea614244a8e6823dd3c7db51567ffbadd71766bd13)
1328
+ check_type(argname="argument runner_class_label", value=runner_class_label, expected_type=type_hints["runner_class_label"])
1329
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "poolTarget", [runner_class_label]))
1330
+
1331
+ @jsii.member(jsii_name="stuckClaimsRelaunched")
1332
+ def stuck_claims_relaunched(self) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1333
+ '''Janitor sweep count: launches that were claimed but never served, re-launched from the orphaned claim.
1334
+
1335
+ This is 0 unless ``recoverStuckLaunches`` is on.
1336
+ '''
1337
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "stuckClaimsRelaunched", []))
1338
+
1339
+ @jsii.member(jsii_name="stuckLaunchesRecovered")
1340
+ def stuck_launches_recovered(self) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1341
+ '''Janitor sweep count: dead-lettered launches re-driven onto the job queue, which is 0 unless ``recoverStuckLaunches`` is on.
1342
+
1343
+ A value that stays high means launches are failing for some reason other than a GitHub outage.
1344
+ '''
1345
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "stuckLaunchesRecovered", []))
1346
+
1347
+ @jsii.member(jsii_name="stuckLaunchesRecoveredAlarm")
1348
+ def stuck_launches_recovered_alarm(
1349
+ self,
1350
+ scope: "_constructs_77d1e7e8.Construct",
1351
+ *,
1352
+ evaluation_periods: typing.Optional[jsii.Number] = None,
1353
+ period: typing.Optional["_aws_cdk_ceddda9d.Duration"] = None,
1354
+ threshold: typing.Optional[jsii.Number] = None,
1355
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Alarm":
1356
+ '''Alarm on stuck-launch recoveries, the dead-lettered launches the janitor re-drove, which only happens with ``recoverStuckLaunches`` on.
1357
+
1358
+ Recoveries
1359
+ that keep coming mean launches are failing for some reason other than a
1360
+ GitHub outage. It fires on one recovery in each of three consecutive
1361
+ 5-minute periods, which rides out a real outage; pass
1362
+ ``RunnerAlarmOptions`` to change that.
1363
+
1364
+ Requires ``GithubMicrovmRunnersProps.emitMetrics``, and throws at synth
1365
+ without it.
1366
+
1367
+ :param scope: -
1368
+ :param evaluation_periods: Consecutive breaching periods before the alarm fires. Default: 1 (3 for the stuck-launch alarm)
1369
+ :param period: Aggregation period for the metric. Default: Duration.minutes(5)
1370
+ :param threshold: Value at or above which the alarm fires. Default: 1
1371
+
1372
+ :default: threshold 1, 3 evaluation periods, 5-minute period
1373
+ '''
1374
+ if __debug__:
1375
+ type_hints = cached_type_hints(_typecheckingstub__e1b4b6b8be54f5289f47b9d302cea17d405e67837d60873b6a6536a2a23457e9)
1376
+ check_type(argname="argument scope", value=scope, expected_type=type_hints["scope"])
1377
+ options = RunnerAlarmOptions(
1378
+ evaluation_periods=evaluation_periods, period=period, threshold=threshold
1379
+ )
1380
+
1381
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Alarm", jsii.invoke(self, "stuckLaunchesRecoveredAlarm", [scope, options]))
1382
+
1383
+ @jsii.member(jsii_name="stuckRunnersReaped")
1384
+ def stuck_runners_reaped(self) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1385
+ '''Janitor sweep count: runners that registered with GitHub and then sat idle past ``idleRunnerGraceSeconds``, reaped once a second sweep has seen them the same way.'''
1386
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "stuckRunnersReaped", []))
1387
+
1388
+ @jsii.member(jsii_name="suspectsCleared")
1389
+ def suspects_cleared(self) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1390
+ '''Janitor sweep count: VMs an earlier sweep had marked as suspect, cleared because this sweep found them accounted for or working again.'''
1391
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "suspectsCleared", []))
1392
+
1393
+ @jsii.member(jsii_name="sweepErrorsAlarm")
1394
+ def sweep_errors_alarm(
1395
+ self,
1396
+ scope: "_constructs_77d1e7e8.Construct",
1397
+ *,
1398
+ evaluation_periods: typing.Optional[jsii.Number] = None,
1399
+ period: typing.Optional["_aws_cdk_ceddda9d.Duration"] = None,
1400
+ threshold: typing.Optional[jsii.Number] = None,
1401
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Alarm":
1402
+ '''Alarm on janitor sweep errors, the per-item failures a sweep isolates and continues past.
1403
+
1404
+ A value that stays above zero means the runner set is
1405
+ failing to reconcile — VMs left running, runners left unreaped — even
1406
+ though each sweep completes. It fires on one error over a single 5-minute
1407
+ period; pass ``RunnerAlarmOptions`` to change that.
1408
+
1409
+ Requires ``GithubMicrovmRunnersProps.emitMetrics``, and throws at synth
1410
+ without it.
1411
+
1412
+ :param scope: -
1413
+ :param evaluation_periods: Consecutive breaching periods before the alarm fires. Default: 1 (3 for the stuck-launch alarm)
1414
+ :param period: Aggregation period for the metric. Default: Duration.minutes(5)
1415
+ :param threshold: Value at or above which the alarm fires. Default: 1
1416
+
1417
+ :default: threshold 1, 1 evaluation period, 5-minute period
1418
+ '''
1419
+ if __debug__:
1420
+ type_hints = cached_type_hints(_typecheckingstub__971f59e7dc4d5b4245765d41b7dcc4075c4dd91870a15df6b9f345ee393cdc61)
1421
+ check_type(argname="argument scope", value=scope, expected_type=type_hints["scope"])
1422
+ options = RunnerAlarmOptions(
1423
+ evaluation_periods=evaluation_periods, period=period, threshold=threshold
1424
+ )
1425
+
1426
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Alarm", jsii.invoke(self, "sweepErrorsAlarm", [scope, options]))
1427
+
1428
+ @jsii.member(jsii_name="tableRowsCleaned")
1429
+ def table_rows_cleaned(self) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1430
+ '''Janitor sweep count: runner table rows deleted, either because the VM they name is confirmed gone or because a real row superseded an orphaned one.'''
1431
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "tableRowsCleaned", []))
1432
+
1433
+ @jsii.member(jsii_name="warmHit")
1434
+ def warm_hit(
1435
+ self,
1436
+ runner_class_label: builtins.str,
1437
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1438
+ '''Launches served from the warm pool: a pre-booted VM claimed and resumed rather than a new one launched.
1439
+
1440
+ :param runner_class_label: -
1441
+ '''
1442
+ if __debug__:
1443
+ type_hints = cached_type_hints(_typecheckingstub__01d47d24112989967339ef8a2bc09fe442210a13ccc3d3fd9583327ebc550f4f)
1444
+ check_type(argname="argument runner_class_label", value=runner_class_label, expected_type=type_hints["runner_class_label"])
1445
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "warmHit", [runner_class_label]))
1446
+
1447
+ @jsii.member(jsii_name="warmSpinUpMs")
1448
+ def warm_spin_up_ms(
1449
+ self,
1450
+ runner_class_label: builtins.str,
1451
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1452
+ '''Milliseconds to spin up a warm launch: claiming the VM, resuming it, and pushing the runner's registration.
1453
+
1454
+ Reported as an average rather than a sum.
1455
+
1456
+ :param runner_class_label: -
1457
+ '''
1458
+ if __debug__:
1459
+ type_hints = cached_type_hints(_typecheckingstub__9b66518a0eb395295f3c9719dcc545a363c50d0d387fe70bdcedc10be787b17a)
1460
+ check_type(argname="argument runner_class_label", value=runner_class_label, expected_type=type_hints["runner_class_label"])
1461
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "warmSpinUpMs", [runner_class_label]))
1462
+
1463
+ @jsii.member(jsii_name="warmThrottled")
1464
+ def warm_throttled(
1465
+ self,
1466
+ runner_class_label: builtins.str,
1467
+ ) -> "_aws_cdk_aws_cloudwatch_ceddda9d.Metric":
1468
+ '''Warm-pool claims that were throttled and fell back to booting a new VM.
1469
+
1470
+ The same launch can also count under ``ColdBoot`` or ``CapacityRejected``.
1471
+
1472
+ :param runner_class_label: -
1473
+ '''
1474
+ if __debug__:
1475
+ type_hints = cached_type_hints(_typecheckingstub__28779afe2a8160e3a792fe0b6661c1c177712ed22d8ddf991f39b625eaa303d6)
1476
+ check_type(argname="argument runner_class_label", value=runner_class_label, expected_type=type_hints["runner_class_label"])
1477
+ return typing.cast("_aws_cdk_aws_cloudwatch_ceddda9d.Metric", jsii.invoke(self, "warmThrottled", [runner_class_label]))
1478
+
1479
+
1480
+ @jsii.data_type(
1481
+ jsii_type="cdk-github-microvm-runners.GithubMicrovmRunnersProps",
1482
+ jsii_struct_bases=[],
1483
+ name_mapping={
1484
+ "github": "github",
1485
+ "scope": "scope",
1486
+ "additional_regions": "additionalRegions",
1487
+ "console_logs": "consoleLogs",
1488
+ "dead_letter_retention": "deadLetterRetention",
1489
+ "emit_metrics": "emitMetrics",
1490
+ "encryption_key": "encryptionKey",
1491
+ "idle_runner_grace_seconds": "idleRunnerGraceSeconds",
1492
+ "image_logs": "imageLogs",
1493
+ "janitor_interval": "janitorInterval",
1494
+ "keep_image_versions": "keepImageVersions",
1495
+ "lambda_memory_size": "lambdaMemorySize",
1496
+ "log_retention": "logRetention",
1497
+ "max_concurrent_vms": "maxConcurrentVms",
1498
+ "max_job_duration": "maxJobDuration",
1499
+ "max_receive_count": "maxReceiveCount",
1500
+ "network": "network",
1501
+ "permissions_boundary": "permissionsBoundary",
1502
+ "point_in_time_recovery": "pointInTimeRecovery",
1503
+ "recover_stuck_launches": "recoverStuckLaunches",
1504
+ "removal_policy": "removalPolicy",
1505
+ "vm_execution_role": "vmExecutionRole",
1506
+ "warm_pool_interval": "warmPoolInterval",
1507
+ "webhook": "webhook",
1508
+ "webhook_reserved_concurrency": "webhookReservedConcurrency",
1509
+ },
1510
+ )
1511
+ class GithubMicrovmRunnersProps:
1512
+ def __init__(
1513
+ self,
1514
+ *,
1515
+ github: "GithubAuth",
1516
+ scope: "RunnerScope",
1517
+ additional_regions: typing.Optional[typing.Sequence[builtins.str]] = None,
1518
+ console_logs: typing.Optional["ConsoleLogs"] = None,
1519
+ dead_letter_retention: typing.Optional["_aws_cdk_ceddda9d.Duration"] = None,
1520
+ emit_metrics: typing.Optional[builtins.bool] = None,
1521
+ encryption_key: typing.Optional["_aws_cdk_aws_kms_ceddda9d.IKey"] = None,
1522
+ idle_runner_grace_seconds: typing.Optional[jsii.Number] = None,
1523
+ image_logs: typing.Optional["ImageLogs"] = None,
1524
+ janitor_interval: typing.Optional["_aws_cdk_ceddda9d.Duration"] = None,
1525
+ keep_image_versions: typing.Optional[jsii.Number] = None,
1526
+ lambda_memory_size: typing.Optional[jsii.Number] = None,
1527
+ log_retention: typing.Optional["_aws_cdk_aws_logs_ceddda9d.RetentionDays"] = None,
1528
+ max_concurrent_vms: typing.Optional[jsii.Number] = None,
1529
+ max_job_duration: typing.Optional["_aws_cdk_ceddda9d.Duration"] = None,
1530
+ max_receive_count: typing.Optional[jsii.Number] = None,
1531
+ network: typing.Optional["RunnerNetwork"] = None,
1532
+ permissions_boundary: typing.Optional["_aws_cdk_aws_iam_ceddda9d.IManagedPolicy"] = None,
1533
+ point_in_time_recovery: typing.Optional[builtins.bool] = None,
1534
+ recover_stuck_launches: typing.Optional[builtins.bool] = None,
1535
+ removal_policy: typing.Optional["_aws_cdk_ceddda9d.RemovalPolicy"] = None,
1536
+ vm_execution_role: typing.Optional["_aws_cdk_aws_iam_ceddda9d.IRole"] = None,
1537
+ warm_pool_interval: typing.Optional["_aws_cdk_ceddda9d.Duration"] = None,
1538
+ webhook: typing.Optional["WebhookEndpoint"] = None,
1539
+ webhook_reserved_concurrency: typing.Optional[jsii.Number] = None,
1540
+ ) -> None:
1541
+ '''Props for ``GithubMicrovmRunners``.
1542
+
1543
+ :param github: How the runner set authenticates to GitHub, as an App or with a personal access token. This also carries the webhook secret.
1544
+ :param scope: Which GitHub scope, an organization or a list of repositories, registered runners are visible to.
1545
+ :param additional_regions: Additional regions to accept as Lambda MicroVMs regions, beyond the ones this library already knows about. Deploying into a region on neither list fails at synth. Default: [] (only the regions this library knows about)
1546
+ :param console_logs: Where a VM's runtime console goes: everything it prints while it boots, runs the runner agent, and runs the job. ``ConsoleLogs.enabled()`` has the construct create the group and expose it as ``vmConsoleLogGroup``, and ``ConsoleLogs.enabled(logGroup)`` uses one you control. The platform writes these logs with the VM's own role, so this requires ``vmExecutionRole``; see ``ConsoleLogs`` for what that role means for job code. Independent of ``imageLogs``. Default: undefined (no runtime console capture)
1547
+ :param dead_letter_retention: How long the dead-letter queue retains a failed launch or terminate intent. SQS allows up to 14 days, which is also how long ``recoverStuckLaunches`` has to re-drive a message before SQS drops it. Default: Duration.days(4) (SQS default)
1548
+ :param emit_metrics: Report this runner set's CloudWatch custom metrics. Those are the janitor's per-sweep counters, the launcher's per-launch outcomes and spin-up timings, and the warm pool's fill numbers — everything ``GithubMicrovmRunnersMetrics`` names. With this off the handlers report none of them. CloudWatch bills custom metrics per metric per month, and this runner set's bill is not a fixed number: the launcher and warm-pool metrics carry a runner-class dimension, so each one becomes a separate billable metric per registered runner class. The two alarms backed by these metrics, ``sweepErrorsAlarm`` and ``stuckLaunchesRecoveredAlarm``, throw at synth unless this is on, since the metric they watch would never report. ``deadLetterQueueNotEmptyAlarm`` watches an SQS metric and works either way. The metric accessors on ``GithubMicrovmRunnersMetrics`` return a ``Metric`` regardless, so a dashboard can be built ahead of turning metrics on. Default: false (no metrics emitted)
1549
+ :param encryption_key: Customer-managed KMS key for this runner set's data at rest: the DynamoDB runner table, the SQS job queue and dead-letter queue, and any log group this construct creates. Log groups you bring yourself, and the GitHub secrets you pass in, keep their own keys. Default: undefined (AWS-managed keys)
1550
+ :param idle_runner_grace_seconds: How many seconds a registered runner may sit idle before the janitor's two-strike sweep treats it as stuck. Default: 600
1551
+ :param image_logs: Where build-time image logs go: the Docker build layers and the ready-probe banner from each image build. ``ImageLogs.enabled()`` sends them to the platform's own CloudWatch group, and ``ImageLogs.enabled(logGroup)`` to a group whose retention and KMS key you control. These are written by the image build role rather than by a VM, so they need no VM execution role. Independent of ``consoleLogs``. Default: undefined (no image logs)
1552
+ :param janitor_interval: How often the janitor sweep runs. Default: Duration.minutes(5)
1553
+ :param keep_image_versions: How many MicroVM image versions to keep per runner class. The janitor prunes inactive versions past this count. Default: 5
1554
+ :param lambda_memory_size: Memory, in MiB, for the handler Lambdas: the webhook, the launcher, the janitor, and the warm pool. The janitor's sweep scans the runner table and reconciles every running VM, so it is the handler most sensitive to this on a busy runner set. Default: 128
1555
+ :param log_retention: Retention for the CloudWatch log groups this construct creates: the handler Lambda log groups and, when console capture is on, the VM console group. Default: logs.RetentionDays.TWO_WEEKS
1556
+ :param max_concurrent_vms: Maximum number of MicroVMs this runner set runs at once. Default: 10
1557
+ :param max_job_duration: How long a job may run before its MicroVM is terminated. The VM is killed five minutes after this value, not at it. The runner set asks the platform for ``maxJobDuration + 5 minutes``, so that a job which reaches its own limit is stopped by the runner — which reports the timeout to GitHub and lets the VM come down cleanly — rather than by the platform removing the machine underneath it. Treat the five minutes as headroom for that shutdown rather than as extra running time. Default: Duration.hours(6)
1558
+ :param max_receive_count: How many times a launch intent is redriven before it dead-letters. A runner set already at ``maxConcurrentVms`` redrives capacity-rejected launches through this same budget, so on a runner set that regularly runs at capacity this count is how long a queued job waits before its launch is dropped. See docs/service-quotas.md. Default: 20
1559
+ :param network: How launched MicroVMs and image builds reach the network. Default: RunnerNetwork.internetEgress()
1560
+ :param permissions_boundary: Permissions boundary applied to every IAM role this construct creates: the handler execution roles, the per-class image build roles, and the network-connector operator role. It is applied once at construct scope, so roles created later also carry it — the warm-pool handler's role, and the build role of any runner class registered after construction. Default: undefined (no boundary)
1561
+ :param point_in_time_recovery: Turn on DynamoDB point-in-time recovery for the runner table. Default: false
1562
+ :param recover_stuck_launches: Recover launches that dead-lettered while GitHub Actions was down. Each janitor sweep re-drives dead-lettered launch messages back onto the job queue, but only for jobs GitHub still reports as queued; a launch whose job has since completed or been cancelled is discarded rather than booting a VM for work nobody is waiting on. Recovery happens once an outage ends, since GitHub dispatches no jobs while it is down. The janitor counts each recovered launch under the ``stuckLaunchesRecovered`` metric, which reports when ``emitMetrics`` is on. Default: false
1563
+ :param removal_policy: Removal policy for this runner set's stateful resources: the runner table and any log group the construct creates. The table holds correlation data for VMs that are currently running, all of which the janitor can rebuild from the MicroVM and GitHub APIs. Default: RemovalPolicy.DESTROY
1564
+ :param vm_execution_role: An AWS identity for this runner set's runner VMs. By default the VMs carry no AWS identity at all: the runner agent talks outbound to GitHub, the just-in-time registration is pushed to the VM over a platform-authenticated channel, and a job that needs AWS assumes its own role through GitHub OIDC. With a role attached, the MicroVM's instance metadata service serves that role's credentials to arbitrary job code, so every job running on this runner set can do whatever the role can do. ``consoleLogs`` requires a role, because the platform writes a VM's console output using it. Default: undefined (the VMs carry no AWS identity)
1565
+ :param warm_pool_interval: How often the warm-pool sweep refills pre-booted VMs. It applies only to runner classes that set ``RunnerClassProps.warmPoolSize``, and the warm-pool handler and its schedule are only created once such a class is registered. A runner set with no warm class never runs this sweep, and never reads this value. Default: Duration.minutes(2)
1566
+ :param webhook: How the webhook handler is exposed to GitHub. Default: WebhookEndpoint.functionUrl()
1567
+ :param webhook_reserved_concurrency: Reserved concurrency for the webhook Lambda, which caps how many webhook deliveries the runner set processes at once. Reserved concurrency is carved out of the account's shared pool of unreserved concurrency, so a runner set that sets it takes that capacity away from every other function in the account. Must be a positive integer when set, since ``0`` would disable the webhook entirely. Default: undefined (no reservation; the webhook draws from the shared pool)
1568
+ '''
1569
+ if __debug__:
1570
+ type_hints = cached_type_hints(_typecheckingstub__17269a525bc0ad3b7797a5131db455fcbc4ced673bbd2895bc90ea581a540971)
1571
+ check_type(argname="argument github", value=github, expected_type=type_hints["github"])
1572
+ check_type(argname="argument scope", value=scope, expected_type=type_hints["scope"])
1573
+ check_type(argname="argument additional_regions", value=additional_regions, expected_type=type_hints["additional_regions"])
1574
+ check_type(argname="argument console_logs", value=console_logs, expected_type=type_hints["console_logs"])
1575
+ check_type(argname="argument dead_letter_retention", value=dead_letter_retention, expected_type=type_hints["dead_letter_retention"])
1576
+ check_type(argname="argument emit_metrics", value=emit_metrics, expected_type=type_hints["emit_metrics"])
1577
+ check_type(argname="argument encryption_key", value=encryption_key, expected_type=type_hints["encryption_key"])
1578
+ check_type(argname="argument idle_runner_grace_seconds", value=idle_runner_grace_seconds, expected_type=type_hints["idle_runner_grace_seconds"])
1579
+ check_type(argname="argument image_logs", value=image_logs, expected_type=type_hints["image_logs"])
1580
+ check_type(argname="argument janitor_interval", value=janitor_interval, expected_type=type_hints["janitor_interval"])
1581
+ check_type(argname="argument keep_image_versions", value=keep_image_versions, expected_type=type_hints["keep_image_versions"])
1582
+ check_type(argname="argument lambda_memory_size", value=lambda_memory_size, expected_type=type_hints["lambda_memory_size"])
1583
+ check_type(argname="argument log_retention", value=log_retention, expected_type=type_hints["log_retention"])
1584
+ check_type(argname="argument max_concurrent_vms", value=max_concurrent_vms, expected_type=type_hints["max_concurrent_vms"])
1585
+ check_type(argname="argument max_job_duration", value=max_job_duration, expected_type=type_hints["max_job_duration"])
1586
+ check_type(argname="argument max_receive_count", value=max_receive_count, expected_type=type_hints["max_receive_count"])
1587
+ check_type(argname="argument network", value=network, expected_type=type_hints["network"])
1588
+ check_type(argname="argument permissions_boundary", value=permissions_boundary, expected_type=type_hints["permissions_boundary"])
1589
+ check_type(argname="argument point_in_time_recovery", value=point_in_time_recovery, expected_type=type_hints["point_in_time_recovery"])
1590
+ check_type(argname="argument recover_stuck_launches", value=recover_stuck_launches, expected_type=type_hints["recover_stuck_launches"])
1591
+ check_type(argname="argument removal_policy", value=removal_policy, expected_type=type_hints["removal_policy"])
1592
+ check_type(argname="argument vm_execution_role", value=vm_execution_role, expected_type=type_hints["vm_execution_role"])
1593
+ check_type(argname="argument warm_pool_interval", value=warm_pool_interval, expected_type=type_hints["warm_pool_interval"])
1594
+ check_type(argname="argument webhook", value=webhook, expected_type=type_hints["webhook"])
1595
+ check_type(argname="argument webhook_reserved_concurrency", value=webhook_reserved_concurrency, expected_type=type_hints["webhook_reserved_concurrency"])
1596
+ self._values: typing.Dict[builtins.str, typing.Any] = {
1597
+ "github": github,
1598
+ "scope": scope,
1599
+ }
1600
+ if additional_regions is not None:
1601
+ self._values["additional_regions"] = additional_regions
1602
+ if console_logs is not None:
1603
+ self._values["console_logs"] = console_logs
1604
+ if dead_letter_retention is not None:
1605
+ self._values["dead_letter_retention"] = dead_letter_retention
1606
+ if emit_metrics is not None:
1607
+ self._values["emit_metrics"] = emit_metrics
1608
+ if encryption_key is not None:
1609
+ self._values["encryption_key"] = encryption_key
1610
+ if idle_runner_grace_seconds is not None:
1611
+ self._values["idle_runner_grace_seconds"] = idle_runner_grace_seconds
1612
+ if image_logs is not None:
1613
+ self._values["image_logs"] = image_logs
1614
+ if janitor_interval is not None:
1615
+ self._values["janitor_interval"] = janitor_interval
1616
+ if keep_image_versions is not None:
1617
+ self._values["keep_image_versions"] = keep_image_versions
1618
+ if lambda_memory_size is not None:
1619
+ self._values["lambda_memory_size"] = lambda_memory_size
1620
+ if log_retention is not None:
1621
+ self._values["log_retention"] = log_retention
1622
+ if max_concurrent_vms is not None:
1623
+ self._values["max_concurrent_vms"] = max_concurrent_vms
1624
+ if max_job_duration is not None:
1625
+ self._values["max_job_duration"] = max_job_duration
1626
+ if max_receive_count is not None:
1627
+ self._values["max_receive_count"] = max_receive_count
1628
+ if network is not None:
1629
+ self._values["network"] = network
1630
+ if permissions_boundary is not None:
1631
+ self._values["permissions_boundary"] = permissions_boundary
1632
+ if point_in_time_recovery is not None:
1633
+ self._values["point_in_time_recovery"] = point_in_time_recovery
1634
+ if recover_stuck_launches is not None:
1635
+ self._values["recover_stuck_launches"] = recover_stuck_launches
1636
+ if removal_policy is not None:
1637
+ self._values["removal_policy"] = removal_policy
1638
+ if vm_execution_role is not None:
1639
+ self._values["vm_execution_role"] = vm_execution_role
1640
+ if warm_pool_interval is not None:
1641
+ self._values["warm_pool_interval"] = warm_pool_interval
1642
+ if webhook is not None:
1643
+ self._values["webhook"] = webhook
1644
+ if webhook_reserved_concurrency is not None:
1645
+ self._values["webhook_reserved_concurrency"] = webhook_reserved_concurrency
1646
+
1647
+ @builtins.property
1648
+ def github(self) -> "GithubAuth":
1649
+ '''How the runner set authenticates to GitHub, as an App or with a personal access token.
1650
+
1651
+ This also carries the webhook secret.
1652
+ '''
1653
+ result = self._values.get("github")
1654
+ assert result is not None, "Required property 'github' is missing"
1655
+ return typing.cast("GithubAuth", result)
1656
+
1657
+ @builtins.property
1658
+ def scope(self) -> "RunnerScope":
1659
+ '''Which GitHub scope, an organization or a list of repositories, registered runners are visible to.'''
1660
+ result = self._values.get("scope")
1661
+ assert result is not None, "Required property 'scope' is missing"
1662
+ return typing.cast("RunnerScope", result)
1663
+
1664
+ @builtins.property
1665
+ def additional_regions(self) -> typing.Optional[typing.List[builtins.str]]:
1666
+ '''Additional regions to accept as Lambda MicroVMs regions, beyond the ones this library already knows about.
1667
+
1668
+ Deploying into a region on neither list
1669
+ fails at synth.
1670
+
1671
+ :default: [] (only the regions this library knows about)
1672
+ '''
1673
+ result = self._values.get("additional_regions")
1674
+ return typing.cast(typing.Optional[typing.List[builtins.str]], result)
1675
+
1676
+ @builtins.property
1677
+ def console_logs(self) -> typing.Optional["ConsoleLogs"]:
1678
+ '''Where a VM's runtime console goes: everything it prints while it boots, runs the runner agent, and runs the job.
1679
+
1680
+ ``ConsoleLogs.enabled()`` has the
1681
+ construct create the group and expose it as ``vmConsoleLogGroup``, and
1682
+ ``ConsoleLogs.enabled(logGroup)`` uses one you control. The platform writes
1683
+ these logs with the VM's own role, so this requires ``vmExecutionRole``; see
1684
+ ``ConsoleLogs`` for what that role means for job code. Independent of
1685
+ ``imageLogs``.
1686
+
1687
+ :default: undefined (no runtime console capture)
1688
+ '''
1689
+ result = self._values.get("console_logs")
1690
+ return typing.cast(typing.Optional["ConsoleLogs"], result)
1691
+
1692
+ @builtins.property
1693
+ def dead_letter_retention(self) -> typing.Optional["_aws_cdk_ceddda9d.Duration"]:
1694
+ '''How long the dead-letter queue retains a failed launch or terminate intent.
1695
+
1696
+ SQS allows up to 14 days, which is also how long
1697
+ ``recoverStuckLaunches`` has to re-drive a message before SQS drops it.
1698
+
1699
+ :default: Duration.days(4) (SQS default)
1700
+ '''
1701
+ result = self._values.get("dead_letter_retention")
1702
+ return typing.cast(typing.Optional["_aws_cdk_ceddda9d.Duration"], result)
1703
+
1704
+ @builtins.property
1705
+ def emit_metrics(self) -> typing.Optional[builtins.bool]:
1706
+ '''Report this runner set's CloudWatch custom metrics.
1707
+
1708
+ Those are the
1709
+ janitor's per-sweep counters, the launcher's per-launch outcomes and
1710
+ spin-up timings, and the warm pool's fill numbers — everything
1711
+ ``GithubMicrovmRunnersMetrics`` names. With this off the handlers report
1712
+ none of them.
1713
+
1714
+ CloudWatch bills custom metrics per metric per month, and this runner set's
1715
+ bill is not a fixed number: the launcher and warm-pool metrics carry a
1716
+ runner-class dimension, so each one becomes a separate billable metric per
1717
+ registered runner class.
1718
+
1719
+ The two alarms backed by these metrics, ``sweepErrorsAlarm`` and
1720
+ ``stuckLaunchesRecoveredAlarm``, throw at synth unless this is on, since the
1721
+ metric they watch would never report. ``deadLetterQueueNotEmptyAlarm``
1722
+ watches an SQS metric and works either way. The metric accessors on
1723
+ ``GithubMicrovmRunnersMetrics`` return a ``Metric`` regardless, so a
1724
+ dashboard can be built ahead of turning metrics on.
1725
+
1726
+ :default: false (no metrics emitted)
1727
+ '''
1728
+ result = self._values.get("emit_metrics")
1729
+ return typing.cast(typing.Optional[builtins.bool], result)
1730
+
1731
+ @builtins.property
1732
+ def encryption_key(self) -> typing.Optional["_aws_cdk_aws_kms_ceddda9d.IKey"]:
1733
+ '''Customer-managed KMS key for this runner set's data at rest: the DynamoDB runner table, the SQS job queue and dead-letter queue, and any log group this construct creates.
1734
+
1735
+ Log groups you bring yourself, and the GitHub
1736
+ secrets you pass in, keep their own keys.
1737
+
1738
+ :default: undefined (AWS-managed keys)
1739
+ '''
1740
+ result = self._values.get("encryption_key")
1741
+ return typing.cast(typing.Optional["_aws_cdk_aws_kms_ceddda9d.IKey"], result)
1742
+
1743
+ @builtins.property
1744
+ def idle_runner_grace_seconds(self) -> typing.Optional[jsii.Number]:
1745
+ '''How many seconds a registered runner may sit idle before the janitor's two-strike sweep treats it as stuck.
1746
+
1747
+ :default: 600
1748
+ '''
1749
+ result = self._values.get("idle_runner_grace_seconds")
1750
+ return typing.cast(typing.Optional[jsii.Number], result)
1751
+
1752
+ @builtins.property
1753
+ def image_logs(self) -> typing.Optional["ImageLogs"]:
1754
+ '''Where build-time image logs go: the Docker build layers and the ready-probe banner from each image build.
1755
+
1756
+ ``ImageLogs.enabled()`` sends them
1757
+ to the platform's own CloudWatch group, and ``ImageLogs.enabled(logGroup)``
1758
+ to a group whose retention and KMS key you control. These are written by
1759
+ the image build role rather than by a VM, so they need no VM execution
1760
+ role. Independent of ``consoleLogs``.
1761
+
1762
+ :default: undefined (no image logs)
1763
+ '''
1764
+ result = self._values.get("image_logs")
1765
+ return typing.cast(typing.Optional["ImageLogs"], result)
1766
+
1767
+ @builtins.property
1768
+ def janitor_interval(self) -> typing.Optional["_aws_cdk_ceddda9d.Duration"]:
1769
+ '''How often the janitor sweep runs.
1770
+
1771
+ :default: Duration.minutes(5)
1772
+ '''
1773
+ result = self._values.get("janitor_interval")
1774
+ return typing.cast(typing.Optional["_aws_cdk_ceddda9d.Duration"], result)
1775
+
1776
+ @builtins.property
1777
+ def keep_image_versions(self) -> typing.Optional[jsii.Number]:
1778
+ '''How many MicroVM image versions to keep per runner class.
1779
+
1780
+ The janitor prunes inactive versions past this count.
1781
+
1782
+ :default: 5
1783
+ '''
1784
+ result = self._values.get("keep_image_versions")
1785
+ return typing.cast(typing.Optional[jsii.Number], result)
1786
+
1787
+ @builtins.property
1788
+ def lambda_memory_size(self) -> typing.Optional[jsii.Number]:
1789
+ '''Memory, in MiB, for the handler Lambdas: the webhook, the launcher, the janitor, and the warm pool.
1790
+
1791
+ The janitor's sweep scans the runner table and
1792
+ reconciles every running VM, so it is the handler most sensitive to this
1793
+ on a busy runner set.
1794
+
1795
+ :default: 128
1796
+ '''
1797
+ result = self._values.get("lambda_memory_size")
1798
+ return typing.cast(typing.Optional[jsii.Number], result)
1799
+
1800
+ @builtins.property
1801
+ def log_retention(
1802
+ self,
1803
+ ) -> typing.Optional["_aws_cdk_aws_logs_ceddda9d.RetentionDays"]:
1804
+ '''Retention for the CloudWatch log groups this construct creates: the handler Lambda log groups and, when console capture is on, the VM console group.
1805
+
1806
+ :default: logs.RetentionDays.TWO_WEEKS
1807
+ '''
1808
+ result = self._values.get("log_retention")
1809
+ return typing.cast(typing.Optional["_aws_cdk_aws_logs_ceddda9d.RetentionDays"], result)
1810
+
1811
+ @builtins.property
1812
+ def max_concurrent_vms(self) -> typing.Optional[jsii.Number]:
1813
+ '''Maximum number of MicroVMs this runner set runs at once.
1814
+
1815
+ :default: 10
1816
+ '''
1817
+ result = self._values.get("max_concurrent_vms")
1818
+ return typing.cast(typing.Optional[jsii.Number], result)
1819
+
1820
+ @builtins.property
1821
+ def max_job_duration(self) -> typing.Optional["_aws_cdk_ceddda9d.Duration"]:
1822
+ '''How long a job may run before its MicroVM is terminated.
1823
+
1824
+ The VM is killed five minutes after this value, not at it. The runner set
1825
+ asks the platform for ``maxJobDuration + 5 minutes``, so that a job which
1826
+ reaches its own limit is stopped by the runner — which reports the timeout
1827
+ to GitHub and lets the VM come down cleanly — rather than by the platform
1828
+ removing the machine underneath it. Treat the five minutes as headroom for
1829
+ that shutdown rather than as extra running time.
1830
+
1831
+ :default: Duration.hours(6)
1832
+ '''
1833
+ result = self._values.get("max_job_duration")
1834
+ return typing.cast(typing.Optional["_aws_cdk_ceddda9d.Duration"], result)
1835
+
1836
+ @builtins.property
1837
+ def max_receive_count(self) -> typing.Optional[jsii.Number]:
1838
+ '''How many times a launch intent is redriven before it dead-letters.
1839
+
1840
+ A
1841
+ runner set already at ``maxConcurrentVms`` redrives capacity-rejected
1842
+ launches through this same budget, so on a runner set that regularly runs
1843
+ at capacity this count is how long a queued job waits before its launch is
1844
+ dropped. See docs/service-quotas.md.
1845
+
1846
+ :default: 20
1847
+ '''
1848
+ result = self._values.get("max_receive_count")
1849
+ return typing.cast(typing.Optional[jsii.Number], result)
1850
+
1851
+ @builtins.property
1852
+ def network(self) -> typing.Optional["RunnerNetwork"]:
1853
+ '''How launched MicroVMs and image builds reach the network.
1854
+
1855
+ :default: RunnerNetwork.internetEgress()
1856
+ '''
1857
+ result = self._values.get("network")
1858
+ return typing.cast(typing.Optional["RunnerNetwork"], result)
1859
+
1860
+ @builtins.property
1861
+ def permissions_boundary(
1862
+ self,
1863
+ ) -> typing.Optional["_aws_cdk_aws_iam_ceddda9d.IManagedPolicy"]:
1864
+ '''Permissions boundary applied to every IAM role this construct creates: the handler execution roles, the per-class image build roles, and the network-connector operator role.
1865
+
1866
+ It is applied once at construct scope, so
1867
+ roles created later also carry it — the warm-pool handler's role, and the
1868
+ build role of any runner class registered after construction.
1869
+
1870
+ :default: undefined (no boundary)
1871
+ '''
1872
+ result = self._values.get("permissions_boundary")
1873
+ return typing.cast(typing.Optional["_aws_cdk_aws_iam_ceddda9d.IManagedPolicy"], result)
1874
+
1875
+ @builtins.property
1876
+ def point_in_time_recovery(self) -> typing.Optional[builtins.bool]:
1877
+ '''Turn on DynamoDB point-in-time recovery for the runner table.
1878
+
1879
+ :default: false
1880
+ '''
1881
+ result = self._values.get("point_in_time_recovery")
1882
+ return typing.cast(typing.Optional[builtins.bool], result)
1883
+
1884
+ @builtins.property
1885
+ def recover_stuck_launches(self) -> typing.Optional[builtins.bool]:
1886
+ '''Recover launches that dead-lettered while GitHub Actions was down.
1887
+
1888
+ Each
1889
+ janitor sweep re-drives dead-lettered launch messages back onto the job
1890
+ queue, but only for jobs GitHub still reports as queued; a launch whose job
1891
+ has since completed or been cancelled is discarded rather than booting a
1892
+ VM for work nobody is waiting on. Recovery happens once an outage ends,
1893
+ since GitHub dispatches no jobs while it is down. The janitor counts each
1894
+ recovered launch under the ``stuckLaunchesRecovered`` metric, which reports
1895
+ when ``emitMetrics`` is on.
1896
+
1897
+ :default: false
1898
+ '''
1899
+ result = self._values.get("recover_stuck_launches")
1900
+ return typing.cast(typing.Optional[builtins.bool], result)
1901
+
1902
+ @builtins.property
1903
+ def removal_policy(self) -> typing.Optional["_aws_cdk_ceddda9d.RemovalPolicy"]:
1904
+ '''Removal policy for this runner set's stateful resources: the runner table and any log group the construct creates.
1905
+
1906
+ The table holds correlation data
1907
+ for VMs that are currently running, all of which the janitor can rebuild
1908
+ from the MicroVM and GitHub APIs.
1909
+
1910
+ :default: RemovalPolicy.DESTROY
1911
+ '''
1912
+ result = self._values.get("removal_policy")
1913
+ return typing.cast(typing.Optional["_aws_cdk_ceddda9d.RemovalPolicy"], result)
1914
+
1915
+ @builtins.property
1916
+ def vm_execution_role(self) -> typing.Optional["_aws_cdk_aws_iam_ceddda9d.IRole"]:
1917
+ '''An AWS identity for this runner set's runner VMs.
1918
+
1919
+ By default the VMs carry
1920
+ no AWS identity at all: the runner agent talks outbound to GitHub, the
1921
+ just-in-time registration is pushed to the VM over a platform-authenticated
1922
+ channel, and a job that needs AWS assumes its own role through GitHub OIDC.
1923
+
1924
+ With a role attached, the MicroVM's instance metadata service serves that
1925
+ role's credentials to arbitrary job code, so every job running on this
1926
+ runner set can do whatever the role can do. ``consoleLogs`` requires a role,
1927
+ because the platform writes a VM's console output using it.
1928
+
1929
+ :default: undefined (the VMs carry no AWS identity)
1930
+ '''
1931
+ result = self._values.get("vm_execution_role")
1932
+ return typing.cast(typing.Optional["_aws_cdk_aws_iam_ceddda9d.IRole"], result)
1933
+
1934
+ @builtins.property
1935
+ def warm_pool_interval(self) -> typing.Optional["_aws_cdk_ceddda9d.Duration"]:
1936
+ '''How often the warm-pool sweep refills pre-booted VMs.
1937
+
1938
+ It applies only to
1939
+ runner classes that set ``RunnerClassProps.warmPoolSize``, and the warm-pool
1940
+ handler and its schedule are only created once such a class is registered.
1941
+ A runner set with no warm class never runs this sweep, and never reads
1942
+ this value.
1943
+
1944
+ :default: Duration.minutes(2)
1945
+ '''
1946
+ result = self._values.get("warm_pool_interval")
1947
+ return typing.cast(typing.Optional["_aws_cdk_ceddda9d.Duration"], result)
1948
+
1949
+ @builtins.property
1950
+ def webhook(self) -> typing.Optional["WebhookEndpoint"]:
1951
+ '''How the webhook handler is exposed to GitHub.
1952
+
1953
+ :default: WebhookEndpoint.functionUrl()
1954
+ '''
1955
+ result = self._values.get("webhook")
1956
+ return typing.cast(typing.Optional["WebhookEndpoint"], result)
1957
+
1958
+ @builtins.property
1959
+ def webhook_reserved_concurrency(self) -> typing.Optional[jsii.Number]:
1960
+ '''Reserved concurrency for the webhook Lambda, which caps how many webhook deliveries the runner set processes at once.
1961
+
1962
+ Reserved concurrency is
1963
+ carved out of the account's shared pool of unreserved concurrency, so a
1964
+ runner set that sets it takes that capacity away from every other function
1965
+ in the account. Must be a positive integer when set, since ``0`` would
1966
+ disable the webhook entirely.
1967
+
1968
+ :default: undefined (no reservation; the webhook draws from the shared pool)
1969
+ '''
1970
+ result = self._values.get("webhook_reserved_concurrency")
1971
+ return typing.cast(typing.Optional[jsii.Number], result)
1972
+
1973
+ def __eq__(self, rhs: typing.Any) -> builtins.bool:
1974
+ return isinstance(rhs, self.__class__) and rhs._values == self._values
1975
+
1976
+ def __ne__(self, rhs: typing.Any) -> builtins.bool:
1977
+ return not (rhs == self)
1978
+
1979
+ def __repr__(self) -> str:
1980
+ return "GithubMicrovmRunnersProps(%s)" % ", ".join(
1981
+ k + "=" + repr(v) for k, v in self._values.items()
1982
+ )
1983
+
1984
+
1985
+ @jsii.data_type(
1986
+ jsii_type="cdk-github-microvm-runners.GithubPatAuthProps",
1987
+ jsii_struct_bases=[],
1988
+ name_mapping={"token": "token", "webhook_secret": "webhookSecret"},
1989
+ )
1990
+ class GithubPatAuthProps:
1991
+ def __init__(
1992
+ self,
1993
+ *,
1994
+ token: "_aws_cdk_aws_secretsmanager_ceddda9d.ISecret",
1995
+ webhook_secret: "_aws_cdk_aws_secretsmanager_ceddda9d.ISecret",
1996
+ ) -> None:
1997
+ '''Props for ``GithubAuth.pat``.
1998
+
1999
+ :param token: Secret holding a GitHub personal access token.
2000
+ :param webhook_secret: Secret holding the webhook secret used to validate inbound deliveries.
2001
+ '''
2002
+ if __debug__:
2003
+ type_hints = cached_type_hints(_typecheckingstub__af35288b9c48eea9cffeb6e1080b684e480ae2227583407499a01041a243a072)
2004
+ check_type(argname="argument token", value=token, expected_type=type_hints["token"])
2005
+ check_type(argname="argument webhook_secret", value=webhook_secret, expected_type=type_hints["webhook_secret"])
2006
+ self._values: typing.Dict[builtins.str, typing.Any] = {
2007
+ "token": token,
2008
+ "webhook_secret": webhook_secret,
2009
+ }
2010
+
2011
+ @builtins.property
2012
+ def token(self) -> "_aws_cdk_aws_secretsmanager_ceddda9d.ISecret":
2013
+ '''Secret holding a GitHub personal access token.'''
2014
+ result = self._values.get("token")
2015
+ assert result is not None, "Required property 'token' is missing"
2016
+ return typing.cast("_aws_cdk_aws_secretsmanager_ceddda9d.ISecret", result)
2017
+
2018
+ @builtins.property
2019
+ def webhook_secret(self) -> "_aws_cdk_aws_secretsmanager_ceddda9d.ISecret":
2020
+ '''Secret holding the webhook secret used to validate inbound deliveries.'''
2021
+ result = self._values.get("webhook_secret")
2022
+ assert result is not None, "Required property 'webhook_secret' is missing"
2023
+ return typing.cast("_aws_cdk_aws_secretsmanager_ceddda9d.ISecret", result)
2024
+
2025
+ def __eq__(self, rhs: typing.Any) -> builtins.bool:
2026
+ return isinstance(rhs, self.__class__) and rhs._values == self._values
2027
+
2028
+ def __ne__(self, rhs: typing.Any) -> builtins.bool:
2029
+ return not (rhs == self)
2030
+
2031
+ def __repr__(self) -> str:
2032
+ return "GithubPatAuthProps(%s)" % ", ".join(
2033
+ k + "=" + repr(v) for k, v in self._values.items()
2034
+ )
2035
+
2036
+
2037
+ @jsii.data_type(
2038
+ jsii_type="cdk-github-microvm-runners.ImageAsset",
2039
+ jsii_struct_bases=[],
2040
+ name_mapping={"source": "source", "target": "target"},
2041
+ )
2042
+ class ImageAsset:
2043
+ def __init__(self, *, source: builtins.str, target: builtins.str) -> None:
2044
+ '''One extra file or directory baked into the image.
2045
+
2046
+ ``source`` is a path on the machine running ``cdk synth``. The image pipeline
2047
+ reads it off disk when it stages the Docker build context, and the rendered
2048
+ Dockerfile copies it to ``target`` inside the image.
2049
+
2050
+ :param source: Path (file or directory) on the build machine to copy into the image.
2051
+ :param target: Absolute path inside the image to copy ``source`` to.
2052
+ '''
2053
+ if __debug__:
2054
+ type_hints = cached_type_hints(_typecheckingstub__d7d3da112d28b428bdfaadffe313adf1b4f0e1018b1cbf217c7687e156912c49)
2055
+ check_type(argname="argument source", value=source, expected_type=type_hints["source"])
2056
+ check_type(argname="argument target", value=target, expected_type=type_hints["target"])
2057
+ self._values: typing.Dict[builtins.str, typing.Any] = {
2058
+ "source": source,
2059
+ "target": target,
2060
+ }
2061
+
2062
+ @builtins.property
2063
+ def source(self) -> builtins.str:
2064
+ '''Path (file or directory) on the build machine to copy into the image.'''
2065
+ result = self._values.get("source")
2066
+ assert result is not None, "Required property 'source' is missing"
2067
+ return typing.cast(builtins.str, result)
2068
+
2069
+ @builtins.property
2070
+ def target(self) -> builtins.str:
2071
+ '''Absolute path inside the image to copy ``source`` to.'''
2072
+ result = self._values.get("target")
2073
+ assert result is not None, "Required property 'target' is missing"
2074
+ return typing.cast(builtins.str, result)
2075
+
2076
+ def __eq__(self, rhs: typing.Any) -> builtins.bool:
2077
+ return isinstance(rhs, self.__class__) and rhs._values == self._values
2078
+
2079
+ def __ne__(self, rhs: typing.Any) -> builtins.bool:
2080
+ return not (rhs == self)
2081
+
2082
+ def __repr__(self) -> str:
2083
+ return "ImageAsset(%s)" % ", ".join(
2084
+ k + "=" + repr(v) for k, v in self._values.items()
2085
+ )
2086
+
2087
+
2088
+ class ImageLogs(
2089
+ metaclass=jsii.JSIIMeta,
2090
+ jsii_type="cdk-github-microvm-runners.ImageLogs",
2091
+ ):
2092
+ '''Build-time image logs for a runner set: the Docker build layers and the ready-probe banner an image emits while it is built.
2093
+
2094
+ Off unless you add it.
2095
+
2096
+ These logs are written while the image builds, by the image build role
2097
+ rather than by a VM, so image logging needs no VM execution role and puts no
2098
+ credentials on a runner. ``ConsoleLogs`` covers the runtime counterpart, which
2099
+ does need a role. The two are independent and can both be on.
2100
+
2101
+ Example::
2102
+
2103
+ GithubMicrovmRunners(stack, "Runners",
2104
+ github=github,
2105
+ scope=scope,
2106
+ image_logs=ImageLogs.enabled()
2107
+ )
2108
+ '''
2109
+
2110
+ @jsii.member(jsii_name="enabled")
2111
+ @builtins.classmethod
2112
+ def enabled(
2113
+ cls,
2114
+ log_group: typing.Optional["_aws_cdk_aws_logs_ceddda9d.ILogGroup"] = None,
2115
+ ) -> "ImageLogs":
2116
+ '''Send image-build logs to CloudWatch.
2117
+
2118
+ With no argument they go to the
2119
+ platform's own group (``/aws/lambda-microvms/…``). Pass an ``ILogGroup`` to
2120
+ send them to a group whose retention and KMS key you control.
2121
+
2122
+ :param log_group: destination group. Omitted, the platform's own group.
2123
+
2124
+ Example::
2125
+
2126
+ build_logs = ImageLogs.enabled(my_build_log_group)
2127
+ '''
2128
+ if __debug__:
2129
+ type_hints = cached_type_hints(_typecheckingstub__5c18afa4a05ee3b3cf8019fa8e3bf98a9dc803b4ff31600008a4916dce15cd6a)
2130
+ check_type(argname="argument log_group", value=log_group, expected_type=type_hints["log_group"])
2131
+ return typing.cast("ImageLogs", jsii.sinvoke(cls, "enabled", [log_group]))
2132
+
2133
+ @builtins.property
2134
+ @jsii.member(jsii_name="logGroup")
2135
+ def log_group(self) -> typing.Optional["_aws_cdk_aws_logs_ceddda9d.ILogGroup"]:
2136
+ '''The group build logs go to, when one was passed to ``ImageLogs.enabled()``. ``undefined`` means the platform's own group.'''
2137
+ return typing.cast(typing.Optional["_aws_cdk_aws_logs_ceddda9d.ILogGroup"], jsii.get(self, "logGroup"))
2138
+
2139
+
2140
+ class ImagePipeline(
2141
+ _constructs_77d1e7e8.Construct,
2142
+ metaclass=jsii.JSIIMeta,
2143
+ jsii_type="cdk-github-microvm-runners.ImagePipeline",
2144
+ ):
2145
+ '''The build behind one runner class's MicroVM image.
2146
+
2147
+ ``addRunnerClass`` creates
2148
+ one for each class it registers and returns it as
2149
+ ``RunnerClass.imagePipeline``, so this is a handle you read rather than a
2150
+ construct you instantiate.
2151
+
2152
+ It stages the class's Dockerfile and build context as a CDK asset, declares
2153
+ the ``AWS::Lambda::MicrovmImage`` resource that CloudFormation builds from it,
2154
+ and creates the IAM role that build runs as. Reading it is how you reach the
2155
+ built image's name and ARN, and the role the build runs as.
2156
+
2157
+ Example::
2158
+
2159
+ build_class = runners.add_runner_class("build",
2160
+ size=MicrovmSize.GB4,
2161
+ image=RunnerImage.from_options(system_packages=["jq"])
2162
+ )
2163
+
2164
+ cdk.CfnOutput(stack, "BuildImageName",
2165
+ value=build_class.image_pipeline.image_name
2166
+ )
2167
+ '''
2168
+
2169
+ def __init__(
2170
+ self,
2171
+ scope: "_constructs_77d1e7e8.Construct",
2172
+ id: builtins.str,
2173
+ *,
2174
+ image: "RunnerImage",
2175
+ network: "RunnerNetwork",
2176
+ runner_set_id: builtins.str,
2177
+ size: "MicrovmSize",
2178
+ base_image_version: typing.Optional[builtins.str] = None,
2179
+ image_logs: typing.Optional["ImageLogs"] = None,
2180
+ ready_timeout_seconds: typing.Optional[jsii.Number] = None,
2181
+ run_timeout_seconds: typing.Optional[jsii.Number] = None,
2182
+ ) -> None:
2183
+ '''
2184
+ :param scope: -
2185
+ :param id: -
2186
+ :param image: The runner image to build: ``RunnerImage.fromOptions()`` for a synthesized Dockerfile, ``RunnerImage.fromInline(text)`` for Dockerfile text, or ``RunnerImage.fromDockerfile(dir)`` for a Dockerfile and build context on disk.
2187
+ :param network: How the build and the VMs reach the network.
2188
+ :param runner_set_id: Identifier for the runner set this image belongs to. It is combined with the first 8 hex characters of the image's ``contentHash`` to form the image's name, so a content change publishes a new image and an unchanged one is a no-op. Must match ``^[a-zA-Z0-9-_]+$``, and must be 55 characters or fewer so that ``<runnerSetId>-<8-hex-chars>`` stays within the service's 64-character name limit.
2189
+ :param size: The size the built image runs at, which becomes its memory floor.
2190
+ :param base_image_version: Version of the managed ``al2023-1`` base image to build from. Default: '0'
2191
+ :param image_logs: Where the build's logs go. Left unset, the build emits no logs; ``ImageLogs.enabled()`` sends them to the platform's group, and ``ImageLogs.enabled(logGroup)`` to a group you supply.
2192
+ :param ready_timeout_seconds: Seconds the service waits for the in-VM agent's ``/ready`` hook before failing the image build. Default: 300
2193
+ :param run_timeout_seconds: Seconds the service waits for the in-VM agent's ``/run`` hook to accept a launch. Service maximum: 60. Default: 60
2194
+ '''
2195
+ if __debug__:
2196
+ type_hints = cached_type_hints(_typecheckingstub__38e8b0a65e40c9bfd130b4244eef3af238aac180d3c10606a513fef86e7aa169)
2197
+ check_type(argname="argument scope", value=scope, expected_type=type_hints["scope"])
2198
+ check_type(argname="argument id", value=id, expected_type=type_hints["id"])
2199
+ props = ImagePipelineProps(
2200
+ image=image,
2201
+ network=network,
2202
+ runner_set_id=runner_set_id,
2203
+ size=size,
2204
+ base_image_version=base_image_version,
2205
+ image_logs=image_logs,
2206
+ ready_timeout_seconds=ready_timeout_seconds,
2207
+ run_timeout_seconds=run_timeout_seconds,
2208
+ )
2209
+
2210
+ jsii.create(self.__class__, self, [scope, id, props])
2211
+
2212
+ @builtins.property
2213
+ @jsii.member(jsii_name="buildRole")
2214
+ def build_role(self) -> "_aws_cdk_aws_iam_ceddda9d.IRole":
2215
+ '''IAM role the image build runs as, able to read the staged build context and pull any private container base image.'''
2216
+ return typing.cast("_aws_cdk_aws_iam_ceddda9d.IRole", jsii.get(self, "buildRole"))
2217
+
2218
+ @builtins.property
2219
+ @jsii.member(jsii_name="imageArn")
2220
+ def image_arn(self) -> builtins.str:
2221
+ '''ARN of the built MicroVM image.'''
2222
+ return typing.cast(builtins.str, jsii.get(self, "imageArn"))
2223
+
2224
+ @builtins.property
2225
+ @jsii.member(jsii_name="imageName")
2226
+ def image_name(self) -> builtins.str:
2227
+ '''Name of the built MicroVM image, ``<runnerSetId>-<8 hex characters of the content hash>``.'''
2228
+ return typing.cast(builtins.str, jsii.get(self, "imageName"))
2229
+
2230
+ @builtins.property
2231
+ @jsii.member(jsii_name="imageResource")
2232
+ def image_resource(self) -> "_aws_cdk_aws_lambda_ceddda9d.CfnMicrovmImage":
2233
+ '''The underlying ``AWS::Lambda::MicrovmImage`` resource.'''
2234
+ return typing.cast("_aws_cdk_aws_lambda_ceddda9d.CfnMicrovmImage", jsii.get(self, "imageResource"))
2235
+
2236
+
2237
+ @jsii.data_type(
2238
+ jsii_type="cdk-github-microvm-runners.ImagePipelineProps",
2239
+ jsii_struct_bases=[],
2240
+ name_mapping={
2241
+ "image": "image",
2242
+ "network": "network",
2243
+ "runner_set_id": "runnerSetId",
2244
+ "size": "size",
2245
+ "base_image_version": "baseImageVersion",
2246
+ "image_logs": "imageLogs",
2247
+ "ready_timeout_seconds": "readyTimeoutSeconds",
2248
+ "run_timeout_seconds": "runTimeoutSeconds",
2249
+ },
2250
+ )
2251
+ class ImagePipelineProps:
2252
+ def __init__(
2253
+ self,
2254
+ *,
2255
+ image: "RunnerImage",
2256
+ network: "RunnerNetwork",
2257
+ runner_set_id: builtins.str,
2258
+ size: "MicrovmSize",
2259
+ base_image_version: typing.Optional[builtins.str] = None,
2260
+ image_logs: typing.Optional["ImageLogs"] = None,
2261
+ ready_timeout_seconds: typing.Optional[jsii.Number] = None,
2262
+ run_timeout_seconds: typing.Optional[jsii.Number] = None,
2263
+ ) -> None:
2264
+ '''Props for ``ImagePipeline``.
2265
+
2266
+ :param image: The runner image to build: ``RunnerImage.fromOptions()`` for a synthesized Dockerfile, ``RunnerImage.fromInline(text)`` for Dockerfile text, or ``RunnerImage.fromDockerfile(dir)`` for a Dockerfile and build context on disk.
2267
+ :param network: How the build and the VMs reach the network.
2268
+ :param runner_set_id: Identifier for the runner set this image belongs to. It is combined with the first 8 hex characters of the image's ``contentHash`` to form the image's name, so a content change publishes a new image and an unchanged one is a no-op. Must match ``^[a-zA-Z0-9-_]+$``, and must be 55 characters or fewer so that ``<runnerSetId>-<8-hex-chars>`` stays within the service's 64-character name limit.
2269
+ :param size: The size the built image runs at, which becomes its memory floor.
2270
+ :param base_image_version: Version of the managed ``al2023-1`` base image to build from. Default: '0'
2271
+ :param image_logs: Where the build's logs go. Left unset, the build emits no logs; ``ImageLogs.enabled()`` sends them to the platform's group, and ``ImageLogs.enabled(logGroup)`` to a group you supply.
2272
+ :param ready_timeout_seconds: Seconds the service waits for the in-VM agent's ``/ready`` hook before failing the image build. Default: 300
2273
+ :param run_timeout_seconds: Seconds the service waits for the in-VM agent's ``/run`` hook to accept a launch. Service maximum: 60. Default: 60
2274
+ '''
2275
+ if __debug__:
2276
+ type_hints = cached_type_hints(_typecheckingstub__c8c6d3e3f7a844305d6a7d687bb74edba13926e6cdb04020bf45116bbb7f2407)
2277
+ check_type(argname="argument image", value=image, expected_type=type_hints["image"])
2278
+ check_type(argname="argument network", value=network, expected_type=type_hints["network"])
2279
+ check_type(argname="argument runner_set_id", value=runner_set_id, expected_type=type_hints["runner_set_id"])
2280
+ check_type(argname="argument size", value=size, expected_type=type_hints["size"])
2281
+ check_type(argname="argument base_image_version", value=base_image_version, expected_type=type_hints["base_image_version"])
2282
+ check_type(argname="argument image_logs", value=image_logs, expected_type=type_hints["image_logs"])
2283
+ check_type(argname="argument ready_timeout_seconds", value=ready_timeout_seconds, expected_type=type_hints["ready_timeout_seconds"])
2284
+ check_type(argname="argument run_timeout_seconds", value=run_timeout_seconds, expected_type=type_hints["run_timeout_seconds"])
2285
+ self._values: typing.Dict[builtins.str, typing.Any] = {
2286
+ "image": image,
2287
+ "network": network,
2288
+ "runner_set_id": runner_set_id,
2289
+ "size": size,
2290
+ }
2291
+ if base_image_version is not None:
2292
+ self._values["base_image_version"] = base_image_version
2293
+ if image_logs is not None:
2294
+ self._values["image_logs"] = image_logs
2295
+ if ready_timeout_seconds is not None:
2296
+ self._values["ready_timeout_seconds"] = ready_timeout_seconds
2297
+ if run_timeout_seconds is not None:
2298
+ self._values["run_timeout_seconds"] = run_timeout_seconds
2299
+
2300
+ @builtins.property
2301
+ def image(self) -> "RunnerImage":
2302
+ '''The runner image to build: ``RunnerImage.fromOptions()`` for a synthesized Dockerfile, ``RunnerImage.fromInline(text)`` for Dockerfile text, or ``RunnerImage.fromDockerfile(dir)`` for a Dockerfile and build context on disk.'''
2303
+ result = self._values.get("image")
2304
+ assert result is not None, "Required property 'image' is missing"
2305
+ return typing.cast("RunnerImage", result)
2306
+
2307
+ @builtins.property
2308
+ def network(self) -> "RunnerNetwork":
2309
+ '''How the build and the VMs reach the network.'''
2310
+ result = self._values.get("network")
2311
+ assert result is not None, "Required property 'network' is missing"
2312
+ return typing.cast("RunnerNetwork", result)
2313
+
2314
+ @builtins.property
2315
+ def runner_set_id(self) -> builtins.str:
2316
+ '''Identifier for the runner set this image belongs to.
2317
+
2318
+ It is combined with
2319
+ the first 8 hex characters of the image's ``contentHash`` to form the
2320
+ image's name, so a content change publishes a new image and an unchanged
2321
+ one is a no-op. Must match ``^[a-zA-Z0-9-_]+$``, and must be 55 characters
2322
+ or fewer so that ``<runnerSetId>-<8-hex-chars>`` stays within the service's
2323
+ 64-character name limit.
2324
+ '''
2325
+ result = self._values.get("runner_set_id")
2326
+ assert result is not None, "Required property 'runner_set_id' is missing"
2327
+ return typing.cast(builtins.str, result)
2328
+
2329
+ @builtins.property
2330
+ def size(self) -> "MicrovmSize":
2331
+ '''The size the built image runs at, which becomes its memory floor.'''
2332
+ result = self._values.get("size")
2333
+ assert result is not None, "Required property 'size' is missing"
2334
+ return typing.cast("MicrovmSize", result)
2335
+
2336
+ @builtins.property
2337
+ def base_image_version(self) -> typing.Optional[builtins.str]:
2338
+ '''Version of the managed ``al2023-1`` base image to build from.
2339
+
2340
+ :default: '0'
2341
+ '''
2342
+ result = self._values.get("base_image_version")
2343
+ return typing.cast(typing.Optional[builtins.str], result)
2344
+
2345
+ @builtins.property
2346
+ def image_logs(self) -> typing.Optional["ImageLogs"]:
2347
+ '''Where the build's logs go.
2348
+
2349
+ Left unset, the build emits no logs; ``ImageLogs.enabled()`` sends them to the platform's group, and ``ImageLogs.enabled(logGroup)`` to a group you supply.
2350
+ '''
2351
+ result = self._values.get("image_logs")
2352
+ return typing.cast(typing.Optional["ImageLogs"], result)
2353
+
2354
+ @builtins.property
2355
+ def ready_timeout_seconds(self) -> typing.Optional[jsii.Number]:
2356
+ '''Seconds the service waits for the in-VM agent's ``/ready`` hook before failing the image build.
2357
+
2358
+ :default: 300
2359
+ '''
2360
+ result = self._values.get("ready_timeout_seconds")
2361
+ return typing.cast(typing.Optional[jsii.Number], result)
2362
+
2363
+ @builtins.property
2364
+ def run_timeout_seconds(self) -> typing.Optional[jsii.Number]:
2365
+ '''Seconds the service waits for the in-VM agent's ``/run`` hook to accept a launch.
2366
+
2367
+ Service maximum: 60.
2368
+
2369
+ :default: 60
2370
+ '''
2371
+ result = self._values.get("run_timeout_seconds")
2372
+ return typing.cast(typing.Optional[jsii.Number], result)
2373
+
2374
+ def __eq__(self, rhs: typing.Any) -> builtins.bool:
2375
+ return isinstance(rhs, self.__class__) and rhs._values == self._values
2376
+
2377
+ def __ne__(self, rhs: typing.Any) -> builtins.bool:
2378
+ return not (rhs == self)
2379
+
2380
+ def __repr__(self) -> str:
2381
+ return "ImagePipelineProps(%s)" % ", ".join(
2382
+ k + "=" + repr(v) for k, v in self._values.items()
2383
+ )
2384
+
2385
+
2386
+ @jsii.data_type(
2387
+ jsii_type="cdk-github-microvm-runners.MicrovmIdlePolicy",
2388
+ jsii_struct_bases=[],
2389
+ name_mapping={
2390
+ "max_idle_duration": "maxIdleDuration",
2391
+ "suspended_duration": "suspendedDuration",
2392
+ "auto_resume": "autoResume",
2393
+ },
2394
+ )
2395
+ class MicrovmIdlePolicy:
2396
+ def __init__(
2397
+ self,
2398
+ *,
2399
+ max_idle_duration: "_aws_cdk_ceddda9d.Duration",
2400
+ suspended_duration: "_aws_cdk_ceddda9d.Duration",
2401
+ auto_resume: typing.Optional[builtins.bool] = None,
2402
+ ) -> None:
2403
+ '''When the platform suspends and resumes a runner class's cold-launched VMs.
2404
+
2405
+ This mirrors the MicroVM service's own ``idlePolicy`` shape, expressed as
2406
+ ``Duration``s rather than raw seconds. Set it on a runner class through
2407
+ ``RunnerClassProps.idlePolicy``.
2408
+
2409
+ :param max_idle_duration: Idle time before the platform auto-suspends the VM.
2410
+ :param suspended_duration: How long a suspended VM is kept before the platform terminates it. Required. The MicroVM service rejects a launch whose idle policy omits this value, and it offers no value meaning "keep the suspended VM indefinitely", so every idle policy names a duration.
2411
+ :param auto_resume: Auto-resume the VM on activity. Default: false
2412
+ '''
2413
+ if __debug__:
2414
+ type_hints = cached_type_hints(_typecheckingstub__299a6933558be6e9144d105ecb8f71fdfa2731d8f7934fa33118eb063140eb10)
2415
+ check_type(argname="argument max_idle_duration", value=max_idle_duration, expected_type=type_hints["max_idle_duration"])
2416
+ check_type(argname="argument suspended_duration", value=suspended_duration, expected_type=type_hints["suspended_duration"])
2417
+ check_type(argname="argument auto_resume", value=auto_resume, expected_type=type_hints["auto_resume"])
2418
+ self._values: typing.Dict[builtins.str, typing.Any] = {
2419
+ "max_idle_duration": max_idle_duration,
2420
+ "suspended_duration": suspended_duration,
2421
+ }
2422
+ if auto_resume is not None:
2423
+ self._values["auto_resume"] = auto_resume
2424
+
2425
+ @builtins.property
2426
+ def max_idle_duration(self) -> "_aws_cdk_ceddda9d.Duration":
2427
+ '''Idle time before the platform auto-suspends the VM.'''
2428
+ result = self._values.get("max_idle_duration")
2429
+ assert result is not None, "Required property 'max_idle_duration' is missing"
2430
+ return typing.cast("_aws_cdk_ceddda9d.Duration", result)
2431
+
2432
+ @builtins.property
2433
+ def suspended_duration(self) -> "_aws_cdk_ceddda9d.Duration":
2434
+ '''How long a suspended VM is kept before the platform terminates it.
2435
+
2436
+ Required. The MicroVM service rejects a launch whose idle policy omits
2437
+ this value, and it offers no value meaning "keep the suspended VM
2438
+ indefinitely", so every idle policy names a duration.
2439
+ '''
2440
+ result = self._values.get("suspended_duration")
2441
+ assert result is not None, "Required property 'suspended_duration' is missing"
2442
+ return typing.cast("_aws_cdk_ceddda9d.Duration", result)
2443
+
2444
+ @builtins.property
2445
+ def auto_resume(self) -> typing.Optional[builtins.bool]:
2446
+ '''Auto-resume the VM on activity.
2447
+
2448
+ :default: false
2449
+ '''
2450
+ result = self._values.get("auto_resume")
2451
+ return typing.cast(typing.Optional[builtins.bool], result)
2452
+
2453
+ def __eq__(self, rhs: typing.Any) -> builtins.bool:
2454
+ return isinstance(rhs, self.__class__) and rhs._values == self._values
2455
+
2456
+ def __ne__(self, rhs: typing.Any) -> builtins.bool:
2457
+ return not (rhs == self)
2458
+
2459
+ def __repr__(self) -> str:
2460
+ return "MicrovmIdlePolicy(%s)" % ", ".join(
2461
+ k + "=" + repr(v) for k, v in self._values.items()
2462
+ )
2463
+
2464
+
2465
+ class MicrovmSize(
2466
+ metaclass=jsii.JSIIMeta,
2467
+ jsii_type="cdk-github-microvm-runners.MicrovmSize",
2468
+ ):
2469
+ '''The memory a MicroVM runs with.
2470
+
2471
+ Each runner class picks one preset, and that preset becomes the memory floor
2472
+ of the image the class builds. Pick from the static presets below; the
2473
+ constructor is private.
2474
+
2475
+ A preset is a **floor, not an allocation**. It is the minimum the image is
2476
+ built with, and the platform provisions above it — measured at roughly four
2477
+ times the request, so a class on ``GB1`` has been observed booting with about
2478
+ 4 GB and 2 vCPU, and one on ``GB4`` with about 16 GB and 8 vCPU. Two things
2479
+ follow: a workload usually fits a smaller preset than its memory figure
2480
+ suggests, and the account's memory quota is charged the measured allocation
2481
+ rather than the floor. The service quotas guide carries the arithmetic.
2482
+
2483
+ vCPU and disk follow from the preset and are not separately settable — the
2484
+ image resource takes a memory floor and nothing else.
2485
+
2486
+ Example::
2487
+
2488
+ runners.add_runner_class("microvm-8gb", size=MicrovmSize.GB8)
2489
+ '''
2490
+
2491
+ @jsii.python.classproperty
2492
+ @jsii.member(jsii_name="GB0_5")
2493
+ def GB0_5(cls) -> "MicrovmSize":
2494
+ '''Memory floor of 0.5 GB.'''
2495
+ return typing.cast("MicrovmSize", jsii.sget(cls, "GB0_5"))
2496
+
2497
+ @jsii.python.classproperty
2498
+ @jsii.member(jsii_name="GB1")
2499
+ def GB1(cls) -> "MicrovmSize":
2500
+ '''Memory floor of 1 GB.'''
2501
+ return typing.cast("MicrovmSize", jsii.sget(cls, "GB1"))
2502
+
2503
+ @jsii.python.classproperty
2504
+ @jsii.member(jsii_name="GB2")
2505
+ def GB2(cls) -> "MicrovmSize":
2506
+ '''Memory floor of 2 GB.'''
2507
+ return typing.cast("MicrovmSize", jsii.sget(cls, "GB2"))
2508
+
2509
+ @jsii.python.classproperty
2510
+ @jsii.member(jsii_name="GB4")
2511
+ def GB4(cls) -> "MicrovmSize":
2512
+ '''Memory floor of 4 GB.'''
2513
+ return typing.cast("MicrovmSize", jsii.sget(cls, "GB4"))
2514
+
2515
+ @jsii.python.classproperty
2516
+ @jsii.member(jsii_name="GB8")
2517
+ def GB8(cls) -> "MicrovmSize":
2518
+ '''Memory floor of 8 GB.'''
2519
+ return typing.cast("MicrovmSize", jsii.sget(cls, "GB8"))
2520
+
2521
+ @builtins.property
2522
+ @jsii.member(jsii_name="memoryGb")
2523
+ def memory_gb(self) -> jsii.Number:
2524
+ '''Memory floor in GB.'''
2525
+ return typing.cast(jsii.Number, jsii.get(self, "memoryGb"))
2526
+
2527
+ @builtins.property
2528
+ @jsii.member(jsii_name="memoryMib")
2529
+ def memory_mib(self) -> jsii.Number:
2530
+ '''Memory in MiB, the unit the MicroVM image's ``minimumMemoryInMiB`` takes.'''
2531
+ return typing.cast(jsii.Number, jsii.get(self, "memoryMib"))
2532
+
2533
+
2534
+ @jsii.data_type(
2535
+ jsii_type="cdk-github-microvm-runners.RunnerAlarmOptions",
2536
+ jsii_struct_bases=[],
2537
+ name_mapping={
2538
+ "evaluation_periods": "evaluationPeriods",
2539
+ "period": "period",
2540
+ "threshold": "threshold",
2541
+ },
2542
+ )
2543
+ class RunnerAlarmOptions:
2544
+ def __init__(
2545
+ self,
2546
+ *,
2547
+ evaluation_periods: typing.Optional[jsii.Number] = None,
2548
+ period: typing.Optional["_aws_cdk_ceddda9d.Duration"] = None,
2549
+ threshold: typing.Optional[jsii.Number] = None,
2550
+ ) -> None:
2551
+ '''Tuning for the ready-made alarms on ``GithubMicrovmRunnersMetrics``.
2552
+
2553
+ :param evaluation_periods: Consecutive breaching periods before the alarm fires. Default: 1 (3 for the stuck-launch alarm)
2554
+ :param period: Aggregation period for the metric. Default: Duration.minutes(5)
2555
+ :param threshold: Value at or above which the alarm fires. Default: 1
2556
+ '''
2557
+ if __debug__:
2558
+ type_hints = cached_type_hints(_typecheckingstub__117c59b0a49dcc58de82f614adb71603b95518cace779312d23dbc393e930d0f)
2559
+ check_type(argname="argument evaluation_periods", value=evaluation_periods, expected_type=type_hints["evaluation_periods"])
2560
+ check_type(argname="argument period", value=period, expected_type=type_hints["period"])
2561
+ check_type(argname="argument threshold", value=threshold, expected_type=type_hints["threshold"])
2562
+ self._values: typing.Dict[builtins.str, typing.Any] = {}
2563
+ if evaluation_periods is not None:
2564
+ self._values["evaluation_periods"] = evaluation_periods
2565
+ if period is not None:
2566
+ self._values["period"] = period
2567
+ if threshold is not None:
2568
+ self._values["threshold"] = threshold
2569
+
2570
+ @builtins.property
2571
+ def evaluation_periods(self) -> typing.Optional[jsii.Number]:
2572
+ '''Consecutive breaching periods before the alarm fires.
2573
+
2574
+ :default: 1 (3 for the stuck-launch alarm)
2575
+ '''
2576
+ result = self._values.get("evaluation_periods")
2577
+ return typing.cast(typing.Optional[jsii.Number], result)
2578
+
2579
+ @builtins.property
2580
+ def period(self) -> typing.Optional["_aws_cdk_ceddda9d.Duration"]:
2581
+ '''Aggregation period for the metric.
2582
+
2583
+ :default: Duration.minutes(5)
2584
+ '''
2585
+ result = self._values.get("period")
2586
+ return typing.cast(typing.Optional["_aws_cdk_ceddda9d.Duration"], result)
2587
+
2588
+ @builtins.property
2589
+ def threshold(self) -> typing.Optional[jsii.Number]:
2590
+ '''Value at or above which the alarm fires.
2591
+
2592
+ :default: 1
2593
+ '''
2594
+ result = self._values.get("threshold")
2595
+ return typing.cast(typing.Optional[jsii.Number], result)
2596
+
2597
+ def __eq__(self, rhs: typing.Any) -> builtins.bool:
2598
+ return isinstance(rhs, self.__class__) and rhs._values == self._values
2599
+
2600
+ def __ne__(self, rhs: typing.Any) -> builtins.bool:
2601
+ return not (rhs == self)
2602
+
2603
+ def __repr__(self) -> str:
2604
+ return "RunnerAlarmOptions(%s)" % ", ".join(
2605
+ k + "=" + repr(v) for k, v in self._values.items()
2606
+ )
2607
+
2608
+
2609
+ @jsii.data_type(
2610
+ jsii_type="cdk-github-microvm-runners.RunnerClass",
2611
+ jsii_struct_bases=[],
2612
+ name_mapping={
2613
+ "image_arn": "imageArn",
2614
+ "image_pipeline": "imagePipeline",
2615
+ "label": "label",
2616
+ "size": "size",
2617
+ },
2618
+ )
2619
+ class RunnerClass:
2620
+ def __init__(
2621
+ self,
2622
+ *,
2623
+ image_arn: builtins.str,
2624
+ image_pipeline: "ImagePipeline",
2625
+ label: builtins.str,
2626
+ size: "MicrovmSize",
2627
+ ) -> None:
2628
+ '''Handle returned by ``GithubMicrovmRunners.addRunnerClass``.
2629
+
2630
+ :param image_arn: ARN of this class's built MicroVM image (a CloudFormation token at synth).
2631
+ :param image_pipeline: The image pipeline that builds and publishes this class's MicroVM image.
2632
+ :param label: The ``runs-on`` label workflows target to run on this class.
2633
+ :param size: The VM memory floor this class launches at.
2634
+ '''
2635
+ if __debug__:
2636
+ type_hints = cached_type_hints(_typecheckingstub__23472a12727bc341ce55563246f68a52c49776a6814a52ab825cec01db0cdc89)
2637
+ check_type(argname="argument image_arn", value=image_arn, expected_type=type_hints["image_arn"])
2638
+ check_type(argname="argument image_pipeline", value=image_pipeline, expected_type=type_hints["image_pipeline"])
2639
+ check_type(argname="argument label", value=label, expected_type=type_hints["label"])
2640
+ check_type(argname="argument size", value=size, expected_type=type_hints["size"])
2641
+ self._values: typing.Dict[builtins.str, typing.Any] = {
2642
+ "image_arn": image_arn,
2643
+ "image_pipeline": image_pipeline,
2644
+ "label": label,
2645
+ "size": size,
2646
+ }
2647
+
2648
+ @builtins.property
2649
+ def image_arn(self) -> builtins.str:
2650
+ '''ARN of this class's built MicroVM image (a CloudFormation token at synth).'''
2651
+ result = self._values.get("image_arn")
2652
+ assert result is not None, "Required property 'image_arn' is missing"
2653
+ return typing.cast(builtins.str, result)
2654
+
2655
+ @builtins.property
2656
+ def image_pipeline(self) -> "ImagePipeline":
2657
+ '''The image pipeline that builds and publishes this class's MicroVM image.'''
2658
+ result = self._values.get("image_pipeline")
2659
+ assert result is not None, "Required property 'image_pipeline' is missing"
2660
+ return typing.cast("ImagePipeline", result)
2661
+
2662
+ @builtins.property
2663
+ def label(self) -> builtins.str:
2664
+ '''The ``runs-on`` label workflows target to run on this class.'''
2665
+ result = self._values.get("label")
2666
+ assert result is not None, "Required property 'label' is missing"
2667
+ return typing.cast(builtins.str, result)
2668
+
2669
+ @builtins.property
2670
+ def size(self) -> "MicrovmSize":
2671
+ '''The VM memory floor this class launches at.'''
2672
+ result = self._values.get("size")
2673
+ assert result is not None, "Required property 'size' is missing"
2674
+ return typing.cast("MicrovmSize", result)
2675
+
2676
+ def __eq__(self, rhs: typing.Any) -> builtins.bool:
2677
+ return isinstance(rhs, self.__class__) and rhs._values == self._values
2678
+
2679
+ def __ne__(self, rhs: typing.Any) -> builtins.bool:
2680
+ return not (rhs == self)
2681
+
2682
+ def __repr__(self) -> str:
2683
+ return "RunnerClass(%s)" % ", ".join(
2684
+ k + "=" + repr(v) for k, v in self._values.items()
2685
+ )
2686
+
2687
+
2688
+ @jsii.data_type(
2689
+ jsii_type="cdk-github-microvm-runners.RunnerClassProps",
2690
+ jsii_struct_bases=[],
2691
+ name_mapping={
2692
+ "size": "size",
2693
+ "idle_policy": "idlePolicy",
2694
+ "image": "image",
2695
+ "warm_pool_size": "warmPoolSize",
2696
+ },
2697
+ )
2698
+ class RunnerClassProps:
2699
+ def __init__(
2700
+ self,
2701
+ *,
2702
+ size: "MicrovmSize",
2703
+ idle_policy: typing.Optional[typing.Union["MicrovmIdlePolicy", typing.Dict[builtins.str, typing.Any]]] = None,
2704
+ image: typing.Optional["RunnerImage"] = None,
2705
+ warm_pool_size: typing.Optional[jsii.Number] = None,
2706
+ ) -> None:
2707
+ '''Props for ``GithubMicrovmRunners.addRunnerClass``.
2708
+
2709
+ :param size: VM memory floor for this class.
2710
+ :param idle_policy: Auto-suspend and auto-resume policy for this class's cold-launched VMs. Mutually exclusive with ``warmPoolSize`` on the same class, since both drive the VM's suspended state; setting both throws at ``addRunnerClass`` time. Default: undefined (no idle policy; the platform never auto-suspends)
2711
+ :param image: Image this class builds from. Default: RunnerImage.fromOptions()
2712
+ :param warm_pool_size: How many pre-booted, suspended VMs to keep ready for this class. A job that matches this class resumes one of them instead of cold-launching a new VM, and falls back to a cold launch when none is available. This is a count, not a flag: ``warmPoolSize: 3`` keeps three VMs ready. The runner set refills the pool on the ``warmPoolInterval`` schedule. Default: undefined (no warm pool; every job cold-launches)
2713
+ '''
2714
+ if isinstance(idle_policy, dict):
2715
+ idle_policy = MicrovmIdlePolicy(**idle_policy)
2716
+ if __debug__:
2717
+ type_hints = cached_type_hints(_typecheckingstub__6e46f2bbd7884caef4f137e6dac784cd99ef109d84b58f3f7acf963f03c723a7)
2718
+ check_type(argname="argument size", value=size, expected_type=type_hints["size"])
2719
+ check_type(argname="argument idle_policy", value=idle_policy, expected_type=type_hints["idle_policy"])
2720
+ check_type(argname="argument image", value=image, expected_type=type_hints["image"])
2721
+ check_type(argname="argument warm_pool_size", value=warm_pool_size, expected_type=type_hints["warm_pool_size"])
2722
+ self._values: typing.Dict[builtins.str, typing.Any] = {
2723
+ "size": size,
2724
+ }
2725
+ if idle_policy is not None:
2726
+ self._values["idle_policy"] = idle_policy
2727
+ if image is not None:
2728
+ self._values["image"] = image
2729
+ if warm_pool_size is not None:
2730
+ self._values["warm_pool_size"] = warm_pool_size
2731
+
2732
+ @builtins.property
2733
+ def size(self) -> "MicrovmSize":
2734
+ '''VM memory floor for this class.'''
2735
+ result = self._values.get("size")
2736
+ assert result is not None, "Required property 'size' is missing"
2737
+ return typing.cast("MicrovmSize", result)
2738
+
2739
+ @builtins.property
2740
+ def idle_policy(self) -> typing.Optional["MicrovmIdlePolicy"]:
2741
+ '''Auto-suspend and auto-resume policy for this class's cold-launched VMs.
2742
+
2743
+ Mutually exclusive with ``warmPoolSize`` on the same class, since both drive
2744
+ the VM's suspended state; setting both throws at ``addRunnerClass`` time.
2745
+
2746
+ :default: undefined (no idle policy; the platform never auto-suspends)
2747
+ '''
2748
+ result = self._values.get("idle_policy")
2749
+ return typing.cast(typing.Optional["MicrovmIdlePolicy"], result)
2750
+
2751
+ @builtins.property
2752
+ def image(self) -> typing.Optional["RunnerImage"]:
2753
+ '''Image this class builds from.
2754
+
2755
+ :default: RunnerImage.fromOptions()
2756
+ '''
2757
+ result = self._values.get("image")
2758
+ return typing.cast(typing.Optional["RunnerImage"], result)
2759
+
2760
+ @builtins.property
2761
+ def warm_pool_size(self) -> typing.Optional[jsii.Number]:
2762
+ '''How many pre-booted, suspended VMs to keep ready for this class.
2763
+
2764
+ A job
2765
+ that matches this class resumes one of them instead of cold-launching a
2766
+ new VM, and falls back to a cold launch when none is available. This is a
2767
+ count, not a flag: ``warmPoolSize: 3`` keeps three VMs ready. The runner set
2768
+ refills the pool on the ``warmPoolInterval`` schedule.
2769
+
2770
+ :default: undefined (no warm pool; every job cold-launches)
2771
+ '''
2772
+ result = self._values.get("warm_pool_size")
2773
+ return typing.cast(typing.Optional[jsii.Number], result)
2774
+
2775
+ def __eq__(self, rhs: typing.Any) -> builtins.bool:
2776
+ return isinstance(rhs, self.__class__) and rhs._values == self._values
2777
+
2778
+ def __ne__(self, rhs: typing.Any) -> builtins.bool:
2779
+ return not (rhs == self)
2780
+
2781
+ def __repr__(self) -> str:
2782
+ return "RunnerClassProps(%s)" % ", ".join(
2783
+ k + "=" + repr(v) for k, v in self._values.items()
2784
+ )
2785
+
2786
+
2787
+ class RunnerImage(
2788
+ metaclass=jsii.JSIIMeta,
2789
+ jsii_type="cdk-github-microvm-runners.RunnerImage",
2790
+ ):
2791
+ '''The image a runner class's VMs boot from: one this library synthesizes, or one you author yourself.
2792
+
2793
+ Build one with the static factories below; the constructor is private.
2794
+
2795
+ Example::
2796
+
2797
+ runners.add_runner_class("build",
2798
+ size=MicrovmSize.GB4,
2799
+ image=RunnerImage.from_options(
2800
+ system_packages=["jq", "ripgrep"]
2801
+ )
2802
+ )
2803
+ '''
2804
+
2805
+ @jsii.member(jsii_name="fromDockerfile")
2806
+ @builtins.classmethod
2807
+ def from_dockerfile(cls, dir: builtins.str) -> "RunnerImage":
2808
+ '''Use your own Dockerfile, and the build context around it, from the directory ``dir``.
2809
+
2810
+ The whole directory is staged as the Docker build
2811
+ context, so a Dockerfile that needs to ``COPY`` files of its own belongs
2812
+ here rather than in ``RunnerImage.fromInline``.
2813
+
2814
+ The ``contentHash`` recorded on the returned instance is derived from the
2815
+ path string. The directory's actual contents are read and hashed later,
2816
+ when the image pipeline stages them as a CDK asset.
2817
+
2818
+ A relative ``dir`` is resolved against the process working directory, which
2819
+ is wherever ``cdk`` was invoked. Anchor it to the file that declares the
2820
+ runner class instead by passing ``path.join(__dirname, 'runner-image')``.
2821
+
2822
+ :param dir: -
2823
+
2824
+ Example::
2825
+
2826
+ custom_image = RunnerImage.from_dockerfile("runner-image")
2827
+ '''
2828
+ if __debug__:
2829
+ type_hints = cached_type_hints(_typecheckingstub__979f58111abc04b49545772d5ca529893452a46b62def883e284f0c2b1f0f73d)
2830
+ check_type(argname="argument dir", value=dir, expected_type=type_hints["dir"])
2831
+ return typing.cast("RunnerImage", jsii.sinvoke(cls, "fromDockerfile", [dir]))
2832
+
2833
+ @jsii.member(jsii_name="fromInline")
2834
+ @builtins.classmethod
2835
+ def from_inline(cls, dockerfile: builtins.str) -> "RunnerImage":
2836
+ '''Use your own Dockerfile, supplied as text.
2837
+
2838
+ The text is staged verbatim as
2839
+ the build context's ``Dockerfile``, alongside the ``microvm-runner/``
2840
+ directory the image pipeline injects and nothing else. A Dockerfile that
2841
+ needs to ``COPY`` files of its own belongs with
2842
+ ``RunnerImage.fromDockerfile``, which stages a whole directory.
2843
+
2844
+ The text must ``COPY microvm-runner/agent.mjs`` and start the staged
2845
+ entrypoint, because that agent is what serves the MicroVM lifecycle hooks
2846
+ the platform calls. This is checked here, and a Dockerfile that does not
2847
+ copy the agent throws.
2848
+
2849
+ The ``contentHash`` recorded on the returned instance is a sha256 over the
2850
+ supplied text.
2851
+
2852
+ :param dockerfile: -
2853
+
2854
+ Example::
2855
+
2856
+ inline_image = RunnerImage.from_inline("""
2857
+ FROM public.ecr.aws/lambda/microvms:al2023-minimal
2858
+
2859
+ RUN dnf install -y git jq
2860
+
2861
+ COPY microvm-runner/agent.mjs /opt/microvm-runner/agent.mjs
2862
+ COPY microvm-runner/entrypoint.sh /opt/microvm-runner/entrypoint.sh
2863
+ ENTRYPOINT ["/opt/microvm-runner/entrypoint.sh"]
2864
+ """)
2865
+ '''
2866
+ if __debug__:
2867
+ type_hints = cached_type_hints(_typecheckingstub__2d9215b22cc740b2c93f48e4e0744fe50e43a7a33f85974d48dbebd1e184d9d4)
2868
+ check_type(argname="argument dockerfile", value=dockerfile, expected_type=type_hints["dockerfile"])
2869
+ return typing.cast("RunnerImage", jsii.sinvoke(cls, "fromInline", [dockerfile]))
2870
+
2871
+ @jsii.member(jsii_name="fromOptions")
2872
+ @builtins.classmethod
2873
+ def from_options(
2874
+ cls,
2875
+ *,
2876
+ additional_os_capabilities: typing.Optional[typing.Sequence[builtins.str]] = None,
2877
+ assets: typing.Optional[typing.Sequence[typing.Union["ImageAsset", typing.Dict[builtins.str, typing.Any]]]] = None,
2878
+ environment: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
2879
+ runner_version: typing.Optional["RunnerVersion"] = None,
2880
+ setup_commands: typing.Optional[typing.Sequence[builtins.str]] = None,
2881
+ system_packages: typing.Optional[typing.Sequence[builtins.str]] = None,
2882
+ toolchains: typing.Optional[typing.Sequence["RunnerToolchain"]] = None,
2883
+ ) -> "RunnerImage":
2884
+ '''Synthesize a Dockerfile from ``opts`` — extra packages, setup commands, assets, environment variables, toolchains, and the ``actions/runner`` release to install.
2885
+
2886
+ The image's ``contentHash`` is computed here, over the
2887
+ rendered Dockerfile text and the list of assets it copies.
2888
+
2889
+ :param additional_os_capabilities: Extra Linux capabilities granted to the MicroVM's operating system. Default: ['ALL']
2890
+ :param assets: Extra files and directories to copy into the image.
2891
+ :param environment: Extra environment variables baked into the image.
2892
+ :param runner_version: ``actions/runner`` release to install. Default: RunnerVersion.latest()
2893
+ :param setup_commands: Extra ``RUN`` commands, executed in order after packages, assets, and environment variables are laid down.
2894
+ :param system_packages: Extra ``dnf`` packages to install alongside the fixed base set.
2895
+ :param toolchains: Language runtimes to bake into the hosted tool cache, so ``actions/setup-*`` finds them without downloading anything. An image with none of these is smaller; one toolchain entry is needed per version your workflows ask for. Default: []
2896
+
2897
+ Example::
2898
+
2899
+ build_image = RunnerImage.from_options(
2900
+ system_packages=["jq", "ripgrep"],
2901
+ setup_commands=["npm install -g pnpm@10"],
2902
+ environment={"LANG": "C.UTF-8"},
2903
+ toolchains=[RunnerToolchain.python("3.12.7")]
2904
+ )
2905
+ '''
2906
+ opts = RunnerImageOptions(
2907
+ additional_os_capabilities=additional_os_capabilities,
2908
+ assets=assets,
2909
+ environment=environment,
2910
+ runner_version=runner_version,
2911
+ setup_commands=setup_commands,
2912
+ system_packages=system_packages,
2913
+ toolchains=toolchains,
2914
+ )
2915
+
2916
+ return typing.cast("RunnerImage", jsii.sinvoke(cls, "fromOptions", [opts]))
2917
+
2918
+ @builtins.property
2919
+ @jsii.member(jsii_name="additionalOsCapabilities")
2920
+ def additional_os_capabilities(self) -> typing.List[builtins.str]:
2921
+ '''Extra Linux capabilities granted to the MicroVM's operating system.
2922
+
2923
+ ``fromOptions()`` takes this from ``RunnerImageOptions``; ``fromDockerfile()``
2924
+ and ``fromInline()`` always carry ``['ALL']``.
2925
+ '''
2926
+ return typing.cast(typing.List[builtins.str], jsii.get(self, "additionalOsCapabilities"))
2927
+
2928
+ @builtins.property
2929
+ @jsii.member(jsii_name="contentHash")
2930
+ def content_hash(self) -> builtins.str:
2931
+ '''sha256 content hash, part of the built image's name.
2932
+
2933
+ For
2934
+ ``fromOptions()`` it covers the rendered Dockerfile and the options that
2935
+ produced it; for ``fromInline()``, the supplied Dockerfile text; for
2936
+ ``fromDockerfile()``, the directory path.
2937
+ '''
2938
+ return typing.cast(builtins.str, jsii.get(self, "contentHash"))
2939
+
2940
+ @builtins.property
2941
+ @jsii.member(jsii_name="assets")
2942
+ def assets(self) -> typing.Optional[typing.List["ImageAsset"]]:
2943
+ '''The ``{source, target}`` pairs from ``RunnerImageOptions.assets``. Set for ``fromOptions()``, whose rendered Dockerfile copies each one into the image. ``undefined`` for ``fromDockerfile()``, which stages your whole directory instead, and for ``fromInline()``, whose build context holds the Dockerfile and the injected agent and nothing else.'''
2944
+ return typing.cast(typing.Optional[typing.List["ImageAsset"]], jsii.get(self, "assets"))
2945
+
2946
+ @builtins.property
2947
+ @jsii.member(jsii_name="dockerfile")
2948
+ def dockerfile(self) -> typing.Optional[builtins.str]:
2949
+ '''Dockerfile text: rendered for ``fromOptions()``, supplied by you for ``fromInline()``.
2950
+
2951
+ ``undefined`` for ``fromDockerfile()``, whose Dockerfile
2952
+ lives on disk under ``dockerfileDir``.
2953
+ '''
2954
+ return typing.cast(typing.Optional[builtins.str], jsii.get(self, "dockerfile"))
2955
+
2956
+ @builtins.property
2957
+ @jsii.member(jsii_name="dockerfileDir")
2958
+ def dockerfile_dir(self) -> typing.Optional[builtins.str]:
2959
+ '''The directory holding your own Dockerfile and build context.
2960
+
2961
+ Set for
2962
+ ``fromDockerfile()``, ``undefined`` for ``fromOptions()`` and ``fromInline()``,
2963
+ which both carry their Dockerfile as ``dockerfile`` text.
2964
+ '''
2965
+ return typing.cast(typing.Optional[builtins.str], jsii.get(self, "dockerfileDir"))
2966
+
2967
+
2968
+ @jsii.data_type(
2969
+ jsii_type="cdk-github-microvm-runners.RunnerImageOptions",
2970
+ jsii_struct_bases=[],
2971
+ name_mapping={
2972
+ "additional_os_capabilities": "additionalOsCapabilities",
2973
+ "assets": "assets",
2974
+ "environment": "environment",
2975
+ "runner_version": "runnerVersion",
2976
+ "setup_commands": "setupCommands",
2977
+ "system_packages": "systemPackages",
2978
+ "toolchains": "toolchains",
2979
+ },
2980
+ )
2981
+ class RunnerImageOptions:
2982
+ def __init__(
2983
+ self,
2984
+ *,
2985
+ additional_os_capabilities: typing.Optional[typing.Sequence[builtins.str]] = None,
2986
+ assets: typing.Optional[typing.Sequence[typing.Union["ImageAsset", typing.Dict[builtins.str, typing.Any]]]] = None,
2987
+ environment: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
2988
+ runner_version: typing.Optional["RunnerVersion"] = None,
2989
+ setup_commands: typing.Optional[typing.Sequence[builtins.str]] = None,
2990
+ system_packages: typing.Optional[typing.Sequence[builtins.str]] = None,
2991
+ toolchains: typing.Optional[typing.Sequence["RunnerToolchain"]] = None,
2992
+ ) -> None:
2993
+ '''Options for ``RunnerImage.fromOptions``.
2994
+
2995
+ :param additional_os_capabilities: Extra Linux capabilities granted to the MicroVM's operating system. Default: ['ALL']
2996
+ :param assets: Extra files and directories to copy into the image.
2997
+ :param environment: Extra environment variables baked into the image.
2998
+ :param runner_version: ``actions/runner`` release to install. Default: RunnerVersion.latest()
2999
+ :param setup_commands: Extra ``RUN`` commands, executed in order after packages, assets, and environment variables are laid down.
3000
+ :param system_packages: Extra ``dnf`` packages to install alongside the fixed base set.
3001
+ :param toolchains: Language runtimes to bake into the hosted tool cache, so ``actions/setup-*`` finds them without downloading anything. An image with none of these is smaller; one toolchain entry is needed per version your workflows ask for. Default: []
3002
+ '''
3003
+ if __debug__:
3004
+ type_hints = cached_type_hints(_typecheckingstub__6fd3c247efc1bf4be6517722f43505c0df1d4ecea290c5f473194c94910a6cb8)
3005
+ check_type(argname="argument additional_os_capabilities", value=additional_os_capabilities, expected_type=type_hints["additional_os_capabilities"])
3006
+ check_type(argname="argument assets", value=assets, expected_type=type_hints["assets"])
3007
+ check_type(argname="argument environment", value=environment, expected_type=type_hints["environment"])
3008
+ check_type(argname="argument runner_version", value=runner_version, expected_type=type_hints["runner_version"])
3009
+ check_type(argname="argument setup_commands", value=setup_commands, expected_type=type_hints["setup_commands"])
3010
+ check_type(argname="argument system_packages", value=system_packages, expected_type=type_hints["system_packages"])
3011
+ check_type(argname="argument toolchains", value=toolchains, expected_type=type_hints["toolchains"])
3012
+ self._values: typing.Dict[builtins.str, typing.Any] = {}
3013
+ if additional_os_capabilities is not None:
3014
+ self._values["additional_os_capabilities"] = additional_os_capabilities
3015
+ if assets is not None:
3016
+ self._values["assets"] = assets
3017
+ if environment is not None:
3018
+ self._values["environment"] = environment
3019
+ if runner_version is not None:
3020
+ self._values["runner_version"] = runner_version
3021
+ if setup_commands is not None:
3022
+ self._values["setup_commands"] = setup_commands
3023
+ if system_packages is not None:
3024
+ self._values["system_packages"] = system_packages
3025
+ if toolchains is not None:
3026
+ self._values["toolchains"] = toolchains
3027
+
3028
+ @builtins.property
3029
+ def additional_os_capabilities(self) -> typing.Optional[typing.List[builtins.str]]:
3030
+ '''Extra Linux capabilities granted to the MicroVM's operating system.
3031
+
3032
+ :default: ['ALL']
3033
+ '''
3034
+ result = self._values.get("additional_os_capabilities")
3035
+ return typing.cast(typing.Optional[typing.List[builtins.str]], result)
3036
+
3037
+ @builtins.property
3038
+ def assets(self) -> typing.Optional[typing.List["ImageAsset"]]:
3039
+ '''Extra files and directories to copy into the image.'''
3040
+ result = self._values.get("assets")
3041
+ return typing.cast(typing.Optional[typing.List["ImageAsset"]], result)
3042
+
3043
+ @builtins.property
3044
+ def environment(
3045
+ self,
3046
+ ) -> typing.Optional[typing.Mapping[builtins.str, builtins.str]]:
3047
+ '''Extra environment variables baked into the image.'''
3048
+ result = self._values.get("environment")
3049
+ return typing.cast(typing.Optional[typing.Mapping[builtins.str, builtins.str]], result)
3050
+
3051
+ @builtins.property
3052
+ def runner_version(self) -> typing.Optional["RunnerVersion"]:
3053
+ '''``actions/runner`` release to install.
3054
+
3055
+ :default: RunnerVersion.latest()
3056
+ '''
3057
+ result = self._values.get("runner_version")
3058
+ return typing.cast(typing.Optional["RunnerVersion"], result)
3059
+
3060
+ @builtins.property
3061
+ def setup_commands(self) -> typing.Optional[typing.List[builtins.str]]:
3062
+ '''Extra ``RUN`` commands, executed in order after packages, assets, and environment variables are laid down.'''
3063
+ result = self._values.get("setup_commands")
3064
+ return typing.cast(typing.Optional[typing.List[builtins.str]], result)
3065
+
3066
+ @builtins.property
3067
+ def system_packages(self) -> typing.Optional[typing.List[builtins.str]]:
3068
+ '''Extra ``dnf`` packages to install alongside the fixed base set.'''
3069
+ result = self._values.get("system_packages")
3070
+ return typing.cast(typing.Optional[typing.List[builtins.str]], result)
3071
+
3072
+ @builtins.property
3073
+ def toolchains(self) -> typing.Optional[typing.List["RunnerToolchain"]]:
3074
+ '''Language runtimes to bake into the hosted tool cache, so ``actions/setup-*`` finds them without downloading anything.
3075
+
3076
+ An image with none of these is smaller; one toolchain entry is needed per version your workflows ask for.
3077
+
3078
+ :default: []
3079
+ '''
3080
+ result = self._values.get("toolchains")
3081
+ return typing.cast(typing.Optional[typing.List["RunnerToolchain"]], result)
3082
+
3083
+ def __eq__(self, rhs: typing.Any) -> builtins.bool:
3084
+ return isinstance(rhs, self.__class__) and rhs._values == self._values
3085
+
3086
+ def __ne__(self, rhs: typing.Any) -> builtins.bool:
3087
+ return not (rhs == self)
3088
+
3089
+ def __repr__(self) -> str:
3090
+ return "RunnerImageOptions(%s)" % ", ".join(
3091
+ k + "=" + repr(v) for k, v in self._values.items()
3092
+ )
3093
+
3094
+
3095
+ class RunnerNetwork(
3096
+ metaclass=jsii.JSIIMeta,
3097
+ jsii_type="cdk-github-microvm-runners.RunnerNetwork",
3098
+ ):
3099
+ '''How a runner set's MicroVMs reach the network: direct internet egress, Lambda VPC runtime connectors you already have, or a connector the construct builds from a CDK VPC.
3100
+
3101
+ Build one with the static factories below; the constructor is private.
3102
+
3103
+ Example::
3104
+
3105
+ GithubMicrovmRunners(stack, "Runners",
3106
+ github=github,
3107
+ scope=scope,
3108
+ network=RunnerNetwork.vpc(vpc)
3109
+ )
3110
+ '''
3111
+
3112
+ @jsii.member(jsii_name="internetEgress")
3113
+ @builtins.classmethod
3114
+ def internet_egress(cls) -> "RunnerNetwork":
3115
+ '''Runners egress directly to the internet (no VPC connector).
3116
+
3117
+ Example::
3118
+
3119
+ network = RunnerNetwork.internet_egress()
3120
+ '''
3121
+ return typing.cast("RunnerNetwork", jsii.sinvoke(cls, "internetEgress", []))
3122
+
3123
+ @jsii.member(jsii_name="vpc")
3124
+ @builtins.classmethod
3125
+ def vpc(
3126
+ cls,
3127
+ vpc: "_aws_cdk_aws_ec2_ceddda9d.IVpc",
3128
+ *,
3129
+ security_groups: typing.Optional[typing.Sequence["_aws_cdk_aws_ec2_ceddda9d.ISecurityGroup"]] = None,
3130
+ subnets: typing.Optional[typing.Union["_aws_cdk_aws_ec2_ceddda9d.SubnetSelection", typing.Dict[builtins.str, typing.Any]]] = None,
3131
+ ) -> "RunnerNetwork":
3132
+ '''Runners egress through a network connector the construct builds from the given CDK VPC, along with the security group and the ENI-management operator role that connector needs.
3133
+
3134
+ You supply the VPC; no connector ARN
3135
+ is required. ``connectorArns`` is empty on the returned instance, and the
3136
+ construct fills in the connector's real ARN at synth.
3137
+
3138
+ :param vpc: -
3139
+ :param security_groups: Security groups attached to the connector's ENIs. Default: - a new security group is created on the VPC
3140
+ :param subnets: Which of the VPC's subnets the connector's ENIs land in. Default: - the VPC's private-with-egress subnets (CDK's ``selectSubnets()`` default; falls back to isolated, then public, subnets if the VPC has none of the preceding kind)
3141
+
3142
+ Example::
3143
+
3144
+ network = RunnerNetwork.vpc(vpc,
3145
+ subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE_ISOLATED),
3146
+ security_groups=[my_security_group]
3147
+ )
3148
+ '''
3149
+ if __debug__:
3150
+ type_hints = cached_type_hints(_typecheckingstub__d2491e4912faff169fbaad1c4fb389eb29f77ce187a242a0af01526ea53ad4d7)
3151
+ check_type(argname="argument vpc", value=vpc, expected_type=type_hints["vpc"])
3152
+ opts = RunnerNetworkVpcOptions(
3153
+ security_groups=security_groups, subnets=subnets
3154
+ )
3155
+
3156
+ return typing.cast("RunnerNetwork", jsii.sinvoke(cls, "vpc", [vpc, opts]))
3157
+
3158
+ @jsii.member(jsii_name="vpcConnector")
3159
+ @builtins.classmethod
3160
+ def vpc_connector(
3161
+ cls,
3162
+ connector_arns: typing.Sequence[builtins.str],
3163
+ ) -> "RunnerNetwork":
3164
+ '''Runners are attached to the given Lambda runtime connector ARNs.
3165
+
3166
+ :param connector_arns: -
3167
+
3168
+ Example::
3169
+
3170
+ network = RunnerNetwork.vpc_connector(["arn:aws:lambda:us-east-1:111122223333:network-connector:my-connector"
3171
+ ])
3172
+ '''
3173
+ if __debug__:
3174
+ type_hints = cached_type_hints(_typecheckingstub__6db194aaaafe63c89cd0b5bfe1846487e92f6c50fdaa2c8c2e3d8689ff54dfd1)
3175
+ check_type(argname="argument connector_arns", value=connector_arns, expected_type=type_hints["connector_arns"])
3176
+ return typing.cast("RunnerNetwork", jsii.sinvoke(cls, "vpcConnector", [connector_arns]))
3177
+
3178
+ @builtins.property
3179
+ @jsii.member(jsii_name="connectorArns")
3180
+ def connector_arns(self) -> typing.List[builtins.str]:
3181
+ '''Runtime connector ARNs.
3182
+
3183
+ Empty for direct internet egress, and empty for
3184
+ a ``vpc()`` network until the construct builds its connector at synth.
3185
+ '''
3186
+ return typing.cast(typing.List[builtins.str], jsii.get(self, "connectorArns"))
3187
+
3188
+ @builtins.property
3189
+ @jsii.member(jsii_name="kind")
3190
+ def kind(self) -> "RunnerNetworkKind":
3191
+ '''Which networking mode this instance carries.'''
3192
+ return typing.cast("RunnerNetworkKind", jsii.get(self, "kind"))
3193
+
3194
+ @builtins.property
3195
+ @jsii.member(jsii_name="securityGroups")
3196
+ def security_groups(
3197
+ self,
3198
+ ) -> typing.Optional[typing.List["_aws_cdk_aws_ec2_ceddda9d.ISecurityGroup"]]:
3199
+ '''Security groups for the built connector, set only for a ``vpc()`` network.'''
3200
+ return typing.cast(typing.Optional[typing.List["_aws_cdk_aws_ec2_ceddda9d.ISecurityGroup"]], jsii.get(self, "securityGroups"))
3201
+
3202
+ @builtins.property
3203
+ @jsii.member(jsii_name="sourceVpc")
3204
+ def source_vpc(self) -> typing.Optional["_aws_cdk_aws_ec2_ceddda9d.IVpc"]:
3205
+ '''The VPC to build a connector from, set only for a ``vpc()`` network.'''
3206
+ return typing.cast(typing.Optional["_aws_cdk_aws_ec2_ceddda9d.IVpc"], jsii.get(self, "sourceVpc"))
3207
+
3208
+ @builtins.property
3209
+ @jsii.member(jsii_name="subnets")
3210
+ def subnets(self) -> typing.Optional["_aws_cdk_aws_ec2_ceddda9d.SubnetSelection"]:
3211
+ '''Subnet selection for the built connector, set only for a ``vpc()`` network.'''
3212
+ return typing.cast(typing.Optional["_aws_cdk_aws_ec2_ceddda9d.SubnetSelection"], jsii.get(self, "subnets"))
3213
+
3214
+
3215
+ @jsii.enum(jsii_type="cdk-github-microvm-runners.RunnerNetworkKind")
3216
+ class RunnerNetworkKind(enum.Enum):
3217
+ '''Which networking mode a ``RunnerNetwork`` carries.'''
3218
+
3219
+ INTERNET = "INTERNET"
3220
+ '''Direct internet egress, with no Lambda VPC runtime connector.'''
3221
+ CONNECTORS = "CONNECTORS"
3222
+ '''Runners attached to caller-supplied Lambda runtime connector ARNs.'''
3223
+ VPC = "VPC"
3224
+ '''Runners attached to a connector the construct builds from a CDK VPC.'''
3225
+
3226
+
3227
+ @jsii.data_type(
3228
+ jsii_type="cdk-github-microvm-runners.RunnerNetworkVpcOptions",
3229
+ jsii_struct_bases=[],
3230
+ name_mapping={"security_groups": "securityGroups", "subnets": "subnets"},
3231
+ )
3232
+ class RunnerNetworkVpcOptions:
3233
+ def __init__(
3234
+ self,
3235
+ *,
3236
+ security_groups: typing.Optional[typing.Sequence["_aws_cdk_aws_ec2_ceddda9d.ISecurityGroup"]] = None,
3237
+ subnets: typing.Optional[typing.Union["_aws_cdk_aws_ec2_ceddda9d.SubnetSelection", typing.Dict[builtins.str, typing.Any]]] = None,
3238
+ ) -> None:
3239
+ '''Options for ``RunnerNetwork.vpc``.
3240
+
3241
+ :param security_groups: Security groups attached to the connector's ENIs. Default: - a new security group is created on the VPC
3242
+ :param subnets: Which of the VPC's subnets the connector's ENIs land in. Default: - the VPC's private-with-egress subnets (CDK's ``selectSubnets()`` default; falls back to isolated, then public, subnets if the VPC has none of the preceding kind)
3243
+ '''
3244
+ if isinstance(subnets, dict):
3245
+ subnets = _aws_cdk_aws_ec2_ceddda9d.SubnetSelection(**subnets)
3246
+ if __debug__:
3247
+ type_hints = cached_type_hints(_typecheckingstub__940d8682a239c73072340b0492130025e2f1d8c0d2614466732fa878426abf8c)
3248
+ check_type(argname="argument security_groups", value=security_groups, expected_type=type_hints["security_groups"])
3249
+ check_type(argname="argument subnets", value=subnets, expected_type=type_hints["subnets"])
3250
+ self._values: typing.Dict[builtins.str, typing.Any] = {}
3251
+ if security_groups is not None:
3252
+ self._values["security_groups"] = security_groups
3253
+ if subnets is not None:
3254
+ self._values["subnets"] = subnets
3255
+
3256
+ @builtins.property
3257
+ def security_groups(
3258
+ self,
3259
+ ) -> typing.Optional[typing.List["_aws_cdk_aws_ec2_ceddda9d.ISecurityGroup"]]:
3260
+ '''Security groups attached to the connector's ENIs.
3261
+
3262
+ :default: - a new security group is created on the VPC
3263
+ '''
3264
+ result = self._values.get("security_groups")
3265
+ return typing.cast(typing.Optional[typing.List["_aws_cdk_aws_ec2_ceddda9d.ISecurityGroup"]], result)
3266
+
3267
+ @builtins.property
3268
+ def subnets(self) -> typing.Optional["_aws_cdk_aws_ec2_ceddda9d.SubnetSelection"]:
3269
+ '''Which of the VPC's subnets the connector's ENIs land in.
3270
+
3271
+ :default:
3272
+
3273
+ - the VPC's private-with-egress subnets (CDK's
3274
+ ``selectSubnets()`` default; falls back to isolated, then public, subnets
3275
+ if the VPC has none of the preceding kind)
3276
+ '''
3277
+ result = self._values.get("subnets")
3278
+ return typing.cast(typing.Optional["_aws_cdk_aws_ec2_ceddda9d.SubnetSelection"], result)
3279
+
3280
+ def __eq__(self, rhs: typing.Any) -> builtins.bool:
3281
+ return isinstance(rhs, self.__class__) and rhs._values == self._values
3282
+
3283
+ def __ne__(self, rhs: typing.Any) -> builtins.bool:
3284
+ return not (rhs == self)
3285
+
3286
+ def __repr__(self) -> str:
3287
+ return "RunnerNetworkVpcOptions(%s)" % ", ".join(
3288
+ k + "=" + repr(v) for k, v in self._values.items()
3289
+ )
3290
+
3291
+
3292
+ class RunnerScope(
3293
+ metaclass=jsii.JSIIMeta,
3294
+ jsii_type="cdk-github-microvm-runners.RunnerScope",
3295
+ ):
3296
+ '''Which GitHub scope registered runners are visible to: an entire organization, or an explicit list of ``owner/repo`` repositories.
3297
+
3298
+ Build one with the static factories below; the constructor is private.
3299
+
3300
+ Example::
3301
+
3302
+ org_scope = RunnerScope.org("my-org")
3303
+ '''
3304
+
3305
+ @jsii.member(jsii_name="org")
3306
+ @builtins.classmethod
3307
+ def org(cls, org: builtins.str) -> "RunnerScope":
3308
+ '''Runners are registered at the organization level.
3309
+
3310
+ :param org: -
3311
+
3312
+ Example::
3313
+
3314
+ org_scope = RunnerScope.org("my-org")
3315
+ '''
3316
+ if __debug__:
3317
+ type_hints = cached_type_hints(_typecheckingstub__a306ec12ac8b86304298f1b4650c1ad69974d0773eef2b6f6245b92666d89c8f)
3318
+ check_type(argname="argument org", value=org, expected_type=type_hints["org"])
3319
+ return typing.cast("RunnerScope", jsii.sinvoke(cls, "org", [org]))
3320
+
3321
+ @jsii.member(jsii_name="repos")
3322
+ @builtins.classmethod
3323
+ def repos(cls, repos: typing.Sequence[builtins.str]) -> "RunnerScope":
3324
+ '''Runners are registered against an explicit list of ``owner/repo`` repos.
3325
+
3326
+ :param repos: -
3327
+
3328
+ Example::
3329
+
3330
+ repo_scope = RunnerScope.repos(["my-org/api", "my-org/web"])
3331
+ '''
3332
+ if __debug__:
3333
+ type_hints = cached_type_hints(_typecheckingstub__a81b46da719563a103d6a4dc08b1337ad955b8bb3e417594cba8689ec1b3990e)
3334
+ check_type(argname="argument repos", value=repos, expected_type=type_hints["repos"])
3335
+ return typing.cast("RunnerScope", jsii.sinvoke(cls, "repos", [repos]))
3336
+
3337
+ @jsii.member(jsii_name="toJson")
3338
+ def to_json(self) -> builtins.str:
3339
+ '''Serialize this scope to the JSON form the runner set's handlers read at runtime.'''
3340
+ return typing.cast(builtins.str, jsii.invoke(self, "toJson", []))
3341
+
3342
+ @builtins.property
3343
+ @jsii.member(jsii_name="kind")
3344
+ def kind(self) -> "RunnerScopeKind":
3345
+ '''Whether this scope is an organization or a list of repositories.'''
3346
+ return typing.cast("RunnerScopeKind", jsii.get(self, "kind"))
3347
+
3348
+ @builtins.property
3349
+ @jsii.member(jsii_name="organization")
3350
+ def organization(self) -> typing.Optional[builtins.str]:
3351
+ '''The organization, for a scope built with ``RunnerScope.org()``.'''
3352
+ return typing.cast(typing.Optional[builtins.str], jsii.get(self, "organization"))
3353
+
3354
+ @builtins.property
3355
+ @jsii.member(jsii_name="repositories")
3356
+ def repositories(self) -> typing.Optional[typing.List[builtins.str]]:
3357
+ '''The ``owner/repo`` list, for a scope built with ``RunnerScope.repos()``.'''
3358
+ return typing.cast(typing.Optional[typing.List[builtins.str]], jsii.get(self, "repositories"))
3359
+
3360
+
3361
+ @jsii.enum(jsii_type="cdk-github-microvm-runners.RunnerScopeKind")
3362
+ class RunnerScopeKind(enum.Enum):
3363
+ '''Which GitHub scope a ``RunnerScope`` represents.'''
3364
+
3365
+ ORG = "ORG"
3366
+ '''Runners are registered at the organization level.'''
3367
+ REPOS = "REPOS"
3368
+ '''Runners are registered against an explicit list of repositories.'''
3369
+
3370
+
3371
+ class RunnerToolchain(
3372
+ metaclass=jsii.JSIIMeta,
3373
+ jsii_type="cdk-github-microvm-runners.RunnerToolchain",
3374
+ ):
3375
+ '''A language runtime baked into the runner image's hosted tool cache at ``/opt/hostedtoolcache``, where ``actions/setup-python`` and ``actions/setup-node`` find it without downloading anything.
3376
+
3377
+ Those actions otherwise fetch
3378
+ OS-specific prebuilt runtimes, which are not published for the AL2023 image
3379
+ these runners use.
3380
+
3381
+ Several versions can be baked in at once, and a workflow asking for
3382
+ ``python-version: "3.12"`` matches a baked ``3.12.7``.
3383
+
3384
+ Build one with the static factories below; the constructor is private.
3385
+
3386
+ Example::
3387
+
3388
+ test_image = RunnerImage.from_options(
3389
+ toolchains=[
3390
+ RunnerToolchain.python("3.12.7"),
3391
+ RunnerToolchain.node("22.11.0")
3392
+ ]
3393
+ )
3394
+ '''
3395
+
3396
+ @jsii.member(jsii_name="node")
3397
+ @builtins.classmethod
3398
+ def node(cls, version: builtins.str) -> "RunnerToolchain":
3399
+ '''Node.js, from the official arm64 tarball. Full semver, e.g. ``'22.11.0'``.
3400
+
3401
+ :param version: -
3402
+
3403
+ Example::
3404
+
3405
+ node = RunnerToolchain.node("22.11.0")
3406
+ '''
3407
+ if __debug__:
3408
+ type_hints = cached_type_hints(_typecheckingstub__8065d4439361f9bbc84bece8879c41a0993b4a423dfaaebe72c927aabc755b9f)
3409
+ check_type(argname="argument version", value=version, expected_type=type_hints["version"])
3410
+ return typing.cast("RunnerToolchain", jsii.sinvoke(cls, "node", [version]))
3411
+
3412
+ @jsii.member(jsii_name="python")
3413
+ @builtins.classmethod
3414
+ def python(cls, version: builtins.str) -> "RunnerToolchain":
3415
+ '''CPython, built from source.
3416
+
3417
+ ``version`` is a full semver, e.g. ``'3.12.7'``.
3418
+
3419
+ :param version: -
3420
+
3421
+ Example::
3422
+
3423
+ python = RunnerToolchain.python("3.12.7")
3424
+ '''
3425
+ if __debug__:
3426
+ type_hints = cached_type_hints(_typecheckingstub__4f0bd7c1b2e6ac73b706efb23ff6a848ee29451cef59db547c475ce88a7b1cd8)
3427
+ check_type(argname="argument version", value=version, expected_type=type_hints["version"])
3428
+ return typing.cast("RunnerToolchain", jsii.sinvoke(cls, "python", [version]))
3429
+
3430
+ @builtins.property
3431
+ @jsii.member(jsii_name="kind")
3432
+ def kind(self) -> "ToolchainKind":
3433
+ '''Which runtime this is.'''
3434
+ return typing.cast("ToolchainKind", jsii.get(self, "kind"))
3435
+
3436
+ @builtins.property
3437
+ @jsii.member(jsii_name="version")
3438
+ def version(self) -> builtins.str:
3439
+ '''The full semver release baked in, e.g. ``'3.12.7'``.'''
3440
+ return typing.cast(builtins.str, jsii.get(self, "version"))
3441
+
3442
+
3443
+ class RunnerVersion(
3444
+ metaclass=jsii.JSIIMeta,
3445
+ jsii_type="cdk-github-microvm-runners.RunnerVersion",
3446
+ ):
3447
+ '''Which ``actions/runner`` release to install on the MicroVM image.
3448
+
3449
+ Build one with the static factories below; the constructor is private.
3450
+
3451
+ Example::
3452
+
3453
+ pinned_image = RunnerImage.from_options(
3454
+ runner_version=RunnerVersion.of("2.328.0")
3455
+ )
3456
+ '''
3457
+
3458
+ @jsii.member(jsii_name="latest")
3459
+ @builtins.classmethod
3460
+ def latest(cls) -> "RunnerVersion":
3461
+ '''Use the ``actions/runner`` release this library currently pins (``DEFAULT_RUNNER_VERSION``).
3462
+
3463
+ No version is carried on the instance; the
3464
+ image build fills the pinned value in at synth.
3465
+
3466
+ Example::
3467
+
3468
+ runner_version = RunnerVersion.latest()
3469
+ '''
3470
+ return typing.cast("RunnerVersion", jsii.sinvoke(cls, "latest", []))
3471
+
3472
+ @jsii.member(jsii_name="of")
3473
+ @builtins.classmethod
3474
+ def of(cls, version: builtins.str) -> "RunnerVersion":
3475
+ '''Pin an explicit ``actions/runner`` release, e.g. ``"2.319.1"``.
3476
+
3477
+ :param version: -
3478
+
3479
+ Example::
3480
+
3481
+ pinned_runner = RunnerVersion.of("2.328.0")
3482
+ '''
3483
+ if __debug__:
3484
+ type_hints = cached_type_hints(_typecheckingstub__3a8918cc0c6992425d68b709ebd9491ff2eb553b34ff18a312fc59f3604f2a65)
3485
+ check_type(argname="argument version", value=version, expected_type=type_hints["version"])
3486
+ return typing.cast("RunnerVersion", jsii.sinvoke(cls, "of", [version]))
3487
+
3488
+ @builtins.property
3489
+ @jsii.member(jsii_name="version")
3490
+ def version(self) -> typing.Optional[builtins.str]:
3491
+ '''The pinned release, for a version built with ``RunnerVersion.of()``. ``undefined`` for ``RunnerVersion.latest()``.'''
3492
+ return typing.cast(typing.Optional[builtins.str], jsii.get(self, "version"))
3493
+
3494
+
3495
+ @jsii.enum(jsii_type="cdk-github-microvm-runners.ToolchainKind")
3496
+ class ToolchainKind(enum.Enum):
3497
+ '''How a toolchain is installed into the image's hosted tool cache.'''
3498
+
3499
+ PYTHON = "PYTHON"
3500
+ '''CPython, built from source (``configure --prefix … --enable-shared``) on AL2023.'''
3501
+ NODE = "NODE"
3502
+ '''Node.js, unpacked from the official nodejs.org linux-arm64 tarball.'''
3503
+
3504
+
3505
+ class WebhookEndpoint(
3506
+ metaclass=jsii.JSIIMeta,
3507
+ jsii_type="cdk-github-microvm-runners.WebhookEndpoint",
3508
+ ):
3509
+ '''How the webhook handler is exposed to GitHub's ``workflow_job`` deliveries.
3510
+
3511
+ The one form today is a Lambda Function URL. It is created with
3512
+ ``authType: NONE``; the auth boundary is the HMAC-SHA256 signature GitHub
3513
+ sends with every delivery, which the handler verifies against the webhook
3514
+ secret before it does anything else.
3515
+
3516
+ Example::
3517
+
3518
+ GithubMicrovmRunners(stack, "Runners",
3519
+ github=github,
3520
+ scope=scope,
3521
+ webhook=WebhookEndpoint.function_url()
3522
+ )
3523
+ '''
3524
+
3525
+ @jsii.member(jsii_name="functionUrl")
3526
+ @builtins.classmethod
3527
+ def function_url(cls) -> "WebhookEndpoint":
3528
+ '''Expose the webhook handler on a Lambda Function URL.
3529
+
3530
+ Example::
3531
+
3532
+ webhook = WebhookEndpoint.function_url()
3533
+ '''
3534
+ return typing.cast("WebhookEndpoint", jsii.sinvoke(cls, "functionUrl", []))
3535
+
3536
+ @builtins.property
3537
+ @jsii.member(jsii_name="kind")
3538
+ def kind(self) -> "WebhookEndpointKind":
3539
+ '''Which form of endpoint this instance represents.'''
3540
+ return typing.cast("WebhookEndpointKind", jsii.get(self, "kind"))
3541
+
3542
+
3543
+ @jsii.enum(jsii_type="cdk-github-microvm-runners.WebhookEndpointKind")
3544
+ class WebhookEndpointKind(enum.Enum):
3545
+ '''Which form of endpoint a ``WebhookEndpoint`` represents.'''
3546
+
3547
+ FUNCTION_URL = "FUNCTION_URL"
3548
+ '''A Lambda Function URL.'''
3549
+
3550
+
3551
+ __all__ = [
3552
+ "ConsoleLogs",
3553
+ "GithubAppAuthProps",
3554
+ "GithubAppId",
3555
+ "GithubAppKey",
3556
+ "GithubAuth",
3557
+ "GithubAuthKind",
3558
+ "GithubMicrovmRunners",
3559
+ "GithubMicrovmRunnersMetrics",
3560
+ "GithubMicrovmRunnersProps",
3561
+ "GithubPatAuthProps",
3562
+ "ImageAsset",
3563
+ "ImageLogs",
3564
+ "ImagePipeline",
3565
+ "ImagePipelineProps",
3566
+ "MicrovmIdlePolicy",
3567
+ "MicrovmSize",
3568
+ "RunnerAlarmOptions",
3569
+ "RunnerClass",
3570
+ "RunnerClassProps",
3571
+ "RunnerImage",
3572
+ "RunnerImageOptions",
3573
+ "RunnerNetwork",
3574
+ "RunnerNetworkKind",
3575
+ "RunnerNetworkVpcOptions",
3576
+ "RunnerScope",
3577
+ "RunnerScopeKind",
3578
+ "RunnerToolchain",
3579
+ "RunnerVersion",
3580
+ "ToolchainKind",
3581
+ "WebhookEndpoint",
3582
+ "WebhookEndpointKind",
3583
+ ]
3584
+
3585
+ publication.publish()
3586
+
3587
+ def _typecheckingstub__0548cfa81769608b199a2a959830e9d47b13bb916b5f4181e977c1f03bfa81c7(
3588
+ log_group: typing.Optional[_aws_cdk_aws_logs_ceddda9d.ILogGroup] = None,
3589
+ ) -> None:
3590
+ """Type checking stubs"""
3591
+ pass
3592
+
3593
+ def _typecheckingstub__e6e950b235861eb6ceb59c62bebf431836fac157088b1334fedf915b19dec8a0(
3594
+ *,
3595
+ app_id: GithubAppId,
3596
+ private_key: GithubAppKey,
3597
+ webhook_secret: _aws_cdk_aws_secretsmanager_ceddda9d.ISecret,
3598
+ ) -> None:
3599
+ """Type checking stubs"""
3600
+ pass
3601
+
3602
+ def _typecheckingstub__aba0941864c428347812aa10a096b18df3ae77616773316bd134e2764ae2657c(
3603
+ secret: _aws_cdk_aws_secretsmanager_ceddda9d.ISecret,
3604
+ ) -> None:
3605
+ """Type checking stubs"""
3606
+ pass
3607
+
3608
+ def _typecheckingstub__6b22d9ed14d289886f4ed34dc8cfd25d58e6bbda42419d5097a7a73ea48bd10f(
3609
+ value: builtins.str,
3610
+ ) -> None:
3611
+ """Type checking stubs"""
3612
+ pass
3613
+
3614
+ def _typecheckingstub__cc9961f681c63592cc8d260fe3e536a71239765b00a83bdec348eff483581f2b(
3615
+ key: _aws_cdk_aws_kms_ceddda9d.IKey,
3616
+ ) -> None:
3617
+ """Type checking stubs"""
3618
+ pass
3619
+
3620
+ def _typecheckingstub__b690547df91198ce68555c0eef4906781d6764b68601422750750169edd60220(
3621
+ secret: _aws_cdk_aws_secretsmanager_ceddda9d.ISecret,
3622
+ ) -> None:
3623
+ """Type checking stubs"""
3624
+ pass
3625
+
3626
+ def _typecheckingstub__970e66a646e2fc86c035066057cff7cd247b431b227ac9f2d5a8c86ed710c988(
3627
+ grantee: _aws_cdk_aws_iam_ceddda9d.IGrantable,
3628
+ ) -> None:
3629
+ """Type checking stubs"""
3630
+ pass
3631
+
3632
+ def _typecheckingstub__80b1bb7036f50148e74587e4f8bac689760f9f314afddfa588fbc2a86d5062dd(
3633
+ grantee: _aws_cdk_aws_iam_ceddda9d.IGrantable,
3634
+ ) -> None:
3635
+ """Type checking stubs"""
3636
+ pass
3637
+
3638
+ def _typecheckingstub__f9ccf90cd58a24eaea8e05c002c729a7c0b56c6afdd04d8cd485938eed8facc5(
3639
+ scope_: _constructs_77d1e7e8.Construct,
3640
+ id: builtins.str,
3641
+ *,
3642
+ github: GithubAuth,
3643
+ scope: RunnerScope,
3644
+ additional_regions: typing.Optional[typing.Sequence[builtins.str]] = None,
3645
+ console_logs: typing.Optional[ConsoleLogs] = None,
3646
+ dead_letter_retention: typing.Optional[_aws_cdk_ceddda9d.Duration] = None,
3647
+ emit_metrics: typing.Optional[builtins.bool] = None,
3648
+ encryption_key: typing.Optional[_aws_cdk_aws_kms_ceddda9d.IKey] = None,
3649
+ idle_runner_grace_seconds: typing.Optional[jsii.Number] = None,
3650
+ image_logs: typing.Optional[ImageLogs] = None,
3651
+ janitor_interval: typing.Optional[_aws_cdk_ceddda9d.Duration] = None,
3652
+ keep_image_versions: typing.Optional[jsii.Number] = None,
3653
+ lambda_memory_size: typing.Optional[jsii.Number] = None,
3654
+ log_retention: typing.Optional[_aws_cdk_aws_logs_ceddda9d.RetentionDays] = None,
3655
+ max_concurrent_vms: typing.Optional[jsii.Number] = None,
3656
+ max_job_duration: typing.Optional[_aws_cdk_ceddda9d.Duration] = None,
3657
+ max_receive_count: typing.Optional[jsii.Number] = None,
3658
+ network: typing.Optional[RunnerNetwork] = None,
3659
+ permissions_boundary: typing.Optional[_aws_cdk_aws_iam_ceddda9d.IManagedPolicy] = None,
3660
+ point_in_time_recovery: typing.Optional[builtins.bool] = None,
3661
+ recover_stuck_launches: typing.Optional[builtins.bool] = None,
3662
+ removal_policy: typing.Optional[_aws_cdk_ceddda9d.RemovalPolicy] = None,
3663
+ vm_execution_role: typing.Optional[_aws_cdk_aws_iam_ceddda9d.IRole] = None,
3664
+ warm_pool_interval: typing.Optional[_aws_cdk_ceddda9d.Duration] = None,
3665
+ webhook: typing.Optional[WebhookEndpoint] = None,
3666
+ webhook_reserved_concurrency: typing.Optional[jsii.Number] = None,
3667
+ ) -> None:
3668
+ """Type checking stubs"""
3669
+ pass
3670
+
3671
+ def _typecheckingstub__48e77fc4d43555e4d6ac3d9e5b13de1cbbb1ef807cfc4ce1e0688ac170b863d1(
3672
+ label: builtins.str,
3673
+ *,
3674
+ size: MicrovmSize,
3675
+ idle_policy: typing.Optional[typing.Union[MicrovmIdlePolicy, typing.Dict[builtins.str, typing.Any]]] = None,
3676
+ image: typing.Optional[RunnerImage] = None,
3677
+ warm_pool_size: typing.Optional[jsii.Number] = None,
3678
+ ) -> None:
3679
+ """Type checking stubs"""
3680
+ pass
3681
+
3682
+ def _typecheckingstub__e74a92121e3eaec69c4009cc027a299a12257a68eccc613318213d92545265f4(
3683
+ label: builtins.str,
3684
+ ) -> None:
3685
+ """Type checking stubs"""
3686
+ pass
3687
+
3688
+ def _typecheckingstub__8c3f47f3bf65ddc91d8c105e318179909d2860c56e34cc7b699066e3f7952170(
3689
+ runner_set_id: builtins.str,
3690
+ dead_letter_queue: _aws_cdk_aws_sqs_ceddda9d.IQueue,
3691
+ emit_metrics: typing.Optional[builtins.bool] = None,
3692
+ ) -> None:
3693
+ """Type checking stubs"""
3694
+ pass
3695
+
3696
+ def _typecheckingstub__5f2f67ec6dfaab44c26a53e7bc3669af5ba74fc2c75f8dcd8afb795779d8ddfa(
3697
+ runner_class_label: builtins.str,
3698
+ ) -> None:
3699
+ """Type checking stubs"""
3700
+ pass
3701
+
3702
+ def _typecheckingstub__36d476c29eaf47f8fca7a5205fb5caa5a9749233fb462a093763a6a3200429b4(
3703
+ runner_class_label: builtins.str,
3704
+ ) -> None:
3705
+ """Type checking stubs"""
3706
+ pass
3707
+
3708
+ def _typecheckingstub__baf385f806ff6b0b2ae70b9d811654097d3297503bd8bdbafe09f5d9ed879629(
3709
+ runner_class_label: builtins.str,
3710
+ ) -> None:
3711
+ """Type checking stubs"""
3712
+ pass
3713
+
3714
+ def _typecheckingstub__75224a1d56fc14f38e94a3639da5e4ae455f069c6e477bb8fa010333e5c6ae4f(
3715
+ runner_class_label: builtins.str,
3716
+ ) -> None:
3717
+ """Type checking stubs"""
3718
+ pass
3719
+
3720
+ def _typecheckingstub__aca740a7276ae0a556b6b45300b0252ac7d8074b4dd0ab26c6b38fba7adb7eaf(
3721
+ scope: _constructs_77d1e7e8.Construct,
3722
+ *,
3723
+ evaluation_periods: typing.Optional[jsii.Number] = None,
3724
+ period: typing.Optional[_aws_cdk_ceddda9d.Duration] = None,
3725
+ threshold: typing.Optional[jsii.Number] = None,
3726
+ ) -> None:
3727
+ """Type checking stubs"""
3728
+ pass
3729
+
3730
+ def _typecheckingstub__6153c9a068c30f370bb352b26cc502516866238676a7d8139fc2dd0736dff46a(
3731
+ runner_class_label: builtins.str,
3732
+ ) -> None:
3733
+ """Type checking stubs"""
3734
+ pass
3735
+
3736
+ def _typecheckingstub__767e219d8c25f069fd8964ea100816a20869884628a2606dfd2fb3e63b857a8e(
3737
+ runner_class_label: builtins.str,
3738
+ ) -> None:
3739
+ """Type checking stubs"""
3740
+ pass
3741
+
3742
+ def _typecheckingstub__6513962ff7d13ce627041f61f3e7a3b13688b30e0cb4c5bfbc63f79b8a32b595(
3743
+ runner_class_label: builtins.str,
3744
+ ) -> None:
3745
+ """Type checking stubs"""
3746
+ pass
3747
+
3748
+ def _typecheckingstub__1ba8a40c62efe6c3a58896ea614244a8e6823dd3c7db51567ffbadd71766bd13(
3749
+ runner_class_label: builtins.str,
3750
+ ) -> None:
3751
+ """Type checking stubs"""
3752
+ pass
3753
+
3754
+ def _typecheckingstub__e1b4b6b8be54f5289f47b9d302cea17d405e67837d60873b6a6536a2a23457e9(
3755
+ scope: _constructs_77d1e7e8.Construct,
3756
+ *,
3757
+ evaluation_periods: typing.Optional[jsii.Number] = None,
3758
+ period: typing.Optional[_aws_cdk_ceddda9d.Duration] = None,
3759
+ threshold: typing.Optional[jsii.Number] = None,
3760
+ ) -> None:
3761
+ """Type checking stubs"""
3762
+ pass
3763
+
3764
+ def _typecheckingstub__971f59e7dc4d5b4245765d41b7dcc4075c4dd91870a15df6b9f345ee393cdc61(
3765
+ scope: _constructs_77d1e7e8.Construct,
3766
+ *,
3767
+ evaluation_periods: typing.Optional[jsii.Number] = None,
3768
+ period: typing.Optional[_aws_cdk_ceddda9d.Duration] = None,
3769
+ threshold: typing.Optional[jsii.Number] = None,
3770
+ ) -> None:
3771
+ """Type checking stubs"""
3772
+ pass
3773
+
3774
+ def _typecheckingstub__01d47d24112989967339ef8a2bc09fe442210a13ccc3d3fd9583327ebc550f4f(
3775
+ runner_class_label: builtins.str,
3776
+ ) -> None:
3777
+ """Type checking stubs"""
3778
+ pass
3779
+
3780
+ def _typecheckingstub__9b66518a0eb395295f3c9719dcc545a363c50d0d387fe70bdcedc10be787b17a(
3781
+ runner_class_label: builtins.str,
3782
+ ) -> None:
3783
+ """Type checking stubs"""
3784
+ pass
3785
+
3786
+ def _typecheckingstub__28779afe2a8160e3a792fe0b6661c1c177712ed22d8ddf991f39b625eaa303d6(
3787
+ runner_class_label: builtins.str,
3788
+ ) -> None:
3789
+ """Type checking stubs"""
3790
+ pass
3791
+
3792
+ def _typecheckingstub__17269a525bc0ad3b7797a5131db455fcbc4ced673bbd2895bc90ea581a540971(
3793
+ *,
3794
+ github: GithubAuth,
3795
+ scope: RunnerScope,
3796
+ additional_regions: typing.Optional[typing.Sequence[builtins.str]] = None,
3797
+ console_logs: typing.Optional[ConsoleLogs] = None,
3798
+ dead_letter_retention: typing.Optional[_aws_cdk_ceddda9d.Duration] = None,
3799
+ emit_metrics: typing.Optional[builtins.bool] = None,
3800
+ encryption_key: typing.Optional[_aws_cdk_aws_kms_ceddda9d.IKey] = None,
3801
+ idle_runner_grace_seconds: typing.Optional[jsii.Number] = None,
3802
+ image_logs: typing.Optional[ImageLogs] = None,
3803
+ janitor_interval: typing.Optional[_aws_cdk_ceddda9d.Duration] = None,
3804
+ keep_image_versions: typing.Optional[jsii.Number] = None,
3805
+ lambda_memory_size: typing.Optional[jsii.Number] = None,
3806
+ log_retention: typing.Optional[_aws_cdk_aws_logs_ceddda9d.RetentionDays] = None,
3807
+ max_concurrent_vms: typing.Optional[jsii.Number] = None,
3808
+ max_job_duration: typing.Optional[_aws_cdk_ceddda9d.Duration] = None,
3809
+ max_receive_count: typing.Optional[jsii.Number] = None,
3810
+ network: typing.Optional[RunnerNetwork] = None,
3811
+ permissions_boundary: typing.Optional[_aws_cdk_aws_iam_ceddda9d.IManagedPolicy] = None,
3812
+ point_in_time_recovery: typing.Optional[builtins.bool] = None,
3813
+ recover_stuck_launches: typing.Optional[builtins.bool] = None,
3814
+ removal_policy: typing.Optional[_aws_cdk_ceddda9d.RemovalPolicy] = None,
3815
+ vm_execution_role: typing.Optional[_aws_cdk_aws_iam_ceddda9d.IRole] = None,
3816
+ warm_pool_interval: typing.Optional[_aws_cdk_ceddda9d.Duration] = None,
3817
+ webhook: typing.Optional[WebhookEndpoint] = None,
3818
+ webhook_reserved_concurrency: typing.Optional[jsii.Number] = None,
3819
+ ) -> None:
3820
+ """Type checking stubs"""
3821
+ pass
3822
+
3823
+ def _typecheckingstub__af35288b9c48eea9cffeb6e1080b684e480ae2227583407499a01041a243a072(
3824
+ *,
3825
+ token: _aws_cdk_aws_secretsmanager_ceddda9d.ISecret,
3826
+ webhook_secret: _aws_cdk_aws_secretsmanager_ceddda9d.ISecret,
3827
+ ) -> None:
3828
+ """Type checking stubs"""
3829
+ pass
3830
+
3831
+ def _typecheckingstub__d7d3da112d28b428bdfaadffe313adf1b4f0e1018b1cbf217c7687e156912c49(
3832
+ *,
3833
+ source: builtins.str,
3834
+ target: builtins.str,
3835
+ ) -> None:
3836
+ """Type checking stubs"""
3837
+ pass
3838
+
3839
+ def _typecheckingstub__5c18afa4a05ee3b3cf8019fa8e3bf98a9dc803b4ff31600008a4916dce15cd6a(
3840
+ log_group: typing.Optional[_aws_cdk_aws_logs_ceddda9d.ILogGroup] = None,
3841
+ ) -> None:
3842
+ """Type checking stubs"""
3843
+ pass
3844
+
3845
+ def _typecheckingstub__38e8b0a65e40c9bfd130b4244eef3af238aac180d3c10606a513fef86e7aa169(
3846
+ scope: _constructs_77d1e7e8.Construct,
3847
+ id: builtins.str,
3848
+ *,
3849
+ image: RunnerImage,
3850
+ network: RunnerNetwork,
3851
+ runner_set_id: builtins.str,
3852
+ size: MicrovmSize,
3853
+ base_image_version: typing.Optional[builtins.str] = None,
3854
+ image_logs: typing.Optional[ImageLogs] = None,
3855
+ ready_timeout_seconds: typing.Optional[jsii.Number] = None,
3856
+ run_timeout_seconds: typing.Optional[jsii.Number] = None,
3857
+ ) -> None:
3858
+ """Type checking stubs"""
3859
+ pass
3860
+
3861
+ def _typecheckingstub__c8c6d3e3f7a844305d6a7d687bb74edba13926e6cdb04020bf45116bbb7f2407(
3862
+ *,
3863
+ image: RunnerImage,
3864
+ network: RunnerNetwork,
3865
+ runner_set_id: builtins.str,
3866
+ size: MicrovmSize,
3867
+ base_image_version: typing.Optional[builtins.str] = None,
3868
+ image_logs: typing.Optional[ImageLogs] = None,
3869
+ ready_timeout_seconds: typing.Optional[jsii.Number] = None,
3870
+ run_timeout_seconds: typing.Optional[jsii.Number] = None,
3871
+ ) -> None:
3872
+ """Type checking stubs"""
3873
+ pass
3874
+
3875
+ def _typecheckingstub__299a6933558be6e9144d105ecb8f71fdfa2731d8f7934fa33118eb063140eb10(
3876
+ *,
3877
+ max_idle_duration: _aws_cdk_ceddda9d.Duration,
3878
+ suspended_duration: _aws_cdk_ceddda9d.Duration,
3879
+ auto_resume: typing.Optional[builtins.bool] = None,
3880
+ ) -> None:
3881
+ """Type checking stubs"""
3882
+ pass
3883
+
3884
+ def _typecheckingstub__117c59b0a49dcc58de82f614adb71603b95518cace779312d23dbc393e930d0f(
3885
+ *,
3886
+ evaluation_periods: typing.Optional[jsii.Number] = None,
3887
+ period: typing.Optional[_aws_cdk_ceddda9d.Duration] = None,
3888
+ threshold: typing.Optional[jsii.Number] = None,
3889
+ ) -> None:
3890
+ """Type checking stubs"""
3891
+ pass
3892
+
3893
+ def _typecheckingstub__23472a12727bc341ce55563246f68a52c49776a6814a52ab825cec01db0cdc89(
3894
+ *,
3895
+ image_arn: builtins.str,
3896
+ image_pipeline: ImagePipeline,
3897
+ label: builtins.str,
3898
+ size: MicrovmSize,
3899
+ ) -> None:
3900
+ """Type checking stubs"""
3901
+ pass
3902
+
3903
+ def _typecheckingstub__6e46f2bbd7884caef4f137e6dac784cd99ef109d84b58f3f7acf963f03c723a7(
3904
+ *,
3905
+ size: MicrovmSize,
3906
+ idle_policy: typing.Optional[typing.Union[MicrovmIdlePolicy, typing.Dict[builtins.str, typing.Any]]] = None,
3907
+ image: typing.Optional[RunnerImage] = None,
3908
+ warm_pool_size: typing.Optional[jsii.Number] = None,
3909
+ ) -> None:
3910
+ """Type checking stubs"""
3911
+ pass
3912
+
3913
+ def _typecheckingstub__979f58111abc04b49545772d5ca529893452a46b62def883e284f0c2b1f0f73d(
3914
+ dir: builtins.str,
3915
+ ) -> None:
3916
+ """Type checking stubs"""
3917
+ pass
3918
+
3919
+ def _typecheckingstub__2d9215b22cc740b2c93f48e4e0744fe50e43a7a33f85974d48dbebd1e184d9d4(
3920
+ dockerfile: builtins.str,
3921
+ ) -> None:
3922
+ """Type checking stubs"""
3923
+ pass
3924
+
3925
+ def _typecheckingstub__6fd3c247efc1bf4be6517722f43505c0df1d4ecea290c5f473194c94910a6cb8(
3926
+ *,
3927
+ additional_os_capabilities: typing.Optional[typing.Sequence[builtins.str]] = None,
3928
+ assets: typing.Optional[typing.Sequence[typing.Union[ImageAsset, typing.Dict[builtins.str, typing.Any]]]] = None,
3929
+ environment: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
3930
+ runner_version: typing.Optional[RunnerVersion] = None,
3931
+ setup_commands: typing.Optional[typing.Sequence[builtins.str]] = None,
3932
+ system_packages: typing.Optional[typing.Sequence[builtins.str]] = None,
3933
+ toolchains: typing.Optional[typing.Sequence[RunnerToolchain]] = None,
3934
+ ) -> None:
3935
+ """Type checking stubs"""
3936
+ pass
3937
+
3938
+ def _typecheckingstub__d2491e4912faff169fbaad1c4fb389eb29f77ce187a242a0af01526ea53ad4d7(
3939
+ vpc: _aws_cdk_aws_ec2_ceddda9d.IVpc,
3940
+ *,
3941
+ security_groups: typing.Optional[typing.Sequence[_aws_cdk_aws_ec2_ceddda9d.ISecurityGroup]] = None,
3942
+ subnets: typing.Optional[typing.Union[_aws_cdk_aws_ec2_ceddda9d.SubnetSelection, typing.Dict[builtins.str, typing.Any]]] = None,
3943
+ ) -> None:
3944
+ """Type checking stubs"""
3945
+ pass
3946
+
3947
+ def _typecheckingstub__6db194aaaafe63c89cd0b5bfe1846487e92f6c50fdaa2c8c2e3d8689ff54dfd1(
3948
+ connector_arns: typing.Sequence[builtins.str],
3949
+ ) -> None:
3950
+ """Type checking stubs"""
3951
+ pass
3952
+
3953
+ def _typecheckingstub__940d8682a239c73072340b0492130025e2f1d8c0d2614466732fa878426abf8c(
3954
+ *,
3955
+ security_groups: typing.Optional[typing.Sequence[_aws_cdk_aws_ec2_ceddda9d.ISecurityGroup]] = None,
3956
+ subnets: typing.Optional[typing.Union[_aws_cdk_aws_ec2_ceddda9d.SubnetSelection, typing.Dict[builtins.str, typing.Any]]] = None,
3957
+ ) -> None:
3958
+ """Type checking stubs"""
3959
+ pass
3960
+
3961
+ def _typecheckingstub__a306ec12ac8b86304298f1b4650c1ad69974d0773eef2b6f6245b92666d89c8f(
3962
+ org: builtins.str,
3963
+ ) -> None:
3964
+ """Type checking stubs"""
3965
+ pass
3966
+
3967
+ def _typecheckingstub__a81b46da719563a103d6a4dc08b1337ad955b8bb3e417594cba8689ec1b3990e(
3968
+ repos: typing.Sequence[builtins.str],
3969
+ ) -> None:
3970
+ """Type checking stubs"""
3971
+ pass
3972
+
3973
+ def _typecheckingstub__8065d4439361f9bbc84bece8879c41a0993b4a423dfaaebe72c927aabc755b9f(
3974
+ version: builtins.str,
3975
+ ) -> None:
3976
+ """Type checking stubs"""
3977
+ pass
3978
+
3979
+ def _typecheckingstub__4f0bd7c1b2e6ac73b706efb23ff6a848ee29451cef59db547c475ce88a7b1cd8(
3980
+ version: builtins.str,
3981
+ ) -> None:
3982
+ """Type checking stubs"""
3983
+ pass
3984
+
3985
+ def _typecheckingstub__3a8918cc0c6992425d68b709ebd9491ff2eb553b34ff18a312fc59f3604f2a65(
3986
+ version: builtins.str,
3987
+ ) -> None:
3988
+ """Type checking stubs"""
3989
+ pass