From bc83ce243feadc66bd2ff003e02c933e9224f173 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Wed, 11 Jan 2023 09:22:58 +0100 Subject: [PATCH] wire ignition bp customization to simplified-installer and raw image Signed-off-by: Antonio Murdaca --- internal/blueprint/customizations.go | 4 +- internal/distro/rhel9/images.go | 21 +- internal/ignition/ignition.go | 21 +- internal/image/ostree_simplified_installer.go | 5 - internal/manifest/coi_iso_tree.go | 4 +- internal/manifest/coreos_installer.go | 4 +- test/cases/ostree-simplified-installer.sh | 346 +++++++++++++++--- 7 files changed, 323 insertions(+), 82 deletions(-) diff --git a/internal/blueprint/customizations.go b/internal/blueprint/customizations.go index d7433961b0..234b11771b 100644 --- a/internal/blueprint/customizations.go +++ b/internal/blueprint/customizations.go @@ -33,7 +33,7 @@ type IgnitionCustomization struct { type EmbeddedIgnitionCustomization struct { ProvisioningURL string `json:"url,omitempty" toml:"url,omitempty"` - Data64 string `json:"data,omitempty" toml:"data,omitempty"` + Config string `json:"config,omitempty" toml:"config,omitempty"` } type FirstBootIgnitionCustomization struct { @@ -421,7 +421,7 @@ func (c *EmbeddedIgnitionCustomization) CheckEmbeddedIgnition() error { if c == nil { return nil } - if c.Data64 != "" && c.ProvisioningURL != "" { + if c.Config != "" && c.ProvisioningURL != "" { t := reflect.TypeOf(*c) return &CustomizationError{fmt.Sprintf("'%s' and '%s' are not allowed at the same time", t.Field(0).Name, t.Field(1).Name)} } diff --git a/internal/distro/rhel9/images.go b/internal/distro/rhel9/images.go index 8ff3895d12..3e60259279 100644 --- a/internal/distro/rhel9/images.go +++ b/internal/distro/rhel9/images.go @@ -211,8 +211,8 @@ func edgeCommitImage(workload workload.Workload, img.Platform = t.platform img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations) - if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || !common.VersionLessThan(t.arch.distro.osVersion, "9-stream") { - img.OSCustomizations.EnabledServices = append(img.OSCustomizations.EnabledServices, "ignition-firstboot-complete.service", "coreos-ignition-write-issues", "coreos-ignition-write-issues") + if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || t.arch.distro.osVersion == "9-stream" { + img.OSCustomizations.EnabledServices = append(img.OSCustomizations.EnabledServices, "ignition-firstboot-complete.service", "coreos-ignition-write-issues.service") } img.Environment = t.environment img.Workload = workload @@ -247,8 +247,8 @@ func edgeContainerImage(workload workload.Workload, img.Platform = t.platform img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations) - if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || !common.VersionLessThan(t.arch.distro.osVersion, "9-stream") { - img.OSCustomizations.EnabledServices = append(img.OSCustomizations.EnabledServices, "ignition-firstboot-complete.service", "coreos-ignition-write-issues", "coreos-ignition-write-issues") + if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || t.arch.distro.osVersion == "9-stream" { + img.OSCustomizations.EnabledServices = append(img.OSCustomizations.EnabledServices, "ignition-firstboot-complete.service", "coreos-ignition-write-issues.service") } img.ContainerLanguage = img.OSCustomizations.Language img.Environment = t.environment @@ -421,6 +421,10 @@ func edgeSimplifiedInstallerImage(workload workload.Workload, rawImg.Filename = t.Filename() + if bpIgnition := customizations.GetIgnition(); bpIgnition != nil && bpIgnition.FirstBoot != nil && bpIgnition.FirstBoot.ProvisioningURL != "" { + rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, "ignition.config.url="+bpIgnition.FirstBoot.ProvisioningURL) + } + // 92+ only if kopts := customizations.GetKernel(); kopts != nil && kopts.Append != "" { rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, kopts.Append) @@ -436,11 +440,12 @@ func edgeSimplifiedInstallerImage(workload workload.Workload, } // ignition configs from blueprint if bpIgnition := customizations.GetIgnition(); bpIgnition != nil { - if bpIgnition.FirstBoot != nil { - img.IgnitionFirstBoot = ignition.FirstbootOptionsFromBP(*bpIgnition.FirstBoot) - } if bpIgnition.Embedded != nil { - img.IgnitionEmbedded = ignition.EmbeddedOptionsFromBP(*bpIgnition.Embedded) + var err error + img.IgnitionEmbedded, err = ignition.EmbeddedOptionsFromBP(*bpIgnition.Embedded) + if err != nil { + return nil, err + } } } diff --git a/internal/ignition/ignition.go b/internal/ignition/ignition.go index cca1afe0a1..a6a14e7962 100644 --- a/internal/ignition/ignition.go +++ b/internal/ignition/ignition.go @@ -1,6 +1,11 @@ package ignition -import "github.com/osbuild/osbuild-composer/internal/blueprint" +import ( + "encoding/base64" + "errors" + + "github.com/osbuild/osbuild-composer/internal/blueprint" +) type FirstBootOptions struct { ProvisioningURL string @@ -13,10 +18,16 @@ func FirstbootOptionsFromBP(bpIgnitionFirstboot blueprint.FirstBootIgnitionCusto type EmbeddedOptions struct { ProvisioningURL string - Data64 string + Config string } -func EmbeddedOptionsFromBP(bpIgnitionEmbedded blueprint.EmbeddedIgnitionCustomization) *EmbeddedOptions { - ignition := EmbeddedOptions(bpIgnitionEmbedded) - return &ignition +func EmbeddedOptionsFromBP(bpIgnitionEmbedded blueprint.EmbeddedIgnitionCustomization) (*EmbeddedOptions, error) { + decodedConfig, err := base64.StdEncoding.DecodeString(bpIgnitionEmbedded.Config) + if err != nil { + return nil, errors.New("can't decode Ignition config") + } + return &EmbeddedOptions{ + ProvisioningURL: bpIgnitionEmbedded.ProvisioningURL, + Config: string(decodedConfig), + }, nil } diff --git a/internal/image/ostree_simplified_installer.go b/internal/image/ostree_simplified_installer.go index 15ca7ea6d1..c3c6da6787 100644 --- a/internal/image/ostree_simplified_installer.go +++ b/internal/image/ostree_simplified_installer.go @@ -127,11 +127,6 @@ func (img *OSTreeSimplifiedInstaller) InstantiateManifest(m *manifest.Manifest, } } - // ignition firstboot options - if img.IgnitionFirstBoot != nil { - kernelOpts = append(kernelOpts, "coreos.inst.append ignition.config.url="+img.IgnitionFirstBoot.ProvisioningURL) - } - bootTreePipeline.KernelOpts = kernelOpts rootfsPartitionTable := &disk.PartitionTable{ diff --git a/internal/manifest/coi_iso_tree.go b/internal/manifest/coi_iso_tree.go index 028e613371..ca379a0f37 100644 --- a/internal/manifest/coi_iso_tree.go +++ b/internal/manifest/coi_iso_tree.go @@ -72,9 +72,9 @@ func (p *CoreOSISOTree) serialize() osbuild.Pipeline { copyInput := "" // These specific filenames in the root of the ISO are expected by // coreos-installer-dracut during installation - if p.coiPipeline.Ignition.Data64 != "" { + if p.coiPipeline.Ignition.Config != "" { filename = "ignition_config" - copyInput = p.coiPipeline.Ignition.Data64 + copyInput = p.coiPipeline.Ignition.Config } else { filename = "ignition_url" copyInput = p.coiPipeline.Ignition.ProvisioningURL diff --git a/internal/manifest/coreos_installer.go b/internal/manifest/coreos_installer.go index 973958990a..3fded726cd 100644 --- a/internal/manifest/coreos_installer.go +++ b/internal/manifest/coreos_installer.go @@ -138,8 +138,8 @@ func (p *CoreOSInstaller) getInline() []string { } // inline data for ignition embedded (url or data) if p.Ignition != nil { - if p.Ignition.Data64 != "" { - inlineData = append(inlineData, p.Ignition.Data64) + if p.Ignition.Config != "" { + inlineData = append(inlineData, p.Ignition.Config) } else { inlineData = append(inlineData, p.Ignition.ProvisioningURL) } diff --git a/test/cases/ostree-simplified-installer.sh b/test/cases/ostree-simplified-installer.sh index 353e22b82b..20da908b09 100755 --- a/test/cases/ostree-simplified-installer.sh +++ b/test/cases/ostree-simplified-installer.sh @@ -827,6 +827,8 @@ sudo tee "$IGN_CONFIG_PATH" > /dev/null << EOF } EOF +BASE64_IGN_CONFIG=$(cat "$IGN_CONFIG_PATH" | base64) + IGN_CONFIG_SAMPLE_PATH="${IGN_PATH}/sample.ign" sudo tee "$IGN_CONFIG_SAMPLE_PATH" > /dev/null << EOF { @@ -867,6 +869,290 @@ sudo tee "$IGN_CONFIG_SAMPLE_PATH" > /dev/null << EOF EOF sudo chmod -R +r ${HTTPD_PATH}/ignition/* +######################################################################## +## +## Build edge-simplified-installer with ignition embedded +## (only on rhel92+) +## +######################################################################## + +if [[ "${ID}-${VERSION_ID}" = "rhel-9.2" || "${ID}-${VERSION_ID}" = "centos-9" ]]; then +# tee "$BLUEPRINT_FILE" > /dev/null < /dev/null +# ISO_FILENAME="${COMPOSE_ID}-${INSTALLER_FILENAME}" +# sudo cp "${ISO_FILENAME}" /var/lib/libvirt/images + +# # Clean compose and blueprints. +# greenprint "๐Ÿงน Clean up simplified_iso_with_ignition_embedded_url blueprint and compose" +# sudo composer-cli compose delete "${COMPOSE_ID}" > /dev/null +# sudo composer-cli blueprints delete simplified_iso_with_ignition_embedded_url > /dev/null + +# # Ensure SELinux is happy with our new images. +# greenprint "๐Ÿ‘ฟ Running restorecon on image directory" +# sudo restorecon -Rv /var/lib/libvirt/images/ + +# # Create qcow2 file for virt install. +# greenprint "๐Ÿ–ฅ Create qcow2 file for virt install" +# sudo qemu-img create -f qcow2 "${LIBVIRT_IMAGE_PATH}" 20G + +# greenprint "๐Ÿ’ฟ Install ostree image via installer(ISO) on UEFI VM" +# sudo virt-install --name="${IMAGE_KEY}-simplified_iso_with_ignition_embedded_url"\ +# --disk path="${LIBVIRT_IMAGE_PATH}",format=qcow2 \ +# --ram "${MEMORY}" \ +# --vcpus 2 \ +# --network network=integration,mac=34:49:22:B0:83:33 \ +# --os-type linux \ +# --os-variant ${OS_VARIANT} \ +# --cdrom "/var/lib/libvirt/images/${ISO_FILENAME}" \ +# --boot "$BOOT_ARGS" \ +# --tpm backend.type=emulator,backend.version=2.0,model=tpm-crb \ +# --nographics \ +# --noautoconsole \ +# --wait=15 \ +# --noreboot + +# # Installation can get stuck, destroying VM helps +# # See https://github.com/osbuild/osbuild-composer/issues/2413 +# if [[ $(sudo virsh domstate "${IMAGE_KEY}-simplified_iso_with_ignition_embedded_url") == "running" ]]; then +# sudo virsh destroy "${IMAGE_KEY}-simplified_iso_with_ignition_embedded_url" +# fi + +# # Start VM. +# greenprint "๐Ÿ’ป Start UEFI VM" +# sudo virsh start "${IMAGE_KEY}-simplified_iso_with_ignition_embedded_url" + +# # Check for ssh ready to go. +# greenprint "๐Ÿ›ƒ Checking for SSH is ready to go" +# for LOOP_COUNTER in $(seq 0 30); do +# RESULTS="$(wait_for_ssh_up $IGNITION_GUEST_ADDRESS)" +# if [[ $RESULTS == 1 ]]; then +# echo "SSH is ready now! ๐Ÿฅณ" +# break +# fi +# sleep 10 +# done + +# # Check image installation result +# check_result + +# greenprint "๐Ÿ•น Get ostree install commit value" +# INSTALL_HASH=$(curl "${PROD_REPO_URL}/refs/heads/${OSTREE_REF}") + +# if [[ ${IGNITION} -eq 0 ]]; then +# # Add instance IP address into /etc/ansible/hosts +# sudo tee "${TEMPDIR}"/inventory > /dev/null << EOF +# [ostree_guest] +# ${IGNITION_GUEST_ADDRESS} + +# [ostree_guest:vars] +# ansible_python_interpreter=/usr/bin/python3 +# ansible_user=core +# ansible_private_key_file=${SSH_KEY} +# ansible_ssh_common_args="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" +# ansible_become=yes +# ansible_become_method=sudo +# ansible_become_pass=${EDGE_USER_PASSWORD} +# EOF + +# # Test IoT/Edge OS +# sudo ansible-playbook -v -i "${TEMPDIR}"/inventory -e image_type=redhat -e ostree_commit="${INSTALL_HASH}" -e skip_rollback_test="true" -e ignition="${HAS_IGNITION}" -e edge_type=edge-simplified-installer -e fdo_credential="false" /usr/share/tests/osbuild-composer/ansible/check_ostree.yaml || RESULTS=0 +# check_result +# fi + +# # now try with blueprint user + +# # Add instance IP address into /etc/ansible/hosts +# sudo tee "${TEMPDIR}"/inventory > /dev/null << EOF +# [ostree_guest] +# ${IGNITION_GUEST_ADDRESS} + +# [ostree_guest:vars] +# ansible_python_interpreter=/usr/bin/python3 +# ansible_user=admin +# ansible_private_key_file=${SSH_KEY} +# ansible_ssh_common_args="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" +# ansible_become=yes +# ansible_become_method=sudo +# ansible_become_pass=${EDGE_USER_PASSWORD} +# EOF + +# # Test IoT/Edge OS +# sudo ansible-playbook -v -i "${TEMPDIR}"/inventory -e image_type=redhat -e ostree_commit="${INSTALL_HASH}" -e skip_rollback_test="true" -e ignition="${HAS_IGNITION}" -e edge_type=edge-simplified-installer -e fdo_credential="false" /usr/share/tests/osbuild-composer/ansible/check_ostree.yaml || RESULTS=0 +# check_result + +# greenprint "๐Ÿงน Clean up VM" +# if [[ $(sudo virsh domstate "${IMAGE_KEY}-simplified_iso_with_ignition_embedded_url") == "running" ]]; then +# sudo virsh destroy "${IMAGE_KEY}-simplified_iso_with_ignition_embedded_url" +# fi +# sudo virsh undefine "${IMAGE_KEY}-simplified_iso_with_ignition_embedded_url" --nvram +# sudo virsh vol-delete --pool images "$LIBVIRT_IMAGE_PATH" + + # embedded base64 ign config + + tee "$BLUEPRINT_FILE" > /dev/null < /dev/null + ISO_FILENAME="${COMPOSE_ID}-${INSTALLER_FILENAME}" + sudo cp "${ISO_FILENAME}" /var/lib/libvirt/images + + # Clean compose and blueprints. + greenprint "๐Ÿงน Clean up simplified_iso_with_ignition_embedded_config blueprint and compose" + sudo composer-cli compose delete "${COMPOSE_ID}" > /dev/null + sudo composer-cli blueprints delete simplified_iso_with_ignition_embedded_config > /dev/null + + # Ensure SELinux is happy with our new images. + greenprint "๐Ÿ‘ฟ Running restorecon on image directory" + sudo restorecon -Rv /var/lib/libvirt/images/ + + # Create qcow2 file for virt install. + greenprint "๐Ÿ–ฅ Create qcow2 file for virt install" + sudo qemu-img create -f qcow2 "${LIBVIRT_IMAGE_PATH}" 20G + + greenprint "๐Ÿ’ฟ Install ostree image via installer(ISO) on UEFI VM" + sudo virt-install --name="${IMAGE_KEY}-simplified_iso_with_ignition_embedded_config"\ + --disk path="${LIBVIRT_IMAGE_PATH}",format=qcow2 \ + --ram "${MEMORY}" \ + --vcpus 2 \ + --network network=integration,mac=34:49:22:B0:83:33 \ + --os-type linux \ + --os-variant ${OS_VARIANT} \ + --cdrom "/var/lib/libvirt/images/${ISO_FILENAME}" \ + --boot "$BOOT_ARGS" \ + --tpm backend.type=emulator,backend.version=2.0,model=tpm-crb \ + --nographics \ + --noautoconsole \ + --wait=15 \ + --noreboot + + # Installation can get stuck, destroying VM helps + # See https://github.com/osbuild/osbuild-composer/issues/2413 + if [[ $(sudo virsh domstate "${IMAGE_KEY}-simplified_iso_with_ignition_embedded_config") == "running" ]]; then + sudo virsh destroy "${IMAGE_KEY}-simplified_iso_with_ignition_embedded_config" + fi + + # Start VM. + greenprint "๐Ÿ’ป Start UEFI VM" + sudo virsh start "${IMAGE_KEY}-simplified_iso_with_ignition_embedded_config" + + # Check for ssh ready to go. + greenprint "๐Ÿ›ƒ Checking for SSH is ready to go" + for LOOP_COUNTER in $(seq 0 30); do + RESULTS="$(wait_for_ssh_up $IGNITION_GUEST_ADDRESS)" + if [[ $RESULTS == 1 ]]; then + echo "SSH is ready now! ๐Ÿฅณ" + break + fi + sleep 10 + done + + # Check image installation result + check_result + + greenprint "๐Ÿ•น Get ostree install commit value" + INSTALL_HASH=$(curl "${PROD_REPO_URL}/refs/heads/${OSTREE_REF}") + + if [[ ${IGNITION} -eq 0 ]]; then + # Add instance IP address into /etc/ansible/hosts + sudo tee "${TEMPDIR}"/inventory > /dev/null << EOF +[ostree_guest] +${IGNITION_GUEST_ADDRESS} + +[ostree_guest:vars] +ansible_python_interpreter=/usr/bin/python3 +ansible_user=core +ansible_private_key_file=${SSH_KEY} +ansible_ssh_common_args="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" +ansible_become=yes +ansible_become_method=sudo +ansible_become_pass=${EDGE_USER_PASSWORD} +EOF + + # Test IoT/Edge OS + sudo ansible-playbook -v -i "${TEMPDIR}"/inventory -e image_type=redhat -e ostree_commit="${INSTALL_HASH}" -e skip_rollback_test="true" -e ignition="${HAS_IGNITION}" -e edge_type=edge-simplified-installer -e fdo_credential="false" /usr/share/tests/osbuild-composer/ansible/check_ostree.yaml || RESULTS=0 + check_result + fi + + # now try with blueprint user + + # Add instance IP address into /etc/ansible/hosts + sudo tee "${TEMPDIR}"/inventory > /dev/null << EOF +[ostree_guest] +${IGNITION_GUEST_ADDRESS} + +[ostree_guest:vars] +ansible_python_interpreter=/usr/bin/python3 +ansible_user=admin +ansible_private_key_file=${SSH_KEY} +ansible_ssh_common_args="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" +ansible_become=yes +ansible_become_method=sudo +ansible_become_pass=${EDGE_USER_PASSWORD} +EOF + + # Test IoT/Edge OS + sudo ansible-playbook -v -i "${TEMPDIR}"/inventory -e image_type=redhat -e ostree_commit="${INSTALL_HASH}" -e skip_rollback_test="true" -e ignition="${HAS_IGNITION}" -e edge_type=edge-simplified-installer -e fdo_credential="false" /usr/share/tests/osbuild-composer/ansible/check_ostree.yaml || RESULTS=0 + check_result + + greenprint "๐Ÿงน Clean up VM" + if [[ $(sudo virsh domstate "${IMAGE_KEY}-simplified_iso_with_ignition_embedded_config") == "running" ]]; then + sudo virsh destroy "${IMAGE_KEY}-simplified_iso_with_ignition_embedded_config" + fi + sudo virsh undefine "${IMAGE_KEY}-simplified_iso_with_ignition_embedded_config" --nvram + sudo virsh vol-delete --pool images "$LIBVIRT_IMAGE_PATH" +else + greenprint "Skipping ignition embedded url test, it's only for RHEL9" +fi + +# TODO(runcom): + if [[ ${IGNITION} -eq 0 ]]; then tee "$BLUEPRINT_FILE" > /dev/null << EOF name = "simplified_iso_without_fdo" @@ -878,8 +1164,8 @@ if [[ ${IGNITION} -eq 0 ]]; then [customizations] installation_device = "/dev/vda" - [customizations.kernel] - append = "ignition.config.url=http://192.168.100.1/ignition/config.ign" + [customizations.ignition.firstboot] + url = "http://192.168.100.1/ignition/config.ign" EOF else tee "$BLUEPRINT_FILE" > /dev/null << EOF @@ -1009,62 +1295,6 @@ EOF sudo ansible-playbook -v -i "${TEMPDIR}"/inventory -e image_type=redhat -e ostree_commit="${INSTALL_HASH}" -e skip_rollback_test="true" -e ignition="${HAS_IGNITION}" -e edge_type=edge-simplified-installer -e fdo_credential="false" /usr/share/tests/osbuild-composer/ansible/check_ostree.yaml || RESULTS=0 check_result -######################################################################## -## -## Build edge-simplified-installer with ignition embedded -## (only on rhel92+) -## -######################################################################## - -if [[ "${ID}-${VERSION_ID}" = "rhel-9.2" || "${ID}-${VERSION_ID}" = "centos-9" ]]; then - tee "$BLUEPRINT_FILE" > /dev/null < /dev/null - ISO_FILENAME="${COMPOSE_ID}-${INSTALLER_FILENAME}" - - # Mount the image - sudo mkdir /mnt/installer - sudo mount -o loop "${ISO_FILENAME}" /mnt/installer - - # Check that the image contains the ignition_url file - if [[ -f "/mnt/installer/ignition_url" ]]; then - RESULTS=1 - fi - - # Check the resulting image - check_result - - sudo umount /mnt/installer - - # TODO(runcom): run with this image and basically check the same as the previous test with core user -else - greenprint "Skipping ignition embedded url test, it's only for RHEL9" -fi - - ######################## ## ## Build upgrade image