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

(aws_apigateway): Requests route to proxy for resources defined by RestApi.from_rest_api_attributes #16000

Closed
crawfobw opened this issue Aug 11, 2021 · 9 comments
Assignees
Labels
@aws-cdk/aws-apigateway Related to Amazon API Gateway bug This issue is a bug. closed-for-staleness This issue was automatically closed because it hadn't received any attention in a while. response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days.

Comments

@crawfobw
Copy link

crawfobw commented Aug 11, 2021

When defining a RestApi CDK resources via aws_apigateway.RestApi.from_rest_api_attributes any resources added to the API where the Integration Request is a Lambda Proxy result in a method request path of {proxy=bar} when it should be a method request path of {} and route to the defined resource+lambda.

Reproduction Steps

class ApiStack(core.Stack):
  def __init__(self, scope: core.Construct, construct_id: str, foo_function: aws_lambda.Function **kwargs):
    super().__init__(scope, construct_id, **kwargs)
    self.api = aws_apigateway.RestApi(self, 'some-api', rest_api_name='some-api')
    foo_resource = self.api.root.add_resource('foo').add_method('GET', integration = aws_apigateway.LambdaIntegration(foo_function))

class ApiResourceStack(core.Stack):
  def __init__(self, scope: core.Construct, construct_id: str, rest_api_id: str, root_resource_id: str, bar_function: aws_lambda.Function, **kwargs):
    super().__init__(scope, construct_id, **kwargs)
    api = aws_apigateway.RestApi.from_rest_api_attributes(self, 'some-api-import', rest_api_id=rest_api_id, root_resource_id=root_resource_id)
    bar_resource = api.root.add_resource('bar')
    bar_resource.add_method('GET', integration = aws_apigateway.LambdaIntegration(bar_function))

What did you expect to happen?

Lambda function on /bar resource to invoke

What actually happened?

API returns attempts to invoke {+proxy} path resulting in 500 error

Environment

  • CDK CLI Version : 1.108.1
  • Framework Version: 1.111.0
  • Node.js Version: 16.3.0
  • OS : OSx 11.4
  • Language (Version): Python 3.8

Other

Note there is clearly a GET /bar method + resource on the API gateway displayed in the AWS console.

API execution logs

API-Gateway-Execution-Logs_<redacted_api_id>/prod ff7b731a2abc9f1c6522cc4346a7f6cf (60098f2f-3b91-43f5-8891-098d9562e993) HTTP Method: GET, Resource Path: /bar
API-Gateway-Execution-Logs_<redacted_api_id>/prod ff7b731a2abc9f1c6522cc4346a7f6cf (60098f2f-3b91-43f5-8891-098d9562e993) Method request path: {proxy=bar}
API-Gateway-Execution-Logs_<redacted_api_id>/prod ff7b731a2abc9f1c6522cc4346a7f6cf (60098f2f-3b91-43f5-8891-098d9562e993) Method request query string: {}

The lambda that is created gets two Policy Statements under Resource-based policy assigned like

{
 "ArnLike": {
  "AWS:SourceArn": "arn:aws:execute-api:us-east-2:948097384784:<rest-api-id>/test-invoke-stage/GET/bar"
 }
}

and

{
 "ArnLike": {
  "AWS:SourceArn": "arn:aws:execute-api:us-east-2:948097384784:<rest-api-id>/*/GET/bar"
 }
}

I noticed that methods with Lambda Integrations defined in the same stack as the one where the RestApi is created do not have these Policy Statements


This is 🐛 Bug Report

@crawfobw crawfobw added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Aug 11, 2021
@peterwoodworth
Copy link
Contributor

When you import existing resources, you are returning an interface rather than the actual construct. Note how the IRestApi doesn't have an add_resource function. The RestApi construct doesn't either, you're using the APIGateway::RestApi right? I'm pretty confused, does your code successfully deploy how it is?

Regardless of that, according to our docs you are not able to modify imported resources:

Although you can use an imported resource anywhere, you cannot modify the imported resource. For example, calling addToResourcePolicy (Python: add_to_resource_policy) on an imported s3.Bucket does nothing.

@peterwoodworth peterwoodworth self-assigned this Aug 11, 2021
@peterwoodworth peterwoodworth added response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. and removed needs-triage This issue or PR still needs to be triaged. labels Aug 11, 2021
@crawfobw
Copy link
Author

Something unexpected is happening then because this does deploy correctly and resources are added to the API Gateway. The request is not actually routed to those created resources but to the /{+proxy} endpoint

In fact it seems this feature was implemented specifically for IRestApi #8270

Perhaps this is unexpected behavior due to the exceptional nature of the feature?

@peterwoodworth
Copy link
Contributor

peterwoodworth commented Aug 12, 2021

Ah yes this is a special case, I remember this now. You have to use the fromRestApiAttributes function (which you do) and then use the methods on the root property of the imported api.

api.root.addResource('foo') or api.root.addMethod(...)

You don't specify the root property in the code you posted, which is what lead to my confusion. I wonder if this is the source of your issue - but then I would still be confused why your code is deploying. Let me know, and I'll see if I can reproduce this.

@crawfobw
Copy link
Author

crawfobw commented Aug 13, 2021

Ah sorry, I tried to simplify the example in order to make it more clear but forgot to include root. Editing it to include root (which I am doing locally).

Note I just deployed the example and was able to observe the issue.

What's really interesting though is that if you make a subsequent change to the stack containing the rest API, in this case, ApiStack then the resources added in the ApiResourceStack will begin to function properly.

Example:

Deploy

class ApiStack(core.Stack):
  def __init__(self, scope: core.Construct, construct_id: str, foo_function: aws_lambda.Function **kwargs):
    super().__init__(scope, construct_id, **kwargs)
    self.api = aws_apigateway.RestApi(self, 'some-api', rest_api_name='some-api')
    foo_resource = self.api.root.add_resource('foo').add_method('GET', integration = aws_apigateway.LambdaIntegration(foo_function))

class ApiResourceStack(core.Stack):
  def __init__(self, scope: core.Construct, construct_id: str, rest_api_id: str, root_resource_id: str, bar_function: aws_lambda.Function, **kwargs):
    super().__init__(scope, construct_id, **kwargs)
    api = aws_apigateway.RestApi.from_rest_api_attributes(self, 'some-api-import', rest_api_id=rest_api_id, root_resource_id=root_resource_id)
    bar_resource = api.root.add_resource('bar')
    bar_resource.add_method('GET', integration = aws_apigateway.LambdaIntegration(bar_function))

observe invocation of /bar results in
{"message":"Missing Authentication Token"}

Update the code to the below and deploy ApiResourceStack again

class ApiStack(core.Stack):
  def __init__(self, scope: core.Construct, construct_id: str, foo_function: aws_lambda.Function **kwargs):
    super().__init__(scope, construct_id, **kwargs)
    self.api = aws_apigateway.RestApi(self, 'some-api', rest_api_name='some-api')
    foo_resource = self.api.root.add_resource('foo').add_method('GET', integration = aws_apigateway.LambdaIntegration(foo_function))
    # add new resource in base ApiStack to force update
    baz_resource = self.api.root.add_resource('baz').add_method('GET', integration = aws_apigateway.LambdaIntegration(foo_function))

class ApiResourceStack(core.Stack):
  def __init__(self, scope: core.Construct, construct_id: str, rest_api_id: str, root_resource_id: str, bar_function: aws_lambda.Function, **kwargs):
    super().__init__(scope, construct_id, **kwargs)
    api = aws_apigateway.RestApi.from_rest_api_attributes(self, 'some-api-import', rest_api_id=rest_api_id, root_resource_id=root_resource_id)
    bar_resource = api.root.add_resource('bar')
    bar_resource.add_method('GET', integration = aws_apigateway.LambdaIntegration(bar_function))

Observe that now invocations of /bar work properly

@peterwoodworth peterwoodworth removed the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Aug 13, 2021
@peterwoodworth
Copy link
Contributor

Thank you very much for clarifying and for the reproduction steps. I've been able to reproduce this issue exactly how you've described it.

/bar results in {"message":"Missing Authentication Token"} until the initial stack is updated AND the stack with the import is also updated. This is really interesting, if I first deploy the initial stack with the update the invocations of /bar fail. I then have to deploy the stack with the import, even though all that does is result in (no changes)

@nija-at I'm curious about what's happening here, can you take a look at this when you get the chance?

@peterwoodworth peterwoodworth removed their assignment Aug 13, 2021
@crawfobw
Copy link
Author

@nija-at Wanted to follow up and see when you have time to look at this? Currently this issue stands in the way of deploying new API resources independently. Thanks!

@peterwoodworth peterwoodworth added p1 and removed p2 labels Aug 19, 2021
@nija-at nija-at removed the p1 label Aug 23, 2021
@Arvanaghi
Copy link

Hi folks. This is a major blocker for us. Can someone take a look?

@nija-at nija-at added @aws-cdk/aws-apigateway Related to Amazon API Gateway needs-triage This issue or PR still needs to be triaged. labels Sep 3, 2021
@nija-at
Copy link
Contributor

nija-at commented Sep 3, 2021

Looks like this is because of the limitation of fromRestApiAttributes() that we are aware of, and are tracking here - #12417 (upvote for prioritization)

The RestApi construct when initialized via its constructor automatically creates a Deployment resource. The CDK keeps track of all Resource and Method changes and changes the LogicalId of the Deployment resource accordingly so that it is "replaced" whenever changes occur. Replacement causes the Deployment resource to track the latest changes.

The fromRestApiAttributes() was introduced as a mechanism to break up Methods and Resources of a single Rest API into multiple stacks (to overcome limits on how many resources can be part of a CloudFormation stack).
For this reason, it is currently not possible to keep track of Resources and Methods defined on the instance of RestApi created via this method.
This means that the auto-created Deployment resources cannot keep track of changes occurring in the "imported" RestApi.

There are two options here -

(1) Don't use fromRestApiAttributes() and simply pass in the instance of RestApi from one stack to the other. This is the best option if you're not worried about the CloudFormation limit. Refer to 'Resource' row here

(2) Disable the automatic deployment creation in RestApi (by unsetting the deploy prop) and create your own "Deployment" stack. This stack will look something like -

const deployStack = new Stack(app, 'DeployStack');

const deployimported = RestApi.fromRestApiAttributes(barFunction, 'ImportedApi', { ... });
const deployment = new Deployment(deployStack, 'Deployment', { api: deployimported });

const logicalId = // salted id
const cfnDeployment = deployment.node.defaultChild as CfnDeployment;
cfnDeployment.overrideLogicalId(logicalId);

The value of the logicalId here should be such that it changes every time a Resource or Method is modified, and this stack must be deployed last.

Can you check if something like this works?

@nija-at nija-at added the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Sep 3, 2021
@github-actions
Copy link

This issue has not received a response in a while. If you want to keep this issue open, please leave a comment below and auto-close will be canceled.

@github-actions github-actions bot added the closing-soon This issue will automatically close in 4 days unless further comments are made. label Sep 21, 2021
@peterwoodworth peterwoodworth removed the needs-triage This issue or PR still needs to be triaged. label Sep 21, 2021
@github-actions github-actions bot added closed-for-staleness This issue was automatically closed because it hadn't received any attention in a while. and removed closing-soon This issue will automatically close in 4 days unless further comments are made. labels Sep 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-apigateway Related to Amazon API Gateway bug This issue is a bug. closed-for-staleness This issue was automatically closed because it hadn't received any attention in a while. response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days.
Projects
None yet
Development

No branches or pull requests

4 participants