Browse Source

Merge pull request #6 from nmasse-itix/main

Implement automation + automatic update
ca-cert-and-multi-ks
Bertrand d'Hérouville 2 years ago
committed by GitHub
parent
commit
9f52a45777
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      .gitignore
  2. 2
      ansible/.gitignore
  3. 55
      ansible/README.MD
  4. 6
      ansible/ansible.cfg
  5. 77
      ansible/bootstrap-ostree.yaml
  6. 270
      ansible/build.yaml
  7. 6
      ansible/files/edge-installer.toml
  8. 6
      ansible/files/minimal.toml
  9. 5
      ansible/group_vars/all/config.yaml
  10. 5
      ansible/plugins/modules/README.md
  11. 339
      ansible/plugins/modules/start_compose2.py
  12. 148
      ansible/prerequisites.yaml
  13. 4
      ansible/requirements.yaml
  14. 68
      ansible/templates/kiosk.ks.j2
  15. 74
      ansible/templates/kiosk.toml.j2
  16. 6
      documentation/INSTALL_RHEL9.md
  17. 23
      imagebuilder/kiosk.ks
  18. 29
      imagebuilder/kiosk.toml
  19. 9
      rpms/SPECS/kiosk-config.spec

1
.gitignore

@ -0,0 +1 @@
.vscode

2
ansible/.gitignore

@ -0,0 +1,2 @@
inventory.yaml
vault.yaml

55
ansible/README.MD

@ -0,0 +1,55 @@
# Installation on RHEL 9 Automatisation
Ansible Playbook allowing to create ostree images for edge using Ansible.
## Pre-requisites
RHEL 9 pre-requisites :
- RHEL 9 is installed
- The Red Hat repositories **baseos** and **appstream** are reachable
Microshift pre-requisites :
- RHEL 9.2 or 9.3
- LVM volume group (VG) with unused space
## Pre-requisites on the target machine
```sh
sudo subscription-manager register --username $RHN_LOGIN --auto-attach
sudo subscription-manager attach --pool=$RHN_POOL_ID
```
## Ansible Config
Create a `inventory.yaml` file inside the ansible folder or define the inventory path inside the `ansible.cfg` file
Update `config.yaml` in `ansible/group_vars/all/` to match your environment.
Create an ansible vault named `vault.yaml` in `ansible/group_vars/all/` with the following content.
```yaml
blueprint_admin_password_hash: # Generate one with "mkpasswd -m bcrypt"
blueprint_kiosk_password_hash: # Generate one with "mkpasswd -m bcrypt"
kickstart_microshift_pull_secret: # Generate one on https://console.redhat.com/openshift/install/pull-secret
```
Install the required collections.
```sh
ansible-galaxy collection install -r requirements.yaml
```
## Prepare the target machine
```sh
ansible-playbook prerequisites.yaml
ansible-playbook bootstrap-ostree.yaml
```
## Regular builds
```sh
ansible-playbook build.yaml
```

6
ansible/ansible.cfg

@ -0,0 +1,6 @@
[defaults]
# Use the provided inventory
inventory = inventory.yaml
# Use a forked copy of the infra.osbuild plugins
library = plugins/modules

77
ansible/bootstrap-ostree.yaml

@ -0,0 +1,77 @@
- name: Create the initial ostree repo
hosts: all
become: false
tasks:
- name: Read blueprint
register: results
args:
executable: /usr/bin/python3
stdin: "{{ lookup('ansible.builtin.file', playbook_dir ~ '/files/minimal.toml') }}"
shell: |
import toml
import json
import sys
str=sys.stdin.read()
obj=toml.loads(str)
print(json.dumps(obj))
delegate_to: localhost
become: false
changed_when: false
- set_fact:
blueprint_name: '{{ blueprint_object.name }}'
vars:
blueprint_object: '{{ results.stdout | from_json }}'
- name: Push blueprint
infra.osbuild.push_blueprint:
blueprint: "{{ lookup('ansible.builtin.file', playbook_dir ~ '/files/minimal.toml') }}"
- name: Start ostree compose
infra.osbuild.start_compose:
blueprint: "{{ blueprint_name }}"
allow_duplicate: true
compose_type: edge-commit
timeout: "{{ compose_timeout }}"
register: builder_compose_start_out
- ansible.builtin.set_fact:
compose_id: "{{ builder_compose_start_out['result']['body']['build_id'] }}"
- name: Wait for compose to finish
infra.osbuild.wait_compose:
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 }}"
dest: "{{ tmp.path }}/{{ compose_id }}.tar"
- name: Clear directory /var/www/repo
ansible.builtin.file:
path: "{{ www_location }}/repo"
state: absent
- name: Extract compose artifact into /var/www/repo
ansible.builtin.unarchive:
src: "{{ tmp.path }}/{{ compose_id }}.tar"
dest: "{{ www_location }}"
remote_src: true
become: true
- name: Create an empty tree
ansible.builtin.file:
path: "{{ tmp.path }}/empty-tree"
mode: '0755'
state: directory
become: true
- name: Create an empty commit
ansible.builtin.shell: "ostree --repo={{ www_location }}/repo commit -b 'empty' --tree=dir={{ tmp.path }}/empty-tree"
become: true

270
ansible/build.yaml

@ -0,0 +1,270 @@
- name: Build the Kiosk images
hosts: all
become: false
tasks:
- name: Checkout the git repo
ansible.builtin.git:
repo: 'https://github.com/nmasse-itix/red-hat-kiosk.git'
dest: "{{ ansible_user_dir }}/red-hat-kiosk"
update: yes
clone: yes
- ansible.builtin.tempfile:
state: directory
suffix: -build
register: tmp
##
## Cleanup
##
- name: Get all images for removal
ansible.builtin.command: /usr/bin/composer-cli compose list
register: builder_output
changed_when: false
- name: Remove each image by UUID
ansible.builtin.command: "/usr/bin/composer-cli compose delete {{ (item | split)[0] }}"
loop: "{{ builder_output.stdout_lines }}"
loop_control:
label: "{{ (item | split)[0] }}"
changed_when: true
when: (item | split)[0] != "ID"
##
## RPM construction
##
- debug:
msg: "Starting RPM build..."
- name: Ensure ~/rpmbuild is a symbolic link
ansible.builtin.file:
src: "{{ ansible_user_dir }}/red-hat-kiosk/rpms"
dest: "{{ ansible_user_dir }}/rpmbuild"
state: link
- name: Build the kiosk-config RPMS
ansible.builtin.shell: |
spectool -g -R {{ ansible_user_dir }}/rpmbuild/SPECS/kiosk-config.spec
rpmbuild -ba {{ ansible_user_dir }}/rpmbuild/SPECS/kiosk-config.spec
- name: Build the microshift-manifests RPM
ansible.builtin.shell: |
spectool -g -R {{ ansible_user_dir }}/rpmbuild/SPECS/microshift-manifests.spec
rpmbuild -ba {{ ansible_user_dir }}/rpmbuild/SPECS/microshift-manifests.spec
- name: Ensure the VENDOR directory exists
ansible.builtin.file:
path: "{{ ansible_user_dir }}/rpmbuild/VENDOR"
state: directory
mode: '0755'
- name: Download Google Chrome RPM
ansible.builtin.get_url:
url: https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
dest: "{{ ansible_user_dir }}/rpmbuild/VENDOR/google-chrome-stable_current_x86_64.rpm"
- name: Rebuild the Google Chrome RPM
ansible.builtin.shell: |
set -Eeuo pipefail
rpmrebuild -s {{ ansible_user_dir }}/rpmbuild/SPECS/google-chrome-stable.spec -p {{ ansible_user_dir }}/rpmbuild/VENDOR/google-chrome-stable_current_x86_64.rpm
RPM=$(rpm -q {{ ansible_user_dir }}/rpmbuild/VENDOR/google-chrome-stable_current_x86_64.rpm)
mkdir -p {{ ansible_user_dir }}/rpmbuild/BUILDROOT/$RPM/
rpm2cpio {{ ansible_user_dir }}/rpmbuild/VENDOR/google-chrome-stable_current_x86_64.rpm | cpio -idmv -D {{ ansible_user_dir }}/rpmbuild/BUILDROOT/$RPM/
mv {{ ansible_user_dir }}/rpmbuild/BUILDROOT/$RPM/opt/google/ {{ ansible_user_dir }}/rpmbuild/BUILDROOT/$RPM/usr/bin/
cd {{ ansible_user_dir }}/rpmbuild/BUILDROOT/$RPM/usr/bin/
rm -f google-chrome-stable
ln -s google/chrome/google-chrome google-chrome-stable
ln -s google/chrome/google-chrome chrome
sed -i.${EPOCHREALTIME:-bak} 's|/opt/google|/usr/bin/google|g' {{ ansible_user_dir }}/rpmbuild/SPECS/google-chrome-stable.spec
rpmbuild -bb {{ ansible_user_dir }}/rpmbuild/SPECS/google-chrome-stable.spec
args:
executable: /bin/bash
register: rebuild_result
failed_when: rebuild_result.rc != 0
- name: Get built RPMS
ansible.builtin.find:
path: "{{ ansible_user_dir }}/rpmbuild/RPMS/x86_64/"
patterns: "*.rpm"
register: build_rpms
- name: Extract filenames from paths of built RPMs
ansible.builtin.set_fact:
rpm_filenames: "{{ build_rpms.files | map(attribute='path') | list }}"
- name: Copy RPMs to the repository location
ansible.builtin.copy:
src: '{{ item }}'
dest: "{{ repo_location }}"
owner: root
group: root
mode: '0644'
remote_src: yes
loop: '{{ rpm_filenames }}'
loop_control:
label: "{{ item | basename }}"
become: true
- name: Update the repository with createrepo
become: true
ansible.builtin.command:
cmd: "createrepo {{ repo_location }}"
- name: Clean dnf cache
become: true
ansible.builtin.command:
cmd: dnf clean all
##
## Ostree construction
##
- debug:
msg: "Starting ostree build..."
- name: Parse blueprint
register: results
args:
executable: /usr/bin/python3
stdin: "{{ lookup('ansible.builtin.template', 'kiosk.toml.j2') }}"
shell: |
import toml
import json
import sys
str=sys.stdin.read()
obj=toml.loads(str)
print(json.dumps(obj))
become: false
changed_when: false
- set_fact:
blueprint_name: '{{ blueprint_object.name }}'
vars:
blueprint_object: '{{ results.stdout | from_json }}'
- name: Push Blueprint
infra.osbuild.push_blueprint:
blueprint: "{{ lookup('ansible.builtin.template', 'kiosk.toml.j2') }}"
- name: Start ostree compose
start_compose2:
blueprint: "{{ blueprint_name }}"
allow_duplicate: true
compose_type: edge-commit
ostree_ref: "rhel/9/{{ ansible_facts['userspace_architecture'] }}/edge-kiosk"
ostree_parent: "rhel/9/{{ ansible_facts['userspace_architecture'] }}/edge"
ostree_url: http://{{ ansible_default_ipv4.address }}/repo
timeout: "{{ compose_timeout }}"
register: builder_compose_start_out
- ansible.builtin.set_fact:
compose_id: "{{ builder_compose_start_out['result']['body']['build_id'] }}"
- name: Wait for compose to finish
infra.osbuild.wait_compose:
compose_id: "{{ compose_id }}"
timeout: 3600
- name: Export the compose artifact
infra.osbuild.export_compose: # noqa only-builtins
compose_id: "{{ compose_id }}"
dest: "{{ tmp.path }}/{{ compose_id }}.tar"
- name: Create commit directory
ansible.builtin.file:
path: "{{ tmp.path }}/{{ compose_id }}"
mode: '0755'
state: directory
- name: Extract compose artifact
ansible.builtin.unarchive:
src: "{{ tmp.path }}/{{ compose_id }}.tar"
dest: "{{ tmp.path }}/{{ compose_id }}"
remote_src: true
- name: Pull local ostree repository
ansible.builtin.shell: ostree --repo={{ www_location }}/repo pull-local "{{ tmp.path }}/{{ compose_id }}/repo"
become: true
##
## ISO Construction
##
- debug:
msg: "Starting ISO build..."
- name: Read blueprint
register: results
args:
executable: /usr/bin/python3
stdin: "{{ lookup('ansible.builtin.file', playbook_dir ~ '/files/edge-installer.toml') }}"
shell: |
import toml
import json
import sys
str=sys.stdin.read()
obj=toml.loads(str)
print(json.dumps(obj))
delegate_to: localhost
become: false
changed_when: false
- set_fact:
blueprint_name: '{{ blueprint_object.name }}'
vars:
blueprint_object: '{{ results.stdout | from_json }}'
- name: Push blueprint
infra.osbuild.push_blueprint:
blueprint: "{{ lookup('ansible.builtin.file', playbook_dir ~ '/files/edge-installer.toml') }}"
- name: Start ostree compose
start_compose2:
blueprint: "{{ blueprint_name }}"
allow_duplicate: true
compose_type: edge-installer
ostree_ref: empty
ostree_url: http://{{ ansible_default_ipv4.address }}/repo
timeout: "{{ compose_timeout }}"
register: builder_compose_start_out
- ansible.builtin.set_fact:
compose_id: "{{ builder_compose_start_out['result']['body']['build_id'] }}"
- name: Wait for compose to finish
infra.osbuild.wait_compose:
compose_id: "{{ compose_id }}"
timeout: 3600
- name: Export the compose artifact
infra.osbuild.export_compose: # noqa only-builtins
compose_id: "{{ compose_id }}"
dest: "{{ tmp.path }}/{{ compose_id }}.iso"
- name: Create kiosk.ks from template
ansible.builtin.template:
src: "kiosk.ks.j2"
dest: "{{ tmp.path }}/kiosk.ks"
- name: Validate kiosk.ks using ksvalidator
ansible.builtin.command:
cmd: "ksvalidator {{ tmp.path }}/kiosk.ks"
- name: Create new kiosk.iso file
ansible.builtin.command:
cmd: "mkksiso -r 'inst.ks' --ks {{ tmp.path }}/kiosk.ks {{ tmp.path }}/{{ compose_id }}.iso {{ tmp.path }}/kiosk.iso"
- name: Copy new ISO to /var/www
copy:
src: "{{ tmp.path }}/kiosk.iso"
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

6
ansible/files/edge-installer.toml

@ -0,0 +1,6 @@
name = "edge-installer"
description = ""
version = "0.0.0"
modules = []
groups = []
packages = []

6
ansible/files/minimal.toml

@ -0,0 +1,6 @@
name = "minimal-rhel9"
description = "minimal blueprint for ostree commit"
version = "1.1.0"
modules = []
groups = []
distro = "rhel-93"

5
ansible/group_vars/all/config.yaml

@ -0,0 +1,5 @@
repo_location: /opt/custom-rpms
blueprint_admin_ssh_public_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFW62WJXI1ZCMfNA4w0dMpL0fsldhbEfULNGIUB0nQui nmasse@localhost.localdomain
www_location: /var/www
compose_timeout: 300
kickstart_root_disk: /dev/disk/by-path/pci-0000:00:12.0-ata-1

5
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".

339
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()

148
ansible/prerequisites.yaml

@ -0,0 +1,148 @@
- name: Install prerequisites
hosts: all
become: true
tasks:
- community.general.rhsm_repository:
name:
- rhocp-4.14-for-rhel-9-{{ ansible_facts['userspace_architecture'] }}-rpms
- fast-datapath-for-rhel-9-{{ ansible_facts['userspace_architecture'] }}-rpms
state: enabled
- name: Install EPEL release package
become: true
ansible.builtin.dnf:
name: https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
state: present
disable_gpg_check: true
- name: Install packages
ansible.builtin.dnf:
name:
- python3-toml
- createrepo
- git
- rpm-build
- rpmdevtools
- rpmrebuild
- mkpasswd
- podman
- buildah
- nginx
- lorax
- pykickstart
- osbuild-composer
- composer-cli
- cockpit-composer
- git
- firewalld
state: installed
- name: Start services
ansible.builtin.systemd:
name: "{{ item }}"
enabled: yes
state: started
loop:
- osbuild-composer.socket
- firewalld.service
- cockpit.socket
- nginx.service
- name: Adding ansible_user to the weldr group
ansible.builtin.user:
name: '{{ ansible_user | default(ansible_env.SUDO_USER) }}'
groups: weldr
append: yes
- name: Allow HTTP and HTTPS
ansible.posix.firewalld:
service: '{{ item }}'
permanent: true
immediate: true
state: enabled
loop:
- http
- https
- name: Ensure the ostree directory exists
become: true
ansible.builtin.file:
path: "{{ www_location }}"
state: directory
mode: '0755'
serole: object_r
setype: httpd_sys_content_t
seuser: system_u
- name: Configure nginx
lineinfile:
path: /etc/nginx/nginx.conf
line: "root {{ www_location }};"
regexp: "^\\s*root\\s+.*;"
- name: Restart nginx
ansible.builtin.systemd:
name: nginx.service
state: restarted
- name: Ensure the repository directory exists
become: true
ansible.builtin.file:
path: "{{ repo_location }}"
state: directory
mode: '0755'
- name: Update the repository with createrepo
become: true
ansible.builtin.command:
cmd: "createrepo {{ repo_location }}"
- name: Add custom repository
ansible.builtin.yum_repository:
name: custom
file: custom
description: Custom RPMS
baseurl: file://{{ repo_location }}
enabled: true
gpgcheck: false
- name: Add sources
infra.osbuild.repository: '{{ item }}'
loop:
- repo_name: custom packages for RHEL
type: yum-baseurl
base_url: file://{{ repo_location }}
check_gpg: false
check_ssl: false
rhsm: false
state: present
- repo_name: Red Hat OpenShift Container Platform 4.14 for RHEL 9
type: yum-baseurl
base_url: https://cdn.redhat.com/content/dist/layered/rhel9/{{ ansible_facts['userspace_architecture'] }}/rhocp/4.14/os
check_gpg: true
check_ssl: true
rhsm: true
state: present
- repo_name: Fast Datapath for RHEL 9
type: yum-baseurl
base_url: https://cdn.redhat.com/content/dist/layered/rhel9/{{ ansible_facts['userspace_architecture'] }}/fast-datapath/os
check_gpg: true
check_ssl: true
rhsm: true
state: present
- repo_name: Extra Packages for Enterprise Linux
type: yum-baseurl
base_url: http://mirror.in2p3.fr/pub/epel/9/Everything/{{ ansible_facts['userspace_architecture'] }}/
check_gpg: false
check_ssl: false
rhsm: false
state: present
loop_control:
label: '{{ item.repo_name }}'
- name: Install packages on the ansible controller
dnf:
name:
- python3-toml
state: installed
delegate_to: localhost

4
ansible/requirements.yaml

@ -0,0 +1,4 @@
collections:
- infra.osbuild
- community.general
- ansible.posix

68
ansible/templates/kiosk.ks.j2

@ -0,0 +1,68 @@
##
## Environment setup
##
# French I18n
lang fr_FR.UTF-8
# French keyboard layout
keyboard fr
# Timezone is UTC to avoid issue with DST
timezone UTC --utc
# Configure NTP
timesource --ntp-server=rhel.pool.ntp.org
# Which action to perform after install: poweroff or reboot
reboot
# Install mode: text (interactive installs) or cmdline (unattended installs)
text
##
## 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=1024 --grow --ondisk={{ kickstart_root_disk }}
volgroup rhel pv.01
logvol / --fstype="xfs" --size=10240 --name=root --vgname=rhel
##
## Network configuration
##
# Configure the first network device
network --bootproto=dhcp --device=enp1s0 --noipv6 --activate
# Configure hostname
network --hostname=kiosk.localdomain
##
## Ostree installation
##
# 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
##
## Post install scripts
##
%post --log=/var/log/anaconda/post-install.log --erroronfail
# Add the pull secret to CRI-O and set root user-only read/write permissions
cat > /etc/crio/openshift-pull-secret << 'EOF'
{{ kickstart_microshift_pull_secret }}
EOF
chmod 600 /etc/crio/openshift-pull-secret
# Configure the firewall with the mandatory rules for MicroShift
firewall-offline-cmd --zone=trusted --add-source=10.42.0.0/16
firewall-offline-cmd --zone=trusted --add-source=169.254.169.1
# Do not ask password for sudo
sed -i.post-install -e "s/^%wheel\tALL=(ALL)\tALL/%wheel ALL=(ALL) NOPASSWD: ALL/" /etc/sudoers
%end

74
ansible/templates/kiosk.toml.j2

@ -0,0 +1,74 @@
name = "kiosk"
description = "Example Kiosk"
version = "0.0.8"
modules = []
groups = []
[[packages]]
name = "kiosk-config"
version = "*"
[[packages]]
name = "cockpit"
[[packages]]
name = "microshift-manifests"
version = "*"
[[packages]]
name = "cockpit-system"
[customizations]
hostname = "kiosk.local"
[customizations.services]
enabled = ["cockpit.socket", "sshd", "microshift", "rpm-ostreed", "rpm-ostreed-automatic.timer"]
[customizations.timezone]
timezone = "Europe/Paris"
ntpservers = ["0.fr.pool.ntp.org", "1.fr.pool.ntp.org"]
[customizations.locale]
languages = ["fr_FR.UTF-8"]
keyboard = "fr"
#22 ssh / 9090 cockpit / 6443 microshift
[customizations.firewall]
ports = ["22:tcp", "30000:tcp", "9090:tcp", "6443:tcp"]
##
## Automatic updates
##
## This file is used by the rpm-ostreed service that is triggered by the
## rpm-ostreed-automatic systemd timer:
##
## [Timer]
## OnBootSec=1h # 1 hour after boot
## OnUnitInactiveSec=1d # 1 day after last check
##
## But you can trigger a check manually with:
##
## sudo rpm-ostree upgrade --trigger-automatic-update-policy
##
[[customizations.files]]
path = "/etc/rpm-ostreed.conf"
data = """[Daemon]
AutomaticUpdatePolicy=apply
"""
[[customizations.user]]
name = "admin"
description = "admin"
password = '{{ blueprint_admin_password_hash }}'
key = "{{ blueprint_admin_ssh_public_key }}"
home = "/home/admin/"
shell = "/usr/bin/bash"
groups = ["users", "wheel"]
[[customizations.user]]
name = "kiosk"
description = "kiosk"
password = '{{ blueprint_kiosk_password_hash }}'
home = "/home/kiosk/"
shell = "/bin/bash"

6
documentation/INSTALL_RHEL9.md

@ -17,8 +17,9 @@ Microshift pre-requisites :
```sh ```sh
sudo subscription-manager register --username $RHN_LOGIN --auto-attach sudo subscription-manager register --username $RHN_LOGIN --auto-attach
sudo subscription-manager attach --pool=$RHN_POOL_ID sudo subscription-manager attach --pool=$RHN_POOL_ID
sudo dnf install -y osbuild-composer composer-cli cockpit-composer sudo dnf install -y osbuild-composer composer-cli cockpit-composer git firewalld python3-toml
sudo systemctl enable --now osbuild-composer.socket sudo systemctl enable --now osbuild-composer.socket
sudo systemctl enable --now firewalld
sudo systemctl enable --now cockpit.socket sudo systemctl enable --now cockpit.socket
sudo systemctl restart osbuild-composer sudo systemctl restart osbuild-composer
sudo usermod -a -G weldr "$(id -un)" sudo usermod -a -G weldr "$(id -un)"
@ -45,7 +46,7 @@ baseos
## Clone this repository ## Clone this repository
```sh ```sh
git clone https://github.com/nmasse-itix/red-hat-kiosk.git git clone https://github.com/ePietry/red-hat-kiosk.git
cd red-hat-kiosk cd red-hat-kiosk
export GIT_REPO_CLONE="$PWD" export GIT_REPO_CLONE="$PWD"
``` ```
@ -181,6 +182,7 @@ rpmbuild -ba $HOME/rpmbuild/SPECS/microshift-manifests.spec
Rebuild the Google Chrome RPM Rebuild the Google Chrome RPM
```sh ```sh
sudo dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
mkdir $HOME/rpmbuild/VENDOR mkdir $HOME/rpmbuild/VENDOR
curl -s -Lo $HOME/rpmbuild/VENDOR/google-chrome-stable_current_x86_64.rpm https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm curl -s -Lo $HOME/rpmbuild/VENDOR/google-chrome-stable_current_x86_64.rpm https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
rpmrebuild -s $HOME/rpmbuild/SPECS/google-chrome-stable.spec -p $HOME/rpmbuild/VENDOR/google-chrome-stable_current_x86_64.rpm rpmrebuild -s $HOME/rpmbuild/SPECS/google-chrome-stable.spec -p $HOME/rpmbuild/VENDOR/google-chrome-stable_current_x86_64.rpm

23
imagebuilder/kiosk.ks

@ -21,32 +21,15 @@ reboot
text text
## ##
## Storage configuration ## Storage configuration for only one disk
##
# 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
## /dev/disk/by-path/pci-0000:00:12.0-ata-1 instead of sda when sda is taken by the usb stick ## /dev/disk/by-path/pci-0000:00:12.0-ata-1 instead of sda when sda is taken by the usb stick
## ##
zerombr zerombr
clearpart --all --initlabel clearpart --all --initlabel
reqpart --add-boot reqpart --add-boot
part pv.01 --size=10G --ondisk=/dev/disk/by-path/pci-0000:00:12.0-ata-1 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 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 ## Network configuration

29
imagebuilder/kiosk.toml

@ -22,7 +22,7 @@ name = "cockpit-system"
hostname = "kiosk.local" hostname = "kiosk.local"
[customizations.services] [customizations.services]
enabled = ["cockpit.socket", "sshd", "microshift"] enabled = ["cockpit.socket", "sshd", "microshift", "rpm-ostreed", "rpm-ostreed-automatic.timer"]
[customizations.timezone] [customizations.timezone]
timezone = "Europe/Paris" timezone = "Europe/Paris"
@ -36,6 +36,26 @@ keyboard = "fr"
[customizations.firewall] [customizations.firewall]
ports = ["22:tcp", "30000:tcp", "9090:tcp", "6443:tcp"] ports = ["22:tcp", "30000:tcp", "9090:tcp", "6443:tcp"]
##
## Automatic updates
##
## This file is used by the rpm-ostreed service that is triggered by the
## rpm-ostreed-automatic systemd timer:
##
## [Timer]
## OnBootSec=1h # 1 hour after boot
## OnUnitInactiveSec=1d # 1 day after last check
##
## But you can trigger a check manually with:
##
## sudo rpm-ostree upgrade --trigger-automatic-update-policy
##
[[customizations.files]]
path = "/etc/rpm-ostreed.conf"
data = """[Daemon]
AutomaticUpdatePolicy=apply
"""
[[customizations.user]] [[customizations.user]]
name = "admin" name = "admin"
description = "admin" description = "admin"
@ -44,3 +64,10 @@ key = "__ADMIN_SSH_PUBLIC_KEY__"
home = "/home/admin/" home = "/home/admin/"
shell = "/usr/bin/bash" shell = "/usr/bin/bash"
groups = ["users", "wheel"] groups = ["users", "wheel"]
[[customizations.user]]
name = "kiosk"
description = "kiosk"
password = '__KIOSK_PASSWORD__'
home = "/home/kiosk/"
shell = "/bin/bash"

9
rpms/SPECS/kiosk-config.spec

@ -72,8 +72,13 @@ install -m 0755 -D kiosk-app %{buildroot}/usr/bin/kiosk-app
%attr(0755, root, root) /usr/bin/kiosk-app %attr(0755, root, root) /usr/bin/kiosk-app
%pre %pre
getent group kiosk >/dev/null 2>&1 || groupadd -r kiosk ##
getent passwd kiosk >/dev/null 2>&1 || useradd -r -N -g kiosk -d /home/kiosk -m kiosk ## HEADS UP !!!
##
## The kiosk user needs to be created in the kickstart now.
##
#getent group kiosk >/dev/null 2>&1 || groupadd -r kiosk
#getent passwd kiosk >/dev/null 2>&1 || useradd -r -N -g kiosk -d /home/kiosk -m kiosk
%post %post
%systemd_user_post com.redhat.Kiosk.SampleApp.service %systemd_user_post com.redhat.Kiosk.SampleApp.service

Loading…
Cancel
Save