Skip to content

Commit

Permalink
snap-repair: add uc20 support
Browse files Browse the repository at this point in the history
* snap-repair: minimal uc20 support

This is a minimal change for snap-repair to support uc20. The tests
require a rework, ideally we would run all tests for a uc16 and
a uc20 model.

* snap-repair: improve tests by checking that we write a valid modeenv

Thanks to Ian.

* tests: move uc20 snap-repair test into tests/main/snap-repair

* snap-reapir: extract uc16/uc18 specific tests into runner16Suite

This commit keeps the version independent tests in runnerSuite.
It also uses testutil.BaseTest to simplify things.

* snap-repair: refactor tests and add uc20 TestLoadStateInitDeviceInfoFail

The tests that deal with initState/writeSeedAssert now run with
both uc16/uc20 where it makes sense.

A new test runner20Suite.TestLoadStateInitDeviceInfoFail that checks
the failure conditions for modeenv reading is also added.

* snap-repair: simplify unit tests some more

* snap-repair: use goconfigparser to get model from modeenv

* snap-repair: use systems/*/model as source for findTimeLowerBound()

* snap-repair: tweak findBrandAndModel16 helper

Co-authored-by: Maciej Borzecki <maciek.borzecki@gmail.com>

* cmd/snap-repair: tweak model variable name, fix build

Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>

Co-authored-by: Maciej Borzecki <maciek.borzecki@gmail.com>
Co-authored-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
  • Loading branch information
3 people committed Sep 29, 2020
1 parent 84c33a3 commit 882d37e
Show file tree
Hide file tree
Showing 3 changed files with 323 additions and 160 deletions.
96 changes: 77 additions & 19 deletions cmd/snap-repair/runner.go
Expand Up @@ -38,6 +38,7 @@ import (
"syscall"
"time"

"github.com/mvo5/goconfigparser"
"gopkg.in/retry.v1"

"github.com/snapcore/snapd/arch"
Expand Down Expand Up @@ -575,11 +576,9 @@ func (run *Runner) initState() error {
os.Remove(dirs.SnapRepairStateFile)
run.state = state{}
// initialize time lower bound with image built time/seed.yaml time
info, err := os.Stat(filepath.Join(dirs.SnapSeedDir, "seed.yaml"))
if err != nil {
if err := run.findTimeLowerBound(); err != nil {
return err
}
run.moveTimeLowerBound(info.ModTime())
// initialize device info
if err := run.initDeviceInfo(); err != nil {
return err
Expand Down Expand Up @@ -655,16 +654,67 @@ func verifySignatures(a asserts.Assertion, workBS asserts.Backstore, trusted ass
return nil
}

func (run *Runner) initDeviceInfo() error {
const errPrefix = "cannot set device information: "
func (run *Runner) findTimeLowerBound() error {
timeLowerBoundSources := []string{
// uc16
filepath.Join(dirs.SnapSeedDir, "seed.yaml"),
// uc20+
dirs.SnapModeenvFile,
}
// add all model files from uc20 seeds
allModels, err := filepath.Glob(filepath.Join(dirs.SnapSeedDir, "systems/*/model"))
if err != nil {
return err
}
timeLowerBoundSources = append(timeLowerBoundSources, allModels...)

// use all files as potential time inputs
for _, p := range timeLowerBoundSources {
info, err := os.Stat(p)
if os.IsNotExist(err) {
continue
}
if err != nil {
return err
}
run.moveTimeLowerBound(info.ModTime())
}
return nil
}

func findBrandAndModel() (string, string, error) {
if osutil.FileExists(dirs.SnapModeenvFile) {
return findBrandAndModel20()
}
return findBrandAndModel16()
}

func findBrandAndModel20() (brand, model string, err error) {
cfg := goconfigparser.New()
cfg.AllowNoSectionHeader = true
if err := cfg.ReadFile(dirs.SnapModeenvFile); err != nil {
return "", "", err
}
brandAndModel, err := cfg.Get("", "model")
if err != nil {
return "", "", err
}
l := strings.SplitN(brandAndModel, "/", 2)
if len(l) != 2 {
return "", "", fmt.Errorf("cannot find brand/model in modeenv model string %q", brandAndModel)
}

return l[0], l[1], nil
}

func findBrandAndModel16() (brand, model string, err error) {
workBS := asserts.NewMemoryBackstore()
assertSeedDir := filepath.Join(dirs.SnapSeedDir, "assertions")
dc, err := ioutil.ReadDir(assertSeedDir)
if err != nil {
return err
return "", "", err
}
var model *asserts.Model
var modelAs *asserts.Model
for _, fi := range dc {
fn := filepath.Join(assertSeedDir, fi.Name())
f, err := os.Open(fn)
Expand All @@ -681,37 +731,45 @@ func (run *Runner) initDeviceInfo() error {
}
switch a.Type() {
case asserts.ModelType:
if model != nil {
return fmt.Errorf(errPrefix + "multiple models in seed assertions")
if modelAs != nil {
return "", "", fmt.Errorf("multiple models in seed assertions")
}
model = a.(*asserts.Model)
modelAs = a.(*asserts.Model)
case asserts.AccountType, asserts.AccountKeyType:
workBS.Put(a.Type(), a)
}
}
}
if model == nil {
return fmt.Errorf(errPrefix + "no model assertion in seed data")
if modelAs == nil {
return "", "", fmt.Errorf("no model assertion in seed data")
}
trustedBS := trustedBackstore(sysdb.Trusted())
if err := verifySignatures(model, workBS, trustedBS); err != nil {
return fmt.Errorf(errPrefix+"%v", err)
if err := verifySignatures(modelAs, workBS, trustedBS); err != nil {
return "", "", err
}
acctPK := []string{model.BrandID()}
acctPK := []string{modelAs.BrandID()}
acctMaxSupFormat := asserts.AccountType.MaxSupportedFormat()
acct, err := trustedBS.Get(asserts.AccountType, acctPK, acctMaxSupFormat)
if err != nil {
var err error
acct, err = workBS.Get(asserts.AccountType, acctPK, acctMaxSupFormat)
if err != nil {
return fmt.Errorf(errPrefix + "no brand account assertion in seed data")
return "", "", fmt.Errorf("no brand account assertion in seed data")
}
}
if err := verifySignatures(acct, workBS, trustedBS); err != nil {
return fmt.Errorf(errPrefix+"%v", err)
return "", "", err
}
return modelAs.BrandID(), modelAs.Model(), nil
}

func (run *Runner) initDeviceInfo() error {
brandID, model, err := findBrandAndModel()
if err != nil {
return fmt.Errorf("cannot set device information: %v", err)
}
run.state.Device.Brand = model.BrandID()
run.state.Device.Model = model.Model()
run.state.Device.Brand = brandID
run.state.Device.Model = model
return nil
}

Expand Down

0 comments on commit 882d37e

Please sign in to comment.