This commit is contained in:
plasmagoat 2025-06-09 00:55:51 +02:00
parent a8aa633c49
commit de99267348
11 changed files with 55 additions and 252 deletions

View file

@ -71,33 +71,13 @@ jobs:
echo "tag=dev-$(date +%s)" >> $GITHUB_OUTPUT echo "tag=dev-$(date +%s)" >> $GITHUB_OUTPUT
fi fi
- name: Upload image to Proxmox and manage templates - name: Setup Ancible
run: | run: |
set -e nix-env -iA ancible
FOLDER="result/"
IMAGE_PATH=$(find "$FOLDER" -maxdepth 1 -type f -name '*.vma.zst' | head -n 1)
IMAGE=$(basename "$IMAGE_PATH")
REMOTE_NAME="nixos-base-image-${{ steps.version.outputs.tag}}.vma.zst" - name: Run Upload Template Runbook
REMOTE_PATH="/var/lib/vz/dump/" run: |
./sripts/run_ancible_ci.sh
echo "Uploading $IMAGE to Proxmox as $REMOTE_NAME"
scp $IMAGE_PATH $PROXMOX_USER@$PROXMOX_HOST:$REMOTE_PATH
echo "Restoring as VMID $TEMPLATE_VMID"
ssh $PROXMOX_USER@$PROXMOX_HOST "
cd $REMOTE_PATH
cp $IMAGE $REMOTE_NAME
qm destroy $TEMPLATE_VMID --purge || true
qmrestore $REMOTE_PATH $TEMPLATE_VMID --unique
qm template $TEMPLATE_VMID
echo 'Cloning to $LATEST_TEMPLATE_VMID as latest'
qm destroy $LATEST_TEMPLATE_VMID --purge || true
qm clone $TEMPLATE_VMID $LATEST_TEMPLATE_VMID --name nixos-base-latest
qm template $TEMPLATE_VMID
"
release: release:
name: Release Image name: Release Image

1
.gitignore vendored
View file

@ -9,3 +9,4 @@ result-*
# ---> Ansible # ---> Ansible
*.retry *.retry
*.vault_pass.txt

View file

View file

@ -1 +0,0 @@
ansible-galaxy collection install -r requirements.yml

View file

@ -1,16 +0,0 @@
- name: Restore and Convert to Template on Proxmox
hosts: proxmox # Target the Proxmox host
become: true # Need root/sudo on Proxmox host for qm commands
vars:
# VM/Template specifics (can be passed via --extra-vars or from group_vars)
vmid_base_template: "{{ template_vmid }}"
vmname_base_template: "{{ template_vm_name }}"
vmid_latest_template: "{{ latest_template_vmid }}"
vmname_latest_template: "{{ latest_template_vm_name }}"
# Configuration for the restored VM
cpu_cores: 2
memory_mb: 2048
tasks:

View file

@ -1,130 +0,0 @@
---
- name: Build and Upload NixOS Image
hosts: localhost # Run NixOS build and image upload on the CI runner or local machine
gather_facts: false # No need to gather facts for localhost
vars:
nixos_build_path: "{{ playbook_dir }}/../" # Path to your flake.nix
result_symlink_path: "{{ nixos_build_path }}/result" # Expected symlink output from Nix
dest_dir: "{{ dest_image_dir }}" # From group_vars/all.yml
tasks:
- name: Ensure NixOS image build script is executable
ansible.builtin.file:
path: "{{ playbook_dir }}/../scripts/build_nixos_image.sh"
mode: "0755"
- name: Build NixOS image (creates result/ symlink)
ansible.builtin.command: "{{ playbook_dir }}/../scripts/build_nixos_image.sh"
args:
chdir: "{{ nixos_build_path }}"
register: nixos_build_result
changed_when: nixos_build_result.rc == 0 # Consider it changed if build succeeds
- name: Get built image file (.vma.zst) from result/
ansible.builtin.find:
paths: "{{ result_symlink_path }}"
patterns: "*.vma.zst"
file_type: file # Ensure it's a file
register: built_image_files
delegate_to: localhost
- name: Fail if no image was built
ansible.builtin.fail:
msg: "No .vma.zst image file found in {{ result_symlink_path }}/"
when: built_image_files.files | length == 0
delegate_to: localhost
- name: Set fact for built image path and filename
ansible.builtin.set_fact:
local_image_path: "{{ built_image_files.files[0].path | realpath }}"
image_filename: "{{ built_image_files.files[0].path | basename }}"
delegate_to: localhost
- name: Display paths (for debugging)
ansible.builtin.debug:
msg: "Local image path: {{ local_image_path }}, Filename: {{ image_filename }}"
- name: Copy image to Proxmox server
ansible.builtin.copy:
src: "{{ local_image_path }}"
dest: "{{ dest_dir }}{{ image_filename }}"
mode: "0644" # Ensure correct permissions on the destination
- name: Clean up local build result symlink
ansible.builtin.file:
path: "{{ result_symlink_path }}"
state: absent
delegate_to: localhost
- name: Clean up local store paths (optional, for disk space on CI)
ansible.builtin.command: "nix store gc"
args:
chdir: "{{ nixos_build_path }}"
changed_when: true # Always consider it a change
- name: Restore and Convert to Template on Proxmox
hosts: proxmox_servers # Target the Proxmox host
become: true # Need root/sudo on Proxmox host for qm commands
vars:
# Use variables from group_vars/all.yml and vault.yml
# Access API credentials from vault for this part
proxmox_api_user_id: "{{ proxmox_ci_api_user_name }}@{{ proxmox_ci_api_user_realm }}"
proxmox_api_token_id: "{{ proxmox_ci_api_token_id }}"
proxmox_api_token_secret: "{{ proxmox_ci_api_token_secret }}"
# VM/Template specifics (can be passed via --extra-vars or from group_vars)
vmid_base_template: "{{ template_vmid }}"
vmname_base_template: "{{ template_vm_name }}"
vmid_latest_template: "{{ latest_template_vmid }}"
vmname_latest_template: "{{ latest_template_vm_name }}"
# Configuration for the restored VM
cpu_cores: 2
memory_mb: 2048
tasks:
- name: Set full image path on Proxmox
ansible.builtin.set_fact:
remote_image_path: "{{ dest_image_dir }}{{ image_filename }}"
delegate_to: localhost # Run this locally to set a fact accessible globally
- name: Destroy existing base template VM (if it exists)
ansible.builtin.shell: "qm destroy {{ vmid_base_template }} --purge || true"
args:
warn: no
changed_when: false # Assume idempotency; if it didn't exist, no change
failed_when: false # Don't fail if VM isn't found
- name: Destroy existing 'latest' template VM (if it exists)
ansible.builtin.shell: "qm destroy {{ vmid_latest_template }} --purge || true"
args:
warn: no
changed_when: false
failed_when: false
- name: Restore VM from image to base template VMID
ansible.builtin.command: >
qmrestore {{ remote_image_path }} {{ vmid_base_template }} --unique true --name {{ vmname_base_template }} --storage {{ storage_name }}
args:
creates: "/etc/pve/qemu-server/{{ vmid_base_template }}.conf" # Idempotency check: only run if config file doesn't exist
- name: Set CPU and memory for the base template VM
ansible.builtin.command: >
qm set {{ vmid_base_template }} --cores {{ cpu_cores }} --memory {{ memory_mb }}
# This task is not fully idempotent; it will always try to set.
# You'd need complex 'when' conditions to check current settings.
- name: Convert base template VM to a template
ansible.builtin.command: "qm template {{ vmid_base_template }}"
# This command is largely idempotent for the `qm template` operation itself.
- name: Clone base template to 'latest' template VMID
ansible.builtin.command: >
qm clone {{ vmid_base_template }} {{ vmid_latest_template }} --name {{ vmname_latest_template }} --full --storage {{ storage_name }}
args:
creates: "/etc/pve/qemu-server/{{ vmid_latest_template }}.conf" # Idempotency check: only run if config file doesn't exist
- name: Convert 'latest' template VM to a template
ansible.builtin.command: "qm template {{ vmid_latest_template }}"

View file

@ -1,38 +0,0 @@
- name: Build and Upload NixOS Image, Restore and Convert to Template
hosts: proxmox
gather_facts: false
vars:
image_dir: "{{ playbook_dir }}/../result"
dest_dir: "/var/lib/vz/dump/"
tasks:
- name: Get built image file (.vma.zst) from result/
ansible.builtin.find:
paths: "{{ result_path }}"
patterns: "*.vma.zst"
file_type: file # Ensure it's a file
register: built_image_files
delegate_to: localhost
- name: Fail if no image was built
ansible.builtin.fail:
msg: "No .vma.zst image file found in {{ result_path }}/"
when: built_image_files.files | length == 0
delegate_to: localhost
- name: Set fact for built image path and filename
ansible.builtin.set_fact:
local_image_path: "{{ built_image_files.files[0].path | realpath }}"
image_filename: "{{ built_image_files.files[0].path | basename }}"
delegate_to: localhost
- name: Display paths (for debugging)
ansible.builtin.debug:
msg: "Local image path: {{ local_image_path }}, Filename: {{ image_filename }}"
- name: Copy image to Proxmox server
ansible.builtin.copy:
src: "{{ local_image_path }}"
dest: "{{ dest_dir }}"
mode: "0644" # Ensure correct permissions on the destination

View file

@ -1,37 +1,37 @@
- name: Set full image path on Proxmox - name: Set full image path on Proxmox
ansible.builtin.set_fact: ansible.builtin.set_fact:
remote_image_path: "{{ dest_image_path }}{{ image_filename }}" remote_image_path: "{{ dest_image_path }}{{ image_filename }}"
delegate_to: localhost # Run this locally to set a fact accessible globally delegate_to: localhost
- name: Destroy existing backup template VM (if it exists) - name: Check if 'backup' template VM exists
ansible.builtin.shell: "qm destroy {{ vmid_backup_template }} --purge || true" ansible.builtin.command: "qm status {{ vmid_backup_template }}"
# args: register: backup_vm_status
# warn: no failed_when: false
# changed_when: false # Assume idempotency; if it didn't exist, no change changed_when: false
failed_when: false # Don't fail if VM isn't found
- name: Clone 'lastest' template to 'backup' template VMID - name: Check if 'latest' template VM exists
ansible.builtin.command: > ansible.builtin.command: "qm status {{ vmid_latest_template }}"
qm clone {{ vmid_latest_template }} {{ vmid_backup_template }} --name {{ vmname_backup_template }} --full register: latest_vm_status
# args: failed_when: false
# creates: "/etc/pve/qemu-server/{{ vmid_backup_template }}.conf" # Idempotency check: only run if config file doesn't exist changed_when: false
# failed_when: false # Don't fail if VM isn't found
- name: Convert 'backup' template VM to a template - name: Destroy existing 'backup' template VM (to ensure a clean slate for rotation)
ansible.builtin.command: "qm template {{ vmid_backup_template }}" ansible.builtin.command: "qm destroy {{ vmid_backup_template }} --purge"
when: backup_vm_status.rc == 0 # Only destroy if it actually exists
register: destroy_backup_result
- name: Destroy existing backup template VM (if it exists) - name: Clone 'latest' template to 'backup' template VMID (if 'latest' exists)
ansible.builtin.shell: "qm destroy {{ vmid_backup_template }} --purge || true" ansible.builtin.shell: |
qm clone {{ vmid_latest_template }} {{ vmid_backup_template }} --name {{ vmname_backup_template }} --full --storage {{ storage_name }}
qm template {{ vmid_backup_template }}
qm destroy {{ vmid_latest_template }} --purge
when: latest_vm_status.rc == 0 # Only clone if 'latest' VM exists
register: clone_to_backup_result
- name: Restore VM from image to 'latest' template VMID - name: Restore VM from image to 'latest' template VMID
ansible.builtin.command: > ansible.builtin.shell: |
qmrestore {{ remote_image_path }} {{ vmid_latest_template }} --unique true qmrestore {{ remote_image_path }} {{ vmid_latest_template }} --unique true --storage {{ storage_name }}
# args:
# creates: "/etc/pve/qemu-server/{{ vmid_latest_template }}.conf" # Idempotency check: only run if config file doesn't exist
- name: Set CPU and memory for the base template VM
ansible.builtin.command: >
qm set {{ vmid_latest_template }} --cores {{ cpu_cores }} --memory {{ memory_mb }} --name {{ vmname_latest_template }} qm set {{ vmid_latest_template }} --cores {{ cpu_cores }} --memory {{ memory_mb }} --name {{ vmname_latest_template }}
qm template {{ vmid_latest_template }}
- name: Convert 'backup' template VM to a template register: restore_new_latest_result
ansible.builtin.command: "qm template {{ vmid_latest_template }}" changed_when: restore_new_latest_result.rc == 0

View file

@ -1,15 +0,0 @@
# To edit this file: ansible-vault edit ansible/vault.yml
# For initial Proxmox user creation (using root@pam to create the API user)
proxmox_root_password: "YOUR_PROXMOX_ROOT_PASSWORD_HERE" # CHANGE THIS AND ENCRYPT
# For the dedicated Proxmox API user and token (for CI/CD)
proxmox_ci_api_user_name: cicd-api-user
proxmox_ci_api_user_realm: pam
proxmox_ci_api_user_password: "SUPER_STRONG_PASSWORD_FOR_API_USER" # CHANGE THIS AND ENCRYPT
proxmox_ci_api_token_id: cicd-deploy-token
# The 'value' of the API token secret should be generated by 'pveum' or 'proxmox_api_token' module.
# You will get this after running proxmox_setup.yml for the first time.
# Then update this vault.yml with the actual secret.
proxmox_ci_api_token_secret: "pveum_token_secret_from_first_run_here" # POPULATE THIS AFTER FIRST RUN AND ENCRYPT

View file

@ -0,0 +1,9 @@
#!/bin/bash
set -euo pipefail
# This script assumes 'flake.nix' and 'configuration.nix' are in the parent directory
# and outputs the result to a symlink named 'result'
echo "Building NixOS image..."
nix build .#nixosConfigurations.proxmox-vm.config.system.build.qemu-image
echo "NixOS image build complete."

13
scripts/run_ancible_ci.sh Normal file
View file

@ -0,0 +1,13 @@
#!/bin/bash
set -euo pipefail
# Navigate to the ansible directory
cd ansible
# Run the image deployment playbook
echo "Running Ansible upload-template playbook..."
ansible-playbook upload-template.yml \
-e "cpu_cores=4" \
-e "memory_mb=4096"
echo "Ansible playbook completed."