2019-10-25 23:45:41 +03:00
// 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/.
2019-07-31 17:16:10 +00:00
2020-04-23 21:12:44 -07:00
package install
2019-07-31 17:16:10 +00:00
import (
2022-11-24 22:48:22 +04:00
"context"
2019-10-16 04:50:06 +00:00
"fmt"
2020-03-10 07:52:10 -07:00
"log"
2019-07-31 17:16:10 +00:00
2022-09-20 22:51:04 +05:30
"github.com/siderolabs/go-blockdevice/blockdevice"
2022-11-01 12:06:37 +04:00
"github.com/siderolabs/go-procfs/procfs"
2020-10-20 15:49:45 +03:00
"golang.org/x/sys/unix"
2020-10-05 20:35:41 +03:00
2022-11-02 15:06:45 +04:00
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/board"
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader"
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub"
2023-03-14 14:33:11 +04:00
"github.com/siderolabs/talos/internal/pkg/meta"
2022-11-02 15:06:45 +04:00
"github.com/siderolabs/talos/internal/pkg/mount"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/kernel"
"github.com/siderolabs/talos/pkg/version"
2019-07-31 17:16:10 +00:00
)
2020-04-23 21:12:44 -07:00
// Options represents the set of options available for an install.
type Options struct {
2021-05-26 18:31:14 +03:00
ConfigSource string
Disk string
2023-05-18 16:04:27 -04:00
DiskSize int
2021-05-26 18:31:14 +03:00
Platform string
Arch string
Board string
ExtraKernelArgs [ ] string
Upgrade bool
Force bool
Zero bool
LegacyBIOSSupport bool
2023-03-27 14:16:06 +04:00
MetaValues MetaValues
2020-04-23 21:12:44 -07:00
}
// Install installs Talos.
func Install ( p runtime . Platform , seq runtime . Sequence , opts * Options ) ( err error ) {
cmdline := procfs . NewCmdline ( "" )
cmdline . Append ( constants . KernelParamPlatform , p . Name ( ) )
2020-11-25 18:00:02 +03:00
if opts . ConfigSource != "" {
cmdline . Append ( constants . KernelParamConfig , opts . ConfigSource )
}
2020-04-23 21:12:44 -07:00
2020-12-23 18:11:41 +03:00
cmdline . SetAll ( p . KernelArgs ( ) . Strings ( ) )
2020-04-23 21:12:44 -07:00
2020-12-10 17:10:06 +03:00
// first defaults, then extra kernel args to allow extra kernel args to override defaults
2020-12-24 15:19:41 +03:00
if err = cmdline . AppendAll ( kernel . DefaultArgs ) ; err != nil {
return err
}
2020-12-10 17:10:06 +03:00
2022-07-05 10:01:12 +03:00
if err = cmdline . AppendAll (
opts . ExtraKernelArgs ,
procfs . WithOverwriteArgs ( "console" ) ,
procfs . WithOverwriteArgs ( constants . KernelParamPlatform ) ,
) ; err != nil {
2020-04-23 21:12:44 -07:00
return err
}
i , err := NewInstaller ( cmdline , seq , opts )
if err != nil {
return err
}
if err = i . Install ( seq ) ; err != nil {
return err
}
log . Printf ( "installation of %s complete" , version . Tag )
return nil
}
2019-07-31 17:16:10 +00:00
// Installer represents the installer logic. It serves as the entrypoint to all
// installation methods.
type Installer struct {
2020-08-18 15:52:26 -07:00
cmdline * procfs . Cmdline
options * Options
manifest * Manifest
bootloader bootloader . Bootloader
2020-10-20 15:49:45 +03:00
bootPartitionFound bool
2022-02-09 21:11:42 +01:00
Current grub . BootLabel
Next grub . BootLabel
2019-07-31 17:16:10 +00:00
}
// NewInstaller initializes and returns an Installer.
2020-04-23 21:12:44 -07:00
func NewInstaller ( cmdline * procfs . Cmdline , seq runtime . Sequence , opts * Options ) ( i * Installer , err error ) {
2019-08-15 15:21:55 +00:00
i = & Installer {
2019-07-31 17:16:10 +00:00
cmdline : cmdline ,
2020-04-23 21:12:44 -07:00
options : opts ,
2019-07-31 17:16:10 +00:00
}
2020-10-20 15:49:45 +03:00
if err = i . probeBootPartition ( ) ; err != nil {
2020-03-26 11:04:55 -07:00
return nil , err
}
2020-03-10 07:52:10 -07:00
2022-02-09 21:11:42 +01:00
i . manifest , err = NewManifest ( string ( i . Next ) , seq , i . bootPartitionFound , i . options )
2020-03-26 11:04:55 -07:00
if err != nil {
return nil , fmt . Errorf ( "failed to create installation manifest: %w" , err )
2019-08-15 15:21:55 +00:00
}
2019-07-31 17:16:10 +00:00
2019-08-15 15:21:55 +00:00
return i , nil
2019-07-31 17:16:10 +00:00
}
2020-10-20 15:49:45 +03:00
// Verify existence of boot partition.
func ( i * Installer ) probeBootPartition ( ) error {
// there's no reason to discover boot partition if the disk is about to be wiped
if ! i . options . Zero {
2021-01-21 20:49:00 +03:00
dev , err := blockdevice . Open ( i . options . Disk )
if err != nil {
2020-10-20 15:49:45 +03:00
i . bootPartitionFound = false
2021-01-21 20:49:00 +03:00
return err
}
2021-03-05 11:52:08 +03:00
defer dev . Close ( ) //nolint:errcheck
2021-01-21 20:49:00 +03:00
if part , err := dev . GetPartition ( constants . BootPartitionLabel ) ; err != nil {
i . bootPartitionFound = false
} else {
2020-10-20 15:49:45 +03:00
i . bootPartitionFound = true
// mount the boot partition temporarily to find the bootloader labels
mountpoints := mount . NewMountPoints ( )
2021-01-21 20:49:00 +03:00
partPath , err := part . Path ( )
if err != nil {
return err
}
fsType , err := part . Filesystem ( )
if err != nil {
return err
}
mountpoint := mount . NewMountPoint ( partPath , constants . BootMountPoint , fsType , unix . MS_NOATIME | unix . MS_RDONLY , "" )
2020-10-20 15:49:45 +03:00
mountpoints . Set ( constants . BootPartitionLabel , mountpoint )
if err := mount . Mount ( mountpoints ) ; err != nil {
2021-01-21 20:49:00 +03:00
log . Printf ( "warning: failed to mount boot partition %q: %s" , partPath , err )
2020-10-20 15:49:45 +03:00
} else {
2021-03-05 11:52:08 +03:00
defer mount . Unmount ( mountpoints ) //nolint:errcheck
2020-10-20 15:49:45 +03:00
}
}
}
2022-02-09 21:11:42 +01:00
grubConf , err := grub . Read ( grub . ConfigPath )
if err != nil {
return err
}
2020-10-20 15:49:45 +03:00
2022-02-09 21:11:42 +01:00
next := grub . BootA
if grubConf != nil {
i . Current = grubConf . Default
next , err = grub . FlipBootLabel ( grubConf . Default )
if err != nil {
return err
}
i . bootloader = grubConf
}
i . Next = next
2020-10-20 15:49:45 +03:00
return err
}
2019-07-31 17:16:10 +00:00
// Install fetches the necessary data locations and copies or extracts
// to the target locations.
2020-03-10 07:52:10 -07:00
//
2021-03-12 02:17:28 +03:00
//nolint:gocyclo,cyclop
2020-04-23 21:12:44 -07:00
func ( i * Installer ) Install ( seq runtime . Sequence ) ( err error ) {
2022-11-24 21:45:08 +04:00
errataBTF ( )
2023-05-29 23:00:13 +04:00
if seq == runtime . SequenceUpgrade {
if err = i . errataNetIfnames ( ) ; err != nil {
return err
}
}
2022-11-24 22:48:22 +04:00
if err = i . runPreflightChecks ( seq ) ; err != nil {
return err
}
2022-01-20 20:53:28 +03:00
if err = i . installExtensions ( ) ; err != nil {
return err
}
2020-11-25 18:00:02 +03:00
if i . options . Board != constants . BoardNone {
var b runtime . Board
b , err = board . NewBoard ( i . options . Board )
if err != nil {
return err
}
i . cmdline . Append ( constants . KernelParamBoard , b . Name ( ) )
2020-12-01 06:21:38 -08:00
2020-12-23 18:11:41 +03:00
i . cmdline . SetAll ( b . KernelArgs ( ) . Strings ( ) )
2020-11-25 18:00:02 +03:00
}
2020-10-16 17:00:40 +03:00
if err = i . manifest . Execute ( ) ; err != nil {
return err
2019-07-31 17:16:10 +00:00
}
2020-08-18 15:52:26 -07:00
// Mount the partitions.
2021-02-16 22:50:18 +03:00
mountpoints := mount . NewMountPoints ( )
for _ , label := range [ ] string { constants . BootPartitionLabel , constants . EFIPartitionLabel } {
err = func ( ) error {
var device string
// searching targets for the device to be used
OuterLoop :
for dev , targets := range i . manifest . Targets {
for _ , target := range targets {
if target . Label == label {
device = dev
break OuterLoop
}
}
}
2020-03-10 07:52:10 -07:00
2021-02-16 22:50:18 +03:00
if device == "" {
return fmt . Errorf ( "failed to detect %s target device" , label )
}
var bd * blockdevice . BlockDevice
bd , err = blockdevice . Open ( device )
if err != nil {
return err
}
defer bd . Close ( ) //nolint:errcheck
var mountpoint * mount . Point
mountpoint , err = mount . SystemMountPointForLabel ( bd , label )
if err != nil {
return err
}
mountpoints . Set ( label , mountpoint )
return nil
} ( )
if err != nil {
return err
}
2020-03-10 07:52:10 -07:00
}
2019-08-08 00:16:43 -05:00
2020-08-18 15:52:26 -07:00
if err = mount . Mount ( mountpoints ) ; err != nil {
return err
}
defer func ( ) {
e := mount . Unmount ( mountpoints )
if e != nil {
log . Printf ( "failed to unmount: %v" , e )
}
} ( )
2019-07-31 17:16:10 +00:00
// Install the assets.
for _ , targets := range i . manifest . Targets {
for _ , target := range targets {
// Handle the download and extraction of assets.
if err = target . Save ( ) ; err != nil {
return err
}
}
}
// Install the bootloader.
2022-02-09 21:11:42 +01:00
var conf * grub . Config
if i . bootloader == nil {
conf = grub . NewConfig ( i . cmdline . String ( ) )
} else {
existingConf , ok := i . bootloader . ( * grub . Config )
if ! ok {
return fmt . Errorf ( "unsupported bootloader type: %T" , i . bootloader )
}
if err = existingConf . Put ( i . Next , i . cmdline . String ( ) ) ; err != nil {
return err
}
existingConf . Default = i . Next
existingConf . Fallback = i . Current
2020-08-18 15:52:26 -07:00
2022-02-09 21:11:42 +01:00
conf = existingConf
2020-08-18 15:52:26 -07:00
}
2022-02-09 21:11:42 +01:00
i . bootloader = conf
err = i . bootloader . Install ( i . options . Disk , i . options . Arch )
if err != nil {
2020-08-18 15:52:26 -07:00
return err
2020-03-10 07:52:10 -07:00
}
2019-11-05 23:13:50 +00:00
2020-11-25 18:00:02 +03:00
if i . options . Board != constants . BoardNone {
var b runtime . Board
b , err = board . NewBoard ( i . options . Board )
if err != nil {
return err
}
2020-11-28 16:52:25 -08:00
log . Printf ( "installing U-Boot for %q" , b . Name ( ) )
2020-11-25 18:00:02 +03:00
2020-11-28 16:52:25 -08:00
if err = b . Install ( i . options . Disk ) ; err != nil {
2020-11-25 18:00:02 +03:00
return err
}
}
2023-03-27 14:16:06 +04:00
if seq == runtime . SequenceUpgrade || len ( i . options . MetaValues . values ) > 0 {
2023-03-14 14:33:11 +04:00
var metaState * meta . Meta
2020-10-16 17:00:40 +03:00
2023-03-14 14:33:11 +04:00
if metaState , err = meta . New ( context . Background ( ) , nil ) ; err != nil {
2020-10-16 17:00:40 +03:00
return err
}
2023-03-14 14:33:11 +04:00
var ok bool
2020-10-16 17:00:40 +03:00
2023-03-27 14:16:06 +04:00
if seq == runtime . SequenceUpgrade {
if ok , err = metaState . SetTag ( context . Background ( ) , meta . Upgrade , string ( i . Current ) ) ; ! ok || err != nil {
return fmt . Errorf ( "failed to set upgrade tag: %q" , i . Current )
}
}
for _ , v := range i . options . MetaValues . values {
if ok , err = metaState . SetTag ( context . Background ( ) , v . Key , v . Value ) ; ! ok || err != nil {
return fmt . Errorf ( "failed to set meta tag: %q -> %q" , v . Key , v . Value )
}
2020-10-16 17:00:40 +03:00
}
2023-03-14 14:33:11 +04:00
if err = metaState . Flush ( ) ; err != nil {
2020-10-16 17:00:40 +03:00
return err
}
}
2020-03-10 07:52:10 -07:00
return nil
2019-07-31 17:16:10 +00:00
}
2022-11-24 22:48:22 +04:00
func ( i * Installer ) runPreflightChecks ( seq runtime . Sequence ) error {
if seq != runtime . SequenceUpgrade {
// pre-flight checks only apply to upgrades
return nil
}
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
checks , err := NewPreflightChecks ( ctx )
if err != nil {
return fmt . Errorf ( "error initializing pre-flight checks: %w" , err )
}
defer checks . Close ( ) //nolint:errcheck
return checks . Run ( ctx )
}