Getting only currently-deployed services via the API

Hi everyone,

When I call the /v5/service_install?$filter=device%20eq%20’<device_id>’ API, I get a list of all the services that have ever run on the device. I would like to get a list of all the services that are currently deployed on the device in the current app version. How can I do that?

Hi,
A way to achieve this is by starting from the service resource and filter based on the images that the target device has running image installs for.
If you are using the node balena-sdk you can achieve that by doing something like:

await sdk.pine.get({
	resource: 'service',
	options: {
        $filter: {
            is_built_by__image: {
                image_install: {
                    $any: {
                        $alias: 'ii',
                        $expr: {
                            ii: {
                                device: targetDeviceId,
                                status: 'Running',
                            },
                        },
                    },
                },
            }
        },
    }
});

In case that you prefer a plain API query, the equivalent one would be:

/v5/service?$filter=is_built_by__image/image_install/any(ii:(ii/device eq 1709675) and (ii/status eq 'Running'))

Let us know whether that fits the needs of your use case.

Kind regards,
Thodoris

Thank you very much @thgreasi, that fit the bill perfectly!

Thank you @thgreasi. Sorry for the long delay, this got put in the back burner for a while.

I’m trying the following (copy/pasted from your response, replacing spaces with %20):

GET https://api.balena-cloud.com/v5/service?$filter=is_built_by__image/image_install/any(ii:(ii/device%20eq%201709675)%20and%20(ii/status%20eq%20'Running'))

And I get a 400 with:

Malformed url: '/resin/service?%24filter=is_built_by__image%2Fimage_install%2Fany%28ii%3A%28ii%2Fdevice+eq+1709675%29+and+%28ii%2Fstatus+eq+%27Running%27%29%29&$format=json;metadata=full'"

any ideas?

Hello, I just tried a curl request using quotes instead of replacing the spaces with %20, and did not recieve that error. E.g., in your case that would be:

curl -X GET "https://api.balena-cloud.com/v5/service?$filter=is_built_by__image/image_install/any(ii:(ii/device eq 1709675) and (ii/status eq 'Running'))
" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <AUTH_TOKEN>"

Please could you try that and see if the error persists?
Thank you

I got this:

$ curl --verbose -X GET "https://api.balena-cloud.com/v5/service?\$filter=is_built_by__image/image_install/any(ii:(ii/device eq 1709675) and (ii/status eq 'Running'))" -H "Content-Type: application/json" -H "Authorization: Bearer $(cat $HOME/.balena/token)"
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 18.208.61.234:443...
* TCP_NODELAY set
* Connected to api.balena-cloud.com (18.208.61.234) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=balena.io
*  start date: Sep  3 00:00:00 2020 GMT
*  expire date: Oct  4 12:00:00 2021 GMT
*  subjectAltName: host "api.balena-cloud.com" matched cert's "*.balena-cloud.com"
*  issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x562e9f09a0c0)
> GET /v5/service?$filter=is_built_by__image/image_install/any(ii:(ii/device eq 1709675) and (ii/status eq 'Running')) HTTP/2
> Host: api.balena-cloud.com
> user-agent: curl/7.68.0
> accept: */*
> content-type: application/json
> authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MzQ5NzQsInVzZXJuYW1lIjoiZ2hfbWNhbW91Iiwiand0X3NlY3JldCI6IlJLMk9DUVA0UERVUE1JRUdEQ0pZRkdNR1pRTUJGTlpYIiwidHdvRmFjdG9yUmVxdWlyZWQiOmZhbHNlLCJuZWVkc1Bhc3N3b3JkUmVzZXQiOmZhbHNlLCJhdXRoVGltZSI6MTU5NzQwODc0NTk5MiwiYWN0b3IiOjI2MDQyMzcsInBlcm1pc3Npb25zIjpbXSwiZW1haWwiOiJtY2Ftb3VAdGVjbm9ndXJ1LmNvbSIsImNyZWF0ZWRfYXQiOiIyMDE4LTAzLTA1VDE0OjE2OjQ0LjAzOVoiLCJoYXNfZGlzYWJsZWRfbmV3c2xldHRlciI6ZmFsc2UsImZpcnN0X25hbWUiOiJNYXJpbyIsImxhc3RfbmFtZSI6IkNhbW91IiwiYWNjb3VudF90eXBlIjpudWxsLCJzb2NpYWxfc2VydmljZV9hY2NvdW50IjpbeyJwcm92aWRlciI6ImdpdGh1YiIsImRpc3BsYXlfbmFtZSI6Im1jYW1vdSJ9XSwiY29tcGFueSI6IiIsImhhc1Bhc3N3b3JkU2V0Ijp0cnVlLCJwdWJsaWNfa2V5Ijp0cnVlLCJmZWF0dXJlcyI6W10sImludGVyY29tVXNlck5hbWUiOiJnaF9tY2Ftb3UiLCJpbnRlcmNvbVVzZXJIYXNoIjoiOThiY2IwM2I2OTk2NGE4MDYwNmVkY2VlYmU0NTFmMmUxMzA4YTc5M2EwOGJmNDUwN2RlY2U4NDBiZDI0NDZlNyIsImlhdCI6MTYwMDk1MDAyMCwiZXhwIjoxNjAxNTU0ODIwfQ.kQqlWOK2l8oAIBQa7rplmBBvWZf6SYjY8XVrKGkxuyo
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 400 
< date: Thu, 24 Sep 2020 13:42:23 GMT
< 
* Connection #0 to host api.balena-cloud.com left intact

However, replacing the spaces with + actually worked (I got {"d":[]} but replacing the device ID with one of my own did work). Thanks!

Ah, glad it worked with the +. Will ask my colleagues for a canonical answer on that. Best regards.

I was digging through the Balena API and, since what I have is the device UUID (not its ID) I got the following:

curl -X GET "https://api.balena-cloud.com/v5/device?\$filter=uuid+eq+'$device_uuid'&\$expand=should_be_running__release" -H "Content-Type: application/json" -H "Authorization: Bearer $(cat $HOME/.balena/token)" | jq -r ".d[].should_be_running__release[].composition.services|keys[]"

It gives me all the currently-deployed services (not just those that are running) but it’s good for my purposes. Any reason not to use this?

I don’t know of any reason why you should not use this route, if it fits your purposes. By the way, you can get the deviceId from the DeviceUUID (and vice versa) using the cli: balena device <idOrUuid>.