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
o/devicestate: consider snapd snap when remodeling #13242
o/devicestate: consider snapd snap when remodeling #13242
Conversation
Codecov Report
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. @@ Coverage Diff @@
## master #13242 +/- ##
==========================================
- Coverage 78.83% 78.83% -0.01%
==========================================
Files 1016 1013 -3
Lines 126663 126392 -271
==========================================
- Hits 99857 99636 -221
+ Misses 20557 20527 -30
+ Partials 6249 6229 -20
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 17 files with indirect coverage changes 📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
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.
Looks good! One quick question.
overlord/devicestate/devicestate.go
Outdated
// new required snaps or a track for existing ones needs to be updated | ||
for _, modelSnap := range new.Snaps() { | ||
switch modelSnap.SnapType { | ||
case "gadget", "kernel", "base", "core": |
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.
Would
if snapsAccountedFor[modelSnap.Name] {
continue
}
work instead of specifically calling out out the already handled ones? It looks like new.Base()
, which is used as a key in snapsAccountedFor
earlier, returns something that may not be the name, so it might not. If it does, though, I think it'd be a bit better.
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.
Actually this is an excellent suggestion, as now I notice that this could overlook non-rootfs bases (we can have UC22 but also a core20 snap installed).
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.
not sure it makes sense to treat snapd as we treat app snaps
overlord/devicestate/devicestate.go
Outdated
for _, modelSnap := range new.Snaps() { | ||
if snapsAccountedFor[modelSnap.Name] { | ||
continue | ||
} |
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.
I don't think we want to switch snapd track in the middle of other things, it needs to be treated as one of the essential snaps I think and processed first as we do during regular refreshes
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.
It'd be really cool if we could do the essential snaps in a loop, rather than having them all listed like they are now. Although some of the special cases make that difficult. Specifically, new.BaseSnap()
can return nil
on UC16, and only the gadget snap's task set gets passed to updateNeededSnapsFromTs
.
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.
I want ahead with this, hopefully it looks a bit better now.
08da44d
to
c0c43ea
Compare
Thanks for the reviews, I've repushed now so snapd is considered jointly with the other essential snaps. To be clear, by looking at how are things implemented at the moment I don't think it will make a difference with the previous approach. |
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.
Looks good! Just one comment
asserts/model.go
Outdated
@@ -523,6 +523,14 @@ func (mod *Model) StorageSafety() StorageSafety { | |||
return mod.storageSafety | |||
} | |||
|
|||
// SnapdSnap returns the details of the snapd snap for this model. | |||
func (mod *Model) SnapdSnap() *ModelSnap { | |||
if mod.Base() == "" || mod.Base() == "core" { |
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.
A comment explaining why these checks are made might be useful. Additionally, rather than directly accessing the slice of snaps, maybe store a pointer to the snap on *Model
like the rest of the essential snaps. Although that really doesn't matter too much.
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.
I think core18 style classic models can have no base but a snapd snap?
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.
But then how you would know whether to seed core or core18? I don't see examples of 18.04 classic models in https://github.com/snapcore/models/tree/master , do you know where could I find one?
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.
I think the hybrid installer might have one, maybe some cloud images. We should have some example in our own unit test somewhere, though they test might be signing it on the fly.
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.
also on classic afair if there's a snapd it will be used in preference to core
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.
I've changed the strategy and now I simply check the name of the first snap in the slice, to avoid this issues. The only example that I've seen of classic 18 model is snapd/tests/lib/assertions/developer1-my-classic-w-gadget-18.model
, which has:
required-snaps:
- core18
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.
question
asserts/model.go
Outdated
@@ -523,6 +523,15 @@ func (mod *Model) StorageSafety() StorageSafety { | |||
return mod.storageSafety | |||
} | |||
|
|||
// SnapdSnap returns the details of the snapd snap for this model. | |||
func (mod *Model) SnapdSnap() *ModelSnap { |
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.
it's unclear to me if this is enough, snapd can be explicit or implicit in UC18 and UC20+ models, but seed code will pick what to use even if it's implicit:
https://github.com/snapcore/snapd/blob/2.60.4/seed/seed20.go#L699
https://github.com/snapcore/snapd/blob/2.60.4/seed/seed16.go#L321
and at least with UC20+ potentially moving from an implicit to and explict or the other way around snapd might involve a channel change but is not clear to me that this simple method plus the code in devicestate will cover those scenarios correctly?
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.
I have rewritten the PR so this method is not needed anymore, accounting for cases when snapd information is implicit. In the end, modelSnaps type and other types reflect only the explicit information in the model assertion, and we are not changing that now, although I think it would be good to reflect also implicit information there in the future.
360fe17
to
76e6425
Compare
overlord/devicestate/devicestate.go
Outdated
@@ -541,6 +536,10 @@ func remodelEssentialSnapTasks(ctx context.Context, st *state.State, pathSI *pat | |||
} | |||
|
|||
if !changed { | |||
addExistingSnapTasks := snapstate.LinkNewBaseOrKernel |
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.
I like this change, but I think it'd be even more clear if addExistingSnapTasks
was eliminated and the functions were called directly.
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.
Done
overlord/devicestate/devicestate.go
Outdated
var pathSi *pathSideInfo | ||
// Implicit new channel if snapd is not explicitly in the model | ||
newSnapdChannel := "latest/stable" | ||
essentialSnaps := newModel.EssentialSnaps() | ||
if essentialSnaps[0].SnapType == "snapd" { | ||
// snapd can be specified explicitly in the model (UC20+) | ||
newSnapdChannel = essentialSnaps[0].DefaultChannel | ||
} | ||
pathSi = sideInfoAndPathFromID(localSnaps, paths, naming.WellKnownSnapID("snapd")) |
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.
var pathSi *pathSideInfo | |
// Implicit new channel if snapd is not explicitly in the model | |
newSnapdChannel := "latest/stable" | |
essentialSnaps := newModel.EssentialSnaps() | |
if essentialSnaps[0].SnapType == "snapd" { | |
// snapd can be specified explicitly in the model (UC20+) | |
newSnapdChannel = essentialSnaps[0].DefaultChannel | |
} | |
pathSi = sideInfoAndPathFromID(localSnaps, paths, naming.WellKnownSnapID("snapd")) | |
// Implicit new channel if snapd is not explicitly in the model | |
newSnapdChannel := "latest/stable" | |
essentialSnaps := newModel.EssentialSnaps() | |
if essentialSnaps[0].SnapType == "snapd" { | |
// snapd can be specified explicitly in the model (UC20+) | |
newSnapdChannel = essentialSnaps[0].DefaultChannel | |
} | |
pathSi := sideInfoAndPathFromID(localSnaps, paths, naming.WellKnownSnapID("snapd")) |
No need to declare pathSi
early.
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.
Right, done now
return nil, err | ||
} | ||
if ts != nil { | ||
tss = append(tss, ts) | ||
if modelSnap.SnapType == "gadget" { |
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.
Do you know why we have to have this special case? Is it because gadgets can have prerequisites, and other types cannot?
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.
Yes, that is the case. Bases cannot depend on other base, and kernels do not depend on a base, at least for the moment. And neither of them can have content providers I think.
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.
Updates look good!
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.
thanks, left a couple of comments on the new version
overlord/managers_test.go
Outdated
snapdPath, _ := s.makeStoreTestSnap(c, "name: snapd\ntype: snapd\nversion: 123", "1") | ||
s.serveSnap(snapdPath, "2") | ||
|
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.
why do we need this change? shouldn't we make it so that there's no channel change instead? the test doesn't seem to be about this anyway
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.
Without adding this the test does not progress enough and the error:
unexpectedly empty response from the server (try again later)
is returned. snapd snap is set in the state for the test, so at some point the code is trying to contact the fakestore for information about it.
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.
sorry, maybe I wasn't clear but the issue why you need this is that the new code thinks there's a channel change for snapd: this works too and is a bit cleaner I think:
diff --git a/overlord/managers_test.go b/overlord/managers_test.go
index f216eccaf5..b51b17af30 100644
--- a/overlord/managers_test.go
+++ b/overlord/managers_test.go
@@ -8460,8 +8460,6 @@ func (s *mgrsSuiteCore) TestRemodelUC20SnapWithPrereqsMissingDeps(c *C) {
snapPath, _ := s.makeStoreTestSnap(c, prereqSnapYaml, "1")
s.serveSnap(snapPath, "1")
- snapdPath, _ := s.makeStoreTestSnap(c, "name: snapd\ntype: snapd\nversion: 123", "1")
- s.serveSnap(snapdPath, "2")
st := s.o.State()
st.Lock()
@@ -8475,6 +8473,7 @@ func (s *mgrsSuiteCore) TestRemodelUC20SnapWithPrereqsMissingDeps(c *C) {
},
Current: snap.R(1),
SnapType: "snapd",
+ TrackingChannel: "latest/stable",
Flags: snapstate.Flags{
Required: true,
},
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.
Oh, right, I've changed this now. Sorry, I should have read more carefully!
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.
Thanks for the reviews, I've addressed the comments now.
overlord/devicestate/devicestate.go
Outdated
@@ -541,6 +536,10 @@ func remodelEssentialSnapTasks(ctx context.Context, st *state.State, pathSI *pat | |||
} | |||
|
|||
if !changed { | |||
addExistingSnapTasks := snapstate.LinkNewBaseOrKernel |
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.
Done
overlord/devicestate/devicestate.go
Outdated
var pathSi *pathSideInfo | ||
// Implicit new channel if snapd is not explicitly in the model | ||
newSnapdChannel := "latest/stable" | ||
essentialSnaps := newModel.EssentialSnaps() | ||
if essentialSnaps[0].SnapType == "snapd" { | ||
// snapd can be specified explicitly in the model (UC20+) | ||
newSnapdChannel = essentialSnaps[0].DefaultChannel | ||
} | ||
pathSi = sideInfoAndPathFromID(localSnaps, paths, naming.WellKnownSnapID("snapd")) |
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.
Right, done now
return nil, err | ||
} | ||
if ts != nil { | ||
tss = append(tss, ts) | ||
if modelSnap.SnapType == "gadget" { |
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.
Yes, that is the case. Bases cannot depend on other base, and kernels do not depend on a base, at least for the moment. And neither of them can have content providers I think.
overlord/managers_test.go
Outdated
snapdPath, _ := s.makeStoreTestSnap(c, "name: snapd\ntype: snapd\nversion: 123", "1") | ||
s.serveSnap(snapdPath, "2") | ||
|
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.
Without adding this the test does not progress enough and the error:
unexpectedly empty response from the server (try again later)
is returned. snapd snap is set in the state for the test, so at some point the code is trying to contact the fakestore for information about it.
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.
thank you, further comment about the managers_test.go change
overlord/managers_test.go
Outdated
snapdPath, _ := s.makeStoreTestSnap(c, "name: snapd\ntype: snapd\nversion: 123", "1") | ||
s.serveSnap(snapdPath, "2") | ||
|
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.
sorry, maybe I wasn't clear but the issue why you need this is that the new code thinks there's a channel change for snapd: this works too and is a bit cleaner I think:
diff --git a/overlord/managers_test.go b/overlord/managers_test.go
index f216eccaf5..b51b17af30 100644
--- a/overlord/managers_test.go
+++ b/overlord/managers_test.go
@@ -8460,8 +8460,6 @@ func (s *mgrsSuiteCore) TestRemodelUC20SnapWithPrereqsMissingDeps(c *C) {
snapPath, _ := s.makeStoreTestSnap(c, prereqSnapYaml, "1")
s.serveSnap(snapPath, "1")
- snapdPath, _ := s.makeStoreTestSnap(c, "name: snapd\ntype: snapd\nversion: 123", "1")
- s.serveSnap(snapdPath, "2")
st := s.o.State()
st.Lock()
@@ -8475,6 +8473,7 @@ func (s *mgrsSuiteCore) TestRemodelUC20SnapWithPrereqsMissingDeps(c *C) {
},
Current: snap.R(1),
SnapType: "snapd",
+ TrackingChannel: "latest/stable",
Flags: snapstate.Flags{
Required: true,
},
Changes to snapd in a new model assertion (which can be only changes in tracked channel at the moment) where not being applied, make sure this does not happen anymore. Fixes LP#2035186.
760192a
to
3ef4823
Compare
History rewritten and rebased to master. |
Changes to snapd in a new model assertion (which can be only changes
in tracked channel at the moment) where not being applied, make sure
this does not happen anymore. Fixes LP#2035186.