From e32ff933da48e404fb48bebb626e34f38e2d6d2a Mon Sep 17 00:00:00 2001 From: Steven <106664555+stevius10@users.noreply.github.com> Date: Tue, 5 Aug 2025 21:40:34 +0200 Subject: [PATCH 01/13] proxmox9.0/changeable-network-share-storage (#53) * convenience in recurse deployment * adjust to default user and group * changed for ext. mount; needs ext to be created * config as default group * append proxmox 9.0 * configure default groups * configure default groups * allow different storage types --- .gitignore | 3 +-- README.md | 12 ++++++---- base/default.yml | 2 +- base/roles/base/tasks/main.yml | 12 +++++++--- base/roles/base/vars/main.yml | 10 ++++---- config.env | 2 +- config/attributes/default.rb | 2 +- config/libraries/common.rb | 34 +++++++++++++++++++++++----- libs/assistant/attributes/default.rb | 10 ++++---- libs/bridge/attributes/default.rb | 2 +- libs/broker/attributes/default.rb | 2 +- libs/proxy/attributes/default.rb | 2 +- libs/share/attributes/default.rb | 2 +- 13 files changed, 63 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index fb180bb..640f707 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ **/.git/ -config.json - +local/config.json local/*.hash .DS_Store diff --git a/README.md b/README.md index bc3f8dd..7832527 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ -[![Build Status](https://github.com/stevius10/Proxmox-GitOps/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/stevius10/Proxmox-GitOps/actions/workflows/build.yml) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Build Status](https://github.com/stevius10/Proxmox-GitOps/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/stevius10/Proxmox-GitOps/actions/workflows/build.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) + +[![PVE 8.4](https://img.shields.io/badge/PVE-8.4-orange)](https://www.proxmox.com/) [![PVE 9.0](https://img.shields.io/badge/PVE-9.0-orange)](https://www.proxmox.com/) + ## Table of Contents - [Overview](#overview) @@ -61,7 +63,7 @@ This system implements stateless infrastructure management on Proxmox VE, ensuri ### Requirements - Docker -- Proxmox VE 8.4 +- Proxmox VE 8.4-9.0 - Proxmox API token - See [Wiki](https://github.com/stevius10/Proxmox-GitOps/wiki) for recommendations @@ -130,8 +132,8 @@ package 'apache2' file '/var/www/html/index.html' do content "

Hello from #{Env.get(node, 'login')}

" mode '0644' - owner 'app' # see base/roles/base/tasks/main.yml - group 'app' # each container is configured identically + owner 'app' # see base/roles/base/tasks/main.yml + group 'config' # each container is configured identically end Common.application 'apache2' # provided by convention diff --git a/base/default.yml b/base/default.yml index be1e98d..2b83f88 100644 --- a/base/default.yml +++ b/base/default.yml @@ -118,7 +118,7 @@ - name: Set shared mounts set_fact: - mounts: "{{ mounts | default({}) | combine({ 'mp' ~ idx: 'local:' ~ item.split(':')[1] ~ ',mp=/share/' ~ (item.split(':')[0] if item.split(':')[0] != 'share' else '') }) }}" + mounts: "{{ mounts | default({}) | combine({ ('mp' ~ idx): (item.split(':')[0] ~ ':' ~ item.split(':')[1] ~ ',mp=/share/' ~ (item.split(':')[2] if item.split(':')[2] != 'share' else '') ) }) }}" loop: "{{ (mount | default('')).split(',') | select('match', '^.+:.+$') }}" loop_control: index_var: idx diff --git a/base/roles/base/tasks/main.yml b/base/roles/base/tasks/main.yml index 618da86..0fe1871 100644 --- a/base/roles/base/tasks/main.yml +++ b/base/roles/base/tasks/main.yml @@ -13,14 +13,20 @@ name: "{{ default_packages }}" state: present + - name: Create default groups + group: + name: "{{ item }}" + state: present + loop: "{{ default_groups }}" + - name: Create default users user: name: "{{ item.name }}" shell: "{{ item.shell | default('/bin/bash') }}" groups: "{{ item.groups | default(omit) }}" - create_home: yes + create_home: "{{ item.create_home | default(true) }}" state: present - loop: "{{ users }}" + loop: "{{ default_users }}" register: created_users - name: Configure Sudo @@ -74,7 +80,7 @@ path: "/app" state: directory owner: app - group: app + group: config mode: 0755 - name: SSH configuration diff --git a/base/roles/base/vars/main.yml b/base/roles/base/vars/main.yml index 8cc7b17..959f564 100644 --- a/base/roles/base/vars/main.yml +++ b/base/roles/base/vars/main.yml @@ -8,10 +8,12 @@ default_packages: - ansible-core - ansible -users: - - { name: "app", create_home: false } - - { name: "config", groups: ["root", "sudo"], create_home: true } - - { name: "user", shell: "/bin/bash", groups: ["root", "sudo"], create_home: true } +default_groups: ["config"] + +default_users: + - { name: "app", groups: ["config"], create_home: false } + - { name: "config", groups: ["config", "root", "sudo"], create_home: true } + - { name: "user", shell: "/bin/bash", groups: ["config", "root", "sudo"], create_home: true } ssh_users: - config diff --git a/config.env b/config.env index 98213aa..8334307 100644 --- a/config.env +++ b/config.env @@ -5,5 +5,5 @@ CORES=4 MEMORY=6144 SWAP=1024 DISK=local-lvm:16 -MOUNT=share:32 +MOUNT=local:16:share,ext:32:ext BOOT=yes \ No newline at end of file diff --git a/config/attributes/default.rb b/config/attributes/default.rb index 14d9ccf..5102b4c 100644 --- a/config/attributes/default.rb +++ b/config/attributes/default.rb @@ -3,7 +3,7 @@ default['key'] = ENV['KEY'].to_s.presence ? ENV['KEY'] : "/share/.ssh/#{node['id']}" default['git']['app']['user'] = 'app' -default['git']['app']['group'] = 'app' +default['git']['app']['group'] = 'config' default['git']['dir']['install'] = '/app/git' default['git']['dir']['data'] = '/app/git/data' diff --git a/config/libraries/common.rb b/config/libraries/common.rb index 16633f1..a3c1f1a 100644 --- a/config/libraries/common.rb +++ b/config/libraries/common.rb @@ -12,8 +12,8 @@ def self.packages(ctx, *pkgs, action: :install) def self.directories(ctx, dirs, opts = {}) dirs = Array(dirs) - owner = opts[:owner] || 'root' - group = opts[:group] || 'root' + owner = opts[:owner] || 'app' + group = opts[:group] || 'config' mode = opts[:mode] || '0755' recursive = opts.key?(:recursive) ? opts[:recursive] : true recreate = opts[:recreate] || false @@ -113,10 +113,6 @@ def self.request(uri, user: nil, pass: nil, headers: {}, method: Net::HTTP::Get, expect ? false : response end - def self.latest(url) - request(url).body[/title>.*?v?([0-9]+\.[0-9]+(?:\.[0-9]+)?)/, 1].to_s || "latest" - end - def self.download(ctx, path, url:, owner: 'root', group: 'root', mode: '0644', action: :create) ctx.remote_file path do source url.respond_to?(:call) ? lazy { url.call } : url @@ -127,6 +123,32 @@ def self.download(ctx, path, url:, owner: 'root', group: 'root', mode: '0644', a end end + def self.snapshot(ctx, dir, snapshot_dir: '/share/snapshot', restore: false) + cookbook = ctx.cookbook_name + timestamp = Time.now.strftime('%H%M-%d%m%y') + file = File.join(snapshot_dir, "#{cookbook}-#{timestamp}.tar.gz") + + if restore + latest = Dir[File.join(snapshot_dir, "#{cookbook}-*.tar.gz")].max_by { |f| File.mtime(f) } + + ctx.execute "common_restore_snapshot_#{dir}" do + command "tar -xzf #{latest} -C #{File.dirname(dir)}" + only_if { latest && ::File.exist?(latest) } + end + latest + else + ctx.execute "common_create_snapshot_#{dir}" do + command "rm -f #{File.join(snapshot_dir, "#{cookbook}-*.tar.gz")} && tar -czf #{file} -C #{File.dirname(dir)} #{File.basename(dir)}" + only_if { ::Dir.exist?(dir) } + end + file + end + end + + def self.latest(url) + request(url).body[/title>.*?v?([0-9]+\.[0-9]+(?:\.[0-9]+)?)/, 1].to_s || "latest" + end + # Utility def self.wait(condition = nil, timeout: 20, sleep_interval: 5, &block) diff --git a/libs/assistant/attributes/default.rb b/libs/assistant/attributes/default.rb index f0c9eb6..cf761c3 100644 --- a/libs/assistant/attributes/default.rb +++ b/libs/assistant/attributes/default.rb @@ -1,9 +1,9 @@ -default['ip'] = "#{ENV['IP']}" +default['ip'] = "#{ENV['IP']}" -default['app']['user'] = 'app' -default['app']['group'] = 'app' +default['app']['user'] = 'app' +default['app']['group'] = 'config' -default['homeassistant']['dir']['venv'] = '/app/venv' +default['homeassistant']['dir']['venv'] = '/app/venv' default['homeassistant']['dir']['config'] = '/app/homeassistant' -default['configurator']['dir'] = '/app/configurator' \ No newline at end of file +default['configurator']['dir'] = '/app/configurator' \ No newline at end of file diff --git a/libs/bridge/attributes/default.rb b/libs/bridge/attributes/default.rb index 6f81745..e0a116e 100644 --- a/libs/bridge/attributes/default.rb +++ b/libs/bridge/attributes/default.rb @@ -1,5 +1,5 @@ default['app']['user'] = 'app' -default['app']['group'] = 'app' +default['app']['group'] = 'config' default['bridge']['port'] = 8080 default['bridge']['serial'] = Env.get(node, 'serial') || '/dev/serial/by-id/' diff --git a/libs/broker/attributes/default.rb b/libs/broker/attributes/default.rb index 1d011ec..91b009c 100644 --- a/libs/broker/attributes/default.rb +++ b/libs/broker/attributes/default.rb @@ -1,7 +1,7 @@ default['ip'] = "#{ENV['IP']}" default['app']['user'] = 'app' -default['app']['group'] = 'app' +default['app']['group'] = 'config' default['broker']['port'] = 1883 diff --git a/libs/proxy/attributes/default.rb b/libs/proxy/attributes/default.rb index 1d9ac5d..690f539 100644 --- a/libs/proxy/attributes/default.rb +++ b/libs/proxy/attributes/default.rb @@ -1,7 +1,7 @@ default['ip'] = ENV['IP'] default['app']['user'] = 'app' -default['app']['group'] = 'app' +default['app']['group'] = 'config' default['proxy']['config']['domain'] = 'lan' default['proxy']['dir']['app'] = '/app/proxy' diff --git a/libs/share/attributes/default.rb b/libs/share/attributes/default.rb index b764cb4..f889234 100644 --- a/libs/share/attributes/default.rb +++ b/libs/share/attributes/default.rb @@ -1,7 +1,7 @@ include_attribute 'config::default' default['mount'] = ENV['MOUNT'].to_s.split(',').each_with_object({}) do |entry, hash| - name, size = entry.split(':') + storage, size, name = entry.split(':') hash[name.strip] = { 'path' => name == 'share' ? '/share' : "/share/#{name}", 'size' => size.to_i From d8a1bdcac95e96a85f5ac097ae27b8c98248e358 Mon Sep 17 00:00:00 2001 From: Steven <106664555+stevius10@users.noreply.github.com> Date: Wed, 6 Aug 2025 22:17:41 +0200 Subject: [PATCH 02/13] dev/1.1 (#54) * convenience in recurse deployment * adjust to default user and group * changed for ext. mount; needs ext to be created * config as default group * append proxmox 9.0 * configure default groups * configure default groups * allow different storage types * catch runtime update from references * adjusted to different passing approach * refactored mount behavior * refactored mount behavior * enhanced alongside root user integration * example data filler * remain proxmox8.4 token based option * redefine share as repeatable application logic * fail-resistence root user access req. due pve9 kernel * encapsulate proxmox api * use of root user capabilities if available * fix container os api call * reuse generic pipelines * wait condition for os download if missing * simplify samba logic * add root user api token to trade off * typo --- .gitea/workflows/pipeline.yml | 5 +- README.md | 2 + base/.gitea/workflows/action.yml | 3 +- base/default.yml | 156 ++++++++++++------ base/roles/mount/tasks/main.yml | 3 +- config.env | 6 +- config/libraries/common.rb | 27 ++- config/recipes/repo.rb | 23 +++ .../pipeline_generic_pipeline.yml.erb | 0 .../templates/pipeline_generic_sync.yml.erb | 25 +++ libs/bridge/.config.json | 4 - libs/bridge/.gitea/workflows/pipeline.yml | 14 -- libs/bridge/attributes/default.rb | 4 +- libs/bridge/config.env | 2 +- libs/bridge/recipes/default.rb | 96 +++-------- libs/broker/.gitea/workflows/pipeline.yml | 14 -- libs/proxy/.gitea/workflows/pipeline.yml | 14 -- libs/proxy/recipes/default.rb | 43 +---- libs/share/attributes/default.rb | 10 +- libs/share/recipes/default.rb | 32 +--- libs/share/templates/smb.conf.erb | 11 +- local/.config.json | 4 +- 22 files changed, 248 insertions(+), 250 deletions(-) rename libs/assistant/.gitea/workflows/pipeline.yml => config/templates/pipeline_generic_pipeline.yml.erb (100%) create mode 100644 config/templates/pipeline_generic_sync.yml.erb delete mode 100644 libs/bridge/.config.json delete mode 100644 libs/bridge/.gitea/workflows/pipeline.yml delete mode 100644 libs/broker/.gitea/workflows/pipeline.yml delete mode 100644 libs/proxy/.gitea/workflows/pipeline.yml diff --git a/.gitea/workflows/pipeline.yml b/.gitea/workflows/pipeline.yml index cf31f81..5269aa9 100644 --- a/.gitea/workflows/pipeline.yml +++ b/.gitea/workflows/pipeline.yml @@ -72,9 +72,8 @@ jobs: run: | tar -c config -cz . | \ ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${id}" "config@${ip}" \ - 'sudo tar xz -C /tmp && sudo env IP="'"${ip}"'" ID="'"${id}"'" MOUNT="'"${mount}"'" LOGIN="'"${login}"'" PASSWORD="'"${password}"'" \ + 'sudo tar xz -C /tmp && sudo env IP="'"${ip}"'" ID="'"${id}"'" LOGIN="'"${login}"'" PASSWORD="'"${password}"'" \ cinc-client -l info --local-mode --chef-license accept --config-option cookbook_path="[\"'"/tmp/config"'\", \"'"/tmp/config/libs"'\"]" -o share' - if: ${{ gitea.ref == 'refs/heads/release' }} config: runs-on: [ "shell" ] @@ -92,7 +91,7 @@ jobs: login: ${{ vars.LOGIN }} password: ${{ vars.PASSWORD }} run: | - git -C config submodule update --init --recursive + git -C config submodule update --remote --recursive tar -c config -cz . | \ ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${id}" "config@${ip}" \ 'sudo tar xz -C /tmp && sudo env \ diff --git a/README.md b/README.md index 7832527..466b92c 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,8 @@ This system implements stateless infrastructure management on Proxmox VE, ensuri - **Git Convention vs. Infrastructure State:** Uses Git as a state engine rather than versioning in volatile, stateless contexts. Monorepository representation, however, encapsulates the entire infrastructure as a self-contained asset suited for version control. +- **API Token Restriction vs. Automation:** With Proxmox 9, stricter privilege separation prevents privileged containers from mounting shares via API token; automation capabilities, however, are mainly within the root user context. As a consequence, root user-based API access takes precedence over token-based authentication. + ## Usage ### Requirements diff --git a/base/.gitea/workflows/action.yml b/base/.gitea/workflows/action.yml index 4189e1c..6cebea2 100644 --- a/base/.gitea/workflows/action.yml +++ b/base/.gitea/workflows/action.yml @@ -43,12 +43,13 @@ runs: ansible-playbook default.yml -e "ip=${{ inputs.ip }}" -e "id=${{ inputs.id }}" \ -e "hostname=${{ inputs.hostname }}" -e "cores=${{ inputs.cores }}" -e "memory=${{ inputs.memory }}" \ -e "swap=${{ inputs.swap }}" -e "disk=${{ inputs.disk }}" -e "boot=${{ inputs.boot }}" \ - -e "mount=${{ inputs.mount }}" -e "share=${{ inputs.share }}" + -e "mount=${{ inputs.mount }}" -e "share=${{ env.PROXMOX_PASSWORD && inputs.share || 'false' }}" env: HOST: ${{ vars.HOST }} LOGIN: ${{ vars.LOGIN }} PASSWORD: ${{ vars.PASSWORD }} PROXMOX_HOST: ${{ vars.PROXMOX_HOST }} PROXMOX_USER: ${{ vars.PROXMOX_USER }} + PROXMOX_PASSWORD: ${{ vars.PROXMOX_PASSWORD }} PROXMOX_TOKEN: ${{ vars.PROXMOX_TOKEN }} PROXMOX_SECRET: ${{ vars.PROXMOX_SECRET }} \ No newline at end of file diff --git a/base/default.yml b/base/default.yml index 2b83f88..327b1a4 100644 --- a/base/default.yml +++ b/base/default.yml @@ -3,9 +3,21 @@ hosts: localhost gather_facts: no vars: + proxmox_credentials: &proxmox_credentials + api_host: "{{ PROXMOX_HOST }}" + api_user: "{{ PROXMOX_USER }}" + api_password: "{{ PROXMOX_PASSWORD | default(omit) }}" + api_token_id: "{{ (PROXMOX_PASSWORD | default('') == '') | ternary(PROXMOX_TOKEN, omit) }}" + api_token_secret: "{{ (PROXMOX_PASSWORD | default('') == '') | ternary(PROXMOX_SECRET, omit) }}" + api_port: 8006 + node: "pve" os: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst" keys_dir: "/share/.ssh" + tasks: + + # Preparation + - name: Check container configuration ansible.builtin.stat: path: "config.json" @@ -21,11 +33,34 @@ ansible.builtin.set_fact: PROXMOX_HOST: "{{ lookup('env', 'PROXMOX_HOST') }}" PROXMOX_USER: "{{ lookup('env', 'PROXMOX_USER') }}" + PROXMOX_PASSWORD: "{{ lookup('env', 'PROXMOX_PASSWORD') }}" PROXMOX_TOKEN: "{{ lookup('env', 'PROXMOX_TOKEN') }}" PROXMOX_SECRET: "{{ lookup('env', 'PROXMOX_SECRET') }}" when: - (config_file.stat.exists and config.proxmox is defined) or (not config_file.stat.exists) + - name: Get Proxmox ticket + uri: + url: "https://{{ PROXMOX_HOST }}:8006/api2/json/access/ticket" + method: POST + body: + username: "{{ PROXMOX_USER }}" + password: "{{ PROXMOX_PASSWORD }}" + body_format: json + validate_certs: no + register: proxmox_login + when: PROXMOX_PASSWORD is defined and PROXMOX_PASSWORD != '' + + - name: Set Proxmox request headers + set_fact: + proxmox_auth: >- + {{ (PROXMOX_PASSWORD | default('') == '') | ternary( + {'Authorization': 'PVEAPIToken=' + PROXMOX_USER + '!' + PROXMOX_TOKEN + '=' + PROXMOX_SECRET}, + {'Cookie': 'PVEAuthCookie=' + proxmox_login.json.data.ticket, + 'CSRFPreventionToken': proxmox_login.json.data.CSRFPreventionToken}) }} + + # Key handling + - name: Ensure container keys directory on host ansible.builtin.file: path: "{{ keys_dir }}" @@ -37,14 +72,11 @@ type: ed25519 force: false + # Container deletion + - name: Check container existence community.general.proxmox_vm_info: - api_host: "{{ PROXMOX_HOST }}" - api_user: "{{ PROXMOX_USER }}" - api_token_id: "{{ PROXMOX_TOKEN }}" - api_token_secret: "{{ PROXMOX_SECRET }}" - api_port: 8006 - node: "pve" + <<: *proxmox_credentials vmid: "{{ id }}" register: container_info delegate_to: localhost @@ -56,13 +88,8 @@ - name: Stop container community.general.proxmox: - api_host: "{{ PROXMOX_HOST }}" - api_user: "{{ PROXMOX_USER }}" - api_token_id: "{{ PROXMOX_TOKEN }}" - api_token_secret: "{{ PROXMOX_SECRET }}" - api_port: 8006 + <<: *proxmox_credentials vmid: "{{ id }}" - node: "pve" state: stopped force: yes delegate_to: localhost @@ -73,8 +100,7 @@ uri: url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/lxc/{{ id }}/status/current" method: GET - headers: - Authorization: "PVEAPIToken={{ PROXMOX_USER }}!{{ PROXMOX_TOKEN }}={{ PROXMOX_SECRET }}" + headers: "{{ proxmox_auth }}" validate_certs: no register: container_status until: container_status.json.data.status == "stopped" @@ -86,13 +112,8 @@ - name: Remove container community.general.proxmox: - api_host: "{{ PROXMOX_HOST }}" - api_user: "{{ PROXMOX_USER }}" - api_token_id: "{{ PROXMOX_TOKEN }}" - api_token_secret: "{{ PROXMOX_SECRET }}" - api_port: 8006 + <<: *proxmox_credentials vmid: "{{ id }}" - node: "pve" state: absent delegate_to: localhost ignore_errors: yes @@ -103,8 +124,7 @@ uri: url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/lxc/{{ id }}/status/current" method: GET - headers: - Authorization: "PVEAPIToken={{ PROXMOX_USER }}!{{ PROXMOX_TOKEN }}={{ PROXMOX_SECRET }}" + headers: "{{ proxmox_auth }}" validate_certs: no register: container_status until: @@ -116,26 +136,40 @@ delegate_to: localhost when: container_exists - - name: Set shared mounts + # Container creation + + - name: Init mounts + set_fact: + mounts: "{{ mounts | default({}) }}" + + - name: Set mounts set_fact: - mounts: "{{ mounts | default({}) | combine({ ('mp' ~ idx): (item.split(':')[0] ~ ':' ~ item.split(':')[1] ~ ',mp=/share/' ~ (item.split(':')[2] if item.split(':')[2] != 'share' else '') ) }) }}" - loop: "{{ (mount | default('')).split(',') | select('match', '^.+:.+$') }}" + mounts: >- + {{ mounts | default({}) | combine({('mp'~idx): + ((item.split(':')[:-1] | join(':')) ~ ',mp=' ~ (item.split(':')[-1]) ~ ',ro=0,acl=1') }) }} + loop: "{{ mount.split(',') | select('search', ':') }}" loop_control: index_var: idx when: - mount is defined - - (share | default(false, true) | bool) - - (mount | trim | length) > 0 + - share | default(true) | bool + - mount | trim | length > 0 + + - name: Set passthrough + set_fact: + mounts: "{{ mounts | combine({ ('dev' ~ idx): item }) }}" + loop: "{{ mount.split(',') | select('match', '^/[^:]+$') }}" + loop_control: + index_var: idx + when: + - mount is defined + - share | default(true) | bool + - mount | trim | length > 0 - name: Create container - community.general.proxmox: - api_host: "{{ PROXMOX_HOST }}" - api_user: "{{ PROXMOX_USER }}" - api_token_id: "{{ PROXMOX_TOKEN }}" - api_token_secret: "{{ PROXMOX_SECRET }}" - api_port: 8006 + community.general.proxmox: &create_container + <<: *proxmox_credentials vmid: "{{ id }}" - node: "pve" hostname: "{{ hostname }}" ostemplate: "{{ os }}" cores: "{{ cores }}" @@ -143,22 +177,57 @@ pubkey: "{{ lookup('file', [keys_dir, id ~ '.pub'] | path_join) }}" swap: "{{ swap }}" disk: "{{ disk }}" + mounts: >- + {{ (mounts if mounts and (PROXMOX_PASSWORD is defined and PROXMOX_PASSWORD != '') else omit) }} + features: >- + {{ (['mount=cifs'] if (share | default(false)) and (mount | default('') | trim != '') + and (PROXMOX_PASSWORD is defined and PROXMOX_PASSWORD != '') else omit) }} + unprivileged: "{{ (share | default(false) and mount | default('') | trim != '') | ternary(false, true) }}" netif: net0: "name=eth0,gw=192.168.178.1,ip={{ ip }}/24,bridge=vmbr0" - mounts: "{{ mounts if (share | default(false, true) | bool) else {} }}" - unprivileged: >- - {{ true if (share | default(false) | bool) else false - if (mount is defined and (mount | trim | length > 0)) else true }} onboot: "{{ boot }}" state: present delegate_to: localhost + register: container_creation + ignore_errors: true + + - name: Download container os if missing + uri: + url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/storage/local/download-url" + method: POST + headers: "{{ proxmox_auth | combine({'Content-Type': 'application/json'}) }}" + body_format: json + body: + url: "http://download.proxmox.com/images/system/{{ os.split('/')[-1] }}" + filename: "{{ os.split('/')[-1] }}" + content: "vztmpl" + validate_certs: no + when: + - container_creation.failed + - os in container_creation.msg | default('') + register: os_download + + - name: Wait for os to be downloaded + uri: + url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/storage/local/content" + method: GET + headers: "{{ proxmox_auth }}" + validate_certs: no + register: os_available + until: os in (os_available.json.data | map(attribute='volid') | list) + retries: 10 + delay: 6 + when: os_download is defined + + - name: Retry create container with os downloaded + community.general.proxmox: *create_container + when: os_available is defined and os in (os_available.json.data | map(attribute='volid') | list) - name: Wait for container to be created uri: url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/lxc/{{ id }}/config" method: GET - headers: - Authorization: "PVEAPIToken={{ PROXMOX_USER }}!{{ PROXMOX_TOKEN }}={{ PROXMOX_SECRET }}" + headers: "{{ proxmox_auth }}" validate_certs: no register: container_status until: @@ -171,14 +240,9 @@ - name: Start container community.general.proxmox: - api_host: "{{ PROXMOX_HOST }}" - api_user: "{{ PROXMOX_USER }}" - api_token_id: "{{ PROXMOX_TOKEN }}" - api_token_secret: "{{ PROXMOX_SECRET }}" - api_port: 8006 + <<: *proxmox_credentials vmid: "{{ id }}" hostname: "{{ hostname }}" - node: "pve" state: started delegate_to: localhost diff --git a/base/roles/mount/tasks/main.yml b/base/roles/mount/tasks/main.yml index 673c09a..826adda 100644 --- a/base/roles/mount/tasks/main.yml +++ b/base/roles/mount/tasks/main.yml @@ -31,4 +31,5 @@ label: "{{ item }}" - name: Restart mount - ansible.builtin.command: mount -a \ No newline at end of file + ansible.builtin.command: mount -a + ignore_errors: yes # avoid permission error if token usage \ No newline at end of file diff --git a/config.env b/config.env index 8334307..8fc4ba4 100644 --- a/config.env +++ b/config.env @@ -4,6 +4,6 @@ HOSTNAME=config CORES=4 MEMORY=6144 SWAP=1024 -DISK=local-lvm:16 -MOUNT=local:16:share,ext:32:ext -BOOT=yes \ No newline at end of file +DISK=local-lvm:16 # adjust if needed +MOUNT=/mnt/ext/config:/share,/mnt/ext/files:/share/files # adjust +BOOT=yes diff --git a/config/libraries/common.rb b/config/libraries/common.rb index a3c1f1a..d94eaa2 100644 --- a/config/libraries/common.rb +++ b/config/libraries/common.rb @@ -113,6 +113,27 @@ def self.request(uri, user: nil, pass: nil, headers: {}, method: Net::HTTP::Get, expect ? false : response end + def self.proxmox(uri, node, path, expect: true) + host = Env.get(node, 'proxmox_host') + user = Env.get(node, 'proxmox_user') + pass = Env.get(node, 'proxmox_password') + token = Env.get(node, 'proxmox_token') + secret = Env.get(node, 'proxmox_secret') + + url = "https://#{host}:8006/api2/json/#{path}" + if pass && !pass.empty? + response = request("https://#{host}:8006/api2/json/access/ticket", method: Net::HTTP::Post, + body: URI.encode_www_form(username: user, password: pass), headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }) + raise "[#{__method__}] login: #{response.code} #{response.message}" unless response.is_a?(Net::HTTPSuccess) + headers = { 'Cookie' => "PVEAuthCookie=#{JSON.parse(login.body)['data']['ticket']}" } + else + headers = { 'Authorization' => "PVEAPIToken=#{user}!#{token}=#{secret}" } + end + + res = request(url, headers: headers, expect: expect) + expect ? JSON.parse(res.body)['data'] : res + end + def self.download(ctx, path, url:, owner: 'root', group: 'root', mode: '0644', action: :create) ctx.remote_file path do source url.respond_to?(:call) ? lazy { url.call } : url @@ -123,14 +144,14 @@ def self.download(ctx, path, url:, owner: 'root', group: 'root', mode: '0644', a end end - def self.snapshot(ctx, dir, snapshot_dir: '/share/snapshot', restore: false) + def self.snapshot(ctx, dir, snapshot_dir: '/share/snapshots', restore: false) cookbook = ctx.cookbook_name timestamp = Time.now.strftime('%H%M-%d%m%y') file = File.join(snapshot_dir, "#{cookbook}-#{timestamp}.tar.gz") if restore latest = Dir[File.join(snapshot_dir, "#{cookbook}-*.tar.gz")].max_by { |f| File.mtime(f) } - + ctx.execute "common_restore_snapshot_#{dir}" do command "tar -xzf #{latest} -C #{File.dirname(dir)}" only_if { latest && ::File.exist?(latest) } @@ -200,6 +221,8 @@ def self.create_dir(ctx, dir, owner, group, mode, recursive) recursive recursive action :create end + rescue => e + Chef::Log.warn("Skipping #{dir}: #{e}") end def self.delete_dir(ctx, dir) diff --git a/config/recipes/repo.rb b/config/recipes/repo.rb index 98b67cb..4387146 100644 --- a/config/recipes/repo.rb +++ b/config/recipes/repo.rb @@ -134,6 +134,29 @@ action :run end + directory "#{path_destination}/.gitea" do + action :create + end + + template "#{path_destination}/.gitea/sync.yml" do + source 'pipeline_generic_sync.yml.erb' + owner node['git']['app']['user'] + group node['git']['app']['group'] + mode '0644' + action :create + not_if { File.exist?("#{path_destination}/.gitea/sync.yml") } + end + + template "#{path_destination}/.gitea/pipeline.yml" do + source 'pipeline_generic_pipeline.yml.erb' + owner node['git']['app']['user'] + group node['git']['app']['group'] + mode '0644' + action :create + only_if { repository.include?('libs/') and File.exist?("#{path_destination}/config.env") } + not_if { File.exist?("#{path_destination}/.gitea/pipeline.yml") } + end + if monorepo submodules = repositories.reject { |r| r == "./" } # without itself Chef::Log.info("#{repository} (monorepository): referencing #{submodules}") diff --git a/libs/assistant/.gitea/workflows/pipeline.yml b/config/templates/pipeline_generic_pipeline.yml.erb similarity index 100% rename from libs/assistant/.gitea/workflows/pipeline.yml rename to config/templates/pipeline_generic_pipeline.yml.erb diff --git a/config/templates/pipeline_generic_sync.yml.erb b/config/templates/pipeline_generic_sync.yml.erb new file mode 100644 index 0000000..26d884d --- /dev/null +++ b/config/templates/pipeline_generic_sync.yml.erb @@ -0,0 +1,25 @@ +on: + workflow_dispatch: + push: + branches: [ main ] + +jobs: + include: + runs-on: shell + steps: + - name: Checkout monorepo + uses: https://gitea.com/actions/checkout@v4 + with: + repository: 'main/config' + ref: 'main' + path: 'config' + + - name: Sync submodule + run: | + cd monorepo + git submodule update --remote + git add $(git submodule status | awk '{print $2}') + git diff --cached --quiet || git commit -m "[sync] main/$repo updated [skip ci]" + git push origin main + env: + repo: ${{ gitea.repository }} \ No newline at end of file diff --git a/libs/bridge/.config.json b/libs/bridge/.config.json deleted file mode 100644 index 4c885df..0000000 --- a/libs/bridge/.config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "serial": "", - "adapter": "" -} \ No newline at end of file diff --git a/libs/bridge/.gitea/workflows/pipeline.yml b/libs/bridge/.gitea/workflows/pipeline.yml deleted file mode 100644 index ff44609..0000000 --- a/libs/bridge/.gitea/workflows/pipeline.yml +++ /dev/null @@ -1,14 +0,0 @@ -on: - workflow_dispatch: - push: - branches: [ release, main, develop ] - -jobs: - include: - runs-on: shell - steps: - - id: init - uses: main/config/.gitea/workflows@main - with: - repo: ${{ gitea.repository }} - ref: ${{ gitea.ref_name }} diff --git a/libs/bridge/attributes/default.rb b/libs/bridge/attributes/default.rb index e0a116e..83dcdb7 100644 --- a/libs/bridge/attributes/default.rb +++ b/libs/bridge/attributes/default.rb @@ -2,8 +2,8 @@ default['app']['group'] = 'config' default['bridge']['port'] = 8080 -default['bridge']['serial'] = Env.get(node, 'serial') || '/dev/serial/by-id/' -default['bridge']['adapter'] = Env.get(node, 'adapter') || '' +default['bridge']['adapter'] = Env.get(node, 'adapter') || 'zstack' # overwrite +default['bridge']['serial'] = Env.get(node, 'serial') || '/dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_1c27822ced5fec11a1d52e5f25bfaa52-if00-port0' default['bridge']['dir'] = '/app/bridge' default['bridge']['data'] = "#{node['bridge']['dir']}/data" diff --git a/libs/bridge/config.env b/libs/bridge/config.env index 7a6cf03..67e39e2 100644 --- a/libs/bridge/config.env +++ b/libs/bridge/config.env @@ -5,5 +5,5 @@ CORES=2 MEMORY=2048 SWAP=512 DISK=local-lvm:8 -MOUNT=share +MOUNT=share,/dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_1c27822ced5fec11a1d52e5f25bfaa52-if00-port0 BOOT=yes \ No newline at end of file diff --git a/libs/bridge/recipes/default.rb b/libs/bridge/recipes/default.rb index f81cfc4..4174c25 100644 --- a/libs/bridge/recipes/default.rb +++ b/libs/bridge/recipes/default.rb @@ -2,6 +2,12 @@ Common.directories(self, [node['bridge']['dir'], node['bridge']['data']], owner: node['app']['user'], group: node['app']['group']) +group 'dialout' do + action :modify + members [node['app']['user']] + append true +end + execute 'setup_node' do command 'curl -fsSL https://deb.nodesource.com/setup_20.x | bash -' not_if 'dpkg -l | grep -q nodejs' @@ -11,64 +17,40 @@ package "nodejs" -group 'dialout' do - action :modify - members [node['app']['user']] - append true -end - execute 'enable_corepack' do command 'corepack enable && corepack prepare pnpm --activate' not_if 'which pnpm' end -z2m_file = Common.download(self, "/tmp/zigbee2mqtt.zip", - url: -> { ver = Common.latest('https://github.com/Koenkk/zigbee2mqtt/releases/latest') - "https://github.com/Koenkk/zigbee2mqtt/archive/refs/tags/#{ver}.zip" }, - owner: node['git']['app']['user'], - group: node['git']['app']['group'], - mode: '0644' -) -z2m_file.notifies :stop, "service[zigbee2mqtt]", :immediately if resources("service[zigbee2mqtt]") rescue nil -z2m_file.notifies :run, 'execute[create_backup]', :immediately -z2m_file.notifies :run, 'execute[zigbee2mqtt_extract]', :immediately -z2m_file.notifies :run, 'execute[install_dependencies]', :delayed -z2m_file.notifies :run, 'execute[zigbee2mqtt_build]', :delayed +installed_version = ::File.exist?("#{node['bridge']['dir']}/.version") ? ::File.read("#{node['bridge']['dir']}/.version").strip : nil +Chef::Log.info("installed version: #{installed_version}") -execute 'create_backup' do - command "tar -czf #{node['bridge']['dir']}/backup_$(date +%Y%m%d%H%M%S).tar.gz -C #{node['bridge']['dir']} . && find #{node['bridge']['dir']} -name 'backup_*.tar.gz' -type f | head -n -3 | xargs rm -f || true" - user node['app']['user'] - group node['app']['group'] - cwd node['bridge']['dir'] - only_if { ::Dir.exist?("#{node['bridge']['dir']}/node_modules") } - action :nothing -end +latest_version = Common.latest('https://github.com/Koenkk/zigbee2mqtt/releases/latest') +Chef::Log.info("latest version: #{latest_version}") if latest_version -execute 'zigbee2mqtt_extract' do - command lazy { "unzip -o #{"/tmp/zigbee2mqtt.zip"} -d #{node['bridge']['dir']} && mv #{node['bridge']['dir']}/zigbee2mqtt*/* #{node['bridge']['dir']}/ && rm -rf #{node['bridge']['dir']}/zigbee2mqtt" } - user node['app']['user'] - group node['app']['group'] - only_if { ::File.exist?("/tmp/zigbee2mqtt.zip") } - action :nothing -end +latest_version = false unless installed_version.nil? || Gem::Version.new(latest_version) > Gem::Version.new(installed_version) + +Common.download(self, "/tmp/zigbee2mqtt.zip", + url: "https://github.com/Koenkk/zigbee2mqtt/archive/refs/tags/#{latest_version}.zip", + owner: node['app']['user'], group: node['app']['group'], mode: '0644') +.notifies :stop, "service[zigbee2mqtt]", :immediately if resources("service[zigbee2mqtt]") rescue nil if latest_version -execute 'install_dependencies' do - command 'pnpm install --frozen-lockfile' +Common.snapshot(self, node['bridge']['data']) if latest_version + +execute 'zigbee2mqtt_files' do + command lazy { "unzip -o #{"/tmp/zigbee2mqtt.zip"} -d #{node['bridge']['dir']} && mv #{node['bridge']['dir']}/zigbee2mqtt*/* #{node['bridge']['dir']}/ && rm -rf #{node['bridge']['dir']}/zigbee2mqtt" } user node['app']['user'] group node['app']['group'] - cwd node['bridge']['dir'] - environment('HOME' => "/home/#{node['app']['user']}") - action :nothing + only_if { latest_version && ::File.exist?("/tmp/zigbee2mqtt.zip") } end execute 'zigbee2mqtt_build' do - command 'pnpm build' + command 'pnpm install --frozen-lockfile && pnpm build' user node['app']['user'] group node['app']['group'] cwd node['bridge']['dir'] environment('HOME' => "/home/#{node['app']['user']}") - action :nothing - notifies :restart, "service[zigbee2mqtt]", :delayed + only_if { latest_version } end template "#{node['bridge']['data']}/configuration.yaml" do @@ -86,40 +68,14 @@ broker_user: Env.get(node, 'login'), broker_password: Env.get(node, 'password') ) + only_if { latest_version } not_if { ::File.exist?("#{node['bridge']['data']}/configuration.yaml") } - notifies :restart, "service[zigbee2mqtt]", :delayed end +Common.snapshot(self, node['bridge']['data'], restore: true) + Common.application(self, 'zigbee2mqtt', user: node['app']['user'], cwd: node['bridge']['dir'], exec: "/usr/bin/node #{node['bridge']['dir']}/index.js", unit: { 'Service' => { 'Environment' => 'NODE_ENV=production', 'PermissionsStartOnly' => 'true', 'ExecStartPre' => "-/bin/chown #{node['app']['user']}:#{node['app']['group']} #{node['bridge']['serial']}" } } ) - -# removed due root user permissions: -# -# ruby_block 'proxmox_config' do -# block do -# require 'net/http' -# require 'openssl' -# require 'json' -# -# proxmox_host = Env.get(node, 'proxmox_host') -# proxmox_user = Env.get(node, 'proxmox_user') -# proxmox_token = Env.get(node, 'proxmox_token') -# proxmox_secret = Env.get(node, 'proxmox_secret') -# -# uri = URI("https://#{proxmox_host}:8006/api2/json/nodes/pve/lxc/#{ENV['ID']}/config") -# http = Net::HTTP.new(uri.hostname, uri.port) -# http.use_ssl = true -# http.verify_mode = OpenSSL::SSL::VERIFY_NONE -# -# req = Net::HTTP::Put.new(uri.request_uri) -# req['Authorization'] = "PVEAPIToken=#{proxmox_user}!#{proxmox_token}=#{proxmox_secret}" -# req['Content-Type'] = 'application/json' -# req.body = { dev0: node['bridge']['serial'] }.to_json -# -# http.request(req) -# end -# action :run -# end \ No newline at end of file diff --git a/libs/broker/.gitea/workflows/pipeline.yml b/libs/broker/.gitea/workflows/pipeline.yml deleted file mode 100644 index ff44609..0000000 --- a/libs/broker/.gitea/workflows/pipeline.yml +++ /dev/null @@ -1,14 +0,0 @@ -on: - workflow_dispatch: - push: - branches: [ release, main, develop ] - -jobs: - include: - runs-on: shell - steps: - - id: init - uses: main/config/.gitea/workflows@main - with: - repo: ${{ gitea.repository }} - ref: ${{ gitea.ref_name }} diff --git a/libs/proxy/.gitea/workflows/pipeline.yml b/libs/proxy/.gitea/workflows/pipeline.yml deleted file mode 100644 index ff44609..0000000 --- a/libs/proxy/.gitea/workflows/pipeline.yml +++ /dev/null @@ -1,14 +0,0 @@ -on: - workflow_dispatch: - push: - branches: [ release, main, develop ] - -jobs: - include: - runs-on: shell - steps: - - id: init - uses: main/config/.gitea/workflows@main - with: - repo: ${{ gitea.repository }} - ref: ${{ gitea.ref_name }} diff --git a/libs/proxy/recipes/default.rb b/libs/proxy/recipes/default.rb index 505b3b3..9925e22 100644 --- a/libs/proxy/recipes/default.rb +++ b/libs/proxy/recipes/default.rb @@ -2,45 +2,18 @@ Common.directories(self, [node['proxy']['dir']['app'], node['proxy']['dir']['logs']], owner: node['app']['user'], group: node['app']['group']) -ruby_block 'fetch_proxmox_containers' do +ruby_block 'proxmox_containers' do block do - require 'net/http' - require 'openssl' - require 'json' - - proxmox_host = Env.get(node, 'proxmox_host') - proxmox_user = Env.get(node, 'proxmox_user') - proxmox_token = Env.get(node, 'proxmox_token') - proxmox_secret = Env.get(node, 'proxmox_secret') - - def fetch_data(uri, user, token, secret) - http = Net::HTTP.new(uri.hostname, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - req = Net::HTTP::Get.new(uri.request_uri) - req['Authorization'] = "PVEAPIToken=#{user}!#{token}=#{secret}" - res = http.request(req) - raise "API-Fehler #{res.code}" unless res.is_a?(Net::HTTPSuccess) - JSON.parse(res.body)['data'] - end - - uri_lxc = URI("https://#{proxmox_host}:8006/api2/json/nodes/pve/lxc") - containers = fetch_data(uri_lxc, proxmox_user, proxmox_token, proxmox_secret) - # .select { |c| c['status'] == 'running' } - - proxy_hosts = containers.map do |c| - vmid = c['vmid'] - name = c['name'] - uri_config = URI("https://#{proxmox_host}:8006/api2/json/nodes/pve/lxc/#{vmid}/config") - config = fetch_data(uri_config, proxmox_user, proxmox_token, proxmox_secret) + domain = node['proxy']['config']['domain'] + node.run_state['proxy_hosts'] = Common.proxmox(URI, node, 'nodes/pve/lxc').map do |state| + vmid = state['vmid'] + name = state['name'] + config = Common.proxmox(URI, node, "nodes/pve/lxc/#{vmid}/config") ip = config['net0'] ? config['net0'].match(/ip=([\d\.]+)/)&.[](1) : "404" - "#{name}.#{node['proxy']['config']['domain']} #{ip}" + "#{name}.#{domain} #{ip}" end - - node.run_state['proxy_hosts'] = proxy_hosts - Chef::Log.info(proxy_hosts) + Chef::Log.info(node.run_state['proxy_hosts']) end - action :run end template "#{node['proxy']['dir']['app']}/Caddyfile" do diff --git a/libs/share/attributes/default.rb b/libs/share/attributes/default.rb index f889234..70f744a 100644 --- a/libs/share/attributes/default.rb +++ b/libs/share/attributes/default.rb @@ -1,9 +1,5 @@ include_attribute 'config::default' -default['mount'] = ENV['MOUNT'].to_s.split(',').each_with_object({}) do |entry, hash| - storage, size, name = entry.split(':') - hash[name.strip] = { - 'path' => name == 'share' ? '/share' : "/share/#{name}", - 'size' => size.to_i - } -end \ No newline at end of file +default['mount'] = [ + 'share:/share' +] diff --git a/libs/share/recipes/default.rb b/libs/share/recipes/default.rb index f7db09a..b05187c 100644 --- a/libs/share/recipes/default.rb +++ b/libs/share/recipes/default.rb @@ -3,39 +3,21 @@ Common.packages(self, %w[samba samba-common samba-client]) -node['mount'].each do |name| - path = (name == 'share' ? '/share' : "/share/#{name}") +id=100000 +group(login) { gid id; action :create } +user(login) { uid id; gid id; shell '/bin/false'; manage_home false; action :create } - directory path do - owner node['git']['app']['user'] - group node['git']['app']['group'] - mode '2775' - recursive true - action :create - end +execute "create_samba_#{login}" do + command "printf '#{password}\\n#{password}\\n' | smbpasswd -a -s #{login}" + not_if "pdbedit -L | grep -w #{login}" end template '/etc/samba/smb.conf' do source 'smb.conf.erb' - variables( - login: login, - user: node['git']['app']['user'], - group: node['git']['app']['group'], - share: node['mount'] - ) + variables(login: login, shares: node['mount']) notifies :restart, 'service[smb]' end -execute "create_user_#{login}" do - command "useradd --no-create-home --shell /bin/false #{login}" - not_if "id -u #{login}" -end - -execute "create_samba_#{login}" do - command "printf '#{password}\\n#{password}\\n' | smbpasswd -a -s #{login}" - not_if "pdbedit -L | grep -w #{login}" -end - service 'smb' do action [:enable, :start] end diff --git a/libs/share/templates/smb.conf.erb b/libs/share/templates/smb.conf.erb index b5e0a59..3329a3d 100644 --- a/libs/share/templates/smb.conf.erb +++ b/libs/share/templates/smb.conf.erb @@ -4,13 +4,14 @@ passdb backend = tdbsam map to guest = bad user usershare allow guests = no -<% @share.each do |name, config| %> +<% @shares.each do |entry| -%> + <% name, path = entry.split(':', 2) -%> [<%= name %>] - path = <%= config['path'] || "/share/#{name}" %> + path = <%= path %> valid users = <%= @login %> - force user = <%= @user %> - force group = <%= @group %> + force user = <%= @login %> + force group = <%= @login %> read only = no create mask = 0775 directory mask = 2775 -<% end %> +<% end -%> \ No newline at end of file diff --git a/local/.config.json b/local/.config.json index 9d0d47e..f1a1ff6 100644 --- a/local/.config.json +++ b/local/.config.json @@ -3,10 +3,8 @@ "proxmox": { "host": "", - "api": "", "user": "", - "token": "", - "secret": "" + "password": "" }, "login": "", From 51fe793161dce941bc6de2d96592309b610af198 Mon Sep 17 00:00:00 2001 From: Steven <106664555+stevius10@users.noreply.github.com> Date: Sat, 9 Aug 2025 07:05:19 +0200 Subject: [PATCH 03/13] dev/v1.1 (#55) * convenience in recurse deployment * adjust to default user and group * changed for ext. mount; needs ext to be created * config as default group * append proxmox 9.0 * configure default groups * configure default groups * allow different storage types * catch runtime update from references * adjusted to different passing approach * refactored mount behavior * refactored mount behavior * enhanced alongside root user integration * example data filler * remain proxmox8.4 token based option * redefine share as repeatable application logic * fail-resistence root user access req. due pve9 kernel * encapsulate proxmox api * use of root user capabilities if available * fix container os api call * reuse generic pipelines * wait condition for os download if missing * simplify samba logic * add root user api token to trade off * typo * added: log lib * changed: separated commons * adopt: lib changes * added: advanced samba configuration * changed: log level for readability * updated to current state * set example configuration * consistency * refactor node attributes * fix path * prepare gitea customization * clean code and enhanced debugging * changed: rename logs to avoid conflict * changed: consistent naming * fix: order * flexibler logging * fix assignments * fix loop setting variables * iterate array * pass node array to be processed in template --- .gitignore | 2 +- README.md | 24 +-- config.env | 2 +- config/attributes/default.rb | 22 ++- config/libraries/common.rb | 137 +---------------- config/libraries/env.rb | 30 ++-- config/libraries/logs.rb | 64 ++++++++ config/libraries/utils.rb | 139 ++++++++++++++++++ config/recipes/config.rb | 26 ++-- config/recipes/customize.rb | 1 + config/recipes/default.rb | 1 + config/recipes/git.rb | 6 +- config/recipes/prepare.rb | 4 +- config/recipes/repo.rb | 57 +++---- config/recipes/runner.rb | 10 +- .../templates/pipeline_generic_sync.yml.erb | 2 +- config/templates/repo_config.erb | 2 +- config/templates/runner.config.yaml.erb | 4 +- libs/assistant/recipes/default.rb | 2 +- libs/bridge/recipes/default.rb | 14 +- libs/proxy/recipes/default.rb | 6 +- libs/share/attributes/default.rb | 7 +- libs/share/recipes/default.rb | 29 +++- libs/share/templates/smb.conf.erb | 25 +++- local/run.sh | 2 +- 25 files changed, 356 insertions(+), 262 deletions(-) create mode 100644 config/libraries/logs.rb create mode 100644 config/libraries/utils.rb create mode 100644 config/recipes/customize.rb diff --git a/.gitignore b/.gitignore index 640f707..a65d4a9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ local/config.json local/*.hash .DS_Store -.idea \ No newline at end of file +.idea diff --git a/README.md b/README.md index 466b92c..f548e5e 100644 --- a/README.md +++ b/README.md @@ -108,24 +108,6 @@ DISK=local-lvm:8 BOOT=yes ``` -- Paste generic pipeline in container's `.gitea/workflows`: -```yaml -on: - workflow_dispatch: - push: - branches: [ release, main, develop ] - -jobs: - include: - runs-on: shell - steps: - - id: init - uses: main/config/.gitea/workflows@main - with: - repo: ${{ gitea.repository }} - ref: ${{ gitea.ref_name }} -``` - - Add your cookbook to the container definition root: ```ruby # libs/apache/recipes/default.rb @@ -138,14 +120,12 @@ file '/var/www/html/index.html' do group 'config' # each container is configured identically end -Common.application 'apache2' # provided by convention +Common.application(self, 'apache2') # provided by convention ``` - Optionally, use `Env.get()` and `Env.set()` to access Gitea environment variables. -- a) **Deploy**: Push to the `release` branch of a new repository - -- b) **Add to Monorepository**: Redeploy +- Add to Monorepository and redeploy. The container can be tested locally running `./local/run.sh [container]` (_wip_) diff --git a/config.env b/config.env index 8fc4ba4..bccbd62 100644 --- a/config.env +++ b/config.env @@ -5,5 +5,5 @@ CORES=4 MEMORY=6144 SWAP=1024 DISK=local-lvm:16 # adjust if needed -MOUNT=/mnt/ext/config:/share,/mnt/ext/files:/share/files # adjust +MOUNT=/mnt/ext:/share/files # adjust BOOT=yes diff --git a/config/attributes/default.rb b/config/attributes/default.rb index 5102b4c..62247f4 100644 --- a/config/attributes/default.rb +++ b/config/attributes/default.rb @@ -1,28 +1,34 @@ -default['host'] = ENV['IP'].to_s.presence ? ENV['IP'] : "127.0.0.1" default['id'] = ENV['ID'] + +default['host'] = ENV['IP'].to_s.presence ? ENV['IP'] : "127.0.0.1" default['key'] = ENV['KEY'].to_s.presence ? ENV['KEY'] : "/share/.ssh/#{node['id']}" default['git']['app']['user'] = 'app' default['git']['app']['group'] = 'config' +default['git']['conf']['customize'] = true + default['git']['dir']['install'] = '/app/git' default['git']['dir']['data'] = '/app/git/data' -default['git']['home'] = "/home/#{node['git']['app']['user']}/git" -default['git']['workspace'] = '/share/workspace' +default['git']['dir']['home'] = "/home/#{node['git']['app']['user']}/git" +default['git']['dir']['workspace'] = "/home/#{node['git']['app']['user']}/workspace" default['git']['port']['http'] = 8080 +default['git']['host']['http'] = "http://#{node['host']}:#{node['git']['port']['http']}" + default['git']['port']['ssh'] = 2222 -default['git']['version'] = "v1" -default['git']['host'] = "http://#{node['host']}:#{node['git']['port']['http']}" -default['git']['endpoint'] = "http://#{node['host']}:#{node['git']['port']['http']}/api/#{node['git']['version']}" +default['git']['host']['ssh'] = "#{node['host']}:#{node['git']['port']['ssh']}" + +default['git']['api']['version'] = "v1" +default['git']['api']['endpoint'] = "http://#{node['host']}:#{node['git']['port']['http']}/api/#{node['git']['api']['version']}" default['git']['org']['main'] = 'main' default['git']['org']['stage'] = 'stage' -default['git']['repo']['ssh'] = "#{node['host']}:#{node['git']['port']['ssh']}" default['runner']['dir']['install'] = '/app/runner' default['runner']['dir']['cache'] = '/tmp' default['runner']['file']['marker'] = "#{node['runner']['dir']['install']}/.runner" -default['runner']['labels'] = 'shell' + +default['runner']['conf']['label'] = 'shell' default['git']['repositories'] = [ "./", "./base", "./config/libraries", "./libs" ] diff --git a/config/libraries/common.rb b/config/libraries/common.rb index d94eaa2..63cde5f 100644 --- a/config/libraries/common.rb +++ b/config/libraries/common.rb @@ -80,139 +80,6 @@ def self.application(ctx, name, user: 'app', group: user, exec: nil, cwd: nil, u end end - def self.arch(node) - case node['kernel']['machine'].to_s - when /arm64|aarch64/ - 'arm64' - when /armv6|armv7l/ - 'armv7' - else - 'amd64' - end - end - - # Remote - - def self.request(uri, user: nil, pass: nil, headers: {}, method: Net::HTTP::Get, body: nil, expect: false) - req = method.new(u = URI(uri)) - req.basic_auth(user, pass) if user && pass - req.body = body if body - headers.each { |k, v| req[k] = v } - response = Net::HTTP.start(u.host, u.port, use_ssl: u.scheme == 'https') { |http| http.request(req) } - Chef::Log.info("[#{__method__}] request #{uri}: #{response.code} #{response.message}") - - if response.is_a?(Net::HTTPSuccess) - return expect ? true : response - end - if response.is_a?(Net::HTTPRedirection) - loc = response['location'] - loc = "#{u.scheme}://#{u.host}#{loc}" if loc&.start_with?('/') - return request(loc, user: user, pass: pass, headers: headers, method: method, body: body, expect: expect) - end - - expect ? false : response - end - - def self.proxmox(uri, node, path, expect: true) - host = Env.get(node, 'proxmox_host') - user = Env.get(node, 'proxmox_user') - pass = Env.get(node, 'proxmox_password') - token = Env.get(node, 'proxmox_token') - secret = Env.get(node, 'proxmox_secret') - - url = "https://#{host}:8006/api2/json/#{path}" - if pass && !pass.empty? - response = request("https://#{host}:8006/api2/json/access/ticket", method: Net::HTTP::Post, - body: URI.encode_www_form(username: user, password: pass), headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }) - raise "[#{__method__}] login: #{response.code} #{response.message}" unless response.is_a?(Net::HTTPSuccess) - headers = { 'Cookie' => "PVEAuthCookie=#{JSON.parse(login.body)['data']['ticket']}" } - else - headers = { 'Authorization' => "PVEAPIToken=#{user}!#{token}=#{secret}" } - end - - res = request(url, headers: headers, expect: expect) - expect ? JSON.parse(res.body)['data'] : res - end - - def self.download(ctx, path, url:, owner: 'root', group: 'root', mode: '0644', action: :create) - ctx.remote_file path do - source url.respond_to?(:call) ? lazy { url.call } : url - owner owner - group group - mode mode - action action - end - end - - def self.snapshot(ctx, dir, snapshot_dir: '/share/snapshots', restore: false) - cookbook = ctx.cookbook_name - timestamp = Time.now.strftime('%H%M-%d%m%y') - file = File.join(snapshot_dir, "#{cookbook}-#{timestamp}.tar.gz") - - if restore - latest = Dir[File.join(snapshot_dir, "#{cookbook}-*.tar.gz")].max_by { |f| File.mtime(f) } - - ctx.execute "common_restore_snapshot_#{dir}" do - command "tar -xzf #{latest} -C #{File.dirname(dir)}" - only_if { latest && ::File.exist?(latest) } - end - latest - else - ctx.execute "common_create_snapshot_#{dir}" do - command "rm -f #{File.join(snapshot_dir, "#{cookbook}-*.tar.gz")} && tar -czf #{file} -C #{File.dirname(dir)} #{File.basename(dir)}" - only_if { ::Dir.exist?(dir) } - end - file - end - end - - def self.latest(url) - request(url).body[/title>.*?v?([0-9]+\.[0-9]+(?:\.[0-9]+)?)/, 1].to_s || "latest" - end - - # Utility - - def self.wait(condition = nil, timeout: 20, sleep_interval: 5, &block) - return Kernel.sleep(condition) if condition.is_a?(Integer) - return Timeout.timeout(timeout) { block.call } if block_given? - raise ArgumentError unless condition - - Timeout.timeout(timeout) do - loop do - ok = false - if condition =~ %r{^https?://} - uri = URI(condition) - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = (uri.scheme == 'https') - http.verify_mode = OpenSSL::SSL::VERIFY_NONE if uri.scheme == 'https' - begin - res = http.get(uri.path.empty? ? '/' : uri.path) - ok = res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPRedirection) - rescue - ok = false - end - else - host_port = condition.include?('@') ? condition.split('@', 2).last : condition - host, port = host_port.split(':', 2) - port = (port || '80').to_i - begin - TCPSocket.new(host, port).close - ok = true - rescue - ok = false - end - end - break if ok - sleep sleep_interval - end - end - true - rescue Timeout::Error, StandardError - false - end - - # Helper - def self.create_dir(ctx, dir, owner, group, mode, recursive) ctx.directory dir do owner owner @@ -222,7 +89,7 @@ def self.create_dir(ctx, dir, owner, group, mode, recursive) action :create end rescue => e - Chef::Log.warn("Skipping #{dir}: #{e}") + Logs.warn("Skipping #{dir}: #{e}") end def self.delete_dir(ctx, dir) @@ -237,6 +104,4 @@ def self.sort_dir(dirs) Array(dirs).sort_by { |d| -d.count('/') } end - private_class_method :create_dir, :delete_dir - end diff --git a/config/libraries/env.rb b/config/libraries/env.rb index c8e1de3..df1da61 100644 --- a/config/libraries/env.rb +++ b/config/libraries/env.rb @@ -5,26 +5,26 @@ module Env def self.creds(node, login_key = 'login', password_key = 'password') - [login = ENV[login_key.upcase] || node[login_key.to_sym], pass = ENV[password_key.upcase] || node[password_key.to_sym]].tap { Chef::Log.info(mask(login)); Chef::Log.info(mask(pass)) } + [ Logs.assignment(login_key, (login=ENV[login_key.upcase] || node[login_key.to_sym])), + Logs.assignment(password_key, (password=ENV[password_key.upcase] || node[password_key.to_sym])) ] end def self.get(node, key) - Chef::Log.info("[#{__method__}] #{key}: #{mask(val = node[key].to_s.presence || ENV[key.to_s.upcase].presence || get_variable(node, key))}"); val + Logs.assignment(key, val=(node[key].to_s.presence || ENV[key.to_s.upcase].presence || get_variable(node, key))); val rescue => e - Chef::Log.warn("[#{__method__}] #{e.message} node[#{key}]: #{node[key].inspect} ENV[#{key}]: #{ENV[key.to_s.upcase].inspect}") + Logs.debug(:warn, "failed get '#{key}'", :node_key, node[key], :env_key, ENV[key.to_s.upcase]) end def self.get_variable(node, key) JSON.parse(request(node, key).body)['data'] rescue => e - Chef::Log.warn("[#{__method__}] #{e.message} failed get '#{key}' on #{endpoint(node)} node[#{key}]: #{node[key].inspect} ENV[#{key}]: #{ENV[key.to_s.upcase].inspect}") + Logs.debug(:warn, "failed get variable '#{key}'", :error, e.message, :endpoint, endpoint(node), :node_key, node[key], :env_key, ENV[key.to_s.upcase]) end def self.set_variable(node, key, val) request(node, key, { name: key, value: val.to_s }.to_json) rescue => e - Chef::Log.warn("[#{__method__}] #{e.message} failed set '#{key}' on #{endpoint(node)} node[#{key}]: #{node[key].inspect} ENV[#{key}]: #{ENV[key.to_s.upcase].inspect}") - raise + Logs.debug(:warn, "failed set #{Logs.mask(val)} for variable '#{key}'", :error, e.message, :endpoint, endpoint(node), :node_key, node[key], :env_key, ENV[key.to_s.upcase]) end class << self @@ -36,23 +36,21 @@ class << self end private_class_method def self.endpoint(node, port=or_default(node.dig('git', 'port', 'http'), '8080')) - or_default(node.dig('git', 'endpoint'), + or_default(node.dig('git', 'api', 'endpoint'), "http://#{or_default(node['host'].to_s.presence || ENV['HOST'].to_s.presence, '127.0.0.1')}:#{port}/api/#{or_default(node.dig('git', 'version'), 'v1')}") end private_class_method def self.request(node, key, body = nil) - uri = URI("#{endpoint(node)}/orgs/#{or_default(node.dig('git', 'repo', 'org'), 'main')}/actions/variables/#{key}") - (body ? [Net::HTTP::Put, Net::HTTP::Post] : [Net::HTTP::Get]).each do |m| - req = m.new(uri) + uri = URI("#{endpoint(node)}/orgs/#{or_default(node.dig('git', 'org', 'main'), 'main')}/actions/variables/#{key}") + (body ? [Net::HTTP::Put, Net::HTTP::Post] : [Net::HTTP::Get]).each do |method| + req = method.new(uri) req.basic_auth(*creds(node)) req['Content-Type'] = 'application/json' req.body = body if body - response = Net::HTTP.start(uri.host, uri.port) { |h| h.request(req) } - Chef::Log.info("[#{__method__}] request #{uri}: #{response.code} #{response.message}") - return response unless body && response.code.to_i == 404 + Logs.request(uri, response=(Net::HTTP.start(uri.host, uri.port) { |h| h.request(req) })) + return response unless body && response.code.to_i == 404 or Logs.request!(uri, response) end end - end class Object @@ -69,7 +67,3 @@ class NilClass def blank?; true; end def presence; nil; end end - -def mask(str) - str.to_s.length <= 2 ? '*' * str.to_s.length : "#{str[0]}#{'*' * (str.length - 2)}#{str[-1]}" -end \ No newline at end of file diff --git a/config/libraries/logs.rb b/config/libraries/logs.rb new file mode 100644 index 0000000..82524c3 --- /dev/null +++ b/config/libraries/logs.rb @@ -0,0 +1,64 @@ +module Logs + + FORMAT_WITH = "\e[1m[%s] %s (%s:%d)\e[0m" + FORMAT_NO = "%s (%s:%d)" + IGNORES = [__FILE__, %r{libraries}] + + def self.callsite + s = caller_locations(2,60) + s.find { |l| IGNORES.none? { |ig| ig.is_a?(Regexp) ? l.path =~ ig : l.path == ig } } || s.first + end + + def self.method_label(loc) + label = (loc.respond_to?(:label) ? loc.label : loc.to_s).sub(/block.*in /, '') + return nil if label == 'from_file' + label + end + + def self.log(level, msg, masks = []) + c = callsite + masks.each { |m| msg = mask(msg, m) } + label = method_label(c) + if label + Chef::Log.send(level, FORMAT_WITH % [label, msg, File.basename(c.path), c.lineno]) + else + Chef::Log.send(level, FORMAT_NO % [msg, File.basename(c.path), c.lineno]) + end + end + + def self.info(msg); log(:info, msg) end + def self.warn(msg); log(:warn, msg) end + def self.error(msg); log(:error, msg) end + def self.request(uri, response); info("request #{uri}: #{response.code} #{response.message}") end + def self.assignment(key, val); info("#{key}: #{mask(val)}"); return val end + + def self.debug(level, msg, *pairs) + ctx = pairs.flatten.each_slice(2).map { |k, v| "#{k}=#{v.inspect}" }.join(" ") + log(level, [msg, ctx].reject(&:empty?).join(" ")) + end + + def self.fail!(msg) + error(msg) + c = callsite + label = method_label(c) + raise(label ? "[#{label}] #{msg}" : msg) + end + + def self.request!(uri, response, msg="failed request") + warn(msg) + c = callsite + label = method_label(c) + s = "#{msg} (#{uri}: #{response.code} #{response.message})" + raise(label ? "[#{label}] #{s}" : s) + end + + def self.mask(str, term = nil) + return obfuscate(str) unless term + str.to_s.gsub(term.to_s, obfuscate(term.to_s)) + end + + def self.obfuscate(s) + s.length <= 2 ? '*' * s.length : "#{s[0]}#{'*'*(s.length-2)}#{s[-1]}" + end + +end \ No newline at end of file diff --git a/config/libraries/utils.rb b/config/libraries/utils.rb new file mode 100644 index 0000000..274d8c9 --- /dev/null +++ b/config/libraries/utils.rb @@ -0,0 +1,139 @@ +require 'net/http' +require 'uri' +require 'json' + +module Utils + + # General + + def self.wait(condition = nil, timeout: 20, sleep_interval: 5, &block) + return Kernel.sleep(condition) if condition.is_a?(Integer) + return Timeout.timeout(timeout) { block.call } if block_given? + ArgumentError unless condition + + Timeout.timeout(timeout) do + loop do + ok = false + if condition =~ %r{^https?://} + uri = URI(condition) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = (uri.scheme == 'https') + http.verify_mode = OpenSSL::SSL::VERIFY_NONE if uri.scheme == 'https' + begin + res = http.get(uri.path.empty? ? '/' : uri.path) + ok = res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPRedirection) + rescue + ok = false + end + else + host_port = condition.include?('@') ? condition.split('@', 2).last : condition + host, port = host_port.split(':', 2) + port = (port || '80').to_i + begin + TCPSocket.new(host, port).close + ok = true + rescue + ok = false + end + end + break if ok + sleep sleep_interval + end + end + true + rescue Timeout::Error, StandardError + false + end + + # System + + def self.arch(node) + case node['kernel']['machine'].to_s + when /arm64|aarch64/ + 'arm64' + when /armv6|armv7l/ + 'armv7' + else + 'amd64' + end + end + + def self.snapshot(ctx, dir, snapshot_dir: '/share/snapshots', name: ctx.cookbook_name, restore: false) + timestamp = Time.now.strftime('%H%M-%d%m%y') + file = File.join(snapshot_dir, "#{name}-#{timestamp}.tar.gz") + + if restore + latest = Dir[File.join(snapshot_dir, "#{name}-*.tar.gz")].max_by { |f| File.mtime(f) } + + ctx.execute "common_restore_snapshot_#{dir}" do + command "tar -xzf #{latest} -C #{File.dirname(dir)}" + only_if { latest && ::File.exist?(latest) } + end + latest + else + ctx.execute "common_create_snapshot_#{dir}" do + command "mkdir -p $(dirname #{file}) && tar -czf #{file} -C #{File.dirname(dir)} #{File.basename(dir)}" + only_if { ::Dir.exist?(dir) } + end + file + end + end + + def self.proxmox(uri, node, path, expect: true) + host = Env.get(node, 'proxmox_host') + user = Env.get(node, 'proxmox_user') + pass = Env.get(node, 'proxmox_password') + token = Env.get(node, 'proxmox_token') + secret = Env.get(node, 'proxmox_secret') + + url = "https://#{host}:8006/api2/json/#{path}" + if pass && !pass.empty? + response = request(uri="https://#{host}:8006/api2/json/access/ticket", method: Net::HTTP::Post, + body: URI.encode_www_form(username: user, password: pass), headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }) + Logs.request!("Proxmox ticket could not be retrieved", uri, response) unless response.is_a?(Net::HTTPSuccess) + headers = { 'Cookie' => "PVEAuthCookie=#{JSON.parse(login.body)['data']['ticket']}" } + else + headers = { 'Authorization' => "PVEAPIToken=#{user}!#{token}=#{secret}" } + end + + res = request(url, headers: headers, expect: expect) + expect ? JSON.parse(res.body)['data'] : res + end + + # Remote + + def self.request(uri, user: nil, pass: nil, headers: {}, method: Net::HTTP::Get, body: nil, expect: false) + req = method.new(u = URI(uri)) + req.basic_auth(user, pass) if user && pass + req.body = body if body + headers.each { |k, v| req[k] = v } + response = Net::HTTP.start(u.host, u.port, use_ssl: u.scheme == 'https') { |http| http.request(req) } + Logs.request(uri, response) + + if response.is_a?(Net::HTTPSuccess) + return expect ? true : response + end + if response.is_a?(Net::HTTPRedirection) + loc = response['location'] + loc = "#{u.scheme}://#{u.host}#{loc}" if loc&.start_with?('/') + return request(loc, user: user, pass: pass, headers: headers, method: method, body: body, expect: expect) + end + + expect ? false : response + end + + def self.download(ctx, path, url:, owner: 'root', group: 'root', mode: '0644', action: :create) + ctx.remote_file path do + source url.respond_to?(:call) ? lazy { url.call } : url + owner owner + group group + mode mode + action action + end + end + + def self.latest(url) + request(url).body[/title>.*?v?([0-9]+\.[0-9]+(?:\.[0-9]+)?)/, 1].to_s || "latest" + end + +end \ No newline at end of file diff --git a/config/recipes/config.rb b/config/recipes/config.rb index b477db6..084ddb3 100644 --- a/config/recipes/config.rb +++ b/config/recipes/config.rb @@ -1,5 +1,5 @@ ruby_block 'config_wait_http' do - block do Common.wait("127.0.0.1:#{node['git']['port']['http']}", timeout: 15, sleep_interval: 1) end + block do Utils.wait("127.0.0.1:#{node['git']['port']['http']}", timeout: 15, sleep_interval: 1) end end execute 'config_set_user' do @@ -14,7 +14,7 @@ fi $base create $create $user EOH - not_if { Common.request("#{node['git']['endpoint']}/user", user: Env.get(node, 'login'), pass: Env.get(node, 'password'), expect: true) } + not_if { Utils.request("#{node['git']['api']['endpoint']}/user", user: Env.get(node, 'login'), pass: Env.get(node, 'password'), expect: true) } end ruby_block 'config_set_key' do @@ -22,23 +22,23 @@ require 'json' login = Env.get(node, 'login') password = Env.get(node, 'password') - url = "#{node['git']['endpoint']}/admin/users/#{login}/keys" + url = "#{node['git']['api']['endpoint']}/admin/users/#{login}/keys" key = ::File.read("#{node['key']}.pub").strip - (JSON.parse(Common.request(url, user: login, pass: password).body) rescue []).each do |k| - Common.request("#{url}/#{k['id']}", method: Net::HTTP::Delete, user: login, pass: password) if k['key'] && k['key'].strip == key + (JSON.parse(Utils.request(url, user: login, pass: password).body) rescue []).each do |k| + Utils.request("#{url}/#{k['id']}", method: Net::HTTP::Delete, user: login, pass: password) if k['key'] && k['key'].strip == key end - result = Common.request(url, body: { title: "config-#{login}", key: key }.to_json, - user: login, pass: password, method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' }) - raise "Set new key failed (#{result.code}): #{result.body}" unless [201, 422].include?(result.code.to_i) + status_code = (response = Utils.request(url, body: { title: "config-#{login}", key: key }.to_json, + user: login, pass: password, method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' })).code.to_i + Logs.request!("Set key failed", url, response) unless [201, 422].include?(status_code) end action :run only_if { ::File.exist?("#{node['key']}.pub") } not_if do next false unless ::File.exist?("#{node['key']}.pub") begin - resp = Common.request("#{node['git']['endpoint']}/admin/users/#{Env.get(node, 'login')}/keys", user: Env.get(node, 'login'), pass: Env.get(node, 'password')) - (JSON.parse(resp.body) rescue []).any? { |k| k['key'] && k['key'].strip == ::File.read("#{node['key']}.pub").strip } + response = Utils.request("#{node['git']['api']['endpoint']}/admin/users/#{Env.get(node, 'login')}/keys", user: Env.get(node, 'login'), pass: Env.get(node, 'password')) + (JSON.parse(response.body) rescue []).any? { |k| k['key'] && k['key'].strip == ::File.read("#{node['key']}.pub").strip } end end end @@ -64,7 +64,7 @@ end ruby_block 'config_wait_ssh' do - block do Common.wait("#{Env.get(node, 'login')}@#{node['host']}:#{node['git']['port']['ssh']}") end + block do Utils.wait("#{Env.get(node, 'login')}@#{node['host']}:#{node['git']['port']['ssh']}") end end execute 'config_git_safe_directory' do @@ -91,12 +91,12 @@ ruby_block "config_git_org_#{org}" do block do require 'json' - status_code = (result = Common.request("#{node['git']['endpoint']}/orgs", + status_code = (response = Utils.request(uri="#{node['git']['api']['endpoint']}/orgs", method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' }, user: Env.get(node, 'login'), pass: Env.get(node, 'password'), body: { username: org }.to_json )).code.to_i - raise "HTTP #{status_code}: #{result.body}" unless [201, 409, 422].include? status_code + Logs.request!("Create organization '#{org}' failed", uri, response) unless [201, 409, 422].include? status_code end action :run end diff --git a/config/recipes/customize.rb b/config/recipes/customize.rb new file mode 100644 index 0000000..7dbc7e3 --- /dev/null +++ b/config/recipes/customize.rb @@ -0,0 +1 @@ +Chef::Log.info("") \ No newline at end of file diff --git a/config/recipes/default.rb b/config/recipes/default.rb index a44c90d..a4f03d8 100644 --- a/config/recipes/default.rb +++ b/config/recipes/default.rb @@ -3,3 +3,4 @@ include_recipe 'config::runner' include_recipe 'config::config' include_recipe 'config::repo' +include_recipe('config::customize') if node['git']['conf']['customize'] \ No newline at end of file diff --git a/config/recipes/git.rb b/config/recipes/git.rb index 94635e3..17fbe18 100644 --- a/config/recipes/git.rb +++ b/config/recipes/git.rb @@ -1,6 +1,6 @@ -Common.download(self, "#{node['git']['dir']['install']}/gitea", - url: -> { ver = Common.latest('https://github.com/go-gitea/gitea/releases/latest') - "https://github.com/go-gitea/gitea/releases/download/v#{ver}/gitea-#{ver}-linux-#{Common.arch(node)}" }, +Utils.download(self, "#{node['git']['dir']['install']}/gitea", + url: -> { ver = Utils.latest('https://github.com/go-gitea/gitea/releases/latest') + "https://github.com/go-gitea/gitea/releases/download/v#{ver}/gitea-#{ver}-linux-#{Utils.arch(node)}" }, owner: node['git']['app']['user'], group: node['git']['app']['group'], mode: '0755' ) diff --git a/config/recipes/prepare.rb b/config/recipes/prepare.rb index 4f05777..8905758 100644 --- a/config/recipes/prepare.rb +++ b/config/recipes/prepare.rb @@ -1,7 +1,7 @@ # Filesystem Common.directories(self, [ "/home/#{node['git']['app']['user']}", - "#{node['git']['home']}", + "#{node['git']['dir']['home']}", "#{node['git']['dir']['install']}", "#{node['git']['dir']['data']}", "#{node['git']['dir']['data']}/custom", @@ -11,7 +11,7 @@ "#{node['git']['dir']['data']}/custom/conf", "#{node['runner']['dir']['install']}", "#{::File.dirname(node['key'])}", - "#{node['git']['workspace']}" + "#{node['git']['dir']['workspace']}" ], owner: node['git']['app']['user'], group: node['git']['app']['group']) Common.packages(self, %w(git acl python3-pip ansible nodejs npm python3-proxmoxer)) diff --git a/config/recipes/repo.rb b/config/recipes/repo.rb index 4387146..1e3d6d5 100644 --- a/config/recipes/repo.rb +++ b/config/recipes/repo.rb @@ -3,7 +3,7 @@ home = "/home/#{node['git']['app']['user']}" source = ENV['PWD'] -destination = node['git']['workspace'] +destination = node['git']['dir']['workspace'] working = "#{destination}/workdir" Common.directories(self, [destination, working], recreate: true, @@ -20,12 +20,12 @@ path_working = "#{working}/#{name_repo}" path_destination = monorepo ? destination : File.expand_path(name_repo, destination) - Chef::Log.info("#{repository} (#{name_repo})") + Logs.info("#{repository} (#{name_repo})") ruby_block "repo_exists_#{name_repo}" do block do node.run_state["#{name_repo}_repo_exists"] = - (Common.request("#{node['git']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", + (Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", user: Env.get(node, 'login'), pass: Env.get(node, 'password')) ).code.to_i != 404 end @@ -34,8 +34,8 @@ execute "repo_exists_snapshot_create_#{name_repo}" do command <<-EOH - if git ls-remote ssh://#{node['git']['app']['user']}@#{node['git']['repo']['ssh']}/#{node['git']['org']['main']}/#{name_repo}.git HEAD | grep -q .; then - git clone --recurse-submodules ssh://#{node['git']['app']['user']}@#{node['git']['repo']['ssh']}/#{node['git']['org']['main']}/#{name_repo}.git #{path_working} + if git ls-remote ssh://#{node['git']['app']['user']}@#{node['git']['host']['ssh']}/#{node['git']['org']['main']}/#{name_repo}.git HEAD | grep -q .; then + git clone --recurse-submodules ssh://#{node['git']['app']['user']}@#{node['git']['host']['ssh']}/#{node['git']['org']['main']}/#{name_repo}.git #{path_working} cd #{path_working} && git submodule update --init --recursive find . -type d -name .git -exec rm -rf {} + else @@ -44,31 +44,31 @@ EOH user node['git']['app']['user'] environment 'HOME' => home - only_if { Chef::Log.info("[#{repository} (#{name_repo})]: delete repository after snapshot") + only_if { Logs.info("[#{repository} (#{name_repo})]: delete repository after snapshot") node.run_state["#{name_repo}_repo_exists"] } end ruby_block "repo_exists_reset_#{name_repo}" do block do - unless [204, 404].include?(status_code = (result = Common.request("#{node['git']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", + unless [204, 404].include?(status_code = (response = Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", method: Net::HTTP::Delete, user: Env.get(node, 'login'), pass: Env.get(node, 'password'))).code.to_i) - raise "Failed to delete #{name_repo} (#{status_code}): #{result.body}" + Logs.request!("Failed to delete #{name_repo}", uri, response) end end action :run only_if { node.run_state["#{name_repo}_repo_exists"] } end - Chef::Log.info("[#{repository} (#{name_repo})]: request repository") + Logs.info("[#{repository} (#{name_repo})]: request repository") ruby_block "repo_request_#{name_repo}" do block do require 'json' - (result = Common.request( - "#{node['git']['endpoint']}/admin/users/#{node['git']['org']['main']}/repos", + (response = Utils.request( + uri="#{node['git']['api']['endpoint']}/admin/users/#{node['git']['org']['main']}/repos", method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' }, user: Env.get(node, 'login'), pass: Env.get(node, 'password'), body: { name: name_repo, private: false, auto_init: false, default_branch: 'main' }.to_json - )).code.to_i == 201 or raise "Error creating repository '#{name_repo}': #{result.code} #{result.message} #{result.body}" + )).code.to_i == 201 or Logs.request!("Error creating repository '#{name_repo}'", uri, response) end action :run end @@ -91,7 +91,7 @@ only_if { ::File.directory?("#{path_destination}/.git") } end - Chef::Log.info("[#{repository} (#{name_repo})]: base commit") + Logs.info("[#{repository} (#{name_repo})]: base commit") execute "repo_git_empty_#{name_repo}" do command <<-EOH git commit --allow-empty -m "base commit [skip ci]" && git checkout -b release @@ -112,7 +112,7 @@ cwd path_destination user node['git']['app']['user'] environment 'HOME' => home - only_if { Chef::Log.info("[#{repository} (#{name_repo})]: snapshot commit") + only_if { Logs.info("[#{repository} (#{name_repo})]: snapshot commit") node.run_state["#{name_repo}_repo_exists"] } end @@ -134,32 +134,34 @@ action :run end - directory "#{path_destination}/.gitea" do + directory "#{path_destination}/.gitea/workflows" do action :create + recursive true end - template "#{path_destination}/.gitea/sync.yml" do + template "#{path_destination}/.gitea/workflows/sync.yml" do source 'pipeline_generic_sync.yml.erb' owner node['git']['app']['user'] group node['git']['app']['group'] mode '0644' action :create - not_if { File.exist?("#{path_destination}/.gitea/sync.yml") } + not_if { monorepo } + not_if { File.exist?("#{path_destination}/.gitea/workflows/sync.yml") } end - template "#{path_destination}/.gitea/pipeline.yml" do + template "#{path_destination}/.gitea/workflows/pipeline.yml" do source 'pipeline_generic_pipeline.yml.erb' owner node['git']['app']['user'] group node['git']['app']['group'] mode '0644' action :create only_if { repository.include?('libs/') and File.exist?("#{path_destination}/config.env") } - not_if { File.exist?("#{path_destination}/.gitea/pipeline.yml") } + not_if { File.exist?("#{path_destination}/.gitea/workflows/pipeline.yml") } end if monorepo submodules = repositories.reject { |r| r == "./" } # without itself - Chef::Log.info("#{repository} (monorepository): referencing #{submodules}") + Logs.info("#{repository} (monorepository): referencing #{submodules}") ruby_block 'repo_mono_submodule_rewritten' do block do @@ -178,7 +180,7 @@ path_module = submodule.sub(%r{^\./}, '') module_name = File.basename(path_module) - module_url = "#{node['git']['host']}/#{node['git']['org']['main']}/#{module_name}.git" + module_url = "#{node['git']['host']['http']}/#{node['git']['org']['main']}/#{module_name}.git" # delete module files in last ordered monorepository directory File.join(path_destination, path_module) do @@ -186,7 +188,7 @@ action :delete end - Chef::Log.info("#{repository} (monorepository): referencing #{path_module} (#{module_name})") + Logs.info("#{repository} (monorepository): referencing #{path_module} (#{module_name})") execute "repo_mono_submodule_references_#{module_name}" do cwd path_destination @@ -209,6 +211,7 @@ end # Repositories + execute "repo_push_#{name_repo}" do cwd path_destination user node['git']['app']['user'] @@ -244,11 +247,11 @@ ruby_block "repo_stage_fork_clean_#{name_repo}" do block do - if Common.request("#{node['git']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", + if Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", user: Env.get(node, 'login'), pass: Env.get(node, 'password')).code.to_i != 404 - status_code = (Common.request("#{node['git']['endpoint']}/repos/#{node['git']['org']['stage']}/#{name_repo}", + status_code = (response = Utils.request(uri="#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['stage']}/#{name_repo}", method: Net::HTTP::Delete, user: Env.get(node, 'login'), pass: Env.get(node, 'password'))).code.to_i - raise "Failed to clean test/#{name_repo} (#{status_code})" unless [204, 404].include?(status_code) + Logs.request!("Failed to clean test/#{name_repo} (#{status_code})", uri, response) unless [204, 404].include?(status_code) end end action :run @@ -256,12 +259,12 @@ ruby_block "repo_stage_fork_create_#{name_repo}" do block do - status_code = Common.request("#{node['git']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}/forks", + status_code = Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}/forks", method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' }, user: Env.get(node, 'login'), pass: Env.get(node, 'password'), body: { name: name_repo, organization: node['git']['org']['stage'] }.to_json ).code.to_i - raise "Forking to #{node['git']['org']['stage']}/#{name_repo} failed (#{status_code})" unless [201, 202].include?(status_code) + Logs.request!("Forking to #{node['git']['org']['stage']}/#{name_repo} failed", uri, response) unless [201, 202].include?(status_code) end action :run end diff --git a/config/recipes/runner.rb b/config/recipes/runner.rb index c0d67ca..d850c7c 100644 --- a/config/recipes/runner.rb +++ b/config/recipes/runner.rb @@ -1,8 +1,8 @@ Common.directories(self, node['runner']['dir']['install'], owner: node['git']['app']['user'], group: node['git']['app']['group']) -Common.download(self, "#{node['runner']['dir']['install']}/act_runner", - url: -> { ver = Common.latest('https://gitea.com/gitea/act_runner/releases/latest') - "https://gitea.com/gitea/act_runner/releases/download/v#{ver}/act_runner-#{ver}-linux-#{Common.arch(node)}" }, +Utils.download(self, "#{node['runner']['dir']['install']}/act_runner", + url: -> { ver = Utils.latest('https://gitea.com/gitea/act_runner/releases/latest') + "https://gitea.com/gitea/act_runner/releases/download/v#{ver}/act_runner-#{ver}-linux-#{Utils.arch(node)}" }, owner: node['git']['app']['user'], group: node['git']['app']['group'], mode: '0755' @@ -27,7 +27,7 @@ uri = URI("http://localhost:#{node['git']['port']['http']}") connected = 15.times.any? do begin - res = Common.request(uri) + res = Utils.request(uri) res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPRedirection) rescue Errno::ECONNREFUSED, SocketError false @@ -35,7 +35,7 @@ sleep 5 end end - raise 'Gitea not responding' unless connected + 'Gitea not responding' unless connected (token = Mixlib::ShellOut.new( "#{node['git']['dir']['install']}/gitea actions --config #{node['git']['dir']['install']}/app.ini generate-runner-token", diff --git a/config/templates/pipeline_generic_sync.yml.erb b/config/templates/pipeline_generic_sync.yml.erb index 26d884d..a3a1794 100644 --- a/config/templates/pipeline_generic_sync.yml.erb +++ b/config/templates/pipeline_generic_sync.yml.erb @@ -12,7 +12,7 @@ jobs: with: repository: 'main/config' ref: 'main' - path: 'config' + path: 'monorepo' - name: Sync submodule run: | diff --git a/config/templates/repo_config.erb b/config/templates/repo_config.erb index 47746ff..1371e00 100644 --- a/config/templates/repo_config.erb +++ b/config/templates/repo_config.erb @@ -5,7 +5,7 @@ logallrefupdates = true [remote "origin"] - url = ssh://<%= node['git']['app']['user'] %>@<%= node['git']['repo']['ssh'] %>/<%= node['git']['org']['main'] %>/<%= @repo %>.git + url = ssh://<%= node['git']['app']['user'] %>@<%= node['git']['host']['ssh'] %>/<%= node['git']['org']['main'] %>/<%= @repo %>.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "main"] diff --git a/config/templates/runner.config.yaml.erb b/config/templates/runner.config.yaml.erb index f19c5c9..1f126e9 100644 --- a/config/templates/runner.config.yaml.erb +++ b/config/templates/runner.config.yaml.erb @@ -1,5 +1,5 @@ name: <%= node['hostname'] %> -labels: ["<%= node['runner']['labels'] %>"] +labels: ["<%= node['runner']['conf']['label'] %>"] log: level: warn @@ -8,7 +8,7 @@ runner: file: .runner capacity: 1 timeout: 1h - labels: ["<%= node['runner']['labels'] %>"] + labels: ["<%= node['runner']['conf']['label'] %>"] cache: enabled: false diff --git a/libs/assistant/recipes/default.rb b/libs/assistant/recipes/default.rb index d7a65de..162585b 100644 --- a/libs/assistant/recipes/default.rb +++ b/libs/assistant/recipes/default.rb @@ -19,7 +19,7 @@ action :create end -Common.download(self, '/tmp/Python-3.13.5.tgz', url: 'https://www.python.org/ftp/python/3.13.5/Python-3.13.5.tgz') +Utils.download(self, '/tmp/Python-3.13.5.tgz', url: 'https://www.python.org/ftp/python/3.13.5/Python-3.13.5.tgz') bash 'install_python3135' do cwd '/tmp' diff --git a/libs/bridge/recipes/default.rb b/libs/bridge/recipes/default.rb index 4174c25..6bef7e4 100644 --- a/libs/bridge/recipes/default.rb +++ b/libs/bridge/recipes/default.rb @@ -23,19 +23,19 @@ end installed_version = ::File.exist?("#{node['bridge']['dir']}/.version") ? ::File.read("#{node['bridge']['dir']}/.version").strip : nil -Chef::Log.info("installed version: #{installed_version}") +Logs.info("installed version: #{installed_version}") -latest_version = Common.latest('https://github.com/Koenkk/zigbee2mqtt/releases/latest') -Chef::Log.info("latest version: #{latest_version}") if latest_version +latest_version = Utils.latest('https://github.com/Koenkk/zigbee2mqtt/releases/latest') +Logs.info("latest version: #{latest_version}") if latest_version latest_version = false unless installed_version.nil? || Gem::Version.new(latest_version) > Gem::Version.new(installed_version) -Common.download(self, "/tmp/zigbee2mqtt.zip", +Utils.download(self, "/tmp/zigbee2mqtt.zip", url: "https://github.com/Koenkk/zigbee2mqtt/archive/refs/tags/#{latest_version}.zip", owner: node['app']['user'], group: node['app']['group'], mode: '0644') .notifies :stop, "service[zigbee2mqtt]", :immediately if resources("service[zigbee2mqtt]") rescue nil if latest_version -Common.snapshot(self, node['bridge']['data']) if latest_version +Utils.snapshot(self, node['bridge']['data']) if latest_version execute 'zigbee2mqtt_files' do command lazy { "unzip -o #{"/tmp/zigbee2mqtt.zip"} -d #{node['bridge']['dir']} && mv #{node['bridge']['dir']}/zigbee2mqtt*/* #{node['bridge']['dir']}/ && rm -rf #{node['bridge']['dir']}/zigbee2mqtt" } @@ -49,7 +49,7 @@ user node['app']['user'] group node['app']['group'] cwd node['bridge']['dir'] - environment('HOME' => "/home/#{node['app']['user']}") + # environment('HOME' => "/home/#{node['app']['user']}") only_if { latest_version } end @@ -72,7 +72,7 @@ not_if { ::File.exist?("#{node['bridge']['data']}/configuration.yaml") } end -Common.snapshot(self, node['bridge']['data'], restore: true) +Utils.snapshot(self, node['bridge']['data'], restore: true) Common.application(self, 'zigbee2mqtt', user: node['app']['user'], cwd: node['bridge']['dir'], diff --git a/libs/proxy/recipes/default.rb b/libs/proxy/recipes/default.rb index 9925e22..a98ad4d 100644 --- a/libs/proxy/recipes/default.rb +++ b/libs/proxy/recipes/default.rb @@ -5,14 +5,14 @@ ruby_block 'proxmox_containers' do block do domain = node['proxy']['config']['domain'] - node.run_state['proxy_hosts'] = Common.proxmox(URI, node, 'nodes/pve/lxc').map do |state| + node.run_state['proxy_hosts'] = Utils.proxmox(URI, node, 'nodes/pve/lxc').map do |state| vmid = state['vmid'] name = state['name'] - config = Common.proxmox(URI, node, "nodes/pve/lxc/#{vmid}/config") + config = Utils.proxmox(URI, node, "nodes/pve/lxc/#{vmid}/config") ip = config['net0'] ? config['net0'].match(/ip=([\d\.]+)/)&.[](1) : "404" "#{name}.#{domain} #{ip}" end - Chef::Log.info(node.run_state['proxy_hosts']) + Logs.info(node.run_state['proxy_hosts']) end end diff --git a/libs/share/attributes/default.rb b/libs/share/attributes/default.rb index 70f744a..7e6cf91 100644 --- a/libs/share/attributes/default.rb +++ b/libs/share/attributes/default.rb @@ -1,5 +1,8 @@ include_attribute 'config::default' -default['mount'] = [ - 'share:/share' +default['share']['mount'] = [ + '/share' ] + +default['share']['user']['uid'] = 100000 +default['share']['user']['gid'] = 100000 diff --git a/libs/share/recipes/default.rb b/libs/share/recipes/default.rb index b05187c..35bd84e 100644 --- a/libs/share/recipes/default.rb +++ b/libs/share/recipes/default.rb @@ -1,11 +1,30 @@ +Common.packages(self, %w[samba samba-common samba-client]) + login = Env.get(node, 'login') password = Env.get(node, 'password') -Common.packages(self, %w[samba samba-common samba-client]) +group login do + gid node['share']['user']['gid'] + action :create +end -id=100000 -group(login) { gid id; action :create } -user(login) { uid id; gid id; shell '/bin/false'; manage_home false; action :create } +user login do + uid node['share']['user']['uid'] + gid node['share']['user']['gid'] + shell '/bin/false' + manage_home false + action :create +end + +Array(node.dig('share','mount')).each do |path| + directory path do + owner login + group login + mode '2775' + recursive false + action :create + end +end execute "create_samba_#{login}" do command "printf '#{password}\\n#{password}\\n' | smbpasswd -a -s #{login}" @@ -14,7 +33,7 @@ template '/etc/samba/smb.conf' do source 'smb.conf.erb' - variables(login: login, shares: node['mount']) + variables(login: login, shares: Array(node['share']['mount'])) notifies :restart, 'service[smb]' end diff --git a/libs/share/templates/smb.conf.erb b/libs/share/templates/smb.conf.erb index 3329a3d..2a9bf90 100644 --- a/libs/share/templates/smb.conf.erb +++ b/libs/share/templates/smb.conf.erb @@ -1,12 +1,31 @@ [global] +server min protocol = SMB2 +server max protocol = SMB3 + security = user passdb backend = tdbsam map to guest = bad user usershare allow guests = no -<% @shares.each do |entry| -%> - <% name, path = entry.split(':', 2) -%> - [<%= name %>] +socket options = TCP_NODELAY SO_RCVBUF=524288 SO_SNDBUF=524288 + +aio read size = 1 +aio write size = 1 + +vfs objects = catia fruit streams_xattr +fruit:metadata = stream +fruit:resource = file +fruit:model = MacSamba +fruit:locking = none + +dirsort = yes + +fruit:posix_rename = yes +fruit:veto_appledouble = no +fruit:nfs_aces = no + +<% Array(@shares).each do |path| -%> + [<%= File.basename(path.to_s.chomp('/')) %>] path = <%= path %> valid users = <%= @login %> force user = <%= @login %> diff --git a/local/run.sh b/local/run.sh index 8fd719f..1bf17b2 100755 --- a/local/run.sh +++ b/local/run.sh @@ -62,7 +62,7 @@ CONTAINER_ID=$(docker run -d --privileged --cgroupns=host --tmpfs /tmp \ log "container" "started:${CONTAINER_ID}" sleep "$DOCKER_WAIT" -cmd="cinc-client -l debug --local-mode --config-option node_path=/tmp/nodes \ +cmd="cinc-client -l info --local-mode --config-option node_path=/tmp/nodes \ --config-option cookbook_path=${COOKBOOK_PATH} ${CONFIG_FILE} --chef-license accept -o ${RECIPE}" docker exec "$CONTAINER_ID" bash -c "$cmd" || log "error" "exec_failed" From cf87aba88c379266fb11d07e63c34642723c59b9 Mon Sep 17 00:00:00 2001 From: Steven <106664555+stevius10@users.noreply.github.com> Date: Wed, 13 Aug 2025 21:02:51 +0200 Subject: [PATCH 04/13] dev/v1.1 (#56) * use latest version boolean * updated for latest changes * extend uniformed naming * better naming readability * refactor user handling and shared responsibilities * reorganize ssh keys * bring vcs in context * privilege separation for git app and config user * modularize * use shared home * fix rerun command * privilege separated key handling in shared home * split playbook accordingly * cleanup from playbook split * avoid overwriting * refactor playbook modularized * refactor playbook modularized * fix delay in redeploy while pipeline run * change order if dependent * debloat default actions * global authorized keys * logging workaround for runtime caption * pass user environment for home variable * changed order for self-contained monorepo * reorganize job conditions --- .gitea/workflows/action.yml | 8 +- .gitea/workflows/pipeline.yml | 25 +- README.md | 30 +- base/default.yml | 268 +----------------- base/roles/base/tasks/access.yml | 79 ++++++ base/roles/base/tasks/defaults.yml | 19 ++ base/roles/base/tasks/main.yml | 138 ++------- base/roles/base/tasks/state.yml | 38 +++ base/roles/base/tasks/tools.yml | 30 ++ base/roles/base/vars/main.yml | 10 +- base/roles/container/defaults/main.yml | 20 ++ base/roles/container/tasks/add.yml | 28 ++ base/roles/container/tasks/create.yml | 61 ++++ base/roles/container/tasks/main.yml | 37 +++ base/roles/container/tasks/os.yml | 26 ++ base/roles/container/tasks/remove.yml | 38 +++ base/roles/container/tasks/start.yml | 27 ++ config/attributes/default.rb | 24 +- config/libraries/common.rb | 1 - config/libraries/logs.rb | 5 + config/recipes/config.rb | 37 +-- config/recipes/git.rb | 24 +- config/recipes/prepare.rb | 53 ++-- config/recipes/repo.rb | 76 ++--- config/recipes/runner.rb | 38 ++- config/templates/git_app.ini.erb | 18 +- config/templates/repo_config.erb | 2 +- ...pipeline.yml.erb => repo_pipeline.yml.erb} | 0 ...generic_sync.yml.erb => repo_sync.yml.erb} | 0 libs/assistant/recipes/default.rb | 1 - libs/bridge/recipes/default.rb | 5 +- libs/broker/recipes/default.rb | 1 - libs/proxy/recipes/default.rb | 1 - libs/share/recipes/default.rb | 3 - local/Dockerfile | 6 +- local/run.sh | 11 +- 36 files changed, 588 insertions(+), 600 deletions(-) create mode 100644 base/roles/base/tasks/access.yml create mode 100644 base/roles/base/tasks/defaults.yml create mode 100644 base/roles/base/tasks/state.yml create mode 100644 base/roles/base/tasks/tools.yml create mode 100644 base/roles/container/defaults/main.yml create mode 100644 base/roles/container/tasks/add.yml create mode 100644 base/roles/container/tasks/create.yml create mode 100644 base/roles/container/tasks/main.yml create mode 100644 base/roles/container/tasks/os.yml create mode 100644 base/roles/container/tasks/remove.yml create mode 100644 base/roles/container/tasks/start.yml rename config/templates/{pipeline_generic_pipeline.yml.erb => repo_pipeline.yml.erb} (100%) rename config/templates/{pipeline_generic_sync.yml.erb => repo_sync.yml.erb} (100%) diff --git a/.gitea/workflows/action.yml b/.gitea/workflows/action.yml index 1a6ece3..ab74452 100644 --- a/.gitea/workflows/action.yml +++ b/.gitea/workflows/action.yml @@ -62,7 +62,9 @@ runs: - name: Configure container run: | - tar cz . | ssh -o StrictHostKeyChecking=no -i /share/.ssh/${{ env.id }} config@${{ env.ip }} \ - 'sudo tar xz -C /tmp && sudo IP=${{ env.ip }} ID=${{ env.id }} HOST=${{ vars.HOST }} LOGIN=${{ vars.LOGIN }} PASSWORD=${{ vars.PASSWORD }} PWD="$(pwd)" \ - cinc-client -l info --local-mode --chef-license accept --config-option cookbook_path="/tmp" $( [[ -f "/tmp/repo/config.json" ]] && echo "-j /tmp/repo/config.json" ) -o repo' + tar cz . | \ + ssh -o StrictHostKeyChecking=no -i /share/.ssh/${{ env.id }} config@${{ env.ip }} 'sudo tar xz -C /tmp && env \ + IP=${{ env.ip }} ID=${{ env.id }} HOST=${{ vars.HOST }} LOGIN=${{ vars.LOGIN }} PASSWORD=${{ vars.PASSWORD }} PWD="$(pwd)" \ + cinc-client -l info --local-mode $( [[ -f "/tmp/repo/config.json" ]] && echo "-j /tmp/repo/config.json" ) \ + --chef-license accept --config-option cookbook_path="/tmp" -o repo' shell: bash diff --git a/.gitea/workflows/pipeline.yml b/.gitea/workflows/pipeline.yml index 5269aa9..880f1fd 100644 --- a/.gitea/workflows/pipeline.yml +++ b/.gitea/workflows/pipeline.yml @@ -2,7 +2,6 @@ on: workflow_dispatch: push: branches: [ release, main ] - jobs: init: @@ -51,11 +50,11 @@ jobs: boot: ${{ needs.init.outputs.boot }} mount: ${{ needs.init.outputs.mount }} share: true - if: ${{ gitea.ref == 'refs/heads/release' }} + if: ${{ gitea.ref == 'refs/heads/release' }} share: runs-on: [ "shell" ] - needs: init + needs: [ init, base ] steps: - name: Checkout repository uses: https://gitea.com/actions/checkout@v4 @@ -71,13 +70,15 @@ jobs: password: ${{ vars.PASSWORD }} run: | tar -c config -cz . | \ - ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${id}" "config@${ip}" \ - 'sudo tar xz -C /tmp && sudo env IP="'"${ip}"'" ID="'"${id}"'" LOGIN="'"${login}"'" PASSWORD="'"${password}"'" \ - cinc-client -l info --local-mode --chef-license accept --config-option cookbook_path="[\"'"/tmp/config"'\", \"'"/tmp/config/libs"'\"]" -o share' + ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${id}" "config@${ip}" 'sudo tar xz -C /tmp && sudo \ + $(sudo -u config env) IP="'"${ip}"'" ID="'"${id}"'" LOGIN="'"${login}"'" PASSWORD="'"${password}"'" \ + cinc-client -l info --local-mode --chef-license accept --config-option \ + cookbook_path="[\"'"/tmp/config"'\", \"'"/tmp/config/libs"'\"]" -o share' + if: ${{ gitea.ref != 'refs/heads/release' || success() }} config: runs-on: [ "shell" ] - needs: [init] + needs: [ init, base ] steps: - name: Checkout repository uses: https://gitea.com/actions/checkout@v4 @@ -93,8 +94,8 @@ jobs: run: | git -C config submodule update --remote --recursive tar -c config -cz . | \ - ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${id}" "config@${ip}" \ - 'sudo tar xz -C /tmp && sudo env \ - IP="'"${ip}"'" ID="'"${id}"'" LOGIN="'"${login}"'" PASSWORD="'"${password}"'" PWD="/tmp/config" \ - cinc-client -l info --local-mode --chef-license accept --config-option \ - cookbook_path="/tmp/config" $( [ -f /tmp/config/local/config.json ] && echo "-j /tmp/config/local/config.json" ) -o config' \ No newline at end of file + ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${id}" "config@${ip}" 'sudo tar xz -C /tmp && sudo \ + $(sudo -u config env) IP="'"${ip}"'" ID="'"${id}"'" LOGIN="'"${login}"'" PASSWORD="'"${password}"'" PWD="/tmp/config" \ + cinc-client -l info --local-mode $( [ -f /tmp/config/local/config.json ] && echo "-j /tmp/config/local/config.json" ) \ + --config-option cookbook_path="/tmp/config" --chef-license accept -o config' + if: ${{ gitea.ref != 'refs/heads/release' || success() }} diff --git a/README.md b/README.md index f548e5e..4e08968 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ - [Design](#design) - [Trade-offs](#trade-offs) - [Usage](#usage) - - [Requirements](#requirements) - [Lifecycle](#lifecycle) + - [Self-contained Monorepository](#self-contained-monorepository) + - [Requirements](#requirements) - [Getting Started](#getting-started) - [Development and Container Extension](#development-and-container-extension) @@ -62,27 +63,30 @@ This system implements stateless infrastructure management on Proxmox VE, ensuri ## Usage -### Requirements +### Lifecycle -- Docker -- Proxmox VE 8.4-9.0 -- Proxmox API token -- See [Wiki](https://github.com/stevius10/Proxmox-GitOps/wiki) for recommendations +#### Self-contained Monorepository -### Lifecycle +- `git clone --recurse-submodules` + - e. g., **Version-Controlled Mirrored** + +- **Backup**: See [Self-contained Monorepository](#self-contained-monorepository) + - store /share for persistence, disable network access for security -- **Self-contained Monorepository** Artifact for **Version-Controlled Mirroring** - - `git clone --recurse-submodules` (store /share for persistence, disable network access for security) +- **Update**: See [Self-contained Monorepository](#self-contained-monorepository), and redeploy merged -- **Backup**: See previous +- **Rollback**: See [Self-contained Monorepository](#self-contained-monorepository), or set `snapshot` branch to `release` at runtime -- **Update**: See previous, and redeploy merged +### Requirements -- **Rollback**: See previous, or set `snapshot` to `release` at runtime +- Docker +- Proxmox VE 8.4-9.0 +- See [Wiki](https://github.com/stevius10/Proxmox-GitOps/wiki) for recommendations ### Getting Started -- Set **credentials and Proxmox API token** in [`local/.config.json`](local/.config.json) as `./local/config.json` +- Set **Proxmox** and **global usage credentials** in [`local/.config.json`](local/.config.json) as `./local/config.json` +- Ensure **container configuration** in [`config.env`](config.env) and verify storage - Run `./local/run.sh` for local Docker environment - Accept the Pull Request at `localhost:8080/main/config/pulls/1` to deploy on Proxmox VE diff --git a/base/default.yml b/base/default.yml index 327b1a4..fbb95f5 100644 --- a/base/default.yml +++ b/base/default.yml @@ -3,20 +3,19 @@ hosts: localhost gather_facts: no vars: - proxmox_credentials: &proxmox_credentials - api_host: "{{ PROXMOX_HOST }}" - api_user: "{{ PROXMOX_USER }}" - api_password: "{{ PROXMOX_PASSWORD | default(omit) }}" - api_token_id: "{{ (PROXMOX_PASSWORD | default('') == '') | ternary(PROXMOX_TOKEN, omit) }}" - api_token_secret: "{{ (PROXMOX_PASSWORD | default('') == '') | ternary(PROXMOX_SECRET, omit) }}" - api_port: 8006 - node: "pve" - os: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst" - keys_dir: "/share/.ssh" - + os: "{{ OS | default('local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst') }}" + key_dir: "{{ KEYS_DIR | default('/share/.ssh') }}" tasks: + - name: Ensure key directory + file: + path: "{{ key_dir }}" + state: directory - # Preparation + - name: Generate container key on host + community.crypto.openssh_keypair: + path: "{{ key_dir }}/{{ id }}" + type: ed25519 + force: false - name: Check container configuration ansible.builtin.stat: @@ -39,248 +38,9 @@ when: - (config_file.stat.exists and config.proxmox is defined) or (not config_file.stat.exists) - - name: Get Proxmox ticket - uri: - url: "https://{{ PROXMOX_HOST }}:8006/api2/json/access/ticket" - method: POST - body: - username: "{{ PROXMOX_USER }}" - password: "{{ PROXMOX_PASSWORD }}" - body_format: json - validate_certs: no - register: proxmox_login - when: PROXMOX_PASSWORD is defined and PROXMOX_PASSWORD != '' - - - name: Set Proxmox request headers - set_fact: - proxmox_auth: >- - {{ (PROXMOX_PASSWORD | default('') == '') | ternary( - {'Authorization': 'PVEAPIToken=' + PROXMOX_USER + '!' + PROXMOX_TOKEN + '=' + PROXMOX_SECRET}, - {'Cookie': 'PVEAuthCookie=' + proxmox_login.json.data.ticket, - 'CSRFPreventionToken': proxmox_login.json.data.CSRFPreventionToken}) }} - - # Key handling - - - name: Ensure container keys directory on host - ansible.builtin.file: - path: "{{ keys_dir }}" - state: directory - - - name: Generate container key on host - community.crypto.openssh_keypair: - path: "{{ keys_dir }}/{{ id }}" - type: ed25519 - force: false - - # Container deletion - - - name: Check container existence - community.general.proxmox_vm_info: - <<: *proxmox_credentials - vmid: "{{ id }}" - register: container_info - delegate_to: localhost - ignore_errors: yes - - - name: Set container existence - set_fact: - container_exists: "{{ container_info is success and (container_info.proxmox_vms | length) > 0 }}" - - - name: Stop container - community.general.proxmox: - <<: *proxmox_credentials - vmid: "{{ id }}" - state: stopped - force: yes - delegate_to: localhost - ignore_errors: yes - when: container_exists - - - name: Wait for container to be stopped - uri: - url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/lxc/{{ id }}/status/current" - method: GET - headers: "{{ proxmox_auth }}" - validate_certs: no - register: container_status - until: container_status.json.data.status == "stopped" - retries: 5 - delay: 2 - ignore_errors: true - delegate_to: localhost - when: container_exists - - - name: Remove container - community.general.proxmox: - <<: *proxmox_credentials - vmid: "{{ id }}" - state: absent - delegate_to: localhost - ignore_errors: yes - register: removal - when: container_exists - - - name: Wait for container to be removed - uri: - url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/lxc/{{ id }}/status/current" - method: GET - headers: "{{ proxmox_auth }}" - validate_certs: no - register: container_status - until: - - container_status.status == 500 - - "'does not exist' in container_status.json.message" - retries: 5 - delay: 4 - failed_when: false - delegate_to: localhost - when: container_exists - - # Container creation - - - name: Init mounts - set_fact: - mounts: "{{ mounts | default({}) }}" - - - name: Set mounts - set_fact: - mounts: >- - {{ mounts | default({}) | combine({('mp'~idx): - ((item.split(':')[:-1] | join(':')) ~ ',mp=' ~ (item.split(':')[-1]) ~ ',ro=0,acl=1') }) }} - loop: "{{ mount.split(',') | select('search', ':') }}" - loop_control: - index_var: idx - when: - - mount is defined - - share | default(true) | bool - - mount | trim | length > 0 - - - name: Set passthrough - set_fact: - mounts: "{{ mounts | combine({ ('dev' ~ idx): item }) }}" - loop: "{{ mount.split(',') | select('match', '^/[^:]+$') }}" - loop_control: - index_var: idx - when: - - mount is defined - - share | default(true) | bool - - mount | trim | length > 0 - - - name: Create container - community.general.proxmox: &create_container - <<: *proxmox_credentials - vmid: "{{ id }}" - hostname: "{{ hostname }}" - ostemplate: "{{ os }}" - cores: "{{ cores }}" - memory: "{{ memory }}" - pubkey: "{{ lookup('file', [keys_dir, id ~ '.pub'] | path_join) }}" - swap: "{{ swap }}" - disk: "{{ disk }}" - mounts: >- - {{ (mounts if mounts and (PROXMOX_PASSWORD is defined and PROXMOX_PASSWORD != '') else omit) }} - features: >- - {{ (['mount=cifs'] if (share | default(false)) and (mount | default('') | trim != '') - and (PROXMOX_PASSWORD is defined and PROXMOX_PASSWORD != '') else omit) }} - unprivileged: "{{ (share | default(false) and mount | default('') | trim != '') | ternary(false, true) }}" - netif: - net0: "name=eth0,gw=192.168.178.1,ip={{ ip }}/24,bridge=vmbr0" - onboot: "{{ boot }}" - state: present - delegate_to: localhost - register: container_creation - ignore_errors: true - - - name: Download container os if missing - uri: - url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/storage/local/download-url" - method: POST - headers: "{{ proxmox_auth | combine({'Content-Type': 'application/json'}) }}" - body_format: json - body: - url: "http://download.proxmox.com/images/system/{{ os.split('/')[-1] }}" - filename: "{{ os.split('/')[-1] }}" - content: "vztmpl" - validate_certs: no - when: - - container_creation.failed - - os in container_creation.msg | default('') - register: os_download - - - name: Wait for os to be downloaded - uri: - url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/storage/local/content" - method: GET - headers: "{{ proxmox_auth }}" - validate_certs: no - register: os_available - until: os in (os_available.json.data | map(attribute='volid') | list) - retries: 10 - delay: 6 - when: os_download is defined - - - name: Retry create container with os downloaded - community.general.proxmox: *create_container - when: os_available is defined and os in (os_available.json.data | map(attribute='volid') | list) - - - name: Wait for container to be created - uri: - url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/lxc/{{ id }}/config" - method: GET - headers: "{{ proxmox_auth }}" - validate_certs: no - register: container_status - until: - - container_status.status == 200 - - "'hostname' in container_status.json.data" - retries: 5 - delay: 6 - ignore_errors: yes - delegate_to: localhost - - - name: Start container - community.general.proxmox: - <<: *proxmox_credentials - vmid: "{{ id }}" - hostname: "{{ hostname }}" - state: started - delegate_to: localhost - - - name: Wait for container to be connectable - wait_for: - port: 22 - host: "{{ ip }}" - timeout: 180 - delegate_to: localhost - - - name: Check technical user configured - command: "ssh -o BatchMode=yes -o ConnectTimeout=5 -i {{ keys_dir }}/{{ id }} config@{{ ip }} echo success" - register: ssh_config_test - failed_when: false - changed_when: false - when: container_exists - - - name: Set technical user - set_fact: - ssh_user: "{{ 'config' if (container_exists and (ssh_config_test.rc | default(1)) == 0) else 'root' }}" - - - name: Add container - add_host: + - name: Container + ansible.builtin.include_role: name: container - id: "{{ id }}" - ansible_host: "{{ ip }}" - ansible_user: "{{ ssh_user }}" - ansible_ssh_private_key_file: "{{ [keys_dir, id] | path_join }}" - ansible_ssh_common_args: '-o StrictHostKeyChecking=no' - public_key: "{{ lookup('file', [keys_dir, id ~ '.pub'] | path_join) }}" - private_key: "{{ lookup('file', [keys_dir, id] | path_join) }}" - - - name: Reset known hosts - ansible.builtin.known_hosts: - name: "{{ ip }}" - state: absent - delegate_to: localhost - become: false - name: Remote container configuration hosts: container @@ -303,4 +63,4 @@ when: - not (share | default(false) | bool) - mount is defined - tags: mounts \ No newline at end of file + tags: mounts diff --git a/base/roles/base/tasks/access.yml b/base/roles/base/tasks/access.yml new file mode 100644 index 0000000..f103c76 --- /dev/null +++ b/base/roles/base/tasks/access.yml @@ -0,0 +1,79 @@ +- name: SSH configuration + notify: Restart SSH + when: (configure_ssh | default(true) | bool) + block: + - name: Disable root login + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: "^#?PermitRootLogin" + line: "PermitRootLogin no" + backup: yes + state: present + + - name: Disable password authentication + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: "^#?PasswordAuthentication" + line: "PasswordAuthentication no" + backup: yes + state: present + + - name: Enable public key authentication + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: '^#?PubkeyAuthentication' + line: 'PubkeyAuthentication yes' + state: present + backup: yes + + - name: Authorized keys + block: + - name: Create global authorized keys + file: + path: /etc/ssh/authorized_keys + state: directory + owner: root + group: root + mode: "0755" + + - name: Set global authorized keys + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: '^#?AuthorizedKeysFile' + line: 'AuthorizedKeysFile /etc/ssh/authorized_keys/%u' + state: present + backup: yes + +- name: Container accessibility + block: + - name: Store container key + ansible.builtin.set_fact: + private_key: "{{ private_key ~ (private_key.endswith('\n') | ternary('', '\n')) }}" + public_key: "{{ public_key ~ (public_key.endswith('\n') | ternary('', '\n')) }}" + + - name: Save container keys + copy: + content: "{{ item.content }}" + dest: "{{ key_dir }}/{{ item.name }}" + owner: app + group: config + mode: "{{ item.mode }}" + loop: + - { name: "{{ id }}", content: "{{ private_key }}", mode: "0600" } + - { name: "{{ id }}.pub", content: "{{ public_key }}", mode: "0644" } + + - name: Verify container key + shell: "ssh-keygen -y -f {{ key_dir }}/{{ id }} | diff - {{ key_dir }}/{{ id }}.pub" + register: verify + changed_when: false + failed_when: verify.rc != 0 + + - name: Set container authorized keys + ansible.posix.authorized_key: + user: "{{ item }}" + key: "{{ public_key }}" + state: present + exclusive: no + manage_dir: false + path: "/etc/ssh/authorized_keys/{{ item }}" + loop: "{{ ssh_users }}" diff --git a/base/roles/base/tasks/defaults.yml b/base/roles/base/tasks/defaults.yml new file mode 100644 index 0000000..1e08280 --- /dev/null +++ b/base/roles/base/tasks/defaults.yml @@ -0,0 +1,19 @@ +- name: User defaults + block: + - name: Set root bash configuration + copy: + src: .bashrc + dest: /root/.bashrc + owner: root + group: root + mode: '0644' + + - name: Set user bash configuration + copy: + src: .bashrc + dest: "{{ (item.home | default('/home/' + item.name)) }}/.bashrc" + owner: "{{ item.name }}" + group: "{{ item.name }}" + mode: '0644' + loop: "{{ default_users }}" + when: item.name in ssh_users diff --git a/base/roles/base/tasks/main.yml b/base/roles/base/tasks/main.yml index 0fe1871..c011dcd 100644 --- a/base/roles/base/tasks/main.yml +++ b/base/roles/base/tasks/main.yml @@ -1,5 +1,9 @@ - name: Base container configuration block: + - name: Set key directory + set_fact: + key_dir: "{{ KEYS_DIR | default('/share/.ssh') }}" + - name: Update system apt: update_cache: true @@ -25,6 +29,7 @@ shell: "{{ item.shell | default('/bin/bash') }}" groups: "{{ item.groups | default(omit) }}" create_home: "{{ item.create_home | default(true) }}" + home: "{{ item.home | default(omit) }}" state: present loop: "{{ default_users }}" register: created_users @@ -42,127 +47,18 @@ create: yes loop: "{{ ssh_users }}" -- name: Install configuration management - block: - - name: Get latest Cinc version - ansible.builtin.uri: - url: https://downloads.cinc.sh/files/stable/cinc/ - return_content: yes - register: download_configuration_management_version - retries: 5 - delay: 3 - until: download_configuration_management_version.status == 200 - changed_when: false +- name: Ensure files and directories + include_tasks: state.yml + vars: + extra: + - { path: "/app", state: "directory", mode: "0755", owner: "app", group: "config" } + - { path: "/app/.ssh", state: "directory", mode: "0711", owner: "app", group: "config" } - - name: Get configuration management version - ansible.builtin.set_fact: - cinc_version: "{{ download_configuration_management_version.content | regex_findall('href=\"(\\d+\\.\\d+\\.\\d+)/\"') | sort | last }}" +- name: Container accessibility + import_tasks: access.yml - - name: Download configuration management - ansible.builtin.get_url: - url: "https://downloads.cinc.sh/files/stable/cinc/{{ cinc_version }}/debian/12/cinc_{{ cinc_version }}-1_{{ arch | default('amd64') }}.deb" - dest: /tmp/cinc.deb - validate_certs: no - register: download_configuration_management_result - retries: 5 - delay: 3 - until: download_configuration_management_result is succeeded +- name: Configuration management + import_tasks: tools.yml - - name: Install configuration management - ansible.builtin.apt: - deb: /tmp/cinc.deb - state: present - -- name: System Preparation - block: - - name: Create application directory - file: - path: "/app" - state: directory - owner: app - group: config - mode: 0755 - -- name: SSH configuration - notify: Restart SSH - when: (configure_ssh | default(true) | bool) - block: - - name: Disable SSH password authentication - ansible.builtin.lineinfile: - path: /etc/ssh/sshd_config - regexp: "^#?PasswordAuthentication" - line: "PasswordAuthentication no" - backup: yes - state: present - - - name: Disable SSH root login - ansible.builtin.lineinfile: - path: /etc/ssh/sshd_config - regexp: "^#?PermitRootLogin" - line: "PermitRootLogin no" - backup: yes - state: present - -- name: SSH key management - block: - - name: Store container key - ansible.builtin.set_fact: - private_key: "{{ private_key ~ (private_key.endswith('\n') | ternary('', '\n')) }}" - public_key: "{{ public_key ~ (public_key.endswith('\n') | ternary('', '\n')) }}" - - - name: Ensure container key directory - file: - path: "/root/.ssh" - state: directory - owner: root - group: root - recurse: yes - - - name: Save container private key - copy: - content: "{{ private_key }}" - dest: "/root/id_rsa" - owner: root - group: root - mode: 0600 - - - name: Save container public key - copy: - content: "{{ public_key }}" - dest: "/root/id_rsa.pub" - owner: root - group: root - mode: 0644 - - - name: Verify container key - shell: ssh-keygen -y -f /root/id_rsa | diff - /root/id_rsa.pub - register: verify - changed_when: false - failed_when: verify.rc != 0 - - - name: Set container authorized key - ansible.posix.authorized_key: - user: "{{ item }}" - key: "{{ public_key }}" - state: present - exclusive: no - loop: "{{ ssh_users }}" - -- name: Customization - block: - - name: Copy bash configuration for root - copy: - src: .bashrc - dest: /root/.bashrc - owner: root - group: root - mode: '0644' - - - name: Copy bash configuration for users - copy: - src: .bashrc - dest: "/home/{{ item }}/.bashrc" - owner: "{{ item }}" - group: "{{ item }}" - mode: '0644' - loop: "{{ ssh_users }}" +- name: User default settings + import_tasks: defaults.yml diff --git a/base/roles/base/tasks/state.yml b/base/roles/base/tasks/state.yml new file mode 100644 index 0000000..e2c4794 --- /dev/null +++ b/base/roles/base/tasks/state.yml @@ -0,0 +1,38 @@ +- name: Set files and directories + set_fact: + defaults: + - { path: "{{ key_dir }}", state: "directory", mode: "0711", owner: "app", group: "config" } + - { path: "{{ key_dir }}/{{ id }}", state: "file", mode: "0600", owner: "app", group: "config" } + - { path: "{{ key_dir }}/{{ id }}.pub", state: "file", mode: "0644", owner: "app", group: "config" } + when: defaults is not defined + +- name: Gather files and directories + set_fact: + objects: "{{ defaults + (extra | default([])) }}" + +- name: Ensure directories exists + file: + path: "{{ item.path }}" + state: directory + owner: "{{ item.owner }}" + group: "{{ item.group }}" + mode: "{{ item.mode }}" + loop: "{{ objects }}" + when: item.state == 'directory' + +- name: Check if files exist + stat: + path: "{{ item.path }}" + register: file + loop: "{{ objects }}" + when: item.state == 'file' + +- name: Ensure permissions on existing files + file: + path: "{{ item.item.path }}" + state: file + owner: "{{ item.item.owner }}" + group: "{{ item.item.group }}" + mode: "{{ item.item.mode }}" + loop: "{{ file.results }}" + when: item.stat is defined and item.stat.exists diff --git a/base/roles/base/tasks/tools.yml b/base/roles/base/tasks/tools.yml new file mode 100644 index 0000000..e845e73 --- /dev/null +++ b/base/roles/base/tasks/tools.yml @@ -0,0 +1,30 @@ +- name: Install configuration management + block: + - name: Get latest Cinc version + ansible.builtin.uri: + url: https://downloads.cinc.sh/files/stable/cinc/ + return_content: yes + register: download_configuration_management_version + retries: 5 + delay: 3 + until: download_configuration_management_version.status == 200 + changed_when: false + + - name: Get configuration management version + ansible.builtin.set_fact: + cinc_version: "{{ download_configuration_management_version.content | regex_findall('href=\"(\\d+\\.\\d+\\.\\d+)/\"') | sort | last }}" + + - name: Download configuration management + ansible.builtin.get_url: + url: "https://downloads.cinc.sh/files/stable/cinc/{{ cinc_version }}/debian/12/cinc_{{ cinc_version }}-1_{{ arch | default('amd64') }}.deb" + dest: /tmp/cinc.deb + validate_certs: no + register: download_configuration_management_result + retries: 5 + delay: 3 + until: download_configuration_management_result is succeeded + + - name: Install configuration management + ansible.builtin.apt: + deb: /tmp/cinc.deb + state: present diff --git a/base/roles/base/vars/main.yml b/base/roles/base/vars/main.yml index 959f564..e83319e 100644 --- a/base/roles/base/vars/main.yml +++ b/base/roles/base/vars/main.yml @@ -2,18 +2,14 @@ default_packages: - vim - wget - sudo - - net-tools - - procps - curl - - ansible-core - - ansible default_groups: ["config"] default_users: - - { name: "app", groups: ["config"], create_home: false } - - { name: "config", groups: ["config", "root", "sudo"], create_home: true } - - { name: "user", shell: "/bin/bash", groups: ["config", "root", "sudo"], create_home: true } + - { name: "app", groups: ["config"], create_home: false, home: "/app" } + - { name: "config", groups: ["config", "root", "sudo"], create_home: false, home: "/app" } + - { name: "user", shell: "/bin/bash", groups: ["root", "sudo"], create_home: true } ssh_users: - config diff --git a/base/roles/container/defaults/main.yml b/base/roles/container/defaults/main.yml new file mode 100644 index 0000000..1dd6a3e --- /dev/null +++ b/base/roles/container/defaults/main.yml @@ -0,0 +1,20 @@ +PROXMOX_HOST: "{{ lookup('env', 'PROXMOX_HOST') }}" +PROXMOX_USER: "{{ lookup('env', 'PROXMOX_USER') }}" +PROXMOX_PASSWORD: "{{ lookup('env', 'PROXMOX_PASSWORD') }}" +PROXMOX_TOKEN: "{{ lookup('env', 'PROXMOX_TOKEN') }}" +PROXMOX_SECRET: "{{ lookup('env', 'PROXMOX_SECRET') }}" + +proxmox_auth: >- + {{ (PROXMOX_PASSWORD | default('') == '') | ternary( + {'Authorization': 'PVEAPIToken=' + PROXMOX_USER + '!' + PROXMOX_TOKEN + '=' + PROXMOX_SECRET}, + {'Cookie': 'PVEAuthCookie=' + proxmox_login.json.data.ticket, + 'CSRFPreventionToken': proxmox_login.json.data.CSRFPreventionToken}) }} + +proxmox_cred: + api_host: "{{ PROXMOX_HOST }}" + api_user: "{{ PROXMOX_USER }}" + api_password: "{{ PROXMOX_PASSWORD | default(omit) }}" + api_token_id: "{{ (PROXMOX_PASSWORD | default('') == '') | ternary(PROXMOX_TOKEN, omit) }}" + api_token_secret: "{{ (PROXMOX_PASSWORD | default('') == '') | ternary(PROXMOX_SECRET, omit) }}" + api_port: 8006 + node: "pve" \ No newline at end of file diff --git a/base/roles/container/tasks/add.yml b/base/roles/container/tasks/add.yml new file mode 100644 index 0000000..e732146 --- /dev/null +++ b/base/roles/container/tasks/add.yml @@ -0,0 +1,28 @@ +- name: Container addition + block: + - name: Check config user + command: "ssh -o BatchMode=yes -o ConnectTimeout=5 -i {{ key_dir }}/{{ id }} config@{{ ip }} echo success" + register: ssh_config_test + failed_when: false + changed_when: false + when: container_exists + + - name: Set configuration user + set_fact: + ssh_user: "{{ 'config' if (container_exists and (ssh_config_test.rc | default(1)) == 0) else 'root' }}" + + - name: Add container + add_host: + name: container + id: "{{ id }}" + ansible_host: "{{ ip }}" + ansible_user: "{{ ssh_user }}" + ansible_ssh_private_key_file: "{{ [key_dir, id] | path_join }}" + ansible_ssh_common_args: '-o StrictHostKeyChecking=no' + public_key: "{{ lookup('file', [key_dir, id ~ '.pub'] | path_join) }}" + private_key: "{{ lookup('file', [key_dir, id] | path_join) }}" + + - name: Reset known hosts + ansible.builtin.known_hosts: + name: "{{ ip }}" + state: absent diff --git a/base/roles/container/tasks/create.yml b/base/roles/container/tasks/create.yml new file mode 100644 index 0000000..6f20c0c --- /dev/null +++ b/base/roles/container/tasks/create.yml @@ -0,0 +1,61 @@ +- name: Container creation + block: + - name: Init mounts + set_fact: + mounts: "{{ mounts | default({}) }}" + + - name: Set mounts + set_fact: + mounts: >- + {{ mounts | default({}) | combine({('mp'~idx): + ((item.split(':')[:-1] | join(':')) ~ ',mp=' ~ (item.split(':')[-1]) ~ ',ro=0,acl=1') }) }} + loop: "{{ mount.split(',') | select('search', ':') }}" + loop_control: + index_var: idx + when: + - mount is defined + - share | default(true) | bool + - mount | trim | length > 0 + + - name: Set passthrough + set_fact: + mounts: "{{ mounts | combine({ ('dev' ~ idx): item }) }}" + loop: "{{ mount.split(',') | select('match', '^/[^:]+$') }}" + loop_control: + index_var: idx + when: + - mount is defined + - share | default(true) | bool + - mount | trim | length > 0 + + - name: Create container + community.general.proxmox: + vmid: "{{ id }}" + hostname: "{{ hostname }}" + ostemplate: "{{ os }}" + cores: "{{ cores }}" + memory: "{{ memory }}" + pubkey: "{{ lookup('file', [key_dir, id ~ '.pub'] | path_join) }}" + swap: "{{ swap }}" + disk: "{{ disk }}" + mounts: >- + {{ (mounts if mounts and (PROXMOX_PASSWORD is defined and PROXMOX_PASSWORD != '') else omit) }} + features: >- + {{ (['mount=cifs'] if (share | default(false)) and (mount | default('') | trim != '') + and (PROXMOX_PASSWORD is defined and PROXMOX_PASSWORD != '') else omit) }} + unprivileged: "{{ (share | default(false) and mount | default('') | trim != '') | ternary(false, true) }}" + netif: + net0: "name=eth0,gw=192.168.178.1,ip={{ ip }}/24,bridge=vmbr0" + onboot: "{{ boot }}" + state: present + register: container_creation + + - name: Download container os if missing + include_tasks: os.yml + when: + - container_creation.failed + - os in (container_creation.msg | default('') ) + + - name: Retry create container with os downloaded + community.general.proxmox: + when: os_available is defined and os in (os_available.json.data | map(attribute='volid') | list) diff --git a/base/roles/container/tasks/main.yml b/base/roles/container/tasks/main.yml new file mode 100644 index 0000000..9e635be --- /dev/null +++ b/base/roles/container/tasks/main.yml @@ -0,0 +1,37 @@ +- name: Proxmox authorization + block: + - name: Get Proxmox ticket + uri: + url: "https://{{ PROXMOX_HOST }}:8006/api2/json/access/ticket" + method: POST + body: + username: "{{ PROXMOX_USER }}" + password: "{{ PROXMOX_PASSWORD }}" + body_format: json + validate_certs: no + register: proxmox_login + when: PROXMOX_PASSWORD is defined and PROXMOX_PASSWORD != '' + +- name: Container orchestration + block: + - name: Check container + community.general.proxmox_vm_info: + vmid: "{{ id }}" + register: container_exists + + - name: Remove existing container + include_tasks: remove.yml + when: container_exists is success and (container_exists.proxmox_vms | length) > 0 + + - name: Create container + include_tasks: create.yml + + - name: Start container + include_tasks: start.yml + + - name: Add container + include_tasks: add.yml + + module_defaults: + community.general.proxmox: "{{ proxmox_cred }}" + community.general.proxmox_vm_info: "{{ proxmox_cred }}" \ No newline at end of file diff --git a/base/roles/container/tasks/os.yml b/base/roles/container/tasks/os.yml new file mode 100644 index 0000000..087d442 --- /dev/null +++ b/base/roles/container/tasks/os.yml @@ -0,0 +1,26 @@ +- name: Container OS + block: + - name: Download container os if missing + uri: + url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/storage/local/download-url" + method: POST + headers: "{{ proxmox_auth | combine({'Content-Type': 'application/json'}) }}" + body_format: json + body: + url: "http://download.proxmox.com/images/system/{{ os.split('/')[-1] }}" + filename: "{{ os.split('/')[-1] }}" + content: "vztmpl" + validate_certs: no + register: os_download + + - name: Wait for os to be downloaded + uri: + url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/storage/local/content" + method: GET + headers: "{{ proxmox_auth }}" + validate_certs: no + register: os_available + until: os in (os_available.json.data | map(attribute='volid') | list) + retries: 10 + delay: 6 + when: os_download is defined diff --git a/base/roles/container/tasks/remove.yml b/base/roles/container/tasks/remove.yml new file mode 100644 index 0000000..e6979f9 --- /dev/null +++ b/base/roles/container/tasks/remove.yml @@ -0,0 +1,38 @@ +- name: Container removal + block: + - name: Stop container + community.general.proxmox: + vmid: "{{ id }}" + state: stopped + force: yes + + - name: Wait for container to be stopped + uri: + url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/lxc/{{ id }}/status/current" + method: GET + headers: "{{ proxmox_auth }}" + validate_certs: no + register: container_status + until: container_status.json.data.status == "stopped" + retries: 5 + delay: 2 + + - name: Remove container + community.general.proxmox: + vmid: "{{ id }}" + state: absent + register: removal + + - name: Wait for container to be removed + uri: + url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/lxc/{{ id }}/status/current" + method: GET + headers: "{{ proxmox_auth }}" + validate_certs: no + register: container_status + until: + - container_status.status == 500 + - "'does not exist' in container_status.json.message" + retries: 5 + delay: 4 + failed_when: false diff --git a/base/roles/container/tasks/start.yml b/base/roles/container/tasks/start.yml new file mode 100644 index 0000000..17a8493 --- /dev/null +++ b/base/roles/container/tasks/start.yml @@ -0,0 +1,27 @@ +- name: Container start + block: + - name: Wait for container to be created + uri: + url: "https://{{ PROXMOX_HOST }}:8006/api2/json/nodes/pve/lxc/{{ id }}/config" + method: GET + headers: "{{ proxmox_auth }}" + validate_certs: no + register: container_status + until: + - container_status.status == 200 + - "'hostname' in container_status.json.data" + retries: 5 + delay: 6 + ignore_errors: yes + + - name: Start container + community.general.proxmox: + vmid: "{{ id }}" + hostname: "{{ hostname }}" + state: started + + - name: Wait for container to be connectable + wait_for: + port: 22 + host: "{{ ip }}" + timeout: 180 diff --git a/config/attributes/default.rb b/config/attributes/default.rb index 62247f4..cf7578c 100644 --- a/config/attributes/default.rb +++ b/config/attributes/default.rb @@ -1,22 +1,21 @@ default['id'] = ENV['ID'] - default['host'] = ENV['IP'].to_s.presence ? ENV['IP'] : "127.0.0.1" default['key'] = ENV['KEY'].to_s.presence ? ENV['KEY'] : "/share/.ssh/#{node['id']}" -default['git']['app']['user'] = 'app' -default['git']['app']['group'] = 'config' +default['git']['user']['app'] = 'app' +default['git']['user']['group'] = 'config' +default['git']['user']['ssh'] = 'config' default['git']['conf']['customize'] = true +default['git']['conf']['repo'] = [ "./", "./base", "./config/libraries", "./libs" ] -default['git']['dir']['install'] = '/app/git' -default['git']['dir']['data'] = '/app/git/data' -default['git']['dir']['home'] = "/home/#{node['git']['app']['user']}/git" -default['git']['dir']['workspace'] = "/home/#{node['git']['app']['user']}/workspace" +default['git']['dir']['app'] = '/app/git' +default['git']['dir']['home'] = ENV['HOME'] +default['git']['dir']['workspace'] = "#{node['git']['dir']['home']}/workspace" default['git']['port']['http'] = 8080 -default['git']['host']['http'] = "http://#{node['host']}:#{node['git']['port']['http']}" - default['git']['port']['ssh'] = 2222 +default['git']['host']['http'] = "http://#{node['host']}:#{node['git']['port']['http']}" default['git']['host']['ssh'] = "#{node['host']}:#{node['git']['port']['ssh']}" default['git']['api']['version'] = "v1" @@ -25,10 +24,9 @@ default['git']['org']['main'] = 'main' default['git']['org']['stage'] = 'stage' -default['runner']['dir']['install'] = '/app/runner' +# Runner + +default['runner']['dir']['app'] = '/app/runner' default['runner']['dir']['cache'] = '/tmp' -default['runner']['file']['marker'] = "#{node['runner']['dir']['install']}/.runner" default['runner']['conf']['label'] = 'shell' - -default['git']['repositories'] = [ "./", "./base", "./config/libraries", "./libs" ] diff --git a/config/libraries/common.rb b/config/libraries/common.rb index 63cde5f..20f1545 100644 --- a/config/libraries/common.rb +++ b/config/libraries/common.rb @@ -86,7 +86,6 @@ def self.create_dir(ctx, dir, owner, group, mode, recursive) group group mode mode recursive recursive - action :create end rescue => e Logs.warn("Skipping #{dir}: #{e}") diff --git a/config/libraries/logs.rb b/config/libraries/logs.rb index 82524c3..3f1d55c 100644 --- a/config/libraries/logs.rb +++ b/config/libraries/logs.rb @@ -37,6 +37,11 @@ def self.debug(level, msg, *pairs) log(level, [msg, ctx].reject(&:empty?).join(" ")) end + def self.info?(msg) + log(:info, msg) + true + end + def self.fail!(msg) error(msg) c = callsite diff --git a/config/recipes/config.rb b/config/recipes/config.rb index 084ddb3..69e1776 100644 --- a/config/recipes/config.rb +++ b/config/recipes/config.rb @@ -3,10 +3,10 @@ end execute 'config_set_user' do - user node['git']['app']['user'] + user node['git']['user']['app'] command <<-EOH login="#{Env.get(node, 'login')}" - base="#{node['git']['dir']['install']}/gitea admin user --config #{node['git']['dir']['install']}/app.ini" + base="#{node['git']['dir']['app']}/gitea admin user --config #{node['git']['dir']['app']}/app.ini" user="--username #{Env.get(node, 'login')} --password #{Env.get(node, 'password')}" create="--email #{Env.get(node, 'email')} --admin --must-change-password=false" if $base list | awk '{print $2}' | grep -q "^#{Env.get(node, 'login')}$"; then @@ -32,7 +32,6 @@ user: login, pass: password, method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' })).code.to_i Logs.request!("Set key failed", url, response) unless [201, 422].include?(status_code) end - action :run only_if { ::File.exist?("#{node['key']}.pub") } not_if do next false unless ::File.exist?("#{node['key']}.pub") @@ -43,37 +42,11 @@ end end -directory "/home/#{node['git']['app']['user']}/.ssh" do - owner node['git']['app']['user'] - group node['git']['app']['group'] - mode '0700' - action :create -end - -file "/home/#{node['git']['app']['user']}/.ssh/config" do - content <<~CONF - Host #{node['host']} - HostName #{node['host']} - IdentityFile #{node['key']} - StrictHostKeyChecking no - CONF - owner node['git']['app']['user'] - group node['git']['app']['group'] - mode '0600' - action :create_if_missing -end - -ruby_block 'config_wait_ssh' do - block do Utils.wait("#{Env.get(node, 'login')}@#{node['host']}:#{node['git']['port']['ssh']}") end -end - execute 'config_git_safe_directory' do command <<-SH git config --global --add safe.directory "*" && \ git config --system --add safe.directory "*" SH - environment 'HOME' => "/home/#{node['git']['app']['user']}" - action :run end execute 'config_git_user' do @@ -82,9 +55,7 @@ git config --global user.email "#{Env.get(node, 'email')}" git config --global core.excludesfile #{ENV['PWD']}/.gitignore SH - user node['git']['app']['user'] - environment 'HOME' => "/home/#{node['git']['app']['user']}" - action :run + user node['git']['user']['app'] end [node['git']['org']['main'], node['git']['org']['stage']].each do |org| @@ -98,7 +69,6 @@ )).code.to_i Logs.request!("Create organization '#{org}' failed", uri, response) unless [201, 409, 422].include? status_code end - action :run end end @@ -118,5 +88,4 @@ end end end - action :run end \ No newline at end of file diff --git a/config/recipes/git.rb b/config/recipes/git.rb index 17fbe18..e2c0a42 100644 --- a/config/recipes/git.rb +++ b/config/recipes/git.rb @@ -1,20 +1,24 @@ -Utils.download(self, "#{node['git']['dir']['install']}/gitea", +Utils.download(self, "#{node['git']['dir']['app']}/gitea", url: -> { ver = Utils.latest('https://github.com/go-gitea/gitea/releases/latest') "https://github.com/go-gitea/gitea/releases/download/v#{ver}/gitea-#{ver}-linux-#{Utils.arch(node)}" }, - owner: node['git']['app']['user'], - group: node['git']['app']['group'], + owner: node['git']['user']['app'] , + group: node['git']['user']['group'], mode: '0755' ) -template "#{node['git']['dir']['install']}/app.ini" do +template "#{node['git']['dir']['app']}/app.ini" do source 'git_app.ini.erb' - owner node['git']['app']['user'] - group node['git']['app']['group'] + owner node['git']['user']['app'] + group node['git']['user']['group'] mode '0644' + variables(host: node['host'], + run_user: node['git']['user']['app'] , ssh_user: node['git']['user']['ssh'], + app_dir: node['git']['dir']['app'], home_dir: node['git']['dir']['home'], + http_port: node['git']['port']['http'], ssh_port: node['git']['port']['ssh'] ) action :create_if_missing end Common.application(self, 'gitea', - user: node['git']['app']['user'], cwd: node['git']['dir']['data'], - exec: "#{node['git']['dir']['install']}/gitea web --config #{node['git']['dir']['install']}/app.ini", - unit: { 'Service' => { 'Environment' => "USER=#{node['git']['app']['user']} HOME=/home/#{node['git']['app']['user']}" } }, - subscribe: ["template[#{node['git']['dir']['install']}/app.ini]", "remote_file[#{node['git']['dir']['install']}/gitea]"] ) + user: node['git']['user']['app'] , cwd: node['git']['dir']['data'], + exec: "#{node['git']['dir']['app']}/gitea web --config #{node['git']['dir']['app']}/app.ini", + unit: { 'Service' => { 'Environment' => "USER=#{node['git']['user']['app'] } HOME=#{node['git']['dir']['home']}" } }, + subscribe: ["template[#{node['git']['dir']['app']}/app.ini]", "remote_file[#{node['git']['dir']['app']}/gitea]"] ) diff --git a/config/recipes/prepare.rb b/config/recipes/prepare.rb index 8905758..e950b08 100644 --- a/config/recipes/prepare.rb +++ b/config/recipes/prepare.rb @@ -1,52 +1,33 @@ -# Filesystem +Common.directories(self, [ (app = node['git']['dir']['app']), + node['git']['dir']['workspace'], node['runner']['dir']['app'], + "#{app}/custom", "#{app}/data", "#{app}/gitea-repositories", "#{app}/log" ], + owner: node['git']['user']['app'] , + group: node['git']['user']['group']) -Common.directories(self, [ "/home/#{node['git']['app']['user']}", - "#{node['git']['dir']['home']}", - "#{node['git']['dir']['install']}", - "#{node['git']['dir']['data']}", - "#{node['git']['dir']['data']}/custom", - "#{node['git']['dir']['data']}/data", - "#{node['git']['dir']['data']}/data/gitea-repositories", - "#{node['git']['dir']['data']}/log", - "#{node['git']['dir']['data']}/custom/conf", - "#{node['runner']['dir']['install']}", - "#{::File.dirname(node['key'])}", - "#{node['git']['dir']['workspace']}" -], owner: node['git']['app']['user'], group: node['git']['app']['group']) - -Common.packages(self, %w(git acl python3-pip ansible nodejs npm python3-proxmoxer)) +Common.packages(self, %w(git acl python3-pip ansible ansible-core nodejs npm python3-proxmoxer)) execute 'prepare_install_ansible' do command 'python3 -m pip install --upgrade ansible --break-system-packages' + environment 'HOME' => '/tmp' end execute 'prepare_install_ansible_galaxy' do command 'LC_ALL=C.UTF-8 ansible-galaxy collection install community.general' + environment 'HOME' => '/tmp' user 'root' not_if "ansible-galaxy collection list | grep community.general" end # Self-management -file node['key'] do - content lazy { ::File.read('/root/id_rsa') } - owner node['git']['app']['user'] - group node['git']['app']['group'] +file "#{node['git']['dir']['home']}/.ssh/config" do + content <<~CONF + Host #{node['host']} + HostName #{node['host']} + IdentityFile #{node['key']} + StrictHostKeyChecking no + CONF + owner node['git']['user']['app'] + group node['git']['user']['group'] mode '0600' - sensitive true - action :create - only_if { ::File.exist?('/root/id_rsa') } - not_if { ::File.exist?(node['key']) } -end - -file "#{node['key']}.pub" do - content lazy { ::File.read('/root/id_rsa.pub') } - owner node['git']['app']['user'] - group node['git']['app']['group'] - mode '0644' - action :create - only_if { ::File.exist?('/root/id_rsa.pub') } - not_if { ::File.exist?("#{node['key']}.pub") } end - - diff --git a/config/recipes/repo.rb b/config/recipes/repo.rb index 1e3d6d5..9d46986 100644 --- a/config/recipes/repo.rb +++ b/config/recipes/repo.rb @@ -1,15 +1,14 @@ require 'find' require 'fileutils' -home = "/home/#{node['git']['app']['user']}" source = ENV['PWD'] destination = node['git']['dir']['workspace'] working = "#{destination}/workdir" Common.directories(self, [destination, working], recreate: true, - owner: node['git']['app']['user'], group: node['git']['app']['group']) + owner: node['git']['user']['app'] , group: node['git']['user']['group']) -(repositories = node['git']['repositories'] +(repositories = node['git']['conf']['repo'] .flat_map { |r| (r == './libs') ? Dir.glob(File.join(source, r, '*')).select { |d| File.directory?(d) }.map { |p| p.sub(source, '.') } : r } .sort_by { |r| r == "./" ? 1 : 0 }).each do |repository| # dynamically resolved libs before monorepo @@ -20,30 +19,27 @@ path_working = "#{working}/#{name_repo}" path_destination = monorepo ? destination : File.expand_path(name_repo, destination) - Logs.info("#{repository} (#{name_repo})") - ruby_block "repo_exists_#{name_repo}" do + only_if { Logs.info?("#{repository} (#{name_repo})") } block do node.run_state["#{name_repo}_repo_exists"] = (Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", user: Env.get(node, 'login'), pass: Env.get(node, 'password')) ).code.to_i != 404 end - action :run end execute "repo_exists_snapshot_create_#{name_repo}" do command <<-EOH - if git ls-remote ssh://#{node['git']['app']['user']}@#{node['git']['host']['ssh']}/#{node['git']['org']['main']}/#{name_repo}.git HEAD | grep -q .; then - git clone --recurse-submodules ssh://#{node['git']['app']['user']}@#{node['git']['host']['ssh']}/#{node['git']['org']['main']}/#{name_repo}.git #{path_working} + if git ls-remote ssh://#{node['git']['user']['app'] }@#{node['git']['host']['ssh']}/#{node['git']['org']['main']}/#{name_repo}.git HEAD | grep -q .; then + git clone --recurse-submodules ssh://#{node['git']['user']['app'] }@#{node['git']['host']['ssh']}/#{node['git']['org']['main']}/#{name_repo}.git #{path_working} cd #{path_working} && git submodule update --init --recursive find . -type d -name .git -exec rm -rf {} + else mkdir -p #{path_working} fi EOH - user node['git']['app']['user'] - environment 'HOME' => home + user node['git']['user']['app'] only_if { Logs.info("[#{repository} (#{name_repo})]: delete repository after snapshot") node.run_state["#{name_repo}_repo_exists"] } end @@ -55,12 +51,11 @@ Logs.request!("Failed to delete #{name_repo}", uri, response) end end - action :run only_if { node.run_state["#{name_repo}_repo_exists"] } end - Logs.info("[#{repository} (#{name_repo})]: request repository") ruby_block "repo_request_#{name_repo}" do + only_if { Logs.info?("[#{repository} (#{name_repo})] create repository") } block do require 'json' (response = Utils.request( @@ -70,36 +65,32 @@ body: { name: name_repo, private: false, auto_init: false, default_branch: 'main' }.to_json )).code.to_i == 201 or Logs.request!("Error creating repository '#{name_repo}'", uri, response) end - action :run end execute "repo_git_init_#{name_repo}" do command <<-EOH mkdir -p #{path_destination} && cd #{path_destination} && git init -b main EOH - user node['git']['app']['user'] - environment 'HOME' => home + user node['git']['user']['app'] end template "#{path_destination}/.git/config" do source 'repo_config.erb' - owner node['git']['app']['user'] - group node['git']['app']['group'] + owner node['git']['user']['app'] + group node['git']['user']['group'] mode '0644' - variables(repo: name_repo, git_user: node['git']['app']['user']) - action :create + variables(repo: name_repo, git_user: node['git']['user']['app'] ) only_if { ::File.directory?("#{path_destination}/.git") } end - Logs.info("[#{repository} (#{name_repo})]: base commit") execute "repo_git_empty_#{name_repo}" do + only_if { Logs.info?("[#{repository} (#{name_repo})] base commit") } command <<-EOH git commit --allow-empty -m "base commit [skip ci]" && git checkout -b release git push -u origin main && git push -u origin release EOH cwd path_destination - user node['git']['app']['user'] - environment 'HOME' => home + user node['git']['user']['app'] end execute "repo_exists_snapshot_push_#{name_repo}" do @@ -110,8 +101,7 @@ git push -f origin snapshot && (rm -rf #{path_working} || true) EOH cwd path_destination - user node['git']['app']['user'] - environment 'HOME' => home + user node['git']['user']['app'] only_if { Logs.info("[#{repository} (#{name_repo})]: snapshot commit") node.run_state["#{name_repo}_repo_exists"] } end @@ -129,41 +119,38 @@ FileUtils.cp(path_src, path_dst, verbose: true) end end - FileUtils.chown_R(node['git']['app']['user'], node['git']['app']['group'], path_destination) + FileUtils.chown_R(node['git']['user']['app'] , node['git']['user']['group'], path_destination) end - action :run end directory "#{path_destination}/.gitea/workflows" do - action :create recursive true end template "#{path_destination}/.gitea/workflows/sync.yml" do - source 'pipeline_generic_sync.yml.erb' - owner node['git']['app']['user'] - group node['git']['app']['group'] + source 'repo_sync.yml.erb' + owner node['git']['user']['app'] + group node['git']['user']['group'] mode '0644' - action :create not_if { monorepo } not_if { File.exist?("#{path_destination}/.gitea/workflows/sync.yml") } end template "#{path_destination}/.gitea/workflows/pipeline.yml" do - source 'pipeline_generic_pipeline.yml.erb' - owner node['git']['app']['user'] - group node['git']['app']['group'] + source 'repo_pipeline.yml.erb' + owner node['git']['user']['app'] + group node['git']['user']['group'] mode '0644' - action :create only_if { repository.include?('libs/') and File.exist?("#{path_destination}/config.env") } not_if { File.exist?("#{path_destination}/.gitea/workflows/pipeline.yml") } end if monorepo submodules = repositories.reject { |r| r == "./" } # without itself - Logs.info("#{repository} (monorepository): referencing #{submodules}") + ruby_block 'repo_mono_submodule_rewritten' do + only_if { Logs.info?("#{repository} (monorepository): referencing #{submodules}") } block do path_dst_gitmodules = File.join(destination, '.gitmodules') if File.exist?(path_dst_gitmodules) @@ -172,8 +159,7 @@ .gsub(/(url\s*=\s*http:\/\/)([^:\/\s]+)/) { "#{$1}#{node['host']}" }) end end - action :run - end + end # Submodule handling in Monorepository submodules.each do |submodule| @@ -188,12 +174,8 @@ action :delete end - Logs.info("#{repository} (monorepository): referencing #{path_module} (#{module_name})") - execute "repo_mono_submodule_references_#{module_name}" do - cwd path_destination - user node['git']['app']['user'] - environment 'HOME' => home + only_if { Logs.info?("#{repository} (monorepository): referencing #{path_module} (#{module_name})") } command <<-EOH if ! git config --file .gitmodules --get-regexp path | grep -q "^submodule\\.#{module_name}\\.path"; then echo "submodule add: #{module_url} -> #{path_module}" @@ -205,6 +187,8 @@ git add -f local/config.json fi EOH + cwd path_destination + user node['git']['user']['app'] end end @@ -214,8 +198,7 @@ execute "repo_push_#{name_repo}" do cwd path_destination - user node['git']['app']['user'] - environment 'HOME' => home + user node['git']['user']['app'] command <<-EOH git add --all if ! git diff --quiet || ! git diff --cached --quiet; then @@ -234,7 +217,6 @@ fi fi EOH - action :run end directory path_destination do @@ -254,7 +236,6 @@ Logs.request!("Failed to clean test/#{name_repo} (#{status_code})", uri, response) unless [204, 404].include?(status_code) end end - action :run end ruby_block "repo_stage_fork_create_#{name_repo}" do @@ -266,7 +247,6 @@ ).code.to_i Logs.request!("Forking to #{node['git']['org']['stage']}/#{name_repo} failed", uri, response) unless [201, 202].include?(status_code) end - action :run end end diff --git a/config/recipes/runner.rb b/config/recipes/runner.rb index d850c7c..2d7af4e 100644 --- a/config/recipes/runner.rb +++ b/config/recipes/runner.rb @@ -1,25 +1,24 @@ -Common.directories(self, node['runner']['dir']['install'], owner: node['git']['app']['user'], group: node['git']['app']['group']) +Common.directories(self, node['runner']['dir']['app'], owner: node['git']['user']['app'] , group: node['git']['user']['group']) -Utils.download(self, "#{node['runner']['dir']['install']}/act_runner", +Utils.download(self, "#{node['runner']['dir']['app']}/act_runner", url: -> { ver = Utils.latest('https://gitea.com/gitea/act_runner/releases/latest') "https://gitea.com/gitea/act_runner/releases/download/v#{ver}/act_runner-#{ver}-linux-#{Utils.arch(node)}" }, - owner: node['git']['app']['user'], - group: node['git']['app']['group'], + owner: node['git']['user']['app'] , + group: node['git']['user']['group'], mode: '0755' ) -template "#{node['runner']['dir']['install']}/config.yaml" do +template "#{node['runner']['dir']['app']}/config.yaml" do source 'runner.config.yaml.erb' - owner node['git']['app']['user'] - group node['git']['app']['group'] + owner node['git']['user']['app'] + group node['git']['user']['group'] mode '0644' - action :create end Common.application(self, 'runner', - user: node['git']['app']['user'], action: [:enable], cwd: node['runner']['dir']['install'], - exec: "#{node['runner']['dir']['install']}/act_runner daemon --config #{node['runner']['dir']['install']}/config.yaml", - subscribe: ["template[#{node['runner']['dir']['install']}/config.yaml]", "remote_file[#{node['runner']['dir']['install']}/act_runner]"] ) + user: node['git']['user']['app'] , action: [:enable], cwd: node['runner']['dir']['app'], + exec: "#{node['runner']['dir']['app']}/act_runner daemon --config #{node['runner']['dir']['app']}/config.yaml", + subscribe: ["template[#{node['runner']['dir']['app']}/config.yaml]", "remote_file[#{node['runner']['dir']['app']}/act_runner]"] ) ruby_block 'runner_register' do block do @@ -38,29 +37,24 @@ 'Gitea not responding' unless connected (token = Mixlib::ShellOut.new( - "#{node['git']['dir']['install']}/gitea actions --config #{node['git']['dir']['install']}/app.ini generate-runner-token", - user: node['git']['app']['user'], - environment: { 'HOME' => "/home/#{node['git']['app']['user']}" } + "#{node['git']['dir']['app']}/gitea actions --config #{node['git']['dir']['app']}/app.ini generate-runner-token", + user: node['git']['user']['app'] )).run_command token.error! token = token.stdout.strip (register = Mixlib::ShellOut.new( - "#{node['runner']['dir']['install']}/act_runner register " \ + "#{node['runner']['dir']['app']}/act_runner register " \ "--instance http://localhost:#{node['git']['port']['http']} " \ "--token #{token} " \ "--no-interactive " \ "--labels shell " \ - "--config #{node['runner']['dir']['install']}/config.yaml", - cwd: node['runner']['dir']['install'], - user: node['git']['app']['user'], - environment: { 'HOME' => "/home/#{node['git']['app']['user']}" } + "--config #{node['runner']['dir']['app']}/config.yaml", + cwd: node['runner']['dir']['app'], + user: node['git']['user']['app'] )).run_command register.error! - - # File.write(node['runner']['marker_file'], Time.now.to_s) end - # not_if { ::File.exist?(node['runner']['marker_file']) } # stability over convention end Common.application(self, 'runner') diff --git a/config/templates/git_app.ini.erb b/config/templates/git_app.ini.erb index 72ff8d3..d19aaaa 100644 --- a/config/templates/git_app.ini.erb +++ b/config/templates/git_app.ini.erb @@ -1,19 +1,20 @@ ; Gitea Application Configuration APP_NAME = Gitea -RUN_USER = <%= node['git']['app']['user'] %> +RUN_USER = <%= @run_user %> RUN_MODE = prod [server] -DOMAIN = <%= node['host'] %> -HTTP_PORT = <%= node['git']['port']['http'] %> -ROOT_URL = http://<%= node['host'] %>:<%= node['git']['port']['http'] %>/ -SSH_PORT = <%= node['git']['port']['ssh'] %> +DOMAIN = <%= @host %> +HTTP_PORT = <%= @http_port %> +ROOT_URL = http://<%= @host %>:<%= @http_port %>/ +SSH_PORT = <%= @ssh_port %> START_SSH_SERVER = true +BUILTIN_SSH_SERVER_USER = <%= @ssh_user %> [database] DB_TYPE = sqlite3 -PATH = <%= node['git']['dir']['data'] %>/gitea.db +PATH = <%= @app_dir %>/gitea.db [security] INSTALL_LOCK = true @@ -25,7 +26,7 @@ DEFAULT_ACTIONS_URL=self LOG_RETENTION_DAYS = 7 [repository] -ROOT = <%= node['git']['dir']['data'] %>/gitea-repositories +ROOT = <%= @app_dir %>/gitea-repositories REPOSITORY_AVATAR_FALLBACK = none DISABLE_STARS = true DISABLE_MIGRATIONS = true @@ -40,10 +41,11 @@ SHOW_USER_EMAIL = false DISABLE_AVATAR = true [git] +HOME_PATH=<%= @home_dir %> MAX_GIT_DIFF_LINES = 500 MAX_GIT_DIFF_FILES = 50 GC_ARGS = --aggressive [log] MODE = console -LEVEL = Warn \ No newline at end of file +LEVEL = Error \ No newline at end of file diff --git a/config/templates/repo_config.erb b/config/templates/repo_config.erb index 1371e00..3a98311 100644 --- a/config/templates/repo_config.erb +++ b/config/templates/repo_config.erb @@ -5,7 +5,7 @@ logallrefupdates = true [remote "origin"] - url = ssh://<%= node['git']['app']['user'] %>@<%= node['git']['host']['ssh'] %>/<%= node['git']['org']['main'] %>/<%= @repo %>.git + url = ssh://<%= node['git']['user']['ssh'] %>@<%= node['git']['host']['ssh'] %>/<%= node['git']['org']['main'] %>/<%= @repo %>.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "main"] diff --git a/config/templates/pipeline_generic_pipeline.yml.erb b/config/templates/repo_pipeline.yml.erb similarity index 100% rename from config/templates/pipeline_generic_pipeline.yml.erb rename to config/templates/repo_pipeline.yml.erb diff --git a/config/templates/pipeline_generic_sync.yml.erb b/config/templates/repo_sync.yml.erb similarity index 100% rename from config/templates/pipeline_generic_sync.yml.erb rename to config/templates/repo_sync.yml.erb diff --git a/libs/assistant/recipes/default.rb b/libs/assistant/recipes/default.rb index 162585b..ff88131 100644 --- a/libs/assistant/recipes/default.rb +++ b/libs/assistant/recipes/default.rb @@ -16,7 +16,6 @@ to node['homeassistant']['dir']['config'] owner node['app']['user'] group node['app']['group'] - action :create end Utils.download(self, '/tmp/Python-3.13.5.tgz', url: 'https://www.python.org/ftp/python/3.13.5/Python-3.13.5.tgz') diff --git a/libs/bridge/recipes/default.rb b/libs/bridge/recipes/default.rb index 6bef7e4..c6ca999 100644 --- a/libs/bridge/recipes/default.rb +++ b/libs/bridge/recipes/default.rb @@ -49,8 +49,7 @@ user node['app']['user'] group node['app']['group'] cwd node['bridge']['dir'] - # environment('HOME' => "/home/#{node['app']['user']}") - only_if { latest_version } + not_if { !latest_version } end template "#{node['bridge']['data']}/configuration.yaml" do @@ -68,7 +67,7 @@ broker_user: Env.get(node, 'login'), broker_password: Env.get(node, 'password') ) - only_if { latest_version } + not_if { !latest_version } not_if { ::File.exist?("#{node['bridge']['data']}/configuration.yaml") } end diff --git a/libs/broker/recipes/default.rb b/libs/broker/recipes/default.rb index 9982bd8..bb5080d 100644 --- a/libs/broker/recipes/default.rb +++ b/libs/broker/recipes/default.rb @@ -22,7 +22,6 @@ owner node['app']['user'] group node['app']['group'] mode '0640' - action :create_if_missing end execute "user-add_#{Env.get(node, 'login')}" do diff --git a/libs/proxy/recipes/default.rb b/libs/proxy/recipes/default.rb index a98ad4d..2ae2e7c 100644 --- a/libs/proxy/recipes/default.rb +++ b/libs/proxy/recipes/default.rb @@ -25,7 +25,6 @@ hosts: lazy { node.run_state['proxy_hosts'] || [] }, log_dir: node['proxy']['dir']['logs'] ) - action :create end Common.application(self, 'caddy', user: node['app']['user'], diff --git a/libs/share/recipes/default.rb b/libs/share/recipes/default.rb index 35bd84e..093adc4 100644 --- a/libs/share/recipes/default.rb +++ b/libs/share/recipes/default.rb @@ -5,7 +5,6 @@ group login do gid node['share']['user']['gid'] - action :create end user login do @@ -13,7 +12,6 @@ gid node['share']['user']['gid'] shell '/bin/false' manage_home false - action :create end Array(node.dig('share','mount')).each do |path| @@ -22,7 +20,6 @@ group login mode '2775' recursive false - action :create end end diff --git a/local/Dockerfile b/local/Dockerfile index f96c60f..ce5ea59 100644 --- a/local/Dockerfile +++ b/local/Dockerfile @@ -25,14 +25,14 @@ RUN rm -rf /lib/systemd/system/multi-user.target.wants/* && \ RUN python3 -m pip install --upgrade ansible --break-system-packages RUN ansible-galaxy collection install community.general --force -RUN mkdir -p /share/.ssh && ssh-keygen -t ed25519 -f "/share/.ssh/${ID}" -N '' +RUN mkdir -p /app/.ssh /share/.ssh && ssh-keygen -t ed25519 -f "/share/.ssh/${ID}" -N '' COPY ../base/roles /tmp/base RUN ansible localhost -m include_role -a name=base -e \ - "ip=${IP}" -e "id=${ID}" -e "configure_ssh=0" -e "arch=${TARGETARCH}" \ + "ip=${IP}" -e "id=${ID}" -e "KEYS_DIR=/share/.ssh" -e "configure_ssh=0" -e "arch=${TARGETARCH}" \ -e "private_key='$(cat /share/.ssh/${ID})\n'" -e "public_key='$(cat /share/.ssh/${ID}.pub)\n'" -RUN chown -R app:app /share +RUN chown -R app:config /app WORKDIR / diff --git a/local/run.sh b/local/run.sh index 1bf17b2..93b49db 100755 --- a/local/run.sh +++ b/local/run.sh @@ -62,12 +62,13 @@ CONTAINER_ID=$(docker run -d --privileged --cgroupns=host --tmpfs /tmp \ log "container" "started:${CONTAINER_ID}" sleep "$DOCKER_WAIT" -cmd="cinc-client -l info --local-mode --config-option node_path=/tmp/nodes \ - --config-option cookbook_path=${COOKBOOK_PATH} ${CONFIG_FILE} --chef-license accept -o ${RECIPE}" -docker exec "$CONTAINER_ID" bash -c "$cmd" || log "error" "exec_failed" +command='sudo $(sudo -u config env) PWD=/tmp/config --preserve-env=ID \ + cinc-client -l info --local-mode --chef-license accept --config-option node_path=/tmp/nodes \ + --config-option cookbook_path='"$COOKBOOK_PATH"' '"$CONFIG_FILE"' -o '"$RECIPE"'' +docker exec "$CONTAINER_ID" bash -c "$command" || log "error" "exec_failed" -[[ -z "${COOKBOOK_OVERRIDE}" ]] && cmd+="::repo" +[[ -z "${COOKBOOK_OVERRIDE}" ]] && command+="::repo" while true; do log "rerun" "$RECIPE" && read -r - docker exec "$CONTAINER_ID" bash -c "$cmd" || log "error" "exec_failed" + docker exec "$CONTAINER_ID" bash -c "$command" || log "error" "exec_failed" done From 5e0a3a41550495a1827847678bb02fcc3bcc601e Mon Sep 17 00:00:00 2001 From: Steven <106664555+stevius10@users.noreply.github.com> Date: Thu, 14 Aug 2025 22:43:49 +0200 Subject: [PATCH 05/13] dev/v1.1 (#57) * include base folder for docker image build checksum * avoid to push config.json * set storage note bold * default environment and profile * changed due user defaults * changed root user over token * return statement for request log improves integrability * reduce unneeded tmp folders * use utils request function with expect * pass variables while bootstrap within structure * rename for clearance * pass valid home directory * try sudo with -E flag * same code style * dynamic env from user * set home explicit --- .gitea/workflows/action.yml | 12 ++--- .gitea/workflows/pipeline.yml | 19 ++++---- .github/workflows/build.yml | 10 ++--- README.md | 4 +- base/default.yml | 21 --------- base/roles/base/files/environment | 8 ++++ base/roles/base/files/{.bashrc => profile.sh} | 0 base/roles/base/tasks/defaults.yml | 44 ++++++++++++------- base/roles/container/defaults/main.yml | 6 --- base/roles/container/tasks/env.yml | 40 +++++++++++++++++ base/roles/container/tasks/main.yml | 15 +------ config/libraries/env.rb | 33 +++++++------- config/libraries/logs.rb | 2 +- config/recipes/config.rb | 13 +++--- config/recipes/repo.rb | 2 +- local/Dockerfile | 12 ++--- local/run.sh | 14 +++--- 17 files changed, 135 insertions(+), 120 deletions(-) create mode 100644 base/roles/base/files/environment rename base/roles/base/files/{.bashrc => profile.sh} (100%) create mode 100644 base/roles/container/tasks/env.yml diff --git a/.gitea/workflows/action.yml b/.gitea/workflows/action.yml index ab74452..062e057 100644 --- a/.gitea/workflows/action.yml +++ b/.gitea/workflows/action.yml @@ -62,9 +62,11 @@ runs: - name: Configure container run: | - tar cz . | \ - ssh -o StrictHostKeyChecking=no -i /share/.ssh/${{ env.id }} config@${{ env.ip }} 'sudo tar xz -C /tmp && env \ - IP=${{ env.ip }} ID=${{ env.id }} HOST=${{ vars.HOST }} LOGIN=${{ vars.LOGIN }} PASSWORD=${{ vars.PASSWORD }} PWD="$(pwd)" \ - cinc-client -l info --local-mode $( [[ -f "/tmp/repo/config.json" ]] && echo "-j /tmp/repo/config.json" ) \ - --chef-license accept --config-option cookbook_path="/tmp" -o repo' + tar -c repo -cz . | ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${id}" "config@${ip}" 'sudo tar xz -C /tmp + sudo -E IP=${{ env.ip }} ID=${{ env.id }} HOST=${{ vars.HOST }} LOGIN=${{ vars.LOGIN }} PASSWORD=${{ vars.PASSWORD }} PWD="$(pwd)" \ + cinc-client --local-mode $CONFIG_ARGS --config-option cookbook_path="/tmp" -o repo' shell: bash + + + + diff --git a/.gitea/workflows/pipeline.yml b/.gitea/workflows/pipeline.yml index 880f1fd..9419403 100644 --- a/.gitea/workflows/pipeline.yml +++ b/.gitea/workflows/pipeline.yml @@ -69,11 +69,9 @@ jobs: login: ${{ vars.LOGIN }} password: ${{ vars.PASSWORD }} run: | - tar -c config -cz . | \ - ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${id}" "config@${ip}" 'sudo tar xz -C /tmp && sudo \ - $(sudo -u config env) IP="'"${ip}"'" ID="'"${id}"'" LOGIN="'"${login}"'" PASSWORD="'"${password}"'" \ - cinc-client -l info --local-mode --chef-license accept --config-option \ - cookbook_path="[\"'"/tmp/config"'\", \"'"/tmp/config/libs"'\"]" -o share' + tar -c config -cz . | ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${id}" "config@${ip}" 'sudo tar xz -C /tmp + sudo -E IP="'"${ip}"'" ID="'"${id}"'" LOGIN="'"${login}"'" PASSWORD="'"${password}"'" \ + cinc-client --local-mode --config-option cookbook_path="[\"/tmp/config\", \"/tmp/config/libs\"]" -o share' if: ${{ gitea.ref != 'refs/heads/release' || success() }} config: @@ -91,11 +89,10 @@ jobs: id: ${{ needs.init.outputs.id }} login: ${{ vars.LOGIN }} password: ${{ vars.PASSWORD }} + email: ${{ vars.EMAIL }} run: | git -C config submodule update --remote --recursive - tar -c config -cz . | \ - ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${id}" "config@${ip}" 'sudo tar xz -C /tmp && sudo \ - $(sudo -u config env) IP="'"${ip}"'" ID="'"${id}"'" LOGIN="'"${login}"'" PASSWORD="'"${password}"'" PWD="/tmp/config" \ - cinc-client -l info --local-mode $( [ -f /tmp/config/local/config.json ] && echo "-j /tmp/config/local/config.json" ) \ - --config-option cookbook_path="/tmp/config" --chef-license accept -o config' - if: ${{ gitea.ref != 'refs/heads/release' || success() }} + tar -c config -cz . | ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${id}" "config@${ip}" 'sudo tar xz -C /tmp + CONFIG_ARGS=; [ -f /tmp/config/local/config.json ] && CONFIG_ARGS="-j /tmp/config/local/config.json" + sudo -E IP="'"${ip}"'" ID="'"${id}"'" LOGIN="'"${login}"'" PASSWORD="'"${password}"'" EMAIL="'"${email}"'" PWD=/tmp/config \ + cinc-client --local-mode $CONFIG_ARGS --config-option cookbook_path="/tmp/config" -o config' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5d8cdf4..5d90be4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -75,8 +75,8 @@ jobs: - name: Mock config.json run: | cat < ./local/config.json - { "proxmox": { "host": "localhost", "api": "test", "user": "test", "token": "test", "secret": "test" }, - "login": "test", "password": "test", "email": "test@example.com", "pub": "" } + { "proxmox": { "host": "localhost", "user": "test", "password": "test" }, + "login": "test", "password": "test", "email": "test@example.com" } EOF - name: Deploy config env: @@ -84,6 +84,6 @@ jobs: ID: ${{ needs.init.outputs.id }} PWD: ${{ github.workspace }} run: | - sudo env IP="$IP" ID="$ID" PWD="$PWD" \ - cinc-client -l info --local-mode --chef-license accept --config-option cookbook_path="." \ - -j ./local/config.json -o config + sudo env IP="$IP" ID="$ID" PWD="$PWD" HOME="/app" \ + cinc-client -l info --local-mode --chef-license accept --config-option \ + cookbook_path="." -j ./local/config.json -o config diff --git a/README.md b/README.md index 4e08968..d2f8ecd 100644 --- a/README.md +++ b/README.md @@ -86,9 +86,9 @@ This system implements stateless infrastructure management on Proxmox VE, ensuri ### Getting Started - Set **Proxmox** and **global usage credentials** in [`local/.config.json`](local/.config.json) as `./local/config.json` -- Ensure **container configuration** in [`config.env`](config.env) and verify storage +- Ensure **container configuration** in [`config.env`](config.env) and **verify storage** - Run `./local/run.sh` for local Docker environment -- Accept the Pull Request at `localhost:8080/main/config/pulls/1` to deploy on Proxmox VE +- Accept the Pull Request at `localhost:8080/main/config` to deploy on Proxmox VE

in a nutshell diff --git a/base/default.yml b/base/default.yml index fbb95f5..dd18ae3 100644 --- a/base/default.yml +++ b/base/default.yml @@ -17,27 +17,6 @@ type: ed25519 force: false - - name: Check container configuration - ansible.builtin.stat: - path: "config.json" - register: config_file - - - name: Load container configuration - ansible.builtin.include_vars: - file: "config.json" - name: config - when: config_file.stat.exists - - - name: Set Proxmox configuration - ansible.builtin.set_fact: - PROXMOX_HOST: "{{ lookup('env', 'PROXMOX_HOST') }}" - PROXMOX_USER: "{{ lookup('env', 'PROXMOX_USER') }}" - PROXMOX_PASSWORD: "{{ lookup('env', 'PROXMOX_PASSWORD') }}" - PROXMOX_TOKEN: "{{ lookup('env', 'PROXMOX_TOKEN') }}" - PROXMOX_SECRET: "{{ lookup('env', 'PROXMOX_SECRET') }}" - when: - - (config_file.stat.exists and config.proxmox is defined) or (not config_file.stat.exists) - - name: Container ansible.builtin.include_role: name: container diff --git a/base/roles/base/files/environment b/base/roles/base/files/environment new file mode 100644 index 0000000..cffbfd4 --- /dev/null +++ b/base/roles/base/files/environment @@ -0,0 +1,8 @@ +CHEF_NODE_NAME=$(hostname -f) + +CHEF_LICENSE=accept + +CHEF_LOG_LEVEL=info +CHEF_LOG_LOCATION=STDOUT + +CHEF_CACHE_PATH=/var/cache/cinc \ No newline at end of file diff --git a/base/roles/base/files/.bashrc b/base/roles/base/files/profile.sh similarity index 100% rename from base/roles/base/files/.bashrc rename to base/roles/base/files/profile.sh diff --git a/base/roles/base/tasks/defaults.yml b/base/roles/base/tasks/defaults.yml index 1e08280..6a351ec 100644 --- a/base/roles/base/tasks/defaults.yml +++ b/base/roles/base/tasks/defaults.yml @@ -1,19 +1,31 @@ - name: User defaults block: - - name: Set root bash configuration - copy: - src: .bashrc - dest: /root/.bashrc - owner: root - group: root - mode: '0644' - - name: Set user bash configuration - copy: - src: .bashrc - dest: "{{ (item.home | default('/home/' + item.name)) }}/.bashrc" - owner: "{{ item.name }}" - group: "{{ item.name }}" - mode: '0644' - loop: "{{ default_users }}" - when: item.name in ssh_users + - name: Set environment + copy: + src: environment + dest: /etc/environment + owner: root + group: root + mode: '0644' + + - name: Set profile + copy: + src: profile.sh + dest: /etc/profile.d/10-profile.sh + owner: root + group: root + mode: '0644' + + - name: User + block: + - name: Set root bashrc + copy: + dest: /root/.bashrc + content: | + for f in /etc/profile.d/*.sh; do + . "$f" + done + owner: root + group: root + mode: '0644' \ No newline at end of file diff --git a/base/roles/container/defaults/main.yml b/base/roles/container/defaults/main.yml index 1dd6a3e..fa57cc2 100644 --- a/base/roles/container/defaults/main.yml +++ b/base/roles/container/defaults/main.yml @@ -1,9 +1,3 @@ -PROXMOX_HOST: "{{ lookup('env', 'PROXMOX_HOST') }}" -PROXMOX_USER: "{{ lookup('env', 'PROXMOX_USER') }}" -PROXMOX_PASSWORD: "{{ lookup('env', 'PROXMOX_PASSWORD') }}" -PROXMOX_TOKEN: "{{ lookup('env', 'PROXMOX_TOKEN') }}" -PROXMOX_SECRET: "{{ lookup('env', 'PROXMOX_SECRET') }}" - proxmox_auth: >- {{ (PROXMOX_PASSWORD | default('') == '') | ternary( {'Authorization': 'PVEAPIToken=' + PROXMOX_USER + '!' + PROXMOX_TOKEN + '=' + PROXMOX_SECRET}, diff --git a/base/roles/container/tasks/env.yml b/base/roles/container/tasks/env.yml new file mode 100644 index 0000000..764d277 --- /dev/null +++ b/base/roles/container/tasks/env.yml @@ -0,0 +1,40 @@ +- name: Environment + block: + - name: Configuration file + block: + - name: Get configuration file + ansible.builtin.stat: + path: "config.json" + register: config_file + + - name: Load configuration file + ansible.builtin.include_vars: + file: "config.json" + name: config + when: config_file.stat.exists + + - name: Proxmox configuration + block: + - name: Set Proxmox environment + ansible.builtin.set_fact: + PROXMOX_HOST: "{{ lookup('env', 'PROXMOX_HOST') }}" + PROXMOX_USER: "{{ lookup('env', 'PROXMOX_USER') }}" + PROXMOX_PASSWORD: "{{ lookup('env', 'PROXMOX_PASSWORD') }}" + PROXMOX_TOKEN: "{{ lookup('env', 'PROXMOX_TOKEN') }}" + PROXMOX_SECRET: "{{ lookup('env', 'PROXMOX_SECRET') }}" + when: + - (config_file.stat.exists and config.proxmox is defined) or (not config_file.stat.exists) + + - name: Get root ticket + block: + - name: Get Proxmox ticket + uri: + url: "https://{{ PROXMOX_HOST }}:8006/api2/json/access/ticket" + method: POST + body: + username: "{{ PROXMOX_USER }}" + password: "{{ PROXMOX_PASSWORD }}" + body_format: json + validate_certs: no + register: proxmox_login + when: (PROXMOX_PASSWORD | default('')) | trim != '' diff --git a/base/roles/container/tasks/main.yml b/base/roles/container/tasks/main.yml index 9e635be..357c855 100644 --- a/base/roles/container/tasks/main.yml +++ b/base/roles/container/tasks/main.yml @@ -1,16 +1,5 @@ -- name: Proxmox authorization - block: - - name: Get Proxmox ticket - uri: - url: "https://{{ PROXMOX_HOST }}:8006/api2/json/access/ticket" - method: POST - body: - username: "{{ PROXMOX_USER }}" - password: "{{ PROXMOX_PASSWORD }}" - body_format: json - validate_certs: no - register: proxmox_login - when: PROXMOX_PASSWORD is defined and PROXMOX_PASSWORD != '' +- name: Environment + include_tasks: env.yml - name: Container orchestration block: diff --git a/config/libraries/env.rb b/config/libraries/env.rb index df1da61..26138c7 100644 --- a/config/libraries/env.rb +++ b/config/libraries/env.rb @@ -5,8 +5,9 @@ module Env def self.creds(node, login_key = 'login', password_key = 'password') - [ Logs.assignment(login_key, (login=ENV[login_key.upcase] || node[login_key.to_sym])), - Logs.assignment(password_key, (password=ENV[password_key.upcase] || node[password_key.to_sym])) ] + user ||= Logs.assignment(login_key, (login=ENV[login_key.upcase] || node[login_key.to_sym])) + pass ||= Logs.assignment(password_key, (password=ENV[password_key.upcase] || node[password_key.to_sym])) + return user, pass end def self.get(node, key) @@ -18,39 +19,39 @@ def self.get(node, key) def self.get_variable(node, key) JSON.parse(request(node, key).body)['data'] rescue => e - Logs.debug(:warn, "failed get variable '#{key}'", :error, e.message, :endpoint, endpoint(node), :node_key, node[key], :env_key, ENV[key.to_s.upcase]) + Logs.debug(:warn, "failed get variable '#{key}'", +:error, e.message, :endpoint, endpoint(node), :node_key, node[key], :env_key, ENV[key.to_s.upcase]) end def self.set_variable(node, key, val) - request(node, key, { name: key, value: val.to_s }.to_json) + raise unless Logs.assignment(key, request(node, key, { name: key, value: val.to_s }.to_json, expect: true)) rescue => e - Logs.debug(:warn, "failed set #{Logs.mask(val)} for variable '#{key}'", :error, e.message, :endpoint, endpoint(node), :node_key, node[key], :env_key, ENV[key.to_s.upcase]) + Logs.debug(:error, "failed set #{Logs.mask(val)} for variable '#{key}'", [:error, e.message, :endpoint, endpoint(node), :node_key, node[key], :env_key, ENV[key.to_s.upcase] ]) end class << self alias_method :set, :set_variable end - private_class_method def self.or_default(var, default) - var.to_s.presence ? var.to_s : default.to_s + def self.or_default(var, default) + var.to_s.presence || default.to_s end - private_class_method def self.endpoint(node, port=or_default(node.dig('git', 'port', 'http'), '8080')) + def self.endpoint(node, port = nil) + port ||= or_default(node.dig('git', 'port', 'http'), '8080') or_default(node.dig('git', 'api', 'endpoint'), -"http://#{or_default(node['host'].to_s.presence || ENV['HOST'].to_s.presence, '127.0.0.1')}:#{port}/api/#{or_default(node.dig('git', 'version'), 'v1')}") + "http://#{or_default(node['host'].to_s.presence || ENV['HOST'].to_s.presence, '127.0.0.1')} + :#{port}/api/#{or_default(node.dig('git', 'version'), 'v1')}") end - private_class_method def self.request(node, key, body = nil) + def self.request(node, key, body = nil, expect = false) + user, pass = creds(node) uri = URI("#{endpoint(node)}/orgs/#{or_default(node.dig('git', 'org', 'main'), 'main')}/actions/variables/#{key}") (body ? [Net::HTTP::Put, Net::HTTP::Post] : [Net::HTTP::Get]).each do |method| - req = method.new(uri) - req.basic_auth(*creds(node)) - req['Content-Type'] = 'application/json' - req.body = body if body - Logs.request(uri, response=(Net::HTTP.start(uri.host, uri.port) { |h| h.request(req) })) - return response unless body && response.code.to_i == 404 or Logs.request!(uri, response) + Utils.request(uri, user: user, pass: pass, expect: expect, headers: { 'Content-Type' => 'application/json' }, method: method, body: body) end end + end class Object diff --git a/config/libraries/logs.rb b/config/libraries/logs.rb index 3f1d55c..177a84a 100644 --- a/config/libraries/logs.rb +++ b/config/libraries/logs.rb @@ -29,7 +29,7 @@ def self.log(level, msg, masks = []) def self.info(msg); log(:info, msg) end def self.warn(msg); log(:warn, msg) end def self.error(msg); log(:error, msg) end - def self.request(uri, response); info("request #{uri}: #{response.code} #{response.message}") end + def self.request(uri, response); info("request #{uri}: #{response&.code} #{response&.message}"); return response end def self.assignment(key, val); info("#{key}: #{mask(val)}"); return val end def self.debug(level, msg, *pairs) diff --git a/config/recipes/config.rb b/config/recipes/config.rb index 69e1776..8aa2851 100644 --- a/config/recipes/config.rb +++ b/config/recipes/config.rb @@ -74,17 +74,16 @@ ruby_block 'config_git_environment' do block do - %w(proxmox login password email host).each do |parent_key| - value = node[parent_key] + %w(proxmox login password email host).each do |parent| + value = node[parent] next if value.nil? || value.to_s.strip.empty? if value.is_a?(Hash) - value.each do |subkey, subvalue| - next if subvalue.nil? || subvalue.to_s.strip.empty? - combined_key = "#{parent_key}_#{subkey}" - Env.set_variable(Chef.run_context.node, combined_key, subvalue) + value.each do |child, child_value| + next if child_value.nil? || child_value.to_s.strip.empty? + Env.set_variable(node, "#{parent}_#{child}", child_value) end else - Env.set_variable(Chef.run_context.node, parent_key, value) + Env.set_variable(Chef.node, parent, value) end end end diff --git a/config/recipes/repo.rb b/config/recipes/repo.rb index 9d46986..4798c88 100644 --- a/config/recipes/repo.rb +++ b/config/recipes/repo.rb @@ -182,7 +182,7 @@ git submodule add #{module_url} #{path_module} fi git submodule update --init --recursive - # bootstrap only + # pass variables in bootstrap if [ "#{Env.get(node, 'host')}" = "127.0.0.1" ] && [ -f local/config.json ]; then git add -f local/config.json fi diff --git a/local/Dockerfile b/local/Dockerfile index ce5ea59..8d568ee 100644 --- a/local/Dockerfile +++ b/local/Dockerfile @@ -1,11 +1,7 @@ FROM debian:bookworm ARG TARGETARCH=arm64 -ENV IP=127.0.0.1 \ - ID=254 \ - ANSIBLE_ROLES_PATH=/tmp/base \ - LC_ALL=C.UTF-8 - +ENV IP=127.0.0.1 ID=254 ANSIBLE_ROLES_PATH=/tmp/config/base LC_ALL=C.UTF-8 STOPSIGNAL SIGRTMIN+3 RUN apt update -y && \ @@ -27,9 +23,9 @@ RUN ansible-galaxy collection install community.general --force RUN mkdir -p /app/.ssh /share/.ssh && ssh-keygen -t ed25519 -f "/share/.ssh/${ID}" -N '' -COPY ../base/roles /tmp/base -RUN ansible localhost -m include_role -a name=base -e \ - "ip=${IP}" -e "id=${ID}" -e "KEYS_DIR=/share/.ssh" -e "configure_ssh=0" -e "arch=${TARGETARCH}" \ +COPY ../base/roles /tmp/config/base +RUN ansible localhost -m include_role -a name=base \ + -e "ip=${IP}" -e "id=${ID}" -e "KEYS_DIR=/share/.ssh" -e "configure_ssh=0" -e "arch=${TARGETARCH}" \ -e "private_key='$(cat /share/.ssh/${ID})\n'" -e "public_key='$(cat /share/.ssh/${ID}.pub)\n'" RUN chown -R app:config /app diff --git a/local/run.sh b/local/run.sh index 93b49db..10f4146 100755 --- a/local/run.sh +++ b/local/run.sh @@ -30,17 +30,15 @@ esac export DOCKER_DEFAULT_PLATFORM="${DOCKER_DEFAULT_PLATFORM:-linux/${TARGETARCH}}" DOCKERFILE_PATH="${DEVELOP_DIR}/Dockerfile" -[[ -f "${DOCKERFILE_PATH}" ]] || fail "dockerfile_missing:${DOCKERFILE_PATH}" -DOCKERFILE_HASH=$(md5sum "${DOCKERFILE_PATH}" | awk '{print $1}') +BASE=$(find "base" -type f -not -path "*/.git/*" -print0 | sort -z | xargs -0 md5sum | md5sum | awk '{print $1}') +HASH=$(echo "$(md5sum "$DOCKERFILE_PATH" | awk '{print $1}')${BASE}" | md5sum | awk '{print $1}') STORED_HASH_FILE="${DEVELOP_DIR}/.${DOCKER_IMAGE_NAME}.hash" -STORED_HASH=$(cat "${STORED_HASH_FILE}" 2>/dev/null || true) +STORED_HASH=$(cat "$STORED_HASH_FILE" 2>/dev/null || true) -BUILD_NEEDED=false -if [[ -z "$(docker images -q "${DOCKER_IMAGE_NAME}")" || "${STORED_HASH}" != "${DOCKERFILE_HASH}" ]]; then - BUILD_NEEDED=true +if [[ -z "$(docker images -q "${DOCKER_IMAGE_NAME}")" || "$STORED_HASH" != "$HASH" ]]; then log "image" "build_required" docker build --build-arg TARGETARCH="$TARGETARCH" -t "$DOCKER_IMAGE_NAME" -f "$DOCKERFILE_PATH" "$PROJECT_DIR" || fail "build_failed" - echo "$DOCKERFILE_HASH" > "$STORED_HASH_FILE" + echo "$HASH" > "$STORED_HASH_FILE" log "image" "build_complete" fi @@ -63,7 +61,7 @@ log "container" "started:${CONTAINER_ID}" sleep "$DOCKER_WAIT" command='sudo $(sudo -u config env) PWD=/tmp/config --preserve-env=ID \ - cinc-client -l info --local-mode --chef-license accept --config-option node_path=/tmp/nodes \ + cinc-client --local-mode --config-option node_path=/tmp/nodes \ --config-option cookbook_path='"$COOKBOOK_PATH"' '"$CONFIG_FILE"' -o '"$RECIPE"'' docker exec "$CONTAINER_ID" bash -c "$command" || log "error" "exec_failed" From b28fa46dcb8ddb4775584c5126260c3df1ebf400 Mon Sep 17 00:00:00 2001 From: Steven <106664555+stevius10@users.noreply.github.com> Date: Fri, 15 Aug 2025 20:59:27 +0200 Subject: [PATCH 06/13] dev/v1.1-environment-variables (#58) * use action variables * mounts need to be evaluated independent of sharing * return statements for log messages for better integration * added: default.rb * snapshot with base checksum and enhanced requests * explicit info logging * set back 127.0.0.1 for pattern matching * updated with default usage example * feature to dump variables on repository level * adjust return behavior * environment overview added --- .gitea/workflows/action.yml | 2 +- README.md | 21 +++--- base/roles/container/tasks/create.yml | 2 - config/attributes/default.rb | 8 +-- config/libraries/common.rb | 70 +++++++++++--------- config/libraries/default.rb | 19 ++++++ config/libraries/env.rb | 89 +++++++++++++++----------- config/libraries/logs.rb | 8 +-- config/libraries/utils.rb | 61 +++++++++++------- config/recipes/config.rb | 36 ++++++----- config/recipes/git.rb | 16 ++--- config/recipes/prepare.rb | 8 +-- config/recipes/repo.rb | 58 +++++++++-------- config/recipes/runner.rb | 17 +++-- config/templates/repo_config.erb | 2 +- docs/environment.png | Bin 0 -> 152975 bytes libs/assistant/attributes/default.rb | 4 +- libs/bridge/attributes/default.rb | 4 +- libs/broker/attributes/default.rb | 4 +- libs/proxy/attributes/default.rb | 15 +++-- local/run.sh | 2 +- 21 files changed, 258 insertions(+), 188 deletions(-) create mode 100644 config/libraries/default.rb create mode 100644 docs/environment.png diff --git a/.gitea/workflows/action.yml b/.gitea/workflows/action.yml index 062e057..3a684ee 100644 --- a/.gitea/workflows/action.yml +++ b/.gitea/workflows/action.yml @@ -62,7 +62,7 @@ runs: - name: Configure container run: | - tar -c repo -cz . | ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${id}" "config@${ip}" 'sudo tar xz -C /tmp + tar -c repo -cz . | ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${{ env.id }}" "config@${{ env.ip }}" 'sudo tar xz -C /tmp sudo -E IP=${{ env.ip }} ID=${{ env.id }} HOST=${{ vars.HOST }} LOGIN=${{ vars.LOGIN }} PASSWORD=${{ vars.PASSWORD }} PWD="$(pwd)" \ cinc-client --local-mode $CONFIG_ARGS --config-option cookbook_path="/tmp" -o repo' shell: bash diff --git a/README.md b/README.md index d2f8ecd..2b1e138 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ - [Self-contained Monorepository](#self-contained-monorepository) - [Requirements](#requirements) - [Getting Started](#getting-started) - - [Development and Container Extension](#development-and-container-extension) + - [Development and Extension](#development-and-extension) --- @@ -85,8 +85,8 @@ This system implements stateless infrastructure management on Proxmox VE, ensuri ### Getting Started -- Set **Proxmox** and **global usage credentials** in [`local/.config.json`](local/.config.json) as `./local/config.json` -- Ensure **container configuration** in [`config.env`](config.env) and **verify storage** +- Set **Proxmox** and **global usage credentials** in [`local/.config.json`](local/.config.json) as [`./local/config.json`](https://github.com/stevius10/Proxmox-GitOps/wiki/Example-Configuration#file-localconfigjson) +- Ensure **container configuration** in [`config.env`](config.env) and [**verify storage**](https://github.com/stevius10/Proxmox-GitOps/wiki/Example-Configuration#file-configenv) - Run `./local/run.sh` for local Docker environment - Accept the Pull Request at `localhost:8080/main/config` to deploy on Proxmox VE @@ -94,11 +94,9 @@ This system implements stateless infrastructure management on Proxmox VE, ensuri in a nutshell

-#### Development and Container Extension +#### Development and Extension -Reusable container definitions are stored in the [`libs`](libs) folder. - -Copy an example container (like [`libs/broker`](libs/broker) or [`libs/proxy`](libs/proxy)) as a template, or create a new container lib from scratch and follow these steps: +Reusable container definitions are stored in the [`libs`](libs) folder. Copy an example container (like [`libs/broker`](libs/broker) or [`libs/proxy`](libs/proxy)) as a template, or create a new container lib from scratch and follow these steps: - Add `config.env` to your container's root directory (e.g. `./libs/apache`): ```dotenv @@ -113,6 +111,7 @@ BOOT=yes ``` - Add your cookbook to the container definition root: + ```ruby # libs/apache/recipes/default.rb package 'apache2' @@ -120,8 +119,8 @@ package 'apache2' file '/var/www/html/index.html' do content "

Hello from #{Env.get(node, 'login')}

" mode '0644' - owner 'app' # see base/roles/base/tasks/main.yml - group 'config' # each container is configured identically + owner Default.user(self) # see base/roles/base/tasks/main.yml + group Default.group(self) # each container is configured identically end Common.application(self, 'apache2') # provided by convention @@ -129,6 +128,10 @@ Common.application(self, 'apache2') # provided by convention - Optionally, use `Env.get()` and `Env.set()` to access Gitea environment variables. +

+ Global Environment +

+ - Add to Monorepository and redeploy. The container can be tested locally running `./local/run.sh [container]` (_wip_) diff --git a/base/roles/container/tasks/create.yml b/base/roles/container/tasks/create.yml index 6f20c0c..989d982 100644 --- a/base/roles/container/tasks/create.yml +++ b/base/roles/container/tasks/create.yml @@ -14,7 +14,6 @@ index_var: idx when: - mount is defined - - share | default(true) | bool - mount | trim | length > 0 - name: Set passthrough @@ -25,7 +24,6 @@ index_var: idx when: - mount is defined - - share | default(true) | bool - mount | trim | length > 0 - name: Create container diff --git a/config/attributes/default.rb b/config/attributes/default.rb index cf7578c..44be7f0 100644 --- a/config/attributes/default.rb +++ b/config/attributes/default.rb @@ -2,15 +2,15 @@ default['host'] = ENV['IP'].to_s.presence ? ENV['IP'] : "127.0.0.1" default['key'] = ENV['KEY'].to_s.presence ? ENV['KEY'] : "/share/.ssh/#{node['id']}" -default['git']['user']['app'] = 'app' -default['git']['user']['group'] = 'config' -default['git']['user']['ssh'] = 'config' +default['app']['user'] = Default.user(node, default: true) +default['app']['group'] = Default.group(node, default: true) +default['app']['config'] = Default.config(node, default: true) default['git']['conf']['customize'] = true default['git']['conf']['repo'] = [ "./", "./base", "./config/libraries", "./libs" ] default['git']['dir']['app'] = '/app/git' -default['git']['dir']['home'] = ENV['HOME'] +default['git']['dir']['home'] = Dir.home(node['app']['user']) or ENV['HOME'] default['git']['dir']['workspace'] = "#{node['git']['dir']['home']}/workspace" default['git']['port']['http'] = 8080 diff --git a/config/libraries/common.rb b/config/libraries/common.rb index 20f1545..5491f46 100644 --- a/config/libraries/common.rb +++ b/config/libraries/common.rb @@ -2,42 +2,46 @@ module Common # General - def self.packages(ctx, *pkgs, action: :install) + def self.packages(node, *pkgs, action: :install) Array(pkgs).flatten.each do |pkg| - ctx.package pkg do + node.package pkg do action action end end end - def self.directories(ctx, dirs, opts = {}) + def self.directories(node, dirs, opts = {}) dirs = Array(dirs) - owner = opts[:owner] || 'app' - group = opts[:group] || 'config' + owner = opts[:owner] || Default.app(node) + group = opts[:group] || Default.app(node) mode = opts[:mode] || '0755' recursive = opts.key?(:recursive) ? opts[:recursive] : true recreate = opts[:recreate] || false if recreate - sort_dir(dirs).each { |dir| delete_dir(ctx, dir) } + sort_dir(dirs).each { |dir| delete_dir(node, dir) } end - dirs.each { |dir| create_dir(ctx, dir, owner, group, mode, recursive) } + dirs.each { |dir| create_dir(node, dir, owner, group, mode, recursive) } end # System - def self.daemon(ctx, name) - ctx.find_resource!(:execute, name) + def self.daemon(node, name) + node.find_resource!(:execute, name) rescue Chef::Exceptions::ResourceNotFound - ctx.execute name do + node.execute name do command 'systemctl daemon-reload' action :nothing end end - def self.application(ctx, name, user: 'app', group: user, exec: nil, cwd: nil, unit: {}, action: [:enable, :start], restart: 'on-failure', subscribe: nil, reload: 'systemd_reload') + def self.application(node, name, user: nil, group: nil, exec: nil, cwd: nil, unit: {}, action: [:enable, :start], restart: 'on-failure', subscribe: nil, reload: 'systemd_reload') + user ||= Default.user(node) + group ||= Default.group(node) + user = user.to_s + group = group.to_s if exec || !unit.empty? - daemon(ctx, reload) + daemon(node, reload) service = { 'Type' => 'simple', @@ -65,7 +69,7 @@ def self.application(ctx, name, user: 'app', group: user, exec: nil, cwd: nil, u "[#{section}]\n#{lines}" end.join("\n\n") - ctx.file "/etc/systemd/system/#{name}.service" do + node.file "/etc/systemd/system/#{name}.service" do owner 'root' group 'root' mode '0644' @@ -74,33 +78,41 @@ def self.application(ctx, name, user: 'app', group: user, exec: nil, cwd: nil, u end end - ctx.service name do + node.service name do action action Array(subscribe).flatten.each { |ref| subscribes :restart, ref, :delayed } if subscribe end end - def self.create_dir(ctx, dir, owner, group, mode, recursive) - ctx.directory dir do - owner owner - group group - mode mode - recursive recursive - end + def self.create_dir(node, dir, owner, group, mode, recursive) + node.directory dir do owner owner; group group; mode mode; recursive recursive end rescue => e - Logs.warn("Skipping #{dir}: #{e}") + Logs.warn("Skip create #{dir}: #{e}") end - def self.delete_dir(ctx, dir) - ctx.directory dir do - action :delete - recursive true - only_if { ::Dir.exist?(dir) } - end - end + def self.delete_dir(node, dir) + node.directory dir do action :delete; recursive true; only_if { ::Dir.exist?(dir) } end + rescue => e + Logs.warn("Skip delete #{dir}: #{e}") + end def self.sort_dir(dirs) Array(dirs).sort_by { |d| -d.count('/') } end end + +class Object + def blank? + respond_to?(:empty?) ? empty? : !self + end + + def presence + blank? ? nil : self + end +end + +class NilClass + def blank?; true; end + def presence; nil; end +end diff --git a/config/libraries/default.rb b/config/libraries/default.rb new file mode 100644 index 0000000..35cfd2c --- /dev/null +++ b/config/libraries/default.rb @@ -0,0 +1,19 @@ +module Default + + def self.user(node, default: nil) + @user ||= default.presence ? 'app' : or_default(Env.get(node, "app_user"), user(node, default: true)) + end + + def self.group(node, default: nil) + @group ||= default.presence ? 'config' : or_default(Env.get(node, "app_group"), group(node, default: true)) + end + + def self.config(node, default: nil) + @config ||= default.presence ? 'config' : or_default(Env.get(node, "app_config"), config(node, default: true)) + end + + def self.or_default(var, default) + var.to_s.presence || default.to_s + end + +end diff --git a/config/libraries/env.rb b/config/libraries/env.rb index 26138c7..2273251 100644 --- a/config/libraries/env.rb +++ b/config/libraries/env.rb @@ -5,66 +5,77 @@ module Env def self.creds(node, login_key = 'login', password_key = 'password') - user ||= Logs.assignment(login_key, (login=ENV[login_key.upcase] || node[login_key.to_sym])) - pass ||= Logs.assignment(password_key, (password=ENV[password_key.upcase] || node[password_key.to_sym])) + user ||= ENV[login_key.upcase] || node[login_key.to_sym] + pass ||= ENV[password_key.upcase] || node[password_key.to_sym] return user, pass end def self.get(node, key) - Logs.assignment(key, val=(node[key].to_s.presence || ENV[key.to_s.upcase].presence || get_variable(node, key))); val - rescue => e - Logs.debug(:warn, "failed get '#{key}'", :node_key, node[key], :env_key, ENV[key.to_s.upcase]) + begin + Logs.assignment(key, val=(node[key].to_s.presence || ENV[key.to_s.upcase].presence || get_variable(node, key))); val + rescue => e + Logs.debug(:warn, "failed get '#{key}' (#{e})", :node_key, node[key], :env_key, ENV[key.to_s.upcase]) + end end - def self.get_variable(node, key) - JSON.parse(request(node, key).body)['data'] - rescue => e - Logs.debug(:warn, "failed get variable '#{key}'", -:error, e.message, :endpoint, endpoint(node), :node_key, node[key], :env_key, ENV[key.to_s.upcase]) + def self.get_variable(node, key, repo: nil) + begin + JSON.parse(request(node, key, repo: repo).body)['data'] + rescue => e + Logs.debug(:error, "failed set '#{key}' to #{Logs.mask(val)}", + [:error, e.message, :endpoint, endpoint(node), :node, node[key], :env, ENV[key.to_s.upcase], :repo, repo ] ) + end end - def self.set_variable(node, key, val) - raise unless Logs.assignment(key, request(node, key, { name: key, value: val.to_s }.to_json, expect: true)) +def self.set_variable(node, key, val, repo: nil) + begin + request(node, key, body: ({ name: key, value: val.to_s }.to_json), repo: repo, expect: true) rescue => e - Logs.debug(:error, "failed set #{Logs.mask(val)} for variable '#{key}'", [:error, e.message, :endpoint, endpoint(node), :node_key, node[key], :env_key, ENV[key.to_s.upcase] ]) + Logs.debug(:error, "failed set '#{key}' to #{Logs.mask(val)}", +[:error, e.message, :endpoint, endpoint(node), :node, node[key], :env, ENV[key.to_s.upcase], :val, val, :repo, repo]) + raise end +end class << self alias_method :set, :set_variable end - def self.or_default(var, default) - var.to_s.presence || default.to_s - end - - def self.endpoint(node, port = nil) - port ||= or_default(node.dig('git', 'port', 'http'), '8080') - or_default(node.dig('git', 'api', 'endpoint'), - "http://#{or_default(node['host'].to_s.presence || ENV['HOST'].to_s.presence, '127.0.0.1')} - :#{port}/api/#{or_default(node.dig('git', 'version'), 'v1')}") + def self.endpoint(node) + host = Default.or_default(node.dig('git', 'host', 'http'), "http://localhost:#{Default.or_default(node.dig('git', 'port', 'http'), 8080)}") + "#{host}/api/#{Default.or_default(node.dig('git', 'version'), 'v1')}" end - def self.request(node, key, body = nil, expect = false) + def self.request(node, key, body: nil, repo: nil, expect: false) user, pass = creds(node) - uri = URI("#{endpoint(node)}/orgs/#{or_default(node.dig('git', 'org', 'main'), 'main')}/actions/variables/#{key}") - (body ? [Net::HTTP::Put, Net::HTTP::Post] : [Net::HTTP::Get]).each do |method| - Utils.request(uri, user: user, pass: pass, expect: expect, headers: { 'Content-Type' => 'application/json' }, method: method, body: body) + owner = Default.or_default(node.dig('git', 'org', 'main'), 'main') + uri = URI("#{endpoint(node)}/#{repo.to_s.strip.size>0 ? + "repos/#{owner}/#{repo.to_s}" : "orgs/#{owner}"}/actions/variables/#{key}") + response = Utils.request(uri, user: user, pass: pass, headers: {}, method: Net::HTTP::Get, expect: expect, log: false) + if body + method = response && response.respond_to?(:code) && response.code.to_i < 300 ? Net::HTTP::Put : Net::HTTP::Post + return Utils.request(uri, user: user, pass: pass, headers: { 'Content-Type' => 'application/json' }, method: method, body: body, expect: expect, log: "(#{user}:#{Logs.mask(pass)})") end end -end - -class Object - def blank? - respond_to?(:empty?) ? empty? : !self - end - - def presence - blank? ? nil : self + def self.dump(node, dict, repo) + begin + dict.each do |parent, value| + next if value.nil? || value.to_s.strip.empty? + if value.is_a?(Hash) + value.each do |child, child_value| + next if child_value.nil? || child_value.to_s.strip.empty? + set_variable(node, "#{parent}_#{child}", child_value, repo: repo) + end + else + set_variable(node, parent.to_s, value, repo: repo) + end + end + true + rescue => e + Logs.debug(:error, "failed dump variables", :error, e.message, :repo, repo) + raise + end end -end -class NilClass - def blank?; true; end - def presence; nil; end end diff --git a/config/libraries/logs.rb b/config/libraries/logs.rb index 177a84a..0ff9327 100644 --- a/config/libraries/logs.rb +++ b/config/libraries/logs.rb @@ -29,8 +29,8 @@ def self.log(level, msg, masks = []) def self.info(msg); log(:info, msg) end def self.warn(msg); log(:warn, msg) end def self.error(msg); log(:error, msg) end - def self.request(uri, response); info("request #{uri}: #{response&.code} #{response&.message}"); return response end - def self.assignment(key, val); info("#{key}: #{mask(val)}"); return val end + def self.request(uri, response); info("requested #{uri}: #{response&.code} #{response&.message}"); return response end + def self.assignment(key, val, mask=true); info("assigned '#{key}' value '#{mask ? mask(val) : val}'"); return val end def self.debug(level, msg, *pairs) ctx = pairs.flatten.each_slice(2).map { |k, v| "#{k}=#{v.inspect}" }.join(" ") @@ -59,11 +59,11 @@ def self.request!(uri, response, msg="failed request") def self.mask(str, term = nil) return obfuscate(str) unless term - str.to_s.gsub(term.to_s, obfuscate(term.to_s)) + str.to_s.gsub(term.to_s, obfuscate(term.to_s)) rescue str end def self.obfuscate(s) - s.length <= 2 ? '*' * s.length : "#{s[0]}#{'*'*(s.length-2)}#{s[-1]}" + s.length <= 2 ? '*' * s.length : "#{s[0]}#{'*'*(s.length-2)}#{s[-1]}" rescue s end end \ No newline at end of file diff --git a/config/libraries/utils.rb b/config/libraries/utils.rb index 274d8c9..46d2a44 100644 --- a/config/libraries/utils.rb +++ b/config/libraries/utils.rb @@ -1,6 +1,9 @@ +require 'digest' +require 'fileutils' +require 'json' require 'net/http' +require 'tmpdir' require 'uri' -require 'json' module Utils @@ -58,24 +61,37 @@ def self.arch(node) end end - def self.snapshot(ctx, dir, snapshot_dir: '/share/snapshots', name: ctx.cookbook_name, restore: false) + def self.snapshot(node, dir, snapshot_dir: '/share/snapshots', name: node.cookbook_name, restore: false) timestamp = Time.now.strftime('%H%M-%d%m%y') file = File.join(snapshot_dir, "#{name}-#{timestamp}.tar.gz") + md5_dir = ->(path) { + Digest::MD5.new.tap do |md5| + Dir.glob("#{path}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }.sort.each do |f| + md5.update(File.read(f)) + end + end.hexdigest + } + verify = ->(archive, compare_dir) { + Dir.mktmpdir do |tmp| + raise Logs.error("extraction failed for '#{archive}'") unless system("tar -xzf #{archive} -C #{tmp}") + raise "md5 mismatch indicates error" unless md5_dir.(File.join(tmp, File.basename(compare_dir))) == md5_dir.(compare_dir) + end + true + } if restore latest = Dir[File.join(snapshot_dir, "#{name}-*.tar.gz")].max_by { |f| File.mtime(f) } - - ctx.execute "common_restore_snapshot_#{dir}" do + return false unless latest && File.exist?(latest) + node.execute("common_restore_snapshot_#{dir}") do command "tar -xzf #{latest} -C #{File.dirname(dir)}" - only_if { latest && ::File.exist?(latest) } end - latest + verify.(latest, dir) else - ctx.execute "common_create_snapshot_#{dir}" do + return false unless Dir.exist?(dir) + node.execute("common_create_snapshot_#{dir}") do command "mkdir -p $(dirname #{file}) && tar -czf #{file} -C #{File.dirname(dir)} #{File.basename(dir)}" - only_if { ::Dir.exist?(dir) } end - file + verify.(file, dir) end end @@ -102,28 +118,27 @@ def self.proxmox(uri, node, path, expect: true) # Remote - def self.request(uri, user: nil, pass: nil, headers: {}, method: Net::HTTP::Get, body: nil, expect: false) - req = method.new(u = URI(uri)) + def self.request(uri, user: nil, pass: nil, headers: {}, method: Net::HTTP::Get, body: nil, expect: false, log: true) + u = URI(uri) + req = method.new(u) req.basic_auth(user, pass) if user && pass req.body = body if body headers.each { |k, v| req[k] = v } response = Net::HTTP.start(u.host, u.port, use_ssl: u.scheme == 'https') { |http| http.request(req) } - Logs.request(uri, response) - - if response.is_a?(Net::HTTPSuccess) - return expect ? true : response - end - if response.is_a?(Net::HTTPRedirection) + if response.is_a?(Net::HTTPRedirection) && response['location'] loc = response['location'] - loc = "#{u.scheme}://#{u.host}#{loc}" if loc&.start_with?('/') - return request(loc, user: user, pass: pass, headers: headers, method: method, body: body, expect: expect) + loc = loc.start_with?('http://', 'https://') ? loc : (loc.start_with?('/') ? "#{u.scheme}://#{u.host}#{loc}" : URI.join("#{u.scheme}://#{u.host}#{u.path}", loc).to_s) + response = request(loc, user: user, pass: pass, headers: headers, method: method, body: body, expect: expect, log: log) end - - expect ? false : response + if log + tag = log.is_a?(String) ? " #{log}" : "" + Logs.request("#{u}#{tag} (#{body})", response) + end + return expect ? response.is_a?(Net::HTTPSuccess) : response end - def self.download(ctx, path, url:, owner: 'root', group: 'root', mode: '0644', action: :create) - ctx.remote_file path do + def self.download(node, path, url:, owner: Default.app(node), group: Default.app(node), mode: '0754', action: :create) + node.remote_file path do source url.respond_to?(:call) ? lazy { url.call } : url owner owner group group diff --git a/config/recipes/config.rb b/config/recipes/config.rb index 8aa2851..7836add 100644 --- a/config/recipes/config.rb +++ b/config/recipes/config.rb @@ -1,27 +1,31 @@ +login = Env.get(node, 'login') +password = Env.get(node, 'password') +email = Env.get(node, 'email') + ruby_block 'config_wait_http' do block do Utils.wait("127.0.0.1:#{node['git']['port']['http']}", timeout: 15, sleep_interval: 1) end end execute 'config_set_user' do - user node['git']['user']['app'] + user node['app']['user'] command <<-EOH - login="#{Env.get(node, 'login')}" + login="#{login}" base="#{node['git']['dir']['app']}/gitea admin user --config #{node['git']['dir']['app']}/app.ini" - user="--username #{Env.get(node, 'login')} --password #{Env.get(node, 'password')}" - create="--email #{Env.get(node, 'email')} --admin --must-change-password=false" - if $base list | awk '{print $2}' | grep -q "^#{Env.get(node, 'login')}$"; then + user="--username #{login} --password #{password}" + create="--email #{email} --admin --must-change-password=false" + if $base list | awk '{print $2}' | grep -q "^#{login}$"; then $base delete $user fi $base create $create $user EOH - not_if { Utils.request("#{node['git']['api']['endpoint']}/user", user: Env.get(node, 'login'), pass: Env.get(node, 'password'), expect: true) } + not_if { Utils.request("#{node['git']['api']['endpoint']}/user", user: login, pass: password, expect: true) } end ruby_block 'config_set_key' do block do require 'json' - login = Env.get(node, 'login') - password = Env.get(node, 'password') + login = login + password = password url = "#{node['git']['api']['endpoint']}/admin/users/#{login}/keys" key = ::File.read("#{node['key']}.pub").strip @@ -36,7 +40,7 @@ not_if do next false unless ::File.exist?("#{node['key']}.pub") begin - response = Utils.request("#{node['git']['api']['endpoint']}/admin/users/#{Env.get(node, 'login')}/keys", user: Env.get(node, 'login'), pass: Env.get(node, 'password')) + response = Utils.request("#{node['git']['api']['endpoint']}/admin/users/#{login}/keys", user: login, pass: password) (JSON.parse(response.body) rescue []).any? { |k| k['key'] && k['key'].strip == ::File.read("#{node['key']}.pub").strip } end end @@ -51,11 +55,11 @@ execute 'config_git_user' do command <<-SH - git config --global user.name "#{Env.get(node, 'login')}" - git config --global user.email "#{Env.get(node, 'email')}" + git config --global user.name "#{login}" + git config --global user.email "#{email}" git config --global core.excludesfile #{ENV['PWD']}/.gitignore SH - user node['git']['user']['app'] + user node['app']['user'] end [node['git']['org']['main'], node['git']['org']['stage']].each do |org| @@ -64,7 +68,7 @@ require 'json' status_code = (response = Utils.request(uri="#{node['git']['api']['endpoint']}/orgs", method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' }, - user: Env.get(node, 'login'), pass: Env.get(node, 'password'), + user: login, pass: password, body: { username: org }.to_json )).code.to_i Logs.request!("Create organization '#{org}' failed", uri, response) unless [201, 409, 422].include? status_code @@ -74,7 +78,7 @@ ruby_block 'config_git_environment' do block do - %w(proxmox login password email host).each do |parent| + %w(proxmox host app login password email).each do |parent| value = node[parent] next if value.nil? || value.to_s.strip.empty? if value.is_a?(Hash) @@ -83,8 +87,8 @@ Env.set_variable(node, "#{parent}_#{child}", child_value) end else - Env.set_variable(Chef.node, parent, value) + Env.set_variable(node, parent, value) end end end -end \ No newline at end of file +end diff --git a/config/recipes/git.rb b/config/recipes/git.rb index e2c0a42..d69d900 100644 --- a/config/recipes/git.rb +++ b/config/recipes/git.rb @@ -1,24 +1,24 @@ Utils.download(self, "#{node['git']['dir']['app']}/gitea", url: -> { ver = Utils.latest('https://github.com/go-gitea/gitea/releases/latest') "https://github.com/go-gitea/gitea/releases/download/v#{ver}/gitea-#{ver}-linux-#{Utils.arch(node)}" }, - owner: node['git']['user']['app'] , - group: node['git']['user']['group'], - mode: '0755' ) + owner: node['app']['user'] , + group: node['app']['group'] +) template "#{node['git']['dir']['app']}/app.ini" do source 'git_app.ini.erb' - owner node['git']['user']['app'] - group node['git']['user']['group'] + owner node['app']['user'] + group node['app']['group'] mode '0644' variables(host: node['host'], - run_user: node['git']['user']['app'] , ssh_user: node['git']['user']['ssh'], + run_user: node['app']['user'] , ssh_user: node['app']['config'], app_dir: node['git']['dir']['app'], home_dir: node['git']['dir']['home'], http_port: node['git']['port']['http'], ssh_port: node['git']['port']['ssh'] ) action :create_if_missing end Common.application(self, 'gitea', - user: node['git']['user']['app'] , cwd: node['git']['dir']['data'], + user: node['app']['user'] , cwd: node['git']['dir']['data'], exec: "#{node['git']['dir']['app']}/gitea web --config #{node['git']['dir']['app']}/app.ini", - unit: { 'Service' => { 'Environment' => "USER=#{node['git']['user']['app'] } HOME=#{node['git']['dir']['home']}" } }, + unit: { 'Service' => { 'Environment' => "USER=#{node['app']['user'] } HOME=#{node['git']['dir']['home']}" } }, subscribe: ["template[#{node['git']['dir']['app']}/app.ini]", "remote_file[#{node['git']['dir']['app']}/gitea]"] ) diff --git a/config/recipes/prepare.rb b/config/recipes/prepare.rb index e950b08..25215af 100644 --- a/config/recipes/prepare.rb +++ b/config/recipes/prepare.rb @@ -1,8 +1,8 @@ Common.directories(self, [ (app = node['git']['dir']['app']), node['git']['dir']['workspace'], node['runner']['dir']['app'], "#{app}/custom", "#{app}/data", "#{app}/gitea-repositories", "#{app}/log" ], - owner: node['git']['user']['app'] , - group: node['git']['user']['group']) + owner: node['app']['user'] , + group: node['app']['group']) Common.packages(self, %w(git acl python3-pip ansible ansible-core nodejs npm python3-proxmoxer)) @@ -27,7 +27,7 @@ IdentityFile #{node['key']} StrictHostKeyChecking no CONF - owner node['git']['user']['app'] - group node['git']['user']['group'] + owner node['app']['user'] + group node['app']['group'] mode '0600' end diff --git a/config/recipes/repo.rb b/config/recipes/repo.rb index 4798c88..06f1521 100644 --- a/config/recipes/repo.rb +++ b/config/recipes/repo.rb @@ -6,7 +6,7 @@ working = "#{destination}/workdir" Common.directories(self, [destination, working], recreate: true, - owner: node['git']['user']['app'] , group: node['git']['user']['group']) + owner: node['app']['user'] , group: node['app']['group']) (repositories = node['git']['conf']['repo'] .flat_map { |r| (r == './libs') ? Dir.glob(File.join(source, r, '*')).select { |d| File.directory?(d) }.map { |p| p.sub(source, '.') } : r } @@ -31,15 +31,15 @@ execute "repo_exists_snapshot_create_#{name_repo}" do command <<-EOH - if git ls-remote ssh://#{node['git']['user']['app'] }@#{node['git']['host']['ssh']}/#{node['git']['org']['main']}/#{name_repo}.git HEAD | grep -q .; then - git clone --recurse-submodules ssh://#{node['git']['user']['app'] }@#{node['git']['host']['ssh']}/#{node['git']['org']['main']}/#{name_repo}.git #{path_working} + if git ls-remote ssh://#{node['app']['user'] }@#{node['git']['host']['ssh']}/#{node['git']['org']['main']}/#{name_repo}.git HEAD | grep -q .; then + git clone --recurse-submodules ssh://#{node['app']['user'] }@#{node['git']['host']['ssh']}/#{node['git']['org']['main']}/#{name_repo}.git #{path_working} cd #{path_working} && git submodule update --init --recursive find . -type d -name .git -exec rm -rf {} + else mkdir -p #{path_working} fi EOH - user node['git']['user']['app'] + user node['app']['user'] only_if { Logs.info("[#{repository} (#{name_repo})]: delete repository after snapshot") node.run_state["#{name_repo}_repo_exists"] } end @@ -71,15 +71,15 @@ command <<-EOH mkdir -p #{path_destination} && cd #{path_destination} && git init -b main EOH - user node['git']['user']['app'] + user node['app']['user'] end template "#{path_destination}/.git/config" do source 'repo_config.erb' - owner node['git']['user']['app'] - group node['git']['user']['group'] + owner node['app']['user'] + group node['app']['group'] mode '0644' - variables(repo: name_repo, git_user: node['git']['user']['app'] ) + variables(repo: name_repo, git_user: node['app']['user'] ) only_if { ::File.directory?("#{path_destination}/.git") } end @@ -90,18 +90,18 @@ git push -u origin main && git push -u origin release EOH cwd path_destination - user node['git']['user']['app'] + user node['app']['user'] end execute "repo_exists_snapshot_push_#{name_repo}" do command <<-EOH cp -r #{path_destination}/.git #{path_working} - cd #{path_working} && git checkout -b snapshot && git add -A + cd #{path_working} && git checkout -b snapshot && git add -A git commit --allow-empty -m "snapshot [skip ci]" git push -f origin snapshot && (rm -rf #{path_working} || true) EOH cwd path_destination - user node['git']['user']['app'] + user node['app']['user'] only_if { Logs.info("[#{repository} (#{name_repo})]: snapshot commit") node.run_state["#{name_repo}_repo_exists"] } end @@ -119,7 +119,7 @@ FileUtils.cp(path_src, path_dst, verbose: true) end end - FileUtils.chown_R(node['git']['user']['app'] , node['git']['user']['group'], path_destination) + FileUtils.chown_R(node['app']['user'] , node['app']['group'], path_destination) end end @@ -129,8 +129,8 @@ template "#{path_destination}/.gitea/workflows/sync.yml" do source 'repo_sync.yml.erb' - owner node['git']['user']['app'] - group node['git']['user']['group'] + owner node['app']['user'] + group node['app']['group'] mode '0644' not_if { monorepo } not_if { File.exist?("#{path_destination}/.gitea/workflows/sync.yml") } @@ -138,8 +138,8 @@ template "#{path_destination}/.gitea/workflows/pipeline.yml" do source 'repo_pipeline.yml.erb' - owner node['git']['user']['app'] - group node['git']['user']['group'] + owner node['app']['user'] + group node['app']['group'] mode '0644' only_if { repository.include?('libs/') and File.exist?("#{path_destination}/config.env") } not_if { File.exist?("#{path_destination}/.gitea/workflows/pipeline.yml") } @@ -182,13 +182,13 @@ git submodule add #{module_url} #{path_module} fi git submodule update --init --recursive - # pass variables in bootstrap + # pass variables in bootstrap if [ "#{Env.get(node, 'host')}" = "127.0.0.1" ] && [ -f local/config.json ]; then git add -f local/config.json fi EOH cwd path_destination - user node['git']['user']['app'] + user node['app']['user'] end end @@ -198,7 +198,7 @@ execute "repo_push_#{name_repo}" do cwd path_destination - user node['git']['user']['app'] + user node['app']['user'] command <<-EOH git add --all if ! git diff --quiet || ! git diff --cached --quiet; then @@ -219,12 +219,6 @@ EOH end - directory path_destination do - action :delete - recursive true - only_if { ::Dir.exist?(path_destination) } - end - # Fork as stage repository ruby_block "repo_stage_fork_clean_#{name_repo}" do @@ -249,4 +243,18 @@ end end + directory path_destination do + action :delete + recursive true + only_if { ::Dir.exist?(path_destination) } + end + +end + +ruby_block "repository_variable_dump" do + block do + Env.dump(node, node['git'], cookbook_name.to_s) + Env.dump(node, node['runner'], cookbook_name.to_s) + end + ignore_failure true end diff --git a/config/recipes/runner.rb b/config/recipes/runner.rb index 2d7af4e..ee233a7 100644 --- a/config/recipes/runner.rb +++ b/config/recipes/runner.rb @@ -1,22 +1,21 @@ -Common.directories(self, node['runner']['dir']['app'], owner: node['git']['user']['app'] , group: node['git']['user']['group']) +Common.directories(self, node['runner']['dir']['app'], owner: node['app']['user'] , group: node['app']['group']) Utils.download(self, "#{node['runner']['dir']['app']}/act_runner", url: -> { ver = Utils.latest('https://gitea.com/gitea/act_runner/releases/latest') "https://gitea.com/gitea/act_runner/releases/download/v#{ver}/act_runner-#{ver}-linux-#{Utils.arch(node)}" }, - owner: node['git']['user']['app'] , - group: node['git']['user']['group'], - mode: '0755' + owner: node['app']['user'] , + group: node['app']['group'], ) template "#{node['runner']['dir']['app']}/config.yaml" do source 'runner.config.yaml.erb' - owner node['git']['user']['app'] - group node['git']['user']['group'] + owner node['app']['user'] + group node['app']['group'] mode '0644' end Common.application(self, 'runner', - user: node['git']['user']['app'] , action: [:enable], cwd: node['runner']['dir']['app'], + user: node['app']['user'] , action: [:enable], cwd: node['runner']['dir']['app'], exec: "#{node['runner']['dir']['app']}/act_runner daemon --config #{node['runner']['dir']['app']}/config.yaml", subscribe: ["template[#{node['runner']['dir']['app']}/config.yaml]", "remote_file[#{node['runner']['dir']['app']}/act_runner]"] ) @@ -38,7 +37,7 @@ (token = Mixlib::ShellOut.new( "#{node['git']['dir']['app']}/gitea actions --config #{node['git']['dir']['app']}/app.ini generate-runner-token", - user: node['git']['user']['app'] + user: node['app']['user'] )).run_command token.error! token = token.stdout.strip @@ -51,7 +50,7 @@ "--labels shell " \ "--config #{node['runner']['dir']['app']}/config.yaml", cwd: node['runner']['dir']['app'], - user: node['git']['user']['app'] + user: node['app']['user'] )).run_command register.error! end diff --git a/config/templates/repo_config.erb b/config/templates/repo_config.erb index 3a98311..af3ba20 100644 --- a/config/templates/repo_config.erb +++ b/config/templates/repo_config.erb @@ -5,7 +5,7 @@ logallrefupdates = true [remote "origin"] - url = ssh://<%= node['git']['user']['ssh'] %>@<%= node['git']['host']['ssh'] %>/<%= node['git']['org']['main'] %>/<%= @repo %>.git + url = ssh://<%= node['app']['config'] %>@<%= node['git']['host']['ssh'] %>/<%= node['git']['org']['main'] %>/<%= @repo %>.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "main"] diff --git a/docs/environment.png b/docs/environment.png new file mode 100644 index 0000000000000000000000000000000000000000..6af83b74a912464beff70e6b4dada6d5f79d7208 GIT binary patch literal 152975 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 Date: Sat, 16 Aug 2025 14:17:45 +0200 Subject: [PATCH 07/13] dev/v1.1/context-handling (#59) * higher resolution * set recursive for directories per default * fix renaming typo * dynamic folder name to identify cookbook programmatically * ctx in libraries by convention * self reference and fallback by convention * robustness for flexible input * reduce overhead and redundancy * contextual response * fallback connections * self and node passed environment --- .gitea/workflows/action.yml | 10 ++-- config/attributes/default.rb | 6 +-- config/libraries/common.rb | 44 ++++++++-------- config/libraries/default.rb | 15 ++++-- config/libraries/env.rb | 80 +++++++++++++++++------------- config/libraries/logs.rb | 23 +++++---- config/libraries/utils.rb | 30 +++++------ config/recipes/config.rb | 25 +++------- config/recipes/repo.rb | 40 +++++++-------- config/templates/git_app.ini.erb | 1 + docs/environment.png | Bin 152975 -> 253559 bytes libs/assistant/recipes/default.rb | 6 ++- libs/bridge/attributes/default.rb | 4 +- libs/bridge/recipes/default.rb | 55 +++++++++++--------- libs/broker/recipes/default.rb | 12 +++-- libs/proxy/recipes/default.rb | 4 +- libs/share/recipes/default.rb | 4 +- 17 files changed, 193 insertions(+), 166 deletions(-) diff --git a/.gitea/workflows/action.yml b/.gitea/workflows/action.yml index 3a684ee..68efe50 100644 --- a/.gitea/workflows/action.yml +++ b/.gitea/workflows/action.yml @@ -15,7 +15,7 @@ runs: with: repository: "${{ inputs.repo }}" ref: "${{ inputs.ref }}" - path: repo + path: "${{ inputs.repo }}" - name: Configure container id: init @@ -51,20 +51,20 @@ runs: with: repository: "${{ inputs.repo }}" ref: "${{ gitea.ref_name }}" - path: repo + path: "${{ inputs.repo }}" - name: Checkout libraries uses: https://gitea.com/actions/checkout@v4 with: repository: 'main/libraries' ref: 'main' - path: 'repo/libraries' + path: "${{ inputs.repo }}/libraries" - name: Configure container run: | - tar -c repo -cz . | ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${{ env.id }}" "config@${{ env.ip }}" 'sudo tar xz -C /tmp + tar -c "${{ inputs.repo }}" -cz . | ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${{ env.id }}" "config@${{ env.ip }}" 'sudo tar xz -C /tmp sudo -E IP=${{ env.ip }} ID=${{ env.id }} HOST=${{ vars.HOST }} LOGIN=${{ vars.LOGIN }} PASSWORD=${{ vars.PASSWORD }} PWD="$(pwd)" \ - cinc-client --local-mode $CONFIG_ARGS --config-option cookbook_path="/tmp" -o repo' + cinc-client --local-mode $CONFIG_ARGS --config-option cookbook_path="/tmp" -o "${{ inputs.repo }}"' shell: bash diff --git a/config/attributes/default.rb b/config/attributes/default.rb index 44be7f0..cd8edab 100644 --- a/config/attributes/default.rb +++ b/config/attributes/default.rb @@ -1,6 +1,6 @@ default['id'] = ENV['ID'] -default['host'] = ENV['IP'].to_s.presence ? ENV['IP'] : "127.0.0.1" -default['key'] = ENV['KEY'].to_s.presence ? ENV['KEY'] : "/share/.ssh/#{node['id']}" +default['host'] = ENV['IP'].to_s.presence || "127.0.0.1" +default['key'] = ENV['KEY'].to_s.presence || "/share/.ssh/#{node['id']}" default['app']['user'] = Default.user(node, default: true) default['app']['group'] = Default.group(node, default: true) @@ -10,7 +10,7 @@ default['git']['conf']['repo'] = [ "./", "./base", "./config/libraries", "./libs" ] default['git']['dir']['app'] = '/app/git' -default['git']['dir']['home'] = Dir.home(node['app']['user']) or ENV['HOME'] +default['git']['dir']['home'] = Dir.home(node['app']['user']) || ENV['HOME'] || '/app' default['git']['dir']['workspace'] = "#{node['git']['dir']['home']}/workspace" default['git']['port']['http'] = 8080 diff --git a/config/libraries/common.rb b/config/libraries/common.rb index 5491f46..b137e53 100644 --- a/config/libraries/common.rb +++ b/config/libraries/common.rb @@ -2,46 +2,46 @@ module Common # General - def self.packages(node, *pkgs, action: :install) + def self.packages(ctx, *pkgs, action: :install) Array(pkgs).flatten.each do |pkg| - node.package pkg do + ctx.package pkg do action action end end end - def self.directories(node, dirs, opts = {}) + def self.directories(ctx, dirs, opts = {}) dirs = Array(dirs) - owner = opts[:owner] || Default.app(node) - group = opts[:group] || Default.app(node) + owner = opts[:owner] || Default.app(ctx) + group = opts[:group] || Default.app(ctx) mode = opts[:mode] || '0755' - recursive = opts.key?(:recursive) ? opts[:recursive] : true + recursive = true recreate = opts[:recreate] || false if recreate - sort_dir(dirs).each { |dir| delete_dir(node, dir) } + sort_dir(dirs).each { |dir| delete_dir(ctx, dir) } end - dirs.each { |dir| create_dir(node, dir, owner, group, mode, recursive) } + dirs.each { |dir| create_dir(ctx, dir, owner, group, mode, recursive) } end # System - def self.daemon(node, name) - node.find_resource!(:execute, name) + def self.daemon(ctx, name) + ctx.find_resource!(:execute, name) rescue Chef::Exceptions::ResourceNotFound - node.execute name do + ctx.execute name do command 'systemctl daemon-reload' action :nothing end end - def self.application(node, name, user: nil, group: nil, exec: nil, cwd: nil, unit: {}, action: [:enable, :start], restart: 'on-failure', subscribe: nil, reload: 'systemd_reload') - user ||= Default.user(node) - group ||= Default.group(node) + def self.application(ctx, name, user: nil, group: nil, exec: nil, cwd: nil, unit: {}, action: [:enable, :start], restart: 'on-failure', subscribe: nil, reload: 'systemd_reload') + user ||= Default.user(ctx) + group ||= Default.group(ctx) user = user.to_s group = group.to_s if exec || !unit.empty? - daemon(node, reload) + daemon(ctx, reload) service = { 'Type' => 'simple', @@ -69,7 +69,7 @@ def self.application(node, name, user: nil, group: nil, exec: nil, cwd: nil, uni "[#{section}]\n#{lines}" end.join("\n\n") - node.file "/etc/systemd/system/#{name}.service" do + ctx.file "/etc/systemd/system/#{name}.service" do owner 'root' group 'root' mode '0644' @@ -78,26 +78,26 @@ def self.application(node, name, user: nil, group: nil, exec: nil, cwd: nil, uni end end - node.service name do + ctx.service name do action action Array(subscribe).flatten.each { |ref| subscribes :restart, ref, :delayed } if subscribe end end - def self.create_dir(node, dir, owner, group, mode, recursive) - node.directory dir do owner owner; group group; mode mode; recursive recursive end + def self.create_dir(ctx, dir, owner, group, mode, recursive) + ctx.directory dir do owner owner; group group; mode mode; recursive recursive end rescue => e Logs.warn("Skip create #{dir}: #{e}") end - def self.delete_dir(node, dir) - node.directory dir do action :delete; recursive true; only_if { ::Dir.exist?(dir) } end + def self.delete_dir(ctx, dir) + ctx.directory dir do action :delete; recursive true; only_if { ::Dir.exist?(dir) } end rescue => e Logs.warn("Skip delete #{dir}: #{e}") end def self.sort_dir(dirs) - Array(dirs).sort_by { |d| -d.count('/') } + Array(dirs).compact.sort_by { |d| -d.count('/') } end end diff --git a/config/libraries/default.rb b/config/libraries/default.rb index 35cfd2c..effd384 100644 --- a/config/libraries/default.rb +++ b/config/libraries/default.rb @@ -1,14 +1,17 @@ module Default - def self.user(node, default: nil) + def self.user(ctx, default: nil) + node = Ctx.node(ctx) @user ||= default.presence ? 'app' : or_default(Env.get(node, "app_user"), user(node, default: true)) end - def self.group(node, default: nil) + def self.group(ctx, default: nil) + node = Ctx.node(ctx) @group ||= default.presence ? 'config' : or_default(Env.get(node, "app_group"), group(node, default: true)) end - def self.config(node, default: nil) + def self.config(ctx, default: nil) + node = Ctx.node(ctx) @config ||= default.presence ? 'config' : or_default(Env.get(node, "app_config"), config(node, default: true)) end @@ -17,3 +20,9 @@ def self.or_default(var, default) end end + +module Ctx + def self.node(obj) + obj.respond_to?(:node) ? obj.node : obj + end +end diff --git a/config/libraries/env.rb b/config/libraries/env.rb index 2273251..ec3b8d8 100644 --- a/config/libraries/env.rb +++ b/config/libraries/env.rb @@ -4,36 +4,36 @@ module Env - def self.creds(node, login_key = 'login', password_key = 'password') - user ||= ENV[login_key.upcase] || node[login_key.to_sym] - pass ||= ENV[password_key.upcase] || node[password_key.to_sym] + def self.creds(ctx, login = 'login', password = 'password') + node = Ctx.node(ctx) + user ||= ENV[login.upcase] || node[login.to_sym] + pass ||= ENV[password.upcase] || node[password.to_sym] return user, pass end - def self.get(node, key) - begin - Logs.assignment(key, val=(node[key].to_s.presence || ENV[key.to_s.upcase].presence || get_variable(node, key))); val - rescue => e - Logs.debug(:warn, "failed get '#{key}' (#{e})", :node_key, node[key], :env_key, ENV[key.to_s.upcase]) - end + def self.get(ctx, key) + node = Ctx.node(ctx) + env_key = ENV[key.to_s.upcase] + return node[key] unless node[key].nil? || node[key].to_s.strip.empty? + return env_key unless env_key.nil? || env_key.to_s.strip.empty? + return get_variable(ctx, key) + rescue => e + return Logs.debug(:warn, "failed get '#{key}'", :error, e.message, ctx: ctx) end - def self.get_variable(node, key, repo: nil) + def self.get_variable(ctx, key, repo: nil) begin - JSON.parse(request(node, key, repo: repo).body)['data'] + response = request(ctx, key, repo: repo); response && response.body ? JSON.parse(response.body)['data'] : nil rescue => e - Logs.debug(:error, "failed set '#{key}' to #{Logs.mask(val)}", - [:error, e.message, :endpoint, endpoint(node), :node, node[key], :env, ENV[key.to_s.upcase], :repo, repo ] ) + Logs.debug(:warn, "failed get variable '#{key}'", :error, e.message, :endpoint, endpoint(ctx), :repo, repo, ctx: ctx) end end -def self.set_variable(node, key, val, repo: nil) +def self.set_variable(ctx, key, val, repo: nil) begin - request(node, key, body: ({ name: key, value: val.to_s }.to_json), repo: repo, expect: true) + request(Ctx.node(ctx), key, body: ({ name: key, value: val.to_s }.to_json), repo: repo, expect: true) rescue => e - Logs.debug(:error, "failed set '#{key}' to #{Logs.mask(val)}", -[:error, e.message, :endpoint, endpoint(node), :node, node[key], :env, ENV[key.to_s.upcase], :val, val, :repo, repo]) - raise + Logs.raise!("failed set variable '#{key}' to #{Logs.mask(val)}", :val, val, :repo, repo, e: e) end end @@ -41,40 +41,50 @@ class << self alias_method :set, :set_variable end - def self.endpoint(node) - host = Default.or_default(node.dig('git', 'host', 'http'), "http://localhost:#{Default.or_default(node.dig('git', 'port', 'http'), 8080)}") + def self.endpoint(ctx) + node = Ctx.node(ctx) + host = Default.or_default(node.dig('git', 'host', 'http'), "http://#{Default.or_default(Env.get(node, 'host'), '127.0.0.1')}:#{Default.or_default(node.dig('git', 'port', 'http'), 8080)}") "#{host}/api/#{Default.or_default(node.dig('git', 'version'), 'v1')}" end - def self.request(node, key, body: nil, repo: nil, expect: false) + def self.request(ctx, key, body: nil, repo: nil, expect: false) + node = Ctx.node(ctx) user, pass = creds(node) owner = Default.or_default(node.dig('git', 'org', 'main'), 'main') uri = URI("#{endpoint(node)}/#{repo.to_s.strip.size>0 ? "repos/#{owner}/#{repo.to_s}" : "orgs/#{owner}"}/actions/variables/#{key}") - response = Utils.request(uri, user: user, pass: pass, headers: {}, method: Net::HTTP::Get, expect: expect, log: false) - if body - method = response && response.respond_to?(:code) && response.code.to_i < 300 ? Net::HTTP::Put : Net::HTTP::Post - return Utils.request(uri, user: user, pass: pass, headers: { 'Content-Type' => 'application/json' }, method: method, body: body, expect: expect, log: "(#{user}:#{Logs.mask(pass)})") + response = Utils.request(uri, user: user, pass: pass, headers: {}, method: Net::HTTP::Get, expect: (not body.nil? or expect), log: false) + if not body.nil? + method = (response ? Net::HTTP::Put : Net::HTTP::Post) + response = Utils.request(uri, user: user, pass: pass, headers: { 'Content-Type' => 'application/json' }, method: method, body: body, expect: expect, log: "(#{user}:#{Logs.mask(pass)})") end + response end - def self.dump(node, dict, repo) + def self.dump(ctx, *keys, repo: nil) begin - dict.each do |parent, value| - next if value.nil? || value.to_s.strip.empty? - if value.is_a?(Hash) - value.each do |child, child_value| - next if child_value.nil? || child_value.to_s.strip.empty? - set_variable(node, "#{parent}_#{child}", child_value, repo: repo) + node = Ctx.node(ctx) + keys.flatten.each do |key| + value = node[key] + next if value.nil? || (value.respond_to?(:empty?) && value.empty?) + case value + when Hash + value.each do |subkey, subvalue| + next if subvalue.nil? || subvalue.to_s.strip.empty? + Env.set_variable(node, "#{key}_#{subkey}", subvalue, repo: repo) + end + when Array + value.each_with_index do |item, i| + next if item.nil? || item.to_s.strip.empty? + Env.set_variable(node, "#{key}_#{i}", item, repo: repo) end else - set_variable(node, parent.to_s, value, repo: repo) + Env.set_variable(node, key, value, repo: repo) end end true rescue => e - Logs.debug(:error, "failed dump variables", :error, e.message, :repo, repo) - raise + Logs.raise!("failed dump variables", :repo, repo, e: e) end end diff --git a/config/libraries/logs.rb b/config/libraries/logs.rb index 0ff9327..8fb60d7 100644 --- a/config/libraries/logs.rb +++ b/config/libraries/logs.rb @@ -31,9 +31,13 @@ def self.warn(msg); log(:warn, msg) end def self.error(msg); log(:error, msg) end def self.request(uri, response); info("requested #{uri}: #{response&.code} #{response&.message}"); return response end def self.assignment(key, val, mask=true); info("assigned '#{key}' value '#{mask ? mask(val) : val}'"); return val end + def self.return(msg); log(:info, msg.to_s); return msg end - def self.debug(level, msg, *pairs) - ctx = pairs.flatten.each_slice(2).map { |k, v| "#{k}=#{v.inspect}" }.join(" ") + def self.debug(level, msg, *pairs, ctx: nil) + input = pairs.flatten.each_slice(2).to_h + input['env'] = ENV.to_h; + input['ctx'] = ctx.to_h if ctx + ctx = input.map { |k, v| "#{k}=#{v.inspect}" }.join(" ") log(level, [msg, ctx].reject(&:empty?).join(" ")) end @@ -42,19 +46,19 @@ def self.info?(msg) true end - def self.fail!(msg) - error(msg) + def self.raise!(msg, *pairs, e:nil, ctx: nil) c = callsite label = method_label(c) - raise(label ? "[#{label}] #{msg}" : msg) + debug(:error, msg, *pairs, ctx) + raise("[#{label}] #{e.message if e} #{msg}") end - def self.request!(uri, response, msg="failed request") + def self.request!(uri, response, msg="failed request", e: nil) warn(msg) c = callsite label = method_label(c) - s = "#{msg} (#{uri}: #{response.code} #{response.message})" - raise(label ? "[#{label}] #{s}" : s) + raise("[#{label}] #{e.message if e} #{msg} (#{uri}" + + response ? "#{response.code} #{response.message}" : "" ) end def self.mask(str, term = nil) @@ -63,7 +67,8 @@ def self.mask(str, term = nil) end def self.obfuscate(s) - s.length <= 2 ? '*' * s.length : "#{s[0]}#{'*'*(s.length-2)}#{s[-1]}" rescue s + return s unless s.is_a?(String) + s.length <= 4 ? '*' * s.length : "#{s[0]}#{s[1]}#{'*'*(s.length-4)}#{s[-2]}#{s[-1]}" rescue s end end \ No newline at end of file diff --git a/config/libraries/utils.rb b/config/libraries/utils.rb index 46d2a44..276bf9b 100644 --- a/config/libraries/utils.rb +++ b/config/libraries/utils.rb @@ -50,8 +50,8 @@ def self.wait(condition = nil, timeout: 20, sleep_interval: 5, &block) # System - def self.arch(node) - case node['kernel']['machine'].to_s + def self.arch(ctx) + case ctx['kernel']['machine'].to_s when /arm64|aarch64/ 'arm64' when /armv6|armv7l/ @@ -61,7 +61,7 @@ def self.arch(node) end end - def self.snapshot(node, dir, snapshot_dir: '/share/snapshots', name: node.cookbook_name, restore: false) + def self.snapshot(ctx, dir, snapshot_dir: '/share/snapshots', name: ctx.cookbook_name, restore: false) timestamp = Time.now.strftime('%H%M-%d%m%y') file = File.join(snapshot_dir, "#{name}-#{timestamp}.tar.gz") @@ -82,32 +82,32 @@ def self.snapshot(node, dir, snapshot_dir: '/share/snapshots', name: node.cookbo if restore latest = Dir[File.join(snapshot_dir, "#{name}-*.tar.gz")].max_by { |f| File.mtime(f) } return false unless latest && File.exist?(latest) - node.execute("common_restore_snapshot_#{dir}") do + ctx.execute("common_restore_snapshot_#{dir}") do command "tar -xzf #{latest} -C #{File.dirname(dir)}" end verify.(latest, dir) else return false unless Dir.exist?(dir) - node.execute("common_create_snapshot_#{dir}") do + ctx.execute("common_create_snapshot_#{dir}") do command "mkdir -p $(dirname #{file}) && tar -czf #{file} -C #{File.dirname(dir)} #{File.basename(dir)}" end verify.(file, dir) end end - def self.proxmox(uri, node, path, expect: true) - host = Env.get(node, 'proxmox_host') - user = Env.get(node, 'proxmox_user') - pass = Env.get(node, 'proxmox_password') - token = Env.get(node, 'proxmox_token') - secret = Env.get(node, 'proxmox_secret') + def self.proxmox(uri, ctx, path, expect: true) + host = Env.get(ctx, 'proxmox_host') + user = Env.get(ctx, 'proxmox_user') + pass = Env.get(ctx, 'proxmox_password') + token = Env.get(ctx, 'proxmox_token') + secret = Env.get(ctx, 'proxmox_secret') url = "https://#{host}:8006/api2/json/#{path}" if pass && !pass.empty? response = request(uri="https://#{host}:8006/api2/json/access/ticket", method: Net::HTTP::Post, body: URI.encode_www_form(username: user, password: pass), headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }) - Logs.request!("Proxmox ticket could not be retrieved", uri, response) unless response.is_a?(Net::HTTPSuccess) - headers = { 'Cookie' => "PVEAuthCookie=#{JSON.parse(login.body)['data']['ticket']}" } + Logs.request!(uri, response, msg="Proxmox ticket could not be retrieved", ) unless response.is_a?(Net::HTTPSuccess) + headers = { 'Cookie' => "PVEAuthCookie=#{JSON.parse(response.body)['data']['ticket']}" } else headers = { 'Authorization' => "PVEAPIToken=#{user}!#{token}=#{secret}" } end @@ -137,8 +137,8 @@ def self.request(uri, user: nil, pass: nil, headers: {}, method: Net::HTTP::Get, return expect ? response.is_a?(Net::HTTPSuccess) : response end - def self.download(node, path, url:, owner: Default.app(node), group: Default.app(node), mode: '0754', action: :create) - node.remote_file path do + def self.download(ctx, path, url:, owner: Default.app(ctx), group: Default.app(ctx), mode: '0754', action: :create) + ctx.remote_file path do source url.respond_to?(:call) ? lazy { url.call } : url owner owner group group diff --git a/config/recipes/config.rb b/config/recipes/config.rb index 7836add..d4821d0 100644 --- a/config/recipes/config.rb +++ b/config/recipes/config.rb @@ -1,6 +1,6 @@ -login = Env.get(node, 'login') -password = Env.get(node, 'password') -email = Env.get(node, 'email') +login = Env.get(self, 'login') +password = Env.get(self, 'password') +email = Env.get(self, 'email') ruby_block 'config_wait_http' do block do Utils.wait("127.0.0.1:#{node['git']['port']['http']}", timeout: 15, sleep_interval: 1) end @@ -24,8 +24,6 @@ ruby_block 'config_set_key' do block do require 'json' - login = login - password = password url = "#{node['git']['api']['endpoint']}/admin/users/#{login}/keys" key = ::File.read("#{node['key']}.pub").strip @@ -34,7 +32,7 @@ end status_code = (response = Utils.request(url, body: { title: "config-#{login}", key: key }.to_json, user: login, pass: password, method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' })).code.to_i - Logs.request!("Set key failed", url, response) unless [201, 422].include?(status_code) + Logs.request!(url, response, msg="Set key failed") unless [201, 422].include?(status_code) end only_if { ::File.exist?("#{node['key']}.pub") } not_if do @@ -71,24 +69,13 @@ user: login, pass: password, body: { username: org }.to_json )).code.to_i - Logs.request!("Create organization '#{org}' failed", uri, response) unless [201, 409, 422].include? status_code + Logs.request!(uri, response, msg="Create organization '#{org}' failed") unless [201, 409, 422].include? status_code end end end ruby_block 'config_git_environment' do block do - %w(proxmox host app login password email).each do |parent| - value = node[parent] - next if value.nil? || value.to_s.strip.empty? - if value.is_a?(Hash) - value.each do |child, child_value| - next if child_value.nil? || child_value.to_s.strip.empty? - Env.set_variable(node, "#{parent}_#{child}", child_value) - end - else - Env.set_variable(node, parent, value) - end - end + Env.dump(self, 'proxmox', 'host', 'app', 'login', 'password', 'email') end end diff --git a/config/recipes/repo.rb b/config/recipes/repo.rb index 06f1521..8a15f51 100644 --- a/config/recipes/repo.rb +++ b/config/recipes/repo.rb @@ -1,10 +1,12 @@ require 'find' require 'fileutils' -source = ENV['PWD'] +source = ENV['PWD'] || Dir.pwd destination = node['git']['dir']['workspace'] working = "#{destination}/workdir" +is_bootstrap = ['127.0.0.1', 'localhost', '::1'].include?((Env.get(self, 'host'))) + Common.directories(self, [destination, working], recreate: true, owner: node['app']['user'] , group: node['app']['group']) @@ -14,7 +16,7 @@ monorepo = (repository == "./") - path_source = monorepo ? source : File.expand_path(repository, source) + path_source = monorepo ? source : File.expand_path(repository.to_s, source.to_s) name_repo = File.basename(path_source) path_working = "#{working}/#{name_repo}" path_destination = monorepo ? destination : File.expand_path(name_repo, destination) @@ -24,7 +26,7 @@ block do node.run_state["#{name_repo}_repo_exists"] = (Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", - user: Env.get(node, 'login'), pass: Env.get(node, 'password')) + user: Env.get(self, 'login'), pass: Env.get(self, 'password')) ).code.to_i != 404 end end @@ -47,8 +49,8 @@ ruby_block "repo_exists_reset_#{name_repo}" do block do unless [204, 404].include?(status_code = (response = Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", - method: Net::HTTP::Delete, user: Env.get(node, 'login'), pass: Env.get(node, 'password'))).code.to_i) - Logs.request!("Failed to delete #{name_repo}", uri, response) + method: Net::HTTP::Delete, user: Env.get(self, 'login'), pass: Env.get(self, 'password'))).code.to_i) + Logs.request!(uri, response, msg="Failed to delete #{name_repo}") end end only_if { node.run_state["#{name_repo}_repo_exists"] } @@ -61,9 +63,9 @@ (response = Utils.request( uri="#{node['git']['api']['endpoint']}/admin/users/#{node['git']['org']['main']}/repos", method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' }, - user: Env.get(node, 'login'), pass: Env.get(node, 'password'), + user: Env.get(self, 'login'), pass: Env.get(self, 'password'), body: { name: name_repo, private: false, auto_init: false, default_branch: 'main' }.to_json - )).code.to_i == 201 or Logs.request!("Error creating repository '#{name_repo}'", uri, response) + )).code.to_i == 201 or Logs.request!(uri, response, msg="Error creating repository '#{name_repo}'") end end @@ -108,7 +110,7 @@ ruby_block "repo_files_#{name_repo}" do block do - Find.find(path_source) do |path_src| + Find.find(path_source) do |path_src| next if path_src =~ /(^|\/)\.git(\/|$)/ || path_src =~ /(^|\/)\.gitmodules(\/|$)/ path_src_rel = path_src.sub(/^#{Regexp.escape(path_source)}\/?/, '') path_dst = File.join(path_destination, path_src_rel) @@ -183,7 +185,7 @@ fi git submodule update --init --recursive # pass variables in bootstrap - if [ "#{Env.get(node, 'host')}" = "127.0.0.1" ] && [ -f local/config.json ]; then + if [ "#{is_bootstrap}" = "true" ] && [ -f local/config.json ]; then git add -f local/config.json fi EOH @@ -206,8 +208,8 @@ git push -f origin HEAD:main sleep 3 if ! git ls-remote origin refs/for/release | grep -q "$(git rev-parse HEAD)"; then - if { [ "#{repository}" != "./" ] && [ "#{Env.get(node, 'host')}" != "127.0.0.1" ]; } || \ - { [ "#{repository}" = "./" ] && [ "#{Env.get(node, 'host')}" = "127.0.0.1" ]; }; then + if { [ "#{repository}" != "./" ] && [ "#{is_bootstrap}" = "false" ]; } || \ + { [ "#{repository}" = "./" ] && [ "#{is_bootstrap}" = "true" ]; }; then git push origin HEAD:refs/for/release \ -o topic="release" \ -o title="Release Pull Request" \ @@ -224,10 +226,10 @@ ruby_block "repo_stage_fork_clean_#{name_repo}" do block do if Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", - user: Env.get(node, 'login'), pass: Env.get(node, 'password')).code.to_i != 404 + user: Env.get(self, 'login'), pass: Env.get(self, 'password')).code.to_i != 404 status_code = (response = Utils.request(uri="#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['stage']}/#{name_repo}", - method: Net::HTTP::Delete, user: Env.get(node, 'login'), pass: Env.get(node, 'password'))).code.to_i - Logs.request!("Failed to clean test/#{name_repo} (#{status_code})", uri, response) unless [204, 404].include?(status_code) + method: Net::HTTP::Delete, user: Env.get(self, 'login'), pass: Env.get(self, 'password'))).code.to_i + Logs.request!(uri, response, msg="Failed to clean test/#{name_repo} (#{status_code})") unless [204, 404].include?(status_code) end end end @@ -236,10 +238,10 @@ block do status_code = Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}/forks", method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' }, - user: Env.get(node, 'login'), pass: Env.get(node, 'password'), + user: Env.get(self, 'login'), pass: Env.get(self, 'password'), body: { name: name_repo, organization: node['git']['org']['stage'] }.to_json ).code.to_i - Logs.request!("Forking to #{node['git']['org']['stage']}/#{name_repo} failed", uri, response) unless [201, 202].include?(status_code) + Logs.request!(uri, response, msg="Forking to #{node['git']['org']['stage']}/#{name_repo} failed") unless [201, 202].include?(status_code) end end @@ -251,10 +253,8 @@ end -ruby_block "repository_variable_dump" do +ruby_block "#{cookbook_name}_env_dump" do block do - Env.dump(node, node['git'], cookbook_name.to_s) - Env.dump(node, node['runner'], cookbook_name.to_s) + Env.dump(self, 'git', 'runner', repo: cookbook_name) end - ignore_failure true end diff --git a/config/templates/git_app.ini.erb b/config/templates/git_app.ini.erb index d19aaaa..06aeadd 100644 --- a/config/templates/git_app.ini.erb +++ b/config/templates/git_app.ini.erb @@ -11,6 +11,7 @@ ROOT_URL = http://<%= @host %>:<%= @http_port %>/ SSH_PORT = <%= @ssh_port %> START_SSH_SERVER = true BUILTIN_SSH_SERVER_USER = <%= @ssh_user %> +HTTP_ADDR = 0.0.0.0 [database] DB_TYPE = sqlite3 diff --git a/docs/environment.png b/docs/environment.png index 6af83b74a912464beff70e6b4dada6d5f79d7208..502e4e65b2ff293856621f21e84ee2a38a5a9477 100644 GIT binary patch literal 253559 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

O*g3ai4UNo;CcZCMLnY-^6mw1b5(IJ;Bd| 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%0D9UV_N~kQ;Hj@Tv)L4y%spa1OI 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 z�QaUcxgl%_I!*_hzda<{L>DD}4^W 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 z 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∨NrQrI-(+?j$ zh&rpuo{bcLyk@UEkK$KE*VYbZbwz99SE`l#^WC`XtN!=0Pj-i8v?P&;2HdS2GO+ksF$i4Lz+R3X7P_($Grp+|TyGY}!NZO6FOOq$EgN z1=4ge>hI|;5ho*5knj{~lCi$^JHVbcf+11PxQ0>144rh8$hApY(9Kuj8Vu1O*+@1$ z##YTZ4xbgud(^!KS=w#2H%k-VZ*Y-TW;(cOuzX^u+H_*A_UtUF{Pt0~j!G?)uB__= z%0Zc-M)W+{bujkCQozj1vw~4 zKyUx~7Uq&aC~Tk=@r2L&k%;?2YqV734#9KA26dku(|PGcBP28)hc8mr@!EY{T$1{rsNawY5v@1d!0rk$H$XNeI_&3#*cK$! zi4!qfV79qa_@S-#e`W-wfsIw~o=S}dfkqd97h*CUI7fz_g;8ug>_dfE<`Hgwk9^U@ zo$pm~SY5~sLRgr}WMuF;t2Uf~K%l{ZH;QSM;;LgV-fAQ^o%v`-LHA?lXPZ!)4*rT> z+HcOlw{2|PLL}&hZuWAg@~Mw6t@GygppTyC7} z$zev>5nua7;574N4^L6Ld?v5$FgS=9zz(hKdmqWA(L)yj7kt8AkSQ?jc!@T*e?=Y? zWvN&GxJ}s1OEdC)Y~c>REHtm)xQ${AFaL-6Q?8wZ=frX$$z86U0nEl<)rEtNIQ-Z1 zbuDOw^w;3~E~`71a+9zu?DOhm#Rv)eavJ*m=@L834GXk-6ZoV!RqoBHocoRl81cNC zRFj^36=QBepqZ~oX|q~h#xY{dz^nAEs5w&suXk|Tb+a?kFL9xmq1n~xn; zC8vmZrCe`zg>g+!(dL8@_c&)GBCF0|Nfj1XYSPKer{{FyJEgYnliX+YC0mA>{^ubz zpI4Mel*HyqqdH#xzBE+pOqRX}12k*3E4;6qICAAB`Apj0@a0}9i1@0`#2Sk!HrF|n z8WIwqZ?8ka3qNm$Pt`5Pqm}8J@1)m5+$WND>8@tP5!YWHxE{;k>Cv&OC3Bkp@kc*G z_4OxsvvgYpaC-qjJo&lAp~g&MH*CEN{~(w=?0Mo5fG&t1a6R$ZibPRF6r*Bp}V`AAqNKL9iQj- zeAm0qzp&P<`@YXUXYYOO&qcdQn!wrx?Ru#cbDr*!>vWXD~r!3M?ll+qmsa&qNk>!n4v2;WFNA-oLTYjSU>?QKv!Nc$68tCjz_bFjgdW(NGx09t@{vgAp)^PQq zS^d)3vM;_s;?w+MeQO__oTy4U^X)flCUOJEK$@w(41ZFU=keTTcT3l#TetehZ-k`e zd{A|t!~@_$PA-z;OZ3_1gmgHwm5~w3uX=qxIa?)Fj~~x-A=T|W{Gtm23MR-W61~X3 z5L$aJVv5By$Pz_|OkHYFKcvRTMyW1Y{sPf6U+O)&w_cIV7ZDk{e7Ojn*on^&m$?MK4I+w?n;JLrj@SbO-d6X{HZo$!c6!QL%cvI4#A!8~pAhA7dO z{RiM|5g5<9u`*VbKLSM}sB z-Cw?IPqO#l;EWIp?5+qLl&xD zwOzNh+8o>Ik;r{ZmNU7Rr`JW3>!-X7iwte8b8_Ep?7*^pd~HA6;hLg<47lf?5?@h_ zK7l%{LU4K~q$xWb>QL+rro|`Q@OR)k1Y>l;1Y7802Vr!|ueLvq%g!7EiY(i{$FR7sMbN
iCvSJlAZa|)H;4*Qgv68#lc@aN__oo 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 z zda_XWOx!DO!WPw3@xM233C;D||5K{d3qx+C-bO!_SaMS8#Q=X^%Bwvuk~PH)_ Gem::Version.new(installed_version) -Utils.download(self, "/tmp/zigbee2mqtt.zip", - url: "https://github.com/Koenkk/zigbee2mqtt/archive/refs/tags/#{latest_version}.zip", - owner: node['app']['user'], group: node['app']['group'], mode: '0644') -.notifies :stop, "service[zigbee2mqtt]", :immediately if resources("service[zigbee2mqtt]") rescue nil if latest_version +update_needed = false +if latest_version + update_needed = installed_version.nil? || Gem::Version.new(latest_version) > Gem::Version.new(installed_version) +end + +if update_needed + Utils.snapshot(self, node['bridge']['data']) + Utils.download(self, "/tmp/zigbee2mqtt.zip", + url: "https://github.com/Koenkk/zigbee2mqtt/archive/refs/tags/#{latest_version}.zip", + owner: node['app']['user'], group: node['app']['group']) + .notifies :stop, "service[zigbee2mqtt]", :immediately if latest_version and resources("service[zigbee2mqtt]") -Utils.snapshot(self, node['bridge']['data']) if latest_version + execute 'zigbee2mqtt_files' do + command lazy { "unzip -o /tmp/zigbee2mqtt.zip -d #{node['bridge']['dir']} && mv #{node['bridge']['dir']}/zigbee2mqtt*/* #{node['bridge']['dir']}/ && rm -rf #{node['bridge']['dir']}/zigbee2mqtt*" } + user node['app']['user'] + group node['app']['group'] + only_if { ::File.exist?('/tmp/zigbee2mqtt.zip') } + notifies :run, 'execute[zigbee2mqtt_build]', :immediately + end -execute 'zigbee2mqtt_files' do - command lazy { "unzip -o #{"/tmp/zigbee2mqtt.zip"} -d #{node['bridge']['dir']} && mv #{node['bridge']['dir']}/zigbee2mqtt*/* #{node['bridge']['dir']}/ && rm -rf #{node['bridge']['dir']}/zigbee2mqtt" } - user node['app']['user'] - group node['app']['group'] - only_if { latest_version && ::File.exist?("/tmp/zigbee2mqtt.zip") } -end + execute 'zigbee2mqtt_build' do + command 'pnpm install --frozen-lockfile && pnpm build' + user node['app']['user'] + group node['app']['group'] + cwd node['bridge']['dir'] + action :nothing + end -execute 'zigbee2mqtt_build' do - command 'pnpm install --frozen-lockfile && pnpm build' - user node['app']['user'] - group node['app']['group'] - cwd node['bridge']['dir'] - not_if { !latest_version } end template "#{node['bridge']['data']}/configuration.yaml" do @@ -63,12 +73,11 @@ adapter: node['bridge']['adapter'], data_dir: node['bridge']['data'], logs_dir: node['bridge']['logs'], - broker_host: Env.get(node, 'broker'), - broker_user: Env.get(node, 'login'), - broker_password: Env.get(node, 'password') + broker_host: Env.get(self, 'broker'), + broker_user: Env.get(self, 'login'), + broker_password: Env.get(self, 'password') ) - not_if { !latest_version } - not_if { ::File.exist?("#{node['bridge']['data']}/configuration.yaml") } + only_if { latest_version && !::File.exist?("#{node['bridge']['data']}/configuration.yaml") } end Utils.snapshot(self, node['bridge']['data'], restore: true) diff --git a/libs/broker/recipes/default.rb b/libs/broker/recipes/default.rb index bb5080d..abc9bc9 100644 --- a/libs/broker/recipes/default.rb +++ b/libs/broker/recipes/default.rb @@ -1,9 +1,11 @@ -Env.set(node, 'broker', "mqtt://#{node['ip']}:#{node['broker']['port']}") - -package 'mosquitto' +Env.dump(self, cookbook_name, repo: cookbook_name) Common.directories(self, [node['broker']['dir']['data'], node['broker']['dir']['log']], owner: node['app']['user'], group: node['app']['group']) +Env.set(self, 'broker', "mqtt://#{node['ip']}:#{node['broker']['port']}") + +package 'mosquitto' + template node['broker']['file']['config'] do source 'mosquitto.conf.erb' owner node['app']['user'] @@ -24,8 +26,8 @@ mode '0640' end -execute "user-add_#{Env.get(node, 'login')}" do - command "mosquitto_passwd -b #{node['broker']['file']['user']} '#{Env.get(node, 'login')}' '#{Env.get(node, 'password')}'" +execute "user_add_#{Env.get(self, 'login')}" do + command "mosquitto_passwd -b #{node['broker']['file']['user']} '#{Env.get(self, 'login')}' '#{Env.get(self, 'password')}'" user 'root' sensitive true end diff --git a/libs/proxy/recipes/default.rb b/libs/proxy/recipes/default.rb index 2ae2e7c..252df0b 100644 --- a/libs/proxy/recipes/default.rb +++ b/libs/proxy/recipes/default.rb @@ -1,7 +1,9 @@ -package 'caddy' +Env.dump(self, cookbook_name, repo: cookbook_name) Common.directories(self, [node['proxy']['dir']['app'], node['proxy']['dir']['logs']], owner: node['app']['user'], group: node['app']['group']) +package 'caddy' + ruby_block 'proxmox_containers' do block do domain = node['proxy']['config']['domain'] diff --git a/libs/share/recipes/default.rb b/libs/share/recipes/default.rb index 093adc4..1f3130b 100644 --- a/libs/share/recipes/default.rb +++ b/libs/share/recipes/default.rb @@ -1,7 +1,7 @@ Common.packages(self, %w[samba samba-common samba-client]) -login = Env.get(node, 'login') -password = Env.get(node, 'password') +login = Env.get(self, 'login') +password = Env.get(self, 'password') group login do gid node['share']['user']['gid'] From 0fb03e751286063f09be5854772aee166dba5bd6 Mon Sep 17 00:00:00 2001 From: Steven <106664555+stevius10@users.noreply.github.com> Date: Sat, 16 Aug 2025 21:16:31 +0200 Subject: [PATCH 08/13] dev/1.1/shared-container-libraries (#60) * app user for consistence reasons * assign variables in recipe * dynamic repo path to determine cookbook name later on * stop service if installed * positional arguments * use updated libraries * explicit arguments * restricted access not justifies to disable * safe log debug with even arguments * snapshot and restore * template needs to be updated --- .gitea/workflows/action.yml | 16 +++--- README.md | 2 +- config/libraries/common.rb | 4 +- config/libraries/env.rb | 8 +-- config/libraries/logs.rb | 15 +++--- config/libraries/utils.rb | 83 +++++++++++++++++-------------- config/recipes/config.rb | 4 +- config/recipes/git.rb | 8 +-- config/recipes/prepare.rb | 4 +- config/recipes/repo.rb | 18 ++++--- config/recipes/runner.rb | 7 +-- config/templates/git_app.ini.erb | 2 +- config/templates/repo_config.erb | 2 +- libs/assistant/recipes/default.rb | 14 ++---- libs/bridge/recipes/default.rb | 16 +++--- libs/broker/recipes/default.rb | 12 ++--- libs/proxy/recipes/default.rb | 8 ++- 17 files changed, 111 insertions(+), 112 deletions(-) diff --git a/.gitea/workflows/action.yml b/.gitea/workflows/action.yml index 68efe50..ffad8d9 100644 --- a/.gitea/workflows/action.yml +++ b/.gitea/workflows/action.yml @@ -15,11 +15,13 @@ runs: with: repository: "${{ inputs.repo }}" ref: "${{ inputs.ref }}" - path: "${{ inputs.repo }}" + path: repo - name: Configure container id: init run: | + repository="${{ inputs.repo }}" + echo "repo=${repository##*/}" >> "$GITEA_ENV" source repo/config.env echo "ip=$IP" >> $GITEA_ENV echo "id=$ID" >> $GITEA_ENV @@ -51,22 +53,18 @@ runs: with: repository: "${{ inputs.repo }}" ref: "${{ gitea.ref_name }}" - path: "${{ inputs.repo }}" + path: "${{ env.repo }}" - name: Checkout libraries uses: https://gitea.com/actions/checkout@v4 with: repository: 'main/libraries' ref: 'main' - path: "${{ inputs.repo }}/libraries" + path: "${{ env.repo }}/libraries" - name: Configure container run: | - tar -c "${{ inputs.repo }}" -cz . | ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${{ env.id }}" "config@${{ env.ip }}" 'sudo tar xz -C /tmp + tar -c "${{ env.repo }}" -cz . | ssh -o StrictHostKeyChecking=no -i "/share/.ssh/${{ env.id }}" "config@${{ env.ip }}" 'sudo tar xz -C /tmp sudo -E IP=${{ env.ip }} ID=${{ env.id }} HOST=${{ vars.HOST }} LOGIN=${{ vars.LOGIN }} PASSWORD=${{ vars.PASSWORD }} PWD="$(pwd)" \ - cinc-client --local-mode $CONFIG_ARGS --config-option cookbook_path="/tmp" -o "${{ inputs.repo }}"' + cinc-client --local-mode $CONFIG_ARGS --config-option cookbook_path="/tmp" -o "${{ env.repo }}"' shell: bash - - - - diff --git a/README.md b/README.md index 2b1e138..8a2497a 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ This system implements stateless infrastructure management on Proxmox VE, ensuri - e. g., **Version-Controlled Mirrored** - **Backup**: See [Self-contained Monorepository](#self-contained-monorepository) - - store /share for persistence, disable network access for security + - store /share for persistence - **Update**: See [Self-contained Monorepository](#self-contained-monorepository), and redeploy merged diff --git a/config/libraries/common.rb b/config/libraries/common.rb index b137e53..c94f711 100644 --- a/config/libraries/common.rb +++ b/config/libraries/common.rb @@ -12,8 +12,8 @@ def self.packages(ctx, *pkgs, action: :install) def self.directories(ctx, dirs, opts = {}) dirs = Array(dirs) - owner = opts[:owner] || Default.app(ctx) - group = opts[:group] || Default.app(ctx) + owner = opts[:owner] || Default.user(ctx) + group = opts[:group] || Default.group(ctx) mode = opts[:mode] || '0755' recursive = true recreate = opts[:recreate] || false diff --git a/config/libraries/env.rb b/config/libraries/env.rb index ec3b8d8..0abb244 100644 --- a/config/libraries/env.rb +++ b/config/libraries/env.rb @@ -18,14 +18,14 @@ def self.get(ctx, key) return env_key unless env_key.nil? || env_key.to_s.strip.empty? return get_variable(ctx, key) rescue => e - return Logs.debug(:warn, "failed get '#{key}'", :error, e.message, ctx: ctx) + return Logs.debug(:warn, "failed get '#{key}'", [:error, e.message], ctx: ctx) end def self.get_variable(ctx, key, repo: nil) begin response = request(ctx, key, repo: repo); response && response.body ? JSON.parse(response.body)['data'] : nil rescue => e - Logs.debug(:warn, "failed get variable '#{key}'", :error, e.message, :endpoint, endpoint(ctx), :repo, repo, ctx: ctx) + Logs.debug(:warn, "failed get variable '#{key}'", [:error, e.message, :endpoint, endpoint(ctx), :repo, repo], ctx: ctx) end end @@ -33,7 +33,7 @@ def self.set_variable(ctx, key, val, repo: nil) begin request(Ctx.node(ctx), key, body: ({ name: key, value: val.to_s }.to_json), repo: repo, expect: true) rescue => e - Logs.raise!("failed set variable '#{key}' to #{Logs.mask(val)}", :val, val, :repo, repo, e: e) + Logs.raise!("failed set variable '#{key}' to #{Logs.mask(val)}", [:val, val, :repo, repo], e: e) end end @@ -84,7 +84,7 @@ def self.dump(ctx, *keys, repo: nil) end true rescue => e - Logs.raise!("failed dump variables", :repo, repo, e: e) + Logs.raise!("failed dump variables", [:repo, repo], e: e) end end diff --git a/config/libraries/logs.rb b/config/libraries/logs.rb index 8fb60d7..ddfa0eb 100644 --- a/config/libraries/logs.rb +++ b/config/libraries/logs.rb @@ -34,11 +34,13 @@ def self.assignment(key, val, mask=true); info("assigned '#{key}' value '#{mask def self.return(msg); log(:info, msg.to_s); return msg end def self.debug(level, msg, *pairs, ctx: nil) - input = pairs.flatten.each_slice(2).to_h - input['env'] = ENV.to_h; - input['ctx'] = ctx.to_h if ctx - ctx = input.map { |k, v| "#{k}=#{v.inspect}" }.join(" ") - log(level, [msg, ctx].reject(&:empty?).join(" ")) + flat = pairs.flatten + raise ArgumentError, "debug requires key value pairs (#{flat.length}: #{flat.inspect})" unless flat.length.even? + input = flat.each_slice(2).to_h.transform_keys(&:to_s) + input['env'] = ENV.to_h + input['ctx'] = ctx.respond_to?(:to_h) ? ctx.to_h : ctx + payload = input.map { |k, v| "#{k}=#{v.inspect}" }.join(" ") + log(level, [msg, payload].reject { |s| s.nil? || s.empty? }.join(" ")) end def self.info?(msg) @@ -47,9 +49,10 @@ def self.info?(msg) end def self.raise!(msg, *pairs, e:nil, ctx: nil) + pairs = pairs.flatten c = callsite label = method_label(c) - debug(:error, msg, *pairs, ctx) + debug(:error, msg, *pairs, ctx: ctx) raise("[#{label}] #{e.message if e} #{msg}") end diff --git a/config/libraries/utils.rb b/config/libraries/utils.rb index 276bf9b..6603749 100644 --- a/config/libraries/utils.rb +++ b/config/libraries/utils.rb @@ -2,6 +2,9 @@ require 'fileutils' require 'json' require 'net/http' +require 'openssl' +require 'socket' +require 'timeout' require 'tmpdir' require 'uri' @@ -12,8 +15,7 @@ module Utils def self.wait(condition = nil, timeout: 20, sleep_interval: 5, &block) return Kernel.sleep(condition) if condition.is_a?(Integer) return Timeout.timeout(timeout) { block.call } if block_given? - ArgumentError unless condition - + return Kernel.sleep(timeout) if condition.nil? Timeout.timeout(timeout) do loop do ok = false @@ -62,40 +64,51 @@ def self.arch(ctx) end def self.snapshot(ctx, dir, snapshot_dir: '/share/snapshots', name: ctx.cookbook_name, restore: false) - timestamp = Time.now.strftime('%H%M-%d%m%y') - file = File.join(snapshot_dir, "#{name}-#{timestamp}.tar.gz") + begin + timestamp = Time.now.strftime('%H%M-%d%m%y') + snapshot = File.join(snapshot_dir, name, "#{name}-#{timestamp}.tar.gz") - md5_dir = ->(path) { - Digest::MD5.new.tap do |md5| - Dir.glob("#{path}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }.sort.each do |f| - md5.update(File.read(f)) + md5_dir = ->(path) { Digest::MD5.new.tap do |md5| + Dir.glob("#{path}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }.sort.each do |f| + md5.update(File.read(f)) + end + end.hexdigest } + verify = ->(archive, compare_dir) { + Dir.mktmpdir do |tmp| + Logs.raise!("extraction failed for '#{archive}'") unless system("tar -xzf #{archive} -C #{tmp}") + md5_base = md5_dir.(File.join(tmp, File.basename(compare_dir))) + md5_compare = md5_dir.(compare_dir) + Logs.raise!("verify snapshot failed",[:base, File.join(tmp, File.basename(compare_dir)), + :compare, compare_dir, :md5_base, md5_base, :md5_compare, md5_compare] + ) unless md5_base == md5_compare + end; true } + + FileUtils.mkdir_p(File.dirname(snapshot)) + latest = Dir[File.join(snapshot_dir, name, "#{name}*.tar.gz")].max_by { |f| File.mtime(f) } + if restore + if latest && ::File.exist?(latest) + system("tar -czf #{snapshot} -C #{File.dirname(dir)} #{File.basename(dir)}") or + Logs.raise!("snapshot restore failed", [:dir, dir, :snapshot, snapshot, :dir, dir], e: e) + return verify.(latest, dir) + end + return true # initial + + else + if Dir.exists?(dir) + system("tar -czf #{snapshot} -C #{File.dirname(dir)} #{File.basename(dir)}") or + Logs.raise!("snapshot creation failed", [:dir, dir, :snapshot, snapshot, :dir, dir], e: e) + return verify.(snapshot, dir) + else + return true # initial end - end.hexdigest - } - verify = ->(archive, compare_dir) { - Dir.mktmpdir do |tmp| - raise Logs.error("extraction failed for '#{archive}'") unless system("tar -xzf #{archive} -C #{tmp}") - raise "md5 mismatch indicates error" unless md5_dir.(File.join(tmp, File.basename(compare_dir))) == md5_dir.(compare_dir) - end - true - } - if restore - latest = Dir[File.join(snapshot_dir, "#{name}-*.tar.gz")].max_by { |f| File.mtime(f) } - return false unless latest && File.exist?(latest) - ctx.execute("common_restore_snapshot_#{dir}") do - command "tar -xzf #{latest} -C #{File.dirname(dir)}" - end - verify.(latest, dir) - else - return false unless Dir.exist?(dir) - ctx.execute("common_create_snapshot_#{dir}") do - command "mkdir -p $(dirname #{file}) && tar -czf #{file} -C #{File.dirname(dir)} #{File.basename(dir)}" end - verify.(file, dir) + rescue => e + action = restore ? 'restore' : 'create' + Logs.raise!("error in snapshot", [:action, action, :dir, dir, :snapshot, snapshot], e: e) end end - def self.proxmox(uri, ctx, path, expect: true) + def self.proxmox(uri, ctx, path) host = Env.get(ctx, 'proxmox_host') user = Env.get(ctx, 'proxmox_user') pass = Env.get(ctx, 'proxmox_password') @@ -106,14 +119,12 @@ def self.proxmox(uri, ctx, path, expect: true) if pass && !pass.empty? response = request(uri="https://#{host}:8006/api2/json/access/ticket", method: Net::HTTP::Post, body: URI.encode_www_form(username: user, password: pass), headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }) - Logs.request!(uri, response, msg="Proxmox ticket could not be retrieved", ) unless response.is_a?(Net::HTTPSuccess) + Logs.request!(uri, response, "Proxmox ticket could not be retrieved") unless response.is_a?(Net::HTTPSuccess) headers = { 'Cookie' => "PVEAuthCookie=#{JSON.parse(response.body)['data']['ticket']}" } else headers = { 'Authorization' => "PVEAPIToken=#{user}!#{token}=#{secret}" } end - - res = request(url, headers: headers, expect: expect) - expect ? JSON.parse(res.body)['data'] : res + JSON.parse(request(url, headers: headers).body)['data'] end # Remote @@ -137,7 +148,7 @@ def self.request(uri, user: nil, pass: nil, headers: {}, method: Net::HTTP::Get, return expect ? response.is_a?(Net::HTTPSuccess) : response end - def self.download(ctx, path, url:, owner: Default.app(ctx), group: Default.app(ctx), mode: '0754', action: :create) + def self.download(ctx, path, url:, owner: Default.user(ctx), group: Default.group(ctx), mode: '0754', action: :create) ctx.remote_file path do source url.respond_to?(:call) ? lazy { url.call } : url owner owner @@ -148,7 +159,7 @@ def self.download(ctx, path, url:, owner: Default.app(ctx), group: Default.app(c end def self.latest(url) - request(url).body[/title>.*?v?([0-9]+\.[0-9]+(?:\.[0-9]+)?)/, 1].to_s || "latest" + (request(url).body[/title>.*?v?([0-9]+\.[0-9]+(?:\.[0-9]+)?)/, 1] || "latest").to_s end end \ No newline at end of file diff --git a/config/recipes/config.rb b/config/recipes/config.rb index d4821d0..fae3e19 100644 --- a/config/recipes/config.rb +++ b/config/recipes/config.rb @@ -32,7 +32,7 @@ end status_code = (response = Utils.request(url, body: { title: "config-#{login}", key: key }.to_json, user: login, pass: password, method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' })).code.to_i - Logs.request!(url, response, msg="Set key failed") unless [201, 422].include?(status_code) + Logs.request!(url, response, "Set key failed") unless [201, 422].include?(status_code) end only_if { ::File.exist?("#{node['key']}.pub") } not_if do @@ -69,7 +69,7 @@ user: login, pass: password, body: { username: org }.to_json )).code.to_i - Logs.request!(uri, response, msg="Create organization '#{org}' failed") unless [201, 409, 422].include? status_code + Logs.request!(uri, response, "Create organization '#{org}' failed") unless [201, 409, 422].include? status_code end end end diff --git a/config/recipes/git.rb b/config/recipes/git.rb index d69d900..a37ada9 100644 --- a/config/recipes/git.rb +++ b/config/recipes/git.rb @@ -1,17 +1,13 @@ Utils.download(self, "#{node['git']['dir']['app']}/gitea", url: -> { ver = Utils.latest('https://github.com/go-gitea/gitea/releases/latest') - "https://github.com/go-gitea/gitea/releases/download/v#{ver}/gitea-#{ver}-linux-#{Utils.arch(node)}" }, - owner: node['app']['user'] , - group: node['app']['group'] -) + "https://github.com/go-gitea/gitea/releases/download/v#{ver}/gitea-#{ver}-linux-#{Utils.arch(node)}" } ) template "#{node['git']['dir']['app']}/app.ini" do source 'git_app.ini.erb' owner node['app']['user'] group node['app']['group'] mode '0644' - variables(host: node['host'], - run_user: node['app']['user'] , ssh_user: node['app']['config'], + variables(host: node['host'], app_user: node['app']['user'] , ssh_user: node['app']['config'], app_dir: node['git']['dir']['app'], home_dir: node['git']['dir']['home'], http_port: node['git']['port']['http'], ssh_port: node['git']['port']['ssh'] ) action :create_if_missing diff --git a/config/recipes/prepare.rb b/config/recipes/prepare.rb index 25215af..7ec7aa8 100644 --- a/config/recipes/prepare.rb +++ b/config/recipes/prepare.rb @@ -1,8 +1,6 @@ Common.directories(self, [ (app = node['git']['dir']['app']), node['git']['dir']['workspace'], node['runner']['dir']['app'], - "#{app}/custom", "#{app}/data", "#{app}/gitea-repositories", "#{app}/log" ], - owner: node['app']['user'] , - group: node['app']['group']) + "#{app}/custom", "#{app}/data", "#{app}/gitea-repositories", "#{app}/log" ]) Common.packages(self, %w(git acl python3-pip ansible ansible-core nodejs npm python3-proxmoxer)) diff --git a/config/recipes/repo.rb b/config/recipes/repo.rb index 8a15f51..fa1772b 100644 --- a/config/recipes/repo.rb +++ b/config/recipes/repo.rb @@ -7,8 +7,7 @@ is_bootstrap = ['127.0.0.1', 'localhost', '::1'].include?((Env.get(self, 'host'))) -Common.directories(self, [destination, working], recreate: true, - owner: node['app']['user'] , group: node['app']['group']) +Common.directories(self, [destination, working], recreate: true) (repositories = node['git']['conf']['repo'] .flat_map { |r| (r == './libs') ? Dir.glob(File.join(source, r, '*')).select { |d| File.directory?(d) }.map { |p| p.sub(source, '.') } : r } @@ -50,7 +49,7 @@ block do unless [204, 404].include?(status_code = (response = Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", method: Net::HTTP::Delete, user: Env.get(self, 'login'), pass: Env.get(self, 'password'))).code.to_i) - Logs.request!(uri, response, msg="Failed to delete #{name_repo}") + Logs.request!(uri, response, "Failed to delete #{name_repo}") end end only_if { node.run_state["#{name_repo}_repo_exists"] } @@ -65,7 +64,7 @@ method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' }, user: Env.get(self, 'login'), pass: Env.get(self, 'password'), body: { name: name_repo, private: false, auto_init: false, default_branch: 'main' }.to_json - )).code.to_i == 201 or Logs.request!(uri, response, msg="Error creating repository '#{name_repo}'") + )).code.to_i == 201 or Logs.request!(uri, response, "Error creating repository '#{name_repo}'") end end @@ -81,7 +80,10 @@ owner node['app']['user'] group node['app']['group'] mode '0644' - variables(repo: name_repo, git_user: node['app']['user'] ) + variables(repo: name_repo, + config: node['app']['config'], + org: node['git']['org']['main'], + ssh: node['git']['host']['ssh']) only_if { ::File.directory?("#{path_destination}/.git") } end @@ -134,8 +136,8 @@ owner node['app']['user'] group node['app']['group'] mode '0644' + variables(host: node['host']) not_if { monorepo } - not_if { File.exist?("#{path_destination}/.gitea/workflows/sync.yml") } end template "#{path_destination}/.gitea/workflows/pipeline.yml" do @@ -229,7 +231,7 @@ user: Env.get(self, 'login'), pass: Env.get(self, 'password')).code.to_i != 404 status_code = (response = Utils.request(uri="#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['stage']}/#{name_repo}", method: Net::HTTP::Delete, user: Env.get(self, 'login'), pass: Env.get(self, 'password'))).code.to_i - Logs.request!(uri, response, msg="Failed to clean test/#{name_repo} (#{status_code})") unless [204, 404].include?(status_code) + Logs.request!(uri, response, "Failed to clean test/#{name_repo} (#{status_code})") unless [204, 404].include?(status_code) end end end @@ -241,7 +243,7 @@ user: Env.get(self, 'login'), pass: Env.get(self, 'password'), body: { name: name_repo, organization: node['git']['org']['stage'] }.to_json ).code.to_i - Logs.request!(uri, response, msg="Forking to #{node['git']['org']['stage']}/#{name_repo} failed") unless [201, 202].include?(status_code) + Logs.request!(uri, response, "Forking to #{node['git']['org']['stage']}/#{name_repo} failed") unless [201, 202].include?(status_code) end end diff --git a/config/recipes/runner.rb b/config/recipes/runner.rb index ee233a7..62c381e 100644 --- a/config/recipes/runner.rb +++ b/config/recipes/runner.rb @@ -1,11 +1,8 @@ -Common.directories(self, node['runner']['dir']['app'], owner: node['app']['user'] , group: node['app']['group']) +Common.directories(self, node['runner']['dir']['app']) Utils.download(self, "#{node['runner']['dir']['app']}/act_runner", url: -> { ver = Utils.latest('https://gitea.com/gitea/act_runner/releases/latest') - "https://gitea.com/gitea/act_runner/releases/download/v#{ver}/act_runner-#{ver}-linux-#{Utils.arch(node)}" }, - owner: node['app']['user'] , - group: node['app']['group'], -) + "https://gitea.com/gitea/act_runner/releases/download/v#{ver}/act_runner-#{ver}-linux-#{Utils.arch(node)}" } ) template "#{node['runner']['dir']['app']}/config.yaml" do source 'runner.config.yaml.erb' diff --git a/config/templates/git_app.ini.erb b/config/templates/git_app.ini.erb index 06aeadd..560d27d 100644 --- a/config/templates/git_app.ini.erb +++ b/config/templates/git_app.ini.erb @@ -1,7 +1,7 @@ ; Gitea Application Configuration APP_NAME = Gitea -RUN_USER = <%= @run_user %> +RUN_USER = <%= @app_user %> RUN_MODE = prod [server] diff --git a/config/templates/repo_config.erb b/config/templates/repo_config.erb index af3ba20..4616285 100644 --- a/config/templates/repo_config.erb +++ b/config/templates/repo_config.erb @@ -5,7 +5,7 @@ logallrefupdates = true [remote "origin"] - url = ssh://<%= node['app']['config'] %>@<%= node['git']['host']['ssh'] %>/<%= node['git']['org']['main'] %>/<%= @repo %>.git + url = ssh://<%= @config %>@<%= @ssh %>/<%= @org %>/<%= @repo %>.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "main"] diff --git a/libs/assistant/recipes/default.rb b/libs/assistant/recipes/default.rb index 6d34207..76818a6 100644 --- a/libs/assistant/recipes/default.rb +++ b/libs/assistant/recipes/default.rb @@ -1,6 +1,6 @@ Env.dump(self, cookbook_name, repo: cookbook_name) -Common.directories(self, [node['homeassistant']['dir']['config'], '/app/uv-cache'], owner: node['app']['user'], group: node['app']['group']) +Common.directories(self, [node['homeassistant']['dir']['config'], '/app/uv-cache']) execute 'fix_broken_apt' do command 'apt-get --fix-broken install -y' @@ -42,7 +42,7 @@ user node['app']['user'] group node['app']['group'] environment( - 'HOME' => "/home/#{node['app']['user']}", + # 'HOME' => "/home/#{node['app']['user']}", 'UV_CACHE_DIR' => '/app/uv-cache' ) not_if { ::File.exist?("#{node['homeassistant']['dir']['venv']}/bin/activate") } @@ -52,9 +52,7 @@ command "#{node['homeassistant']['dir']['venv']}/bin/python -m ensurepip" user node['app']['user'] group node['app']['group'] - environment( - 'HOME' => "/home/#{node['app']['user']}" - ) + # environment('HOME' => "/home/#{node['app']['user']}") not_if { ::File.exist?("#{node['homeassistant']['dir']['venv']}/bin/pip") } end @@ -88,12 +86,10 @@ not_if { ::File.exist?("#{node['configurator']['dir']}/bin/hass-configurator") } end -Common.application(self, 'homeassistant', - user: node['app']['user'], cwd: node['homeassistant']['dir']['config'], +Common.application(self, 'homeassistant', cwd: node['homeassistant']['dir']['config'], exec: "#{node['homeassistant']['dir']['venv']}/bin/python3 -m homeassistant --config #{node['homeassistant']['dir']['config']}", unit: { 'Service' => { 'RestartForceExitStatus' => '100', 'Environment' => "PATH=#{node['homeassistant']['dir']['venv']}/bin:/usr/local/bin:/usr/bin:/usr/local/bin/uv" } } ) -Common.application(self, 'hass-configurator', - user: node['app']['user'], cwd: node['homeassistant']['dir']['config'], +Common.application(self, 'hass-configurator', cwd: node['homeassistant']['dir']['config'], exec: "#{node['configurator']['dir']}/bin/hass-configurator -s -e -b #{node['homeassistant']['dir']['config']}" ) \ No newline at end of file diff --git a/libs/bridge/recipes/default.rb b/libs/bridge/recipes/default.rb index 43305b4..6a6080c 100644 --- a/libs/bridge/recipes/default.rb +++ b/libs/bridge/recipes/default.rb @@ -1,6 +1,6 @@ Env.dump(self, cookbook_name, repo: cookbook_name) -Common.directories(self, [node['bridge']['dir'], node['bridge']['data']], owner: node['app']['user'], group: node['app']['group']) +Common.directories(self, [node['bridge']['dir'], node['bridge']['data']]) Common.packages(self, %w[unzip curl]) @@ -38,11 +38,16 @@ end if update_needed + if latest_version && ::File.exist?("/etc/systemd/system/zigbee2mqtt.service") + execute 'stop_zigbee2mqtt' do + command 'systemctl stop zigbee2mqtt || true' + action :run + end + end + Utils.snapshot(self, node['bridge']['data']) Utils.download(self, "/tmp/zigbee2mqtt.zip", - url: "https://github.com/Koenkk/zigbee2mqtt/archive/refs/tags/#{latest_version}.zip", - owner: node['app']['user'], group: node['app']['group']) - .notifies :stop, "service[zigbee2mqtt]", :immediately if latest_version and resources("service[zigbee2mqtt]") + url: "https://github.com/Koenkk/zigbee2mqtt/archive/refs/tags/#{latest_version}.zip") execute 'zigbee2mqtt_files' do command lazy { "unzip -o /tmp/zigbee2mqtt.zip -d #{node['bridge']['dir']} && mv #{node['bridge']['dir']}/zigbee2mqtt*/* #{node['bridge']['dir']}/ && rm -rf #{node['bridge']['dir']}/zigbee2mqtt*" } @@ -82,8 +87,7 @@ Utils.snapshot(self, node['bridge']['data'], restore: true) -Common.application(self, 'zigbee2mqtt', - user: node['app']['user'], cwd: node['bridge']['dir'], +Common.application(self, 'zigbee2mqtt', cwd: node['bridge']['dir'], exec: "/usr/bin/node #{node['bridge']['dir']}/index.js", unit: { 'Service' => { 'Environment' => 'NODE_ENV=production', 'PermissionsStartOnly' => 'true', 'ExecStartPre' => "-/bin/chown #{node['app']['user']}:#{node['app']['group']} #{node['bridge']['serial']}" } } ) diff --git a/libs/broker/recipes/default.rb b/libs/broker/recipes/default.rb index abc9bc9..4c23ab5 100644 --- a/libs/broker/recipes/default.rb +++ b/libs/broker/recipes/default.rb @@ -1,6 +1,6 @@ Env.dump(self, cookbook_name, repo: cookbook_name) -Common.directories(self, [node['broker']['dir']['data'], node['broker']['dir']['log']], owner: node['app']['user'], group: node['app']['group']) +Common.directories(self, [node['broker']['dir']['data'], node['broker']['dir']['log']]) Env.set(self, 'broker', "mqtt://#{node['ip']}:#{node['broker']['port']}") @@ -11,12 +11,8 @@ owner node['app']['user'] group node['app']['group'] mode '0644' - variables({ - port: node['broker']['port'], - data_dir: node['broker']['dir']['data'], - log_dir: node['broker']['dir']['log'], - user_file: node['broker']['file']['user'] - }) + variables({ port: node['broker']['port'], user_file: node['broker']['file']['user'], + data_dir: node['broker']['dir']['data'], log_dir: node['broker']['dir']['log'] }) notifies :restart, 'service[mosquitto]', :delayed end @@ -32,6 +28,6 @@ sensitive true end -Common.application(self, 'mosquitto', user: node['app']['user'], +Common.application(self, 'mosquitto', exec: "/usr/sbin/mosquitto -c #{node['broker']['file']['config']}", subscribe: "template[#{node['broker']['file']['config']}]" ) \ No newline at end of file diff --git a/libs/proxy/recipes/default.rb b/libs/proxy/recipes/default.rb index 252df0b..6a40274 100644 --- a/libs/proxy/recipes/default.rb +++ b/libs/proxy/recipes/default.rb @@ -1,6 +1,6 @@ Env.dump(self, cookbook_name, repo: cookbook_name) -Common.directories(self, [node['proxy']['dir']['app'], node['proxy']['dir']['logs']], owner: node['app']['user'], group: node['app']['group']) +Common.directories(self, [node['proxy']['dir']['app'], node['proxy']['dir']['logs']]) package 'caddy' @@ -24,10 +24,8 @@ group 'root' mode '0644' variables( - hosts: lazy { node.run_state['proxy_hosts'] || [] }, - log_dir: node['proxy']['dir']['logs'] - ) + log_dir: node['proxy']['dir']['logs'], hosts: lazy { node.run_state['proxy_hosts'] || [] } ) end -Common.application(self, 'caddy', user: node['app']['user'], +Common.application(self, 'caddy', subscribe: "template[#{node['proxy']['dir']['app']}/Caddyfile]" ) From d551865f5be89a7a2d515de774ebb902c93e7dcf Mon Sep 17 00:00:00 2001 From: Steven <106664555+stevius10@users.noreply.github.com> Date: Sun, 17 Aug 2025 12:37:48 +0200 Subject: [PATCH 09/13] dev/v1.1-generic-monkey-patching (#62) * app user for consistence reasons * assign variables in recipe * dynamic repo path to determine cookbook name later on * stop service if installed * positional arguments * use updated libraries * explicit arguments * restricted access not justifies to disable * safe log debug with even arguments * snapshot and restore * template needs to be updated * set cache dir temporary * generic monkey patching * handle exception properly * mask in collections * consistent monkey patching usage --- config/libraries/common.rb | 19 +------ config/libraries/constants.rb | 12 +++++ config/libraries/default.rb | 12 ++--- config/libraries/env.rb | 66 ++++++++++-------------- config/libraries/logs.rb | 83 +++++++++++++----------------- config/libraries/patch.rb | 58 +++++++++++++++++++++ config/libraries/utils.rb | 15 +++--- config/recipes/config.rb | 29 +++++------ config/recipes/repo.rb | 45 ++++++++-------- config/recipes/runner.rb | 29 ++++------- config/templates/repo_sync.yml.erb | 4 ++ libs/bridge/recipes/default.rb | 10 ++-- 12 files changed, 201 insertions(+), 181 deletions(-) create mode 100644 config/libraries/constants.rb create mode 100644 config/libraries/patch.rb diff --git a/config/libraries/common.rb b/config/libraries/common.rb index c94f711..eded496 100644 --- a/config/libraries/common.rb +++ b/config/libraries/common.rb @@ -40,7 +40,7 @@ def self.application(ctx, name, user: nil, group: nil, exec: nil, cwd: nil, unit group ||= Default.group(ctx) user = user.to_s group = group.to_s - if exec || !unit.empty? + if exec || unit.present? daemon(ctx, reload) service = { @@ -100,19 +100,4 @@ def self.sort_dir(dirs) Array(dirs).compact.sort_by { |d| -d.count('/') } end -end - -class Object - def blank? - respond_to?(:empty?) ? empty? : !self - end - - def presence - blank? ? nil : self - end -end - -class NilClass - def blank?; true; end - def presence; nil; end -end +end \ No newline at end of file diff --git a/config/libraries/constants.rb b/config/libraries/constants.rb new file mode 100644 index 0000000..b6ff880 --- /dev/null +++ b/config/libraries/constants.rb @@ -0,0 +1,12 @@ +module Constants + + HEADER_JSON = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' + }.freeze + + HEADER_FORM = { + 'Content-Type' => 'application/x-www-form-urlencoded' + }.freeze + +end \ No newline at end of file diff --git a/config/libraries/default.rb b/config/libraries/default.rb index effd384..ed65e5b 100644 --- a/config/libraries/default.rb +++ b/config/libraries/default.rb @@ -2,20 +2,20 @@ module Default def self.user(ctx, default: nil) node = Ctx.node(ctx) - @user ||= default.presence ? 'app' : or_default(Env.get(node, "app_user"), user(node, default: true)) + @user ||= default.presence ? 'app' : presence_or(Env.get(node, "app_user"), user(node, default: true)) end def self.group(ctx, default: nil) node = Ctx.node(ctx) - @group ||= default.presence ? 'config' : or_default(Env.get(node, "app_group"), group(node, default: true)) + @group ||= default.presence ? 'config' : presence_or(Env.get(node, "app_group"), group(node, default: true)) end def self.config(ctx, default: nil) node = Ctx.node(ctx) - @config ||= default.presence ? 'config' : or_default(Env.get(node, "app_config"), config(node, default: true)) + @config ||= default.presence ? 'config' : presence_or(Env.get(node, "app_config"), config(node, default: true)) end - def self.or_default(var, default) + def self.presence_or(var, default) var.to_s.presence || default.to_s end @@ -23,6 +23,6 @@ def self.or_default(var, default) module Ctx def self.node(obj) - obj.respond_to?(:node) ? obj.node : obj + obj.try(:node) || obj end -end +end \ No newline at end of file diff --git a/config/libraries/env.rb b/config/libraries/env.rb index 0abb244..a73fc90 100644 --- a/config/libraries/env.rb +++ b/config/libraries/env.rb @@ -12,70 +12,60 @@ def self.creds(ctx, login = 'login', password = 'password') end def self.get(ctx, key) - node = Ctx.node(ctx) - env_key = ENV[key.to_s.upcase] - return node[key] unless node[key].nil? || node[key].to_s.strip.empty? - return env_key unless env_key.nil? || env_key.to_s.strip.empty? - return get_variable(ctx, key) - rescue => e - return Logs.debug(:warn, "failed get '#{key}'", [:error, e.message], ctx: ctx) + Logs.try!("failed get '#{key}'") do + node = Ctx.node(ctx) + env_key = ENV[key.to_s.upcase] + return node[key] if node[key].present? + return env_key if env_key.present? + return get_variable(ctx, key) + end end def self.get_variable(ctx, key, repo: nil) - begin - response = request(ctx, key, repo: repo); response && response.body ? JSON.parse(response.body)['data'] : nil - rescue => e - Logs.debug(:warn, "failed get variable '#{key}'", [:error, e.message, :endpoint, endpoint(ctx), :repo, repo], ctx: ctx) + Logs.try!("failed get variable '#{key}'", [:repo, repo]) do + request(ctx, key, repo: repo, json: true)['data'] end end -def self.set_variable(ctx, key, val, repo: nil) - begin - request(Ctx.node(ctx), key, body: ({ name: key, value: val.to_s }.to_json), repo: repo, expect: true) - rescue => e - Logs.raise!("failed set variable '#{key}' to #{Logs.mask(val)}", [:val, val, :repo, repo], e: e) - end -end - - class << self - alias_method :set, :set_variable - end + def self.set_variable(ctx, key, val, repo: nil) + Logs.try!("failed set variable '#{key}' to #{val.mask}", [:val, val, :repo, repo], raise: true) do + request(Ctx.node(ctx), key, body: ({ name: key, value: val.to_s }.to_json), repo: repo, expect: true) + end + end; class << self; alias_method :set, :set_variable; end def self.endpoint(ctx) node = Ctx.node(ctx) - host = Default.or_default(node.dig('git', 'host', 'http'), "http://#{Default.or_default(Env.get(node, 'host'), '127.0.0.1')}:#{Default.or_default(node.dig('git', 'port', 'http'), 8080)}") - "#{host}/api/#{Default.or_default(node.dig('git', 'version'), 'v1')}" + host = Default.presence_or(node.dig('git', 'host', 'http'), "http://#{Default.presence_or(Env.get(node, 'host'), '127.0.0.1')}:#{Default.presence_or(node.dig('git', 'port', 'http'), 8080)}") + "#{host}/api/#{Default.presence_or(node.dig('git', 'version'), 'v1')}" end - def self.request(ctx, key, body: nil, repo: nil, expect: false) - node = Ctx.node(ctx) - user, pass = creds(node) - owner = Default.or_default(node.dig('git', 'org', 'main'), 'main') - uri = URI("#{endpoint(node)}/#{repo.to_s.strip.size>0 ? - "repos/#{owner}/#{repo.to_s}" : "orgs/#{owner}"}/actions/variables/#{key}") + def self.request(ctx, key, body: nil, repo: nil, expect: false, json: false) + user, pass = creds(ctx) + owner = Default.presence_or(ctx.dig('git', 'org', 'main'), 'main') + uri = URI("#{endpoint(ctx)}/#{repo.to_s.strip.size>0 ? "repos/#{owner}/#{repo.to_s}" : "orgs/#{owner}"}/actions/variables/#{key}") response = Utils.request(uri, user: user, pass: pass, headers: {}, method: Net::HTTP::Get, expect: (not body.nil? or expect), log: false) if not body.nil? method = (response ? Net::HTTP::Put : Net::HTTP::Post) - response = Utils.request(uri, user: user, pass: pass, headers: { 'Content-Type' => 'application/json' }, method: method, body: body, expect: expect, log: "(#{user}:#{Logs.mask(pass)})") + response = Utils.request(uri, user: user, pass: pass, headers: Constants::HEADER_JSON, method: method, body: body, expect: expect) end - response + return (json and not expect ? response.json : response) end def self.dump(ctx, *keys, repo: nil) - begin + Logs.try!("failed dump variables", [:repo, repo], raise: true) do node = Ctx.node(ctx) keys.flatten.each do |key| value = node[key] - next if value.nil? || (value.respond_to?(:empty?) && value.empty?) + next if value.blank? case value when Hash value.each do |subkey, subvalue| - next if subvalue.nil? || subvalue.to_s.strip.empty? + next if subvalue.blank? Env.set_variable(node, "#{key}_#{subkey}", subvalue, repo: repo) end when Array value.each_with_index do |item, i| - next if item.nil? || item.to_s.strip.empty? + next if item.blank? Env.set_variable(node, "#{key}_#{i}", item, repo: repo) end else @@ -83,9 +73,7 @@ def self.dump(ctx, *keys, repo: nil) end end true - rescue => e - Logs.raise!("failed dump variables", [:repo, repo], e: e) end end -end +end \ No newline at end of file diff --git a/config/libraries/logs.rb b/config/libraries/logs.rb index ddfa0eb..9261a6c 100644 --- a/config/libraries/logs.rb +++ b/config/libraries/logs.rb @@ -1,23 +1,7 @@ module Logs - FORMAT_WITH = "\e[1m[%s] %s (%s:%d)\e[0m" - FORMAT_NO = "%s (%s:%d)" - IGNORES = [__FILE__, %r{libraries}] - - def self.callsite - s = caller_locations(2,60) - s.find { |l| IGNORES.none? { |ig| ig.is_a?(Regexp) ? l.path =~ ig : l.path == ig } } || s.first - end - - def self.method_label(loc) - label = (loc.respond_to?(:label) ? loc.label : loc.to_s).sub(/block.*in /, '') - return nil if label == 'from_file' - label - end - - def self.log(level, msg, masks = []) + def self.log(msg, level: :info) c = callsite - masks.each { |m| msg = mask(msg, m) } label = method_label(c) if label Chef::Log.send(level, FORMAT_WITH % [label, msg, File.basename(c.path), c.lineno]) @@ -26,52 +10,55 @@ def self.log(level, msg, masks = []) end end - def self.info(msg); log(:info, msg) end - def self.warn(msg); log(:warn, msg) end - def self.error(msg); log(:error, msg) end + def self.info(msg); log(msg) end; def self.warn(msg); log(msg, level: :warn) end; def self.error(msg); log(msg, level: :error) end + def self.info?(msg, result: true); log(msg); result; end def self.request(uri, response); info("requested #{uri}: #{response&.code} #{response&.message}"); return response end - def self.assignment(key, val, mask=true); info("assigned '#{key}' value '#{mask ? mask(val) : val}'"); return val end - def self.return(msg); log(:info, msg.to_s); return msg end + def self.assignment(key, val, hide=true); info("assigned '#{key}' value '#{hide ? val.mask : val}'"); return val end + def self.return(msg); log(msg.to_s); return msg end - def self.debug(level, msg, *pairs, ctx: nil) + def self.debug(msg, *pairs, ctx: nil, level: :info) flat = pairs.flatten raise ArgumentError, "debug requires key value pairs (#{flat.length}: #{flat.inspect})" unless flat.length.even? input = flat.each_slice(2).to_h.transform_keys(&:to_s) - input['env'] = ENV.to_h - input['ctx'] = ctx.respond_to?(:to_h) ? ctx.to_h : ctx + input['env'] = ENV.to_h; input['ctx'] = ctx.respond_to?(:to_h) ? ctx.to_h : ctx payload = input.map { |k, v| "#{k}=#{v.inspect}" }.join(" ") - log(level, [msg, payload].reject { |s| s.nil? || s.empty? }.join(" ")) + log([msg, payload].reject { |s| s.blank? }.join(" "), level: level) end - def self.info?(msg) - log(:info, msg) - true + def self.try!(msg, *pairs, e:nil, ctx: nil, raise: false) + yield + rescue => e + debug("#{msg}: #{e.message}", *(pairs.flatten), ctx: ctx, level: (raise ? :error : :warn)) + raise("[#{method_label(callsite)}] #{e.message if e} #{msg}") if raise end - def self.raise!(msg, *pairs, e:nil, ctx: nil) - pairs = pairs.flatten - c = callsite - label = method_label(c) - debug(:error, msg, *pairs, ctx: ctx) - raise("[#{label}] #{e.message if e} #{msg}") + def self.request!(uri, response, valid=[], msg: nil, ctx: nil) + res = try!("failed request", [:uri, uri, :response, response]) do + if valid.presence # status code required + raise("[#{method_label(callsite)}] #{e.message if e} #{msg}") unless + (valid == true && response.is_a?(Net::HTTPSuccess) or valid.include?(response.code.to_i)) + end + response ? "#{response.code} #{response.message}" : response # status code irrelevant + end + debug("[#{msg}] responded #{res}", [:uri, uri, :response, response], ctx: ctx) + return response end - def self.request!(uri, response, msg="failed request", e: nil) - warn(msg) - c = callsite - label = method_label(c) - raise("[#{label}] #{e.message if e} #{msg} (#{uri}" + - response ? "#{response.code} #{response.message}" : "" ) - end + # Helper + + FORMAT_WITH = "\e[1m[%s] %s (%s:%d)\e[0m" + FORMAT_NO = "%s (%s:%d)" + IGNORES = [__FILE__, %r{libraries}] - def self.mask(str, term = nil) - return obfuscate(str) unless term - str.to_s.gsub(term.to_s, obfuscate(term.to_s)) rescue str + def self.callsite + s = caller_locations(2,60) + s.find { |l| IGNORES.none? { |ig| ig.is_a?(Regexp) ? l.path =~ ig : l.path == ig } } || s.first end - def self.obfuscate(s) - return s unless s.is_a?(String) - s.length <= 4 ? '*' * s.length : "#{s[0]}#{s[1]}#{'*'*(s.length-4)}#{s[-2]}#{s[-1]}" rescue s + def self.method_label(loc) + label = (loc.respond_to?(:label) ? loc.label : loc.to_s).sub(/block.*in /, '') + return nil if label == 'from_file' + label end end \ No newline at end of file diff --git a/config/libraries/patch.rb b/config/libraries/patch.rb new file mode 100644 index 0000000..8a5f341 --- /dev/null +++ b/config/libraries/patch.rb @@ -0,0 +1,58 @@ +require 'json' + +class Object + def blank?; respond_to?(:empty?) ? empty? : !self; end + def present?; !blank?; end + def presence; blank? ? nil : self; end + def presence_in(collection); return nil unless collection&.respond_to?(:include?); collection.include?(self) ? self : nil; end + def try(method_name = nil, *args, &block) + return nil if nil?; return instance_eval(&block) if block + return nil unless (method_name || respond_to?(method_name, true)) + public_send(method_name, *args) + end +end + +class NilClass + def blank?; true; end + def present?; false; end + def presence; nil; end +end + +# Data types + +class String + def blank?; strip.empty? end + def squish; strip.gsub(/\s+/, " ") end + def mask; (length <= 4) ? '*' * length : "#{self[0]}#{self[1]}#{'*' * (length - 4)}#{self[-2]}#{self[-1]}" ; end +end + +class Integer + def minutes; self * 60; end + def hours; self * 3600; end +end + +class Hash + def slice(*keys); keys.each_with_object({}) { |k,h| h[k] = self[k] if key?(k) }; end + def except(*keys); dup.tap { |h| keys.each { |k| h.delete(k) } }; end + def json(*a); JSON.generate(self, *a); end + def mask; JSON.generate(transform_values { |v| v.is_a?(String) ? v.mask : v.to_s }) end +end + +class Array + def mask; JSON.generate(map { |v| v.is_a?(String) ? v.mask : v.to_s }) end +end + +# Extension + +class Net::HTTPResponse + def json(symbolize_names: false, allow_blank: false, validate_content_type: false) + ct = self['content-type'] + return nil if validate_content_type && !(ct && ct.downcase.include?('application/json')) + s = body.to_s + return nil if allow_blank && s.strip.empty? + JSON.parse(s, symbolize_names: symbolize_names) + rescue + self + end + +end \ No newline at end of file diff --git a/config/libraries/utils.rb b/config/libraries/utils.rb index 6603749..5cf8ebe 100644 --- a/config/libraries/utils.rb +++ b/config/libraries/utils.rb @@ -109,22 +109,19 @@ def self.snapshot(ctx, dir, snapshot_dir: '/share/snapshots', name: ctx.cookbook end def self.proxmox(uri, ctx, path) - host = Env.get(ctx, 'proxmox_host') - user = Env.get(ctx, 'proxmox_user') - pass = Env.get(ctx, 'proxmox_password') - token = Env.get(ctx, 'proxmox_token') - secret = Env.get(ctx, 'proxmox_secret') + host = Env.get(ctx, 'proxmox_host'); user = Env.get(ctx, 'proxmox_user'); pass = Env.get(ctx, 'proxmox_password') + token = Env.get(ctx, 'proxmox_token'); secret = Env.get(ctx, 'proxmox_secret') url = "https://#{host}:8006/api2/json/#{path}" if pass && !pass.empty? response = request(uri="https://#{host}:8006/api2/json/access/ticket", method: Net::HTTP::Post, - body: URI.encode_www_form(username: user, password: pass), headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }) - Logs.request!(uri, response, "Proxmox ticket could not be retrieved") unless response.is_a?(Net::HTTPSuccess) - headers = { 'Cookie' => "PVEAuthCookie=#{JSON.parse(response.body)['data']['ticket']}" } + body: URI.encode_www_form(username: user, password: pass), headers: Constants::HEADER_FORM) + Logs.request!(uri, response, true, msg: "Proxmox ticket could not be retrieved") + headers = { 'Cookie' => "PVEAuthCookie=#{response.json['data']['ticket']}" } else headers = { 'Authorization' => "PVEAPIToken=#{user}!#{token}=#{secret}" } end - JSON.parse(request(url, headers: headers).body)['data'] + request(url, headers: headers).json['data'] end # Remote diff --git a/config/recipes/config.rb b/config/recipes/config.rb index fae3e19..2465b87 100644 --- a/config/recipes/config.rb +++ b/config/recipes/config.rb @@ -23,23 +23,23 @@ ruby_block 'config_set_key' do block do - require 'json' - url = "#{node['git']['api']['endpoint']}/admin/users/#{login}/keys" key = ::File.read("#{node['key']}.pub").strip + uri = "#{node['git']['api']['endpoint']}/user/keys" - (JSON.parse(Utils.request(url, user: login, pass: password).body) rescue []).each do |k| - Utils.request("#{url}/#{k['id']}", method: Net::HTTP::Delete, user: login, pass: password) if k['key'] && k['key'].strip == key + Utils.request(uri, user: login, pass: password).json.each do |k| + Utils.request("#{uri}/#{k['id']}", user: login, pass: password, + method: Net::HTTP::Delete) if k['key'] && k['key'].strip == key end - status_code = (response = Utils.request(url, body: { title: "config-#{login}", key: key }.to_json, - user: login, pass: password, method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' })).code.to_i - Logs.request!(url, response, "Set key failed") unless [201, 422].include?(status_code) + + response = Utils.request(uri, body: { title: login, key: key }.json, user: login, pass: password, method: Net::HTTP::Post, headers: Constants::HEADER_JSON) + Logs.request!(uri, response, [201, 422], msg: "set key") end only_if { ::File.exist?("#{node['key']}.pub") } not_if do next false unless ::File.exist?("#{node['key']}.pub") begin - response = Utils.request("#{node['git']['api']['endpoint']}/admin/users/#{login}/keys", user: login, pass: password) - (JSON.parse(response.body) rescue []).any? { |k| k['key'] && k['key'].strip == ::File.read("#{node['key']}.pub").strip } + response = Utils.request("#{node['git']['api']['endpoint']}/user/keys", user: login, pass: password) + response.json.any? { |k| k['key'] && k['key'].strip == ::File.read("#{node['key']}.pub").strip } end end end @@ -63,13 +63,10 @@ [node['git']['org']['main'], node['git']['org']['stage']].each do |org| ruby_block "config_git_org_#{org}" do block do - require 'json' - status_code = (response = Utils.request(uri="#{node['git']['api']['endpoint']}/orgs", - method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' }, - user: login, pass: password, - body: { username: org }.to_json - )).code.to_i - Logs.request!(uri, response, "Create organization '#{org}' failed") unless [201, 409, 422].include? status_code + (response = Utils.request(uri="#{node['git']['api']['endpoint']}/orgs", + method: Net::HTTP::Post, headers: Constants::HEADER_JSON, + body: { username: org }.json, user: login, pass: password, )) + Logs.request!(uri, response, [201, 409, 422], msg: "create organization '#{org}'") end end end diff --git a/config/recipes/repo.rb b/config/recipes/repo.rb index fa1772b..683a20b 100644 --- a/config/recipes/repo.rb +++ b/config/recipes/repo.rb @@ -25,8 +25,7 @@ block do node.run_state["#{name_repo}_repo_exists"] = (Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", - user: Env.get(self, 'login'), pass: Env.get(self, 'password')) - ).code.to_i != 404 + user: Env.get(self, 'login'), pass: Env.get(self, 'password'))).code.to_i != 404 end end @@ -41,16 +40,15 @@ fi EOH user node['app']['user'] - only_if { Logs.info("[#{repository} (#{name_repo})]: delete repository after snapshot") + only_if { Logs.info("[#{repository} (#{name_repo})] delete stored repository") node.run_state["#{name_repo}_repo_exists"] } end ruby_block "repo_exists_reset_#{name_repo}" do block do - unless [204, 404].include?(status_code = (response = Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", - method: Net::HTTP::Delete, user: Env.get(self, 'login'), pass: Env.get(self, 'password'))).code.to_i) - Logs.request!(uri, response, "Failed to delete #{name_repo}") - end + uri = "#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}" + response = Utils.request(uri, method: Net::HTTP::Delete, user: Env.get(self, 'login'), pass: Env.get(self, 'password')) + Logs.request!(uri, response, [204, 404], msg: "Delete #{name_repo}") end only_if { node.run_state["#{name_repo}_repo_exists"] } end @@ -58,13 +56,13 @@ ruby_block "repo_request_#{name_repo}" do only_if { Logs.info?("[#{repository} (#{name_repo})] create repository") } block do - require 'json' - (response = Utils.request( - uri="#{node['git']['api']['endpoint']}/admin/users/#{node['git']['org']['main']}/repos", - method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' }, + uri="#{node['git']['api']['endpoint']}/admin/users/#{node['git']['org']['main']}/repos" + response = Utils.request(uri, method: Net::HTTP::Post, headers: Constants::HEADER_JSON, user: Env.get(self, 'login'), pass: Env.get(self, 'password'), - body: { name: name_repo, private: false, auto_init: false, default_branch: 'main' }.to_json - )).code.to_i == 201 or Logs.request!(uri, response, "Error creating repository '#{name_repo}'") + body: { name: name_repo, private: false, auto_init: false, default_branch: 'main' }.json ) + + Logs.request!(uri, response, [201], msg: "Create repository '#{name_repo}'") + response.json end end @@ -163,7 +161,7 @@ .gsub(/(url\s*=\s*http:\/\/)([^:\/\s]+)/) { "#{$1}#{node['host']}" }) end end - end + end # Submodule handling in Monorepository submodules.each do |submodule| @@ -227,23 +225,22 @@ ruby_block "repo_stage_fork_clean_#{name_repo}" do block do - if Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}", - user: Env.get(self, 'login'), pass: Env.get(self, 'password')).code.to_i != 404 - status_code = (response = Utils.request(uri="#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['stage']}/#{name_repo}", - method: Net::HTTP::Delete, user: Env.get(self, 'login'), pass: Env.get(self, 'password'))).code.to_i - Logs.request!(uri, response, "Failed to clean test/#{name_repo} (#{status_code})") unless [204, 404].include?(status_code) + uri="#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}" + if Utils.request(uri, user: Env.get(self, 'login'), pass: Env.get(self, 'password')).code.to_i != 404 + response = Utils.request(uri="#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['stage']}/#{name_repo}", + method: Net::HTTP::Delete, user: Env.get(self, 'login'), pass: Env.get(self, 'password')) + Logs.request!(uri, response, [204, 404], msg: "Clean #{node['git']['org']['stage']}/#{name_repo}") end end end ruby_block "repo_stage_fork_create_#{name_repo}" do block do - status_code = Utils.request("#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}/forks", - method: Net::HTTP::Post, headers: { 'Content-Type' => 'application/json' }, + uri="#{node['git']['api']['endpoint']}/repos/#{node['git']['org']['main']}/#{name_repo}/forks" + Logs.request!(uri, Utils.request(uri, method: Net::HTTP::Post, headers: Constants::HEADER_JSON, user: Env.get(self, 'login'), pass: Env.get(self, 'password'), - body: { name: name_repo, organization: node['git']['org']['stage'] }.to_json - ).code.to_i - Logs.request!(uri, response, "Forking to #{node['git']['org']['stage']}/#{name_repo} failed") unless [201, 202].include?(status_code) + body: { name: name_repo, organization: node['git']['org']['stage'] }.json ), + [201, 202], msg: "Fork to #{node['git']['org']['stage']}/#{name_repo}") end end diff --git a/config/recipes/runner.rb b/config/recipes/runner.rb index 62c381e..cf4e689 100644 --- a/config/recipes/runner.rb +++ b/config/recipes/runner.rb @@ -18,34 +18,27 @@ ruby_block 'runner_register' do block do - require 'net/http' uri = URI("http://localhost:#{node['git']['port']['http']}") - connected = 15.times.any? do - begin - res = Utils.request(uri) - res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPRedirection) - rescue Errno::ECONNREFUSED, SocketError - false - ensure - sleep 5 - end + Logs.try!("Gitea not responding", [:uri, uri], raise: true) do + connected = 15.times.any? do + begin + res = Utils.request(uri, expect: true) + rescue Errno::ECONNREFUSED, SocketError + false + ensure; sleep 5; end + end unless connected end - 'Gitea not responding' unless connected (token = Mixlib::ShellOut.new( "#{node['git']['dir']['app']}/gitea actions --config #{node['git']['dir']['app']}/app.ini generate-runner-token", - user: node['app']['user'] - )).run_command + user: node['app']['user'])).run_command token.error! - token = token.stdout.strip (register = Mixlib::ShellOut.new( "#{node['runner']['dir']['app']}/act_runner register " \ "--instance http://localhost:#{node['git']['port']['http']} " \ - "--token #{token} " \ - "--no-interactive " \ - "--labels shell " \ - "--config #{node['runner']['dir']['app']}/config.yaml", + "--token #{token.stdout.strip} --no-interactive " \ + "--config #{node['runner']['dir']['app']}/config.yaml --labels #{node['runner']['conf']['label']} ", cwd: node['runner']['dir']['app'], user: node['app']['user'] )).run_command diff --git a/config/templates/repo_sync.yml.erb b/config/templates/repo_sync.yml.erb index a3a1794..a971581 100644 --- a/config/templates/repo_sync.yml.erb +++ b/config/templates/repo_sync.yml.erb @@ -1,3 +1,6 @@ +permissions: + contents: write + on: workflow_dispatch: push: @@ -17,6 +20,7 @@ jobs: - name: Sync submodule run: | cd monorepo + git remote set-url origin <%= @host %> git submodule update --remote git add $(git submodule status | awk '{print $2}') git diff --cached --quiet || git commit -m "[sync] main/$repo updated [skip ci]" diff --git a/libs/bridge/recipes/default.rb b/libs/bridge/recipes/default.rb index 6a6080c..b5b85a8 100644 --- a/libs/bridge/recipes/default.rb +++ b/libs/bridge/recipes/default.rb @@ -1,6 +1,6 @@ Env.dump(self, cookbook_name, repo: cookbook_name) -Common.directories(self, [node['bridge']['dir'], node['bridge']['data']]) +Utils.snapshot(self, node['bridge']['data']) Common.packages(self, %w[unzip curl]) @@ -19,8 +19,8 @@ package "nodejs" -execute 'enable_corepack' do - command 'corepack enable && corepack prepare pnpm --activate' +execute 'install_pnpm' do + command 'npm i -g pnpm@9' not_if 'which pnpm' end @@ -45,7 +45,8 @@ end end - Utils.snapshot(self, node['bridge']['data']) + Common.directories(self, [node['bridge']['dir'], node['bridge']['data']], recreate: true) + Utils.download(self, "/tmp/zigbee2mqtt.zip", url: "https://github.com/Koenkk/zigbee2mqtt/archive/refs/tags/#{latest_version}.zip") @@ -61,6 +62,7 @@ command 'pnpm install --frozen-lockfile && pnpm build' user node['app']['user'] group node['app']['group'] + environment('HOME' => '/tmp') cwd node['bridge']['dir'] action :nothing end From 3a3f2649dd932443072f23d737dfa6d284d7b80f Mon Sep 17 00:00:00 2001 From: Steven <106664555+stevius10@users.noreply.github.com> Date: Sun, 17 Aug 2025 13:40:33 +0200 Subject: [PATCH 10/13] fixed mask for generic object (#63) --- config/libraries/env.rb | 2 +- config/libraries/logs.rb | 1 - config/libraries/patch.rb | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/config/libraries/env.rb b/config/libraries/env.rb index a73fc90..bd59dc8 100644 --- a/config/libraries/env.rb +++ b/config/libraries/env.rb @@ -28,7 +28,7 @@ def self.get_variable(ctx, key, repo: nil) end def self.set_variable(ctx, key, val, repo: nil) - Logs.try!("failed set variable '#{key}' to #{val.mask}", [:val, val, :repo, repo], raise: true) do + Logs.try!("failed set variable '#{key}' to #{val.try(:mask) || val}", [:val, val, :repo, repo], raise: true) do request(Ctx.node(ctx), key, body: ({ name: key, value: val.to_s }.to_json), repo: repo, expect: true) end end; class << self; alias_method :set, :set_variable; end diff --git a/config/libraries/logs.rb b/config/libraries/logs.rb index 9261a6c..8b5e25a 100644 --- a/config/libraries/logs.rb +++ b/config/libraries/logs.rb @@ -13,7 +13,6 @@ def self.log(msg, level: :info) def self.info(msg); log(msg) end; def self.warn(msg); log(msg, level: :warn) end; def self.error(msg); log(msg, level: :error) end def self.info?(msg, result: true); log(msg); result; end def self.request(uri, response); info("requested #{uri}: #{response&.code} #{response&.message}"); return response end - def self.assignment(key, val, hide=true); info("assigned '#{key}' value '#{hide ? val.mask : val}'"); return val end def self.return(msg); log(msg.to_s); return msg end def self.debug(msg, *pairs, ctx: nil, level: :info) diff --git a/config/libraries/patch.rb b/config/libraries/patch.rb index 8a5f341..edac41a 100644 --- a/config/libraries/patch.rb +++ b/config/libraries/patch.rb @@ -7,7 +7,7 @@ def presence; blank? ? nil : self; end def presence_in(collection); return nil unless collection&.respond_to?(:include?); collection.include?(self) ? self : nil; end def try(method_name = nil, *args, &block) return nil if nil?; return instance_eval(&block) if block - return nil unless (method_name || respond_to?(method_name, true)) + return nil unless method_name && respond_to?(method_name, true) public_send(method_name, *args) end end From e3a8625c277188f90ad31adad17b676e9ecc0f43 Mon Sep 17 00:00:00 2001 From: Steven <106664555+stevius10@users.noreply.github.com> Date: Sun, 17 Aug 2025 17:50:09 +0200 Subject: [PATCH 11/13] fix/library-usage (#64) * fixed snapshot function by adjusting library usage * correct extraction * adopt uri in sum * add comment for purpose * safe string return * stricter directories --- config/libraries/common.rb | 10 ++--- config/libraries/default.rb | 6 +-- config/libraries/logs.rb | 4 +- config/libraries/utils.rb | 66 ++++++++++++------------------ config/recipes/repo.rb | 2 +- config/templates/repo_sync.yml.erb | 4 +- libs/bridge/recipes/default.rb | 18 ++++---- 7 files changed, 51 insertions(+), 59 deletions(-) diff --git a/config/libraries/common.rb b/config/libraries/common.rb index eded496..47794be 100644 --- a/config/libraries/common.rb +++ b/config/libraries/common.rb @@ -11,12 +11,12 @@ def self.packages(ctx, *pkgs, action: :install) end def self.directories(ctx, dirs, opts = {}) - dirs = Array(dirs) - owner = opts[:owner] || Default.user(ctx) - group = opts[:group] || Default.group(ctx) - mode = opts[:mode] || '0755' + dirs = Array(dirs).compact.uniq + owner = opts[:owner] || Default.user(ctx) + group = opts[:group] || Default.group(ctx) + mode = opts[:mode] || '0755' recursive = true - recreate = opts[:recreate] || false + recreate = !!opts[:recreate] || false if recreate sort_dir(dirs).each { |dir| delete_dir(ctx, dir) } diff --git a/config/libraries/default.rb b/config/libraries/default.rb index ed65e5b..661e67c 100644 --- a/config/libraries/default.rb +++ b/config/libraries/default.rb @@ -2,17 +2,17 @@ module Default def self.user(ctx, default: nil) node = Ctx.node(ctx) - @user ||= default.presence ? 'app' : presence_or(Env.get(node, "app_user"), user(node, default: true)) + @user ||= (default.presence ? 'app' : presence_or(Env.get(node, "app_user"), user(node, default: true))).to_s end def self.group(ctx, default: nil) node = Ctx.node(ctx) - @group ||= default.presence ? 'config' : presence_or(Env.get(node, "app_group"), group(node, default: true)) + @group ||= (default.presence ? 'config' : presence_or(Env.get(node, "app_group"), group(node, default: true))).to_s end def self.config(ctx, default: nil) node = Ctx.node(ctx) - @config ||= default.presence ? 'config' : presence_or(Env.get(node, "app_config"), config(node, default: true)) + @config ||= (default.presence ? 'config' : presence_or(Env.get(node, "app_config"), config(node, default: true))).to_s end def self.presence_or(var, default) diff --git a/config/libraries/logs.rb b/config/libraries/logs.rb index 8b5e25a..304eaca 100644 --- a/config/libraries/logs.rb +++ b/config/libraries/logs.rb @@ -33,11 +33,11 @@ def self.try!(msg, *pairs, e:nil, ctx: nil, raise: false) def self.request!(uri, response, valid=[], msg: nil, ctx: nil) res = try!("failed request", [:uri, uri, :response, response]) do - if valid.presence # status code required + if valid.presence # if valid status code required raise("[#{method_label(callsite)}] #{e.message if e} #{msg}") unless (valid == true && response.is_a?(Net::HTTPSuccess) or valid.include?(response.code.to_i)) end - response ? "#{response.code} #{response.message}" : response # status code irrelevant + response ? "#{response.code} #{response.message}" : response end debug("[#{msg}] responded #{res}", [:uri, uri, :response, response], ctx: ctx) return response diff --git a/config/libraries/utils.rb b/config/libraries/utils.rb index 5cf8ebe..2839495 100644 --- a/config/libraries/utils.rb +++ b/config/libraries/utils.rb @@ -64,47 +64,35 @@ def self.arch(ctx) end def self.snapshot(ctx, dir, snapshot_dir: '/share/snapshots', name: ctx.cookbook_name, restore: false) - begin - timestamp = Time.now.strftime('%H%M-%d%m%y') - snapshot = File.join(snapshot_dir, name, "#{name}-#{timestamp}.tar.gz") - - md5_dir = ->(path) { Digest::MD5.new.tap do |md5| - Dir.glob("#{path}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }.sort.each do |f| - md5.update(File.read(f)) - end - end.hexdigest } - verify = ->(archive, compare_dir) { - Dir.mktmpdir do |tmp| - Logs.raise!("extraction failed for '#{archive}'") unless system("tar -xzf #{archive} -C #{tmp}") - md5_base = md5_dir.(File.join(tmp, File.basename(compare_dir))) - md5_compare = md5_dir.(compare_dir) - Logs.raise!("verify snapshot failed",[:base, File.join(tmp, File.basename(compare_dir)), - :compare, compare_dir, :md5_base, md5_base, :md5_compare, md5_compare] - ) unless md5_base == md5_compare - end; true } - - FileUtils.mkdir_p(File.dirname(snapshot)) - latest = Dir[File.join(snapshot_dir, name, "#{name}*.tar.gz")].max_by { |f| File.mtime(f) } - if restore - if latest && ::File.exist?(latest) - system("tar -czf #{snapshot} -C #{File.dirname(dir)} #{File.basename(dir)}") or - Logs.raise!("snapshot restore failed", [:dir, dir, :snapshot, snapshot, :dir, dir], e: e) - return verify.(latest, dir) - end - return true # initial - - else - if Dir.exists?(dir) - system("tar -czf #{snapshot} -C #{File.dirname(dir)} #{File.basename(dir)}") or - Logs.raise!("snapshot creation failed", [:dir, dir, :snapshot, snapshot, :dir, dir], e: e) - return verify.(snapshot, dir) - else - return true # initial + timestamp = Time.now.strftime('%H%M-%d%m%y') + snapshot = File.join(snapshot_dir, name, "#{name}-#{timestamp}.tar.gz") + md5_dir = ->(path) { Digest::MD5.new.tap { |md5| Dir.glob("#{path}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }.sort.each { |f| md5.update(File.read(f)) } }.hexdigest } + verify = ->(archive, compare_dir) { + Dir.mktmpdir do |tmp| + Logs.try!("snapshot extraction", [:archive, archive, :tmp, tmp], raise: true) do + system("tar -xzf #{archive} -C #{tmp}") or raise("tar failed") end + md5_base = md5_dir.(File.join(tmp, File.basename(compare_dir))) + md5_compare = md5_dir.(compare_dir) if Dir.exist?(compare_dir) + md5_compare = '' unless Dir.exist?(compare_dir) + raise("verify snapshot failed") unless md5_base == md5_compare + end; true + } + FileUtils.mkdir_p(File.dirname(snapshot)) + if restore + FileUtils.rm_rf(dir); FileUtils.mkdir_p(File.dirname(dir)) + latest = Dir[File.join(snapshot_dir, name, "#{name}*.tar.gz")].max_by { |f| File.mtime(f) } + return true unless latest && ::File.exist?(latest) + Logs.try!("snapshot restore", [:dir, dir, :archive, latest], raise: true) do + system("tar -xzf #{latest} -C #{File.dirname(dir)}") or raise("tar extract failed") + end + return verify.(latest, dir) + else + return true unless Dir.exist?(dir) + Logs.try!("snapshot creation", [:dir, dir, :snapshot, snapshot], raise: true) do + system("tar -czf #{snapshot} -C #{File.dirname(dir)} #{File.basename(dir)}") or raise("tar compress failed") end - rescue => e - action = restore ? 'restore' : 'create' - Logs.raise!("error in snapshot", [:action, action, :dir, dir, :snapshot, snapshot], e: e) + return verify.(snapshot, dir) end end diff --git a/config/recipes/repo.rb b/config/recipes/repo.rb index 683a20b..bae35a4 100644 --- a/config/recipes/repo.rb +++ b/config/recipes/repo.rb @@ -134,7 +134,7 @@ owner node['app']['user'] group node['app']['group'] mode '0644' - variables(host: node['host']) + variables(host: node['host'], org: node['git']['org']['main'], name: cookbook_name) not_if { monorepo } end diff --git a/config/templates/repo_sync.yml.erb b/config/templates/repo_sync.yml.erb index a971581..ceecde3 100644 --- a/config/templates/repo_sync.yml.erb +++ b/config/templates/repo_sync.yml.erb @@ -13,14 +13,14 @@ jobs: - name: Checkout monorepo uses: https://gitea.com/actions/checkout@v4 with: - repository: 'main/config' + repository: <%= @org %>/<%= @name %>' ref: 'main' path: 'monorepo' - name: Sync submodule run: | cd monorepo - git remote set-url origin <%= @host %> + git remote set-url origin <%= @host %>/<%= @org %>/<%= @name %> git submodule update --remote git add $(git submodule status | awk '{print $2}') git diff --cached --quiet || git commit -m "[sync] main/$repo updated [skip ci]" diff --git a/libs/bridge/recipes/default.rb b/libs/bridge/recipes/default.rb index b5b85a8..134116d 100644 --- a/libs/bridge/recipes/default.rb +++ b/libs/bridge/recipes/default.rb @@ -37,6 +37,8 @@ update_needed = installed_version.nil? || Gem::Version.new(latest_version) > Gem::Version.new(installed_version) end +Common.directories(self, [node['bridge']['dir'], node['bridge']['data']], recreate: update_needed) + if update_needed if latest_version && ::File.exist?("/etc/systemd/system/zigbee2mqtt.service") execute 'stop_zigbee2mqtt' do @@ -45,8 +47,6 @@ end end - Common.directories(self, [node['bridge']['dir'], node['bridge']['data']], recreate: true) - Utils.download(self, "/tmp/zigbee2mqtt.zip", url: "https://github.com/Koenkk/zigbee2mqtt/archive/refs/tags/#{latest_version}.zip") @@ -87,9 +87,13 @@ only_if { latest_version && !::File.exist?("#{node['bridge']['data']}/configuration.yaml") } end -Utils.snapshot(self, node['bridge']['data'], restore: true) +ruby_block "restore_snapshot_if_exists" do + block { Utils.snapshot(self, node['bridge']['data'], restore: true) } +end -Common.application(self, 'zigbee2mqtt', cwd: node['bridge']['dir'], - exec: "/usr/bin/node #{node['bridge']['dir']}/index.js", - unit: { 'Service' => { 'Environment' => 'NODE_ENV=production', 'PermissionsStartOnly' => 'true', - 'ExecStartPre' => "-/bin/chown #{node['app']['user']}:#{node['app']['group']} #{node['bridge']['serial']}" } } ) +ruby_block cookbook_name do + Common.application(self, 'zigbee2mqtt', cwd: node['bridge']['dir'], + exec: "/usr/bin/node #{node['bridge']['dir']}/index.js", + unit: { 'Service' => { 'Environment' => 'NODE_ENV=production', 'PermissionsStartOnly' => 'true', + 'ExecStartPre' => "-/bin/chown #{node['app']['user']}:#{node['app']['group']} #{node['bridge']['serial']}" } } ) +end From 27ef12cb1d0a90fa780f8d20e54964dd2dfdb6e8 Mon Sep 17 00:00:00 2001 From: Steven <106664555+stevius10@users.noreply.github.com> Date: Sun, 17 Aug 2025 17:54:21 +0200 Subject: [PATCH 12/13] fix/lib-related-ref (#65) * fixed snapshot function by adjusting library usage * correct extraction * adopt uri in sum * add comment for purpose * safe string return * stricter directories * sync must be on behalf * improved context module * context usage with updated methods * use find to trigger service reload * fix block enclosing * sync must be on behalf * improved context module * context usage with updated methods * use find to trigger service reload * fix block enclosing --- config/libraries/common.rb | 14 ++++++------- config/libraries/default.rb | 33 +++++++++++++++++++++++++++++- config/recipes/repo.rb | 9 -------- config/templates/repo_sync.yml.erb | 29 -------------------------- libs/bridge/recipes/default.rb | 11 ++++++---- 5 files changed, 46 insertions(+), 50 deletions(-) delete mode 100644 config/templates/repo_sync.yml.erb diff --git a/config/libraries/common.rb b/config/libraries/common.rb index 47794be..c8b1562 100644 --- a/config/libraries/common.rb +++ b/config/libraries/common.rb @@ -27,11 +27,9 @@ def self.directories(ctx, dirs, opts = {}) # System def self.daemon(ctx, name) - ctx.find_resource!(:execute, name) - rescue Chef::Exceptions::ResourceNotFound - ctx.execute name do + Ctx.find(ctx, :execute, name) do command 'systemctl daemon-reload' - action :nothing + action :nothing end end @@ -69,19 +67,21 @@ def self.application(ctx, name, user: nil, group: nil, exec: nil, cwd: nil, unit "[#{section}]\n#{lines}" end.join("\n\n") - ctx.file "/etc/systemd/system/#{name}.service" do + Ctx.dsl(ctx).file "/etc/systemd/system/#{name}.service" do owner 'root' group 'root' mode '0644' content unit_content - notifies :run, "execute[#{reload}]", :immediately end + Ctx.find(ctx, :execute, reload) + end - ctx.service name do + Ctx.dsl(ctx).service name do action action Array(subscribe).flatten.each { |ref| subscribes :restart, ref, :delayed } if subscribe end + end def self.create_dir(ctx, dir, owner, group, mode, recursive) diff --git a/config/libraries/default.rb b/config/libraries/default.rb index 661e67c..70aaac6 100644 --- a/config/libraries/default.rb +++ b/config/libraries/default.rb @@ -22,7 +22,38 @@ def self.presence_or(var, default) end module Ctx + def self.node(obj) - obj.try(:node) || obj + return obj.node if defined?(Context) && obj.is_a?(Context) + return obj.node if obj.respond_to?(:node) + if obj.respond_to?(:run_context) && obj.run_context && obj.run_context.respond_to?(:node) + return obj.run_context.node + end + obj + end + + def self.dsl(obj) + return obj if obj.respond_to?(:file) + rctx = rc(obj) + return obj unless rctx + cb = obj.respond_to?(:cookbook_name) ? obj.cookbook_name : nil + rn = obj.respond_to?(:recipe_name) ? obj.recipe_name : nil + Chef::Recipe.new(cb, rn, rctx) + end + + def self.rc(obj) + return obj.run_context if obj.respond_to?(:run_context) + return obj if defined?(Chef::RunContext) && obj.is_a?(Chef::RunContext) + nil + end + + def self.find(obj, type, name) + rctx = rc(obj) + return nil unless rctx && rctx.respond_to?(:resource_collection) + rctx.resource_collection.find("#{type}[#{name}]") + dsl(obj).public_send(type, name, &block) + rescue Chef::Exceptions::ResourceNotFound + nil end + end \ No newline at end of file diff --git a/config/recipes/repo.rb b/config/recipes/repo.rb index bae35a4..30b4cdb 100644 --- a/config/recipes/repo.rb +++ b/config/recipes/repo.rb @@ -129,15 +129,6 @@ recursive true end - template "#{path_destination}/.gitea/workflows/sync.yml" do - source 'repo_sync.yml.erb' - owner node['app']['user'] - group node['app']['group'] - mode '0644' - variables(host: node['host'], org: node['git']['org']['main'], name: cookbook_name) - not_if { monorepo } - end - template "#{path_destination}/.gitea/workflows/pipeline.yml" do source 'repo_pipeline.yml.erb' owner node['app']['user'] diff --git a/config/templates/repo_sync.yml.erb b/config/templates/repo_sync.yml.erb deleted file mode 100644 index ceecde3..0000000 --- a/config/templates/repo_sync.yml.erb +++ /dev/null @@ -1,29 +0,0 @@ -permissions: - contents: write - -on: - workflow_dispatch: - push: - branches: [ main ] - -jobs: - include: - runs-on: shell - steps: - - name: Checkout monorepo - uses: https://gitea.com/actions/checkout@v4 - with: - repository: <%= @org %>/<%= @name %>' - ref: 'main' - path: 'monorepo' - - - name: Sync submodule - run: | - cd monorepo - git remote set-url origin <%= @host %>/<%= @org %>/<%= @name %> - git submodule update --remote - git add $(git submodule status | awk '{print $2}') - git diff --cached --quiet || git commit -m "[sync] main/$repo updated [skip ci]" - git push origin main - env: - repo: ${{ gitea.repository }} \ No newline at end of file diff --git a/libs/bridge/recipes/default.rb b/libs/bridge/recipes/default.rb index 134116d..aeb8881 100644 --- a/libs/bridge/recipes/default.rb +++ b/libs/bridge/recipes/default.rb @@ -92,8 +92,11 @@ end ruby_block cookbook_name do - Common.application(self, 'zigbee2mqtt', cwd: node['bridge']['dir'], - exec: "/usr/bin/node #{node['bridge']['dir']}/index.js", - unit: { 'Service' => { 'Environment' => 'NODE_ENV=production', 'PermissionsStartOnly' => 'true', - 'ExecStartPre' => "-/bin/chown #{node['app']['user']}:#{node['app']['group']} #{node['bridge']['serial']}" } } ) + block do + Common.application(self, 'zigbee2mqtt', cwd: node['bridge']['dir'], + exec: "/usr/bin/node #{node['bridge']['dir']}/index.js", + unit: { 'Service' => { 'Environment' => 'NODE_ENV=production', 'PermissionsStartOnly' => 'true', + 'ExecStartPre' => "-/bin/chown #{node['app']['user']}:#{node['app']['group']} #{node['bridge']['serial']}" } } ) + end + action :run end From 8cbee9f3eab08eb6e7b6bec156d6f51603865630 Mon Sep 17 00:00:00 2001 From: Steven <106664555+stevius10@users.noreply.github.com> Date: Mon, 18 Aug 2025 21:26:52 +0200 Subject: [PATCH 13/13] dev/minor-lib-fixes (#68) * yaml anchor for retry after os download * find with reference * attributes to be passed with node * call from within recipe * get variable called with node * log function to be better readable in code usage * safer snapshot handling * removed unused --- base/roles/container/tasks/create.yml | 4 ++- config/libraries/default.rb | 12 ++++---- config/libraries/env.rb | 20 +++++++------- config/libraries/logs.rb | 2 +- config/libraries/utils.rb | 40 +++++++++++++++------------ libs/bridge/attributes/default.rb | 4 +-- libs/bridge/recipes/default.rb | 23 +++++++-------- 7 files changed, 56 insertions(+), 49 deletions(-) diff --git a/base/roles/container/tasks/create.yml b/base/roles/container/tasks/create.yml index 989d982..688fd68 100644 --- a/base/roles/container/tasks/create.yml +++ b/base/roles/container/tasks/create.yml @@ -27,7 +27,7 @@ - mount | trim | length > 0 - name: Create container - community.general.proxmox: + community.general.proxmox: &create_container vmid: "{{ id }}" hostname: "{{ hostname }}" ostemplate: "{{ os }}" @@ -56,4 +56,6 @@ - name: Retry create container with os downloaded community.general.proxmox: + <<: *create_container + register: container_creation when: os_available is defined and os in (os_available.json.data | map(attribute='volid') | list) diff --git a/config/libraries/default.rb b/config/libraries/default.rb index 70aaac6..6bb17ea 100644 --- a/config/libraries/default.rb +++ b/config/libraries/default.rb @@ -47,13 +47,15 @@ def self.rc(obj) nil end - def self.find(obj, type, name) + def self.find(obj, type, name, &block) rctx = rc(obj) - return nil unless rctx && rctx.respond_to?(:resource_collection) - rctx.resource_collection.find("#{type}[#{name}]") + if rctx && rctx.respond_to?(:resource_collection) + begin + return rctx.resource_collection.find("#{type}[#{name}]") + rescue Chef::Exceptions::ResourceNotFound + end + end dsl(obj).public_send(type, name, &block) - rescue Chef::Exceptions::ResourceNotFound - nil end end \ No newline at end of file diff --git a/config/libraries/env.rb b/config/libraries/env.rb index bd59dc8..aaa65a1 100644 --- a/config/libraries/env.rb +++ b/config/libraries/env.rb @@ -12,7 +12,7 @@ def self.creds(ctx, login = 'login', password = 'password') end def self.get(ctx, key) - Logs.try!("failed get '#{key}'") do + Logs.try!("get '#{key}'") do node = Ctx.node(ctx) env_key = ENV[key.to_s.upcase] return node[key] if node[key].present? @@ -22,13 +22,13 @@ def self.get(ctx, key) end def self.get_variable(ctx, key, repo: nil) - Logs.try!("failed get variable '#{key}'", [:repo, repo]) do - request(ctx, key, repo: repo, json: true)['data'] + Logs.try!("get variable '#{key}'", [:repo, repo]) do + request(Ctx.node(ctx), key, repo: repo).json['data'] end end def self.set_variable(ctx, key, val, repo: nil) - Logs.try!("failed set variable '#{key}' to #{val.try(:mask) || val}", [:val, val, :repo, repo], raise: true) do + Logs.try!("set variable '#{key}' to #{val.try(:mask) || val}", [:val, val, :repo, repo], raise: true) do request(Ctx.node(ctx), key, body: ({ name: key, value: val.to_s }.to_json), repo: repo, expect: true) end end; class << self; alias_method :set, :set_variable; end @@ -36,23 +36,23 @@ def self.set_variable(ctx, key, val, repo: nil) def self.endpoint(ctx) node = Ctx.node(ctx) host = Default.presence_or(node.dig('git', 'host', 'http'), "http://#{Default.presence_or(Env.get(node, 'host'), '127.0.0.1')}:#{Default.presence_or(node.dig('git', 'port', 'http'), 8080)}") - "#{host}/api/#{Default.presence_or(node.dig('git', 'version'), 'v1')}" + "#{host}/api/#{Default.presence_or(node.dig('git', 'api', 'version'), 'v1')}" end - def self.request(ctx, key, body: nil, repo: nil, expect: false, json: false) + def self.request(ctx, key, body: nil, repo: nil, expect: false) user, pass = creds(ctx) owner = Default.presence_or(ctx.dig('git', 'org', 'main'), 'main') uri = URI("#{endpoint(ctx)}/#{repo.to_s.strip.size>0 ? "repos/#{owner}/#{repo.to_s}" : "orgs/#{owner}"}/actions/variables/#{key}") - response = Utils.request(uri, user: user, pass: pass, headers: {}, method: Net::HTTP::Get, expect: (not body.nil? or expect), log: false) - if not body.nil? + response = Utils.request(uri, user: user, pass: pass, headers: {}, method: Net::HTTP::Get, expect: (body.present? or expect), log: false) + if body.present? method = (response ? Net::HTTP::Put : Net::HTTP::Post) response = Utils.request(uri, user: user, pass: pass, headers: Constants::HEADER_JSON, method: method, body: body, expect: expect) end - return (json and not expect ? response.json : response) + return response end def self.dump(ctx, *keys, repo: nil) - Logs.try!("failed dump variables", [:repo, repo], raise: true) do + Logs.try!("dump variables", [:repo, repo], raise: true) do node = Ctx.node(ctx) keys.flatten.each do |key| value = node[key] diff --git a/config/libraries/logs.rb b/config/libraries/logs.rb index 304eaca..9f2b7de 100644 --- a/config/libraries/logs.rb +++ b/config/libraries/logs.rb @@ -27,7 +27,7 @@ def self.debug(msg, *pairs, ctx: nil, level: :info) def self.try!(msg, *pairs, e:nil, ctx: nil, raise: false) yield rescue => e - debug("#{msg}: #{e.message}", *(pairs.flatten), ctx: ctx, level: (raise ? :error : :warn)) + debug("failed: #{msg}: #{e.message}", *(pairs.flatten), ctx: ctx, level: (raise ? :error : :warn)) raise("[#{method_label(callsite)}] #{e.message if e} #{msg}") if raise end diff --git a/config/libraries/utils.rb b/config/libraries/utils.rb index 2839495..eba3ac4 100644 --- a/config/libraries/utils.rb +++ b/config/libraries/utils.rb @@ -3,6 +3,7 @@ require 'json' require 'net/http' require 'openssl' +require 'shellwords' require 'socket' require 'timeout' require 'tmpdir' @@ -66,34 +67,39 @@ def self.arch(ctx) def self.snapshot(ctx, dir, snapshot_dir: '/share/snapshots', name: ctx.cookbook_name, restore: false) timestamp = Time.now.strftime('%H%M-%d%m%y') snapshot = File.join(snapshot_dir, name, "#{name}-#{timestamp}.tar.gz") - md5_dir = ->(path) { Digest::MD5.new.tap { |md5| Dir.glob("#{path}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }.sort.each { |f| md5.update(File.read(f)) } }.hexdigest } + md5_dir = ->(path) { + entries = Dir.glob("#{path}/**/*", File::FNM_DOTMATCH) + files = entries.reject { |f| File.directory?(f) || File.basename(f).start_with?('._') } + Digest::MD5.new.tap { |md5| files.sort.each { |f| File.open(f, 'rb') { |io| md5.update(io.read) } } }.hexdigest } verify = ->(archive, compare_dir) { Dir.mktmpdir do |tmp| Logs.try!("snapshot extraction", [:archive, archive, :tmp, tmp], raise: true) do - system("tar -xzf #{archive} -C #{tmp}") or raise("tar failed") + system("tar -xzf #{Shellwords.escape(archive)} -C #{Shellwords.escape(tmp)}") or raise("tar failed") end - md5_base = md5_dir.(File.join(tmp, File.basename(compare_dir))) - md5_compare = md5_dir.(compare_dir) if Dir.exist?(compare_dir) - md5_compare = '' unless Dir.exist?(compare_dir) + roots = Dir.children(tmp).map { |e| File.join(tmp, e) }.select { |p| File.directory?(p) } + base = roots.size == 1 ? roots.first : File.join(tmp, File.basename(compare_dir)) + base = roots.first unless Dir.exist?(base) + md5_base = md5_dir.(base) + md5_compare = Dir.exist?(compare_dir) ? md5_dir.(compare_dir) : '' raise("verify snapshot failed") unless md5_base == md5_compare - end; true + end + true } - FileUtils.mkdir_p(File.dirname(snapshot)) if restore - FileUtils.rm_rf(dir); FileUtils.mkdir_p(File.dirname(dir)) latest = Dir[File.join(snapshot_dir, name, "#{name}*.tar.gz")].max_by { |f| File.mtime(f) } - return true unless latest && ::File.exist?(latest) + return true unless latest && ::File.exist?(latest) # no snapshot available + FileUtils.rm_rf(dir) + FileUtils.mkdir_p(File.dirname(dir)) Logs.try!("snapshot restore", [:dir, dir, :archive, latest], raise: true) do - system("tar -xzf #{latest} -C #{File.dirname(dir)}") or raise("tar extract failed") - end - return verify.(latest, dir) - else - return true unless Dir.exist?(dir) - Logs.try!("snapshot creation", [:dir, dir, :snapshot, snapshot], raise: true) do - system("tar -czf #{snapshot} -C #{File.dirname(dir)} #{File.basename(dir)}") or raise("tar compress failed") + system("tar -xzf #{Shellwords.escape(latest)} -C #{Shellwords.escape(File.dirname(dir))}") or raise("tar extract failed") end - return verify.(snapshot, dir) end + return true unless Dir.exist?(dir) # true for convenience in idempotency + FileUtils.mkdir_p(File.dirname(snapshot)) + Logs.try!("snapshot creation", [:dir, dir, :snapshot, snapshot], raise: true) do + system("tar -czf #{Shellwords.escape(snapshot)} -C #{Shellwords.escape(File.dirname(dir))} #{Shellwords.escape(File.basename(dir))}") or raise("tar compress failed") + end + return verify.(snapshot, dir) end def self.proxmox(uri, ctx, path) diff --git a/libs/bridge/attributes/default.rb b/libs/bridge/attributes/default.rb index 6eafb2a..e59d6c7 100644 --- a/libs/bridge/attributes/default.rb +++ b/libs/bridge/attributes/default.rb @@ -2,8 +2,8 @@ default['app']['group'] = Default.group(node) default['bridge']['port'] = 8080 -default['bridge']['adapter'] = Env.get(self, 'adapter') || 'zstack' # overwrite -default['bridge']['serial'] = Env.get(self, 'serial') || '/dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_1c27822ced5fec11a1d52e5f25bfaa52-if00-port0' +default['bridge']['adapter'] = Env.get(node, 'adapter') || 'zstack' # overwrite +default['bridge']['serial'] = Env.get(node, 'serial') || '/dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_1c27822ced5fec11a1d52e5f25bfaa52-if00-port0' default['bridge']['dir'] = '/app/bridge' default['bridge']['data'] = "#{node['bridge']['dir']}/data" diff --git a/libs/bridge/recipes/default.rb b/libs/bridge/recipes/default.rb index aeb8881..e193367 100644 --- a/libs/bridge/recipes/default.rb +++ b/libs/bridge/recipes/default.rb @@ -1,6 +1,8 @@ Env.dump(self, cookbook_name, repo: cookbook_name) -Utils.snapshot(self, node['bridge']['data']) +login = Env.get(self, 'login') +password = Env.get(self, 'password') +broker = Env.get(self, 'broker') Common.packages(self, %w[unzip curl]) @@ -80,9 +82,9 @@ adapter: node['bridge']['adapter'], data_dir: node['bridge']['data'], logs_dir: node['bridge']['logs'], - broker_host: Env.get(self, 'broker'), - broker_user: Env.get(self, 'login'), - broker_password: Env.get(self, 'password') + broker_host: broker, + broker_user: login, + broker_password: password ) only_if { latest_version && !::File.exist?("#{node['bridge']['data']}/configuration.yaml") } end @@ -91,12 +93,7 @@ block { Utils.snapshot(self, node['bridge']['data'], restore: true) } end -ruby_block cookbook_name do - block do - Common.application(self, 'zigbee2mqtt', cwd: node['bridge']['dir'], - exec: "/usr/bin/node #{node['bridge']['dir']}/index.js", - unit: { 'Service' => { 'Environment' => 'NODE_ENV=production', 'PermissionsStartOnly' => 'true', - 'ExecStartPre' => "-/bin/chown #{node['app']['user']}:#{node['app']['group']} #{node['bridge']['serial']}" } } ) - end - action :run -end +Common.application(self, 'zigbee2mqtt', cwd: node['bridge']['dir'], + exec: "/usr/bin/node #{node['bridge']['dir']}/index.js", + unit: { 'Service' => { 'Environment' => 'NODE_ENV=production', 'PermissionsStartOnly' => 'true', + 'ExecStartPre' => "-/bin/chown #{node['app']['user']}:#{node['app']['group']} #{node['bridge']['serial']}" } } )