diff --git a/ci/playbooks/bootc-ipa-pre-deploy.yml b/ci/playbooks/bootc-ipa-pre-deploy.yml new file mode 100644 index 00000000..9580a5b0 --- /dev/null +++ b/ci/playbooks/bootc-ipa-pre-deploy.yml @@ -0,0 +1,284 @@ +--- +- name: Build and host bootc IPA artifacts + hosts: "{{ cifmw_target_hook_host | default(cifmw_target_host | default('localhost')) }}" + gather_facts: false + vars: + cifmw_bootc_ipa_artifact_dir: "{{ cifmw_basedir }}/artifacts/bootc-ipa" + cifmw_bootc_ipa_build_dir: "{{ ansible_user_dir }}/bootc-ipa-build" + cifmw_bootc_ipa_build_enabled: false + cifmw_bootc_ipa_build_output_prefix: "ipa-centos9-stable-2025.2-podman" + cifmw_bootc_ipa_release: "9-stream" + cifmw_bootc_ipa_branch: "stable/2025.2" + cifmw_bootc_ipa_flavor: "centos9" + cifmw_bootc_ipa_distro: "centos" + cifmw_bootc_ipa_server_namespace: "openstack" + cifmw_bootc_ipa_server_name: "bootc-ipa-artifacts" + cifmw_bootc_ipa_server_port: 8080 + cifmw_bootc_ipa_server_image: "quay.io/prometheus/busybox:latest" + cifmw_bootc_ipa_published_basename: >- + ipa-{{ cifmw_bootc_ipa_flavor }}-{{ cifmw_bootc_ipa_branch | replace('/', '-') }} + cifmw_bootc_ipa_raw_kernel_file: >- + {{ cifmw_bootc_ipa_artifact_dir }}/{{ cifmw_bootc_ipa_build_output_prefix }}.kernel + cifmw_bootc_ipa_raw_initramfs_file: >- + {{ cifmw_bootc_ipa_artifact_dir }}/{{ cifmw_bootc_ipa_build_output_prefix }}.initramfs + cifmw_bootc_ipa_kernel_file: >- + {{ cifmw_bootc_ipa_artifact_dir }}/{{ cifmw_bootc_ipa_published_basename }}.kernel + cifmw_bootc_ipa_initramfs_file: >- + {{ cifmw_bootc_ipa_artifact_dir }}/{{ cifmw_bootc_ipa_published_basename }}.initramfs + cifmw_bootc_ipa_tarball: >- + {{ cifmw_bootc_ipa_artifact_dir }}/{{ cifmw_bootc_ipa_published_basename }}.tar.gz + cifmw_bootc_ipa_service_url: >- + http://{{ cifmw_bootc_ipa_server_name }}.{{ cifmw_bootc_ipa_server_namespace }}.svc.cluster.local:{{ cifmw_bootc_ipa_server_port }} + tasks: + - name: Ensure IPA artifact directory exists + ansible.builtin.file: + path: "{{ cifmw_bootc_ipa_artifact_dir }}" + state: directory + mode: "0755" + + - name: Ensure IPA build directory exists + when: cifmw_bootc_ipa_build_enabled | bool + ansible.builtin.file: + path: "{{ cifmw_bootc_ipa_build_dir }}" + state: directory + mode: "0755" + + - name: Write minimal container steps file + when: cifmw_bootc_ipa_build_enabled | bool + ansible.builtin.copy: + dest: "{{ cifmw_bootc_ipa_build_dir }}/mysteps.yaml" + content: | + steps: [] + mode: "0644" + + - name: Build podman-enabled IPA artifacts + when: cifmw_bootc_ipa_build_enabled | bool + args: + chdir: "{{ cifmw_bootc_ipa_artifact_dir }}" + creates: "{{ cifmw_bootc_ipa_raw_initramfs_file }}" + executable: /bin/bash + ansible.builtin.shell: | + set -euo pipefail + python3 -m venv "{{ cifmw_bootc_ipa_build_dir }}/.venv" + . "{{ cifmw_bootc_ipa_build_dir }}/.venv/bin/activate" + python -m pip install --upgrade pip setuptools wheel + python -m pip install ironic-python-agent-builder + export DIB_ALLOW_ARBITRARY_CONTAINERS=true + export DIB_RUNNER=podman + export DIB_STEPS_FILE_PATH="{{ cifmw_bootc_ipa_build_dir }}/mysteps.yaml" + ironic-python-agent-builder \ + -o "{{ cifmw_bootc_ipa_build_output_prefix }}" \ + -r "{{ cifmw_bootc_ipa_release }}" \ + -b "{{ cifmw_bootc_ipa_branch }}" \ + -e ironic-python-agent-podman \ + -v "{{ cifmw_bootc_ipa_distro }}" + + - name: Check for raw IPA artifacts + ansible.builtin.stat: + path: "{{ item }}" + loop: + - "{{ cifmw_bootc_ipa_raw_kernel_file }}" + - "{{ cifmw_bootc_ipa_raw_initramfs_file }}" + register: cifmw_bootc_ipa_raw_artifacts + + - name: Assert raw IPA artifacts exist + ansible.builtin.assert: + that: + - cifmw_bootc_ipa_raw_artifacts.results | map(attribute='stat.exists') | min + fail_msg: >- + Missing raw IPA artifacts. Expected + {{ cifmw_bootc_ipa_raw_kernel_file }} and + {{ cifmw_bootc_ipa_raw_initramfs_file }}. + + - name: Copy kernel to published filename + ansible.builtin.copy: + src: "{{ cifmw_bootc_ipa_raw_kernel_file }}" + dest: "{{ cifmw_bootc_ipa_kernel_file }}" + mode: "0644" + remote_src: true + + - name: Copy initramfs to published filename + ansible.builtin.copy: + src: "{{ cifmw_bootc_ipa_raw_initramfs_file }}" + dest: "{{ cifmw_bootc_ipa_initramfs_file }}" + mode: "0644" + remote_src: true + + - name: Package IPA tarball with downloader-compatible filenames + args: + chdir: "{{ cifmw_bootc_ipa_artifact_dir }}" + executable: /bin/bash + ansible.builtin.shell: | + set -euo pipefail + tar -caf "{{ cifmw_bootc_ipa_tarball }}" \ + "{{ cifmw_bootc_ipa_published_basename }}.kernel" \ + "{{ cifmw_bootc_ipa_published_basename }}.initramfs" + + - name: Ensure IPA hosting namespace exists + kubernetes.core.k8s: + kubeconfig: "{{ cifmw_openshift_kubeconfig }}" + state: present + definition: + apiVersion: v1 + kind: Namespace + metadata: + name: "{{ cifmw_bootc_ipa_server_namespace }}" + + - name: Create IPA hosting deployment + kubernetes.core.k8s: + kubeconfig: "{{ cifmw_openshift_kubeconfig }}" + state: present + definition: + apiVersion: apps/v1 + kind: Deployment + metadata: + name: "{{ cifmw_bootc_ipa_server_name }}" + namespace: "{{ cifmw_bootc_ipa_server_namespace }}" + spec: + replicas: 1 + selector: + matchLabels: + app: "{{ cifmw_bootc_ipa_server_name }}" + template: + metadata: + labels: + app: "{{ cifmw_bootc_ipa_server_name }}" + spec: + containers: + - name: server + image: "{{ cifmw_bootc_ipa_server_image }}" + command: + - sh + - -c + - >- + mkdir -p /srv && + exec busybox httpd -f -p {{ cifmw_bootc_ipa_server_port }} -h /srv + ports: + - containerPort: "{{ cifmw_bootc_ipa_server_port }}" + volumeMounts: + - name: content + mountPath: /srv + volumes: + - name: content + emptyDir: {} + + - name: Create IPA hosting service + kubernetes.core.k8s: + kubeconfig: "{{ cifmw_openshift_kubeconfig }}" + state: present + definition: + apiVersion: v1 + kind: Service + metadata: + name: "{{ cifmw_bootc_ipa_server_name }}" + namespace: "{{ cifmw_bootc_ipa_server_namespace }}" + spec: + selector: + app: "{{ cifmw_bootc_ipa_server_name }}" + ports: + - name: http + port: "{{ cifmw_bootc_ipa_server_port }}" + targetPort: "{{ cifmw_bootc_ipa_server_port }}" + + - name: Wait for IPA hosting pod to be ready + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + ansible.builtin.command: + cmd: >- + oc wait pod -n {{ cifmw_bootc_ipa_server_namespace }} + -l app={{ cifmw_bootc_ipa_server_name }} + --for=condition=Ready + --timeout=300s + + - name: Get IPA hosting pod name + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + ansible.builtin.command: + cmd: >- + oc get pod -n {{ cifmw_bootc_ipa_server_namespace }} + -l app={{ cifmw_bootc_ipa_server_name }} + -o jsonpath='{.items[0].metadata.name}' + register: cifmw_bootc_ipa_server_pod + changed_when: false + + - name: Copy IPA tarball into hosting pod + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + ansible.builtin.command: + cmd: >- + oc cp + "{{ cifmw_bootc_ipa_tarball }}" + "{{ cifmw_bootc_ipa_server_namespace }}/{{ cifmw_bootc_ipa_server_pod.stdout }}:/srv/{{ cifmw_bootc_ipa_tarball | basename }}" + + - name: Verify IPA tarball is served from hosting pod + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + ansible.builtin.command: + cmd: >- + oc exec -n {{ cifmw_bootc_ipa_server_namespace }} + {{ cifmw_bootc_ipa_server_pod.stdout }} -- + sh -c "wget -qO /dev/null http://127.0.0.1:{{ cifmw_bootc_ipa_server_port }}/{{ cifmw_bootc_ipa_tarball | basename }}" + + - name: Feed generated bootc IPA values back to main play + ansible.builtin.copy: + dest: "{{ cifmw_basedir }}/artifacts/{{ step }}_{{ hook_name }}.yml" + mode: "0644" + content: | + cifmw_bootc_ipa_service_url: "{{ cifmw_bootc_ipa_service_url }}" + cifmw_edpm_prepare_extra_vars: + BMO_IPA_BASEURI: "{{ cifmw_bootc_ipa_service_url }}" + BMO_IPA_BRANCH: "{{ cifmw_bootc_ipa_branch }}" + BMO_IPA_FLAVOR: "{{ cifmw_bootc_ipa_flavor }}" + + - name: Find ironic configmap namespace + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + ansible.builtin.command: + cmd: >- + oc get configmap ironic-bmo-configmap -A + -o jsonpath='{.items[0].metadata.namespace}' + register: cifmw_bootc_ipa_ironic_namespace + changed_when: false + failed_when: false + + - name: Patch live ironic configmap with bootc IPA source + when: cifmw_bootc_ipa_ironic_namespace.rc == 0 and cifmw_bootc_ipa_ironic_namespace.stdout | length > 0 + kubernetes.core.k8s: + kubeconfig: "{{ cifmw_openshift_kubeconfig }}" + state: patched + definition: + apiVersion: v1 + kind: ConfigMap + metadata: + name: ironic-bmo-configmap + namespace: "{{ cifmw_bootc_ipa_ironic_namespace.stdout }}" + data: + IPA_BASEURI: "{{ cifmw_bootc_ipa_service_url }}" + IPA_BRANCH: "{{ cifmw_bootc_ipa_branch }}" + IPA_FLAVOR: "{{ cifmw_bootc_ipa_flavor }}" + + - name: Restart ironic deployment to re-run IPA downloader + when: cifmw_bootc_ipa_ironic_namespace.rc == 0 and cifmw_bootc_ipa_ironic_namespace.stdout | length > 0 + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + ansible.builtin.command: + cmd: >- + oc rollout restart deployment/ironic + -n {{ cifmw_bootc_ipa_ironic_namespace.stdout }} + + - name: Wait for ironic rollout after configmap patch + when: cifmw_bootc_ipa_ironic_namespace.rc == 0 and cifmw_bootc_ipa_ironic_namespace.stdout | length > 0 + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + ansible.builtin.command: + cmd: >- + oc rollout status deployment/ironic + -n {{ cifmw_bootc_ipa_ironic_namespace.stdout }} + --timeout=10m diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml index e366284f..597c880c 100644 --- a/zuul.d/jobs.yaml +++ b/zuul.d/jobs.yaml @@ -12,6 +12,30 @@ - job: name: openstack-baremetal-operator-crc-podified-edpm-baremetal parent: cifmw-crc-podified-edpm-baremetal + +- job: + name: openstack-baremetal-operator-crc-podified-edpm-baremetal-bootc + parent: cifmw-crc-podified-edpm-baremetal-bootc + vars: + cifmw_update_containers: true + cifmw_update_containers_openstack: true + cifmw_update_containers_edpm_image_url: oci://quay.io/rabi/edpm-bootc:oci-test + pre_deploy: + - name: 61 Bootc IPA artifacts + type: playbook + source: "{{ ansible_user_dir }}/src/github.com/openstack-k8s-operators/openstack-baremetal-operator/ci/playbooks/bootc-ipa-pre-deploy.yml" + extra_vars: + cifmw_bootc_ipa_build_enabled: true + post_ctlplane_deploy: + - name: 61 Bootc IPA reconfigure ironic + type: playbook + source: "{{ ansible_user_dir }}/src/github.com/openstack-k8s-operators/openstack-baremetal-operator/ci/playbooks/bootc-ipa-pre-deploy.yml" + extra_vars: + cifmw_bootc_ipa_build_enabled: false + cifmw_install_yamls_vars: + BAREMETAL_OS_IMG_TYPE: PassThrough + BMO_DEFAULT_DEPLOY_INTERFACE: bootc + - job: name: openstack-baremetal-operator-edpm-baremetal-minor-update parent: cifmw-crc-podified-edpm-baremetal-minor-update diff --git a/zuul.d/projects.yaml b/zuul.d/projects.yaml index 2821b20a..e6a4e565 100644 --- a/zuul.d/projects.yaml +++ b/zuul.d/projects.yaml @@ -9,6 +9,9 @@ - openstack-baremetal-operator-crc-podified-edpm-baremetal: dependencies: - openstack-baremetal-operator-content-provider + - openstack-baremetal-operator-crc-podified-edpm-baremetal-bootc: + dependencies: + - openstack-baremetal-operator-content-provider - openstack-baremetal-operator-edpm-baremetal-minor-update: dependencies: - openstack-baremetal-operator-content-provider