talos/cmd/installer/pkg/install/manifest_test.go
Andrey Smirnov e0f383598e
chore: clean up the output of the imager
Use `Progress`, and options to pass around the way messages are written.

Fixed some tiny issues in the code, but otherwise no functional changes.

To make colored output work with `docker run`, switched back image
generation to use volume mount for output (old mode is still
functioning, but it's not the default, and it works when docker is not
running on the same host).

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
2023-08-07 16:00:14 +04:00

329 lines
10 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package install_test
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"testing"
"github.com/siderolabs/go-blockdevice/blockdevice"
"github.com/siderolabs/go-blockdevice/blockdevice/loopback"
"github.com/stretchr/testify/suite"
"github.com/siderolabs/talos/cmd/installer/pkg/install"
"github.com/siderolabs/talos/internal/pkg/mount"
"github.com/siderolabs/talos/internal/pkg/partition"
"github.com/siderolabs/talos/pkg/machinery/constants"
)
// Some tests in this package cannot be run under buildkit, as buildkit doesn't propagate partition devices
// like /dev/loopXpY into the sandbox. To run the tests on your local computer, do the following:
//
// go test -exec sudo -v --count 1 ./cmd/installer/pkg/install/
type manifestSuite struct {
suite.Suite
disk *os.File
loopbackDevice *os.File
}
const (
diskSize = 4 * 1024 * 1024 * 1024 // 4 GiB
lbaSize = 512
gptReserved = 67
)
func TestManifestSuite(t *testing.T) {
suite.Run(t, new(manifestSuite))
}
func (suite *manifestSuite) SetupTest() {
suite.skipIfNotRoot()
var err error
suite.disk, err = os.CreateTemp("", "talos")
suite.Require().NoError(err)
suite.Require().NoError(suite.disk.Truncate(diskSize))
suite.loopbackDevice, err = loopback.NextLoopDevice()
suite.Require().NoError(err)
suite.T().Logf("Using %s", suite.loopbackDevice.Name())
suite.Require().NoError(loopback.Loop(suite.loopbackDevice, suite.disk))
suite.Require().NoError(loopback.LoopSetReadWrite(suite.loopbackDevice))
// set the env vars xfsprogs expects to use Talos STATE partition which is 100Megs
// whereas xfs expects a default minimum size of 300Megs if these are not set.
// Ref: https://git.kernel.org/pub/scm/fs/xfs/xfsprogs-dev.git/tree/mkfs/xfs_mkfs.c?h=v6.3.0#n2582
suite.T().Setenv("TEST_DIR", "true")
suite.T().Setenv("TEST_DEV", "true")
suite.T().Setenv("QA_CHECK_FS", "true")
}
func (suite *manifestSuite) TearDownTest() {
if suite.loopbackDevice != nil {
suite.Assert().NoError(loopback.Unloop(suite.loopbackDevice))
}
if suite.disk != nil {
suite.Assert().NoError(os.Remove(suite.disk.Name()))
suite.Assert().NoError(suite.disk.Close())
}
}
func (suite *manifestSuite) skipUnderBuildkit() {
hostname, _ := os.Hostname() //nolint:errcheck
if hostname == "buildkitsandbox" {
suite.T().Skip("test not supported under buildkit as partition devices are not propagated from /dev")
}
}
func (suite *manifestSuite) skipIfNotRoot() {
if os.Getuid() != 0 {
suite.T().Skip("can't run the test as non-root")
}
}
func (suite *manifestSuite) verifyBlockdevice(manifest *install.Manifest, current, next string, verifyConfigPersistence, verifyEphemeralPersistence bool) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
bd, err := blockdevice.Open(suite.loopbackDevice.Name())
suite.Require().NoError(err)
defer bd.Close() //nolint:errcheck
table, err := bd.PartitionTable()
suite.Require().NoError(err)
// verify partition table
suite.Assert().Len(table.Partitions().Items(), 6)
part := table.Partitions().Items()[0]
suite.Assert().Equal(partition.EFISystemPartition, strings.ToUpper(part.Type.String()))
suite.Assert().Equal(constants.EFIPartitionLabel, part.Name)
suite.Assert().EqualValues(0, part.Attributes)
suite.Assert().EqualValues(partition.EFISize/lbaSize, part.Length())
part = table.Partitions().Items()[1]
suite.Assert().Equal(partition.BIOSBootPartition, strings.ToUpper(part.Type.String()))
suite.Assert().Equal(constants.BIOSGrubPartitionLabel, part.Name)
suite.Assert().EqualValues(4, part.Attributes)
suite.Assert().EqualValues(partition.BIOSGrubSize/lbaSize, part.Length())
part = table.Partitions().Items()[2]
suite.Assert().Equal(partition.LinuxFilesystemData, strings.ToUpper(part.Type.String()))
suite.Assert().Equal(constants.BootPartitionLabel, part.Name)
suite.Assert().EqualValues(0, part.Attributes)
suite.Assert().EqualValues(partition.BootSize/lbaSize, part.Length())
part = table.Partitions().Items()[3]
suite.Assert().Equal(partition.LinuxFilesystemData, strings.ToUpper(part.Type.String()))
suite.Assert().Equal(constants.MetaPartitionLabel, part.Name)
suite.Assert().EqualValues(0, part.Attributes)
suite.Assert().EqualValues(partition.MetaSize/lbaSize, part.Length())
part = table.Partitions().Items()[4]
suite.Assert().Equal(partition.LinuxFilesystemData, strings.ToUpper(part.Type.String()))
suite.Assert().Equal(constants.StatePartitionLabel, part.Name)
suite.Assert().EqualValues(0, part.Attributes)
suite.Assert().EqualValues(partition.StateSize/lbaSize, part.Length())
part = table.Partitions().Items()[5]
suite.Assert().Equal(partition.LinuxFilesystemData, strings.ToUpper(part.Type.String()))
suite.Assert().Equal(constants.EphemeralPartitionLabel, part.Name)
suite.Assert().EqualValues(0, part.Attributes)
suite.Assert().Greater(part.Length(), uint64(1024*1024*1024/lbaSize))
suite.Assert().NoError(bd.Close())
// query mount points directly for the device
mountpoints, err := mount.SystemMountPointsForDevice(ctx, suite.loopbackDevice.Name())
suite.Require().NoError(err)
suite.Assert().Equal(4, mountpoints.Len())
// verify filesystems by mounting and unmounting
tempDir := suite.T().TempDir()
mountpoints, err = manifest.SystemMountpoints(ctx)
suite.Require().NoError(err)
suite.Assert().Equal(4, mountpoints.Len())
suite.Require().NoError(mount.PrefixMountTargets(mountpoints, tempDir))
err = mount.Mount(mountpoints)
suite.Require().NoError(err)
defer func() {
suite.Assert().NoError(mount.Unmount(mountpoints))
}()
metaPath := fmt.Sprintf("%sp%d", suite.loopbackDevice.Name(), table.Partitions().Items()[3].Number)
if verifyConfigPersistence {
suite.Assert().FileExists(filepath.Join(tempDir, "system", "state", "config.yaml"))
}
if verifyEphemeralPersistence {
suite.Assert().FileExists(filepath.Join(tempDir, "var", "content"))
}
if current != "" {
// verify that current was preserved
suite.Assert().DirExists(filepath.Join(tempDir, "boot", current))
suite.Assert().FileExists(filepath.Join(tempDir, "boot", current, "kernel"))
buf := make([]byte, len(current))
f, err := os.Open(metaPath)
suite.Require().NoError(err)
_, err = io.ReadFull(f, buf)
suite.Require().NoError(err)
suite.Assert().Equal(current, string(buf))
suite.Assert().NoError(f.Close())
}
if next != "" {
suite.Assert().NoError(os.MkdirAll(filepath.Join(tempDir, "boot", next), 0o700))
suite.Assert().NoError(os.WriteFile(filepath.Join(tempDir, "boot", next, "kernel"), []byte("LINUX!"), 0o660))
suite.Assert().NoError(os.WriteFile(filepath.Join(tempDir, "system", "state", "config.yaml"), []byte("#!yaml"), 0o660))
buf := []byte(next)
f, err := os.OpenFile(metaPath, os.O_WRONLY, 0)
suite.Require().NoError(err)
_, err = f.Write(buf)
suite.Require().NoError(err)
suite.Assert().NoError(f.Close())
}
suite.Assert().NoError(os.WriteFile(filepath.Join(tempDir, "var", "content"), []byte("data"), 0o600))
}
func (suite *manifestSuite) TestExecuteManifestClean() {
suite.skipUnderBuildkit()
manifest, err := install.NewManifest(install.ModeInstall, false, false, &install.Options{
Disk: suite.loopbackDevice.Name(),
Force: true,
Board: constants.BoardNone,
Printf: suite.T().Logf,
})
suite.Require().NoError(err)
// in the tests overlay mounts should be ignored
dev := manifest.Devices[suite.loopbackDevice.Name()]
dev.SkipOverlayMountsCheck = true
manifest.Devices[suite.loopbackDevice.Name()] = dev
suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "", "A", false, false)
}
func (suite *manifestSuite) TestExecuteManifestForce() {
suite.skipUnderBuildkit()
manifest, err := install.NewManifest(install.ModeInstall, false, false, &install.Options{
Disk: suite.loopbackDevice.Name(),
Force: true,
Board: constants.BoardNone,
Printf: suite.T().Logf,
})
suite.Require().NoError(err)
// in the tests overlay mounts should be ignored
dev := manifest.Devices[suite.loopbackDevice.Name()]
dev.SkipOverlayMountsCheck = true
manifest.Devices[suite.loopbackDevice.Name()] = dev
suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "", "A", false, false)
// reinstall
manifest, err = install.NewManifest(install.ModeUpgrade, false, true, &install.Options{
Disk: suite.loopbackDevice.Name(),
Force: true,
Zero: true,
Board: constants.BoardNone,
Printf: suite.T().Logf,
})
suite.Require().NoError(err)
// in the tests overlay mounts should be ignored
dev = manifest.Devices[suite.loopbackDevice.Name()]
dev.SkipOverlayMountsCheck = true
manifest.Devices[suite.loopbackDevice.Name()] = dev
suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "A", "B", true, false)
}
func (suite *manifestSuite) TestExecuteManifestPreserve() {
suite.skipUnderBuildkit()
manifest, err := install.NewManifest(install.ModeInstall, false, false, &install.Options{
Disk: suite.loopbackDevice.Name(),
Force: true,
Board: constants.BoardNone,
Printf: suite.T().Logf,
})
suite.Require().NoError(err)
// in the tests overlay mounts should be ignored
dev := manifest.Devices[suite.loopbackDevice.Name()]
dev.SkipOverlayMountsCheck = true
manifest.Devices[suite.loopbackDevice.Name()] = dev
suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "", "A", false, false)
// reinstall
manifest, err = install.NewManifest(install.ModeUpgrade, false, true, &install.Options{
Disk: suite.loopbackDevice.Name(),
Force: false,
Board: constants.BoardNone,
Printf: suite.T().Logf,
})
suite.Require().NoError(err)
// in the tests overlay mounts should be ignored
dev = manifest.Devices[suite.loopbackDevice.Name()]
dev.SkipOverlayMountsCheck = true
manifest.Devices[suite.loopbackDevice.Name()] = dev
suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "A", "B", true, true)
}