terraform-cdk-serverless-github-actions-runner-controller 0.0.0 → 0.0.2

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,355 @@
1
+ {
2
+ "//": {
3
+ "metadata": {
4
+ "backend": "local",
5
+ "stackName": "google-cloud-run",
6
+ "version": "0.20.0"
7
+ },
8
+ "outputs": {
9
+ }
10
+ },
11
+ "locals": {
12
+ "cr_autoscalerMember_AA00D082": "serviceAccount:${google_service_account.cr_autoscalerServiceAccount_37C5FFAD.email}",
13
+ "cr_bucketModification_B1ECB227": "CLOUDSDK_CORE_DISABLE_PROMPTS=1 gcloud alpha storage buckets create gs://gha-runner-job-externals --project=gha-runner-example --location=europe-north1 --uniform-bucket-level-access --enable-hierarchical-namespace",
14
+ "cr_ghaMember_2ADFD977": "serviceAccount:${google_service_account.cr_jobServiceAccount_4D2CB679.email}"
15
+ },
16
+ "resource": {
17
+ "google_artifact_registry_repository": {
18
+ "cr_registry_886F88F0": {
19
+ "//": {
20
+ "metadata": {
21
+ "path": "google-cloud-run/cr/registry",
22
+ "uniqueId": "cr_registry_886F88F0"
23
+ }
24
+ },
25
+ "description": "Repository to host run and resulting images from GHA runs",
26
+ "format": "DOCKER",
27
+ "mode": "REMOTE_REPOSITORY",
28
+ "remote_repository_config": {
29
+ "docker_repository": {
30
+ "custom_repository": {
31
+ "uri": "https://ghcr.io"
32
+ }
33
+ }
34
+ },
35
+ "repository_id": "gha-runner-test"
36
+ }
37
+ },
38
+ "google_cloud_run_service": {
39
+ "cr_autoscalerService_A4FAA2B6": {
40
+ "//": {
41
+ "metadata": {
42
+ "path": "google-cloud-run/cr/autoscalerService",
43
+ "uniqueId": "cr_autoscalerService_A4FAA2B6"
44
+ }
45
+ },
46
+ "location": "europe-north1",
47
+ "metadata": {
48
+ "annotations": {
49
+ "run.googleapis.com/ingress": "internal"
50
+ }
51
+ },
52
+ "name": "gha-autoscaler",
53
+ "template": {
54
+ "metadata": {
55
+ "annotations": {
56
+ "autoscaling.knative.dev/maxScale": "1",
57
+ "autoscaling.knative.dev/minScale": "1",
58
+ "run.googleapis.com/cpu-throttling": "false",
59
+ "run.googleapis.com/startup-cpu-boost": "false"
60
+ }
61
+ },
62
+ "spec": {
63
+ "container_concurrency": 1,
64
+ "containers": [
65
+ {
66
+ "env": [
67
+ {
68
+ "name": "PAT",
69
+ "value": "${var.PAT}"
70
+ },
71
+ {
72
+ "name": "GITHUB_CONFIG_URL",
73
+ "value": "${var.github_config_url}"
74
+ },
75
+ {
76
+ "name": "JOB_NAME",
77
+ "value": "${google_cloud_run_v2_job.cr_ghaJob_3BDFC08A.name}"
78
+ },
79
+ {
80
+ "name": "SCALE_SET_NAME",
81
+ "value": "cr-runner-set"
82
+ },
83
+ {
84
+ "name": "CLOUDSDK_RUN_REGION",
85
+ "value": "europe-north1"
86
+ },
87
+ {
88
+ "name": "GOOGLE_CLOUD_PROJECT",
89
+ "value": "gha-runner-example"
90
+ }
91
+ ],
92
+ "image": "${google_artifact_registry_repository.cr_registry_886F88F0.location}-docker.pkg.dev/gha-runner-example/${google_artifact_registry_repository.cr_registry_886F88F0.repository_id}/hi-fi/gha-runners-on-managed-env:test",
93
+ "resources": {
94
+ "limits": {
95
+ "cpu": "1000m",
96
+ "memory": "512Mi"
97
+ }
98
+ }
99
+ }
100
+ ],
101
+ "service_account_name": "${google_service_account.cr_autoscalerServiceAccount_37C5FFAD.email}"
102
+ }
103
+ }
104
+ }
105
+ },
106
+ "google_cloud_run_v2_job": {
107
+ "cr_ghaJob_3BDFC08A": {
108
+ "//": {
109
+ "metadata": {
110
+ "path": "google-cloud-run/cr/ghaJob",
111
+ "uniqueId": "cr_ghaJob_3BDFC08A"
112
+ }
113
+ },
114
+ "deletion_protection": false,
115
+ "depends_on": [
116
+ "null_resource.cr_gcloud_3D7A726F"
117
+ ],
118
+ "location": "europe-north1",
119
+ "name": "gha-runner-job",
120
+ "template": {
121
+ "template": {
122
+ "containers": [
123
+ {
124
+ "command": [
125
+ "/home/runner/ephemeral_runner.sh"
126
+ ],
127
+ "env": [
128
+ {
129
+ "name": "CLOUDSDK_RUN_REGION",
130
+ "value": "europe-north1"
131
+ },
132
+ {
133
+ "name": "GOOGLE_CLOUD_PROJECT",
134
+ "value": "gha-runner-example"
135
+ },
136
+ {
137
+ "name": "EXTERNAL_STORAGE_NAME",
138
+ "value": "gha-runner-job-externals"
139
+ },
140
+ {
141
+ "name": "TAR_OPTIONS",
142
+ "value": "--touch --no-overwrite-dir --no-same-owner"
143
+ }
144
+ ],
145
+ "image": "${google_artifact_registry_repository.cr_registry_886F88F0.location}-docker.pkg.dev/gha-runner-example/${google_artifact_registry_repository.cr_registry_886F88F0.repository_id}/hi-fi/actions-runner:cr",
146
+ "resources": {
147
+ "limits": {
148
+ "cpu": "1",
149
+ "memory": "2Gi"
150
+ }
151
+ },
152
+ "volume_mounts": [
153
+ {
154
+ "mount_path": "/home/runner/_work/externals",
155
+ "name": "externals"
156
+ }
157
+ ]
158
+ }
159
+ ],
160
+ "max_retries": 0,
161
+ "service_account": "${google_service_account.cr_jobServiceAccount_4D2CB679.email}",
162
+ "volumes": [
163
+ {
164
+ "gcs": {
165
+ "bucket": "gha-runner-job-externals"
166
+ },
167
+ "name": "externals"
168
+ }
169
+ ]
170
+ }
171
+ }
172
+ }
173
+ },
174
+ "google_project_iam_custom_role": {
175
+ "cr_autoscalerRole_8A950337": {
176
+ "//": {
177
+ "metadata": {
178
+ "path": "google-cloud-run/cr/autoscalerRole",
179
+ "uniqueId": "cr_autoscalerRole_8A950337"
180
+ }
181
+ },
182
+ "permissions": [
183
+ "artifactregistry.dockerimages.get",
184
+ "artifactregistry.dockerimages.list",
185
+ "run.jobs.run",
186
+ "run.jobs.create",
187
+ "run.jobs.delete"
188
+ ],
189
+ "role_id": "ghaAutoscalerRole",
190
+ "title": "GHA Autoscaler Role"
191
+ },
192
+ "cr_runnerRole_07508BC4": {
193
+ "//": {
194
+ "metadata": {
195
+ "path": "google-cloud-run/cr/runnerRole",
196
+ "uniqueId": "cr_runnerRole_07508BC4"
197
+ }
198
+ },
199
+ "permissions": [
200
+ "artifactregistry.dockerimages.get",
201
+ "artifactregistry.dockerimages.list",
202
+ "run.jobs.run",
203
+ "run.jobs.create",
204
+ "run.jobs.delete",
205
+ "run.jobs.list",
206
+ "run.executions.get"
207
+ ],
208
+ "role_id": "ghaRunnerRole",
209
+ "title": "GHA Runner Role"
210
+ }
211
+ },
212
+ "google_project_iam_member": {
213
+ "cr_autoscalerRoleBindingRunServiceAgent_83C78179": {
214
+ "//": {
215
+ "metadata": {
216
+ "path": "google-cloud-run/cr/autoscalerRoleBindingRunServiceAgent",
217
+ "uniqueId": "cr_autoscalerRoleBindingRunServiceAgent_83C78179"
218
+ }
219
+ },
220
+ "member": "${local.cr_autoscalerMember_AA00D082}",
221
+ "project": "gha-runner-example",
222
+ "role": "roles/run.serviceAgent"
223
+ },
224
+ "cr_autoscalerRoleBindingRun_76E1E813": {
225
+ "//": {
226
+ "metadata": {
227
+ "path": "google-cloud-run/cr/autoscalerRoleBindingRun",
228
+ "uniqueId": "cr_autoscalerRoleBindingRun_76E1E813"
229
+ }
230
+ },
231
+ "member": "${local.cr_autoscalerMember_AA00D082}",
232
+ "project": "gha-runner-example",
233
+ "role": "roles/run.developer"
234
+ },
235
+ "cr_autoscalerRoleBindingStorage_C1A676AA": {
236
+ "//": {
237
+ "metadata": {
238
+ "path": "google-cloud-run/cr/autoscalerRoleBindingStorage",
239
+ "uniqueId": "cr_autoscalerRoleBindingStorage_C1A676AA"
240
+ }
241
+ },
242
+ "member": "${local.cr_autoscalerMember_AA00D082}",
243
+ "project": "gha-runner-example",
244
+ "role": "roles/storage.admin"
245
+ },
246
+ "cr_runnerRoleBindingRunServiceAgent_88652FB8": {
247
+ "//": {
248
+ "metadata": {
249
+ "path": "google-cloud-run/cr/runnerRoleBindingRunServiceAgent",
250
+ "uniqueId": "cr_runnerRoleBindingRunServiceAgent_88652FB8"
251
+ }
252
+ },
253
+ "member": "${local.cr_ghaMember_2ADFD977}",
254
+ "project": "gha-runner-example",
255
+ "role": "roles/run.serviceAgent"
256
+ },
257
+ "cr_runnerRoleBindingRunViewer_328C548F": {
258
+ "//": {
259
+ "metadata": {
260
+ "path": "google-cloud-run/cr/runnerRoleBindingRunViewer",
261
+ "uniqueId": "cr_runnerRoleBindingRunViewer_328C548F"
262
+ }
263
+ },
264
+ "member": "${local.cr_ghaMember_2ADFD977}",
265
+ "project": "gha-runner-example",
266
+ "role": "roles/run.viewer"
267
+ },
268
+ "cr_runnerRoleBindingStorage_8D8C3B87": {
269
+ "//": {
270
+ "metadata": {
271
+ "path": "google-cloud-run/cr/runnerRoleBindingStorage",
272
+ "uniqueId": "cr_runnerRoleBindingStorage_8D8C3B87"
273
+ }
274
+ },
275
+ "member": "${local.cr_ghaMember_2ADFD977}",
276
+ "project": "gha-runner-example",
277
+ "role": "roles/storage.admin"
278
+ },
279
+ "cr_runnerRoleBinding_E20193A5": {
280
+ "//": {
281
+ "metadata": {
282
+ "path": "google-cloud-run/cr/runnerRoleBinding",
283
+ "uniqueId": "cr_runnerRoleBinding_E20193A5"
284
+ }
285
+ },
286
+ "member": "${local.cr_ghaMember_2ADFD977}",
287
+ "project": "gha-runner-example",
288
+ "role": "${google_project_iam_custom_role.cr_runnerRole_07508BC4.id}"
289
+ }
290
+ },
291
+ "google_service_account": {
292
+ "cr_autoscalerServiceAccount_37C5FFAD": {
293
+ "//": {
294
+ "metadata": {
295
+ "path": "google-cloud-run/cr/autoscalerServiceAccount",
296
+ "uniqueId": "cr_autoscalerServiceAccount_37C5FFAD"
297
+ }
298
+ },
299
+ "account_id": "autoscaler-sa"
300
+ },
301
+ "cr_jobServiceAccount_4D2CB679": {
302
+ "//": {
303
+ "metadata": {
304
+ "path": "google-cloud-run/cr/jobServiceAccount",
305
+ "uniqueId": "cr_jobServiceAccount_4D2CB679"
306
+ }
307
+ },
308
+ "account_id": "gha-runner-job-sa"
309
+ }
310
+ },
311
+ "null_resource": {
312
+ "cr_gcloud_3D7A726F": {
313
+ "//": {
314
+ "metadata": {
315
+ "path": "google-cloud-run/cr/gcloud",
316
+ "uniqueId": "cr_gcloud_3D7A726F"
317
+ }
318
+ },
319
+ "provisioner": [
320
+ {
321
+ "local-exec": {
322
+ "command": "${local.cr_bucketModification_B1ECB227}"
323
+ }
324
+ }
325
+ ],
326
+ "triggers": {
327
+ "fqn": "${local.cr_bucketModification_B1ECB227}"
328
+ }
329
+ }
330
+ }
331
+ },
332
+ "terraform": {
333
+ "required_providers": {
334
+ "google": {
335
+ "source": "google",
336
+ "version": "6.32.0"
337
+ },
338
+ "null": {
339
+ "source": "null",
340
+ "version": "3.2.4"
341
+ }
342
+ }
343
+ },
344
+ "variable": {
345
+ "PAT": {
346
+ "description": "Github PAT with Actions:Read and Admin:Read+Write scopes",
347
+ "nullable": false,
348
+ "sensitive": true
349
+ },
350
+ "github_config_url": {
351
+ "description": "Github URL where runners should register to. Format https://<GitHub host>/<your_enterprise/org/repo>",
352
+ "nullable": false
353
+ }
354
+ }
355
+ }
package/package.json CHANGED
@@ -35,7 +35,11 @@
35
35
  "organization": false
36
36
  },
37
37
  "devDependencies": {
38
+ "@aws-sdk/client-ecs": "^3.797.0",
39
+ "@cdktf/provider-aws": "19.62.0",
38
40
  "@cdktf/provider-azurerm": "13.26.0",
41
+ "@cdktf/provider-google": "14.31.0",
42
+ "@cdktf/provider-null": "10.0.2",
39
43
  "@cdktf/provider-random": "11.1.1",
40
44
  "@types/jest": "^29.5.14",
41
45
  "@types/node": "ts5.6",
@@ -65,7 +69,10 @@
65
69
  "typescript": "~5.6.0"
66
70
  },
67
71
  "peerDependencies": {
72
+ "@cdktf/provider-aws": "^19.62.0",
68
73
  "@cdktf/provider-azurerm": "^13.26.0",
74
+ "@cdktf/provider-google": "^14.31.0",
75
+ "@cdktf/provider-null": "^10.0.2",
69
76
  "@cdktf/provider-random": "^11.1.1",
70
77
  "cdktf": ">=0.20.0",
71
78
  "constructs": ">=10.4.2"
@@ -84,7 +91,7 @@
84
91
  "publishConfig": {
85
92
  "access": "public"
86
93
  },
87
- "version": "0.0.0",
94
+ "version": "0.0.2",
88
95
  "jest": {
89
96
  "coverageProvider": "v8",
90
97
  "testMatch": [
@@ -0,0 +1,188 @@
1
+ // Code generated with Gemini
2
+
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+
6
+ interface VariableDefinition {
7
+ name: string;
8
+ description: string;
9
+ submodules: string[];
10
+ customInfo?: Record<string, any>;
11
+ }
12
+
13
+ interface SubmoduleVariables {
14
+ [submodulePath: string]: VariableDefinition[];
15
+ }
16
+
17
+ const ROOT_DIR = '.';
18
+ const OUTPUT_FILE = 'variables.tf';
19
+ const VARIABLE_FILE_NAME = 'cdk.tf.json';
20
+ const GENERATED_COMMENT_START = '# Variables generated by terraform-variable-collector start';
21
+ const GENERATED_COMMENT_END = '# Variables generated by terraform-variable-collector end';
22
+
23
+ /**
24
+ * Extracts variable definitions from a cdk.tf.json file.
25
+ */
26
+ function extractVariables(filePath: string): VariableDefinition[] {
27
+ try {
28
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
29
+ const jsonData = JSON.parse(fileContent);
30
+
31
+ if (!jsonData.variable || typeof jsonData.variable !== 'object') {
32
+ return [];
33
+ }
34
+
35
+ const variables: VariableDefinition[] = [];
36
+ for (const varName in jsonData.variable) {
37
+ if (jsonData.variable.hasOwnProperty(varName)) {
38
+ const varConfig = jsonData.variable[varName];
39
+ if (typeof varConfig === 'object' && varConfig !== null) {
40
+ const description = typeof varConfig.description === 'string' ? varConfig.description : 'No description provided.';
41
+ const customInfo = varConfig.customInfo ? varConfig.customInfo : {};
42
+ variables.push({ name: varName, description, submodules: [], customInfo });
43
+ }
44
+ }
45
+ }
46
+ return variables;
47
+ } catch (error) {
48
+ console.error(`Error processing file: ${filePath}`, error);
49
+ return [];
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Recursively searches for cdk.tf.json files within the "modules" subdirectory.
55
+ */
56
+ function findSubmoduleVariables(dir: string): SubmoduleVariables {
57
+ const submoduleVariables: SubmoduleVariables = {};
58
+ const modulesDir = path.join(dir, 'modules');
59
+
60
+ if (!fs.existsSync(modulesDir)) {
61
+ console.warn(`Warning: 'modules' directory not found at ${modulesDir}. No variables will be collected.`);
62
+ return {};
63
+ }
64
+
65
+ function walk(currentDir: string) {
66
+ const files = fs.readdirSync(currentDir);
67
+ for (const file of files) {
68
+ const filePath = path.join(currentDir, file);
69
+ const stat = fs.statSync(filePath);
70
+
71
+ if (stat.isDirectory()) {
72
+ walk(filePath);
73
+ } else if (file === VARIABLE_FILE_NAME) {
74
+ const submodulePath = path.relative(modulesDir, currentDir);
75
+ if (submodulePath) {
76
+ const variables = extractVariables(filePath);
77
+ if (variables.length > 0) {
78
+ submoduleVariables[submodulePath] = variables;
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+
85
+ walk(modulesDir);
86
+ return submoduleVariables;
87
+ }
88
+
89
+ /**
90
+ * Generates the content for the root module's variables.tf file, combining
91
+ * similarly named variables. It uses the description from the first
92
+ * occurrence of the variable. It now updates an existing file, preserving
93
+ * content outside of the generated variable block.
94
+ */
95
+ function generateRootVariablesTf(collectedVariables: SubmoduleVariables): void {
96
+ let existingContent = '';
97
+ if (fs.existsSync(OUTPUT_FILE)) {
98
+ existingContent = fs.readFileSync(OUTPUT_FILE, 'utf-8');
99
+ }
100
+
101
+ let outputContent = `${GENERATED_COMMENT_START}\n`;
102
+ const combinedVariables: { [name: string]: VariableDefinition } = {};
103
+
104
+ for (const submodulePath in collectedVariables) {
105
+ if (collectedVariables.hasOwnProperty(submodulePath)) {
106
+ const variables = collectedVariables[submodulePath];
107
+ for (const variable of variables) {
108
+ if (combinedVariables[variable.name]) {
109
+ // Variable name already exists, so combine, but keep original description
110
+ combinedVariables[variable.name].submodules.push(`modules/${submodulePath}`);
111
+ } else {
112
+ // Variable name doesn't exist, so add it
113
+ combinedVariables[variable.name] = {
114
+ ...variable,
115
+ submodules: [`modules/${submodulePath}`],
116
+ };
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ for (const varName in combinedVariables) {
123
+ if (combinedVariables.hasOwnProperty(varName)) {
124
+ const variable = combinedVariables[varName];
125
+ const submoduleList = variable.submodules.join(', ');
126
+ outputContent += `variable "${varName}" {\n`;
127
+ outputContent += ` type = any\n`;
128
+ outputContent += ` description = "${variable.description} (Defined in: ${submoduleList})"\n`;
129
+ if (variable.customInfo && Object.keys(variable.customInfo).length > 0) {
130
+ outputContent += ` # Custom Information:\n`;
131
+ for (const key in variable.customInfo) {
132
+ if (variable.customInfo.hasOwnProperty(key)) {
133
+ const value = variable.customInfo[key];
134
+ outputContent += ` # ${key} = ${JSON.stringify(value)}\n`;
135
+ }
136
+ }
137
+ }
138
+ outputContent += `}\n\n`;
139
+ }
140
+ }
141
+ outputContent += `${GENERATED_COMMENT_END}\n`;
142
+
143
+ // Use regular expressions to find and replace the generated block
144
+ const startMarkerRegex = new RegExp(GENERATED_COMMENT_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
145
+ const endMarkerRegex = new RegExp(GENERATED_COMMENT_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
146
+ const existingGeneratedBlockRegex = new RegExp(
147
+ `${startMarkerRegex.source}[\\s\\S]*${endMarkerRegex.source}`,
148
+ 'g',
149
+ );
150
+
151
+ let updatedContent: string;
152
+ if (existingGeneratedBlockRegex.test(existingContent)) {
153
+ // Replace the existing generated block with the new content
154
+ updatedContent = existingContent.replace(existingGeneratedBlockRegex, outputContent);
155
+ } else {
156
+ // Append the generated block to the end of the file, or create a new file
157
+ updatedContent = existingContent + outputContent;
158
+ }
159
+
160
+ fs.writeFileSync(OUTPUT_FILE, updatedContent);
161
+ console.log(
162
+ `Generated (or updated) root module's ${OUTPUT_FILE} with combined variables, preserving existing content.`,
163
+ );
164
+ console.log('Review and adjust the types and other attributes as needed.');
165
+ }
166
+
167
+ function main(): void {
168
+ const collectedVars = findSubmoduleVariables(ROOT_DIR);
169
+
170
+ if (Object.keys(collectedVars).length > 0) {
171
+ console.log('Collected variables from submodules:');
172
+ for (const submodulePath in collectedVars) {
173
+ if (collectedVars.hasOwnProperty(submodulePath)) {
174
+ console.log(` Submodule: modules/${submodulePath}`);
175
+ const variables = collectedVars[submodulePath];
176
+ for (const varDef of variables) {
177
+ console.log(` - Name: ${varDef.name}, Description: ${varDef.description}`);
178
+ }
179
+ }
180
+ }
181
+ generateRootVariablesTf(collectedVars);
182
+ } else {
183
+ console.log('No submodules with cdk.tf.json files found in the "modules" directory.');
184
+ }
185
+ }
186
+
187
+ main();
188
+
package/variables.tf ADDED
@@ -0,0 +1,27 @@
1
+ variable "runner_platform" {
2
+ nullable = false
3
+ type = string
4
+ description = "Runner platform hosting ARC and runners. Possible values ACA (Azure Container Apps), ECS (Elastic Container Service) and GCR (Google Cloud Run)"
5
+ validation {
6
+ condition = can(regex("^(ACA|ECR|GCR)$", upper(var.runner_platform)))
7
+ error_message = "Runner platform has to be either ACA, ECR or GCR (case insensitive)."
8
+ }
9
+ }
10
+
11
+ # Variables generated by terraform-variable-collector start
12
+ variable "PAT" {
13
+ type = any
14
+ description = "Github PAT with Actions:Read and Admin:Read+Write scopes (Defined in: modules/azure-container-apps, modules/elastic-container-service, modules/google-cloud-run)"
15
+ }
16
+
17
+ variable "aca_location_486B7D73" {
18
+ type = any
19
+ description = "Location where to provision resources to (Defined in: modules/azure-container-apps)"
20
+ }
21
+
22
+ variable "github_config_url" {
23
+ type = any
24
+ description = "Github URL where runners should register to. Format https://<GitHub host>/<your_enterprise/org/repo> (Defined in: modules/azure-container-apps, modules/elastic-container-service, modules/google-cloud-run)"
25
+ }
26
+
27
+ # Variables generated by terraform-variable-collector end