aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.64k stars 3.91k forks source link

(aws-events): RuleTargetInput.fromObject generating invalid template during CDK deploy #31851

Closed CrypticCabub closed 1 day ago

CrypticCabub commented 1 week ago

Describe the bug

When using RuleTargetInput.fromObject() with EventField.fromPath fields, CDK generates an invalid InputPathsMap by including a trailing comma in the InputsPathMap.

However, this behavior is not consistent. When running cdk synth, the InputsPathMap is generated correctly, but it is not generated correctly when attempting to deploy with cdk deploy or with Template.fromStack() in snapshot tests. (see examples)

Regression Issue

Last Known Working CDK Version

N/A

Expected Behavior

When using RuleTargetInput.fromObject(), I expect a valid InputsPathMap to be generated every time

Current Behavior

Running a snapshot test or cdk deploy produces the following:

"InputTransformer": {
  "InputPathsMap": {
    "detail-type": "$.detail-type",
    "f2": "$.",
  },
  "InputTemplate": "{"message":"EventBus received <detail-type> event","module":"EventBus","event":<f2>}",
},

whereas running cdk synth produces the following:

"InputTransformer": {
 "InputPathsMap": {
  "detail-type": "$.detail-type",
  "f2": "$."
 },

The additional comma in the first example makes the stack undeployable and causes the following CloudFormation Error:

12:07:26 PM | CREATE_FAILED        | AWS::Events::Rule                   | EventBusRule4D34F922
Resource handler returned message: "InputPath for target Target0 is invalid. (Service: EventBridge, Status Code: 400, Request ID: 1a2365b2-6d9b-4f00-a1b3-d809f3db94d8)" (RequestToken: 0887dd15-041d-e40a-144c-1d787923e0e5, HandlerErrorCode: GeneralServiceException)

Reproduction Steps

cdk app:

import { Stack, type StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import {EventBus, EventField, Rule, RuleTargetInput} from "aws-cdk-lib/aws-events";
import {CloudWatchLogGroup} from "aws-cdk-lib/aws-events-targets";
import {LogGroup} from "aws-cdk-lib/aws-logs";
import * as cdk from "aws-cdk-lib";

export class BrokenExample extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const logGroup = new LogGroup(this, "LogGroup", {});

    const eventBus = new EventBus(this, "EventBus", {
      description: "example event bus",
    });

    new Rule(this, "EventBusRule", {
      description: "logs all events that get sent to the event bus",
      eventBus: eventBus,
      targets: [
        new CloudWatchLogGroup(logGroup, {
          installLatestAwsSdk: false,
          retryAttempts: 20,
          logEvent: RuleTargetInput.fromObject({
            message: `EventBus received ${EventField.fromPath("$.detail-type")} event`,
            module: "EventBus",
            event: EventField.fromPath("$."),
          }),
        }),
      ],
      eventPattern: {
        source: [{ prefix: "" }] as any[],
      },
    });
  }
}

const app = new cdk.App();

new BrokenExample(app, "BrokenExample", {})

snapshot test example:

import { describe, expect, it } from "vitest";
import {App} from "aws-cdk-lib";
import {Template} from "aws-cdk-lib/assertions";
//import BrokenExample

describe("Snapshot", () => {
  it("matches the snapshot", () => {
    const app = new App();
    const stack = new BrokenExample(app, "example");
    const template = Template.fromStack(stack);
    expect(template).toMatchSnapshot();
  });
});

Possible Solution

Not sure why the use of cdk synth would have a different result than cdk deploy, but the issue is likely a simple off-by-one error on a for loop somewhere.

Additional Information/Context

No response

CDK CLI Version

2.163.0 (build ad5325a)

Framework Version

No response

Node.js Version

18.18.2

OS

Mac

Language

TypeScript

Language Version

5.6.3

Other information

No response

CrypticCabub commented 1 week ago

further debugging on my side makes it appear that the culprit for the deployment error is actually the selector under event: and the extra comma is an artifact of jest snapshots but doesn't make it through to the deployment console.

Am doing some further testing to confirm, but this may be another case of playing hide-and-seek with punctuation

khushail commented 1 week ago

Hi @CrypticCabub , thanks for reaching out.

I tried to repro the mentioned scenario. However for me, the comma is not added in both the cases when I do cdk synth or cdk deploy but deployment fails in both cases with the same error of GeneralServiceException. I am trying to identify the root cause though.

CrypticCabub commented 6 days ago

I believe the root cause is that "$." is not the correct selector but rather should be just "$". Am still having some problems with permissions not allowing EventBridge to actually write the log to cloudwatch, but deployment isn't failing anymore

khushail commented 1 day ago

@CrypticCabub ,I am still not able to repro the scenario you mentioned initially but did some observations by tweaking the code -

So I tried replacing the event.fromPath("$") with different value and my synth as well as deployment succeeded. Sharing the code and result -

  1. initial code -

    const logGroup = new LogGroup(this, "LogGroup", {});
    
    const eventBus = new EventBus(this, "EventBus", {
      description: "example event bus",
    });
    
    new Rule(this, "EventBusRule", {
      description: "logs all events that get sent to the event bus",
      eventBus: eventBus,
      targets: [
        new CloudWatchLogGroup(logGroup, {
          installLatestAwsSdk: false,
          retryAttempts: 20,
          logEvent: RuleTargetInput.fromObject({
            message: `EventBus received ${EventField.fromPath("$.detail-type")} event`,
            module: "EventBus",
            event: EventField.fromPath("$."),
          }),
        }),
      ],
      eventPattern: {
        source: [{ prefix: "" }] as any[],
      },
    });
    }
    1. changed to this -
      
      const logGroup = new LogGroup(this, "LogGroup", {});

    const eventBus = new EventBus(this, "EventBus", { description: "example event bus", });

    new Rule(this, "EventBusRule", { description: "logs all events that get sent to the event bus", eventBus: eventBus, targets: [ new CloudWatchLogGroup(logGroup, { installLatestAwsSdk: false, retryAttempts: 20, logEvent: RuleTargetInput.fromObject({ message: EventBus received ${EventField.fromPath("$.detail-type")} event, module: "EventBus", event: EventField.fromPath("$.detail-type"), }), }), ], eventPattern: { source: [{ prefix: "" }] as any[], }, }); }

    
    3. deploying code 1 resulted in `error - "InputPath for target Target0 is invalid.` but when I changed the `.fromPath("$.detail-type"), the result from `cdk diff` -
Screenshot 2024-10-29 at 11 36 49 AM

which means the earlier InputTemplate was wrongly generated due to the path mentioned.

Deploying the changed code succeeded-

Screenshot 2024-10-29 at 12 00 03 PM

AFAIU, the fromPath() should have complete path value which is properly synthesized and deployed. Giving the value as "$." is incorrect (as you also noticed above) as CDK might be expecting complete path here. However I am trying to look for evidence to support my claim :)

CrypticCabub commented 1 day ago

yes, I can confirm that the issue appears to have been the $. selector, so this looks like a false-positive. Thanks for taking the time to look into this.

github-actions[bot] commented 1 day ago

Comments on closed issues and PRs are hard for our team to see. If you need help, please open a new issue that references this one.