Calling the supervisor API from within a udev-triggered script

I have a couple of udev rules in a container that trigger when a USB storage device is inserted or removed (with a Raspberry Pi). The rules trigger correctly, and the relevant scripts are run.

Within the scripts, I want to call the Balena Supervisor API using curl, to restart an adjacent service. This call is not working.

  • I determined that the BALENA* environment variables are unavailable in the udev environment, and I fixed that (by caching them in files)
  • The curl command runs correctly when called from a terminal in the container
  • Presumably there is some other udev limitation that I’m not aware of? I tried adding IPAddressAllow=127.0.0.1 to /lib/systemd/system/udev.service, but that’s not it; perhaps the Balena udev service is configured differently?

Any ideas?

Thanks,
Peter

Hi, for the BALENA_* environment variables to be applied in the container, you need to set the io.balena.features.supervisor-api label to true in your docker-compose file: https://www.balena.io/docs/learn/develop/multicontainer/#labels

Please let us know whether that solves the issue for you.

Thanks,
Zahari

Yes, I’ve already done that, as evidenced by the fact that supervisor API call works correctly when run from a terminal in the container.

In the UDEV context the BALENA_* env. vars are not present, but I set them manually using values cached in files by the main container process when it starts.

Thanks.

Hi, thanks for the clarification. So you are running a privileged container with UDEV=1 in the dockerfile and then also have some udev rules that trigger some scripts. I will ping some of my teammates that could have some knowledge of why the environment variables are not present in the udev environment and we will get back to you. For reference this is the script that launches udevd inside a container: https://github.com/balena-io-library/base-images/blob/master/scripts/assets/entry.sh
Thanks,
Zahari

Thanks.

Mostly correct … I’m not expecting the the BALENA_* variables to be present in the UDEV context, so I deal with that manually, and that part works.

However, something is preventing the curl command reaching the supervisor API, presumably some UDEV environment restriction. (Or, it’s just a silly bug on my part, of course).

Yes, I wanted to get more info from my teammates on it as it may very well be related.
Thanks,
Zahari

What is the command you use with curl? What is curl's exit status when you run it? Does it output anything?

The curl command is as follows:

curl --header "Content-Type:application/json" "$BALENA_SUPERVISOR_ADDRESS/v2/applications/$BALENA_APP_ID/restart-service?apikey=$BALENA_SUPERVISOR_API_KEY" \
     -d '{"serviceName": "nas", "force": "true"}'

When it’s run within the script triggered by the UDEV event, I logged the following output from the script:

Fri Oct 11 10:00:58 UTC 2019 : USB device inserted
BALENA_SUPERVISOR_ADDRESS: http://127.0.0.1:48484
BALENA_APP_ID: 1303046
BALENA_SUPERVISOR_API_KEY: bf4d12c92a8e4b7de390ddbb2dead3d01c9f4f2635c4b5eea1f803aaf7d6d5
curl: (7) Couldn't connect to server

Note that the BALENA_* variables are not part of the script’s environment, so I set them manually using values cached in a file by the main container process.

The curl command works fine when executed within the main container process.

I suspect there’s something limiting what can be done within the UDEV context.

As an alternative I got an idea - instead of defining UDEV=1 in your Dockerfile, you may try running from your start script udevd with the same code from the entry script I posted a link to above. Could you try that please and let us know whether it is working for you?
Thanks,
Zahari

For now I’ve decided just to work around this by setting a (file-based) flag in the script triggered by UDEV, with the main container process subsequently acting on the flag and executing the curl command to the supervisor API.

It’s clear that the UDEV context is restricted somehow, but I don’t have the time to experiment my way through finding out how to lift the restrictions.

Thanks for letting us know.
We are glad that the workaround you mentioned worked for you, but could you provide a part of your docker-compose & dockerfile to further investigate this?

Sure. This is the relevant excerpt from docker-compose:

services:
  manager:
    build: ./manager
    volumes:
      - 'shared-data:/data'
    network_mode: host
    restart: always
    labels:
      io.balena.features.supervisor-api: '1'
      io.balena.features.dbus: '1'
    privileged: true

And this is the relevant excerpt from the Dockerfile.template for the manager container:

FROM balenalib/%%BALENA_MACHINE_NAME%%:stretch

...

# Copy over the start script
COPY start.sh /.

# Copy over the UDEV rules and scripts
COPY usb-insertion.rules /etc/udev/rules.d/.
COPY udev-trigger-usb-inserted.sh /.
COPY udev-trigger-usb-removed.sh /.
RUN chmod +x /udev-trigger-usb-*

# Enable udevd in this container
ENV UDEV=1

# Entry point is the start script
CMD /bin/bash /start.sh

Regards,
Peter

Hi, it looks like a solid approach for a workaround over the udev context restrictions. Please let us know if you have any further issues or questions.
Thanks,
Zahari