Skip to content

Commit

Permalink
o/devicestate: consider the current model's validation sets when crea…
Browse files Browse the repository at this point in the history
…ting a recovery system
  • Loading branch information
andrewphelpsj authored and Meulengracht committed Feb 23, 2024
1 parent b79debc commit a1e658d
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 12 deletions.
20 changes: 11 additions & 9 deletions overlord/devicestate/devicestate.go
Original file line number Diff line number Diff line change
Expand Up @@ -1628,23 +1628,25 @@ func CreateRecoverySystem(st *state.State, label string, opts CreateRecoverySyst
return nil, fmt.Errorf("cannot create new recovery systems until fully seeded")
}

valsets := snapasserts.NewValidationSets()
for _, vs := range opts.ValidationSets {
if err := valsets.Add(vs); err != nil {
return nil, err
}
model, err := findModel(st)
if err != nil {
return nil, err
}

if err := valsets.Conflict(); err != nil {
valsets, err := assertstate.TrackedEnforcedValidationSetsForModel(st, model)
if err != nil {
return nil, err
}

revisions, err := valsets.Revisions()
if err != nil {
for _, vs := range opts.ValidationSets {
valsets.Add(vs)
}

if err := valsets.Conflict(); err != nil {
return nil, err
}

model, err := findModel(st)
revisions, err := valsets.Revisions()
if err != nil {
return nil, err
}
Expand Down
170 changes: 167 additions & 3 deletions overlord/devicestate/devicestate_systems_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2957,6 +2957,106 @@ func (s *deviceMgrSystemsCreateSuite) TestDeviceManagerCreateRecoverySystemValid
c.Check(vSetErr.Snaps[fakeSnapID("pc")].Error(), Equals, `cannot constrain snap "pc" at different revisions 12 (canonical/vset-1), 13 (canonical/vset-2)`)
}

func (s *deviceMgrSystemsCreateSuite) TestDeviceManagerCreateRecoverySystemValidationSetsConflictWithModel(c *C) {
devicestate.SetBootOkRan(s.mgr, true)

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

s.model = s.makeModelAssertionInState(c, "canonical", "pc-20", map[string]interface{}{
"architecture": "amd64",
"grade": "dangerous",
"base": "core20",
"revision": "2",
"snaps": []interface{}{
map[string]interface{}{
"name": "pc-kernel",
"id": s.ss.AssertedSnapID("pc-kernel"),
"type": "kernel",
"default-channel": "20",
},
map[string]interface{}{
"name": "pc",
"id": s.ss.AssertedSnapID("pc"),
"type": "gadget",
"default-channel": "20",
},
map[string]interface{}{
"name": "core20",
"id": s.ss.AssertedSnapID("core20"),
"type": "base",
},
map[string]interface{}{
"name": "snapd",
"id": s.ss.AssertedSnapID("snapd"),
"type": "snapd",
},
},
"validation-sets": []interface{}{
map[string]interface{}{
"account-id": "canonical",
"name": "vset-model",
"mode": "enforce",
},
},
})

vsetModel, err := s.brands.Signing("canonical").Sign(asserts.ValidationSetType, map[string]interface{}{
"type": "validation-set",
"authority-id": "canonical",
"series": "16",
"account-id": "canonical",
"name": "vset-model",
"sequence": "1",
"snaps": []interface{}{
map[string]interface{}{
"name": "pc",
"id": fakeSnapID("pc"),
"revision": "12",
"presence": "required",
},
},
"timestamp": time.Now().UTC().Format(time.RFC3339),
}, nil, "")
c.Assert(err, IsNil)

assertstatetest.AddMany(s.state, vsetModel)
assertstate.UpdateValidationSet(s.state, &assertstate.ValidationSetTracking{
AccountID: "canonical",
Name: "vset-model",
Mode: assertstate.Enforce,
Current: 1,
})

vset1, err := s.brands.Signing("canonical").Sign(asserts.ValidationSetType, map[string]interface{}{
"type": "validation-set",
"authority-id": "canonical",
"series": "16",
"account-id": "canonical",
"name": "vset-1",
"sequence": "1",
"snaps": []interface{}{
map[string]interface{}{
"name": "pc",
"id": fakeSnapID("pc"),
"revision": "13",
"presence": "required",
},
},
"timestamp": time.Now().UTC().Format(time.RFC3339),
}, nil, "")
c.Assert(err, IsNil)

_, err = devicestate.CreateRecoverySystem(s.state, "1234", devicestate.CreateRecoverySystemOptions{
ValidationSets: []*asserts.ValidationSet{vset1.(*asserts.ValidationSet)},
})
c.Assert(err, testutil.ErrorIs, &snapasserts.ValidationSetsConflictError{})

vSetErr := &snapasserts.ValidationSetsConflictError{}
c.Check(errors.As(err, &vSetErr), Equals, true)
c.Check(vSetErr.Snaps[fakeSnapID("pc")].Error(), Equals, `cannot constrain snap "pc" at different revisions 12 (canonical/vset-model), 13 (canonical/vset-1)`)
}

func (s *deviceMgrSystemsCreateSuite) TestDeviceManagerCreateRecoverySystemNoTestSystemMarkCurrent(c *C) {
const markCurrent = true
s.testDeviceManagerCreateRecoverySystemNoTestSystem(c, markCurrent)
Expand Down Expand Up @@ -3067,6 +3167,73 @@ func (s *deviceMgrSystemsCreateSuite) TestDeviceManagerCreateRecoverySystemValid
func (s *deviceMgrSystemsCreateSuite) testDeviceManagerCreateRecoverySystemValidationSetsHappy(c *C, markCurrent bool) {
devicestate.SetBootOkRan(s.mgr, true)

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

s.model = s.makeModelAssertionInState(c, "canonical", "pc-20", map[string]interface{}{
"architecture": "amd64",
"grade": "dangerous",
"base": "core20",
"revision": "2",
"snaps": []interface{}{
map[string]interface{}{
"name": "pc-kernel",
"id": s.ss.AssertedSnapID("pc-kernel"),
"type": "kernel",
"default-channel": "20",
},
map[string]interface{}{
"name": "pc",
"id": s.ss.AssertedSnapID("pc"),
"type": "gadget",
"default-channel": "20",
},
map[string]interface{}{
"name": "core20",
"id": s.ss.AssertedSnapID("core20"),
"type": "base",
},
map[string]interface{}{
"name": "snapd",
"id": s.ss.AssertedSnapID("snapd"),
"type": "snapd",
},
},
"validation-sets": []interface{}{
map[string]interface{}{
"account-id": "canonical",
"name": "vset-model",
"mode": "enforce",
},
},
})

vsetModel, err := s.brands.Signing("canonical").Sign(asserts.ValidationSetType, map[string]interface{}{
"type": "validation-set",
"authority-id": "canonical",
"series": "16",
"account-id": "canonical",
"name": "vset-model",
"sequence": "1",
"snaps": []interface{}{
map[string]interface{}{
"name": "pc",
"id": fakeSnapID("pc"),
"presence": "required",
},
},
"timestamp": time.Now().UTC().Format(time.RFC3339),
}, nil, "")
c.Assert(err, IsNil)

assertstatetest.AddMany(s.state, vsetModel)
assertstate.UpdateValidationSet(s.state, &assertstate.ValidationSetTracking{
AccountID: "canonical",
Name: "vset-model",
Mode: assertstate.Enforce,
Current: 1,
})

snapRevisions := map[string]snap.Revision{
"pc": snap.R(10),
"pc-kernel": snap.R(11),
Expand Down Expand Up @@ -3211,9 +3378,6 @@ func (s *deviceMgrSystemsCreateSuite) testDeviceManagerCreateRecoverySystemValid
return ts, info, nil
})

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

s.state.Set("refresh-privacy-key", "some-privacy-key")
s.mockStandardSnapsModeenvAndBootloaderState(c)

Expand Down

0 comments on commit a1e658d

Please sign in to comment.