MP 6: Building a Microservice from Existing Code

Due Date: Completed and turned in via git before October 18, 2022 at 11:59pm
Points: MP 6 is worth 40 points
Semester-Long Details: Programming Environment and MP Policy

Overview

Welcome to beginning of Act II – we will begin to use the Python programming language to work towards creating cloud-based services for the remaining of the semester.

Many cloud services are wrappers around other tools with easy-to-use API interfaces for users and developers to easily interact with the tools. For this MP, you will create a web service that “wraps” your mp2 solution to create a web-based microservice that will extracts hidden GIFs from within PNGs.

For extra credit, you can place it all in a docker container!

Initial Files

In your CS 340 directory, merge the initial starting files with the following commands:

git fetch release
git merge release/mp6 --allow-unrelated-histories -m "Merging initial files"

MP Overview Session

An MP Overview will be held by Ameya on Thursday, October 13 at 5:00pm. The video is linked [here].(https://mediaspace.illinois.edu/media/t/1_2zou828q)The slides can be found here.

Implementation

We have split the implementation into two parts:

  1. Implementing a flask-based web service.
  2. Packaging the above flask-application in a docker container (extra credit).

Modifiable Files

In your solution, you can modify any file in your /mp2 and /mp6 directory except the following files:

  • /mp6/test_flask.py
  • /mp6/test_docker.py

Part 1: Flask Application

Machine Problem

Complete the app.py program using the Python flask library to create a web-based service that has two different routes.

One route receives a PNG file (as png in the POST data) via a HTTP POST request to /extract:

  • If the PNG file has a hidden GIF image (as defined by mp2), the hidden GIF file is returned.
  • If the PNG file does not have a hidden GIF image (or is an invalid PNG file), an HTTP 500 response is returned with some useful status text.

One route via a HTTP GET request to /extract/<image_num> will return the nth extracted gif:

  • If the nth gif exists, the hidden GIF file is returned.
  • If the flask program has not received n gifs, a HTTP 500 response is returned with some useful status text.

Installing Python

This MP requires a modern version of Python (ex: 3.8+).

Once Python is installed, you will need to install the Flask library. Using native Python, this can be done via:

# Windows:
py -m pip install flask

# macOS:
python3 -m pip install flask

# If nether of the above works, you can also try:
pip install flask
pip3 install flask
python -m pip install flask

Using flask

For this MP, the complete flask code is provided for you in the provided app.py:

from flask import Flask, render_template, send_file, request
import os

app = Flask(__name__)

# Route for "/" for a web-based interface to this micro-service:
@app.route('/')
def index():
  return render_template("index.html")


# Extract a hidden "uiuc" GIF from a PNG image:
@app.route('/extract', methods=["POST"])
def extract_hidden_gif():
  # ...your code here...

# Get the nth saved "uiuc" GIF:
@app.route('/extract/<int:image_num>', methods=['GET'])
def extract_image(image_num):
  # ...your code here...

To run this program, run:

  • py -m flask run (or python3 -m flask run on macOS) to launch your app.py flask server.
  • Follow the instructions on the console to view your microservice in your web browser.

Now you need to complete the extract_hidden_gif to make your microservice work! :)

Using Your MP2

As part of the extract_hidden_gif, you will need to use your mp2 png-extractGIF program. You may want to verify that your png-extractGIF program is complete and functional and that you can run the following commands:

  • We have provided a Makefile so you can use make in mp6 to compile png-extractGIF. This uses your code from your mp2 directory.
  • Testing using a PNG with a hidden GIF: ./png-extractGIF sample/waf.png taylor.gif. (This should successfully save taylor.gif.)
  • Testing using a PNG without a uiuc chunk: ./png-extractGIF sample/no-uiuc-chunk.png nothing.gif. (This should run, not crash, and probably return a non-zero value from main.)

You may need to modify your MP2 program slightly to fix any bugs to ensure your program can work with your new microservice.

Useful Python Functions

There will be a few tasks you will need to complete in Python:

  • You will need to save the contents of the POST request (the .png file). For this purpose, your application should create a temp directory if it doesn’t exist, so you can save the contents in this directory with a unique filename.
    • You can use the python os.makedirs function.
    • The contents of the data sent via POST is stored by flask in the variable request.files['png'].
    • You can find many examples of reading and writing files in Python via a search.
  • You will need to programmatically run your ./png-extractGIF program. To do this, you will likely use the python os.system function. This function will return the exit value from png-extractGIF. If you followed our code style, the exit value should be 0 on success and non-zero on failure.

  • Once you have the GIF file saved, you will likely want to use flask’s send_file function to send the GIF file.

  • If anything goes wrong, you can return "Error Message", 500 to return an error (but change “Error Message” to something useful).

Testing Your Microservice

You can test your program in two different ways:

  1. By using your web browser and visiting your flask server (ex: http://127.0.0.1:5000/).

  2. By the command line, using curl to make a request to your web server: curl -f -o output.gif http://localhost:5000/extract -F "png=@sample/waf.png"

    • You can replace @sample/waf.png with another file. Ensure that you keep the @ symbol to tell curl to send the contents of the file.
    • You should inspect output.gif to ensure the extraction was successful.
    • Make sure that you get an error when sending it an invalid PNG file.
    • Make sure you also get an error when sending it a PNG file without a hidden uiuc chunk.

Extra Credit: Packaging as a Docker Container

For +5 extra credit points, you will wrap your above flask application in a docker container.

Machine Problem

You need to complete the Dockerfile that will run your flask application in a container. Specifically, it must:

  • Launch the png_microservice
  • Be accessible on the host computer at http://127.0.0.1:5000/ just like you weren’t running in docker.

Installing Docker

You will need to have docker to complete this extra credit. The easiest way to install docker is to download the free Docker Desktop.

Building and Running the Docker

You need to make sure you specify the correct DOCKER_BUILD_COMMAND and DOCKER_RUN_COMMAND in the .env file. We will use the command specified in the .env file to launch your docker container.

Running Tests

Run pytest test_docker.py to run tests corresponding to the docker container.

Note: Running these tests locally will require you to install other python dependencies included in the requirements.txt. You can install these python libraries using pip install -r requirements.txt.

Note: The test script doesn’t automatically kill the docker container it launches. Once you run the tests for the docker container, run docker container ls to identify the container name and then run docker kill <container-name> to kill the corresponding container, otherwise you might get an error saying the port is already occupied.

Submit

When you have completed your program, double-check that your server runs as expected with a GIF image that has hidden data and a GIF image without hidden data. When you are ready, submit the code via the following git commands:

Note: Please run make clean before submitting your code.

git add -u
git commit -m "MP6 submission"
git push origin main

You can verify your code was successfully submitted by viewing your git repo on https://github.com.