Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ilude committed May 1, 2024
1 parent 0b47343 commit 2bb8bf2
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
dotfiles_url: "{{ lookup('env', 'DOTFILES_URL') }}"
dotfiles_url: "{{ lookup('env', 'DOTFILES_URL', default='https://github.com/ilude/dotfiles.git') }}"
dotfiles_path: "{{ lookup('env', 'HOME') + '/.dotfiles' }}"
1 change: 0 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
"peakchen90.open-html-in-browser",
"ms-python.autopep8",
"codezombiech.gitignore",
"ms-vscode.makefile-tools",
"yy0931.save-as-root",
"ms-python.isort",
"ZainChen.json",
Expand Down
9 changes: 7 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ ENV PYTHONPATH="${PYTHONPATH}:${PYTHON_DEPS_PATH}"
ENV PYTHONUNBUFFERED=TRUE

ENV LANGUAGE=en_US.UTF-8
ENV LC_ALL=en_US.UTF-8
ENV LANG=en_US.UTF-8
ENV LC_ALL=C.UTF-8
ENV DEBIAN_FRONTEND=noninteractive
ENV DEBCONF_NONINTERACTIVE_SEEN=true

Expand All @@ -45,6 +45,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
sudo \
tzdata \
zsh && \
# locales
echo "$(LANGUAGE)" > /etc/locale.gen && \
locale-gen en_US.UTF-8 && \
# cleanup
apt-get autoremove -fy && \
apt-get clean && \
Expand Down Expand Up @@ -191,8 +194,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
rm -rf /var/lib/apt/lists/*



RUN echo ${USER} ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/${USER} && \
chmod 0440 /etc/sudoers.d/${USER}
chmod 0440 /etc/sudoers.d/${USER} && \
chown ${USER} /var/run/docker.sock

USER ${USER}

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ bash-image: build-image
docker run -it --rm -p 9830:9830 -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/traefikturkey/onboard:latest bash

ansible:
-LC_ALL=C.UTF-8 ansible-playbook --inventory 127.0.0.1 --connection=local .devcontainer/ansible/requirements.yml
-LC_ALL=C.UTF-8 ansible-playbook --inventory 127.0.0.1 --connection=local .devcontainer/ansible/setup-container.yml
-ansible-playbook --inventory 127.0.0.1 --connection=local .devcontainer/ansible/requirements.yml
-ansible-playbook --inventory 127.0.0.1 --connection=local .devcontainer/ansible/setup-container.yml
82 changes: 25 additions & 57 deletions app/app.py
Original file line number Diff line number Diff line change
@@ -1,70 +1,38 @@
import docker
import time
import signal
import sys
import re
from docker_monitor import DockerMonitor

# Create a Docker client instance
client = docker.from_env()
monitor = None

# Define a function to extract FQDNs from Traefik labels

def signal_handler(received_signal: signal.Signals, frame):
print(f"\nReceived signal: {signal.Signals(received_signal).name}, stopping event listener...")
if monitor:
monitor.stop()
sys.exit(0)

def extract_fqdns_from_labels(labels):
traefik_host_labels = {k: v for k, v in labels.items() if k.startswith('traefik.http.routers.')}
fqdns = []
for label_key, label_value in traefik_host_labels.items():
match = re.search(r'rule=Host\(`(.+?)`\)', label_value)
if match:
fqdn = match.group(1)
fqdns.append(fqdn)
return fqdns

# Define a callback function to handle events


def event_callback(event):
if event['Action'] == 'start':
container_name = event['Actor']['Attributes']['name']
container = client.containers.get(container_name)
labels = container.labels
fqdns = extract_fqdns_from_labels(labels)

if fqdns:
print(f"Container {container_name} started with Traefik FQDNs: {', '.join(fqdns)}")
else:
print(f"Container {container_name} started without Traefik host labels.")

elif event['Action'] == 'stop':
print(f"Container {event['Actor']['Attributes']['name']} stopped.")

# Define a signal handler function

def main():
# Set up signal handlers
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)

def signal_handler(signal, frame):
print("Received signal, stopping event listener...")
sys.exit(0)
# Create a DockerMonitor instance
global monitor
monitor = DockerMonitor()

# Start the event listener in a separate thread
event_thread = monitor.start()

# Set up signal handlers
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
try:
# Keep the main thread alive
while True:
pass
except KeyboardInterrupt:
print("Keyboard interrupt received, stopping event listener...")
monitor.stop()

# Get running containers with Traefik labels on startup
print("Fetching running containers with Traefik labels...")
running_containers = client.containers.list(filters={'status': 'running'})
for container in running_containers:
labels = container.labels
fqdns = extract_fqdns_from_labels(labels)
if fqdns:
print(f"Container {container.name} is running with Traefik FQDNs: {', '.join(fqdns)}")

# Start listening for events
print("Listening for Docker events...")
try:
for event in client.events(decode=True):
event_callback(event)
# Flush the output buffer to ensure the message is printed immediately
time.sleep(0.1)
except KeyboardInterrupt:
print("Keyboard interrupt received, stopping event listener...")
if __name__ == "__main__":
main()
45 changes: 45 additions & 0 deletions app/corefile_generateor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from enum import Enum
from typing import Set
from jinja2 import Environment, FileSystemLoader


class docker:
class ActionType(Enum):
START = 1
STOP = 2

class Event:
def __init__(self, fqdn_name: str, action_type):
self._fqdn_name = fqdn_name
self._action_type = action_type

@property
def fqdn_name(self) -> str:
return self._fqdn_name

@property
def action_type(self):
return self._action_type


class CorefileGenerator:
def __init__(self, template_path: str):
self.template_path = template_path
self.active_fqdns: Set[str] = set()
self.env = Environment(loader=FileSystemLoader('/path/to/templates'))

def process_event(self, event: docker.Event):
fqdn_name = event.fqdn_name
action_type = event.action_type

if action_type == docker.ActionType.START:
self.active_fqdns.add(fqdn_name)
elif action_type == docker.ActionType.STOP:
self.active_fqdns.discard(fqdn_name)

def generate_corefile(self, output_path: str):
template = self.env.get_template('Corefile.template')
corefile_content = template.render(active_fqdns=self.active_fqdns)

with open(output_path, 'w') as corefile:
corefile.write(corefile_content)
77 changes: 77 additions & 0 deletions app/docker_monitor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import os
import docker
import re
import threading
from typing import Set


class DockerMonitor:
is_manager: bool = False
host_ip: str
active_names: Set[str] = set()
_stop_event = threading.Event()

def __init__(self):
self.client = docker.from_env()
self.host_ip = os.environ.get('HOSTIP', '127.0.0.1')
self.get_running_containers_with_traefik_labels()

def update_core_dns(self, host_ip, fqdns):
print("Updating CoreDNS with new FQDNs...")

def extract_fqdns_from_labels(self, container, action):
labels = container.labels
traefik_host_labels = {k: v for k, v in labels.items() if k.startswith('traefik.http.routers.')}
onetime_enabled = any(label.startswith('onetime.enabled=false') for label in labels)
if not onetime_enabled:
return False

changed = False
for _, label_value in traefik_host_labels.items():
match = re.search(r'rule=Host\(`(.+?)`\)', label_value)
if match and match.group(1).strip() not in self.active_names:
fqdn = match.group(1).strip()
if action == 'start' and fqdn not in self.active_names:
self.active_names.add(fqdn)
changed = True
if action == 'stop' and fqdn in self.active_names:
self.active_names.discard(fqdn)
changed = True
return changed

def event_callback(self, event):
container_name = event['Actor']['Attributes']['name']
container = self.client.containers.get(container_name)
changed = self.extract_fqdns_from_labels(container, event['Action'])
if changed:
self.update_core_dns(self.host_ip, self.active_names)

def get_running_containers_with_traefik_labels(self):
for container in self.client.containers.list(filters={'status': 'running'}):
if 'coredns' in container.image.tags:
self.is_manager = True
next
self.extract_fqdns_from_labels(container, 'start')
self.update_core_dns(self.host_ip, self.active_names)

def start(self):
print("Listening for Docker events...")
event_thread = threading.Thread(target=self.event_loop)
event_thread.start()
return event_thread

def stop(self):
self._stop_event.set()

def event_loop(self):
while not self._stop_event.is_set():
try:
events = self.client.events(decode=True)
for event in events:
if self._stop_event.is_set():
break
self.event_callback(event)
events.close()
except KeyboardInterrupt:
self.stop()
break
42 changes: 42 additions & 0 deletions app/reloader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import os
import sys
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler


class ReloadHandler(FileSystemEventHandler):
def on_any_event(self, event):
if event.is_directory:
return
elif event.event_type == 'created':
print(f"Created file: {event.src_path}")
elif event.event_type == 'modified':
print(f"Modified file: {event.src_path}")
self.reload()

def reload(self):
python = sys.executable
args = sys.argv[:]
print(f"Reloading {' '.join(args)}")
args.insert(0, python)
os.execvp(python, args)


def start_observer(paths):
observer = Observer()
event_handler = ReloadHandler()
for path in paths:
observer.schedule(event_handler, path, recursive=True)
observer.start()
return observer


if __name__ == "__main__":
paths = ['.']
observer = start_observer(paths)
try:
while True:
pass
except KeyboardInterrupt:
observer.stop()
observer.join()
Empty file added app/templates/Corefile.template
Empty file.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
docker
python-dotenv
pyyaml
watchdog

0 comments on commit 2bb8bf2

Please sign in to comment.