Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EventBridge replaces default CPU/Memory on Override to ECS #5253

Open
quinnjr opened this issue Feb 28, 2025 · 3 comments
Open

EventBridge replaces default CPU/Memory on Override to ECS #5253

quinnjr opened this issue Feb 28, 2025 · 3 comments
Labels
awaiting-upstream The issue cannot be resolved without action in another repository (may be owned by Pulumi). kind/bug Some behavior is incorrect or out of spec

Comments

@quinnjr
Copy link

quinnjr commented Feb 28, 2025

Describe what happened

EventBridge invocations of ECS tasks are overriding the CPU/Memory configurations of the container being invoke on ECS with defaults of 0 CPU and 0 Memory unless specifically non-defaulted in the EventBridge configuration.

Sample program

EventBridge pipe:

      this.pipe = new aws.pipes.Pipe(
        generateNameTag('level-zero-pipe'),
        {
          name: generateNameTag('level-zero-pipe'),
          roleArn: this.pipeRole.arn,
          source: args.queues.levelZeroQueue!.arn,
          target: args.ecs.recordbossCluster!.arn,
          sourceParameters: {
            sqsQueueParameters: {
              batchSize: 1
            }
          },
          targetParameters: {
            ecsTaskParameters: {
              taskDefinitionArn: args.ecs.levelZeroTaskDefinition!.arn,
              taskCount: 1,
              launchType: 'FARGATE',
              platformVersion: 'LATEST',
              networkConfiguration: {
                awsVpcConfiguration: {
                  assignPublicIp: 'DISABLED',
                  subnets: [
                    args.vpc.privateSubnetZoneA.id,
                    args.vpc.privateSubnetZoneB.id
                  ],
                  securityGroups: [this.securityGroup.id]
                }
              },
              overrides: {
                containerOverrides: [
                  {
                    name: generateNameTag('ecs-level-zero-container'),
                    cpu: 2048, // Must specify or defaults to 0
                    memory: 12288, // Must specify or defaults to 0
                    environments: [
                      {
                        name: 'SQS_MESSAGE',
                        value: '$.body'
                      }
                    ]
                  }
                ]
              }
            },
            inputTemplate: JSON.stringify({
              containerOverrides: [
                {
                  name: generateNameTag('ecs-level-zero-container'),
                  environment: [
                    {
                      name: 'SQS_MESSAGE',
                      value: '<$.body>'
                    }
                  ]
                }
              ]
            })
          },
          logConfiguration: {
            includeExecutionDatas: ['ALL'],
            level: 'INFO',
            cloudwatchLogsLogDestination: {
              logGroupArn: args.ecs.levelZeroLogGroup!.arn
            }
          },
          tags: {
            ...commonTag
          }
        },
        {
          parent: this,
          dependsOn: [
            this.eventRolePolicyAttachment,
            this.securityGroup,
            this.pipeRole
          ]
        }
      );
    }

ECS Task Definition:

this.containerDefinition = {
        name: generateNameTag('ecs-level-zero-container'),
        image: `947723167413.dkr.ecr.us-west-2.amazonaws.com/recordboss-level-0:${environment === 'production' ? 'latest' : 'develop'}`,
        essential: true,
        cpu: 2048,
        memory: 12288,
        portMappings: [
          {
            containerPort: 80,
            hostPort: 80,
            protocol: 'tcp'
          },
          {
            containerPort: 443,
            hostPort: 443,
            protocol: 'tcp'
          },
          {
            containerPort: 3306,
            hostPort: 3306,
            protocol: 'tcp'
          },
          {
            containerPort: 5432,
            hostPort: 5432,
            protocol: 'tcp'
          }
        ],
        logConfiguration: {
          logDriver: 'awslogs',
          options: {
            'awslogs-group': '/ecs/recordboss/level-zero',
            'awslogs-region': aws.config.requireRegion(),
            'awslogs-stream-prefix': 'ecs'
          }
        },
        environment: [
          {
            name: 'ENVIRONMENT',
            value: environment
          },
          {
            name: 'AWS_REGION',
            value: aws.config.requireRegion()
          },
          {
            name: 'S3_BUCKET',
            value: generateNameTag('files')
          },
          {
            name: 'SQS_MESSAGE',
            value: ''
          },
          {
            name: 'DB_NAME',
            value: 'recordboss'
          }
        ],
        secrets: [
          {
            name: 'DB_PASSWORD',
            valueFrom: `arn:aws:ssm:us-west-2:947723167413:parameter/${environment}/database/password`
          },
          {
            name: 'DB_USERNAME',
            valueFrom: `arn:aws:ssm:us-west-2:947723167413:parameter/${environment}/database/username`
          },
          {
            name: 'DB_HOST',
            valueFrom: `arn:aws:ssm:us-west-2:947723167413:parameter/${environment}/database/url`
          }
        ]
      };

      this.taskDefinition = new aws.ecs.TaskDefinition(
        generateNameTag('ecs-level-zero-task-definition'),
        {
          family: generateNameTag('ecs-level-zero'),
          cpu: this.containerDefinition.cpu.toString(),
          memory: this.containerDefinition.memory.toString(),
          networkMode: 'awsvpc',
          requiresCompatibilities: ['FARGATE'],
          executionRoleArn: this.taskExecRole.arn,
          taskRoleArn: this.taskRole.arn,
          trackLatest: true,
          runtimePlatform: {
            cpuArchitecture: 'ARM64'
          },
          ephemeralStorage: {
            sizeInGib: 40
          },
          containerDefinitions: pulumi
            .all([this.containerDefinition, aws.config.requireRegion()])
            .apply(([container, region]) => {
              const resolvedContainer = {
                ...container,
                logConfiguration: {
                  ...container.logConfiguration,
                  options: {
                    ...container.logConfiguration.options,
                    'awslogs-region': region
                  }
                },
                environment: container.environment.map((env) => {
                  return env;
                })
              };
              return JSON.stringify([resolvedContainer]);
            })
        },
        childOpts
      );

Log output

No log output is given, but the override on the container definition in the EventBridge pipe shows 0 on CPU/Memory if defaults are not set.

Affected Resource(s)

No response

Output of pulumi about

CLI
Version 3.153.0
Go Version go1.23.6
Go Compiler gc

Plugins
KIND NAME VERSION
resource aws 6.66.2
resource awsx 2.19.0
resource docker 4.5.8
resource docker 3.6.1
resource grafana 0.4.2
language nodejs 3.153.0
resource random 4.16.8

Host
OS ubuntu
Version 22.04
Arch x86_64

This project is written in nodejs: executable='/home/joseph/.local/share/nvm/versions/node/v22.13.1/bin/node' version='v22.13.1'

Backend
Name pulumi.com
URL https://app.pulumi.com/jquinn
User jquinn
Organizations jquinn, recordboss
Token type personal

Dependencies:
NAME VERSION
ts-node 10.9.2
uuid 9.0.1
@pulumi/aws 6.66.2
@typescript-eslint/eslint-plugin 8.19.1
@typescript-eslint/parser 8.19.1
eslint-plugin-unicorn 55.0.0
husky 9.1.7
typescript 5.7.2
@pulumi/awsx 2.19.0
@types/node 20.17.12
eslint-config-prettier 9.1.0
eslint-plugin-prettier 5.2.1
eslint 9.17.0
@aws-sdk/client-rds-data 3.723.0
@pulumi/pulumi 3.145.0
@types/aws-sdk 2.7.4
lint-staged 15.3.0
@pulumi/random 4.16.8
@pulumiverse/grafana v0.4.2
aws-sdk 2.1692.0
prettier 3.4.2

Pulumi locates its logs in /tmp by default

Additional context

No response

Contributing

Vote on this issue by adding a 👍 reaction.
To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already).

@quinnjr quinnjr added kind/bug Some behavior is incorrect or out of spec needs-triage Needs attention from the triage team labels Feb 28, 2025
@VenelinMartinov
Copy link
Contributor

Hey @quinnjr, thanks for reporting and sorry you've hit this.

Can you please provide a self-contained program which reproduces the issue? That'd help us a lot in narrowing down the cause. The code excerpts look like they might be part of a component.

@VenelinMartinov VenelinMartinov added awaiting-feedback Blocked on input from the author needs-repro Needs repro steps before it can be triaged or fixed and removed needs-triage Needs attention from the triage team labels Mar 3, 2025
@quinnjr
Copy link
Author

quinnjr commented Mar 10, 2025

import aws from '@pulumi/aws';

const queue = new aws.sqs.Queue('test-queue', {
  name: 'test-queue',
  visibilityTimeoutSeconds: 60,
  fifoQueue: true,
  contentBasedDeduplication: true,
  sqsManagedSseEnabled: true
});

const taskRole = new aws.iam.Role('task-role', {
  assumeRolePolicy: JSON.stringify({
    Version: '2012-10-17',
    Statement: [{
      Action: 'sts:AssumeRole',
      Effect: 'Allow',
      Principal: {
        Service: 'ecs-tasks.amazonaws.com'
      }
    }]
  })
});

new aws.iam.RolePolicyAttachment('task-role-policy', {
  policyArn: aws.iam.ManagedPolicy.AmazonECSTaskExecutionRolePolicy,
  role: taskRole
});

const containerDefinition = {
  name: 'test',
  image: 'foobar',
  essential: true,
  portMappings: [
    {
      containerPort: 80,
      hostPort: 80,
      protocol: 'tcp'
    },
    {
      containerPort: 443,
      hostPort: 443,
      protocol: 'tcp'
    }
  ],
  environment: [
    {
      name: 'SQS_MESSAGE',
      value: ''
    }
  ],
  memory: 2048,
  cpu: 1024
};

const taskDefinition = new aws.ecs.TaskDefinition('test', {
  family: 'test',
  cpu: '1024',
  memory: '2048',
  networkMode: 'awsvpc',
  requiresCompatibilities: ['FARGATE'],
  trackLatest: true,
  containerDefinitions: JSON.stringify([containerDefinition]),
  executionRoleArn: taskRole.arn
});

const cluster = new aws.ecs.Cluster('test');

const eventRole = new aws.iam.Role('event-role', {
  assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({
    Service: 'events.amazonaws.com'
  })
});

new aws.iam.RolePolicyAttachment('event-role-policy', {
  policyArn: 'arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceEventsRole',
  role: eventRole
});

const pipeRole = new aws.iam.Role('pipe-role', {
  assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({
    Service: 'pipes.amazonaws.com'
  })
});

new aws.iam.RolePolicy('pipe-role-policy', {
  role: pipeRole,
  policy: JSON.stringify({
    Version: '2012-10-17',
    Statement: [
      {
        Effect: 'Allow',
        Action: [
          'events:PutEvents',
          'sqs:SendMessage',
          'sqs:GetQueueAttributes',
          'sqs:ReceiveMessage',
          'sqs:DeleteMessage'
        ],
        Resource: '*'
      },
      {
        Effect: 'Allow',
        Action: ['ecs:RunTask'],
        Resource: taskDefinition.arn,
        Condition: {
          ArnEquals: {
            'ecs:cluster': cluster.arn
          }
        }
      },
      {
        Effect: 'Allow',
        Action: ['iam:PassRole'],
        Resource: '*'
      }
    ]
  })
});

const pipe = new aws.pipes.Pipe('test-pipe', {
  roleArn: pipeRole.arn,
  source: queue.arn,
  target: cluster.arn,
  sourceParameters: {
    sqsQueueParameters: {
      batchSize: 1
    }
  },
  targetParameters: {
    ecsTaskParameters: {
      taskDefinitionArn: taskDefinition.arn,
      taskCount: 1,
      launchType: 'FARGATE',
      platformVersion: '1.4.0',
      overrides: {
        containerOverrides: [
          {
            name: containerDefinition.name,
            environments: [
              {
                name: 'SQS_MESSAGE',
                value: 'test'
              }
            ]
          }
        ]
      },
    },
    inputTemplate: JSON.stringify({
      containerOverrides: [
        {
          name: containerDefinition.name,
          environment: [
            {
              name: 'SQS_MESSAGE',
              value: '<$.body>'
            }
          ]
        }
      ]
    })
  }
});

@pulumi-bot pulumi-bot added needs-triage Needs attention from the triage team and removed awaiting-feedback Blocked on input from the author labels Mar 10, 2025
@corymhall corymhall added awaiting-upstream The issue cannot be resolved without action in another repository (may be owned by Pulumi). and removed needs-repro Needs repro steps before it can be triaged or fixed needs-triage Needs attention from the triage team labels Mar 11, 2025
@corymhall
Copy link
Contributor

@quinnjr thanks for the repro. It looks like this is a bug with the upstream provider.

hashicorp/terraform-provider-aws#40117

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting-upstream The issue cannot be resolved without action in another repository (may be owned by Pulumi). kind/bug Some behavior is incorrect or out of spec
Projects
None yet
Development

No branches or pull requests

4 participants