fix: k3d/LocalStack networking - use shared Docker network and container name

This commit is contained in:
frostebite
2026-01-27 19:49:50 +00:00
parent 8319673c26
commit 258e40d807
5 changed files with 130 additions and 20 deletions

View File

@@ -15,6 +15,44 @@ import { AwsClientFactory } from './aws-client-factory';
class AWSTaskRunner {
private static readonly encodedUnderscore = `$252F`;
/**
* Transform localhost endpoints to host.docker.internal for container environments.
* When LocalStack is used, ECS tasks run in Docker containers that need to reach
* LocalStack on the host machine via host.docker.internal.
*/
private static transformEndpointsForContainer(
environment: CloudRunnerEnvironmentVariable[],
): CloudRunnerEnvironmentVariable[] {
const endpointEnvironmentNames = new Set([
'AWS_S3_ENDPOINT',
'AWS_ENDPOINT',
'AWS_CLOUD_FORMATION_ENDPOINT',
'AWS_ECS_ENDPOINT',
'AWS_KINESIS_ENDPOINT',
'AWS_CLOUD_WATCH_LOGS_ENDPOINT',
'INPUT_AWSS3ENDPOINT',
'INPUT_AWSENDPOINT',
]);
return environment.map((x) => {
let value = x.value;
if (
typeof value === 'string' &&
endpointEnvironmentNames.has(x.name) &&
(value.startsWith('http://localhost') || value.startsWith('http://127.0.0.1'))
) {
// Replace localhost with host.docker.internal so ECS containers can access host services
value = value
.replace('http://localhost', 'http://host.docker.internal')
.replace('http://127.0.0.1', 'http://host.docker.internal');
CloudRunnerLogger.log(`AWS TaskRunner: Replaced localhost with host.docker.internal for ${x.name}: ${value}`);
}
return { name: x.name, value };
});
}
static async runTask(
taskDef: CloudRunnerAWSTaskDef,
environment: CloudRunnerEnvironmentVariable[],
@@ -32,6 +70,9 @@ class AWSTaskRunner {
const streamName =
taskDef.taskDefResources?.find((x) => x.LogicalResourceId === 'KinesisStream')?.PhysicalResourceId || '';
// Transform localhost endpoints for container environment
const transformedEnvironment = AWSTaskRunner.transformEndpointsForContainer(environment);
const runParameters = {
cluster,
taskDefinition,
@@ -40,7 +81,7 @@ class AWSTaskRunner {
containerOverrides: [
{
name: taskDef.taskDefStackName,
environment,
environment: transformedEnvironment,
command: ['-c', CommandHookService.ApplyHooksToCommands(commands, CloudRunner.buildParameters)],
},
],

View File

@@ -4,6 +4,7 @@ import { CommandHookService } from '../../services/hooks/command-hook-service';
import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
import CloudRunnerSecret from '../../options/cloud-runner-secret';
import CloudRunner from '../../cloud-runner';
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
class KubernetesJobSpecFactory {
static getJobSpec(
@@ -32,6 +33,12 @@ class KubernetesJobSpecFactory {
'INPUT_AWSS3ENDPOINT',
'INPUT_AWSENDPOINT',
]);
// Determine the LocalStack hostname to use for K8s pods
// Priority: LOCALSTACK_HOST env var > localstack-main (container name on shared network)
const localstackHost = process.env['LOCALSTACK_HOST'] || 'localstack-main';
CloudRunnerLogger.log(`K8s pods will use LocalStack host: ${localstackHost}`);
const adjustedEnvironment = environment.map((x) => {
let value = x.value;
if (
@@ -39,11 +46,12 @@ class KubernetesJobSpecFactory {
endpointEnvironmentNames.has(x.name) &&
(value.startsWith('http://localhost') || value.startsWith('http://127.0.0.1'))
) {
// Replace localhost with host.k3d.internal so pods can access host services
// This simulates accessing external services (like real AWS S3)
// Replace localhost with the LocalStack container hostname
// When k3d and LocalStack are on the same Docker network, pods can reach LocalStack by container name
value = value
.replace('http://localhost', 'http://host.k3d.internal')
.replace('http://127.0.0.1', 'http://host.k3d.internal');
.replace('http://localhost', `http://${localstackHost}`)
.replace('http://127.0.0.1', `http://${localstackHost}`);
CloudRunnerLogger.log(`Replaced localhost with ${localstackHost} for ${x.name}: ${value}`);
}
return { name: x.name, value } as CloudRunnerEnvironmentVariable;