Command line interface to manipulate services

As a follow up from a point raised by @pedronis elsewhere, perhaps we can improve a bit the output of “snap services” so reading it is easier to tell what the output means at a glance.

The output today looks something along these lines:

$ snap services
Snap   Service  Enabled  Active
foo    bar-00   false    false
foo    bar-01   false    true
foo    bar-10   true     false
foo    bar-11   true     true

A couple of issues with this:

  • on a long list, it won’t be clear what true/false means anymore
  • when grepping, it will be hard to tell because headers are gone
  • it’s not very visually appealing (wall of bools, as @pedronis pointed out)

So, here is a suggestion which tweaks the output slightly:

$ snap services
Snap   Service  Startup   Current
foo    bar-00   disabled  inactive
foo    bar-01   disabled  active
foo    bar-10   enabled   inactive 
foo    bar-11   enabled   active

How does that look?

At the standup today we talked about how having a list of booleans wasn’t particularly friendly, and that while any of the four combinations were allowed, we could present the ones that were anomalous.

@niemeyer said he’d work on getting some options (and he beat me to it, posting it anyway), but meanwhile I thought I’d give it a try and see. First, what we’d have as the PR stands:

Snap          Service      Active  Enabled
another-snap  svc1         true    false
another-snap  svc2         true    true
some-snap     service-one  false   true
some-snap     service-two  false   false

now with the discussed “disabled and active is weird, and enabled and inactive is weird”, but the status in English:

Snap          Service      Status
another-snap  svc1         disabled but active
another-snap  svc2         active
some-snap     service-one  enabled but inactive
some-snap     service-two  disabled

or a more machine-like status,

Snap          Service      Status
another-snap  svc1         disabled,active
another-snap  svc2         active
some-snap     service-one  enabled,inactive
some-snap     service-two  disabled

thinking forward a little bit, to when something might be enabled and inactive because it’s fired by a timer,

Snap         Service      Status
timely-snap  service-two  enabled, next run at 2pm

or

Snap         Service      Status
timely-snap  service-two  enabled, runs daily@2pm

i think either works.

For observers, we were both in the same conversation elsewhere, and ended up writing down slightly different proposals out of it.

I wrote that exact one myself before sending the proposal above. I quite like it as well, but ended up deleting and sending the one above that leverages the same logic, with enabled/disabled and active/inactive explicit terminology, because it assumes less in the sense that snaps may choose to use manually started services based on internal configuration. In that sense, we’d be highlighting a specific state as awkward but for the snap itself it’s actually normal.

Other than that, I think we’re pretty much aligned.

to be clear, I prefer your proposal (although extending it for services with timers will probably result in a new column) to either of mine.

One (hopefully last) quirk: as defined above, snap restart foo will start foo if it’s not running, whereas snap restart --reload foo will not. Documenting that is tedious, and using that is confusing.

This is because --reload changes systemctl restart (which starts services that are stopped) to systemctl try-reload-or-restart (which doesn’t). There exist also try-restart and reload-or-restart, either of which would make this problem go away. Could we change it?

Thanks for catching that. +1 on using reload-or-restart instead. We can make the “don’t do anything if not running” an orthogonal flag in the future, which would act both on restart and restart --reload.

I would not name that “–try”, though. That terminology seems pretty confusing.

1 Like

this is on master. Might need a second pass to improve the task descriptions.

2 Likes

Necrobumping an old topic.

Both sockets and timers are in snapd now, but are not visible in snap services output, nor can be manipulated using snap(-ctl) <start|stop|restart> command.

I tried to experiment with this a bit and ended up with something like this:

$ snap services
Service                                       Startup   Current
lxd.activate                                  enabled   inactive
lxd.daemon                                    enabled   inactive
lxd.daemon.unix.socket                        enabled   active
test-snapd-timer-service.random-timer         disabled  inactive
test-snapd-timer-service.random-timer.timer   enabled   active
test-snapd-timer-service.regular-timer        disabled  inactive
test-snapd-timer-service.regular-timer.timer  enabled   active

Sockets end up with <socket-name>.socket suffix, while timers (there is only one per service), are suffixed with .timer.

The the manipulation could be done like this:

# stop the socket
$ snap stop lxd.daemon.unix.socket
# start the service
$ snap stop lxd.daemon.unix
# start the timer
$ snap start test-snapd-timer-service.regular-timer.timer
# logs of the timer (i.e. when it fired)
$ snap logs test-snapd-timer-service.regular-timer.timer

No need for extra --timer|--socket switches. One can just copy & paste the service/socket/timer name.

I already have some changes and can propose a branch. I’m open to suggestions.

In my branch here, I implemented the ability for snap stop and snapctl stop to stop/disable the socket and timer services alongside the original service when stopped so that snap stop lxd.daemon stops both the associated service and the socket (and same thing for timers).

What is an example use case of being able to start/stop the sockets/timers independently of the service?

@mborzecki That proposal makes sense if you look from the perspective of systemd units, but this feels like leaking an implementation detail that is not necessarily useful at a higher level.

If we ignore the implementation altogether, we have applications with a timer. That’s pretty straightforward… the application will be run in the schedule, as defined. From that angle, we can also extrapolate the meaning of commands on the application:

  • stop - Seems easy… stop the application if it’s running.
  • stop --disable - Also feels straightforward… stop it now, and prevent it from running again.
  • start - Not so obvious… I’d make this run the service once, so that the next one makes sense too.
  • start --enable - Run it once and enable the timer so it runs in the future as well.

Right, @ijohnson’s question is spot on.

I agree with @niemeyer about not exposing timers / sockets as toplevel addressable things via services. I would, however, tweak snap services output to better support services that are socket-activated, or have timers. E.g. for timer-based, have a note about when it’ll next be run (or if the timer has been disabled); for socket-activated, mention whether the socket unit itself is enabled/disabled. This could be done via a Notes column.

1 Like

@Chipaca Agree with most of it… just not sure about the enable/disable of socket. It seems to fall into a similar realm of the service/timer separation. How much is the socket actually independent from the service, and for what purpose? Might be good to at least postpone that part until we understand that better.

I agree, they should go hand-in-hand. When they get out of sync for some reason, that’s when snap services should point it out.

1 Like

At least for timers, the active state of the service and the timer will be out of sync most of the time as the timer is active while the service is not.

For sockets, we can have many of those. Each corresponds to some ListenStream. I can see a case when one would want to disable/stop one socket, while leaving the rest enabled/started.

How about this format for snap services:

$ snap services
Service                                       Startup   Current   Notes
lxd.activate                                  enabled   inactive  -
lxd.daemon                                    enabled   inactive  -
lxd.daemon.unix                               enabled   active    socket
test-snapd-timer-service.random-timer         disabled  inactive  timer
test-snapd-timer-service.regular-timer        enabled   active    timer

For timers, the Current and Startup columns, show the status of the timer unit. Maybe we could show the status of the *.service associated with the timer in the notes column? Eg.:

$ snap services
Service                                       Startup   Current   Notes
...
test-snapd-timer-service.regular-timer        enabled   active    timer,running

Although the output may be confusing at times as the timer service can be started outside of defined timer window (eg. when the schedule cannot be expressed by systemd timer syntax and is effectively gated by snap run).

Can you describe this use case more fully? I still don’t see a full use case where you need to be able to differentiate between different sockets associated with a single service. To me, it seems like if you do need this you should have different services, no?

you could have a tcp socket you only want enabled when the service is properly secured, for example.

Let’s call socket/timer units “activator” units, of which a service can have multiple.

  • where all the activator units are in the same state, Startup and Current talk about the these units; in Notes, we add a socket-activated or timer-activated (or both), and if the main service is actually running, e.g. socket-activated,running.
  • if the activator units aren’t in the same state, but at least one of them is enabled/active, the Startup/Current column talks about that unit, and say x-activated[,running],see-verbose, with --verbose being a new thing that outputs a more hierarchical and detailed description (you could even include mount units here if appropriate). If the unit that is enabled is not the unit that is active, then flag that somehow also (but also point to --verbose).

this should cover the ~main use case still within snap services, and guide users to find out more.

One thing we could do to simplify formatting of --verbose (or whatever we call it) is only allow a single thing there, instead of the more generous snap services input. Maybe snap services --detailed-service=lxd.daemon or something.

Let’s see how that could look like:

$ snap services
Service                                       Startup   Current   Notes
lxd.activate                                  enabled   inactive  -
lxd.daemon                                    enabled   active    socket-activated,running
owner-of.many-sockets                         enabled   active    socket-activated,see-verbose
test-snapd-timer-service.random-timer         disabled  inactive  timer-activated
test-snapd-timer-service.regular-timer        enabled   active    timer-activated,running

And the detailed/verbose output:

$ snap services --verbose owner-of.many-sockets
Service                                       Startup   Current
owner-of.many-sockets.one                     enabled   active
owner-of.many-sockets.two                     enabled   inactive
owner-of.many-sockets.three                   disabled  inactive

Where one, two, three, would be the socket names.

We have had some recent feedback that the behavior of the snap start|restart|stop <snap> variants is not always what is expected, especially in the presence of disabled services: https://bugs.launchpad.net/snapd/+bug/1803212

especially the fact that snap restart <snap> now starts not running disabled services as well can be considered unexpected and problematic. We are considering this different set of behaviors:

  • snap stop SNAP should stop all the running services of the snap

  • snap restart SNAP would restart all the running services of the snap (even if disabled) but not disabled and not running ones. This is a change from current behavior also because it means a failed service would not be restarted by this, one would need to use the service-specific variant for that.

  • snap start SNAP would start all the enabled services of the snap. Right now it starts all services.

We could move to this behavior if nothing is deeply relying on the fine points of the current behavior (and introduce possibly flags to get the current behavior). If changing the current behavior along these lines causes real breakage we would have to instead offer a set of flags to get this new behavior.

We are interested on feedback on this?

I think the case for an “enabled and not running” service not being started with snap restart SNAP is wrong. I think failed services should be started, and also what about oneshot daemons? I think that oneshot daemons should also be started with snap restart, but only if the oneshot is enabled.

On a related note, is there a way to distinguish between a “succeeded” and “failed” oneshot daemon? The snap services output doesn’t seem provide any way to do so.

1 Like