From 0696218eb6fcc337136d34f241b366d16dd2fd89 Mon Sep 17 00:00:00 2001 From: Maxim Vershinin Date: Thu, 17 Sep 2026 10:16:05 +0400 Subject: [PATCH] feat(pb,task): moves input validation into separated tasks files. Play fails immediately if a validation fails --- .../deploy_and_set_up_lxc_on_proxmox.yaml | 62 ++++--------------- tasks/hostname_validation.yaml | 23 +++++++ tasks/human_admin_user_validation.yaml | 22 +++++++ tasks/ip_validation.yaml | 20 ++++++ tasks/lxc_privileged_validation.yaml | 22 +++++++ tasks/password_validation.yaml | 38 ++++++++++++ 6 files changed, 137 insertions(+), 50 deletions(-) create mode 100644 tasks/hostname_validation.yaml create mode 100644 tasks/human_admin_user_validation.yaml create mode 100644 tasks/ip_validation.yaml create mode 100644 tasks/lxc_privileged_validation.yaml create mode 100644 tasks/password_validation.yaml diff --git a/playbooks/deploy_and_set_up_lxc_on_proxmox.yaml b/playbooks/deploy_and_set_up_lxc_on_proxmox.yaml index 86440ca..d264115 100644 --- a/playbooks/deploy_and_set_up_lxc_on_proxmox.yaml +++ b/playbooks/deploy_and_set_up_lxc_on_proxmox.yaml @@ -10,65 +10,27 @@ ansible_user_passwd_hash: "{{ ansible_password | password_hash('sha512', 's3edscrj45e6r') }}" user_passwd_hash: "{{ user_password | password_hash('sha512', 's3ed6123jhgcr') }}" - vars_prompt: - - name: lxc_hostname - prompt: "Hostname for the container (may only contain letters (a-z, A-Z), numbers (0-9), and hyphens)" - default: "new-lxc-test" - private: false + pre_tasks: + - name: Include input and validation for hostname + ansible.builtin.include_tasks: ../tasks/hostname_validation.yaml + + - name: Include input and validation for password + ansible.builtin.include_tasks: ../tasks/password_validation.yaml - - name: lxc_root_password - prompt: "Root password" - default: "new-lxc-test" - private: true - confirm: true - encrypt: sha512_crypt + - name: Include input and validation for IP + ansible.builtin.include_tasks: ../tasks/ip_validation.yaml - - name: lxc_ip_address - prompt: "IP address for the container (in x.x.x.x/x formant)" - default: "192.168.0.90/24" - private: false + - name: Include input and validation for LXC privilege + ansible.builtin.include_tasks: ../tasks/lxc_privileged_validation.yaml - - name: lxc_is_privileged - prompt: "Unprivileged LXC (true/false)?" - default: true - private: false + - name: Include input and validation for human admin user creation + ansible.builtin.include_tasks: ../tasks/human_admin_user_validation.yaml - - name: lxc_create_human_admin_user - prompt: "Create human admin user? (true/false)?" - default: true - private: false - - pre_tasks: - - name: Validate hostname - ansible.builtin.fail: - msg: | - Invalid hostname: "{{ lxc_hostname }}" - Hostname must: - - contain only letters (a-z, A-Z), digits (0-9), hyphens (-), and dots (.) - - not start or end with a hyphen or dot - - have each label between 1 and 63 characters - - not exceed 253 characters in total - when: > - lxc_hostname | length > 253 or - lxc_hostname is not regex('^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$') - run_once: true - - - name: Validate IP address - ansible.builtin.fail: - msg: | - Invalid IP address: "{{ lxc_ip_address }}" - IP address must be in x.x.x.x/x format (e.g., 192.168.0.90/24). - when: not lxc_ip_address is regex('^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)(\\/(3[0-2]|[12]?\\d))?$') - run_once: true roles: - ../roles/deploy_lxc_on_proxmox tasks: - - name: Make the prompted hostname available to the whole playbook - ansible.builtin.set_fact: - fact_lxc_hostname: "{{ lxc_hostname }}" - - name: Make the prompted IP available to the whole playbook ansible.builtin.set_fact: fact_lxc_ip_address: "{{ lxc_ip_address.split('/') | first }}" diff --git a/tasks/hostname_validation.yaml b/tasks/hostname_validation.yaml new file mode 100644 index 0000000..7ff704c --- /dev/null +++ b/tasks/hostname_validation.yaml @@ -0,0 +1,23 @@ +--- + +- name: Set default hostname + ansible.builtin.set_fact: + lxc_hostname: "new-lxc-test" + +- name: Prompt for hostname with immediate validation + ansible.builtin.pause: + prompt: "Enter hostname (letters, digits, hyphens, dots; max 253 chars) [new-lxc-test]" + register: hostname_result + until: > + hostname_result.user_input | length == 0 or + ( + hostname_result.user_input | length <= 253 and + hostname_result.user_input is regex('^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$') + ) + retries: 2 + delay: 0 + +- name: Use user input only if provided, otherwise keep the default + ansible.builtin.set_fact: + lxc_hostname: "{{ hostname_result.user_input }}" + when: hostname_result.user_input | length > 0 diff --git a/tasks/human_admin_user_validation.yaml b/tasks/human_admin_user_validation.yaml new file mode 100644 index 0000000..df6d04b --- /dev/null +++ b/tasks/human_admin_user_validation.yaml @@ -0,0 +1,22 @@ +--- + +- name: Set default human admin user + ansible.builtin.set_fact: + lxc_human_admin_user: true + +- name: Prompt for human admin user creation with immediate validation + ansible.builtin.pause: + prompt: "Create human admin user? (true/false) [true]" + register: human_admin_user_result + until: > + human_admin_user_result.user_input | length == 0 or + ( + human_admin_user_result.user_input in ['true', 'false'] + ) + retries: 2 + delay: 0 + +- name: Use user input only if provided, otherwise keep the default + ansible.builtin.set_fact: + lxc_human_admin_user: "{{ human_admin_user_result.user_input }}" + when: human_admin_user_result.user_input | length > 0 diff --git a/tasks/ip_validation.yaml b/tasks/ip_validation.yaml new file mode 100644 index 0000000..bbbd866 --- /dev/null +++ b/tasks/ip_validation.yaml @@ -0,0 +1,20 @@ +--- + +- name: Set default ip + ansible.builtin.set_fact: + lxc_ip_address: "192.168.0.90/24" + +- name: Prompt for IP with immediate validation + ansible.builtin.pause: + prompt: "Enter ip in x.x.x.x/x format [192.168.0.90/24]" + register: ip_result + until: > + ip_result.user_input | length == 0 or + ip_result.user_input is regex('^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)/(3[0-2]|[12]?\\d)$') + retries: 2 + delay: 0 + +- name: Use user input only if provided, otherwise keep the default + ansible.builtin.set_fact: + lxc_ip_address: "{{ ip_result.user_input }}" + when: ip_result.user_input | length > 0 diff --git a/tasks/lxc_privileged_validation.yaml b/tasks/lxc_privileged_validation.yaml new file mode 100644 index 0000000..5b4c3af --- /dev/null +++ b/tasks/lxc_privileged_validation.yaml @@ -0,0 +1,22 @@ +--- + +- name: Set default privileged + ansible.builtin.set_fact: + lxc_privileged: false + +- name: Prompt for privileged with immediate validation + ansible.builtin.pause: + prompt: "Privileged LXC? (true/false) [false]" + register: privileged_result + until: > + privileged_result.user_input | length == 0 or + ( + privileged_result.user_input in ['true', 'false'] + ) + retries: 2 + delay: 0 + +- name: Use user input only if provided, otherwise keep the default + ansible.builtin.set_fact: + lxc_privileged: "{{ privileged_result.user_input }}" + when: privileged_result.user_input | length > 0 diff --git a/tasks/password_validation.yaml b/tasks/password_validation.yaml new file mode 100644 index 0000000..e40e8bd --- /dev/null +++ b/tasks/password_validation.yaml @@ -0,0 +1,38 @@ +--- + +- name: Set default password + ansible.builtin.set_fact: + lxc_root_password: "new-lxc-test" + +- name: Prompt for password + ansible.builtin.pause: + prompt: "Enter password (letters, digits, hyphens, dots; max 253 chars) [new-lxc-test]" + echo: false + register: password_result_1 + until: > + password_result_1.user_input | length == 0 or + ( + password_result_1.user_input | length <= 253 and + password_result_1.user_input is regex('^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$') + ) + retries: 3 + delay: 0 + +- name: Prompt for password (confirmation) + ansible.builtin.pause: + prompt: "Confirm your new password" + echo: no + register: password_result_2 + +- name: Ensure passwords match + ansible.builtin.assert: + that: + - password_result_1.user_input == password_result_2.user_input + fail_msg: "Passwords do not match!" + success_msg: "Password confirmed." + +- name: Use user input only if provided, otherwise keep the default + ansible.builtin.set_fact: + lxc_root_password: "{{ password_result_1.user_input }}" + when: password_result_1.user_input | length > 0 +