-
Notifications
You must be signed in to change notification settings - Fork 601
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
seed: ReadSystemEssentialAndBetterEarliestTime #10005
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,7 +1,7 @@ | ||||||||||||||||||||||||||||
// -*- Mode: Go; indent-tabs-mode: t -*- | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
/* | ||||||||||||||||||||||||||||
* Copyright (C) 2019-2020 Canonical Ltd | ||||||||||||||||||||||||||||
* Copyright (C) 2019-2021 Canonical Ltd | ||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||
* This program is free software: you can redistribute it and/or modify | ||||||||||||||||||||||||||||
* it under the terms of the GNU General Public License version 3 as | ||||||||||||||||||||||||||||
|
@@ -24,6 +24,7 @@ import ( | |||||||||||||||||||||||||||
"errors" | ||||||||||||||||||||||||||||
"fmt" | ||||||||||||||||||||||||||||
"path/filepath" | ||||||||||||||||||||||||||||
"time" | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
"github.com/snapcore/snapd/asserts" | ||||||||||||||||||||||||||||
"github.com/snapcore/snapd/seed/internal" | ||||||||||||||||||||||||||||
|
@@ -153,3 +154,62 @@ func ReadSystemEssential(seedDir, label string, essentialTypes []snap.Type, tm t | |||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
return seed20.Model(), seed20.EssentialSnaps(), nil | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// ReadSystemEssentialAndBetterEarliestTime retrieves in one go | ||||||||||||||||||||||||||||
// information about the model and essential snaps of the given types | ||||||||||||||||||||||||||||
// for the Core 20 recovery system seed specified by seedDir and label | ||||||||||||||||||||||||||||
// (which cannot be empty). | ||||||||||||||||||||||||||||
// It can operate even if current system time is unreliable by taking | ||||||||||||||||||||||||||||
// a earliestTime lower bound for current time. | ||||||||||||||||||||||||||||
// It returns as well an improved lower bound by considering | ||||||||||||||||||||||||||||
// appropriate assertions in the seed. | ||||||||||||||||||||||||||||
func ReadSystemEssentialAndBetterEarliestTime(seedDir, label string, essentialTypes []snap.Type, earliestTime time.Time, tm timings.Measurer) (*asserts.Model, []*Snap, time.Time, error) { | ||||||||||||||||||||||||||||
if label == "" { | ||||||||||||||||||||||||||||
return nil, nil, time.Time{}, fmt.Errorf("system label cannot be empty") | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
seed20, err := Open(seedDir, label) | ||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||
return nil, nil, time.Time{}, err | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
improve := func(a asserts.Assertion) { | ||||||||||||||||||||||||||||
// we consider only snap-revisions as they are stored-signed | ||||||||||||||||||||||||||||
// and more recent than snap-declarations anyway, | ||||||||||||||||||||||||||||
// other assertions are ignored as they might be added | ||||||||||||||||||||||||||||
// with unreliable times | ||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used a slightly different explanation in the end |
||||||||||||||||||||||||||||
if a.Type() == asserts.SnapRevisionType { | ||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as mentioned on IRC, we could also use the snap-declaration timestamp here, since the checkConsistency method also verified that the snap-declarations are store-signed too, the case is unlikely where the snap-declaration is newer than the snap-revision, but it could happen and I think it's okay to be slightly more accepting of assertion types here |
||||||||||||||||||||||||||||
sr := a.(*asserts.SnapRevision) | ||||||||||||||||||||||||||||
if sr.Timestamp().After(earliestTime) { | ||||||||||||||||||||||||||||
earliestTime = sr.Timestamp() | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// create a temporary database, commitTo will invoke improve | ||||||||||||||||||||||||||||
db, commitTo, err := newMemAssertionsDB(improve) | ||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||
return nil, nil, time.Time{}, err | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
// set up the database to check for key expiry only assuming | ||||||||||||||||||||||||||||
// earliestTime (if not zero) | ||||||||||||||||||||||||||||
db.SetEarliestTime(earliestTime) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// load assertions into the temporary database | ||||||||||||||||||||||||||||
if err := seed20.LoadAssertions(db, commitTo); err != nil { | ||||||||||||||||||||||||||||
return nil, nil, time.Time{}, err | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// load and verify info about essential snaps | ||||||||||||||||||||||||||||
if err := seed20.LoadEssentialMeta(essentialTypes, tm); err != nil { | ||||||||||||||||||||||||||||
return nil, nil, time.Time{}, err | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// consider model own timestamp as well | ||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||
mod := seed20.Model() | ||||||||||||||||||||||||||||
if mod.Timestamp().After(earliestTime) { | ||||||||||||||||||||||||||||
earliestTime = mod.Timestamp() | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
return mod, seed20.EssentialSnaps(), earliestTime, nil | ||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -832,6 +832,131 @@ func (s *seed20Suite) TestLoadEssentialMetaCore20(c *C) { | |||
} | ||||
} | ||||
|
||||
func (s *seed20Suite) TestReadSystemEssentialAndBetterEarliestTime(c *C) { | ||||
r := seed.MockTrusted(s.StoreSigning.Trusted) | ||||
defer r() | ||||
|
||||
s.makeSnap(c, "snapd", "") | ||||
s.makeSnap(c, "core20", "") | ||||
s.makeSnap(c, "pc-kernel=20", "") | ||||
s.makeSnap(c, "pc=20", "") | ||||
s.makeSnap(c, "core18", "") | ||||
t0 := time.Now().UTC().Truncate(time.Second) | ||||
s.SetSnapAssertionNow(t0.Add(2 * time.Second)) | ||||
s.makeSnap(c, "required18", "developerid") | ||||
s.SetSnapAssertionNow(time.Time{}) | ||||
|
||||
snapdSnap := &seed.Snap{ | ||||
Path: s.expectedPath("snapd"), | ||||
SideInfo: &s.AssertedSnapInfo("snapd").SideInfo, | ||||
EssentialType: snap.TypeSnapd, | ||||
Essential: true, | ||||
Required: true, | ||||
Channel: "latest/stable", | ||||
} | ||||
pcKernelSnap := &seed.Snap{ | ||||
Path: s.expectedPath("pc-kernel"), | ||||
SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo, | ||||
EssentialType: snap.TypeKernel, | ||||
Essential: true, | ||||
Required: true, | ||||
Channel: "20", | ||||
} | ||||
core20Snap := &seed.Snap{Path: s.expectedPath("core20"), | ||||
SideInfo: &s.AssertedSnapInfo("core20").SideInfo, | ||||
EssentialType: snap.TypeBase, | ||||
Essential: true, | ||||
Required: true, | ||||
Channel: "latest/stable", | ||||
} | ||||
pcSnap := &seed.Snap{ | ||||
Path: s.expectedPath("pc"), | ||||
SideInfo: &s.AssertedSnapInfo("pc").SideInfo, | ||||
EssentialType: snap.TypeGadget, | ||||
Essential: true, | ||||
Required: true, | ||||
Channel: "20", | ||||
} | ||||
|
||||
tests := []struct { | ||||
onlyTypes []snap.Type | ||||
expected []*seed.Snap | ||||
}{ | ||||
{[]snap.Type{snap.TypeSnapd}, []*seed.Snap{snapdSnap}}, | ||||
{[]snap.Type{snap.TypeKernel}, []*seed.Snap{pcKernelSnap}}, | ||||
{[]snap.Type{snap.TypeBase}, []*seed.Snap{core20Snap}}, | ||||
{[]snap.Type{snap.TypeGadget}, []*seed.Snap{pcSnap}}, | ||||
{[]snap.Type{snap.TypeSnapd, snap.TypeKernel, snap.TypeBase}, []*seed.Snap{snapdSnap, pcKernelSnap, core20Snap}}, | ||||
// the order in essentialTypes is not relevant | ||||
{[]snap.Type{snap.TypeGadget, snap.TypeKernel}, []*seed.Snap{pcKernelSnap, pcSnap}}, | ||||
// degenerate case | ||||
{[]snap.Type{}, []*seed.Snap(nil)}, | ||||
} | ||||
|
||||
baseLabel := "20210315" | ||||
|
||||
testReadSystemEssentialAndBetterEarliestTime := func(sysLabel string, earliestTime, modelTime, improvedTime time.Time) { | ||||
s.MakeSeed(c, sysLabel, "my-brand", "my-model", map[string]interface{}{ | ||||
"display-name": "my model", | ||||
"timestamp": modelTime.Format(time.RFC3339), | ||||
"architecture": "amd64", | ||||
"base": "core20", | ||||
"snaps": []interface{}{ | ||||
map[string]interface{}{ | ||||
"name": "pc-kernel", | ||||
"id": s.AssertedSnapID("pc-kernel"), | ||||
"type": "kernel", | ||||
"default-channel": "20", | ||||
}, | ||||
map[string]interface{}{ | ||||
"name": "pc", | ||||
"id": s.AssertedSnapID("pc"), | ||||
"type": "gadget", | ||||
"default-channel": "20", | ||||
}, | ||||
map[string]interface{}{ | ||||
"name": "core18", | ||||
"id": s.AssertedSnapID("core18"), | ||||
"type": "base", | ||||
}, | ||||
map[string]interface{}{ | ||||
"name": "required18", | ||||
"id": s.AssertedSnapID("required18"), | ||||
}}, | ||||
}, nil) | ||||
|
||||
for _, t := range tests { | ||||
// test short-cut helper as well | ||||
mod, essSnaps, betterTime, err := seed.ReadSystemEssentialAndBetterEarliestTime(s.SeedDir, sysLabel, t.onlyTypes, earliestTime, s.perfTimings) | ||||
c.Assert(err, IsNil) | ||||
c.Check(mod.BrandID(), Equals, "my-brand") | ||||
c.Check(mod.Model(), Equals, "my-model") | ||||
c.Check(mod.Timestamp().Equal(modelTime), Equals, true) | ||||
c.Check(essSnaps, HasLen, len(t.expected)) | ||||
c.Check(essSnaps, DeepEquals, t.expected) | ||||
c.Check(betterTime.Equal(improvedTime), Equals, true, Commentf("%v expected: %v", betterTime, improvedTime)) | ||||
} | ||||
|
||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
} | ||||
|
||||
revsTime := s.AssertedSnapRevision("required18").Timestamp() | ||||
t2 := revsTime.Add(1 * time.Second) | ||||
|
||||
timeCombos := []struct { | ||||
earliestTime, modelTime, improvedTime time.Time | ||||
}{ | ||||
{time.Time{}, t0, revsTime}, | ||||
{t2.AddDate(-1, 0, 0), t0, revsTime}, | ||||
{t2.AddDate(-1, 0, 0), t2, t2}, | ||||
{t2.AddDate(0, 1, 0), t2, t2.AddDate(0, 1, 0)}, | ||||
} | ||||
|
||||
for i, c := range timeCombos { | ||||
label := fmt.Sprintf("%s%d", baseLabel, i) | ||||
testReadSystemEssentialAndBetterEarliestTime(label, c.earliestTime, c.modelTime, c.improvedTime) | ||||
} | ||||
} | ||||
|
||||
func (s *seed20Suite) TestLoadEssentialAndMetaCore20(c *C) { | ||||
r := seed.MockTrusted(s.StoreSigning.Trusted) | ||||
defer r() | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.