-
Notifications
You must be signed in to change notification settings - Fork 42
/
Copy pathmassinstall.go
244 lines (206 loc) · 6.91 KB
/
massinstall.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// 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
}