Confusing behavior of snapctl get

TL;DR: snapctl get .. output is confusing when asking for a root-level config key when only some of its sub-keys were updated. It returns only the updated sub-key values, ignoring other subkeys under the root key. It actually looks like a bug of snapctl and config transaction handling to me. I was made aware of this confusing behavior by @ondra

For example, let’s consider the following initial configuration:

$ sudo snap set mysnap key1.subkey1=a key1.subkey2=b

$ sudo snap get -d mysnap
{
        "key1": {
                "subkey1": "a",
                "subkey2": "b"
        }
}

And the following configure hook for debugging purposes:

#!/bin/sh
snapctl get key1 > $SNAP_COMMON/debug
snapctl get key1.subkey1 key1.subkey2 >> $SNAP_COMMON/debug

After initializing snap configuration with above snap set ..., the output of this hooks is as follows:

$ cat /var/snap/mysnap/common/debug                 
{
        "subkey1": "a",
        "subkey2": "b"
}
{
        "key1.subkey1": "a",
        "key1.subkey2": "b"
}

So far so good and as expected. Now, let’s update just the first subkey:

$ sudo snap set confhook key1.subkey1=c 

The output of configure hook is now:

$ cat /var/snap/mysnap/common/debug       
{
        "subkey1": "c"
}
{
        "key1.subkey1": "c",
        "key1.subkey2": "b"
}

What happened here is snapctl get key1 only printed the changed subkey1 and omitted subkey2. However, when queried using dotted notation and full path, it was able to get both subkey1 and subkey values.

It works like this at the moment because of how we treat transactions on snapctl get. When hook is executed, snapd creates a transaction object to carry config changes (so that the changes are not visible to the rest of the system until the hook finishes and we commit them). When reading the configuration via snapctl, we first look into the transaction object, and then as a fallback into the existing configuration; and this is where problem lies, because transaction only contains the changed elements in case of nested data structures, not the complete structure (for those who want to look into the code, this logic is in the Get method in transaction.go).

@pedronis What do you think?

On a side note, @ondra pointed out this behavior is currently useful to find what subkeys have changed and suggested we implement some other way (e.g. a new flag) for finding this out, if we decide to fix the above.

1 Like

Yes, this looks like a bug, snapctl get should observe the merged state (as it would be after the transaction is committed), OTOH a way to know what’s the delta/which keys were changed so far it’s reasonable but would need a dedicated spelling.

1 Like

PR to fix the problem: https://github.com/snapcore/snapd/pull/6322

1 Like