file change on agent
This commit is contained in:
parent
6217874cb0
commit
eb99a1718b
3 changed files with 182 additions and 45 deletions
108
agent.py
108
agent.py
|
@ -20,6 +20,7 @@ load_dotenv()
|
||||||
|
|
||||||
# Fixed configuration
|
# Fixed configuration
|
||||||
CADDYFILE_PATH = "/app/Caddyfile" # Fixed internal path
|
CADDYFILE_PATH = "/app/Caddyfile" # Fixed internal path
|
||||||
|
NGINX_CONFIG_PATH = "/app/nginx" # Fixed internal path for nginx configs
|
||||||
DASHBOARD_URL = os.getenv('DASHBOARD_URL', 'http://caddydb-server:5000/api/update')
|
DASHBOARD_URL = os.getenv('DASHBOARD_URL', 'http://caddydb-server:5000/api/update')
|
||||||
SERVER_NAME = os.getenv('SERVER_NAME', socket.gethostname())
|
SERVER_NAME = os.getenv('SERVER_NAME', socket.gethostname())
|
||||||
API_KEY = os.getenv('API_KEY')
|
API_KEY = os.getenv('API_KEY')
|
||||||
|
@ -64,6 +65,9 @@ if not API_KEY:
|
||||||
last_data_sent = None
|
last_data_sent = None
|
||||||
last_send_time = datetime.min
|
last_send_time = datetime.min
|
||||||
|
|
||||||
|
# Flag to determine what type of config to monitor
|
||||||
|
IS_NGINX = SERVER_TYPE.lower() == 'nginx'
|
||||||
|
|
||||||
def parse_caddyfile():
|
def parse_caddyfile():
|
||||||
"""Parse the Caddyfile to extract domains and their proxy targets"""
|
"""Parse the Caddyfile to extract domains and their proxy targets"""
|
||||||
entries = {}
|
entries = {}
|
||||||
|
@ -120,12 +124,68 @@ def create_auth_token():
|
||||||
logger.error(f"Error creating JWT token: {e}")
|
logger.error(f"Error creating JWT token: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def parse_nginx_configs():
|
||||||
|
"""Parse Nginx config files to extract domains and their proxy targets"""
|
||||||
|
entries = {}
|
||||||
|
|
||||||
|
if not os.path.exists(NGINX_CONFIG_PATH) or not os.path.isdir(NGINX_CONFIG_PATH):
|
||||||
|
logger.error(f"Nginx config directory not found at {NGINX_CONFIG_PATH}")
|
||||||
|
return entries
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Find all .conf files in the directory and subdirectories
|
||||||
|
conf_files = []
|
||||||
|
for root, _, files in os.walk(NGINX_CONFIG_PATH):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith('.conf'):
|
||||||
|
conf_files.append(os.path.join(root, file))
|
||||||
|
|
||||||
|
logger.info(f"Found {len(conf_files)} Nginx config files")
|
||||||
|
|
||||||
|
# Pattern to match server_name and proxy_pass directives
|
||||||
|
server_name_pattern = re.compile(r'server_name\s+([^;]+);', re.IGNORECASE)
|
||||||
|
proxy_pass_pattern = re.compile(r'proxy_pass\s+([^;]+);', re.IGNORECASE)
|
||||||
|
|
||||||
|
for conf_file in conf_files:
|
||||||
|
try:
|
||||||
|
with open(conf_file, 'r') as file:
|
||||||
|
content = file.read()
|
||||||
|
|
||||||
|
# Extract server blocks
|
||||||
|
server_blocks = re.findall(r'server\s*{([^}]+)}', content, re.DOTALL)
|
||||||
|
|
||||||
|
for block in server_blocks:
|
||||||
|
server_names = server_name_pattern.search(block)
|
||||||
|
proxy_pass = proxy_pass_pattern.search(block)
|
||||||
|
|
||||||
|
if server_names and proxy_pass:
|
||||||
|
server_names = server_names.group(1).strip().split()
|
||||||
|
target = proxy_pass.group(1).strip()
|
||||||
|
|
||||||
|
for name in server_names:
|
||||||
|
# Skip default names like "_" or localhost
|
||||||
|
if name != "_" and name != "localhost" and name != "localhost.localdomain":
|
||||||
|
entries[name] = target
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error parsing Nginx config file {conf_file}: {e}")
|
||||||
|
|
||||||
|
return entries
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error parsing Nginx configs: {e}")
|
||||||
|
return entries
|
||||||
|
|
||||||
def send_update(force=False):
|
def send_update(force=False):
|
||||||
"""Send Caddyfile data to the dashboard server"""
|
"""Send configuration data to the dashboard server"""
|
||||||
global last_data_sent, last_send_time
|
global last_data_sent, last_send_time
|
||||||
|
|
||||||
# Parse the Caddyfile
|
# Parse the appropriate configuration
|
||||||
current_data = parse_caddyfile()
|
if IS_NGINX:
|
||||||
|
current_data = parse_nginx_configs()
|
||||||
|
else:
|
||||||
|
current_data = parse_caddyfile()
|
||||||
|
|
||||||
current_time = datetime.now()
|
current_time = datetime.now()
|
||||||
|
|
||||||
# Only send if data changed or enough time passed since last update
|
# Only send if data changed or enough time passed since last update
|
||||||
|
@ -134,9 +194,9 @@ def send_update(force=False):
|
||||||
(current_time - last_send_time).total_seconds() < CHECK_INTERVAL):
|
(current_time - last_send_time).total_seconds() < CHECK_INTERVAL):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Always include the server name in data payload
|
# Create the data payload
|
||||||
data = {
|
data = {
|
||||||
"server": SERVER_NAME, # Always include this
|
"server": SERVER_NAME,
|
||||||
"entries": current_data,
|
"entries": current_data,
|
||||||
"timestamp": current_time.isoformat(),
|
"timestamp": current_time.isoformat(),
|
||||||
"type": SERVER_TYPE
|
"type": SERVER_TYPE
|
||||||
|
@ -171,12 +231,19 @@ def send_update(force=False):
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
logger.error(f"Connection error sending update: {e}")
|
logger.error(f"Connection error sending update: {e}")
|
||||||
|
|
||||||
class CaddyFileHandler(FileSystemEventHandler):
|
class ConfigFileHandler(FileSystemEventHandler):
|
||||||
"""Watch for changes to the Caddyfile and send updates"""
|
"""Watch for changes to configuration files and send updates"""
|
||||||
def on_modified(self, event):
|
def on_modified(self, event):
|
||||||
if event.src_path == CADDYFILE_PATH:
|
if IS_NGINX:
|
||||||
logger.info(f"Caddyfile changed, sending update")
|
# For Nginx, check if it's a .conf file in the watched directory
|
||||||
send_update()
|
if event.src_path.endswith('.conf'):
|
||||||
|
logger.info(f"Nginx config changed: {event.src_path}, sending update")
|
||||||
|
send_update()
|
||||||
|
else:
|
||||||
|
# For Caddy, check if it's the Caddyfile
|
||||||
|
if event.src_path == CADDYFILE_PATH:
|
||||||
|
logger.info(f"Caddyfile changed, sending update")
|
||||||
|
send_update()
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main function to start the agent"""
|
"""Main function to start the agent"""
|
||||||
|
@ -184,13 +251,28 @@ def main():
|
||||||
send_update(force=True)
|
send_update(force=True)
|
||||||
|
|
||||||
# Set up file watching
|
# Set up file watching
|
||||||
event_handler = CaddyFileHandler()
|
event_handler = ConfigFileHandler()
|
||||||
observer = Observer()
|
observer = Observer()
|
||||||
observer.schedule(event_handler, path=os.path.dirname(CADDYFILE_PATH), recursive=False)
|
|
||||||
|
if IS_NGINX:
|
||||||
|
# Watch the Nginx config directory
|
||||||
|
if not os.path.exists(NGINX_CONFIG_PATH):
|
||||||
|
logger.error(f"Nginx config path not found: {NGINX_CONFIG_PATH}")
|
||||||
|
sys.exit(1)
|
||||||
|
observer.schedule(event_handler, path=NGINX_CONFIG_PATH, recursive=True)
|
||||||
|
logger.info(f"Watching Nginx configs in {NGINX_CONFIG_PATH} for changes")
|
||||||
|
else:
|
||||||
|
# Watch the Caddyfile
|
||||||
|
if not os.path.exists(CADDYFILE_PATH):
|
||||||
|
logger.error(f"Caddyfile not found: {CADDYFILE_PATH}")
|
||||||
|
sys.exit(1)
|
||||||
|
observer.schedule(event_handler, path=os.path.dirname(CADDYFILE_PATH), recursive=False)
|
||||||
|
logger.info(f"Watching {CADDYFILE_PATH} for changes")
|
||||||
|
|
||||||
observer.start()
|
observer.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"Agent started. Watching {CADDYFILE_PATH} for changes")
|
logger.info(f"{SERVER_TYPE.capitalize()} agent started successfully")
|
||||||
while True:
|
while True:
|
||||||
# Send periodic updates
|
# Send periodic updates
|
||||||
send_update()
|
send_update()
|
||||||
|
|
108
app.py
108
app.py
|
@ -10,6 +10,9 @@ from dotenv import load_dotenv
|
||||||
import re
|
import re
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
from watchdog.observers import Observer
|
||||||
|
from watchdog.events import FileSystemEventHandler
|
||||||
|
|
||||||
# Load environment variables
|
# Load environment variables
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
@ -51,6 +54,29 @@ caddy_proxies = {} # Server name -> {domain: target}
|
||||||
nginx_proxies = {} # Server name -> {domain: target}
|
nginx_proxies = {} # Server name -> {domain: target}
|
||||||
timestamps = {} # Server name -> timestamp
|
timestamps = {} # Server name -> timestamp
|
||||||
|
|
||||||
|
# File change monitoring
|
||||||
|
file_observer = None
|
||||||
|
|
||||||
|
class ConfigFileHandler(FileSystemEventHandler):
|
||||||
|
"""Watch for changes to configuration files and update data"""
|
||||||
|
def on_modified(self, event):
|
||||||
|
if USE_LOCAL_CADDYFILE and event.src_path == CADDYFILE_PATH:
|
||||||
|
logger.info(f"Local Caddyfile changed, updating entries")
|
||||||
|
entries = parse_local_caddyfile()
|
||||||
|
if entries:
|
||||||
|
caddy_proxies[LOCAL_SERVER_NAME] = entries
|
||||||
|
timestamps[LOCAL_SERVER_NAME] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
logger.info(f"Updated {len(entries)} entries from local Caddyfile")
|
||||||
|
|
||||||
|
# For Nginx, we need to check if the modified file is in the nginx config directory
|
||||||
|
if USE_LOCAL_NGINX and NGINX_CONFIG_PATH in event.src_path and event.src_path.endswith('.conf'):
|
||||||
|
logger.info(f"Local Nginx config changed, updating entries")
|
||||||
|
entries = parse_nginx_configs()
|
||||||
|
if entries:
|
||||||
|
nginx_proxies[f"{LOCAL_SERVER_NAME} Nginx"] = entries
|
||||||
|
timestamps[f"{LOCAL_SERVER_NAME} Nginx"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
logger.info(f"Updated {len(entries)} entries from local Nginx configs")
|
||||||
|
|
||||||
def verify_token(token):
|
def verify_token(token):
|
||||||
"""Verify the JWT token from an agent"""
|
"""Verify the JWT token from an agent"""
|
||||||
if not API_KEY:
|
if not API_KEY:
|
||||||
|
@ -266,42 +292,72 @@ def delete_server():
|
||||||
logger.info(f"Deleted server: {server_name}")
|
logger.info(f"Deleted server: {server_name}")
|
||||||
return jsonify({"status": "success", "message": f"Server {server_name} deleted"})
|
return jsonify({"status": "success", "message": f"Server {server_name} deleted"})
|
||||||
|
|
||||||
# Initialize with local files if available
|
def start_file_monitoring():
|
||||||
|
"""Start monitoring configuration files for changes"""
|
||||||
|
global file_observer
|
||||||
|
|
||||||
|
if not (USE_LOCAL_CADDYFILE or USE_LOCAL_NGINX):
|
||||||
|
logger.info("No local configuration files to monitor")
|
||||||
|
return
|
||||||
|
|
||||||
|
event_handler = ConfigFileHandler()
|
||||||
|
file_observer = Observer()
|
||||||
|
|
||||||
|
if USE_LOCAL_CADDYFILE:
|
||||||
|
# Monitor the Caddyfile
|
||||||
|
file_observer.schedule(
|
||||||
|
event_handler,
|
||||||
|
path=os.path.dirname(CADDYFILE_PATH),
|
||||||
|
recursive=False
|
||||||
|
)
|
||||||
|
logger.info(f"Monitoring local Caddyfile at {CADDYFILE_PATH}")
|
||||||
|
|
||||||
|
if USE_LOCAL_NGINX:
|
||||||
|
# Monitor Nginx config directory
|
||||||
|
file_observer.schedule(
|
||||||
|
event_handler,
|
||||||
|
path=NGINX_CONFIG_PATH,
|
||||||
|
recursive=True # Monitor all subdirectories too
|
||||||
|
)
|
||||||
|
logger.info(f"Monitoring local Nginx configs at {NGINX_CONFIG_PATH}")
|
||||||
|
|
||||||
|
file_observer.start()
|
||||||
|
logger.info("File monitoring started")
|
||||||
|
|
||||||
|
# Add cleanup function for graceful shutdown
|
||||||
|
def stop_file_monitoring():
|
||||||
|
"""Stop file monitoring"""
|
||||||
|
if file_observer:
|
||||||
|
file_observer.stop()
|
||||||
|
file_observer.join()
|
||||||
|
logger.info("File monitoring stopped")
|
||||||
|
|
||||||
|
# Enhanced signal handler
|
||||||
|
def signal_handler(sig, frame):
|
||||||
|
logger.info("Shutdown signal received, exiting gracefully...")
|
||||||
|
stop_file_monitoring()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
signal.signal(signal.SIGTERM, signal_handler)
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
|
# Initialize with local files and start monitoring
|
||||||
if USE_LOCAL_CADDYFILE:
|
if USE_LOCAL_CADDYFILE:
|
||||||
entries = parse_local_caddyfile()
|
entries = parse_local_caddyfile()
|
||||||
if entries:
|
if entries:
|
||||||
caddy_proxies[LOCAL_SERVER_NAME] = entries # Use LOCAL_SERVER_NAME for local configs
|
caddy_proxies[LOCAL_SERVER_NAME] = entries
|
||||||
timestamps[LOCAL_SERVER_NAME] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
timestamps[LOCAL_SERVER_NAME] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
logger.info(f"Loaded {len(entries)} entries from local Caddyfile")
|
logger.info(f"Loaded {len(entries)} entries from local Caddyfile")
|
||||||
|
|
||||||
if USE_LOCAL_NGINX:
|
if USE_LOCAL_NGINX:
|
||||||
entries = parse_nginx_configs()
|
entries = parse_nginx_configs()
|
||||||
if entries:
|
if entries:
|
||||||
nginx_proxies[f"{LOCAL_SERVER_NAME} Nginx"] = entries # Use LOCAL_SERVER_NAME for Nginx too
|
nginx_proxies[f"{LOCAL_SERVER_NAME} Nginx"] = entries
|
||||||
timestamps[f"{LOCAL_SERVER_NAME} Nginx"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
timestamps[f"{LOCAL_SERVER_NAME} Nginx"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
logger.info(f"Loaded {len(entries)} entries from local Nginx configs")
|
logger.info(f"Loaded {len(entries)} entries from local Nginx configs")
|
||||||
|
|
||||||
def signal_handler(sig, frame):
|
# Start file monitoring after initializing
|
||||||
logger.info("Shutdown signal received, exiting gracefully...")
|
start_file_monitoring()
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
signal.signal(signal.SIGTERM, signal_handler)
|
if __name__ == "__main__":
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
app.run(host="0.0.0.0", port=5000, debug=DEBUG_MODE)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
if USE_LOCAL_CADDYFILE:
|
|
||||||
logger.info(f"Local Caddyfile found at {CADDYFILE_PATH} - will display its data")
|
|
||||||
# Load it initially
|
|
||||||
local_data = parse_local_caddyfile()
|
|
||||||
if local_data:
|
|
||||||
caddy_proxies[LOCAL_SERVER_NAME] = local_data
|
|
||||||
timestamps[LOCAL_SERVER_NAME] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
|
|
||||||
if not API_KEY:
|
|
||||||
logger.warning("API_KEY not set - running without authentication!")
|
|
||||||
|
|
||||||
# Use HTTPS in production
|
|
||||||
if DEBUG_MODE:
|
|
||||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
|
||||||
else:
|
|
||||||
app.run(host='0.0.0.0', port=5000)
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
flask
|
flask>=2.0.0
|
||||||
requests
|
pyjwt>=2.0.0
|
||||||
pyjwt
|
python-dotenv>=0.19.0
|
||||||
cryptography
|
requests>=2.25.0
|
||||||
watchdog
|
watchdog>=2.1.0
|
||||||
python-dotenv
|
|
Loading…
Add table
Add a link
Reference in a new issue