diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml new file mode 100644 index 0000000000..b81f2f56a9 --- /dev/null +++ b/.github/workflows/python-ci.yml @@ -0,0 +1,114 @@ +name: python-ci + +on: + workflow_dispatch: + push: + branches: ["lab03", "lab05", "master"] + paths: + - "app_python/**" + - ".github/workflows/python-ci.yml" + pull_request: + branches: ["master"] + paths: + - "app_python/**" + - ".github/workflows/python-ci.yml" + +concurrency: + group: python-ci-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +env: + IMAGE_NAME: devops-info-service + APP_DIR: app_python + +jobs: + test-and-lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + cache: "pip" + cache-dependency-path: | + app_python/requirements.txt + app_python/requirements-dev.txt + + - name: Install deps + run: | + python -m pip install --upgrade pip + pip install -r app_python/requirements.txt + pip install -r app_python/requirements-dev.txt + + - name: Lint (ruff) + run: | + cd app_python + ruff check . + + - name: Tests (pytest) + coverage + run: | + cd app_python + pytest -q tests --cov=. --cov-report=term-missing --cov-report=xml + + - name: Upload coverage artifact + uses: actions/upload-artifact@v4 + with: + name: coverage-xml + path: app_python/coverage.xml + + - name: Install Snyk CLI + run: npm install -g snyk + + - name: Snyk scan (dependencies) + continue-on-error: true + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + run: | + cd app_python + snyk test --severity-threshold=high --file=requirements.txt + + docker-build-and-push: + runs-on: ubuntu-latest + needs: test-and-lint + if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/lab03' || github.ref == 'refs/heads/lab05') + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Prepare CalVer tags + run: | + echo "CALVER_MONTH=$(date -u +'%Y.%m')" >> $GITHUB_ENV + echo "CALVER_BUILD=$(date -u +'%Y.%m').${{ github.run_number }}" >> $GITHUB_ENV + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: ${{ env.APP_DIR }} + file: ${{ env.APP_DIR }}/Dockerfile + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ env.CALVER_BUILD }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ env.CALVER_MONTH }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/terraform-ci.yml b/.github/workflows/terraform-ci.yml new file mode 100644 index 0000000000..d6bc61d1a6 --- /dev/null +++ b/.github/workflows/terraform-ci.yml @@ -0,0 +1,57 @@ +name: Terraform CI + +on: + pull_request: + paths: + - "terraform/**" + - ".github/workflows/terraform-ci.yml" + push: + branches: + - master + - lab04 + paths: + - "terraform/**" + - ".github/workflows/terraform-ci.yml" + +jobs: + terraform-check: + name: "fmt / validate / tflint" + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + workdir: + - terraform + - terraform/github + + defaults: + run: + working-directory: ${{ matrix.workdir }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_wrapper: false + + - name: Terraform fmt + run: terraform fmt -check -recursive + + - name: Terraform init (no backend) + run: terraform init -backend=false + + - name: Terraform validate + run: terraform validate -no-color + + - name: Setup TFLint + uses: terraform-linters/setup-tflint@v3 + + - name: TFLint init + run: tflint --init + + - name: TFLint + run: tflint diff --git a/.gitignore b/.gitignore index 30d74d2584..e050328db5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,15 @@ -test \ No newline at end of file +test +.env +key.json +# --- Ansible --- +*.retry +.vault_pass +ansible/inventory/*.pyc +ansible/inventory/__pycache__/ +__pycache__/ + +# --- Vagrant --- +.vagrant/ + +# Do not commit real inventory with IPs if you don't want +# ansible/inventory/hosts.ini diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000000..7bf76d526b --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,21 @@ +Vagrant.configure("2") do |config| + config.vm.box = "ubuntu/jammy64" + config.vm.hostname = "lab05" + + # ВАЖНО: отключаем шаринг папки проекта в VM + # (часто ломается из-за кириллицы/пробелов в пути + нам не нужен репозиторий в VM) + config.vm.synced_folder ".", "/vagrant", disabled: true + + # Пробрасываем порты на Windows-хост + # host_ip "0.0.0.0" нужно, чтобы WSL мог подключиться к проброшенному порту через IP Windows-хоста. + config.vm.network "forwarded_port", guest: 22, host: 2222, host_ip: "0.0.0.0", id: "ssh", auto_correct: true + config.vm.network "forwarded_port", guest: 5000, host: 5000, host_ip: "0.0.0.0", id: "app", auto_correct: true + + config.ssh.insert_key = true + + config.vm.provider "virtualbox" do |vb| + vb.name = "lab05-ansible" + vb.memory = 2048 + vb.cpus = 2 + end +end diff --git a/ansible/README.md b/ansible/README.md new file mode 100644 index 0000000000..d680e5b46e --- /dev/null +++ b/ansible/README.md @@ -0,0 +1,24 @@ +# Lab05 — Ansible + +See: +- `labs/lab05.md` — assignment +- `ansible/docs/LAB05.md` — report template + +## Quick start + +```bash +cd ansible + +# Install required collections +ansible-galaxy collection install -r requirements.yml + +# Connectivity test +ansible all -m ping + +# Provision the target VM (run twice to prove idempotency) +ansible-playbook playbooks/provision.yml +ansible-playbook playbooks/provision.yml + +# Deploy the application (uses Ansible Vault) +ansible-playbook playbooks/deploy.yml --ask-vault-pass +``` diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg new file mode 100644 index 0000000000..b6a8c1d248 --- /dev/null +++ b/ansible/ansible.cfg @@ -0,0 +1,14 @@ +[defaults] +inventory = inventory/hosts.ini +roles_path = roles +host_key_checking = False +# For Vagrant boxes the default SSH user is usually "vagrant". +# You can still override this per-host in inventory/hosts.ini. +remote_user = vagrant +retry_files_enabled = False +interpreter_python = auto_silent + +[privilege_escalation] +become = True +become_method = sudo +become_user = root diff --git a/ansible/docs/LAB05.md b/ansible/docs/LAB05.md new file mode 100644 index 0000000000..112d992ddc --- /dev/null +++ b/ansible/docs/LAB05.md @@ -0,0 +1,450 @@ +# LAB05 — Ansible Fundamentals (Report) + +## 1. Architecture Overview + +### Control node +- OS: Windows 11 + WSL (Ubuntu) +- Ansible is executed inside WSL +- Ansible version: + +```bash +ansible --version +``` + +```text +ansible [core 2.20.3] + config file = /home/dorley/projects/DevOps-Core-Course/ansible/ansible.cfg + configured module search path = ['/home/dorley/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] + ansible python module location = /home/dorley/.local/share/pipx/venvs/ansible-core/lib/python3.12/site-packages/ansible + ansible collection location = /home/dorley/.ansible/collections:/usr/share/ansible/collections + executable location = /home/dorley/.local/bin/ansible + python version = 3.12.3 (main, Jan 22 2026, 20:57:42) [GCC 13.3.0] (/home/dorley/.local/share/pipx/venvs/ansible-core/bin/python) + jinja version = 3.1.6 + pyyaml version = 6.0.3 (with libyaml v0.2.5) +``` + +### Target node +- Provisioned via: **Vagrant + VirtualBox** +- SSH access: forwarded port (guest 22 -> host 2222) +- OS (queried via Ansible): + +```bash +ansible -i inventory/hosts.ini webservers -a "lsb_release -a" +``` + +```text +vagrant1 | CHANGED | rc=0 >> +Distributor ID: Ubuntu +Description: Ubuntu 22.04.5 LTS +Release: 22.04 +Codename: jammyNo LSB modules are available. +``` + +### Networking note (WSL + Windows) +From WSL, SSH access to the VM uses the **Windows host LAN IP** (not 127.0.0.1). + +Example values used in this lab: +- Windows host IP: `192.168.31.32` +- SSH forwarded port: `2222` +- App forwarded port: `5000` + +> If your Windows host IP differs, replace `192.168.31.32` accordingly. + +--- + +## 2. Project Structure (Ansible) + +```text +ansible/ +├── ansible.cfg +├── inventory/ +│ └── hosts.ini +├── group_vars/ +│ └── webservers.yml # non-secret variables (public image) +├── playbooks/ +│ ├── provision.yml +│ └── deploy.yml +└── roles/ + ├── common/ + ├── docker/ + └── app_deploy/ +``` + +--- + +## 3. Roles Documentation + +### Role: `common` +**Purpose** +- Base OS provisioning: apt cache update + essential packages. +- Optional timezone configuration. + +**Key tasks** +- Update apt cache +- Install common packages +- Set timezone (optional) + +**Variables** +- `common_packages` (list) +- `common_timezone` (string) +- `common_set_timezone` (bool) + +--- + +### Role: `docker` +**Purpose** +- Install Docker Engine from the official Docker APT repository. +- Enable and start Docker. +- Add SSH user to the `docker` group. +- Install `python3-docker` for Ansible Docker modules. + +**Key tasks** +- Add Docker GPG key + repo +- Install Docker packages +- Ensure `docker` service is enabled and running +- Add user to docker group +- Install Docker SDK for Python (`python3-docker`) + +**Variables** +- `docker_user` +- `docker_packages` + +--- + +### Role: `app_deploy` +**Purpose** +- Pull the application image. +- Run the container with a stable name, port mapping and restart policy. +- Wait for readiness and verify `/health` and `/`. + +**Key tasks** +- Optional `docker_login` (executed only if password is provided) +- `docker_image` pull +- `docker_container` start +- `wait_for` + HTTP checks + +**Variables** +- `dockerhub_username` +- `dockerhub_password` (empty for public image) +- `docker_image` +- `docker_image_tag` +- `app_name` +- `app_container_name` +- `app_port` / `container_port` +- `app_restart_policy` +- `app_env` + +--- + +## 4. Idempotency Demonstration (Provisioning) + +### 4.1 First run + +```bash +ansible-playbook -i inventory/hosts.ini playbooks/provision.yml +``` + +```text + +PLAY [Provision web servers] ************************************************************************************************************************* + +TASK [Gathering Facts] ******************************************************************************************************************************* +ok: [vagrant1] + +TASK [common : Update apt cache] ********************************************************************************************************************* +ok: [vagrant1] + +TASK [common : Install common packages] ************************************************************************************************************** +ok: [vagrant1] + +TASK [common : Set timezone] ************************************************************************************************************************* +skipping: [vagrant1] + +TASK [docker : Install prerequisites for Docker repository] ****************************************************************************************** +ok: [vagrant1] + +TASK [docker : Ensure /etc/apt/keyrings exists] ****************************************************************************************************** +ok: [vagrant1] + +TASK [docker : Download Docker GPG key (ASCII)] ****************************************************************************************************** +ok: [vagrant1] + +TASK [docker : Check if Docker keyring already exists] *********************************************************************************************** +ok: [vagrant1] + +TASK [docker : Convert (dearmor) Docker GPG key to keyring] ****************************************************************************************** +skipping: [vagrant1] + +TASK [docker : Set correct permissions on Docker keyring] ******************************************************************************************** +ok: [vagrant1] + +TASK [docker : Set Docker APT architecture mapping] ************************************************************************************************** +[WARNING]: Deprecation warnings can be disabled by setting `deprecation_warnings=False` in ansible.cfg. +[DEPRECATION WARNING]: INJECT_FACTS_AS_VARS default to `True` is deprecated, top-level facts will not be auto injected after the change. This feature will be removed from ansible-core version 2.24. +Origin: /home/dorley/projects/DevOps-Core-Course/ansible/roles/docker/tasks/main.yml:42:22 + +40 - name: Set Docker APT architecture mapping +41 ansible.builtin.set_fact: +42 docker_apt_arch: "{{ {'x86_64':'amd64','aarch64':'arm64'}.get(ansible_architecture, ansible_architecture) }}" + ^ column 22 + +Use `ansible_facts["fact_name"]` (no `ansible_` prefix) instead. + +ok: [vagrant1] + +TASK [docker : Add official Docker APT repository] *************************************************************************************************** +[DEPRECATION WARNING]: INJECT_FACTS_AS_VARS default to `True` is deprecated, top-level facts will not be auto injected after the change. This feature will be removed from ansible-core version 2.24. +Origin: /home/dorley/projects/DevOps-Core-Course/ansible/roles/docker/tasks/main.yml:46:11 + +44 - name: Add official Docker APT repository +45 ansible.builtin.apt_repository: +46 repo: "deb [arch={{ docker_apt_arch }} signed-by={{ docker_keyring_path }}] https://download.docker.com/linux/... + ^ column 11 + +Use `ansible_facts["fact_name"]` (no `ansible_` prefix) instead. + +ok: [vagrant1] + +TASK [docker : Install Docker Engine packages] ******************************************************************************************************* +ok: [vagrant1] + +TASK [docker : Ensure Docker service is enabled and running] ***************************************************************************************** +ok: [vagrant1] + +TASK [docker : Ensure docker group exists] *********************************************************************************************************** +ok: [vagrant1] + +TASK [docker : Add user to docker group] ************************************************************************************************************* +ok: [vagrant1] + +TASK [docker : Install Docker SDK for Python on target (for Ansible docker modules)] ***************************************************************** +ok: [vagrant1] + +PLAY RECAP ******************************************************************************************************************************************* +vagrant1 : ok=15 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 +``` + +### 4.2 Second run + +```bash +ansible-playbook -i inventory/hosts.ini playbooks/provision.yml +``` + +```text +PLAY [Provision web servers] ************************************************************************************************************************* + +TASK [Gathering Facts] ******************************************************************************************************************************* +ok: [vagrant1] + +TASK [common : Update apt cache] ********************************************************************************************************************* +ok: [vagrant1] + +TASK [common : Install common packages] ************************************************************************************************************** +ok: [vagrant1] + +TASK [common : Set timezone] ************************************************************************************************************************* +skipping: [vagrant1] + +TASK [docker : Install prerequisites for Docker repository] ****************************************************************************************** +ok: [vagrant1] + +TASK [docker : Ensure /etc/apt/keyrings exists] ****************************************************************************************************** +ok: [vagrant1] + +TASK [docker : Download Docker GPG key (ASCII)] ****************************************************************************************************** +ok: [vagrant1] + +TASK [docker : Check if Docker keyring already exists] *********************************************************************************************** +ok: [vagrant1] + +TASK [docker : Convert (dearmor) Docker GPG key to keyring] ****************************************************************************************** +skipping: [vagrant1] + +TASK [docker : Set correct permissions on Docker keyring] ******************************************************************************************** +ok: [vagrant1] + +TASK [docker : Set Docker APT architecture mapping] ************************************************************************************************** +[WARNING]: Deprecation warnings can be disabled by setting `deprecation_warnings=False` in ansible.cfg. +[DEPRECATION WARNING]: INJECT_FACTS_AS_VARS default to `True` is deprecated, top-level facts will not be auto injected after the change. This feature will be removed from ansible-core version 2.24. +Origin: /home/dorley/projects/DevOps-Core-Course/ansible/roles/docker/tasks/main.yml:42:22 + +40 - name: Set Docker APT architecture mapping +41 ansible.builtin.set_fact: +42 docker_apt_arch: "{{ {'x86_64':'amd64','aarch64':'arm64'}.get(ansible_architecture, ansible_architecture) }}" + ^ column 22 + +Use `ansible_facts["fact_name"]` (no `ansible_` prefix) instead. + +ok: [vagrant1] + +TASK [docker : Add official Docker APT repository] *************************************************************************************************** +[DEPRECATION WARNING]: INJECT_FACTS_AS_VARS default to `True` is deprecated, top-level facts will not be auto injected after the change. This feature will be removed from ansible-core version 2.24. +Origin: /home/dorley/projects/DevOps-Core-Course/ansible/roles/docker/tasks/main.yml:46:11 + +44 - name: Add official Docker APT repository +45 ansible.builtin.apt_repository: +46 repo: "deb [arch={{ docker_apt_arch }} signed-by={{ docker_keyring_path }}] https://download.docker.com/linux/... + ^ column 11 + +Use `ansible_facts["fact_name"]` (no `ansible_` prefix) instead. + +ok: [vagrant1] + +TASK [docker : Install Docker Engine packages] ******************************************************************************************************* +ok: [vagrant1] + +TASK [docker : Ensure Docker service is enabled and running] ***************************************************************************************** +ok: [vagrant1] + +TASK [docker : Ensure docker group exists] *********************************************************************************************************** +ok: [vagrant1] + +TASK [docker : Add user to docker group] ************************************************************************************************************* +ok: [vagrant1] + +TASK [docker : Install Docker SDK for Python on target (for Ansible docker modules)] ***************************************************************** +ok: [vagrant1] + +PLAY RECAP ******************************************************************************************************************************************* +vagrant1 : ok=15 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 +``` + +### 4.3 Analysis +First run changes the system (packages, repositories, services). Second run converges to the desired state and should show `changed=0` (or close to it), proving idempotency. + +--- + +## 5. Secrets / Vault + +This lab uses a **public Docker Hub image**, therefore no registry password is required for `docker pull`. + +Variables are stored in `group_vars/webservers.yml` and `dockerhub_password` is set to an empty string. + +Optional: Ansible Vault can still be used for secrets (e.g., if using a private image), but is not required for this public-image setup. + +--- + +## 6. Deployment Verification + +### 6.1 Deploy run + +```bash +ansible-playbook -i inventory/hosts.ini playbooks/deploy.yml +``` + +```text + +PLAY [Deploy application] **************************************************************************************************************************** + +TASK [Gathering Facts] ******************************************************************************************************************************* +ok: [vagrant1] + +TASK [app_deploy : Ensure Docker SDK for Python is installed on target] ****************************************************************************** +ok: [vagrant1] + +TASK [app_deploy : Login to Docker registry] ********************************************************************************************************* +skipping: [vagrant1] + +TASK [app_deploy : Pull application image] *********************************************************************************************************** +ok: [vagrant1] + +TASK [app_deploy : Check current container (if exists)] ********************************************************************************************** +ok: [vagrant1] + +TASK [app_deploy : Decide if redeploy is needed] ***************************************************************************************************** +ok: [vagrant1] + +TASK [app_deploy : Stop existing container (only if redeploy needed)] ******************************************************************************** +skipping: [vagrant1] + +TASK [app_deploy : Remove existing container (only if redeploy needed)] ****************************************************************************** +skipping: [vagrant1] + +TASK [app_deploy : Run application container] ******************************************************************************************************** +ok: [vagrant1] + +TASK [app_deploy : Wait for the application port to become available] ******************************************************************************** +ok: [vagrant1] + +TASK [app_deploy : Health check (/health)] *********************************************************************************************************** +ok: [vagrant1] + +TASK [app_deploy : Verify main endpoint (/)] ********************************************************************************************************* +ok: [vagrant1] + +PLAY RECAP ******************************************************************************************************************************************* +vagrant1 : ok=9 changed=0 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0 +``` + +### 6.2 Container status + +```bash +ansible -i inventory/hosts.ini webservers -a "docker ps" +``` + +```text +vagrant1 | CHANGED | rc=0 >> +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +a0a73f77e763 dorley174/devops-info-service:latest "python app.py" 35 minutes ago Up 34 minutes 0.0.0.0:5000->5000/tcp devops-info-service +``` + +### 6.3 Health check + +From the target VM (via Ansible): + +```bash +ansible -i inventory/hosts.ini webservers -a "curl -i http://127.0.0.1:5000/health" +``` + +From the control node (WSL) via Windows host forwarding: + +```bash +curl -i "http://192.168.31.32:5000/health" +curl -i "http://192.168.31.32:5000/" +``` + +```text +curl -i "http://192.168.31.32:5000/" +HTTP/1.1 200 OK +Server: Werkzeug/3.1.5 Python/3.13.1 +Date: Thu, 26 Feb 2026 20:05:00 GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 82 +Connection: close + +{"status":"healthy","timestamp":"2026-02-26T20:05:00.236Z","uptime_seconds":2111} +HTTP/1.1 200 OK +Server: Werkzeug/3.1.5 Python/3.13.1 +Date: Thu, 26 Feb 2026 20:05:00 GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 675 +Connection: close + +{"endpoints":[{"description":"Service information","method":"GET","path":"/"},{"description":"Health check","method":"GET","path":"/health"}],"request":{"client_ip":"192.168.31.32","method":"GET","path":"/","user_agent":"curl/8.5.0"},"runtime":{"current_time":"2026-02-26T20:05:00.254Z","timezone":"UTC","uptime_human":"0 hours, 35 minutes","uptime_seconds":2111},"service":{"description":"DevOps course info service","framework":"Flask","name":"devops-info-service","version":"1.0.0"},"system":{"architecture":"x86_64","cpu_count":2,"hostname":"a0a73f77e763","platform":"Linux","platform_version":"Linux-5.15.0-170-generic-x86_64-with-glibc2.36","python_version":"3.13.1"}} +``` + +--- + +## 7. Key Decisions (Short Answers) + +1. **Why use roles instead of plain playbooks?** + Roles keep automation modular and reusable, making playbooks shorter and easier to maintain. + +2. **How do roles improve reusability?** + The same role can be applied to different hosts/projects by changing variables without copying tasks. + +3. **What makes a task idempotent?** + Stateful modules that only change the system if the current state differs from desired state. + +4. **How do handlers improve efficiency?** + Handlers run only when notified, avoiding unnecessary restarts. + +5. **Why would Ansible Vault be needed?** + To store sensitive credentials safely in version control when secrets are required. + +--- + +## 8. Challenges (Optional) + +- VirtualBox/Vagrant leftovers caused VM name conflicts; fixed by removing stale VMs from VirtualBox. +- WSL could not access `127.0.0.1:2222` forwarded port; fixed by using Windows LAN IP (e.g., `192.168.31.32`). diff --git a/ansible/group_vars/all.yml.example b/ansible/group_vars/all.yml.example new file mode 100644 index 0000000000..2dbc32167c --- /dev/null +++ b/ansible/group_vars/all.yml.example @@ -0,0 +1,13 @@ +--- +# Пример! Настоящие значения храните в ЗАШИФРОВАННОМ файле: +# ansible-vault create ansible/group_vars/all.yml + +dockerhub_username: "CHANGE_ME" +dockerhub_password: "CHANGE_ME" # лучше токен, не пароль + +app_name: "devops-info-service" +docker_image: "{{ dockerhub_username }}/{{ app_name }}" +docker_image_tag: "latest" + +app_port: 5000 +app_container_name: "{{ app_name }}" diff --git a/ansible/group_vars/webservers.yml b/ansible/group_vars/webservers.yml new file mode 100644 index 0000000000..cd5ba022fb --- /dev/null +++ b/ansible/group_vars/webservers.yml @@ -0,0 +1,10 @@ +--- +dockerhub_username: "dorley174" +dockerhub_password: "" + +app_name: "devops-info-service" +docker_image: "{{ dockerhub_username }}/{{ app_name }}" +docker_image_tag: "latest" + +app_port: 5000 +app_container_name: "{{ app_name }}" diff --git a/ansible/inventory/hosts.ini b/ansible/inventory/hosts.ini new file mode 100644 index 0000000000..94c50c5212 --- /dev/null +++ b/ansible/inventory/hosts.ini @@ -0,0 +1,19 @@ +# Option A (cloud VM from Lab04): +# web1 ansible_host= ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_ed25519 + +# Option B (100% free, recommended for Windows 11): Vagrant + VirtualBox target VM +# 1) Run `vagrant up` on Windows (PowerShell) in the repo root. +# 2) Check forwarded ports: `vagrant port`. +# 3) In WSL, get Windows host IP: +# WIN_HOST_IP=$(grep -m1 nameserver /etc/resolv.conf | awk '{print $2}') +# 4) Copy Vagrant private key from the project to Linux home and chmod 600. +# 5) Put the detected values below: +# vagrant1 ansible_host= ansible_port= ansible_user=vagrant ansible_ssh_private_key_file=~/.ssh/lab05_vagrant_key + +# Option C (local-only): run on localhost (WSL acts as a "server") +# localhost ansible_connection=local ansible_python_interpreter=/usr/bin/python3 +[webservers] +vagrant1 ansible_host=192.168.31.32 ansible_port=2222 ansible_user=vagrant ansible_ssh_private_key_file=~/.ssh/lab05_vagrant_key + +[webservers:vars] +ansible_python_interpreter=/usr/bin/python3 diff --git a/ansible/inventory/yandex.yml.example b/ansible/inventory/yandex.yml.example new file mode 100644 index 0000000000..5d211343b3 --- /dev/null +++ b/ansible/inventory/yandex.yml.example @@ -0,0 +1,32 @@ +--- +# Пример dynamic inventory для Yandex Cloud (Bonus). +# Требует установленного Python SDK и/или ansible collection, в зависимости от плагина. +# Скопируйте в ansible/inventory/yandex.yml и заполните параметры. +# +# ВНИМАНИЕ: точное имя плагина и поля могут отличаться в зависимости от коллекции. +# См. `ansible-doc -t inventory -l | grep -i yandex`. + +plugin: yandex.cloud.yandex_compute + +# Один из вариантов аутентификации (пример): +# auth_kind: serviceaccount +# service_account_key_file: /home//.config/yandex-cloud/key.json + +folder_id: "CHANGE_ME_FOLDER_ID" + +# Группируем ВМ по label project=lab04, чтобы автоматически получить группу webservers +filters: + labels.project: "lab04" + +# Собираем ansible_host из публичного IP +compose: + ansible_host: network_interfaces[0].primary_v4_address.one_to_one_nat.address + ansible_user: "ubuntu" + +keyed_groups: + - key: labels.project + prefix: "project" + +# Можно дополнительно создать группу webservers +# groups: +# webservers: "labels.project == 'lab04'" diff --git a/ansible/playbooks/deploy.yml b/ansible/playbooks/deploy.yml new file mode 100644 index 0000000000..af1b388e3f --- /dev/null +++ b/ansible/playbooks/deploy.yml @@ -0,0 +1,10 @@ +--- +- name: Deploy application + hosts: webservers + become: true + + vars_files: + - ../group_vars/webservers.yml + + roles: + - app_deploy diff --git a/ansible/playbooks/provision.yml b/ansible/playbooks/provision.yml new file mode 100644 index 0000000000..7cc2e6678d --- /dev/null +++ b/ansible/playbooks/provision.yml @@ -0,0 +1,8 @@ +--- +- name: Provision web servers + hosts: webservers + become: true + + roles: + - common + - docker diff --git a/ansible/playbooks/site.yml b/ansible/playbooks/site.yml new file mode 100644 index 0000000000..ae8e6f8951 --- /dev/null +++ b/ansible/playbooks/site.yml @@ -0,0 +1,9 @@ +--- +- name: Provision and deploy + hosts: webservers + become: true + + roles: + - common + - docker + - app_deploy diff --git a/ansible/requirements.yml b/ansible/requirements.yml new file mode 100644 index 0000000000..b869f415df --- /dev/null +++ b/ansible/requirements.yml @@ -0,0 +1,4 @@ +--- +collections: + - name: community.docker + - name: community.general diff --git a/ansible/roles/app_deploy/defaults/main.yml b/ansible/roles/app_deploy/defaults/main.yml new file mode 100644 index 0000000000..86a10efbf9 --- /dev/null +++ b/ansible/roles/app_deploy/defaults/main.yml @@ -0,0 +1,20 @@ +--- +# App defaults (can be overridden from group_vars/all.yml vault file) +app_name: "devops-info-service" +app_container_name: "{{ app_name }}" + +docker_image_tag: "latest" + +# Host->container port mapping +app_port: 5000 +container_port: 5000 + +# Docker container restart policy +app_restart_policy: "unless-stopped" + +# Environment variables passed to container (dict) +app_env: {} + +# Optional registry URL (empty means Docker Hub) +# Example: "cr.yandex/xxx" or "ghcr.io" +docker_registry: "" diff --git a/ansible/roles/app_deploy/handlers/main.yml b/ansible/roles/app_deploy/handlers/main.yml new file mode 100644 index 0000000000..1fc3fba48b --- /dev/null +++ b/ansible/roles/app_deploy/handlers/main.yml @@ -0,0 +1,6 @@ +--- +- name: restart app container + community.docker.docker_container: + name: "{{ app_container_name }}" + state: started + restart: true diff --git a/ansible/roles/app_deploy/tasks/main.yml b/ansible/roles/app_deploy/tasks/main.yml new file mode 100644 index 0000000000..934817c0c3 --- /dev/null +++ b/ansible/roles/app_deploy/tasks/main.yml @@ -0,0 +1,77 @@ +--- +- name: Ensure Docker SDK for Python is installed on target + ansible.builtin.apt: + name: python3-docker + state: present + update_cache: true + +- name: Login to Docker registry + community.docker.docker_login: + registry_url: "{{ (docker_registry | length > 0) | ternary(docker_registry, omit) }}" + username: "{{ dockerhub_username }}" + password: "{{ dockerhub_password }}" + no_log: true + when: dockerhub_password is defined and dockerhub_password | length > 0 + +- name: Pull application image + community.docker.docker_image: + name: "{{ (docker_image | default(dockerhub_username ~ '/' ~ app_name)) }}:{{ (docker_image_tag | default('latest')) }}" + source: pull + register: app_image_pull + +- name: Check current container (if exists) + community.docker.docker_container_info: + name: "{{ app_container_name }}" + register: app_container_info + failed_when: false + +- name: Decide if redeploy is needed + ansible.builtin.set_fact: + app_needs_redeploy: "{{ app_image_pull.changed or (not app_container_info.exists) }}" + +- name: Stop existing container (only if redeploy needed) + community.docker.docker_container: + name: "{{ app_container_name }}" + state: stopped + when: app_container_info.exists and app_needs_redeploy + +- name: Remove existing container (only if redeploy needed) + community.docker.docker_container: + name: "{{ app_container_name }}" + state: absent + when: app_container_info.exists and app_needs_redeploy + +- name: Run application container + community.docker.docker_container: + name: "{{ app_container_name }}" + image: "{{ (docker_image | default(dockerhub_username ~ '/' ~ app_name)) }}:{{ (docker_image_tag | default('latest')) }}" + state: started + restart_policy: "{{ app_restart_policy }}" + published_ports: + - "{{ app_port }}:{{ container_port }}" + env: "{{ app_env }}" + notify: restart app container + +- name: Wait for the application port to become available + ansible.builtin.wait_for: + host: "127.0.0.1" + port: "{{ app_port }}" + timeout: 60 + +- name: Health check (/health) + ansible.builtin.uri: + url: "http://127.0.0.1:{{ app_port }}/health" + method: GET + status_code: 200 + return_content: true + register: app_health + retries: 10 + delay: 3 + until: app_health.status == 200 + +- name: Verify main endpoint (/) + ansible.builtin.uri: + url: "http://127.0.0.1:{{ app_port }}/" + method: GET + status_code: 200 + return_content: false diff --git a/ansible/roles/common/defaults/main.yml b/ansible/roles/common/defaults/main.yml new file mode 100644 index 0000000000..9864c34cc2 --- /dev/null +++ b/ansible/roles/common/defaults/main.yml @@ -0,0 +1,20 @@ +--- +# Packages that are useful on almost any Ubuntu server +common_packages: + - ca-certificates + - curl + - git + - vim + - htop + - jq + - unzip + - python3-pip + - python3-venv + - tzdata + +# Default timezone (change if needed) +common_timezone: "Europe/Moscow" + +# В WSL и некоторых окружениях timedatectl может быть недоступен. +# По умолчанию timezone НЕ меняем. +common_set_timezone: false diff --git a/ansible/roles/common/tasks/main.yml b/ansible/roles/common/tasks/main.yml new file mode 100644 index 0000000000..a2464dd655 --- /dev/null +++ b/ansible/roles/common/tasks/main.yml @@ -0,0 +1,15 @@ +--- +- name: Update apt cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 + +- name: Install common packages + ansible.builtin.apt: + name: "{{ common_packages }}" + state: present + +- name: Set timezone + community.general.timezone: + name: "{{ common_timezone }}" + when: common_set_timezone | bool diff --git a/ansible/roles/docker/defaults/main.yml b/ansible/roles/docker/defaults/main.yml new file mode 100644 index 0000000000..7c7861e59f --- /dev/null +++ b/ansible/roles/docker/defaults/main.yml @@ -0,0 +1,19 @@ +--- +# User to be added to docker group. +# By default uses the SSH user (Vagrant boxes usually use "vagrant"). +docker_user: "{{ ansible_user | default('vagrant') }}" + +# Official Docker repository key and repo +# (Ubuntu) +docker_gpg_key_url: "https://download.docker.com/linux/ubuntu/gpg" +docker_keyring_path: "/etc/apt/keyrings/docker.gpg" +docker_repo_filename: "docker" + +# Docker packages to install +# docker-compose-plugin gives `docker compose` command. +docker_packages: + - docker-ce + - docker-ce-cli + - containerd.io + - docker-buildx-plugin + - docker-compose-plugin diff --git a/ansible/roles/docker/handlers/main.yml b/ansible/roles/docker/handlers/main.yml new file mode 100644 index 0000000000..1a5058da5e --- /dev/null +++ b/ansible/roles/docker/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: restart docker + ansible.builtin.service: + name: docker + state: restarted diff --git a/ansible/roles/docker/tasks/main.yml b/ansible/roles/docker/tasks/main.yml new file mode 100644 index 0000000000..b98c9a8450 --- /dev/null +++ b/ansible/roles/docker/tasks/main.yml @@ -0,0 +1,77 @@ +--- +- name: Install prerequisites for Docker repository + ansible.builtin.apt: + name: + - ca-certificates + - curl + - gnupg + state: present + update_cache: true + +- name: Ensure /etc/apt/keyrings exists + ansible.builtin.file: + path: /etc/apt/keyrings + state: directory + mode: "0755" + +- name: Download Docker GPG key (ASCII) + ansible.builtin.get_url: + url: "{{ docker_gpg_key_url }}" + dest: /tmp/docker.gpg + mode: "0644" + register: docker_gpg_download + +- name: Check if Docker keyring already exists + ansible.builtin.stat: + path: "{{ docker_keyring_path }}" + register: docker_keyring_stat + +- name: Convert (dearmor) Docker GPG key to keyring + ansible.builtin.command: + cmd: "gpg --dearmor -o {{ docker_keyring_path }} /tmp/docker.gpg" + when: docker_gpg_download.changed or (not docker_keyring_stat.stat.exists) + notify: restart docker + +- name: Set correct permissions on Docker keyring + ansible.builtin.file: + path: "{{ docker_keyring_path }}" + mode: "0644" + +- name: Set Docker APT architecture mapping + ansible.builtin.set_fact: + docker_apt_arch: "{{ {'x86_64':'amd64','aarch64':'arm64'}.get(ansible_architecture, ansible_architecture) }}" + +- name: Add official Docker APT repository + ansible.builtin.apt_repository: + repo: "deb [arch={{ docker_apt_arch }} signed-by={{ docker_keyring_path }}] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable" + state: present + filename: "{{ docker_repo_filename }}" + +- name: Install Docker Engine packages + ansible.builtin.apt: + name: "{{ docker_packages }}" + state: present + update_cache: true + notify: restart docker + +- name: Ensure Docker service is enabled and running + ansible.builtin.service: + name: docker + state: started + enabled: true + +- name: Ensure docker group exists + ansible.builtin.group: + name: docker + state: present + +- name: Add user to docker group + ansible.builtin.user: + name: "{{ docker_user }}" + groups: docker + append: true + +- name: Install Docker SDK for Python on target (for Ansible docker modules) + ansible.builtin.apt: + name: python3-docker + state: present diff --git a/app_python/.dockerignore b/app_python/.dockerignore new file mode 100644 index 0000000000..a253025893 --- /dev/null +++ b/app_python/.dockerignore @@ -0,0 +1,23 @@ +# Python bytecode / cache +__pycache__/ +*.pyc +*.pyo +*.pyd + +# Virtualenvs +.venv/ +venv/ + +# VCS +.git/ +.gitignore + +# IDEs / OS junk +.vscode/ +.idea/ +.DS_Store + +# Docs & tests are not needed at runtime +docs/ +tests/ +README.md diff --git a/app_python/.gitignore b/app_python/.gitignore new file mode 100644 index 0000000000..061f19a9c0 --- /dev/null +++ b/app_python/.gitignore @@ -0,0 +1,13 @@ +# Python +__pycache__/ +*.py[cod] +venv/ +*.log +.env + +# IDE +.vscode/ +.idea/ + +# OS +.DS_Store diff --git a/app_python/Dockerfile b/app_python/Dockerfile new file mode 100644 index 0000000000..d8c40287b9 --- /dev/null +++ b/app_python/Dockerfile @@ -0,0 +1,31 @@ +# syntax=docker/dockerfile:1 + +# Production-oriented image for a small Flask app. +# Pin a specific Python version for reproducible builds. +FROM python:3.13.1-slim + +# Python runtime defaults: no .pyc files, unbuffered logs (better for containers) +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +WORKDIR /app + +# Create a dedicated non-root user (mandatory best practice). +RUN addgroup --system app \ + && adduser --system --ingroup app --no-create-home app + +# Install dependencies first to leverage Docker layer caching. +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +# Copy only the application code needed at runtime. +COPY app.py ./ + +# Drop privileges +USER app + +# Document the port (Flask defaults to 5000 in this repo) +EXPOSE 5000 + +# Start the application +CMD ["python", "app.py"] diff --git a/app_python/README.md b/app_python/README.md new file mode 100644 index 0000000000..48cffa6caa --- /dev/null +++ b/app_python/README.md @@ -0,0 +1,159 @@ +# DevOps Info Service + +[![python-ci](https://github.com/dorley174/DevOps-Core-Course/actions/workflows/python-ci.yml/badge.svg)](https://github.com/dorley174/DevOps-Core-Course/actions/workflows/python-ci.yml) + +## Overview +DevOps Info Service is a production-ready starter web service for the DevOps course. +It reports service metadata, runtime details, and basic system information. + +The service exposes two endpoints: +- `GET /` — service + system + runtime + request information +- `GET /health` — health check endpoint (used later for Kubernetes probes) + +## Prerequisites +- Python **3.11+** +- `pip` +- (Recommended) Virtual environment (`venv`) +- **Windows:** Python Launcher `py` is recommended + +## Installation + +```bash +python -m venv venv +# Windows: .\venv\Scripts\activate +# Linux/macOS: source venv/bin/activate + +pip install -r requirements.txt +``` + +## Running the Application + +### Default run (port 5000) +> If `PORT` is not set, the application runs on **0.0.0.0:5000**. +```bash +python app.py +``` + +### Custom configuration + +**Linux/Mac:** +```bash +HOST=127.0.0.1 PORT=8080 DEBUG=True python app.py +``` + +**Windows (PowerShell):** +```powershell +$env:HOST="127.0.0.1" +$env:PORT="8080" +$env:DEBUG="True" +python app.py +``` + +**Windows (CMD):** +```bat +set HOST=127.0.0.1 +set PORT=8080 +set DEBUG=True +python app.py +``` + +## API Endpoints + +### `GET /` +Returns service metadata, system information, runtime details, request info, and a list of available endpoints. + +Example: +```bash +curl http://127.0.0.1:5000/ +``` + +### `GET /health` +Returns a minimal health response for monitoring. + +Example (includes HTTP status): +```bash +curl -i http://127.0.0.1:5000/health +``` + +## Testing / Pretty Output + +### Pretty-printed JSON +**Windows PowerShell tip:** use `curl.exe`. +```bash +curl -s http://127.0.0.1:5000/ | python -m json.tool +``` + +## Testing & Linting (LAB03) + +> Dev dependencies live in `requirements-dev.txt` (pytest, coverage, ruff). + +Install dev deps: +```bash +pip install -r requirements-dev.txt +``` + +Run linter: +```bash +ruff check . +``` + +Run tests + coverage: +```bash +pytest -q tests --cov=. --cov-report=term-missing +``` + +## CI/CD Secrets (GitHub Actions) + +In your GitHub repository: +**Settings → Secrets and variables → Actions → New repository secret** + +Add: +- `DOCKERHUB_USERNAME` — your Docker Hub username +- `DOCKERHUB_TOKEN` — Docker Hub Access Token (Account Settings → Security) +- `SNYK_TOKEN` — Snyk API token (Account settings → API token) + +## Configuration + +| Variable | Default | Description | +|----------|---------|-------------| +| HOST | 0.0.0.0 | Bind address | +| PORT | 5000 | HTTP port | +| DEBUG | False | Flask debug mode | + +--- + +## Docker + +> Examples below use placeholders like `` and ``. + +### Build (local) + +```bash +docker build -t : . +``` + +### Run + +```bash +docker run --rm -p 5000:5000 : +``` + +(Optional: override env vars) + +```bash +docker run --rm -p 5000:5000 -e PORT=5000 -e DEBUG=false : +``` + +### Pull from Docker Hub + +```bash +docker pull /: +docker run --rm -p 5000:5000 /: +``` + +### Quick test + +```bash +curl http://localhost:5000/health +curl http://localhost:5000/ +``` diff --git a/app_python/app.py b/app_python/app.py new file mode 100644 index 0000000000..931e2ee42b --- /dev/null +++ b/app_python/app.py @@ -0,0 +1,212 @@ +""" +DevOps Info Service +Main application module (Flask) + +Endpoints: +- GET / : service + system + runtime + request info +- GET /health : health check (for probes/monitoring) +""" + +from __future__ import annotations + +import logging +import os +import platform +import socket +from datetime import datetime, timezone +from typing import Any, Dict + +from flask import Flask, jsonify, request + +# ----------------------------------------------------------------------------- +# App & Config +# ----------------------------------------------------------------------------- + +app = Flask(__name__) + +HOST: str = os.getenv("HOST", "0.0.0.0") +PORT: int = int(os.getenv("PORT", "5000")) +DEBUG: bool = os.getenv("DEBUG", "False").strip().lower() == "true" + +SERVICE_NAME = "devops-info-service" +SERVICE_VERSION = "1.0.0" +SERVICE_DESCRIPTION = "DevOps course info service" +SERVICE_FRAMEWORK = "Flask" + +START_TIME_UTC = datetime.now(timezone.utc) + +# ----------------------------------------------------------------------------- +# Logging +# ----------------------------------------------------------------------------- + +logging.basicConfig( + level=logging.DEBUG if DEBUG else logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", +) +logger = logging.getLogger("devops-info-service") + + +@app.before_request +def log_request() -> None: + logger.debug("Request: %s %s", request.method, request.path) + + +@app.after_request +def add_headers(response): + # Helpful defaults + response.headers["Content-Type"] = "application/json; charset=utf-8" + return response + + +# ----------------------------------------------------------------------------- +# Helpers +# ----------------------------------------------------------------------------- + +def iso_utc_now_z() -> str: + """Return current UTC time in ISO format with 'Z' suffix.""" + return datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z") + + +def get_client_ip() -> str: + """ + Best-effort client IP resolution. + Prefers X-Forwarded-For (common behind reverse proxies). + """ + forwarded_for = request.headers.get("X-Forwarded-For", "") + if forwarded_for: + # "client, proxy1, proxy2" + return forwarded_for.split(",")[0].strip() + return request.remote_addr or "unknown" + + +def get_uptime() -> Dict[str, Any]: + """Calculate service uptime since START_TIME_UTC.""" + delta = datetime.now(timezone.utc) - START_TIME_UTC + seconds = int(delta.total_seconds()) + hours = seconds // 3600 + minutes = (seconds % 3600) // 60 + + hours_part = f"{hours} hour" + ("" if hours == 1 else "s") + minutes_part = f"{minutes} minute" + ("" if minutes == 1 else "s") + + return { + "seconds": seconds, + "human": f"{hours_part}, {minutes_part}", + } + + +def get_system_info() -> Dict[str, Any]: + """Collect system information using Python standard library.""" + return { + "hostname": socket.gethostname(), + "platform": platform.system(), + # platform.platform() gives a more descriptive string than version/release alone + "platform_version": platform.platform(), + "architecture": platform.machine(), + "cpu_count": os.cpu_count() or 0, + "python_version": platform.python_version(), + } + + +def build_endpoints() -> list[Dict[str, str]]: + return [ + {"path": "/", "method": "GET", "description": "Service information"}, + {"path": "/health", "method": "GET", "description": "Health check"}, + ] + + +# ----------------------------------------------------------------------------- +# Routes +# ----------------------------------------------------------------------------- + +@app.get("/") +def index(): + """Main endpoint - service and system information.""" + uptime = get_uptime() + + payload: Dict[str, Any] = { + "service": { + "name": SERVICE_NAME, + "version": SERVICE_VERSION, + "description": SERVICE_DESCRIPTION, + "framework": SERVICE_FRAMEWORK, + }, + "system": get_system_info(), + "runtime": { + "uptime_seconds": uptime["seconds"], + "uptime_human": uptime["human"], + "current_time": iso_utc_now_z(), + "timezone": "UTC", + }, + "request": { + "client_ip": get_client_ip(), + "user_agent": request.headers.get("User-Agent", "unknown"), + "method": request.method, + "path": request.path, + }, + "endpoints": build_endpoints(), + } + + return jsonify(payload), 200 + + +@app.get("/health") +def health(): + """Health check endpoint (for probes/monitoring).""" + uptime = get_uptime() + return jsonify( + { + "status": "healthy", + "timestamp": iso_utc_now_z(), + "uptime_seconds": uptime["seconds"], + } + ), 200 + + +# ----------------------------------------------------------------------------- +# Error Handlers +# ----------------------------------------------------------------------------- + +@app.errorhandler(404) +def not_found(_error): + return ( + jsonify( + { + "error": "Not Found", + "message": "Endpoint does not exist", + "timestamp": iso_utc_now_z(), + } + ), + 404, + ) + + +@app.errorhandler(500) +def internal_error(_error): + logger.exception("Unhandled error") + return ( + jsonify( + { + "error": "Internal Server Error", + "message": "An unexpected error occurred", + "timestamp": iso_utc_now_z(), + } + ), + 500, + ) + + +# ----------------------------------------------------------------------------- +# Entrypoint +# ----------------------------------------------------------------------------- + +def main() -> None: + logger.info("Starting %s v%s (%s)", SERVICE_NAME, SERVICE_VERSION, SERVICE_FRAMEWORK) + logger.info("Config: HOST=%s PORT=%s DEBUG=%s", HOST, PORT, DEBUG) + # NOTE: Flask built-in server is fine for lab/dev. + # For production you'd run via a WSGI server (e.g., gunicorn). + app.run(host=HOST, port=PORT, debug=DEBUG) + + +if __name__ == "__main__": + main() diff --git a/app_python/docs/LAB01.md b/app_python/docs/LAB01.md new file mode 100644 index 0000000000..1bce3a2986 --- /dev/null +++ b/app_python/docs/LAB01.md @@ -0,0 +1,150 @@ +# LAB01 — DevOps Info Service (Python) + +## 1) Framework Selection + +### Chosen Framework: Flask + +I chose **Flask** because: +- It is lightweight and easy to set up for a small service with 2 endpoints. +- Minimal boilerplate: perfect for a “foundation” lab where the focus is DevOps workflow. +- Great learning curve and widely used in industry for microservices. + +### Comparison Table + +| Framework | Pros | Cons | Fit for this Lab | +|---|---|---|---| +| **Flask** | Simple, minimal, fast to implement, huge ecosystem | No built-in async, fewer built-ins than Django/FastAPI | Quick & clean | +| FastAPI | Modern, async-ready, auto Swagger/OpenAPI docs | Slightly more setup, concepts (pydantic/models) | Also good | +| Django | Full-featured (ORM, auth, admin, etc.) | Overkill for 2 endpoints | Too heavy | + +## 2) Best Practices Applied + +### 2.1 Clean Code Organization (PEP 8, structure) + +- Imports grouped. +- Constants for configuration and service metadata. + + +**Example (helpers):** +```python +def get_uptime() -> Dict[str, Any]: + delta = datetime.now(timezone.utc) - START_TIME_UTC + seconds = int(delta.total_seconds()) + ... +``` + +### 2.2 Configuration via Environment Variables + +Implemented: +- `HOST` (default `0.0.0.0`) +- `PORT` (default `5000`) +- `DEBUG` (default `False`) + +**Example:** +```python +HOST = os.getenv("HOST", "0.0.0.0") +PORT = int(os.getenv("PORT", "5000")) +DEBUG = os.getenv("DEBUG", "False").lower() == "true" +``` + +Why it matters: +- Same artifact runs in different environments without code changes. + +### 2.3 Error Handling + +Added: +- `404` handler returning JSON +- `500` handler returning JSON + logging exception + +**Example:** +```python +@app.errorhandler(404) +def not_found(_error): + return jsonify({"error": "Not Found", ...}), 404 +``` + +Why it matters: +- Predictable API responses +- Easier debugging and monitoring + +### 2.4 Logging + +- `logging.basicConfig(...)` +- Debug request logging in `@app.before_request` +- Startup logs show config + +Why it matters: +- Logs are essential for observability (containers, CI/CD, production troubleshooting). + +## 3) API Documentation + +### 3.1 `GET /` + +Returns: +- `service`: name/version/description/framework +- `system`: hostname/platform/platform_version/architecture/cpu_count/python_version +- `runtime`: uptime_seconds/uptime_human/current_time/timezone +- `request`: client_ip/user_agent/method/path +- `endpoints`: list of available endpoints + +**Test command:** +```bash +curl -s http://127.0.0.1:5000/ | python -m json.tool +``` + +### 3.2 `GET /health` + +Returns: +```json +{ + "status": "healthy", + "timestamp": "....Z", + "uptime_seconds": 123 +} +``` + +**Test command:** +```bash +curl -s http://127.0.0.1:5000/health | python -m json.tool +``` + +### 3.3 Configuration Tests + +```bash +python app.py +PORT=8080 python app.py +HOST=127.0.0.1 PORT=3000 python app.py +DEBUG=true python app.py +``` + +## 4) Testing Evidence (Screenshots) + +Screenshots are stored in: +`app_python/docs/screenshots/` + +Required screenshots: +1. `01-main-endpoint.png` — Browser/terminal showing full JSON from `GET /` +2. `02-health-check.png` — Response from `GET /health` +3. `03-formatted-output.png` — Pretty-printed JSON output (example: `python -m json.tool`) + +## 5) Challenges & Solutions + +### Challenge 1: Correct client IP behind proxy + +**Problem:** `request.remote_addr` may show proxy IP. +**Solution:** Prefer `X-Forwarded-For` header when available: +```python +forwarded_for = request.headers.get("X-Forwarded-For", "") +``` + +### Challenge 2: UTC timestamp format with `Z` + +**Problem:** `datetime.isoformat()` returns `+00:00`. +**Solution:** Convert to `Z` suffix: +```python +datetime.now(timezone.utc).isoformat(...).replace("+00:00", "Z") +``` + +## 6) GitHub Community + +Starring repositories is a useful way to bookmark valuable projects and also signals appreciation/support to maintainers, improving a project’s visibility in GitHub search. Following developers (professor/TAs/classmates) helps with networking, discovering useful code patterns, and makes collaboration easier by tracking teammates’ activity and progress. diff --git a/app_python/docs/LAB02.md b/app_python/docs/LAB02.md new file mode 100644 index 0000000000..cf73086f00 --- /dev/null +++ b/app_python/docs/LAB02.md @@ -0,0 +1,188 @@ +# LAB02 — Docker Containerization + +This document describes how the Flask app from Lab 1 was containerized using Docker best practices. + +--- + +## 1) Docker Best Practices Applied + +### 1.1 Pinned base image version +Using a specific tag makes builds reproducible and avoids unexpected changes when a floating tag updates. + +```dockerfile +FROM python:3.13.1-slim +``` + +### 1.2 Non-root user +Containers should not run as root to reduce the blast radius if the process is compromised. + +```dockerfile +RUN addgroup --system app \ + && adduser --system --ingroup app --no-create-home app +USER app +``` + +### 1.3 Layer caching (install deps before copying source) +Copy `requirements.txt` first and install dependencies. When you change only `app.py`, Docker can reuse the cached dependency layer. + +```dockerfile +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt +COPY app.py ./ +``` + +### 1.4 Minimal copy & build context (.dockerignore) +`.dockerignore` keeps your build context small and avoids shipping dev artifacts, docs, and VCS metadata. + +Examples excluded: +- `.git/` +- `__pycache__/` +- `docs/`, `tests/` + +--- + +## 2) Image Information & Decisions + +### 2.1 Base image choice +- **Chosen:** `python:3.13.1-slim` +- **Why:** small Debian-based runtime, good compatibility for Python wheels, smaller than full images. + +### 2.2 Image size +Record final size: + +```bash +docker images | grep +``` + +**Result:** +```bash +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +9b452f420205 dorley174/devops-info-service:lab02 "python app.py" 28 seconds ago Up 28 seconds 0.0.0.0:8080->5000/tcp, [::]:8080->5000/tcp keen_cannon +``` + +### 2.3 Layer structure +Explain key layers: +1. Base runtime layer (Python slim) +2. User creation (security) +3. Dependencies install (cached) +4. App code copy + +### 2.4 Optimizations +- `pip install --no-cache-dir` to avoid keeping pip cache in the image +- `.dockerignore` to reduce build context and speed up builds + +--- + +## 3) Build & Run Process + +### 3.1 Build output +```bash +docker build -t devops-info-service:lab02 . +``` + +**Output:** +```text +[+] Building 141.0s (13/13) FINISHED docker:desktop-linux + => [internal] load build definition from Dockerfile 0.1s + => => transferring dockerfile: 860B 0.0s + => resolve image config for docker-image://docker.io/docker/dockerfile:1 4.3s + => docker-image://docker.io/docker/dockerfile:1@sha256:b6afd42430b15f2d2a4c5a02b919e98a525b785b1aaff16747d2f623364e39b6 48.9s + => => resolve docker.io/docker/dockerfile:1@sha256:b6afd42430b15f2d2a4c5a02b919e98a525b785b1aaff16747d2f623364e39b6 0.0s + => => sha256:77246a01651da592b7bae79e0e20ed3b4f2e4c00a1b54b7c921c91ae3fa9ef07 13.57MB / 13.57MB 48.7s + => => extracting sha256:77246a01651da592b7bae79e0e20ed3b4f2e4c00a1b54b7c921c91ae3fa9ef07 0.1s + => [internal] load metadata for docker.io/library/python:3.13.1-slim 2.4s + => [internal] load .dockerignore 0.0s + => => transferring context: 260B 0.0s + => [1/6] FROM docker.io/library/python:3.13.1-slim@sha256:031ebf3cde9f3719d2db385233bcb18df5162038e9cda20e64e08f49f4b47a 73.5s + => => resolve docker.io/library/python:3.13.1-slim@sha256:031ebf3cde9f3719d2db385233bcb18df5162038e9cda20e64e08f49f4b47a2 0.0s + => => sha256:e18acc4841d040a12b49e06abbb2c9096bb559fa8d853543ff7ddc2e410531ff 249B / 249B 0.5s + => => sha256:a9910b4c71585ea2d9ca0acc308ab5ed7cc807819405bb23b5de7538142e4f36 12.58MB / 12.58MB 29.6s + => => sha256:ad24708d5ee00d90d30e51e59ffce29e5764775c877a74798e85bcc3176cde76 3.51MB / 3.51MB 15.2s + => => sha256:c29f5b76f736a8b555fd191c48d6581bb918bcd605a7cbcc76205dd6acff3260 28.21MB / 28.21MB 69.4s + => => extracting sha256:c29f5b76f736a8b555fd191c48d6581bb918bcd605a7cbcc76205dd6acff3260 0.7s + => => extracting sha256:ad24708d5ee00d90d30e51e59ffce29e5764775c877a74798e85bcc3176cde76 0.0s + => => extracting sha256:a9910b4c71585ea2d9ca0acc308ab5ed7cc807819405bb23b5de7538142e4f36 3.2s + => => extracting sha256:e18acc4841d040a12b49e06abbb2c9096bb559fa8d853543ff7ddc2e410531ff 0.0s + => [internal] load build context 0.1s + => => transferring context: 6.51kB 0.0s + => [2/6] WORKDIR /app 0.3s + => [3/6] RUN addgroup --system app && adduser --system --ingroup app --no-create-home app 0.4s + => [4/6] COPY requirements.txt ./ 0.0s + => [5/6] RUN pip install --no-cache-dir -r requirements.txt 9.2s + => [6/6] COPY app.py ./ 0.0s + => exporting to image 0.6s + => => exporting layers 0.4s + => => exporting manifest sha256:409e654da6806d719714cca291fe908afa3a7a1f78a444630451c3b6aea4fd52 0.0s + => => exporting config sha256:09310faf41ce8b42a107e1ec5a0e880b29a6aa7664b862574ecfa84c92183f4a 0.0s + => => exporting attestation manifest sha256:fccefc3ee604d7bca7e605be0deb0341e96a260a53534af34245a6e2013a34ec 0.0s + +View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/vg7v11zlyt4npch7bw4olkcat +``` + +### 3.2 Run output +```bash +docker run --rm -p 8080:5000 --name devops-info devops-info-service:lab02 +``` + +**Output:** +```text + * Serving Flask app 'app' + * Debug mode: off +2026-02-04 19:29:00,312 - devops-info-service - INFO - Starting devops-info-service v1.0.0 (Flask) +2026-02-04 19:29:00,312 - devops-info-service - INFO - Config: HOST=0.0.0.0 PORT=5000 DEBUG=False +2026-02-04 19:29:00,322 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://172.17.0.2:5000 +2026-02-04 19:29:00,322 - werkzeug - INFO - Press CTRL+C to quit +2026-02-04 19:30:45,863 - werkzeug - INFO - 172.17.0.1 - - [04/Feb/2026 19:30:45] "GET / HTTP/1.1" 200 - +2026-02-04 19:30:59,367 - werkzeug - INFO - 172.17.0.1 - - [04/Feb/2026 19:30:59] "GET /health HTTP/1.1" 200 - +``` + +### 3.3 Endpoint tests +```bash +curl http://localhost:5000/health +curl http://localhost:5000/ +``` + +**Output:** +```text +https://hub.docker.com/r/dorley174/devops-info-service +``` + +### 3.4 Docker Hub +- **Repo URL:** `https://hub.docker.com/r/dorley174/devops-info-service` +- **Tags pushed:** `latest, lab02` + +--- + +## 4) Technical Analysis + +### Why does this Dockerfile work? +- The app listens on `0.0.0.0` and port `5000` by default, so it is reachable from outside the container when `-p 5000:5000` is used. +- Dependencies are installed before source code to maximize cache hits. + +### What happens if you change layer order? +If you copy `app.py` before installing dependencies, any change in source invalidates the cache and forces dependency reinstall, making rebuilds slower. + +### Security considerations implemented +- Non-root user with `USER app` +- Minimal base image (`slim`) +- No extra tools installed + +### How does `.dockerignore` help? +- Smaller build context → faster builds +- Prevents shipping irrelevant files into the image (security + size) + +--- + +## 5) Challenges & Solutions + +Document what happened on your machine. Typical examples: +- **Port not reachable:** fixed by ensuring the app binds to `0.0.0.0` (already default in `app.py`) and using `-p host:container`. +- **Permission issues:** ensured runtime user exists and app files are readable by it. + +What you learned: +- How Docker layer caching changes rebuild speed +- Why non-root matters +- How `.dockerignore` affects build context and image size diff --git a/app_python/docs/LAB03.md b/app_python/docs/LAB03.md new file mode 100644 index 0000000000..cd7976fa9b --- /dev/null +++ b/app_python/docs/LAB03.md @@ -0,0 +1,74 @@ +# LAB03 — CI/CD (GitHub Actions) + +## 1) Overview + +### Testing framework +This project uses **pytest** because it provides: +- clean assertions and fixtures +- Flask test client without running a live server +- easy coverage reporting via `pytest-cov` + +### Test coverage (what is tested) +- `GET /` — validates the JSON structure and required fields +- `GET /health` — validates the health-check response +- `GET /does-not-exist` — validates the JSON 404 error handler +- 500-case — forces an internal error and validates the JSON 500 error handler + +### CI triggers +The workflow runs on `push` and `pull_request` for the selected branches, **only when** these paths change: +- `app_python/**` +- `.github/workflows/python-ci.yml` + +### Versioning strategy +We use **CalVer** for Docker image tags: +- monthly tag: `YYYY.MM` (e.g., `2026.02`) +- build tag: `YYYY.MM.` (e.g., `2026.02.31`) +- plus `latest` + +This makes it easy to see *when* an image was built and to find the most recent build. + +--- + +## 2) How to run locally + +From the `app_python` directory: + +```bash +python -m venv .venv +# Windows: .\.venv\Scripts\activate +# Linux/macOS: source .venv/bin/activate + +pip install -r requirements.txt +pip install -r requirements-dev.txt + +ruff check . +pytest -q tests --cov=. --cov-report=term-missing +``` + +--- + +## 3) Workflow evidence (paste links here) + +Add links to prove the pipeline works: + +- ✅ Successful workflow run: [https://github.com/dorley174/DevOps-Core-Course/actions/runs/21917089826/job/63287177794](https://github.com/dorley174/DevOps-Core-Course/actions/runs/21917089826/job/63287177794) +- ✅ Docker Hub image/repo: [https://hub.docker.com/repository/docker/dorley174/devops-info-service/general](https://hub.docker.com/repository/docker/dorley174/devops-info-service/general) +- ✅ Status badge in README: see `README.md` +or go +[![python-ci](https://github.com/dorley174/DevOps-Core-Course/actions/workflows/python-ci.yml/badge.svg)](https://github.com/dorley174/DevOps-Core-Course/actions/workflows/python-ci.yml) + +--- + +## 4) Best practices implemented + +- **Path filters**: CI does not run if changes are outside `app_python/**` +- **Job dependency**: Docker push runs only if lint + tests succeeded +- **Concurrency**: cancels outdated runs on the same branch +- **Least privileges**: `permissions: contents: read` +- **Caching**: + - pip cache via `actions/setup-python` + - Docker layer cache via `cache-to/cache-from type=gha` +- **Snyk scan**: + - scans dependencies from `requirements.txt` + - severity threshold = high + - `continue-on-error: true` (learning mode; does not block pipeline) diff --git a/app_python/docs/LAB04.md b/app_python/docs/LAB04.md new file mode 100644 index 0000000000..32c243bde7 --- /dev/null +++ b/app_python/docs/LAB04.md @@ -0,0 +1,173 @@ +# LAB04 — Infrastructure as Code (Terraform + Pulumi) + +## 0) Почему Yandex Cloud (я из РФ) + +From Russia is better to work with **Yandex Cloud** (registry, billing, accessability). As a zone i use `ru-central1-a`. + +Alternatives: VK Cloud / Selectel + +--- + +## 1) Terraform + +### 1.1. hat to create + +- VPC Network +- Subnet +- Security Group с inbound rules: + - **22/tcp** (SSH) — using my IP (`allowed_ssh_cidr`) + - **80/tcp** — open out + - **5000/tcp** — open out +- VM (Compute Instance) with public IP (NAT) + +### 1.2. Repo structure + +``` +terraform/ + main.tf + providers.tf + variables.tf + locals.tf + outputs.tf + versions.tf + .gitignore + terraform.tfvars.example + .tflint.hcl +``` + +### 1.3. Prepare + +1) Download Terraform. +2) Download `yc` CLI. +3) Create an SSH key: + +```bash +ssh-keygen -t ed25519 -C "lab04" -f ~/.ssh/lab04_ed25519 +``` + +4) How to know my IP (example): + +```bash +curl ifconfig.me +``` + +### 1.4. Start + +```bash +cd terraform +cp terraform.tfvars.example terraform.tfvars +# filling terraform.tfvars + +terraform init +terraform fmt +terraform validate +terraform plan +terraform apply +``` + +**Output after apply** (вставьте сюда ваш вывод): + +- `terraform version` +- `terraform plan` output +- `terraform apply` output +- `public_ip` / `ssh_command` + +### 1.5. Access check + +```bash +ssh -i ~/.ssh/lab04_ed25519 ubuntu@ +``` + +### 1.6. Delete resourses + +```bash +terraform destroy +``` + +--- + +## 2) Pulumi + +### 2.1. What to create + +Same is in Terraform (Network/Subnet/SG/VM). + +### 2.2. Structure + +``` +pulumi/ + Pulumi.yaml + requirements.txt + __main__.py + README.md + .gitignore +``` + +### 2.3. Authorization YC + +Using env (`YC_TOKEN`, `YC_CLOUD_ID`, `YC_FOLDER_ID`, `YC_ZONE`) or using `pulumi config` keys `yandex:*`. + +### 2.4. Start + +```bash +cd pulumi +python -m venv venv +# Windows: +# .\venv\Scripts\activate +# Linux/macOS: +# source venv/bin/activate +pip install -r requirements.txt + +pulumi login +pulumi stack init dev + +# Provider config (пример): +pulumi config set yandex:cloudId +pulumi config set yandex:folderId +pulumi config set yandex:zone ru-central1-a +pulumi config set --secret yandex:token + +# Project config: +pulumi config set zone ru-central1-a +pulumi config set subnetCidr 10.10.0.0/24 +pulumi config set allowedSshCidr "<ваш_IP>/32" +pulumi config set sshUser ubuntu +pulumi config set sshPublicKeyPath "~/.ssh/lab04_ed25519.pub" + +pulumi preview +pulumi up +``` + +**Output after up**: + +- `pulumi version` +- `pulumi up` output +- `public_ip` / `ssh_command` + +### 2.5. Delete resourses + +```bash +pulumi destroy +``` + +--- + +## 3) Comparison Terraform vs Pulumi + +| Criteria | Terraform | Pulumi | +|---|---|---| +| Language | HCL | Python | +| Reusing | modules | full language abstraction | +| entry threshold | lower | upper (needed) | +| State | loval / remote backend | Pulumi Cloud / local | + +--- + +## Bonus 1 — Terraform CI (fmt/validate/tflint) + +There are workflow `.github/workflows/terraform-ci.yml`. + +all logs in lab04 folder + +--- + diff --git a/app_python/docs/screenshots/01-main-endpoint.png b/app_python/docs/screenshots/01-main-endpoint.png new file mode 100644 index 0000000000..37f9f4b206 Binary files /dev/null and b/app_python/docs/screenshots/01-main-endpoint.png differ diff --git a/app_python/docs/screenshots/02-health-check.png b/app_python/docs/screenshots/02-health-check.png new file mode 100644 index 0000000000..dbaf3f82e2 Binary files /dev/null and b/app_python/docs/screenshots/02-health-check.png differ diff --git a/app_python/docs/screenshots/03-formatted-output.png b/app_python/docs/screenshots/03-formatted-output.png new file mode 100644 index 0000000000..e806eddcf2 Binary files /dev/null and b/app_python/docs/screenshots/03-formatted-output.png differ diff --git a/app_python/requirements-dev.txt b/app_python/requirements-dev.txt new file mode 100644 index 0000000000..3365ea3398 --- /dev/null +++ b/app_python/requirements-dev.txt @@ -0,0 +1,3 @@ +pytest==8.3.4 +pytest-cov==6.0.0 +ruff==0.9.7 \ No newline at end of file diff --git a/app_python/requirements.txt b/app_python/requirements.txt new file mode 100644 index 0000000000..78180a1ad1 --- /dev/null +++ b/app_python/requirements.txt @@ -0,0 +1 @@ +Flask==3.1.0 \ No newline at end of file diff --git a/app_python/tests/__init__.py b/app_python/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app_python/tests/conftest.py b/app_python/tests/conftest.py new file mode 100644 index 0000000000..29d0202acc --- /dev/null +++ b/app_python/tests/conftest.py @@ -0,0 +1,9 @@ +import pytest +from app import app as flask_app + + +@pytest.fixture() +def client(): + flask_app.config.update(TESTING=True) + with flask_app.test_client() as client: + yield client diff --git a/app_python/tests/test_endpoints.py b/app_python/tests/test_endpoints.py new file mode 100644 index 0000000000..8a6e199c1d --- /dev/null +++ b/app_python/tests/test_endpoints.py @@ -0,0 +1,40 @@ +import app as app_module +from app import app as flask_app + + +def test_root_ok_structure(client): + resp = client.get("/", headers={"User-Agent": "pytest"}) + assert resp.status_code == 200 + data = resp.get_json() + assert isinstance(data, dict) + for key in ("service", "system", "runtime", "request", "endpoints"): + assert key in data + + +def test_health_ok(client): + resp = client.get("/health") + assert resp.status_code == 200 + data = resp.get_json() + assert data["status"] == "healthy" + + +def test_404_error_shape(client): + resp = client.get("/does-not-exist") + assert resp.status_code == 404 + data = resp.get_json() + assert data["error"] == "Not Found" + + +def test_500_error_shape(monkeypatch): + flask_app.config.update(TESTING=False, PROPAGATE_EXCEPTIONS=False) + + def boom(): + raise RuntimeError("boom") + + monkeypatch.setattr(app_module, "get_system_info", boom) + + with flask_app.test_client() as client: + resp = client.get("/") + assert resp.status_code == 500 + data = resp.get_json() + assert data["error"] == "Internal Server Error" diff --git a/lab04_proofs/proof.png b/lab04_proofs/proof.png new file mode 100644 index 0000000000..35724a8b29 Binary files /dev/null and b/lab04_proofs/proof.png differ diff --git a/lab04_proofs/pulumi_config_log.txt b/lab04_proofs/pulumi_config_log.txt new file mode 100644 index 0000000000..94e3e04dd9 --- /dev/null +++ b/lab04_proofs/pulumi_config_log.txt @@ -0,0 +1,282 @@ +PS C:\DevOps\DevOps-Core-Course> git add . +PS C:\DevOps\DevOps-Core-Course> git commit -m "feat: added terraform lab part" +[lab04 4686605] feat: added terraform lab part + 30 files changed, 1767 insertions(+), 1 deletion(-) + create mode 100644 .github/workflows/terraform-ci.yml + create mode 100644 app_python/docs/LAB04.md + create mode 100644 lab04_proofs/proof.png + create mode 100644 lab04_proofs/terraform_intalling_log.txt + create mode 100644 lab04_proofs/ubuntu_logs.txt + create mode 100644 pulumi/.gitignore + create mode 100644 pulumi/Pulumi.yaml + create mode 100644 pulumi/README.md + create mode 100644 pulumi/__main__.py + create mode 100644 pulumi/requirements.txt + create mode 100644 scripts/load-env.ps1 + create mode 100644 terraform/.gitignore + create mode 100644 terraform/.tflint.hcl + create mode 100644 terraform/README.md + create mode 100644 terraform/github/.gitignore + create mode 100644 terraform/github/.tflint.hcl + create mode 100644 terraform/github/main.tf + create mode 100644 terraform/github/outputs.tf + create mode 100644 terraform/github/providers.tf + create mode 100644 terraform/github/terraform.tfvars.example + create mode 100644 terraform/github/variables.tf + create mode 100644 terraform/github/versions.tf + create mode 100644 terraform/locals.tf + create mode 100644 terraform/main.tf + create mode 100644 terraform/outputs.tf + create mode 100644 terraform/providers.tf + create mode 100644 terraform/terraform.tfvars.example + create mode 100644 terraform/variables.tf + create mode 100644 terraform/versions.tf +PS C:\DevOps\DevOps-Core-Course> git push origin lab04 +Enumerating objects: 47, done. +Counting objects: 100% (47/47), done. +Delta compression using up to 20 threads +Compressing objects: 100% (36/36), done. +Writing objects: 100% (41/41), 113.24 KiB | 5.66 MiB/s, done. +Total 41 (delta 2), reused 0 (delta 0), pack-reused 0 (from 0) +remote: Resolving deltas: 100% (2/2), completed with 2 local objects. +remote: +remote: Create a pull request for 'lab04' on GitHub by visiting: +remote: https://github.com/dorley174/DevOps-Core-Course/pull/new/lab04 +remote: +To https://github.com/dorley174/DevOps-Core-Course + * [new branch] lab04 -> lab04 +PS C:\DevOps\DevOps-Core-Course> .\scripts\load-env.ps1 +Loaded .env OK +YC_CLOUD_ID=b1gca960emnn9qqikne9 +YC_FOLDER_ID=b1g82kdcn5grlmu79ano +YC_ZONE=ru-central1-a +YC_SERVICE_ACCOUNT_KEY_FILE=C:\DevOps\DevOps-Core-Course\key.json +PS C:\DevOps\DevOps-Core-Course> winget install -e --id Pulumi.Pulumi +Найдено Pulumi [Pulumi.Pulumi] Версия 3.222.0 +Лицензия на это приложение предоставлена вам владельцем. +Корпорация Майкрософт не несет ответственность за сторонние пакеты и не предоставляет для них никакие лицензии. +Скачивание https://github.com/pulumi/pulumi-winget/releases/download/v3.222.0/pulumi-3.222.0-windows-x64.msi + ██████████████████████████████ 87.8 MB / 87.8 MB +Хэш установщика успешно проверен +Запуск установки пакета... +Успешно установлено +PS C:\DevOps\DevOps-Core-Course> pulumi version +pulumi : Имя "pulumi" не распознано как имя командлета, функции, файла сценария или выполняемой программы. Проверьте прави +льность написания имени, а также наличие и правильность пути, после чего повторите попытку. +строка:1 знак:1 ++ pulumi version ++ ~~~~~~ + + CategoryInfo : ObjectNotFound: (pulumi:String) [], CommandNotFoundException + + FullyQualifiedErrorId : CommandNotFoundException + +PS C:\DevOps\DevOps-Core-Course> + * Журнал восстановлен + + +PS C:\DevOps\DevOps-Core-Course> .\scripts\load-env.ps1 +Loaded .env OK +YC_CLOUD_ID=b1gca960emnn9qqikne9 +YC_FOLDER_ID=b1g82kdcn5grlmu79ano +YC_ZONE=ru-central1-a +YC_SERVICE_ACCOUNT_KEY_FILE=C:\DevOps\DevOps-Core-Course\key.json +PS C:\DevOps\DevOps-Core-Course> pulumi version +v3.222.0 +PS C:\DevOps\DevOps-Core-Course> mkdir pulumi +mkdir : Элемент с указанным именем C:\DevOps\DevOps-Core-Course\pulumi уже существует. +строка:1 знак:1 ++ mkdir pulumi ++ ~~~~~~~~~~~~ + + CategoryInfo : ResourceExists: (C:\DevOps\DevOps-Core-Course\pulumi:String) [New-Item], IOException + + FullyQualifiedErrorId : DirectoryExist,Microsoft.PowerShell.Commands.NewItemCommand + +PS C:\DevOps\DevOps-Core-Course> cd pulumi +PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi new python +error: C:\DevOps\DevOps-Core-Course\pulumi is not empty; use --force to continue and overwrite existing files, or use --dir to specify an empty directory. +PS C:\DevOps\DevOps-Core-Course\pulumi> tree /f +Структура папок +Серийный номер тома: 56C3-7760 +C:. +│ .gitignore +│ Pulumi.yaml +│ README.md +│ requirements.txt +│ __main__.py +│ +└───__pycache__ + __main__.cpython-311.pyc + +PS C:\DevOps\DevOps-Core-Course\pulumi> python -m venv venv +PS C:\DevOps\DevOps-Core-Course\pulumi> .\venv\Scripts\activate +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pip install -r requirements.txt +Collecting pulumi<4.0.0,>=3.0.0 (from -r requirements.txt (line 1)) + Downloading pulumi-3.222.0-py3-none-any.whl.metadata (3.8 kB) +Collecting pulumi-yandex (from -r requirements.txt (line 2)) + Downloading pulumi_yandex-0.13.0.tar.gz (425 kB) + Installing build dependencies ... done + Getting requirements to build wheel ... done + Preparing metadata (pyproject.toml) ... done +Collecting debugpy~=1.8.7 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1)) + Downloading debugpy-1.8.20-cp313-cp313-win_amd64.whl.metadata (1.5 kB) +Collecting dill~=0.4 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1)) + Downloading dill-0.4.1-py3-none-any.whl.metadata (10 kB) +Collecting grpcio<2,>=1.68.1 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1)) + Downloading grpcio-1.78.0-cp313-cp313-win_amd64.whl.metadata (3.9 kB) +Requirement already satisfied: pip>=24.3.1 in c:\devops\devops-core-course\pulumi\venv\lib\site-packages (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1)) (24.3.1) +Collecting protobuf<7,>=3.20.3 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1)) + Downloading protobuf-6.33.5-cp310-abi3-win_amd64.whl.metadata (593 bytes) +Collecting pyyaml~=6.0 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1)) + Downloading pyyaml-6.0.3-cp313-cp313-win_amd64.whl.metadata (2.4 kB) +Collecting semver~=3.0 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1)) + Downloading semver-3.0.4-py3-none-any.whl.metadata (6.8 kB) +Collecting parver>=0.2.1 (from pulumi-yandex->-r requirements.txt (line 2)) + Downloading parver-0.5-py3-none-any.whl.metadata (2.7 kB) +Collecting typing-extensions~=4.12 (from grpcio<2,>=1.68.1->pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1)) + Using cached typing_extensions-4.15.0-py3-none-any.whl.metadata (3.3 kB) +Collecting arpeggio>=1.7 (from parver>=0.2.1->pulumi-yandex->-r requirements.txt (line 2)) + Downloading Arpeggio-2.0.3-py2.py3-none-any.whl.metadata (2.4 kB) +Collecting attrs>=19.2 (from parver>=0.2.1->pulumi-yandex->-r requirements.txt (line 2)) + Using cached attrs-25.4.0-py3-none-any.whl.metadata (10 kB) +Downloading pulumi-3.222.0-py3-none-any.whl (390 kB) +Downloading debugpy-1.8.20-cp313-cp313-win_amd64.whl (5.4 MB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.4/5.4 MB 7.1 MB/s eta 0:00:00 +Downloading dill-0.4.1-py3-none-any.whl (120 kB) +Downloading grpcio-1.78.0-cp313-cp313-win_amd64.whl (4.8 MB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.8/4.8 MB 7.6 MB/s eta 0:00:00 +Downloading parver-0.5-py3-none-any.whl (15 kB) +Downloading protobuf-6.33.5-cp310-abi3-win_amd64.whl (437 kB) +Downloading pyyaml-6.0.3-cp313-cp313-win_amd64.whl (154 kB) +Downloading semver-3.0.4-py3-none-any.whl (17 kB) +Downloading Arpeggio-2.0.3-py2.py3-none-any.whl (54 kB) +Using cached attrs-25.4.0-py3-none-any.whl (67 kB) +Using cached typing_extensions-4.15.0-py3-none-any.whl (44 kB) +Building wheels for collected packages: pulumi-yandex + Building wheel for pulumi-yandex (pyproject.toml) ... done + Created wheel for pulumi-yandex: filename=pulumi_yandex-0.13.0-py3-none-any.whl size=606675 sha256=8441929cfc6b1f5855695216aff4c9946e00cbdecca3d15f56ed027966eaa71e + Stored in directory: c:\users\данил\appdata\local\pip\cache\wheels\d0\b7\cf\9a6c587521036e8cbd68fa735d2f5d352bb7fb4f71d8d5aaac +Successfully built pulumi-yandex +Installing collected packages: arpeggio, typing-extensions, semver, pyyaml, protobuf, dill, debugpy, attrs, parver, grpcio, pulumi, pulumi-yandex +Successfully installed arpeggio-2.0.3 attrs-25.4.0 debugpy-1.8.20 dill-0.4.1 grpcio-1.78.0 parver-0.5 protobuf-6.33.5 pulumi-3.222.0 pulumi-yandex-0.13.0 pyyaml-6.0.3 semver-3.0.4 typing-extensions-4.15.0 + +[notice] A new release of pip is available: 24.3.1 -> 26.0.1 +[notice] To update, run: python.exe -m pip install --upgrade pip +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi version +v3.222.0 +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> python --version +Python 3.13.1 +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi login file://C:\DevOps\DevOps-Core-Course\pulumi\.pulumi-state +error: problem logging in: unable to open bucket file:///C:/DevOps/DevOps-Core-Course/pulumi/.pulumi-state?no_tmp_dir=true: GetFileAttributesEx C:\DevOps\DevOps-Core-Course\pulumi\.pulumi-state: The system cannot find the file specified. +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> New-Item -ItemType Directory -Force .pulumi-state | Out-Null +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi login file://C:\DevOps\DevOps-Core-Course\pulumi\.pulumi-state +Logged in to Dorley as Dorley\Данил (file://C:/DevOps/DevOps-Core-Course/pulumi/.pulumi-state) +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi whoami +Dorley\Данил +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi stack ls +NAME LAST UPDATE RESOURCE COUNT +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi stack init dev +Enter your passphrase to protect config/secrets: +Re-enter your passphrase to confirm: +Created stack 'dev' +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> cd C:\DevOps\DevOps-Core-Course +(venv) PS C:\DevOps\DevOps-Core-Course> .\scripts\load-env.ps1 +Loaded .env OK +YC_CLOUD_ID=b1gca960emnn9qqikne9 +YC_FOLDER_ID=b1g82kdcn5grlmu79ano +YC_ZONE=ru-central1-a +YC_SERVICE_ACCOUNT_KEY_FILE=C:\DevOps\DevOps-Core-Course\key.json +(venv) PS C:\DevOps\DevOps-Core-Course> cd pulumi +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> .\venv\Scripts\activate +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi config set zone ru-central1-a +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi config set subnetCidr 10.10.0.0/24 +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi config set allowedSshCidr "95.111.204.70/32" +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi config set sshUser ubuntu +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi config set sshPublicKeyPath "~/.ssh/lab04_ed25519.pub" +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi config set imageFamily ubuntu-2404-lts +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi preview +Enter your passphrase to unlock config/secrets + (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember): +Enter your passphrase to unlock config/secrets +Previewing update (dev): + Type Name Plan Info + + pulumi:pulumi:Stack lab04-pulumi-yc-dev create 1 error +Diagnostics: + pulumi:pulumi:Stack (lab04-pulumi-yc-dev): + error: Program failed with an unhandled exception: + Traceback (most recent call last): + File "C:\DevOps\DevOps-Core-Course\pulumi\__main__.py", line 4, in + import pulumi_yandex as yandex + File "C:\DevOps\DevOps-Core-Course\pulumi\venv\Lib\site-packages\pulumi_yandex\__init__.py", line 5, in + from . import _utilities + File "C:\DevOps\DevOps-Core-Course\pulumi\venv\Lib\site-packages\pulumi_yandex\_utilities.py", line 10, in + import pkg_resources + ModuleNotFoundError: No module named 'pkg_resources' + +Resources: + + 1 to create + 1 errored + +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> python -m pip install --upgrade pip setuptools wheel +Requirement already satisfied: pip in c:\devops\devops-core-course\pulumi\venv\lib\site-packages (24.3.1) +Collecting pip + Downloading pip-26.0.1-py3-none-any.whl.metadata (4.7 kB) +Collecting setuptools + Using cached setuptools-82.0.0-py3-none-any.whl.metadata (6.6 kB) +Collecting wheel + Downloading wheel-0.46.3-py3-none-any.whl.metadata (2.4 kB) +Collecting packaging>=24.0 (from wheel) + Using cached packaging-26.0-py3-none-any.whl.metadata (3.3 kB) +Downloading pip-26.0.1-py3-none-any.whl (1.8 MB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.8/1.8 MB 8.3 MB/s eta 0:00:00 +Using cached setuptools-82.0.0-py3-none-any.whl (1.0 MB) +Downloading wheel-0.46.3-py3-none-any.whl (30 kB) +Using cached packaging-26.0-py3-none-any.whl (74 kB) +Installing collected packages: setuptools, pip, packaging, wheel + Attempting uninstall: pip + Found existing installation: pip 24.3.1 + Uninstalling pip-24.3.1: + Successfully uninstalled pip-24.3.1 +Successfully installed packaging-26.0 pip-26.0.1 setuptools-82.0.0 wheel-0.46.3 +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> python -c "import pkg_resources; print('pkg_resources OK')" +Traceback (most recent call last): + File "", line 1, in + import pkg_resources; print('pkg_resources OK') + ^^^^^^^^^^^^^^^^^^^^ +ModuleNotFoundError: No module named 'pkg_resources' +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> winget install -e --id Python.Python.3.12 +Найден существующий установленный пакет. Попытка обновления установленного пакета... +Найдено Python 3.12 [Python.Python.3.12] Версия 3.12.10 +Лицензия на это приложение предоставлена вам владельцем. +Корпорация Майкрософт не несет ответственность за сторонние пакеты и не предоставляет для них никакие лицензии. +Скачивание https://www.python.org/ftp/python/3.12.10/python-3.12.10-amd64.exe + ██████████████████████████████ 25.7 MB / 25.7 MB +Хэш установщика успешно проверен +Запуск установки пакета... +Вы отменили установку. +Сбой установки с кодом выхода: 1602 +Журнал установщика доступен по адресу: C:\Users\Данил\AppData\Local\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\DiagOutputDir\Python.Python.3.12.3.12.10-26-02-19-23-43-29.log +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pip uninstall -y setuptools +Found existing installation: setuptools 82.0.0 +Uninstalling setuptools-82.0.0: + Successfully uninstalled setuptools-82.0.0 +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pip install setuptools==80.9.0 +Collecting setuptools==80.9.0 + Downloading setuptools-80.9.0-py3-none-any.whl.metadata (6.6 kB) +Downloading setuptools-80.9.0-py3-none-any.whl (1.2 MB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.2/1.2 MB 6.8 MB/s 0:00:00 +Installing collected packages: setuptools +Successfully installed setuptools-80.9.0 +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> python -c "import pkg_resources; print('pkg_resources OK')" +:1: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81. + import pkg_resources; print('pkg_resources OK') +pkg_resources OK +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi stack ls +NAME LAST UPDATE RESOURCE COUNT +dev* 11 minutes ago 0 +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi stack select dev +(venv) PS C:\DevOps\DevOps-Core-Course\pulumi> pulumi config +KEY VALUE +allowedSshCidr 95.111.204.70/32 +imageFamily ubuntu-2404-lts +sshPublicKeyPath ~/.ssh/lab04_ed25519.pub +sshUser ubuntu +subnetCidr 10.10.0.0/24 +zone ru-central1-a \ No newline at end of file diff --git a/lab04_proofs/pulumi_result.txt b/lab04_proofs/pulumi_result.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lab04_proofs/terraform_intalling_log.txt b/lab04_proofs/terraform_intalling_log.txt new file mode 100644 index 0000000000..ae90e9f9bf --- /dev/null +++ b/lab04_proofs/terraform_intalling_log.txt @@ -0,0 +1,282 @@ +PS C:\DevOps\DevOps-Core-Course> .\scripts\load-env.ps1 +Loaded .env OK +YC_CLOUD_ID=b1gca960emnn9qqikne9 +YC_FOLDER_ID=b1g82kdcn5grlmu79ano +YC_ZONE=ru-central1-a +YC_SERVICE_ACCOUNT_KEY_FILE=C:\DevOps\DevOps-Core-Course\key.json +PS C:\DevOps\DevOps-Core-Course> terraform -chdir=terraform apply +data.yandex_compute_image.os: Reading... +data.yandex_compute_image.os: Read complete after 1s [id=fd8lt661chfo5i13a40d] + +Terraform used the selected providers to generate the following execution plan. Resource actions are indicated +with the following symbols: + + create + +Terraform will perform the following actions: + + # yandex_compute_instance.vm will be created + + resource "yandex_compute_instance" "vm" { + + allow_stopping_for_update = true + + created_at = (known after apply) + + folder_id = (known after apply) + + fqdn = (known after apply) + + gpu_cluster_id = (known after apply) + + hardware_generation = (known after apply) + + hostname = (known after apply) + + id = (known after apply) + + labels = { + + "project" = "lab04" + } + + maintenance_grace_period = (known after apply) + + maintenance_policy = (known after apply) + + metadata = { + + "ssh-keys" = "ubuntu:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5nKglXX/E2/S3oPKXahGr77IZELnnZlqE4oGCyEhZQ Данил@Dorley" + } + + name = "lab04-vm" + + network_acceleration_type = "standard" + + platform_id = "standard-v2" + + status = (known after apply) + + zone = "ru-central1-a" + + + boot_disk { + + auto_delete = true + + device_name = (known after apply) + + disk_id = (known after apply) + + mode = (known after apply) + + + initialize_params { + + block_size = (known after apply) + + description = (known after apply) + + image_id = "fd8lt661chfo5i13a40d" + + name = (known after apply) + + size = 10 + + snapshot_id = (known after apply) + + type = "network-hdd" + } + } + + + metadata_options (known after apply) + + + network_interface { + + index = (known after apply) + + ip_address = (known after apply) + + ipv4 = true + + ipv6 = (known after apply) + + ipv6_address = (known after apply) + + mac_address = (known after apply) + + nat = true + + nat_ip_address = (known after apply) + + nat_ip_version = (known after apply) + + security_group_ids = (known after apply) + + subnet_id = (known after apply) + } + + + placement_policy (known after apply) + + + resources { + + core_fraction = 20 + + cores = 2 + + memory = 1 + } + + + scheduling_policy { + + preemptible = false + } + } + + # yandex_vpc_network.lab_net will be created + + resource "yandex_vpc_network" "lab_net" { + + created_at = (known after apply) + + default_security_group_id = (known after apply) + + folder_id = (known after apply) + + id = (known after apply) + + labels = { + + "project" = "lab04" + } + + name = "lab04-net" + + subnet_ids = (known after apply) + } + + # yandex_vpc_security_group.vm_sg will be created + + resource "yandex_vpc_security_group" "vm_sg" { + + created_at = (known after apply) + + description = "Security Group for Lab04 VM" + + folder_id = (known after apply) + + id = (known after apply) + + labels = { + + "project" = "lab04" + } + + name = "lab04-sg" + + network_id = (known after apply) + + status = (known after apply) + + + egress { + + description = "Allow all outbound" + + from_port = 0 + + id = (known after apply) + + labels = (known after apply) + + port = -1 + + protocol = "ANY" + + to_port = 65535 + + v4_cidr_blocks = [ + + "0.0.0.0/0", + ] + + v6_cidr_blocks = [] + # (2 unchanged attributes hidden) + } + + + ingress { + + description = "App port (Flask)" + + from_port = -1 + + id = (known after apply) + + labels = (known after apply) + + port = 5000 + + protocol = "TCP" + + to_port = -1 + + v4_cidr_blocks = [ + + "0.0.0.0/0", + ] + + v6_cidr_blocks = [] + # (2 unchanged attributes hidden) + } + + ingress { + + description = "HTTP" + + from_port = -1 + + id = (known after apply) + + labels = (known after apply) + + port = 80 + + protocol = "TCP" + + to_port = -1 + + v4_cidr_blocks = [ + + "0.0.0.0/0", + ] + + v6_cidr_blocks = [] + # (2 unchanged attributes hidden) + } + + ingress { + + description = "SSH from allowed CIDR" + + from_port = -1 + + id = (known after apply) + + labels = (known after apply) + + port = 22 + + protocol = "TCP" + + to_port = -1 + + v4_cidr_blocks = [ + + "95.111.204.70/32", + ] + + v6_cidr_blocks = [] + # (2 unchanged attributes hidden) + } + } + + # yandex_vpc_subnet.lab_subnet will be created + + resource "yandex_vpc_subnet" "lab_subnet" { + + created_at = (known after apply) + + folder_id = (known after apply) + + id = (known after apply) + + labels = { + + "project" = "lab04" + } + + name = "lab04-subnet" + + network_id = (known after apply) + + v4_cidr_blocks = [ + + "10.10.0.0/24", + ] + + v6_cidr_blocks = (known after apply) + + zone = "ru-central1-a" + } + +Plan: 4 to add, 0 to change, 0 to destroy. + +Changes to Outputs: + + app_url = (known after apply) + + http_url = (known after apply) + + internal_ip = (known after apply) + + public_ip = (known after apply) + + ssh_command = (known after apply) + + vm_id = (known after apply) +╷ +│ Warning: Cannot connect to YC tool initialization service. Network connectivity to the service is required for provider version control. +│ +│ +│ with provider["registry.terraform.io/yandex-cloud/yandex"], +│ on providers.tf line 1, in provider "yandex": +│ 1: provider "yandex" { +│ +╵ + +Do you want to perform these actions? + Terraform will perform the actions described above. + Only 'yes' will be accepted to approve. + + Enter a value: yes + +yandex_vpc_network.lab_net: Creating... +yandex_vpc_network.lab_net: Creation complete after 4s [id=enpq59lnhclspku28afm] +yandex_vpc_subnet.lab_subnet: Creating... +yandex_vpc_security_group.vm_sg: Creating... +yandex_vpc_subnet.lab_subnet: Creation complete after 1s [id=e9bedb7ig70u3rpvqeru] +yandex_vpc_security_group.vm_sg: Creation complete after 3s [id=enp5bnpeefpntqlukehb] +yandex_compute_instance.vm: Creating... +yandex_compute_instance.vm: Still creating... [00m10s elapsed] +yandex_compute_instance.vm: Still creating... [00m20s elapsed] +yandex_compute_instance.vm: Still creating... [00m30s elapsed] +yandex_compute_instance.vm: Creation complete after 35s [id=fhmdplo02ifil4mk7odj] +╷ +│ Warning: Cannot connect to YC tool initialization service. Network connectivity to the service is required for provider version control. +│ +│ +│ with provider["registry.terraform.io/yandex-cloud/yandex"], +│ on providers.tf line 1, in provider "yandex": +│ 1: provider "yandex" { +│ +╵ + +Apply complete! Resources: 4 added, 0 changed, 0 destroyed. + +Outputs: + +app_url = "http://89.169.147.72:5000/" +http_url = "http://89.169.147.72/" +internal_ip = "10.10.0.22" +public_ip = "89.169.147.72" +ssh_command = "ssh -i ~/.ssh/id_ed25519 ubuntu@89.169.147.72" +vm_id = "fhmdplo02ifil4mk7odj" +PS C:\DevOps\DevOps-Core-Course> ssh -i $env:USERPROFILE\.ssh\lab04_ed25519 ubuntu@89.169.147.72 +The authenticity of host '89.169.147.72 (89.169.147.72)' can't be established. +ED25519 key fingerprint is SHA256:87OV4P2mx41pGWV3CIi0gSmPp8kE6JXzmB6sfnxBg2c. +This key is not known by any other names. +Are you sure you want to continue connecting (yes/no/[fingerprint])? yes +Warning: Permanently added '89.169.147.72' (ED25519) to the list of known hosts. +Welcome to Ubuntu 24.04.4 LTS (GNU/Linux 6.8.0-100-generic x86_64) + + * Documentation: https://help.ubuntu.com + * Management: https://landscape.canonical.com + * Support: https://ubuntu.com/pro + + System information as of Thu Feb 19 19:29:02 UTC 2026 + + System load: 0.2 Processes: 100 + Usage of /: 23.1% of 9.04GB Users logged in: 0 + Memory usage: 19% IPv4 address for eth0: 10.10.0.22 + Swap usage: 0% + + +Expanded Security Maintenance for Applications is not enabled. + +0 updates can be applied immediately. + +Enable ESM Apps to receive additional future security updates. +See https://ubuntu.com/esm or run: sudo pro status + + + +The programs included with the Ubuntu system are free software; +the exact distribution terms for each program are described in the +individual files in /usr/share/doc/*/copyright. + +Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by +applicable law. + +To run a command as administrator (user "root"), use "sudo ". +See "man sudo_root" for details. \ No newline at end of file diff --git a/lab04_proofs/terraform_result.txt b/lab04_proofs/terraform_result.txt new file mode 100644 index 0000000000..ca88584f03 --- /dev/null +++ b/lab04_proofs/terraform_result.txt @@ -0,0 +1,488 @@ +ubuntu@fhmdplo02ifil4mk7odj:~$ sudo apt update +Hit:1 http://mirror.yandex.ru/ubuntu noble InRelease +Get:2 http://mirror.yandex.ru/ubuntu noble-updates InRelease [126 kB] +Get:3 http://mirror.yandex.ru/ubuntu noble-backports InRelease [126 kB] +Get:4 http://security.ubuntu.com/ubuntu noble-security InRelease [126 kB] +Get:5 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 Packages [1771 kB] +Get:6 http://mirror.yandex.ru/ubuntu noble-updates/main Translation-en [328 kB] +Get:7 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 Components [175 kB] +Get:8 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 c-n-f Metadata [16.5 kB] +Get:9 http://mirror.yandex.ru/ubuntu noble-updates/universe amd64 Packages [1557 kB] +Get:10 http://mirror.yandex.ru/ubuntu noble-updates/universe Translation-en [316 kB] +Get:11 http://mirror.yandex.ru/ubuntu noble-updates/universe amd64 Components [386 kB] +Get:12 http://mirror.yandex.ru/ubuntu noble-updates/universe amd64 c-n-f Metadata [32.6 kB] +Get:13 http://mirror.yandex.ru/ubuntu noble-updates/restricted amd64 Packages [2663 kB] +Get:14 http://mirror.yandex.ru/ubuntu noble-updates/restricted Translation-en [613 kB] +Get:15 http://mirror.yandex.ru/ubuntu noble-updates/restricted amd64 Components [212 B] +Get:16 http://mirror.yandex.ru/ubuntu noble-updates/multiverse Translation-en [7044 B] +Get:17 http://mirror.yandex.ru/ubuntu noble-updates/multiverse amd64 Components [940 B] +Get:18 http://mirror.yandex.ru/ubuntu noble-backports/main amd64 Components [7308 B] +Get:19 http://mirror.yandex.ru/ubuntu noble-backports/universe amd64 Components [10.5 kB] +Get:20 http://mirror.yandex.ru/ubuntu noble-backports/restricted amd64 Components [216 B] +Get:21 http://mirror.yandex.ru/ubuntu noble-backports/multiverse amd64 Components [212 B] +Get:22 http://security.ubuntu.com/ubuntu noble-security/main amd64 Packages [1474 kB] +Get:23 http://security.ubuntu.com/ubuntu noble-security/main Translation-en [237 kB] +Get:24 http://security.ubuntu.com/ubuntu noble-security/main amd64 Components [21.5 kB] +Get:25 http://security.ubuntu.com/ubuntu noble-security/main amd64 c-n-f Metadata [9892 B] +Get:26 http://security.ubuntu.com/ubuntu noble-security/universe amd64 Packages [935 kB] +Get:27 http://security.ubuntu.com/ubuntu noble-security/universe Translation-en [214 kB] +Get:28 http://security.ubuntu.com/ubuntu noble-security/universe amd64 Components [74.2 kB] +Get:29 http://security.ubuntu.com/ubuntu noble-security/universe amd64 c-n-f Metadata [20.0 kB] +Get:30 http://security.ubuntu.com/ubuntu noble-security/restricted amd64 Packages [2516 kB] +Get:31 http://security.ubuntu.com/ubuntu noble-security/restricted Translation-en [582 kB] +Get:32 http://security.ubuntu.com/ubuntu noble-security/restricted amd64 Components [212 B] +Get:33 http://security.ubuntu.com/ubuntu noble-security/multiverse amd64 Components [212 B] +Fetched 14.3 MB in 3s (5184 kB/s) +Reading package lists... Done +Building dependency tree... Done +Reading state information... Done +8 packages can be upgraded. Run 'apt list --upgradable' to see them. +ubuntu@fhmdplo02ifil4mk7odj:~$ sudo apt install -y python3-venv python3-pip +Reading package lists... Done +Building dependency tree... Done +Reading state information... Done +The following additional packages will be installed: + binutils binutils-common binutils-x86-64-linux-gnu build-essential bzip2 cpp cpp-13 cpp-13-x86-64-linux-gnu + cpp-x86-64-linux-gnu dpkg-dev fakeroot g++ g++-13 g++-13-x86-64-linux-gnu g++-x86-64-linux-gnu gcc gcc-13 + gcc-13-base gcc-13-x86-64-linux-gnu gcc-14-base gcc-x86-64-linux-gnu javascript-common libalgorithm-diff-perl + libalgorithm-diff-xs-perl libalgorithm-merge-perl libasan8 libatomic1 libbinutils libcc1-0 libctf-nobfd0 + libctf0 libdpkg-perl libexpat1 libexpat1-dev libfakeroot libfile-fcntllock-perl libgcc-13-dev libgcc-s1 + libgomp1 libgprofng0 libhwasan0 libisl23 libitm1 libjs-jquery libjs-sphinxdoc libjs-underscore liblsan0 libmpc3 + libpython3-dev libpython3.12-dev libquadmath0 libsframe1 libstdc++-13-dev libstdc++6 libtsan2 libubsan1 + lto-disabled-list make python3-dev python3-pip-whl python3-setuptools-whl python3-wheel python3.12-dev + python3.12-venv zlib1g-dev +Suggested packages: + binutils-doc gprofng-gui bzip2-doc cpp-doc gcc-13-locales cpp-13-doc debian-keyring g++-multilib + g++-13-multilib gcc-13-doc gcc-multilib autoconf automake libtool flex bison gdb gcc-doc gcc-13-multilib + gdb-x86-64-linux-gnu apache2 | lighttpd | httpd bzr libstdc++-13-doc make-doc +The following NEW packages will be installed: + binutils binutils-common binutils-x86-64-linux-gnu build-essential bzip2 cpp cpp-13 cpp-13-x86-64-linux-gnu + cpp-x86-64-linux-gnu dpkg-dev fakeroot g++ g++-13 g++-13-x86-64-linux-gnu g++-x86-64-linux-gnu gcc gcc-13 + gcc-13-base gcc-13-x86-64-linux-gnu gcc-x86-64-linux-gnu javascript-common libalgorithm-diff-perl + libalgorithm-diff-xs-perl libalgorithm-merge-perl libasan8 libatomic1 libbinutils libcc1-0 libctf-nobfd0 + libctf0 libdpkg-perl libexpat1-dev libfakeroot libfile-fcntllock-perl libgcc-13-dev libgomp1 libgprofng0 + libhwasan0 libisl23 libitm1 libjs-jquery libjs-sphinxdoc libjs-underscore liblsan0 libmpc3 libpython3-dev + libpython3.12-dev libquadmath0 libsframe1 libstdc++-13-dev libtsan2 libubsan1 lto-disabled-list make + python3-dev python3-pip python3-pip-whl python3-setuptools-whl python3-venv python3-wheel python3.12-dev + python3.12-venv zlib1g-dev +The following packages will be upgraded: + gcc-14-base libexpat1 libgcc-s1 libstdc++6 +4 upgraded, 63 newly installed, 0 to remove and 4 not upgraded. +Need to get 79.5 MB of archives. +After this operation, 272 MB of additional disk space will be used. +Get:1 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 gcc-14-base amd64 14.2.0-4ubuntu2~24.04.1 [51.0 kB] +Get:2 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libstdc++6 amd64 14.2.0-4ubuntu2~24.04.1 [792 kB] +Get:3 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libgcc-s1 amd64 14.2.0-4ubuntu2~24.04.1 [78.4 kB] +Get:4 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libexpat1 amd64 2.6.1-2ubuntu0.4 [88.2 kB] +Get:5 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 binutils-common amd64 2.42-4ubuntu2.8 [240 kB] +Get:6 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libsframe1 amd64 2.42-4ubuntu2.8 [15.6 kB] +Get:7 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libbinutils amd64 2.42-4ubuntu2.8 [576 kB] +Get:8 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libctf-nobfd0 amd64 2.42-4ubuntu2.8 [97.9 kB] +Get:9 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libctf0 amd64 2.42-4ubuntu2.8 [94.5 kB] +Get:10 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libgprofng0 amd64 2.42-4ubuntu2.8 [849 kB] +Get:11 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 binutils-x86-64-linux-gnu amd64 2.42-4ubuntu2.8 [2463 kB] +Get:12 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 binutils amd64 2.42-4ubuntu2.8 [18.1 kB] +Get:13 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 gcc-13-base amd64 13.3.0-6ubuntu2~24.04.1 [51.6 kB] +Get:14 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libisl23 amd64 0.26-3build1.1 [680 kB] +Get:15 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libmpc3 amd64 1.3.1-1build1.1 [54.6 kB] +Get:16 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 cpp-13-x86-64-linux-gnu amd64 13.3.0-6ubuntu2~24.04.1 [10.7 MB] +Get:17 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 cpp-13 amd64 13.3.0-6ubuntu2~24.04.1 [1042 B] +Get:18 http://mirror.yandex.ru/ubuntu noble/main amd64 cpp-x86-64-linux-gnu amd64 4:13.2.0-7ubuntu1 [5326 B] +Get:19 http://mirror.yandex.ru/ubuntu noble/main amd64 cpp amd64 4:13.2.0-7ubuntu1 [22.4 kB] +Get:20 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libcc1-0 amd64 14.2.0-4ubuntu2~24.04.1 [48.0 kB] +Get:21 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libgomp1 amd64 14.2.0-4ubuntu2~24.04.1 [148 kB] +Get:22 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libitm1 amd64 14.2.0-4ubuntu2~24.04.1 [29.7 kB] +Get:23 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libatomic1 amd64 14.2.0-4ubuntu2~24.04.1 [10.5 kB] +Get:24 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libasan8 amd64 14.2.0-4ubuntu2~24.04.1 [3027 kB] +Get:25 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 liblsan0 amd64 14.2.0-4ubuntu2~24.04.1 [1322 kB] +Get:26 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libtsan2 amd64 14.2.0-4ubuntu2~24.04.1 [2772 kB] +Get:27 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libubsan1 amd64 14.2.0-4ubuntu2~24.04.1 [1184 kB] +Get:28 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libhwasan0 amd64 14.2.0-4ubuntu2~24.04.1 [1641 kB] +Get:29 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libquadmath0 amd64 14.2.0-4ubuntu2~24.04.1 [153 kB] +Get:30 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libgcc-13-dev amd64 13.3.0-6ubuntu2~24.04.1 [2681 kB] +Get:31 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 gcc-13-x86-64-linux-gnu amd64 13.3.0-6ubuntu2~24.04.1 [21.1 MB] +Get:32 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 gcc-13 amd64 13.3.0-6ubuntu2~24.04.1 [494 kB] +Get:33 http://mirror.yandex.ru/ubuntu noble/main amd64 gcc-x86-64-linux-gnu amd64 4:13.2.0-7ubuntu1 [1212 B] +Get:34 http://mirror.yandex.ru/ubuntu noble/main amd64 gcc amd64 4:13.2.0-7ubuntu1 [5018 B] +Get:35 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libstdc++-13-dev amd64 13.3.0-6ubuntu2~24.04.1 [2420 kB] +Get:36 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 g++-13-x86-64-linux-gnu amd64 13.3.0-6ubuntu2~24.04.1 [12.2 MB] +Get:37 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 g++-13 amd64 13.3.0-6ubuntu2~24.04.1 [16.0 kB] +Get:38 http://mirror.yandex.ru/ubuntu noble/main amd64 g++-x86-64-linux-gnu amd64 4:13.2.0-7ubuntu1 [964 B] +Get:39 http://mirror.yandex.ru/ubuntu noble/main amd64 g++ amd64 4:13.2.0-7ubuntu1 [1100 B] +Get:40 http://mirror.yandex.ru/ubuntu noble/main amd64 make amd64 4.3-4.1build2 [180 kB] +Get:41 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libdpkg-perl all 1.22.6ubuntu6.5 [269 kB] +Get:42 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 bzip2 amd64 1.0.8-5.1build0.1 [34.5 kB] +Get:43 http://mirror.yandex.ru/ubuntu noble/main amd64 lto-disabled-list all 47 [12.4 kB] +Get:44 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 dpkg-dev all 1.22.6ubuntu6.5 [1074 kB] +Get:45 http://mirror.yandex.ru/ubuntu noble/main amd64 build-essential amd64 12.10ubuntu1 [4928 B] +Get:46 http://mirror.yandex.ru/ubuntu noble/main amd64 libfakeroot amd64 1.33-1 [32.4 kB] +Get:47 http://mirror.yandex.ru/ubuntu noble/main amd64 fakeroot amd64 1.33-1 [67.2 kB] +Get:48 http://mirror.yandex.ru/ubuntu noble/main amd64 javascript-common all 11+nmu1 [5936 B] +Get:49 http://mirror.yandex.ru/ubuntu noble/main amd64 libalgorithm-diff-perl all 1.201-1 [41.8 kB] +Get:50 http://mirror.yandex.ru/ubuntu noble/main amd64 libalgorithm-diff-xs-perl amd64 0.04-8build3 [11.2 kB] +Get:51 http://mirror.yandex.ru/ubuntu noble/main amd64 libalgorithm-merge-perl all 0.08-5 [11.4 kB] +Get:52 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libexpat1-dev amd64 2.6.1-2ubuntu0.4 [140 kB] +Get:53 http://mirror.yandex.ru/ubuntu noble/main amd64 libfile-fcntllock-perl amd64 0.22-4ubuntu5 [30.7 kB] +Get:54 http://mirror.yandex.ru/ubuntu noble/main amd64 libjs-jquery all 3.6.1+dfsg+~3.5.14-1 [328 kB] +Get:55 http://mirror.yandex.ru/ubuntu noble/main amd64 libjs-underscore all 1.13.4~dfsg+~1.11.4-3 [118 kB] +Get:56 http://mirror.yandex.ru/ubuntu noble/main amd64 libjs-sphinxdoc all 7.2.6-6 [149 kB] +Get:57 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 zlib1g-dev amd64 1:1.3.dfsg-3.1ubuntu2.1 [894 kB] +Get:58 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libpython3.12-dev amd64 3.12.3-1ubuntu0.11 [5683 kB] +Get:59 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 libpython3-dev amd64 3.12.3-0ubuntu2.1 [10.3 kB] +Get:60 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 python3.12-dev amd64 3.12.3-1ubuntu0.11 [498 kB] +Get:61 http://mirror.yandex.ru/ubuntu noble-updates/main amd64 python3-dev amd64 3.12.3-0ubuntu2.1 [26.7 kB] +Get:62 http://mirror.yandex.ru/ubuntu noble/universe amd64 python3-wheel all 0.42.0-2 [53.1 kB] +Get:63 http://mirror.yandex.ru/ubuntu noble-updates/universe amd64 python3-pip all 24.0+dfsg-1ubuntu1.3 [1320 kB] +Get:64 http://mirror.yandex.ru/ubuntu noble-updates/universe amd64 python3-pip-whl all 24.0+dfsg-1ubuntu1.3 [1707 kB] +Get:65 http://mirror.yandex.ru/ubuntu noble-updates/universe amd64 python3-setuptools-whl all 68.1.2-2ubuntu1.2 [716 kB] +Get:66 http://mirror.yandex.ru/ubuntu noble-updates/universe amd64 python3.12-venv amd64 3.12.3-1ubuntu0.11 [5680 B] +Get:67 http://mirror.yandex.ru/ubuntu noble-updates/universe amd64 python3-venv amd64 3.12.3-0ubuntu2.1 [1032 B] +Fetched 79.5 MB in 1s (68.1 MB/s) +Extracting templates from packages: 100% +(Reading database ... 106316 files and directories currently installed.) +Preparing to unpack .../gcc-14-base_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking gcc-14-base:amd64 (14.2.0-4ubuntu2~24.04.1) over (14.2.0-4ubuntu2~24.04) ... +Setting up gcc-14-base:amd64 (14.2.0-4ubuntu2~24.04.1) ... +(Reading database ... 106316 files and directories currently installed.) +Preparing to unpack .../libstdc++6_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking libstdc++6:amd64 (14.2.0-4ubuntu2~24.04.1) over (14.2.0-4ubuntu2~24.04) ... +Setting up libstdc++6:amd64 (14.2.0-4ubuntu2~24.04.1) ... +(Reading database ... 106316 files and directories currently installed.) +Preparing to unpack .../libgcc-s1_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking libgcc-s1:amd64 (14.2.0-4ubuntu2~24.04.1) over (14.2.0-4ubuntu2~24.04) ... +Setting up libgcc-s1:amd64 (14.2.0-4ubuntu2~24.04.1) ... +(Reading database ... 106316 files and directories currently installed.) +Preparing to unpack .../00-libexpat1_2.6.1-2ubuntu0.4_amd64.deb ... +Unpacking libexpat1:amd64 (2.6.1-2ubuntu0.4) over (2.6.1-2ubuntu0.3) ... +Selecting previously unselected package binutils-common:amd64. +Preparing to unpack .../01-binutils-common_2.42-4ubuntu2.8_amd64.deb ... +Unpacking binutils-common:amd64 (2.42-4ubuntu2.8) ... +Selecting previously unselected package libsframe1:amd64. +Preparing to unpack .../02-libsframe1_2.42-4ubuntu2.8_amd64.deb ... +Unpacking libsframe1:amd64 (2.42-4ubuntu2.8) ... +Selecting previously unselected package libbinutils:amd64. +Preparing to unpack .../03-libbinutils_2.42-4ubuntu2.8_amd64.deb ... +Unpacking libbinutils:amd64 (2.42-4ubuntu2.8) ... +Selecting previously unselected package libctf-nobfd0:amd64. +Preparing to unpack .../04-libctf-nobfd0_2.42-4ubuntu2.8_amd64.deb ... +Unpacking libctf-nobfd0:amd64 (2.42-4ubuntu2.8) ... +Selecting previously unselected package libctf0:amd64. +Preparing to unpack .../05-libctf0_2.42-4ubuntu2.8_amd64.deb ... +Unpacking libctf0:amd64 (2.42-4ubuntu2.8) ... +Selecting previously unselected package libgprofng0:amd64. +Preparing to unpack .../06-libgprofng0_2.42-4ubuntu2.8_amd64.deb ... +Unpacking libgprofng0:amd64 (2.42-4ubuntu2.8) ... +Selecting previously unselected package binutils-x86-64-linux-gnu. +Preparing to unpack .../07-binutils-x86-64-linux-gnu_2.42-4ubuntu2.8_amd64.deb ... +Unpacking binutils-x86-64-linux-gnu (2.42-4ubuntu2.8) ... +Selecting previously unselected package binutils. +Preparing to unpack .../08-binutils_2.42-4ubuntu2.8_amd64.deb ... +Unpacking binutils (2.42-4ubuntu2.8) ... +Selecting previously unselected package gcc-13-base:amd64. +Preparing to unpack .../09-gcc-13-base_13.3.0-6ubuntu2~24.04.1_amd64.deb ... +Unpacking gcc-13-base:amd64 (13.3.0-6ubuntu2~24.04.1) ... +Selecting previously unselected package libisl23:amd64. +Preparing to unpack .../10-libisl23_0.26-3build1.1_amd64.deb ... +Unpacking libisl23:amd64 (0.26-3build1.1) ... +Selecting previously unselected package libmpc3:amd64. +Preparing to unpack .../11-libmpc3_1.3.1-1build1.1_amd64.deb ... +Unpacking libmpc3:amd64 (1.3.1-1build1.1) ... +Selecting previously unselected package cpp-13-x86-64-linux-gnu. +Preparing to unpack .../12-cpp-13-x86-64-linux-gnu_13.3.0-6ubuntu2~24.04.1_amd64.deb ... +Unpacking cpp-13-x86-64-linux-gnu (13.3.0-6ubuntu2~24.04.1) ... +Selecting previously unselected package cpp-13. +Preparing to unpack .../13-cpp-13_13.3.0-6ubuntu2~24.04.1_amd64.deb ... +Unpacking cpp-13 (13.3.0-6ubuntu2~24.04.1) ... +Selecting previously unselected package cpp-x86-64-linux-gnu. +Preparing to unpack .../14-cpp-x86-64-linux-gnu_4%3a13.2.0-7ubuntu1_amd64.deb ... +Unpacking cpp-x86-64-linux-gnu (4:13.2.0-7ubuntu1) ... +Selecting previously unselected package cpp. +Preparing to unpack .../15-cpp_4%3a13.2.0-7ubuntu1_amd64.deb ... +Unpacking cpp (4:13.2.0-7ubuntu1) ... +Selecting previously unselected package libcc1-0:amd64. +Preparing to unpack .../16-libcc1-0_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking libcc1-0:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Selecting previously unselected package libgomp1:amd64. +Preparing to unpack .../17-libgomp1_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking libgomp1:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Selecting previously unselected package libitm1:amd64. +Preparing to unpack .../18-libitm1_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking libitm1:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Selecting previously unselected package libatomic1:amd64. +Preparing to unpack .../19-libatomic1_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking libatomic1:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Selecting previously unselected package libasan8:amd64. +Preparing to unpack .../20-libasan8_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking libasan8:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Selecting previously unselected package liblsan0:amd64. +Preparing to unpack .../21-liblsan0_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking liblsan0:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Selecting previously unselected package libtsan2:amd64. +Preparing to unpack .../22-libtsan2_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking libtsan2:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Selecting previously unselected package libubsan1:amd64. +Preparing to unpack .../23-libubsan1_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking libubsan1:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Selecting previously unselected package libhwasan0:amd64. +Preparing to unpack .../24-libhwasan0_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking libhwasan0:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Selecting previously unselected package libquadmath0:amd64. +Preparing to unpack .../25-libquadmath0_14.2.0-4ubuntu2~24.04.1_amd64.deb ... +Unpacking libquadmath0:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Selecting previously unselected package libgcc-13-dev:amd64. +Preparing to unpack .../26-libgcc-13-dev_13.3.0-6ubuntu2~24.04.1_amd64.deb ... +Unpacking libgcc-13-dev:amd64 (13.3.0-6ubuntu2~24.04.1) ... +Selecting previously unselected package gcc-13-x86-64-linux-gnu. +Preparing to unpack .../27-gcc-13-x86-64-linux-gnu_13.3.0-6ubuntu2~24.04.1_amd64.deb ... +Unpacking gcc-13-x86-64-linux-gnu (13.3.0-6ubuntu2~24.04.1) ... +Selecting previously unselected package gcc-13. +Preparing to unpack .../28-gcc-13_13.3.0-6ubuntu2~24.04.1_amd64.deb ... +Unpacking gcc-13 (13.3.0-6ubuntu2~24.04.1) ... +Selecting previously unselected package gcc-x86-64-linux-gnu. +Preparing to unpack .../29-gcc-x86-64-linux-gnu_4%3a13.2.0-7ubuntu1_amd64.deb ... +Unpacking gcc-x86-64-linux-gnu (4:13.2.0-7ubuntu1) ... +Selecting previously unselected package gcc. +Preparing to unpack .../30-gcc_4%3a13.2.0-7ubuntu1_amd64.deb ... +Unpacking gcc (4:13.2.0-7ubuntu1) ... +Selecting previously unselected package libstdc++-13-dev:amd64. +Preparing to unpack .../31-libstdc++-13-dev_13.3.0-6ubuntu2~24.04.1_amd64.deb ... +Unpacking libstdc++-13-dev:amd64 (13.3.0-6ubuntu2~24.04.1) ... +Selecting previously unselected package g++-13-x86-64-linux-gnu. +Preparing to unpack .../32-g++-13-x86-64-linux-gnu_13.3.0-6ubuntu2~24.04.1_amd64.deb ... +Unpacking g++-13-x86-64-linux-gnu (13.3.0-6ubuntu2~24.04.1) ... +Selecting previously unselected package g++-13. +Preparing to unpack .../33-g++-13_13.3.0-6ubuntu2~24.04.1_amd64.deb ... +Unpacking g++-13 (13.3.0-6ubuntu2~24.04.1) ... +Selecting previously unselected package g++-x86-64-linux-gnu. +Preparing to unpack .../34-g++-x86-64-linux-gnu_4%3a13.2.0-7ubuntu1_amd64.deb ... +Unpacking g++-x86-64-linux-gnu (4:13.2.0-7ubuntu1) ... +Selecting previously unselected package g++. +Preparing to unpack .../35-g++_4%3a13.2.0-7ubuntu1_amd64.deb ... +Unpacking g++ (4:13.2.0-7ubuntu1) ... +Selecting previously unselected package make. +Preparing to unpack .../36-make_4.3-4.1build2_amd64.deb ... +Unpacking make (4.3-4.1build2) ... +Selecting previously unselected package libdpkg-perl. +Preparing to unpack .../37-libdpkg-perl_1.22.6ubuntu6.5_all.deb ... +Unpacking libdpkg-perl (1.22.6ubuntu6.5) ... +Selecting previously unselected package bzip2. +Preparing to unpack .../38-bzip2_1.0.8-5.1build0.1_amd64.deb ... +Unpacking bzip2 (1.0.8-5.1build0.1) ... +Selecting previously unselected package lto-disabled-list. +Preparing to unpack .../39-lto-disabled-list_47_all.deb ... +Unpacking lto-disabled-list (47) ... +Selecting previously unselected package dpkg-dev. +Preparing to unpack .../40-dpkg-dev_1.22.6ubuntu6.5_all.deb ... +Unpacking dpkg-dev (1.22.6ubuntu6.5) ... +Selecting previously unselected package build-essential. +Preparing to unpack .../41-build-essential_12.10ubuntu1_amd64.deb ... +Unpacking build-essential (12.10ubuntu1) ... +Selecting previously unselected package libfakeroot:amd64. +Preparing to unpack .../42-libfakeroot_1.33-1_amd64.deb ... +Unpacking libfakeroot:amd64 (1.33-1) ... +Selecting previously unselected package fakeroot. +Preparing to unpack .../43-fakeroot_1.33-1_amd64.deb ... +Unpacking fakeroot (1.33-1) ... +Selecting previously unselected package javascript-common. +Preparing to unpack .../44-javascript-common_11+nmu1_all.deb ... +Unpacking javascript-common (11+nmu1) ... +Selecting previously unselected package libalgorithm-diff-perl. +Preparing to unpack .../45-libalgorithm-diff-perl_1.201-1_all.deb ... +Unpacking libalgorithm-diff-perl (1.201-1) ... +Selecting previously unselected package libalgorithm-diff-xs-perl:amd64. +Preparing to unpack .../46-libalgorithm-diff-xs-perl_0.04-8build3_amd64.deb ... +Unpacking libalgorithm-diff-xs-perl:amd64 (0.04-8build3) ... +Selecting previously unselected package libalgorithm-merge-perl. +Preparing to unpack .../47-libalgorithm-merge-perl_0.08-5_all.deb ... +Unpacking libalgorithm-merge-perl (0.08-5) ... +Selecting previously unselected package libexpat1-dev:amd64. +Preparing to unpack .../48-libexpat1-dev_2.6.1-2ubuntu0.4_amd64.deb ... +Unpacking libexpat1-dev:amd64 (2.6.1-2ubuntu0.4) ... +Selecting previously unselected package libfile-fcntllock-perl. +Preparing to unpack .../49-libfile-fcntllock-perl_0.22-4ubuntu5_amd64.deb ... +Unpacking libfile-fcntllock-perl (0.22-4ubuntu5) ... +Selecting previously unselected package libjs-jquery. +Preparing to unpack .../50-libjs-jquery_3.6.1+dfsg+~3.5.14-1_all.deb ... +Unpacking libjs-jquery (3.6.1+dfsg+~3.5.14-1) ... +Selecting previously unselected package libjs-underscore. +Preparing to unpack .../51-libjs-underscore_1.13.4~dfsg+~1.11.4-3_all.deb ... +Unpacking libjs-underscore (1.13.4~dfsg+~1.11.4-3) ... +Selecting previously unselected package libjs-sphinxdoc. +Preparing to unpack .../52-libjs-sphinxdoc_7.2.6-6_all.deb ... +Unpacking libjs-sphinxdoc (7.2.6-6) ... +Selecting previously unselected package zlib1g-dev:amd64. +Preparing to unpack .../53-zlib1g-dev_1%3a1.3.dfsg-3.1ubuntu2.1_amd64.deb ... +Unpacking zlib1g-dev:amd64 (1:1.3.dfsg-3.1ubuntu2.1) ... +Selecting previously unselected package libpython3.12-dev:amd64. +Preparing to unpack .../54-libpython3.12-dev_3.12.3-1ubuntu0.11_amd64.deb ... +Unpacking libpython3.12-dev:amd64 (3.12.3-1ubuntu0.11) ... +Selecting previously unselected package libpython3-dev:amd64. +Preparing to unpack .../55-libpython3-dev_3.12.3-0ubuntu2.1_amd64.deb ... +Unpacking libpython3-dev:amd64 (3.12.3-0ubuntu2.1) ... +Selecting previously unselected package python3.12-dev. +Preparing to unpack .../56-python3.12-dev_3.12.3-1ubuntu0.11_amd64.deb ... +Unpacking python3.12-dev (3.12.3-1ubuntu0.11) ... +Selecting previously unselected package python3-dev. +Preparing to unpack .../57-python3-dev_3.12.3-0ubuntu2.1_amd64.deb ... +Unpacking python3-dev (3.12.3-0ubuntu2.1) ... +Selecting previously unselected package python3-wheel. +Preparing to unpack .../58-python3-wheel_0.42.0-2_all.deb ... +Unpacking python3-wheel (0.42.0-2) ... +Selecting previously unselected package python3-pip. +Preparing to unpack .../59-python3-pip_24.0+dfsg-1ubuntu1.3_all.deb ... +Unpacking python3-pip (24.0+dfsg-1ubuntu1.3) ... +Selecting previously unselected package python3-pip-whl. +Preparing to unpack .../60-python3-pip-whl_24.0+dfsg-1ubuntu1.3_all.deb ... +Unpacking python3-pip-whl (24.0+dfsg-1ubuntu1.3) ... +Selecting previously unselected package python3-setuptools-whl. +Preparing to unpack .../61-python3-setuptools-whl_68.1.2-2ubuntu1.2_all.deb ... +Unpacking python3-setuptools-whl (68.1.2-2ubuntu1.2) ... +Selecting previously unselected package python3.12-venv. +Preparing to unpack .../62-python3.12-venv_3.12.3-1ubuntu0.11_amd64.deb ... +Unpacking python3.12-venv (3.12.3-1ubuntu0.11) ... +Selecting previously unselected package python3-venv. +Preparing to unpack .../63-python3-venv_3.12.3-0ubuntu2.1_amd64.deb ... +Unpacking python3-venv (3.12.3-0ubuntu2.1) ... +Setting up libexpat1:amd64 (2.6.1-2ubuntu0.4) ... +Setting up javascript-common (11+nmu1) ... +Setting up python3-setuptools-whl (68.1.2-2ubuntu1.2) ... +Setting up lto-disabled-list (47) ... +Setting up libfile-fcntllock-perl (0.22-4ubuntu5) ... +Setting up python3-pip-whl (24.0+dfsg-1ubuntu1.3) ... +Setting up libalgorithm-diff-perl (1.201-1) ... +Setting up binutils-common:amd64 (2.42-4ubuntu2.8) ... +Setting up libctf-nobfd0:amd64 (2.42-4ubuntu2.8) ... +Setting up libgomp1:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Setting up bzip2 (1.0.8-5.1build0.1) ... +Setting up python3-wheel (0.42.0-2) ... +Setting up libsframe1:amd64 (2.42-4ubuntu2.8) ... +Setting up libfakeroot:amd64 (1.33-1) ... +Setting up fakeroot (1.33-1) ... +update-alternatives: using /usr/bin/fakeroot-sysv to provide /usr/bin/fakeroot (fakeroot) in auto mode +Setting up gcc-13-base:amd64 (13.3.0-6ubuntu2~24.04.1) ... +Setting up libexpat1-dev:amd64 (2.6.1-2ubuntu0.4) ... +Setting up make (4.3-4.1build2) ... +Setting up libquadmath0:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Setting up libmpc3:amd64 (1.3.1-1build1.1) ... +Setting up libatomic1:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Setting up python3-pip (24.0+dfsg-1ubuntu1.3) ... +Setting up libdpkg-perl (1.22.6ubuntu6.5) ... +Setting up libubsan1:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Setting up zlib1g-dev:amd64 (1:1.3.dfsg-3.1ubuntu2.1) ... +Setting up libhwasan0:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Setting up libasan8:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Setting up libtsan2:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Setting up libjs-jquery (3.6.1+dfsg+~3.5.14-1) ... +Setting up libbinutils:amd64 (2.42-4ubuntu2.8) ... +Setting up libisl23:amd64 (0.26-3build1.1) ... +Setting up libalgorithm-diff-xs-perl:amd64 (0.04-8build3) ... +Setting up libcc1-0:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Setting up liblsan0:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Setting up libitm1:amd64 (14.2.0-4ubuntu2~24.04.1) ... +Setting up libjs-underscore (1.13.4~dfsg+~1.11.4-3) ... +Setting up libalgorithm-merge-perl (0.08-5) ... +Setting up libctf0:amd64 (2.42-4ubuntu2.8) ... +Setting up python3.12-venv (3.12.3-1ubuntu0.11) ... +Setting up cpp-13-x86-64-linux-gnu (13.3.0-6ubuntu2~24.04.1) ... +Setting up libpython3.12-dev:amd64 (3.12.3-1ubuntu0.11) ... +Setting up libgprofng0:amd64 (2.42-4ubuntu2.8) ... +Setting up python3-venv (3.12.3-0ubuntu2.1) ... +Setting up python3.12-dev (3.12.3-1ubuntu0.11) ... +Setting up libjs-sphinxdoc (7.2.6-6) ... +Setting up libgcc-13-dev:amd64 (13.3.0-6ubuntu2~24.04.1) ... +Setting up libstdc++-13-dev:amd64 (13.3.0-6ubuntu2~24.04.1) ... +Setting up binutils-x86-64-linux-gnu (2.42-4ubuntu2.8) ... +Setting up cpp-x86-64-linux-gnu (4:13.2.0-7ubuntu1) ... +Setting up libpython3-dev:amd64 (3.12.3-0ubuntu2.1) ... +Setting up cpp-13 (13.3.0-6ubuntu2~24.04.1) ... +Setting up gcc-13-x86-64-linux-gnu (13.3.0-6ubuntu2~24.04.1) ... +Setting up binutils (2.42-4ubuntu2.8) ... +Setting up dpkg-dev (1.22.6ubuntu6.5) ... +Setting up python3-dev (3.12.3-0ubuntu2.1) ... +Setting up gcc-13 (13.3.0-6ubuntu2~24.04.1) ... +Setting up cpp (4:13.2.0-7ubuntu1) ... +Setting up g++-13-x86-64-linux-gnu (13.3.0-6ubuntu2~24.04.1) ... +Setting up gcc-x86-64-linux-gnu (4:13.2.0-7ubuntu1) ... +Setting up gcc (4:13.2.0-7ubuntu1) ... +Setting up g++-x86-64-linux-gnu (4:13.2.0-7ubuntu1) ... +Setting up g++-13 (13.3.0-6ubuntu2~24.04.1) ... +Setting up g++ (4:13.2.0-7ubuntu1) ... +update-alternatives: using /usr/bin/g++ to provide /usr/bin/c++ (c++) in auto mode +Setting up build-essential (12.10ubuntu1) ... +Processing triggers for man-db (2.12.0-4build2) ... +Processing triggers for libc-bin (2.39-0ubuntu8.7) ... +Scanning processes... +Scanning candidates... +Scanning linux images... + +Running kernel seems to be up-to-date. + +Restarting services... + systemctl restart multipathd.service packagekit.service polkit.service + +Service restarts being deferred: + /etc/needrestart/restart.d/dbus.service + systemctl restart unattended-upgrades.service + +No containers need to be restarted. + +User sessions running outdated binaries: + ubuntu @ session #1: apt[1405] + +No VM guests are running outdated hypervisor (qemu) binaries on this host. +ubuntu@fhmdplo02ifil4mk7odj:~$ sudo apt install -y python3-venv python3-pip +Reading package lists... Done +Building dependency tree... Done +Reading state information... Done +python3-venv is already the newest version (3.12.3-0ubuntu2.1). +python3-pip is already the newest version (24.0+dfsg-1ubuntu1.3). +0 upgraded, 0 newly installed, 0 to remove and 4 not upgraded. +ubuntu@fhmdplo02ifil4mk7odj:~$ mkdir -p ~/app && cd ~/app +ubuntu@fhmdplo02ifil4mk7odj:~/app$ cat > app.py <<'PY' +> from flask import Flask +> app = Flask(__name__) +> +> @app.get("/") +> def hello(): +> return "OK: lab04 VM is alive\n" +> PY +ubuntu@fhmdplo02ifil4mk7odj:~/app$ python3 -m venv venv +ubuntu@fhmdplo02ifil4mk7odj:~/app$ source venv/bin/activate +(venv) ubuntu@fhmdplo02ifil4mk7odj:~/app$ pip install flask +Collecting flask + Downloading flask-3.1.3-py3-none-any.whl.metadata (3.2 kB) +Collecting blinker>=1.9.0 (from flask) + Downloading blinker-1.9.0-py3-none-any.whl.metadata (1.6 kB) +Collecting click>=8.1.3 (from flask) + Downloading click-8.3.1-py3-none-any.whl.metadata (2.6 kB) +Collecting itsdangerous>=2.2.0 (from flask) + Downloading itsdangerous-2.2.0-py3-none-any.whl.metadata (1.9 kB) +Collecting jinja2>=3.1.2 (from flask) + Downloading jinja2-3.1.6-py3-none-any.whl.metadata (2.9 kB) +Collecting markupsafe>=2.1.1 (from flask) + Downloading markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (2.7 kB) +Collecting werkzeug>=3.1.0 (from flask) + Downloading werkzeug-3.1.6-py3-none-any.whl.metadata (4.0 kB) +Downloading flask-3.1.3-py3-none-any.whl (103 kB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 103.4/103.4 kB 1.8 MB/s eta 0:00:00 +Downloading blinker-1.9.0-py3-none-any.whl (8.5 kB) +Downloading click-8.3.1-py3-none-any.whl (108 kB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 108.3/108.3 kB 7.4 MB/s eta 0:00:00 +Downloading itsdangerous-2.2.0-py3-none-any.whl (16 kB) +Downloading jinja2-3.1.6-py3-none-any.whl (134 kB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 134.9/134.9 kB 8.5 MB/s eta 0:00:00 +Downloading markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (22 kB) +Downloading werkzeug-3.1.6-py3-none-any.whl (225 kB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 225.2/225.2 kB 11.1 MB/s eta 0:00:00 +Installing collected packages: markupsafe, itsdangerous, click, blinker, werkzeug, jinja2, flask +Successfully installed blinker-1.9.0 click-8.3.1 flask-3.1.3 itsdangerous-2.2.0 jinja2-3.1.6 markupsafe-3.0.3 werkzeug-3.1.6 +(venv) ubuntu@fhmdplo02ifil4mk7odj:~/app$ FLASK_APP=app.py flask run --host=0.0.0.0 --port=5000 + * Serving Flask app 'app.py' + * Debug mode: off +WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.10.0.22:5000 +Press CTRL+C to quit +95.111.204.70 - - [19/Feb/2026 19:33:15] "GET / HTTP/1.1" 200 - +95.111.204.70 - - [19/Feb/2026 19:33:17] "GET /favicon.ico HTTP/1.1" 404 - \ No newline at end of file diff --git a/provision_run1.log b/provision_run1.log new file mode 100644 index 0000000000..740f606508 --- /dev/null +++ b/provision_run1.log @@ -0,0 +1,60 @@ + +PLAY [Provision web servers] *************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [vagrant1] + +TASK [common : Update apt cache] *********************************************** +changed: [vagrant1] + +TASK [common : Install common packages] **************************************** +changed: [vagrant1] + +TASK [common : Set timezone] *************************************************** +skipping: [vagrant1] + +TASK [docker : Install prerequisites for Docker repository] ******************** +ok: [vagrant1] + +TASK [docker : Ensure /etc/apt/keyrings exists] ******************************** +ok: [vagrant1] + +TASK [docker : Download Docker GPG key (ASCII)] ******************************** +changed: [vagrant1] + +TASK [docker : Check if Docker keyring already exists] ************************* +ok: [vagrant1] + +TASK [docker : Convert (dearmor) Docker GPG key to keyring] ******************** +changed: [vagrant1] + +TASK [docker : Set correct permissions on Docker keyring] ********************** +ok: [vagrant1] + +TASK [docker : Set Docker APT architecture mapping] **************************** +ok: [vagrant1] + +TASK [docker : Add official Docker APT repository] ***************************** +changed: [vagrant1] + +TASK [docker : Install Docker Engine packages] ********************************* +changed: [vagrant1] + +TASK [docker : Ensure Docker service is enabled and running] ******************* +ok: [vagrant1] + +TASK [docker : Ensure docker group exists] ************************************* +ok: [vagrant1] + +TASK [docker : Add user to docker group] *************************************** +changed: [vagrant1] + +TASK [docker : Install Docker SDK for Python on target (for Ansible docker modules)] *** +changed: [vagrant1] + +RUNNING HANDLER [docker : restart docker] ************************************** +changed: [vagrant1] + +PLAY RECAP ********************************************************************* +vagrant1 : ok=17 changed=9 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 + diff --git a/provision_run2.log b/provision_run2.log new file mode 100644 index 0000000000..4a8768b0e9 --- /dev/null +++ b/provision_run2.log @@ -0,0 +1,57 @@ + +PLAY [Provision web servers] *************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [vagrant1] + +TASK [common : Update apt cache] *********************************************** +ok: [vagrant1] + +TASK [common : Install common packages] **************************************** +ok: [vagrant1] + +TASK [common : Set timezone] *************************************************** +skipping: [vagrant1] + +TASK [docker : Install prerequisites for Docker repository] ******************** +ok: [vagrant1] + +TASK [docker : Ensure /etc/apt/keyrings exists] ******************************** +ok: [vagrant1] + +TASK [docker : Download Docker GPG key (ASCII)] ******************************** +ok: [vagrant1] + +TASK [docker : Check if Docker keyring already exists] ************************* +ok: [vagrant1] + +TASK [docker : Convert (dearmor) Docker GPG key to keyring] ******************** +skipping: [vagrant1] + +TASK [docker : Set correct permissions on Docker keyring] ********************** +ok: [vagrant1] + +TASK [docker : Set Docker APT architecture mapping] **************************** +ok: [vagrant1] + +TASK [docker : Add official Docker APT repository] ***************************** +ok: [vagrant1] + +TASK [docker : Install Docker Engine packages] ********************************* +ok: [vagrant1] + +TASK [docker : Ensure Docker service is enabled and running] ******************* +ok: [vagrant1] + +TASK [docker : Ensure docker group exists] ************************************* +ok: [vagrant1] + +TASK [docker : Add user to docker group] *************************************** +ok: [vagrant1] + +TASK [docker : Install Docker SDK for Python on target (for Ansible docker modules)] *** +ok: [vagrant1] + +PLAY RECAP ********************************************************************* +vagrant1 : ok=15 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 + diff --git a/pulumi/.gitignore b/pulumi/.gitignore new file mode 100644 index 0000000000..4aff6b34cb --- /dev/null +++ b/pulumi/.gitignore @@ -0,0 +1,12 @@ +# Python +__pycache__/ +*.py[cod] +.venv/ +venv/ + +# Pulumi state/config +Pulumi.*.yaml + +# IDE +.vscode/ +.idea/ diff --git a/pulumi/.pulumi-state/.pulumi/backups/lab04-pulumi-yc/dev/dev.1771534421400792500.json b/pulumi/.pulumi-state/.pulumi/backups/lab04-pulumi-yc/dev/dev.1771534421400792500.json new file mode 100644 index 0000000000..b14f996692 --- /dev/null +++ b/pulumi/.pulumi-state/.pulumi/backups/lab04-pulumi-yc/dev/dev.1771534421400792500.json @@ -0,0 +1,427 @@ +{ + "version": 3, + "checkpoint": { + "stack": "organization/lab04-pulumi-yc/dev", + "latest": { + "manifest": { + "time": "2026-02-19T23:53:41.118824+03:00", + "magic": "7b54cd6e79f5cecd9ae124cb92b834b486c7e21993124e3a4e456c27c3ce48f9", + "version": "v3.222.0" + }, + "secrets_providers": { + "type": "passphrase", + "state": { + "salt": "v1:3yNf1Kmt4mA=:v1:Uk9In8z0RD2XJTza:lncnLY42u3UftJy+QfrDw6S2OT6vmQ==" + } + }, + "resources": [ + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0", + "custom": true, + "id": "7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "type": "pulumi:providers:yandex", + "inputs": { + "__internal": {}, + "version": "0.13.0" + }, + "outputs": { + "version": "0.13.0" + }, + "created": "2026-02-19T20:52:45.8287147Z", + "modified": "2026-02-19T20:52:45.8287147Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "custom": false, + "type": "pulumi:pulumi:Stack", + "outputs": { + "app_url": "http://89.169.135.154:5000/", + "http_url": "http://89.169.135.154/", + "internal_ip": "10.10.0.6", + "public_ip": "89.169.135.154", + "ssh_command": "ssh -i ~/.ssh/lab04_ed25519 ubuntu@89.169.135.154" + }, + "created": "2026-02-19T20:52:45.8630518Z", + "modified": "2026-02-19T20:52:45.8630518Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net", + "custom": true, + "id": "enpet9l87ashn294afa1", + "type": "yandex:index/vpcNetwork:VpcNetwork", + "inputs": { + "__defaults": [ + "name" + ], + "name": "lab04-net-7bf49ed" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":60000000000,\"delete\":60000000000,\"update\":60000000000}}", + "createdAt": "2026-02-19T20:52:48Z", + "defaultSecurityGroupId": "enp912ofg69vrp8iegur", + "description": "", + "folderId": "b1g82kdcn5grlmu79ano", + "id": "enpet9l87ashn294afa1", + "labels": {}, + "name": "lab04-net-7bf49ed", + "subnetIds": [] + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "created": "2026-02-19T20:52:49.994421Z", + "modified": "2026-02-19T20:52:49.994421Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSubnet:VpcSubnet::lab04-subnet", + "custom": true, + "id": "e9b3hl3h09t6drs68pbg", + "type": "yandex:index/vpcSubnet:VpcSubnet", + "inputs": { + "__defaults": [ + "name" + ], + "name": "lab04-subnet-1fa918f", + "networkId": "enpet9l87ashn294afa1", + "v4CidrBlocks": [ + "10.10.0.0/24" + ], + "zone": "ru-central1-a" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":180000000000,\"delete\":180000000000,\"update\":180000000000}}", + "createdAt": "2026-02-19T20:52:50Z", + "description": "", + "dhcpOptions": null, + "folderId": "b1g82kdcn5grlmu79ano", + "id": "e9b3hl3h09t6drs68pbg", + "labels": {}, + "name": "lab04-subnet-1fa918f", + "networkId": "enpet9l87ashn294afa1", + "routeTableId": "", + "v4CidrBlocks": [ + "10.10.0.0/24" + ], + "v6CidrBlocks": [], + "zone": "ru-central1-a" + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "dependencies": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ], + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "propertyDependencies": { + "networkId": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ], + "v4CidrBlocks": [], + "zone": [] + }, + "created": "2026-02-19T20:52:50.5480973Z", + "modified": "2026-02-19T20:52:50.5480973Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSecurityGroup:VpcSecurityGroup::lab04-sg", + "custom": true, + "id": "enpb8heveqi8pp7mvbcv", + "type": "yandex:index/vpcSecurityGroup:VpcSecurityGroup", + "inputs": { + "__defaults": [ + "name" + ], + "egresses": [ + { + "__defaults": [ + "port" + ], + "description": "Allow all outbound", + "fromPort": 0, + "port": -1, + "protocol": "ANY", + "toPort": 65535, + "v4CidrBlocks": [ + "0.0.0.0/0" + ] + } + ], + "ingresses": [ + { + "__defaults": [ + "fromPort", + "toPort" + ], + "description": "SSH", + "fromPort": -1, + "port": 22, + "protocol": "TCP", + "toPort": -1, + "v4CidrBlocks": [ + "95.111.204.70/32" + ] + }, + { + "__defaults": [ + "fromPort", + "toPort" + ], + "description": "HTTP", + "fromPort": -1, + "port": 80, + "protocol": "TCP", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ] + }, + { + "__defaults": [ + "fromPort", + "toPort" + ], + "description": "App 5000", + "fromPort": -1, + "port": 5000, + "protocol": "TCP", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ] + } + ], + "name": "lab04-sg-7fdbda7", + "networkId": "enpet9l87ashn294afa1" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":180000000000,\"delete\":180000000000,\"update\":180000000000}}", + "createdAt": "2026-02-19T20:52:52Z", + "description": "", + "egresses": [ + { + "description": "Allow all outbound", + "fromPort": 0, + "id": "enpqsfspqfq9fqiee48t", + "labels": {}, + "port": -1, + "predefinedTarget": "", + "protocol": "ANY", + "securityGroupId": "", + "toPort": 65535, + "v4CidrBlocks": [ + "0.0.0.0/0" + ], + "v6CidrBlocks": [] + } + ], + "folderId": "b1g82kdcn5grlmu79ano", + "id": "enpb8heveqi8pp7mvbcv", + "ingresses": [ + { + "description": "SSH", + "fromPort": -1, + "id": "enpbhntafog7fraba6rd", + "labels": {}, + "port": 22, + "predefinedTarget": "", + "protocol": "TCP", + "securityGroupId": "", + "toPort": -1, + "v4CidrBlocks": [ + "95.111.204.70/32" + ], + "v6CidrBlocks": [] + }, + { + "description": "HTTP", + "fromPort": -1, + "id": "enp80blit93rhb8rh3eb", + "labels": {}, + "port": 80, + "predefinedTarget": "", + "protocol": "TCP", + "securityGroupId": "", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ], + "v6CidrBlocks": [] + }, + { + "description": "App 5000", + "fromPort": -1, + "id": "enp39asi999eg5hhq4o1", + "labels": {}, + "port": 5000, + "predefinedTarget": "", + "protocol": "TCP", + "securityGroupId": "", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ], + "v6CidrBlocks": [] + } + ], + "labels": {}, + "name": "lab04-sg-7fdbda7", + "networkId": "enpet9l87ashn294afa1", + "status": "ACTIVE" + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "dependencies": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ], + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "propertyDependencies": { + "egresses": [], + "ingresses": [], + "networkId": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ] + }, + "created": "2026-02-19T20:52:52.5246052Z", + "modified": "2026-02-19T20:52:52.5246052Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/computeInstance:ComputeInstance::lab04-vm", + "custom": true, + "id": "fhm7ed1a5b85rs8u3uop", + "type": "yandex:index/computeInstance:ComputeInstance", + "inputs": { + "__defaults": [ + "name", + "networkAccelerationType" + ], + "allowStoppingForUpdate": true, + "bootDisk": { + "__defaults": [ + "autoDelete" + ], + "autoDelete": true, + "initializeParams": { + "__defaults": [], + "imageId": "fd8lt661chfo5i13a40d", + "size": 10, + "type": "network-hdd" + } + }, + "metadata": { + "__defaults": [], + "ssh-keys": "ubuntu:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5nKglXX/E2/S3oPKXahGr77IZELnnZlqE4oGCyEhZQ Данил@Dorley" + }, + "name": "lab04-vm-6023a00", + "networkAccelerationType": "standard", + "networkInterfaces": [ + { + "__defaults": [ + "ipv4" + ], + "ipv4": true, + "nat": true, + "securityGroupIds": [ + "enpb8heveqi8pp7mvbcv" + ], + "subnetId": "e9b3hl3h09t6drs68pbg" + } + ], + "platformId": "standard-v2", + "resources": { + "__defaults": [], + "coreFraction": 20, + "cores": 2, + "memory": 1 + }, + "zone": "ru-central1-a" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":300000000000,\"delete\":300000000000,\"update\":300000000000},\"schema_version\":\"1\"}", + "allowStoppingForUpdate": true, + "bootDisk": { + "autoDelete": true, + "deviceName": "fhmonvdcv96scusbdsm3", + "diskId": "fhmonvdcv96scusbdsm3", + "initializeParams": { + "blockSize": 4096, + "description": "", + "imageId": "fd8lt661chfo5i13a40d", + "name": "", + "size": 10, + "snapshotId": "", + "type": "network-hdd" + }, + "mode": "READ_WRITE" + }, + "createdAt": "2026-02-19T20:52:53Z", + "description": "", + "folderId": "b1g82kdcn5grlmu79ano", + "fqdn": "fhm7ed1a5b85rs8u3uop.auto.internal", + "hostname": "fhm7ed1a5b85rs8u3uop", + "id": "fhm7ed1a5b85rs8u3uop", + "labels": {}, + "metadata": { + "ssh-keys": "ubuntu:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5nKglXX/E2/S3oPKXahGr77IZELnnZlqE4oGCyEhZQ Данил@Dorley" + }, + "name": "lab04-vm-6023a00", + "networkAccelerationType": "standard", + "networkInterfaces": [ + { + "dnsRecords": [], + "index": 0, + "ipAddress": "10.10.0.6", + "ipv4": true, + "ipv6": false, + "ipv6Address": "", + "ipv6DnsRecords": [], + "macAddress": "d0:0d:77:34:2a:2a", + "nat": true, + "natDnsRecords": [], + "natIpAddress": "89.169.135.154", + "natIpVersion": "IPV4", + "securityGroupIds": [ + "enpb8heveqi8pp7mvbcv" + ], + "subnetId": "e9b3hl3h09t6drs68pbg" + } + ], + "placementPolicy": { + "placementGroupId": "" + }, + "platformId": "standard-v2", + "resources": { + "coreFraction": 20, + "cores": 2, + "gpus": 0, + "memory": 1 + }, + "schedulingPolicy": { + "preemptible": false + }, + "secondaryDisks": [], + "serviceAccountId": "", + "status": "running", + "zone": "ru-central1-a" + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "dependencies": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSecurityGroup:VpcSecurityGroup::lab04-sg", + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSubnet:VpcSubnet::lab04-subnet" + ], + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "propertyDependencies": { + "allowStoppingForUpdate": [], + "bootDisk": [], + "metadata": [], + "networkInterfaces": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSecurityGroup:VpcSecurityGroup::lab04-sg", + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSubnet:VpcSubnet::lab04-subnet" + ], + "platformId": [], + "resources": [], + "zone": [] + }, + "created": "2026-02-19T20:53:41.0764236Z", + "modified": "2026-02-19T20:53:41.0764236Z" + } + ], + "metadata": {} + } + } +} diff --git a/pulumi/.pulumi-state/.pulumi/backups/lab04-pulumi-yc/dev/dev.1771534421400792500.json.attrs b/pulumi/.pulumi-state/.pulumi/backups/lab04-pulumi-yc/dev/dev.1771534421400792500.json.attrs new file mode 100644 index 0000000000..4dbba8b4a3 --- /dev/null +++ b/pulumi/.pulumi-state/.pulumi/backups/lab04-pulumi-yc/dev/dev.1771534421400792500.json.attrs @@ -0,0 +1 @@ +{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":null,"md5":"8aVExZ8N8dRKSmZc6/Q9ng=="} diff --git a/pulumi/.pulumi-state/.pulumi/history/lab04-pulumi-yc/dev/dev-1771534421389384600.checkpoint.json b/pulumi/.pulumi-state/.pulumi/history/lab04-pulumi-yc/dev/dev-1771534421389384600.checkpoint.json new file mode 100644 index 0000000000..b14f996692 --- /dev/null +++ b/pulumi/.pulumi-state/.pulumi/history/lab04-pulumi-yc/dev/dev-1771534421389384600.checkpoint.json @@ -0,0 +1,427 @@ +{ + "version": 3, + "checkpoint": { + "stack": "organization/lab04-pulumi-yc/dev", + "latest": { + "manifest": { + "time": "2026-02-19T23:53:41.118824+03:00", + "magic": "7b54cd6e79f5cecd9ae124cb92b834b486c7e21993124e3a4e456c27c3ce48f9", + "version": "v3.222.0" + }, + "secrets_providers": { + "type": "passphrase", + "state": { + "salt": "v1:3yNf1Kmt4mA=:v1:Uk9In8z0RD2XJTza:lncnLY42u3UftJy+QfrDw6S2OT6vmQ==" + } + }, + "resources": [ + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0", + "custom": true, + "id": "7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "type": "pulumi:providers:yandex", + "inputs": { + "__internal": {}, + "version": "0.13.0" + }, + "outputs": { + "version": "0.13.0" + }, + "created": "2026-02-19T20:52:45.8287147Z", + "modified": "2026-02-19T20:52:45.8287147Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "custom": false, + "type": "pulumi:pulumi:Stack", + "outputs": { + "app_url": "http://89.169.135.154:5000/", + "http_url": "http://89.169.135.154/", + "internal_ip": "10.10.0.6", + "public_ip": "89.169.135.154", + "ssh_command": "ssh -i ~/.ssh/lab04_ed25519 ubuntu@89.169.135.154" + }, + "created": "2026-02-19T20:52:45.8630518Z", + "modified": "2026-02-19T20:52:45.8630518Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net", + "custom": true, + "id": "enpet9l87ashn294afa1", + "type": "yandex:index/vpcNetwork:VpcNetwork", + "inputs": { + "__defaults": [ + "name" + ], + "name": "lab04-net-7bf49ed" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":60000000000,\"delete\":60000000000,\"update\":60000000000}}", + "createdAt": "2026-02-19T20:52:48Z", + "defaultSecurityGroupId": "enp912ofg69vrp8iegur", + "description": "", + "folderId": "b1g82kdcn5grlmu79ano", + "id": "enpet9l87ashn294afa1", + "labels": {}, + "name": "lab04-net-7bf49ed", + "subnetIds": [] + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "created": "2026-02-19T20:52:49.994421Z", + "modified": "2026-02-19T20:52:49.994421Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSubnet:VpcSubnet::lab04-subnet", + "custom": true, + "id": "e9b3hl3h09t6drs68pbg", + "type": "yandex:index/vpcSubnet:VpcSubnet", + "inputs": { + "__defaults": [ + "name" + ], + "name": "lab04-subnet-1fa918f", + "networkId": "enpet9l87ashn294afa1", + "v4CidrBlocks": [ + "10.10.0.0/24" + ], + "zone": "ru-central1-a" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":180000000000,\"delete\":180000000000,\"update\":180000000000}}", + "createdAt": "2026-02-19T20:52:50Z", + "description": "", + "dhcpOptions": null, + "folderId": "b1g82kdcn5grlmu79ano", + "id": "e9b3hl3h09t6drs68pbg", + "labels": {}, + "name": "lab04-subnet-1fa918f", + "networkId": "enpet9l87ashn294afa1", + "routeTableId": "", + "v4CidrBlocks": [ + "10.10.0.0/24" + ], + "v6CidrBlocks": [], + "zone": "ru-central1-a" + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "dependencies": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ], + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "propertyDependencies": { + "networkId": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ], + "v4CidrBlocks": [], + "zone": [] + }, + "created": "2026-02-19T20:52:50.5480973Z", + "modified": "2026-02-19T20:52:50.5480973Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSecurityGroup:VpcSecurityGroup::lab04-sg", + "custom": true, + "id": "enpb8heveqi8pp7mvbcv", + "type": "yandex:index/vpcSecurityGroup:VpcSecurityGroup", + "inputs": { + "__defaults": [ + "name" + ], + "egresses": [ + { + "__defaults": [ + "port" + ], + "description": "Allow all outbound", + "fromPort": 0, + "port": -1, + "protocol": "ANY", + "toPort": 65535, + "v4CidrBlocks": [ + "0.0.0.0/0" + ] + } + ], + "ingresses": [ + { + "__defaults": [ + "fromPort", + "toPort" + ], + "description": "SSH", + "fromPort": -1, + "port": 22, + "protocol": "TCP", + "toPort": -1, + "v4CidrBlocks": [ + "95.111.204.70/32" + ] + }, + { + "__defaults": [ + "fromPort", + "toPort" + ], + "description": "HTTP", + "fromPort": -1, + "port": 80, + "protocol": "TCP", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ] + }, + { + "__defaults": [ + "fromPort", + "toPort" + ], + "description": "App 5000", + "fromPort": -1, + "port": 5000, + "protocol": "TCP", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ] + } + ], + "name": "lab04-sg-7fdbda7", + "networkId": "enpet9l87ashn294afa1" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":180000000000,\"delete\":180000000000,\"update\":180000000000}}", + "createdAt": "2026-02-19T20:52:52Z", + "description": "", + "egresses": [ + { + "description": "Allow all outbound", + "fromPort": 0, + "id": "enpqsfspqfq9fqiee48t", + "labels": {}, + "port": -1, + "predefinedTarget": "", + "protocol": "ANY", + "securityGroupId": "", + "toPort": 65535, + "v4CidrBlocks": [ + "0.0.0.0/0" + ], + "v6CidrBlocks": [] + } + ], + "folderId": "b1g82kdcn5grlmu79ano", + "id": "enpb8heveqi8pp7mvbcv", + "ingresses": [ + { + "description": "SSH", + "fromPort": -1, + "id": "enpbhntafog7fraba6rd", + "labels": {}, + "port": 22, + "predefinedTarget": "", + "protocol": "TCP", + "securityGroupId": "", + "toPort": -1, + "v4CidrBlocks": [ + "95.111.204.70/32" + ], + "v6CidrBlocks": [] + }, + { + "description": "HTTP", + "fromPort": -1, + "id": "enp80blit93rhb8rh3eb", + "labels": {}, + "port": 80, + "predefinedTarget": "", + "protocol": "TCP", + "securityGroupId": "", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ], + "v6CidrBlocks": [] + }, + { + "description": "App 5000", + "fromPort": -1, + "id": "enp39asi999eg5hhq4o1", + "labels": {}, + "port": 5000, + "predefinedTarget": "", + "protocol": "TCP", + "securityGroupId": "", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ], + "v6CidrBlocks": [] + } + ], + "labels": {}, + "name": "lab04-sg-7fdbda7", + "networkId": "enpet9l87ashn294afa1", + "status": "ACTIVE" + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "dependencies": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ], + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "propertyDependencies": { + "egresses": [], + "ingresses": [], + "networkId": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ] + }, + "created": "2026-02-19T20:52:52.5246052Z", + "modified": "2026-02-19T20:52:52.5246052Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/computeInstance:ComputeInstance::lab04-vm", + "custom": true, + "id": "fhm7ed1a5b85rs8u3uop", + "type": "yandex:index/computeInstance:ComputeInstance", + "inputs": { + "__defaults": [ + "name", + "networkAccelerationType" + ], + "allowStoppingForUpdate": true, + "bootDisk": { + "__defaults": [ + "autoDelete" + ], + "autoDelete": true, + "initializeParams": { + "__defaults": [], + "imageId": "fd8lt661chfo5i13a40d", + "size": 10, + "type": "network-hdd" + } + }, + "metadata": { + "__defaults": [], + "ssh-keys": "ubuntu:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5nKglXX/E2/S3oPKXahGr77IZELnnZlqE4oGCyEhZQ Данил@Dorley" + }, + "name": "lab04-vm-6023a00", + "networkAccelerationType": "standard", + "networkInterfaces": [ + { + "__defaults": [ + "ipv4" + ], + "ipv4": true, + "nat": true, + "securityGroupIds": [ + "enpb8heveqi8pp7mvbcv" + ], + "subnetId": "e9b3hl3h09t6drs68pbg" + } + ], + "platformId": "standard-v2", + "resources": { + "__defaults": [], + "coreFraction": 20, + "cores": 2, + "memory": 1 + }, + "zone": "ru-central1-a" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":300000000000,\"delete\":300000000000,\"update\":300000000000},\"schema_version\":\"1\"}", + "allowStoppingForUpdate": true, + "bootDisk": { + "autoDelete": true, + "deviceName": "fhmonvdcv96scusbdsm3", + "diskId": "fhmonvdcv96scusbdsm3", + "initializeParams": { + "blockSize": 4096, + "description": "", + "imageId": "fd8lt661chfo5i13a40d", + "name": "", + "size": 10, + "snapshotId": "", + "type": "network-hdd" + }, + "mode": "READ_WRITE" + }, + "createdAt": "2026-02-19T20:52:53Z", + "description": "", + "folderId": "b1g82kdcn5grlmu79ano", + "fqdn": "fhm7ed1a5b85rs8u3uop.auto.internal", + "hostname": "fhm7ed1a5b85rs8u3uop", + "id": "fhm7ed1a5b85rs8u3uop", + "labels": {}, + "metadata": { + "ssh-keys": "ubuntu:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5nKglXX/E2/S3oPKXahGr77IZELnnZlqE4oGCyEhZQ Данил@Dorley" + }, + "name": "lab04-vm-6023a00", + "networkAccelerationType": "standard", + "networkInterfaces": [ + { + "dnsRecords": [], + "index": 0, + "ipAddress": "10.10.0.6", + "ipv4": true, + "ipv6": false, + "ipv6Address": "", + "ipv6DnsRecords": [], + "macAddress": "d0:0d:77:34:2a:2a", + "nat": true, + "natDnsRecords": [], + "natIpAddress": "89.169.135.154", + "natIpVersion": "IPV4", + "securityGroupIds": [ + "enpb8heveqi8pp7mvbcv" + ], + "subnetId": "e9b3hl3h09t6drs68pbg" + } + ], + "placementPolicy": { + "placementGroupId": "" + }, + "platformId": "standard-v2", + "resources": { + "coreFraction": 20, + "cores": 2, + "gpus": 0, + "memory": 1 + }, + "schedulingPolicy": { + "preemptible": false + }, + "secondaryDisks": [], + "serviceAccountId": "", + "status": "running", + "zone": "ru-central1-a" + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "dependencies": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSecurityGroup:VpcSecurityGroup::lab04-sg", + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSubnet:VpcSubnet::lab04-subnet" + ], + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "propertyDependencies": { + "allowStoppingForUpdate": [], + "bootDisk": [], + "metadata": [], + "networkInterfaces": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSecurityGroup:VpcSecurityGroup::lab04-sg", + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSubnet:VpcSubnet::lab04-subnet" + ], + "platformId": [], + "resources": [], + "zone": [] + }, + "created": "2026-02-19T20:53:41.0764236Z", + "modified": "2026-02-19T20:53:41.0764236Z" + } + ], + "metadata": {} + } + } +} diff --git a/pulumi/.pulumi-state/.pulumi/history/lab04-pulumi-yc/dev/dev-1771534421389384600.checkpoint.json.attrs b/pulumi/.pulumi-state/.pulumi/history/lab04-pulumi-yc/dev/dev-1771534421389384600.checkpoint.json.attrs new file mode 100644 index 0000000000..4dbba8b4a3 --- /dev/null +++ b/pulumi/.pulumi-state/.pulumi/history/lab04-pulumi-yc/dev/dev-1771534421389384600.checkpoint.json.attrs @@ -0,0 +1 @@ +{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":null,"md5":"8aVExZ8N8dRKSmZc6/Q9ng=="} diff --git a/pulumi/.pulumi-state/.pulumi/history/lab04-pulumi-yc/dev/dev-1771534421389384600.history.json b/pulumi/.pulumi-state/.pulumi/history/lab04-pulumi-yc/dev/dev-1771534421389384600.history.json new file mode 100644 index 0000000000..aeb75b84a2 --- /dev/null +++ b/pulumi/.pulumi-state/.pulumi/history/lab04-pulumi-yc/dev/dev-1771534421389384600.history.json @@ -0,0 +1,44 @@ +{ + "kind": "update", + "startTime": 1771534364, + "message": "feat: added terraform lab part", + "environment": { + "exec.kind": "cli", + "git.author": "dorley174", + "git.author.email": "d.valiev@innopolis.university", + "git.committer": "dorley174", + "git.committer.email": "d.valiev@innopolis.university", + "git.dirty": "true", + "git.head": "46866051a9837ae9503ab3f8665d0317ce29e2dc", + "git.headName": "refs/heads/lab04", + "pulumi.arch": "amd64", + "pulumi.os": "windows", + "pulumi.version": "v3.222.0", + "runtime.executable": "C:\\DevOps\\DevOps-Core-Course\\pulumi\\venv\\Scripts\\python.exe", + "runtime.metadata.toolchain": "Pip", + "runtime.metadata.toolchainVersion": "26.0.1", + "runtime.metadata.typechecker": "None", + "runtime.name": "python", + "runtime.version": "3.13.1", + "stack.environments": "[]", + "updatePlan": "false", + "vcs.kind": "github.com", + "vcs.owner": "dorley174", + "vcs.repo": "DevOps-Core-Course", + "vcs.root": "pulumi" + }, + "config": { + "lab04-pulumi-yc:allowedSshCidr": "95.111.204.70/32", + "lab04-pulumi-yc:imageFamily": "ubuntu-2404-lts", + "lab04-pulumi-yc:sshPublicKeyPath": "~/.ssh/lab04_ed25519.pub", + "lab04-pulumi-yc:sshUser": "ubuntu", + "lab04-pulumi-yc:subnetCidr": "10.10.0.0/24", + "lab04-pulumi-yc:zone": "ru-central1-a" + }, + "version": 0, + "result": "succeeded", + "endTime": 1771534421, + "resourceChanges": { + "create": 5 + } +} diff --git a/pulumi/.pulumi-state/.pulumi/history/lab04-pulumi-yc/dev/dev-1771534421389384600.history.json.attrs b/pulumi/.pulumi-state/.pulumi/history/lab04-pulumi-yc/dev/dev-1771534421389384600.history.json.attrs new file mode 100644 index 0000000000..9921a06eaa --- /dev/null +++ b/pulumi/.pulumi-state/.pulumi/history/lab04-pulumi-yc/dev/dev-1771534421389384600.history.json.attrs @@ -0,0 +1 @@ +{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":null,"md5":"ff0qIY8bvz4izQQb8WO+fA=="} diff --git a/pulumi/.pulumi-state/.pulumi/meta.yaml b/pulumi/.pulumi-state/.pulumi/meta.yaml new file mode 100644 index 0000000000..b82551848c --- /dev/null +++ b/pulumi/.pulumi-state/.pulumi/meta.yaml @@ -0,0 +1 @@ +version: 1 diff --git a/pulumi/.pulumi-state/.pulumi/meta.yaml.attrs b/pulumi/.pulumi-state/.pulumi/meta.yaml.attrs new file mode 100644 index 0000000000..4466031b30 --- /dev/null +++ b/pulumi/.pulumi-state/.pulumi/meta.yaml.attrs @@ -0,0 +1 @@ +{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":null,"md5":"EaRWdV65+nlqCnYlI4a4Wg=="} diff --git a/pulumi/.pulumi-state/.pulumi/stacks/lab04-pulumi-yc/dev.json b/pulumi/.pulumi-state/.pulumi/stacks/lab04-pulumi-yc/dev.json new file mode 100644 index 0000000000..b14f996692 --- /dev/null +++ b/pulumi/.pulumi-state/.pulumi/stacks/lab04-pulumi-yc/dev.json @@ -0,0 +1,427 @@ +{ + "version": 3, + "checkpoint": { + "stack": "organization/lab04-pulumi-yc/dev", + "latest": { + "manifest": { + "time": "2026-02-19T23:53:41.118824+03:00", + "magic": "7b54cd6e79f5cecd9ae124cb92b834b486c7e21993124e3a4e456c27c3ce48f9", + "version": "v3.222.0" + }, + "secrets_providers": { + "type": "passphrase", + "state": { + "salt": "v1:3yNf1Kmt4mA=:v1:Uk9In8z0RD2XJTza:lncnLY42u3UftJy+QfrDw6S2OT6vmQ==" + } + }, + "resources": [ + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0", + "custom": true, + "id": "7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "type": "pulumi:providers:yandex", + "inputs": { + "__internal": {}, + "version": "0.13.0" + }, + "outputs": { + "version": "0.13.0" + }, + "created": "2026-02-19T20:52:45.8287147Z", + "modified": "2026-02-19T20:52:45.8287147Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "custom": false, + "type": "pulumi:pulumi:Stack", + "outputs": { + "app_url": "http://89.169.135.154:5000/", + "http_url": "http://89.169.135.154/", + "internal_ip": "10.10.0.6", + "public_ip": "89.169.135.154", + "ssh_command": "ssh -i ~/.ssh/lab04_ed25519 ubuntu@89.169.135.154" + }, + "created": "2026-02-19T20:52:45.8630518Z", + "modified": "2026-02-19T20:52:45.8630518Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net", + "custom": true, + "id": "enpet9l87ashn294afa1", + "type": "yandex:index/vpcNetwork:VpcNetwork", + "inputs": { + "__defaults": [ + "name" + ], + "name": "lab04-net-7bf49ed" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":60000000000,\"delete\":60000000000,\"update\":60000000000}}", + "createdAt": "2026-02-19T20:52:48Z", + "defaultSecurityGroupId": "enp912ofg69vrp8iegur", + "description": "", + "folderId": "b1g82kdcn5grlmu79ano", + "id": "enpet9l87ashn294afa1", + "labels": {}, + "name": "lab04-net-7bf49ed", + "subnetIds": [] + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "created": "2026-02-19T20:52:49.994421Z", + "modified": "2026-02-19T20:52:49.994421Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSubnet:VpcSubnet::lab04-subnet", + "custom": true, + "id": "e9b3hl3h09t6drs68pbg", + "type": "yandex:index/vpcSubnet:VpcSubnet", + "inputs": { + "__defaults": [ + "name" + ], + "name": "lab04-subnet-1fa918f", + "networkId": "enpet9l87ashn294afa1", + "v4CidrBlocks": [ + "10.10.0.0/24" + ], + "zone": "ru-central1-a" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":180000000000,\"delete\":180000000000,\"update\":180000000000}}", + "createdAt": "2026-02-19T20:52:50Z", + "description": "", + "dhcpOptions": null, + "folderId": "b1g82kdcn5grlmu79ano", + "id": "e9b3hl3h09t6drs68pbg", + "labels": {}, + "name": "lab04-subnet-1fa918f", + "networkId": "enpet9l87ashn294afa1", + "routeTableId": "", + "v4CidrBlocks": [ + "10.10.0.0/24" + ], + "v6CidrBlocks": [], + "zone": "ru-central1-a" + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "dependencies": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ], + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "propertyDependencies": { + "networkId": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ], + "v4CidrBlocks": [], + "zone": [] + }, + "created": "2026-02-19T20:52:50.5480973Z", + "modified": "2026-02-19T20:52:50.5480973Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSecurityGroup:VpcSecurityGroup::lab04-sg", + "custom": true, + "id": "enpb8heveqi8pp7mvbcv", + "type": "yandex:index/vpcSecurityGroup:VpcSecurityGroup", + "inputs": { + "__defaults": [ + "name" + ], + "egresses": [ + { + "__defaults": [ + "port" + ], + "description": "Allow all outbound", + "fromPort": 0, + "port": -1, + "protocol": "ANY", + "toPort": 65535, + "v4CidrBlocks": [ + "0.0.0.0/0" + ] + } + ], + "ingresses": [ + { + "__defaults": [ + "fromPort", + "toPort" + ], + "description": "SSH", + "fromPort": -1, + "port": 22, + "protocol": "TCP", + "toPort": -1, + "v4CidrBlocks": [ + "95.111.204.70/32" + ] + }, + { + "__defaults": [ + "fromPort", + "toPort" + ], + "description": "HTTP", + "fromPort": -1, + "port": 80, + "protocol": "TCP", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ] + }, + { + "__defaults": [ + "fromPort", + "toPort" + ], + "description": "App 5000", + "fromPort": -1, + "port": 5000, + "protocol": "TCP", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ] + } + ], + "name": "lab04-sg-7fdbda7", + "networkId": "enpet9l87ashn294afa1" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":180000000000,\"delete\":180000000000,\"update\":180000000000}}", + "createdAt": "2026-02-19T20:52:52Z", + "description": "", + "egresses": [ + { + "description": "Allow all outbound", + "fromPort": 0, + "id": "enpqsfspqfq9fqiee48t", + "labels": {}, + "port": -1, + "predefinedTarget": "", + "protocol": "ANY", + "securityGroupId": "", + "toPort": 65535, + "v4CidrBlocks": [ + "0.0.0.0/0" + ], + "v6CidrBlocks": [] + } + ], + "folderId": "b1g82kdcn5grlmu79ano", + "id": "enpb8heveqi8pp7mvbcv", + "ingresses": [ + { + "description": "SSH", + "fromPort": -1, + "id": "enpbhntafog7fraba6rd", + "labels": {}, + "port": 22, + "predefinedTarget": "", + "protocol": "TCP", + "securityGroupId": "", + "toPort": -1, + "v4CidrBlocks": [ + "95.111.204.70/32" + ], + "v6CidrBlocks": [] + }, + { + "description": "HTTP", + "fromPort": -1, + "id": "enp80blit93rhb8rh3eb", + "labels": {}, + "port": 80, + "predefinedTarget": "", + "protocol": "TCP", + "securityGroupId": "", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ], + "v6CidrBlocks": [] + }, + { + "description": "App 5000", + "fromPort": -1, + "id": "enp39asi999eg5hhq4o1", + "labels": {}, + "port": 5000, + "predefinedTarget": "", + "protocol": "TCP", + "securityGroupId": "", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ], + "v6CidrBlocks": [] + } + ], + "labels": {}, + "name": "lab04-sg-7fdbda7", + "networkId": "enpet9l87ashn294afa1", + "status": "ACTIVE" + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "dependencies": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ], + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "propertyDependencies": { + "egresses": [], + "ingresses": [], + "networkId": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ] + }, + "created": "2026-02-19T20:52:52.5246052Z", + "modified": "2026-02-19T20:52:52.5246052Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/computeInstance:ComputeInstance::lab04-vm", + "custom": true, + "id": "fhm7ed1a5b85rs8u3uop", + "type": "yandex:index/computeInstance:ComputeInstance", + "inputs": { + "__defaults": [ + "name", + "networkAccelerationType" + ], + "allowStoppingForUpdate": true, + "bootDisk": { + "__defaults": [ + "autoDelete" + ], + "autoDelete": true, + "initializeParams": { + "__defaults": [], + "imageId": "fd8lt661chfo5i13a40d", + "size": 10, + "type": "network-hdd" + } + }, + "metadata": { + "__defaults": [], + "ssh-keys": "ubuntu:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5nKglXX/E2/S3oPKXahGr77IZELnnZlqE4oGCyEhZQ Данил@Dorley" + }, + "name": "lab04-vm-6023a00", + "networkAccelerationType": "standard", + "networkInterfaces": [ + { + "__defaults": [ + "ipv4" + ], + "ipv4": true, + "nat": true, + "securityGroupIds": [ + "enpb8heveqi8pp7mvbcv" + ], + "subnetId": "e9b3hl3h09t6drs68pbg" + } + ], + "platformId": "standard-v2", + "resources": { + "__defaults": [], + "coreFraction": 20, + "cores": 2, + "memory": 1 + }, + "zone": "ru-central1-a" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":300000000000,\"delete\":300000000000,\"update\":300000000000},\"schema_version\":\"1\"}", + "allowStoppingForUpdate": true, + "bootDisk": { + "autoDelete": true, + "deviceName": "fhmonvdcv96scusbdsm3", + "diskId": "fhmonvdcv96scusbdsm3", + "initializeParams": { + "blockSize": 4096, + "description": "", + "imageId": "fd8lt661chfo5i13a40d", + "name": "", + "size": 10, + "snapshotId": "", + "type": "network-hdd" + }, + "mode": "READ_WRITE" + }, + "createdAt": "2026-02-19T20:52:53Z", + "description": "", + "folderId": "b1g82kdcn5grlmu79ano", + "fqdn": "fhm7ed1a5b85rs8u3uop.auto.internal", + "hostname": "fhm7ed1a5b85rs8u3uop", + "id": "fhm7ed1a5b85rs8u3uop", + "labels": {}, + "metadata": { + "ssh-keys": "ubuntu:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5nKglXX/E2/S3oPKXahGr77IZELnnZlqE4oGCyEhZQ Данил@Dorley" + }, + "name": "lab04-vm-6023a00", + "networkAccelerationType": "standard", + "networkInterfaces": [ + { + "dnsRecords": [], + "index": 0, + "ipAddress": "10.10.0.6", + "ipv4": true, + "ipv6": false, + "ipv6Address": "", + "ipv6DnsRecords": [], + "macAddress": "d0:0d:77:34:2a:2a", + "nat": true, + "natDnsRecords": [], + "natIpAddress": "89.169.135.154", + "natIpVersion": "IPV4", + "securityGroupIds": [ + "enpb8heveqi8pp7mvbcv" + ], + "subnetId": "e9b3hl3h09t6drs68pbg" + } + ], + "placementPolicy": { + "placementGroupId": "" + }, + "platformId": "standard-v2", + "resources": { + "coreFraction": 20, + "cores": 2, + "gpus": 0, + "memory": 1 + }, + "schedulingPolicy": { + "preemptible": false + }, + "secondaryDisks": [], + "serviceAccountId": "", + "status": "running", + "zone": "ru-central1-a" + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "dependencies": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSecurityGroup:VpcSecurityGroup::lab04-sg", + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSubnet:VpcSubnet::lab04-subnet" + ], + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "propertyDependencies": { + "allowStoppingForUpdate": [], + "bootDisk": [], + "metadata": [], + "networkInterfaces": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSecurityGroup:VpcSecurityGroup::lab04-sg", + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSubnet:VpcSubnet::lab04-subnet" + ], + "platformId": [], + "resources": [], + "zone": [] + }, + "created": "2026-02-19T20:53:41.0764236Z", + "modified": "2026-02-19T20:53:41.0764236Z" + } + ], + "metadata": {} + } + } +} diff --git a/pulumi/.pulumi-state/.pulumi/stacks/lab04-pulumi-yc/dev.json.attrs b/pulumi/.pulumi-state/.pulumi/stacks/lab04-pulumi-yc/dev.json.attrs new file mode 100644 index 0000000000..4dbba8b4a3 --- /dev/null +++ b/pulumi/.pulumi-state/.pulumi/stacks/lab04-pulumi-yc/dev.json.attrs @@ -0,0 +1 @@ +{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":null,"md5":"8aVExZ8N8dRKSmZc6/Q9ng=="} diff --git a/pulumi/.pulumi-state/.pulumi/stacks/lab04-pulumi-yc/dev.json.bak b/pulumi/.pulumi-state/.pulumi/stacks/lab04-pulumi-yc/dev.json.bak new file mode 100644 index 0000000000..5e5007b70d --- /dev/null +++ b/pulumi/.pulumi-state/.pulumi/stacks/lab04-pulumi-yc/dev.json.bak @@ -0,0 +1,420 @@ +{ + "version": 3, + "checkpoint": { + "stack": "organization/lab04-pulumi-yc/dev", + "latest": { + "manifest": { + "time": "2026-02-19T23:53:41.0764236+03:00", + "magic": "7b54cd6e79f5cecd9ae124cb92b834b486c7e21993124e3a4e456c27c3ce48f9", + "version": "v3.222.0" + }, + "secrets_providers": { + "type": "passphrase", + "state": { + "salt": "v1:3yNf1Kmt4mA=:v1:Uk9In8z0RD2XJTza:lncnLY42u3UftJy+QfrDw6S2OT6vmQ==" + } + }, + "resources": [ + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0", + "custom": true, + "id": "7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "type": "pulumi:providers:yandex", + "inputs": { + "__internal": {}, + "version": "0.13.0" + }, + "outputs": { + "version": "0.13.0" + }, + "created": "2026-02-19T20:52:45.8287147Z", + "modified": "2026-02-19T20:52:45.8287147Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "custom": false, + "type": "pulumi:pulumi:Stack", + "created": "2026-02-19T20:52:45.8630518Z", + "modified": "2026-02-19T20:52:45.8630518Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net", + "custom": true, + "id": "enpet9l87ashn294afa1", + "type": "yandex:index/vpcNetwork:VpcNetwork", + "inputs": { + "__defaults": [ + "name" + ], + "name": "lab04-net-7bf49ed" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":60000000000,\"delete\":60000000000,\"update\":60000000000}}", + "createdAt": "2026-02-19T20:52:48Z", + "defaultSecurityGroupId": "enp912ofg69vrp8iegur", + "description": "", + "folderId": "b1g82kdcn5grlmu79ano", + "id": "enpet9l87ashn294afa1", + "labels": {}, + "name": "lab04-net-7bf49ed", + "subnetIds": [] + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "created": "2026-02-19T20:52:49.994421Z", + "modified": "2026-02-19T20:52:49.994421Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSubnet:VpcSubnet::lab04-subnet", + "custom": true, + "id": "e9b3hl3h09t6drs68pbg", + "type": "yandex:index/vpcSubnet:VpcSubnet", + "inputs": { + "__defaults": [ + "name" + ], + "name": "lab04-subnet-1fa918f", + "networkId": "enpet9l87ashn294afa1", + "v4CidrBlocks": [ + "10.10.0.0/24" + ], + "zone": "ru-central1-a" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":180000000000,\"delete\":180000000000,\"update\":180000000000}}", + "createdAt": "2026-02-19T20:52:50Z", + "description": "", + "dhcpOptions": null, + "folderId": "b1g82kdcn5grlmu79ano", + "id": "e9b3hl3h09t6drs68pbg", + "labels": {}, + "name": "lab04-subnet-1fa918f", + "networkId": "enpet9l87ashn294afa1", + "routeTableId": "", + "v4CidrBlocks": [ + "10.10.0.0/24" + ], + "v6CidrBlocks": [], + "zone": "ru-central1-a" + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "dependencies": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ], + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "propertyDependencies": { + "networkId": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ], + "v4CidrBlocks": [], + "zone": [] + }, + "created": "2026-02-19T20:52:50.5480973Z", + "modified": "2026-02-19T20:52:50.5480973Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSecurityGroup:VpcSecurityGroup::lab04-sg", + "custom": true, + "id": "enpb8heveqi8pp7mvbcv", + "type": "yandex:index/vpcSecurityGroup:VpcSecurityGroup", + "inputs": { + "__defaults": [ + "name" + ], + "egresses": [ + { + "__defaults": [ + "port" + ], + "description": "Allow all outbound", + "fromPort": 0, + "port": -1, + "protocol": "ANY", + "toPort": 65535, + "v4CidrBlocks": [ + "0.0.0.0/0" + ] + } + ], + "ingresses": [ + { + "__defaults": [ + "fromPort", + "toPort" + ], + "description": "SSH", + "fromPort": -1, + "port": 22, + "protocol": "TCP", + "toPort": -1, + "v4CidrBlocks": [ + "95.111.204.70/32" + ] + }, + { + "__defaults": [ + "fromPort", + "toPort" + ], + "description": "HTTP", + "fromPort": -1, + "port": 80, + "protocol": "TCP", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ] + }, + { + "__defaults": [ + "fromPort", + "toPort" + ], + "description": "App 5000", + "fromPort": -1, + "port": 5000, + "protocol": "TCP", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ] + } + ], + "name": "lab04-sg-7fdbda7", + "networkId": "enpet9l87ashn294afa1" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":180000000000,\"delete\":180000000000,\"update\":180000000000}}", + "createdAt": "2026-02-19T20:52:52Z", + "description": "", + "egresses": [ + { + "description": "Allow all outbound", + "fromPort": 0, + "id": "enpqsfspqfq9fqiee48t", + "labels": {}, + "port": -1, + "predefinedTarget": "", + "protocol": "ANY", + "securityGroupId": "", + "toPort": 65535, + "v4CidrBlocks": [ + "0.0.0.0/0" + ], + "v6CidrBlocks": [] + } + ], + "folderId": "b1g82kdcn5grlmu79ano", + "id": "enpb8heveqi8pp7mvbcv", + "ingresses": [ + { + "description": "SSH", + "fromPort": -1, + "id": "enpbhntafog7fraba6rd", + "labels": {}, + "port": 22, + "predefinedTarget": "", + "protocol": "TCP", + "securityGroupId": "", + "toPort": -1, + "v4CidrBlocks": [ + "95.111.204.70/32" + ], + "v6CidrBlocks": [] + }, + { + "description": "HTTP", + "fromPort": -1, + "id": "enp80blit93rhb8rh3eb", + "labels": {}, + "port": 80, + "predefinedTarget": "", + "protocol": "TCP", + "securityGroupId": "", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ], + "v6CidrBlocks": [] + }, + { + "description": "App 5000", + "fromPort": -1, + "id": "enp39asi999eg5hhq4o1", + "labels": {}, + "port": 5000, + "predefinedTarget": "", + "protocol": "TCP", + "securityGroupId": "", + "toPort": -1, + "v4CidrBlocks": [ + "0.0.0.0/0" + ], + "v6CidrBlocks": [] + } + ], + "labels": {}, + "name": "lab04-sg-7fdbda7", + "networkId": "enpet9l87ashn294afa1", + "status": "ACTIVE" + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "dependencies": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ], + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "propertyDependencies": { + "egresses": [], + "ingresses": [], + "networkId": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcNetwork:VpcNetwork::lab04-net" + ] + }, + "created": "2026-02-19T20:52:52.5246052Z", + "modified": "2026-02-19T20:52:52.5246052Z" + }, + { + "urn": "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/computeInstance:ComputeInstance::lab04-vm", + "custom": true, + "id": "fhm7ed1a5b85rs8u3uop", + "type": "yandex:index/computeInstance:ComputeInstance", + "inputs": { + "__defaults": [ + "name", + "networkAccelerationType" + ], + "allowStoppingForUpdate": true, + "bootDisk": { + "__defaults": [ + "autoDelete" + ], + "autoDelete": true, + "initializeParams": { + "__defaults": [], + "imageId": "fd8lt661chfo5i13a40d", + "size": 10, + "type": "network-hdd" + } + }, + "metadata": { + "__defaults": [], + "ssh-keys": "ubuntu:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5nKglXX/E2/S3oPKXahGr77IZELnnZlqE4oGCyEhZQ Данил@Dorley" + }, + "name": "lab04-vm-6023a00", + "networkAccelerationType": "standard", + "networkInterfaces": [ + { + "__defaults": [ + "ipv4" + ], + "ipv4": true, + "nat": true, + "securityGroupIds": [ + "enpb8heveqi8pp7mvbcv" + ], + "subnetId": "e9b3hl3h09t6drs68pbg" + } + ], + "platformId": "standard-v2", + "resources": { + "__defaults": [], + "coreFraction": 20, + "cores": 2, + "memory": 1 + }, + "zone": "ru-central1-a" + }, + "outputs": { + "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":300000000000,\"delete\":300000000000,\"update\":300000000000},\"schema_version\":\"1\"}", + "allowStoppingForUpdate": true, + "bootDisk": { + "autoDelete": true, + "deviceName": "fhmonvdcv96scusbdsm3", + "diskId": "fhmonvdcv96scusbdsm3", + "initializeParams": { + "blockSize": 4096, + "description": "", + "imageId": "fd8lt661chfo5i13a40d", + "name": "", + "size": 10, + "snapshotId": "", + "type": "network-hdd" + }, + "mode": "READ_WRITE" + }, + "createdAt": "2026-02-19T20:52:53Z", + "description": "", + "folderId": "b1g82kdcn5grlmu79ano", + "fqdn": "fhm7ed1a5b85rs8u3uop.auto.internal", + "hostname": "fhm7ed1a5b85rs8u3uop", + "id": "fhm7ed1a5b85rs8u3uop", + "labels": {}, + "metadata": { + "ssh-keys": "ubuntu:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5nKglXX/E2/S3oPKXahGr77IZELnnZlqE4oGCyEhZQ Данил@Dorley" + }, + "name": "lab04-vm-6023a00", + "networkAccelerationType": "standard", + "networkInterfaces": [ + { + "dnsRecords": [], + "index": 0, + "ipAddress": "10.10.0.6", + "ipv4": true, + "ipv6": false, + "ipv6Address": "", + "ipv6DnsRecords": [], + "macAddress": "d0:0d:77:34:2a:2a", + "nat": true, + "natDnsRecords": [], + "natIpAddress": "89.169.135.154", + "natIpVersion": "IPV4", + "securityGroupIds": [ + "enpb8heveqi8pp7mvbcv" + ], + "subnetId": "e9b3hl3h09t6drs68pbg" + } + ], + "placementPolicy": { + "placementGroupId": "" + }, + "platformId": "standard-v2", + "resources": { + "coreFraction": 20, + "cores": 2, + "gpus": 0, + "memory": 1 + }, + "schedulingPolicy": { + "preemptible": false + }, + "secondaryDisks": [], + "serviceAccountId": "", + "status": "running", + "zone": "ru-central1-a" + }, + "parent": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:pulumi:Stack::lab04-pulumi-yc-dev", + "dependencies": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSecurityGroup:VpcSecurityGroup::lab04-sg", + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSubnet:VpcSubnet::lab04-subnet" + ], + "provider": "urn:pulumi:dev::lab04-pulumi-yc::pulumi:providers:yandex::default_0_13_0::7d12287d-55f3-465d-8fa0-e63bfc770a1f", + "propertyDependencies": { + "allowStoppingForUpdate": [], + "bootDisk": [], + "metadata": [], + "networkInterfaces": [ + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSecurityGroup:VpcSecurityGroup::lab04-sg", + "urn:pulumi:dev::lab04-pulumi-yc::yandex:index/vpcSubnet:VpcSubnet::lab04-subnet" + ], + "platformId": [], + "resources": [], + "zone": [] + }, + "created": "2026-02-19T20:53:41.0764236Z", + "modified": "2026-02-19T20:53:41.0764236Z" + } + ], + "metadata": {} + } + } +} diff --git a/pulumi/.pulumi-state/.pulumi/stacks/lab04-pulumi-yc/dev.json.bak.attrs b/pulumi/.pulumi-state/.pulumi/stacks/lab04-pulumi-yc/dev.json.bak.attrs new file mode 100644 index 0000000000..60a4efc8b8 --- /dev/null +++ b/pulumi/.pulumi-state/.pulumi/stacks/lab04-pulumi-yc/dev.json.bak.attrs @@ -0,0 +1 @@ +{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":null,"md5":"N1dWOE4/X1j2axM1iAPBng=="} diff --git a/pulumi/Pulumi.yaml b/pulumi/Pulumi.yaml new file mode 100644 index 0000000000..7e92a0ad45 --- /dev/null +++ b/pulumi/Pulumi.yaml @@ -0,0 +1,6 @@ +name: lab04-pulumi-yc +runtime: + name: python + options: + virtualenv: venv +description: Lab04 infrastructure in Yandex Cloud using Pulumi (Python) diff --git a/pulumi/README.md b/pulumi/README.md new file mode 100644 index 0000000000..92b74ed68e --- /dev/null +++ b/pulumi/README.md @@ -0,0 +1,62 @@ +# Lab04 — Pulumi (Yandex Cloud, Python) + +## Before + +1) Download Pulumi. +2) Download Python 3.10+. +3) Download project requirements: + +```bash +python -m venv venv +# Windows: +# .\venv\Scripts\activate +# macOS/Linux: +# source venv/bin/activate +pip install -r requirements.txt +``` + +## Auth using Yandex Cloud + +Pulumi provider Yandex uses same env/confic as Terrarform +Simplier + +- add env variables `YC_TOKEN`, `YC_CLOUD_ID`, `YC_FOLDER_ID`, `YC_ZONE` + +or (using pulumi config): + +```bash +pulumi config set yandex:cloudId +pulumi config set yandex:folderId +pulumi config set yandex:zone ru-central1-a +pulumi config set --secret yandex:token +``` + +## Project settings (my vaaariables) + +```bash +pulumi config set zone ru-central1-a +pulumi config set subnetCidr 10.10.0.0/24 +pulumi config set allowedSshCidr "<ваш_IP>/32" + +pulumi config set sshUser ubuntu +pulumi config set sshPublicKeyPath "~/.ssh/lab04_ed25519.pub" +# либо: +# pulumi config set sshPublicKey "ssh-ed25519 AAAA... lab04" + +pulumi config set imageFamily ubuntu-2404-lts +``` + +## Start + +```bash +pulumi preview +pulumi up +``` + +After `pulumi up` in output will be `public_ip` and `ssh_command`. + +## Delete resourses + +```bash +pulumi destroy +``` diff --git a/pulumi/__main__.py b/pulumi/__main__.py new file mode 100644 index 0000000000..738775ea30 --- /dev/null +++ b/pulumi/__main__.py @@ -0,0 +1,146 @@ +import os + +import pulumi +import pulumi_yandex as yandex + +cfg = pulumi.Config() + +# ----------------- +# Config (project) +# ----------------- +zone = cfg.get("zone") or os.getenv("YC_ZONE") or "ru-central1-a" +subnet_cidr = cfg.get("subnetCidr") or "10.10.0.0/24" +allowed_ssh_cidr = cfg.get("allowedSshCidr") or "0.0.0.0/0" + +ssh_user = cfg.get("sshUser") or "ubuntu" +ssh_public_key = cfg.get("sshPublicKey") +ssh_public_key_path = cfg.get("sshPublicKeyPath") + +image_family = cfg.get("imageFamily") or "ubuntu-2404-lts" + +vm_cores = int(cfg.get("vmCores") or 2) +vm_memory_gb = int(cfg.get("vmMemoryGb") or 1) +vm_core_fraction = int(cfg.get("vmCoreFraction") or 20) + +disk_size_gb = int(cfg.get("diskSizeGb") or 10) +disk_type = cfg.get("diskType") or "network-hdd" + +platform_id = cfg.get("platformId") or "standard-v2" + +# SSH public key: either inline or from file +if not ssh_public_key: + if ssh_public_key_path: + path = os.path.expanduser(ssh_public_key_path) + with open(path, "r", encoding="utf-8") as f: + ssh_public_key = f.read().strip() + else: + raise Exception( + "Set sshPublicKey (inline) or sshPublicKeyPath (path to .pub) via pulumi config" + ) + +# ----------------- +# Data: image +# ----------------- +image = yandex.get_compute_image(family=image_family) + +# ----------------- +# Network +# ----------------- +net = yandex.VpcNetwork("lab04-net") + +subnet = yandex.VpcSubnet( + "lab04-subnet", + network_id=net.id, + zone=zone, + v4_cidr_blocks=[subnet_cidr], +) + +# ----------------- +# Security Group +# ----------------- +sg = yandex.VpcSecurityGroup( + "lab04-sg", + network_id=net.id, + ingresses=[ + yandex.VpcSecurityGroupIngressArgs( + protocol="TCP", + description="SSH", + v4_cidr_blocks=[allowed_ssh_cidr], + port=22, + ), + yandex.VpcSecurityGroupIngressArgs( + protocol="TCP", + description="HTTP", + v4_cidr_blocks=["0.0.0.0/0"], + port=80, + ), + yandex.VpcSecurityGroupIngressArgs( + protocol="TCP", + description="App 5000", + v4_cidr_blocks=["0.0.0.0/0"], + port=5000, + ), + ], + egresses=[ + yandex.VpcSecurityGroupEgressArgs( + protocol="ANY", + description="Allow all outbound", + v4_cidr_blocks=["0.0.0.0/0"], + from_port=0, + to_port=65535, + ) + ], +) + + +# ----------------- +# VM +# ----------------- +vm = yandex.ComputeInstance( + "lab04-vm", + zone=zone, + platform_id=platform_id, + resources=yandex.ComputeInstanceResourcesArgs( + cores=vm_cores, + memory=vm_memory_gb, + core_fraction=vm_core_fraction, + ), + boot_disk=yandex.ComputeInstanceBootDiskArgs( + initialize_params=yandex.ComputeInstanceBootDiskInitializeParamsArgs( + image_id=image.id, + size=disk_size_gb, + type=disk_type, + ) + ), + network_interfaces=[ + yandex.ComputeInstanceNetworkInterfaceArgs( + subnet_id=subnet.id, + nat=True, + security_group_ids=[sg.id], + ) + ], + metadata={ + "ssh-keys": f"{ssh_user}:{ssh_public_key}", + }, + allow_stopping_for_update=True, +) + + +def _nat_ip(network_interfaces): + """Handle both dict-style and typed outputs.""" + ni0 = network_interfaces[0] + if isinstance(ni0, dict): + return ni0.get("nat_ip_address") + return getattr(ni0, "nat_ip_address", None) + + +public_ip = vm.network_interfaces.apply(_nat_ip) +internal_ip = vm.network_interfaces.apply( + lambda nis: nis[0].get("ip_address") if isinstance(nis[0], dict) else getattr(nis[0], "ip_address", None) +) + +pulumi.export("public_ip", public_ip) +pulumi.export("internal_ip", internal_ip) +pulumi.export("ssh_command", public_ip.apply(lambda ip: f"ssh -i ~/.ssh/lab04_ed25519 {ssh_user}@{ip}")) +pulumi.export("http_url", public_ip.apply(lambda ip: f"http://{ip}/")) +pulumi.export("app_url", public_ip.apply(lambda ip: f"http://{ip}:5000/")) diff --git a/pulumi/requirements.txt b/pulumi/requirements.txt new file mode 100644 index 0000000000..4fcd3c0981 --- /dev/null +++ b/pulumi/requirements.txt @@ -0,0 +1,2 @@ +pulumi>=3.0.0,<4.0.0 +pulumi-yandex diff --git a/scripts/load-env.ps1 b/scripts/load-env.ps1 new file mode 100644 index 0000000000..498d804eea --- /dev/null +++ b/scripts/load-env.ps1 @@ -0,0 +1,33 @@ +$envFile = Join-Path (Get-Location) ".env" +if (!(Test-Path $envFile)) { + throw "Не найден .env в корне: $envFile" +} + +Get-Content $envFile | ForEach-Object { + $line = $_.Trim() + if ($line.Length -eq 0) { return } + if ($line.StartsWith("#")) { return } + + $parts = $line -split "=", 2 + if ($parts.Count -ne 2) { return } + + $name = $parts[0].Trim() + $value = $parts[1].Trim().Trim('"').Trim("'") + + # экспорт в текущее окружение + Set-Item -Path "Env:$name" -Value $value +} + +# привести YC_SERVICE_ACCOUNT_KEY_FILE к абсолютному пути, если он относительный +if ($env:YC_SERVICE_ACCOUNT_KEY_FILE -and !(Split-Path $env:YC_SERVICE_ACCOUNT_KEY_FILE -IsAbsolute)) { + $candidate = Join-Path (Get-Location) $env:YC_SERVICE_ACCOUNT_KEY_FILE + if (Test-Path $candidate) { + $env:YC_SERVICE_ACCOUNT_KEY_FILE = (Resolve-Path $candidate).Path + } +} + +Write-Host "Loaded .env OK" +Write-Host "YC_CLOUD_ID=$env:YC_CLOUD_ID" +Write-Host "YC_FOLDER_ID=$env:YC_FOLDER_ID" +Write-Host "YC_ZONE=$env:YC_ZONE" +Write-Host "YC_SERVICE_ACCOUNT_KEY_FILE=$env:YC_SERVICE_ACCOUNT_KEY_FILE" diff --git a/terraform/.gitignore b/terraform/.gitignore new file mode 100644 index 0000000000..2d2b98b02a --- /dev/null +++ b/terraform/.gitignore @@ -0,0 +1,21 @@ +# Terraform local state / cache +.terraform/ +*.tfstate +*.tfstate.* + +# Variables (не коммитим) +*.tfvars +terraform.tfvars + +# Crash logs +crash.log +crash.*.log + +# Override files +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Lock file (в реальных проектах часто коммитят, но в рамках лабы можно игнорировать) +.terraform.lock.hcl diff --git a/terraform/.tflint.hcl b/terraform/.tflint.hcl new file mode 100644 index 0000000000..6eb5862287 --- /dev/null +++ b/terraform/.tflint.hcl @@ -0,0 +1,6 @@ +plugin "terraform" { + enabled = true +} + +# Базовые правила tflint включаются по умолчанию. +# Провайдер-специфичные ruleset'ы можно подключать отдельно, но для лабы достаточно базового. diff --git a/terraform/README.md b/terraform/README.md new file mode 100644 index 0000000000..def80b4b55 --- /dev/null +++ b/terraform/README.md @@ -0,0 +1,56 @@ +# Lab04 — Terraform (Yandex Cloud) + +Target: up the VM or netwwork and security group using ports **22/80/5000**. + +## Before + +1) Download Terraform. +2) Download YC CLI и залогиньтесь. +3) Prepare SSH-ключ (ed25519 рекомендуется): + +```bash +ssh-keygen -t ed25519 -C "lab04" -f ~/.ssh/lab04_ed25519 +``` + +## Variables settings + +Copy exaample andd fill: + +```bash +cp terraform.tfvars.example terraform.tfvars +``` + +Check that: +- `allowed_ssh_cidr` is better to take in outter IP `/32`. +- SSH-key may transfer using `ssh_public_key` (as a string), лor `ssh_public_key_path`. + +### How to transfer creds of Yandex Cloud + +There are 2 ways: + +**A) Using env:** +- `YC_TOKEN` или `YC_SERVICE_ACCOUNT_KEY_FILE` +- `YC_CLOUD_ID` +- `YC_FOLDER_ID` + +**B) Using terraform.tfvars:** +- `cloud_id`, `folder_id` +- `service_account_key_file` or `yc_token` + +## Start + +```bash +terraform init +terraform fmt +terraform validate +terraform plan +terraform apply +``` + +After `apply` Terraform outputs `public_ip` и `ssh_command`. + +## Delete resourses + +```bash +terraform destroy +``` diff --git a/terraform/github/.gitignore b/terraform/github/.gitignore new file mode 100644 index 0000000000..6c721539e4 --- /dev/null +++ b/terraform/github/.gitignore @@ -0,0 +1,7 @@ +.terraform/ +*.tfstate +*.tfstate.* +*.tfvars +terraform.tfvars +crash.log +.terraform.lock.hcl diff --git a/terraform/github/.tflint.hcl b/terraform/github/.tflint.hcl new file mode 100644 index 0000000000..75d15f14aa --- /dev/null +++ b/terraform/github/.tflint.hcl @@ -0,0 +1,3 @@ +plugin "terraform" { + enabled = true +} diff --git a/terraform/github/main.tf b/terraform/github/main.tf new file mode 100644 index 0000000000..2709d7849d --- /dev/null +++ b/terraform/github/main.tf @@ -0,0 +1,11 @@ +resource "github_repository" "course_repo" { + name = var.repo_name + description = var.repo_description + visibility = var.visibility + + has_issues = true + has_projects = false + has_wiki = false + + # auto_init = true # включите, если хотите создать repo с пустым коммитом +} diff --git a/terraform/github/outputs.tf b/terraform/github/outputs.tf new file mode 100644 index 0000000000..81dffb5fb3 --- /dev/null +++ b/terraform/github/outputs.tf @@ -0,0 +1,9 @@ +output "repo_full_name" { + value = github_repository.course_repo.full_name + description = "owner/name" +} + +output "repo_url" { + value = github_repository.course_repo.html_url + description = "URL репозитория" +} diff --git a/terraform/github/providers.tf b/terraform/github/providers.tf new file mode 100644 index 0000000000..fa406ffc5e --- /dev/null +++ b/terraform/github/providers.tf @@ -0,0 +1,8 @@ +provider "github" { + # Можно передать token/owner через переменные, либо через env: + # GITHUB_TOKEN + # GITHUB_OWNER (не всегда поддерживается, обычно owner задают в provider block) + + token = var.github_token != "" ? var.github_token : null + owner = var.github_owner != "" ? var.github_owner : null +} diff --git a/terraform/github/terraform.tfvars.example b/terraform/github/terraform.tfvars.example new file mode 100644 index 0000000000..3557c3db2d --- /dev/null +++ b/terraform/github/terraform.tfvars.example @@ -0,0 +1,14 @@ +# Лучше хранить токен в env: +# export GITHUB_TOKEN=... +# или в PowerShell: +# $env:GITHUB_TOKEN="..." + +# Но если надо: +# github_token = "..." # НЕ КОММИТИТЬ + +# Если owner не задан, GitHub provider обычно берёт owner из токена. +# github_owner = "your-username-or-org" + +repo_name = "DevOps-Core-Course" +repo_description = "DevOps course repository managed by Terraform" +visibility = "public" diff --git a/terraform/github/variables.tf b/terraform/github/variables.tf new file mode 100644 index 0000000000..87b10b3062 --- /dev/null +++ b/terraform/github/variables.tf @@ -0,0 +1,30 @@ +variable "github_token" { + type = string + description = "GitHub Personal Access Token (PAT). Можно оставить пустым и задать через env GITHUB_TOKEN" + default = "" + sensitive = true +} + +variable "github_owner" { + type = string + description = "Владелец (username или org). Можно оставить пустым — owner будет взят из токена (если провайдер сможет)" + default = "" +} + +variable "repo_name" { + type = string + description = "Название репозитория (например DevOps-Core-Course)" + default = "DevOps-Core-Course" +} + +variable "repo_description" { + type = string + description = "Описание репозитория" + default = "DevOps course repository managed by Terraform" +} + +variable "visibility" { + type = string + description = "public/private/internal" + default = "public" +} diff --git a/terraform/github/versions.tf b/terraform/github/versions.tf new file mode 100644 index 0000000000..dd2ecc138e --- /dev/null +++ b/terraform/github/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.6.0" + + required_providers { + github = { + source = "integrations/github" + version = ">= 5.0.0" + } + } +} diff --git a/terraform/locals.tf b/terraform/locals.tf new file mode 100644 index 0000000000..3704986e2d --- /dev/null +++ b/terraform/locals.tf @@ -0,0 +1,15 @@ +locals { + # Можно задать ключ прямо строкой (ssh_public_key), а можно указать путь к файлу (ssh_public_key_path). + # try(...) нужен, чтобы terraform validate проходил даже без файла. + ssh_public_key_resolved = trimspace(coalesce( + (var.ssh_public_key != "" ? var.ssh_public_key : null), + try(file(var.ssh_public_key_path), null), + "" + )) + + instance_metadata = local.ssh_public_key_resolved != "" ? { + # Формат, который ожидает cloud-init в образах YC: "user:ssh_public_key" + # (см. офиц. примеры/доки) + "ssh-keys" = "${var.ssh_user}:${local.ssh_public_key_resolved}" + } : {} +} diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000000..b933dbe32d --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,88 @@ +data "yandex_compute_image" "os" { + family = var.image_family +} + +resource "yandex_vpc_network" "lab_net" { + name = "${var.project_name}-net" + labels = var.labels +} + +resource "yandex_vpc_subnet" "lab_subnet" { + name = "${var.project_name}-subnet" + zone = var.zone + network_id = yandex_vpc_network.lab_net.id + v4_cidr_blocks = [var.subnet_cidr] + labels = var.labels +} + +resource "yandex_vpc_security_group" "vm_sg" { + name = "${var.project_name}-sg" + description = "Security Group for Lab04 VM" + network_id = yandex_vpc_network.lab_net.id + + ingress { + description = "SSH from allowed CIDR" + protocol = "TCP" + v4_cidr_blocks = [var.allowed_ssh_cidr] + port = 22 + } + + ingress { + description = "HTTP" + protocol = "TCP" + v4_cidr_blocks = ["0.0.0.0/0"] + port = 80 + } + + ingress { + description = "App port (Flask)" + protocol = "TCP" + v4_cidr_blocks = ["0.0.0.0/0"] + port = 5000 + } + + egress { + description = "Allow all outbound" + protocol = "ANY" + v4_cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + to_port = 65535 + } + + labels = var.labels +} + +resource "yandex_compute_instance" "vm" { + name = "${var.project_name}-vm" + platform_id = var.platform_id + zone = var.zone + + resources { + cores = var.vm_cores + memory = var.vm_memory_gb + core_fraction = var.vm_core_fraction + } + + scheduling_policy { + preemptible = var.preemptible + } + + boot_disk { + initialize_params { + image_id = data.yandex_compute_image.os.id + size = var.disk_size_gb + type = var.disk_type + } + } + + network_interface { + subnet_id = yandex_vpc_subnet.lab_subnet.id + nat = true + security_group_ids = [yandex_vpc_security_group.vm_sg.id] + } + + metadata = local.instance_metadata + + allow_stopping_for_update = true + labels = var.labels +} diff --git a/terraform/outputs.tf b/terraform/outputs.tf new file mode 100644 index 0000000000..1be16233f7 --- /dev/null +++ b/terraform/outputs.tf @@ -0,0 +1,29 @@ +output "vm_id" { + value = yandex_compute_instance.vm.id + description = "ID виртуальной машины" +} + +output "internal_ip" { + value = yandex_compute_instance.vm.network_interface[0].ip_address + description = "Внутренний IP (в подсети)" +} + +output "public_ip" { + value = yandex_compute_instance.vm.network_interface[0].nat_ip_address + description = "Публичный IP (NAT)" +} + +output "ssh_command" { + description = "Команда для подключения по SSH (подставьте путь к приватному ключу)" + value = "ssh -i ~/.ssh/id_ed25519 ${var.ssh_user}@${yandex_compute_instance.vm.network_interface[0].nat_ip_address}" +} + +output "http_url" { + description = "URL для HTTP (если вы поставите веб-сервер на VM)" + value = "http://${yandex_compute_instance.vm.network_interface[0].nat_ip_address}/" +} + +output "app_url" { + description = "URL для приложения на 5000 порту (если вы его запустите)" + value = "http://${yandex_compute_instance.vm.network_interface[0].nat_ip_address}:5000/" +} diff --git a/terraform/providers.tf b/terraform/providers.tf new file mode 100644 index 0000000000..365effc630 --- /dev/null +++ b/terraform/providers.tf @@ -0,0 +1,12 @@ +provider "yandex" { + # Можно передавать через переменные (terraform.tfvars), а можно через env: + # YC_CLOUD_ID, YC_FOLDER_ID, YC_TOKEN / YC_SERVICE_ACCOUNT_KEY_FILE + # Если значения пустые, Terraform передаст null и провайдер попробует взять их из env. + + cloud_id = var.cloud_id != "" ? var.cloud_id : null + folder_id = var.folder_id != "" ? var.folder_id : null + zone = var.zone + + token = var.yc_token != "" ? var.yc_token : null + service_account_key_file = var.service_account_key_file != "" ? var.service_account_key_file : null +} diff --git a/terraform/terraform.tfvars.example b/terraform/terraform.tfvars.example new file mode 100644 index 0000000000..e766e30f61 --- /dev/null +++ b/terraform/terraform.tfvars.example @@ -0,0 +1,40 @@ +# --- YC identifiers --- +# Можно оставить пустым и задать через env YC_CLOUD_ID / YC_FOLDER_ID +cloud_id = "" +folder_id = "" + +# --- Zone / Network --- +zone = "ru-central1-a" +subnet_cidr = "10.10.0.0/24" + +# Разрешить SSH только с вашего IP (пример): +# allowed_ssh_cidr = "93.184.216.34/32" +allowed_ssh_cidr = "0.0.0.0/0" + +# --- Auth --- +# Рекомендуется: env YC_TOKEN / YC_SERVICE_ACCOUNT_KEY_FILE. +# yc_token = "..." +# service_account_key_file = "C:/Users//key.json" + +# --- SSH --- +ssh_user = "ubuntu" + +# ВАРИАНТ 1: вставить ключ строкой +# ssh_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... lab04" + +# ВАРИАНТ 2: путь до .pub +ssh_public_key_path = "~/.ssh/lab04_ed25519.pub" + +# --- VM sizing --- +vm_cores = 2 +vm_memory_gb = 1 +vm_core_fraction = 20 + +preemptible = false +platform_id = "standard-v2" +disk_size_gb = 10 +disk_type = "network-hdd" + +# image_family может отличаться. Если data source не найдёт образ — +# уточните доступные семьи образов в YC. +image_family = "ubuntu-2404-lts" diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000000..49f75e7e93 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,133 @@ +variable "project_name" { + type = string + description = "Префикс/название для ресурсов Lab04" + default = "lab04" +} + +variable "labels" { + type = map(string) + description = "Лейблы Yandex Cloud (опционально)" + default = { + project = "lab04" + } +} + +# --- Yandex Cloud provider config --- + +variable "cloud_id" { + type = string + description = "YC cloud_id (можно оставить пустым и задать через env YC_CLOUD_ID)" + default = "" +} + +variable "folder_id" { + type = string + description = "YC folder_id (можно оставить пустым и задать через env YC_FOLDER_ID)" + default = "b1g82kdcn5grlmu79ano" +} + +variable "zone" { + type = string + description = "Зона доступности (например, ru-central1-a)" + default = "ru-central1-a" +} + +variable "yc_token" { + type = string + description = "OAuth/IAM токен (можно оставить пустым и задать через env YC_TOKEN)" + default = "" + sensitive = true +} + +variable "service_account_key_file" { + type = string + description = "Путь к JSON-ключу сервисного аккаунта (можно оставить пустым и задать через env YC_SERVICE_ACCOUNT_KEY_FILE)" + default = "" + sensitive = true +} + +# --- Network / Security --- + +variable "subnet_cidr" { + type = string + description = "CIDR подсети" + default = "10.10.0.0/24" +} + +variable "allowed_ssh_cidr" { + type = string + description = "CIDR, откуда разрешён SSH (желательно ваш белый IP /32)" + # Чтобы не заблокировать себя на первом запуске, оставляем дефолт открытым. + # В отчёте лучше указать ваш IP/32. + default = "0.0.0.0/0" +} + +# --- VM --- + +variable "image_family" { + type = string + description = "Семейство образа (data.yandex_compute_image.family)" + default = "ubuntu-2404-lts" +} + +variable "platform_id" { + type = string + description = "Платформа VM" + default = "standard-v2" +} + +variable "vm_cores" { + type = number + description = "vCPU" + default = 2 +} + +variable "vm_memory_gb" { + type = number + description = "RAM (GB)" + default = 1 +} + +variable "vm_core_fraction" { + type = number + description = "Доля CPU (5/20/50/100). 20 часто дешевле" + default = 20 +} + +variable "disk_size_gb" { + type = number + description = "Размер диска (GB)" + default = 10 +} + +variable "disk_type" { + type = string + description = "Тип диска (например network-hdd / network-ssd)" + default = "network-hdd" +} + +variable "preemptible" { + type = bool + description = "Преемптивная VM (дешевле, но может выключаться)" + default = false +} + +# --- SSH keys --- + +variable "ssh_user" { + type = string + description = "Пользователь в образе (для Ubuntu обычно ubuntu)" + default = "ubuntu" +} + +variable "ssh_public_key" { + type = string + description = "Содержимое публичного ключа (ssh-ed25519 AAAA... comment). Можно не задавать, если задаёте ssh_public_key_path" + default = "" +} + +variable "ssh_public_key_path" { + type = string + description = "Путь к публичному ключу (например ~/.ssh/id_ed25519.pub). Используется, если ssh_public_key пустой" + default = "" +} diff --git a/terraform/versions.tf b/terraform/versions.tf new file mode 100644 index 0000000000..b0f4ecb816 --- /dev/null +++ b/terraform/versions.tf @@ -0,0 +1,12 @@ +terraform { + required_version = ">= 1.6.0" + + required_providers { + yandex = { + source = "yandex-cloud/yandex" + # Не фиксируем точную версию, чтобы не ломаться при обновлениях. + # При желании можно зафиксировать (например, ">= 0.170.0"). + version = ">= 0.100.0" + } + } +}