diff options
| author | Samuele Pedroni <pedronis@lucediurna.net> | 2019-08-23 12:03:26 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-08-23 12:03:26 +0200 |
| commit | ac8d580a7bdf7489261f6cbde52ca597cb4351e2 (patch) | |
| tree | 29701b095891617bb0205790b9a656010ed4e9f1 | |
| parent | 8307c94d7b17d59dec83b1aa7162b3b0c7a58e34 (diff) | |
| parent | 73ecfb35d3f78fa2d26e72f4dc7ddd9393c29ccb (diff) | |
snap/naming: introduce SnapRef, Snap, and SnapSet
This introduces the concept of a SnapRef(erence) with a new interface. The main goal here is to be able to write code that can work both in cases where a snap id is known for some snaps or the name only. This will now be the case for code that deals with model assertions, so far they carried only names but for Core 20 they will carry snap ids for the grade stable at least. This is introduces also SnapSet to be able to work with a set of these.
| -rw-r--r-- | snap/naming/snapref.go | 129 | ||||
| -rw-r--r-- | snap/naming/snapref_test.go | 89 | ||||
| -rw-r--r-- | snap/naming/validate.go | 2 |
3 files changed, 219 insertions, 1 deletions
diff --git a/snap/naming/snapref.go b/snap/naming/snapref.go new file mode 100644 index 0000000000..60e2150ead --- /dev/null +++ b/snap/naming/snapref.go @@ -0,0 +1,129 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2019 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package naming + +// A SnapRef references a snap by name and/or id. +type SnapRef interface { + SnapName() string + ID() string +} + +// Snap references a snap by name only. +type Snap string + +func (s Snap) SnapName() string { + return string(s) +} + +func (s Snap) ID() string { + return "" +} + +type snapRef struct { + name string + id string +} + +// NewSnapRef returns a reference to the snap with given name and id. +func NewSnapRef(name, id string) SnapRef { + return &snapRef{name: name, id: id} +} + +func (r *snapRef) SnapName() string { + return r.name +} + +func (r *snapRef) ID() string { + return r.id +} + +// SameSnap returns whether the two arguments refer to the same snap. +// If ids are not available for both it will fallback to names. +func SameSnap(snapRef1, snapRef2 SnapRef) bool { + id1 := snapRef1.ID() + id2 := snapRef2.ID() + if id1 != "" && id2 != "" { + return id1 == id2 + } + return snapRef1.SnapName() == snapRef2.SnapName() +} + +// SnapSet can hold a set of references to snaps. +type SnapSet struct { + byID map[string]SnapRef + byNameWithID map[string]SnapRef + byNameOnly map[string]SnapRef +} + +// NewSnapSet builds a snap set with the given references. +func NewSnapSet(refs []SnapRef) *SnapSet { + s := &SnapSet{ + byID: make(map[string]SnapRef), + byNameWithID: make(map[string]SnapRef), + byNameOnly: make(map[string]SnapRef), + } + for _, r := range refs { + s.Add(r) + } + return s +} + +// Lookup finds the reference in the set matching the given one if any. +func (s *SnapSet) Lookup(which SnapRef) SnapRef { + whichID := which.ID() + name := which.SnapName() + if whichID != "" { + if ref := s.byID[whichID]; ref != nil { + return ref + } + } else { + if ref := s.byNameWithID[name]; ref != nil { + return ref + } + } + return s.byNameOnly[name] +} + +// Contains returns whether the set has a matching reference already. +func (s *SnapSet) Contains(ref SnapRef) bool { + return s.Lookup(ref) != nil +} + +// Add adds one reference to the set. +// Already added ids or names will be ignored. The assumption is that +// a SnapSet is populated with distinct snaps. +func (s *SnapSet) Add(ref SnapRef) { + if s.Contains(ref) { + // nothing to do + return + } + id := ref.ID() + if id != "" { + s.byID[id] = ref + } + name := ref.SnapName() + if name != "" { + if id != "" { + s.byNameWithID[name] = ref + } else { + s.byNameOnly[name] = ref + } + } +} diff --git a/snap/naming/snapref_test.go b/snap/naming/snapref_test.go new file mode 100644 index 0000000000..ceadde0301 --- /dev/null +++ b/snap/naming/snapref_test.go @@ -0,0 +1,89 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2019 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package naming_test + +import ( + . "gopkg.in/check.v1" + + "github.com/snapcore/snapd/snap/naming" +) + +type snapRefSuite struct{} + +var _ = Suite(&snapRefSuite{}) + +func (s *snapRefSuite) TestNewSnapRef(c *C) { + fooRef := naming.NewSnapRef("foo", "foo-id") + c.Check(fooRef.SnapName(), Equals, "foo") + c.Check(fooRef.ID(), Equals, "foo-id") + + fooNameOnlyRef := naming.NewSnapRef("foo", "") + c.Check(fooNameOnlyRef.SnapName(), Equals, "foo") + c.Check(fooNameOnlyRef.ID(), Equals, "") +} + +func (s *snapRefSuite) TestSnap(c *C) { + fooNameOnlyRef := naming.Snap("foo") + c.Check(fooNameOnlyRef.SnapName(), Equals, "foo") + c.Check(fooNameOnlyRef.ID(), Equals, "") +} + +func (s *snapRefSuite) TestSameSnap(c *C) { + fooRef := naming.NewSnapRef("foo", "foo-id") + fooNameOnlyRef := naming.NewSnapRef("foo", "") + altFooRef := naming.NewSnapRef("foo-proj", "foo-id") + barNameOnylRef := naming.NewSnapRef("bar", "") + unrelFooRef := naming.NewSnapRef("foo", "unrel-id") + + c.Check(naming.SameSnap(fooRef, altFooRef), Equals, true) + c.Check(naming.SameSnap(fooRef, fooNameOnlyRef), Equals, true) + c.Check(naming.SameSnap(altFooRef, fooNameOnlyRef), Equals, false) + c.Check(naming.SameSnap(unrelFooRef, fooRef), Equals, false) + c.Check(naming.SameSnap(fooRef, barNameOnylRef), Equals, false) + // weakness but expected + c.Check(naming.SameSnap(unrelFooRef, fooNameOnlyRef), Equals, true) +} + +func (s *snapRefSuite) TestSnapSet(c *C) { + ss := naming.NewSnapSet(nil) + fooRef := naming.NewSnapRef("foo", "foo-id") + fooNameOnlyRef := naming.Snap("foo") + + ss.Add(fooRef) + ss.Add(fooNameOnlyRef) + + altFooRef := naming.NewSnapRef("foo-proj", "foo-id") + c.Check(ss.Lookup(fooRef), Equals, fooRef) + c.Check(ss.Lookup(fooNameOnlyRef), Equals, fooRef) + c.Check(ss.Lookup(altFooRef), Equals, fooRef) + + barNameOnylRef := naming.NewSnapRef("bar", "") + unrelFooRef := naming.NewSnapRef("foo", "unrel-id") + c.Check(ss.Lookup(barNameOnylRef), Equals, nil) + c.Check(ss.Lookup(unrelFooRef), Equals, nil) + + // weaker behavior but expected + ss1 := naming.NewSnapSet([]naming.SnapRef{fooNameOnlyRef}) + ss1.Add(fooRef) + c.Check(ss1.Lookup(fooRef), Equals, fooNameOnlyRef) + c.Check(ss1.Lookup(altFooRef), Equals, nil) + c.Check(ss1.Lookup(barNameOnylRef), Equals, nil) + c.Check(ss1.Lookup(unrelFooRef), Equals, fooNameOnlyRef) +} diff --git a/snap/naming/validate.go b/snap/naming/validate.go index 69d2299222..b739a8cb77 100644 --- a/snap/naming/validate.go +++ b/snap/naming/validate.go @@ -17,7 +17,7 @@ * */ -// Package naming implements naming constraints for snaps and their elements. +// Package naming implements naming constraints and concepts for snaps and their elements. package naming import ( |
