Skip to content

Commit 2f64fe2

Browse files
committed
parent 4b106cb
author previ <roberto.previtera@gmail.com> 1667160130 +0100 committer previ <roberto.previtera@gmail.com> 1668461534 +0100 add thread lock on TinyDB
1 parent 4b106cb commit 2f64fe2

File tree

14 files changed

+185
-124
lines changed

14 files changed

+185
-124
lines changed
File renamed without changes.

.github/workflows/build_backend.yml

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
container: coderbot/coderbot-ci:3.9-bullseye-slim
1212
steps:
1313
- uses: actions/checkout@v3 # Checking out the repo
14-
- run: pip install -r requirements_stub.txt
14+
- run: pip install -r docker/stub/requirements.txt
1515
- run: |
1616
export PYTHONPATH=./coderbot:./stub:./test
1717
mkdir test-reports
@@ -58,7 +58,7 @@ jobs:
5858
type=ref,event=pr
5959
# push event
6060
type=sha,enable=true,prefix=git-,format=short
61-
- uses: actions/checkout@v2 # Checking out the repo
61+
- uses: actions/checkout@v3 # Checking out the repo
6262
- name: Set up QEMU
6363
uses: docker/setup-qemu-action@v2
6464
- name: Set up Docker Buildx
@@ -74,9 +74,46 @@ jobs:
7474
uses: docker/build-push-action@v3
7575
with:
7676
push: true
77+
build-args: CODERBOT_VERSION=${{github.ref_name}}-${{github.sha}}
7778
platforms: linux/arm/v7
7879
tags: ${{ steps.meta.outputs.tags }}
7980
context: .
8081
file: docker/Dockerfile
8182
cache-from: type=registry,ref=ghcr.io/coderbotorg/backend:latest
8283
cache-to: type=inline
84+
85+
release-stub:
86+
needs: [test]
87+
runs-on: ubuntu-latest
88+
steps:
89+
- name: Docker meta
90+
id: meta
91+
uses: docker/metadata-action@v4
92+
with:
93+
# list of Docker images to use as base name for tags
94+
images: ghcr.io/coderbotorg/backend
95+
# generate Docker tags based on the following events/attributes
96+
tags: |
97+
# always latest
98+
type=raw,value=stub-latest
99+
- uses: actions/checkout@v3 # Checking out the repo
100+
- name: Set up Docker Buildx
101+
id: buildx
102+
uses: docker/setup-buildx-action@v2
103+
- name: Login to DockerHub
104+
uses: docker/login-action@v2
105+
with:
106+
registry: ghcr.io
107+
username: ${{ github.actor }}
108+
password: ${{ secrets.GITHUB_TOKEN }}
109+
- name: Build and push
110+
uses: docker/build-push-action@v3
111+
with:
112+
push: true
113+
build-args: CODERBOT_VERSION=${{github.ref_name}}-${{github.sha}}
114+
platforms: linux/amd64
115+
tags: ${{ steps.meta.outputs.tags }}
116+
context: .
117+
file: docker/stub/Dockerfile
118+
cache-from: type=registry,ref=ghcr.io/coderbotorg/backend:stub-latest
119+
cache-to: type=inline

coderbot/activity.py

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from tinydb import TinyDB, Query
2-
2+
from threading import Lock
33
# Programs and Activities databases
44
class Activities():
55
_instance = None
@@ -13,34 +13,39 @@ def get_instance(cls):
1313
def __init__(self):
1414
self.activities = TinyDB("data/activities.json")
1515
self.query = Query()
16+
self.lock = Lock()
1617

1718
def load(self, name, default):
18-
if name and default is None:
19-
activities = self.activities.search(self.query.name == name)
20-
if len(activities) > 0:
21-
return activities[0]
22-
elif default is not None:
23-
if len(self.activities.search(self.query.default == True)) > 0:
24-
return self.activities.search(self.query.default == True)[0]
19+
with self.lock:
20+
if name and default is None:
21+
activities = self.activities.search(self.query.name == name)
22+
if len(activities) > 0:
23+
return activities[0]
24+
elif default is not None:
25+
if len(self.activities.search(self.query.default == True)) > 0:
26+
return self.activities.search(self.query.default == True)[0]
27+
return None
2528
return None
26-
return None
2729

2830
def save(self, name, activity):
29-
# if saved activity is "default", reset existing default activity to "non-default"
30-
if activity.get("default", False) is True:
31-
self.activities.update({'default': False})
32-
if self.activities.search(self.query.name == name) == []:
33-
self.activities.insert(activity)
34-
else:
35-
self.activities.update(activity, self.query.name == activity["name"])
31+
with self.lock:
32+
# if saved activity is "default", reset existing default activity to "non-default"
33+
if activity.get("default", False) is True:
34+
self.activities.update({'default': False})
35+
if self.activities.search(self.query.name == name) == []:
36+
self.activities.insert(activity)
37+
else:
38+
self.activities.update(activity, self.query.name == activity["name"])
3639

3740
def delete(self, name):
38-
activities = self.activities.search(self.query.name == name)
39-
if len(activities) > 0:
40-
activity = activities[0]
41-
if activity.get("default", False) is True:
42-
self.activities.update({'default': True}, self.query.stock == True)
43-
self.activities.remove(self.query.name == activity["name"])
41+
with self.lock:
42+
activities = self.activities.search(self.query.name == name)
43+
if len(activities) > 0:
44+
activity = activities[0]
45+
if activity.get("default", False) is True:
46+
self.activities.update({'default': True}, self.query.stock == True)
47+
self.activities.remove(self.query.name == activity["name"])
4448

4549
def list(self):
46-
return self.activities.all()
50+
with self.lock:
51+
return self.activities.all()

coderbot/api.py

Lines changed: 8 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,14 @@ def get_status():
6868
uptime = 0
6969
try:
7070
uptime = subprocess.check_output(["uptime"]).decode('utf-8').replace('\n', '')
71-
except:
71+
except Exception:
7272
pass
7373

7474
internet_status = False
7575
try:
7676
urllib.request.urlopen("https://coderbot.org")
7777
internet_status = True
78-
except:
78+
except Exception:
7979
pass
8080

8181
return {'internet_status': internet_status,
@@ -87,42 +87,20 @@ def get_info():
8787
Expose informations about the CoderBot system.
8888
(Cached method)
8989
"""
90-
backend_commit = "undefined"
91-
coderbot_version = "undefined"
92-
update_status = "ok"
9390
device = {}
94-
motors = 'undefined'
95-
96-
try:
97-
# manifest.json is generated while building/copying the backend
98-
with open('manifest.json', 'r') as f:
99-
metadata = json.load(f)
100-
backend_commit = metadata["backend_commit"][0:7]
101-
coderbot_version = metadata["backend_version"][0:7]
102-
except Exception:
103-
pass
104-
105-
try:
106-
encoder = bool(Config.read().get('encoder'))
107-
if(encoder):
108-
motors = 'DC encoder motors'
109-
else:
110-
motors = 'DC motors'
111-
except Exception:
112-
pass
113-
11491
serial = get_serial()
11592

11693
try:
117-
device = Baleba.get_instance().device()
94+
device = Balena.get_instance().device()
95+
logging.info("device: %s", str(device))
11896
except Exception:
11997
pass
120-
return { 'backend_commit': device.get("commit"),
121-
'coderbot_version': coderbot_version,
98+
99+
return { 'release_commit': device.get("commit"),
100+
'coderbot_version': os.getenv("CODERBOT_VERSION"),
122101
'update_status': device.get("status"),
123102
'kernel': device.get("os_version"),
124-
'serial': serial,
125-
'motors': motors }
103+
'serial': serial }
126104

127105
prog = None
128106
prog_engine = ProgramEngine.get_instance()
@@ -249,39 +227,6 @@ def deletePhoto(name):
249227
except FileNotFoundError:
250228
return 404
251229

252-
## System
253-
254-
def status():
255-
sts = get_status()
256-
# getting reset log file
257-
data = []
258-
try:
259-
with open('/home/pi/coderbot/logs/reset_trigger_service.log', 'r') as log_file:
260-
data = [x for x in log_file.read().split('\n') if x]
261-
except Exception: # direct control case
262-
pass # if file doesn't exist, no restore as ever been performed. return empty data
263-
264-
265-
return {
266-
"status": "ok",
267-
"internetConnectivity": sts["internet_status"],
268-
"temp": sts["temp"],
269-
"uptime": sts["uptime"],
270-
"log": data
271-
}
272-
273-
def info():
274-
inf = get_info()
275-
return {
276-
"model": 1,
277-
"version": inf["coderbot_version"],
278-
"backend commit build": inf["backend_commit"],
279-
"kernel" : inf["kernel"],
280-
"update status": inf["update_status"],
281-
"serial": inf["serial"],
282-
"motors": inf["motors"]
283-
}
284-
285230
def restoreSettings():
286231
Config.restore()
287232
return restart()

coderbot/balena/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,24 @@ def __init__(self):
2121
def purge(self):
2222
logging.debug("reset bot")
2323
req = Request(f'{self.supervisor_address}/v1/purge?apikey={self.supervisor_key}', data=self.app_id_data, headers=self.headers, method='POST')
24-
return urlopen(req).read()
24+
return json.load(urlopen(req))
2525

2626
def shutdown(self):
2727
logging.debug("shutdown bot")
2828
req = Request(f'{self.supervisor_address}/v1/shutdown?apikey={self.supervisor_key}', headers=self.headers, method='POST')
29-
return urlopen(req).read()
29+
return json.load(urlopen(req))
3030

3131
def restart(self):
3232
logging.debug("restarting bot")
3333
req = Request(f'{self.supervisor_address}/v1/restart?apikey={self.supervisor_key}', data=self.app_id_data, headers=self.headers, method='POST')
34-
return urlopen(req).read()
34+
return json.load(urlopen(req))
3535

3636
def reboot(self):
3737
logging.debug("reboot bot")
3838
req = Request(f'{self.supervisor_address}/v1/reboot?apikey={self.supervisor_key}', headers=self.headers, method='POST')
39-
return urlopen(req).read()
39+
return json.load(urlopen(req))
4040

4141
def device(self):
4242
logging.debug("reboot bot", f'{self.supervisor_address}get?apikey={self.supervisor_key}')
43-
req = Request(f'{self.supervisor_address}/device?apikey={self.supervisor_key}', headers=self.headers, method='GET')
43+
req = Request(f'{self.supervisor_address}/v1/device?apikey={self.supervisor_key}', headers=self.headers, method='GET')
4444
return json.load(urlopen(req))

coderbot/program.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import math
2727
from tinydb import TinyDB, Query
28+
from threading import Lock
2829

2930
import coderbot
3031
import camera
@@ -80,6 +81,7 @@ def __init__(self):
8081
self._programs = TinyDB(PROGRAMS_DB)
8182
# initialise DB from default programs
8283
query = Query()
84+
self.lock = Lock()
8385
for dirname, dirnames, filenames, in os.walk(PROGRAMS_PATH_DEFAULTS):
8486
dirnames
8587
for filename in filenames:
@@ -102,28 +104,31 @@ def prog_list(self):
102104
return self._programs.all()
103105

104106
def save(self, program):
105-
query = Query()
106-
self._program = program
107-
program_db_entry = self._program.as_dict()
108-
if self._programs.search(query.name == program.name) != []:
109-
self._programs.update(program_db_entry, query.name == program.name)
110-
else:
111-
self._programs.insert(program_db_entry)
107+
with self.lock:
108+
query = Query()
109+
self._program = program
110+
program_db_entry = self._program.as_dict()
111+
if self._programs.search(query.name == program.name) != []:
112+
self._programs.update(program_db_entry, query.name == program.name)
113+
else:
114+
self._programs.insert(program_db_entry)
112115

113116
def load(self, name):
114-
query = Query()
115-
program_db_entries = self._programs.search(query.name == name)
116-
if len(program_db_entries) > 0:
117-
prog_db_entry = program_db_entries[0]
118-
logging.debug(prog_db_entry)
119-
self._program = Program.from_dict(prog_db_entry)
120-
return self._program
121-
return None
117+
with self.lock:
118+
query = Query()
119+
program_db_entries = self._programs.search(query.name == name)
120+
if len(program_db_entries) > 0:
121+
prog_db_entry = program_db_entries[0]
122+
logging.debug(prog_db_entry)
123+
self._program = Program.from_dict(prog_db_entry)
124+
return self._program
125+
return None
122126

123127
def delete(self, name):
124-
query = Query()
125-
program_db_entries = self._programs.search(query.name == name)
126-
self._programs.remove(query.name == name)
128+
with self.lock:
129+
query = Query()
130+
program_db_entries = self._programs.search(query.name == name)
131+
self._programs.remove(query.name == name)
127132

128133
def create(self, name, code):
129134
self._program = Program(name, code)

coderbot/v1.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ paths:
397397
description: "upload failed"
398398
/system/status:
399399
get:
400-
operationId: "api.status"
400+
operationId: "api.get_status"
401401
summary: "Bot general informations, execution status and reset log file"
402402
tags:
403403
- System operations
@@ -422,7 +422,7 @@ paths:
422422
description: Test ended.
423423
/system/info:
424424
get:
425-
operationId: "api.info"
425+
operationId: "api.get_info"
426426
summary: "Bot general informations and execution status"
427427
tags:
428428
- System operations

docker/Dockerfile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ RUN pip install --no-cache-dir -r /tmp/requirements.txt
5050
RUN mkdir -p /coderbot && \
5151
mkdir -p /coderbot/data && \
5252
mkdir -p /coderbot/logs && \
53-
mkdir -p /coderbot/updatePackages && \
5453
mkdir -p /coderbot/cnn_modules && \
5554
mkdir -p /coderbot/coderbot && \
5655
mkdir -p /coderbot/defaults && \
@@ -63,5 +62,9 @@ ADD sounds /coderbot/sounds/.
6362
ADD docker/scripts/*.sh /tmp/.
6463
RUN /tmp/install_generic_cnn_models.sh
6564
RUN /tmp/install_lib_firmware.sh
65+
ADD docker/start.sh /coderbot/.
6666

67-
ENTRYPOINT cd /coderbot && modprobe i2c-dev && python3 coderbot/main.py
67+
ARG CODERBOT_VERSION
68+
ENV CODERBOT_VERSION=${CODERBOT_VERSION}
69+
70+
ENTRYPOINT /coderbot/start.sh

docker/start.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
3+
cd /coderbot && modprobe i2c-dev && python3 coderbot/main.py

0 commit comments

Comments
 (0)