fix: don't touch any partitions on upgrade with --preserve

This fixes a case of upgrade from 0.9.0-alpha.4 to 0.9.0-beta.0. With
introduced proper partition alignment and physical block size != 512,
partitions before ephemeral will be moved around a bit (due to the
alignment), and `STATE` partition size might change a bit.

If encryption is enabled, contents are preserved as raw bytes, so
partition size should be exactly same during restore.

Drop code (mostly tests) which handled 0.6 to 0.7 upgrades.

On upgrade with preserve don't touch any partitions, at least for 0.8 ->
0.9 layout hasn't changed.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
This commit is contained in:
Andrey Smirnov 2021-03-15 17:29:27 +03:00 committed by talos-bot
parent 891f90fee9
commit 3c5bfbb473
4 changed files with 73 additions and 180 deletions

View File

@ -14,10 +14,8 @@ import (
"strings"
"time"
"github.com/dustin/go-humanize"
"github.com/talos-systems/go-blockdevice/blockdevice"
"github.com/talos-systems/go-blockdevice/blockdevice/partition/gpt"
"github.com/talos-systems/go-blockdevice/blockdevice/util"
"github.com/talos-systems/go-retry/retry"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
@ -140,15 +138,16 @@ func NewManifest(label string, sequence runtime.Sequence, bootPartitionFound boo
ephemeralTarget := EphemeralTarget(opts.Disk, NoFilesystem)
if opts.Force {
ephemeralTarget.Force = true
} else {
ephemeralTarget.Force = false
ephemeralTarget.Skip = true
stateTarget.Size = 0 // expand previous partition to cover whatever space is available
targets := []*Target{efiTarget, biosTarget, bootTarget, metaTarget, stateTarget, ephemeralTarget}
if !opts.Force {
for _, target := range targets {
target.Force = false
target.Skip = true
}
}
for _, target := range []*Target{efiTarget, biosTarget, bootTarget, metaTarget, stateTarget, ephemeralTarget} {
for _, target := range targets {
if target == nil {
continue
}
@ -373,6 +372,10 @@ func (m *Manifest) preserveContents(device Device, targets []*Target) (err error
anyPreserveContents := false
for _, target := range targets {
if target.Skip {
continue
}
if target.PreserveContents {
anyPreserveContents = true
@ -405,6 +408,10 @@ func (m *Manifest) preserveContents(device Device, targets []*Target) (err error
}
for _, target := range targets {
if target.Skip {
continue
}
if !target.PreserveContents {
continue
}
@ -500,44 +507,3 @@ func (m *Manifest) zeroDevice(device Device) (err error) {
return bd.Close()
}
// Partition creates a new partition on the specified device.
func (t *Target) Partition(pt *gpt.GPT, pos int, bd *blockdevice.BlockDevice) (err error) {
if t.Skip {
part := pt.Partitions().FindByName(t.Label)
if part != nil {
log.Printf("skipped %s (%s) size %d blocks", t.PartitionName, t.Label, part.Length())
}
return nil
}
log.Printf("partitioning %s - %s %q\n", t.Device, t.Label, humanize.Bytes(t.Size))
opts := []gpt.PartitionOption{
gpt.WithPartitionType(t.PartitionType),
gpt.WithPartitionName(t.Label),
}
if t.Size == 0 {
opts = append(opts, gpt.WithMaximumSize(true))
}
if t.LegacyBIOSBootable {
opts = append(opts, gpt.WithLegacyBIOSBootableAttribute(true))
}
part, err := pt.InsertAt(pos, t.Size, opts...)
if err != nil {
return err
}
t.PartitionName, err = util.PartPath(t.Device, int(part.Number))
if err != nil {
return err
}
log.Printf("created %s (%s) size %d blocks", t.PartitionName, t.Label, part.Length())
return nil
}

View File

@ -16,15 +16,12 @@ import (
"github.com/stretchr/testify/suite"
"github.com/talos-systems/go-blockdevice/blockdevice"
"github.com/talos-systems/go-blockdevice/blockdevice/loopback"
"github.com/talos-systems/go-blockdevice/blockdevice/partition/gpt"
"github.com/talos-systems/go-blockdevice/blockdevice/util"
"github.com/talos-systems/talos/cmd/installer/pkg/install"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/internal/pkg/mount"
"github.com/talos-systems/talos/internal/pkg/partition"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/makefs"
)
// Some tests in this package cannot be run under buildkit, as buildkit doesn't propagate partition devices
@ -45,11 +42,6 @@ const (
gptReserved = 67
)
const (
legacyBootSize = 512 * partition.MiB
legacyEphemeralSize = diskSize - legacyBootSize - gptReserved*lbaSize
)
func TestManifestSuite(t *testing.T) {
suite.Run(t, new(manifestSuite))
}
@ -99,7 +91,7 @@ func (suite *manifestSuite) skipIfNotRoot() {
}
}
func (suite *manifestSuite) verifyBlockdevice(manifest *install.Manifest, current, next string, verifyConfigPersistence, verifyEphemeralPersistence, upgradeFromLegacy bool) {
func (suite *manifestSuite) verifyBlockdevice(manifest *install.Manifest, current, next string, verifyConfigPersistence, verifyEphemeralPersistence bool) {
bd, err := blockdevice.Open(suite.loopbackDevice.Name())
suite.Require().NoError(err)
@ -141,22 +133,14 @@ func (suite *manifestSuite) verifyBlockdevice(manifest *install.Manifest, curren
suite.Assert().Equal(constants.StatePartitionLabel, part.Name)
suite.Assert().EqualValues(0, part.Attributes)
if !upgradeFromLegacy {
suite.Assert().EqualValues(partition.StateSize/lbaSize, part.Length())
} else {
suite.Assert().EqualValues((diskSize-legacyEphemeralSize-partition.EFISize-partition.BIOSGrubSize-partition.BootSize-partition.MetaSize)/lbaSize-gptReserved, part.Length())
}
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)
if !upgradeFromLegacy {
suite.Assert().EqualValues((diskSize-partition.EFISize-partition.BIOSGrubSize-partition.BootSize-partition.MetaSize-partition.StateSize)/lbaSize-gptReserved, part.Length())
} else {
suite.Assert().EqualValues(legacyEphemeralSize/lbaSize, part.Length())
}
suite.Assert().EqualValues((diskSize-partition.EFISize-partition.BIOSGrubSize-partition.BootSize-partition.MetaSize-partition.StateSize)/lbaSize-gptReserved, part.Length())
suite.Assert().NoError(bd.Close())
@ -251,7 +235,7 @@ func (suite *manifestSuite) TestExecuteManifestClean() {
suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "", "A", false, false, false)
suite.verifyBlockdevice(manifest, "", "A", false, false)
}
func (suite *manifestSuite) TestExecuteManifestForce() {
@ -267,7 +251,7 @@ func (suite *manifestSuite) TestExecuteManifestForce() {
suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "", "A", false, false, false)
suite.verifyBlockdevice(manifest, "", "A", false, false)
// reinstall
@ -282,7 +266,7 @@ func (suite *manifestSuite) TestExecuteManifestForce() {
suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "A", "B", true, false, false)
suite.verifyBlockdevice(manifest, "A", "B", true, false)
}
func (suite *manifestSuite) TestExecuteManifestPreserve() {
@ -298,7 +282,7 @@ func (suite *manifestSuite) TestExecuteManifestPreserve() {
suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "", "A", false, false, false)
suite.verifyBlockdevice(manifest, "", "A", false, false)
// reinstall
@ -312,47 +296,7 @@ func (suite *manifestSuite) TestExecuteManifestPreserve() {
suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "A", "B", true, true, false)
}
func (suite *manifestSuite) TestExecuteManifestLegacyForce() {
suite.skipUnderBuildkit()
suite.createTalosLegacyLayout()
// upgrade with force
manifest, err := install.NewManifest("A", runtime.SequenceUpgrade, true, &install.Options{
Disk: suite.loopbackDevice.Name(),
Bootloader: true,
Force: true,
Board: constants.BoardNone,
})
suite.Require().NoError(err)
suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "", "", true, false, false)
}
func (suite *manifestSuite) TestExecuteManifestLegacyPreserve() {
suite.skipUnderBuildkit()
suite.createTalosLegacyLayout()
// upgrade with preserve
manifest, err := install.NewManifest("A", runtime.SequenceUpgrade, true, &install.Options{
Disk: suite.loopbackDevice.Name(),
Bootloader: true,
Force: false,
Board: constants.BoardNone,
})
suite.Require().NoError(err)
suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "", "", true, true, true)
suite.verifyBlockdevice(manifest, "A", "B", true, true)
}
func (suite *manifestSuite) TestTargetInstall() {
@ -389,65 +333,3 @@ func (suite *manifestSuite) TestTargetInstall() {
suite.Require().NoError(err)
}
}
func (suite *manifestSuite) createTalosLegacyLayout() {
bd, err := blockdevice.Open(suite.loopbackDevice.Name())
suite.Require().NoError(err)
defer bd.Close() //nolint:errcheck
// create Talos 0.6 partitions
table, err := gpt.New(bd.Device())
suite.Require().NoError(err)
partBoot, err := table.Add(512*partition.MiB,
gpt.WithLegacyBIOSBootableAttribute(true),
gpt.WithPartitionName(constants.LegacyBootPartitionLabel),
gpt.WithPartitionType("28732AC1-1FF8-D211-BA4B-00A0C93EC93B"),
)
suite.Require().NoError(err)
partEphemeral, err := table.Add(0,
gpt.WithPartitionName(constants.EphemeralPartitionLabel),
gpt.WithPartitionType("0FC63DAF-8483-4772-8E79-3D69D8477DE4"),
gpt.WithMaximumSize(true),
)
suite.Require().NoError(err)
suite.Require().NoError(table.Write())
suite.Require().NoError(bd.Close())
// format partitions
partBootPath, err := util.PartPath(suite.loopbackDevice.Name(), int(partBoot.Number))
suite.Require().NoError(err)
suite.Require().NoError(makefs.VFAT(partBootPath))
partEphemeralPath, err := util.PartPath(suite.loopbackDevice.Name(), int(partEphemeral.Number))
suite.Require().NoError(err)
suite.Require().NoError(makefs.XFS(partEphemeralPath, makefs.WithLabel(constants.EphemeralPartitionLabel)))
// mount partitions temporarily and fill with data
tempDir, err := ioutil.TempDir("", "talos")
suite.Require().NoError(err)
defer func() {
suite.Assert().NoError(os.RemoveAll(tempDir))
}()
mountpoints := mount.NewMountPoints()
mountpoints.Set(constants.LegacyBootPartitionLabel, mount.NewMountPoint(partBootPath, filepath.Join(tempDir, "boot"), partition.FilesystemTypeVFAT, 0, ""))
mountpoints.Set(constants.EphemeralPartitionLabel, mount.NewMountPoint(partEphemeralPath, filepath.Join(tempDir, "var"), partition.FilesystemTypeXFS, 0, ""))
err = mount.Mount(mountpoints)
suite.Require().NoError(err)
defer func() {
suite.Assert().NoError(mount.Unmount(mountpoints))
}()
suite.Assert().NoError(ioutil.WriteFile(filepath.Join(tempDir, "boot", "config.yaml"), []byte("#!yaml"), 0o600))
suite.Assert().NoError(ioutil.WriteFile(filepath.Join(tempDir, "var", "content"), []byte("data"), 0o600))
}

View File

@ -14,6 +14,8 @@ import (
"os"
"path/filepath"
"github.com/dustin/go-humanize"
"github.com/talos-systems/go-blockdevice/blockdevice"
"github.com/talos-systems/go-blockdevice/blockdevice/partition/gpt"
"github.com/talos-systems/go-blockdevice/blockdevice/util"
"golang.org/x/sys/unix"
@ -161,7 +163,7 @@ func (t *Target) Locate(pt *gpt.GPT) (*gpt.Partition, error) {
if part.Name == t.Label {
var err error
t.PartitionName, err = util.PartPath(t.Device, int(part.Number))
t.PartitionName, err = part.Path()
if err != nil {
return part, err
}
@ -173,6 +175,52 @@ func (t *Target) Locate(pt *gpt.GPT) (*gpt.Partition, error) {
return nil, nil
}
// Partition creates a new partition on the specified device.
func (t *Target) Partition(pt *gpt.GPT, pos int, bd *blockdevice.BlockDevice) (err error) {
if t.Skip {
part := pt.Partitions().FindByName(t.Label)
if part != nil {
log.Printf("skipped %s (%s) size %d blocks", t.PartitionName, t.Label, part.Length())
t.PartitionName, err = part.Path()
if err != nil {
return err
}
}
return nil
}
log.Printf("partitioning %s - %s %q\n", t.Device, t.Label, humanize.Bytes(t.Size))
opts := []gpt.PartitionOption{
gpt.WithPartitionType(t.PartitionType),
gpt.WithPartitionName(t.Label),
}
if t.Size == 0 {
opts = append(opts, gpt.WithMaximumSize(true))
}
if t.LegacyBIOSBootable {
opts = append(opts, gpt.WithLegacyBIOSBootableAttribute(true))
}
part, err := pt.InsertAt(pos, t.Size, opts...)
if err != nil {
return err
}
t.PartitionName, err = part.Path()
if err != nil {
return err
}
log.Printf("created %s (%s) size %d blocks", t.PartitionName, t.Label, part.Length())
return nil
}
// Format creates a filesystem on the device/partition.
func (t *Target) Format() error {
if t.Skip {

View File

@ -106,9 +106,6 @@ const (
// the boot path.
BootMountPoint = "/boot"
// LegacyBootPartitionLabel is the label of the boot partition in older versions of Talos.
LegacyBootPartitionLabel = "ESP"
// EphemeralPartitionLabel is the label of the partition to use for
// mounting at the data path.
EphemeralPartitionLabel = "EPHEMERAL"