From 8065e12e974039af33fc37283caeec013a992b34 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Wed, 1 Nov 2023 14:02:04 +0100
Subject: [PATCH 01/17] basic docker image
---
.dockerignore | 1 +
astrid/__init__.py | 14 ++++-----
astrid/pages.py | 67 ++++++++++++++++++++------------------------
astrid/server.py | 18 ++++++------
astrid/templating.py | 13 ++++-----
docker/Dockerfile | 26 +++++++++++++++++
docker/README.md | 11 ++++++++
docker/entrypoint.sh | 4 +++
bin/astrid => main | 4 +++
requirements.txt | 2 ++
setup.py | 2 +-
11 files changed, 100 insertions(+), 62 deletions(-)
create mode 100644 .dockerignore
create mode 100644 docker/Dockerfile
create mode 100644 docker/README.md
create mode 100755 docker/entrypoint.sh
rename bin/astrid => main (91%)
create mode 100644 requirements.txt
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..9a4b405
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+docker/Dockerfile
diff --git a/astrid/__init__.py b/astrid/__init__.py
index e9fc3f2..8ce06e1 100644
--- a/astrid/__init__.py
+++ b/astrid/__init__.py
@@ -8,31 +8,31 @@
class BuildLogger:
separator = ";"
-
+
def __init__(self, repodir):
self.repodir = repodir
-
+
def _getLogfile(self, reponame):
return os.path.expanduser('~/.astrid/{}.log'.format(reponame))
def _getBuildlogfile(self, reponame):
return os.path.expanduser('~/.astrid/{}.build.log'.format(reponame))
-
+
def log(self, reponame, message, sendMail = False):
logfile = self._getLogfile(reponame)
f = open(logfile, "a")
user = cherrypy.request.login
f.write(BuildLogger.separator.join([datetime.now().isoformat(' '), message, user]) + "\n")
f.close()
-
+
if sendMail:
self._sendMail(reponame, message)
-
+
def getLogs(self, reponame):
logfile = self._getLogfile(reponame)
try:
f = open(logfile, "r")
-
+
records = [row.split(BuildLogger.separator) for row in reversed(list(f))]
f.close()
return records
@@ -89,5 +89,3 @@ def _sendMail(self, reponame, message):
root.build = BuilderPage(repodir, repos, locks)
root.info = InfoPage(repodir, repos, locks)
root.buildlog = BuildlogPage(repodir, repos, locks)
-
-
diff --git a/astrid/pages.py b/astrid/pages.py
index 43657df..fdb5cd2 100644
--- a/astrid/pages.py
+++ b/astrid/pages.py
@@ -19,7 +19,7 @@ def __init__(self, repodir, repos, locks):
self.repos = repos
self.repodir = repodir
self.locks = locks
-
+
def _checkAccess(self, reponame):
user = cherrypy.request.login
if user not in self.repos.get(reponame, "users"):
@@ -31,22 +31,22 @@ def _isBuilding(self, reponame):
if not building: # repo's not building, we release the lock
lock.release()
return building
-
+
class BuilderPage(BasePage):
-
+
@cherrypy.expose
def default(self, reponame):
if reponame not in self.repos.sections():
raise cherrypy.HTTPError(404)
-
+
self._checkAccess(reponame)
lock = self.locks[reponame]
msg = None
sendMail = False
-
+
lock.acquire()
- try:
+ try:
try:
time_start = time.time()
time_end = None
@@ -68,11 +68,11 @@ def default(self, reponame):
msg_class = "red"
sendMail = True
raise
-
+
if time_end != None:
msg += " ({:.2f} s)".format(time_end - time_start)
-
+
template = Template("templates/build.html")
template.assignData("reponame", reponame)
template.assignData("message", msg)
@@ -82,15 +82,17 @@ def default(self, reponame):
finally:
logger = BuildLogger(self.repodir)
logger.log(reponame, msg, sendMail)
- lock.release()
-
+ lock.release()
+
def _updateRepo(self, reponame):
+ print(f"Updating repository {reponame}")
remotepath = self.repos.get(reponame, "path")
submodules = self.repos.get(reponame, "submodules") if self.repos.has_option(reponame, "submodules") else False
localpath = os.path.join(self.repodir, reponame)
os.umask(0o007) # create repo content not readable to others
if not os.path.isdir(localpath):
+ print(f"Repository {reponame} empty, creating")
g = Git()
g.clone(remotepath, localpath)
@@ -101,8 +103,9 @@ def _updateRepo(self, reponame):
# not-working umask workaround
p = subprocess.Popen(["chmod", "g+w", localpath])
- p.wait()
+ p.wait()
else:
+ print(f"Pulling repository {reponame}")
repo = Repo(localpath)
try:
repo.git.update_index("--refresh")
@@ -122,33 +125,33 @@ def _updateRepo(self, reponame):
# now set correct group (same as build user)
usr = self.repos.get(reponame, "build_usr")
p = subprocess.Popen(["chgrp", usr, "-R", "-f", localpath])
- p.wait()
+ p.wait()
return repo
-
+
def _build(self, reponame):
usr = self.repos.get(reponame, "build_usr")
cmd = self.repos.get(reponame, "build_cmd")
args = self.repos.get(reponame, "build_args")
cwd = os.path.join(self.repodir, reponame)
-
+
logfilename = os.path.expanduser("~/.astrid/{}.build.log".format(reponame))
logfile = open(logfilename, "w")
- p = subprocess.Popen(["sudo", "-u", usr, cmd] + shlex.split(args), cwd=cwd, stdout=logfile, stderr=logfile, stdin=open("/dev/null"))
- p.wait()
+ p = subprocess.Popen([cmd] + shlex.split(args), cwd=cwd, stdout=logfile, stderr=logfile, stdin=open("/dev/null"))
+ p.wait()
return p.returncode == 0
-
+
class InfoPage(BasePage):
@cherrypy.expose
def default(self, reponame):
if reponame not in self.repos.sections():
raise cherrypy.HTTPError(404)
-
+
self._checkAccess(reponame)
-
+
logger = BuildLogger(self.repodir)
-
+
msg = "Time | Message | User |
"
first = True
for record in logger.getLogs(reponame):
@@ -158,9 +161,9 @@ def default(self, reponame):
first = False
else:
msg += """{} | {} | {} |
""".format(record[0], record[1], record[2])
-
+
msg += "
"
-
+
template = Template("templates/info.html")
template.assignData("reponame", reponame)
for k, v in self.repos.items(reponame):
@@ -168,7 +171,7 @@ def default(self, reponame):
template.assignData("messages", msg)
template.assignData("pagetitle", reponame + " info")
-
+
return template.render()
class BuildlogPage(BasePage):
@@ -177,9 +180,9 @@ class BuildlogPage(BasePage):
def default(self, reponame):
if reponame not in self.repos.sections():
raise cherrypy.HTTPError(404)
-
+
self._checkAccess(reponame)
-
+
logger = BuildLogger(self.repodir)
building = self._isBuilding(reponame)
building = ', building…' if building else ''
@@ -190,7 +193,7 @@ def default(self, reponame):
template.assignData("buildlog", logger.getBuildlog(reponame))
template.assignData("pagetitle", reponame + " build log")
-
+
return template.render()
@@ -219,7 +222,7 @@ class DashboardPage(BasePage):
}
# 'tools.staticdir.index' : 'index.html',
}
-
+
@cherrypy.expose
def index(self, **params):
template = Template('templates/home.html')
@@ -231,18 +234,10 @@ def index(self, **params):
building = self._isBuilding(section)
building = ', building…' if building else ''
repos += """{} (info{})""".format(section, section, section, building)
-
+
template.assignData("pagetitle", "Astrid")
template.assignData("repos", repos)
template.assignData("user", user)
return template.render()
-
index._cp_config = {'tools.staticdir.on': False}
-
-
-
-
-
-
-
diff --git a/astrid/server.py b/astrid/server.py
index 7aa1a5c..0217832 100644
--- a/astrid/server.py
+++ b/astrid/server.py
@@ -32,7 +32,7 @@ def htmldir( section="", dir="", path="", hdr=True, **kwargs ):
fh += '
\n'
fh += 'File | Size | Last mod |
'
-
+
fh += """{}/ | {} | {} |
""".format("..", "..", "", "")
for dpath, ddirs, dfiles in os.walk( path ):
@@ -87,13 +87,13 @@ def staticdirindex(section, dir, root="", match="", content_types=None, index=""
"""
req = cherrypy.request
response = cherrypy.response
-
+
user = req.login
reponame = req.path_info.split("/")[1]
if reponame not in ["info", "build", "buildlog"]:
if reponame not in astrid.repos.sections():
raise cherrypy.HTTPError(404)
-
+
if user not in astrid.repos.get(reponame, "users"):
raise cherrypy.HTTPError(401)
@@ -113,7 +113,7 @@ def staticdirindex(section, dir, root="", match="", content_types=None, index=""
# the following block of code directly copied from static.py staticdir
if match and not re.search(match, cherrypy.request.path_info):
return False
-
+
# Allow the use of '~' to refer to a user's home directory.
dir = os.path.expanduser(dir)
@@ -123,7 +123,7 @@ def staticdirindex(section, dir, root="", match="", content_types=None, index=""
msg = "Static dir requires an absolute dir (or root)."
raise ValueError(msg)
dir = os.path.join(root, dir)
-
+
# Determine where we are in the object tree relative to 'section'
# (where the static tool was defined).
if section == 'global':
@@ -131,10 +131,10 @@ def staticdirindex(section, dir, root="", match="", content_types=None, index=""
section = section.rstrip(r"\/")
branch = cherrypy.request.path_info[len(section) + 1:]
branch = urllib.parse.unquote(branch.lstrip(r"\/"))
-
+
# If branch is "", filename will end in a slash
filename = os.path.join(dir, branch)
-
+
# There's a chance that the branch pulled from the URL might
# have ".." or similar uplevel attacks in it. Check that the final
# filename is a child of dir.
@@ -155,7 +155,7 @@ def staticdirindex(section, dir, root="", match="", content_types=None, index=""
# paths become absolute by supplying a value for "tools.static.root".
if not os.path.isabs(path):
raise ValueError("'{}' is not an absolute path.".format(path))
-
+
try:
st = os.stat(path)
except OSError:
@@ -179,5 +179,3 @@ def staticdirindex(section, dir, root="", match="", content_types=None, index=""
# Replace the real staticdir with our version
cherrypy.tools.staticdir = cherrypy._cptools.HandlerTool( staticdirindex )
-
-
diff --git a/astrid/templating.py b/astrid/templating.py
index 268a123..ddd38bd 100644
--- a/astrid/templating.py
+++ b/astrid/templating.py
@@ -9,17 +9,17 @@ def __init__(self, filename):
self.filename = filename
self.includes = {}
self.data = {}
-
+
def assignData(self, name, data):
self.data[name] = str(data)
-
+
def render(self):
# firstly include files
template = pkg_resources.resource_stream('astrid', self.filename).read().decode()
template = re.sub('\{include ([^\}]*)\}', self._include, template)
# secondly expand variables
return re.sub('\{(!)?([^\}]*)\}', self._expand, template)
-
+
def _include(self, mo):
filename = os.path.dirname(self.filename) + "/" + mo.group(1)
if filename not in self.includes:
@@ -29,16 +29,15 @@ def _include(self, mo):
else:
self.includes[filename] = f.read().decode()
f.close()
-
+
return self.includes[filename]
-
+
def _expand(self, mo):
if mo.group(2) in self.data:
data = self.data[mo.group(2)]
else:
- data = "undefined"
+ data = "undefined"
if mo.group(1) == "!":
return html.escape(data)
else:
return data
-
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000..5158860
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,26 @@
+FROM python:3.12
+
+# install docker
+RUN apt update && apt install -y podman
+
+# add astrid user
+ARG DOCKER_USER=astrid
+RUN useradd -m $DOCKER_USER
+
+# change workdir
+WORKDIR /data/
+
+# install python requirements
+COPY requirements.txt .
+RUN pip install -r requirements.txt
+
+# install astrid under astrid user
+COPY . .
+RUN chown -R $DOCKER_USER:$DOCKER_USER /data
+USER $DOCKER_USER
+#RUN ./build.sh
+
+# create config directory
+RUN mkdir ~/.astrid
+
+ENTRYPOINT ./docker/entrypoint.sh
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 0000000..bf40931
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,11 @@
+# Docker container
+## Build
+ - in root directory:
+ ```
+ podman build . -f docker/Dockerfile --tag astrid
+ ```
+
+## Run
+```
+podman run --rm --name astrid --volume "./config:/home/astrid/.astrid" --volume "./ssh:/home/astrid/.ssh" -p 8080:8080 astrid
+```
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
new file mode 100755
index 0000000..ab04fc7
--- /dev/null
+++ b/docker/entrypoint.sh
@@ -0,0 +1,4 @@
+cp -n /data/config.ini.sample /home/astrid/.astrid/config.ini
+cp -n /data/repos.ini.sample /home/astrid/.astrid/repos.ini
+
+python3 -u ./main
diff --git a/bin/astrid b/main
similarity index 91%
rename from bin/astrid
rename to main
index aed5de8..2ddf9a2 100755
--- a/bin/astrid
+++ b/main
@@ -8,6 +8,8 @@ import os
from cherrypy.process.plugins import Daemonizer
from cherrypy.process.plugins import PIDFile
+print("Starting Astrid service")
+
daemon = False
argc = len(sys.argv)
@@ -26,6 +28,8 @@ if daemon:
Daemonizer(cherrypy.engine).subscribe()
PIDFile(cherrypy.engine, pidfile=pidfile).subscribe()
+print("Starting web server")
+
cherrypy.tree.mount(astrid.root, "/", config=astrid.config)
cherrypy.engine.start()
cherrypy.engine.block()
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..9e3f2a0
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+CherryPy
+GitPython
diff --git a/setup.py b/setup.py
index a335f32..d49bdac 100644
--- a/setup.py
+++ b/setup.py
@@ -28,7 +28,7 @@
'cherrypy'
],
scripts=[
- 'bin/astrid',
+ 'main',
'bin/touch-astrid.sample'
],
package_dir={
From 050c1df8ca1c8f19670e9778e678c5d1edacddf2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Tue, 26 Dec 2023 01:52:18 +0100
Subject: [PATCH 02/17] working build inside container
---
.dockerignore | 3 ++-
astrid/pages.py | 9 ++++-----
docker/.gitignore | 4 ++++
docker/Dockerfile | 2 +-
docker/docker-compose.yml | 21 +++++++++++++++++++++
docker/start.sh | 1 +
repos.ini.sample | 32 +++++---------------------------
7 files changed, 38 insertions(+), 34 deletions(-)
create mode 100644 docker/.gitignore
create mode 100644 docker/docker-compose.yml
create mode 100755 docker/start.sh
diff --git a/.dockerignore b/.dockerignore
index 9a4b405..2c1cd4e 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1 +1,2 @@
-docker/Dockerfile
+docker/*
+!docker/entrypoint.sh
diff --git a/astrid/pages.py b/astrid/pages.py
index fdb5cd2..c497466 100644
--- a/astrid/pages.py
+++ b/astrid/pages.py
@@ -122,6 +122,7 @@ def _updateRepo(self, reponame):
repo.git.submodule("foreach", "git", "fetch")
repo.git.submodule("update")
+ print(f"Building {reponame}")
# now set correct group (same as build user)
usr = self.repos.get(reponame, "build_usr")
p = subprocess.Popen(["chgrp", usr, "-R", "-f", localpath])
@@ -130,15 +131,13 @@ def _updateRepo(self, reponame):
return repo
def _build(self, reponame):
- usr = self.repos.get(reponame, "build_usr")
cmd = self.repos.get(reponame, "build_cmd")
- args = self.repos.get(reponame, "build_args")
- cwd = os.path.join(self.repodir, reponame)
+ cwd = os.path.join(self.repodir, reponame) # current working directory
+ image_version = self.repos.get(reponame, "image_version")
logfilename = os.path.expanduser("~/.astrid/{}.build.log".format(reponame))
logfile = open(logfilename, "w")
- p = subprocess.Popen([cmd] + shlex.split(args), cwd=cwd, stdout=logfile, stderr=logfile, stdin=open("/dev/null"))
- p.wait()
+ p = subprocess.run(["podman", "run", "--rm", "-v", f"{cwd}:/usr/src/local", f"fykosak/buildtools:{image_version}"] + cmd.split(), cwd=cwd, stdout=logfile, stderr=logfile, check=False)
return p.returncode == 0
class InfoPage(BasePage):
diff --git a/docker/.gitignore b/docker/.gitignore
new file mode 100644
index 0000000..d0549f0
--- /dev/null
+++ b/docker/.gitignore
@@ -0,0 +1,4 @@
+config
+containers
+repos
+ssh
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 5158860..6924cc3 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,7 +1,7 @@
FROM python:3.12
# install docker
-RUN apt update && apt install -y podman
+RUN apt update && apt install -y podman containers-storage
# add astrid user
ARG DOCKER_USER=astrid
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
new file mode 100644
index 0000000..2106062
--- /dev/null
+++ b/docker/docker-compose.yml
@@ -0,0 +1,21 @@
+version: '3'
+
+services:
+ astrid:
+ image: astrid
+ build:
+ context: ..
+ dockerfile: docker/Dockerfile
+ restart: on-failure:3
+ container_name: astrid
+ environment:
+ TZ: 'Europe/Prague'
+ privileged: true # needed for containers
+ volumes:
+ - ./config:/home/astrid/.astrid
+ - ./ssh:/home/astrid/.ssh
+ - ./containers:/home/astrid/.local/share/containers
+ - ./repos:/data/repos
+ ports:
+ - 8080:8080 # opened port mapping, not needed with proxy
+ user: 1000:1000 # expects the main user with uid:pid
diff --git a/docker/start.sh b/docker/start.sh
new file mode 100755
index 0000000..a36d856
--- /dev/null
+++ b/docker/start.sh
@@ -0,0 +1 @@
+docker run --rm --name astrid --volume "./config:/home/astrid/.astrid" --volume "./ssh:/home/astrid/.ssh" --volume "./containers:/home/astrid/.local/share/containers" --volume "./repos:/data/repos" -p 8080:8080 --privileged astrid
diff --git a/repos.ini.sample b/repos.ini.sample
index 60c9af5..9c658e8 100644
--- a/repos.ini.sample
+++ b/repos.ini.sample
@@ -1,28 +1,6 @@
-[fykos26]
-path=/home/fksgit/data/fykos26.git
+[fykos37]
+path=gitea@fykos.cz:FYKOS/fykos37.git
users=fykos,repo
-build_usr=fksgit
-build_cmd=make
-build_args=all
-
-[fykos25]
-path=/home/fksgit/data/fykos25.git
-users=fykos,repo
-build_usr=fksgit
-build_cmd=make
-build_args=all
-
-[vyfuk2]
-path=/home/fksgit/data/vyfuk2.git
-users=fykos,vyfuk,repo
-build_usr=vyfuk
-build_cmd=make
-build_args=all
-
-[vyfuk1]
-path=/home/fksgit/data/vyfuk1.git
-users=fykos,vyfuk,repo
-build_usr=vyfuk
-build_cmd=make
-build_args=all
-
+image_version=latest
+build_usr=astrid
+build_cmd=make -k all
From 184e1d7f20ba0748e186af3d524114f195c2cc83 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Tue, 26 Dec 2023 21:48:11 +0100
Subject: [PATCH 03/17] update python syntax
---
astrid/pages.py | 44 +++++++++++++++-----------------------
astrid/server.py | 17 ++++++---------
astrid/templates/info.html | 2 +-
astrid/templating.py | 9 ++++----
4 files changed, 29 insertions(+), 43 deletions(-)
diff --git a/astrid/pages.py b/astrid/pages.py
index c497466..fbafcf6 100644
--- a/astrid/pages.py
+++ b/astrid/pages.py
@@ -1,14 +1,10 @@
-import cherrypy
import os.path
import os
import subprocess
-import shlex
-import threading
-import sys
import time
+import cherrypy
from git import Repo, Git, GitCommandError
-from configparser import ConfigParser
from astrid import BuildLogger
from astrid.templating import Template
@@ -53,26 +49,26 @@ def default(self, reponame):
repo = self._updateRepo(reponame)
try:
if self._build(reponame):
- msg = "#{} Build succeeded.".format(repo.head.commit.hexsha[0:6])
+ msg = f"#{repo.head.commit.hexsha[0:6]} Build succeeded."
msg_class = "green"
time_end = time.time()
else:
- msg = "#{} Build failed.".format(repo.head.commit.hexsha[0:6])
+ msg = f"#{repo.head.commit.hexsha[0:6]} Build failed."
msg_class = "red"
+ time_end = time.time()
except:
- msg = "#{} Build error.".format(repo.head.commit.hexsha[0:6])
+ msg = f"#{repo.head.commit.hexsha[0:6]} Build error."
msg_class = "red"
raise
except GitCommandError as e:
- msg = "Pull error. ({})".format(str(e))
+ msg = f"Pull error. ({e})"
msg_class = "red"
sendMail = True
raise
- if time_end != None:
+ if time_end is not None:
msg += " ({:.2f} s)".format(time_end - time_start)
-
template = Template("templates/build.html")
template.assignData("reponame", reponame)
template.assignData("message", msg)
@@ -102,8 +98,7 @@ def _updateRepo(self, reponame):
repo.git.submodule("init")
# not-working umask workaround
- p = subprocess.Popen(["chmod", "g+w", localpath])
- p.wait()
+ subprocess.run(["chmod", "g+w", localpath], check=False)
else:
print(f"Pulling repository {reponame}")
repo = Repo(localpath)
@@ -122,12 +117,6 @@ def _updateRepo(self, reponame):
repo.git.submodule("foreach", "git", "fetch")
repo.git.submodule("update")
- print(f"Building {reponame}")
- # now set correct group (same as build user)
- usr = self.repos.get(reponame, "build_usr")
- p = subprocess.Popen(["chgrp", usr, "-R", "-f", localpath])
- p.wait()
-
return repo
def _build(self, reponame):
@@ -135,7 +124,8 @@ def _build(self, reponame):
cwd = os.path.join(self.repodir, reponame) # current working directory
image_version = self.repos.get(reponame, "image_version")
- logfilename = os.path.expanduser("~/.astrid/{}.build.log".format(reponame))
+ print(f"Building {reponame}")
+ logfilename = os.path.expanduser(f"~/.astrid/{reponame}.build.log")
logfile = open(logfilename, "w")
p = subprocess.run(["podman", "run", "--rm", "-v", f"{cwd}:/usr/src/local", f"fykosak/buildtools:{image_version}"] + cmd.split(), cwd=cwd, stdout=logfile, stderr=logfile, check=False)
return p.returncode == 0
@@ -156,10 +146,10 @@ def default(self, reponame):
for record in logger.getLogs(reponame):
record += ['']*(3-len(record))
if first:
- msg += """{} | {} | {} |
""".format(record[0], reponame, record[1], record[2])
+ msg += f'{record[0]} | {record[1]} | {record[2]} |
'
first = False
else:
- msg += """{} | {} | {} |
""".format(record[0], record[1], record[2])
+ msg += f"{record[0]} | {record[1]} | {record[2]} |
"
msg += "
"
@@ -168,7 +158,7 @@ def default(self, reponame):
for k, v in self.repos.items(reponame):
template.assignData("repo." + k, v)
- template.assignData("messages", msg)
+ template.assignData("messages", msg)
template.assignData("pagetitle", reponame + " info")
return template.render()
@@ -185,7 +175,7 @@ def default(self, reponame):
logger = BuildLogger(self.repodir)
building = self._isBuilding(reponame)
building = ', building…' if building else ''
-
+
template = Template("templates/buildlog.html")
template.assignData("reponame", reponame)
template.assignData("building", building)
@@ -219,7 +209,7 @@ class DashboardPage(BasePage):
#'sample': ??? how about this stuff?
#'Makefile': ??? how about Makefiles
}
-# 'tools.staticdir.index' : 'index.html',
+ #'tools.staticdir.index' : 'index.html',
}
@cherrypy.expose
@@ -228,11 +218,11 @@ def index(self, **params):
repos = ""
user = cherrypy.request.login
- for section in self.repos.sections():
+ for section in self.repos.sections():
if user in self.repos.get(section, "users").split(","):
building = self._isBuilding(section)
building = ', building…' if building else ''
- repos += """{} (info{})""".format(section, section, section, building)
+ repos += f'{section} (info{building})'
template.assignData("pagetitle", "Astrid")
template.assignData("repos", repos)
diff --git a/astrid/server.py b/astrid/server.py
index 0217832..c649f75 100644
--- a/astrid/server.py
+++ b/astrid/server.py
@@ -1,18 +1,15 @@
-import cherrypy
import os.path
-import cherrypy.lib.auth_basic
import re
import stat
import urllib.request, urllib.parse, urllib.error
import datetime
+import cherrypy
+import cherrypy.lib.auth_basic
from cherrypy.lib import cptools, httputil
from cherrypy.lib.static import staticdir
-from configparser import ConfigParser
-
import astrid
-
def htmldir( section="", dir="", path="", hdr=True, **kwargs ):
fh = ''
url = "http://" + cherrypy.request.headers.get('Host', '') + \
@@ -33,7 +30,7 @@ def htmldir( section="", dir="", path="", hdr=True, **kwargs ):
'
\n'
fh += 'File | Size | Last mod |
'
- fh += """{}/ | {} | {} |
""".format("..", "..", "", "")
+ fh += '../ | | |
'
for dpath, ddirs, dfiles in os.walk( path ):
for dn in sorted( ddirs ):
@@ -42,7 +39,7 @@ def htmldir( section="", dir="", path="", hdr=True, **kwargs ):
fdn = os.path.join( dpath, dn )
dmtime = os.path.getmtime( fdn )
dtim = datetime.datetime.fromtimestamp( dmtime ).isoformat(' ')
- fh += """{}/ | {} | {} |
""".format(dn + '/', dn, "", dtim)
+ fh += f'{dn}/ | | {dtim} |
'
del ddirs[:] # limit to one level
@@ -53,7 +50,7 @@ def htmldir( section="", dir="", path="", hdr=True, **kwargs ):
siz = os.path.getsize( fn )
fmtime = os.path.getmtime( fn )
ftim = datetime.datetime.fromtimestamp( fmtime ).isoformat(' ')
- fh += """{} | {} | {} |
""".format(fil, fil, siz, ftim)
+ fh += f'{fil} | {siz} | {ftim} |
'
fh += '
'
# postamble
@@ -92,7 +89,7 @@ def staticdirindex(section, dir, root="", match="", content_types=None, index=""
reponame = req.path_info.split("/")[1]
if reponame not in ["info", "build", "buildlog"]:
if reponame not in astrid.repos.sections():
- raise cherrypy.HTTPError(404)
+ raise cherrypy.HTTPError(404)
if user not in astrid.repos.get(reponame, "users"):
raise cherrypy.HTTPError(401)
@@ -154,7 +151,7 @@ def staticdirindex(section, dir, root="", match="", content_types=None, index=""
# variety of paths). If using tools.static, you can make your relative
# paths become absolute by supplying a value for "tools.static.root".
if not os.path.isabs(path):
- raise ValueError("'{}' is not an absolute path.".format(path))
+ raise ValueError(f"'{path}' is not an absolute path.")
try:
st = os.stat(path)
diff --git a/astrid/templates/info.html b/astrid/templates/info.html
index 0286857..b31cfe9 100644
--- a/astrid/templates/info.html
+++ b/astrid/templates/info.html
@@ -5,7 +5,7 @@ Information for repository {reponame}
Address: {!repo.path}
Permitted users: {!repo.users}
Build user: {!repo.build_usr}
-Build command: {!repo.build_cmd} {!repo.build_args}
+Build command: {!repo.build_cmd}
{messages}
{include footer.inc}
diff --git a/astrid/templating.py b/astrid/templating.py
index ddd38bd..159e354 100644
--- a/astrid/templating.py
+++ b/astrid/templating.py
@@ -14,11 +14,11 @@ def assignData(self, name, data):
self.data[name] = str(data)
def render(self):
- # firstly include files
+ # firstly include files
template = pkg_resources.resource_stream('astrid', self.filename).read().decode()
- template = re.sub('\{include ([^\}]*)\}', self._include, template)
+ template = re.sub('{include ([^}]*)}', self._include, template)
# secondly expand variables
- return re.sub('\{(!)?([^\}]*)\}', self._expand, template)
+ return re.sub('{(!)?([^}]*)}', self._expand, template)
def _include(self, mo):
filename = os.path.dirname(self.filename) + "/" + mo.group(1)
@@ -39,5 +39,4 @@ def _expand(self, mo):
data = "undefined"
if mo.group(1) == "!":
return html.escape(data)
- else:
- return data
+ return data
From 96064a5cb5791609ca2c9c1b461035b617a26a75 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Tue, 26 Dec 2023 22:00:11 +0100
Subject: [PATCH 04/17] build image on gh
---
.github/workflows/deploy-image.yml | 46 ++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
create mode 100644 .github/workflows/deploy-image.yml
diff --git a/.github/workflows/deploy-image.yml b/.github/workflows/deploy-image.yml
new file mode 100644
index 0000000..e874649
--- /dev/null
+++ b/.github/workflows/deploy-image.yml
@@ -0,0 +1,46 @@
+name: Create and publish a Docker image
+
+on:
+ push:
+ branchers:
+ - master
+ - dev-docker
+ paths:
+ - 'docker/**'
+ - '.github/workflows/deploy-image.yml'
+
+env:
+ REGISTRY: ghcr.io
+ IMAGE_NAME: ${{ github.repository }}
+
+jobs:
+ build-and-push-image:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+ - name: Log in to the Container registry
+ uses: docker/login-action@v3
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Extract metadata (tags, labels) for Docker
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+ - name: Build and push Docker image
+ uses: docker/build-push-action@v5
+ with:
+ context: docker
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
From d86d3a2b8b03ec704b9215edfe58e60f1e83c0be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Tue, 26 Dec 2023 22:03:26 +0100
Subject: [PATCH 05/17] fix gh action
---
.github/workflows/deploy-image.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/deploy-image.yml b/.github/workflows/deploy-image.yml
index e874649..c4de4b4 100644
--- a/.github/workflows/deploy-image.yml
+++ b/.github/workflows/deploy-image.yml
@@ -38,7 +38,8 @@ jobs:
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
- context: docker
+ context: .
+ file: ./docker/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
From eab860dacfd479f4bb98cf42490a7a780b6c17c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Tue, 26 Dec 2023 22:51:49 +0100
Subject: [PATCH 06/17] fix dirpaths for container
---
astrid/__init__.py | 4 ++--
config.ini.sample | 2 +-
docker/Dockerfile | 7 +++----
docker/README.md | 16 +++++++---------
docker/docker-compose.prod.yml | 16 ++++++++++++++++
docker/docker-compose.yml | 4 ++--
docker/entrypoint.sh | 4 ++--
docker/start.sh | 1 -
8 files changed, 33 insertions(+), 21 deletions(-)
create mode 100644 docker/docker-compose.prod.yml
delete mode 100755 docker/start.sh
diff --git a/astrid/__init__.py b/astrid/__init__.py
index 8ce06e1..f985eef 100644
--- a/astrid/__init__.py
+++ b/astrid/__init__.py
@@ -68,8 +68,8 @@ def _sendMail(self, reponame, message):
from configparser import ConfigParser
-REPOS_INI = '~/.astrid/repos.ini'
-CONFIG_INI = '~/.astrid/config.ini'
+REPOS_INI = '/app/config/repos.ini'
+CONFIG_INI = '/app/config/config.ini'
repos = ConfigParser()
repos.read(os.path.expanduser(REPOS_INI))
diff --git a/config.ini.sample b/config.ini.sample
index 0c4c376..b8dcfe9 100644
--- a/config.ini.sample
+++ b/config.ini.sample
@@ -7,7 +7,7 @@ tools.auth_basic.on: True
tools.auth_basic.realm: 'Astrid authorization'
tools.auth_basic.checkpassword: cherrypy.lib.auth_basic.checkpassword_dict({'user': 'passwd'})
tools.expires.sec = 0
-repodir = 'data'
+repodir = '/app/repos'
[/style.css]
tools.staticfile.on = True
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 6924cc3..d42c42e 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -8,7 +8,7 @@ ARG DOCKER_USER=astrid
RUN useradd -m $DOCKER_USER
# change workdir
-WORKDIR /data/
+WORKDIR /app
# install python requirements
COPY requirements.txt .
@@ -16,11 +16,10 @@ RUN pip install -r requirements.txt
# install astrid under astrid user
COPY . .
-RUN chown -R $DOCKER_USER:$DOCKER_USER /data
+RUN chown -R $DOCKER_USER:$DOCKER_USER /app
USER $DOCKER_USER
-#RUN ./build.sh
# create config directory
-RUN mkdir ~/.astrid
+RUN mkdir /app/config /app/repos
ENTRYPOINT ./docker/entrypoint.sh
diff --git a/docker/README.md b/docker/README.md
index bf40931..c0f9fa9 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -1,11 +1,9 @@
# Docker container
-## Build
- - in root directory:
- ```
- podman build . -f docker/Dockerfile --tag astrid
- ```
-## Run
-```
-podman run --rm --name astrid --volume "./config:/home/astrid/.astrid" --volume "./ssh:/home/astrid/.ssh" -p 8080:8080 astrid
-```
+## Vývoj
+- nutnost: nainstalovaný `docker` a `docker-compose-plugin`
+- vytvoření aktuální image: `docker compose build` (potřeba spustit před prvním spuštěním)
+- vytvoření mount složek (případný `chown` pro vlastnění správným uživatelem): `mkdir config containers repos ssh`
+- spuštění: `docker compose up`
+- spouštění příkazů uvnitř dockeru: `docker exec -it astrid `
+ - stáhnutí `fykosak/buildtools` (potřeba před prvním buildem repozitáře): `docker exec -it astrid podman pull docker.io/fykosak/buildtools`
diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml
new file mode 100644
index 0000000..c26fbbd
--- /dev/null
+++ b/docker/docker-compose.prod.yml
@@ -0,0 +1,16 @@
+version: '3'
+
+services:
+ astrid:
+ image: ghcr.io/fykosak/astrid:master
+ restart: on-failure:3
+ container_name: astrid
+ environment:
+ TZ: 'Europe/Prague'
+ privileged: true # needed for containers
+ volumes:
+ - ./config:/home/astrid/.astrid
+ - ./ssh:/home/astrid/.ssh
+ - ./containers:/home/astrid/.local/share/containers
+ - ./repos:/data/repos
+ user: 1000:1000 # expects the main user with uid:pid
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 2106062..27ec522 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -12,10 +12,10 @@ services:
TZ: 'Europe/Prague'
privileged: true # needed for containers
volumes:
- - ./config:/home/astrid/.astrid
+ - ./config:/app/config
- ./ssh:/home/astrid/.ssh
- ./containers:/home/astrid/.local/share/containers
- - ./repos:/data/repos
+ - ./repos:/app/repos
ports:
- 8080:8080 # opened port mapping, not needed with proxy
user: 1000:1000 # expects the main user with uid:pid
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
index ab04fc7..720f64e 100755
--- a/docker/entrypoint.sh
+++ b/docker/entrypoint.sh
@@ -1,4 +1,4 @@
-cp -n /data/config.ini.sample /home/astrid/.astrid/config.ini
-cp -n /data/repos.ini.sample /home/astrid/.astrid/repos.ini
+cp -n /app/config.ini.sample /app/config/config.ini
+cp -n /app/repos.ini.sample /app/config/repos.ini
python3 -u ./main
diff --git a/docker/start.sh b/docker/start.sh
deleted file mode 100755
index a36d856..0000000
--- a/docker/start.sh
+++ /dev/null
@@ -1 +0,0 @@
-docker run --rm --name astrid --volume "./config:/home/astrid/.astrid" --volume "./ssh:/home/astrid/.ssh" --volume "./containers:/home/astrid/.local/share/containers" --volume "./repos:/data/repos" -p 8080:8080 --privileged astrid
From b58fbec9a38e80814d2b7c6fa059b373b0e9744a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Thu, 28 Dec 2023 17:14:52 +0100
Subject: [PATCH 07/17] make docker work under different user than 1000
---
.dockerignore | 4 ++++
.gitignore | 1 +
astrid/__init__.py | 8 ++++----
astrid/pages.py | 3 ++-
config.ini.sample | 4 ++--
docker/.gitignore | 4 ----
docker/Dockerfile | 11 ++++++++---
docker/README.md | 2 +-
docker/docker-compose.prod.yml | 7 ++-----
docker/docker-compose.yml | 7 ++-----
docker/entrypoint.sh | 19 +++++++++++++++++--
docker/libpod.conf | 1 +
docker/ssh.conf | 7 +++++++
docker/sshd.conf | 2 ++
docker/storage.conf | 3 +++
main | 7 ++++---
16 files changed, 60 insertions(+), 30 deletions(-)
delete mode 100644 docker/.gitignore
create mode 100644 docker/libpod.conf
create mode 100644 docker/ssh.conf
create mode 100644 docker/sshd.conf
create mode 100644 docker/storage.conf
diff --git a/.dockerignore b/.dockerignore
index 2c1cd4e..e780801 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,2 +1,6 @@
docker/*
!docker/entrypoint.sh
+!docker/ssh.conf
+!docker/sshd.conf
+!docker/libpod.conf
+!docker/storage.conf
diff --git a/.gitignore b/.gitignore
index 468b214..21332cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@ dist
astrid.egg-info/
*~
*.pyc
+docker/data
diff --git a/astrid/__init__.py b/astrid/__init__.py
index f985eef..2318b44 100644
--- a/astrid/__init__.py
+++ b/astrid/__init__.py
@@ -13,10 +13,10 @@ def __init__(self, repodir):
self.repodir = repodir
def _getLogfile(self, reponame):
- return os.path.expanduser('~/.astrid/{}.log'.format(reponame))
+ return f'/data/log/{reponame}.log'
def _getBuildlogfile(self, reponame):
- return os.path.expanduser('~/.astrid/{}.build.log'.format(reponame))
+ return f'/data/log/{reponame}.build.log'
def log(self, reponame, message, sendMail = False):
logfile = self._getLogfile(reponame)
@@ -68,8 +68,8 @@ def _sendMail(self, reponame, message):
from configparser import ConfigParser
-REPOS_INI = '/app/config/repos.ini'
-CONFIG_INI = '/app/config/config.ini'
+REPOS_INI = '/data/config/repos.ini'
+CONFIG_INI = '/data/config/config.ini'
repos = ConfigParser()
repos.read(os.path.expanduser(REPOS_INI))
diff --git a/astrid/pages.py b/astrid/pages.py
index fbafcf6..653f29a 100644
--- a/astrid/pages.py
+++ b/astrid/pages.py
@@ -125,9 +125,10 @@ def _build(self, reponame):
image_version = self.repos.get(reponame, "image_version")
print(f"Building {reponame}")
- logfilename = os.path.expanduser(f"~/.astrid/{reponame}.build.log")
+ logfilename = os.path.expanduser(f"/data/log/{reponame}.build.log")
logfile = open(logfilename, "w")
p = subprocess.run(["podman", "run", "--rm", "-v", f"{cwd}:/usr/src/local", f"fykosak/buildtools:{image_version}"] + cmd.split(), cwd=cwd, stdout=logfile, stderr=logfile, check=False)
+ logfile.close()
return p.returncode == 0
class InfoPage(BasePage):
diff --git a/config.ini.sample b/config.ini.sample
index b8dcfe9..a187c1f 100644
--- a/config.ini.sample
+++ b/config.ini.sample
@@ -1,13 +1,13 @@
[global]
log.screen = False
server.socket_port = 8080
-server.socket_host = '127.0.0.1'
+server.socket_host = '0.0.0.0'
server.thread_pool = 10
tools.auth_basic.on: True
tools.auth_basic.realm: 'Astrid authorization'
tools.auth_basic.checkpassword: cherrypy.lib.auth_basic.checkpassword_dict({'user': 'passwd'})
tools.expires.sec = 0
-repodir = '/app/repos'
+repodir = '/data/repos'
[/style.css]
tools.staticfile.on = True
diff --git a/docker/.gitignore b/docker/.gitignore
deleted file mode 100644
index d0549f0..0000000
--- a/docker/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-config
-containers
-repos
-ssh
diff --git a/docker/Dockerfile b/docker/Dockerfile
index d42c42e..c70e968 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -14,12 +14,17 @@ WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
+# copy ssh config
+COPY ./docker/ssh.conf /etc/ssh/ssh_config.d/99-astrid.conf
+COPY ./docker/sshd.conf /etc/ssh/sshd_config.d/99-astrid.conf
+
+# copy containers config
+COPY ./docker/libpod.conf /etc/containers/libpod.conf
+COPY ./docker/storage.conf /etc/containers/storage.conf
+
# install astrid under astrid user
COPY . .
RUN chown -R $DOCKER_USER:$DOCKER_USER /app
USER $DOCKER_USER
-# create config directory
-RUN mkdir /app/config /app/repos
-
ENTRYPOINT ./docker/entrypoint.sh
diff --git a/docker/README.md b/docker/README.md
index c0f9fa9..905d21a 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -3,7 +3,7 @@
## Vývoj
- nutnost: nainstalovaný `docker` a `docker-compose-plugin`
- vytvoření aktuální image: `docker compose build` (potřeba spustit před prvním spuštěním)
-- vytvoření mount složek (případný `chown` pro vlastnění správným uživatelem): `mkdir config containers repos ssh`
+- vytvoření mount složky `data` (případně `chown`, aby byla vlastněna uživatelem, pod kterým kontejner poběží)
- spuštění: `docker compose up`
- spouštění příkazů uvnitř dockeru: `docker exec -it astrid `
- stáhnutí `fykosak/buildtools` (potřeba před prvním buildem repozitáře): `docker exec -it astrid podman pull docker.io/fykosak/buildtools`
diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml
index c26fbbd..21eed2b 100644
--- a/docker/docker-compose.prod.yml
+++ b/docker/docker-compose.prod.yml
@@ -9,8 +9,5 @@ services:
TZ: 'Europe/Prague'
privileged: true # needed for containers
volumes:
- - ./config:/home/astrid/.astrid
- - ./ssh:/home/astrid/.ssh
- - ./containers:/home/astrid/.local/share/containers
- - ./repos:/data/repos
- user: 1000:1000 # expects the main user with uid:pid
+ - ./data:/data
+ user: 1000:1000 # expects the main user as uid:pid
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 27ec522..485992e 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -12,10 +12,7 @@ services:
TZ: 'Europe/Prague'
privileged: true # needed for containers
volumes:
- - ./config:/app/config
- - ./ssh:/home/astrid/.ssh
- - ./containers:/home/astrid/.local/share/containers
- - ./repos:/app/repos
+ - ./data:/data
ports:
- 8080:8080 # opened port mapping, not needed with proxy
- user: 1000:1000 # expects the main user with uid:pid
+ user: 1000:1000 # expects the main user as uid:pid
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
index 720f64e..85fff87 100755
--- a/docker/entrypoint.sh
+++ b/docker/entrypoint.sh
@@ -1,4 +1,19 @@
-cp -n /app/config.ini.sample /app/config/config.ini
-cp -n /app/repos.ini.sample /app/config/repos.ini
+#!/bin/bash
+
+DATA_OWNER=$(stat -c '%u' /data)
+
+if [ "$DATA_OWNER" -ne "$UID" ]; then
+ echo "Directory 'data' not owned by target user with $UID, instead owned by user with uid $DATA_OWNER"
+ exit 1
+fi
+
+mkdir -p /data/config /data/containers /data/log /data/repos /data/ssh
+
+cp -n /app/config.ini.sample /data/config/config.ini
+cp -n /app/repos.ini.sample /data/config/repos.ini
+
+if [ $(ls "/data/ssh" | grep ".pub" | wc -l) -eq 0 ]; then
+ ssh-keygen -t ed25519 -f /data/ssh/id_ed25519
+fi
python3 -u ./main
diff --git a/docker/libpod.conf b/docker/libpod.conf
new file mode 100644
index 0000000..86e8062
--- /dev/null
+++ b/docker/libpod.conf
@@ -0,0 +1 @@
+static_dir = "/data/containers/storage/libpod"
diff --git a/docker/ssh.conf b/docker/ssh.conf
new file mode 100644
index 0000000..9c3c96d
--- /dev/null
+++ b/docker/ssh.conf
@@ -0,0 +1,7 @@
+# change .ssh files to /data/ssh
+Host *
+ IdentityFile /data/ssh/id_rsa
+ IdentityFile /data/ssh/id_dsa
+ IdentityFile /data/ssh/id_ecdsa
+ IdentityFile /data/ssh/id_ed25519
+ UserKnownHostsFile /data/ssh/known_hosts
diff --git a/docker/sshd.conf b/docker/sshd.conf
new file mode 100644
index 0000000..4ec32c7
--- /dev/null
+++ b/docker/sshd.conf
@@ -0,0 +1,2 @@
+# change .ssh files to /data/ssh
+AuthorizedKeysFile /data/ssh/authorized_keys
diff --git a/docker/storage.conf b/docker/storage.conf
new file mode 100644
index 0000000..f5091cd
--- /dev/null
+++ b/docker/storage.conf
@@ -0,0 +1,3 @@
+[storage]
+driver = "overlay"
+rootless_storage_path = "/data/containers/storage"
diff --git a/main b/main
index 2ddf9a2..ccffe5b 100755
--- a/main
+++ b/main
@@ -1,13 +1,14 @@
#!/usr/bin/env python3
-import cherrypy
-import astrid
import sys
import os
+import cherrypy
from cherrypy.process.plugins import Daemonizer
from cherrypy.process.plugins import PIDFile
+import astrid
+
print("Starting Astrid service")
daemon = False
@@ -16,7 +17,7 @@ argc = len(sys.argv)
if argc == 1:
daemon = False
elif argc == 2 and sys.argv[1] == '-d':
- pidfile = os.path.expanduser("~/.astrid/pidfile")
+ pidfile = os.path.expanduser("/data/pidfile")
daemon = True
elif argc == 3 and sys.argv[1] == '-d':
pidfile = sys.argv[2]
From 9b1d01ff7dab94e7822443e24a02b1f14732cd2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Thu, 28 Dec 2023 19:19:30 +0100
Subject: [PATCH 08/17] fix ssh and home dir
---
docker/Dockerfile | 1 +
docker/README.md | 4 ++--
docker/docker-compose.prod.yml | 1 +
docker/docker-compose.yml | 1 +
docker/entrypoint.sh | 5 +++++
5 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/docker/Dockerfile b/docker/Dockerfile
index c70e968..d798ac1 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -6,6 +6,7 @@ RUN apt update && apt install -y podman containers-storage
# add astrid user
ARG DOCKER_USER=astrid
RUN useradd -m $DOCKER_USER
+RUN chmod 777 /home
# change workdir
WORKDIR /app
diff --git a/docker/README.md b/docker/README.md
index 905d21a..e20cdf5 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -1,9 +1,9 @@
# Docker container
-
-## Vývoj
- nutnost: nainstalovaný `docker` a `docker-compose-plugin`
- vytvoření aktuální image: `docker compose build` (potřeba spustit před prvním spuštěním)
- vytvoření mount složky `data` (případně `chown`, aby byla vlastněna uživatelem, pod kterým kontejner poběží)
- spuštění: `docker compose up`
- spouštění příkazů uvnitř dockeru: `docker exec -it astrid `
- stáhnutí `fykosak/buildtools` (potřeba před prvním buildem repozitáře): `docker exec -it astrid podman pull docker.io/fykosak/buildtools`
+- po fungování gitu potřeba přidat ssh klíč na cílový server
+ - je možné, že ssh bude vyhazovat chybu špatně nastaveného přístupu k privátnímu klíči, v takovém případě je potřeba přes `chmod` nastavit privátnímu klíči práva `600`
diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml
index 21eed2b..e1f0c38 100644
--- a/docker/docker-compose.prod.yml
+++ b/docker/docker-compose.prod.yml
@@ -10,4 +10,5 @@ services:
privileged: true # needed for containers
volumes:
- ./data:/data
+ - /etc/passwd:/etc/passwd:ro # needed for ssh-keygen to work
user: 1000:1000 # expects the main user as uid:pid
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 485992e..22f6d14 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -13,6 +13,7 @@ services:
privileged: true # needed for containers
volumes:
- ./data:/data
+ - /etc/passwd:/etc/passwd:ro # needed for ssh-keygen to work
ports:
- 8080:8080 # opened port mapping, not needed with proxy
user: 1000:1000 # expects the main user as uid:pid
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
index 85fff87..79d8fd4 100755
--- a/docker/entrypoint.sh
+++ b/docker/entrypoint.sh
@@ -7,6 +7,11 @@ if [ "$DATA_OWNER" -ne "$UID" ]; then
exit 1
fi
+# create home folder
+export HOME="/home/$(id -u)"
+mkdir -p $HOME
+
+# create needed files if missing
mkdir -p /data/config /data/containers /data/log /data/repos /data/ssh
cp -n /app/config.ini.sample /data/config/config.ini
From f84f49cd7d60f370b90e1bbf0772b3b27b3667c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Thu, 28 Dec 2023 23:33:50 +0100
Subject: [PATCH 09/17] create docker as root for user creation
---
astrid/pages.py | 3 +--
docker/Dockerfile | 9 +--------
docker/docker-compose.prod.yml | 3 ++-
docker/docker-compose.yml | 4 ++--
docker/entrypoint.sh | 37 ++++++++++++++++++++++++----------
repos.ini.sample | 2 +-
6 files changed, 33 insertions(+), 25 deletions(-)
diff --git a/astrid/pages.py b/astrid/pages.py
index 653f29a..6cdd069 100644
--- a/astrid/pages.py
+++ b/astrid/pages.py
@@ -81,14 +81,13 @@ def default(self, reponame):
lock.release()
def _updateRepo(self, reponame):
- print(f"Updating repository {reponame}")
remotepath = self.repos.get(reponame, "path")
submodules = self.repos.get(reponame, "submodules") if self.repos.has_option(reponame, "submodules") else False
localpath = os.path.join(self.repodir, reponame)
os.umask(0o007) # create repo content not readable to others
if not os.path.isdir(localpath):
- print(f"Repository {reponame} empty, creating")
+ print(f"Repository {reponame} empty, cloning")
g = Git()
g.clone(remotepath, localpath)
diff --git a/docker/Dockerfile b/docker/Dockerfile
index d798ac1..04e9e98 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -3,11 +3,6 @@ FROM python:3.12
# install docker
RUN apt update && apt install -y podman containers-storage
-# add astrid user
-ARG DOCKER_USER=astrid
-RUN useradd -m $DOCKER_USER
-RUN chmod 777 /home
-
# change workdir
WORKDIR /app
@@ -23,9 +18,7 @@ COPY ./docker/sshd.conf /etc/ssh/sshd_config.d/99-astrid.conf
COPY ./docker/libpod.conf /etc/containers/libpod.conf
COPY ./docker/storage.conf /etc/containers/storage.conf
-# install astrid under astrid user
+# install astrid
COPY . .
-RUN chown -R $DOCKER_USER:$DOCKER_USER /app
-USER $DOCKER_USER
ENTRYPOINT ./docker/entrypoint.sh
diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml
index e1f0c38..6ea7561 100644
--- a/docker/docker-compose.prod.yml
+++ b/docker/docker-compose.prod.yml
@@ -7,8 +7,9 @@ services:
container_name: astrid
environment:
TZ: 'Europe/Prague'
+ PUID: 1000
+ GUID: 1000
privileged: true # needed for containers
volumes:
- ./data:/data
- /etc/passwd:/etc/passwd:ro # needed for ssh-keygen to work
- user: 1000:1000 # expects the main user as uid:pid
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 22f6d14..b12e92b 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -10,10 +10,10 @@ services:
container_name: astrid
environment:
TZ: 'Europe/Prague'
+ PUID: 1000
+ GUID: 1000
privileged: true # needed for containers
volumes:
- ./data:/data
- - /etc/passwd:/etc/passwd:ro # needed for ssh-keygen to work
ports:
- 8080:8080 # opened port mapping, not needed with proxy
- user: 1000:1000 # expects the main user as uid:pid
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
index 79d8fd4..a3e6d97 100755
--- a/docker/entrypoint.sh
+++ b/docker/entrypoint.sh
@@ -1,24 +1,39 @@
#!/bin/bash
-DATA_OWNER=$(stat -c '%u' /data)
+set -e
-if [ "$DATA_OWNER" -ne "$UID" ]; then
- echo "Directory 'data' not owned by target user with $UID, instead owned by user with uid $DATA_OWNER"
+# check required variables
+if [ -z "$PUID" ]; then
+ echo 'Environment variable $PUID not specified'
exit 1
fi
-# create home folder
-export HOME="/home/$(id -u)"
-mkdir -p $HOME
+if [ -z "$GUID" ]; then
+ echo 'Environment variable $PUID not specified'
+ exit 1
+fi
+
+# create astrid user and group
+if [ ! $(getent group astrid) ]; then
+ groupadd --gid $GUID astrid
+ echo "Group astrid with GID $GUID created."
+fi
+if [ ! $(getent passwd astrid) ]; then
+ useradd --uid $PUID --gid $GUID --create-home --add-subids-for-system astrid
+ echo "User astrid with UID $PUID created."
+fi
+
+# set ownership of /data to target user
+chown "$PUID:$GUID" /data
# create needed files if missing
-mkdir -p /data/config /data/containers /data/log /data/repos /data/ssh
+su - astrid -c "mkdir -p /data/config /data/containers /data/log /data/repos /data/ssh"
-cp -n /app/config.ini.sample /data/config/config.ini
-cp -n /app/repos.ini.sample /data/config/repos.ini
+su - astrid -c "cp -n /app/config.ini.sample /data/config/config.ini"
+su - astrid -c "cp -n /app/repos.ini.sample /data/config/repos.ini"
if [ $(ls "/data/ssh" | grep ".pub" | wc -l) -eq 0 ]; then
- ssh-keygen -t ed25519 -f /data/ssh/id_ed25519
+ su - astrid -c "ssh-keygen -t ed25519 -f /data/ssh/id_ed25519"
fi
-python3 -u ./main
+su - astrid -c "python3 -u /app/main"
diff --git a/repos.ini.sample b/repos.ini.sample
index 9c658e8..fddac6e 100644
--- a/repos.ini.sample
+++ b/repos.ini.sample
@@ -1,6 +1,6 @@
[fykos37]
path=gitea@fykos.cz:FYKOS/fykos37.git
-users=fykos,repo
+users=user
image_version=latest
build_usr=astrid
build_cmd=make -k all
From 9b4ef39279fe9f1e318572a54af8bb20faf4d086 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Thu, 28 Dec 2023 23:46:26 +0100
Subject: [PATCH 10/17] fix group and user existance check
---
docker/docker-compose.yml | 2 +-
docker/entrypoint.sh | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index b12e92b..2180bee 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -11,7 +11,7 @@ services:
environment:
TZ: 'Europe/Prague'
PUID: 1000
- GUID: 1000
+ GUID: 65534
privileged: true # needed for containers
volumes:
- ./data:/data
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
index a3e6d97..34b421a 100755
--- a/docker/entrypoint.sh
+++ b/docker/entrypoint.sh
@@ -9,16 +9,16 @@ if [ -z "$PUID" ]; then
fi
if [ -z "$GUID" ]; then
- echo 'Environment variable $PUID not specified'
+ echo 'Environment variable $GUID not specified'
exit 1
fi
# create astrid user and group
-if [ ! $(getent group astrid) ]; then
+if [ ! $(getent group astrid) ] && [ ! $(getent group $GUID) ]; then
groupadd --gid $GUID astrid
echo "Group astrid with GID $GUID created."
fi
-if [ ! $(getent passwd astrid) ]; then
+if [ ! $(getent passwd astrid) ] && [ ! $(getent passwd $PUID) ]; then
useradd --uid $PUID --gid $GUID --create-home --add-subids-for-system astrid
echo "User astrid with UID $PUID created."
fi
From 0fa27705a8104fb573d035590d213ac0fece27ae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Fri, 29 Dec 2023 00:22:22 +0100
Subject: [PATCH 11/17] fix subuid
---
docker/README.md | 2 ++
docker/docker-compose.yml | 2 +-
docker/entrypoint.sh | 29 ++++++++++++++++++++++++-----
3 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/docker/README.md b/docker/README.md
index e20cdf5..15cab8a 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -7,3 +7,5 @@
- stáhnutí `fykosak/buildtools` (potřeba před prvním buildem repozitáře): `docker exec -it astrid podman pull docker.io/fykosak/buildtools`
- po fungování gitu potřeba přidat ssh klíč na cílový server
- je možné, že ssh bude vyhazovat chybu špatně nastaveného přístupu k privátnímu klíči, v takovém případě je potřeba přes `chmod` nastavit privátnímu klíči práva `600`
+- Možno přes env_var `PUID` a `GUID` nastavit uživatele, pod kterým to pojede. Pozor ale na UID, které již můžou existovat v dockeru, potom se to rozbije.
+ - pokud je PUID nebo GUID změněno, je potřeba smazat starý kontejner (`docker rm astrid`) a znovu jej vytvořit
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 2180bee..5288866 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -10,7 +10,7 @@ services:
container_name: astrid
environment:
TZ: 'Europe/Prague'
- PUID: 1000
+ PUID: 950
GUID: 65534
privileged: true # needed for containers
volumes:
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
index 34b421a..a13e2e5 100755
--- a/docker/entrypoint.sh
+++ b/docker/entrypoint.sh
@@ -2,6 +2,13 @@
set -e
+function checksubid {
+ if [ $(grep -E "^$1:" $2 | wc -l) -eq 0 ]; then
+ exit 1
+ fi
+ exit 0
+}
+
# check required variables
if [ -z "$PUID" ]; then
echo 'Environment variable $PUID not specified'
@@ -23,17 +30,29 @@ if [ ! $(getent passwd astrid) ] && [ ! $(getent passwd $PUID) ]; then
echo "User astrid with UID $PUID created."
fi
+USER=$(id -nu $PUID)
+
+# add to subuid and subgid
+if ! $(checksubid $USER /etc/subuid); then
+ echo "$USER:100000:65536" >> /etc/subuid
+fi
+
+if ! $(checksubid $USER /etc/subgid); then
+ echo "$USER:100000:65536" >> /etc/subgid
+fi
+
# set ownership of /data to target user
chown "$PUID:$GUID" /data
+
# create needed files if missing
-su - astrid -c "mkdir -p /data/config /data/containers /data/log /data/repos /data/ssh"
+su - $USER -c "mkdir -p /data/config /data/containers /data/log /data/repos /data/ssh"
-su - astrid -c "cp -n /app/config.ini.sample /data/config/config.ini"
-su - astrid -c "cp -n /app/repos.ini.sample /data/config/repos.ini"
+su - $USER -c "cp -n /app/config.ini.sample /data/config/config.ini"
+su - $USER -c "cp -n /app/repos.ini.sample /data/config/repos.ini"
if [ $(ls "/data/ssh" | grep ".pub" | wc -l) -eq 0 ]; then
- su - astrid -c "ssh-keygen -t ed25519 -f /data/ssh/id_ed25519"
+ su - $USER -c "ssh-keygen -t ed25519 -f /data/ssh/id_ed25519"
fi
-su - astrid -c "python3 -u /app/main"
+su - $USER -c "python3 -u /app/main"
From 7adecd7c293e5a6a1cbaf31b8a5658956abb1809 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Fri, 29 Dec 2023 12:29:40 +0100
Subject: [PATCH 12/17] yeet podman
---
.dockerignore | 3 +--
astrid/pages.py | 2 +-
docker/Dockerfile | 5 ++---
docker/daemon.json | 6 ++++++
docker/docker-compose.yml | 4 ++--
docker/entrypoint.sh | 2 ++
docker/libpod.conf | 1 -
docker/storage.conf | 3 ---
8 files changed, 14 insertions(+), 12 deletions(-)
create mode 100644 docker/daemon.json
delete mode 100644 docker/libpod.conf
delete mode 100644 docker/storage.conf
diff --git a/.dockerignore b/.dockerignore
index e780801..234ea3e 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -2,5 +2,4 @@ docker/*
!docker/entrypoint.sh
!docker/ssh.conf
!docker/sshd.conf
-!docker/libpod.conf
-!docker/storage.conf
+!docker/daemon.json
diff --git a/astrid/pages.py b/astrid/pages.py
index 6cdd069..9059bf3 100644
--- a/astrid/pages.py
+++ b/astrid/pages.py
@@ -126,7 +126,7 @@ def _build(self, reponame):
print(f"Building {reponame}")
logfilename = os.path.expanduser(f"/data/log/{reponame}.build.log")
logfile = open(logfilename, "w")
- p = subprocess.run(["podman", "run", "--rm", "-v", f"{cwd}:/usr/src/local", f"fykosak/buildtools:{image_version}"] + cmd.split(), cwd=cwd, stdout=logfile, stderr=logfile, check=False)
+ p = subprocess.run(["docker", "run", "--rm", "-v", f"{cwd}:/usr/src/local", f"fykosak/buildtools:{image_version}"] + cmd.split(), cwd=cwd, stdout=logfile, stderr=logfile, check=False)
logfile.close()
return p.returncode == 0
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 04e9e98..6a93e43 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,7 +1,7 @@
FROM python:3.12
# install docker
-RUN apt update && apt install -y podman containers-storage
+RUN apt update && apt install -y docker.io containers-storage
# change workdir
WORKDIR /app
@@ -15,8 +15,7 @@ COPY ./docker/ssh.conf /etc/ssh/ssh_config.d/99-astrid.conf
COPY ./docker/sshd.conf /etc/ssh/sshd_config.d/99-astrid.conf
# copy containers config
-COPY ./docker/libpod.conf /etc/containers/libpod.conf
-COPY ./docker/storage.conf /etc/containers/storage.conf
+COPY ./docker/daemon.json /etc/docker/daemon.json
# install astrid
COPY . .
diff --git a/docker/daemon.json b/docker/daemon.json
new file mode 100644
index 0000000..5996548
--- /dev/null
+++ b/docker/daemon.json
@@ -0,0 +1,6 @@
+{
+ "data-root": "/data/containers",
+ "live-restore": true,
+ "log-driver": "json-file",
+ "init": true
+}
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 5288866..b12e92b 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -10,8 +10,8 @@ services:
container_name: astrid
environment:
TZ: 'Europe/Prague'
- PUID: 950
- GUID: 65534
+ PUID: 1000
+ GUID: 1000
privileged: true # needed for containers
volumes:
- ./data:/data
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
index a13e2e5..90205fc 100755
--- a/docker/entrypoint.sh
+++ b/docker/entrypoint.sh
@@ -31,6 +31,7 @@ if [ ! $(getent passwd astrid) ] && [ ! $(getent passwd $PUID) ]; then
fi
USER=$(id -nu $PUID)
+usermod -a -G docker $USER
# add to subuid and subgid
if ! $(checksubid $USER /etc/subuid); then
@@ -55,4 +56,5 @@ if [ $(ls "/data/ssh" | grep ".pub" | wc -l) -eq 0 ]; then
su - $USER -c "ssh-keygen -t ed25519 -f /data/ssh/id_ed25519"
fi
+dockerd &
su - $USER -c "python3 -u /app/main"
diff --git a/docker/libpod.conf b/docker/libpod.conf
deleted file mode 100644
index 86e8062..0000000
--- a/docker/libpod.conf
+++ /dev/null
@@ -1 +0,0 @@
-static_dir = "/data/containers/storage/libpod"
diff --git a/docker/storage.conf b/docker/storage.conf
deleted file mode 100644
index f5091cd..0000000
--- a/docker/storage.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-[storage]
-driver = "overlay"
-rootless_storage_path = "/data/containers/storage"
From b728cc155e170efb89f15700d5ef0b7196fd1e4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Fri, 29 Dec 2023 13:26:51 +0100
Subject: [PATCH 13/17] run as current user
---
astrid/pages.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/astrid/pages.py b/astrid/pages.py
index 9059bf3..f2e8930 100644
--- a/astrid/pages.py
+++ b/astrid/pages.py
@@ -126,7 +126,7 @@ def _build(self, reponame):
print(f"Building {reponame}")
logfilename = os.path.expanduser(f"/data/log/{reponame}.build.log")
logfile = open(logfilename, "w")
- p = subprocess.run(["docker", "run", "--rm", "-v", f"{cwd}:/usr/src/local", f"fykosak/buildtools:{image_version}"] + cmd.split(), cwd=cwd, stdout=logfile, stderr=logfile, check=False)
+ p = subprocess.run(["docker", "run", "--rm", "-v", f"{cwd}:/usr/src/local", f"--user={os.getuid()}", f"fykosak/buildtools:{image_version}"] + cmd.split(), cwd=cwd, stdout=logfile, stderr=logfile, check=False)
logfile.close()
return p.returncode == 0
From af69841af3820c8749b37fe382f233aa69a37685 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Fri, 29 Dec 2023 13:29:11 +0100
Subject: [PATCH 14/17] include docker dependencies
---
.github/workflows/deploy-image.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/workflows/deploy-image.yml b/.github/workflows/deploy-image.yml
index c4de4b4..ee65abb 100644
--- a/.github/workflows/deploy-image.yml
+++ b/.github/workflows/deploy-image.yml
@@ -6,7 +6,10 @@ on:
- master
- dev-docker
paths:
+ - 'astrid/**'
- 'docker/**'
+ - 'main'
+ - 'requirements.txt'
- '.github/workflows/deploy-image.yml'
env:
From 737a02d21a01ce32dc93229826c81307af6f3282 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Fri, 29 Dec 2023 14:36:39 +0100
Subject: [PATCH 15/17] run as gid
---
astrid/pages.py | 2 +-
docker/docker-compose.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/astrid/pages.py b/astrid/pages.py
index f2e8930..eb7133d 100644
--- a/astrid/pages.py
+++ b/astrid/pages.py
@@ -126,7 +126,7 @@ def _build(self, reponame):
print(f"Building {reponame}")
logfilename = os.path.expanduser(f"/data/log/{reponame}.build.log")
logfile = open(logfilename, "w")
- p = subprocess.run(["docker", "run", "--rm", "-v", f"{cwd}:/usr/src/local", f"--user={os.getuid()}", f"fykosak/buildtools:{image_version}"] + cmd.split(), cwd=cwd, stdout=logfile, stderr=logfile, check=False)
+ p = subprocess.run(["docker", "run", "--rm", "-v", f"{cwd}:/usr/src/local", f"--user={os.getuid()}:{os.getgid()}", f"fykosak/buildtools:{image_version}"] + cmd.split(), cwd=cwd, stdout=logfile, stderr=logfile, check=False)
logfile.close()
return p.returncode == 0
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index b12e92b..2180bee 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -11,7 +11,7 @@ services:
environment:
TZ: 'Europe/Prague'
PUID: 1000
- GUID: 1000
+ GUID: 65534
privileged: true # needed for containers
volumes:
- ./data:/data
From fc52a938c65a6fdc3ac93bae041e10e00b731206 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Sat, 30 Dec 2023 22:41:40 +0100
Subject: [PATCH 16/17] instant http response
---
astrid/__init__.py | 12 +++---
astrid/pages.py | 94 +++++++++++++++++++++++++++++-----------------
2 files changed, 65 insertions(+), 41 deletions(-)
diff --git a/astrid/__init__.py b/astrid/__init__.py
index 2318b44..90d7a97 100644
--- a/astrid/__init__.py
+++ b/astrid/__init__.py
@@ -18,10 +18,9 @@ def _getLogfile(self, reponame):
def _getBuildlogfile(self, reponame):
return f'/data/log/{reponame}.build.log'
- def log(self, reponame, message, sendMail = False):
+ def log(self, reponame, user, message, sendMail = False):
logfile = self._getLogfile(reponame)
f = open(logfile, "a")
- user = cherrypy.request.login
f.write(BuildLogger.separator.join([datetime.now().isoformat(' '), message, user]) + "\n")
f.close()
@@ -80,12 +79,13 @@ def _sendMail(self, reponame, message):
# prepare locks for each repository
locks = {section: threading.Lock() for section in repos.sections()}
+waitLocks = {section: threading.Lock() for section in repos.sections()}
from astrid.pages import BuilderPage, InfoPage, DashboardPage, BuildlogPage
repodir = cherrypy.config.get("repodir")
-root = DashboardPage(repodir, repos, locks)
-root.build = BuilderPage(repodir, repos, locks)
-root.info = InfoPage(repodir, repos, locks)
-root.buildlog = BuildlogPage(repodir, repos, locks)
+root = DashboardPage(repodir, repos, locks, waitLocks)
+root.build = BuilderPage(repodir, repos, locks, waitLocks)
+root.info = InfoPage(repodir, repos, locks, waitLocks)
+root.buildlog = BuildlogPage(repodir, repos, locks, waitLocks)
diff --git a/astrid/pages.py b/astrid/pages.py
index eb7133d..8969856 100644
--- a/astrid/pages.py
+++ b/astrid/pages.py
@@ -2,6 +2,7 @@
import os
import subprocess
import time
+from threading import Thread
import cherrypy
from git import Repo, Git, GitCommandError
@@ -11,10 +12,11 @@
import astrid.server
class BasePage(object):
- def __init__(self, repodir, repos, locks):
+ def __init__(self, repodir, repos, locks, waitLocks):
self.repos = repos
self.repodir = repodir
self.locks = locks
+ self.waitLocks = waitLocks
def _checkAccess(self, reponame):
user = cherrypy.request.login
@@ -37,48 +39,70 @@ def default(self, reponame):
raise cherrypy.HTTPError(404)
self._checkAccess(reponame)
+
+ user = cherrypy.request.login
+
+ thread = Thread(target=self._queueJob, args=(reponame,user))
+ thread.daemon = True
+ thread.start()
+
+ raise cherrypy.HTTPRedirect("/")
+
+ def _queueJob(self, reponame, user):
lock = self.locks[reponame]
+ waitLock = self.waitLocks[reponame]
+
+ # if job is not running
+ if lock.acquire(blocking=False):
+ # lock job running and run job
+ try:
+ self._runJob(reponame, user)
+ finally: # ensure lock release on exception
+ lock.release() # release lock for job run
+ return
+
+ # if job is already running
+ if not waitLock.acquire(blocking=False): # if another process is already waiting
+ return # skip waiting
+
+ # if it's not waiting
+ # we now own lock for waiting
+ lock.acquire() # wait for job lock to release and acquire it
+ waitLock.release() # relase lock for waiting
+ try:
+ self._runJob(reponame, user) # run job
+ finally: # ensure lock release on exception
+ lock.release() # release lock for job run
+
+ def _runJob(self, reponame, user):
msg = None
sendMail = False
- lock.acquire()
+ time_start = time.time()
+ time_end = None
+
try:
+ repo = self._updateRepo(reponame)
try:
- time_start = time.time()
- time_end = None
- repo = self._updateRepo(reponame)
- try:
- if self._build(reponame):
- msg = f"#{repo.head.commit.hexsha[0:6]} Build succeeded."
- msg_class = "green"
- time_end = time.time()
- else:
- msg = f"#{repo.head.commit.hexsha[0:6]} Build failed."
- msg_class = "red"
- time_end = time.time()
- except:
- msg = f"#{repo.head.commit.hexsha[0:6]} Build error."
- msg_class = "red"
- raise
- except GitCommandError as e:
- msg = f"Pull error. ({e})"
- msg_class = "red"
- sendMail = True
+ if self._build(reponame):
+ msg = f"#{repo.head.commit.hexsha[0:6]} Build succeeded."
+ time_end = time.time()
+ else:
+ msg = f"#{repo.head.commit.hexsha[0:6]} Build failed."
+ time_end = time.time()
+ except:
+ msg = f"#{repo.head.commit.hexsha[0:6]} Build error."
raise
+ except GitCommandError as e:
+ msg = f"Pull error. ({e})"
+ sendMail = True
+ raise
- if time_end is not None:
- msg += " ({:.2f} s)".format(time_end - time_start)
-
- template = Template("templates/build.html")
- template.assignData("reponame", reponame)
- template.assignData("message", msg)
- template.assignData("msg_class", msg_class)
- template.assignData("pagetitle", "Build")
- return template.render()
- finally:
- logger = BuildLogger(self.repodir)
- logger.log(reponame, msg, sendMail)
- lock.release()
+ if time_end is not None:
+ msg += " ({:.2f} s)".format(time_end - time_start)
+
+ logger = BuildLogger(self.repodir)
+ logger.log(reponame, user, msg, sendMail)
def _updateRepo(self, reponame):
remotepath = self.repos.get(reponame, "path")
From 0f26856127bc27f3f52a951291dda33b508ae30c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Kr=C5=A1ka?=
Date: Sat, 6 Jan 2024 20:00:29 +0100
Subject: [PATCH 17/17] update production mounts
---
docker/docker-compose.prod.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml
index 6ea7561..1313e6e 100644
--- a/docker/docker-compose.prod.yml
+++ b/docker/docker-compose.prod.yml
@@ -12,4 +12,3 @@ services:
privileged: true # needed for containers
volumes:
- ./data:/data
- - /etc/passwd:/etc/passwd:ro # needed for ssh-keygen to work