Skip to content
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/snapstate: account for remodeling when installing prereqs as well as updating them #14137

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions overlord/snapstate/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,15 @@ func (m *SnapManager) installOneBaseOrRequired(t *state.Task, snapName string, c
return true, nil
}

// if we are remodeling, then we should return early due to the way that
// tasks are ordered by the remodeling code. specifically, all snap
// downloads during a remodel happen prior to snap installation. thus,
// we cannot wait for snaps to be installed here. see remodelTasks for
// more information on how the tasks are ordered.
if deviceCtx.ForRemodeling() {
return nil, nil
}

if isInstalled {
if len(contentAttrs) > 0 {
// the default provider is already installed, update it if it's missing content attributes the snap needs
Expand All @@ -351,22 +360,14 @@ func (m *SnapManager) installOneBaseOrRequired(t *state.Task, snapName string, c
} else if ok {
return nil, onInFlight
}

return nil, nil
}

// not installed, wait for it if it is. If not, we'll install it
if ok, err := inProgress(snapName); err != nil {
return nil, err
} else if ok {
// if we are remodeling, then we should return early due to the way that
// tasks are ordered by the remodeling code. specifically, all snap
// downloads during a remodel happen prior to snap installation. thus,
// we cannot wait for snaps to be installed here. see remodelTasks for
// more information on how the tasks are ordered.
if deviceCtx.ForRemodeling() {
return nil, nil
}

return nil, onInFlight
}

Expand Down
70 changes: 70 additions & 0 deletions overlord/snapstate/handlers_prereq_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,76 @@ func (s *prereqSuite) TestDoPrereqRetryWhenBaseInFlight(c *C) {
c.Check(chg.Status(), Equals, state.DoneStatus)
}

func (s *prereqSuite) TestDoPrereqNoRetryWhenBaseInFlightDuringRemodel(c *C) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could have other variants (skips when there's a install in progress, a possible update of a prereq, a missing base, etc) but maybe it's not worth it to add those right now since we want this merged soon

restore := snapstate.MockPrerequisitesRetryTimeout(1 * time.Millisecond)
defer restore()

s.runner.AddHandler("link-snap", func(task *state.Task, _ *tomb.Tomb) error {
st := task.State()
st.Lock()
defer st.Unlock()

snapsup, err := snapstate.TaskSnapSetup(task)
c.Assert(err, IsNil)
fmt.Println(snapsup.InstanceName())

return nil
}, nil)

s.state.Lock()
defer s.state.Unlock()

restore = snapstatetest.MockDeviceContext(&snapstatetest.TrivialDeviceContext{
Remodeling: true,
})
defer restore()

// make it look like core is already installed
snapstate.Set(s.state, "core", &snapstate.SnapState{
Active: true,
Sequence: snapstatetest.NewSequenceFromSnapSideInfos([]*snap.SideInfo{
{RealName: "core", Revision: snap.R(1)},
}),
Current: snap.R(1),
SnapType: "os",
})

// pretend foo gets installed and needs core (which we will make it look
// like the install is in progress)
prereqTask := s.state.NewTask("prerequisites", "foo")
prereqTask.Set("snap-setup", &snapstate.SnapSetup{
SideInfo: &snap.SideInfo{
RealName: "foo",
},
})

tCore := s.state.NewTask("link-snap", "Pretend core gets installed")
tCore.Set("snap-setup", &snapstate.SnapSetup{
SideInfo: &snap.SideInfo{
RealName: "core",
Revision: snap.R(11),
},
})

// this makes sure that the prereq task runs first, but the link-snap task
// for core will be found by the prereq task handler
tCore.WaitFor(prereqTask)

chg := s.state.NewChange("sample", "...")
chg.AddTask(prereqTask)
chg.AddTask(tCore)

s.state.Unlock()
s.se.Ensure()
s.se.Wait()
s.state.Lock()

// prereq task is done, but the core task isn't done. this means that we
// didn't wait for the core task to finish, since we are remodeling
c.Check(prereqTask.Status(), Equals, state.DoneStatus)
c.Check(tCore.Status(), Equals, state.DoStatus)
}

func (s *prereqSuite) TestDoPrereqChannelEnvvars(c *C) {
os.Setenv("SNAPD_BASES_CHANNEL", "edge")
defer os.Unsetenv("SNAPD_BASES_CHANNEL")
Expand Down
Loading