Skip to content

Commit 053054a

Browse files
Fresnel Imaging Teamcopybara-github
Fresnel Imaging Team
authored andcommitted
Add support for downloading and placing ffu configuration files to the provisioned drive.
PiperOrigin-RevId: 402865488
1 parent 856c38c commit 053054a

File tree

4 files changed

+274
-100
lines changed

4 files changed

+274
-100
lines changed

cli/config/config.go

+31-19
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,17 @@ const (
5757
// required to obtain the resources required to install it.
5858
type distribution struct {
5959
os OperatingSystem
60-
name string // Friendly name: e.g. Corp Windows.
60+
confStore string // The relative path where configs are located.
61+
sfuDest string // The relative path where SFU files will be stored.
62+
imageServer string // The base image is obtained here.
6163
label string // If set, is used to set partition labels.
62-
seedServer string // If set, a seed is obtained from here.
63-
seedFile string // This file is hashed when obtainng a seed.
64+
name string // Friendly name: e.g. Corp Windows.
6465
seedDest string // The relative path where the seed should be written.
65-
ffuDest string // The relative path where SFU files will be stored.
66-
imageServer string // The base image is obtained here.
66+
seedFile string // This file is hashed when obtainng a seed.
67+
seedServer string // If set, a seed is obtained from here.
6768
images map[string]string
68-
ffus map[string]string // Contains SFU manifests names.
69+
sfus map[string]string // Contains SFU manifests names.
70+
configs map[string]string // Contains config file names.
6971
}
7072

7173
// Configuration represents the state of all flags and selections provided
@@ -88,11 +90,11 @@ type Configuration struct {
8890
func New(cleanup, warning, eject, ffu, update bool, devices []string, os, track, seedServer string) (*Configuration, error) {
8991
// Create a partial config using known good values.
9092
conf := &Configuration{
91-
cleanup: cleanup,
92-
warning: warning,
93-
ffu: ffu,
94-
eject: eject,
95-
update: update,
93+
cleanup: cleanup,
94+
warning: warning,
95+
ffu: ffu,
96+
eject: eject,
97+
update: update,
9698
}
9799
if len(devices) > 0 {
98100
if err := conf.addDeviceList(devices); err != nil {
@@ -257,22 +259,32 @@ func (c *Configuration) FFU() bool {
257259
return c.ffu
258260
}
259261

260-
// FFUDest returns the relative path where the SFU files should be written.
261-
func (c *Configuration) FFUDest() string {
262-
return c.distro.ffuDest
262+
// SFUDest returns the relative path where the SFU files should be written.
263+
func (c *Configuration) SFUDest() string {
264+
return c.distro.sfuDest
263265
}
264266

265-
// FFUManifest returns the filename of the SFU manifest file for this configuration.
266-
func (c *Configuration) FFUManifest() string {
267+
// SFUManifest returns the filename of the SFU manifest file for this configuration.
268+
func (c *Configuration) SFUManifest() string {
267269
// Return the filename only.
268-
return filepath.Base(c.distro.ffus[c.track])
270+
return filepath.Base(c.distro.sfus[c.track])
269271
}
270272

271-
// FFUPath returns the path to the SFU manifest.
272-
func (c *Configuration) FFUPath() string {
273+
// SFUPath returns the path to the SFU manifest.
274+
func (c *Configuration) SFUPath() string {
273275
return fmt.Sprintf(`%s/%s/%s`, c.distro.imageServer, c.distro.name, c.track)
274276
}
275277

278+
// FileName returns the name of the config file.
279+
func (c *Configuration) FileName() string {
280+
return c.distro.configs[c.track]
281+
}
282+
283+
// Path returns the path to the config.
284+
func (c *Configuration) Path() string {
285+
return fmt.Sprintf(`%s/%s/%s`, c.distro.imageServer, c.distro.confStore, c.distro.configs[c.track])
286+
}
287+
276288
// PowerOff returns whether or not devices should be powered off after write
277289
// operations.
278290
func (c *Configuration) PowerOff() bool {

cli/config/config_test.go

+40-10
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ func TestImageFile(t *testing.T) {
470470
}
471471
}
472472

473-
func TestFFUPath(t *testing.T) {
473+
func TestSFUPath(t *testing.T) {
474474
track := `default`
475475
distro := distribution{
476476
imageServer: `https://foo.bar.com`,
@@ -481,42 +481,72 @@ func TestFFUPath(t *testing.T) {
481481
}
482482
want := "https://foo.bar.com/nombre/default"
483483
c := Configuration{track: track, distro: &distro}
484-
if got := c.FFUPath(); got != want {
485-
t.Errorf("FFUPath() got: %q, want: %q", got, want)
484+
if got := c.SFUPath(); got != want {
485+
t.Errorf("SFUPath() got: %q, want: %q", got, want)
486486
}
487487
}
488488

489-
func TestFFUManifest(t *testing.T) {
489+
func TestSFUManifest(t *testing.T) {
490490
tests := []struct {
491491
desc string
492-
ffus map[string]string
492+
sfus map[string]string
493493
want string
494494
}{
495495
{
496496
desc: "json",
497-
ffus: map[string]string{"default": "manifest.json"},
497+
sfus: map[string]string{"default": "manifest.json"},
498498
want: "manifest.json",
499499
},
500500
{
501501
desc: "nested json",
502-
ffus: map[string]string{"default": "nested/manifest.json"},
502+
sfus: map[string]string{"default": "nested/manifest.json"},
503503
want: "manifest.json",
504504
},
505505
}
506506
for _, tt := range tests {
507507
c := Configuration{
508508
track: "default",
509509
distro: &distribution{
510-
ffus: tt.ffus,
510+
sfus: tt.sfus,
511511
},
512512
}
513-
got := c.FFUManifest()
513+
got := c.SFUManifest()
514514
if got != tt.want {
515-
t.Errorf("%s: FFUManifest() got: %q, want: %q", tt.desc, got, tt.want)
515+
t.Errorf("%s: SFUManifest() got: %q, want: %q", tt.desc, got, tt.want)
516516
}
517517
}
518518
}
519519

520+
func TestFileName(t *testing.T) {
521+
track := `default`
522+
distro := distribution{
523+
configs: map[string]string{
524+
track: "conf.yaml",
525+
},
526+
}
527+
want := "conf.yaml"
528+
c := Configuration{track: track, distro: &distro}
529+
if got := c.FileName(); got != want {
530+
t.Errorf("FileName() got: %q, want: %q", got, want)
531+
}
532+
}
533+
534+
func TestPath(t *testing.T) {
535+
track := `default`
536+
distro := distribution{
537+
imageServer: `https://foo.bar.com`,
538+
confStore: `configs/yaml`,
539+
configs: map[string]string{
540+
track: "conf.yaml",
541+
},
542+
}
543+
want := "https://foo.bar.com/configs/yaml/conf.yaml"
544+
c := Configuration{track: track, distro: &distro}
545+
if got := c.Path(); got != want {
546+
t.Errorf("Path() got: %q, want: %q", got, want)
547+
}
548+
}
549+
520550
func TestCleanup(t *testing.T) {
521551
want := true
522552
c := Configuration{cleanup: want}

cli/installer/installer.go

+75-46
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,16 @@ var (
5959
// Wrapped errors for testing.
6060
errCache = errors.New("missing cache")
6161
errConfig = errors.New("invalid config")
62+
errConfName = errors.New("missing configuration file name")
63+
errConfPath = errors.New("missing configuration file path")
6264
errConnect = errors.New("connect error")
6365
errDownload = errors.New("download error")
6466
errDevice = errors.New("device error")
6567
errElevation = errors.New("elevation is required for this operation")
6668
errEmpty = errors.New("iso is empty")
6769
errEmptyUser = errors.New("could not determine username")
70+
errSFUManifest = errors.New("missing sfu manifest")
71+
errSFUPath = errors.New("sfu path empty")
6872
errFile = errors.New("file error")
6973
errFinalize = errors.New("finalize error")
7074
errFormat = errors.New("format error")
@@ -87,6 +91,7 @@ var (
8791
errUnsupported = errors.New("unsupported")
8892
errUser = errors.New("user detection error")
8993
errWipe = errors.New("device wipe error")
94+
errYAML = errors.New("yaml retrieval error")
9095

9196
// ErrLabel is made public to that callers can warn on mismatches.
9297
ErrLabel = errors.New(`label error`)
@@ -112,14 +117,16 @@ type Configuration interface {
112117
ImageFile() string
113118
Elevated() bool
114119
FFU() bool
115-
FFUDest() string
116-
FFUManifest() string
117-
FFUPath() string
120+
SFUManifest() string
121+
SFUPath() string
118122
PowerOff() bool
119123
SeedDest() string
120124
SeedFile() string
121125
SeedServer() string
126+
SFUDest() string
122127
UpdateOnly() bool
128+
FileName() string
129+
Path() string
123130
}
124131

125132
// Device represents storage.Device.
@@ -269,20 +276,34 @@ func (i *Installer) Retrieve() (err error) {
269276
return i.retrieveFile(i.config.ImageFile(), i.config.Image())
270277
}
271278

272-
// Check FFU Path configuration
273-
if i.config.FFUPath() == "" {
274-
return fmt.Errorf("missing FFU path: %w", errConfig)
279+
// Check FFU Path configuration.
280+
if i.config.SFUPath() == "" {
281+
return errSFUPath
275282
}
276283

277-
// Check FFU Manifest configuration
278-
if i.config.FFUManifest() == "" {
279-
return fmt.Errorf("missing FFU manifest: %w", errConfig)
284+
// Check FFU Manifest configuration.
285+
if i.config.SFUManifest() == "" {
286+
return errSFUManifest
287+
}
288+
289+
// Check for missing conf file name.
290+
if i.config.FileName() == "" {
291+
return errConfName
292+
}
293+
294+
// Check conf path configuration.
295+
if i.config.Path() == "" {
296+
return errConfPath
297+
}
298+
299+
if err := i.retrieveFile(i.config.FileName(), i.config.Path()); err != nil {
300+
return fmt.Errorf("%w: %v", errYAML, err)
280301
}
281302

282303
if err := i.retrieveFile(i.config.ImageFile(), i.config.Image()); err != nil {
283304
return fmt.Errorf("%w: %v", errImage, err)
284305
}
285-
return i.retrieveFile(i.config.FFUManifest(), fmt.Sprintf("%s/%s", i.config.FFUPath(), i.config.FFUManifest()))
306+
return i.retrieveFile(i.config.SFUManifest(), fmt.Sprintf("%s/%s", i.config.SFUPath(), i.config.SFUManifest()))
286307
}
287308

288309
// download obtains the installer using the provided client and writes it
@@ -450,7 +471,7 @@ func (i *Installer) DownloadSFU() error {
450471
if i.cache == "" {
451472
return fmt.Errorf("missing cache location: %w", errCache)
452473
}
453-
sfus, err := getManifest(filepath.Join(i.cache, i.config.FFUManifest()))
474+
sfus, err := getManifest(filepath.Join(i.cache, i.config.SFUManifest()))
454475
if err != nil {
455476
return fmt.Errorf("readManifest() %w: %v", errManifest, err)
456477
}
@@ -463,63 +484,71 @@ func (i *Installer) DownloadSFU() error {
463484
path := filepath.Join(i.cache, sfu.Filename)
464485
f, err := os.Create(path)
465486
if err != nil {
466-
return fmt.Errorf("ioutil.TempFile(%q, %q) returned %w: %v", i.cache, i.config.FFUManifest(), errFile, err)
487+
return fmt.Errorf("ioutil.TempFile(%q, %q) returned %w: %v", i.cache, i.config.SFUManifest(), errFile, err)
467488
}
468489
defer f.Close()
469490

470-
if err := downloadFile(client, fmt.Sprintf(`%s/%s`, i.config.FFUPath(), sfu.Filename), f); err != nil {
491+
if err := downloadFile(client, fmt.Sprintf(`%s/%s`, i.config.SFUPath(), sfu.Filename), f); err != nil {
471492
return fmt.Errorf("DownloadSFU() returned %w: %v", errDownload, err)
472493
}
473494

474495
}
475496
return nil
476497
}
477498

478-
// PlaceSFU copies SFU files onto provisioned media from the local cache.
499+
// PlaceSFU copies SFU files and config files onto provisioned media
500+
// from the local cache.
479501
func (i *Installer) PlaceSFU(d Device) error {
480502
// Find a compatible partition to write the FFU to.
481503
logger.V(2).Infof("Searching for FFU %q for a %q partition larger than %v.", d.FriendlyName(), humanize.Bytes(minSFUPartSize), storage.FAT32)
482504
p, err := selectPart(d, minSFUPartSize, storage.FAT32)
483505
if err != nil {
484506
return fmt.Errorf("SelectPartition(%q, %q, %q) returned %w: %v", d.FriendlyName(), humanize.Bytes(minSFUPartSize), storage.FAT32, errPartition, err)
485507
}
486-
sfus, err := getManifest(filepath.Join(i.cache, i.config.FFUManifest()))
508+
sfus, err := getManifest(filepath.Join(i.cache, i.config.SFUManifest()))
487509
if err != nil {
488510
return fmt.Errorf("getManifest() returned: %w: %v", errManifest, err)
489511
}
512+
// Copy SFU files.
490513
for ind, sfu := range sfus {
491-
// This is done as a separate function call to handle closing
492-
// the files through the defer at the end of each iteration
493-
// of the loop instead of waiting until the end of the function.
494-
func() error {
495-
path := filepath.Join(i.cache, sfu.Filename)
496-
newPath := filepath.Join(p.MountPoint(), i.config.FFUDest(), sfu.Filename)
497-
// Add colon for windows paths if its a drive root.
498-
if runtime.GOOS == "windows" && len(p.MountPoint()) < 2 {
499-
newPath = filepath.Join(fmt.Sprintf("%s:", p.MountPoint()), i.config.FFUDest(), sfu.Filename)
500-
}
501-
console.Printf("Copying SFU %d of %d...", ind+1, len(sfus))
502-
if err := os.MkdirAll(filepath.Dir(newPath), 0644); err != nil {
503-
return fmt.Errorf("failed to create path: %v", err)
504-
}
505-
source, err := os.Open(path)
506-
if err != nil {
507-
return fmt.Errorf("%w: couldn't open file(%s) from cache: %v", errPath, path, err)
508-
}
509-
defer source.Close()
510-
destination, err := os.Create(newPath)
511-
if err != nil {
512-
return fmt.Errorf("%w: couldn't create target file(%s): %v", errFile, path, err)
513-
}
514-
defer destination.Close()
515-
cBytes, err := io.Copy(destination, source)
516-
if err != nil {
517-
return fmt.Errorf("failed to copy file to %s: %v", newPath, err)
518-
}
519-
console.Printf("Copied %d bytes", cBytes)
520-
return nil
521-
}()
514+
console.Printf("Copying SFU %d of %d...", ind+1, len(sfus))
515+
if err := fileCopy(sfu.Filename, i.config.SFUDest(), i.cache, p); err != nil {
516+
return fmt.Errorf("fileCopy() failed for %s to %s: %v", sfu.Filename, i.config.SFUDest(), err)
517+
}
518+
}
519+
// Copy config.
520+
console.Printf("Copying %s", i.config.FileName())
521+
if err := fileCopy(i.config.FileName(), i.config.SFUDest(), i.cache, p); err != nil {
522+
return fmt.Errorf("fileCopy() failed for %s to %s: %v", i.config.FileName(), i.config.SFUDest(), err)
523+
}
524+
return nil
525+
}
526+
527+
func fileCopy(srcFile, dest, cache string, p partition) error {
528+
path := filepath.Join(cache, srcFile)
529+
newPath := filepath.Join(p.MountPoint(), dest, srcFile)
530+
// Add colon for windows paths if its a drive root.
531+
if runtime.GOOS == "windows" && len(p.MountPoint()) < 2 {
532+
newPath = filepath.Join(fmt.Sprintf("%s:", p.MountPoint()), dest, srcFile)
533+
}
534+
if err := os.MkdirAll(filepath.Dir(newPath), 0744); err != nil {
535+
return fmt.Errorf("failed to create path: %v", err)
536+
}
537+
source, err := os.Open(path)
538+
if err != nil {
539+
return fmt.Errorf("%w: couldn't open file(%s) from cache: %v", errPath, path, err)
540+
}
541+
defer source.Close()
542+
destination, err := os.Create(newPath)
543+
if err != nil {
544+
return fmt.Errorf("%w: couldn't create target file(%s): %v", errFile, path, err)
545+
}
546+
defer destination.Close()
547+
cBytes, err := io.Copy(destination, source)
548+
if err != nil {
549+
return fmt.Errorf("failed to copy file to %s: %v", newPath, err)
522550
}
551+
console.Printf("Copied %d bytes", cBytes)
523552
return nil
524553
}
525554

0 commit comments

Comments
 (0)