Refresh control

Every snap updates automatically, and by default, the snapd daemon checks for updates 4 times a day. Each update check is called a refresh.

For snap developers, autonomous updates can occasionally cause issues. A snap may update while it’s being used for ongoing video playback, for example, or it may be providing a critical service for flying a drone.

In such cases, it can be beneficial to have greater control over when a snap is refreshed, and the impact that a refresh has on any dependent snaps.

Dependent snaps include those that:

For snap developers, the gate-auto-refresh hook, and its associated snapctl refresh commands, can be used to control when a snap is refreshed and how a refresh interacts with any dependent snaps.

This feature is currently considered experimental and requires an experimental feature-flag to be set before use:

$ sudo snap set system experimental.gate-auto-refresh-hook=true

Experimental refresh control also requires snapd version 2.53+ .


The gate-auto-refresh hook

The gate-auto-refresh hook is executed by snapd for every snap that will be updated with the next automatic refresh. It’s also executed for every snap that is dependent on a snap that will be updated.

This hook is capable of executing the snapctl refresh command with 3 specific arguments, hold, proceed and pending:

snapctl refresh --hold

Requests that snapd does not refresh the calling snap, nor the snaps it depends upon, during the current automatic refresh. A snap can hold its own refresh for up to 90 days and other snaps for up to 48 hours. The command prints an error and returns a non-zero exit status if these deadlines are reached and the refresh can no longer be held.

snapctl refresh --proceed

Signals to snapd that a refresh can proceed for both the calling snap and the snaps it depends upon. This does not necessarily mean the update will happen, because they may be held by other snaps, and snapd only proceeds with auto-refresh after consulting gate-auto-refresh hooks of all potentially affected snaps.

snapctl refresh --pending

Checks whether the executing snap has a pending refresh, or will be affected by the refresh of its base snap.

The output from snapctl refresh --pending includes the following details:

  • pending: none, inhibited or ready
  • channel: tracking-channel
  • version: version (only if there is a pending refresh for the snap itself)
  • revision: revision (only if there is a pending refresh for the snap itself)
  • base: true or false (true if the snap is affected by refresh of its base snap)
  • restart: true or false (true if refresh will cause system restart)

The pending output value is set to “none” if there is no pending refresh for the snap and the value is “ready” if there is a pending refresh. A pending value of “inhibited” indicates that the next refresh is inhibited because one or more of the snap’s applications are running. This currently requires the experimental refresh app awareness feature to be enabled (see below).

The snap-refresh-control interface

The snapctl refresh --proceed command can be executed by a snapped application outside of the gate-auto-refresh hook if the snap has the snap-refresh-control interface and the interface is connected. This enables the snap to trigger an auto-refresh outside of the normal auto-refresh schedule and should be used cautiously.

Please note that the “snapctl refresh” commands cannot be used from hooks other than gate-auto-refresh hook.

If the gate-auto-refresh-hook doesn’t invoke “snapctl refresh --proceed” or “snapctl refresh --hold” commands and exits with exit code 0, the refresh proceeds normally as if the hook didn’t exist (except for respecting “inhibited” status if refresh app awareness is in use).

If the hook fails with an error, snapd assumes “hold” as long as the maximum deadline hasn’t been reached.

2 Likes

Refresh control is tailor made for the use-cases where a snap needs to self-manage its upgrade cycle, such for as Desktop applications, tools like microk8s or multipass, IoT devices, etc. It complements well the option for the upgrade cycle to be externally managed, using cohorts for the use-case where the snap is managed by something like a Charmed Operator or manually by an sysadmin, especially where upgrades need to be coordinated across multiple machines.

Should the title be different to disambiguate this content from Refresh Control (the feature)?

That’s a good point. I think you’re right. How about something more like Refresh control with snapctl?

We seem to have a clash of names with the Refresh Control feature (I am not sure where that doc is now). I’m guessing this new hook should be documented here, the snapctl part should be documented here, and this new feature-level guide should be incorporated into a new top level section “Managing Refreshes” which lists/covers all such methods, where this is a new device-side method. (Maybe this proposed new section would also cover Refresh Control, Validation Sets, Managed Updates, Snap Store Proxy Overrides, and maybe Refresh App Awareness.)

Thanks @kyleN. I was going to add to the hook and snapctl docs after this landed. I think you’re right in that the various docs that deal with refresh control need to be better arranged and referenced so there’s no naming confusion. I’ll do this.

Is this feature possible only for automatic refresh? Can I control and conditionally stop manual refresh as well with this?

The alleged explanation, “The gate-auto-refresh hook is executed by snapd for every snap that will be updated with the next automatic refresh. It’s also executed for every snap that is dependent on a snap that will be updated.” says nothing about when that hook is actually invoked.

So that hook is executed by snapd for certain snaps … when?

Is it worth mentioning the upcoming “–hold=forever” option?

Two questions about gate-auto-refresh hook (one which I asked before).

First, when is that hook script run? That is, what precisely is the action that triggers it? Is it snapd just about to perform a refresh operation?

Second, this page suggests that hook is still experimental. Is that true?

It would be great to see the gate-auto-refresh hook documented along with the other hooks in Supported snap hooks and wherever that page migrated to in the ReadTheDocs.

I have a few questions at the moment:

  1. Does the gate-auto-refresh hook run before or after the pre-refresh hook? And if it runs before but sets snapctl refresh --hold, does that prevent the pre-refresh hook from running?
  2. How does snapctl refresh --hold and snapctl refresh --proceed called from within hooks/scripts interact with settings the user has set with snap refresh --hold <snapname>? If the user has set a hold, is the snap capable of un-holding itself (I assume that would be bad)?
  3. I’m curious about the output of snapctl refresh --pending: does it return “inhibited” if there was a snapctl refresh --hold and/or snap refresh --hold <snapname>?
  4. What happens when snapctl refresh --proceed is called? Does it immediately trigger a refresh (assuming a previous refresh was held by a gate-auto-refresh hook call to snapctl refresh --hold)? Does the gate-auto-refresh hook get run again, and/or the pre-refresh hook?
  5. If the snap refresh is inhibited by a hold on one of its dependencies, what happens if snapctl refresh --proceed is called? I assume it still doesn’t proceed until the dependency is un-held?

I think refresh control could be an excellent solution to the problem of creating e.g. database backups before performing a refresh. There’s a timeout in the pre-refresh hook (30s I believe), so if the database backup would take too long, it could be killed and the refresh would fail. I think the gate-auto-refresh hook could be used to avoid this by holding the refresh, triggering a backup service to run separately, then that backup script would run snapctl refresh --proceed when it’s done creating the backup.