@friggframework/devtools 2.0.0--canary.517.bd5ddfc.0 → 2.0.0--canary.517.9750290.0
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.
|
@@ -178,6 +178,16 @@ class AdminScriptBuilder extends InfrastructureBuilder {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
createSchedulerResources(appDefinition, result) {
|
|
181
|
+
// Reference the executor by a constructed ARN (Fn::Sub) rather than
|
|
182
|
+
// Fn::GetAtt. GetAtt creates a CloudFormation dependency edge, and an
|
|
183
|
+
// edge from the scheduler role to the executor closed a cycle through the
|
|
184
|
+
// shared Lambda execution role (role → executor → IamRoleLambdaExecution
|
|
185
|
+
// → PassRole → role). The name is deterministic, so a Sub is safe.
|
|
186
|
+
const executorArn = {
|
|
187
|
+
'Fn::Sub':
|
|
188
|
+
'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${self:service}-${self:provider.stage}-adminScriptExecutor',
|
|
189
|
+
};
|
|
190
|
+
|
|
181
191
|
// Create IAM role for EventBridge Scheduler
|
|
182
192
|
result.resources.AdminScriptSchedulerRole = {
|
|
183
193
|
Type: 'AWS::IAM::Role',
|
|
@@ -198,7 +208,7 @@ class AdminScriptBuilder extends InfrastructureBuilder {
|
|
|
198
208
|
Statement: [{
|
|
199
209
|
Effect: 'Allow',
|
|
200
210
|
Action: 'lambda:InvokeFunction',
|
|
201
|
-
Resource:
|
|
211
|
+
Resource: executorArn,
|
|
202
212
|
}],
|
|
203
213
|
},
|
|
204
214
|
}],
|
|
@@ -213,13 +223,25 @@ class AdminScriptBuilder extends InfrastructureBuilder {
|
|
|
213
223
|
},
|
|
214
224
|
};
|
|
215
225
|
|
|
216
|
-
//
|
|
217
|
-
//
|
|
226
|
+
// SCHEDULER_PROVIDER is a constant, so it's safe on the shared provider
|
|
227
|
+
// environment inherited by every function.
|
|
218
228
|
result.environment.SCHEDULER_PROVIDER = 'aws';
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
229
|
+
|
|
230
|
+
// The remaining vars carry resource references and are consumed ONLY by
|
|
231
|
+
// the router (it builds the AWS scheduler adapter in the schedule
|
|
232
|
+
// handlers). Scope them to the router function rather than the shared
|
|
233
|
+
// provider environment: broadcasting the executor's own ARN into every
|
|
234
|
+
// function's env made the executor depend on itself, and the role/group
|
|
235
|
+
// refs made every function depend on the scheduler role — both closed
|
|
236
|
+
// CloudFormation circular dependencies on deploy. Names must match
|
|
237
|
+
// admin-script-router.js exactly.
|
|
238
|
+
result.functions.adminScriptRouter.environment = {
|
|
239
|
+
...(result.functions.adminScriptRouter.environment || {}),
|
|
240
|
+
SCHEDULER_ROLE_ARN: {
|
|
241
|
+
'Fn::GetAtt': ['AdminScriptSchedulerRole', 'Arn'],
|
|
242
|
+
},
|
|
243
|
+
ADMIN_SCRIPT_SCHEDULE_GROUP: { Ref: 'AdminScriptScheduleGroup' },
|
|
244
|
+
ADMIN_SCRIPT_EXECUTOR_LAMBDA_ARN: executorArn,
|
|
223
245
|
};
|
|
224
246
|
|
|
225
247
|
// The router manages schedules through the AWS scheduler adapter, so it
|
|
@@ -366,17 +366,33 @@ describe('AdminScriptBuilder', () => {
|
|
|
366
366
|
expect(result.resources.AdminScriptScheduleGroup).toBeDefined();
|
|
367
367
|
expect(result.resources.AdminScriptScheduleGroup.Type).toBe('AWS::Scheduler::ScheduleGroup');
|
|
368
368
|
|
|
369
|
-
//
|
|
369
|
+
// SCHEDULER_PROVIDER is a constant and stays on the shared env.
|
|
370
370
|
expect(result.environment.SCHEDULER_PROVIDER).toBe('aws');
|
|
371
|
-
|
|
371
|
+
|
|
372
|
+
// Resource-reference env vars are scoped to the router function (not
|
|
373
|
+
// the shared provider env) to avoid CloudFormation circular deps.
|
|
374
|
+
const routerEnv = result.functions.adminScriptRouter.environment;
|
|
375
|
+
expect(routerEnv.SCHEDULER_ROLE_ARN).toEqual({
|
|
372
376
|
'Fn::GetAtt': ['AdminScriptSchedulerRole', 'Arn'],
|
|
373
377
|
});
|
|
374
|
-
expect(
|
|
378
|
+
expect(routerEnv.ADMIN_SCRIPT_SCHEDULE_GROUP).toEqual({
|
|
375
379
|
Ref: 'AdminScriptScheduleGroup',
|
|
376
380
|
});
|
|
377
|
-
|
|
378
|
-
|
|
381
|
+
// Executor referenced by constructed ARN (Fn::Sub), not Fn::GetAtt,
|
|
382
|
+
// so it creates no dependency edge.
|
|
383
|
+
expect(routerEnv.ADMIN_SCRIPT_EXECUTOR_LAMBDA_ARN).toEqual({
|
|
384
|
+
'Fn::Sub':
|
|
385
|
+
'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${self:service}-${self:provider.stage}-adminScriptExecutor',
|
|
379
386
|
});
|
|
387
|
+
|
|
388
|
+
// Regression guard: these must NOT leak onto the shared provider env
|
|
389
|
+
// (that is what produced the circular dependency, incl. the executor
|
|
390
|
+
// self-reference).
|
|
391
|
+
expect(result.environment.SCHEDULER_ROLE_ARN).toBeUndefined();
|
|
392
|
+
expect(result.environment.ADMIN_SCRIPT_SCHEDULE_GROUP).toBeUndefined();
|
|
393
|
+
expect(
|
|
394
|
+
result.environment.ADMIN_SCRIPT_EXECUTOR_LAMBDA_ARN
|
|
395
|
+
).toBeUndefined();
|
|
380
396
|
});
|
|
381
397
|
|
|
382
398
|
it('should not create scheduler resources when enableScheduling is false', async () => {
|
|
@@ -494,10 +510,15 @@ describe('AdminScriptBuilder', () => {
|
|
|
494
510
|
const policies = result.resources.AdminScriptSchedulerRole.Properties.Policies;
|
|
495
511
|
|
|
496
512
|
expect(policies[0].PolicyName).toBe('InvokeLambda');
|
|
513
|
+
// Constructed ARN (Fn::Sub), not Fn::GetAtt — a GetAtt here closes a
|
|
514
|
+
// circular dependency through the shared Lambda execution role.
|
|
497
515
|
expect(policies[0].PolicyDocument.Statement[0]).toEqual({
|
|
498
516
|
Effect: 'Allow',
|
|
499
517
|
Action: 'lambda:InvokeFunction',
|
|
500
|
-
Resource: {
|
|
518
|
+
Resource: {
|
|
519
|
+
'Fn::Sub':
|
|
520
|
+
'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${self:service}-${self:provider.stage}-adminScriptExecutor',
|
|
521
|
+
},
|
|
501
522
|
});
|
|
502
523
|
});
|
|
503
524
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/devtools",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.517.
|
|
4
|
+
"version": "2.0.0--canary.517.9750290.0",
|
|
5
5
|
"bin": {
|
|
6
6
|
"frigg": "./frigg-cli/index.js"
|
|
7
7
|
},
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"@babel/eslint-parser": "^7.18.9",
|
|
26
26
|
"@babel/parser": "^7.25.3",
|
|
27
27
|
"@babel/traverse": "^7.25.3",
|
|
28
|
-
"@friggframework/core": "2.0.0--canary.517.
|
|
29
|
-
"@friggframework/schemas": "2.0.0--canary.517.
|
|
30
|
-
"@friggframework/test": "2.0.0--canary.517.
|
|
28
|
+
"@friggframework/core": "2.0.0--canary.517.9750290.0",
|
|
29
|
+
"@friggframework/schemas": "2.0.0--canary.517.9750290.0",
|
|
30
|
+
"@friggframework/test": "2.0.0--canary.517.9750290.0",
|
|
31
31
|
"@hapi/boom": "^10.0.1",
|
|
32
32
|
"@inquirer/prompts": "^5.3.8",
|
|
33
33
|
"axios": "^1.18.0",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"validate-npm-package-name": "^5.0.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@friggframework/eslint-config": "2.0.0--canary.517.
|
|
59
|
-
"@friggframework/prettier-config": "2.0.0--canary.517.
|
|
58
|
+
"@friggframework/eslint-config": "2.0.0--canary.517.9750290.0",
|
|
59
|
+
"@friggframework/prettier-config": "2.0.0--canary.517.9750290.0",
|
|
60
60
|
"aws-sdk-client-mock": "^4.1.0",
|
|
61
61
|
"aws-sdk-client-mock-jest": "^4.1.0",
|
|
62
62
|
"jest": "^30.1.3",
|
|
@@ -88,5 +88,5 @@
|
|
|
88
88
|
"publishConfig": {
|
|
89
89
|
"access": "public"
|
|
90
90
|
},
|
|
91
|
-
"gitHead": "
|
|
91
|
+
"gitHead": "9750290bdb331b5f2d1304d7e64adc3c480f1fb7"
|
|
92
92
|
}
|