Skip to content

Commit 4ab7583

Browse files
committed
o/snapstate, o/devicestate: update hooks to let devicestate own what results in a seed-refresh
1 parent fe74aec commit 4ab7583

8 files changed

Lines changed: 590 additions & 374 deletions

File tree

overlord/devicestate/devicestate.go

Lines changed: 105 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ func delayedCrossMgrInit() {
298298
snapstate.DeviceCtx = DeviceCtx
299299
snapstate.RemodelingChange = RemodelingChange
300300
snapstate.SeedRefreshTasks = SeedRefreshTasks
301-
snapstate.AppendSeedRefreshSetupTaskIDs = AppendSeedRefreshSetupTaskIDs
301+
snapstate.UpdateSeedRefreshChange = UpdateSeedRefreshChange
302302
}
303303

304304
// proxyStore returns the store assertion for the proxy store if one is set.
@@ -1763,26 +1763,41 @@ func removeRecoverySystemTask(st *state.State, label string) *state.Task {
17631763
}
17641764

17651765
// SeedRefreshTasks returns a [snapstate.SeedRefreshTaskSet] that carries the
1766-
// tasks needed to refresh the seed managed by seed-refresh mode. The caller
1767-
// must provide the tasks IDs that can be used by the seed creation tasks to
1768-
// find the new snaps to include in the seed. Otherwise, already installed snaps
1769-
// will be used to create the seed. Older seed-refresh systems are removed so
1766+
// tasks needed to refresh the seed managed by seed-refresh mode, plus the snap
1767+
// names selected for that seed refresh. The selected setup task IDs are written
1768+
// into the recovery-system setup payload so the new seed can consume the
1769+
// refreshed snaps and components. Older seed-refresh systems are removed so
17701770
// that, after finalize records the new system, the two most recently created
17711771
// seed-refresh systems remain tracked.
1772-
func SeedRefreshTasks(st *state.State, snapSetupTasks, compSetupTasks []string) (*snapstate.SeedRefreshTaskSet, error) {
1772+
func SeedRefreshTasks(st *state.State, dctx snapstate.DeviceContext, candidates []snapstate.SeedRefreshCandidate) (*snapstate.SeedRefreshTaskSet, map[string]bool, error) {
1773+
var snapsups, compsups []string
1774+
added := make(map[string]bool, len(candidates))
1775+
for _, candidate := range candidates {
1776+
if !seedRefreshIncludesSnap(dctx, candidate.InstanceName) {
1777+
continue
1778+
}
1779+
added[candidate.InstanceName] = true
1780+
1781+
snapsups = append(snapsups, candidate.SnapSetupTasks...)
1782+
compsups = append(compsups, candidate.ComponentSetupTasks...)
1783+
}
1784+
if len(added) == 0 {
1785+
return nil, nil, nil
1786+
}
1787+
17731788
labelBase := timeNow().Format("20060102")
17741789
label, err := pickRecoverySystemLabel(labelBase)
17751790
if err != nil {
1776-
return nil, fmt.Errorf("cannot select non-conflicting label for recovery system %q: %v", labelBase, err)
1791+
return nil, nil, fmt.Errorf("cannot select non-conflicting label for recovery system %q: %v", labelBase, err)
17771792
}
17781793

1779-
ts, err := createRecoverySystemTasks(st, label, snapSetupTasks, compSetupTasks, CreateRecoverySystemOptions{
1794+
ts, err := createRecoverySystemTasks(st, label, snapsups, compsups, CreateRecoverySystemOptions{
17801795
TestSystem: true,
17811796
MarkDefault: true,
17821797
SeedRefresh: true,
17831798
})
17841799
if err != nil {
1785-
return nil, err
1800+
return nil, nil, err
17861801
}
17871802

17881803
var create, finalize *state.Task
@@ -1796,12 +1811,12 @@ func SeedRefreshTasks(st *state.State, snapSetupTasks, compSetupTasks []string)
17961811
}
17971812

17981813
if create == nil || finalize == nil {
1799-
return nil, errors.New("internal error: expected create and finalize recovery system tasks")
1814+
return nil, nil, errors.New("internal error: expected create and finalize recovery system tasks")
18001815
}
18011816

18021817
removeLabels, err := seedRefreshLabelsToRemove(st)
18031818
if err != nil {
1804-
return nil, err
1819+
return nil, nil, err
18051820
}
18061821

18071822
removals := make([]*state.Task, 0, len(removeLabels))
@@ -1815,23 +1830,97 @@ func SeedRefreshTasks(st *state.State, snapSetupTasks, compSetupTasks []string)
18151830
Create: create,
18161831
Finalize: finalize,
18171832
Remove: removals,
1818-
}, nil
1833+
}, added, nil
1834+
}
1835+
1836+
// UpdateSeedRefreshChange adds a late candidate to an existing seed-refresh
1837+
// change when the snap should participate in the refreshed seed.
1838+
func UpdateSeedRefreshChange(chg *state.Change, dctx snapstate.DeviceContext, candidate snapstate.SeedRefreshCandidate) (*snapstate.SeedRefreshTaskSet, error) {
1839+
if !seedRefreshIncludesSnap(dctx, candidate.InstanceName) {
1840+
return nil, nil
1841+
}
1842+
1843+
seedTS, err := findSeedRefreshTasks(chg)
1844+
if err != nil {
1845+
return nil, err
1846+
}
1847+
1848+
if err := appendSeedRefreshCandidate(seedTS.Create, candidate.SnapSetupTasks, candidate.ComponentSetupTasks); err != nil {
1849+
return nil, err
1850+
}
1851+
1852+
return seedTS, nil
18191853
}
18201854

1821-
// AppendSeedRefreshSetupTaskIDs appends unique setup task IDs to the
1822-
// create-recovery-system task recovery-system-setup payload.
1823-
func AppendSeedRefreshSetupTaskIDs(create *state.Task, snapSetupTask string, compSetupTasks []string) error {
1855+
func appendSeedRefreshCandidate(create *state.Task, snapSetupTasks, compSetupTasks []string) error {
18241856
setup, err := taskRecoverySystemSetup(create)
18251857
if err != nil {
18261858
return err
18271859
}
18281860

1829-
setup.SnapSetupTasks = appendUnique(setup.SnapSetupTasks, snapSetupTask)
1861+
setup.SnapSetupTasks = appendUnique(setup.SnapSetupTasks, snapSetupTasks...)
18301862
setup.ComponentSetupTasks = appendUnique(setup.ComponentSetupTasks, compSetupTasks...)
18311863

18321864
return setTaskRecoverySystemSetup(create, setup)
18331865
}
18341866

1867+
func seedRefreshIncludesSnap(dctx snapstate.DeviceContext, instanceName string) bool {
1868+
// TODO:SEEDREFRESH: consider the intersections of snaps in the model and
1869+
// snaps currently present in the seed, not all snaps in the model.
1870+
if instanceName == "snapd" {
1871+
return true
1872+
}
1873+
1874+
for _, sn := range dctx.Model().AllSnaps() {
1875+
if sn.SnapName() == instanceName {
1876+
return true
1877+
}
1878+
}
1879+
1880+
return false
1881+
}
1882+
1883+
func findSeedRefreshTasks(chg *state.Change) (*snapstate.SeedRefreshTaskSet, error) {
1884+
var finalize *state.Task
1885+
var removals []*state.Task
1886+
for _, t := range chg.Tasks() {
1887+
switch t.Kind() {
1888+
case "finalize-recovery-system":
1889+
if t.Status().Ready() {
1890+
continue
1891+
}
1892+
if finalize != nil {
1893+
return nil, errors.New("internal error: found multiple pending seed finalization tasks in change")
1894+
}
1895+
finalize = t
1896+
case "remove-recovery-system":
1897+
if !t.Status().Ready() {
1898+
removals = append(removals, t)
1899+
}
1900+
}
1901+
}
1902+
1903+
if finalize == nil {
1904+
return nil, errors.New("internal error: seed-refresh change is missing pending finalize-recovery-system task")
1905+
}
1906+
1907+
var createID string
1908+
if err := finalize.Get("recovery-system-setup-task", &createID); err != nil {
1909+
return nil, err
1910+
}
1911+
1912+
create := chg.State().Task(createID)
1913+
if create == nil || create.Change().ID() != chg.ID() || create.Kind() != "create-recovery-system" {
1914+
return nil, errors.New("internal error: seed-refresh change is missing paired create-recovery-system task")
1915+
}
1916+
1917+
return &snapstate.SeedRefreshTaskSet{
1918+
Create: create,
1919+
Finalize: finalize,
1920+
Remove: removals,
1921+
}, nil
1922+
}
1923+
18351924
func appendUnique(slice []string, additions ...string) []string {
18361925
seen := make(map[string]bool, len(slice))
18371926
for _, id := range slice {

overlord/devicestate/devicestate_systems_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2755,8 +2755,14 @@ func (s *deviceMgrSystemsCreateSuite) TestSeedRefreshTasksFinalizeUndoDoesNotRes
27552755
}
27562756
s.state.Set("seeded-systems", []devicestate.SeededSystem{keepSeededSystem, removeSeededSystem})
27572757

2758-
seedTS, err := devicestate.SeedRefreshTasks(s.state, nil, nil)
2758+
dctx := &snapstatetest.TrivialDeviceContext{DeviceModel: s.model}
2759+
seedTS, added, err := devicestate.SeedRefreshTasks(s.state, dctx, []snapstate.SeedRefreshCandidate{
2760+
{
2761+
InstanceName: s.model.Kernel(),
2762+
},
2763+
})
27592764
c.Assert(err, IsNil)
2765+
c.Assert(added, DeepEquals, map[string]bool{s.model.Kernel(): true})
27602766
c.Assert(seedTS, NotNil)
27612767
c.Assert(seedTS.Remove, HasLen, 1)
27622768

0 commit comments

Comments
 (0)