Editing Balena-Dash Scheduler

Hi team,

I have been using part of Balena Dash’s Scheduler code since it seems to be a great way to get chron jobs working at a container level. For my code, I would like to stop a container at night and start it again in the morning.

After starting with the Scheduler code, I know I need to interact with the Supervisor API. To do this, I changed the Dockerfile from alpine to alpine-python. In addition to that, it looks like the %%BALENA_MACHINE_NAME%% variable breaks the code for my Raspberry Pi 4, giving the error “invalid reference format: repository name must be lowercase”. I was able to get around this by using the image located at FROM balenalib/raspberrypi3-alpine-python

After that, I know interacting with the supervisor seems to require curl, so I add it via
RUN install_packages wget unzip curl

From there, I make the bash and python files executable and run start.sh, which I will paste here:

start.sh
#!/bin/bash

if [ "$ENABLE_TIMER" -eq "1" ]
then
  (crontab -l; echo "${TURN_ON:-0 8 * * *} /usr/src/turn_on.py") | crontab -
  (crontab -l; echo "${TURN_OFF:-0 23 * * *} /usr/src/turn_off.py") | crontab -
fi

crond -f

And here is the turn_off.py script that runs out of the start.sh file. The turn_on.py script is almost the same.

turn_off.py
import sys
import os

def turnOff():
    print('Turning off in turn_off.py')
    os.system('curl --header "Content-Type:application/json" "$BALENA_SUPERVISOR_ADDRESS/v2/applications/$BALENA_APP_ID/stop-service?apikey=$BALENA_SUPERVISOR_API_KEY" -d \'{"serviceName": "electronpi4"}\'')
    os.system('curl --header "Content-Type:application/json" "$BALENA_SUPERVISOR_ADDRESS/v2/applications/$BALENA_APP_ID/stop-service?apikey=$BALENA_SUPERVISOR_API_KEY" -d \'{"serviceName": "wifi-connect"}\'')
    os.system('curl --header "Content-Type:application/json" "$BALENA_SUPERVISOR_ADDRESS/v2/applications/$BALENA_APP_ID/stop-service?apikey=$BALENA_SUPERVISOR_API_KEY" -d \'{"serviceName": "keyboard"}\'')

turnOff()

While this compiles, the terminal throws the following errors:

Error Log

/usr/src/turn_off.py: line 1: import: not found
/usr/src/turn_off.py: line 2: import: not found
/usr/src/turn_off.py: line 3: syntax error: unexpected word (expecting “)”)

It seems like I am missing some frameworks to interact with the Supervisor API. Any thoughts on how to solve this?

TL;DR - Trying to interact with the Supervisor within the Scheduler container seems to throw errors.

If you want to use a python script without running it through the python interpreter you need to add a line at the top indicating which program should be used to execute the script:

$ python turn_off.py`

or turn_off.py needs to contain:

#!/bin/env python

import sys
...

see https://en.wikipedia.org/wiki/Shebang_(Unix)

You might be interested in https://github.com/balena-io/balena-sdk-python :wink:

Hi @robertgzr

Sorry for taking so long to get back to you. I tried what you said but I think I’m missing something. Here is a ZIP file of my current code-- I’m wondering if there’s something missing from the base image? Forgive me if the issue is obvious; I’m still learning.

Here is my current Docker-Compose:
version: '2'

services:
electronpi4:
build: ./electronpi4
privileged: true
labels:
io.balena.features.dbus: ‘1’
keyboard:
privileged: true
restart: always
build: ./keyboard
labels:
io.balena.features.supervisor-api: ‘1’
wifi:
build: ./wifi
restart: always
network_mode: host
privileged: true
labels:
io.balena.features.dbus: ‘1’
io.balena.features.firmware: ‘1’
scheduler:
build: ./scheduler
restart: always
privileged: true

@cpollock can you share your Python scripts along with the errors you’re now getting here on the forum? If you implemented the changes Robert suggested the error messages should have changed to something different.

@chrisys sure! It looks like the start.sh script is unable to find the Python files. Since start.sh and the turn_on.py files are in the same directory, I thought I could just launch by either using python turn_on.py or just simply saying turn_on.py. When that didn’t work, I also tried with the path /usr/src/turn_on.py.

Different error than before, and I can’t seem to figure out why this isn’t working. Thanks for bearing with me.

Here is the actual error log.
 scheduler  crond[54]: USER root pid 118 cmd turn_off.py
 scheduler  /bin/ash: turn_off.py: not found

Thank you. Can you also share the scripts with the suggested changes?

@thundron I apologize-- my previous link from November 19th had incorrect sharing settings. I have fixed it now if you’d like to take a look.I have made a new ZIP file containing the scripts with the suggested changes. In the comments at the bottom of each start.sh file, I have pasted the error log results.

Let me know your thoughts; my only goal is to shut off certain containers at a certain time.

Hello, I’ve taken a look through your files, and the errors in the comments seem to suggest that cron can’t find the scripts at the locations you specified. I would suggest trying a to create a minimal single example, and double check the location of the scripts, and the paths provided to cron. With a single example it will be easier for us to help if there is still an issue.

Thank you

Hi @srlowe you’re right-- it does look like the files can’t be found. Just to confirm though, I used the web terminal to SSH into the device and locate the files. Any thoughts?

Attaching screenshot:

image

In the build that I uploaded in a previous post, you can see where I change the code to look for these scripts, but have not yielded any results yet.

Hi @cpollock,

So I’ve had a look at your ZIP file and there are several issues:

  • The Python scripts have Windows line ending ( /r/n ). If you look at the hexdump of the files, you’ll see 0d 0a . If you look in vi in the main service on-device, it looks like this:
#!/usr/bin/env python3^M
^M
import sys^M
...

The Python interpreter won’t be able to parse this, and will throw an error. You should run a tool such as dos2unix over the scripts to remove these Windows line endings. If you’re developing on Windows, you should ensure that your editor preserves Unix line endings and doesn’t change them, else the same thing will happen again

  • The start.sh has no shebang, and bash therefore doesn’t know how to interpret it. The first line should be #!/bin/bash
  • The start.sh script’s second line does not use a conditional test properly. -eq is a numeric quality comparator operator, it’s not used on strings. Try if [ "$ENABLE_TIMER" == "1" ] instead
  • Relative paths are assumed, but you should really set your WORKDIR to the place where all this is carried out in your Dockerfile: WORKDIR /usr/src
  • Both the turn_on.py and turn_off.py scripts are pointing to non-existant interpreters. You should change #! /bin/env python to #!/usr/bin/env python3 , which correctly passes the environment variables to the python3 interpreter. Note that it’s python3 and not python , you’re installing python3 in the Dockerfile, and the python executable doesn’t exist. If you want to symlink python3 to python , that would also work

I think if you make these changes, the setup script should work as expected!. There are some great tutorials covering both bash and Dockerfiles.

Best regards,

Heds

Thanks so much for your insight @hedss

I have cleaned up the code a little according to your notes, but I found that adding #!/usr/bin/env python3 made it so the start.sh script couldn’t find the turn_off/on.py files.

Now that the files are found, the issue I’m running into is that the turn_off/on.py files aren’t communicating properly with the Balena Supervisor API. I’ve got this line of code:

    os.system('curl --header "Content-Type:application/json" "$BALENA_SUPERVISOR_ADDRESS/v2/applications/$BALENA_APP_ID/start-service?apikey=$BALENA_SUPERVISOR_API_KEY" -d \'{"serviceName": "electronpi4"}\'')

and it returns this:

 scheduler  curl: (3) URL using bad/illegal format or missing URL

This code works properly in my Keyboard container in my BalenaPi4Kiosk project.

Here is a copy of what I have so far.

Any thoughts?

Hi there.
You may check the actual curl command used from curl within os.system, for checking why it’s using bad/illegal format or missing URL.
Regards!

Hi @cpollock,

I suspect the issue here is probably that the Supervisor API hasn’t been enabled via the use of the labels in your docker-compose for the relevant service. You need a labels section such as the following:

services:
  my-service:
    ...
    labels:
      io.balena.features.supervisor-api: '1'

This will enable the Supervisor API inside the service container. More details can be found here.

If you’re using a single container application (ie. no docker-compose.yml file), then the Supervisor should be activated by default. As Mike says, the way to go here is to SSH into the service container and run the curl command manually to see what the result is. If that works, then it sounds like an escaping issue in the execution of the command via Python.

I’ve tried to have a look at the referenced BalenaPi4Kiosk, but it appears to be a private project and therefore can’t be viewed without credentials.

Best regards,

Heds

@hedss

That totally worked-- everything is functioning correctly now; thank you! I will share my code in my next post :slight_smile:

Thanks again for your help everyone-- here is my completed code: https://github.com/cjpollock/balena-scheduler