Polish Pipeline

Get Endpoints

Stepping back, we can see a problem now that our app is being deployed by our pipeline. There is no easy way to find the endpoints of our application (the TableViewer and LambdaRestApi endpoints), so we can’t call it! Let’s add a little bit of code to expose these more obviously.

First edit cdk-workshop/cdk-workshop_stack.py to get these values and expose them as properties of our stack:

from aws_cdk import (
    core,
    aws_lambda as _lambda,
    aws_apigateway as apigw,
)
from cdk_dynamo_table_viewer import TableViewer
from hitcounter import HitCounter

class CdkWorkshopStack(core.Stack):

    @property
    def hc_endpoint(self):
        return self._hc_endpoint

    @property
    def hc_viewer_url(self):
        return self._hc_viewer_url

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # Defines an AWS Lambda resource
        my_lambda = _lambda.Function(
            self, 'HelloHandler',
            runtime=_lambda.Runtime.PYTHON_3_7,
            code=_lambda.Code.asset('lambda'),
            handler='hello.handler',
        )

        hello_with_counter = HitCounter(
            self, 'HelloHitCounter',
            downstream=my_lambda
        )

        gateway = apigw.LambdaRestApi(
            self, 'Endpoint',
            handler=hello_with_counter.handler
        )

        tv = TableViewer(
            self, 'ViewHitCounter',
            title='Hello Hits',
            table=hello_with_counter.table
        )

        self._hc_endpoint = core.CfnOutput(
            self, 'GatewayUrl',
            value=gateway.url
        )

        self._hc_viewer_url = core.CfnOutput(
            self, 'TableViewerUrl',
            value=tv.endpoint
        )

By adding outputs hc_viewer_url and hc_endpoint, we expose the necessary endpoints to our HitCounter application. We are using the core construct CfnOutput to declare these as Cloudformation stack outputs (we will get to this in a minute).

Let’s commit these changes to our repo (git commit -am "MESSAGE" && git push), and navigate to the Cloudformation console. You can see there are three stacks.

  • CDKToolkit: The first is the integrated CDK stack (you should always see this on bootstrapped accounts). You can ignore this.
  • WorkshopPipelineStack: This is the stack that declares our pipeline. It isn’t the one we need right now.
  • Deploy-WebService: Here is our application! Select this, and under details, select the Outputs tab. Here you should see four endpoints (two pairs of duplicate values). Two of them, EndpointXXXXXX and ViewerHitCounterViewerEndpointXXXXXXX, are defaults generated by Cloudformation, and the other two are the outputs we declared ourselves.

If you click the TableViewerUrl value, you should see our pretty hitcounter table that we created in the initial workshop.

Add Validation Test

Now we have our application deployed, but no CD pipeline is complete without tests!

Let’s start with a simple test to ping our endpoints to see if they are alive. Return to cdk-workshop/pipeline_stack.py and add the following:

from aws_cdk import (
    core,
    aws_codecommit as codecommit,
    aws_codepipeline as codepipeline,
    aws_codepipeline_actions as codepipeline_actions,
    pipelines as pipelines
)
from pipeline_stage import WorkshopPipelineStage

class WorkshopPipelineStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # PIPELINE CODE HERE...

        deploy = WorkshopPipelineStage(self, 'Deploy')
        deploy_stage = pipeline.add_application_stage(deploy)
        deploy_stage.add_actions(pipelines.ShellScriptAction(
            action_name='TestViewerEndpoint',
            use_outputs={
                'ENDPOINT_URL': # TBD
            },
            commands=['curl -Ssf $ENDPOINT_URL']
        ))
        deploy_stage.add_actions(pipelines.ShellScriptAction(
            action_name='TestAPIGatewayEndpoint',
            use_outputs={
                'ENDPOINT_URL': # TBD
            },
            commands=[
                'curl -Ssf $ENDPOINT_URL',
                'curl -Ssf $ENDPOINT_URL/hello',
                'curl -Ssf $ENDPOINT_URL/test'
            ]
        ))

First we add a ShellScriptAction from CDK Pipelines. This is a construct that simply executes one or more shell script commands. Then we add two actions to our deployment stage that test our TableViewer endpoint and our APIGateway endpoint respectively.

Note: We submit several curl requests to the APIGateway endpoint so that when we look at our tableviewer, there are several values already populated.

You may notice that we have not yet set the URLs of these endpoints. This is because they are not yet exposed to this stack!

With a slight modification to cdk-workshop/pipeline_stage.py we can expose them:

from aws_cdk import (
    core
)
from pypipworkshop_stack import PypipworkshopStack

class WorkshopPipelineStage(core.Stage):

    @property
    def hc_endpoint(self):
        return self._hc_endpoint

    @property
    def hc_viewer_url(self):
        return self._hc_viewer_url

    def __init__(self, scope: core.Construct, id: str, **kwargs):
        super().__init__(scope, id, **kwargs)

        service = PypipworkshopStack(self, 'WebService')

        self._hc_enpdoint = service.hc_endpoint
        self._hc_viewer_url = service.hc_viewer_url

Now we can add those values to our actions in cdk-workshop/pipeline_stack.py by getting the stack_output of our pipeline stack:

  # CODE HERE...

  deploy = WorkshopPipelineStage(self, 'Deploy')
  deploy_stage = pipeline.add_application_stage(deploy)
  deploy_stage.add_actions(pipelines.ShellScriptAction(
      action_name='TestViewerEndpoint',
      use_outputs={
          'ENDPOINT_URL': pipeline.stack_output(deploy.hc_viewer_url)
      },
      commands=['curl -Ssf $ENDPOINT_URL']
  ))
  deploy_stage.add_actions(pipelines.ShellScriptAction(
      action_name='TestAPIGatewayEndpoint',
      use_outputs={
          'ENDPOINT_URL': pipeline.stack_output(deploy.hc_endpoint)
      },
      commands=[
          'curl -Ssf $ENDPOINT_URL',
          'curl -Ssf $ENDPOINT_URL/hello',
          'curl -Ssf $ENDPOINT_URL/test'
      ]
  ))

Commit and View!

Commit those changes, wait for the pipeline to re-deploy the app, and navigate back to the CodePipeline Console and you can now see that there are two test actions contained within the Deploy stage!

Congratulations! You have successfully created a CD pipeline for your application complete with tests and all! Feel free to explore the console to see the details of the stack created, or check out the API Reference section on CDK Pipelines and build one for your application.