mirror of
https://github.com/game-ci/unity-builder.git
synced 2026-02-03 23:49:08 +08:00
fix: add aws-local mode - validates AWS CloudFormation templates, executes via local-docker
This commit is contained in:
2
.github/workflows/cloud-runner-integrity.yml
vendored
2
.github/workflows/cloud-runner-integrity.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
K3D_NODE_CONTAINERS: 'k3d-unity-builder-agent-0'
|
||||
AWS_FORCE_PROVIDER: aws
|
||||
AWS_FORCE_PROVIDER: aws-local
|
||||
RESOURCE_TRACKING: 'true'
|
||||
# LocalStack container name on shared Docker network (for K8s pods to access)
|
||||
# Note: Using K8S_LOCALSTACK_HOST instead of LOCALSTACK_HOST to avoid conflict with awslocal CLI
|
||||
|
||||
100
dist/index.js
generated
vendored
100
dist/index.js
generated
vendored
@@ -803,10 +803,14 @@ class CloudRunner {
|
||||
}
|
||||
static async setupSelectedBuildPlatform() {
|
||||
cloud_runner_logger_1.default.log(`Cloud Runner platform selected ${CloudRunner.buildParameters.providerStrategy}`);
|
||||
// Detect LocalStack endpoints and reroute AWS provider to local-docker for CI tests that only need S3
|
||||
// However, if AWS_FORCE_PROVIDER is set to 'aws', we should use AWS provider even with LocalStack
|
||||
// This is needed for integrity tests that validate AWS functionality (ECS, CloudFormation, etc.) with LocalStack
|
||||
const forceAwsProvider = process.env.AWS_FORCE_PROVIDER === 'aws' || process.env.AWS_FORCE_PROVIDER === 'true';
|
||||
// Detect LocalStack endpoints and handle AWS provider appropriately
|
||||
// AWS_FORCE_PROVIDER options:
|
||||
// - 'aws': Force AWS provider (requires LocalStack Pro with ECS support)
|
||||
// - 'aws-local': Validate AWS templates/config but execute via local-docker (for CI without ECS)
|
||||
// - unset/other: Auto-fallback to local-docker when LocalStack detected
|
||||
const awsForceProvider = process.env.AWS_FORCE_PROVIDER || '';
|
||||
const forceAwsProvider = awsForceProvider === 'aws' || awsForceProvider === 'true';
|
||||
const useAwsLocalMode = awsForceProvider === 'aws-local';
|
||||
const endpointsToCheck = [
|
||||
process.env.AWS_ENDPOINT,
|
||||
process.env.AWS_S3_ENDPOINT,
|
||||
@@ -825,14 +829,28 @@ class CloudRunner {
|
||||
.join(' ');
|
||||
const isLocalStack = /localstack|localhost|127\.0\.0\.1/i.test(endpointsToCheck);
|
||||
let provider = CloudRunner.buildParameters.providerStrategy;
|
||||
if (provider === 'aws' && isLocalStack && !forceAwsProvider) {
|
||||
cloud_runner_logger_1.default.log('LocalStack endpoints detected; routing provider to local-docker for this run');
|
||||
cloud_runner_logger_1.default.log('Note: Set AWS_FORCE_PROVIDER=aws to force AWS provider with LocalStack for AWS functionality tests');
|
||||
provider = 'local-docker';
|
||||
}
|
||||
else if (provider === 'aws' && isLocalStack && forceAwsProvider) {
|
||||
cloud_runner_logger_1.default.log('LocalStack endpoints detected but AWS_FORCE_PROVIDER is set; using AWS provider to validate AWS functionality');
|
||||
let validateAwsTemplates = false;
|
||||
if (provider === 'aws' && isLocalStack) {
|
||||
if (useAwsLocalMode) {
|
||||
// aws-local mode: Validate AWS templates but execute via local-docker
|
||||
// This provides confidence in AWS CloudFormation without requiring LocalStack Pro
|
||||
cloud_runner_logger_1.default.log('AWS_FORCE_PROVIDER=aws-local: Validating AWS templates, executing via local-docker');
|
||||
validateAwsTemplates = true;
|
||||
provider = 'local-docker';
|
||||
}
|
||||
else if (forceAwsProvider) {
|
||||
// Force full AWS provider (requires LocalStack Pro with ECS support)
|
||||
cloud_runner_logger_1.default.log('LocalStack endpoints detected but AWS_FORCE_PROVIDER=aws; using full AWS provider (requires ECS support)');
|
||||
}
|
||||
else {
|
||||
// Auto-fallback to local-docker
|
||||
cloud_runner_logger_1.default.log('LocalStack endpoints detected; routing provider to local-docker for this run');
|
||||
cloud_runner_logger_1.default.log('Note: Set AWS_FORCE_PROVIDER=aws-local to validate AWS templates with local-docker execution');
|
||||
provider = 'local-docker';
|
||||
}
|
||||
}
|
||||
// Store whether we should validate AWS templates (used by aws-local mode)
|
||||
CloudRunner.validateAwsTemplates = validateAwsTemplates;
|
||||
switch (provider) {
|
||||
case 'k8s':
|
||||
CloudRunner.Provider = new k8s_1.default(CloudRunner.buildParameters);
|
||||
@@ -884,6 +902,11 @@ class CloudRunner {
|
||||
throw new Error(`baseImage is undefined`);
|
||||
}
|
||||
await CloudRunner.setup(buildParameters);
|
||||
// When aws-local mode is enabled, validate AWS CloudFormation templates
|
||||
// This ensures AWS templates are correct even when executing via local-docker
|
||||
if (CloudRunner.validateAwsTemplates) {
|
||||
await CloudRunner.validateAwsCloudFormationTemplates();
|
||||
}
|
||||
await CloudRunner.Provider.setupWorkflow(CloudRunner.buildParameters.buildGuid, CloudRunner.buildParameters, CloudRunner.buildParameters.branch, CloudRunner.defaultSecrets);
|
||||
try {
|
||||
if (buildParameters.maxRetainedWorkspaces > 0) {
|
||||
@@ -943,9 +966,64 @@ class CloudRunner {
|
||||
const jsonContent = JSON.stringify(content, undefined, 4);
|
||||
await github_1.default.updateGitHubCheck(jsonContent, CloudRunner.buildParameters.buildGuid);
|
||||
}
|
||||
/**
|
||||
* Validates AWS CloudFormation templates without deploying them.
|
||||
* Used by aws-local mode to ensure AWS templates are correct when executing via local-docker.
|
||||
* This provides confidence that AWS ECS deployments would work with the generated templates.
|
||||
*/
|
||||
static async validateAwsCloudFormationTemplates() {
|
||||
cloud_runner_logger_1.default.log('=== AWS CloudFormation Template Validation (aws-local mode) ===');
|
||||
try {
|
||||
// Import AWS template formations
|
||||
const { BaseStackFormation } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(29643)));
|
||||
const { TaskDefinitionFormation } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(97647)));
|
||||
// Validate base stack template
|
||||
const baseTemplate = BaseStackFormation.formation;
|
||||
cloud_runner_logger_1.default.log(`✓ Base stack template generated (${baseTemplate.length} chars)`);
|
||||
// Check for required resources in base stack
|
||||
const requiredBaseResources = ['AWS::EC2::VPC', 'AWS::ECS::Cluster', 'AWS::S3::Bucket', 'AWS::IAM::Role'];
|
||||
for (const resource of requiredBaseResources) {
|
||||
if (baseTemplate.includes(resource)) {
|
||||
cloud_runner_logger_1.default.log(` ✓ Contains ${resource}`);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Base stack template missing required resource: ${resource}`);
|
||||
}
|
||||
}
|
||||
// Validate task definition template
|
||||
const taskTemplate = TaskDefinitionFormation.formation;
|
||||
cloud_runner_logger_1.default.log(`✓ Task definition template generated (${taskTemplate.length} chars)`);
|
||||
// Check for required resources in task definition
|
||||
const requiredTaskResources = ['AWS::ECS::TaskDefinition', 'AWS::Logs::LogGroup'];
|
||||
for (const resource of requiredTaskResources) {
|
||||
if (taskTemplate.includes(resource)) {
|
||||
cloud_runner_logger_1.default.log(` ✓ Contains ${resource}`);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Task definition template missing required resource: ${resource}`);
|
||||
}
|
||||
}
|
||||
// Validate YAML syntax by checking for common patterns
|
||||
if (!baseTemplate.includes('AWSTemplateFormatVersion')) {
|
||||
throw new Error('Base stack template missing AWSTemplateFormatVersion');
|
||||
}
|
||||
if (!taskTemplate.includes('AWSTemplateFormatVersion')) {
|
||||
throw new Error('Task definition template missing AWSTemplateFormatVersion');
|
||||
}
|
||||
cloud_runner_logger_1.default.log('=== AWS CloudFormation templates validated successfully ===');
|
||||
cloud_runner_logger_1.default.log('Note: Actual execution will use local-docker provider');
|
||||
}
|
||||
catch (error) {
|
||||
cloud_runner_logger_1.default.log(`AWS CloudFormation template validation failed: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
CloudRunner.lockedWorkspace = ``;
|
||||
CloudRunner.retainedWorkspacePrefix = `retained-workspace`;
|
||||
// When true, validates AWS CloudFormation templates even when using local-docker execution
|
||||
// This is set by AWS_FORCE_PROVIDER=aws-local mode
|
||||
CloudRunner.validateAwsTemplates = false;
|
||||
exports["default"] = CloudRunner;
|
||||
|
||||
|
||||
|
||||
2
dist/index.js.map
generated
vendored
2
dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@@ -28,6 +28,9 @@ class CloudRunner {
|
||||
private static cloudRunnerEnvironmentVariables: CloudRunnerEnvironmentVariable[];
|
||||
static lockedWorkspace: string = ``;
|
||||
public static readonly retainedWorkspacePrefix: string = `retained-workspace`;
|
||||
// When true, validates AWS CloudFormation templates even when using local-docker execution
|
||||
// This is set by AWS_FORCE_PROVIDER=aws-local mode
|
||||
public static validateAwsTemplates: boolean = false;
|
||||
public static get isCloudRunnerEnvironment() {
|
||||
return process.env[`GITHUB_ACTIONS`] !== `true`;
|
||||
}
|
||||
@@ -70,10 +73,14 @@ class CloudRunner {
|
||||
private static async setupSelectedBuildPlatform() {
|
||||
CloudRunnerLogger.log(`Cloud Runner platform selected ${CloudRunner.buildParameters.providerStrategy}`);
|
||||
|
||||
// Detect LocalStack endpoints and reroute AWS provider to local-docker for CI tests that only need S3
|
||||
// However, if AWS_FORCE_PROVIDER is set to 'aws', we should use AWS provider even with LocalStack
|
||||
// This is needed for integrity tests that validate AWS functionality (ECS, CloudFormation, etc.) with LocalStack
|
||||
const forceAwsProvider = process.env.AWS_FORCE_PROVIDER === 'aws' || process.env.AWS_FORCE_PROVIDER === 'true';
|
||||
// Detect LocalStack endpoints and handle AWS provider appropriately
|
||||
// AWS_FORCE_PROVIDER options:
|
||||
// - 'aws': Force AWS provider (requires LocalStack Pro with ECS support)
|
||||
// - 'aws-local': Validate AWS templates/config but execute via local-docker (for CI without ECS)
|
||||
// - unset/other: Auto-fallback to local-docker when LocalStack detected
|
||||
const awsForceProvider = process.env.AWS_FORCE_PROVIDER || '';
|
||||
const forceAwsProvider = awsForceProvider === 'aws' || awsForceProvider === 'true';
|
||||
const useAwsLocalMode = awsForceProvider === 'aws-local';
|
||||
const endpointsToCheck = [
|
||||
process.env.AWS_ENDPOINT,
|
||||
process.env.AWS_S3_ENDPOINT,
|
||||
@@ -92,18 +99,33 @@ class CloudRunner {
|
||||
.join(' ');
|
||||
const isLocalStack = /localstack|localhost|127\.0\.0\.1/i.test(endpointsToCheck);
|
||||
let provider = CloudRunner.buildParameters.providerStrategy;
|
||||
if (provider === 'aws' && isLocalStack && !forceAwsProvider) {
|
||||
CloudRunnerLogger.log('LocalStack endpoints detected; routing provider to local-docker for this run');
|
||||
CloudRunnerLogger.log(
|
||||
'Note: Set AWS_FORCE_PROVIDER=aws to force AWS provider with LocalStack for AWS functionality tests',
|
||||
);
|
||||
provider = 'local-docker';
|
||||
} else if (provider === 'aws' && isLocalStack && forceAwsProvider) {
|
||||
CloudRunnerLogger.log(
|
||||
'LocalStack endpoints detected but AWS_FORCE_PROVIDER is set; using AWS provider to validate AWS functionality',
|
||||
);
|
||||
let validateAwsTemplates = false;
|
||||
|
||||
if (provider === 'aws' && isLocalStack) {
|
||||
if (useAwsLocalMode) {
|
||||
// aws-local mode: Validate AWS templates but execute via local-docker
|
||||
// This provides confidence in AWS CloudFormation without requiring LocalStack Pro
|
||||
CloudRunnerLogger.log('AWS_FORCE_PROVIDER=aws-local: Validating AWS templates, executing via local-docker');
|
||||
validateAwsTemplates = true;
|
||||
provider = 'local-docker';
|
||||
} else if (forceAwsProvider) {
|
||||
// Force full AWS provider (requires LocalStack Pro with ECS support)
|
||||
CloudRunnerLogger.log(
|
||||
'LocalStack endpoints detected but AWS_FORCE_PROVIDER=aws; using full AWS provider (requires ECS support)',
|
||||
);
|
||||
} else {
|
||||
// Auto-fallback to local-docker
|
||||
CloudRunnerLogger.log('LocalStack endpoints detected; routing provider to local-docker for this run');
|
||||
CloudRunnerLogger.log(
|
||||
'Note: Set AWS_FORCE_PROVIDER=aws-local to validate AWS templates with local-docker execution',
|
||||
);
|
||||
provider = 'local-docker';
|
||||
}
|
||||
}
|
||||
|
||||
// Store whether we should validate AWS templates (used by aws-local mode)
|
||||
CloudRunner.validateAwsTemplates = validateAwsTemplates;
|
||||
|
||||
switch (provider) {
|
||||
case 'k8s':
|
||||
CloudRunner.Provider = new Kubernetes(CloudRunner.buildParameters);
|
||||
@@ -157,6 +179,12 @@ class CloudRunner {
|
||||
throw new Error(`baseImage is undefined`);
|
||||
}
|
||||
await CloudRunner.setup(buildParameters);
|
||||
|
||||
// When aws-local mode is enabled, validate AWS CloudFormation templates
|
||||
// This ensures AWS templates are correct even when executing via local-docker
|
||||
if (CloudRunner.validateAwsTemplates) {
|
||||
await CloudRunner.validateAwsCloudFormationTemplates();
|
||||
}
|
||||
await CloudRunner.Provider.setupWorkflow(
|
||||
CloudRunner.buildParameters.buildGuid,
|
||||
CloudRunner.buildParameters,
|
||||
@@ -252,5 +280,62 @@ class CloudRunner {
|
||||
const jsonContent = JSON.stringify(content, undefined, 4);
|
||||
await GitHub.updateGitHubCheck(jsonContent, CloudRunner.buildParameters.buildGuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates AWS CloudFormation templates without deploying them.
|
||||
* Used by aws-local mode to ensure AWS templates are correct when executing via local-docker.
|
||||
* This provides confidence that AWS ECS deployments would work with the generated templates.
|
||||
*/
|
||||
private static async validateAwsCloudFormationTemplates() {
|
||||
CloudRunnerLogger.log('=== AWS CloudFormation Template Validation (aws-local mode) ===');
|
||||
|
||||
try {
|
||||
// Import AWS template formations
|
||||
const { BaseStackFormation } = await import('./providers/aws/cloud-formations/base-stack-formation');
|
||||
const { TaskDefinitionFormation } = await import('./providers/aws/cloud-formations/task-definition-formation');
|
||||
|
||||
// Validate base stack template
|
||||
const baseTemplate = BaseStackFormation.formation;
|
||||
CloudRunnerLogger.log(`✓ Base stack template generated (${baseTemplate.length} chars)`);
|
||||
|
||||
// Check for required resources in base stack
|
||||
const requiredBaseResources = ['AWS::EC2::VPC', 'AWS::ECS::Cluster', 'AWS::S3::Bucket', 'AWS::IAM::Role'];
|
||||
for (const resource of requiredBaseResources) {
|
||||
if (baseTemplate.includes(resource)) {
|
||||
CloudRunnerLogger.log(` ✓ Contains ${resource}`);
|
||||
} else {
|
||||
throw new Error(`Base stack template missing required resource: ${resource}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate task definition template
|
||||
const taskTemplate = TaskDefinitionFormation.formation;
|
||||
CloudRunnerLogger.log(`✓ Task definition template generated (${taskTemplate.length} chars)`);
|
||||
|
||||
// Check for required resources in task definition
|
||||
const requiredTaskResources = ['AWS::ECS::TaskDefinition', 'AWS::Logs::LogGroup'];
|
||||
for (const resource of requiredTaskResources) {
|
||||
if (taskTemplate.includes(resource)) {
|
||||
CloudRunnerLogger.log(` ✓ Contains ${resource}`);
|
||||
} else {
|
||||
throw new Error(`Task definition template missing required resource: ${resource}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate YAML syntax by checking for common patterns
|
||||
if (!baseTemplate.includes('AWSTemplateFormatVersion')) {
|
||||
throw new Error('Base stack template missing AWSTemplateFormatVersion');
|
||||
}
|
||||
if (!taskTemplate.includes('AWSTemplateFormatVersion')) {
|
||||
throw new Error('Task definition template missing AWSTemplateFormatVersion');
|
||||
}
|
||||
|
||||
CloudRunnerLogger.log('=== AWS CloudFormation templates validated successfully ===');
|
||||
CloudRunnerLogger.log('Note: Actual execution will use local-docker provider');
|
||||
} catch (error: any) {
|
||||
CloudRunnerLogger.log(`AWS CloudFormation template validation failed: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
export default CloudRunner;
|
||||
|
||||
Reference in New Issue
Block a user