Skip to content

Latest commit

 

History

History
898 lines (700 loc) · 29.7 KB

File metadata and controls

898 lines (700 loc) · 29.7 KB

LAB 03 - Serverless with AWS Chalice

This exercise is divided into two parts. The first part is about the AWS serverless framework Chalice, and the second part is using the Chalice to run the CloudAlbum application.

Serverless framework

There are various serverless frameworks. Here is a brief introduction to each of the serverless frameworks.

AWS Chalice

Chalice is a microframework(https://github.com/aws/chalice) for writing serverless apps in python. It makes it simple for you to use AWS Lambda and Amazon API Gateway to build serverless apps. It allows you to quickly create and deploy applications that use AWS Lambda. It provides:

  • A command line tool for creating, deploying, and managing your app
  • A decorator based API for integrating with Amazon API Gateway, Amazon S3, Amazon SNS, Amazon SQS, and other AWS services.
  • Automatic IAM policy generation

Serverless

The Serverless Framework (https://serverless.com) is an MIT open source framework that’s actively developed and maintained by a full-time team. At its essence, it allows users to define a serverless application—including Lambda functions and API Gateway APIs—and then deploy it using a command-line interface (CLI). It helps you organize and structure serverless applications, which is of great benefit as you begin to build larger systems, and it’s fully extensible via its plugin system.

Zappa

Zappa (https://github.com/Miserlou/Zappa) makes it super easy to build and deploy server-less, event-driven Python applications (including, but not limited to, WSGI web apps) on AWS Lambda + API Gateway. Think of it as "serverless" web hosting for your Python apps. That means infinite scaling, zero downtime, zero maintenance - and at a fraction of the cost of your current deployments!

NOTE: These serverless frameworks have many similarities. You can choose one framework from the above. In this hands-on lab, you will use AWS Chalice.

TASK 1 : Setup virtualenv

virtualenv is a tool to create isolated Python environments. It is easy to use. We will create virtualenv for AWS Chalice microframework environment.

  1. Open new terminal in the Cloud9 IDE. (for MAC, Option+T is short cut)
  • Perform following command:
cd ~/environment/aws-chalice-migration-workshop/LAB03/
unalias python
python --version
  • output:
Python 2.7.14
which python
  • output:
/usr/bin/python
which python36
  • output:
/usr/bin/python36
virtualenv --version
  • output:
16.0.0
virtualenv -p /usr/bin/python36 venv
  • output:
Running virtualenv with interpreter /usr/bin/python36
Using base prefix '/usr'
New python executable in /home/ec2-user/environment/aws-chalice-migration-workshop/LAB03/venv/bin/python36
Also creating executable in /home/ec2-user/environment/aws-chalice-migration-workshop/LAB03/venv/bin/python
Installing setuptools, pip, wheel...done.
  1. Now you can activate your virtualenv (venv).
source ~/environment/aws-chalice-migration-workshop/LAB03/venv/bin/activate
echo "unalias python" >> ~/.bash_profile
echo "source ~/environment/aws-chalice-migration-workshop/LAB03/venv/bin/activate" >> ~/.bash_profile

which python
  • output :
~/environment/aws-chalice-migration-workshop/LAB03/venv/bin/python
which pip
  • output:
~/environment/aws-chalice-migration-workshop/LAB03/venv/bin/pip
  1. Install AWS Chalice microframework (Of course, you must run following command after venv activation)
pip install chalice
chalice --version
  • output:
chalice 1.6.1, python 3.6.5, linux 4.14.47-56.37.amzn1.x86_64
  1. Install required packages for this lab.
  • Now, we are using venv. So we can run pip not pip-3.6.
pip install -r ~/environment/aws-chalice-migration-workshop/LAB03/02-CloudAlbum-Chalice/cloudalbum/requirements.txt

TASK 2 : Build a simple AWS Chalice serverless app.

This TASK will provide an introduction on how to use AWS Chalice and provide instructions on how to go about building your very first Chalice application.

  1. We installed AWS Chalice serverless framework previous step, it is time to create your first Chalice application. Run the chalice new-project command to create a project called myapp:
mkdir -p ~/environment/aws-chalice-migration-workshop/LAB03/01-Chalice/
cd ~/environment/aws-chalice-migration-workshop/LAB03/01-Chalice/
chalice new-project myapp
  1. Review the generated files which generated by Chalice framework.
sudo yum install tree
tree -a .
  • output:
<username>:~/environment/aws-chalice-migration-workshop/LAB03/01-Chalice (master) $ tree -a .
.
└── myapp
    ├── app.py
    ├── .chalice
    │   └── config.json
    ├── .gitignore
    └── requirements.txt

  • You can find above files after run chalice new-project myapp command.
cd myapp
vi app.py (or use open int the Cloud9 default editor)
from chalice import Chalice

app = Chalice(app_name='myapp')


@app.route('/')
def index():
    return {'hello': 'world'}
  • It looks very similar to the Flask framework.
  1. Run Chalice application as a local application in your machine.
chalice local --port 8080

You can see the following message: Serving on 127.0.0.1:8080 if you want detailed execution information, you can also run Chalice as debug mode chalice --debug local --port 8080

For the convenience of test, we will use httpie in the shell.

  • You can open NEW TERMINAL with bash shell and then type following command.

pip install httpie
  • After httpie is installed as http command, you can run following command for the application test.
http localhost:8080/
  • output:
HTTP/1.1 200 OK
Content-Length: 18
Content-Type: application/json
Date: Sun, 05 Aug 2018 07:02:01 GMT
Server: BaseHTTP/0.6 Python/3.6.5

{
    "hello": "world"
}

  1. To help your understanding, consider the following example.
from chalice import Chalice
from chalice import Response 
import logging

app = Chalice(app_name='myapp')
app.debug = True
app.log.setLevel(logging.DEBUG)


@app.route('/')
def index():
    return {'hello': 'world'}


@app.route('/users/{name}', methods=['GET'])
def user_info(name):
    request=app.current_request
    app.log.debug(request.method)
    app.log.debug(request.to_dict())
    body='<h1> Your name is {0}.</h1>'.format(name)

    return Response(body=body,
        status_code=200,
        headers={'Content-Type': 'text/html; charset=utf-8'})


@app.route('/users', methods=['POST'])
def user_add():
    request = app.current_request
    app.log.debug(request.method)
    app.log.debug(request.to_dict())

    return Response(body=request.json_body,
        status_code=200,
        headers={'Content-Type': 'application/json; charset=utf-8'})
  • Review above code for new app.py. Response and logging are importted from top of the app.py file. Debug option is enabled for the application logging. user_info and user_add functions are added.

  • Replace app.py file with the contents of above source code.

    • app.py file is located in ~/environment/aws-chalice-migration-workshop/LAB03/01-Chalice/myapp/app.py
  1. Stop the previous chalice local --port 8080 command with CTRL+C and run the new version of myapp.
chalice local --port 8080
  1. Test new Chalice application. For the convenience, you can open additional terminal in the Cloud9 environment.

  • Test #1: @app.route('/users/{name}', methods=['GET'])
http localhost:8080/users/David
  • output:
HTTP/1.1 200 OK
Content-Length: 29
Content-Type: text/html; charset=utf-8
Date: Sun, 05 Aug 2018 08:19:38 GMT
Server: BaseHTTP/0.6 Python/3.6.5

<h1> Your name is David.</h1>

  • DEBUG LOG for this request (You can find it on your terminal which running application by Chalice.):
(.....)
myapp - DEBUG - GET
myapp - DEBUG - {'query_params': None, 'headers': {'host': 'localhost:8080', 'user-agent': 'HTTPie/0.9.9', 'accept-encoding': 'gzip, deflate', 'accept': '*/*', 'connection': 'keep-alive'}, 'uri_params': {'name': 'David'}, 'method': 'GET', 'context': {'httpMethod': 'GET', 'resourcePath': '/users/{name}', 'identity': {'sourceIp': '127.0.0.1'}, 'path': '/users/David'}, 'stage_vars': {}}
127.0.0.1 - - [05/Aug/2018 08:19:38] "GET /users/David HTTP/1.1" 200 -
  • TEST #2: @app.route('/users', methods=['POST'])
echo '{"name": "David", "age": 22, "job": "student"}' | http localhost:8080/users
  • output:
HTTP/1.1 200 OK
Content-Length: 46
Content-Type: application/json; charset=utf-8
Date: Sun, 05 Aug 2018 08:26:13 GMT
Server: BaseHTTP/0.6 Python/3.6.5

{
    "age": 22,
    "job": "student",
    "name": "David"
}

  • DEBUG LOG for this request:
myapp - DEBUG - POST
myapp - DEBUG - {'query_params': None, 'headers': {'host': 'localhost:8080', 'user-agent': 'HTTPie/0.9.9', 'accept-encoding': 'gzip, deflate', 'accept': 'application/json, */*', 'connection': 'keep-alive', 'content-type': 'application/json', 'content-length': '48'}, 'uri_params': {}, 'method': 'POST', 'context': {'httpMethod': 'POST', 'resourcePath': '/users', 'identity': {'sourceIp': '127.0.0.1'}, 'path': '/users'}, 'stage_vars': {}}
127.0.0.1 - - [05/Aug/2018 08:26:13] "POST /users HTTP/1.1" 200 -
  1. Deploy to API Gateway and Lambda. You can deploy this application using Chalice CLI command. (Make sure, you working directory is ~/environment/aws-chalice-migration-workshop/LAB03/01-Chalice/myapp)
chalice deploy
  • output
Creating deployment package.
Updating policy for IAM role: myapp-dev
Creating lambda function: myapp-dev
Creating Rest API
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-southeast-1:123456789012:function:myapp-dev
  - Rest API URL: https://aavvueq9we.execute-api.ap-southeast-1.amazonaws.com/api/
  • You can get api URL easily using below command.
chalice url
  • output
https://aavvueq9we.execute-api.ap-southeast-1.amazonaws.com/api/
  • NOTE: If you have following errors, you can check the TASK 0. Permission grant for Cloud9 of LAB 02 - Move to serverless.
Creating deployment package.
Updating policy for IAM role: myapp-dev
Traceback (most recent call last):

  (......)

botocore.exceptions.ClientError: An error occurred (InvalidClientTokenId) when calling the PutRolePolicy operation: The security token included in the request is invalid

During handling of the above exception, another exception occurred:

Traceback (most recent call last):

  (......)

chalice.deploy.deployer.ChaliceDeploymentError: ERROR - While deploying your chalice application, received the following error:

 An error occurred (InvalidClientTokenId) when calling the PutRolePolicy 
 operation: The security token included in the request is invalid
  1. Test your first Chalice application.
  • Test #1: @app.route('/users/{name}', methods=['GET'])
http https://aavvueq9we.execute-api.ap-southeast-1.amazonaws.com/api/users/David
  • output:
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 39
Content-Type: application/json
Date: Fri, 10 Aug 2018 10:42:40 GMT
Via: 1.1 3dc529f17187385cec2a18e8aabac48c.cloudfront.net (CloudFront)
X-Amz-Cf-Id: ZRsVySNWExMqL-BjuF3RL7_oNoGqi44PIw8DmwCUSz_fpSVkciHnTQ==
X-Amzn-Trace-Id: Root=1-5b6d6c20-f399202e60639474e397634d;Sampled=0
X-Cache: Miss from cloudfront
x-amz-apigw-id: LZ3VIFHlyQ0Fm4w=
x-amzn-RequestId: 1b0ee142-9c8a-11e8-9f60-0d307c709c87

<h1> Your name is David.</h1>

  • TEST #2: @app.route('/users', methods=['POST'])
echo '{"name": "David", "age": 22, "job": "student"}' | http https://aavvueq9we.execute-api.ap-southeast-1.amazonaws.com/api/users
  • output:
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 39
Content-Type: application/json
Date: Fri, 10 Aug 2018 10:44:12 GMT
Via: 1.1 25db82f0268a052eaf4b613d96b80533.cloudfront.net (CloudFront)
X-Amz-Cf-Id: IBoM5ONXfIFnGcU35Lu_pByFx-l7won3eBVaPOHKTeMI64ECkZNNmg==
X-Amzn-Trace-Id: Root=1-5b6d6c7c-0a46d560ff5e119c201147a8;Sampled=0
X-Cache: Miss from cloudfront
x-amz-apigw-id: LZ3jbGLmyQ0FVyQ=
x-amzn-RequestId: 519a0bcd-9c8a-11e8-8997-5550927d6916

{
    "age": 22,
    "job": "student",
    "name": "David"
}
  • You can check the files of deployed and deployments directories. (~/environment/aws-chalice-migration-workshop/LAB03/01-Chalice)
tree -a .
  • output:
.
└── myapp
    ├── app.py
    ├── .chalice
    │   ├── config.json
    │   ├── deployed
    │   │   └── dev.json
    │   └── deployments
    │       └── d41d8cd98f00b204e9800998ecf8427e-python3.6.zip
    ├── .gitignore
    ├── __pycache__
    │   └── app.cpython-36.pyc
    └── requirements.txt
5 directories, 7 files
  1. Examine your API Gateway and Lambda Console. You can see the new API and Lambda functions.
  • API Gateway console (myapp)

  • Lambda console (myapp-dev)

  1. Delete deployed application
cd ~/environment/aws-chalice-migration-workshop/LAB03/01-Chalice/myapp
chalice delete
  • output:
Deleting Rest API: w2t3ueq9we
Deleting function: arn:aws:lambda:ap-southeast-1:123456789012:function:myapp-dev
Deleting IAM role: myapp-dev
@app.on_s3_event('mybucket', events=['s3:ObjectCreated:Put'],
                prefix='images/', suffix='.jpg')
def resize_image(event):
with tempfile.NamedTemporaryFile('w') as f:
    s3.download_file(event.bucket, event.key, f.name)
    resize_image(f.name)
    s3.upload_file(event.bucket, 'resized/%s' % event.key, f.name)

TASK 3 : CloudAlbum with AWS Chalice

We have removed server based components via LAB02. We are now going serverless by removing Web Server Tier and App Server Tier.

Finally, all servers are gone!

  1. Let's take a look around ~/environment/aws-chalice-migration-workshop/LAB03/02-CloudAlbum-Chalice/cloudalbum/ directory.
cd ~/environment/aws-chalice-migration-workshop/LAB03/02-CloudAlbum-Chalice/cloudalbum/
tree -L 2 -a .
  • output
├── app.py
├── .chalice
│   └── config.json
├── chalicelib
│   ├── config.py
│   ├── __init__.py
│   ├── models_ddb.py
│   ├── templates
│   └── util.py
├── .gitignore
├── requirements.txt
└── vendor
    ├── bin
    ├── jinja2
    ├── Jinja2-2.10.dist-info
    ├── markupsafe
    ├── pyasn1
    ├── pyasn1-0.4.3.dist-info
    ├── python_jose-3.0.0.dist-info
    ├── rsa
    └── rsa-3.4.2.dist-info

  • All of route functions are in the app.py and template and modules are in the chalicelib directory.

3rd Party Packages: There are two options for handling python package dependencies:

  • requirements.txt - During the packaging process, Chalice will install any packages it finds or can build compatible wheels for. Specifically all pure python packages as well as all packages that upload wheel files for the manylinux1_x86_64 platform will be automatically installable.

  • vendor/ - The contents of this directory are automatically added to the top level of the deployment package. Chalice will also check for an optional vendor/ directory in the project root directory. The contents of this directory are automatically included in the top level of the deployment.

  • Chalice will also check for an optional vendor/ directory in the project root directory. The contents of this directory are automatically included in the top level of the deployment package (see Examples for specific examples). The vendor/ directory is helpful in these scenarios:

  • You need to include custom packages or binary content that is not accessible via pip. These may be internal packages that aren’t public.

  • Wheel files are not available for a package you need from pip.

  • A package is installable with requirements.txt but has optional c extensions. Chalice can build the dependency without the c extensions, but if you want better performance you can vendor a version that is compiled.

  • As a general rule of thumb, code that you write goes in either app.py or chalicelib/, and dependencies are either specified in requirements.txt or placed in the vendor/ directory.

  1. Examine .py files. Flask dependecies are removed. Only Jinja2 package is alived in the vendor directory.

  2. Set up application parameters. We will user Parameter Store (https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html) for the application configuration.

aws ssm put-parameter --name "/cloudalbum/GMAPS_KEY" --value "<REAL_GMAPS_KEY_PROVIDED_BY_INSTRUCTOR>" --type "SecureString"
aws ssm put-parameter --name "/cloudalbum/S3_PHOTO_BUCKET" --value "cloudalbum-<INITIAL>" --type "SecureString"
aws ssm put-parameter --name "/cloudalbum/COGNITO_POOL_ID" --value "<COGNITO_POOL_ID>" --type "SecureString"
aws ssm put-parameter --name "/cloudalbum/COGNITO_CLIENT_ID" --value "<COGNITO_CLIENT_ID>" --type "SecureString"
aws ssm put-parameter --name "/cloudalbum/COGNITO_CLIENT_SECRET" --value "<COGNITO_CLIENT_SECRET>" --type "SecureString"
aws ssm put-parameter --name "/cloudalbum/COGNITO_DOMAIN" --value "<COGNITO_DOMAIN>" --type "SecureString"

aws ssm put-parameter --name "/cloudalbum/THUMBNAIL_WIDTH" --value "300" --type "SecureString"
aws ssm put-parameter --name "/cloudalbum/THUMBNAIL_HEIGHT" --value "200" --type "SecureString"
aws ssm put-parameter --name "/cloudalbum/AWS_REGION" --value "ap-southeast-1" --type "SecureString"
aws ssm put-parameter --name "/cloudalbum/DDB_RCU" --value "10" --type "SecureString"
aws ssm put-parameter --name "/cloudalbum/DDB_WCU" --value "10" --type "SecureString"
aws ssm put-parameter --name "/cloudalbum/BASE_URL" --value "DUMMY" --type "SecureString"
  • Above DUMMY value of /cloudalbum/BASE_URL will be replaced real thing.(We can get API Gateway url after deploy)

  • NOTE: If you put the wrong value, you can overwrite that value with --overwrite parameter. If --overwrite parameter ommited, you can see the following error message. (An error occurred (ParameterAlreadyExists) when calling the PutParameter operation: ...)

  1. Verify your parameters.
aws ssm describe-parameters
  • output
{
    "Parameters": [
        {
            "Name": "/cloudalbum/AWS_REGION",
            "Type": "SecureString",
            "KeyId": "alias/aws/ssm",
            "LastModifiedDate": 1533589218.29,
            "LastModifiedUser": "arn:aws:iam::123456789012:user/poweruser",
            "Version": 1
        },
        {
            "Name": "/cloudalbum/BASE_URL",
            "Type": "SecureString",
            "KeyId": "alias/aws/ssm",
            "LastModifiedDate": 1533589322.314,
            "LastModifiedUser": "arn:aws:iam::123456789012:user/poweruser",
            "Version": 1
        },
        {
            "Name": "/cloudalbum/COGNITO_CLIENT_ID",
            "Type": "SecureString",
            "KeyId": "alias/aws/ssm",
            "LastModifiedDate": 1533589258.2,
            "LastModifiedUser": "arn:aws:iam::123456789012:user/poweruser",
            "Version": 1
        },
        {

            (...........)

        }
}
aws ssm get-parameters --names "/cloudalbum/DDB_RCU" --with-decryption
  • output:
{
    "Parameters": [
        {
            "Name": "/cloudalbum/DDB_RCU",
            "Type": "SecureString",
            "Value": "10",
            "Version": 1,
            "LastModifiedDate": 1542092269.552,
            "ARN": "arn:aws:ssm:ap-southeast-1:389833669077:parameter/cloudalbum/DDB_RCU"
        }
    ],
    "InvalidParameters": []
}
  • You can also check the System Manager - Parameter Store console.

  1. Review config.py file located in 'LAB03/02-CloudAlbum-Chalice/cloudalbum/chalicelib/config.py'.
from chalice import CORSConfig
import boto3


def get_param(param_name):
    """
    This function reads a secure parameter from AWS' SSM service.
    The request must be passed a valid parameter name, as well as
    temporary credentials which can be used to access the parameter.
    The parameter's value is returned.
    """
    # Create the SSM Client
    ssm = boto3.client('ssm')

    # Get the requested parameter
    response = ssm.get_parameters(
        Names=[param_name, ], WithDecryption=True
    )

    # Store the credentials in a variable
    result = response['Parameters'][0]['Value']

    return result


conf = {
    # Mandatory variable
    'GMAPS_KEY': get_param('/cloudalbum/GMAPS_KEY'),

    # Default config values
    'THUMBNAIL_WIDTH': get_param('/cloudalbum/THUMBNAIL_WIDTH'),
    'THUMBNAIL_HEIGHT': get_param('/cloudalbum/THUMBNAIL_HEIGHT'),

    # DynamoDB
    'AWS_REGION': get_param('/cloudalbum/AWS_REGION'),
    'DDB_RCU': get_param('/cloudalbum/DDB_RCU'),
    'DDB_WCU': get_param('/cloudalbum/DDB_WCU'),

    # S3
    'S3_PHOTO_BUCKET': get_param('/cloudalbum/S3_PHOTO_BUCKET'),

    # COGNITO
    'COGNITO_POOL_ID': get_param('/cloudalbum/COGNITO_POOL_ID'),
    'COGNITO_CLIENT_ID': get_param('/cloudalbum/COGNITO_CLIENT_ID'),
    'COGNITO_CLIENT_SECRET': get_param('/cloudalbum/COGNITO_CLIENT_SECRET'),
    'COGNITO_DOMAIN': get_param('/cloudalbum/COGNITO_DOMAIN'),
    'BASE_URL': "https://{0}".format(get_param('/cloudalbum/BASE_URL'))
}


S3_STATIC_URL = "https://s3-{0}.amazonaws.com/{1}/static".format(conf['AWS_REGION'], conf['S3_PHOTO_BUCKET'])

cors_config = CORSConfig(
    allow_origin='*',
    allow_headers=['X-Special-Header'],
    max_age=600,
    expose_headers=['X-Special-Header'],
    allow_credentials=True
)
  1. Review the get_param function in the config.py file. Through this function, we can get value easily in the Parameter Store.
def get_param(param_name):
    """
    This function reads a secure parameter from AWS' SSM service.
    The request must be passed a valid parameter name, as well as
    temporary credentials which can be used to access the parameter.
    The parameter's value is returned.
    """
    # Create the SSM Client
    ssm = boto3.client('ssm')

    # Get the requested parameter
    response = ssm.get_parameters(
        Names=[param_name, ], WithDecryption=True
    )

    # Store the credentials in a variable
    result = response['Parameters'][0]['Value']

    return result
  1. Copy static files to your S3 Bucket for static file hosting such as CSS and JavaScript.
  • Following cloudalbum-<INITIAL> value must replace YOUR OWN VALUE.
aws s3 sync ~/environment/aws-chalice-migration-workshop/resources/static s3://cloudalbum-<INITIAL>/static/ --acl public-read
  1. Enable S3 bucket CORS configuration in your S3 Console. (https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html)

  • REVIEW the CORS configuration
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
</CORSConfiguration>
  1. Review template files. Template files which stored aws-chalice-migration-workshop/LAB03/02-CloudAlbum-Chalice/cloudalbum/chalicelib/templates are already changed to load static resources in your S3 bucket. You can refer above variable in the config.py file.
S3_STATIC_URL = "https://s3-{0}.amazonaws.com/{1}/static".format(conf['AWS_REGION'], conf['S3_PHOTO_BUCKET'])
  1. Review the app.py in the 'LAB03/02-CloudAlbum-Chalice/cloudalbum/app.py'
  • Flask dependencies are removed

    • Flask, url_for, flash, flask_login and so on.
  • Chalice has similar features like flask route decorator structure.

  • CloudAlbum is not restful, it is still tightly coupled with Jinja2 template engine. So, we use Jinja2 template engine in this time.

  • Chalice permmited to load python modules from the chalicelib directory. We will use this directory which contains templates directory. You can refer to following code in LAB03/02-CloudAlbum-Chalice/cloudalbum/app.py.

env = Environment(
    loader=PackageLoader(__name__, 'chalicelib/templates'),
    autoescape=select_autoescape(['html', 'xml']))
  • We can use Jinja2 template engine like below:
t = env.get_template('upload.html')
body = t.render(current_user=user, gmaps_key=conf['GMAPS_KEY'], s3_static_url=S3_STATIC_URL)

NOTE: static contents will be moved S3, so we need to copy static files in your S3 bucket. Jinja2 Template engine will load template in the chalicelib/templates directory.

  1. Review provided Lambda execution policy before run.
  • NOTE: As you know, this policy is just example for the convinience not for practical environment.

  • This policy is provided for the workshop as policy-dev.json in the LAB03/02-CloudAlbum-Chalice/cloudalbum/.chalice/policy-dev.json.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:*",
                "logs:*",
                "ssm:*",
                "dynamodb:*",
                "cognito-idp:*",
                "xray:*"
            ],
            "Resource": "*"
        }
    ]
}
  • Whenever your application is deployed using chalice, the auto generated policy is written to disk at /.chalice/policy.json. When you run the chalice deploy command, you can also specify the --no-autogen-policy option. Doing so will result in the chalice CLI loading the /.chalice/policy.json file and using that file as the policy for the IAM role. You can manually edit this file and specify --no-autogen-policy if you'd like to have full control over what IAM policy to associate with the IAM role.
  1. Now, you can deploy CloudAlbum application with chalice.
chalice deploy --no-autogen-policy
  • output
Creating deployment package.
Updating policy for IAM role: cloudalbum-dev-api_handler
Updating lambda function: cloudalbum-dev
Updating rest API
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-southeast-1:389833669077:function:cloudalbum-dev
  - Rest API URL: https://v1mehmiqsj.execute-api.ap-southeast-1.amazonaws.com/api/
  • Keep the Rest API URL value and update Parameter Store using this value. You should remove https:// and / (last character) like following strins.

    • Rest API URL : v1mehmiqsj.execute-api.ap-southeast-1.amazonaws.com/api
aws ssm put-parameter --name "/cloudalbum/BASE_URL" --value "<YOUR REST API URL>" --type "SecureString" --overwrite
  1. Configure App client cloudalbum in the Cognito console.

  • Update Callback URL(s).
  • Update Sign out URL(s).
  1. Look into your Lambda Console.

  1. Look into your API Gateway Console.

  1. Connect to your application through API Gateway. open https://v1mehmiqsj.execute-api.ap-southeast-1.amazonaws.com/api in your browser.
  • If you missed API Gateway URL, you can use chalice url command.

  1. Perform application test.

Go further

  • Legacy backend is tightly coupled with Jinja2 Template Engine!

  • It means almost request returned rendered HTML not JSON data.
  • If you want to build more flexible backend, you can re-design your client code rendering it self using data response from backend API.
  • AWS Chalice is suitable for RESTful API server.
  • Continuous Deployment (CD)

Congratulation! You completed LAB03.

LAB GUIDE LINKS

Feedback