diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..538e765 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +LICENSE.md +README.md +/system/ +!/system/server.py +!/system/balena.id diff --git a/.gitignore b/.gitignore index b27596c..c60603d 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,4 @@ system/server.ini .remote-sync.json system/osid.desktop system/run_app.sh +#system/balena.id diff --git a/Dockerfile.template b/Dockerfile.template new file mode 100644 index 0000000..85fae4b --- /dev/null +++ b/Dockerfile.template @@ -0,0 +1,30 @@ +# base-image for python on any machine using a template variable, +# see more about dockerfile templates here: https://www.balena.io/docs/learn/develop/dockerfile/ +FROM balenalib/%%BALENA_MACHINE_NAME%%-python:3-stretch-run + +# use `install_packages` if you need to install dependencies, +# for instance if you need git, just uncomment the line below. +# RUN install_packages git + +# Set our working directory +WORKDIR /usr/src/app + +# Copy requirements.txt first for better cache on later pushes +COPY requirements.txt requirements.txt + +# pip install python deps from requirements.txt on the resin.io build server +RUN pip install -r requirements.txt + +# Install dd & partprobe +RUN apt-get update && apt-get install -y dcfldd parted + +# This will copy all files in our root to the working directory in the container +COPY . ./ + +RUN chmod +x balena_startup.sh && ./balena_startup.sh + +# Enable udevd so that plugged dynamic hardware devices show up in our container. +ENV UDEV=1 + +# main.py will run when container starts up on the device +CMD ["python", "system/server.py"] diff --git a/README.md b/README.md index e895f78..2519657 100755 --- a/README.md +++ b/README.md @@ -108,6 +108,10 @@ Then proceed to modify those files to match the system paths for the items in th * Just make sure the path for the run_app.sh script is defined properly. * if you use OSID on headless Raspberry, this file is useless. +### Balena cloud deployment +change `balenaAppId` variable in system/balena.id to your `` +Deploy app to balena cloud + ### Usage #### Accepted image file diff --git a/balena_startup.sh b/balena_startup.sh new file mode 100755 index 0000000..ac77ba3 --- /dev/null +++ b/balena_startup.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +mkdir -p logs +printf "[DuplicatorSettings] +ImagePath = /data +Host = 0.0.0.0 +SocketPort = 80 +Logs = /usr/src/app/logs +SkeletonLocation = /usr/src/app/www/skeleton.min.css +" > system/server.ini +cat system/balena.id >> system/server.ini diff --git a/system/balena.id b/system/balena.id new file mode 100644 index 0000000..db5c3b3 --- /dev/null +++ b/system/balena.id @@ -0,0 +1 @@ +balenaAppId="" diff --git a/system/server.py b/system/server.py index a3f0f3a..9cbba5b 100644 --- a/system/server.py +++ b/system/server.py @@ -12,7 +12,6 @@ class SDCardDupe(object): @cherrypy.expose def index(self): - # get host configs from server.ini config_parse = configparser.ConfigParser() config_parse.sections() @@ -23,11 +22,17 @@ def index(self): www_path = "/".join(os.path.dirname(os.path.realpath(__file__)).split("/")[:-1]) + "/www/" html_string = open(www_path + 'index.html', 'r').read() hostname_port = config_parse['DuplicatorSettings']['Host']+":"+config_parse['DuplicatorSettings']['SocketPort'] - html_string = html_string.replace("replacewithhostnamehere",hostname_port) + html_string = html_string.replace("replacewithhostnamehere",'/') css_string = '' html_string = html_string.replace("",css_string) + html_string = html_string.replace("$IMG_ROOT$", config_parse['DuplicatorSettings']['ImagePath']) + if 'BalenaAppId' in config_parse['DuplicatorSettings']: + balena_app_id = config_parse['DuplicatorSettings']['BalenaAppId'] + else: + balena_app_id = "false" + html_string = html_string.replace("$BALENA_APP_ID$", balena_app_id) return html_string @@ -43,7 +48,7 @@ def monitor(self): www_path = "/".join(os.path.dirname(os.path.realpath(__file__)).split("/")[:-1]) + "/www/" html_string = open(www_path + 'monitor.html', 'r').read() hostname_port = config_parse['DuplicatorSettings']['Host']+":"+config_parse['DuplicatorSettings']['SocketPort'] - html_string = html_string.replace("replacewithhostnamehere",hostname_port) + html_string = html_string.replace("replacewithhostnamehere",'/') css_string = '' html_string = html_string.replace("",css_string) @@ -74,7 +79,7 @@ def posted(self,img_file,devices): # assumptions made, there will be no collisions, dont have to pop element # but to reduce the cost of loop, will pop element by creating new list if dev_path in mounted_item: - umount_disk_cmd = "sudo umount %s"%mounted_item + umount_disk_cmd = "umount %s"%mounted_item subprocess.call(umount_disk_cmd.split(" ")) else: reduced_list.append(mounted_item) @@ -97,11 +102,11 @@ def posted(self,img_file,devices): out.close() # Run dd command and output status into the progress.info file - dd_cmd = "sudo dcfldd bs=4M if=" + img_file + dd_cmd = "dcfldd bs=4M if=" + img_file dd_cmd += " of=" + " of=".join(devices) - dd_cmd += " sizeprobe=if statusinterval=1 2>&1 | sudo tee " + dd_cmd += " sizeprobe=if statusinterval=1 2>&1 | tee " dd_cmd += config_parse['DuplicatorSettings']['Logs'] + "/progress.info" - dd_cmd += " && echo \"osid_completed_task\" | sudo tee -a " + dd_cmd += " && echo \"osid_completed_task\" | tee -a " dd_cmd += config_parse['DuplicatorSettings']['Logs'] + "/progress.info" # Planned to run this in localhost only. @@ -114,7 +119,7 @@ def posted(self,img_file,devices): subprocess.Popen(['sudo', 'bash', dd_cmd_file], close_fds=True) hostname_port = config_parse['DuplicatorSettings']['Host']+":"+config_parse['DuplicatorSettings']['SocketPort'] - monitor_url = "http://" + hostname_port + "/monitor"; + monitor_url = "/monitor"; html_string = "" @@ -140,7 +145,7 @@ def getStatus(self): out.close() # pull data from progress.info file and feed back to call - cat_cmd = "sudo cat "+ progress_file + cat_cmd = "cat "+ progress_file cat_output = str(subprocess.check_output(cat_cmd, shell=True).decode("utf-8")) if "records in" in cat_output and "records out" in cat_output and "osid_completed_task" in cat_output: percentage = "100%" @@ -163,7 +168,7 @@ def getDevices(self): list_devices = [] # Refresh partition to discover all available medias - refresh_disk_cmd = "sudo /sbin/partprobe" + refresh_disk_cmd = "/sbin/partprobe" subprocess.check_output(refresh_disk_cmd, shell=True) # command to get a list of devices on OS @@ -235,10 +240,14 @@ def getImages(self): 'log.access_file' : config_parse['DuplicatorSettings']['Logs']+"/access.log", 'log.screen': False, 'tools.sessions.on': True + }, + '/favicon.ico':{ + 'tools.staticfile.on': True, + 'tools.staticfile.filename': "/".join(os.path.dirname(os.path.realpath(__file__)).split("/")[:-1]) + "/www/favicon.ico" } } # create a daemon for cherrpy so it will create a thread when started - cherrypy.process.plugins.Daemonizer(cherrypy.engine).subscribe() + #cherrypy.process.plugins.Daemonizer(cherrypy.engine).subscribe() cherrypy.quickstart(SDCardDupe(), '/', conf) diff --git a/www/favicon.ico b/www/favicon.ico new file mode 100644 index 0000000..d3cd2d9 Binary files /dev/null and b/www/favicon.ico differ diff --git a/www/index.html b/www/index.html index 2d8a3ef..32ba253 100644 --- a/www/index.html +++ b/www/index.html @@ -3,155 +3,169 @@ - rPi-Dupe - - - - - + + + + + + + + + +
+
+ Open Source Image Duplicator - Python v1.1.1 +
+ +
+
+ + + +
+ + +
diff --git a/www/monitor.html b/www/monitor.html index 6b5c15f..e9f8439 100644 --- a/www/monitor.html +++ b/www/monitor.html @@ -3,7 +3,7 @@ - rPi-Dupe + RPI-Duplicator @@ -20,49 +20,51 @@ @@ -90,7 +92,7 @@

Progress