// 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 }