feat: solidtime VM
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Infrastructure / Check and run Ansbile playbooks (push) Failing after 5m34s
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Infrastructure / Check and run Ansbile playbooks (push) Failing after 5m34s
				
			This commit is contained in:
		
							
								
								
									
										112
									
								
								infra/solidtime/0000_proxmox_playbook.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								infra/solidtime/0000_proxmox_playbook.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| - name: Provision solidtime Proxmox VM | ||||
|   hosts: solidtime | ||||
|   connection: ansible.builtin.local | ||||
|   gather_facts: false | ||||
|   vars: | ||||
|     api_user: "{{ lookup('ansible.builtin.env', 'PROXMOX_USER') }}" | ||||
|     api_host: "{{ lookup('ansible.builtin.env', 'PROXMOX_HOST' ) }}" | ||||
|     api_token_id: "{{ lookup('ansible.builtin.env', 'PROXMOX_TOKEN_ID') }}" | ||||
|     api_token_secret: "{{ lookup('ansible.builtin.env', 'PROXMOX_TOKEN_SECRET') }}" | ||||
|     ssh_public: "{{ lookup('ansible.builtin.env', 'SSH_PUBLIC') }}" | ||||
|     vmname: "{{ inventory_hostname | regex_replace('^([^\\.]+)\\..+$', '\\1') }}" | ||||
|     node: pve2 | ||||
|   module_defaults: | ||||
|     community.general.proxmox_kvm: | ||||
|       api_user: "{{ api_user }}" | ||||
|       api_host: "{{ api_host }}" | ||||
|       api_token_id: "{{ api_token_id }}" | ||||
|       api_token_secret: "{{ api_token_secret }}" | ||||
|       name: "{{ vmname }}" | ||||
|       node: "{{ node }}" | ||||
|     community.general.proxmox_nic: | ||||
|       api_user: "{{ api_user }}" | ||||
|       api_host: "{{ api_host }}" | ||||
|       api_token_id: "{{ api_token_id }}" | ||||
|       api_token_secret: "{{ api_token_secret }}" | ||||
|       name: "{{ vmname }}" | ||||
|     community.general.proxmox_disk: | ||||
|       api_user: "{{ api_user }}" | ||||
|       api_host: "{{ api_host }}" | ||||
|       api_token_id: "{{ api_token_id }}" | ||||
|       api_token_secret: "{{ api_token_secret }}" | ||||
|       name: "{{ vmname }}" | ||||
|   tasks: | ||||
|     # Initial setup | ||||
|     - name: Create VM | ||||
|       community.general.proxmox_kvm: | ||||
|         clone: "{{ node }}-debian-12" | ||||
|         storage: nvme | ||||
|       notify: | ||||
|         - Start VM | ||||
|         - Wait | ||||
|     - name: Wait for status | ||||
|       community.general.proxmox_kvm: | ||||
|         state: current | ||||
|       register: vm | ||||
|       retries: 30 | ||||
|       delay: 10 | ||||
|       until: vm.status is defined | ||||
|  | ||||
|     # Networking and initial config | ||||
|     - name: Add PUB NIC | ||||
|       community.general.proxmox_nic: | ||||
|         interface: net0 | ||||
|         firewall: false | ||||
|         bridge: PUB | ||||
|     - name: Add SRV NIC | ||||
|       community.general.proxmox_nic: | ||||
|         interface: net1 | ||||
|         firewall: false | ||||
|         bridge: SRV | ||||
|     - name: Configure cloud-init | ||||
|       community.general.proxmox_kvm: | ||||
|         update: true | ||||
|         ciuser: debian | ||||
|         sshkeys: "{{ ssh_public }}" | ||||
|         ipconfig: | ||||
|           ipconfig0: ip=dhcp,ip6=auto | ||||
|           ipconfig1: ip=dhcp | ||||
|     - name: Force all notified handlers to run | ||||
|       ansible.builtin.meta: flush_handlers | ||||
|  | ||||
|     # VM Configuration | ||||
|     - name: Resize root disk | ||||
|       community.general.proxmox_disk: | ||||
|         disk: scsi0 | ||||
|         size: 16G | ||||
|         state: resized | ||||
|     - name: Create data disk | ||||
|       community.general.proxmox_disk: | ||||
|         disk: scsi1 | ||||
|         backup: true | ||||
|         storage: nvme | ||||
|         size: 64 | ||||
|     - name: Update VM | ||||
|       community.general.proxmox_kvm: | ||||
|         update: true | ||||
|         agent: enabled=1 | ||||
|         tags: | ||||
|           - debian-12 | ||||
|           - managed | ||||
|         onboot: true | ||||
|         cores: 4 | ||||
|         memory: 8192 | ||||
|  | ||||
|     - name: Retart VM | ||||
|       community.general.proxmox_kvm: | ||||
|         state: restarted | ||||
|         timeout: 60 | ||||
|  | ||||
|   handlers: | ||||
|     # Initial boot | ||||
|     # For some reason debian cloud images don't use | ||||
|     # cloud-init for networking on first boot (cloud-init files | ||||
|     # are regenerated AFTER networking starts). But we need the | ||||
|     # hostname to be registered with DHCP later on so ¯\_(ツ)_/¯ | ||||
|     - name: Start VM | ||||
|       community.general.proxmox_kvm: | ||||
|         state: started | ||||
|       register: start | ||||
|     - name: Wait # Initial apt update, apt upgrade, cloud-init | ||||
|       ansible.builtin.wait_for: | ||||
|         timeout: 90 | ||||
							
								
								
									
										43
									
								
								infra/solidtime/0001_initialise_playbook.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								infra/solidtime/0001_initialise_playbook.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| - name: Initialise VM | ||||
|   hosts: solidtime | ||||
|   gather_facts: false | ||||
|   tasks: | ||||
|     - name: Wait for connection | ||||
|       ansible.builtin.wait_for_connection: | ||||
|         timeout: 300 | ||||
|  | ||||
|     - name: Install system packages | ||||
|       ansible.builtin.apt: | ||||
|         update_cache: true | ||||
|         pkg: | ||||
|           - qemu-guest-agent | ||||
|           - parted | ||||
|       become: true | ||||
|     - name: Enable qemu-guest-agent | ||||
|       ansible.builtin.systemd: | ||||
|         name: qemu-guest-agent | ||||
|         state: started | ||||
|         enabled: true | ||||
|       become: true | ||||
|  | ||||
|     - name: Create data partition | ||||
|       community.general.parted: | ||||
|         device: /dev/disk/by-path/pci-0000:00:05.0-scsi-0:0:0:1 | ||||
|         label: gpt | ||||
|         name: data | ||||
|         number: 1 | ||||
|         state: present | ||||
|       become: true | ||||
|     - name: Create data filesystem | ||||
|       community.general.filesystem: | ||||
|         dev: /dev/disk/by-path/pci-0000:00:05.0-scsi-0:0:0:1-part1 | ||||
|         fstype: ext4 | ||||
|       become: true | ||||
|     - name: Mount data partition | ||||
|       ansible.posix.mount: | ||||
|         src: /dev/disk/by-path/pci-0000:00:05.0-scsi-0:0:0:1-part1 | ||||
|         path: /var/lib/docker | ||||
|         fstype: ext4 | ||||
|         opts: rw,errors=remount-ro,x-systemd.growfs | ||||
|         state: mounted | ||||
|       become: true | ||||
							
								
								
									
										48
									
								
								infra/solidtime/0002_docker_playbook.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								infra/solidtime/0002_docker_playbook.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| - name: Install docker | ||||
|   hosts: solidtime | ||||
|   gather_facts: false | ||||
|   tasks: | ||||
|     - name: Wait for connection | ||||
|       ansible.builtin.wait_for_connection: | ||||
|         timeout: 300 | ||||
|  | ||||
|     - name: Install dependencies | ||||
|       ansible.builtin.apt: | ||||
|         update_cache: true | ||||
|         pkg: | ||||
|           - curl | ||||
|           - python3-apt | ||||
|           - gpg | ||||
|       become: true | ||||
|     - name: Add docker key | ||||
|       ansible.builtin.apt_key: | ||||
|         url: https://download.docker.com/linux/debian/gpg | ||||
|         keyring: /etc/apt/keyrings/docker.gpg | ||||
|       become: true | ||||
|     - name: Add docker repo | ||||
|       ansible.builtin.apt_repository: | ||||
|         repo: deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable | ||||
|       become: true | ||||
|     - name: Install docker | ||||
|       ansible.builtin.apt: | ||||
|         update_cache: true | ||||
|         pkg: | ||||
|           - docker-ce | ||||
|           - docker-ce-cli | ||||
|           - containerd.io | ||||
|           - docker-buildx-plugin | ||||
|           - docker-compose-plugin | ||||
|       become: true | ||||
|     - name: Add user to docker group | ||||
|       ansible.builtin.user: | ||||
|         user: debian | ||||
|         groups: | ||||
|           - docker | ||||
|         append: true | ||||
|       become: true | ||||
|     - name: Enable docker | ||||
|       ansible.builtin.systemd: | ||||
|         name: docker | ||||
|         state: started | ||||
|         enabled: true | ||||
|       become: true | ||||
							
								
								
									
										65
									
								
								infra/solidtime/0003_solidtime_playbook.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								infra/solidtime/0003_solidtime_playbook.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| - name: Deploy app | ||||
|   hosts: solidtime | ||||
|   gather_facts: false | ||||
|   vars: | ||||
|     app: solidtime | ||||
|   tasks: | ||||
|     - name: Wait for connection | ||||
|       ansible.builtin.wait_for_connection: | ||||
|         timeout: 300 | ||||
|  | ||||
|     - name: Check if project exists | ||||
|       ansible.builtin.stat: | ||||
|         path: "$HOME/{{ app }}" | ||||
|       register: project | ||||
|     - name: Docker compose down | ||||
|       when: project.stat.exists | ||||
|       community.docker.docker_compose_v2: | ||||
|         project_src: "$HOME/{{ app }}" | ||||
|         state: absent | ||||
|     - name: Copy project | ||||
|       ansible.builtin.copy: | ||||
|         src: "./{{ app }}" | ||||
|         dest: "$HOME" | ||||
|         mode: "0744" | ||||
|  | ||||
|     - name: Replace APP_KEY secret | ||||
|       ansible.builtin.replace: | ||||
|         path: "$HOME/{{ app }}/laravel.env" | ||||
|         regexp: "APP_KEY_VALUE" | ||||
|         replace: "{{ lookup('infisical.vault.read_secrets', project_id=infisical_project, env_slug='prod', | ||||
|                             path='/solidtime', secret_name='APP_KEY')['value'] }}" | ||||
|     - name: Replace SMTP Password secret | ||||
|       ansible.builtin.replace: | ||||
|         path: "$HOME/{{ app }}/laravel.env" | ||||
|         regexp: "SMTP_PASSWORD_VALUE" | ||||
|         replace: "{{ lookup('ansible.builtin.env', 'SMTP_PASSWORD') }}" | ||||
|     - name: Replace PASSPORT_PRIVATE_KEY secret | ||||
|       ansible.builtin.replace: | ||||
|         path: "$HOME/{{ app }}/laravel.env" | ||||
|         regexp: "PASSPORT_PRIVATE_KEY_VALUE" | ||||
|         replace: "{{ lookup('infisical.vault.read_secrets', project_id=infisical_project, env_slug='prod', | ||||
|                             path='/solidtime', secret_name='PASSPORT_PRIVATE_KEY')['value'] }}" | ||||
|     - name: Replace PASSPORT_PUBLIC_KEY secret | ||||
|       ansible.builtin.replace: | ||||
|         path: "$HOME/{{ app }}/laravel.env" | ||||
|         regexp: "PASSPORT_PUBLIC_KEY_VALUE" | ||||
|         replace: "{{ lookup('infisical.vault.read_secrets', project_id=infisical_project, env_slug='prod', | ||||
|                             path='/solidtime', secret_name='PASSPORT_PUBLIC_KEY')['value'] }}" | ||||
|     - name: Replace DB Password secret (app) | ||||
|       ansible.builtin.replace: | ||||
|         path: "$HOME/{{ app }}/laravel.env" | ||||
|         regexp: "DB_PASSWORD_VALUE" | ||||
|         replace: "{{ lookup('infisical.vault.read_secrets', project_id=infisical_project, env_slug='prod', | ||||
|                             path='/solidtime', secret_name='DB_PASSWORD')['value'] }}" | ||||
|  | ||||
|     - name: Replace DB Password secret (db) | ||||
|       ansible.builtin.replace: | ||||
|         path: "$HOME/{{ app }}/.env" | ||||
|         regexp: "DB_PASSWORD_VALUE" | ||||
|         replace: "{{ lookup('infisical.vault.read_secrets', project_id=infisical_project, env_slug='prod', | ||||
|                             path='/solidtime', secret_name='DB_PASSWORD')['value'] }}" | ||||
|  | ||||
|     - name: Docker compose up | ||||
|       community.docker.docker_compose_v2: | ||||
|         project_src: "$HOME/{{ app }}" | ||||
							
								
								
									
										7
									
								
								infra/solidtime/solidtime/.env
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								infra/solidtime/solidtime/.env
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| APP_DOMAIN=solidtime.koval.net | ||||
| DB_DATABASE=solidtime | ||||
| DB_USERNAME=solidtime | ||||
| FORWARD_APP_PORT=8000 | ||||
| FORWARD_DB_PORT=5432 | ||||
| DB_PASSWORD=DB_PASSWORD_VALUE | ||||
| SOLIDTIME_IMAGE_TAG=latest | ||||
							
								
								
									
										1
									
								
								infra/solidtime/solidtime/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								infra/solidtime/solidtime/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| !*.env | ||||
							
								
								
									
										96
									
								
								infra/solidtime/solidtime/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								infra/solidtime/solidtime/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| services: | ||||
|   app: | ||||
|     restart: always | ||||
|     image: "solidtime/solidtime:${SOLIDTIME_IMAGE_TAG:-latest}" | ||||
|     user: "1000:1000" | ||||
|     ports: | ||||
|       - '${FORWARD_APP_PORT:-8000}:8000' | ||||
|     networks: | ||||
|       - internal | ||||
|     volumes: | ||||
|       - "app-storage:/var/www/html/storage" | ||||
|       - "./logs:/var/www/html/storage/logs" | ||||
|       - "app-storage-app:/var/www/html/storage/app" | ||||
|     environment: | ||||
|       CONTAINER_MODE: http | ||||
|       AUTO_DB_MIGRATE: "true" | ||||
|     healthcheck: | ||||
|       test: [ "CMD-SHELL", "curl --fail http://localhost:8000/health-check/up || exit 1" ] | ||||
|     env_file: | ||||
|       - laravel.env | ||||
|     depends_on: | ||||
|       - database | ||||
|   scheduler: | ||||
|     restart: always | ||||
|     image: "solidtime/solidtime:${SOLIDTIME_IMAGE_TAG:-latest}" | ||||
|     user: "1000:1000" | ||||
|     networks: | ||||
|       - internal | ||||
|     volumes: | ||||
|       - "app-storage:/var/www/html/storage" | ||||
|       - "./logs:/var/www/html/storage/logs" | ||||
|       - "app-storage-app:/var/www/html/storage/app" | ||||
|     environment: | ||||
|       CONTAINER_MODE: scheduler | ||||
|     healthcheck: | ||||
|       test: [ "CMD-SHELL", "supervisorctl status scheduler:scheduler_00" ] | ||||
|     env_file: | ||||
|       - laravel.env | ||||
|     depends_on: | ||||
|       - database | ||||
|   queue: | ||||
|     restart: always | ||||
|     image: "solidtime/solidtime:${SOLIDTIME_IMAGE_TAG:-latest}" | ||||
|     user: "1000:1000" | ||||
|     networks: | ||||
|       - internal | ||||
|     volumes: | ||||
|       - "app-storage:/var/www/html/storage" | ||||
|       - "./logs:/var/www/html/storage/logs" | ||||
|       - "app-storage-app:/var/www/html/storage/app" | ||||
|     environment: | ||||
|       CONTAINER_MODE: worker | ||||
|       WORKER_COMMAND: "php /var/www/html/artisan queue:work" | ||||
|     healthcheck: | ||||
|       test: [ "CMD-SHELL", "supervisorctl status worker:worker_00" ] | ||||
|     env_file: | ||||
|       - laravel.env | ||||
|     depends_on: | ||||
|       - database | ||||
|   database: | ||||
|     restart: always | ||||
|     image: 'postgres:15' | ||||
| #    ports: | ||||
| #      - '${FORWARD_DB_PORT:-5432}:5432' | ||||
|     environment: | ||||
|       PGPASSWORD: '${DB_PASSWORD:-secret}' | ||||
|       POSTGRES_DB: '${DB_DATABASE}' | ||||
|       POSTGRES_USER: '${DB_USERNAME}' | ||||
|       POSTGRES_PASSWORD: '${DB_PASSWORD:-secret}' | ||||
|     volumes: | ||||
|       - 'database-storage:/var/lib/postgresql/data' | ||||
|     networks: | ||||
|       - internal | ||||
|     healthcheck: | ||||
|       test: | ||||
|         - CMD | ||||
|         - pg_isready | ||||
|         - '-q' | ||||
|         - '-d' | ||||
|         - '${DB_DATABASE}' | ||||
|         - '-U' | ||||
|         - '${DB_USERNAME}' | ||||
|       retries: 3 | ||||
|       timeout: 5s | ||||
|   gotenberg: | ||||
|     image: gotenberg/gotenberg:8 | ||||
|     networks: | ||||
|       - internal | ||||
|     healthcheck: | ||||
|       test: [ "CMD", "curl", "--silent", "--fail", "http://localhost:3000/health" ] | ||||
| networks: | ||||
|   internal: | ||||
| volumes: | ||||
|   database-storage: | ||||
|   app-storage: | ||||
|   app-storage-app: | ||||
							
								
								
									
										47
									
								
								infra/solidtime/solidtime/laravel.env
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								infra/solidtime/solidtime/laravel.env
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| APP_NAME="solidtime" | ||||
| VITE_APP_NAME="solidtime" | ||||
| APP_ENV="production" | ||||
| APP_DEBUG="false" | ||||
| APP_URL="https://solidtime.koval.net" | ||||
| APP_FORCE_HTTPS="true" | ||||
| APP_ENABLE_REGISTRATION="false" | ||||
| TRUSTED_PROXIES="10.4.0.1/32" | ||||
|  | ||||
| # Authentication | ||||
| APP_KEY="APP_KEY_VALUE" | ||||
| PASSPORT_PRIVATE_KEY="PASSPORT_PRIVATE_KEY_VALUE" | ||||
| PASSPORT_PUBLIC_KEY="PASSPORT_PUBLIC_KEY_VALUE" | ||||
| SUPER_ADMINS="gleb@koval.net" | ||||
|  | ||||
| # Logging | ||||
| LOG_CHANNEL="stderr_daily" | ||||
| LOG_LEVEL="debug" | ||||
|  | ||||
| # Database | ||||
| DB_CONNECTION="pgsql" | ||||
| DB_HOST="database" | ||||
| DB_PORT="5432" | ||||
| DB_SSLMODE="require" | ||||
| DB_DATABASE="solidtime" | ||||
| DB_USERNAME="solidtime" | ||||
| DB_PASSWORD="DB_PASSWORD_VALUE" | ||||
|  | ||||
| # Mail | ||||
| MAIL_MAILER="smtp" | ||||
| MAIL_HOST="mx.koval.net" | ||||
| MAIL_PORT="587" | ||||
| MAIL_ENCRYPTION="tls" | ||||
| MAIL_FROM_ADDRESS="no-reply@koval.net" | ||||
| MAIL_FROM_NAME="solidtime" | ||||
| MAIL_USERNAME="no-reply@koval.net" | ||||
| MAIL_PASSWORD="SMTP_PASSWORD_VALUE" | ||||
|  | ||||
| # Queue | ||||
| QUEUE_CONNECTION="database" | ||||
|  | ||||
| # File storage | ||||
| FILESYSTEM_DISK="local" | ||||
| PUBLIC_FILESYSTEM_DISK="public" | ||||
|  | ||||
| # Services | ||||
| GOTENBERG_URL="http://gotenberg:3000" | ||||
		Reference in New Issue
	
	Block a user