Supported snap hooks

A hook is an executable file that runs within a snap’s confined environment when a certain action occurs.

Common examples of actions requiring hooks include:

  • notifying a snap that something has happened
    Example: If a snap has been upgraded, the snap may need to trigger a scripted migration process to port an old data format to the new one.

  • notifying a snap that a specific operation is in progress
    Example: A snap may need to know when a specific interface connects or disconnects.

A hook is defined as an executable within a snap’s meta/hooks/ directory, and consequently, also within snap/hooks/ when building with snapcraft. See Snapcraft hook support for more information.

The filename of the executable is based on the name of the hook. If this file exists, snapd will execute the file when required by that hook’s action.

Default shell environment: A hook script can only assume a POSIX-compliant shell environment for its execution. If your script needs a specific shell, such as Bash or Zsh, it needs to be explicitly declared within the script’s hashbang header (#!/bin/bash, for example). Your snap also needs to ensure your chosen shell is available.

Accessing resources

If a hook requires access to system resources outside of a snap’s confined environment, it will need to use slots and plugs via the interface mechanism to access those resources.

When using Snapcraft to build the snap, the interface definition will go inside snapcraft.yaml, and the snapcraft command create a snap.yaml within the snap to hold the required metadata.

For example, the following excerpt registers an install hook making use of a network plug:

apps:
    ...

hooks:
    install:
        plugs: [network]

Hooks are called with no parameters. When a hook needs to request or modify information in within snapd, they can do so via the snapctl tool, which is always available within a snap’s environment. See Using the snapctl tool for further details.

A hook is executed as a single transaction, where a transaction object holds all the configuration changes for that hook. These changes are invisible to the running system until the hook completely finishes. This allows for changes to be rolled back or unset if errors occur during the execution of a hook.

The configure hook

The configure hook is called every time one the following actions happen:

  • initial snap installation
  • snap refresh
  • whenever the user runs snap set|unset to change a configuration option

ⓘ Note that this hook will not get called when the snap itself changes configuration options using snapctl get|set|unset.

The hook should use snapctl get to retrieve configuration values from snapd. If the hook exits with a non-zero status code, the configuration will not be applied.

For example, given the following command:

$ snap set mysnap username=foo password=bar

The configure hook located within the mysnap snap at meta/hooks/configure would be called to apply the configuration changes, if necessary.

The hook might look similar to:

#!/bin/sh -e

username="$(snapctl get username)"
password="$(snapctl get password)"

if [ -z "$username" -o -z "$password" ]; then
    echo "Username and password are required."
    exit 1
fi

mkdir -m 0600 $SNAP_DATA/options
echo "username: $username" > $SNAP_DATA/options/credentials
echo "password: $password" >> $SNAP_DATA/options/credentials

The same hook can also modify the configuration of a snap within the context of the current transaction. This is accomplished using snapctl set and snapctl unset. For more information see Adding Snap configuration and Using the snapctl tool.

ⓘ Note that configuration options do not need to be defined anywhere. snapctl set and snap set will accept any (valid) option name.

The install hook

The install hook is called upon initial install only, i.e. it’s not called on subsequent refreshes.

The hook is executed before starting snap services (if it has any) and before the configure hook. The install hook is the place for one-time actions, such as an early initialisation of a resource when installed for the first time.

The interface hooks

Interface hooks are executed when an interface is either connected or disconnected via the interface’s plugs and slots mechanism.

They can be used to read or write attributes from a connection and, for example, acquire new resources, update internal options or update databases.

For further details, see Interface hooks.

The pre-refresh hook

The pre-refresh hook is called whenever the snap gets refreshed.

This hook is executed for the already installed revision of the snap with its services still running (if the snap has any services) and before switching to the newly installed revision.

This hook is a good place for any maintenance or cleanup actions that prepare the snap for switching to the new revision. It’s also a good place to test whether a refresh will succeed, because if the test causes the hook to fail, the refresh will not proceed.

The post-refresh hook

The post-refresh hook is similar to pre-refresh (above) in that it is called whenever the snap gets refreshed.

This hook is executed for the newly installed snap, before starting new services (if applicable). This hook is a good place for any extra actions that need to be performed for the new revision of the snap. It’s also a good place to test whether a refresh has succeeded, because if the test causes the hook to fail, the refresh will be rolled-back and the original state of the snap restored.

The remove hook

The remove hook is called when the last revision of the snap gets removed from the system.

This hook is executed after stopping the services of the snap (if the snap has any services), therefore it’s useful for any custom cleanup logic.

The prepare-device hook

This hook is only supported in gadget snaps.

See The gadget snap documentation for more details.

1 Like

Content moved out of the wiki.

… and now polished it a bit further.

1 Like

I think we should add set -e to the configure hook example - any objections?

1 Like

I think this should also link/mention the interface hooks from here: Interface hooks

Thanks for the suggestion, but if I understand correctly, it already does (following the paragraph on interface hooks). We should maybe make the link more prominent.

Ah sorry I misread thanks

1 Like

Is it possible to see what install hook prints to the console? Here’s my hook, a slightly modified example version:

#!/bin/sh -e

# Create a default config file
echo "BEFORE BEFORE"
echo "sleep_time 5" > "$SNAP_DATA/hello.conf"
echo "AFTER AFTER"

The hello.conf gets created, but there’re no messages in the console where snap install was executed.

The output is only shown if the hook exits with non-zero exit code for debugging purposes.

What information are you trying to show to the user during the install?

I’m trying to tell the user that manually connecting plugs is required after installation. Otherwise most of the functionality won’t work and the snap is useless (it needs several “unsafe” interfaces which don’t autoconnect).

Have you tried requesting auto-connection through the forum request documented at Process for aliases, auto-connections and tracks ?

Additionally, you can prompt them from a GUI window or a message from the terminal when they first use your application. For example, a zenity GUI prompt is implemented here: https://github.com/Lin-Buo-Ren/guvcview-snap/blob/master/snap/local/launchers/guvcview-launch#L12-L17

Exactly what happens if a hook fails? For example, can a pre-refresh or post-refresh hook be used to prevent a snap refreshing if the hook determines that the refresh won’t work? Is this defined behaviour that we can rely upon?

1 Like

Yes this behavior can be relied on.

2 Likes

I’ve added a some extra text to the pre- and post-refresh hooks to hopefully make this behaviour and use-case clearer (cc @rbasak).

3 Likes

What’s the precise context in which a hook is run? I’d like a post-refresh hook run git-ubuntu.self-test (self-test is defined in snapcraft.yaml as an app) to verify that the new snap will work correctly. self-test already has a carefully defined environment that matches the main git-ubuntu app’s environment precisely. How can I ensure that when I invoke this from the post-refresh hook, the context is identical? Do I need to manually set the environment from inside the hook to the same as that defined in snapcraft.yaml against self-test? Or is there a better way?

Yes

Unfortunately not, what can make this slightly better is that if you don’t use the environment keyword for your self-test app, and do all setup except for running the app itself inside a script, you can then specify that script as a command-chain which can be shared between the app and the post-refresh hook with yaml like this:

hooks:
  post-refresh:
    command-chain:
      - setup-self-test-env.sh
apps:
  self-test:
    command: do-the-test.sh
    command-chain:
      - setup-self-test-env.sh

then in your post-refresh hook you just call do-the-test.sh directly. Alternatively, you could make the post-refresh file itself a symlink to do-the-test.sh if you don’t need to do anything else in the post-refresh hook

1 Like

One thing I do not see documented is when to these hooks run relative to others and if the run before or after interfaces are connected