@friggframework/devtools 2.0.0-next.45 → 2.0.0-next.46
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.
- package/infrastructure/ARCHITECTURE.md +487 -0
- package/infrastructure/HEALTH.md +468 -0
- package/infrastructure/README.md +51 -0
- package/infrastructure/__tests__/postgres-config.test.js +914 -0
- package/infrastructure/__tests__/template-generation.test.js +687 -0
- package/infrastructure/create-frigg-infrastructure.js +1 -1
- package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
- package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
- package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
- package/infrastructure/domains/database/aurora-builder.js +809 -0
- package/infrastructure/domains/database/aurora-builder.test.js +950 -0
- package/infrastructure/domains/database/aurora-discovery.js +87 -0
- package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
- package/infrastructure/domains/database/aurora-resolver.js +210 -0
- package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
- package/infrastructure/domains/database/migration-builder.js +633 -0
- package/infrastructure/domains/database/migration-builder.test.js +294 -0
- package/infrastructure/domains/database/migration-resolver.js +163 -0
- package/infrastructure/domains/database/migration-resolver.test.js +337 -0
- package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
- package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
- package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
- package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
- package/infrastructure/domains/health/application/ports/index.js +26 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
- package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
- package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
- package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
- package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
- package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
- package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
- package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
- package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
- package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
- package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
- package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
- package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
- package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
- package/infrastructure/domains/health/domain/entities/issue.js +299 -0
- package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
- package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
- package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
- package/infrastructure/domains/health/domain/entities/resource.js +159 -0
- package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
- package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
- package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
- package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
- package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
- package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
- package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
- package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
- package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
- package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
- package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
- package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
- package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
- package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
- package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
- package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
- package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
- package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
- package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
- package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
- package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
- package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
- package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
- package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
- package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
- package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
- package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
- package/infrastructure/domains/integration/integration-builder.js +397 -0
- package/infrastructure/domains/integration/integration-builder.test.js +593 -0
- package/infrastructure/domains/integration/integration-resolver.js +170 -0
- package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
- package/infrastructure/domains/integration/websocket-builder.js +69 -0
- package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
- package/infrastructure/domains/networking/vpc-builder.js +1829 -0
- package/infrastructure/domains/networking/vpc-builder.test.js +1262 -0
- package/infrastructure/domains/networking/vpc-discovery.js +177 -0
- package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
- package/infrastructure/domains/networking/vpc-resolver.js +324 -0
- package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
- package/infrastructure/domains/parameters/ssm-builder.js +79 -0
- package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
- package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
- package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
- package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
- package/infrastructure/domains/security/kms-builder.js +366 -0
- package/infrastructure/domains/security/kms-builder.test.js +374 -0
- package/infrastructure/domains/security/kms-discovery.js +80 -0
- package/infrastructure/domains/security/kms-discovery.test.js +177 -0
- package/infrastructure/domains/security/kms-resolver.js +96 -0
- package/infrastructure/domains/security/kms-resolver.test.js +216 -0
- package/infrastructure/domains/shared/base-builder.js +112 -0
- package/infrastructure/domains/shared/base-resolver.js +186 -0
- package/infrastructure/domains/shared/base-resolver.test.js +305 -0
- package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
- package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
- package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
- package/infrastructure/domains/shared/cloudformation-discovery.js +375 -0
- package/infrastructure/domains/shared/cloudformation-discovery.test.js +590 -0
- package/infrastructure/domains/shared/environment-builder.js +119 -0
- package/infrastructure/domains/shared/environment-builder.test.js +247 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.js +544 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +377 -0
- package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
- package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
- package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
- package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
- package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
- package/infrastructure/domains/shared/resource-discovery.js +192 -0
- package/infrastructure/domains/shared/resource-discovery.test.js +552 -0
- package/infrastructure/domains/shared/types/app-definition.js +205 -0
- package/infrastructure/domains/shared/types/discovery-result.js +106 -0
- package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
- package/infrastructure/domains/shared/types/index.js +46 -0
- package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
- package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +380 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
- package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
- package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +138 -0
- package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +2 -1
- package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
- package/infrastructure/esbuild.config.js +53 -0
- package/infrastructure/infrastructure-composer.js +87 -0
- package/infrastructure/{serverless-template.test.js → infrastructure-composer.test.js} +115 -24
- package/infrastructure/scripts/build-prisma-layer.js +553 -0
- package/infrastructure/scripts/build-prisma-layer.test.js +102 -0
- package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +80 -48
- package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
- package/layers/prisma/nodejs/package.json +8 -0
- package/management-ui/server/utils/cliIntegration.js +1 -1
- package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
- package/package.json +11 -11
- package/frigg-cli/.eslintrc.js +0 -141
- package/frigg-cli/__tests__/unit/commands/build.test.js +0 -251
- package/frigg-cli/__tests__/unit/commands/db-setup.test.js +0 -548
- package/frigg-cli/__tests__/unit/commands/install.test.js +0 -400
- package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -346
- package/frigg-cli/__tests__/unit/utils/database-validator.test.js +0 -366
- package/frigg-cli/__tests__/unit/utils/error-messages.test.js +0 -304
- package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +0 -486
- package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
- package/frigg-cli/__tests__/utils/prisma-mock.js +0 -194
- package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
- package/frigg-cli/__tests__/utils/test-setup.js +0 -287
- package/frigg-cli/build-command/index.js +0 -65
- package/frigg-cli/db-setup-command/index.js +0 -193
- package/frigg-cli/deploy-command/index.js +0 -175
- package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -301
- package/frigg-cli/generate-command/azure-generator.js +0 -43
- package/frigg-cli/generate-command/gcp-generator.js +0 -47
- package/frigg-cli/generate-command/index.js +0 -332
- package/frigg-cli/generate-command/terraform-generator.js +0 -555
- package/frigg-cli/generate-iam-command.js +0 -118
- package/frigg-cli/index.js +0 -75
- package/frigg-cli/index.test.js +0 -158
- package/frigg-cli/init-command/backend-first-handler.js +0 -756
- package/frigg-cli/init-command/index.js +0 -93
- package/frigg-cli/init-command/template-handler.js +0 -143
- package/frigg-cli/install-command/backend-js.js +0 -33
- package/frigg-cli/install-command/commit-changes.js +0 -16
- package/frigg-cli/install-command/environment-variables.js +0 -127
- package/frigg-cli/install-command/environment-variables.test.js +0 -136
- package/frigg-cli/install-command/index.js +0 -54
- package/frigg-cli/install-command/install-package.js +0 -13
- package/frigg-cli/install-command/integration-file.js +0 -30
- package/frigg-cli/install-command/logger.js +0 -12
- package/frigg-cli/install-command/template.js +0 -90
- package/frigg-cli/install-command/validate-package.js +0 -75
- package/frigg-cli/jest.config.js +0 -124
- package/frigg-cli/package.json +0 -54
- package/frigg-cli/start-command/index.js +0 -149
- package/frigg-cli/start-command/start-command.test.js +0 -297
- package/frigg-cli/test/init-command.test.js +0 -180
- package/frigg-cli/test/npm-registry.test.js +0 -319
- package/frigg-cli/ui-command/index.js +0 -154
- package/frigg-cli/utils/app-resolver.js +0 -319
- package/frigg-cli/utils/backend-path.js +0 -25
- package/frigg-cli/utils/database-validator.js +0 -161
- package/frigg-cli/utils/error-messages.js +0 -257
- package/frigg-cli/utils/npm-registry.js +0 -167
- package/frigg-cli/utils/prisma-runner.js +0 -280
- package/frigg-cli/utils/process-manager.js +0 -199
- package/frigg-cli/utils/repo-detection.js +0 -405
- package/infrastructure/aws-discovery.js +0 -1176
- package/infrastructure/aws-discovery.test.js +0 -1220
- package/infrastructure/serverless-template.js +0 -2094
- /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
- /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
- /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
- /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
- /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
- /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
- /package/infrastructure/{run-discovery.js → scripts/run-discovery.js} +0 -0
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration Infrastructure Builder
|
|
3
|
+
*
|
|
4
|
+
* Domain Layer - Hexagonal Architecture
|
|
5
|
+
*
|
|
6
|
+
* Responsible for:
|
|
7
|
+
* - SQS queue for migration jobs
|
|
8
|
+
* - Migration worker Lambda function (triggered by SQS)
|
|
9
|
+
* - Migration router Lambda function (HTTP API)
|
|
10
|
+
* - IAM permissions for SQS
|
|
11
|
+
*
|
|
12
|
+
* Only creates infrastructure when PostgreSQL is enabled.
|
|
13
|
+
* MongoDB uses `db push` which doesn't require migration queue/worker.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { InfrastructureBuilder, ValidationResult } = require('../shared/base-builder');
|
|
17
|
+
const { MigrationResourceResolver } = require('./migration-resolver');
|
|
18
|
+
const { createEmptyDiscoveryResult, ResourceOwnership } = require('../shared/types');
|
|
19
|
+
|
|
20
|
+
class MigrationBuilder extends InfrastructureBuilder {
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
this.name = 'MigrationBuilder';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
shouldExecute(appDefinition) {
|
|
27
|
+
// Only create migration infrastructure for PostgreSQL
|
|
28
|
+
// MongoDB uses `db push` which doesn't need queue/worker
|
|
29
|
+
// Skip in local mode
|
|
30
|
+
if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Default to true if not explicitly disabled
|
|
35
|
+
return appDefinition.database?.postgres?.enable !== false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getDependencies() {
|
|
39
|
+
return []; // No dependencies - migrations can run independently
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
validate(appDefinition) {
|
|
43
|
+
const result = new ValidationResult();
|
|
44
|
+
|
|
45
|
+
// No specific validation needed - PostgreSQL builder handles DB validation
|
|
46
|
+
// This builder just creates the migration infrastructure
|
|
47
|
+
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Build migration infrastructure using ownership-based architecture
|
|
53
|
+
*/
|
|
54
|
+
async build(appDefinition, discoveredResources) {
|
|
55
|
+
console.log(`\n[${this.name}] Configuring database migration infrastructure...`);
|
|
56
|
+
|
|
57
|
+
// Backwards compatibility: Translate old schema to new ownership schema
|
|
58
|
+
appDefinition = this.translateLegacyConfig(appDefinition, discoveredResources);
|
|
59
|
+
|
|
60
|
+
const result = {
|
|
61
|
+
functions: {}, // Lambda function definitions
|
|
62
|
+
resources: {},
|
|
63
|
+
iamStatements: [],
|
|
64
|
+
environment: {},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Get structured discovery result
|
|
68
|
+
const discovery = discoveredResources._structured || this.convertFlatDiscoveryToStructured(discoveredResources, appDefinition);
|
|
69
|
+
|
|
70
|
+
// Use MigrationResourceResolver to make ownership decisions
|
|
71
|
+
const resolver = new MigrationResourceResolver();
|
|
72
|
+
const decisions = resolver.resolveAll(appDefinition, discovery);
|
|
73
|
+
|
|
74
|
+
console.log('\n 📋 Resource Ownership Decisions:');
|
|
75
|
+
console.log(` Bucket: ${decisions.bucket.ownership} - ${decisions.bucket.reason}`);
|
|
76
|
+
console.log(` Queue: ${decisions.queue.ownership} - ${decisions.queue.reason}`);
|
|
77
|
+
|
|
78
|
+
// Build resources based on ownership decisions
|
|
79
|
+
await this.buildFromDecisions(decisions, appDefinition, discoveredResources, result);
|
|
80
|
+
|
|
81
|
+
console.log(`[${this.name}] ✅ Migration infrastructure configuration completed`);
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Convert flat discovery to structured discovery
|
|
87
|
+
* Provides backwards compatibility for tests
|
|
88
|
+
*/
|
|
89
|
+
convertFlatDiscoveryToStructured(flatDiscovery, appDefinition = {}) {
|
|
90
|
+
const discovery = createEmptyDiscoveryResult();
|
|
91
|
+
|
|
92
|
+
if (!flatDiscovery) {
|
|
93
|
+
return discovery;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Check if resources are from CloudFormation stack
|
|
97
|
+
const isManagedIsolated = appDefinition.managementMode === 'managed' &&
|
|
98
|
+
(appDefinition.vpcIsolation === 'isolated' || !appDefinition.vpcIsolation);
|
|
99
|
+
const hasExistingStackResources = isManagedIsolated &&
|
|
100
|
+
(flatDiscovery.migrationStatusBucket || flatDiscovery.migrationQueueUrl);
|
|
101
|
+
|
|
102
|
+
if (flatDiscovery.fromCloudFormationStack || hasExistingStackResources) {
|
|
103
|
+
discovery.fromCloudFormation = true;
|
|
104
|
+
discovery.stackName = flatDiscovery.stackName || 'assumed-stack';
|
|
105
|
+
|
|
106
|
+
// Add stack-managed resources
|
|
107
|
+
let existingLogicalIds = flatDiscovery.existingLogicalIds || [];
|
|
108
|
+
|
|
109
|
+
// Infer logical IDs from physical IDs if needed
|
|
110
|
+
if (hasExistingStackResources && existingLogicalIds.length === 0) {
|
|
111
|
+
if (flatDiscovery.migrationStatusBucket) existingLogicalIds.push('FriggMigrationStatusBucket');
|
|
112
|
+
if (flatDiscovery.migrationQueueUrl) existingLogicalIds.push('DbMigrationQueue');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
existingLogicalIds.forEach(logicalId => {
|
|
116
|
+
let resourceType = '';
|
|
117
|
+
let physicalId = '';
|
|
118
|
+
|
|
119
|
+
if (logicalId === 'FriggMigrationStatusBucket') {
|
|
120
|
+
resourceType = 'AWS::S3::Bucket';
|
|
121
|
+
physicalId = flatDiscovery.migrationStatusBucket;
|
|
122
|
+
} else if (logicalId === 'DbMigrationQueue') {
|
|
123
|
+
resourceType = 'AWS::SQS::Queue';
|
|
124
|
+
physicalId = flatDiscovery.migrationQueueUrl;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (physicalId && typeof physicalId === 'string') {
|
|
128
|
+
discovery.stackManaged.push({
|
|
129
|
+
logicalId,
|
|
130
|
+
physicalId,
|
|
131
|
+
resourceType
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
// Resources discovered from AWS API (external)
|
|
137
|
+
if (flatDiscovery.migrationStatusBucket && typeof flatDiscovery.migrationStatusBucket === 'string') {
|
|
138
|
+
discovery.external.push({
|
|
139
|
+
physicalId: flatDiscovery.migrationStatusBucket,
|
|
140
|
+
resourceType: 'AWS::S3::Bucket',
|
|
141
|
+
source: 'aws-discovery'
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (flatDiscovery.migrationQueueUrl && typeof flatDiscovery.migrationQueueUrl === 'string') {
|
|
146
|
+
discovery.external.push({
|
|
147
|
+
physicalId: flatDiscovery.migrationQueueUrl,
|
|
148
|
+
resourceType: 'AWS::SQS::Queue',
|
|
149
|
+
source: 'aws-discovery'
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return discovery;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Translate legacy configuration to ownership-based configuration
|
|
159
|
+
* Provides backwards compatibility
|
|
160
|
+
*/
|
|
161
|
+
translateLegacyConfig(appDefinition, discoveredResources) {
|
|
162
|
+
// If already using ownership schema, return as-is
|
|
163
|
+
if (appDefinition.migration?.ownership) {
|
|
164
|
+
return appDefinition;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const translated = JSON.parse(JSON.stringify(appDefinition));
|
|
168
|
+
|
|
169
|
+
// Initialize ownership sections
|
|
170
|
+
if (!translated.migration) translated.migration = {};
|
|
171
|
+
if (!translated.migration.ownership) {
|
|
172
|
+
translated.migration.ownership = {};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Handle top-level managementMode
|
|
176
|
+
const globalMode = appDefinition.managementMode || 'discover';
|
|
177
|
+
const vpcIsolation = appDefinition.vpcIsolation || 'shared';
|
|
178
|
+
|
|
179
|
+
if (globalMode === 'managed') {
|
|
180
|
+
if (vpcIsolation === 'isolated') {
|
|
181
|
+
const hasStackResources = discoveredResources?.migrationStatusBucket ||
|
|
182
|
+
discoveredResources?.migrationQueueUrl;
|
|
183
|
+
|
|
184
|
+
if (hasStackResources) {
|
|
185
|
+
translated.migration.ownership.bucket = 'auto';
|
|
186
|
+
translated.migration.ownership.queue = 'auto';
|
|
187
|
+
console.log(` managementMode='managed' + vpcIsolation='isolated' → stack has migration resources, reusing`);
|
|
188
|
+
} else {
|
|
189
|
+
translated.migration.ownership.bucket = 'stack';
|
|
190
|
+
translated.migration.ownership.queue = 'stack';
|
|
191
|
+
console.log(` managementMode='managed' + vpcIsolation='isolated' → no stack migration resources, creating new`);
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
translated.migration.ownership.bucket = 'auto';
|
|
195
|
+
translated.migration.ownership.queue = 'auto';
|
|
196
|
+
console.log(` managementMode='managed' + vpcIsolation='shared' → discovering migration resources`);
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
// Default to creating resources (current behavior)
|
|
200
|
+
translated.migration.ownership.bucket = 'stack';
|
|
201
|
+
translated.migration.ownership.queue = 'stack';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return translated;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Build migration resources based on ownership decisions
|
|
209
|
+
*/
|
|
210
|
+
async buildFromDecisions(decisions, appDefinition, discoveredResources, result) {
|
|
211
|
+
// Determine if we need to create resources or use existing ones
|
|
212
|
+
const shouldCreateBucket = decisions.bucket.ownership === ResourceOwnership.STACK;
|
|
213
|
+
const shouldCreateQueue = decisions.queue.ownership === ResourceOwnership.STACK;
|
|
214
|
+
|
|
215
|
+
if (shouldCreateBucket && shouldCreateQueue && !decisions.bucket.physicalId && !decisions.queue.physicalId) {
|
|
216
|
+
// Create all new migration infrastructure
|
|
217
|
+
console.log(' → Creating new migration infrastructure in stack');
|
|
218
|
+
await this.createMigrationInfrastructure(appDefinition, result);
|
|
219
|
+
} else if ((decisions.bucket.ownership === ResourceOwnership.STACK && decisions.bucket.physicalId) ||
|
|
220
|
+
(decisions.queue.ownership === ResourceOwnership.STACK && decisions.queue.physicalId)) {
|
|
221
|
+
// Resources exist in stack - add definitions (CloudFormation idempotency)
|
|
222
|
+
console.log(' → Adding migration definitions to template (existing in stack)');
|
|
223
|
+
await this.createMigrationInfrastructure(appDefinition, result);
|
|
224
|
+
} else {
|
|
225
|
+
// Use external resources
|
|
226
|
+
console.log(' → Using external migration resources');
|
|
227
|
+
await this.useExternalMigrationResources(decisions, appDefinition, result);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Create Lambda function definitions for database migrations
|
|
233
|
+
* Based on refactor/add-better-support-for-commands branch implementation
|
|
234
|
+
*/
|
|
235
|
+
async createFunctionDefinitions(result) {
|
|
236
|
+
console.log(' 🔍 DEBUG: createFunctionDefinitions called');
|
|
237
|
+
console.log(' 🔍 DEBUG: result.functions is:', typeof result.functions, result.functions);
|
|
238
|
+
// Migration WORKER package config (needs Prisma CLI WASM files)
|
|
239
|
+
const migrationWorkerPackageConfig = {
|
|
240
|
+
exclude: [
|
|
241
|
+
// Exclude AWS SDK (provided by Lambda runtime)
|
|
242
|
+
'node_modules/aws-sdk/**',
|
|
243
|
+
'node_modules/@aws-sdk/**',
|
|
244
|
+
// Exclude build tools
|
|
245
|
+
'node_modules/esbuild/**',
|
|
246
|
+
'node_modules/@esbuild/**',
|
|
247
|
+
'node_modules/typescript/**',
|
|
248
|
+
'node_modules/webpack/**',
|
|
249
|
+
'node_modules/osls/**',
|
|
250
|
+
'node_modules/serverless-esbuild/**',
|
|
251
|
+
'node_modules/serverless-jetpack/**',
|
|
252
|
+
'node_modules/serverless-offline/**',
|
|
253
|
+
'node_modules/serverless-offline-sqs/**',
|
|
254
|
+
'node_modules/serverless-dotenv-plugin/**',
|
|
255
|
+
'node_modules/serverless-kms-grants/**',
|
|
256
|
+
'node_modules/@friggframework/test/**',
|
|
257
|
+
'node_modules/@friggframework/eslint-config/**',
|
|
258
|
+
'node_modules/@friggframework/prettier-config/**',
|
|
259
|
+
'node_modules/@friggframework/devtools/**',
|
|
260
|
+
'node_modules/@friggframework/serverless-plugin/**',
|
|
261
|
+
'node_modules/jest/**',
|
|
262
|
+
'node_modules/prettier/**',
|
|
263
|
+
'node_modules/eslint/**',
|
|
264
|
+
'node_modules/@friggframework/core/generated/prisma-mongodb/**',
|
|
265
|
+
'node_modules/@friggframework/core/integrations/**',
|
|
266
|
+
'node_modules/@friggframework/core/user/**',
|
|
267
|
+
'**/query-engine-darwin*',
|
|
268
|
+
'**/schema-engine-darwin*',
|
|
269
|
+
'**/libquery_engine-darwin*',
|
|
270
|
+
'**/*-darwin-arm64*',
|
|
271
|
+
'**/*-darwin*',
|
|
272
|
+
// Migration worker DOES need Prisma CLI WASM files (for migrate deploy)
|
|
273
|
+
// Only exclude runtime engine WASM (query engine internals)
|
|
274
|
+
'**/runtime/*.wasm',
|
|
275
|
+
// Additional size optimizations
|
|
276
|
+
'**/*.map',
|
|
277
|
+
'**/*.md',
|
|
278
|
+
'**/examples/**',
|
|
279
|
+
'**/docs/**',
|
|
280
|
+
'**/*.d.ts',
|
|
281
|
+
'src/**',
|
|
282
|
+
'test/**',
|
|
283
|
+
'layers/**',
|
|
284
|
+
'coverage/**',
|
|
285
|
+
'deploy.log',
|
|
286
|
+
'.env.backup',
|
|
287
|
+
'docker-compose.yml',
|
|
288
|
+
'jest.config.js',
|
|
289
|
+
'jest.unit.config.js',
|
|
290
|
+
'package-lock.json',
|
|
291
|
+
'**/*.test.js',
|
|
292
|
+
'**/*.spec.js',
|
|
293
|
+
'**/.claude-flow/**',
|
|
294
|
+
'**/.swarm/**',
|
|
295
|
+
],
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// Migration ROUTER package config (lighter, no Prisma CLI needed)
|
|
299
|
+
const migrationRouterPackageConfig = {
|
|
300
|
+
exclude: [
|
|
301
|
+
// Exclude AWS SDK (provided by Lambda runtime)
|
|
302
|
+
'node_modules/aws-sdk/**',
|
|
303
|
+
'node_modules/@aws-sdk/**',
|
|
304
|
+
// Exclude build tools
|
|
305
|
+
'node_modules/esbuild/**',
|
|
306
|
+
'node_modules/@esbuild/**',
|
|
307
|
+
'node_modules/typescript/**',
|
|
308
|
+
'node_modules/webpack/**',
|
|
309
|
+
'node_modules/serverless-esbuild/**',
|
|
310
|
+
'node_modules/serverless-jetpack/**',
|
|
311
|
+
'node_modules/serverless-offline/**',
|
|
312
|
+
'node_modules/serverless-offline-sqs/**',
|
|
313
|
+
'node_modules/serverless-dotenv-plugin/**',
|
|
314
|
+
'node_modules/serverless-kms-grants/**',
|
|
315
|
+
'node_modules/@friggframework/test/**',
|
|
316
|
+
'node_modules/@friggframework/eslint-config/**',
|
|
317
|
+
'node_modules/@friggframework/prettier-config/**',
|
|
318
|
+
'node_modules/@friggframework/devtools/**',
|
|
319
|
+
'node_modules/@friggframework/serverless-plugin/**',
|
|
320
|
+
'node_modules/jest/**',
|
|
321
|
+
'node_modules/prettier/**',
|
|
322
|
+
'node_modules/eslint/**',
|
|
323
|
+
'node_modules/@friggframework/core/generated/prisma-mongodb/**',
|
|
324
|
+
'node_modules/@friggframework/core/user/**',
|
|
325
|
+
'**/query-engine-darwin*',
|
|
326
|
+
'**/schema-engine-darwin*',
|
|
327
|
+
'**/libquery_engine-darwin*',
|
|
328
|
+
'**/*-darwin-arm64*',
|
|
329
|
+
'**/*-darwin*',
|
|
330
|
+
// Router doesn't run migrations - exclude ALL WASM files
|
|
331
|
+
'**/runtime/*.wasm',
|
|
332
|
+
'**/*.wasm*',
|
|
333
|
+
// Additional size optimizations
|
|
334
|
+
'**/*.map',
|
|
335
|
+
'**/*.md',
|
|
336
|
+
'**/test/**',
|
|
337
|
+
'**/tests/**',
|
|
338
|
+
'**/__tests__/**',
|
|
339
|
+
'**/examples/**',
|
|
340
|
+
'**/docs/**',
|
|
341
|
+
'**/*.d.ts',
|
|
342
|
+
'src/**',
|
|
343
|
+
'test/**',
|
|
344
|
+
'layers/**',
|
|
345
|
+
'coverage/**',
|
|
346
|
+
'deploy.log',
|
|
347
|
+
'.env.backup',
|
|
348
|
+
'docker-compose.yml',
|
|
349
|
+
'jest.config.js',
|
|
350
|
+
'jest.unit.config.js',
|
|
351
|
+
'package-lock.json',
|
|
352
|
+
'**/*.test.js',
|
|
353
|
+
'**/*.spec.js',
|
|
354
|
+
'**/.claude-flow/**',
|
|
355
|
+
'**/.swarm/**',
|
|
356
|
+
],
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// Create migration worker Lambda (triggered by SQS)
|
|
360
|
+
console.log(' 🔍 DEBUG: About to create dbMigrationWorker...');
|
|
361
|
+
result.functions.dbMigrationWorker = {
|
|
362
|
+
handler: 'node_modules/@friggframework/core/handlers/workers/db-migration.handler',
|
|
363
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }], // Use layer for Prisma client runtime
|
|
364
|
+
skipEsbuild: true,
|
|
365
|
+
timeout: 900, // 15 minutes for long migrations
|
|
366
|
+
memorySize: 1024, // Extra memory for Prisma operations
|
|
367
|
+
reservedConcurrency: 1, // Process one migration at a time (critical for safety)
|
|
368
|
+
description: 'Database migration worker (triggered by SQS queue)',
|
|
369
|
+
package: migrationWorkerPackageConfig,
|
|
370
|
+
environment: {
|
|
371
|
+
// Ensure migration functions get DATABASE_URL from provider.environment
|
|
372
|
+
// Note: Serverless will merge this with provider.environment
|
|
373
|
+
},
|
|
374
|
+
events: [
|
|
375
|
+
{
|
|
376
|
+
sqs: {
|
|
377
|
+
arn: { 'Fn::GetAtt': ['DbMigrationQueue', 'Arn'] },
|
|
378
|
+
batchSize: 1, // Process one migration at a time
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
};
|
|
383
|
+
console.log(' ✓ Created dbMigrationWorker function');
|
|
384
|
+
console.log(' 🔍 DEBUG: result.functions.dbMigrationWorker is:', !!result.functions.dbMigrationWorker);
|
|
385
|
+
|
|
386
|
+
// Create migration router Lambda (HTTP API)
|
|
387
|
+
console.log(' 🔍 DEBUG: About to create dbMigrationRouter...');
|
|
388
|
+
result.functions.dbMigrationRouter = {
|
|
389
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/db-migration.handler',
|
|
390
|
+
// No Prisma layer needed - router doesn't access database
|
|
391
|
+
skipEsbuild: true,
|
|
392
|
+
timeout: 30, // Router just queues jobs, doesn't run migrations
|
|
393
|
+
memorySize: 512,
|
|
394
|
+
description: 'Database migration HTTP API (POST to trigger, GET to check status)',
|
|
395
|
+
package: migrationRouterPackageConfig,
|
|
396
|
+
environment: {
|
|
397
|
+
// Ensure migration functions get DATABASE_URL from provider.environment
|
|
398
|
+
// Note: Serverless will merge this with provider.environment
|
|
399
|
+
},
|
|
400
|
+
events: [
|
|
401
|
+
{ httpApi: { path: '/db-migrate/status', method: 'GET' } },
|
|
402
|
+
{ httpApi: { path: '/db-migrate', method: 'POST' } },
|
|
403
|
+
{ httpApi: { path: '/db-migrate/{processId}', method: 'GET' } },
|
|
404
|
+
],
|
|
405
|
+
};
|
|
406
|
+
console.log(' ✓ Created dbMigrationRouter function');
|
|
407
|
+
|
|
408
|
+
// Add worker function name to router environment (for Lambda invocation)
|
|
409
|
+
// Router needs this to invoke worker for database state checks
|
|
410
|
+
if (!result.functions.dbMigrationRouter.environment) {
|
|
411
|
+
result.functions.dbMigrationRouter.environment = {};
|
|
412
|
+
}
|
|
413
|
+
result.functions.dbMigrationRouter.environment.WORKER_FUNCTION_NAME = {
|
|
414
|
+
Ref: 'DbMigrationWorkerLambdaFunction',
|
|
415
|
+
};
|
|
416
|
+
console.log(' ✓ Added WORKER_FUNCTION_NAME environment variable to router');
|
|
417
|
+
console.log(' 🔍 DEBUG: result.functions keys:', Object.keys(result.functions));
|
|
418
|
+
console.log(' 🔍 DEBUG: Exiting createFunctionDefinitions');
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Create migration infrastructure CloudFormation resources
|
|
423
|
+
* Creates S3 bucket, SQS queue, and Lambda function definitions
|
|
424
|
+
*/
|
|
425
|
+
async createMigrationInfrastructure(appDefinition, result) {
|
|
426
|
+
console.log(' 🔍 DEBUG: createMigrationInfrastructure called');
|
|
427
|
+
console.log(' 🔍 DEBUG: result object before createFunctionDefinitions:', Object.keys(result));
|
|
428
|
+
|
|
429
|
+
// Create Lambda function definitions first (they reference the queue)
|
|
430
|
+
await this.createFunctionDefinitions(result);
|
|
431
|
+
|
|
432
|
+
console.log(' 🔍 DEBUG: result.functions after createFunctionDefinitions:', Object.keys(result.functions || {}));
|
|
433
|
+
|
|
434
|
+
// Create S3 bucket for migration status tracking
|
|
435
|
+
result.resources.FriggMigrationStatusBucket = {
|
|
436
|
+
Type: 'AWS::S3::Bucket',
|
|
437
|
+
DeletionPolicy: 'Retain', // Protect migration history during stack rollbacks/deletions
|
|
438
|
+
UpdateReplacePolicy: 'Retain', // Protect during stack updates that require replacement
|
|
439
|
+
Properties: {
|
|
440
|
+
// Let CloudFormation auto-generate bucket name for global uniqueness
|
|
441
|
+
// Result: ${StackName}-friggmigrationstatusbucket-${randomHash}
|
|
442
|
+
// Example: quo-integrations-prod-friggmigrationstatusbucket-abc123xyz
|
|
443
|
+
// This ensures no conflicts across accounts/regions/stages
|
|
444
|
+
// BucketName: undefined (CloudFormation generates unique name)
|
|
445
|
+
VersioningConfiguration: {
|
|
446
|
+
Status: 'Enabled', // Enable versioning for audit trail
|
|
447
|
+
},
|
|
448
|
+
LifecycleConfiguration: {
|
|
449
|
+
Rules: [
|
|
450
|
+
{
|
|
451
|
+
Id: 'DeleteOldMigrations',
|
|
452
|
+
Status: 'Enabled',
|
|
453
|
+
ExpirationInDays: 90, // Keep migration history for 90 days
|
|
454
|
+
},
|
|
455
|
+
],
|
|
456
|
+
},
|
|
457
|
+
PublicAccessBlockConfiguration: {
|
|
458
|
+
BlockPublicAcls: true,
|
|
459
|
+
BlockPublicPolicy: true,
|
|
460
|
+
IgnorePublicAcls: true,
|
|
461
|
+
RestrictPublicBuckets: true,
|
|
462
|
+
},
|
|
463
|
+
Tags: [
|
|
464
|
+
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
465
|
+
{ Key: 'Purpose', Value: 'MigrationStatusTracking' },
|
|
466
|
+
],
|
|
467
|
+
},
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
console.log(' ✓ Created FriggMigrationStatusBucket resource');
|
|
471
|
+
|
|
472
|
+
// Create SQS queue for migration jobs
|
|
473
|
+
result.resources.DbMigrationQueue = {
|
|
474
|
+
Type: 'AWS::SQS::Queue',
|
|
475
|
+
Properties: {
|
|
476
|
+
QueueName: '${self:service}-${self:provider.stage}-DbMigrationQueue',
|
|
477
|
+
VisibilityTimeout: 900, // 15 minutes for long-running migrations
|
|
478
|
+
MessageRetentionPeriod: 1209600, // 14 days
|
|
479
|
+
ReceiveMessageWaitTimeSeconds: 20, // Long polling
|
|
480
|
+
},
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
console.log(' ✓ Created DbMigrationQueue resource');
|
|
484
|
+
|
|
485
|
+
// Add S3 bucket name to environment (for migration Lambda functions)
|
|
486
|
+
result.environment.S3_BUCKET_NAME = { Ref: 'FriggMigrationStatusBucket' };
|
|
487
|
+
result.environment.MIGRATION_STATUS_BUCKET = { Ref: 'FriggMigrationStatusBucket' };
|
|
488
|
+
|
|
489
|
+
// Add queue URL to environment
|
|
490
|
+
result.environment.DB_MIGRATION_QUEUE_URL = { Ref: 'DbMigrationQueue' };
|
|
491
|
+
|
|
492
|
+
// Hardcode DB_TYPE for PostgreSQL-only migrations
|
|
493
|
+
result.environment.DB_TYPE = 'postgresql';
|
|
494
|
+
|
|
495
|
+
console.log(' ✓ Added S3_BUCKET_NAME, DB_MIGRATION_QUEUE_URL, and DB_TYPE environment variables');
|
|
496
|
+
|
|
497
|
+
// Add IAM permissions for SQS (for Lambda functions)
|
|
498
|
+
result.iamStatements.push({
|
|
499
|
+
Effect: 'Allow',
|
|
500
|
+
Action: [
|
|
501
|
+
'sqs:SendMessage',
|
|
502
|
+
'sqs:GetQueueUrl',
|
|
503
|
+
'sqs:GetQueueAttributes',
|
|
504
|
+
],
|
|
505
|
+
Resource: { 'Fn::GetAtt': ['DbMigrationQueue', 'Arn'] },
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
console.log(' ✓ Added SQS IAM permissions');
|
|
509
|
+
|
|
510
|
+
// Add IAM permissions for S3 (migration status storage)
|
|
511
|
+
// Object-level permissions (put, get, delete)
|
|
512
|
+
result.iamStatements.push({
|
|
513
|
+
Effect: 'Allow',
|
|
514
|
+
Action: [
|
|
515
|
+
's3:PutObject',
|
|
516
|
+
's3:GetObject',
|
|
517
|
+
's3:DeleteObject',
|
|
518
|
+
],
|
|
519
|
+
Resource: {
|
|
520
|
+
'Fn::Join': [
|
|
521
|
+
'',
|
|
522
|
+
[
|
|
523
|
+
{ 'Fn::GetAtt': ['FriggMigrationStatusBucket', 'Arn'] },
|
|
524
|
+
'/migrations/*',
|
|
525
|
+
],
|
|
526
|
+
],
|
|
527
|
+
},
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// Bucket-level permissions (list objects)
|
|
531
|
+
result.iamStatements.push({
|
|
532
|
+
Effect: 'Allow',
|
|
533
|
+
Action: ['s3:ListBucket'],
|
|
534
|
+
Resource: { 'Fn::GetAtt': ['FriggMigrationStatusBucket', 'Arn'] },
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
console.log(' ✓ Added S3 IAM permissions for migration status tracking');
|
|
538
|
+
|
|
539
|
+
// Add IAM permission for router to invoke worker Lambda
|
|
540
|
+
result.iamStatements.push({
|
|
541
|
+
Effect: 'Allow',
|
|
542
|
+
Action: ['lambda:InvokeFunction'],
|
|
543
|
+
Resource: {
|
|
544
|
+
'Fn::Sub': 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-dbMigrationWorker',
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
console.log(' ✓ Added Lambda invocation permissions for router → worker');
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Use external migration resources (S3 bucket and SQS queue)
|
|
553
|
+
* Only references external resources - Lambda functions are defined in serverless.yml
|
|
554
|
+
*/
|
|
555
|
+
async useExternalMigrationResources(decisions, appDefinition, result) {
|
|
556
|
+
// Reference external bucket
|
|
557
|
+
const bucketName = decisions.bucket.physicalId;
|
|
558
|
+
if (!bucketName) {
|
|
559
|
+
throw new Error('External bucket specified but no migrationStatusBucket discovered');
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Reference external queue
|
|
563
|
+
const queueUrl = decisions.queue.physicalId;
|
|
564
|
+
if (!queueUrl) {
|
|
565
|
+
throw new Error('External queue specified but no migrationQueueUrl discovered');
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
console.log(` ✓ Using external S3 bucket: ${bucketName}`);
|
|
569
|
+
console.log(` ✓ Using external SQS queue: ${queueUrl}`);
|
|
570
|
+
|
|
571
|
+
// Extract queue ARN from queue URL for IAM permissions
|
|
572
|
+
const queueArn = queueUrl.replace('https://sqs.', 'arn:aws:sqs:')
|
|
573
|
+
.replace('.amazonaws.com/', ':')
|
|
574
|
+
.replace(/\//g, ':');
|
|
575
|
+
|
|
576
|
+
// Add environment variables (using external resource names/URLs)
|
|
577
|
+
result.environment.S3_BUCKET_NAME = bucketName;
|
|
578
|
+
result.environment.MIGRATION_STATUS_BUCKET = bucketName;
|
|
579
|
+
result.environment.DB_MIGRATION_QUEUE_URL = queueUrl;
|
|
580
|
+
result.environment.DB_TYPE = 'postgresql';
|
|
581
|
+
|
|
582
|
+
console.log(' ✓ Added S3_BUCKET_NAME, DB_MIGRATION_QUEUE_URL, and DB_TYPE environment variables');
|
|
583
|
+
|
|
584
|
+
// Add IAM permissions for external SQS queue
|
|
585
|
+
result.iamStatements.push({
|
|
586
|
+
Effect: 'Allow',
|
|
587
|
+
Action: [
|
|
588
|
+
'sqs:SendMessage',
|
|
589
|
+
'sqs:GetQueueUrl',
|
|
590
|
+
'sqs:GetQueueAttributes',
|
|
591
|
+
],
|
|
592
|
+
Resource: queueArn,
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
console.log(' ✓ Added SQS IAM permissions');
|
|
596
|
+
|
|
597
|
+
// Add IAM permissions for external S3 bucket
|
|
598
|
+
const bucketArn = `arn:aws:s3:::${bucketName}`;
|
|
599
|
+
result.iamStatements.push({
|
|
600
|
+
Effect: 'Allow',
|
|
601
|
+
Action: [
|
|
602
|
+
's3:PutObject',
|
|
603
|
+
's3:GetObject',
|
|
604
|
+
's3:DeleteObject',
|
|
605
|
+
],
|
|
606
|
+
Resource: `${bucketArn}/migrations/*`,
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
result.iamStatements.push({
|
|
610
|
+
Effect: 'Allow',
|
|
611
|
+
Action: ['s3:ListBucket'],
|
|
612
|
+
Resource: bucketArn,
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
console.log(' ✓ Added S3 IAM permissions for migration status tracking');
|
|
616
|
+
|
|
617
|
+
// Add IAM permission for router to invoke worker Lambda
|
|
618
|
+
result.iamStatements.push({
|
|
619
|
+
Effect: 'Allow',
|
|
620
|
+
Action: ['lambda:InvokeFunction'],
|
|
621
|
+
Resource: {
|
|
622
|
+
'Fn::Sub': 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-dbMigrationWorker',
|
|
623
|
+
},
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
console.log(' ✓ Added Lambda invocation permissions for router → worker');
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
module.exports = {
|
|
631
|
+
MigrationBuilder,
|
|
632
|
+
};
|
|
633
|
+
|