Can plugs be added to the global section to affect all apps in the snap?
example:
name: someplug
...
apps:
example:
...
plugs:
wayland:
x11:
Can plugs be added to the global section to affect all apps in the snap?
example:
name: someplug
...
apps:
example:
...
plugs:
wayland:
x11:
Hi,
You have two options, you can either declare all plugs globally and declare no plugs in any app or hook, which would look like this:
apps:
example:
command: bin/sh
example2:
command: bin/sh2
hooks:
configure:
plugs:
wayland:
x11:
or you can declare them for every app/hook like so:
apps:
example:
command: bin/sh
plugs:
wayland:
x11:
example2:
command: bin/sh2
plugs:
wayland:
x11:
hooks:
configure:
plugs:
wayland:
x11:
These two are equivalent for all of the hooks and apps in the snap.
But what you should not do is mix the two patterns because then the plugs declared globally become like a “pool” where they get used up by the first things that declare them and don’t apply to anything else implicitly. For example this will not work the way one expects it:
apps:
example:
command: bin/sh
plugs:
wayland:
x11:
opengl:
example2:
command: bin/sh2
hooks:
configure:
plugs:
wayland:
x11:
What happens here is that the global plugs wayland
and x11
will only apply to the example
app, and the example2
app and the configure hook have no plugs declared at all, which is usually undesirable, but is specifically useful for interfaces that require attributes to be set.
Thank you. This makes it clearer.
About the bug in snapcraft 3.9.x i will talk elsewhere.
@ijohnson
One more question:
If an app inside the app is not listed under apps. Would then the global plugs be applied?
Can you provide an example yaml? I don’t quite follow how you could have an app not under apps.
Sorry I had not seen this thread before reviewing plugs: plugs can have no element by aiqs4 · Pull Request #2840 · canonical/snapcraft · GitHub.
While I don’t really change my position in that PR, I’m a bit perplexed as to @ijohnson’s assertion:
I have not seen documented anywhere? I assume you are correct, but I think we should consider either normalizing the behavior (global plugs apply to all apps regardless of use of per-app plugs), or have snapcraft error when we come across this scenario. Is there a rationale for the current behavior, or just a limitation of the existing implementation?
That’s exactly what I thought intuitively.
Unfortunately yes it is not documented anywhere at the moment, in fact I partially wrote the above to serve as a starting point for a doc that @degville is/will be working on to make this clear going forward.
One reason for this is that when we have a more complicated example like this (from Bug #1824557 “Apparmor “Multiple definitions for hat systemd_run...” : Bugs : snapd):
plugs:
k8s-kubelet:
interface: kubernetes-support
flavor: kubelet
k8s-kubeproxy:
interface: kubernetes-support
flavor: kubeproxy
where these two interfaces should be treated as effectively mutually exclusive in the snap’s hooks/daemons, because they have different flavors. Specifically the “flavors” here correspond to slightly different policy that is generated that fails to compile when combined together. So in this case, we want to be able to declare a full plug definition of the kubernetes-support
interface for the different apps/daemons. We also don’t want both of these things to be auto-generated for the implicit hooks, i.e. hooks that aren’t declared in the snap.yaml.
The other reason is that I think we want to be able to express “self-connecting” snaps with slots exposed by the snap and plugs connected to those slots all in the same snap. Here again there could be conflicting policy between the plug side and the plot side, so for example in the docker snap we have this:
plugs:
docker-cli:
interface: docker
slots:
docker-daemon:
interface: docker
apps:
docker:
...
plugs:
- docker-cli
dockerd:
plugs:
- docker-support
slots:
- docker-daemon
In this case the docker
interface is the interface that allows talking to the docker socket, not permissions to operate as docker (that interface is docker-support
), so the policy between the slot and the plug is not mutually exclusive but there are other examples, like network-manager, etc. that have more complicated policy in the slot and plug that could be mutually exclusive, so applying all top-level plugs and slots to all apps/hooks would be a bad idea.
I admit that this is confusing, I actually had implemented a patch in snapd to fix this and make all of the top-level plugs/slots apply to all hooks and apps, but this broke our unit tests and further investigation led me to my current understanding.
Additionally, there is some more history I’m not fully aware of here, namely @kyrofa’s PR from over 3.5 years ago: snap: add parsing support for hooks. by kyrofa · Pull Request #1240 · canonical/snapd · GitHub, which says this about binding plugs to hooks or apps this way:
Also note that hooks are factored into the equation when considering whether or not a global plug is unbound-- if it’s bound to either an app OR a hook it’s considered bound for both, in which case the plug will not be bound to all apps and hooks. This is up for discussion.
However I can’t find anywhere that that discussion happened or what were other reasons to do it this way.
Hoo boy, hey there archeologist . That was the first in a long line of PRs adding the initial hooks feature to snapd. It didn’t change the logic of how global plugs were bound to apps, just included hooks in the picture. I’m afraid I have no additional insight into why it was designed this way originally.
Assuming my memory is correct, before hooks were a thing, in the code, each plug, slot, and app was essentially a single object that tracks its connections. As a quick example, consider:
apps:
app1:
command: command1
plugs: [plug1]
app2:
command: command2
plugs: [plug1]
app3:
command: command3
plugs:
plug2:
In this case, app1
knows that it’s using plug1
. app2
also knows it’s using plug1
. Likewise, plug1
knows that it’s being used by both app1
and app2
. However, app3
knows it’s not using any plugs. Similarly, plug2
knows that no apps are using it. In the paragraph quoted by @ijohnson, plug2 is what I called “unbound”, i.e. it wasn’t bound to an app, therefore snapd applied it to ALL apps, 1, 2, and 3. If, instead, app3
looked like this:
app3:
command: command3
plugs: [plug2]
Then plug2
would NOT be unbound (it would be bound to app3
), and would thus NOT apply to app1
or app2
.
This particular PR introduced the concept of a hook simply by adding hooks as one more object that was treated exactly like just another app as far as the binding logic is concerned. Basically, unbound plugs would apply to all apps AND hooks. Also, if a hook used a plug, that plug was considered bound, even when it came to apps, thereby NOT applying to all the apps (or the other hooks). If an app used a plug, it was considered bound, thereby NOT applying to all the hooks (or the other apps). To be clear, take this example:
apps:
app1:
command: command1
plugs: [plug1]
app2:
command: command2
plugs: [plug1]
hooks:
hook1:
plugs:
plug2:
plug2
is unbound, thus it applies to app1
, app2
, and hook1
. Now take this:
apps:
app1:
command: command1
plugs: [plug1]
app2:
command: command2
plugs: [plug1]
hooks:
hook1:
plugs: [plug2]
plugs:
plug2:
Now plug2
is bound (it’s associated with hook1
), thus it will NOT be applied to app1
or app2
.
Does that clarify at least the paragraph I wrote a little?
Hey, that’s sound like all case’s where answered now
But it’s not very intuitive.
Maybe it should be a must, that each app or hook indicates the plug or slot that it uses.
In order to avoid duplicate definitions, groups of plugs or slots could be defined.
E. g.
apps:
app1:
command:
plugs: [ plug1, plug_group1 ]
slots: [ plug3 ]
hooks:
hook1:
plugs: [ group2, plug4 ]
plug-groups:
group1: [ plug2, plug3 ]
group2: [ plug1, plug3 ]
Then it would be possible to remove global plugs and slots.
I recommend that the top-level plugs
only be used to declare the configuration of a plug, and that you must specify the plugs that an app or hook requires in the apps
and hooks
stanzas. If all your apps and hooks use the same interfaces then you can use yaml aliases:
apps:
my-app:
plugs: &allplugs
- plug1
- plug2
- plug3
another-app:
plugs: *allplugs
hooks:
plugs: *allplugs
–
I’ve been googling to see if it is possible to superset an alias containing a list with extra items to be added, but can’t find any docs indicating that is possible