From 11b48b013bcdd497e575a7ef84c3ddfb293ee533 Mon Sep 17 00:00:00 2001 From: Al Cutter Date: Wed, 11 Oct 2023 12:16:47 +0100 Subject: [PATCH] Add remaining components to updater (#11) --- release/firmware/update/fetch.go | 94 ++++++++++++++++++--------- release/firmware/update/fetch_test.go | 29 +++++++++ 2 files changed, 94 insertions(+), 29 deletions(-) diff --git a/release/firmware/update/fetch.go b/release/firmware/update/fetch.go index 0b44172..53c6e1c 100644 --- a/release/firmware/update/fetch.go +++ b/release/firmware/update/fetch.go @@ -137,11 +137,13 @@ type Fetcher struct { manifestVerifiers map[string][]note.Verifier - mu sync.Mutex - latestOS *firmwareRelease - latestApplet *firmwareRelease - logState client.LogStateTracker - scanFrom uint64 + mu sync.Mutex + latestOS *firmwareRelease + latestApplet *firmwareRelease + latestBoot *firmwareRelease + latestRecovery *firmwareRelease + logState client.LogStateTracker + scanFrom uint64 } func (f *Fetcher) GetLatestVersions(_ context.Context) (os semver.Version, applet semver.Version, err error) { @@ -185,6 +187,40 @@ func (f *Fetcher) GetApplet(ctx context.Context) (firmware.Bundle, error) { return *f.latestApplet.bundle, nil } +func (f *Fetcher) GetBoot(ctx context.Context) (firmware.Bundle, error) { + f.mu.Lock() + defer f.mu.Unlock() + + if f.latestBoot == nil { + return firmware.Bundle{}, errors.New("no latest boot available") + } + if f.latestBoot.bundle.Firmware == nil { + binary, err := f.binFetcher(ctx, f.latestBoot.manifest) + if err != nil { + return firmware.Bundle{}, fmt.Errorf("BinaryFetcher(): %v", err) + } + f.latestBoot.bundle.Firmware = binary + } + return *f.latestBoot.bundle, nil +} + +func (f *Fetcher) GetRecovery(ctx context.Context) (firmware.Bundle, error) { + f.mu.Lock() + defer f.mu.Unlock() + + if f.latestRecovery == nil { + return firmware.Bundle{}, errors.New("no latest recovery available") + } + if f.latestRecovery.bundle.Firmware == nil { + binary, err := f.binFetcher(ctx, f.latestRecovery.manifest) + if err != nil { + return firmware.Bundle{}, fmt.Errorf("BinaryFetcher(): %v", err) + } + f.latestRecovery.bundle.Firmware = binary + } + return *f.latestRecovery.bundle, nil +} + // Scan gets the latest checkpoint from the log and updates the fetcher's state // to reflect the latest OS and Applet available in the log. func (f *Fetcher) Scan(ctx context.Context) error { @@ -231,31 +267,13 @@ func (f *Fetcher) Scan(ctx context.Context) error { switch manifest.Component { case ftlog.ComponentOS: - // According to the SemVer2.0 spec, equal revisions have no precedence defined. - // In general this won't be an issue; production releases will always be tagged appropriately, - // and there should never be two different releases with the same semver for a given component, - // however, during development, this may not hold. - // To tighten the definition of precedence, we'll use the fact that logs define ordering - // and say that "later" entries take precedence over "earlier" entries with the same version - // numbering. - if f.latestOS == nil || - f.latestOS.manifest.GitTagName.LessThan(manifest.GitTagName) || - f.latestOS.manifest.GitTagName.Equal(manifest.GitTagName) { - f.latestOS = &firmwareRelease{ - bundle: bundle, - manifest: manifest, - } - } + f.latestOS = highestRelease(f.latestOS, &firmwareRelease{bundle: bundle, manifest: manifest}) case ftlog.ComponentApplet: - // See comment above about the Equal case. - if f.latestApplet == nil || - f.latestApplet.manifest.GitTagName.LessThan(manifest.GitTagName) || - f.latestApplet.manifest.GitTagName.Equal(manifest.GitTagName) { - f.latestApplet = &firmwareRelease{ - bundle: bundle, - manifest: manifest, - } - } + f.latestApplet = highestRelease(f.latestApplet, &firmwareRelease{bundle: bundle, manifest: manifest}) + case ftlog.ComponentBoot: + f.latestBoot = highestRelease(f.latestBoot, &firmwareRelease{bundle: bundle, manifest: manifest}) + case ftlog.ComponentRecovery: + f.latestRecovery = highestRelease(f.latestRecovery, &firmwareRelease{bundle: bundle, manifest: manifest}) default: klog.Warningf("unknown component type in log: %q", manifest.Component) } @@ -264,6 +282,24 @@ func (f *Fetcher) Scan(ctx context.Context) error { return nil } +// highestRelease returns the "higher" of the two releases passed in according to SemVer rules. +// +// According to the SemVer2.0 spec, equal revisions have no precedence defined. +// In general this won't be an issue; production releases will always be tagged appropriately, +// and there should never be two different releases with the same semver for a given component, +// however, during development, this may not hold. +// To tighten the definition of precedence, we'll use the fact that logs define ordering +// and say that "later" entries take precedence over "earlier" entries with the same version +// numbering. +func highestRelease(current *firmwareRelease, candidate *firmwareRelease) *firmwareRelease { + if current == nil || + current.manifest.GitTagName.LessThan(candidate.manifest.GitTagName) || + current.manifest.GitTagName.Equal(candidate.manifest.GitTagName) { + return candidate + } + return current +} + func parseLeaf(leaf []byte, verifiers map[string][]note.Verifier) (ftlog.FirmwareRelease, error) { var n *note.Note var err error diff --git a/release/firmware/update/fetch_test.go b/release/firmware/update/fetch_test.go index 4af964c..a8aad37 100644 --- a/release/firmware/update/fetch_test.go +++ b/release/firmware/update/fetch_test.go @@ -101,10 +101,14 @@ func TestFetcher(t *testing.T) { { {Component: ftlog.ComponentOS, GitTagName: *semver.New("1.0.1")}, {Component: ftlog.ComponentApplet, GitTagName: *semver.New("1.1.1")}, + {Component: ftlog.ComponentBoot, GitTagName: *semver.New("1.3.1")}, + {Component: ftlog.ComponentRecovery, GitTagName: *semver.New("1.1.1")}, }, { {Component: ftlog.ComponentOS, GitTagName: *semver.New("1.2.1")}, {Component: ftlog.ComponentApplet, GitTagName: *semver.New("1.3.1")}, + {Component: ftlog.ComponentBoot, GitTagName: *semver.New("1.3.1")}, + {Component: ftlog.ComponentRecovery, GitTagName: *semver.New("1.1.1")}, }, }, want: [][]ftlog.FirmwareRelease{ @@ -115,6 +119,8 @@ func TestFetcher(t *testing.T) { { {Component: ftlog.ComponentOS, GitTagName: *semver.New("1.2.1")}, {Component: ftlog.ComponentApplet, GitTagName: *semver.New("1.3.1")}, + {Component: ftlog.ComponentBoot, GitTagName: *semver.New("1.3.1")}, + {Component: ftlog.ComponentRecovery, GitTagName: *semver.New("1.1.1")}, }, }, }, { @@ -123,14 +129,20 @@ func TestFetcher(t *testing.T) { { {Component: ftlog.ComponentOS, GitTagName: *semver.New("1.0.1")}, {Component: ftlog.ComponentApplet, GitTagName: *semver.New("1.1.1")}, + {Component: ftlog.ComponentBoot, GitTagName: *semver.New("1.3.1")}, + {Component: ftlog.ComponentRecovery, GitTagName: *semver.New("1.1.1")}, {Component: ftlog.ComponentOS, GitTagName: *semver.New("1.0.2")}, {Component: ftlog.ComponentApplet, GitTagName: *semver.New("2.0.1")}, + {Component: ftlog.ComponentBoot, GitTagName: *semver.New("1.7.1")}, + {Component: ftlog.ComponentRecovery, GitTagName: *semver.New("1.8.1")}, }, }, want: [][]ftlog.FirmwareRelease{ { {Component: ftlog.ComponentOS, GitTagName: *semver.New("1.0.2")}, {Component: ftlog.ComponentApplet, GitTagName: *semver.New("2.0.1")}, + {Component: ftlog.ComponentBoot, GitTagName: *semver.New("1.7.1")}, + {Component: ftlog.ComponentRecovery, GitTagName: *semver.New("1.8.1")}, }, }, }, { @@ -208,9 +220,26 @@ func TestFetcher(t *testing.T) { switch want.Component { case ftlog.ComponentApplet: got = applet + if _, err = f.GetApplet(ctx); err != nil { + t.Fatalf("GetApplet: %v", err) + } + case ftlog.ComponentBoot: + if _, err = f.GetBoot(ctx); err != nil { + t.Fatalf("GetBoot: %v", err) + } + got = f.latestBoot.manifest.GitTagName case ftlog.ComponentOS: got = os + if _, err = f.GetOS(ctx); err != nil { + t.Fatalf("GetOS: %v", err) + } + case ftlog.ComponentRecovery: + if _, err = f.GetRecovery(ctx); err != nil { + t.Fatalf("GetRecovery: %v", err) + } + got = f.latestRecovery.manifest.GitTagName } + if got.String() != want.GitTagName.String() { t.Errorf("got %v, want %v", got, want) }