How do you lock a device from being updated?

I have been reading this document on how to prevent balena updates on a device

However while the process of creating and removing a file is clear, it is very unclear how this works in terms of a container.

I have a BalenaOS device with multiple services and it is my understanding that a service would basically handle this functionality. So I wrote a service that handles locking/unlocking and added to my docker-compose file however when I try mounting the host /tmp/balena I get a message back from the Balena CLI saying that Bind mounts are not allowed.

So right now I am at a loss on how do I prevent the entire device from being updated using this method. Am I supposed to ssh into each device and perform the lock by hand directly on the host? It seems putting the lock inside the “locking service” /tmp/balena folder doesn’t work either.

Any guidelines on the matter?

Hi there @alethenorio and welcome to the balena forums!

These update locks are per-service, so each container is responsible for their own locking & unlocking. You don’t need to bind-mount anything, if you take the lock at the path outlined in the documentation you linked to, the supervisor should just ignore that container until the lock has been removed.

If you are looking for a way to prevent updating at a device level, I recommend you look into staged releases. We have an example project for how to pin releases to devices here: https://github.com/balena-io-projects/staged-releases. This strategy will allow you to remotely control when certain devices update generally.

I hope this is helpful! If not, please ask questions and we will do our best to answer them.

Thank you for the response. Pinning and unpinning releases seems the wrong way of doing this, The idea is to prevent deployment under certain temporary conditions and have the device decide that. The lock feature seems perfect for that as it does not require the device to call remote APIs (Which in turn requires an authtoken).

I tried mapping /tmp/balena to a volume (the idea being one container would decide when to write and not write the lock file and all containers would also map the same volume meaning the lock file wold exist on all) however that does not seem to work as the applications that mapped the directory never start and stay on downloaded status.

Is there any other way of doing this without having to put an authtoken on each device and have them call the balena API?

1 Like

Hey @alethenorio the API token should be easy to access, every device has an API key which can be injected into each service without too much trouble, have a look at https://www.balena.io/docs/learn/develop/runtime/#environment-variables for more info. Staged releases should be the correct way to achieve this, let us know if you have any other concerns about it, but if you really want to avoid talking to the API you can still achieve the same through application locks. Each service can try to take the lock independently, there is no need to share it across multiple ones: as long as the lock is there, the supervisor will prevent the update from starting.

Hi @nazrhom and @xginn8

I took a look at the staged releases and attempted using the API to enabled/disable should_track_latest_release however I have not been able to authenticate from any token on the device.

I am able to generate my own token in my account and use that to call the API

$ curl -X PATCH "https://api.balena-cloud.com/v5/application(<APP_ID>)" -H "Content-Type: application/json" -H "Authorization: Bearer <TOKEN>" --data '{ "should_track_latest_release": true }'
OK

However when I look at the device variables, the only token I can see relevant is the BALENA_API_KEY however that does not allow me to authenticate and returns Unauthorized

The BALENA_SUPERVISOR_API_KEY does not seem to work either

Am I missing something?

Hi

Those API keys you tried from the device; the BALENA_SUPERVISOR_API_KEY is a key to talk to the Supervisor’s API, and the BALENA_API_KEY is scoped to only allow some resources to be manipulated. In the query you’re trying run, you’re trying to alter the application resource which that key will not have PATCH access to.

Right, that makes sense. I looked further into the device API So is there any way that a service running in a balenaOS device can take action to temporarily prevent an update to all that device’s services and then later go back to allowing updates again?

It seems the lock file requires each service to do that themselves and the application API requires a user token. I see the device API has a should_be_running__release but I’d need to know what the latest release is which I cannot find an API for in the device API section and feels more cumbersome.

Yes, you would want to pin the device to a given release. The logic is as follows:

If you want to pin to a specific release:

You can get a list of releases from the API [1]. You can then stop the device from tracking the latest release by patching {"should_track_latest_release": false} and select a specific release by patching {"should_be_running__release": _______} [2].

You would unpin and return to rolling releases by patching {"should_track_latest_release": true} and {"should_be_running__release": null}.

You can find scripts which make use of this pattern in our balena-projects public github org, in the repo staged-releases [3].

If you want to want to pin to the latest release:

You can, in this case, do the above process, but determine which release is latest by sorting the results of getting all releases for an application by "created_at".

[1] https://www.balena.io/docs/reference/api/resources/release/
[2] https://www.balena.io/docs/reference/api/resources/device/#set-device-to-release
[3] https://github.com/balena-io-projects/staged-releases

Thank you but I understood that using should_track_latest_release is not possible with the BALENA_API_KEY. I was wondering if it is possible to do that without having a user generated token?

Hi,
There might be some confusion.
To pin a single device you only need to set should_be_running__release on the device resource to the release you want the device to run. The device API key BALENA_API_KEY should allow the device to patch it’s own device resource in the API. With the same key the device should also be able to read the release the device is currently running on. So to build this update lock this should work, but the device cannot read other releases of the application.

Thank you. I have dug a little further into this and have indeed been able to get a device to pin to a release using the should_be_running__release which can be updated using the BALENA_API_KEY.

The device does not seem to perform an update once I disable tracking and there has been a deployment before that. I am trying to do the following

  1. Set should_be_running__release to the same release ID the device is currently running
  2. Perform a Balena deploy to the application which that device belongs to.
  3. Confirm the device is still in the same release
  4. Set should_be_running__release to null
  5. Confirm the device is no longer pinned to a release and see that Target Release is now the latest deploy which is also different than Current release for the device
  6. Wait for the device to update itself to the Target Release but it never happens

I expect the moment I set the device to track the latest release for it to be updated to the latest release but it does not seems to happen. I have waited for quite a long time and nothing.

Restarting or Rebooting the device on the Balena Web UI updates it to the latest but I’d like for that to happen automatically without needing manual input. Shouldn’t this already be the case?

Do I have to tell the device to update itself after setting to track the latest release (And if so, can it be done through the API)?

Devices by default check every 10 minutes for state changes, so that may be the delay you observed. Was the time period longer than that?