In my configuration.nix I have the following:
virtualisation.docker = {
enableOnBoot = false;
daemon.settings = { data-root = "/home/docker"; };
rootless = {
enable = true;
setSocketVariable = true;
};
};
systemd.user.services.webhook =
let py = pkgs.python3.withPackages(ps: with ps; [flask docker]);
in {
description = "Python webhook to build and start images";
wantedBy = [ "multi-user.target" ];
after = [ "network-online.socket" ];
serviceConfig = {
Type = "exec";
ExecStart = "${py}/bin/python /home/myuser/webhook";
};
};
I also have the following python script:
import json
import os
import time
import signal
import docker
import logging
from flask import Flask, request
docker_socket = '/run/user/1000/docker.sock'
client = docker.DockerClient(base_url=f'unix://{docker_socket}')
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = Flask(__name__)
should_stop = False
def signal_handler(sig, frame):
global should_stop
should_stop = True
os.kill(os.getpid(), signal.SIGINT)
signal.signal(signal.SIGINT, signal_handler)
def clean():
try:
containers = client.containers.list(all=True)
for container in containers:
container_name = container.name
container.remove(force=True)
logger.info(f"========================Container '{container_name}' has been deleted.")
volumes = client.volumes.list()
for volume in volumes:
volume_name = volume.name
volume.remove()
logger.info(f"========================Volume '{volume_name}' has been deleted.")
images = client.images.list(all=True)
for image in images:
image_name = image.tags[0] if image.tags else image.id
client.images.remove(image.id, force=True)
logger.info(f"========================Image '{image_name}' has been deleted.")
except docker.errors.BuildError as e:
logger.error(f"CLEAN========================{e}")
raise
except docker.errors.APIError as e:
logger.error(f"CLEAN========================{e}")
raise
except TypeError as e:
logger.error(f"CLEAN========================{e}")
raise
if should_stop:
return
def build():
try:
date_tag = time.strftime('%Y-%m-%d')
tags = [
f"user/group:{date_tag}",
f"user/group:latest"
]
client.images.build(path="/home/myuser/", dockerfile="Dockerfile", tag=tags)
except docker.errors.BuildError as e:
logger.error(f"BUILD========================{e}")
raise
except docker.errors.APIError as e:
logger.error(f"BUILD========================{e}")
raise
except TypeError as e:
logger.error(f"BUILD========================{e}")
raise
if should_stop:
return
def overall():
global should_stop
try:
should_stop = False
clean()
build()
except docker.errors.BuildError as e:
logger.error(f"OVERALL========================{e}")
raise
except docker.errors.APIError as e:
logger.error(f"OVERALL========================{e}")
raise
except TypeError as e:
logger.error(f"OVERALL========================{e}")
raise
@app.route('/smee/', methods=['POST'])
def handle_webhook():
event = request.headers.get('X-GitHub-Event')
data = request.get_json()
if (event == 'push' and data['ref'] == 'refs/heads/main') or \
(event == 'pull_request' and data['action'] == 'closed' and
data['pull_request']['merged'] and data['pull_request']['base']['ref'] == 'main'):
overall()
return 'Build triggered successfully', 202
else:
return 'Event received but not relevant', 204
if __name__ == '__main__':
app.debug = True
app.run(host='localhost', port=8000)
While the config evaluates, creates a user systemd service that runs the script, the problem is when the scripts tries to run the build function I get:
Summary
<!doctype html>
<html lang=en>
<head>
<title>PermissionError: [Errno 13] Permission denied: '/home/myuser/.local/share/docker/overlay2/uspen2fjkov9gl6elohti1txc/work/work'
// Werkzeug Debugger</title>
<link rel="stylesheet" href="?__debugger__=yes&cmd=resource&f=style.css">
<link rel="shortcut icon"
href="?__debugger__=yes&cmd=resource&f=console.png">
<script src="?__debugger__=yes&cmd=resource&f=debugger.js"></script>
<script>
var CONSOLE_MODE = false,
EVALEX = true,
EVALEX_TRUSTED = false,
SECRET = "secret";
</script>
</head>
<body style="background-color: #fff">
<div class="debugger">
<h1>PermissionError</h1>
<div class="detail">
<p class="errormsg">PermissionError: [Errno 13] Permission denied: '/home/myuser/.local/share/docker/overlay2/uspen2fjkov9gl6elohti1txc/work/work'
</p>
</div>
<h2 class="traceback">Traceback <em>(most recent call last)</em></h2>
<div class="traceback">
<h3></h3>
<ul><li><div class="frame" id="frame-140155503796448">
<h4>File <cite class="filename">"/nix/store/4vv3ycrry5k787p8zf9mgzanh8was50h-python3-3.11.8-env/lib/python3.11/site-packages/flask/app.py"</cite>,
line <em class="line">2213</em>,
in <code class="function">__call__</code></h4>
<div class="source library"><pre class="line before"><span class="ws"> </span>def __call__(self, environ: dict, start_response: t.Callable) -> t.Any:</pre>
<pre class="line before"><span class="ws"> </span>"""The WSGI server calls the Flask application object as the</pre>
<pre class="line before"><span class="ws"> </span>WSGI application. This calls :meth:`wsgi_app`, which can be</pre>
<pre class="line before"><span class="ws"> </span>wrapped to apply middleware.</pre>
<pre class="line before"><span class="ws"> </span>"""</pre>
<pre class="line current"><span class="ws"> </span>return self.wsgi_app(environ, start_response)
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre></div>
</div>
<li><div class="frame" id="frame-140155503802496">
<h4>File <cite class="filename">"/nix/store/4vv3ycrry5k787p8zf9mgzanh8was50h-python3-3.11.8-env/lib/python3.11/site-packages/flask/app.py"</cite>,
line <em class="line">2193</em>,
in <code class="function">wsgi_app</code></h4>
<div class="source library"><pre class="line before"><span class="ws"> </span>try:</pre>
<pre class="line before"><span class="ws"> </span>ctx.push()</pre>
<pre class="line before"><span class="ws"> </span>response = self.full_dispatch_request()</pre>
<pre class="line before"><span class="ws"> </span>except Exception as e:</pre>
<pre class="line before"><span class="ws"> </span>error = e</pre>
<pre class="line current"><span class="ws"> </span>response = self.handle_exception(e)
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>except: # noqa: B001</pre>
<pre class="line after"><span class="ws"> </span>error = sys.exc_info()[1]</pre>
<pre class="line after"><span class="ws"> </span>raise</pre>
<pre class="line after"><span class="ws"> </span>return response(environ, start_response)</pre>
<pre class="line after"><span class="ws"> </span>finally:</pre></div>
</div>
Instead of working like it normally would when I manually do docker build -f /home/myuser/Dockerfile ./
, it instead gives permission error trying to access some docker folders that are not accessible by my user either:
d--------- 2 myuser users 4096 Apr 30 16:25 work