add boot order updating to gen2 iso installations
I've created this PR to update how Mariner installs itself during iso installation.
Current Behavior:
Mariner installs itself onto Disk and reboots. Leaving boot order untouched, which causes seconds of failed booting into PXE after the post-install reboot and subsequent reboots.
Desired Behavior:
When in an EFI boot type'd iso install, we have the ability to set our boot order via the EFI boot manager (i.e., efibootmgr). Using this tool's functionality, and the knowledge specified by our image config files (e.g., unattended.json configs, attendedconfig.json, etc) we can set the first target in the boot order to our shim 'bootx64.efi' which typically lives in /boot/efi
Proposed solution:
There are a few approaches we can consider.
1) runliveinstaller bash script
What I have done in this PR is a "low-touch" quick fix which leaves the go tools unaltered. This fix parses the config file used for installation using the jq (json query) tool to identify the disk and partition that the shim lives on. Then, after installation, runliveinstaller uses these values to instruct efibootmgr on the exact details of where bootx64.efi resides.
Note: This fix assumes there are two possible sources for our config. In the unattended install setting, the config is the config file specified at build time and contained in the variable UNATTENDED_CONFIG_FILE in runliveinstaller. In the attended install setting, the current PR assumes this config is named and located at /installer/attendedconfig.json as is the current behavior with attended installs. This makes this fix highly coupled to the current conventions on where config files are named and placed.
2) finding the end of the call stack
Next is a "medium-touch" approach. Journeying into the go tools, runliveinstaller calls liveinstaller.go. The logic within liveinstaller.go creates an imagerArguments object named args with an undefined configFile string. This args is passed into whichever type of installFunction is requested by the call to liveinstaller.go (as decided by it's function InstallerFactory()). Option 2 is to follow this logic along each install type's chain of function calls until the finished config is generated by the gotools. Then at the end of this chain of function calls make a call to a util function named updateBootOrder() that will perform the same call to efibootmgr to set the new boot target.
The benefit of this fix is that it is an improved design over option 1, given that it does not rely on tight coupling between the discovery of config values and the config file's name and destination. One concern that has been risen is that this approach requires future devs to be aware of needing to add this call to updateBootOrder() at the end of any new installFuncs that may be created in the future.
3) passing it back up the call stack
Next we have what I consider a "high-touch" approach. We can reverse the expectation of each of these installFunc call chains, and instead of adding the updateBootOrder() logic at the deepest point in their callstack, we can instead make it so that each installFunc must eventually pass back the location of the final config file. Then, inside of liveinstaller.go, after the installFunc has successfully returned up through its call chain, we can pass this config file path to the updateBootOrder function that lives solely within liveinstaller.go
4) passing the pointer down the call stack
Finally we have one more "high-touch" approach. Currently the imagerArguments object, args, is passed by value to the installFunc. If we updated this to pass-by-pointer and we updated the configFile inside of imagerArguments to a pointer type. We could pass *args into the updateBootOrder() function that lives solely within liveinstaller.go.
Let this be an RFC for which direction we should take this boot-order change. I am still learning go and our tools, so if there are incorrect assumptions, or if I have missed a possible approach, please let me know!
Thank you, Sean
Merge Checklist
All boxes should be checked before merging the PR (just tick any boxes which don't apply to this PR)
- [ ] The toolchain has been rebuilt successfully (or no changes were made to it)
- [ ] The toolchain/worker package manifests are up-to-date
- [ ] Any updated packages successfully build (or no packages were changed)
- [ ] Packages depending on static components modified in this PR (Golang,
*-staticsubpackages, etc.) have had theirReleasetag incremented. - [ ] Package tests (%check section) have been verified with RUN_CHECK=y for existing SPEC files, or added to new SPEC files
- [ ] All package sources are available
- [ ] cgmanifest files are up-to-date and sorted (
./cgmanifest.json,./toolkit/scripts/toolchain/cgmanifest.json,.github/workflows/cgmanifest.json) - [ ] LICENSE-MAP files are up-to-date (
./SPECS/LICENSES-AND-NOTICES/data/licenses.json,./SPECS/LICENSES-AND-NOTICES/LICENSES-MAP.md,./SPECS/LICENSES-AND-NOTICES/LICENSE-EXCEPTIONS.PHOTON) - [ ] All source files have up-to-date hashes in the
*.signatures.jsonfiles - [ ]
sudo make go-tidy-allandsudo make go-test-coveragepass - [ ] Documentation has been updated to match any changes to the build system
- [ ] Ready to merge
Summary
Updates the boot order via efibootmgr after iso installation by setting the first boot target to our shim ( \EFI\BOOT\bootx64.efi )
Change Log
- Changes to runliveinstaller for boot order logic
- Adds the 'jq' tool to the list of iso-initrd-packages
Does this affect the toolchain?
NO
Test Methodology
- local build and hyper-v to verify boot order changes