From f0130c4fc3288408b32607b8010ff0002c1dc628 Mon Sep 17 00:00:00 2001 From: sfulmer Date: Wed, 15 Apr 2026 10:55:25 -0400 Subject: [PATCH 01/11] feat(vm-gitops-export): capture VM defs for GitOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move from openshift_virtualization_migration repo — this is day 2 ops functionality for exporting VM definitions to a GitOps repository. Note: This role references vm_collect which may need to be ported or added as a cross-collection dependency. Co-Authored-By: Claude Opus 4.6 --- playbooks/vm_gitops_export.yml | 15 +++ roles/vm_gitops_export/defaults/main.yml | 71 ++++++++++ roles/vm_gitops_export/meta/main.yml | 10 ++ .../tasks/_clean_manifest.yml | 61 +++++++++ .../vm_gitops_export/tasks/_export_to_git.yml | 121 ++++++++++++++++++ roles/vm_gitops_export/tasks/main.yml | 78 +++++++++++ roles/vm_gitops_export/tests/inventory | 1 + roles/vm_gitops_export/tests/test.yml | 7 + 8 files changed, 364 insertions(+) create mode 100644 playbooks/vm_gitops_export.yml create mode 100644 roles/vm_gitops_export/defaults/main.yml create mode 100644 roles/vm_gitops_export/meta/main.yml create mode 100644 roles/vm_gitops_export/tasks/_clean_manifest.yml create mode 100644 roles/vm_gitops_export/tasks/_export_to_git.yml create mode 100644 roles/vm_gitops_export/tasks/main.yml create mode 100644 roles/vm_gitops_export/tests/inventory create mode 100644 roles/vm_gitops_export/tests/test.yml diff --git a/playbooks/vm_gitops_export.yml b/playbooks/vm_gitops_export.yml new file mode 100644 index 0000000..9b9acb2 --- /dev/null +++ b/playbooks/vm_gitops_export.yml @@ -0,0 +1,15 @@ +--- + +- name: Export VM Definitions to GitOps Repository + hosts: localhost + connection: local + gather_facts: false + tasks: + - name: Invoke VM GitOps Export + ansible.builtin.include_role: + name: infra.openshift_virtualization_ops.vm_gitops_export + vars: + openshift_host: "{{ lookup('ansible.builtin.env', 'K8S_AUTH_HOST', default=Undefined) | default('', True) }}" + openshift_api_key: "{{ lookup('ansible.builtin.env', 'K8S_AUTH_API_KEY', default=Undefined) | default('', True) }}" # noqa: yaml[line-length] + openshift_verify_ssl: "{{ lookup('ansible.builtin.env', 'K8S_AUTH_VERIFY_SSL', default='') | default(false) | bool }}" # noqa: yaml[line-length] +... diff --git a/roles/vm_gitops_export/defaults/main.yml b/roles/vm_gitops_export/defaults/main.yml new file mode 100644 index 0000000..acb7b41 --- /dev/null +++ b/roles/vm_gitops_export/defaults/main.yml @@ -0,0 +1,71 @@ +--- +# defaults file for vm_gitops_export + +# title: GitOps Export Request +# required: True +# description: List of VirtualMachine GitOps Export Requests +vm_gitops_export_request: [] +# - namespace: # Namespace containing VMs to capture. +# names: # List of VM names within a namespace. \ +# Optional when using label_selectors. +# - +# label_selectors: # Label selectors to match VMs. \ +# Cannot be used with list of VM names. +# - = + +# title: Git Repository URL +# required: True +# description: URL of the Git repository to store VM manifests +vm_gitops_export_git_repo_url: "" +# title: Git Branch +# required: False +# description: Git branch to push manifests to +vm_gitops_export_git_branch: "main" +# title: Git Path +# required: False +# description: Directory path within the Git repository to store manifests +vm_gitops_export_git_path: "virtualmachines" +# title: Git User Name +# required: False +# description: Git commit author name +vm_gitops_export_git_user_name: "Ansible Automation" +# title: Git User Email +# required: False +# description: Git commit author email +vm_gitops_export_git_user_email: "ansible@automation.local" +# title: Git SSH Key Path +# required: False +# description: Path to SSH private key for Git authentication +vm_gitops_export_git_ssh_key: "" +# title: Git Token +# required: False +# description: Token for HTTPS Git authentication +vm_gitops_export_git_token: "" +# title: Git Commit Message +# required: False +# description: Commit message for exported manifests +vm_gitops_export_git_commit_message: "Export VirtualMachine definitions via Ansible" + +# title: OpenShift Host +# required: True +# description: OpenShift Host +vm_gitops_export_openshift_host: "{{ openshift_host }}" +# title: OpenShift API Key +# required: True +# description: OpenShift API Key +vm_gitops_export_openshift_api_key: "{{ openshift_api_key }}" +# title: Verify SSL Certificate +# required: True +# description: Verify SSL Certificate +vm_gitops_export_openshift_verify_ssl: "{{ openshift_verify_ssl }}" +# title: KubeVirt API Version +# required: True +# description: KubeVirt API Version +vm_gitops_export_kubevirt_api_version: kubevirt.io/v1 + +# title: Cleanup Working Directory +# required: False +# description: Remove local working directory after export +vm_gitops_export_cleanup: true + +... diff --git a/roles/vm_gitops_export/meta/main.yml b/roles/vm_gitops_export/meta/main.yml new file mode 100644 index 0000000..aa323fb --- /dev/null +++ b/roles/vm_gitops_export/meta/main.yml @@ -0,0 +1,10 @@ +--- +galaxy_info: + author: "" + description: Capture VirtualMachine definitions and export to a GitOps repository. + company: Red Hat + license: GPL-3.0-only + min_ansible_version: 2.15.0 + galaxy_tags: [] +dependencies: [] +... diff --git a/roles/vm_gitops_export/tasks/_clean_manifest.yml b/roles/vm_gitops_export/tasks/_clean_manifest.yml new file mode 100644 index 0000000..d415a19 --- /dev/null +++ b/roles/vm_gitops_export/tasks/_clean_manifest.yml @@ -0,0 +1,61 @@ +--- + +- name: _clean_manifest | Build Cleaned Manifest + ansible.builtin.set_fact: + vm_gitops_export_cleaned_manifests: >- + {{ vm_gitops_export_cleaned_manifests + [vm_gitops_export_cleaned_vm] }} + vars: + vm_gitops_export_source: "{{ vm_gitops_export_vm.response_obj }}" + vm_gitops_export_clean_metadata: >- + {{ + vm_gitops_export_source.metadata + | dict2items + | rejectattr('key', 'in', vm_gitops_export_metadata_remove_keys) + | items2dict + }} + vm_gitops_export_metadata_remove_keys: + - uid + - resourceVersion + - generation + - creationTimestamp + - deletionTimestamp + - deletionGracePeriodSeconds + - managedFields + - ownerReferences + - finalizers + - selfLink + vm_gitops_export_clean_annotations: >- + {{ + vm_gitops_export_clean_metadata.annotations | default({}) + | dict2items + | rejectattr('key', 'match', 'kubectl.kubernetes.io/') + | rejectattr('key', 'match', 'kubevirt.io/latest-observed-api-version') + | rejectattr('key', 'match', 'kubevirt.io/storage-observed-api-version') + | items2dict + }} + vm_gitops_export_final_metadata: >- + {{ + vm_gitops_export_clean_metadata + | ansible.builtin.combine( + {'annotations': vm_gitops_export_clean_annotations} + if vm_gitops_export_clean_annotations | length > 0 + else {}, + recursive=True) + }} + vm_gitops_export_cleaned_vm: + apiVersion: "{{ vm_gitops_export_source.apiVersion }}" + kind: "{{ vm_gitops_export_source.kind }}" + metadata: >- + {{ + vm_gitops_export_final_metadata + | dict2items + | rejectattr('key', 'equalto', 'annotations') + | items2dict + | ansible.builtin.combine( + {'annotations': vm_gitops_export_clean_annotations} + if vm_gitops_export_clean_annotations | length > 0 + else {}) + }} + spec: "{{ vm_gitops_export_source.spec }}" + +... diff --git a/roles/vm_gitops_export/tasks/_export_to_git.yml b/roles/vm_gitops_export/tasks/_export_to_git.yml new file mode 100644 index 0000000..db8bf49 --- /dev/null +++ b/roles/vm_gitops_export/tasks/_export_to_git.yml @@ -0,0 +1,121 @@ +--- + +- name: _export_to_git | Create Working Directory + ansible.builtin.file: + path: "{{ vm_gitops_export_work_dir }}" + state: directory + mode: "0700" + +- name: _export_to_git | Configure Git SSH Command + when: vm_gitops_export_git_ssh_key | default("", true) | length > 0 + ansible.builtin.set_fact: + vm_gitops_export_git_env: + GIT_SSH_COMMAND: >- + ssh -i {{ vm_gitops_export_git_ssh_key }} -o StrictHostKeyChecking=no + +- name: _export_to_git | Build HTTPS URL with Token + when: vm_gitops_export_git_token | default("", true) | length > 0 + ansible.builtin.set_fact: + vm_gitops_export_git_clone_url: >- + {{ vm_gitops_export_git_repo_url + | regex_replace('^https://', + 'https://oauth2:' + vm_gitops_export_git_token + '@') }} + no_log: true + +- name: _export_to_git | Set Clone URL for SSH + when: vm_gitops_export_git_token | default("", true) | length == 0 + ansible.builtin.set_fact: + vm_gitops_export_git_clone_url: "{{ vm_gitops_export_git_repo_url }}" + +- name: _export_to_git | Clone Git Repository + ansible.builtin.git: + repo: "{{ vm_gitops_export_git_clone_url }}" + dest: "{{ vm_gitops_export_work_dir }}/repo" + version: "{{ vm_gitops_export_git_branch }}" + depth: 1 + single_branch: true + key_file: >- + {{ vm_gitops_export_git_ssh_key + if (vm_gitops_export_git_ssh_key | default("", true) | length > 0) + else omit }} + accept_newhostkey: true + no_log: true + +- name: _export_to_git | Create Export Directory + ansible.builtin.file: + path: "{{ vm_gitops_export_work_dir }}/repo/{{ vm_gitops_export_git_path }}" + state: directory + mode: "0755" + +- name: _export_to_git | Write VM Manifests + ansible.builtin.copy: + content: >- + {{ vm_gitops_export_manifest | to_nice_yaml(indent=2, width=200) }} + dest: >- + {{ vm_gitops_export_work_dir }}/repo/{{ vm_gitops_export_git_path }}/{{ + vm_gitops_export_manifest.metadata.namespace }}_{{ + vm_gitops_export_manifest.metadata.name }}.yml + mode: "0644" + loop: "{{ vm_gitops_export_cleaned_manifests }}" + loop_control: + loop_var: vm_gitops_export_manifest + label: >- + {{ vm_gitops_export_manifest.metadata.namespace }}/{{ + vm_gitops_export_manifest.metadata.name }} + register: vm_gitops_export_write_result + +- name: _export_to_git | Configure Git User # noqa: command-instead-of-module + ansible.builtin.command: + cmd: >- + git config user.{{ vm_gitops_export_git_config_item.key }} + '{{ vm_gitops_export_git_config_item.value }}' + chdir: "{{ vm_gitops_export_work_dir }}/repo" + loop: + - key: name + value: "{{ vm_gitops_export_git_user_name }}" + - key: email + value: "{{ vm_gitops_export_git_user_email }}" + loop_control: + loop_var: vm_gitops_export_git_config_item + label: "{{ vm_gitops_export_git_config_item.key }}" + changed_when: true + +- name: _export_to_git | Stage Changes # noqa: command-instead-of-module + ansible.builtin.command: + cmd: "git add {{ vm_gitops_export_git_path }}/" + chdir: "{{ vm_gitops_export_work_dir }}/repo" + changed_when: true + +- name: _export_to_git | Check for Changes # noqa: command-instead-of-module + ansible.builtin.command: + cmd: git diff --cached --quiet + chdir: "{{ vm_gitops_export_work_dir }}/repo" + register: vm_gitops_export_git_diff + changed_when: false + failed_when: false + +- name: _export_to_git | Commit Changes # noqa: command-instead-of-module + when: vm_gitops_export_git_diff.rc == 1 + ansible.builtin.command: + cmd: >- + git commit -m '{{ vm_gitops_export_git_commit_message }}' + chdir: "{{ vm_gitops_export_work_dir }}/repo" + changed_when: true + +- name: _export_to_git | Push Changes # noqa: command-instead-of-module + when: vm_gitops_export_git_diff.rc == 1 + ansible.builtin.command: + cmd: "git push origin {{ vm_gitops_export_git_branch }}" + chdir: "{{ vm_gitops_export_work_dir }}/repo" + environment: "{{ vm_gitops_export_git_env | default({}) }}" + changed_when: true + no_log: true + +- name: _export_to_git | Report No Changes + when: vm_gitops_export_git_diff.rc == 0 + ansible.builtin.debug: + msg: >- + No changes detected in VirtualMachine manifests. + Nothing to commit. + +... diff --git a/roles/vm_gitops_export/tasks/main.yml b/roles/vm_gitops_export/tasks/main.yml new file mode 100644 index 0000000..3a85475 --- /dev/null +++ b/roles/vm_gitops_export/tasks/main.yml @@ -0,0 +1,78 @@ +--- + +- name: Verify vm_gitops_export_request Variable Provided + ansible.builtin.assert: + that: + - vm_gitops_export_request | default("", true) | length > 0 + fail_msg: "'vm_gitops_export_request' Variable Not Provided" + quiet: true + +- name: Verify Required Properties Provided + ansible.builtin.assert: + that: + - vm_gitops_export_request | selectattr('namespace', 'undefined') | list | length == 0 + fail_msg: "Required property 'namespace' in 'vm_gitops_export_request' Variable Not Provided" + quiet: true + +- name: Verify Git Repository URL Provided + ansible.builtin.assert: + that: + - vm_gitops_export_git_repo_url | default("", true) | length > 0 + fail_msg: "'vm_gitops_export_git_repo_url' Variable Not Provided" + quiet: true + +- name: Verify Git Authentication Provided + ansible.builtin.assert: + that: + - >- + (vm_gitops_export_git_ssh_key | default("", true) | length > 0) or + (vm_gitops_export_git_token | default("", true) | length > 0) + fail_msg: "Either 'vm_gitops_export_git_ssh_key' or 'vm_gitops_export_git_token' must be provided" + quiet: true + +- name: Initialize Variables + ansible.builtin.set_fact: + vm_gitops_export_vms: [] + vm_gitops_export_cleaned_manifests: [] + vm_gitops_export_work_dir: "/tmp/vm_gitops_export_{{ ansible_date_time.epoch }}" + +- name: Invoke Collect VM Role + ansible.builtin.include_role: + name: infra.openshift_virtualization_ops.vm_collect + vars: + vm_collect_openshift_api_key: "{{ vm_gitops_export_openshift_api_key }}" + vm_collect_openshift_host: "{{ vm_gitops_export_openshift_host }}" + vm_collect_verify_ssl: "{{ vm_gitops_export_openshift_verify_ssl }}" + vm_collect_vms_var: vm_gitops_export_vms + loop: "{{ vm_gitops_export_request }}" + loop_control: + loop_var: vm_collect_request_instance + +- name: Verify VirtualMachines Found + ansible.builtin.assert: + that: + - vm_gitops_export_vms | length > 0 + fail_msg: "No VirtualMachines found matching the provided criteria" + quiet: true + +- name: Clean VM Manifests + ansible.builtin.include_tasks: + file: _clean_manifest.yml + loop: "{{ vm_gitops_export_vms }}" + loop_control: + loop_var: vm_gitops_export_vm + label: >- + Namespace: {{ vm_gitops_export_vm.response_obj.metadata.namespace }} + - Name: {{ vm_gitops_export_vm.response_obj.metadata.name }} + +- name: Export Manifests to Git Repository + ansible.builtin.include_tasks: + file: _export_to_git.yml + +- name: Cleanup Working Directory + when: vm_gitops_export_cleanup | bool + ansible.builtin.file: + path: "{{ vm_gitops_export_work_dir }}" + state: absent + +... diff --git a/roles/vm_gitops_export/tests/inventory b/roles/vm_gitops_export/tests/inventory new file mode 100644 index 0000000..2fbb50c --- /dev/null +++ b/roles/vm_gitops_export/tests/inventory @@ -0,0 +1 @@ +localhost diff --git a/roles/vm_gitops_export/tests/test.yml b/roles/vm_gitops_export/tests/test.yml new file mode 100644 index 0000000..372b803 --- /dev/null +++ b/roles/vm_gitops_export/tests/test.yml @@ -0,0 +1,7 @@ +--- +- name: Test + hosts: localhost + remote_user: root + roles: + - vm_gitops_export +... From e505401614488d194746125c91f22f1cfcc584ce Mon Sep 17 00:00:00 2001 From: sfulmer Date: Sat, 2 May 2026 09:51:57 -0400 Subject: [PATCH 02/11] fix: add missing allowlist_externals for tox-ansible environments --- tox-ansible.ini | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tox-ansible.ini b/tox-ansible.ini index 3e1b6d9..27d44ea 100644 --- a/tox-ansible.ini +++ b/tox-ansible.ini @@ -31,6 +31,14 @@ allowlist_externals = mkdir ln bash + echo + git + ansible-galaxy + ansible-test + ansible-doc + pytest + mkdir + ln commands = mkdir -p "{env:HOME}/.ansible/collections/ansible_collections" ansible-galaxy collection install "{toxinidir}" -p '{env:HOME}/.ansible/collections/ansible_collections' --force @@ -50,6 +58,14 @@ change_dir = {env:HOME}/.ansible/collections/ansible_collections/infra/openshift skip_install = false allowlist_externals = bash + echo + git + ansible-galaxy + ansible-test + ansible-doc + pytest + mkdir + ln commands = bash -c 'git init --initial-branch=main .' bash -c 'ansible-test integration' From ef9ed3e912b314f47d3c49aca68787d017de06b7 Mon Sep 17 00:00:00 2001 From: sfulmer Date: Sat, 2 May 2026 10:00:32 -0400 Subject: [PATCH 03/11] fix: let tox-ansible manage test commands instead of overriding --- tox-ansible.ini | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tox-ansible.ini b/tox-ansible.ini index 27d44ea..5c79a79 100644 --- a/tox-ansible.ini +++ b/tox-ansible.ini @@ -46,15 +46,13 @@ commands = [testenv] set_env = - COLLECTIONS_PATH = "{env:HOME}/.ansible/collections/ansible_collections" + COLLECTIONS_PATH = {env:HOME}/.ansible/collections/ansible_collections FORCE_COLOR = 1 passenv = HOME ANSIBLE_COLLECTIONS_PATH + ANSIBLE_GALAXY_* no_package = true -deps = - -r tests/integration/requirements.txt -change_dir = {env:HOME}/.ansible/collections/ansible_collections/infra/openshift_virtualization_ops skip_install = false allowlist_externals = bash @@ -66,6 +64,5 @@ allowlist_externals = pytest mkdir ln -commands = - bash -c 'git init --initial-branch=main .' - bash -c 'ansible-test integration' + sh + ade From bf008d0da231cffd7bdc432afbac39eb0e6bb15f Mon Sep 17 00:00:00 2001 From: sfulmer Date: Sat, 2 May 2026 10:11:17 -0400 Subject: [PATCH 04/11] fix: add distlib to integration requirements for manifest support --- tests/integration/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/requirements.txt b/tests/integration/requirements.txt index 8b3ac1e..04708bc 100644 --- a/tests/integration/requirements.txt +++ b/tests/integration/requirements.txt @@ -1 +1,2 @@ # Add python packages that are required for integration testing +distlib From 9deec2c3163fd11fdb35c26f4684767e2d0b01e3 Mon Sep 17 00:00:00 2001 From: sfulmer Date: Sat, 2 May 2026 10:22:20 -0400 Subject: [PATCH 05/11] fix: add distlib to testenv deps for galaxy/sanity manifest support --- tox-ansible.ini | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tox-ansible.ini b/tox-ansible.ini index 5c79a79..e02eef9 100644 --- a/tox-ansible.ini +++ b/tox-ansible.ini @@ -31,14 +31,6 @@ allowlist_externals = mkdir ln bash - echo - git - ansible-galaxy - ansible-test - ansible-doc - pytest - mkdir - ln commands = mkdir -p "{env:HOME}/.ansible/collections/ansible_collections" ansible-galaxy collection install "{toxinidir}" -p '{env:HOME}/.ansible/collections/ansible_collections' --force @@ -54,6 +46,8 @@ passenv = ANSIBLE_GALAXY_* no_package = true skip_install = false +deps = + distlib allowlist_externals = bash echo From 52862530d007bda6e4835f31175be290b6977530 Mon Sep 17 00:00:00 2001 From: sfulmer Date: Sat, 2 May 2026 10:26:50 -0400 Subject: [PATCH 06/11] =?UTF-8?q?fix:=20remove=20testenv=20overrides=20?= =?UTF-8?q?=E2=80=94=20only=20set=20allowlist=5Fexternals,=20let=20tox-ans?= =?UTF-8?q?ible=20manage=20deps/commands?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tox-ansible.ini | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tox-ansible.ini b/tox-ansible.ini index e02eef9..5e0966b 100644 --- a/tox-ansible.ini +++ b/tox-ansible.ini @@ -37,17 +37,6 @@ commands = ansible-galaxy collection install -r "{toxinidir}/requirements-dev.yml" [testenv] -set_env = - COLLECTIONS_PATH = {env:HOME}/.ansible/collections/ansible_collections - FORCE_COLOR = 1 -passenv = - HOME - ANSIBLE_COLLECTIONS_PATH - ANSIBLE_GALAXY_* -no_package = true -skip_install = false -deps = - distlib allowlist_externals = bash echo From 3b50d8d0a0db11791449821d088337aca2e050f8 Mon Sep 17 00:00:00 2001 From: sfulmer Date: Sat, 2 May 2026 10:37:42 -0400 Subject: [PATCH 07/11] fix: add distlib to galaxy tox env for manifest support --- tox-ansible.ini | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tox-ansible.ini b/tox-ansible.ini index 5e0966b..9348a75 100644 --- a/tox-ansible.ini +++ b/tox-ansible.ini @@ -49,3 +49,8 @@ allowlist_externals = ln sh ade + +[testenv:galaxy] +deps = + galaxy-importer>=0.4.31 + distlib From 339a534d98e7a98d8b8e635147d6033fd47b6e98 Mon Sep 17 00:00:00 2001 From: sfulmer Date: Mon, 4 May 2026 08:21:56 -0400 Subject: [PATCH 08/11] fix: add missing README for vm_gitops_export role --- roles/vm_gitops_export/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 roles/vm_gitops_export/README.md diff --git a/roles/vm_gitops_export/README.md b/roles/vm_gitops_export/README.md new file mode 100644 index 0000000..b604419 --- /dev/null +++ b/roles/vm_gitops_export/README.md @@ -0,0 +1,17 @@ +# vm_gitops_export + +Capture VirtualMachine definitions and export to a GitOps repository. + +## Requirements + +- `redhat.openshift_virtualization` collection +- `kubernetes.core` collection +- OpenShift cluster with Virtualization operator installed + +## Role Variables + +See `defaults/main.yml` for available variables. + +## License + +Apache-2.0 From 2f6f54e99937632e6f64bcf23cf5f5cf5453cbd7 Mon Sep 17 00:00:00 2001 From: sfulmer Date: Mon, 4 May 2026 08:23:38 -0400 Subject: [PATCH 09/11] fix: restore original testenv config, only add allowlist_externals --- tox-ansible.ini | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/tox-ansible.ini b/tox-ansible.ini index 9348a75..89248d9 100644 --- a/tox-ansible.ini +++ b/tox-ansible.ini @@ -37,20 +37,29 @@ commands = ansible-galaxy collection install -r "{toxinidir}/requirements-dev.yml" [testenv] +set_env = + COLLECTIONS_PATH = "{env:HOME}/.ansible/collections/ansible_collections" + FORCE_COLOR = 1 +passenv = + HOME + ANSIBLE_COLLECTIONS_PATH +no_package = true +deps = + -r tests/integration/requirements.txt +change_dir = {env:HOME}/.ansible/collections/ansible_collections/infra/openshift_virtualization_ops +skip_install = false allowlist_externals = bash echo git + mkdir + sh + ade ansible-galaxy ansible-test ansible-doc pytest - mkdir ln - sh - ade - -[testenv:galaxy] -deps = - galaxy-importer>=0.4.31 - distlib +commands = + bash -c 'git init --initial-branch=main .' + bash -c 'ansible-test integration' From 2ed17deecdb7fef8bc0363bf39090c80388bb96a Mon Sep 17 00:00:00 2001 From: sfulmer Date: Mon, 4 May 2026 08:36:40 -0400 Subject: [PATCH 10/11] fix: pin tox-ansible<26.2.2 to match main (26.3.0 requires ade) --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 9d3d4f9..0061feb 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,7 +5,7 @@ pytest-cov coverage molecule tox -tox-ansible +tox-ansible<26.2.2 black jmespath ansible-core From 0ddcbb62c21c86bd88d2a7671b81c1f51c8df4d0 Mon Sep 17 00:00:00 2001 From: sfulmer Date: Fri, 15 May 2026 11:56:40 -0400 Subject: [PATCH 11/11] Address review feedback: gather_facts, user-configurable metadata keys, user/password auth --- playbooks/vm_gitops_export.yml | 2 +- roles/vm_gitops_export/defaults/main.yml | 13 ++++++++++ .../tasks/_clean_manifest.yml | 26 +++++++++++-------- .../vm_gitops_export/tasks/_export_to_git.yml | 17 +++++++++++- roles/vm_gitops_export/tasks/main.yml | 9 +++++-- 5 files changed, 52 insertions(+), 15 deletions(-) diff --git a/playbooks/vm_gitops_export.yml b/playbooks/vm_gitops_export.yml index 9b9acb2..be77b47 100644 --- a/playbooks/vm_gitops_export.yml +++ b/playbooks/vm_gitops_export.yml @@ -3,7 +3,7 @@ - name: Export VM Definitions to GitOps Repository hosts: localhost connection: local - gather_facts: false + gather_facts: true tasks: - name: Invoke VM GitOps Export ansible.builtin.include_role: diff --git a/roles/vm_gitops_export/defaults/main.yml b/roles/vm_gitops_export/defaults/main.yml index acb7b41..275dfd0 100644 --- a/roles/vm_gitops_export/defaults/main.yml +++ b/roles/vm_gitops_export/defaults/main.yml @@ -41,6 +41,14 @@ vm_gitops_export_git_ssh_key: "" # required: False # description: Token for HTTPS Git authentication vm_gitops_export_git_token: "" +# title: Git Username +# required: False +# description: Username for HTTPS Git authentication (used with password) +vm_gitops_export_git_username: "" +# title: Git Password +# required: False +# description: Password for HTTPS Git authentication (used with username) +vm_gitops_export_git_password: "" # title: Git Commit Message # required: False # description: Commit message for exported manifests @@ -63,6 +71,11 @@ vm_gitops_export_openshift_verify_ssl: "{{ openshift_verify_ssl }}" # description: KubeVirt API Version vm_gitops_export_kubevirt_api_version: kubevirt.io/v1 +# title: Additional Metadata Keys to Remove +# required: False +# description: Additional metadata keys to strip from captured manifests (appended to built-in list) +vm_gitops_export_extra_metadata_remove_keys: [] + # title: Cleanup Working Directory # required: False # description: Remove local working directory after export diff --git a/roles/vm_gitops_export/tasks/_clean_manifest.yml b/roles/vm_gitops_export/tasks/_clean_manifest.yml index d415a19..36793be 100644 --- a/roles/vm_gitops_export/tasks/_clean_manifest.yml +++ b/roles/vm_gitops_export/tasks/_clean_manifest.yml @@ -13,17 +13,21 @@ | rejectattr('key', 'in', vm_gitops_export_metadata_remove_keys) | items2dict }} - vm_gitops_export_metadata_remove_keys: - - uid - - resourceVersion - - generation - - creationTimestamp - - deletionTimestamp - - deletionGracePeriodSeconds - - managedFields - - ownerReferences - - finalizers - - selfLink + vm_gitops_export_metadata_remove_keys: >- + {{ + [ + 'uid', + 'resourceVersion', + 'generation', + 'creationTimestamp', + 'deletionTimestamp', + 'deletionGracePeriodSeconds', + 'managedFields', + 'ownerReferences', + 'finalizers', + 'selfLink', + ] + (vm_gitops_export_extra_metadata_remove_keys | default([])) + }} vm_gitops_export_clean_annotations: >- {{ vm_gitops_export_clean_metadata.annotations | default({}) diff --git a/roles/vm_gitops_export/tasks/_export_to_git.yml b/roles/vm_gitops_export/tasks/_export_to_git.yml index db8bf49..205534b 100644 --- a/roles/vm_gitops_export/tasks/_export_to_git.yml +++ b/roles/vm_gitops_export/tasks/_export_to_git.yml @@ -22,8 +22,23 @@ 'https://oauth2:' + vm_gitops_export_git_token + '@') }} no_log: true +- name: _export_to_git | Build HTTPS URL with Username/Password + when: + - vm_gitops_export_git_token | default("", true) | length == 0 + - vm_gitops_export_git_ssh_key | default("", true) | length == 0 + - vm_gitops_export_git_username | default("", true) | length > 0 + ansible.builtin.set_fact: + vm_gitops_export_git_clone_url: >- + {{ vm_gitops_export_git_repo_url + | regex_replace('^https://', + 'https://' + (vm_gitops_export_git_username | urlencode) + ':' + + (vm_gitops_export_git_password | urlencode) + '@') }} + no_log: true + - name: _export_to_git | Set Clone URL for SSH - when: vm_gitops_export_git_token | default("", true) | length == 0 + when: + - vm_gitops_export_git_token | default("", true) | length == 0 + - vm_gitops_export_git_username | default("", true) | length == 0 ansible.builtin.set_fact: vm_gitops_export_git_clone_url: "{{ vm_gitops_export_git_repo_url }}" diff --git a/roles/vm_gitops_export/tasks/main.yml b/roles/vm_gitops_export/tasks/main.yml index 3a85475..6abe655 100644 --- a/roles/vm_gitops_export/tasks/main.yml +++ b/roles/vm_gitops_export/tasks/main.yml @@ -26,8 +26,13 @@ that: - >- (vm_gitops_export_git_ssh_key | default("", true) | length > 0) or - (vm_gitops_export_git_token | default("", true) | length > 0) - fail_msg: "Either 'vm_gitops_export_git_ssh_key' or 'vm_gitops_export_git_token' must be provided" + (vm_gitops_export_git_token | default("", true) | length > 0) or + (vm_gitops_export_git_username | default("", true) | length > 0 and + vm_gitops_export_git_password | default("", true) | length > 0) + fail_msg: >- + Git authentication required. Provide one of: + 'vm_gitops_export_git_ssh_key', 'vm_gitops_export_git_token', + or 'vm_gitops_export_git_username' + 'vm_gitops_export_git_password' quiet: true - name: Initialize Variables