Files
snapd/snap/info_snap_yaml_test.go
Samuele Pedroni 314b359bb3 asserts,snap: expose provenance on snap.Info and validate it
also introduce naming.ValidateProvenance,ValidProvenance and
a DefaultProvenance constant
2022-07-17 18:09:14 +02:00

2163 lines
56 KiB
Go

// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2014-2021 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 snap_test
import (
"fmt"
"regexp"
"testing"
"time"
. "gopkg.in/check.v1"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/snap/naming"
"github.com/snapcore/snapd/strutil"
"github.com/snapcore/snapd/testutil"
"github.com/snapcore/snapd/timeout"
)
// Hook up check.v1 into the "go test" runner
func Test(t *testing.T) { TestingT(t) }
type InfoSnapYamlTestSuite struct {
testutil.BaseTest
}
var _ = Suite(&InfoSnapYamlTestSuite{})
var mockYaml = []byte(`name: foo
version: 1.0
type: app
`)
func (s *InfoSnapYamlTestSuite) SetUpTest(c *C) {
s.BaseTest.SetUpTest(c)
s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
}
func (s *InfoSnapYamlTestSuite) TearDownTest(c *C) {
s.BaseTest.TearDownTest(c)
}
func (s *InfoSnapYamlTestSuite) TestSimple(c *C) {
info, err := snap.InfoFromSnapYaml(mockYaml)
c.Assert(err, IsNil)
c.Assert(info.InstanceName(), Equals, "foo")
c.Assert(info.Version, Equals, "1.0")
c.Assert(info.Type(), Equals, snap.TypeApp)
c.Assert(info.Epoch, DeepEquals, snap.E("0"))
c.Assert(info.SnapProvenance, Equals, "")
c.Check(info.Provenance(), Equals, naming.DefaultProvenance)
}
func (s *InfoSnapYamlTestSuite) TestSnapdTypeAddedByMagic(c *C) {
info, err := snap.InfoFromSnapYaml([]byte(`name: snapd
version: 1.0`))
c.Assert(err, IsNil)
c.Assert(info.InstanceName(), Equals, "snapd")
c.Assert(info.Version, Equals, "1.0")
c.Assert(info.Type(), Equals, snap.TypeSnapd)
}
func (s *InfoSnapYamlTestSuite) TestNonDefaultProvenance(c *C) {
info, err := snap.InfoFromSnapYaml([]byte(`name: foo
provenance: delegated-prov
version: 1.0`))
c.Assert(err, IsNil)
c.Check(info.Provenance(), Equals, "delegated-prov")
c.Check(info.SnapProvenance, Equals, "delegated-prov")
}
func (s *InfoSnapYamlTestSuite) TestFail(c *C) {
_, err := snap.InfoFromSnapYaml([]byte("random-crap"))
c.Assert(err, ErrorMatches, "(?m)cannot parse snap.yaml:.*")
}
type YamlSuite struct {
restore func()
testutil.BaseTest
}
var _ = Suite(&YamlSuite{})
func (s *YamlSuite) SetUpTest(c *C) {
s.BaseTest.SetUpTest(c)
s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
hookType := snap.NewHookType(regexp.MustCompile(".*"))
s.restore = snap.MockSupportedHookTypes([]*snap.HookType{hookType})
}
func (s *YamlSuite) TearDownTest(c *C) {
s.BaseTest.TearDownTest(c)
s.restore()
}
func (s *YamlSuite) TestUnmarshalGarbage(c *C) {
_, err := snap.InfoFromSnapYaml([]byte(`"`))
c.Assert(err, ErrorMatches, ".*: yaml: found unexpected end of stream")
}
func (s *YamlSuite) TestUnmarshalEmpty(c *C) {
info, err := snap.InfoFromSnapYaml([]byte(``))
c.Assert(err, IsNil)
c.Assert(info.Plugs, HasLen, 0)
c.Assert(info.Slots, HasLen, 0)
c.Assert(info.Apps, HasLen, 0)
}
// Tests focusing on plugs
func (s *YamlSuite) TestUnmarshalStandaloneImplicitPlug(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
network-client:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Assert(info.Plugs["network-client"], DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "network-client",
Interface: "network-client",
})
}
func (s *YamlSuite) TestUnmarshalStandaloneAbbreviatedPlug(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
net: network-client
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Assert(info.Plugs["net"], DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "net",
Interface: "network-client",
})
}
func (s *YamlSuite) TestUnmarshalStandaloneMinimalisticPlug(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
net:
interface: network-client
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Assert(info.Plugs["net"], DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "net",
Interface: "network-client",
})
}
func (s *YamlSuite) TestUnmarshalStandaloneCompletePlug(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
net:
interface: network-client
ipv6-aware: true
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Assert(info.Plugs["net"], DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "net",
Interface: "network-client",
Attrs: map[string]interface{}{"ipv6-aware": true},
})
}
func (s *YamlSuite) TestUnmarshalStandalonePlugWithIntAndListAndMap(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
iface:
interface: complex
i: 3
l: [1,2,3]
m:
a: A
b: B
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Assert(info.Plugs["iface"], DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "iface",
Interface: "complex",
Attrs: map[string]interface{}{
"i": int64(3),
"l": []interface{}{int64(1), int64(2), int64(3)},
"m": map[string]interface{}{"a": "A", "b": "B"},
},
})
}
func (s *YamlSuite) TestUnmarshalLastPlugDefinitionWins(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
net:
interface: network-client
attr: 1
net:
interface: network-client
attr: 2
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Assert(info.Plugs["net"], DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "net",
Interface: "network-client",
Attrs: map[string]interface{}{"attr": int64(2)},
})
}
func (s *YamlSuite) TestUnmarshalPlugsExplicitlyDefinedImplicitlyBoundToApps(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
network-client:
apps:
app:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 1)
plug := info.Plugs["network-client"]
app := info.Apps["app"]
c.Assert(plug, DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "network-client",
Interface: "network-client",
Apps: map[string]*snap.AppInfo{app.Name: app},
})
c.Assert(app, DeepEquals, &snap.AppInfo{
Snap: info,
Name: "app",
Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
})
}
func (s *YamlSuite) TestUnmarshalGlobalPlugBoundToOneApp(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
network-client:
apps:
with-plug:
plugs: [network-client]
without-plug:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 2)
plug := info.Plugs["network-client"]
withPlugApp := info.Apps["with-plug"]
withoutPlugApp := info.Apps["without-plug"]
c.Assert(plug, DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "network-client",
Interface: "network-client",
Apps: map[string]*snap.AppInfo{withPlugApp.Name: withPlugApp},
})
c.Assert(withPlugApp, DeepEquals, &snap.AppInfo{
Snap: info,
Name: "with-plug",
Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
})
c.Assert(withoutPlugApp, DeepEquals, &snap.AppInfo{
Snap: info,
Name: "without-plug",
Plugs: map[string]*snap.PlugInfo{},
})
}
func (s *YamlSuite) TestUnmarshalPlugsExplicitlyDefinedExplicitlyBoundToApps(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
net: network-client
apps:
app:
plugs: ["net"]
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 1)
plug := info.Plugs["net"]
app := info.Apps["app"]
c.Assert(plug, DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "net",
Interface: "network-client",
Apps: map[string]*snap.AppInfo{app.Name: app},
})
c.Assert(app, DeepEquals, &snap.AppInfo{
Snap: info,
Name: "app",
Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
})
}
func (s *YamlSuite) TestUnmarshalPlugsImplicitlyDefinedExplicitlyBoundToApps(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
apps:
app:
plugs: ["network-client"]
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 1)
plug := info.Plugs["network-client"]
app := info.Apps["app"]
c.Assert(plug, DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "network-client",
Interface: "network-client",
Apps: map[string]*snap.AppInfo{app.Name: app},
})
c.Assert(app, DeepEquals, &snap.AppInfo{
Snap: info,
Name: "app",
Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
})
}
func (s *YamlSuite) TestUnmarshalPlugWithoutInterfaceName(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
network-client:
ipv6-aware: true
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 0)
c.Assert(info.Plugs["network-client"], DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "network-client",
Interface: "network-client",
Attrs: map[string]interface{}{"ipv6-aware": true},
})
}
func (s *YamlSuite) TestUnmarshalPlugWithLabel(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
bool-file:
label: Disk I/O indicator
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 0)
c.Assert(info.Plugs["bool-file"], DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "bool-file",
Interface: "bool-file",
Label: "Disk I/O indicator",
})
}
func (s *YamlSuite) TestUnmarshalCorruptedPlugWithNonStringInterfaceName(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
net:
interface: 1.0
ipv6-aware: true
`))
c.Assert(err, ErrorMatches, `interface name on plug "net" is not a string \(found float64\)`)
}
func (s *YamlSuite) TestUnmarshalCorruptedPlugWithNonStringLabel(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
bool-file:
label: 1.0
`))
c.Assert(err, ErrorMatches, `label of plug "bool-file" is not a string \(found float64\)`)
}
func (s *YamlSuite) TestUnmarshalCorruptedPlugWithNonStringAttributes(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
net:
1: ok
`))
c.Assert(err, ErrorMatches, `plug "net" has attribute key that is not a string \(found int\)`)
}
func (s *YamlSuite) TestUnmarshalCorruptedPlugWithEmptyAttributeKey(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
net:
"": ok
`))
c.Assert(err, ErrorMatches, `plug "net" has an empty attribute key`)
}
func (s *YamlSuite) TestUnmarshalCorruptedPlugWithUnexpectedType(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
net: 5
`))
c.Assert(err, ErrorMatches, `plug "net" has malformed definition \(found int\)`)
}
func (s *YamlSuite) TestUnmarshalReservedPlugAttribute(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
serial:
interface: serial-port
$baud-rate: [9600]
`))
c.Assert(err, ErrorMatches, `plug "serial" uses reserved attribute "\$baud-rate"`)
}
func (s *YamlSuite) TestUnmarshalInvalidPlugAttribute(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
serial:
interface: serial-port
foo: null
`))
c.Assert(err, ErrorMatches, `attribute "foo" of plug \"serial\": invalid scalar:.*`)
}
func (s *YamlSuite) TestUnmarshalInvalidAttributeMapKey(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
serial:
interface: serial-port
bar:
baz:
- 1: A
`))
c.Assert(err, ErrorMatches, `attribute "bar" of plug \"serial\": non-string key: 1`)
}
// Tests focusing on slots
func (s *YamlSuite) TestUnmarshalStandaloneImplicitSlot(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
network-client:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Assert(info.Slots["network-client"], DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "network-client",
Interface: "network-client",
})
}
func (s *YamlSuite) TestUnmarshalStandaloneAbbreviatedSlot(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
net: network-client
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Assert(info.Slots["net"], DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "net",
Interface: "network-client",
})
}
func (s *YamlSuite) TestUnmarshalStandaloneMinimalisticSlot(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
net:
interface: network-client
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Assert(info.Slots["net"], DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "net",
Interface: "network-client",
})
}
func (s *YamlSuite) TestUnmarshalStandaloneCompleteSlot(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
net:
interface: network-client
ipv6-aware: true
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Assert(info.Slots["net"], DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "net",
Interface: "network-client",
Attrs: map[string]interface{}{"ipv6-aware": true},
})
}
func (s *YamlSuite) TestUnmarshalStandaloneSlotWithIntAndListAndMap(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
iface:
interface: complex
i: 3
l: [1,2]
m:
a: "A"
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Assert(info.Slots["iface"], DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "iface",
Interface: "complex",
Attrs: map[string]interface{}{
"i": int64(3),
"l": []interface{}{int64(1), int64(2)},
"m": map[string]interface{}{"a": "A"},
},
})
}
func (s *YamlSuite) TestUnmarshalLastSlotDefinitionWins(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
net:
interface: network-client
attr: 1
net:
interface: network-client
attr: 2
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Assert(info.Slots["net"], DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "net",
Interface: "network-client",
Attrs: map[string]interface{}{"attr": int64(2)},
})
}
func (s *YamlSuite) TestUnmarshalSlotsExplicitlyDefinedImplicitlyBoundToApps(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
network-client:
apps:
app:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Check(info.Apps, HasLen, 1)
slot := info.Slots["network-client"]
app := info.Apps["app"]
c.Assert(slot, DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "network-client",
Interface: "network-client",
Apps: map[string]*snap.AppInfo{app.Name: app},
})
c.Assert(app, DeepEquals, &snap.AppInfo{
Snap: info,
Name: "app",
Slots: map[string]*snap.SlotInfo{slot.Name: slot},
})
}
func (s *YamlSuite) TestUnmarshalSlotsExplicitlyDefinedExplicitlyBoundToApps(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
net: network-client
apps:
app:
slots: ["net"]
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Check(info.Apps, HasLen, 1)
slot := info.Slots["net"]
app := info.Apps["app"]
c.Assert(slot, DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "net",
Interface: "network-client",
Apps: map[string]*snap.AppInfo{app.Name: app},
})
c.Assert(app, DeepEquals, &snap.AppInfo{
Snap: info,
Name: "app",
Slots: map[string]*snap.SlotInfo{slot.Name: slot},
})
}
func (s *YamlSuite) TestUnmarshalSlotsImplicitlyDefinedExplicitlyBoundToApps(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
apps:
app:
slots: ["network-client"]
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Check(info.Apps, HasLen, 1)
slot := info.Slots["network-client"]
app := info.Apps["app"]
c.Assert(slot, DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "network-client",
Interface: "network-client",
Apps: map[string]*snap.AppInfo{app.Name: app},
})
c.Assert(app, DeepEquals, &snap.AppInfo{
Snap: info,
Name: "app",
Slots: map[string]*snap.SlotInfo{slot.Name: slot},
})
}
func (s *YamlSuite) TestUnmarshalSlotWithoutInterfaceName(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
network-client:
ipv6-aware: true
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Check(info.Apps, HasLen, 0)
c.Assert(info.Slots["network-client"], DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "network-client",
Interface: "network-client",
Attrs: map[string]interface{}{"ipv6-aware": true},
})
}
func (s *YamlSuite) TestUnmarshalSlotWithLabel(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
led0:
interface: bool-file
label: Front panel LED (red)
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Check(info.Apps, HasLen, 0)
c.Assert(info.Slots["led0"], DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "led0",
Interface: "bool-file",
Label: "Front panel LED (red)",
})
}
func (s *YamlSuite) TestUnmarshalGlobalSlotsBindToHooks(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
test-slot:
hooks:
test-hook:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Check(info.Apps, HasLen, 0)
c.Check(info.Hooks, HasLen, 1)
slot, ok := info.Slots["test-slot"]
c.Assert(ok, Equals, true, Commentf("Expected slots to include 'test-slot'"))
hook, ok := info.Hooks["test-hook"]
c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
c.Check(slot, DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "test-slot",
Interface: "test-slot",
Hooks: map[string]*snap.HookInfo{hook.Name: hook},
})
c.Check(hook, DeepEquals, &snap.HookInfo{
Snap: info,
Name: "test-hook",
Slots: map[string]*snap.SlotInfo{slot.Name: slot},
Explicit: true,
})
}
func (s *YamlSuite) TestUnmarshalHookWithSlot(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
hooks:
test-hook:
slots: [test-slot]
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 1)
c.Check(info.Apps, HasLen, 0)
c.Check(info.Hooks, HasLen, 1)
slot, ok := info.Slots["test-slot"]
c.Assert(ok, Equals, true, Commentf("Expected slots to include 'test-slot'"))
hook, ok := info.Hooks["test-hook"]
c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
c.Check(slot, DeepEquals, &snap.SlotInfo{
Snap: info,
Name: "test-slot",
Interface: "test-slot",
Hooks: map[string]*snap.HookInfo{hook.Name: hook},
})
c.Check(hook, DeepEquals, &snap.HookInfo{
Snap: info,
Name: "test-hook",
Slots: map[string]*snap.SlotInfo{slot.Name: slot},
Explicit: true,
})
}
func (s *YamlSuite) TestUnmarshalCorruptedSlotWithNonStringInterfaceName(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
net:
interface: 1.0
ipv6-aware: true
`))
c.Assert(err, ErrorMatches, `interface name on slot "net" is not a string \(found float64\)`)
}
func (s *YamlSuite) TestUnmarshalCorruptedSlotWithNonStringLabel(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
bool-file:
label: 1.0
`))
c.Assert(err, ErrorMatches, `label of slot "bool-file" is not a string \(found float64\)`)
}
func (s *YamlSuite) TestUnmarshalCorruptedSlotWithNonStringAttributes(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
net:
1: ok
`))
c.Assert(err, ErrorMatches, `slot "net" has attribute key that is not a string \(found int\)`)
}
func (s *YamlSuite) TestUnmarshalCorruptedSlotWithEmptyAttributeKey(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
net:
"": ok
`))
c.Assert(err, ErrorMatches, `slot "net" has an empty attribute key`)
}
func (s *YamlSuite) TestUnmarshalCorruptedSlotWithUnexpectedType(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
net: 5
`))
c.Assert(err, ErrorMatches, `slot "net" has malformed definition \(found int\)`)
}
func (s *YamlSuite) TestUnmarshalReservedSlotAttribute(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
serial:
interface: serial-port
$baud-rate: [9600]
`))
c.Assert(err, ErrorMatches, `slot "serial" uses reserved attribute "\$baud-rate"`)
}
func (s *YamlSuite) TestUnmarshalInvalidSlotAttribute(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
_, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
serial:
interface: serial-port
foo: null
`))
c.Assert(err, ErrorMatches, `attribute "foo" of slot \"serial\": invalid scalar:.*`)
}
func (s *YamlSuite) TestUnmarshalHook(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
hooks:
test-hook:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 0)
c.Check(info.Hooks, HasLen, 1)
hook, ok := info.Hooks["test-hook"]
c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
c.Check(hook, DeepEquals, &snap.HookInfo{
Snap: info,
Name: "test-hook",
Plugs: nil,
Explicit: true,
})
}
func (s *YamlSuite) TestUnmarshalUnsupportedHook(c *C) {
s.restore()
hookType := snap.NewHookType(regexp.MustCompile("not-test-hook"))
s.restore = snap.MockSupportedHookTypes([]*snap.HookType{hookType})
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
hooks:
test-hook:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 0)
c.Check(info.Hooks, HasLen, 0, Commentf("Expected no hooks to be loaded"))
}
func (s *YamlSuite) TestUnmarshalHookFiltersOutUnsupportedHooks(c *C) {
s.restore()
hookType := snap.NewHookType(regexp.MustCompile("test-.*"))
s.restore = snap.MockSupportedHookTypes([]*snap.HookType{hookType})
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
hooks:
test-hook:
foo-hook:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 0)
c.Check(info.Hooks, HasLen, 1)
hook, ok := info.Hooks["test-hook"]
c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
c.Check(hook, DeepEquals, &snap.HookInfo{
Snap: info,
Name: "test-hook",
Plugs: nil,
Explicit: true,
})
}
func (s *YamlSuite) TestUnmarshalHookWithPlug(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
hooks:
test-hook:
plugs: [test-plug]
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 0)
c.Check(info.Hooks, HasLen, 1)
plug, ok := info.Plugs["test-plug"]
c.Assert(ok, Equals, true, Commentf("Expected plugs to include 'test-plug'"))
hook, ok := info.Hooks["test-hook"]
c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
c.Check(plug, DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "test-plug",
Interface: "test-plug",
Hooks: map[string]*snap.HookInfo{hook.Name: hook},
})
c.Check(hook, DeepEquals, &snap.HookInfo{
Snap: info,
Name: "test-hook",
Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
Explicit: true,
})
}
func (s *YamlSuite) TestUnmarshalGlobalPlugsBindToHooks(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
test-plug:
hooks:
test-hook:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 0)
c.Check(info.Hooks, HasLen, 1)
plug, ok := info.Plugs["test-plug"]
c.Assert(ok, Equals, true, Commentf("Expected plugs to include 'test-plug'"))
hook, ok := info.Hooks["test-hook"]
c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
c.Check(plug, DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "test-plug",
Interface: "test-plug",
Hooks: map[string]*snap.HookInfo{hook.Name: hook},
})
c.Check(hook, DeepEquals, &snap.HookInfo{
Snap: info,
Name: "test-hook",
Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
Explicit: true,
})
}
func (s *YamlSuite) TestUnmarshalGlobalPlugBoundToOneHook(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
test-plug:
hooks:
with-plug:
plugs: [test-plug]
without-plug:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 0)
c.Check(info.Hooks, HasLen, 2)
plug := info.Plugs["test-plug"]
withPlugHook := info.Hooks["with-plug"]
withoutPlugHook := info.Hooks["without-plug"]
c.Assert(plug, DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "test-plug",
Interface: "test-plug",
Hooks: map[string]*snap.HookInfo{withPlugHook.Name: withPlugHook},
})
c.Assert(withPlugHook, DeepEquals, &snap.HookInfo{
Snap: info,
Name: "with-plug",
Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
Explicit: true,
})
c.Assert(withoutPlugHook, DeepEquals, &snap.HookInfo{
Snap: info,
Name: "without-plug",
Plugs: map[string]*snap.PlugInfo{},
Explicit: true,
})
}
func (s *YamlSuite) TestUnmarshalExplicitGlobalPlugBoundToHook(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
test-plug: test-interface
hooks:
test-hook:
plugs: ["test-plug"]
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 0)
c.Check(info.Hooks, HasLen, 1)
plug, ok := info.Plugs["test-plug"]
c.Assert(ok, Equals, true, Commentf("Expected plugs to include 'test-plug'"))
hook, ok := info.Hooks["test-hook"]
c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
c.Check(plug, DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "test-plug",
Interface: "test-interface",
Hooks: map[string]*snap.HookInfo{hook.Name: hook},
})
c.Check(hook, DeepEquals, &snap.HookInfo{
Snap: info,
Name: "test-hook",
Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
Explicit: true,
})
}
func (s *YamlSuite) TestUnmarshalGlobalPlugBoundToHookNotApp(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
plugs:
test-plug:
hooks:
test-hook:
plugs: [test-plug]
apps:
test-app:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 1)
c.Check(info.Slots, HasLen, 0)
c.Check(info.Apps, HasLen, 1)
c.Check(info.Hooks, HasLen, 1)
plug := info.Plugs["test-plug"]
hook := info.Hooks["test-hook"]
app := info.Apps["test-app"]
c.Assert(plug, DeepEquals, &snap.PlugInfo{
Snap: info,
Name: "test-plug",
Interface: "test-plug",
Apps: map[string]*snap.AppInfo{},
Hooks: map[string]*snap.HookInfo{hook.Name: hook},
})
c.Assert(hook, DeepEquals, &snap.HookInfo{
Snap: info,
Name: "test-hook",
Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
Explicit: true,
})
c.Assert(app, DeepEquals, &snap.AppInfo{
Snap: info,
Name: "test-app",
Plugs: map[string]*snap.PlugInfo{},
})
}
func (s *YamlSuite) TestUnmarshalComplexExample(c *C) {
// NOTE: yaml content cannot use tabs, indent the section with spaces.
info, err := snap.InfoFromSnapYaml([]byte(`
name: foo
version: 1.2
title: Foo
summary: foo app
type: app
epoch: 1*
confinement: devmode
license: GPL-3.0
description: |
Foo provides useful services
apps:
daemon:
command: foo --daemon
plugs: [network, network-bind]
slots: [foo-socket-slot]
foo:
command: fooctl
plugs: [foo-socket-plug]
hooks:
test-hook:
plugs: [foo-socket-plug]
slots: [foo-socket-slot]
plugs:
foo-socket-plug:
interface: socket
# $protocol: foo
logging:
interface: syslog
slots:
foo-socket-slot:
interface: socket
path: $SNAP_DATA/socket
protocol: foo
tracing:
interface: ptrace
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "foo")
c.Check(info.Version, Equals, "1.2")
c.Check(info.Type(), Equals, snap.TypeApp)
c.Check(info.Epoch, DeepEquals, snap.E("1*"))
c.Check(info.Confinement, Equals, snap.DevModeConfinement)
c.Check(info.Title(), Equals, "Foo")
c.Check(info.Summary(), Equals, "foo app")
c.Check(info.Description(), Equals, "Foo provides useful services\n")
c.Check(info.Apps, HasLen, 2)
c.Check(info.Plugs, HasLen, 4)
c.Check(info.Slots, HasLen, 2)
// these don't come from snap.yaml
c.Check(info.Publisher, Equals, snap.StoreAccount{})
c.Check(info.Channel, Equals, "")
c.Check(info.License, Equals, "GPL-3.0")
app1 := info.Apps["daemon"]
app2 := info.Apps["foo"]
hook := info.Hooks["test-hook"]
plug1 := info.Plugs["network"]
plug2 := info.Plugs["network-bind"]
plug3 := info.Plugs["foo-socket-plug"]
plug4 := info.Plugs["logging"]
slot1 := info.Slots["foo-socket-slot"]
slot2 := info.Slots["tracing"]
// app1 ("daemon") has three plugs ("network", "network-bind", "logging")
// and two slots ("foo-socket", "tracing"). The slot "tracing" and plug
// "logging" are global, everything else is app-bound.
c.Assert(app1, Not(IsNil))
c.Check(app1.Snap, Equals, info)
c.Check(app1.Name, Equals, "daemon")
c.Check(app1.Command, Equals, "foo --daemon")
c.Check(app1.Plugs, DeepEquals, map[string]*snap.PlugInfo{
plug1.Name: plug1, plug2.Name: plug2, plug4.Name: plug4})
c.Check(app1.Slots, DeepEquals, map[string]*snap.SlotInfo{
slot1.Name: slot1, slot2.Name: slot2})
// app2 ("foo") has two plugs ("foo-socket", "logging") and one slot
// ("tracing"). The slot "tracing" and plug "logging" are global while
// "foo-socket" is app-bound.
c.Assert(app2, Not(IsNil))
c.Check(app2.Snap, Equals, info)
c.Check(app2.Name, Equals, "foo")
c.Check(app2.Command, Equals, "fooctl")
c.Check(app2.Plugs, DeepEquals, map[string]*snap.PlugInfo{
plug3.Name: plug3, plug4.Name: plug4})
c.Check(app2.Slots, DeepEquals, map[string]*snap.SlotInfo{
slot2.Name: slot2})
// hook1 has two plugs ("foo-socket", "logging") and two slots ("foo-socket", "tracing").
// The plug "logging" and slot "tracing" are global while "foo-socket" is hook-bound.
c.Assert(hook, NotNil)
c.Check(hook.Snap, Equals, info)
c.Check(hook.Name, Equals, "test-hook")
c.Check(hook.Plugs, DeepEquals, map[string]*snap.PlugInfo{
plug3.Name: plug3, plug4.Name: plug4})
c.Check(hook.Slots, DeepEquals, map[string]*snap.SlotInfo{
slot1.Name: slot1, slot2.Name: slot2})
// plug1 ("network") is implicitly defined and app-bound to "daemon"
c.Assert(plug1, Not(IsNil))
c.Check(plug1.Snap, Equals, info)
c.Check(plug1.Name, Equals, "network")
c.Check(plug1.Interface, Equals, "network")
c.Check(plug1.Attrs, HasLen, 0)
c.Check(plug1.Label, Equals, "")
c.Check(plug1.Apps, DeepEquals, map[string]*snap.AppInfo{app1.Name: app1})
// plug2 ("network-bind") is implicitly defined and app-bound to "daemon"
c.Assert(plug2, Not(IsNil))
c.Check(plug2.Snap, Equals, info)
c.Check(plug2.Name, Equals, "network-bind")
c.Check(plug2.Interface, Equals, "network-bind")
c.Check(plug2.Attrs, HasLen, 0)
c.Check(plug2.Label, Equals, "")
c.Check(plug2.Apps, DeepEquals, map[string]*snap.AppInfo{app1.Name: app1})
// plug3 ("foo-socket") is app-bound to "foo"
c.Assert(plug3, Not(IsNil))
c.Check(plug3.Snap, Equals, info)
c.Check(plug3.Name, Equals, "foo-socket-plug")
c.Check(plug3.Interface, Equals, "socket")
c.Check(plug3.Attrs, HasLen, 0)
c.Check(plug3.Label, Equals, "")
c.Check(plug3.Apps, DeepEquals, map[string]*snap.AppInfo{app2.Name: app2})
// plug4 ("logging") is global so it is bound to all apps
c.Assert(plug4, Not(IsNil))
c.Check(plug4.Snap, Equals, info)
c.Check(plug4.Name, Equals, "logging")
c.Check(plug4.Interface, Equals, "syslog")
c.Check(plug4.Attrs, HasLen, 0)
c.Check(plug4.Label, Equals, "")
c.Check(plug4.Apps, DeepEquals, map[string]*snap.AppInfo{
app1.Name: app1, app2.Name: app2})
// slot1 ("foo-socket") is app-bound to "daemon"
c.Assert(slot1, Not(IsNil))
c.Check(slot1.Snap, Equals, info)
c.Check(slot1.Name, Equals, "foo-socket-slot")
c.Check(slot1.Interface, Equals, "socket")
c.Check(slot1.Attrs, DeepEquals, map[string]interface{}{
"protocol": "foo", "path": "$SNAP_DATA/socket"})
c.Check(slot1.Label, Equals, "")
c.Check(slot1.Apps, DeepEquals, map[string]*snap.AppInfo{app1.Name: app1})
// slot2 ("tracing") is global so it is bound to all apps
c.Assert(slot2, Not(IsNil))
c.Check(slot2.Snap, Equals, info)
c.Check(slot2.Name, Equals, "tracing")
c.Check(slot2.Interface, Equals, "ptrace")
c.Check(slot2.Attrs, HasLen, 0)
c.Check(slot2.Label, Equals, "")
c.Check(slot2.Apps, DeepEquals, map[string]*snap.AppInfo{
app1.Name: app1, app2.Name: app2})
}
func (s *YamlSuite) TestUnmarshalActivatesOn(c *C) {
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
slots:
test-slot1:
test-slot2:
apps:
daemon:
activates-on: ["test-slot1", "test-slot2"]
foo:
`))
c.Assert(err, IsNil)
c.Check(info.InstanceName(), Equals, "snap")
c.Check(info.Plugs, HasLen, 0)
c.Check(info.Slots, HasLen, 2)
c.Check(info.Apps, HasLen, 2)
c.Check(info.Hooks, HasLen, 0)
app1 := info.Apps["daemon"]
app2 := info.Apps["foo"]
slot1 := info.Slots["test-slot1"]
slot2 := info.Slots["test-slot2"]
c.Assert(app1, Not(IsNil))
c.Check(app1.Name, Equals, "daemon")
c.Check(app1.ActivatesOn, DeepEquals, []*snap.SlotInfo{slot1, slot2})
// activates-on slots are implicitly added to the app
c.Check(app1.Slots, DeepEquals, map[string]*snap.SlotInfo{
slot1.Name: slot1, slot2.Name: slot2})
c.Assert(app2, Not(IsNil))
c.Check(app2.Name, Equals, "foo")
c.Check(app2.ActivatesOn, HasLen, 0)
// As slot has been bound to app1, it isn't implicitly applied here
c.Check(app2.Slots, HasLen, 0)
c.Assert(slot1, Not(IsNil))
c.Check(slot1.Name, Equals, "test-slot1")
c.Check(slot1.Apps, DeepEquals, map[string]*snap.AppInfo{
app1.Name: app1})
c.Assert(slot2, Not(IsNil))
c.Check(slot2.Name, Equals, "test-slot2")
c.Check(slot2.Apps, DeepEquals, map[string]*snap.AppInfo{
app1.Name: app1})
}
func (s *YamlSuite) TestUnmarshalActivatesOnUnknownSlot(c *C) {
info, err := snap.InfoFromSnapYaml([]byte(`
name: snap
apps:
daemon:
activates-on: ["test-slot"]
`))
c.Check(info, IsNil)
c.Check(err, ErrorMatches, `invalid activates-on value "test-slot" on app "daemon": slot not found`)
}
// type and architectures
func (s *YamlSuite) TestSnapYamlTypeDefault(c *C) {
y := []byte(`name: binary
version: 1.0
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Assert(info.Type(), Equals, snap.TypeApp)
}
func (s *YamlSuite) TestSnapYamlEpochDefault(c *C) {
y := []byte(`name: binary
version: 1.0
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Assert(info.Epoch, DeepEquals, snap.E("0"))
}
func (s *YamlSuite) TestSnapYamlConfinementDefault(c *C) {
y := []byte(`name: binary
version: 1.0
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Assert(info.Confinement, Equals, snap.StrictConfinement)
}
func (s *YamlSuite) TestSnapYamlMultipleArchitecturesParsing(c *C) {
y := []byte(`name: binary
version: 1.0
architectures: [i386, armhf]
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Assert(info.Architectures, DeepEquals, []string{"i386", "armhf"})
}
func (s *YamlSuite) TestSnapYamlSingleArchitecturesParsing(c *C) {
y := []byte(`name: binary
version: 1.0
architectures: [i386]
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Assert(info.Architectures, DeepEquals, []string{"i386"})
}
func (s *YamlSuite) TestSnapYamlAssumesParsing(c *C) {
y := []byte(`name: binary
version: 1.0
assumes: [feature2, feature1]
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Assert(info.Assumes, DeepEquals, []string{"feature1", "feature2"})
}
func (s *YamlSuite) TestSnapYamlNoArchitecturesParsing(c *C) {
y := []byte(`name: binary
version: 1.0
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Assert(info.Architectures, DeepEquals, []string{"all"})
}
func (s *YamlSuite) TestSnapYamlBadArchitectureParsing(c *C) {
y := []byte(`name: binary
version: 1.0
architectures:
armhf:
no
`)
_, err := snap.InfoFromSnapYaml(y)
c.Assert(err, NotNil)
}
// apps
func (s *YamlSuite) TestSimpleAppExample(c *C) {
y := []byte(`name: wat
version: 42
apps:
cm:
command: cm0
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Check(info.Apps, DeepEquals, map[string]*snap.AppInfo{
"cm": {
Snap: info,
Name: "cm",
Command: "cm0",
},
})
}
func (s *YamlSuite) TestDaemonEverythingExample(c *C) {
y := []byte(`name: wat
version: 42
apps:
svc:
command: svc1
description: svc one
stop-timeout: 25s
start-timeout: 42m
daemon: forking
daemon-scope: system
stop-command: stop-cmd
post-stop-command: post-stop-cmd
restart-condition: on-abnormal
bus-name: busName
sockets:
sock1:
listen-stream: $SNAP_DATA/sock1.socket
socket-mode: 0666
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
app := snap.AppInfo{
Snap: info,
Name: "svc",
Command: "svc1",
Daemon: "forking",
DaemonScope: snap.SystemDaemon,
RestartCond: snap.RestartOnAbnormal,
StopTimeout: timeout.Timeout(25 * time.Second),
StartTimeout: timeout.Timeout(42 * time.Minute),
StopCommand: "stop-cmd",
PostStopCommand: "post-stop-cmd",
BusName: "busName",
Sockets: map[string]*snap.SocketInfo{},
}
app.Sockets["sock1"] = &snap.SocketInfo{
App: &app,
Name: "sock1",
ListenStream: "$SNAP_DATA/sock1.socket",
SocketMode: 0666,
}
c.Check(info.Apps, DeepEquals, map[string]*snap.AppInfo{"svc": &app})
}
func (s *YamlSuite) TestDaemonUserDaemon(c *C) {
y := []byte(`name: wat
version: 42
apps:
svc:
command: svc1
daemon: simple
daemon-scope: user
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Check(info.Apps["svc"].DaemonScope, Equals, snap.UserDaemon)
}
func (s *YamlSuite) TestDaemonNoDaemonScope(c *C) {
y := []byte(`name: wat
version: 42
apps:
svc:
command: svc1
daemon: simple
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
// If daemon-scope is unset, default to system scope
c.Check(info.Apps["svc"].DaemonScope, Equals, snap.SystemDaemon)
}
func (s *YamlSuite) TestDaemonListenStreamAsInteger(c *C) {
y := []byte(`name: wat
version: 42
apps:
svc:
command: svc
sockets:
sock:
listen-stream: 8080
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
app := snap.AppInfo{
Snap: info,
Name: "svc",
Command: "svc",
Sockets: map[string]*snap.SocketInfo{},
}
app.Sockets["sock"] = &snap.SocketInfo{
App: &app,
Name: "sock",
ListenStream: "8080",
}
c.Check(info.Apps, DeepEquals, map[string]*snap.AppInfo{
"svc": &app,
})
}
func (s *YamlSuite) TestDaemonInvalidSocketMode(c *C) {
y := []byte(`name: wat
version: 42
apps:
svc:
command: svc
sockets:
sock:
listen-stream: 8080
socket-mode: asdfasdf
`)
_, err := snap.InfoFromSnapYaml(y)
c.Check(err.Error(), Matches, "cannot parse snap.yaml: yaml: unmarshal errors:\n"+
" line 9: cannot unmarshal !!str `asdfasdf` into (os|fs).FileMode")
}
func (s *YamlSuite) TestDaemonInvalidDaemonScope(c *C) {
y := []byte(`name: wat
version: 42
apps:
svc:
command: svc
daemon-scope: invalid
`)
_, err := snap.InfoFromSnapYaml(y)
c.Check(err.Error(), Equals, "cannot parse snap.yaml: invalid daemon scope: \"invalid\"")
}
func (s *YamlSuite) TestSnapYamlGlobalEnvironment(c *C) {
y := []byte(`
name: foo
version: 1.0
environment:
foo: bar
baz: boom
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Assert(info.Environment, DeepEquals, *strutil.NewOrderedMap("foo", "bar", "baz", "boom"))
}
func (s *YamlSuite) TestSnapYamlPerAppEnvironment(c *C) {
y := []byte(`
name: foo
version: 1.0
apps:
foo:
environment:
k1: v1
k2: v2
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Assert(info.Apps["foo"].Environment, DeepEquals, *strutil.NewOrderedMap("k1", "v1", "k2", "v2"))
}
func (s *YamlSuite) TestSnapYamlPerHookEnvironment(c *C) {
y := []byte(`
name: foo
version: 1.0
hooks:
foo:
environment:
k1: v1
k2: v2
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Assert(info.Hooks["foo"].Environment, DeepEquals, *strutil.NewOrderedMap("k1", "v1", "k2", "v2"))
}
// classic confinement
func (s *YamlSuite) TestClassicConfinement(c *C) {
y := []byte(`
name: foo
confinement: classic
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Assert(info.Confinement, Equals, snap.ClassicConfinement)
}
func (s *YamlSuite) TestSnapYamlAliases(c *C) {
y := []byte(`
name: foo
version: 1.0
apps:
foo:
aliases: [foo]
bar:
aliases: [bar, bar1]
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Check(info.Apps["foo"].LegacyAliases, DeepEquals, []string{"foo"})
c.Check(info.Apps["bar"].LegacyAliases, DeepEquals, []string{"bar", "bar1"})
c.Check(info.LegacyAliases, DeepEquals, map[string]*snap.AppInfo{
"foo": info.Apps["foo"],
"bar": info.Apps["bar"],
"bar1": info.Apps["bar"],
})
}
func (s *YamlSuite) TestSnapYamlAliasesConflict(c *C) {
y := []byte(`
name: foo
version: 1.0
apps:
foo:
aliases: [bar]
bar:
aliases: [bar]
`)
_, err := snap.InfoFromSnapYaml(y)
c.Assert(err, ErrorMatches, `cannot set "bar" as alias for both ("foo" and "bar"|"bar" and "foo")`)
}
func (s *YamlSuite) TestSnapYamlAppStartOrder(c *C) {
y := []byte(`name: wat
version: 42
apps:
foo:
after: [bar, zed]
bar:
before: [foo]
baz:
after: [foo]
zed:
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Check(info.Apps, DeepEquals, map[string]*snap.AppInfo{
"foo": {
Snap: info,
Name: "foo",
After: []string{"bar", "zed"},
},
"bar": {
Snap: info,
Name: "bar",
Before: []string{"foo"},
},
"baz": {
Snap: info,
Name: "baz",
After: []string{"foo"},
},
"zed": {
Snap: info,
Name: "zed",
},
})
}
func (s *YamlSuite) TestSnapYamlWatchdog(c *C) {
y := []byte(`
name: foo
version: 1.0
apps:
foo:
watchdog-timeout: 12s
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Check(info.Apps["foo"].WatchdogTimeout, Equals, timeout.Timeout(12*time.Second))
}
func (s *YamlSuite) TestLayout(c *C) {
y := []byte(`
name: foo
version: 1.0
layout:
/usr/share/foo:
bind: $SNAP/usr/share/foo
/usr/share/bar:
symlink: $SNAP/usr/share/bar
/etc/froz:
bind-file: $SNAP/etc/froz
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Assert(info.Layout["/usr/share/foo"], DeepEquals, &snap.Layout{
Snap: info,
Path: "/usr/share/foo",
Bind: "$SNAP/usr/share/foo",
User: "root",
Group: "root",
Mode: 0755,
})
c.Assert(info.Layout["/usr/share/bar"], DeepEquals, &snap.Layout{
Snap: info,
Path: "/usr/share/bar",
Symlink: "$SNAP/usr/share/bar",
User: "root",
Group: "root",
Mode: 0755,
})
c.Assert(info.Layout["/etc/froz"], DeepEquals, &snap.Layout{
Snap: info,
Path: "/etc/froz",
BindFile: "$SNAP/etc/froz",
User: "root",
Group: "root",
Mode: 0755,
})
}
func (s *YamlSuite) TestLayoutsWithTypo(c *C) {
y := []byte(`
name: foo
version: 1.0
layouts:
/usr/share/foo:
bind: $SNAP/usr/share/foo
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, ErrorMatches, `cannot parse snap.yaml: typo detected: use singular "layout" instead of plural "layouts"`)
c.Assert(info, IsNil)
}
func (s *YamlSuite) TestSnapYamlAppTimer(c *C) {
y := []byte(`name: wat
version: 42
apps:
foo:
daemon: oneshot
timer: mon,10:00-12:00
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
app := info.Apps["foo"]
c.Check(app.Timer, DeepEquals, &snap.TimerInfo{App: app, Timer: "mon,10:00-12:00"})
}
func (s *YamlSuite) TestSnapYamlAppAutostart(c *C) {
yAutostart := []byte(`name: wat
version: 42
apps:
foo:
command: bin/foo
autostart: foo.desktop
`)
info, err := snap.InfoFromSnapYaml(yAutostart)
c.Assert(err, IsNil)
app := info.Apps["foo"]
c.Check(app.Autostart, Equals, "foo.desktop")
yNoAutostart := []byte(`name: wat
version: 42
apps:
foo:
command: bin/foo
`)
info, err = snap.InfoFromSnapYaml(yNoAutostart)
c.Assert(err, IsNil)
app = info.Apps["foo"]
c.Check(app.Autostart, Equals, "")
}
func (s *YamlSuite) TestSnapYamlAppCommonID(c *C) {
yAutostart := []byte(`name: wat
version: 42
apps:
foo:
command: bin/foo
common-id: org.foo
bar:
command: bin/foo
common-id: org.bar
baz:
command: bin/foo
`)
info, err := snap.InfoFromSnapYaml(yAutostart)
c.Assert(err, IsNil)
c.Check(info.Apps["foo"].CommonID, Equals, "org.foo")
c.Check(info.Apps["bar"].CommonID, Equals, "org.bar")
c.Check(info.Apps["baz"].CommonID, Equals, "")
c.Assert(info.CommonIDs, HasLen, 2)
c.Assert((info.CommonIDs[0] == "org.foo" && info.CommonIDs[1] == "org.bar") ||
(info.CommonIDs[1] == "org.foo" && info.CommonIDs[0] == "org.bar"),
Equals,
true)
}
func (s *YamlSuite) TestSnapYamlCommandChain(c *C) {
yAutostart := []byte(`name: wat
version: 42
apps:
foo:
command: bin/foo
command-chain: [chain1, chain2]
hooks:
configure:
command-chain: [hookchain1, hookchain2]
`)
info, err := snap.InfoFromSnapYaml(yAutostart)
c.Assert(err, IsNil)
app := info.Apps["foo"]
c.Check(app.CommandChain, DeepEquals, []string{"chain1", "chain2"})
hook := info.Hooks["configure"]
c.Check(hook.CommandChain, DeepEquals, []string{"hookchain1", "hookchain2"})
}
func (s *YamlSuite) TestSnapYamlRestartDelay(c *C) {
yAutostart := []byte(`name: wat
version: 42
apps:
foo:
command: bin/foo
daemon: simple
restart-delay: 12s
`)
info, err := snap.InfoFromSnapYaml(yAutostart)
c.Assert(err, IsNil)
app := info.Apps["foo"]
c.Assert(app, NotNil)
c.Check(app.RestartDelay, Equals, timeout.Timeout(12*time.Second))
}
func (s *YamlSuite) TestSnapYamlSystemUsernamesParsing(c *C) {
y := []byte(`name: binary
version: 1.0
system-usernames:
foo: shared
bar:
scope: external
baz:
scope: private
attr1: norf
attr2: corge
attr3: ""
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, IsNil)
c.Check(info.SystemUsernames, HasLen, 3)
c.Assert(info.SystemUsernames["foo"], DeepEquals, &snap.SystemUsernameInfo{
Name: "foo",
Scope: "shared",
})
c.Assert(info.SystemUsernames["bar"], DeepEquals, &snap.SystemUsernameInfo{
Name: "bar",
Scope: "external",
})
c.Assert(info.SystemUsernames["baz"], DeepEquals, &snap.SystemUsernameInfo{
Name: "baz",
Scope: "private",
Attrs: map[string]interface{}{
"attr1": "norf",
"attr2": "corge",
"attr3": "",
},
})
}
func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadType(c *C) {
y := []byte(`name: binary
version: 1.0
system-usernames:
a: true
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, ErrorMatches, `system username "a" has malformed definition \(found bool\)`)
c.Assert(info, IsNil)
}
func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadValue(c *C) {
y := []byte(`name: binary
version: 1.0
system-usernames:
a: [b, c]
`)
info, err := snap.InfoFromSnapYaml(y)
c.Assert(err, ErrorMatches, `system username "a" has malformed definition \(found \[\]interface {}\)`)
c.Assert(info, IsNil)
}
func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadKeyEmpty(c *C) {
y := []byte(`name: binary
version: 1.0
system-usernames:
"": shared
`)
_, err := snap.InfoFromSnapYaml(y)
c.Assert(err, ErrorMatches, `system username cannot be empty`)
}
func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadKeyList(c *C) {
y := []byte(`name: binary
version: 1.0
system-usernames:
- foo: shared
`)
_, err := snap.InfoFromSnapYaml(y)
c.Assert(err, ErrorMatches, `(?m)cannot parse snap.yaml:.*`)
}
func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadValueEmpty(c *C) {
y := []byte(`name: binary
version: 1.0
system-usernames:
a: ""
`)
_, err := snap.InfoFromSnapYaml(y)
c.Assert(err, ErrorMatches, `system username "a" does not specify a scope`)
}
func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadValueNull(c *C) {
y := []byte(`name: binary
version: 1.0
system-usernames:
a: null
`)
_, err := snap.InfoFromSnapYaml(y)
c.Assert(err, ErrorMatches, `system username "a" does not specify a scope`)
}
func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadAttrKeyEmpty(c *C) {
y := []byte(`name: binary
version: 1.0
system-usernames:
foo:
scope: shared
"": bar
`)
_, err := snap.InfoFromSnapYaml(y)
c.Assert(err, ErrorMatches, `system username "foo" has an empty attribute key`)
}
func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadAttrKeyNonString(c *C) {
y := []byte(`name: binary
version: 1.0
system-usernames:
foo:
scope: shared
1: bar
`)
_, err := snap.InfoFromSnapYaml(y)
c.Assert(err, ErrorMatches, `system username "foo" has attribute key that is not a string \(found int\)`)
}
func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadAttrValue(c *C) {
y := []byte(`name: binary
version: 1.0
system-usernames:
foo:
scope: shared
bar: null
`)
_, err := snap.InfoFromSnapYaml(y)
c.Assert(err, ErrorMatches, `attribute "bar" of system username "foo": invalid scalar:.*`)
}
func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadScopeNonString(c *C) {
y := []byte(`name: binary
version: 1.0
system-usernames:
foo:
scope: 10
`)
_, err := snap.InfoFromSnapYaml(y)
c.Assert(err, ErrorMatches, `scope on system username "foo" is not a string \(found int\)`)
}
func (s *YamlSuite) TestSnapYamlLinks(c *C) {
yLinks := []byte(`name: my-snap
version: 1.0
links:
donations:
- https://donate.me
contact:
- mailto:me@toto.space
- https://toto.space
bug-url:
- https://github.com/webteam-space/toto.space/issues
website:
- https://toto.space
source-code:
- https://github.com/webteam-space/toto.space
`)
info, err := snap.InfoFromSnapYaml(yLinks)
c.Assert(err, IsNil)
c.Check(info.Links(), DeepEquals, map[string][]string{
"bug-url": {"https://github.com/webteam-space/toto.space/issues"},
"contact": {"mailto:me@toto.space", "https://toto.space"},
"donations": {"https://donate.me"},
"source-code": {"https://github.com/webteam-space/toto.space"},
"website": {"https://toto.space"},
})
c.Check(info.Contact(), Equals, "mailto:me@toto.space")
}
func (s *YamlSuite) TestSnapYamlEmptyLinksKey(c *C) {
yLinks := []byte(`name: my-snap
version: 1.0
links:
"":
- htps://toto.space
`)
_, err := snap.InfoFromSnapYaml(yLinks)
c.Check(err, ErrorMatches, `links key cannot be empty`)
}
func (s *YamlSuite) TestSnapYamlInvalidLinksKey(c *C) {
yLinks := `name: my-snap
version: 1.0
links:
%s:
- link.website
`
invalid := []string{
"--",
"1-2",
"aa-",
}
for _, k := range invalid {
_, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(yLinks, k)))
c.Check(err, ErrorMatches, fmt.Sprintf(`links key is invalid: %s`, k))
}
}