Skip to content

Commit 3012a3e

Browse files
committed
Se agregan funcionalidades de reinicio de gunircorn y se utiliza script para iniciar gunicorn
1 parent 20e5d4b commit 3012a3e

2 files changed

Lines changed: 99 additions & 89 deletions

File tree

fabfile.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616

1717
from fabricator.deploy import deploy_site
1818
from fabricator.logger import get_logger
19-
from fabricator.recipes import release_lock, rollback_to_previous_release
19+
from fabricator.recipes import (
20+
release_lock,
21+
restart_services,
22+
rollback_to_previous_release,
23+
)
2024
from fabricator.runners import DockerRunner
2125
from fabricator.utils import load_sites, print_site_list
2226

@@ -271,6 +275,63 @@ def unlock_all(c: Connection | DockerRunner | Context) -> None:
271275
logger.info(f"Unlocking site: {site}")
272276
release_lock(c, config, force=True)
273277

278+
@task(help={"site": "Name of the site to restart"})
279+
def restart_site(c: Connection | DockerRunner | Context, site: str) -> None:
280+
"""
281+
Restart a single site defined in the configuration file.
282+
283+
Loads site-specific configuration, resolves the proper connection
284+
(local, Docker, or SSH), and runs the full deployment process.
285+
286+
:param c: Fabric connection or context object.
287+
:type c: Union[Connection, DockerRunner, Context]
288+
289+
:param site: Name of the site as defined in ``sites.yml``.
290+
:type site: str
291+
"""
292+
# Load all site definitions from the YAML config
293+
sites = load_sites()
294+
295+
# Initialize logger for the given site
296+
logger = get_logger(site)
297+
298+
# Abort if the requested site does not exist in config
299+
if site not in sites:
300+
logger.error(f"Site '{site}' not found in sites.yml")
301+
return
302+
303+
# Load config and set the site name explicitly
304+
config = sites[site]
305+
config['name'] = site
306+
307+
# Resolve connection using environment vars if present
308+
c = get_connection(c, config = config)
309+
310+
restart_services(c, config)
311+
312+
@task
313+
def restart_all(c: Connection | DockerRunner | Context) -> None:
314+
"""
315+
Restart all sites listed in the configuration file.
316+
317+
Iterates through all entries in ``sites.yml`` and runs the
318+
deployment pipeline for each one sequentially.
319+
320+
:param c: Fabric connection or context object.
321+
:type c: Union[Connection, DockerRunner, Context]
322+
"""
323+
# Load all site definitions
324+
sites = load_sites()
325+
326+
# Deploy each site iteratively
327+
for site, config in sites.items():
328+
config['name'] = site
329+
# Use remote connection if available from environment
330+
c = get_connection(c, config = config)
331+
logger = get_logger(site)
332+
logger.info(f"Restarting site: {site}")
333+
restart_services(c, config)
334+
274335
# This line exposes the tasks to the Fabric CLI (`fab2 ...`)
275336
ns = Collection(
276337
deploy,
@@ -279,5 +340,7 @@ def unlock_all(c: Connection | DockerRunner | Context) -> None:
279340
rollback,
280341
rollback_all,
281342
unlock,
282-
unlock_all
343+
unlock_all,
344+
restart_site,
345+
restart_all
283346
)

fabricator/recipes.py

Lines changed: 34 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,8 @@ def restart_services(
357357
"""
358358
Start or restart Gunicorn for the project.
359359
360-
Looks for `wsgi.py`, then launches Gunicorn with the appropriate
361-
socket and logging configuration, unless running locally.
360+
Uses the bash script 'start_services.sh' to handle the restart process.
361+
If the script doesn't exist, logs a warning and returns.
362362
363363
:param c: Fabric runner or connection object.
364364
:type c: Union[Connection, DockerRunner, Context]
@@ -368,103 +368,50 @@ def restart_services(
368368
"""
369369
logger = get_logger(config['name'])
370370

371-
# Determine if environment is local
372-
is_local = not isinstance(c, Connection) or getattr(
373-
c, "host", "localhost") in ("localhost", "127.0.0.1")
374-
375-
# Skip Gunicorn startup on local
376-
if is_local:
377-
logger.info("Skipping Gunicorn startup in local environment.")
378-
return
379-
380371
# Get site name from config
381372
site = config['name']
373+
script_path = "/scripts/start_services.sh"
382374

383-
# Define paths
384-
base_path = config['original_path']
385-
current_path = f"{base_path}/current"
386-
venv_path = config.get("venv", "venv")
387-
388-
# Path to Gunicorn executable
389-
gunicorn_bin = f"{current_path}/{venv_path}/bin/gunicorn"
390-
391-
# Define Unix socket for Gunicorn
392-
socket_path = f"/run/gunicorn/{site}.sock"
393-
394-
# Define log file paths
395-
access_log = f"/var/log/gunicorn/{site}-access.log"
396-
error_log = f"/var/log/gunicorn/{site}-error.log"
397-
398-
# Attempt to locate wsgi.py file
399-
result = c.run(
400-
f"find {current_path}/ -maxdepth 2 -name wsgi.py | head -n 1",
375+
# Check if the script exists
376+
check_script = c.run(
377+
f"test -f {script_path} && echo 'exists' || echo 'not_found'",
401378
hide=True,
402-
warn=True,
379+
warn=True
403380
)
381+
if check_script is None:
382+
logger.warning(
383+
f"Script {script_path} not found. Skipping service restart."
384+
)
385+
return
404386

405-
# Abort if wsgi.py not found or if result is None
406-
if not result or result.failed or not result.stdout.strip():
407-
logger.error(f"Could not find wsgi.py file in {current_path}/")
387+
script_exists = check_script.stdout.strip() == "exists"
388+
389+
if not script_exists:
390+
logger.warning(
391+
f"Script {script_path} not found. Skipping service restart."
392+
)
408393
return
409394

410-
# Extract wsgi path and project name
411-
wsgi_path = result.stdout.strip()
412-
project_dir = os.path.dirname(wsgi_path)
413-
project_name = os.path.basename(project_dir)
395+
logger.info(f"Restarting services for {site} using bash script...")
414396

415-
# Log where wsgi.py was found
416-
logger.info(f"Found wsgi.py in {wsgi_path}, project: {project_name}")
417-
logger.info(f"Running Gunicorn for {project_name} in background...")
397+
# Determine if we should restart a specific site or all sites
398+
if site and site != "all":
399+
# Restart specific site
400+
result = c.run(f"{script_path} {site}", warn=True)
401+
else:
402+
# Restart all sites
403+
result = c.run(f"{script_path}", warn=True)
418404

419-
# Kill previous Gunicorn processes for this site
420-
kill_cmd = (
421-
f"ps -eo pid,cmd | grep gunicorn | grep '{socket_path}' "
422-
f"| awk '{{print $1}}' | xargs -r kill"
423-
)
424-
logger.info(f"Stopping previous Gunicorn processes for {site}...")
425-
c.run(kill_cmd, warn=True)
426-
427-
# Generate a unique name for the script
428-
script_name = f"/tmp/start_gunicorn_{uuid.uuid4().hex}.sh"
429-
430-
# Content of the script to start Gunicorn
431-
script_content = f"""#!/bin/bash
432-
cd {project_dir}
433-
export PYTHONPATH={current_path}
434-
{gunicorn_bin} --workers=3 \\
435-
--bind=unix:{socket_path} \\
436-
{project_name}.wsgi:application \\
437-
--access-logfile {access_log} \\
438-
--error-logfile {error_log} \\
439-
--log-level=info
440-
"""
441-
442-
# Create the script on the remote server
443-
c.run(f"cat > {script_name} << 'EOL'\n{script_content}\nEOL", hide=True)
444-
c.run(f"chmod +x {script_name}", hide=True)
445-
446-
# Execute the script with nohup and in background
447-
cmd = f"nohup {script_name} >/dev/null 2>&1 </dev/null & sleep 1"
448-
c.run(cmd, pty=False)
449-
450-
# Delete the script after using it (optional, executed in background)
451-
c.run(f"(sleep 5 && rm -f {script_name} &)", pty=False)
452-
453-
logger.info(f"Gunicorn process for {site} has been launched")
454-
455-
# Optionally, verify if the process is running
456-
check_cmd = (
457-
f"ps -eo pid,cmd | grep gunicorn | "
458-
f"grep '{socket_path}' | grep -v grep"
459-
)
460-
result = c.run(check_cmd, warn=True, hide=True)
461-
if result and not result.failed and result.stdout.strip():
462-
logger.info(f"Verified Gunicorn is running for {site}")
405+
# Check if the command executed successfully
406+
if result and not result.failed:
407+
logger.info(f"Services for {site} restarted successfully")
463408
else:
464-
logger.warning(
465-
f"Could not verify if Gunicorn started for {site}. "
466-
"Check logs manually."
409+
logger.error(f"Failed to restart services for {site}")
410+
error_details = (
411+
result.stderr if result and hasattr(result, 'stderr')
412+
else 'Unknown error'
467413
)
414+
logger.error(f"Error details: {error_details}")
468415

469416
def set_writable_dirs(
470417
c: Connection | DockerRunner | Context,

0 commit comments

Comments
 (0)