You've already forked devops-exercises
Rename exercises dir
Name it instead "topics" so it won't be strange if some topics included "exercises" directory.
This commit is contained in:
60
topics/flask_container_ci/README.md
Normal file
60
topics/flask_container_ci/README.md
Normal file
@@ -0,0 +1,60 @@
|
||||
Your mission, should you choose to accept it, involves fixing the app in this directory, containerize it and set up a CI for it.
|
||||
Please read carefully all the instructions.
|
||||
|
||||
If any of the following steps is not working, it is expected from you to fix them
|
||||
|
||||
## Installation
|
||||
|
||||
1. Create a virtual environment with `python3 -m venv challenge_venv`
|
||||
2. Activate it with `source challenge_venv/bin/activate`
|
||||
3. Install the requirements in this directory `pip install -r requirements.txt`
|
||||
|
||||
## Run the app
|
||||
|
||||
1. Move to `challenges/flask_container_ci` directory, if you are not already there
|
||||
1. Run `export FLASK_APP=app/main.py`
|
||||
1. To run the app execute `flask run`. If it doesn't work, fix it
|
||||
3. Access `http://127.0.0.1:5000`. You should see the following:
|
||||
|
||||
```
|
||||
{
|
||||
"resources_uris": {
|
||||
"user": "/users/\<username\>",
|
||||
"users": "/users"
|
||||
},
|
||||
"current_uri": "/"
|
||||
}
|
||||
```
|
||||
|
||||
4. You should be able to access any of the resources and get the following data:
|
||||
|
||||
* /users - all users data
|
||||
* /users/<username> - data on the specific chosen user
|
||||
|
||||
5. When accessing /users, the data returned should not include the id of the user, only its name and description. Also, the data should be ordered by usernames.
|
||||
|
||||
## Containers
|
||||
|
||||
Using Docker or Podman, containerize the flask app so users can run the following two commands:
|
||||
|
||||
```
|
||||
docker build -t app:latest /path/to/Dockerfile
|
||||
docker run -d -p 5000:5000 app
|
||||
```
|
||||
|
||||
1. You can use any image base you would like
|
||||
2. Containerize only what you need for running the application, nothing else.
|
||||
|
||||
## CI
|
||||
|
||||
Great, now that we have a working app and also can run it in a container, let's set up a CI for it so it won't break again in the future
|
||||
In current directory you have a file called tests.py which includes the tests for the app. What is required from you, is:
|
||||
|
||||
1. The CI should run the app tests. You are free to choose whatever CI system or service you prefer. Use `python tests.py` for running the tests.
|
||||
2. There should be some kind of test for the Dockerfile you wrote
|
||||
3. Add additional unit test (or another level of tests) for testing the app
|
||||
|
||||
### Guidelines
|
||||
|
||||
* Except the app functionality, you can change whatever you want - structure, tooling, libraries, ... If possible add `notes.md` file which explains reasons, logic, thoughts and anything else you would like to share
|
||||
* The CI part should include the source code for the pipeline definition
|
||||
2
topics/flask_container_ci/app/__init__.py
Normal file
2
topics/flask_container_ci/app/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
11
topics/flask_container_ci/app/config.py
Normal file
11
topics/flask_container_ci/app/config.py
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
import os
|
||||
|
||||
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
SECRET_KEY = 'shhh'
|
||||
CSRF_ENABLED = True
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')
|
||||
58
topics/flask_container_ci/app/main.py
Normal file
58
topics/flask_container_ci/app/main.py
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
from flask import Flask
|
||||
from flask import make_response
|
||||
|
||||
import json
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
with open("./users.json", "r") as f:
|
||||
users = json.load(f)
|
||||
|
||||
|
||||
@app.route("/", methods=['GET'])
|
||||
def index():
|
||||
return pretty_json({
|
||||
"resources": {
|
||||
"users": "/users",
|
||||
"user": "/users/<username>",
|
||||
},
|
||||
"current_uri": "/"
|
||||
})
|
||||
|
||||
|
||||
@app.route("/users", methods=['GET'])
|
||||
def all_users():
|
||||
return pretty_json(users)
|
||||
|
||||
|
||||
@app.route("/users/<username>", methods=['GET'])
|
||||
def user_data(username):
|
||||
if username not in users:
|
||||
raise NotFound
|
||||
|
||||
return pretty_json(users[username])
|
||||
|
||||
|
||||
@app.route("/users/<username>/something", methods=['GET'])
|
||||
def user_something(username):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def pretty_json(arg):
|
||||
response = make_response(json.dumps(arg, sort_keys=True, indent=4))
|
||||
response.headers['Content-type'] = "application/json"
|
||||
return response
|
||||
|
||||
|
||||
def create_test_app():
|
||||
app = Flask(__name__)
|
||||
return app
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(port=5000)
|
||||
28
topics/flask_container_ci/app/tests.py
Normal file
28
topics/flask_container_ci/app/tests.py
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from config import basedir
|
||||
from app import app
|
||||
from app import db
|
||||
|
||||
|
||||
class TestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
app.config['TESTING'] = True
|
||||
app.config['WTF_CSRF_ENABLED'] = False
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(
|
||||
basedir, 'test.db')
|
||||
self.app = app.test_client()
|
||||
db.create_all()
|
||||
|
||||
def tearDown(self):
|
||||
db.session.remove()
|
||||
db.drop_all()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
1
topics/flask_container_ci/requirements.txt
Normal file
1
topics/flask_container_ci/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
flask
|
||||
24
topics/flask_container_ci/tests.py
Normal file
24
topics/flask_container_ci/tests.py
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
import unittest
|
||||
|
||||
from app import main
|
||||
|
||||
|
||||
class TestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.app = main.app.test_client()
|
||||
|
||||
def test_main_page(self):
|
||||
response = self.app.get('/', follow_redirects=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_users_page(self):
|
||||
response = self.app.get('/users', follow_redirects=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
22
topics/flask_container_ci/users.json
Normal file
22
topics/flask_container_ci/users.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"geralt" : {
|
||||
"id": "whitewolf",
|
||||
"name": "Geralt of Rivia",
|
||||
"description": "Traveling monster slayer for hire"
|
||||
},
|
||||
"lara_croft" : {
|
||||
"id": "m31a3n6sion",
|
||||
"name": "Lara Croft",
|
||||
"description": "Highly intelligent and athletic English archaeologist"
|
||||
},
|
||||
"mario" : {
|
||||
"id": "smb3igiul",
|
||||
"name": "Mario",
|
||||
"description": "Italian plumber who really likes mushrooms"
|
||||
},
|
||||
"gordon_freeman" : {
|
||||
"id": "nohalflife3",
|
||||
"name": "Gordon Freeman",
|
||||
"description": "Physicist with great shooting skills"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user