diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index 5509ce7..2d1ae5d 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -2,6 +2,5 @@ # Use the provided inventory inventory = inventory.yaml -# To get the vault password from the KDE Wallet -vault_identity_list = itix@/home/nmasse/local/bin/get-vault-password - +# Use a forked copy of the infra.osbuild plugins +library = plugins/modules diff --git a/ansible/build.yaml b/ansible/build.yaml index 3f5f89d..1659e3d 100644 --- a/ansible/build.yaml +++ b/ansible/build.yaml @@ -9,6 +9,11 @@ update: yes clone: yes + - ansible.builtin.tempfile: + state: directory + suffix: -build + register: tmp + ## ## RPM construction ## @@ -127,7 +132,7 @@ blueprint: "{{ lookup('ansible.builtin.template', 'kiosk.toml.j2') }}" - name: Start ostree compose - infra.osbuild.start_compose: + start_compose2: blueprint: "{{ blueprint_name }}" allow_duplicate: true compose_type: edge-commit @@ -145,11 +150,6 @@ compose_id: "{{ compose_id }}" timeout: 3600 - - ansible.builtin.tempfile: - state: directory - suffix: build - register: tmp - - name: Export the compose artifact infra.osbuild.export_compose: # noqa only-builtins compose_id: "{{ compose_id }}" @@ -204,7 +204,7 @@ blueprint: "{{ lookup('ansible.builtin.file', playbook_dir ~ '/files/edge-installer.toml') }}" - name: Start ostree compose - infra.osbuild.start_compose: + start_compose2: blueprint: "{{ blueprint_name }}" allow_duplicate: true compose_type: edge-installer @@ -221,11 +221,6 @@ compose_id: "{{ compose_id }}" timeout: 3600 - - ansible.builtin.tempfile: - state: directory - suffix: build - register: tmp - - name: Export the compose artifact infra.osbuild.export_compose: # noqa only-builtins compose_id: "{{ compose_id }}" @@ -250,3 +245,9 @@ dest: "{{ www_location }}/kiosk.iso" remote_src: true become: true + + post_tasks: + - ansible.builtin.file: + path: "{{ tmp.path }}.iso" + state: absent + when: tmp is defined diff --git a/ansible/plugins/modules/README.md b/ansible/plugins/modules/README.md new file mode 100644 index 0000000..c296fea --- /dev/null +++ b/ansible/plugins/modules/README.md @@ -0,0 +1,5 @@ +# README + +This is a modified version of the start_compose module from the repo https://github.com/redhat-cop/infra.osbuild, commit 6e3416233c84623b2edd503a4b50d15c61d6c155. + +The module has been patched to specify the ostree ref when starting a compose of type "ostree-commit". diff --git a/ansible/plugins/modules/start_compose2.py b/ansible/plugins/modules/start_compose2.py new file mode 100644 index 0000000..7280673 --- /dev/null +++ b/ansible/plugins/modules/start_compose2.py @@ -0,0 +1,339 @@ +#!/usr/bin/python +# Copyright: Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = """ +--- +module: start_compose2 +short_description: Start an ostree compose +description: + - Start an ostree compose +author: + - Adam Miller (@maxamillion) + - Chris Santiago (@resoluteCoder) +options: + blueprint: + description: + - Name of blueprint to iniate a build for + type: str + required: true + size: + description: + - Image size expressed in MiB + type: int + default: 0 + required: false + profile: + description: + - Path to profile toml file + type: str + default: "" + required: false + image_name: + description: + - Image name + type: str + default: "" + required: false + allow_duplicate: + description: + - Allow a duplicate version'd compose. + - (Default osbuild composer functionality is to allow duplicate composes) + type: bool + default: True + required: false + compose_type: + description: + - type of compose + type: str + default: "edge-commit" + required: false + choices: + - ami + - edge-commit + - edge-container + - edge-installer + - edge-raw-image + - edge-simplified-installer + - image-installer + - oci + - openstack + - qcow2 + - tar + - vhd + - vmdk + - iot-commit + - iot-container + - iot-installer + - iot-raw-image + - container + ostree_ref: + description: + - ostree ref + type: str + default: "" + required: false + ostree_parent: + description: + - ostree parent + type: str + default: "" + required: false + ostree_url: + description: + - ostree URL + type: str + default: "" + required: false + timeout: + description: + - timeout for osbuild-compose requests, in seconds + type: int + default: 120 + required: false +notes: + - THIS MODULE IS NOT IDEMPOTENT UNLESS C(allow_duplicate) is set to C(false) + - The params C(profile) and C(image_name) are required together. + - The C(profile) option is not fully implemented at this time. +""" + +EXAMPLES = """ +- name: Start ostree compose size 4096 + start_compose2: + blueprint: rhel-for-edge-demo + image_name: testimage + size: 4096 + +- name: Start ostree compose with idempotent transaction + start_compose2: + blueprint: rhel-for-edge-demo + allow_duplicate: false +""" +import json # noqa E402 +import socket +from typing import Any # noqa E402 + +from ansible.module_utils.basic import AnsibleModule # noqa E402 +from ansible_collections.infra.osbuild.plugins.module_utils.weldr import Weldr # noqa E402 + +argument_spec = dict( + blueprint=dict(type="str", required=True), + size=dict(type="int", required=False, default=0), + profile=dict(type="str", required=False, default=""), + image_name=dict(type="str", required=False, default=""), + allow_duplicate=dict(type="bool", required=False, default=True), + compose_type=dict( + type="str", + required=False, + default="edge-commit", + choices=[ + "ami", + "edge-commit", + "edge-container", + "edge-installer", + "edge-raw-image", + "edge-simplified-installer", + "image-installer", + "oci", + "openstack", + "qcow2", + "tar", + "vhd", + "vmdk", + "iot-commit", + "iot-container", + "iot-installer", + "iot-raw-image", + "container", + ], + ), + ostree_ref=dict(type="str", required=False, default=""), + ostree_parent=dict(type="str", required=False, default=""), + ostree_url=dict(type="str", required=False, default=""), + timeout=dict(type="int", required=False, default=120), +) + + +def start_compose(module, weldr): + changed: bool = False + dupe_compose: list = [] + blueprint_info: dict = weldr.api.get_blueprints_info(module.params["blueprint"]) + blueprint_version: int = blueprint_info["blueprints"][0]["version"] + + # Add check if compose_type is supported + supported_compose_type: dict = weldr.api.get_compose_types() + + is_supported: dict = next((item for item in supported_compose_type["types"] if item["name"] == module.params["compose_type"]), {}) + + if not is_supported: + module.fail_json( + msg="%s is not a valid image type, valid types are: %s" + % (module.params["compose_type"], [[v for k, v in t.items() if k == "name"] for t in supported_compose_type["types"]]), + changed=changed + ) + else: + if not is_supported["enabled"]: + module.fail_json( + msg="%s is not a supported image type, supported image types are: %s" + % (module.params["compose_type"], [[v for k, v in t.items() if k == "enabled" and v is True] for t in supported_compose_type["types"]]), + changed=changed + ) + + if not module.params["allow_duplicate"]: + # only do all this query and filtering if needed + + compose_queue: dict = weldr.api.get_compose_queue() + # {"new":[],"run":[{"id":"930a1584-8737-4b61-ba77-582780f0ff2d","blueprint":"base-image-with-tmux","version":"0.0.5","compose_type":"edge-commit","image_size":0,"queue_status":"RUNNING","job_created":1654620015.4107578,"job_started":1654620015.415151}]} + + compose_queue_run_dupe: list = [ + compose for compose in compose_queue["run"] if (compose["blueprint"] == module.params["blueprint"]) and (compose["version"] == blueprint_version) + ] + compose_queue_new_dupe: list = [ + compose for compose in compose_queue["new"] if (compose["blueprint"] == module.params["blueprint"]) and (compose["version"] == blueprint_version) + ] + + compose_finished: dict = weldr.api.get_compose_finished() + # {"finished":[{"id":"930a1584-8737-4b61-ba77-582780f0ff2d","blueprint":"base-image-with-tmux","version":"0.0.5","compose_type":"edge-commit","image_size":8192,"queue_status":"FINISHED","job_created":1654620015.4107578,"job_started":1654620015.415151,"job_finished":1654620302.9069786}]} + compose_finished_dupe: list = [ + compose + for compose in compose_finished["finished"] + if (compose["blueprint"] == module.params["blueprint"]) and (compose["version"] == blueprint_version) + ] + + compose_failed: dict = weldr.api.get_compose_failed() + # {"failed":[]} + compose_failed_dupe: list = [ + compose + for compose in compose_failed["failed"] + if (compose["blueprint"] == module.params["blueprint"]) and (compose["version"] == blueprint_version) + ] + + dupe_compose: list = compose_queue_run_dupe + compose_queue_new_dupe + compose_failed_dupe + compose_finished_dupe + + if module.params["allow_duplicate"] or (len(dupe_compose) == 0): + # FIXME - build to POST payload and POST that ish + compose_settings: dict[str, Any] = { + "blueprint_name": module.params["blueprint"], + "compose_type": module.params["compose_type"], + "branch": "master", + "size": module.params["size"], + } + + if "edge-commit" in module.params["compose_type"] or "installer" in module.params["compose_type"] or "raw" in module.params["compose_type"]: + compose_settings["ostree"] = { + "ref": module.params["ostree_ref"], + "parent": module.params["ostree_parent"], + "url": module.params["ostree_url"], + } + + try: + result: dict = weldr.api.post_compose(json.dumps(compose_settings), timeout=module.params["timeout"]) + except socket.timeout: + # it's possible we don't get a response back from weldr because on the + # very first run including a new content source composer will build a repo cache + # and when that happens we get an empty JSON response + + compose_queue: dict = weldr.api.get_compose_queue() + # {"new":[],"run":[{"id":"930a1584-8737-4b61-ba77-582780f0ff2d","blueprint":"base-image-with-tmux","version":"0.0.5","compose_type":"edge-commit","image_size":0,"queue_status":"RUNNING","job_created":1654620015.4107578,"job_started":1654620015.415151}]} + + submitted_compose_uuid: str = "" + + submitted_compose_found_run: list[dict[str, str]] = [ + compose + for compose in compose_queue["run"] + if (compose["blueprint"] == module.params["blueprint"]) and (compose["version"] == blueprint_version) + ] + if submitted_compose_found_run: + # we expect it to be RUNNING, so check that first + submitted_compose_uuid: str = submitted_compose_found_run[0]["id"] + else: + # didn't find it running, check for NEW queue status + submitted_compose_found_new: list = [ + compose + for compose in compose_queue["new"] + if (compose["blueprint"] == module.params["blueprint"]) and (compose["version"] == blueprint_version) + ] + + if submitted_compose_found_new: + submitted_compose_uuid: str = submitted_compose_found_new[0]["id"] + + else: + # it's not RUNNING and not NEW, so check for FAILURE state + compose_failed: dict = weldr.api.get_compose_failed() + # {"failed":[]} + submitted_compose_found_failed: list = [ + compose + for compose in compose_failed["failed"] + if (compose["blueprint"] == module.params["blueprint"]) and (compose["version"] == blueprint_version) + ] + if submitted_compose_found_failed: + submitted_compose_uuid: str = submitted_compose_found_failed[0]["id"] + else: + module.fail_json( + msg="Unable to determine state of build, check osbuild-composer system logs. Also, consider increasing the request timeout", + changed=changed + ) + + if submitted_compose_uuid: + result: dict = weldr.api.get_compose_status(submitted_compose_uuid) + result['body'] = { + 'build_id': submitted_compose_uuid + } + + if "status_code" in result.keys(): + if result["status_code"] >= 400: + module.fail_json( + msg="Compose returned body: {0}, msg {1}, and status_code {2}".format(result["body"], result["error_msg"], result["status_code"]), + changed=changed + ) + + # Having received a non-400+ response, we know a compose has started + changed: bool = True + + compose_output_types: dict[str, list[str]] = { + "tar": ["tar", "edge-commit", "iot-commit", "edge-container", "iot-container", "container"], + "iso": ["edge-installer", "edge-simplified-installer", "iot-installer", "image-installer"], + "qcow2": ["qcow2", "openstack", "oci"], + "vmdk": ["vmdk"], + "vhd": ["vhd"], + "raw.xz": ["edge-raw-image", "iot-raw-image"], + "ami": ["ami"], + } + + output_type: str = "" + for compose_type, compose_type_list in compose_output_types.items(): + if module.params["compose_type"] in compose_type_list: + output_type: str = compose_type + result["output_type"] = output_type + + module.exit_json(msg="Compose submitted to queue", result=result, changed=changed) + + else: + changed: bool = False + module.exit_json( + msg="Not queuing a duplicate versioned compose without allow_duplicate set to true", + changed=changed, + ) + + +def main() -> None: + module: AnsibleModule = AnsibleModule( + argument_spec=argument_spec, + required_together=[["image_name", "profile"]], + required_if=[ + ["compose_type", "edge-installer", ["ostree_url"]], + ["compose_type", "iot-installer", ["ostree_url"]], + ], + ) + weldr: Weldr = Weldr(module) + start_compose(module, weldr) + + +if __name__ == "__main__": + main() diff --git a/ansible/templates/kiosk.ks.j2 b/ansible/templates/kiosk.ks.j2 index eee7d1e..c9f0a87 100644 --- a/ansible/templates/kiosk.ks.j2 +++ b/ansible/templates/kiosk.ks.j2 @@ -21,32 +21,15 @@ reboot text ## -## Storage configuration -## - -# Clear the target disk -zerombr - -# Remove existing partitions -clearpart --all --initlabel - -# Automatically create partitions required by hardware platform -# and add a separate /boot partition -reqpart --add-boot - - -## -## Alternative partitioning on only one disk +## Storage configuration for only one disk ## /dev/disk/by-path/pci-0000:00:12.0-ata-1 instead of sda when sda is taken by the usb stick ## zerombr clearpart --all --initlabel reqpart --add-boot -part pv.01 --size=10240 --ondisk=/dev/disk/by-path/pci-0000:00:12.0-ata-1 -volgroup system pv.01 -logvol / --fstype="xfs" --size=1 --grow --name=root --vgname=system -part pv.02 --size=1 --grow --ondisk=/dev/disk/by-path/pci-0000:00:12.0-ata-1 -volgroup data pv.02 +part pv.01 --size=1024 --grow --ondisk=/dev/disk/by-path/pci-0000:00:12.0-ata-1 +volgroup rhel pv.01 +logvol / --fstype="xfs" --size=10240 --name=root --vgname=rhel ## ## Network configuration @@ -62,9 +45,6 @@ network --hostname=kiosk.localdomain ## Ostree installation ## -# Use this line if creating an Edge Installer ISO that includes a local ostree commit -#ostreesetup --nogpg --osname=rhel --remote=edge --url=file:///run/install/repo/ostree/repo --ref=rhel/9/x86_64/edge - # Use this to fetch from a remote URL ostreesetup --nogpg --osname=rhel --remote=edge --url=http://{{ ansible_default_ipv4.address }}/repo --ref=rhel/9/x86_64/edge-kiosk diff --git a/imagebuilder/kiosk.ks b/imagebuilder/kiosk.ks index 839321b..2126483 100644 --- a/imagebuilder/kiosk.ks +++ b/imagebuilder/kiosk.ks @@ -21,32 +21,15 @@ reboot text ## -## Storage configuration -## - -# Clear the target disk -zerombr - -# Remove existing partitions -clearpart --all --initlabel - -# Automatically create partitions required by hardware platform -# and add a separate /boot partition -reqpart --add-boot - - -## -## Alternative partitioning on only one disk +## Storage configuration for only one disk ## /dev/disk/by-path/pci-0000:00:12.0-ata-1 instead of sda when sda is taken by the usb stick ## zerombr clearpart --all --initlabel reqpart --add-boot part pv.01 --size=10240 --ondisk=/dev/disk/by-path/pci-0000:00:12.0-ata-1 -volgroup system pv.01 +volgroup rhel pv.01 logvol / --fstype="xfs" --size=1 --grow --name=root --vgname=system -part pv.02 --size=1 --grow --ondisk=/dev/disk/by-path/pci-0000:00:12.0-ata-1 -volgroup data pv.02 ## ## Network configuration