Add another flask exercise
This commit is contained in:
parent
24edb687e4
commit
85d8a566d1
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
:bar_chart: There are currently **834** questions
|
:bar_chart: There are currently **834** questions
|
||||||
|
|
||||||
:warning: These are not interview questions and most of them shouldn't be used as interview questions. Please read [Q&A](common-qa.md) for more details
|
:warning: You can use these for preparing for an interview but most of the questions and exercises don't represent an actual interview. Please read [Q&A](common-qa.md) for more details
|
||||||
|
|
||||||
:thought_balloon: If you wonder "How to prepare for a DevOps interview?", you might want to read some of my suggestions [here](prepare_for_interview.md)
|
:thought_balloon: If you wonder "How to prepare for a DevOps interview?", you might want to read some of my suggestions [here](prepare_for_interview.md)
|
||||||
|
|
||||||
@ -6125,11 +6125,12 @@ Below you can find several exercises
|
|||||||
* [Jenkins: writing pipelines](exercises/jenkins_pipelines.md)
|
* [Jenkins: writing pipelines](exercises/jenkins_pipelines.md)
|
||||||
* [CI for open source project](exercises/ci_for_open_source_project.md)
|
* [CI for open source project](exercises/ci_for_open_source_project.md)
|
||||||
* [Flask, Containers and CI](exercises/flask_container_ci/README.md)
|
* [Flask, Containers and CI](exercises/flask_container_ci/README.md)
|
||||||
|
* [Flask, Containers and CI 2](exercises/flask_container_ci2/README.md)
|
||||||
|
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
Thanks to all of our amazing [contributors](https://github.com/bregman-arie/devops-exercises/graphs/contributors) who make it easy for everyone to learn and prepare to their interviews.
|
Thanks to all of our amazing [contributors](https://github.com/bregman-arie/devops-exercises/graphs/contributors) who make it easy for everyone to learn new things :)
|
||||||
|
|
||||||
Logos credits can be found [here](credits.md)
|
Logos credits can be found [here](credits.md)
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
Your mission, should you choose to accept it, involves fixing the app in this directory, containerize it and set up a CI for it.
|
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.
|
Please read carefully all the instructions.
|
||||||
|
|
||||||
|
If any of the following steps is not working, it is expected from you to fix them
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Create a virtual environment with `python3 -m venv challenge_venv`
|
1. Create a virtual environment with `python3 -m venv challenge_venv`
|
||||||
@ -9,8 +11,6 @@ Please read carefully all the instructions.
|
|||||||
|
|
||||||
## Run the app
|
## Run the app
|
||||||
|
|
||||||
If any of the following steps is not working, it is expected from you to fix them
|
|
||||||
|
|
||||||
1. Move to `challenges/flask_container_ci` directory, if you are not already there
|
1. Move to `challenges/flask_container_ci` directory, if you are not already there
|
||||||
1. Run `export FLASK_APP=app/main.py`
|
1. Run `export FLASK_APP=app/main.py`
|
||||||
1. To run the app execute `flask run`. If it doesn't works, fix it
|
1. To run the app execute `flask run`. If it doesn't works, fix it
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
#!/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.routee("/", 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
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run(port=5000)
|
|
79
exercises/flask_container_ci2/README.md
Normal file
79
exercises/flask_container_ci2/README.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
Your mission, should you choose to accept it, involves developing an app, 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 works, fix it
|
||||||
|
3. Access `http://127.0.0.1:5000`. You should see the following
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"current_uri": "/",
|
||||||
|
"example": "/matrix/'123n456n789'",
|
||||||
|
"resources": {
|
||||||
|
"column": "/columns/<matrix>/<column_number>",
|
||||||
|
"matrix": "/matrix/<matrix>",
|
||||||
|
"row": "/rows/<matrix>/<row_number>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. You should be able to access any of the resources and get the following data:
|
||||||
|
|
||||||
|
* /matrix/\<matrix\>
|
||||||
|
|
||||||
|
for example, for /matrix/123n456n789 the user will get:
|
||||||
|
|
||||||
|
1 2 3
|
||||||
|
4 5 6
|
||||||
|
7 8 9
|
||||||
|
|
||||||
|
* /matrix/\<matrix\>/\<column_number\>
|
||||||
|
|
||||||
|
for example, for /matrix/123n456n789/2 the user will get:
|
||||||
|
|
||||||
|
2
|
||||||
|
5
|
||||||
|
8
|
||||||
|
|
||||||
|
* /matrix/\<matrix\>/\<row_number\>
|
||||||
|
|
||||||
|
for example, for /matrix/123n456n789/1 the user will get:
|
||||||
|
|
||||||
|
1 2 3
|
||||||
|
|
||||||
|
## 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. Write a CI pipeline that will 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 any other 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
exercises/flask_container_ci2/app/__init__.py
Normal file
2
exercises/flask_container_ci2/app/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# coding=utf-8
|
11
exercises/flask_container_ci2/app/config.py
Normal file
11
exercises/flask_container_ci2/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')
|
50
exercises/flask_container_ci2/app/main.py
Normal file
50
exercises/flask_container_ci2/app/main.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
from flask import Flask
|
||||||
|
from flask import make_response
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.routee("/", methods=['GET'])
|
||||||
|
def index():
|
||||||
|
return pretty_json({
|
||||||
|
"resources": {
|
||||||
|
"matrix": "/matrix/<matrix>",
|
||||||
|
"column": "/columns/<matrix>/<column_number>",
|
||||||
|
"row": "/rows/<matrix>/<row_number>",
|
||||||
|
},
|
||||||
|
"current_uri": "/",
|
||||||
|
"example": "/matrix/'123n456n789'",
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/matrix/<matrix>", methods=['GET'])
|
||||||
|
def matrix(matrix):
|
||||||
|
# TODO: return matrix, each row in a new line
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/matrix/<matrix>/<column_number>", methods=['GET'])
|
||||||
|
def column(matrix, column_number):
|
||||||
|
# TODO: return column based on given column number
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/matrix/<matrix>/<row_number>", methods=['GET'])
|
||||||
|
def row(matrix, row_number):
|
||||||
|
# TODO: return row based on given row number
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def pretty_json(arg):
|
||||||
|
response = make_response(json.dumps(arg, sort_keys=True, indent=4))
|
||||||
|
response.headers['Content-type'] = "application/json"
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(port=5000)
|
28
exercises/flask_container_ci2/app/tests.py
Normal file
28
exercises/flask_container_ci2/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
exercises/flask_container_ci2/requirements.txt
Normal file
1
exercises/flask_container_ci2/requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
flask
|
25
exercises/flask_container_ci2/tests.py
Normal file
25
exercises/flask_container_ci2/tests.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#!/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_matrix(self):
|
||||||
|
response = self.app.get('/matrix/123n459,789', follow_redirects=True)
|
||||||
|
# Change when the matrix route is fixed and returning the actual matrix
|
||||||
|
self.assertEqual(response.status_code, 500)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue
Block a user