Skip to content

Commit dcb3525

Browse files
committed
many: add reprovision API
1 parent a817820 commit dcb3525

15 files changed

Lines changed: 1109 additions & 6 deletions

File tree

boot/assets.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,14 @@ func (b *bootAssetsImpl) UpdateBootEntry() error {
329329
return doUpdateBootEntry(b.bootLoader, b.updatedAssets)
330330
}
331331

332+
func GetTrustedAssetsFromModeenv(m *Modeenv) BootAssets {
333+
return &bootAssetsImpl{
334+
trackedAssets: m.CurrentTrustedBootAssets,
335+
trackedRecoveryAssets: m.CurrentTrustedRecoveryBootAssets,
336+
// No update of boot entry for now
337+
}
338+
}
339+
332340
type trustedAssetsInstallObserverImpl struct {
333341
model *asserts.Model
334342
gadgetDir string

daemon/api_systems.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ var systemsCmd = &Command{
5454
// this command, so we need to set the POST for this command to essentially
5555
// forward to that one
5656
POST: postSystemsAction,
57-
Actions: []string{"reboot", "create", "install", "fix-encryption-support", "generate-recovery-key"},
57+
Actions: []string{"reboot", "create", "install", "reprovision", "fix-encryption-support", "generate-recovery-key"},
5858
WriteAccess: rootAccess{},
5959
}
6060

@@ -217,6 +217,7 @@ var (
217217
devicestateRemoveRecoverySystem = devicestate.RemoveRecoverySystem
218218
devicestateGeneratePreInstallRecoveryKey = devicestate.GeneratePreInstallRecoveryKey
219219
devicestateGenerateReprovisionRecoveryKey = devicestate.GenerateReprovisionRecoveryKey
220+
devicestateReprovision = devicestate.Reprovision
220221
)
221222

222223
func getSystemDetails(c *Command, r *http.Request, user *auth.UserState) Response {
@@ -352,6 +353,11 @@ func postSystemsActionJSON(c *Command, r *http.Request) Response {
352353
return postSystemActionCheckPINQuality(c, systemLabel, &req)
353354
case "fix-encryption-support":
354355
return postSystemActionFixEncryptionSupport(c, systemLabel, &req)
356+
case "reprovision":
357+
if systemLabel != "" {
358+
return BadRequest("label should not be provided for reprovision action")
359+
}
360+
return postSystemActionReprovision(c, &req)
355361
case "generate-recovery-key":
356362
if systemLabel != "" {
357363
// SD201 says that generate-recovery-key should be an action for installation... but it is a step
@@ -849,6 +855,20 @@ func postSystemActionFixEncryptionSupport(c *Command, systemLabel string, req *s
849855
return SyncResponse(*details)
850856
}
851857

858+
func postSystemActionReprovision(c *Command, req *systemActionRequest) Response {
859+
st := c.d.overlord.State()
860+
st.Lock()
861+
defer st.Unlock()
862+
863+
chg, err := devicestateReprovision(st)
864+
if err != nil {
865+
return BadRequest("unexpected error: %s", err)
866+
}
867+
ensureStateSoon(st)
868+
869+
return AsyncResponse(nil, chg.ID())
870+
}
871+
852872
func postSystemActionGenerateRecoveryKey(c *Command, req *systemActionRequest) Response {
853873
st := c.d.overlord.State()
854874
st.Lock()

daemon/api_systems_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3464,6 +3464,66 @@ func (s *systemsCreateSuite) TestCreateSystemActionOfflineJustValidationSets(c *
34643464
c.Check(st.Change(res.Change), check.NotNil)
34653465
}
34663466

3467+
func (s *systemsSuite) TestSystemActionReprovision(c *check.C) {
3468+
s.mockSystemSeeds(c)
3469+
s.daemon(c)
3470+
s.expectRootAccess()
3471+
3472+
defer daemon.MockDevicestateReprovision(func(st *state.State) (*state.Change, error) {
3473+
return st.NewChange("reprovision", "..."), nil
3474+
})()
3475+
soon := 0
3476+
_, restore := daemon.MockEnsureStateSoon(func(st *state.State) {
3477+
soon++
3478+
})
3479+
defer restore()
3480+
3481+
body := map[string]any{
3482+
"action": "reprovision",
3483+
}
3484+
3485+
b, err := json.Marshal(body)
3486+
c.Assert(err, check.IsNil)
3487+
buf := bytes.NewBuffer(b)
3488+
3489+
req, err := http.NewRequest("POST", "/v2/systems", buf)
3490+
c.Assert(err, check.IsNil)
3491+
3492+
rsp := s.asyncReq(c, req, nil, actionIsExpected)
3493+
c.Check(soon, check.Equals, 1)
3494+
c.Check(rsp.Status, check.Equals, 202)
3495+
}
3496+
3497+
func (s *systemsSuite) TestSystemActionReprovisionError(c *check.C) {
3498+
s.mockSystemSeeds(c)
3499+
s.daemon(c)
3500+
s.expectRootAccess()
3501+
3502+
defer daemon.MockDevicestateReprovision(func(st *state.State) (*state.Change, error) {
3503+
return nil, fmt.Errorf("foo")
3504+
})()
3505+
soon := 0
3506+
_, restore := daemon.MockEnsureStateSoon(func(st *state.State) {
3507+
soon++
3508+
})
3509+
defer restore()
3510+
3511+
body := map[string]any{
3512+
"action": "reprovision",
3513+
}
3514+
3515+
b, err := json.Marshal(body)
3516+
c.Assert(err, check.IsNil)
3517+
buf := bytes.NewBuffer(b)
3518+
3519+
req, err := http.NewRequest("POST", "/v2/systems", buf)
3520+
c.Assert(err, check.IsNil)
3521+
3522+
rsp := s.errorReq(c, req, nil, actionIsExpected)
3523+
c.Check(soon, check.Equals, 0)
3524+
c.Check(rsp.Status, check.Equals, 400)
3525+
}
3526+
34673527
func (s *systemsSuite) TestSystemCurrentSystemGenerateRecoveryKey(c *check.C) {
34683528
if (keys.RecoveryKey{}).String() == "not-implemented" {
34693529
c.Skip("needs working secboot recovery key")

daemon/export_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,3 +463,7 @@ func ResetBuildIDDetection() {
463463
buildIDOnce = sync.Once{}
464464
buildID = "unknown"
465465
}
466+
467+
func MockDevicestateReprovision(f func(st *state.State) (*state.Change, error)) (restore func()) {
468+
return testutil.Mock(&devicestateReprovision, f)
469+
}

overlord/devicestate/devicemgr.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ func Manager(s *state.State, hookManager *hookstate.HookManager, runner *state.T
304304
runner.AddBlocked(gadgetUpdateBlocked)
305305
runner.AddBlocked(removeRecoverySystemBlocked)
306306

307+
runner.AddHandler("reprovision", m.doReprovision, nil)
308+
307309
// wire FDE kernel hook support into boot
308310
boot.HookKeyProtectorFactory = m.hookKeyProtectorFactory
309311
hookManager.Register(regexp.MustCompile("^fde-setup$"), newFdeSetupHandler)

overlord/devicestate/devicestate.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ var (
7878
installStepFinishChangeKind = swfeats.RegisterChangeKind("install-step-finish")
7979
installStepSetupStorageEncryptionChangeKind = swfeats.RegisterChangeKind("install-step-setup-storage-encryption")
8080
installStepTargetPreseedChangeKind = swfeats.RegisterChangeKind("install-step-preseed")
81+
reprovisionKind = swfeats.RegisterChangeKind("reprovision")
8182
)
8283

8384
// findModel returns the device model assertion.
@@ -2697,3 +2698,11 @@ func checkInstallChangeConflict(st *state.State) error {
26972698
}
26982699
return nil
26992700
}
2701+
2702+
func Reprovision(st *state.State) (*state.Change, error) {
2703+
chg := st.NewChange(reprovisionKind, fmt.Sprintf("Reprovision"))
2704+
reprovisionTask := st.NewTask("reprovision", fmt.Sprintf("Reprovision"))
2705+
chg.AddTask(reprovisionTask)
2706+
2707+
return chg, nil
2708+
}

overlord/devicestate/export_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/snapcore/snapd/osutil/keyboard"
3838
"github.com/snapcore/snapd/osutil/user"
3939
"github.com/snapcore/snapd/overlord/fdestate"
40+
fdeBackend "github.com/snapcore/snapd/overlord/fdestate/backend"
4041
"github.com/snapcore/snapd/overlord/snapstate"
4142
"github.com/snapcore/snapd/overlord/state"
4243
"github.com/snapcore/snapd/overlord/storecontext"
@@ -775,3 +776,78 @@ type ReprovisionSetupDataType = reprovisionSetupData
775776
func GetCachedReprovisionRecoveryKey(data *ReprovisionSetupDataType) *keys.RecoveryKey {
776777
return data.recoveryKey
777778
}
779+
780+
func MakeReprovisionSetupData(rkey *keys.RecoveryKey, checkContext *secboot.PreinstallCheckContext) *ReprovisionSetupDataType {
781+
return &reprovisionSetupData{
782+
recoveryKey: rkey,
783+
checkContext: checkContext,
784+
}
785+
}
786+
787+
func MockFdestateGetEncryptedContainers(f func(st *state.State) ([]fdeBackend.EncryptedContainer, error)) (restore func()) {
788+
old := fdestateGetEncryptedContainers
789+
fdestateGetEncryptedContainers = f
790+
return func() {
791+
fdestateGetEncryptedContainers = old
792+
}
793+
}
794+
795+
func DoReprovision(m *DeviceManager, t *state.Task) error {
796+
return m.doReprovision(t, nil)
797+
}
798+
799+
func MockSecbootListContainerUnlockKeyNames(f func(disk string) ([]string, error)) (restore func()) {
800+
return testutil.Mock(&secbootListContainerUnlockKeyNames, f)
801+
}
802+
803+
func MockSecbootTestProtectorKey(f func(ctx context.Context, disk string, keyName string, key []byte) (bool, error)) (restore func()) {
804+
return testutil.Mock(&secbootTestProtectorKey, f)
805+
}
806+
807+
func MockSecbootRenameContainerKey(f func(disk string, from, to string) error) (restore func()) {
808+
return testutil.Mock(&secbootRenameContainerKey, f)
809+
}
810+
811+
func MockSecbootGetPCRHandleFromToken(f func(disk string, name string) (uint32, error)) (restore func()) {
812+
return testutil.Mock(&secbootGetPCRHandleFromToken, f)
813+
}
814+
815+
func MockSecbootReleasePCRResourceHandle(f func(nv uint32) error) (restore func()) {
816+
return testutil.Mock(&secbootReleasePCRResourceHandle, f)
817+
}
818+
819+
func MockSecbootDeleteContainerKey(f func(disk string, name string) error) (restore func()) {
820+
return testutil.Mock(&secbootDeleteContainerKey, f)
821+
}
822+
823+
func MockBootMakeRunnableReprovision(f func(model *asserts.Model, protector secboot.KeyProtectorFactory, encryption *boot.EncryptionSetup) error) (restore func()) {
824+
return testutil.Mock(&bootMakeRunnableReprovision, f)
825+
}
826+
827+
func MockSecbootListContainerRecoveryKeyNames(f func(disk string) ([]string, error)) (restore func()) {
828+
return testutil.Mock(&secbootListContainerRecoveryKeyNames, f)
829+
}
830+
831+
func MockSecbootSaveCheckResult(f func(pcc *secboot.PreinstallCheckContext, filename string) error) (restore func()) {
832+
return testutil.Mock(&secbootSaveCheckResult, f)
833+
}
834+
835+
func MockSecbootCheckResult(f func(pcc *secboot.PreinstallCheckContext) (*secboot.PreinstallCheckResult, error)) (restore func()) {
836+
return testutil.Mock(&secbootCheckResult, f)
837+
}
838+
839+
func MockSnapstateKernelInfo(f func(st *state.State, deviceCtx snapstate.DeviceContext) (*snap.Info, error)) (restore func()) {
840+
return testutil.Mock(&snapstateKernelInfo, f)
841+
}
842+
843+
func MockKeysNewProtectorKey(f func() (keys.ProtectorKey, error)) (restore func()) {
844+
return testutil.Mock(&keysNewProtectorKey, f)
845+
}
846+
847+
func MockKeysCreateProtectedKey(f func(k keys.ProtectorKey, primaryKey []byte) (*keys.PlainKey, []byte, []byte, error)) (restore func()) {
848+
return testutil.Mock(&keysCreateProtectedKey, f)
849+
}
850+
851+
func MockKeysPlainKeyWrite(f func(key *keys.PlainKey, writer keys.KeyDataWriter) error) (restore func()) {
852+
return testutil.Mock(&keysPlainKeyWrite, f)
853+
}

0 commit comments

Comments
 (0)