Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 30 additions & 13 deletions mantle/cmd/kola/testiso.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,22 @@ var (
const (
installTimeout = 15 * time.Minute

scenarioPXEInstall = "pxe-install"
scenarioISOInstall = "iso-install"
scenarioISOLiveLogin = "iso-live-login"
scenarioLegacyInstall = "legacy-install"
scenarioPXEInstall = "pxe-install"
scenarioISOInstall = "iso-install"
// this might just be iso-install eventually since the pxe install already
// tests the over-the-network case; we're making it separate for now to
// more easily ratchet osmet into place
scenarioISOOfflineInstall = "iso-offline-install"
scenarioISOLiveLogin = "iso-live-login"
scenarioLegacyInstall = "legacy-install"
)

var allScenarios = map[string]bool{
scenarioPXEInstall: true,
scenarioISOInstall: true,
scenarioISOLiveLogin: true,
scenarioLegacyInstall: true,
scenarioPXEInstall: true,
scenarioISOInstall: true,
scenarioISOOfflineInstall: true,
scenarioISOLiveLogin: true,
scenarioLegacyInstall: true,
}

var signalCompleteString = "coreos-installer-test-OK"
Expand All @@ -103,7 +108,7 @@ func init() {
cmdTestIso.Flags().BoolVar(&console, "console", false, "Display qemu console to stdout")
cmdTestIso.Flags().StringSliceVar(&pxeKernelArgs, "pxe-kargs", nil, "Additional kernel arguments for PXE")
// FIXME move scenarioISOLiveLogin into the defaults once https://github.com/coreos/fedora-coreos-config/pull/339#issuecomment-613000050 is fixed
cmdTestIso.Flags().StringSliceVar(&scenarios, "scenarios", []string{scenarioPXEInstall, scenarioISOInstall}, fmt.Sprintf("Test scenarios (also available: %v)", []string{scenarioLegacyInstall, scenarioISOLiveLogin}))
cmdTestIso.Flags().StringSliceVar(&scenarios, "scenarios", []string{scenarioPXEInstall, scenarioISOInstall}, fmt.Sprintf("Test scenarios (also available: %v)", []string{scenarioLegacyInstall, scenarioISOLiveLogin, scenarioISOOfflineInstall}))
cmdTestIso.Args = cobra.ExactArgs(0)

root.AddCommand(cmdTestIso)
Expand Down Expand Up @@ -172,6 +177,7 @@ func runTestIso(cmd *cobra.Command, args []string) error {
}
if noiso || nolive {
delete(targetScenarios, scenarioISOInstall)
delete(targetScenarios, scenarioISOOfflineInstall)
delete(targetScenarios, scenarioISOLiveLogin)
}

Expand Down Expand Up @@ -241,11 +247,22 @@ func runTestIso(cmd *cobra.Command, args []string) error {
}
ranTest = true
instIso := baseInst // Pretend this is Rust and I wrote .copy()
if err := testLiveIso(instIso, completionfile); err != nil {
if err := testLiveIso(instIso, completionfile, false); err != nil {
return err
}
printSuccess(scenarioISOInstall)
}
if _, ok := targetScenarios[scenarioISOOfflineInstall]; ok {
if kola.CosaBuild.Meta.BuildArtifacts.LiveIso == nil {
return fmt.Errorf("build %s has no live ISO", kola.CosaBuild.Meta.Name)
}
ranTest = true
instIso := baseInst // Pretend this is Rust and I wrote .copy()
if err := testLiveIso(instIso, completionfile, true); err != nil {
return err
}
printSuccess(scenarioISOOfflineInstall)
}
if _, ok := targetScenarios[scenarioISOLiveLogin]; ok {
if kola.CosaBuild.Meta.BuildArtifacts.LiveIso == nil {
return fmt.Errorf("build %s has no live ISO", kola.CosaBuild.Meta.Name)
Expand Down Expand Up @@ -302,7 +319,7 @@ func printSuccess(mode string) {
if kola.QEMUOptions.Native4k {
metaltype = "metal4k"
}
fmt.Printf("Successfully tested scenario:%s for %s on %s (%s)\n", mode, kola.CosaBuild.Meta.OstreeVersion, kola.QEMUOptions.Firmware, metaltype)
fmt.Printf("Successfully tested scenario %s for %s on %s (%s)\n", mode, kola.CosaBuild.Meta.OstreeVersion, kola.QEMUOptions.Firmware, metaltype)
}

func testPXE(inst platform.Install) error {
Expand Down Expand Up @@ -336,7 +353,7 @@ func testPXE(inst platform.Install) error {
return awaitCompletion(mach.QemuInst, completionChannel, []string{signalCompleteString})
}

func testLiveIso(inst platform.Install, completionfile string) error {
func testLiveIso(inst platform.Install, completionfile string, offline bool) error {
inst.Builder = newQemuBuilder(false)
completionChannel, err := inst.Builder.VirtioChannelRead("testisocompletion")
if err != nil {
Expand Down Expand Up @@ -389,7 +406,7 @@ func testLiveIso(inst platform.Install, completionfile string) error {
},
}

mach, err := inst.InstallViaISOEmbed(nil, liveConfig, targetConfig)
mach, err := inst.InstallViaISOEmbed(nil, liveConfig, targetConfig, offline)
if err != nil {
return errors.Wrapf(err, "running iso install")
}
Expand Down
56 changes: 37 additions & 19 deletions mantle/platform/metal.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ func generatePointerIgnitionString(spec2 bool, target string) string {
return string(buf)
}

func (inst *Install) InstallViaISOEmbed(kargs []string, liveIgnition, targetIgnition ignv3types.Config) (*InstalledMachine, error) {
func (inst *Install) InstallViaISOEmbed(kargs []string, liveIgnition, targetIgnition ignv3types.Config, offline bool) (*InstalledMachine, error) {
if inst.CosaBuild.Meta.BuildArtifacts.Metal == nil {
return nil, fmt.Errorf("Build %s must have a `metal` artifact", inst.CosaBuild.Meta.OstreeVersion)
}
Expand Down Expand Up @@ -549,18 +549,36 @@ func (inst *Install) InstallViaISOEmbed(kargs []string, liveIgnition, targetIgni
return nil, errors.Wrapf(err, "setting up metal image")
}

mux := http.NewServeMux()
mux.Handle("/", http.FileServer(http.Dir(tempdir)))
listener, err := net.Listen("tcp", ":0")
if err != nil {
return nil, err
var srcOpt string
var serializedTargetConfig string
if offline {
srcOpt = "--offline"
// we want to test that a full offline install works; that includes the
// final installed host booting offline
serializedTargetConfig = dataurl.EncodeBytes([]byte(renderedTarget))
} else {
mux := http.NewServeMux()
mux.Handle("/", http.FileServer(http.Dir(tempdir)))
listener, err := net.Listen("tcp", ":0")
if err != nil {
return nil, err
}
port := listener.Addr().(*net.TCPAddr).Port
// Yeah this leaks
go func() {
http.Serve(listener, mux)
}()
baseurl := fmt.Sprintf("http://%s:%d", defaultQemuHostIPv4, port)
srcOpt = fmt.Sprintf("--image-url %s/%s", baseurl, metalname)

// In this case; the target config is jut a tiny wrapper that wants to
// fetch our hosted target.ign config

// TODO also use https://github.com/coreos/coreos-installer/issues/118#issuecomment-585572952
// when it arrives
pointerIgnitionStr := generatePointerIgnitionString(inst.IgnitionSpec2, baseurl+"/target.ign")
serializedTargetConfig = dataurl.EncodeBytes([]byte(pointerIgnitionStr))
}
port := listener.Addr().(*net.TCPAddr).Port
// Yeah this leaks
go func() {
http.Serve(listener, mux)
}()
baseurl := fmt.Sprintf("http://%s:%d", defaultQemuHostIPv4, port)

insecureOpt := ""
if inst.Insecure {
Expand All @@ -574,16 +592,12 @@ Wants=network-online.target
[Service]
RemainAfterExit=yes
Type=oneshot
ExecStart=/usr/bin/coreos-installer install --image-url %s/%s --ignition %s %s %s
ExecStart=/usr/bin/coreos-installer install %s --ignition %s %s %s
StandardOutput=kmsg+console
StandardError=kmsg+console
[Install]
WantedBy=multi-user.target
`, baseurl, metalname, pointerIgnitionPath, insecureOpt, targetDevice)
// TODO also use https://github.com/coreos/coreos-installer/issues/118#issuecomment-585572952
// when it arrives
pointerIgnitionStr := generatePointerIgnitionString(inst.IgnitionSpec2, baseurl+"/target.ign")
pointerIgnitionEnc := dataurl.EncodeBytes([]byte(pointerIgnitionStr))
`, srcOpt, pointerIgnitionPath, insecureOpt, targetDevice)
mode := 0644
rebootUnitP := string(rebootUnit)
installerConfig := ignv3types.Config{
Expand Down Expand Up @@ -612,7 +626,7 @@ WantedBy=multi-user.target
},
FileEmbedded1: ignv3types.FileEmbedded1{
Contents: ignv3types.FileContents{
Source: &pointerIgnitionEnc,
Source: &serializedTargetConfig,
},
Mode: &mode,
},
Expand Down Expand Up @@ -653,6 +667,10 @@ WantedBy=multi-user.target
qemubuilder := inst.Builder
qemubuilder.AddInstallIso(isoEmbeddedPath)

if offline {
qemubuilder.Append("-nic", "none")
}

qinst, err := qemubuilder.Exec()
if err != nil {
return nil, err
Expand Down
62 changes: 21 additions & 41 deletions src/cmd-buildextend-installer
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ parser = argparse.ArgumentParser()
parser.add_argument("--build", help="Build ID")
parser.add_argument("--force", action='store_true', default=False,
help="Overwrite previously generated installer")
# Temporary switch while we ratchet osmet in place
parser.add_argument("--osmet", action='store_true', default=False,
help="Generate osmet file")
args = parser.parse_args()

# Identify the builds and target the latest build if none provided
Expand All @@ -57,19 +60,13 @@ squashfs_compression = image_yaml.get('squashfs-compression', 'zstd')

# Hacky mode switch, until we can drop support for the installer images
is_live = os.path.basename(sys.argv[0]).endswith('-live')
is_fulliso = os.path.basename(sys.argv[0]).endswith('-fulliso')
if is_fulliso:
is_live = True
image_type = 'fulliso'
elif is_live:
if is_live:
image_type = 'live'
else:
image_type = 'installer'
meta_keys = {k: 'live-' + k if is_live else k for k in ('iso', 'kernel', 'initramfs')}

config_src = image_type
if image_type == 'fulliso':
config_src = 'live'
srcdir_prefix = f"src/config/{config_src}/"

if not os.path.isdir(srcdir_prefix):
Expand Down Expand Up @@ -142,12 +139,11 @@ def generate_iso():

tmpisofile = os.path.join(tmpdir, iso_name)
img_metal_obj = buildmeta['images'].get('metal')
img_metal = None
if img_metal_obj is not None:
img_metal = os.path.join(builddir, img_metal_obj['path'])
img_src = img_metal
else:
img_src = os.path.join(builddir, buildmeta['images']['qemu']['path'])
if img_metal_obj is None:
raise Exception("ISO generation requires `metal` image")

img_metal = os.path.join(builddir, img_metal_obj['path'])
img_metal_checksum = img_metal_obj['sha256']

# Find the directory under `/usr/lib/modules/<kver>` where the
# kernel/initrd live. It will be the 2nd entity output by
Expand All @@ -174,42 +170,26 @@ def generate_iso():
elif image_type == 'installer':
initramfs_cpio_ext = os.path.join(tmpdir, 'legacy-stamp-initramfs')
generate_initramfs_stampfile(tmpdir, initramfs_cpio_ext, 'etc/coreos-legacy-installer-initramfs')
if is_fulliso:
tmp_initramfs = os.path.join(tmpdir, 'initramfs')

if img_metal is None:
raise Exception("fulliso requires `metal` image")
metal_dest_basename = os.path.basename(img_metal) + '.xz'
metal_dest = os.path.join(tmpisoroot, metal_dest_basename)
with open(metal_dest, 'wb') as f:
run_verbose(['xz', '-c9', '-T2', img_metal], stdout=f)
os.symlink(metal_dest_basename, os.path.join(tmpisoroot, 'image-metal.xz'))

# In the fulliso case, we copy the squashfs to the ISO root.
print(f'Compressing squashfs with {squashfs_compression}')
run_verbose(['/usr/lib/coreos-assembler/gf-mksquashfs',
img_src, os.path.join(tmpisoroot, 'root.squashfs'), squashfs_compression])
# Just pad with NUL bytes for the ISO image. We'll truncate the
# padding off again when copying the PXE initrd.
with open(tmp_initramfs, 'wb') as fdst:
with open(initramfs, 'rb') as fsrc:
shutil.copyfileobj(fsrc, fdst)
with open(initramfs_cpio_ext, 'rb') as fsrc:
shutil.copyfileobj(fsrc, fdst)
fdst.write(bytes(initrd_ignition_padding))
os.rename(tmp_initramfs, initramfs)
elif is_live:
if is_live:
tmp_squashfs = os.path.join(tmpdir, 'root.squashfs')
tmp_cpio = os.path.join(tmpdir, 'root.cpio')
tmp_initramfs = os.path.join(tmpdir, 'initramfs')
cpio_input_files = [os.path.basename(tmp_squashfs)]
if args.osmet:
tmp_osmet = os.path.join(tmpdir, img_metal_obj['path'] + '.osmet')
cpio_input_files += [os.path.basename(tmp_osmet)]
print(f'Generating osmet file')
run_verbose(['/usr/lib/coreos-assembler/osmet-pack',
img_metal, tmp_osmet, img_metal_checksum])

print(f'Compressing squashfs with {squashfs_compression}')
run_verbose(['/usr/lib/coreos-assembler/gf-mksquashfs',
img_src, tmp_squashfs, squashfs_compression])
img_metal, tmp_squashfs, squashfs_compression])
# create a CPIO layer which holds the root squashfs and osmet binary
run_verbose(['cpio', '-o', '-H', 'newc', '-R', 'root:root',
'--quiet', '--reproducible', '--force-local',
'-D', os.path.dirname(tmp_squashfs), '-O', tmp_cpio],
input=os.path.basename(tmp_squashfs).encode())
input=('\n'.join(cpio_input_files).encode()))
# Compression is redundant but the kernel requires it
run_verbose(['gzip', '-1', tmp_cpio])

Expand Down Expand Up @@ -238,7 +218,7 @@ def generate_iso():

# Read and filter kernel arguments for substituting into ISO bootloader
result = run_verbose(['/usr/lib/coreos-assembler/gf-get-kargs',
img_src], stdout=subprocess.PIPE, text=True)
img_metal], stdout=subprocess.PIPE, text=True)
kargs_array = [karg for karg in result.stdout.split()
if karg.split('=')[0] not in live_exclude_kargs]
kargs_array.append(f"coreos.liveiso={volid}")
Expand Down
69 changes: 69 additions & 0 deletions src/osmet-pack
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/bin/bash
set -euo pipefail

if [ ! -f /etc/cosa-supermin ]; then
dn=$(dirname "$0")
# shellcheck source=src/cmdlib.sh
. "${dn}"/cmdlib.sh

img_src=$1; shift
osmet_dest=$1; shift
checksum=$1; shift
coreinst=${1:-${OSMET_PACK_COREOS_INSTALLER:-}}

workdir=$(pwd)
TMPDIR=tmp/tmp-osmet-pack
rm -rf "${TMPDIR}"
mkdir -p "${TMPDIR}"

if [[ $img_src == *.gz || $img_src == *.xz ]]; then
img="$(basename "$img_src")"
fatal "Cannot pack osmet from $img; not an uncompressed image"
fi

set -- "${TMPDIR}/osmet.bin" "${checksum}"
if [ -n "${coreinst:-}" ]; then
cp "${coreinst}" "${TMPDIR}/coreos-installer"
set -- "$@" "${TMPDIR}/coreos-installer"
fi

# stamp it with "osmet" serial so we find it easily in the VM
runvm -drive "if=none,id=osmet,format=raw,readonly=on,file=${img_src}" \
-device virtio-blk,serial=osmet,drive=osmet -- \
/usr/lib/coreos-assembler/osmet-pack "$@"

mv "${TMPDIR}/osmet.bin" "${osmet_dest}"
rm -rf "${TMPDIR}"

exit 0
fi

# This runs inside supermin

osmet_dest=$1; shift
checksum=$1; shift
coreinst=${1:-}

set -x

# we want /dev/disk symlinks for coreos-installer
/usr/lib/systemd/systemd-udevd --daemon
/usr/sbin/udevadm trigger --settle

rootfs=/dev/disk/by-id/virtio-osmet-part4

mkdir -p /sysroot
mount -o ro "${rootfs}" /sysroot
osname=$(ls /sysroot/ostree/deploy)
deploydir=$(find "/sysroot/ostree/deploy/$osname/deploy" -mindepth 1 -maxdepth 1 -type d)
# shellcheck disable=SC1090
description=$(. "${deploydir}/etc/os-release" && echo "${PRETTY_NAME}")

if [ -z "${coreinst}" ]; then
coreinst=${deploydir}/usr/bin/coreos-installer
fi

RUST_BACKTRACE=full "${coreinst}" osmet pack /dev/disk/by-id/virtio-osmet \
--description "${description}" \
--checksum "${checksum}" \
--output "${osmet_dest}"