Parsing `snap known` output

As part of our Ubuntu cloud image validation testing, we want to ensure that the model assertion that we expect to have in place on images has actually been put in place by the build system before we release an image. My current plan is to just pick out the brand-id and model lines from the output of snap known serial. I initially wanted to just parse the entire output, but it’s not in a machine-readable format (or, at least, it isn’t in a common format).

Is there a way that I can get snap known to output a known machine-readable format, so our code could just parse it as (JSON|YAML|…)? Is there somewhere on the filesystem (with a stable format) that I could read instead?

If not, could we consider adding a --format or --output flag which would do what I’m looking for?

1 Like

that’s what we do also in our own tests:

it is machine-readable, it’s really in the assertion format, but the only officially supported reader ATM is the asserts package in snapd itself, OTOH the format is robust for that sort of line picking.

What I’ve ended up doing is removing the signature and then parsing the top part of the output as YAML:

def snap_known_serial():
    """
    Run `snap known serial` and return a parsed dict.
    """
    output = check_output(['snap', 'known', 'serial'])
    # Split the yaml information from the signature at the end of the output
    yaml_part = output.split('\n\n')[0]
    return yaml.load(yaml_part)

This seems to be working.

Yeah, when I originally posted, I hadn’t grokked that the top half is YAML. I do still think it would be still advantageous to provide an output mode that can be parsed as a standard format, so that people don’t have to rediscover this every time. :slightly_smiling_face:

the top half is not quite YAML, because there’s no quoting even when YAML would require it, because the basic format is line oriented

to be clear is just happens that the serial assertions you are dealing with have no values that would upset YAML, but in general this would be legal for example:

serial: #42

OK, sigh; guess my wetware YAML parser was correct after all. Given this, it would be really nice to be able to get snap known to spit out something non-NIH’d.

This is what I’m doing for now:

def snap_known_serial():
    """
    Run `snap known serial` and return a parsed dict.

    This currently returns a subset of the actual data from `snap known
    serial`, to avoid having to handle parsing multi-line output.

    (This would be much easier if/when
    https://forum.snapcraft.io/t/parsing-snap-known-output/5049 is resolved.)
    """
    output = check_output(['snap', 'known', 'serial'])
    # XXX: Handle multi-line values
    key_value_lines = (line for line in output.splitlines()
                       if ':' in line and not line.endswith(':'))
    key_value_tuples = (line.split(':', 1) for line in key_value_lines)
    return {key.strip(): value.strip() for key, value in key_value_tuples}