├── requirements.txt
├── README.md
├── Dockerfile
├── reset_app.sh
├── test_hello_world.py
├── hello_world.py
├── deploy_app.sh
├── .circleci
└── config.yml
└── .gitignore
/requirements.txt:
--------------------------------------------------------------------------------
1 | flask
2 | pyinstaller
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # python-circleci-docker
2 |
3 | Demonstrates how to build, test & deploy a flask python app to Docker Hub then run a Docker container on a server via SSH
4 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:2.7.14
2 |
3 | RUN mkdir /opt/hello_word/
4 | WORKDIR /opt/hello_word/
5 |
6 | COPY requirements.txt .
7 | COPY dist/hello_world /opt/hello_word/
8 |
9 | EXPOSE 80
10 |
11 | CMD [ "./hello_world" ]
12 |
--------------------------------------------------------------------------------
/reset_app.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ############################
4 | # Reset the hellow_world app on a server
5 | #
6 | # Use: ssh -o StrictHostKeyChecking=no root@hello.dpunks.org "./reset_app.sh" replace with our server hostname
7 | #
8 | #############################
9 |
10 | echo "Removing Hello World to Docker Container"
11 | docker stop hello_world
12 | echo "Stoping Hello World in Docker Container"
13 | docker ps -a
--------------------------------------------------------------------------------
/test_hello_world.py:
--------------------------------------------------------------------------------
1 | import hello_world
2 | import unittest
3 |
4 | class TestHelloWorld(unittest.TestCase):
5 |
6 | def setUp(self):
7 | self.app = hello_world.app.test_client()
8 | self.app.testing = True
9 |
10 | def test_status_code(self):
11 | response = self.app.get('/')
12 | self.assertEqual(response.status_code, 200)
13 |
14 | def test_message(self):
15 | response = self.app.get('/')
16 | message = hello_world.wrap_html('Hello PyLadies Chicago!')
17 | self.assertEqual(response.data, message)
18 |
19 | if __name__ == '__main__':
20 | unittest.main()
--------------------------------------------------------------------------------
/hello_world.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 |
3 | app = Flask(__name__)
4 |
5 | def wrap_html(message):
6 | html = """
7 |
8 |
9 |
10 |
11 |
12 |
13 | {0}
14 |
15 |
16 |
17 | """.format(message)
18 | return html
19 |
20 | @app.route('/')
21 | def hello_world():
22 | message = 'Hello PyLadies Chicago!'
23 | html = wrap_html(message)
24 | return html
25 |
26 | if __name__ == '__main__':
27 | app.run(host='0.0.0.0', port=5000)
--------------------------------------------------------------------------------
/deploy_app.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | ########################################
3 | # Put this on a Server
4 | # run chmod +x deploy_app.sh to make the script executable
5 | #
6 | # Execute this script: ./deploy_app.sh ariv3ra/python-circleci-docker:$TAG
7 | # Replace the $TAG with the actual Build Tag you want to deploy
8 | #
9 | ########################################
10 |
11 | set -e
12 |
13 | DOCKER_IMAGE=$1
14 | CONAINER_NAME="hello_world"
15 |
16 | # Check for arguments
17 | if [[ $# -lt 1 ]] ; then
18 | echo '[ERROR] You must supply a Docker Image to pull'
19 | exit 1
20 | fi
21 |
22 | echo "Deploying Hello World to Docker Container"
23 |
24 | #Check for running container & stop it before starting a new one
25 | if [ $(docker inspect -f '{{.State.Running}}' $CONAINER_NAME) = "true" ]; then
26 | docker stop hello_world
27 | fi
28 |
29 | echo "Starting Hello World using Docker Image name: $DOCKER_IMAGE"
30 |
31 | docker run -d --rm=true -p 80:5000 --name hello_world $DOCKER_IMAGE
32 |
33 | docker ps -a
34 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | docker:
5 | - image: circleci/python:2.7.14
6 | environment:
7 | FLASK_CONFIG: testing
8 | steps:
9 | - checkout
10 | - add_ssh_keys:
11 | fingerprints:
12 | - 62:b3:b1:c1:7d:56:af:06:db:9b:26:70:98:e2:da:23
13 | - run:
14 | name: Setup VirtualEnv
15 | command: |
16 | echo 'export TAG=0.1.${CIRCLE_BUILD_NUM}' >> $BASH_ENV
17 | echo 'export IMAGE_NAME=python-circleci-docker' >> $BASH_ENV
18 | virtualenv helloworld
19 | . helloworld/bin/activate
20 | pip install --no-cache-dir -r requirements.txt
21 | - run:
22 | name: Run Tests
23 | command: |
24 | . helloworld/bin/activate
25 | python test_hello_world.py
26 | - setup_remote_docker:
27 | docker_layer_caching: true
28 | - run:
29 | name: Build and push Docker image
30 | command: |
31 | . helloworld/bin/activate
32 | pyinstaller -F hello_world.py
33 | docker build -t $DOCKER_LOGIN/$IMAGE_NAME:$TAG .
34 | echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
35 | docker push $DOCKER_LOGIN/$IMAGE_NAME:$TAG
36 | - run:
37 | name: Deploy app to Linode Producrion Server via Docker
38 | command: |
39 | ssh -o StrictHostKeyChecking=no root@fosscon.punkdata.org "/bin/bash ./deploy_app.sh $DOCKER_LOGIN/$IMAGE_NAME:$TAG"
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
106 | .vscode/
107 | .DS_Store
--------------------------------------------------------------------------------