Skip to content

Commit

Permalink
feat: add grub option to drop to maintenance mode
Browse files Browse the repository at this point in the history
- [x] Support `talos.experimental.wipe=system:EPHEMERAL,STATE` boot kernel arg
- [x] GRUB option to wipe like above
- [x] update GRUB library to handle that

Closes #6842

Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
  • Loading branch information
DmitriyMV committed Mar 7, 2023
1 parent 642fe0c commit 22ef81c
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 89 deletions.
20 changes: 20 additions & 0 deletions cmd/installer/pkg/install/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,26 @@ var NoFilesystem = &Target{
},
}

// ParseTarget parses the target from the label and creates a required target.
func ParseTarget(label, deviceName string) (*Target, error) {
switch label {
case constants.EFIPartitionLabel:
return EFITarget(deviceName, nil), nil
case constants.BIOSGrubPartitionLabel:
return BIOSTarget(deviceName, nil), nil
case constants.BootPartitionLabel:
return BootTarget(deviceName, nil), nil
case constants.MetaPartitionLabel:
return MetaTarget(deviceName, nil), nil
case constants.StatePartitionLabel:
return StateTarget(deviceName, NoFilesystem), nil
case constants.EphemeralPartitionLabel:
return EphemeralTarget(deviceName, NoFilesystem), nil
default:
return nil, fmt.Errorf("label %q is not supported", label)
}
}

// EFITarget builds the default EFI target.
func EFITarget(device string, extra *Target) *Target {
target := &Target{
Expand Down
54 changes: 54 additions & 0 deletions cmd/installer/pkg/install/target_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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 (
"testing"

"github.com/stretchr/testify/require"

"github.com/siderolabs/talos/cmd/installer/pkg/install"
)

func TestParseTarget(t *testing.T) {
type args struct {
label string
deviceName string
}

tests := map[string]struct {
args args
want *install.Target
wantErr bool
}{
"EPHEMERAL": {
args: args{
label: "EPHEMERAL",
deviceName: "/dev/sda",
},
want: install.EphemeralTarget("/dev/sda", install.NoFilesystem),
},
"UNKNOWN": {
args: args{
label: "UNKNOWN",
deviceName: "/dev/sda",
},
wantErr: true,
},
}

for name, tt := range tests {
t.Run(name, func(t *testing.T) {
got, err := install.ParseTarget(tt.args.label, tt.args.deviceName)
if (err != nil) != tt.wantErr {
t.Errorf("ParseTarget() error = %v, wantErr %v", err, tt.wantErr)

return
}

require.Equal(t, tt.want, got)
})
}
}
8 changes: 8 additions & 0 deletions hack/release.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ Modules can still be loaded explicitly by defining it in [machine configuration]
Talos now supports re-building the kernel modules dependency tree information on upgrades.
This allows modules of same name to co-exist as in-tree and external modules.
System Extensions can provide modules installed into `extras` directory and when loading it'll take precendence over the in-tree module.
"""

[notes.kernel-reset-argument]
title = "Kernel Reset Argument"
description="""\
Talos now supports `talos.experimental.wipe=system:EPHEMERAL,STATE` kernel argument.
Talos now also supports the new GRUB boot option - "Reset Talos installation and return to maintenance mode".
Both of this options will reset EPHEMERAL and STATE partitions and will return Talos into maintenance mode after the reboot.
"""

[notes.etcd]
Expand Down
20 changes: 3 additions & 17 deletions internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -661,23 +661,9 @@ func (s *Server) Reset(ctx context.Context, in *machine.ResetRequest) (reply *ma
}

for _, spec := range in.GetSystemPartitionsToWipe() {
var target *installer.Target

switch spec.Label {
case constants.EFIPartitionLabel:
target = installer.EFITarget(bd.Device().Name(), nil)
case constants.BIOSGrubPartitionLabel:
target = installer.BIOSTarget(bd.Device().Name(), nil)
case constants.BootPartitionLabel:
target = installer.BootTarget(bd.Device().Name(), nil)
case constants.MetaPartitionLabel:
target = installer.MetaTarget(bd.Device().Name(), nil)
case constants.StatePartitionLabel:
target = installer.StateTarget(bd.Device().Name(), installer.NoFilesystem)
case constants.EphemeralPartitionLabel:
target = installer.EphemeralTarget(bd.Device().Name(), installer.NoFilesystem)
default:
return nil, fmt.Errorf("label %q is not supported", spec.Label)
target, err := installer.ParseTarget(spec.Label, bd.Device().Name())
if err != nil {
return nil, err
}

_, err = target.Locate(pt)
Expand Down
2 changes: 1 addition & 1 deletion internal/app/machined/pkg/runtime/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

// TaskSetupFunc defines the function that a task will execute for a specific runtime
// mode.
type TaskSetupFunc func(seq Sequence, data interface{}) (TaskExecutionFunc, string)
type TaskSetupFunc func(seq Sequence, data any) (TaskExecutionFunc, string)

// TaskExecutionFunc defines the function that a task will execute for a specific runtime
// mode.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ menuentry "{{ $entry.Name }}" {
initrd {{ $entry.Initrd }}
}
{{ end -}}
{{ $defaultEntry := index .Entries .Default -}}
menuentry "Reset Talos installation and return to maintenance mode" {
set gfxmode=auto
set gfxpayload=text
linux {{ $defaultEntry.Linux }} {{ quote $defaultEntry.Cmdline }} talos.experimental.wipe=system:EPHEMERAL,STATE
initrd {{ $defaultEntry.Initrd }}
}
`

// Write the grub configuration to the given file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ menuentry "A - Test v0.0.1" {
linux /A/vmlinuz cmdline A
initrd /A/initramfs.xz
}
menuentry "Reset Talos installation and return to maintenance mode" {
set gfxmode=auto
set gfxpayload=text
linux /A/vmlinuz cmdline A talos.experimental.wipe=system:EPHEMERAL,STATE
initrd /A/initramfs.xz
}
17 changes: 10 additions & 7 deletions internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (p PhaseList) AppendWhen(when bool, name string, tasks ...runtime.TaskSetup
return p
}

// AppendWithDeferredCheck appends a task to the phase list but skips the sequence if `check func()` returns `true` during execution.
// AppendWithDeferredCheck appends a task to the phase list but skips the sequence if `check func()` returns `false` during execution.
func (p PhaseList) AppendWithDeferredCheck(check func() bool, name string, tasks ...runtime.TaskSetupFunc) PhaseList {
p = append(p, runtime.Phase{
Name: name,
Expand Down Expand Up @@ -118,6 +118,14 @@ func (*Sequencer) Initialize(r runtime.Runtime) []runtime.Phase {
},
"dashboard",
StartDashboard,
).AppendWithDeferredCheck(
func() bool {
wipe := procfs.ProcCmdline().Get(constants.KernelParamWipe).First()

return pointer.SafeDeref(wipe) != ""
},
"wipeDisks",
ResetSystemDiskPartitions,
).AppendWithDeferredCheck(
func() bool {
return r.State().Machine().Installed()
Expand Down Expand Up @@ -200,11 +208,6 @@ func (*Sequencer) Install(r runtime.Runtime) []runtime.Phase {
func (*Sequencer) Boot(r runtime.Runtime) []runtime.Phase {
phases := PhaseList{}

wipe := procfs.ProcCmdline().Get(constants.KernelParamWipe).First()
if wipe != nil && *wipe == "system" {
return phases.Append("wipeSystemDisk", ResetSystemDisk).Append("reboot", Reboot)
}

phases = phases.AppendWhen(
r.State().Platform().Mode() != runtime.ModeContainer,
"saveStateEncryptionConfig",
Expand Down Expand Up @@ -436,7 +439,7 @@ func (*Sequencer) StageUpgrade(r runtime.Runtime, in *machineapi.UpgradeRequest)
}

// MaintenanceUpgrade is the upgrade sequence in maintenance mode.
func (*Sequencer) MaintenanceUpgrade(r runtime.Runtime, in *machineapi.UpgradeRequest) []runtime.Phase {
func (*Sequencer) MaintenanceUpgrade(r runtime.Runtime, _ *machineapi.UpgradeRequest) []runtime.Phase {
phases := PhaseList{}

switch r.State().Platform().Mode() { //nolint:exhaustive
Expand Down
Loading

0 comments on commit 22ef81c

Please sign in to comment.