Archaic Positives

Adventurer, Traveler, Rubyist

Integrating a AWS Proxy Into a Flask App

I ran into an issue recently when I was working on Percolate’s Hello application, which serves as Percolate’s intranet. We have API documentation that is generated by our application codebase, and pushed up to AWS S3 for storage and hosting purposes. It wasn’t immediately clear to me how to proxy the documentation effectively. Do I download the documentation files from AWS, and render each file as a template? Turns out it’s a lot simpler than that.

What is Hello?

Hello is a Flask application that we use as an intranet at Percolate. Flask is a great Python framework that allows us to build small, flexible applications. In our case, we’ve used it to host documentation, serve an API that interfaces with Namely, and also index information specific to Percolate.

The Initial Problem

So the first issue that I ran into was trying to figure out how to properly proxy documentation hosted on AWS S3 through the Hello application. Due to some initial confusion that I had, I didn’t know if AWS S3 would support relative links if they’re proxied on Hello. For example, if an HTML file is proxied, would the relative links on the AWS bucket (for example, CSS/JS files in the HTML file), would those relative links be loaded? I experimented first by downloading the files from S3 to the local filesystem for Hello.

Fail - Other Options?

It turned out to be a failure, because downloading them into the application’s local filesystem was breaking the relative links in all of the HTML templates. I would either have to host the asset files internally from within the application, or find another option forward. It turns out, there’s a method called open_read in boto’s Key library that will allow us to render the HTML template in the application, even though it’s being hosted on AWS S3. So here’s how we set up the /docs endpoint URL in Hello:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@app.route("/docs/<path:aws_key_name>", methods=['GET'])
def s3_key(aws_key_name):
    if aws_key_name.endswith('/'):
        aws_pathname = aws_pathname + 'index.html'

    s3_doc = HelloS3.find_doc_in_bucket(aws_pathname)
    if s3_doc:
        try:
            s3_doc.open_read()
        except S3ResponseError as e:
            return Response(e.body, status=e.status,
                            headers=s3_doc.resp.get_headers())

       headers = dict(s3_doc.resp.get_headers())
       return Response(s3_doc, headers=headers)

    return abort(404)

So to recap, here’s what we’re doing with the logic above. Remember that the documentation we’re hosting on AWS has an index.html template stored in teh documentation’s directory. For example, documentation for the animals API is going to be rendered via a index.html template, so the path for that documentation would be /penguins/index.html. So we have to account for that in our logic. Whenever someone types in a request, let’s say /docs/example_key/, it sets aws_key_name = example_key/. If the key name ends with /, then it will automatically append index.html to the end. Then we have a module called HelloS3 that will search for that key in the AWS S3 bucket. If it exists, It will proxy that file through our application. If it can’t find a file, then it will return a 404 page.

Lesson Learned

Instead of trying to build out everything yourself, check to see if there’s a simpler solution right in front of you. There was one in the boto package, and I missed it. I hope this helps someone else out!