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" "strings"
"time" "time"
"github.com/dustin/go-humanize"
"github.com/talos-systems/go-blockdevice/blockdevice" "github.com/talos-systems/go-blockdevice/blockdevice"
"github.com/talos-systems/go-blockdevice/blockdevice/partition/gpt" "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/go-retry/retry"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime" "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) ephemeralTarget := EphemeralTarget(opts.Disk, NoFilesystem)
if opts.Force { targets := []*Target{efiTarget, biosTarget, bootTarget, metaTarget, stateTarget, ephemeralTarget}
ephemeralTarget.Force = true
} else { if !opts.Force {
ephemeralTarget.Force = false for _, target := range targets {
ephemeralTarget.Skip = true target.Force = false
stateTarget.Size = 0 // expand previous partition to cover whatever space is available target.Skip = true
}
} }
for _, target := range []*Target{efiTarget, biosTarget, bootTarget, metaTarget, stateTarget, ephemeralTarget} { for _, target := range targets {
if target == nil { if target == nil {
continue continue
} }
@ -373,6 +372,10 @@ func (m *Manifest) preserveContents(device Device, targets []*Target) (err error
anyPreserveContents := false anyPreserveContents := false
for _, target := range targets { for _, target := range targets {
if target.Skip {
continue
}
if target.PreserveContents { if target.PreserveContents {
anyPreserveContents = true anyPreserveContents = true
@ -405,6 +408,10 @@ func (m *Manifest) preserveContents(device Device, targets []*Target) (err error
} }
for _, target := range targets { for _, target := range targets {
if target.Skip {
continue
}
if !target.PreserveContents { if !target.PreserveContents {
continue continue
} }
@ -500,44 +507,3 @@ func (m *Manifest) zeroDevice(device Device) (err error) {
return bd.Close() 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/stretchr/testify/suite"
"github.com/talos-systems/go-blockdevice/blockdevice" "github.com/talos-systems/go-blockdevice/blockdevice"
"github.com/talos-systems/go-blockdevice/blockdevice/loopback" "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/cmd/installer/pkg/install"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime" "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/mount"
"github.com/talos-systems/talos/internal/pkg/partition" "github.com/talos-systems/talos/internal/pkg/partition"
"github.com/talos-systems/talos/pkg/machinery/constants" "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 // Some tests in this package cannot be run under buildkit, as buildkit doesn't propagate partition devices
@ -45,11 +42,6 @@ const (
gptReserved = 67 gptReserved = 67
) )
const (
legacyBootSize = 512 * partition.MiB
legacyEphemeralSize = diskSize - legacyBootSize - gptReserved*lbaSize
)
func TestManifestSuite(t *testing.T) { func TestManifestSuite(t *testing.T) {
suite.Run(t, new(manifestSuite)) 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()) bd, err := blockdevice.Open(suite.loopbackDevice.Name())
suite.Require().NoError(err) 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().Equal(constants.StatePartitionLabel, part.Name)
suite.Assert().EqualValues(0, part.Attributes) suite.Assert().EqualValues(0, part.Attributes)
if !upgradeFromLegacy {
suite.Assert().EqualValues(partition.StateSize/lbaSize, part.Length()) 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())
}
part = table.Partitions().Items()[5] part = table.Partitions().Items()[5]
suite.Assert().Equal(partition.LinuxFilesystemData, strings.ToUpper(part.Type.String())) suite.Assert().Equal(partition.LinuxFilesystemData, strings.ToUpper(part.Type.String()))
suite.Assert().Equal(constants.EphemeralPartitionLabel, part.Name) suite.Assert().Equal(constants.EphemeralPartitionLabel, part.Name)
suite.Assert().EqualValues(0, part.Attributes) 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()) 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().NoError(bd.Close()) suite.Assert().NoError(bd.Close())
@ -251,7 +235,7 @@ func (suite *manifestSuite) TestExecuteManifestClean() {
suite.Assert().NoError(manifest.Execute()) suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "", "A", false, false, false) suite.verifyBlockdevice(manifest, "", "A", false, false)
} }
func (suite *manifestSuite) TestExecuteManifestForce() { func (suite *manifestSuite) TestExecuteManifestForce() {
@ -267,7 +251,7 @@ func (suite *manifestSuite) TestExecuteManifestForce() {
suite.Assert().NoError(manifest.Execute()) suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "", "A", false, false, false) suite.verifyBlockdevice(manifest, "", "A", false, false)
// reinstall // reinstall
@ -282,7 +266,7 @@ func (suite *manifestSuite) TestExecuteManifestForce() {
suite.Assert().NoError(manifest.Execute()) suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "A", "B", true, false, false) suite.verifyBlockdevice(manifest, "A", "B", true, false)
} }
func (suite *manifestSuite) TestExecuteManifestPreserve() { func (suite *manifestSuite) TestExecuteManifestPreserve() {
@ -298,7 +282,7 @@ func (suite *manifestSuite) TestExecuteManifestPreserve() {
suite.Assert().NoError(manifest.Execute()) suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "", "A", false, false, false) suite.verifyBlockdevice(manifest, "", "A", false, false)
// reinstall // reinstall
@ -312,47 +296,7 @@ func (suite *manifestSuite) TestExecuteManifestPreserve() {
suite.Assert().NoError(manifest.Execute()) suite.Assert().NoError(manifest.Execute())
suite.verifyBlockdevice(manifest, "A", "B", true, true, false) suite.verifyBlockdevice(manifest, "A", "B", true, true)
}
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)
} }
func (suite *manifestSuite) TestTargetInstall() { func (suite *manifestSuite) TestTargetInstall() {
@ -389,65 +333,3 @@ func (suite *manifestSuite) TestTargetInstall() {
suite.Require().NoError(err) 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" "os"
"path/filepath" "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/partition/gpt"
"github.com/talos-systems/go-blockdevice/blockdevice/util" "github.com/talos-systems/go-blockdevice/blockdevice/util"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@ -161,7 +163,7 @@ func (t *Target) Locate(pt *gpt.GPT) (*gpt.Partition, error) {
if part.Name == t.Label { if part.Name == t.Label {
var err error var err error
t.PartitionName, err = util.PartPath(t.Device, int(part.Number)) t.PartitionName, err = part.Path()
if err != nil { if err != nil {
return part, err return part, err
} }
@ -173,6 +175,52 @@ func (t *Target) Locate(pt *gpt.GPT) (*gpt.Partition, error) {
return nil, nil 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. // Format creates a filesystem on the device/partition.
func (t *Target) Format() error { func (t *Target) Format() error {
if t.Skip { if t.Skip {

View File

@ -106,9 +106,6 @@ const (
// the boot path. // the boot path.
BootMountPoint = "/boot" 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 // EphemeralPartitionLabel is the label of the partition to use for
// mounting at the data path. // mounting at the data path.
EphemeralPartitionLabel = "EPHEMERAL" EphemeralPartitionLabel = "EPHEMERAL"