mirror of
https://github.com/clearlinux/clr-installer.git
synced 2026-09-05 13:11:28 +00:00
245 lines
6.9 KiB
Go
245 lines
6.9 KiB
Go
// Copyright © 2020 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
package massinstall
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/clearlinux/clr-installer/args"
|
|
"github.com/clearlinux/clr-installer/controller"
|
|
"github.com/clearlinux/clr-installer/errors"
|
|
"github.com/clearlinux/clr-installer/log"
|
|
"github.com/clearlinux/clr-installer/model"
|
|
"github.com/clearlinux/clr-installer/progress"
|
|
"github.com/clearlinux/clr-installer/storage"
|
|
"github.com/clearlinux/clr-installer/utils"
|
|
)
|
|
|
|
const (
|
|
// rebootDelay is the number of second before automatic reboot
|
|
rebootDelay = 5
|
|
)
|
|
|
|
// MassInstall is the frontend implementation for the "mass installer" it also
|
|
// implements the progress interface: progress.Client
|
|
type MassInstall struct {
|
|
prgDesc string
|
|
prgIndex int
|
|
step int
|
|
}
|
|
|
|
// New creates a new instance of MassInstall frontend implementation
|
|
func New() *MassInstall {
|
|
return &MassInstall{}
|
|
}
|
|
|
|
func printPipedStatus(mi *MassInstall) bool {
|
|
isStdoutTTY := utils.IsStdoutTTY()
|
|
mi.step++
|
|
|
|
if !isStdoutTTY && mi.step == 1 {
|
|
fmt.Println(mi.prgDesc)
|
|
return true
|
|
} else if !isStdoutTTY {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Step is the progress step implementation for progress.Client interface
|
|
func (mi *MassInstall) Step() {
|
|
if printPipedStatus(mi) {
|
|
return
|
|
}
|
|
|
|
elms := []string{"|", "-", "\\", "|", "/", "-", "\\"}
|
|
|
|
fmt.Printf("%s [%s]\r", mi.prgDesc, elms[mi.prgIndex])
|
|
|
|
if mi.prgIndex+1 == len(elms) {
|
|
mi.prgIndex = 0
|
|
} else {
|
|
mi.prgIndex = mi.prgIndex + 1
|
|
}
|
|
}
|
|
|
|
// LoopWaitDuration is part of the progress.Client implementation and returns the
|
|
// duration each loop progress step should wait
|
|
func (mi *MassInstall) LoopWaitDuration() time.Duration {
|
|
return 50 * time.Millisecond
|
|
}
|
|
|
|
// Desc is part of the implementation for ProgresIface and is used to adjust the progress bar
|
|
// label content
|
|
func (mi *MassInstall) Desc(desc string) {
|
|
mi.prgDesc = desc
|
|
}
|
|
|
|
// Partial is part of the progress.Client implementation and sets the progress bar based
|
|
// on actual progression
|
|
func (mi *MassInstall) Partial(total int, step int) {
|
|
if printPipedStatus(mi) {
|
|
return
|
|
}
|
|
|
|
line := fmt.Sprintf("%s %.0f%%\r", mi.prgDesc, (float64(step)/float64(total))*100)
|
|
fmt.Printf("%s", line)
|
|
}
|
|
|
|
// Success is part of the progress.Client implementation and represents the
|
|
// successful progress completion of a task
|
|
func (mi *MassInstall) Success() {
|
|
if !utils.IsStdoutTTY() {
|
|
mi.step = 0
|
|
return
|
|
}
|
|
|
|
mi.prgIndex = 0
|
|
fmt.Printf("%s [success]\n", mi.prgDesc)
|
|
}
|
|
|
|
// Failure is part of the progress.Client implementation and represents the
|
|
// unsuccessful progress completion of a task
|
|
func (mi *MassInstall) Failure() {
|
|
if !utils.IsStdoutTTY() {
|
|
mi.step = 0
|
|
return
|
|
}
|
|
|
|
mi.prgIndex = 0
|
|
fmt.Printf("%s [*failed*]\n", mi.prgDesc)
|
|
}
|
|
|
|
// MustRun is part of the Frontend implementation and tells the core implementation that this
|
|
// frontend wants or should be executed
|
|
func (mi *MassInstall) MustRun(args *args.Args) bool {
|
|
return args.ConfigFile != "" && (!args.ForceTUI && !args.ForceGUI)
|
|
}
|
|
|
|
// Run is part of the Frontend implementation and is the actual entry point for the
|
|
// "mass installer" frontend
|
|
func (mi *MassInstall) Run(md *model.SystemInstall, rootDir string, options args.Args) (bool, error) {
|
|
var instError error
|
|
var devs []*storage.BlockDevice
|
|
var results []string
|
|
|
|
// If there are no media defined, then we should look for
|
|
// Advanced Configuration labels
|
|
if len(md.TargetMedias) > 0 {
|
|
// If the partitions are defined from the configuration file,
|
|
// assume the user knows what they are doing and ignore validation checks
|
|
if !options.SkipValidationSizeSet && !options.SkipValidationAllSet {
|
|
md.MediaOpts.SkipValidationSize = true
|
|
md.MediaOpts.SkipValidationAll = true
|
|
} else {
|
|
if !options.SkipValidationSizeSet {
|
|
md.MediaOpts.SkipValidationSize = true
|
|
} else {
|
|
if !options.SkipValidationAllSet {
|
|
md.MediaOpts.SkipValidationAll = false
|
|
}
|
|
}
|
|
}
|
|
|
|
// Need to ensure the partitioner knows we are running from
|
|
// the command line and will be using the whole disk
|
|
for _, curr := range md.TargetMedias {
|
|
md.InstallSelected[curr.Name] = storage.InstallTarget{Name: curr.Name, WholeDisk: true}
|
|
log.Debug("Mass installer using defined media in YAML")
|
|
}
|
|
|
|
if md.IsTargetDesktopInstall() {
|
|
results = storage.DesktopValidatePartitions(md.TargetMedias, md.MediaOpts)
|
|
} else {
|
|
results = storage.ServerValidatePartitions(md.TargetMedias, md.MediaOpts)
|
|
}
|
|
|
|
if len(results) > 0 {
|
|
for _, errStr := range results {
|
|
log.Error("Disk Partition: Validation Error: %q", errStr)
|
|
fmt.Printf("Disk Partition: Validation Error: %q\n", errStr)
|
|
}
|
|
|
|
return false, errors.Errorf("Disk partitions failed validation")
|
|
}
|
|
} else {
|
|
// Check for Advance Partitioning labels
|
|
log.Debug("Mass installer found no media in YAML; checking for Advanced Disk Partition Labels.")
|
|
isAdvancedSelected := false
|
|
var err error
|
|
devs, err = storage.ListAvailableBlockDevices(md.TargetMedias)
|
|
log.Debug("massinstall: results of ListAvailableBlockDevices: %+v", devs)
|
|
|
|
if err != nil {
|
|
log.Error("Error detecting advanced partitions: %q", err)
|
|
fmt.Printf("Error detecting advanced partitions: %q\n", err)
|
|
return false, err
|
|
}
|
|
|
|
devs = storage.FindAdvancedInstallTargets(devs)
|
|
for _, curr := range devs {
|
|
md.AddTargetMedia(curr)
|
|
log.Debug("massinstall: AddTargetMedia %+v", curr)
|
|
md.InstallSelected[curr.Name] = storage.InstallTarget{Name: curr.Name, Friendly: curr.Model,
|
|
Removable: curr.RemovableDevice}
|
|
isAdvancedSelected = true
|
|
}
|
|
|
|
if isAdvancedSelected {
|
|
log.Debug("Mass installer operating in Advanced Disk Partition Mode.")
|
|
var results []string
|
|
if md.IsTargetDesktopInstall() {
|
|
results = storage.DesktopValidateAdvancedPartitions(devs, md.MediaOpts)
|
|
} else {
|
|
results = storage.ServerValidateAdvancedPartitions(devs, md.MediaOpts)
|
|
}
|
|
if len(results) > 0 {
|
|
for _, errStr := range results {
|
|
log.Error("Advanced Disk Partition: Validation Error: %q", errStr)
|
|
fmt.Printf("Advanced Disk Partition: Validation Error: %q\n", errStr)
|
|
}
|
|
|
|
return false, errors.Errorf("Disk partitions failed validation")
|
|
}
|
|
} else {
|
|
log.Error("Failed to detected advanced partition labels!")
|
|
fmt.Println("Failed to detected advanced partition labels!")
|
|
return false, errors.Errorf("failed to detected advanced partition labels")
|
|
}
|
|
}
|
|
|
|
progress.Set(mi)
|
|
|
|
log.Debug("Starting install")
|
|
|
|
if !md.AutoUpdate.Value() {
|
|
fmt.Println("Swupd auto-update set to off!")
|
|
}
|
|
|
|
instError = controller.Install(rootDir, md, options)
|
|
if instError != nil {
|
|
if !errors.IsValidationError(instError) {
|
|
fmt.Printf("ERROR: Installation has failed!\n")
|
|
}
|
|
return false, instError
|
|
}
|
|
|
|
if instError != nil {
|
|
return false, instError
|
|
} else if md.PostReboot {
|
|
fmt.Printf("\nSystem will restart -- Control-C to abort!\n\n")
|
|
fmt.Printf("Rebooting in ...")
|
|
for i := rebootDelay; i > 0; i-- {
|
|
fmt.Printf("%d...", i)
|
|
time.Sleep(time.Second * 1)
|
|
}
|
|
fmt.Printf("0\n\n")
|
|
}
|
|
|
|
return md.PostReboot, nil
|
|
}
|