Validation-set prefer-enforce mode ignored on snapd REST API (2.63)

Hello,

Some background: I am making custom Ubuntu Core images with a model assertion with a validation-set inside. In the model assertion I have set the validation-set to mode “prefer-enforce”, so that I can update the validation-set later inside the device:

...
    "validation-sets" : [
        {
            "account-id" : "my-account-id",
            "name" : "my-validation-set",
            "sequence" : "3",
            "mode" : "prefer-enforce"
        }
    ],
...

After booting the image, I can successfully change the validation-set enforced on the device by running something like

$ snap validate --enforce --refresh my-account-id/my-validation-set=4
Enforced validation set "my-account-id/my-validation-set=4"

I want to do this action through the snapd REST API (because I want to run it from Landscape, as we don’t have access to the shell of our devices we want to update).

Here is the script I used
#!/usr/bin/env python3 

# VARIABLES

validationset_name = "my-validation-set"
validationset_sequence = 4
validationset_accountid = "my-account-id"



# IMPORTS

import argparse
import requests
import socket
import os



# SNAPD SETUP

from urllib3.connection import HTTPConnection
from urllib3.connectionpool import HTTPConnectionPool
from requests.adapters import HTTPAdapter

class SnapdConnection(HTTPConnection):
    def __init__(self):
        super().__init__("localhost")

    def connect(self):
        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        self.sock.connect("/run/snapd.socket")

class SnapdConnectionPool(HTTPConnectionPool):
    def __init__(self):
        super().__init__("localhost")

    def _new_conn(self):
        return SnapdConnection()

class SnapdAdapter(HTTPAdapter):
    def get_connection(self, url, proxies=None):
        return SnapdConnectionPool()

session = requests.Session()
session.mount("http://snapd/", SnapdAdapter())

base_url = "http://snapd/v2/"




## SCRIPT

print("Triggering validation set update")
headers = {
    "Content-Type": "application/json"
}
payload = {
    "action" : "apply",
    "mode" : "enforce",
    "sequence" : validationset_sequence,
}
url = f"{base_url}validation-sets/{validationset_accountid}/{validationset_name}"

print(f"Doing request to {url} with payload {payload}")
response = session.post(url, headers=headers, json=payload).json()

print(response)

However, in the snapd API the validate option seems to run into a validation check that prevents this validation from working:

charlee@946146c3-3e6d-4433-aa85-e7ffb1a5335a:~$ sudo python3 ./update-validation.py 
Triggering validation set update
Doing request to http://snapd/v2/validation-sets/my-account-id/my-validation-set with payload {'action': 'apply', 'mode': 'enforce', 'sequence': 4}
{'type': 'error', 'status-code': 400, 'status': 'Bad Request', 'result': {'message': 'cannot enforce validation set: cannot enforce sequence 4 of validation set my-account-id/my-validation-set: only sequence 3 allowed by model'}}

Is this a bug? Or am I trying something that is not supported? (It seems like the refresh option is also not supported on the API :frowning: )

How do you suggest to update validation-sets on IoT devices for which we have no shell access?

I am doing this on Ubuntu Core 22 with snapd 2.63

@Charlee ! Maybe by checking the current sequence, you could fix it. :face_with_monocle:

This line could be switched : validationset_sequence = 3 # e.g -> 3

@Charlee ! First of all, you can do something like this :

import requests

base_url = "http://snapd/v2/"
validationset_name = "my-validation-set"
validationset_accountid = "my-account-id"

url = f"{base_url}validation-sets/{validationset_accountid}/{validationset_name}"

response = requests.get(url)
if response.status_code == 200:
    data = response.json()
    current_sequence = data.get("sequence")
    print(f"Current sequence for {validationset_name}: {current_sequence}")
else:
    print(f"Failed to get validation set details: {response.status_code} {response.text}")

I am not sure if you got the question…

My problem is that I want to increase/bump the sequence number, like done in the CLI example I gave.

However, the same action is not possible when I use the snapd REST API, because it checks whether the “bump” is allowed, and according to the rest API check, it is not. The reason it says its not, is because in the model the validation-set was given with sequence 3. However, in the model assertion the validation-set was given in mode “prefer-enforce”, which should allow increasing the sequence number of the validation-set.

Okay ! I thought the problem was related to coding or something along those lines.