Andrey Smirnov d0585fb6b3
feat: reboot via kexec
This should save a lot of time on BIOS/POST time with bare metal
hardware.

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
2021-09-15 22:14:19 +03:00

232 lines
5.3 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 v1alpha1
import (
"errors"
"os"
multierror "github.com/hashicorp/go-multierror"
"github.com/talos-systems/go-blockdevice/blockdevice/probe"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/disk"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/adv"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha2"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
// State implements the state interface.
type State struct {
platform runtime.Platform
machine *MachineState
cluster *ClusterState
v2 runtime.V1Alpha2State
}
// MachineState represents the machine's state.
type MachineState struct {
platform runtime.Platform
disks map[string]*probe.ProbedBlockDevice
stagedInstall bool
stagedInstallImageRef string
stagedInstallOptions []byte
kexecPrepared bool
}
// ClusterState represents the cluster's state.
type ClusterState struct{}
// NewState initializes and returns the v1alpha1 state.
func NewState() (s *State, err error) {
p, err := platform.CurrentPlatform()
if err != nil {
return nil, err
}
machine := &MachineState{
platform: p,
}
err = machine.probeDisks()
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return nil, err
}
}
machine.probeMeta()
cluster := &ClusterState{}
v2State, err := v1alpha2.NewState()
if err != nil {
return nil, err
}
s = &State{
platform: p,
cluster: cluster,
machine: machine,
v2: v2State,
}
return s, nil
}
// Platform implements the state interface.
func (s *State) Platform() runtime.Platform {
return s.platform
}
// Machine implements the state interface.
func (s *State) Machine() runtime.MachineState {
return s.machine
}
// Cluster implements the state interface.
func (s *State) Cluster() runtime.ClusterState {
return s.cluster
}
// V1Alpha2 implements the state interface.
func (s *State) V1Alpha2() runtime.V1Alpha2State {
return s.v2
}
func (s *MachineState) probeDisks(labels ...string) error {
if s.platform.Mode() == runtime.ModeContainer {
return os.ErrNotExist
}
if len(labels) == 0 {
labels = []string{constants.EphemeralPartitionLabel, constants.BootPartitionLabel, constants.EFIPartitionLabel, constants.StatePartitionLabel}
}
if s.disks == nil {
s.disks = map[string]*probe.ProbedBlockDevice{}
}
for _, label := range labels {
if _, ok := s.disks[label]; ok {
continue
}
var dev *probe.ProbedBlockDevice
dev, err := probe.GetDevWithPartitionName(label)
if err != nil {
return err
}
s.disks[label] = dev
}
return nil
}
func (s *MachineState) probeMeta() {
if s.platform.Mode() == runtime.ModeContainer {
return
}
meta, err := bootloader.NewMeta()
if err != nil {
// ignore missing meta
return
}
defer meta.Close() //nolint:errcheck
stagedInstallImageRef, ok1 := meta.ADV.ReadTag(adv.StagedUpgradeImageRef)
stagedInstallOptions, ok2 := meta.ADV.ReadTag(adv.StagedUpgradeInstallOptions)
s.stagedInstall = ok1 && ok2
if s.stagedInstall {
// clear the staged install flags
meta.ADV.DeleteTag(adv.StagedUpgradeImageRef)
meta.ADV.DeleteTag(adv.StagedUpgradeInstallOptions)
if err = meta.Write(); err != nil {
// failed to delete staged install tags, clear the stagedInstall to prevent boot looping
s.stagedInstall = false
}
s.stagedInstallImageRef = stagedInstallImageRef
s.stagedInstallOptions = []byte(stagedInstallOptions)
}
}
// Disk implements the machine state interface.
func (s *MachineState) Disk(options ...disk.Option) *probe.ProbedBlockDevice {
opts := &disk.Options{
Label: constants.EphemeralPartitionLabel,
}
for _, opt := range options {
opt(opts)
}
s.probeDisks(opts.Label) //nolint:errcheck
return s.disks[opts.Label]
}
// Close implements the machine state interface.
func (s *MachineState) Close() error {
var result *multierror.Error
for label, disk := range s.disks {
if err := disk.Close(); err != nil {
e := multierror.Append(result, err)
if e != nil {
return e
}
}
delete(s.disks, label)
}
return result.ErrorOrNil()
}
// Installed implements the machine state interface.
func (s *MachineState) Installed() bool {
return s.Disk(
disk.WithPartitionLabel(constants.EphemeralPartitionLabel),
) != nil
}
// IsInstallStaged implements the machine state interface.
func (s *MachineState) IsInstallStaged() bool {
return s.stagedInstall
}
// StagedInstallImageRef implements the machine state interface.
func (s *MachineState) StagedInstallImageRef() string {
return s.stagedInstallImageRef
}
// StagedInstallOptions implements the machine state interface.
func (s *MachineState) StagedInstallOptions() []byte {
return s.stagedInstallOptions
}
// KexecPrepared implements the machine state interface.
func (s *MachineState) KexecPrepared(prepared bool) {
s.kexecPrepared = prepared
}
// IsKexecPrepared implements the machine state interface.
func (s *MachineState) IsKexecPrepared() bool {
return s.kexecPrepared
}