diff --git a/.env b/.env deleted file mode 100644 index d49a909..0000000 --- a/.env +++ /dev/null @@ -1,2 +0,0 @@ -PYTHONDONTWRITEBYTECODE=1 -PYTHONUNBUFFERED=1 \ No newline at end of file diff --git a/.env.dist b/.env.dist new file mode 100644 index 0000000..f8ebe7d --- /dev/null +++ b/.env.dist @@ -0,0 +1,32 @@ +# Docker Compose +PROJECT_DOMAIN_SUFFIX=localhost + +COMPOSE_PROJECT_NAME=ninetofiver +COMPOSE_FILE=compose.yaml,compose.dev.yaml,compose.traefik.mixin.yaml +#COMPOSE_FILE=compose.yaml,compose.dev.yaml,compose.fixed-ports.mixin.yaml +COMPOSE_PATH_SEPARATOR=, + +MYSQL_EXPOSED_PORT=3306 +NINETOFIVER_EXPOSED_PORT=8888 + +MYSQL_VERSION=5.7 +MYSQL_DATABASE=ninetofiver-database +MYSQL_USER=ninetofiver-user +MYSQL_PASSWORD=ninetofiver-password + +SUPERUSER_EMAIL=admin@example.com +SUPERUSER_USERNAME=admin +SUPERUSER_PASSWORD=admin + +API_APPLICATION_NAME=application +API_APPLICATION_CLIENT_ID=application-id +API_APPLICATION_CLIENT_SECRET=application-secret + +# Application +DJANGO_SETTINGS_MODULE=ninetofiver.settings +DJANGO_CONFIGURATION=Dev + +CFG_FILE_PATH=/etc/925r/config.yml + +PYTHONDONTWRITEBYTECODE=1 +PYTHONUNBUFFERED=1 diff --git a/.gitignore b/.gitignore index 957caf8..9857393 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,8 @@ docs/_build/ !/media/.gitkeep *.log .tox/ -.env .coverage /scripts/docker/mysql-init/ test_results.html + +/.env diff --git a/Dockerfile b/Dockerfile index 3f239fb..51cee89 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,17 @@ -# Pull base image -FROM python:3.8-slim-buster as builder +FROM python:3.8-slim-buster AS builder # install python project dependencies pre-requisites -RUN apt-get update && \ - apt-get install -y python-dev libldap2-dev libsasl2-dev libssl-dev && \ - apt-get install -y gcc default-libmysqlclient-dev - -# Set environment variables -COPY requirements.txt requirements.txt +RUN apt-get update \ + && apt-get install -y python-dev libldap2-dev libsasl2-dev libssl-dev \ + && apt-get install -y gcc default-libmysqlclient-dev # Install pipenv RUN set -ex && pip install --upgrade pip # Install dependencies +COPY requirements.txt requirements.txt RUN set -ex && pip install -r requirements.txt -FROM builder as final +FROM builder AS final WORKDIR /code -COPY . /app/ - -RUN set -ex && bash -c "eval $(grep 'PYTHONDONTWRITEBYTECODE' .env)" -RUN set -ex && bash -c "eval $(grep 'PYTHONUNBUFFERED' .env)" \ No newline at end of file +COPY . . diff --git a/Makefile b/Makefile deleted file mode 100644 index 896c9a1..0000000 --- a/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -.PHONY: help -help: ## Show this help - @egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' - -.PHONY: build -build: ## Build project with docker compose - docker-compose build - - -.PHONY: up -up: ## Run project with docker compose - docker-compose up --build --remove-orphans web - - -.PHONY: down -down: ## Reset project containers with docker compose - docker-compose down - -.PHONY: clean -clean: ## Clean Reset project containers with docker compose - docker-compose down -v --remove-orphans - -.PHONY: test -test: ## Run project tests and coverage with tox runner - docker-compose run --rm web tox diff --git a/README.md b/README.md index c989d0d..a56c438 100644 --- a/README.md +++ b/README.md @@ -7,112 +7,46 @@ ninetofiver [![License](https://img.shields.io/github/license/kalmanolah/925r.svg)](https://shields.io) ninetofiver (or 925r) is a free and open source time and leave tracking application. + +## Dependencies -## Installation +- [Taskfile](https://taskfile.dev/) +- [Docker Compose plugin](https://docs.docker.com/compose/) -Install build dependencies: - -```bash -apt-get install -y python-dev default-libmysqlclient-dev libldap2-dev libsasl2-dev libssl-dev -``` -or -```bash -sudo dnf install -y mysql-devel openldap-devel -``` - -You'll need [pipenv](https://docs.pipenv.org/). Installing it is super simple: - -```bash -pip install pipenv -``` - -After that, installing the application is smooth sailing: - -```bash -pipenv install -``` - -Once your pipenv is set up, you can use `pipenv shell` to get a shell, or -just prefix additional commands with `pipenv run`. +The Taskfile and Docker Compose setup hides a bit of the setup. +If you want to walk the manual path, check the files `.env.dist`, `Taskfile.dist.yml` and `Dockerfile`. ## Usage -**For usage with Docker, see latter section named _Local Development (with Docker)_.** -1. Run `python manage.py migrate` to create the models. -2. Run `python manage.py createsuperuser` to create an admin user -### Running (development) - -Running the command below starts a development server at -`http://127.0.0.1:8000`. - -```bash -python manage.py runserver -``` +1. Run `task prepare` and check if the values in the .env file are correct for your environment +2. Run `task start` to start the application at `http://localhost:8000`. +3. Run `task app:manage -- migrate` to create the models +4. Run `task app:manage -- createsuperuser` to create an admin user (interactively) +5. Run `task app:manage -- create_test_data` to fill the database with test data (see below for more information) -### Running (production) +For more tasks, check `task --list-all`. + +## First steps -Running the command below starts a server using the production configuration -at `http://127.0.0.1:8000`. +### Set up an application for YaYata -Note: The `insecure` flag is used to allow the server to serve static files. +Before you can set up YaYata, you need to register an application in ninetofiver. -```bash -python manage.py runserver --configuration=Prod --insecure -``` - -## Local Development (with Docker) - -To build, run and test and more ... use magic of make help to play with this project. -Make sure you have installed docker and docker compose. -```shell -make help -``` -and you receive below list: -```text -build Build project with docker compose -clean Clean Reset project containers with docker compose -down Reset project containers with docker compose -help Show this help -test Run project tests and coverage with tox runner -up Run project with docker compose -``` -### How to run local environment with test data. -Build and run docker containers. -```shell -make build -make up -``` -Exec initial migration. After _exec_ should be your 925r container name. -```shell -docker exec 925r-web python manage.py migrate -``` -Interactively create a new superuser. -```shell -docker exec -it 925r-web python manage.py createsuperuser -``` - -If you are running YaYata too (in debug mode), then you could need to change 925r port from -8888 to something else, because YaYata runs webpack on the port 8888. - -### Next steps -If you want to work with YaYata you need to set up an application. 1. Log in. 2. In the right top corner, navigate to **Your Account -> Your applications -> New application**. 3. Fill **Name** and **Client id**. 4. Set **Client type = Public**. 5. Set **Authorization grant type = Resource owner password-based**. -Now you can log in YaYata with root account, or you can create a new test user. -You are all set to work with Admin interface, if you want some test data filled, see next section. +Now you can log in to YaYata with the root account, or you can create a new test user. -## Example/Test data -You can use django command `create_test_data` to fill database with test data. -You can specify the ammount of data to be created by one optional argument -(`small`, `normal` or `extensive`) with `normal` being the default ammount when not specified. -It can run a few minutes depending on resources. For this reason there is a `-t` option, so you -can see what is happening at the moment. +### Add example data + +You can run `task app:manage -- create_test_data` to fill the database with test data. +You can specify the amount of data to be created by one optional argument `amount`. Possible values are +(`small`, `normal` or `extensive`) with `normal` being the default. ```shell -docker exec -t 925r-web python manage.py create_test_data extensive +task app:manage -- create_test_data extensive ``` ## Configuration @@ -135,31 +69,30 @@ could use the following configuration: SECRET_KEY: mae3fo4dooJaiteth2emeaNga1biey9ia8FaiQuooYoac8phohee7r ``` -## Testing +## Test Run the test suite: ```bash -tox +task test ``` Generate dummy data for testing (only in DEBUG mode): ```bash -python manage.py create_test_data # fills almost all tables +task task app:manage -- create_test_data # fills almost all tables ``` -Clean all database: +Clean the complete database: ```bash -python manage.py flush +task app:manage -- flush # or delete db.sqlite3 file in root directory -``` +``` -Other commands for testing: +Other possible commands can be found by running: ```bash -python manage.py help -# [ninetofiver] +task app:manage -- help ``` ## License diff --git a/Taskfile.dist.yml b/Taskfile.dist.yml new file mode 100644 index 0000000..0daa19a --- /dev/null +++ b/Taskfile.dist.yml @@ -0,0 +1,78 @@ +version: '3' + +env: + COMPOSE: docker compose + +dotenv: ['.env'] # first file takes precedence + +tasks: + default: + cmds: + - "'{{.TASK_EXE}}' --list" + + .env: + desc: Initiate the .env file + cmds: + - cp "{{.TASK}}.dist" "{{.TASK}}" + generates: + - "{{.TASK}}" + status: + - test -f "{{.TASK}}" + silent: true + + prepare: + desc: Prepare your setup + deps: [.env] + + build: + desc: Build your services + deps: [prepare] + cmds: + - | + {{.COMPOSE}} build --no-cache + + start: + desc: Start all services + deps: [prepare] + cmds: + - | + {{.COMPOSE}} up --detach --remove-orphans {{.CLI_ARGS}} + + stop: + desc: Stop all services + deps: [prepare] + cmds: + - | + {{.COMPOSE}} down {{.CLI_ARGS}} + + compose:service:*:exec:*: + desc: Open a terminal inside a service + deps: [prepare] + vars: + SERVICE: '{{index .MATCH 0}}' + COMMAND: '{{index .MATCH 1 | default "sh"}}' + cmds: + - | + {{.COMPOSE}} exec --interactive --tty "{{.SERVICE}}" {{.COMMAND}} && exit 0 + + app:manage: + desc: Run a command against manage.py + deps: [prepare] + cmds: + - | + {{.COMPOSE}} exec --interactive --tty ninetofiver python manage.py {{.CLI_ARGS}} + + test: + desc: Run the test suite + deps: [prepare] + cmds: + - | + {{.COMPOSE}} run --rm web tox + + clean: + desc: Clean up + deps: [prepare] + cmds: + - | + {{.COMPOSE}} down --remove-orphans --volumes {{.CLI_ARGS}} + - rm -f .env diff --git a/compose.dev.yaml b/compose.dev.yaml new file mode 100644 index 0000000..2903b26 --- /dev/null +++ b/compose.dev.yaml @@ -0,0 +1,69 @@ +services: + + ldap: + image: osixia/openldap:1.1.7 + networks: + ninetofiver: + + mysql: + image: mysql:${MYSQL_VERSION?} + environment: + - MYSQL_ROOT_PASSWORD=random-password + - MYSQL_DATABASE=${MYSQL_DATABASE?} + - MYSQL_USER=${MYSQL_USER?} + - MYSQL_PASSWORD=${MYSQL_PASSWORD?} + command: + [ + 'mysqld', + '--character-set-server=utf8mb4', + '--collation-server=utf8mb4_unicode_ci', + '--wait_timeout=28800', # needed to be able to add test data + '--max_allowed_packet=128M', # needed to be able to add test data + ] + networks: + ninetofiver: + ports: + - "3306" + volumes: + - "mysql-data:/var/lib/mysql" + healthcheck: + test: [ "CMD-SHELL", "MYSQL_PWD=$${MYSQL_PASSWORD} mysqladmin --host=127.0.0.1 --user=$${MYSQL_USER} ping" ] + interval: 5s + timeout: 3s + retries: 2 + start_period: 0s + + ninetofiver: + environment: + - MYSQL_HOST=mysql + - MYSQL_PORT=3306 + command: | + bash -c " + python manage.py migrate + echo \"from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser('${SUPERUSER_USERNAME?}', '${SUPERUSER_EMAIL?}', '${SUPERUSER_PASSWORD?}')\" | python manage.py shell || true + echo \"from oauth2_provider.models import Application; from django.contrib.auth import get_user_model; User = get_user_model(); client = Application.objects.create(user=User.objects.filter(is_staff=True)[0], name='${API_APPLICATION_NAME?}', client_id='${API_APPLICATION_CLIENT_ID?}', client_secret='${API_APPLICATION_CLIENT_SECRET?}', authorization_grant_type=Application.GRANT_PASSWORD)\" | python manage.py shell || true + python manage.py runserver 0.0.0.0:8000 + " + volumes: + - ninetofiver-files:/code + depends_on: + ldap: + condition: service_started + mysql: + condition: service_healthy + +volumes: + + mysql-data: + name: "mysql-${MYSQL_VERSION?}-data" + + ninetofiver-files: + driver: local + driver_opts: + o: bind + type: none + device: ${PWD?} + +networks: + + ninetofiver: diff --git a/compose.fixed-ports.mixin.yaml b/compose.fixed-ports.mixin.yaml new file mode 100644 index 0000000..ecce002 --- /dev/null +++ b/compose.fixed-ports.mixin.yaml @@ -0,0 +1,9 @@ +services: + + mysql: + ports: + - "${MYSQL_EXPOSED_PORT:?}:3306" + + ninetofiver: + ports: + - "${NINETOFIVER_EXPOSED_PORT:?}:8000" diff --git a/compose.traefik.mixin.yaml b/compose.traefik.mixin.yaml new file mode 100644 index 0000000..e677e7d --- /dev/null +++ b/compose.traefik.mixin.yaml @@ -0,0 +1,15 @@ +services: + + ninetofiver: + networks: + traefik: + labels: + - "traefik.enable=true" + - "traefik.docker.network=traefik" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME?}-ninetofiver.rule=Host(`ninetofiver.${PROJECT_DOMAIN_SUFFIX?}`)" + - "traefik.http.services.${COMPOSE_PROJECT_NAME?}-ninetofiver.loadbalancer.server.port=8000" + +networks: + + traefik: + external: true diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..fd501dc --- /dev/null +++ b/compose.yaml @@ -0,0 +1,26 @@ +services: + + ninetofiver: + build: . + environment: + - DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE?} + - DJANGO_CONFIGURATION=${DJANGO_CONFIGURATION?} + - CFG_FILE_PATH=${CFG_FILE_PATH?} + - MYSQL_DB=${MYSQL_DATABASE?} + - MYSQL_USER=${MYSQL_USER?} + - MYSQL_PASSWORD=${MYSQL_PASSWORD?} + - PYTHONDONTWRITEBYTECODE=${PYTHONDONTWRITEBYTECODE?} + - PYTHONUNBUFFERED=${PYTHONUNBUFFERED?} + command: | + bash -c " + python manage.py migrate + python manage.py runserver 0.0.0.0:8000 + " + networks: + ninetofiver: + ports: + - "8000" + +networks: + + ninetofiver: diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index dd5a037..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: '3.8' - -services: - ldap: - image: osixia/openldap:1.1.7 - web: - container_name: 925r-web - build: . - command: bash -c "python manage.py runserver 0.0.0.0:8000" - volumes: - - .:/code - ports: - - "8888:8000" - depends_on: - - ldap diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile deleted file mode 100644 index f0b0f1d..0000000 --- a/scripts/docker/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM python:3.6 -ENV PYTHONUNBUFFERED 1 -RUN mkdir /code -RUN apt-get update -RUN apt-get install -y python-dev libldap2-dev libsasl2-dev libssl-dev -WORKDIR /code -ADD requirements.txt /code/ -RUN pip install -r requirements.txt -ADD . /code/ diff --git a/scripts/docker/docker-compose.yml b/scripts/docker/docker-compose.yml deleted file mode 100644 index 501a0f4..0000000 --- a/scripts/docker/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: '2' -services: - ldap: - image: osixia/openldap:1.1.7 - web: - build: - context: ../../. - dockerfile: ./scripts/docker/Dockerfile - command: python manage.py runserver 0.0.0.0:8000 - volumes: - - ../../.:/code - ports: - - "8888:8000" - depends_on: - - ldap