diff --git a/mantle/cmd/kola/kola.go b/mantle/cmd/kola/kola.go index 49ad037066..8c4367c352 100644 --- a/mantle/cmd/kola/kola.go +++ b/mantle/cmd/kola/kola.go @@ -15,6 +15,7 @@ package main import ( + "compress/gzip" "encoding/json" "fmt" "io/ioutil" @@ -37,6 +38,7 @@ import ( "github.com/coreos/mantle/kola" "github.com/coreos/mantle/kola/register" "github.com/coreos/mantle/sdk" + "github.com/coreos/mantle/system" "github.com/coreos/mantle/util" // register OS test suite @@ -524,6 +526,7 @@ func syncFindParentImageOptions() error { var err error var parentBaseUrl string + skipSignature := false switch kola.Options.Distribution { case "fcos": // We're taking liberal shortcuts here... the cleaner way to do this is @@ -536,6 +539,11 @@ func syncFindParentImageOptions() error { if err != nil { return err } + case "rhcos": + // Hardcoded for now based on https://github.com/openshift/installer/blob/release-4.3/data/data/rhcos.json + parentBaseUrl = fmt.Sprintf("https://releases-art-rhcos.svc.ci.openshift.org/art/storage/releases/rhcos-4.3/43.81.202003111353.0/%s/", system.RpmArch()) + // sigh...someday we'll get the stuff signed by ART or maybe https://github.com/openshift/enhancements/pull/201 will just happen + skipSignature = true default: return fmt.Errorf("--find-parent-image not yet supported for distro %s", kola.Options.Distribution) } @@ -558,7 +566,7 @@ func syncFindParentImageOptions() error { } qcowUrl := parentBaseUrl + parentCosaBuild.BuildArtifacts.Qemu.Path qcowLocal := filepath.Join(qemuImageDir, parentCosaBuild.BuildArtifacts.Qemu.Path) - decompressedQcowLocal, err := downloadImageAndDecompress(qcowUrl, qcowLocal) + decompressedQcowLocal, err := downloadImageAndDecompress(qcowUrl, qcowLocal, skipSignature) if err != nil { return err } @@ -576,26 +584,57 @@ func syncFindParentImageOptions() error { } // Note this is a no-op if the decompressed dest already exists. -func downloadImageAndDecompress(url, compressedDest string) (string, error) { +func downloadImageAndDecompress(url, compressedDest string, skipSignature bool) (string, error) { var decompressedDest string - if strings.HasSuffix(compressedDest, ".xz") { + if strings.HasSuffix(compressedDest, ".xz") || strings.HasSuffix(compressedDest, ".gz") { // if the decompressed file is already present locally, assume it's // good and verified already - decompressedDest = strings.TrimSuffix(compressedDest, ".xz") + decompressedDest = strings.TrimSuffix(strings.TrimSuffix(compressedDest, ".xz"), ".gz") if exists, err := util.PathExists(decompressedDest); err != nil { return "", err } else if exists { return decompressedDest, nil } else { - if err := sdk.DownloadCompressedSignedFile(decompressedDest, url, nil, "", util.XzDecompressStream); err != nil { - return "", err + if !skipSignature { + if err := sdk.DownloadCompressedSignedFile(decompressedDest, url, nil, "", util.XzDecompressStream); err != nil { + return "", err + } + } else { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return "", err + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("url %s status: %s", url, resp.Status) + } + gzr, err := gzip.NewReader(resp.Body) + if err != nil { + return "", err + } + dst, err := os.OpenFile(decompressedDest, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + return "", err + } + defer dst.Close() + prefix := filepath.Base(decompressedDest) + if _, err := util.CopyProgress(capnslog.INFO, prefix, dst, gzr, resp.ContentLength); err != nil { + return "", err + } } return decompressedDest, nil } } - if err := sdk.DownloadSignedFile(compressedDest, url, nil, ""); err != nil { - return "", err + if !skipSignature { + if err := sdk.DownloadSignedFile(compressedDest, url, nil, ""); err != nil { + return "", err + } } return compressedDest, nil diff --git a/mantle/kola/tests/rhcos/upgrade.go b/mantle/kola/tests/rhcos/upgrade.go new file mode 100644 index 0000000000..d81b9494bf --- /dev/null +++ b/mantle/kola/tests/rhcos/upgrade.go @@ -0,0 +1,101 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rhcos + +import ( + "path/filepath" + + "github.com/coreos/pkg/capnslog" + + "github.com/coreos/mantle/kola" + "github.com/coreos/mantle/kola/cluster" + "github.com/coreos/mantle/kola/register" + "github.com/coreos/mantle/kola/tests/util" + "github.com/coreos/mantle/platform/conf" +) + +var plog = capnslog.NewPackageLogger("github.com/coreos/mantle", "kola/tests/upgrade") + +func init() { + register.RegisterUpgradeTest(®ister.Test{ + Run: rhcosUpgradeLuks, + ClusterSize: 1, + // if renaming this, also rename the command in kolet-httpd.service below + Name: "rhcos.upgrade.luks", + FailFast: true, + Tags: []string{"upgrade"}, + Distros: []string{"rhcos"}, + UserData: conf.Ignition(`{ + "ignition": { + "version": "2.2.0" + }, + "storage": { + "files": [ + { + "filesystem": "root", + "path": "/etc/clevis.json", + "contents": { + "source": "data:text/plain;base64,e30K" + }, + "mode": 420 + } + ] + } + }`), + }) +} + +// Ensure that we can still boot into a system with LUKS rootfs after +// an upgrade. +func rhcosUpgradeLuks(c cluster.TestCluster) { + m := c.Machines()[0] + ostreeCommit := kola.CosaBuild.Meta.OstreeCommit + ostreeTarName := kola.CosaBuild.Meta.BuildArtifacts.Ostree.Path + // See tests/upgrade/basic.go for some more information on this; in the future + // we should optimize this to use virtio-fs for qemu. + c.Run("setup", func(c cluster.TestCluster) { + ostreeTarPath := filepath.Join(kola.CosaBuild.Dir, ostreeTarName) + if err := cluster.DropFile(c.Machines(), ostreeTarPath); err != nil { + c.Fatal(err) + } + + // XXX: Note the '&& sync' here; this is to work around sysroot + // remounting in libostree forcing a cache flush and blocking D-Bus. + // Should drop this once we fix it more properly in {rpm-,}ostree. + // https://github.com/coreos/coreos-assembler/issues/1301 + // Also we should really add a streaming import for this + c.MustSSHf(m, "sudo tar -xf %s -C /var/srv && sudo rm %s", ostreeTarName, ostreeTarName) + c.MustSSHf(m, "sudo ostree --repo=/sysroot/ostree/repo pull-local /var/srv %s && sudo rm -rf /var/srv/* && sudo sync", ostreeCommit) + }) + + c.Run("upgrade-from-previous", func(c cluster.TestCluster) { + c.MustSSHf(m, "sudo rpm-ostree rebase :%s", ostreeCommit) + err := m.Reboot() + if err != nil { + c.Fatalf("Failed to reboot machine: %v", err) + } + }) + + c.Run("verify", func(c cluster.TestCluster) { + d, err := util.GetBootedDeployment(c, m) + if err != nil { + c.Fatal(err) + } + if d.Checksum != kola.CosaBuild.Meta.OstreeCommit { + c.Fatalf("Got booted checksum=%s expected=%s", d.Checksum, kola.CosaBuild.Meta.OstreeCommit) + } + // And we should also like systemctl --failed here and stuff + }) +}