diff --git a/.github/workflows/ci_main.yml b/.github/workflows/ci_main.yml new file mode 100644 index 00000000..f03918ae --- /dev/null +++ b/.github/workflows/ci_main.yml @@ -0,0 +1,103 @@ +name: ci for time application python app + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + name: Build application and lint it with flask + runs-on: ubuntu-latest + env: + working_dir: app_python + strategy: + fail-fast: false + matrix: + python-version: ["3.8"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 + python -m pip install Flask + python -m pip install ntplib + - name: Lint with flake8 + run: | + # exit on critical errors (syntax errors, undefined names) + flake8 ${{ env.working_dir }}/ --count --select=E9,F63,F7,F82 --show-source --statistics + # treat rest of the errors as warnings (display, but do not terminate build process) + flake8 ${{ env.working_dir }}/ --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + test: + name: Run tests + runs-on: ubuntu-latest + env: + working_dir: app_python + strategy: + fail-fast: false + matrix: + python-version: ["3.8"] + needs: build + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install pytest + python -m pip install requests + python -m pip install Flask + python -m pip install ntplib + python -m pip install pytz + - name: Run tests + run: | + cd app_python + python -m unittest test_app.py + + docker: + name: Build, cache and publish docker + needs: build + environment: ci + runs-on: ubuntu-latest + env: + working_dir: app_python + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - name: Login to DockerHub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v3 + with: + context: ./${{ env.working_dir }} + push: true + tags: ${{ secrets.DOCKER_HUB_USERNAME }}/app-python:latest + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + - name: Replace old cache with new cache + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..61770753 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# ignore ALL files in ANY directory named __pycache__ +./app_python/__pycache__ +*.pyc +terraform/.vagrant/ +secret.tfvars \ No newline at end of file diff --git a/ANSIBLE.MD b/ANSIBLE.MD new file mode 100644 index 00000000..21a1da70 --- /dev/null +++ b/ANSIBLE.MD @@ -0,0 +1,3 @@ +## Playbook Run. + +![Ansible playbook](./images/lab5/Ansible-playbook.png) \ No newline at end of file diff --git a/CI.md b/CI.md new file mode 100644 index 00000000..f28c150f --- /dev/null +++ b/CI.md @@ -0,0 +1,23 @@ +# [GitHub CI actions best practices](https://exercism.org/docs/building/github/gha-best-practices). + +### Set timeouts for workflows: + +Many workflows don't need nearly as much time to finish, but sometimes unexpected errors occur or a job hangs until the workflow run is killed 6 hours after starting it. Therefore ***it's recommended to specify a shorter timeout***. This has the following advantages: + +- PRs won't be pending CI for half the day, issues can be caught early or workflow runs can be restarted. +- The number of overall parallel builds is limited, hanging jobs will not cause issues for other PRs if they are cancelled early. + +### Consider if (third-party) actions are really needed: + +Actions should be treated like dependencies in your favourite programming language, they are code written by third party authors outside of the control of Exercism. Even if you trust the authors of the action, there may be a hostile takeover of the repository which will indirectly give those people access to Exercism repos, including write access. Therefore, you should carefully consider if introducing a new action is really worth it or if it's better to move the code into a (new) action under Exercism's control. + +### Pin actions to SHAs: + +When using other actions, pin them to a commit (via their SHA), not to a branch or tag. This ensures that the same code will be executed each time, which is not guaranteed when pinning to a branch or tag. This has two benefits: + +- It makes your build stable +- It prevents an attacker from changing a branch/tag to point to malicious code The only exception to this rule could be actions that we (Exercism) have built ourselves. + +### Consider setting up a concurrency strategy + +It's often not necessary or useful to run CI on intermediate commits if a newer commit has been pushed in the meantime. You can configure a [concurrency strategy](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency) in order to automatically cancel running workflows in the same context. \ No newline at end of file diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 00000000..a7460eb3 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,19 @@ +## [Docker best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/). + +### Create ephemeral containers: + +- The image defined by your `Dockerfile` should generate containers that are as ephemeral as possible. By “ephemeral”, we mean that the container can be stopped and destroyed, then rebuilt and replaced with an absolute minimum set up and configuration. + +### Understand build context: + +- When you issue a `docker build` command, the current working directory is called the build context. By default, the Dockerfile is assumed to be located here, but you can specify a different location with the file flag (`-f`). Regardless of where the `Dockerfile` actually lives, all recursive contents of files and directories in the current directory are sent to the Docker daemon as the build context. + +### Don't install unnecessary packages: + +- To reduce complexity, dependencies, file sizes, and build times, avoid installing extra or unnecessary packages just because they might be “nice to have.” For example, you don’t need to include a text editor in a database image. + +### Minimize the number of layers: + +- Only the instructions `RUN`, `COPY`, `ADD` create layers. Other instructions create temporary intermediate images, and do not increase the size of the build. + +- Where possible, use [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/), and only copy the artifacts you need into the final image. This allows you to include tools and debug information in your intermediate build stages without increasing the size of the final image. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..3c4d40ae --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Roberto Enrique Chavez Rodriguez. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/PYTHON.md b/PYTHON.md new file mode 100644 index 00000000..7900d65c --- /dev/null +++ b/PYTHON.md @@ -0,0 +1,34 @@ + +Frameworks like `Django` and `Flask` can be used for a production environment, but flask is usually used for prototyping since it is easy to work with; however, flask is slow. + +Also with `Flask`we have to use a lot of plugins to get a usual server functionality, that is a disadvantage. + +`Django` is a [“batteries included”](https://en.wikipedia.org/wiki/Batteries_Included) framework. + +For familiarity, Flask was used to write this application. + +About Python best practices, we can say: + +### The code: +- It must do what it is supposed to do. +- It must not contain defects and problems. +- It must be easy to read, maintain, or extend. + +## Unit tests and best practices. + +### [Unit testing best practices](https://docs.python-guide.org/writing/tests/): + +- A testing unit should focus on one single functionality and prove it correct. +- Each test unit must be fully independent. Each test must be able to run alone, and also within the test suite, regardless of the order that they are called. +- Learn your tools and learn how to run a single test or a test case. Then, when developing a function inside a module, run this function’s tests frequently, ideally automatically when you save the code. +- Always run the full test suite before a coding session, and run it again after. This will give you more confidence that you did not break anything in the rest of the code. +- Use long and descriptive names for testing functions. The style guide here is slightly different than that of running code, where short names are often preferred. The reason is testing functions are never called explicitly. `square()` or even `sqr()` is ok in running code, but in testing code you would have names such as `test_square_of_number_2()`, `test_square_negative_number()`. These function names are displayed when a test fails, and should be as descriptive as possible. + +### Unit tests: + +There are three tests: + +- `test__api_time_properly_responds()` tests that an existing api returns time properly. The returned time is from Moscow and we need to make sure the api works well to use it later in the other tests. +- `test__api_and_app__time_diff_less_than_2s()` tests that the returned time by the api and the app coincide and differ in at most 2 seconds. +- `test_time_correctly_changed()` tests that the app in fact returns different times every time the app method `print_time()` is called and that it is displayed correctly in a browser. + diff --git a/README.md b/README.md index b820d671..89d69415 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,45 @@ -# Labs +![Python CI workflow status badge](https://github.com/RobertronS/labsDevOpsMasterProgram/actions/workflows/ci_main.yml/badge.svg) -## Introduction +# Innopolis University Advanced DevOps course - Summer 2022. +## How the application works for lab 1: -Welcome to DevOps course labs. All labs are practical and will be built on each other. You will implement simple application, containerize it, implement simple tests, build CI/CD, collect metrics, logs, etc. +- `Flask` is a small and lightweight Python web framework that provides useful tools and features that make creating web applications in Python easier. It gives developers flexibility and is a more accessible framework for new developers since you can build a web application quickly using only a single Python file. +- The python time method `ctime()` converts a time expressed in seconds since the epoch to a string representing local time. If secs is not provided or None, the current time as returned by time() is used. This function is equivalent to asctime(localtime(secs)). +- The `ntplib` module offers a simple interface to query NTP servers from Python. +- Consequently The method `print_time` prints the time that is in the machine where the app is running. +- `if __name__ == “main”:` is used to execute some code *only if the file was run directly*, and not imported. -## Architecture +## Docker section (Lab2): -This repository contains master branch with introduction and one branch with instructions for each lab. +The Dockerfile creation of this work is based on the article [ How to Dockerize a Flask Application ](https://www.freecodecamp.org/news/how-to-dockerize-a-flask-app/) -## Rules +- In this part of the project, a dockerfile is created. The `Dockerfile` starts with the installation of a python image (`FROM`), in which the application will run. This import is obvious since without python, the app would not run. +- Next, I add the maintainer tag, in which I can let other users know who is the creator of this file. +- There is a couple of `RUN` commands. The first RUN creates a directory in which all the application will be contained in the container, and the second RUN performs command line operations that are needed to run the application. +- The `COPY` command works to copy the the files in our local working directory to the directory in the docker image. +- The `CMD` instruction contains a list of command line commands that will be executed when a container is created from the image. +- Finally, there is an `EXPOSE` tag which simply lets Docker know that the container listens on the specified network ports at runtime. You can specify whether the port listens on TCP or UDP, and the default is TCP if the protocol is not specified. The EXPOSE instruction does not actually publish the port. -Each labs requires the participant to pass all previous labs, therefore participants are required **to submit each lab (and get at least 60% of max grade for each lab) to pass the course**. +- The image can be downloaded from [my Docker Hub](https://hub.docker.com/r/robertrons/app_python/tags). To run the image, you must run this command: `docker run -p 5000:5000 robertrons/app_python:1.0` Then the app will run on: [http://127.0.0.1:5000](http://127.0.0.1:5000) -Grading is based on PRs with your solutions to the corresponding branch of this repository. This repository is read-only for all participants, therefore to be able to create pull requests, a participant should fork this repository to his own workspace and solve labs there. It is recommended to build solution of lab N upon solution of lab N-1, so choose workflow in your fork of this repository wisely. Structure of your repository will not affect your grade, only state of your repository from which the PR is created will be checked and graded (state after last commit in your PR on corresponding lab). +## Continuous integration (Lab3): -### Recommended workflow +### Code testing: -#### For the first lab -1. Fork this repository on your workspace -2. Checkout master branch -3. Complete lab1 tasks -4. Push the code to your repository -5. **UPD.** create new branch (called lab1_submission) from main -6. **UPD.** Create PR to lab1 branch on this repository from lab1_submission -7. Submit zip to moodle -8. Wait for your grade +The file *test_app.py* contains the tests for the *app.py* application file. To run the tests, please use the command `python -m unittest test_app` in a command line interface (cli). Check the file *PYTHON.md* for details of what the tests do. -#### For all other labs -1. Checkout the commit where you finished the previous lab -2. Complete tasks of current lab -3. Push the code to your repository -4. **UPD.** create new branch (called labN_submission) from main -5. **UPD.** Create PR to labN branch on this repository from labN_submission -6. Submit zip to moodle -7. Wait for your grade +Before running the tests command, the python libraries `requests`, `pytz`, and `unittest` should be installed. +### GitHub CI actions: -## Grading +The [configure GitHub Actions](https://docs.docker.com/ci-cd/github-actions/) docker tutorial was used to set up a Docker project. -### Points distribution for the course +A new file ci_main.yml is added that develops the CI tasks for the project. Mainly, 3 jobs are configured for the CI (continuous integration) process: -``` -70 - labs -30 - final exam -``` +1. **Build the application:** The applicattion is build with all its dependencies and connected with the flask framework. +2. **Run tests:** In this stage, unit tests are executed to test the application's correct functionalities. +3. **Build, chache and publish docker:** In this stage, the application is ready for the integration and a docker project is set up. -### Grade ranges - -``` -[90;100] - A -[75;90) - B -[60;75) - C -[0;60) - D -``` - -### Labs grading - -Each lab is marked out of 10. All labs have a set of main tasks and a set of extra tasks. - -Completing main tasks correctly will give you 10 points out of 10. Completing extra tasks correctly will give you N (depends on the complexity of extra tasks) additional points. Your points for main and extra tasks will be summed and cut to 10 if you have more than 10. Basically, extra points will allow you to get max points even if some main tasks are not finished correctly, but will not give you more points for the course. - -## Deadlines and labs distribution - -Participants will be provided 2 labs simultaneously and have 1 week to submit solutions. Moodle will contain presentations and deadlines. - -**UPD.** You are required to submit zip file with your source code to corresponding assignment in moodle. This is required for the university as proof of work. - -### Late submission policy - -Submitting results after the deadline will result in maximum of 6 points for the corresponding lab. As stated before, all labs must be submitted to pass the course. +The CI workflow status badge: +![Python time app workflow](https://github.com/RobertronS/labsDevOpsMasterProgram/actions/workflows/ci_main.yml/badge.svg) \ No newline at end of file diff --git a/TF.md b/TF.md new file mode 100644 index 00000000..e2dff651 --- /dev/null +++ b/TF.md @@ -0,0 +1,30 @@ +## Best practices for Terraform + +- Dont't change terraform state file (.tfstate) manually, it is better to do this by using "apply" command +- Set up a shared remote storage +- Backup state file +- Use 1 state file per Environment (test, dev, or production) +- Use CI for terraform code +- Generate README for each module with input and output variables +- Update terraform version +- Terraform version manager +- Run terraform in docker container + +### About Naming Conventions: + +- Use _ (underscore) instead of - (dash) everywhere (in resource names, data source names, variable names, outputs, etc). +- Prefer to use lowercase letters and numbers (even though UTF-8 is supported). + +## Vagrant. + +Not possible to work with vagrant (VPN issues) + +![VagrantIssues](../images/lab4/vagrant_issues.png) + +## Terraform WorkSpace + +![Terraform](./images/lab4/terraform_workspace.png) + +## Yandex Cloud VM + +![Virtual Machine](./images/lab5/yandex-cloud.png) \ No newline at end of file diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg new file mode 100644 index 00000000..000f981b --- /dev/null +++ b/ansible/ansible.cfg @@ -0,0 +1,7 @@ +[defaults] +private_key_file = inventory/sshk.txt +inventory = inventory/inventory.txt +interpreter_python = auto + +[ssh_connection] +ssh_args = -o ServerAliveInterval=100 -o ControlMaster=auto -o ControlPersist=10m \ No newline at end of file diff --git a/ansible/inventory/inventory.txt b/ansible/inventory/inventory.txt new file mode 100644 index 00000000..4f75dea1 --- /dev/null +++ b/ansible/inventory/inventory.txt @@ -0,0 +1 @@ +158.160.55.161 ansible_user=robertron ansible_sudo_pass="" \ No newline at end of file diff --git a/ansible/inventory/sshk.txt b/ansible/inventory/sshk.txt new file mode 100644 index 00000000..0c0ff6fd --- /dev/null +++ b/ansible/inventory/sshk.txt @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDId99Q5t +RAJkeEv2Fjm9b4AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAINzpvrn2zpaFGwbC +UixYuI6NvvVkuBGqWEPkHTkVlEN3AAAAoKUycK/nCDd+7UP9jVubSe+bh0lKDi0BfR8ZkP +nE0PC7aRqFPaVqxQM7QC4doiROWZTX9yds8WQX4jf/kDz1vjUP+5SipKugg9OsyOhiqkf3 +0I9c0BkT8HSQQ6nUUf5VfEA04+I0mWRD8L8UgQt5bEngWAAvzCB2I6skNX3AaK9AzjzEaI +altEeogPUwgn07KUJED8k+WOyXp/RL4lGkaSo= +-----END OPENSSH PRIVATE KEY----- diff --git a/ansible/inventory/sshk.txt.pub b/ansible/inventory/sshk.txt.pub new file mode 100644 index 00000000..e24d812b --- /dev/null +++ b/ansible/inventory/sshk.txt.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINzpvrn2zpaFGwbCUixYuI6NvvVkuBGqWEPkHTkVlEN3 robertron@LAPTOP-FQ1HKILG diff --git a/ansible/playbooks/docker.yml b/ansible/playbooks/docker.yml new file mode 100644 index 00000000..b9be31ad --- /dev/null +++ b/ansible/playbooks/docker.yml @@ -0,0 +1,4 @@ +- hosts: all + roles: + - role: ../roles/role-docker + become: true \ No newline at end of file diff --git a/ansible/roles/app_python/.travis.yml b/ansible/roles/app_python/.travis.yml new file mode 100644 index 00000000..36bbf620 --- /dev/null +++ b/ansible/roles/app_python/.travis.yml @@ -0,0 +1,29 @@ +--- +language: python +python: "2.7" + +# Use the new container infrastructure +sudo: false + +# Install ansible +addons: + apt: + packages: + - python-pip + +install: + # Install ansible + - pip install ansible + + # Check ansible version + - ansible --version + + # Create ansible.cfg with correct roles_path + - printf '[defaults]\nroles_path=../' >ansible.cfg + +script: + # Basic role syntax check + - ansible-playbook tests/test.yml -i tests/inventory --syntax-check + +notifications: + webhooks: https://galaxy.ansible.com/api/v1/notifications/ \ No newline at end of file diff --git a/ansible/roles/app_python/README.md b/ansible/roles/app_python/README.md new file mode 100644 index 00000000..0fa513da --- /dev/null +++ b/ansible/roles/app_python/README.md @@ -0,0 +1,4 @@ +Role Name +========= + +This role deploy python app docker container to aws diff --git a/ansible/roles/app_python/defaults/main.yml b/ansible/roles/app_python/defaults/main.yml new file mode 100644 index 00000000..3a1eb37e --- /dev/null +++ b/ansible/roles/app_python/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for python_app diff --git a/ansible/roles/app_python/handlers/main.yml b/ansible/roles/app_python/handlers/main.yml new file mode 100644 index 00000000..445c2f61 --- /dev/null +++ b/ansible/roles/app_python/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for python_app diff --git a/ansible/roles/app_python/meta/main.yml b/ansible/roles/app_python/meta/main.yml new file mode 100644 index 00000000..c572acc9 --- /dev/null +++ b/ansible/roles/app_python/meta/main.yml @@ -0,0 +1,52 @@ +galaxy_info: + author: your name + description: your role description + company: your company (optional) + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + # issue_tracker_url: http://example.com/issue/tracker + + # Choose a valid license ID from https://spdx.org - some suggested licenses: + # - BSD-3-Clause (default) + # - MIT + # - GPL-2.0-or-later + # - GPL-3.0-only + # - Apache-2.0 + # - CC-BY-4.0 + license: license (GPL-2.0-or-later, MIT, etc) + + min_ansible_version: 2.1 + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + # + # Provide a list of supported platforms, and for each platform a list of versions. + # If you don't wish to enumerate all versions for a particular platform, use 'all'. + # To view available platforms and versions (or releases), visit: + # https://galaxy.ansible.com/api/v1/platforms/ + # + # platforms: + # - name: Fedora + # versions: + # - all + # - 25 + # - name: SomePlatform + # versions: + # - all + # - 1.0 + # - 7 + # - 99.99 + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] + # List your role dependencies here, one per line. Be sure to remove the '[]' above, + # if you add dependencies to this list. diff --git a/ansible/roles/app_python/tasks/main.yml b/ansible/roles/app_python/tasks/main.yml new file mode 100644 index 00000000..4251770b --- /dev/null +++ b/ansible/roles/app_python/tasks/main.yml @@ -0,0 +1,16 @@ +# - name: killing previous container +# command: docker stop 8d2be9881f8c +# register: kill +# - debug: msg="{{ kill.stdout }}" + +- name: Copy docker-Compose and promtail + copy: + src: /home/behouba/Desktop/devOpsLab/monitoring/ + dest: tmp/ + +- name: test docker-compose + docker_compose: + project_src: tmp/ + state: present + + diff --git a/ansible/roles/app_python/tests/inventory b/ansible/roles/app_python/tests/inventory new file mode 100644 index 00000000..878877b0 --- /dev/null +++ b/ansible/roles/app_python/tests/inventory @@ -0,0 +1,2 @@ +localhost + diff --git a/ansible/roles/app_python/tests/test.yml b/ansible/roles/app_python/tests/test.yml new file mode 100644 index 00000000..832ecdaf --- /dev/null +++ b/ansible/roles/app_python/tests/test.yml @@ -0,0 +1,5 @@ +--- +- hosts: localhost + remote_user: root + roles: + - python_app diff --git a/ansible/roles/app_python/vars/main.yml b/ansible/roles/app_python/vars/main.yml new file mode 100644 index 00000000..d1223fcb --- /dev/null +++ b/ansible/roles/app_python/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for python_app diff --git a/ansible/roles/role-docker/.ansible-lint b/ansible/roles/role-docker/.ansible-lint new file mode 100644 index 00000000..2cd8c9f2 --- /dev/null +++ b/ansible/roles/role-docker/.ansible-lint @@ -0,0 +1,4 @@ +skip_list: + - 'yaml' + - 'risky-shell-pipe' + - 'role-name' diff --git a/ansible/roles/role-docker/.github/FUNDING.yml b/ansible/roles/role-docker/.github/FUNDING.yml new file mode 100644 index 00000000..96b49383 --- /dev/null +++ b/ansible/roles/role-docker/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms +--- +github: geerlingguy +patreon: geerlingguy diff --git a/ansible/roles/role-docker/.github/stale.yml b/ansible/roles/role-docker/.github/stale.yml new file mode 100644 index 00000000..3e8d9310 --- /dev/null +++ b/ansible/roles/role-docker/.github/stale.yml @@ -0,0 +1,57 @@ +# Configuration for probot-stale - https://github.com/probot/stale +--- +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 90 + +# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: 30 + +# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) +onlyLabels: [] + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - bug + - pinned + - security + - planned + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false + +# Set to true to ignore issues with an assignee (defaults to false) +exemptAssignees: false + +# Label to use when marking as stale +staleLabel: stale + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 30 + +pulls: + markComment: |- + This pull request has been marked 'stale' due to lack of recent activity. If there is no further activity, the PR will be closed in another 30 days. Thank you for your contribution! + + Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark pull requests as stale. + + unmarkComment: >- + This pull request is no longer marked for closure. + + closeComment: >- + This pull request has been closed due to inactivity. If you feel this is in error, please reopen the pull request or file a new PR with the relevant details. + +issues: + markComment: |- + This issue has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution! + + Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark issues as stale. + + unmarkComment: >- + This issue is no longer marked for closure. + + closeComment: >- + This issue has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details. diff --git a/ansible/roles/role-docker/.github/workflows/ci.yml b/ansible/roles/role-docker/.github/workflows/ci.yml new file mode 100644 index 00000000..7df509a2 --- /dev/null +++ b/ansible/roles/role-docker/.github/workflows/ci.yml @@ -0,0 +1,72 @@ +--- +name: CI +'on': + pull_request: + push: + branches: + - master + schedule: + - cron: "0 7 * * 0" + +defaults: + run: + working-directory: 'geerlingguy.docker' + +jobs: + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Check out the codebase. + uses: actions/checkout@v2 + with: + path: 'geerlingguy.docker' + + - name: Set up Python 3. + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install test dependencies. + run: pip3 install yamllint + + - name: Lint code. + run: | + yamllint . + + molecule: + name: Molecule + runs-on: ubuntu-latest + strategy: + matrix: + distro: + - rockylinux8 + - centos7 + - ubuntu2004 + - ubuntu1804 + - debian11 + - debian10 + - debian9 + - fedora34 + + steps: + - name: Check out the codebase. + uses: actions/checkout@v2 + with: + path: 'geerlingguy.docker' + + - name: Set up Python 3. + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install test dependencies. + run: pip3 install ansible molecule[docker] docker + + - name: Run Molecule tests. + run: molecule test + env: + PY_COLORS: '1' + ANSIBLE_FORCE_COLOR: '1' + MOLECULE_DISTRO: ${{ matrix.distro }} diff --git a/ansible/roles/role-docker/.github/workflows/release.yml b/ansible/roles/role-docker/.github/workflows/release.yml new file mode 100644 index 00000000..0b04d243 --- /dev/null +++ b/ansible/roles/role-docker/.github/workflows/release.yml @@ -0,0 +1,40 @@ +--- +# This workflow requires a GALAXY_API_KEY secret present in the GitHub +# repository or organization. +# +# See: https://github.com/marketplace/actions/publish-ansible-role-to-galaxy +# See: https://github.com/ansible/galaxy/issues/46 + +name: Release +'on': + push: + tags: + - '*' + +defaults: + run: + working-directory: 'geerlingguy.docker' + +jobs: + + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Check out the codebase. + uses: actions/checkout@v2 + with: + path: 'geerlingguy.docker' + + - name: Set up Python 3. + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install Ansible. + run: pip3 install ansible-core + + - name: Trigger a new import on Galaxy. + run: >- + ansible-galaxy role import --api-key ${{ secrets.GALAXY_API_KEY }} + $(echo ${{ github.repository }} | cut -d/ -f1) $(echo ${{ github.repository }} | cut -d/ -f2) diff --git a/ansible/roles/role-docker/.gitignore b/ansible/roles/role-docker/.gitignore new file mode 100644 index 00000000..8840c8f0 --- /dev/null +++ b/ansible/roles/role-docker/.gitignore @@ -0,0 +1,5 @@ +*.retry +*/__pycache__ +*.pyc +.cache + diff --git a/ansible/roles/role-docker/.yamllint b/ansible/roles/role-docker/.yamllint new file mode 100644 index 00000000..e6fc5387 --- /dev/null +++ b/ansible/roles/role-docker/.yamllint @@ -0,0 +1,11 @@ +--- +extends: default + +rules: + line-length: + max: 200 + level: warning + +ignore: | + .github/stale.yml + .travis.yml diff --git a/ansible/roles/role-docker/LICENSE b/ansible/roles/role-docker/LICENSE new file mode 100644 index 00000000..4275cf3c --- /dev/null +++ b/ansible/roles/role-docker/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017 Jeff Geerling + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/ansible/roles/role-docker/README.md b/ansible/roles/role-docker/README.md new file mode 100644 index 00000000..8bf9e5f5 --- /dev/null +++ b/ansible/roles/role-docker/README.md @@ -0,0 +1,115 @@ +# Ansible Role: Docker + +[![CI](https://github.com/geerlingguy/ansible-role-docker/workflows/CI/badge.svg?event=push)](https://github.com/geerlingguy/ansible-role-docker/actions?query=workflow%3ACI) + +An Ansible Role that installs [Docker](https://www.docker.com) on Linux. + +## Requirements + +None. + +## Role Variables + +Available variables are listed below, along with default values (see `defaults/main.yml`): + + # Edition can be one of: 'ce' (Community Edition) or 'ee' (Enterprise Edition). + docker_edition: 'ce' + docker_package: "docker-{{ docker_edition }}" + docker_package_state: present + +The `docker_edition` should be either `ce` (Community Edition) or `ee` (Enterprise Edition). You can also specify a specific version of Docker to install using the distribution-specific format: Red Hat/CentOS: `docker-{{ docker_edition }}-`; Debian/Ubuntu: `docker-{{ docker_edition }}=`. + +You can control whether the package is installed, uninstalled, or at the latest version by setting `docker_package_state` to `present`, `absent`, or `latest`, respectively. Note that the Docker daemon will be automatically restarted if the Docker package is updated. This is a side effect of flushing all handlers (running any of the handlers that have been notified by this and any other role up to this point in the play). + + docker_service_state: started + docker_service_enabled: true + docker_restart_handler_state: restarted + +Variables to control the state of the `docker` service, and whether it should start on boot. If you're installing Docker inside a Docker container without systemd or sysvinit, you should set these to `stopped` and set the enabled variable to `no`. + + docker_install_compose: true + docker_compose_version: "1.26.0" + docker_compose_arch: x86_64 + docker_compose_path: /usr/local/bin/docker-compose + +Docker Compose installation options. + + docker_repo_url: https://download.docker.com/linux + +The main Docker repo URL, common between Debian and RHEL systems. + + docker_apt_release_channel: stable + docker_apt_arch: amd64 + docker_apt_repository: "deb [arch={{ docker_apt_arch }}] {{ docker_repo_url }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} {{ docker_apt_release_channel }}" + docker_apt_ignore_key_error: True + docker_apt_gpg_key: "{{ docker_repo_url }}/{{ ansible_distribution | lower }}/gpg" + +(Used only for Debian/Ubuntu.) You can switch the channel to `nightly` if you want to use the Nightly release. + +You can change `docker_apt_gpg_key` to a different url if you are behind a firewall or provide a trustworthy mirror. +Usually in combination with changing `docker_apt_repository` as well. + + docker_yum_repo_url: "{{ docker_repo_url }}/{{ (ansible_distribution == 'Fedora') | ternary('fedora','centos') }}/docker-{{ docker_edition }}.repo"docker_edition }}.repo + docker_yum_repo_enable_nightly: '0' + docker_yum_repo_enable_test: '0' + docker_yum_gpg_key: "{{ docker_repo_url }}/centos/gpg" + +(Used only for RedHat/CentOS.) You can enable the Nightly or Test repo by setting the respective vars to `1`. + +You can change `docker_yum_gpg_key` to a different url if you are behind a firewall or provide a trustworthy mirror. +Usually in combination with changing `docker_yum_repository` as well. + + docker_users: + - user1 + - user2 + +A list of system users to be added to the `docker` group (so they can use Docker on the server). + + docker_daemon_options: + storage-driver: "devicemapper" + log-opts: + max-size: "100m" + +Custom `dockerd` options can be configured through this dictionary representing the json file `/etc/docker/daemon.json`. + +## Use with Ansible (and `docker` Python library) + +Many users of this role wish to also use Ansible to then _build_ Docker images and manage Docker containers on the server where Docker is installed. In this case, you can easily add in the `docker` Python library using the `geerlingguy.pip` role: + +```yaml +- hosts: all + + vars: + pip_install_packages: + - name: docker + + roles: + - geerlingguy.pip + - geerlingguy.docker +``` + +## Dependencies + +None. + +## Example Playbook + +```yaml +- hosts: all + roles: + - geerlingguy.docker +``` + +## License + +MIT / BSD + +## Sponsors + +* [We Manage](https://we-manage.de): Helping start-ups and grown-ups scaling their infrastructure in a sustainable way. + +The above sponsor(s) are supporting Jeff Geerling on [GitHub Sponsors](https://github.com/sponsors/geerlingguy). You can sponsor Jeff's work too, to help him continue improving these Ansible open source projects! + +## Author Information + +This role was created in 2017 by [Jeff Geerling](https://www.jeffgeerling.com/), author of [Ansible for DevOps](https://www.ansiblefordevops.com/). diff --git a/ansible/roles/role-docker/defaults/main.yml b/ansible/roles/role-docker/defaults/main.yml new file mode 100644 index 00000000..9e56e9a5 --- /dev/null +++ b/ansible/roles/role-docker/defaults/main.yml @@ -0,0 +1,39 @@ +--- +# Edition can be one of: 'ce' (Community Edition) or 'ee' (Enterprise Edition). +docker_edition: 'ce' +docker_package: "docker-{{ docker_edition }}" +docker_package_state: present + +# Service options. +docker_service_state: started +docker_service_enabled: true +docker_restart_handler_state: restarted + +# Docker Compose options. +docker_install_compose: true +docker_compose_version: "v2.4.1" +docker_compose_arch: x86_64 +docker_compose_url: "https://github.com/docker/compose/releases/download/{{ docker_compose_version }}/docker-compose-linux-{{ docker_compose_arch }}" +docker_compose_path: /usr/local/bin/docker-compose + +# Docker repo URL. +docker_repo_url: https://download.docker.com/linux + +# Used only for Debian/Ubuntu. Switch 'stable' to 'nightly' if needed. +docker_apt_release_channel: stable +docker_apt_arch: amd64 +docker_apt_repository: "deb [arch={{ docker_apt_arch }}] {{ docker_repo_url }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} {{ docker_apt_release_channel }}" +docker_apt_ignore_key_error: true +docker_apt_gpg_key: "{{ docker_repo_url }}/{{ ansible_distribution | lower }}/gpg" + +# Used only for RedHat/CentOS/Fedora. +docker_yum_repo_url: "{{ docker_repo_url }}/{{ (ansible_distribution == 'Fedora') | ternary('fedora','centos') }}/docker-{{ docker_edition }}.repo" +docker_yum_repo_enable_nightly: '0' +docker_yum_repo_enable_test: '0' +docker_yum_gpg_key: "{{ docker_repo_url }}/centos/gpg" + +# A list of users who will be added to the docker group. +docker_users: [] + +# Docker daemon options as a dict +docker_daemon_options: {} diff --git a/ansible/roles/role-docker/handlers/main.yml b/ansible/roles/role-docker/handlers/main.yml new file mode 100644 index 00000000..a173b0d9 --- /dev/null +++ b/ansible/roles/role-docker/handlers/main.yml @@ -0,0 +1,4 @@ +--- +- name: restart docker + service: "name=docker state={{ docker_restart_handler_state }}" + ignore_errors: "{{ ansible_check_mode }}" diff --git a/ansible/roles/role-docker/meta/main.yml b/ansible/roles/role-docker/meta/main.yml new file mode 100644 index 00000000..5f038a3d --- /dev/null +++ b/ansible/roles/role-docker/meta/main.yml @@ -0,0 +1,36 @@ +--- +dependencies: [] + +galaxy_info: + role_name: docker + author: geerlingguy + description: Docker for Linux. + company: "Midwestern Mac, LLC" + license: "license (BSD, MIT)" + min_ansible_version: 2.4 + platforms: + - name: EL + versions: + - 7 + - 8 + - name: Fedora + versions: + - all + - name: Debian + versions: + - stretch + - buster + - bullseye + - name: Ubuntu + versions: + - xenial + - bionic + - focal + galaxy_tags: + - web + - system + - containers + - docker + - orchestration + - compose + - server diff --git a/ansible/roles/role-docker/molecule/default/converge.yml b/ansible/roles/role-docker/molecule/default/converge.yml new file mode 100644 index 00000000..629095b2 --- /dev/null +++ b/ansible/roles/role-docker/molecule/default/converge.yml @@ -0,0 +1,24 @@ +--- +- name: Converge + hosts: all + become: true + + pre_tasks: + - name: Update apt cache. + apt: update_cache=yes cache_valid_time=600 + when: ansible_os_family == 'Debian' + + - name: Wait for systemd to complete initialization. # noqa 303 + command: systemctl is-system-running + register: systemctl_status + until: > + 'running' in systemctl_status.stdout or + 'degraded' in systemctl_status.stdout + retries: 30 + delay: 5 + when: ansible_service_mgr == 'systemd' + changed_when: false + failed_when: systemctl_status.rc > 1 + + roles: + - role: geerlingguy.docker diff --git a/ansible/roles/role-docker/molecule/default/molecule.yml b/ansible/roles/role-docker/molecule/default/molecule.yml new file mode 100644 index 00000000..44ed1958 --- /dev/null +++ b/ansible/roles/role-docker/molecule/default/molecule.yml @@ -0,0 +1,18 @@ +--- +role_name_check: 1 +dependency: + name: galaxy +driver: + name: docker +platforms: + - name: instance + image: "geerlingguy/docker-${MOLECULE_DISTRO:-centos7}-ansible:latest" + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + privileged: true + pre_build_image: true +provisioner: + name: ansible + playbooks: + converge: ${MOLECULE_PLAYBOOK:-converge.yml} diff --git a/ansible/roles/role-docker/tasks/docker-compose.yml b/ansible/roles/role-docker/tasks/docker-compose.yml new file mode 100644 index 00000000..42f89cbf --- /dev/null +++ b/ansible/roles/role-docker/tasks/docker-compose.yml @@ -0,0 +1,29 @@ +--- +- name: Check current docker-compose version. + command: "{{ docker_compose_path }} --version" + register: docker_compose_vsn + check_mode: false + changed_when: false + failed_when: false + +- set_fact: + docker_compose_current_version: "{{ docker_compose_vsn.stdout | regex_search('(\\d+(\\.\\d+)+)') }}" + when: docker_compose_vsn.stdout is defined + +- name: Delete existing docker-compose version if it's different. + file: + path: "{{ docker_compose_path }}" + state: absent + when: > + docker_compose_current_version is defined + and (docker_compose_version | regex_replace('v', '')) not in docker_compose_current_version + +- name: Install Docker Compose (if configured). + get_url: + url: "{{ docker_compose_url }}" + dest: "{{ docker_compose_path }}" + mode: 0755 + when: > + (docker_compose_current_version is not defined) + or (docker_compose_current_version|length == 0) + or (docker_compose_current_version is version((docker_compose_version | regex_replace('v', '')), '<')) diff --git a/ansible/roles/role-docker/tasks/docker-users.yml b/ansible/roles/role-docker/tasks/docker-users.yml new file mode 100644 index 00000000..b3b6e0f1 --- /dev/null +++ b/ansible/roles/role-docker/tasks/docker-users.yml @@ -0,0 +1,7 @@ +--- +- name: Ensure docker users are added to the docker group. + user: + name: "{{ item }}" + groups: docker + append: true + with_items: "{{ docker_users }}" diff --git a/ansible/roles/role-docker/tasks/main.yml b/ansible/roles/role-docker/tasks/main.yml new file mode 100644 index 00000000..1d82fe29 --- /dev/null +++ b/ansible/roles/role-docker/tasks/main.yml @@ -0,0 +1,60 @@ +--- +- include_tasks: setup-RedHat.yml + when: ansible_os_family == 'RedHat' + +- include_tasks: setup-Debian.yml + when: ansible_os_family == 'Debian' + +- name: Install Docker. + package: + name: "{{ docker_package }}" + state: "{{ docker_package_state }}" + notify: restart docker + ignore_errors: "{{ ansible_check_mode }}" + when: "ansible_version.full is version_compare('2.12', '<') or ansible_os_family not in ['RedHat', 'Debian']" + +- name: Install Docker (with downgrade option). + package: + name: "{{ docker_package }}" + state: "{{ docker_package_state }}" + allow_downgrade: true + notify: restart docker + ignore_errors: "{{ ansible_check_mode }}" + when: "ansible_version.full is version_compare('2.12', '>=') and ansible_os_family in ['RedHat', 'Debian']" + +- name: Ensure /etc/docker/ directory exists. + file: + path: /etc/docker + state: directory + mode: 0755 + when: docker_daemon_options.keys() | length > 0 + +- name: Configure Docker daemon options. + copy: + content: "{{ docker_daemon_options | to_nice_json }}" + dest: /etc/docker/daemon.json + mode: 0644 + when: docker_daemon_options.keys() | length > 0 + notify: restart docker + +- name: Ensure Docker is started and enabled at boot. + service: + name: docker + state: "{{ docker_service_state }}" + enabled: "{{ docker_service_enabled }}" + ignore_errors: "{{ ansible_check_mode }}" + +- name: Ensure handlers are notified now to avoid firewall conflicts. + meta: flush_handlers + +- name: Install pip + package: + name: "python3-pip" + state: present + +- name: Install Docker-compose + become: true + pip: + name: "docker-compose" + state: present + diff --git a/ansible/roles/role-docker/tasks/setup-Debian.yml b/ansible/roles/role-docker/tasks/setup-Debian.yml new file mode 100644 index 00000000..cc444ca9 --- /dev/null +++ b/ansible/roles/role-docker/tasks/setup-Debian.yml @@ -0,0 +1,51 @@ +--- +- name: Ensure old versions of Docker are not installed. + package: + name: + - docker + - docker-engine + state: absent + +- name: Ensure dependencies are installed. + apt: + name: + - apt-transport-https + - ca-certificates + state: present + +- name: Ensure additional dependencies are installed (on Ubuntu < 20.04 and any other systems). + apt: + name: gnupg2 + state: present + when: ansible_distribution != 'Ubuntu' or ansible_distribution_version is version('20.04', '<') + +- name: Ensure additional dependencies are installed (on Ubuntu >= 20.04). + apt: + name: gnupg + state: present + when: ansible_distribution == 'Ubuntu' or ansible_distribution_version is version('20.04', '>=') + +- name: Add Docker apt key. + apt_key: + url: "{{ docker_apt_gpg_key }}" + id: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 + state: present + register: add_repository_key + ignore_errors: "{{ docker_apt_ignore_key_error }}" + +- name: Ensure curl is present (on older systems without SNI). + package: name=curl state=present + when: add_repository_key is failed + +- name: Add Docker apt key (alternative for older systems without SNI). + shell: > + curl -sSL {{ docker_apt_gpg_key }} | apt-key add - + args: + warn: false + when: add_repository_key is failed + +- name: Add Docker repository. + apt_repository: + repo: "{{ docker_apt_repository }}" + state: present + update_cache: true diff --git a/ansible/roles/role-docker/tasks/setup-RedHat.yml b/ansible/roles/role-docker/tasks/setup-RedHat.yml new file mode 100644 index 00000000..676c9e5f --- /dev/null +++ b/ansible/roles/role-docker/tasks/setup-RedHat.yml @@ -0,0 +1,52 @@ +--- +- name: Ensure old versions of Docker are not installed. + package: + name: + - docker + - docker-common + - docker-engine + state: absent + +- name: Add Docker GPG key. + rpm_key: + key: "{{ docker_yum_gpg_key }}" + state: present + +- name: Add Docker repository. + get_url: + url: "{{ docker_yum_repo_url }}" + dest: '/etc/yum.repos.d/docker-{{ docker_edition }}.repo' + owner: root + group: root + mode: 0644 + +- name: Configure Docker Nightly repo. + ini_file: + dest: '/etc/yum.repos.d/docker-{{ docker_edition }}.repo' + section: 'docker-{{ docker_edition }}-nightly' + option: enabled + value: '{{ docker_yum_repo_enable_nightly }}' + mode: 0644 + no_extra_spaces: true + +- name: Configure Docker Test repo. + ini_file: + dest: '/etc/yum.repos.d/docker-{{ docker_edition }}.repo' + section: 'docker-{{ docker_edition }}-test' + option: enabled + value: '{{ docker_yum_repo_enable_test }}' + mode: 0644 + no_extra_spaces: true + +- name: Configure containerd on RHEL 8. + block: + - name: Ensure container-selinux is installed. + package: + name: container-selinux + state: present + + - name: Ensure containerd.io is installed. + package: + name: containerd.io + state: present + when: ansible_distribution_major_version | int == 8 diff --git a/app_python/.dockerignore b/app_python/.dockerignore new file mode 100644 index 00000000..4def41a5 --- /dev/null +++ b/app_python/.dockerignore @@ -0,0 +1,2 @@ +__pycache__ +*.md \ No newline at end of file diff --git a/app_python/Dockerfile b/app_python/Dockerfile new file mode 100644 index 00000000..a0fa1fc0 --- /dev/null +++ b/app_python/Dockerfile @@ -0,0 +1,21 @@ +# syntax=docker/dockerfile:1 +FROM python:3.11.0b3-bullseye + +LABEL maintainer="r.chavez@innopolis.ru" + +RUN mkdir -p /app_python + +WORKDIR /app_python + +RUN pip3 install --upgrade pip &&\ + pip3 install flask &&\ + pip install ntplib + +COPY . . + +ENV FLASK_APP=app.py + +CMD ["python3", "-m" , "flask", "run", "--host=0.0.0.0"] + +EXPOSE 5000 + diff --git a/app_python/app.py b/app_python/app.py new file mode 100644 index 00000000..3d29bd68 --- /dev/null +++ b/app_python/app.py @@ -0,0 +1,14 @@ +from flask import Flask +import ntplib +from time import ctime + +app = Flask(__name__) + +@app.route("/") +def print_time(): + ntpClient = ntplib.NTPClient() + response = ntpClient.request('pool.ntp.org') + return ctime(response.tx_time) + +if __name__ == '__main__': + app.run() \ No newline at end of file diff --git a/app_python/playbook.yml b/app_python/playbook.yml new file mode 100644 index 00000000..0a95d2cc --- /dev/null +++ b/app_python/playbook.yml @@ -0,0 +1,9 @@ +--- +- hosts: all + gather_facts: yes + become: true + tasks: + - name: Copy flask app file to the Vagrant servers + copy: + src: app.py + dest: /home/vagrant/app.py \ No newline at end of file diff --git a/app_python/test_app.py b/app_python/test_app.py new file mode 100644 index 00000000..56dc609d --- /dev/null +++ b/app_python/test_app.py @@ -0,0 +1,36 @@ +from time import sleep +from app import print_time +from requests import get as requests_get +from datetime import datetime +import unittest +import pytz + + +class AppTestCase(unittest.TestCase): + + def test__api_time_properly_responds(self): + res = requests_get('http://worldtimeapi.org/api/timezone/Europe/Moscow') + self.assertTrue(res.ok, "API is not responding") + + def test__api_and_app__time_diff_less_than_2s(self): + + generated_time = datetime.strptime(print_time(), "%a %b %d %H:%M:%S %Y") + json = requests_get('http://worldtimeapi.org/api/timezone/' + "Europe/Moscow").json() + api_given_time = datetime.fromisoformat(json["datetime"]) + + delta = abs(api_given_time - pytz.utc.localize(generated_time)).total_seconds() + # The value 10802.0 comes from a 3-hour deferred time (3 hours = 10800 seconds) + # and 2 seconds to measure the difference. + self.assertLessEqual(delta, 10802.0, "Timestamp difference is more than 2 seconds") + + def test_time_correctly_changed(self): + string_time = print_time() + sleep(2) # Sleep for 2 seconds + string_time_two = print_time() + delta = abs(datetime.strptime(string_time, "%a %b %d %H:%M:%S %Y") - + datetime.strptime(string_time_two, "%a %b %d %H:%M:%S %Y")).total_seconds() + self.assertEqual(delta, 2, "The difference is not the same!") + assert string_time != string_time_two + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/images/lab4/terraform_workspace.png b/images/lab4/terraform_workspace.png new file mode 100644 index 00000000..612a1cef Binary files /dev/null and b/images/lab4/terraform_workspace.png differ diff --git a/images/lab4/vagrant_issues.png b/images/lab4/vagrant_issues.png new file mode 100644 index 00000000..f297cf8e Binary files /dev/null and b/images/lab4/vagrant_issues.png differ diff --git a/images/lab5/Ansible-playbook.png b/images/lab5/Ansible-playbook.png new file mode 100644 index 00000000..4f61947d Binary files /dev/null and b/images/lab5/Ansible-playbook.png differ diff --git a/images/lab5/yandex-cloud.png b/images/lab5/yandex-cloud.png new file mode 100644 index 00000000..e567f899 Binary files /dev/null and b/images/lab5/yandex-cloud.png differ diff --git a/terraform/Vagrantfile b/terraform/Vagrantfile new file mode 100644 index 00000000..a99aab09 --- /dev/null +++ b/terraform/Vagrantfile @@ -0,0 +1,43 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +servers=[ + { + :hostname => "server-one", + :box => "ubuntu/xenial64", + :ip => "192.168.50.101", + :ram => 1024, + :cpu => 2 + }, + { + :hostname => "server-two", + :box => "ubuntu/xenial64", + :ip => "192.168.50.102", + :ram => 1024, + :cpu => 4 + }, + { + :hostname => "server-three", + :box => "ubuntu/xenial64", + :ip => "192.168.50.103", + :ram => 1024, + :cpu => 4 + } +] + +Vagrant.configure("2") do |config| + servers.each do |machine| + config.vm.define machine[:hostname] do |node| + node.vm.box = machine[:box] + node.vm.hostname = machine[:hostname] + node.vm.network "private_network", ip: machine[:ip] + node.vm.provider "virtualbox" do |vb| + vb.customize ["modifyvm", :id, "--memory", machine[:ram]] + end + + node.vm.provision "ansible" do |ansible| + ansible.playbook = "../app_python/playbook.yml" + end + end + end +end \ No newline at end of file diff --git a/terraform/main.yml b/terraform/main.yml new file mode 100644 index 00000000..8439f695 --- /dev/null +++ b/terraform/main.yml @@ -0,0 +1,9 @@ +--- +- hosts: all + gather_facts: yes + become: true + tasks: + - name: Put the script into each Vagrant server + copy: + src: ../app_python/app.py + dest: /home/vagrant/app.py \ No newline at end of file diff --git a/terraform/repository/main.tf b/terraform/repository/main.tf new file mode 100644 index 00000000..1753f543 --- /dev/null +++ b/terraform/repository/main.tf @@ -0,0 +1,39 @@ +terraform { + required_providers { + github = { + source = "integrations/github" + version = ">= 4.0" + } + } +} + +# variable "github_token" { +# type = string +# description = "GitHub Access Token" +# } + +provider "github" { + token = "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" + owner = "RobertronS" +} + +resource "github_repository" "labsDevOpsMasterProgram" { + name = "labsDevOpsMasterProgram" + description = "Terraform Lab 4 solution of DevOps labs" + visibility = "public" +} + +resource "github_branch_default" "default" { + repository = github_repository.labsDevOpsMasterProgram.name + branch = "master" +} + +resource "github_branch_protection_v3" "branch_protection" { + repository = github_repository.labsDevOpsMasterProgram.name + branch = "master" + + required_pull_request_reviews { + dismiss_stale_reviews = true + dismissal_users = ["RobertronS"] + } +} \ No newline at end of file diff --git a/terraform/yandex-lab5/main.tf b/terraform/yandex-lab5/main.tf new file mode 100644 index 00000000..7b80944e --- /dev/null +++ b/terraform/yandex-lab5/main.tf @@ -0,0 +1,48 @@ +terraform { + required_providers { + yandex = { + source = "yandex-cloud/yandex" + version = "0.84.0" + } + } +} + +provider "yandex" { + token = var.yc_token + cloud_id = var.yc_cloud_id + folder_id = var.yc_folder_id + zone = var.yc_zone +} + +resource "yandex_compute_instance" "vm-1" { + name = "terraform-app" + + resources { + cores = 2 + memory = 2 + } + + boot_disk { + disk_id = "fhme18hhe1ub0psdrjq7" + } + + network_interface { + subnet_id = yandex_vpc_subnet.subnet-1.id + nat = true + } + + metadata = { + user-data = "${file("./meta.txt")}" + } +} + +resource "yandex_vpc_network" "network-1" { + name = "network1" +} + +resource "yandex_vpc_subnet" "subnet-1" { + name = "subnet1" + zone = "ru-central1-a" + network_id = yandex_vpc_network.network-1.id + v4_cidr_blocks = ["192.168.10.0/24"] +} \ No newline at end of file diff --git a/terraform/yandex-lab5/meta.txt b/terraform/yandex-lab5/meta.txt new file mode 100644 index 00000000..cb391c82 --- /dev/null +++ b/terraform/yandex-lab5/meta.txt @@ -0,0 +1,8 @@ +#cloud-config +users: + - name: robertron + groups: sudo + shell: /bin/bash + sudo: ['ALL=(ALL) NOPASSWD:ALL'] + ssh_authorized_keys: + - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINzpvrn2zpaFGwbCUixYuI6NvvVkuBGqWEPkHTkVlEN3 robertron@LAPTOP-FQ1HKILG \ No newline at end of file