@@ -20,28 +20,32 @@ package disks
20
20
21
21
import (
22
22
"bufio"
23
- "errors"
24
23
"fmt"
25
24
"os"
26
25
"os/exec"
27
26
"path/filepath"
28
- "regexp"
29
27
"sort"
30
28
"strconv"
31
29
"strings"
32
30
33
- sharedErrors "github.com/coreos/ignition/v2/config/shared/errors"
34
31
cutil "github.com/coreos/ignition/v2/config/util"
35
32
"github.com/coreos/ignition/v2/config/v3_5_experimental/types"
36
33
"github.com/coreos/ignition/v2/internal/distro"
37
34
"github.com/coreos/ignition/v2/internal/exec/util"
38
- "github.com/coreos/ignition/v2/internal/sgdisk"
35
+ "github.com/coreos/ignition/v2/internal/log"
36
+ "github.com/coreos/ignition/v2/internal/partitioners"
37
+ "github.com/coreos/ignition/v2/internal/partitioners/sfdisk"
38
+ "github.com/coreos/ignition/v2/internal/partitioners/sgdisk"
39
39
iutil "github.com/coreos/ignition/v2/internal/util"
40
40
)
41
41
42
- var (
43
- ErrBadSgdiskOutput = errors .New ("sgdisk had unexpected output" )
44
- )
42
+ func getDeviceManager (logger * log.Logger , dev string ) partitioners.DeviceManager {
43
+ // To be replaced with build tag support or something similar.
44
+ if false {
45
+ return sgdisk .Begin (logger , dev )
46
+ }
47
+ return sfdisk .Begin (logger , dev )
48
+ }
45
49
46
50
// createPartitions creates the partitions described in config.Storage.Disks.
47
51
func (s stage ) createPartitions (config types.Config ) error {
@@ -76,7 +80,7 @@ func (s stage) createPartitions(config types.Config) error {
76
80
77
81
// partitionMatches determines if the existing partition matches the spec given. See doc/operator notes for what
78
82
// what it means for an existing partition to match the spec. spec must have non-zero Start and Size.
79
- func partitionMatches (existing util.PartitionInfo , spec sgdisk .Partition ) error {
83
+ func partitionMatches (existing util.PartitionInfo , spec partitioners .Partition ) error {
80
84
if err := partitionMatchesCommon (existing , spec ); err != nil {
81
85
return err
82
86
}
@@ -88,13 +92,13 @@ func partitionMatches(existing util.PartitionInfo, spec sgdisk.Partition) error
88
92
89
93
// partitionMatchesResize returns if the existing partition should be resized by evaluating if
90
94
// `resize`field is true and partition matches in all respects except size.
91
- func partitionMatchesResize (existing util.PartitionInfo , spec sgdisk .Partition ) bool {
95
+ func partitionMatchesResize (existing util.PartitionInfo , spec partitioners .Partition ) bool {
92
96
return cutil .IsTrue (spec .Resize ) && partitionMatchesCommon (existing , spec ) == nil
93
97
}
94
98
95
99
// partitionMatchesCommon handles the common tests (excluding the partition size) to determine
96
100
// if the existing partition matches the spec given.
97
- func partitionMatchesCommon (existing util.PartitionInfo , spec sgdisk .Partition ) error {
101
+ func partitionMatchesCommon (existing util.PartitionInfo , spec partitioners .Partition ) error {
98
102
if spec .Number != existing .Number {
99
103
return fmt .Errorf ("partition numbers did not match (specified %d, got %d). This should not happen, please file a bug." , spec .Number , existing .Number )
100
104
}
@@ -114,7 +118,7 @@ func partitionMatchesCommon(existing util.PartitionInfo, spec sgdisk.Partition)
114
118
}
115
119
116
120
// partitionShouldBeInspected returns if the partition has zeroes that need to be resolved to sectors.
117
- func partitionShouldBeInspected (part sgdisk .Partition ) bool {
121
+ func partitionShouldBeInspected (part partitioners .Partition ) bool {
118
122
if part .Number == 0 {
119
123
return false
120
124
}
@@ -134,17 +138,17 @@ func convertMiBToSectors(mib *int, sectorSize int) *int64 {
134
138
// getRealStartAndSize returns a map of partition numbers to a struct that contains what their real start
135
139
// and end sector should be. It runs sgdisk --pretend to determine what the partitions would look like if
136
140
// everything specified were to be (re)created.
137
- func (s stage ) getRealStartAndSize (dev types.Disk , devAlias string , diskInfo util.DiskInfo ) ([]sgdisk .Partition , error ) {
138
- partitions := []sgdisk .Partition {}
141
+ func (s stage ) getRealStartAndSize (dev types.Disk , devAlias string , diskInfo util.DiskInfo ) ([]partitioners .Partition , error ) {
142
+ partitions := []partitioners .Partition {}
139
143
for _ , cpart := range dev .Partitions {
140
- partitions = append (partitions , sgdisk .Partition {
144
+ partitions = append (partitions , partitioners .Partition {
141
145
Partition : cpart ,
142
146
StartSector : convertMiBToSectors (cpart .StartMiB , diskInfo .LogicalSectorSize ),
143
147
SizeInSectors : convertMiBToSectors (cpart .SizeMiB , diskInfo .LogicalSectorSize ),
144
148
})
145
149
}
146
150
147
- op := sgdisk . Begin (s .Logger , devAlias )
151
+ op := getDeviceManager (s .Logger , devAlias )
148
152
for _ , part := range partitions {
149
153
if info , exists := diskInfo .GetPartition (part .Number ); exists {
150
154
// delete all existing partitions
@@ -177,175 +181,29 @@ func (s stage) getRealStartAndSize(dev types.Disk, devAlias string, diskInfo uti
177
181
if err != nil {
178
182
return nil , err
179
183
}
180
-
181
- realDimensions , err := parseSfdiskPretend (output , partitionsToInspect )
184
+ realDimensions , err := op .ParseOutput (output , partitionsToInspect )
182
185
if err != nil {
183
186
return nil , err
184
187
}
185
188
186
- result := []sgdisk .Partition {}
189
+ result := []partitioners .Partition {}
187
190
for _ , part := range partitions {
188
191
if dims , ok := realDimensions [part .Number ]; ok {
189
192
if part .StartSector != nil {
190
- part .StartSector = & dims .start
193
+ part .StartSector = & dims .Start
191
194
}
192
195
if part .SizeInSectors != nil {
193
- part .SizeInSectors = & dims .size
196
+ part .SizeInSectors = & dims .Size
194
197
}
195
198
}
196
199
result = append (result , part )
197
200
}
198
201
return result , nil
199
202
}
200
203
201
- type sgdiskOutput struct {
202
- start int64
203
- size int64
204
- }
205
-
206
- // parseLine takes a regexp that captures an int64 and a string to match on. On success it returns
207
- // the captured int64 and nil. If the regexp does not match it returns -1 and nil. If it encountered
208
- // an error it returns 0 and the error.
209
- func parseLine (r * regexp.Regexp , line string ) (int64 , error ) {
210
- matches := r .FindStringSubmatch (line )
211
- switch len (matches ) {
212
- case 0 :
213
- return - 1 , nil
214
- case 2 :
215
- return strconv .ParseInt (matches [1 ], 10 , 64 )
216
- default :
217
- return 0 , ErrBadSgdiskOutput
218
- }
219
- }
220
-
221
- // parseSgdiskPretend parses the output of running sgdisk pretend with --info specified for each partition
222
- // number specified in partitionNumbers. E.g. if paritionNumbers is [1,4,5], it is expected that the sgdisk
223
- // output was from running `sgdisk --pretend <commands> --info=1 --info=4 --info=5`. It assumes the the
224
- // partition labels are well behaved (i.e. contain no control characters). It returns a list of partitions
225
- // matching the partition numbers specified, but with the start and size information as determined by sgdisk.
226
- // The partition numbers need to passed in because sgdisk includes them in its output.
227
- func parseSgdiskPretend (sgdiskOut string , partitionNumbers []int ) (map [int ]sgdiskOutput , error ) {
228
- if len (partitionNumbers ) == 0 {
229
- return nil , nil
230
- }
231
- startRegex := regexp .MustCompile (`^First sector: (\d*) \(.*\)$` )
232
- endRegex := regexp .MustCompile (`^Last sector: (\d*) \(.*\)$` )
233
- const (
234
- START = iota
235
- END = iota
236
- FAIL_ON_START_END = iota
237
- )
238
-
239
- output := map [int ]sgdiskOutput {}
240
- state := START
241
- current := sgdiskOutput {}
242
- i := 0
243
-
244
- lines := strings .Split (sgdiskOut , "\n " )
245
- for _ , line := range lines {
246
- switch state {
247
- case START :
248
- start , err := parseLine (startRegex , line )
249
- if err != nil {
250
- return nil , err
251
- }
252
- if start != - 1 {
253
- current .start = start
254
- state = END
255
- }
256
- case END :
257
- end , err := parseLine (endRegex , line )
258
- if err != nil {
259
- return nil , err
260
- }
261
- if end != - 1 {
262
- current .size = 1 + end - current .start
263
- output [partitionNumbers [i ]] = current
264
- i ++
265
- if i == len (partitionNumbers ) {
266
- state = FAIL_ON_START_END
267
- } else {
268
- current = sgdiskOutput {}
269
- state = START
270
- }
271
- }
272
- case FAIL_ON_START_END :
273
- if len (startRegex .FindStringSubmatch (line )) != 0 ||
274
- len (endRegex .FindStringSubmatch (line )) != 0 {
275
- return nil , ErrBadSgdiskOutput
276
- }
277
- }
278
- }
279
-
280
- if state != FAIL_ON_START_END {
281
- // We stopped parsing in the middle of a info block. Something is wrong
282
- return nil , ErrBadSgdiskOutput
283
- }
284
-
285
- return output , nil
286
- }
287
-
288
- type sfdiskOutput struct {
289
- start int64
290
- size int64
291
- }
292
-
293
- // ParsePretend takes the output from sfdisk running with the argument --no-act. Similar to sgdisk
294
- // it then uses regex to parse the output into understood values like 'start' 'size' and attempts
295
- // to catch any failures and wrap them to return to the caller.
296
- func parseSfdiskPretend (sfdiskOut string , partitionNumbers []int ) (map [int ]sfdiskOutput , error ) {
297
- if len (partitionNumbers ) == 0 {
298
- return nil , nil
299
- }
300
-
301
- // Prepare the data, and a regex for matching on partitions
302
- partitionRegex := regexp .MustCompile (`^/dev/\S+\s+\S*\s+(\d+)\s+(\d+)\s+\d+\s+\S+\s+\S+\s+\S+.*$` )
303
- output := map [int ]sfdiskOutput {}
304
- current := sfdiskOutput {}
305
- i := 0
306
- lines := strings .Split (sfdiskOut , "\n " )
307
- for _ , line := range lines {
308
- matches := partitionRegex .FindStringSubmatch (line )
309
-
310
- // Sanity check number of partition entries
311
- if i > len (partitionNumbers ) {
312
- return nil , sharedErrors .ErrBadSfdiskPretend
313
- }
314
-
315
- // Verify that we are not reading a 'failed' or 'error'
316
- errorRegex := regexp .MustCompile (`(?i)(failed|error)` )
317
- if errorRegex .MatchString (line ) {
318
- return nil , fmt .Errorf ("%w: sfdisk returned :%v" , sharedErrors .ErrBadSfdiskPretend , line )
319
- }
320
-
321
- // When we get a match it should be
322
- // Whole line at [0]
323
- // Start at [1]
324
- // Size at [2]
325
- if len (matches ) > 1 {
326
- start , err := strconv .Atoi (matches [1 ])
327
- if err != nil {
328
- return nil , err
329
- }
330
- end , err := strconv .Atoi (matches [2 ])
331
- if err != nil {
332
- return nil , err
333
- }
334
-
335
- current .start = int64 (start )
336
- // Add one due to overlap
337
- current .size = int64 (end - start + 1 )
338
- output [partitionNumbers [i ]] = current
339
- i ++
340
- }
341
- }
342
-
343
- return output , nil
344
- }
345
-
346
204
// partitionShouldExist returns whether a bool is indicating if a partition should exist or not.
347
205
// nil (unspecified in json) is treated the same as true.
348
- func partitionShouldExist (part sgdisk .Partition ) bool {
206
+ func partitionShouldExist (part partitioners .Partition ) bool {
349
207
return ! cutil .IsFalse (part .ShouldExist )
350
208
}
351
209
@@ -497,7 +355,7 @@ func (s stage) partitionDisk(dev types.Disk, devAlias string) error {
497
355
return fmt .Errorf ("refusing to operate on directly active disk %q" , devAlias )
498
356
}
499
357
if cutil .IsTrue (dev .WipeTable ) {
500
- op := sgdisk . Begin (s .Logger , devAlias )
358
+ op := getDeviceManager (s .Logger , devAlias )
501
359
s .Logger .Info ("wiping partition table requested on %q" , devAlias )
502
360
if len (activeParts ) > 0 {
503
361
return fmt .Errorf ("refusing to wipe active disk %q" , devAlias )
@@ -516,7 +374,7 @@ func (s stage) partitionDisk(dev types.Disk, devAlias string) error {
516
374
// Ensure all partitions with number 0 are last
517
375
sort .Stable (PartitionList (dev .Partitions ))
518
376
519
- op := sgdisk . Begin (s .Logger , devAlias )
377
+ op := getDeviceManager (s .Logger , devAlias )
520
378
521
379
diskInfo , err := s .getPartitionMap (devAlias )
522
380
if err != nil {
0 commit comments