Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
Already on GitHub? Sign in to your account
interfaces: sanitize plugs and slots early in ReadInfo #3972
4df9f2c
c700354
c252626
eabdbb0
b69d70e
6231016
46c79ae
cfd3b56
ecff4ee
3196ae3
6e2c059
8353ae8
efa9bf8
5e52cf1
40cd900
85124e7
| @@ -20,7 +20,6 @@ | ||
| package interfaces | ||
| import ( | ||
| - "bytes" | ||
| "fmt" | ||
| "sort" | ||
| "strings" | ||
| @@ -46,14 +45,18 @@ type Repository struct { | ||
| // NewRepository creates an empty plug repository. | ||
| func NewRepository() *Repository { | ||
| - return &Repository{ | ||
| + repo := &Repository{ | ||
| ifaces: make(map[string]Interface), | ||
| plugs: make(map[string]map[string]*Plug), | ||
| slots: make(map[string]map[string]*Slot), | ||
| slotPlugs: make(map[*Slot]map[*Plug]*Connection), | ||
| plugSlots: make(map[*Plug]map[*Slot]*Connection), | ||
| backends: make(map[SecuritySystem]SecurityBackend), | ||
| } | ||
| + snap.SanitizePlugsSlots = func(snapInfo *snap.Info) error { | ||
stolowski
Contributor
|
||
| + return repo.SanitizePlugsSlots(snapInfo) | ||
| + } | ||
| + return repo | ||
| } | ||
| // Interface returns an interface with a given name. | ||
| @@ -815,37 +818,49 @@ func (r *Repository) SnapSpecification(securitySystem SecuritySystem, snapName s | ||
| return spec, nil | ||
| } | ||
| -// BadInterfacesError is returned when some snap interfaces could not be registered. | ||
| -// Those interfaces not mentioned in the error were successfully registered. | ||
| -type BadInterfacesError struct { | ||
| - snap string | ||
| - issues map[string]string // slot or plug name => message | ||
| -} | ||
| +func (r *Repository) SanitizePlugsSlots(snapInfo *snap.Info) error { | ||
| + err := snap.Validate(snapInfo) | ||
zyga
Contributor
|
||
| + if err != nil { | ||
| + return err | ||
| + } | ||
| -func (e *BadInterfacesError) Error() string { | ||
| - inverted := make(map[string][]string) | ||
| - for name, reason := range e.issues { | ||
| - inverted[reason] = append(inverted[reason], name) | ||
| - } | ||
| - var buf bytes.Buffer | ||
| - fmt.Fprintf(&buf, "snap %q has bad plugs or slots: ", e.snap) | ||
| - reasons := make([]string, 0, len(inverted)) | ||
| - for reason := range inverted { | ||
| - reasons = append(reasons, reason) | ||
| - } | ||
| - sort.Strings(reasons) | ||
| - for _, reason := range reasons { | ||
| - names := inverted[reason] | ||
| - sort.Strings(names) | ||
| - for i, name := range names { | ||
| - if i > 0 { | ||
| - buf.WriteString(", ") | ||
| - } | ||
| - buf.WriteString(name) | ||
| + for plugName, plugInfo := range snapInfo.Plugs { | ||
| + iface, ok := r.ifaces[plugInfo.Interface] | ||
| + if !ok { | ||
| + snapInfo.BadInterfaces[plugName] = "unknown interface" | ||
| + continue | ||
| + } | ||
| + // Reject plug with invalid name | ||
| + if err := ValidateName(plugName); err != nil { | ||
| + snapInfo.BadInterfaces[plugName] = err.Error() | ||
| + continue | ||
| + } | ||
| + plug := &Plug{PlugInfo: plugInfo} | ||
| + if err := plug.Sanitize(iface); err != nil { | ||
| + snapInfo.BadInterfaces[plugName] = err.Error() | ||
| + continue | ||
| + } | ||
| + } | ||
| + | ||
| + for slotName, slotInfo := range snapInfo.Slots { | ||
| + iface, ok := r.ifaces[slotInfo.Interface] | ||
| + if !ok { | ||
| + snapInfo.BadInterfaces[slotName] = "unknown interface" | ||
| + continue | ||
| + } | ||
| + // Reject slot with invalid name | ||
| + if err := ValidateName(slotName); err != nil { | ||
| + snapInfo.BadInterfaces[slotName] = err.Error() | ||
| + continue | ||
| + } | ||
| + slot := &Slot{SlotInfo: slotInfo} | ||
| + if err := slot.Sanitize(iface); err != nil { | ||
| + snapInfo.BadInterfaces[slotName] = err.Error() | ||
| + continue | ||
| } | ||
| - fmt.Fprintf(&buf, " (%s); ", reason) | ||
| } | ||
| - return strings.TrimSuffix(buf.String(), "; ") | ||
| + | ||
| + return nil | ||
| } | ||
| // AddSnap adds plugs and slots declared by the given snap to the repository. | ||
| @@ -862,11 +877,6 @@ func (e *BadInterfacesError) Error() string { | ||
| // Unknown interfaces and plugs/slots that don't validate are not added. | ||
| // Information about those failures are returned to the caller. | ||
| func (r *Repository) AddSnap(snapInfo *snap.Info) error { | ||
| - err := snap.Validate(snapInfo) | ||
zyga
Contributor
|
||
| - if err != nil { | ||
| - return err | ||
| - } | ||
| - | ||
| r.m.Lock() | ||
| defer r.m.Unlock() | ||
| @@ -876,58 +886,21 @@ func (r *Repository) AddSnap(snapInfo *snap.Info) error { | ||
| return fmt.Errorf("cannot register interfaces for snap %q more than once", snapName) | ||
| } | ||
| - bad := BadInterfacesError{ | ||
| - snap: snapName, | ||
| - issues: make(map[string]string), | ||
| - } | ||
| - | ||
| for plugName, plugInfo := range snapInfo.Plugs { | ||
| - iface, ok := r.ifaces[plugInfo.Interface] | ||
| - if !ok { | ||
| - bad.issues[plugName] = "unknown interface" | ||
| - continue | ||
| - } | ||
| - // Reject plug with invalid name | ||
| - if err := ValidateName(plugName); err != nil { | ||
| - bad.issues[plugName] = err.Error() | ||
| - continue | ||
| - } | ||
| - plug := &Plug{PlugInfo: plugInfo} | ||
| - if err := plug.Sanitize(iface); err != nil { | ||
| - bad.issues[plugName] = err.Error() | ||
| - continue | ||
| - } | ||
| if r.plugs[snapName] == nil { | ||
| r.plugs[snapName] = make(map[string]*Plug) | ||
| } | ||
| + plug := &Plug{PlugInfo: plugInfo} | ||
| r.plugs[snapName][plugName] = plug | ||
| } | ||
| for slotName, slotInfo := range snapInfo.Slots { | ||
| - iface, ok := r.ifaces[slotInfo.Interface] | ||
| - if !ok { | ||
| - bad.issues[slotName] = "unknown interface" | ||
| - continue | ||
| - } | ||
| - // Reject slot with invalid name | ||
| - if err := ValidateName(slotName); err != nil { | ||
| - bad.issues[slotName] = err.Error() | ||
| - continue | ||
| - } | ||
| - slot := &Slot{SlotInfo: slotInfo} | ||
| - if err := slot.Sanitize(iface); err != nil { | ||
| - bad.issues[slotName] = err.Error() | ||
| - continue | ||
| - } | ||
| if r.slots[snapName] == nil { | ||
| r.slots[snapName] = make(map[string]*Slot) | ||
| } | ||
| + slot := &Slot{SlotInfo: slotInfo} | ||
| r.slots[snapName][slotName] = slot | ||
| } | ||
| - | ||
| - if len(bad.issues) > 0 { | ||
| - return &bad | ||
| - } | ||
| return nil | ||
| } | ||
| @@ -1539,7 +1539,7 @@ func (s *AddRemoveSuite) SetUpTest(c *C) { | ||
| c.Assert(err, IsNil) | ||
| } | ||
| -func (s *AddRemoveSuite) TestAddSnapComplexErrorHandling(c *C) { | ||
| +/*func (s *AddRemoveSuite) TestAddSnapComplexErrorHandling(c *C) { | ||
zyga
Contributor
|
||
| err := s.repo.AddInterface(&ifacetest.TestInterface{ | ||
| InterfaceName: "invalid-plug-iface", | ||
| SanitizePlugCallback: func(plug *Plug) error { return fmt.Errorf("plug is invalid") }, | ||
| @@ -1569,7 +1569,7 @@ slots: | ||
| c.Check(s.repo.Plug("complex", "unknown-plug-iface"), IsNil) | ||
| c.Check(s.repo.Slot("complex", "invalid-slot-iface"), IsNil) | ||
| c.Check(s.repo.Slot("complex", "unknown-slot-iface"), IsNil) | ||
| -} | ||
| +}*/ | ||
| const testConsumerYaml = ` | ||
| name: consumer | ||
| @@ -1616,18 +1616,6 @@ func (s *AddRemoveSuite) TestAddSnapAddsPlugs(c *C) { | ||
| c.Assert(s.repo.Plug("consumer", "iface"), Not(IsNil)) | ||
| } | ||
| -func (s *AddRemoveSuite) TestAddSnapErrorsOnInvalidSlotNames(c *C) { | ||
| - _, err := s.addSnap(c, testConsumerInvalidSlotNameYaml) | ||
| - c.Assert(err, NotNil) | ||
| - c.Check(err, ErrorMatches, `snap "consumer" has bad plugs or slots: ttyS5 \(invalid interface name: "ttyS5"\)`) | ||
| -} | ||
| - | ||
| -func (s *AddRemoveSuite) TestAddSnapErrorsOnInvalidPlugNames(c *C) { | ||
| - _, err := s.addSnap(c, testConsumerInvalidPlugNameYaml) | ||
| - c.Assert(err, NotNil) | ||
| - c.Check(err, ErrorMatches, `snap "consumer" has bad plugs or slots: ttyS3 \(invalid interface name: "ttyS3"\)`) | ||
| -} | ||
| - | ||
| func (s *AddRemoveSuite) TestAddSnapErrorsOnExistingSnapPlugs(c *C) { | ||
| _, err := s.addSnap(c, testConsumerYaml) | ||
| c.Assert(err, IsNil) | ||
| @@ -1925,3 +1913,21 @@ slots: | ||
| {Name: "i2", Summary: "i2 summary"}, | ||
| }) | ||
| } | ||
| + | ||
| +func (s *RepositorySuite) TestSanitizeErrorsOnInvalidSlotNames(c *C) { | ||
| + c.Assert(s.testRepo.AddInterface(&ifacetest.TestInterface{InterfaceName: "iface"}), IsNil) | ||
| + snapInfo := snaptest.MockInfo(c, testConsumerInvalidSlotNameYaml, nil) | ||
| + err := s.testRepo.SanitizePlugsSlots(snapInfo) | ||
| + c.Assert(err, IsNil) | ||
| + c.Assert(snapInfo.BadInterfaces, HasLen, 1) | ||
| + c.Check(snapInfo.BadInterfacesInfoString(), Matches, `snap "consumer" has bad plugs or slots: ttyS5 \(invalid interface name: "ttyS5"\)`) | ||
| +} | ||
| + | ||
| +func (s *RepositorySuite) TestSanitizeErrorsOnInvalidPlugNames(c *C) { | ||
| + c.Assert(s.testRepo.AddInterface(&ifacetest.TestInterface{InterfaceName: "iface"}), IsNil) | ||
| + snapInfo := snaptest.MockInfo(c, testConsumerInvalidPlugNameYaml, nil) | ||
| + err := s.testRepo.SanitizePlugsSlots(snapInfo) | ||
| + c.Assert(err, IsNil) | ||
| + c.Assert(snapInfo.BadInterfaces, HasLen, 1) | ||
| + c.Check(snapInfo.BadInterfacesInfoString(), Matches, `snap "consumer" has bad plugs or slots: ttyS3 \(invalid interface name: "ttyS3"\)`) | ||
| +} | ||
Could you please add a check that
snap.SanitizePlugsSlotsis not set yet? I worry that we may end up with sanitizer that checks against some ghost repository for whatever reason later.