From 5ec9c16ff7235f0cb22bf9c024d66f4f9b53d0c3 Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Thu, 27 Oct 2016 14:13:29 -0200 Subject: [PATCH 1/2] overlord/snapstate: add dynamic snapdX.Y assumes --- cmd/version.go | 6 ++ overlord/snapstate/check_snap.go | 49 ++++++++++- overlord/snapstate/check_snap_test.go | 114 +++++++++++++++++++------- 3 files changed, 138 insertions(+), 31 deletions(-) diff --git a/cmd/version.go b/cmd/version.go index 224cedf7901..f75f31ab26b 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -23,3 +23,9 @@ package cmd // Version will be overwritten at build-time via mkversion.sh var Version = "unknown" + +func MockVersion(version string) (restore func()) { + old := Version + Version = version + return func() { Version = old } +} diff --git a/overlord/snapstate/check_snap.go b/overlord/snapstate/check_snap.go index 7da10c29394..db6b412ee46 100644 --- a/overlord/snapstate/check_snap.go +++ b/overlord/snapstate/check_snap.go @@ -21,9 +21,12 @@ package snapstate import ( "fmt" + "regexp" + "strconv" "strings" "github.com/snapcore/snapd/arch" + "github.com/snapcore/snapd/cmd" "github.com/snapcore/snapd/overlord/snapstate/backend" "github.com/snapcore/snapd/overlord/state" "github.com/snapcore/snapd/release" @@ -42,16 +45,60 @@ var featureSet = map[string]bool{ func checkAssumes(s *snap.Info) error { missing := ([]string)(nil) for _, flag := range s.Assumes { + if strings.HasPrefix(flag, "snapd") && checkVersion(flag[5:]) { + continue + } if !featureSet[flag] { missing = append(missing, flag) } } if len(missing) > 0 { - return fmt.Errorf("snap %q assumes unsupported features: %s (try new ubuntu-core)", s.Name(), strings.Join(missing, ", ")) + hint := "try to refresh the core snap" + if release.OnClassic { + hint = "try to update snapd and refresh the core snap" + } + return fmt.Errorf("snap %q assumes unsupported features: %s (%s)", s.Name(), strings.Join(missing, ", "), hint) } return nil } +var versionExp = regexp.MustCompile(`^([1-9][0-9]*)(?:\.([0-9]+)(?:\.([0-9]+))?)?`) + +func checkVersion(version string) bool { + req := versionExp.FindStringSubmatch(version) + if req == nil { + return false + } + + if cmd.Version == "unknown" { + return true // Development tree. + } + + cur := versionExp.FindStringSubmatch(cmd.Version) + if cur == nil { + return false + } + + for i := 1; i < len(req); i++ { + if req[i] == "" { + return true + } + if cur[i] == "" { + return false + } + reqN, err1 := strconv.Atoi(req[i]) + curN, err2 := strconv.Atoi(cur[i]) + if err1 != nil || err2 != nil { + panic("internal error: version regexp is broken") + } + if curN != reqN { + return curN > reqN + } + } + + return true +} + var openSnapFile = backend.OpenSnapFile // checkSnap ensures that the snap can be installed. diff --git a/overlord/snapstate/check_snap_test.go b/overlord/snapstate/check_snap_test.go index f56fb95c824..c9ad1b43381 100644 --- a/overlord/snapstate/check_snap_test.go +++ b/overlord/snapstate/check_snap_test.go @@ -26,6 +26,7 @@ import ( . "gopkg.in/check.v1" "github.com/snapcore/snapd/arch" + "github.com/snapcore/snapd/cmd" "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/overlord/state" "github.com/snapcore/snapd/release" @@ -74,40 +75,93 @@ architectures: c.Assert(err.Error(), Equals, errorMsg) } -func (s *checkSnapSuite) TestCheckSnapInstallMissingAssumes(c *C) { - const yaml = `name: foo -version: 1.0 -assumes: [f1, f2]` - - info, err := snap.InfoFromSnapYaml([]byte(yaml)) - c.Assert(err, IsNil) - - var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { - return info, nil, nil - } - restore := snapstate.MockOpenSnapFile(openSnapFile) +var assumesTests = []struct { + version string + assumes string + classic bool + error string +}{{ + assumes: "[common-data-dir]", +}, { + assumes: "[f1, f2]", + error: `snap "foo" assumes unsupported features: f1, f2 \(try to refresh the core snap\)`, +}, { + assumes: "[f1, f2]", + classic: true, + error: `snap "foo" assumes unsupported features: f1, f2 \(try to update snapd and refresh the core snap\)`, +}, { + assumes: "[snapd2.15]", + version: "unknown", +}, { + assumes: "[snapdnono]", + version: "unknown", + error: `.* unsupported features: snapdnono .*`, +}, { + assumes: "[snapd2.15]", + version: "2.15", +}, { + assumes: "[snapd2.15]", + version: "2.15.1", +}, { + assumes: "[snapd2.15]", + version: "2.15+git", +}, { + assumes: "[snapd2.15]", + version: "2.16", +}, { + assumes: "[snapd2.15.1]", + version: "2.16", +}, { + assumes: "[snapd2.15.2]", + version: "2.16.1", +}, { + assumes: "[snapd3]", + version: "3.1", +}, { + assumes: "[snapd2.16]", + version: "2.15", + error: `.* unsupported features: snapd2\.16 .*`, +}, { + assumes: "[snapd2.15.1]", + version: "2.15", + error: `.* unsupported features: snapd2\.15\.1 .*`, +}, { + assumes: "[snapd2.15.1]", + version: "2.15.0", + error: `.* unsupported features: snapd2\.15\.1 .*`, +}} + +func (s *checkSnapSuite) TestCheckSnapAssumes(c *C) { + restore := cmd.MockVersion("2.15") defer restore() - err = snapstate.CheckSnap(s.st, "snap-path", nil, nil, snapstate.Flags{}) - c.Check(err, ErrorMatches, `snap "foo" assumes unsupported features: f1, f2.*`) -} - -func (s *checkSnapSuite) TestCheckSnapInstallProvidedAssumes(c *C) { - const yaml = `name: foo -version: 1.0 -assumes: [common-data-dir]` - - info, err := snap.InfoFromSnapYaml([]byte(yaml)) - c.Assert(err, IsNil) - - var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { - return info, nil, nil - } - restore := snapstate.MockOpenSnapFile(openSnapFile) + restore = release.MockOnClassic(false) defer restore() - err = snapstate.CheckSnap(s.st, "snap-path", nil, nil, snapstate.Flags{}) - c.Check(err, IsNil) + for _, test := range assumesTests { + cmd.Version = test.version + if cmd.Version == "" { + cmd.Version = "2.15" + } + release.OnClassic = test.classic + + yaml := fmt.Sprintf("name: foo\nversion: 1.0\nassumes: %s\n", test.assumes) + + info, err := snap.InfoFromSnapYaml([]byte(yaml)) + c.Assert(err, IsNil) + + var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { + return info, nil, nil + } + restore := snapstate.MockOpenSnapFile(openSnapFile) + defer restore() + err = snapstate.CheckSnap(s.st, "snap-path", nil, nil, snapstate.Flags{}) + if test.error != "" { + c.Check(err, ErrorMatches, test.error) + } else { + c.Assert(err, IsNil) + } + } } func (s *checkSnapSuite) TestCheckSnapCheckCallbackOK(c *C) { From 29582cbbccf0d31766101f17ac8cdbc45c03528a Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Thu, 27 Oct 2016 14:28:41 -0200 Subject: [PATCH 2/2] Minor fix. --- overlord/snapstate/check_snap.go | 2 +- overlord/snapstate/check_snap_test.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/overlord/snapstate/check_snap.go b/overlord/snapstate/check_snap.go index db6b412ee46..85ad5a56197 100644 --- a/overlord/snapstate/check_snap.go +++ b/overlord/snapstate/check_snap.go @@ -66,7 +66,7 @@ var versionExp = regexp.MustCompile(`^([1-9][0-9]*)(?:\.([0-9]+)(?:\.([0-9]+))?) func checkVersion(version string) bool { req := versionExp.FindStringSubmatch(version) - if req == nil { + if req == nil || req[0] != version { return false } diff --git a/overlord/snapstate/check_snap_test.go b/overlord/snapstate/check_snap_test.go index c9ad1b43381..cb8fa116e68 100644 --- a/overlord/snapstate/check_snap_test.go +++ b/overlord/snapstate/check_snap_test.go @@ -96,6 +96,10 @@ var assumesTests = []struct { assumes: "[snapdnono]", version: "unknown", error: `.* unsupported features: snapdnono .*`, +}, { + assumes: "[snapd2.15nono]", + version: "unknown", + error: `.* unsupported features: snapd2.15nono .*`, }, { assumes: "[snapd2.15]", version: "2.15",