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

apigateway: Unable to import existing API Gateway with Stage (Stage.from_stage_attributes returns __StageBaseProxy) #30385

Open
fabian4cast opened this issue May 30, 2024 · 5 comments
Labels
@aws-cdk/aws-apigateway Related to Amazon API Gateway bug This issue is a bug. effort/small Small work item – less than a day of effort p2

Comments

@fabian4cast
Copy link

fabian4cast commented May 30, 2024

Describe the bug

Hello folks,

We previously deployed a Stack with an API Gateway via the Serverless framework, and decided to switch to CDK and hence rewrote the Stack configuration in CDK.

We managed to let CDK recognize most of the existing resources by overriding the logical IDs, e.g.

rest_api = aws_apigateway.RestApi(...)
rest_api.node.default_child.override_logical_id("<Logical ID of existing RestApi>")

This way CDK recognizes (or imports) the resources in the already existing Stack and updates them (if required) instead of deleting and re-creating. This works well for the RestApi. However, we also have a Stage that already exists for the RestApi, and doing the same with the existing Stage leads to the error Stage already exists. Here it seems that CDK is not able to recognize the existing Stage and just import it, but attempts to create a new one with the same name instead.

Hence, we attempted to just reference the existing Stage using aws_apigateway.Stage.from_stage_attributes as

rest_api = aws_apigateway.RestApi(
    scope=scope,
    id="RestApi",
    rest_api_name="name",
    deploy=False,
)
# Explicitly set logical ID to import existing RestApi previously deployed by Serverless
rest_api.node.default_child.override_logical_id("RestApi")

deployment = aws_apigateway.Deployment(
    scope=scope,
    id="Deployment",
    api=rest_api,
    stage_name="dev",
)

stage = aws_apigateway.Stage.from_stage_attributes(
    scope=scope,
    id="Stage",
    rest_api=rest_api,
    stage_name="dev",
)

# Since `aws_apigateway.RestApi` has `deploy=False`, we need to explicitly set this attribute.
rest_api.deployment_stage = stage

and this then fails because

TypeError: type of argument value must be aws_cdk.aws_apigateway.Stage; got aws_cdk.aws_apigateway._StageBaseProxy instead

According to the docs aws_apigateway.Stage.from_stage_attributes should return IStage, but apparently it seems to return _StageBaseProxy instead.

Thanks for the help in advance!

Moving from Serverless to CDK has generally been a great experience, at least from the point where we discovered the override_logical_id "hack", since that allowed us to import all previously existing resources that were part of the stacks, and didn't require cdk import.

Expected Behavior

aws_cdk.aws_apigateway.Stage.from_stage_attributes returns aws_cdk.aws_apigateway.IStage

Current Behavior

aws_cdk.aws_apigateway.Stage.from_stage_attributes returns aws_cdk.aws_apigateway._StageBaseProxy

Reproduction Steps

import aws_cdk
import aws_cdk.aws_apigateway as aws_apigateway
import constructs


class Stack(aws_cdk.Stack):
    def __init__(
        self,
        scope: constructs.Construct,
        **kwargs,
    ) -> None:
        super().__init__(
            scope=scope,
            id="Stack",
            **kwargs,
        )

        rest_api = aws_apigateway.RestApi(
            scope=self,
            id="RestApi",
            rest_api_name="name",
            deploy=False,
        )
        
        stage = aws_apigateway.Stage.from_stage_attributes(
            scope=self,
            id="Stage",
            rest_api=rest_api,
            stage_name="dev",
        )
        
        # Since `aws_apigateway.RestApi` has `deploy=False`, we need to explicitly set this attribute.
        rest_api.deployment_stage = stage


app = aws_cdk.App()

Stack(scope=app)

then cdk synth

Possible Solution

No response

Additional Information/Context

It is also worth mentioning that the Stage (and its corresponding Deployment) do currently not appear as a Resource of the Stack as of the state Serverless created! It just exists in the AWS API Gateway Console (or area, idk how to describe it). It seems that it was previously implicitly created by setting Stage attributes in Serverless, e.g.

RestApi:
  Type: AWS::ApiGateway::RestApi
  Properties:
    Name: "name"

Domain:
  Type: 'AWS::ApiGateway::DomainName'
  Properties:
    CertificateArn: "<arn>"
    DomainName: "<domain>"
    EndpointConfiguration:
      Types:
        - EDGE

ApiBasePathMapping:
  Type: 'AWS::ApiGateway::BasePathMapping'
  Properties:
    DomainName: !Ref Domain
    RestApiId: !Ref RestApi
    Stage: "dev"

CDK CLI Version

2.138.0 (build 6b41c8b)

Framework Version

No response

Node.js Version

v21.7.3

OS

Ubuntu 22.04 LTS

Language

Python

Language Version

3.12.0

Other information

Traceback (most recent call last):
  File "/home/user/engine/app.py", line 16, in <module>
    stack.Stack(
  File "/home/user/.cache/pypoetry/virtualenvs/engine-2aWaQfPc-py3.12/lib/python3.12/site-packages/jsii/_runtime.py", line 118, in __call__
    inst = super(JSIIMeta, cast(JSIIMeta, cls)).__call__(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/engine/stack.py", line 37, in __init__
    engine_api_gateway, engine_api_gateway_v1 = apigateway.create_resources(
                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/engine/apigateway.py", line 58, in create_resources
    rest_api.deployment_stage = stage
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/.cache/pypoetry/virtualenvs/engine-2aWaQfPc-py3.12/lib/python3.12/site-packages/aws_cdk/aws_apigateway/__init__.py", line 23101, in deployment_stage
    check_type(argname="argument value", value=value, expected_type=type_hints["value"])
  File "/home/user/.cache/pypoetry/virtualenvs/engine-2aWaQfPc-py3.12/lib/python3.12/site-packages/typeguard/__init__.py", line 785, in check_type
    raise TypeError(
TypeError: type of argument value must be aws_cdk.aws_apigateway.Stage; got aws_cdk.aws_apigateway._StageBaseProxy instead
@fabian4cast fabian4cast added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels May 30, 2024
@github-actions github-actions bot added the @aws-cdk/aws-apigateway Related to Amazon API Gateway label May 30, 2024
@fabian4cast fabian4cast changed the title aws_cdk.aws_apigateway: Unable to import existing API Gateway with Stage (moving from Serverless to CDK) aws_cdk.aws_apigateway: Unable to import existing API Gateway with Stage (Stage.from_stage_attributes returns __StageBaseProxy) May 30, 2024
@ashishdhingra ashishdhingra self-assigned this May 30, 2024
@ashishdhingra ashishdhingra added needs-reproduction This issue needs reproduction. and removed needs-triage This issue or PR still needs to be triaged. labels May 30, 2024
@ashishdhingra
Copy link
Contributor

ashishdhingra commented May 31, 2024

@fabian4cast Good afternoon. I was able to reproduce the issue by:

  • Manually creating RestApi along with deployment stage (named BetaStage).
  • Using the below bare minimal code and running cdk synth:
    from aws_cdk import (
      Stack,
      aws_apigateway as apigateway
    )
    from constructs import Construct
    
    class PythonStack(Stack):
    
        def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
            super().__init__(scope, construct_id, **kwargs)
    
            restapi = apigateway.RestApi.from_rest_api_id(self, id="MyRestApi", rest_api_id="<<id-from-aws-console>>")
            stage = apigateway.Stage.from_stage_attributes(self, "MyStage", rest_api=restapi, stage_name="BetaStage")
            restapi.deployment_stage = stage

The reason you are getting the error is that apigateway.Stage.from_stage_attributes() returns IStage interface variable, whereas restapi.deployment_stage expects a concrete type Stage per below decompiled definition form VS Code:

@jsii.interface(jsii_type="aws-cdk-lib.aws_apigateway.IRestApi")
class IRestApi(_IResource_c80c4260, typing_extensions.Protocol):
    @builtins.property
    @jsii.member(jsii_name="restApiId")
    def rest_api_id(self) -> builtins.str:
        '''The ID of this API Gateway RestApi.

        :attribute: true
        '''
        ...
    @builtins.property
    @jsii.member(jsii_name="deploymentStage")
    def deployment_stage(self) -> "Stage":
        '''API Gateway stage that points to the latest deployment (if defined).'''
        ...

    @deployment_stage.setter
    def deployment_stage(self, value: "Stage") -> None:
        ...

Using the similar code in TypeScript reports same issue where IRestApi.deploymentStage is of type Stage.

I'm unsure about the workaround right now. Would review this with team.

Thanks,
Ashish

@ashishdhingra ashishdhingra added p1 effort/small Small work item – less than a day of effort and removed needs-reproduction This issue needs reproduction. labels May 31, 2024
@pahud pahud changed the title aws_cdk.aws_apigateway: Unable to import existing API Gateway with Stage (Stage.from_stage_attributes returns __StageBaseProxy) apigateway: Unable to import existing API Gateway with Stage (Stage.from_stage_attributes returns __StageBaseProxy) May 31, 2024
@pahud
Copy link
Contributor

pahud commented May 31, 2024

Hi

The override_logical_id approach is an interesting hack that makes your CDK code to synthesize into exactly the same logical ID of the existing resource from an existing CFN stack deployed by serverless framework but this could lead to some other issues off the top of my head:

  1. You will have the RestApi managed by CDK as L2 construct
  2. other resources you import with fromXxx methods would not be the L2s nor L1s, instead they would be interfaces and you won't be allowed to use some methods only available for L2 constructs.
  3. Your existing CFN stack would eventually have some resources like Stages or Methods not managed by CDK.

We do not have any public reference on migrating from serverless framework to CDK as there might be some different migration strategies, each has its pros and cons to consider.

I would suggest to reach out to AWS specialists to work together with your team to help you define the migration strategy. Feel free to ping me on cdk.dev if you need any further assistance.

@pahud pahud added p2 and removed p1 labels May 31, 2024
@pahud pahud added the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Jun 3, 2024
Copy link

github-actions bot commented Jun 3, 2024

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 Jun 3, 2024
@fabian4cast
Copy link
Author

Hey, thanks for the replies!

@ashishdhingra as of my experience it often is not so much of an issue whether a method returns e.g. Stage or IStage. Usually with from_xxx_attributes doesn't cause any issues (e.g. for iam.User, which we also use in the app).

@pahud

  1. It's fine if the RestApi is an L2 construct. That is what we want.
  2. It would be okay for some resources such as Stage here to just be in interface because
  3. The Stage currently is already not part of the Stack managed by Serverless. It might be that Serverless can manage the Stage anyways since it somehow knows about it, but apparently it doesn't appear as a resource of the Stack in the CloudFormation console.

For this is, however, a blocker for us, but we are considering to find any other workaround that might help us for now.

@github-actions github-actions bot removed closing-soon This issue will automatically close in 4 days unless further comments are made. response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. labels Jun 6, 2024
@ashishdhingra ashishdhingra removed their assignment Jun 6, 2024
@rmjwilbur
Copy link

@fabian4cast - not sure if/how you resolved this, but I did the following:

  • used the AWS CLI to get/list and note the settings for the base path mapping, stage, and custom domain name
  • used the AWS CLI to delete the base path mapping, stage, and custom domain name
  • used the RestApi's deployOptions to set the stage properties
  • used the RestApi's addDomainName
  • deployed via CDK, which created new base path mapping, stage, and custom domain name
  • used the AWS CLI to get and note the new settings for the custom domain name
  • updated the Route 53 alias records for my custom domain name to point to the new Cloudfront distribution associated with the custom domain name
  • waited several minutes for the updated alias records to take effect
  • tested and verified the custom domain name and api was responding properly

Hope that helps someone

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. effort/small Small work item – less than a day of effort p2
Projects
None yet
Development

No branches or pull requests

4 participants