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 (
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
"path/filepath"
2020-10-20 15:49:45 +03:00
"github.com/talos-systems/go-blockdevice/blockdevice/probe"
2020-02-18 20:07:01 -08:00
"github.com/talos-systems/go-procfs/procfs"
2020-10-20 15:49:45 +03:00
"golang.org/x/sys/unix"
2020-10-05 20:35:41 +03:00
2020-04-23 21:12:44 -07:00
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
2020-11-25 18:00:02 +03:00
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/board"
2020-08-18 15:52:26 -07:00
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader"
2020-12-03 23:56:00 +03:00
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/adv"
2020-08-18 15:52:26 -07:00
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub"
2019-07-31 17:16:10 +00:00
"github.com/talos-systems/talos/internal/pkg/mount"
2020-08-17 19:12:47 +03:00
"github.com/talos-systems/talos/pkg/machinery/constants"
2020-04-23 21:12:44 -07:00
"github.com/talos-systems/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 {
ConfigSource string
Disk string
Platform string
2020-11-25 18:00:02 +03:00
Board string
2020-04-23 21:12:44 -07:00
ExtraKernelArgs [ ] string
Bootloader bool
Upgrade bool
Force bool
Zero bool
}
// 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
if err = cmdline . AppendAll ( p . KernelArgs ( ) . Strings ( ) ) ; err != nil {
return err
}
if err = cmdline . AppendAll ( opts . ExtraKernelArgs ) ; err != nil {
return err
}
cmdline . AppendDefaults ( )
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
2020-08-18 15:52:26 -07:00
Current string
Next string
2019-07-31 17:16:10 +00:00
}
// NewInstaller initializes and returns an Installer.
2020-03-10 07:52:10 -07:00
//
// nolint: gocyclo
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 ,
2020-08-18 15:52:26 -07:00
bootloader : & grub . Grub {
BootDisk : opts . Disk ,
} ,
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
2020-10-20 15:49:45 +03:00
i . manifest , err = NewManifest ( 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 {
if dev , err := probe . DevForFileSystemLabel ( i . options . Disk , constants . BootPartitionLabel ) ; err != nil {
i . bootPartitionFound = false
} else {
//nolint: errcheck
defer dev . Close ( )
i . bootPartitionFound = true
// mount the boot partition temporarily to find the bootloader labels
mountpoints := mount . NewMountPoints ( )
mountpoint := mount . NewMountPoint ( dev . Path , constants . BootMountPoint , dev . SuperBlock . Type ( ) , unix . MS_NOATIME | unix . MS_RDONLY , "" )
mountpoints . Set ( constants . BootPartitionLabel , mountpoint )
if err := mount . Mount ( mountpoints ) ; err != nil {
log . Printf ( "warning: failed to mount boot partition %q: %s" , dev . Path , err )
} else {
defer mount . Unmount ( mountpoints ) //nolint: errcheck
}
}
2020-10-23 23:59:50 +03:00
// try legacy boot partition
//
// TODO: remove this in Talos 0.8 (only required for upgrading from 0.6)
if ! i . bootPartitionFound {
if dev , err := probe . DevForFileSystemLabel ( i . options . Disk , constants . LegacyBootPartitionLabel ) ; err == nil {
//nolint: errcheck
defer dev . Close ( )
i . bootPartitionFound = true
}
}
2020-10-20 15:49:45 +03:00
}
var err error
// anyways run the Labels() to get the defaults initialized
i . Current , i . Next , err = i . bootloader . Labels ( )
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
//
2019-07-31 17:16:10 +00:00
// nolint: gocyclo
2020-04-23 21:12:44 -07:00
func ( i * Installer ) Install ( seq runtime . Sequence ) ( err error ) {
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
err = i . cmdline . AppendAll ( b . KernelArgs ( ) . Strings ( ) )
if err != nil {
return err
}
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.
2020-03-10 07:52:10 -07:00
2020-10-16 17:00:40 +03:00
mountpoints , err := i . manifest . SystemMountpoints ( )
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.
2020-04-23 21:12:44 -07:00
if ! i . options . Bootloader {
2019-07-31 17:16:10 +00:00
return nil
}
2020-08-18 15:52:26 -07:00
i . cmdline . Append ( "initrd" , filepath . Join ( "/" , i . Next , constants . InitramfsAsset ) )
2019-07-31 17:16:10 +00:00
2020-08-18 15:52:26 -07:00
grubcfg := & grub . Cfg {
Default : i . Next ,
Labels : [ ] * grub . Label {
{
Root : i . Next ,
Initrd : filepath . Join ( "/" , i . Next , constants . InitramfsAsset ) ,
Kernel : filepath . Join ( "/" , i . Next , constants . KernelAsset ) ,
Append : i . cmdline . String ( ) ,
2020-03-10 07:52:10 -07:00
} ,
2020-08-18 15:52:26 -07:00
} ,
}
2019-07-31 17:16:10 +00:00
2020-10-16 17:00:40 +03:00
if i . Current != "" {
2020-08-18 15:52:26 -07:00
grubcfg . Fallback = i . Current
grubcfg . Labels = append ( grubcfg . Labels , & grub . Label {
Root : i . Current ,
Initrd : filepath . Join ( "/" , i . Current , constants . InitramfsAsset ) ,
Kernel : filepath . Join ( "/" , i . Current , constants . KernelAsset ) ,
Append : procfs . ProcCmdline ( ) . String ( ) ,
} )
}
2020-10-16 17:00:40 +03:00
if err = i . bootloader . Install ( i . Current , grubcfg , seq ) ; 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
}
}
2020-10-16 17:00:40 +03:00
if seq == runtime . SequenceUpgrade {
var meta * bootloader . Meta
if meta , err = bootloader . NewMeta ( ) ; err != nil {
return err
}
//nolint: errcheck
defer meta . Close ( )
2020-12-03 23:56:00 +03:00
if ok := meta . LegacyADV . SetTag ( adv . Upgrade , i . Current ) ; ! ok {
2020-10-16 17:00:40 +03:00
return fmt . Errorf ( "failed to set upgrade tag: %q" , i . Current )
}
2020-12-03 23:56:00 +03:00
if err = meta . Write ( ) ; 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
}