Skip to content

Commit

Permalink
fix: don't touch any partitions on upgrade with --preserve
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
smira authored and talos-bot committed Mar 15, 2021
1 parent 891f90f commit 3c5bfbb
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 180 deletions.
66 changes: 16 additions & 50 deletions cmd/installer/pkg/install/manifest.go
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
134 changes: 8 additions & 126 deletions cmd/installer/pkg/install/manifest_test.go
Expand Up @@ -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
Expand All @@ -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))
}
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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())

Expand Down Expand Up @@ -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() {
Expand All @@ -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

Expand All @@ -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() {
Expand All @@ -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

Expand All @@ -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() {
Expand Down Expand Up @@ -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))
}
50 changes: 49 additions & 1 deletion cmd/installer/pkg/install/target.go
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand All @@ -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 {
Expand Down
3 changes: 0 additions & 3 deletions pkg/machinery/constants/constants.go
Expand Up @@ -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"
Expand Down

0 comments on commit 3c5bfbb

Please sign in to comment.