Agregue algo a crontab mediante programación (sobre ssh)

13

Tengo una secuencia de comandos de implementación, debe agregar algo a un usuario crontab(desencadenar una secuencia de comandos que limpie los registros cada XXX días), sin embargo, esto solo debe hacerse durante la primera implementación o cuando debe actualizarse.

(Puedo correr xxx.py deploy envo xxx.py update env)

así que tengo que hacer esto:

Check if my cronJob already exist
Put my cronJob if it does not already exist
or
update my cronjob if one of the parameter of the command is different

No veo cómo agregar / verificar / eliminar algo crontabsin usar crontab -eo editar el crontabarchivo (descargarlo, reescribirlo, volver a cargarlo)

PD: este es un cronjob específico del usuario, "webadmin" lo va a hacer y no debe usar sudo para hacerlo.

sliders_alpha
fuente
1
¿Tiene que estar en un crontab específico del usuario? La mayoría de los trabajos cron preempaquetados van a uno de los directorios /etc/cron.*.
un CVn
¿Tiene CentOS /etc/cron.d? Si es así, coloque su script allí usando un nombre único para su aplicación
roaima
Sí, es específico del usuario. No puedo agregarlo a /etc/cron.d porque es un archivo raíz, por lo tanto, solo se permite el trabajo raíz (podría sudo, pero esa es una mala práctica que me han dicho)
sliders_alpha
1
igual que /etc/crontab, los archivos /etc/cron.d/tienen un campo adicional para el nombre de usuario, inmediatamente después de la especificación de programación. por ej * * * * * username /path/to/script. Ver man 5 crontaby buscar SYSTEM CRON.
cas
Ver también stackoverflow.com/questions/610839/…
rogerdpack

Respuestas:

15

mi mejor idea hasta ahora

para verificar primero si el contenido coincide con lo que debería estar allí y solo actualizar si no:

if [[ $(crontab -l | egrep -v "^(#|$)" | grep -q 'some_command'; echo $?) == 1 ]]
then
    echo $(crontab -l ; echo '* 1 * * * some_command') | crontab -
fi

pero esto se complica lo suficiente como para construir un script separado alrededor de esa tarea cron.

otras ideas

puede enviar la cadena a través de stdin a crontab (tenga cuidado, esto borra cualquier entrada crontab anterior):

echo "* 1 * * * some_command" | crontab -

esto incluso debería funcionar a través de ssh:

echo "* 1 * * * some_command" | ssh user@host "crontab -"

si desea agregar al archivo, puede usar esto:

# on the machine itself
echo "$(echo '* 1 * * * some_command' ; crontab -l)" | crontab -
# via ssh
echo "$(echo '* 1 * * * some_command' ; ssh user@host crontab -l)" | ssh user@host "crontab -"
Phillip -Zyan K Lee- Stockmann
fuente
eso depende: ¿necesitas lo que hubiera estado allí? :)
Phillip -Zyan K Lee- Stockmann
awww ... no se ejecuta como root? ... Lo reescribiré ...
Phillip -Zyan K Lee- Stockmann
eh, me gusta eso: D
sliders_alpha
Tuve dos problemas con esta solución: 1) echo '*...expandí *a una lista de archivos. 2) las terminaciones de línea en el crontab se eliminaron.
Heath Raftery
Pude solucionar estos problemas: 1) cambiar a echo "*...y 2) eliminar echo $desde el inicio de la línea.
Heath Raftery
3

Para el registro que voy a sugerir usar /etc/cron.d/. Solo el root puede escribir archivos aquí, pero las entradas se pueden ejecutar como cualquier usuario (sin necesidad de hacerlo sudo).

echo '0 0 * * 0 webadmin /usr/local/bin/tidy_logfiles' > ~/webadmin.cron
scp -p ~/webadmin.cron root@remote_host:/etc/cron.d/webadmin

Esto se puede aplicar varias veces, actualizando el webadmin.cronarchivo local según sea necesario antes de copiarlo.

Incluso puede eliminar el aprovisionamiento:

ssh -q root@remote_host rm -f /etc/cron.d/webadmin

Tenga en cuenta que en muchos casos no puede proporcionar la contraseña de root para los comandos scp/ ssh. En su lugar, debe haber configurado certificados de clave pública / privada. Además, por implicación, la cuenta local (sea lo que sea) tendrá acceso raíz completo al servidor remoto. No está claro en este momento si eso sería un obstáculo para su escenario específico.

roaima
fuente
Es mi servidor cliente, no puedo iniciar sesión como root, puedo sudo en él PERO, si lo hago, me matarán. Este es un trabajo de webadlmin, por lo que solo debería estar en el material de webadmin, eso es lo que me dijo el administrador del sistema.
sliders_alpha
@sliders_alpha el trabajo solo se ejecuta como webadmin. Es el aprovisionamiento que requiere equivalencia raíz. Sin embargo, también buscaré una solución no root.
roaima
1
+1. /etc/cron.d/existe exactamente para este propósito, de modo que los paquetes / implementaciones pueden simplemente soltar un archivo crontab aquí.
cas
3

Recomiendo encarecidamente usar Ansible * para esto en lugar de rodar el suyo. O Puppet o Chef, pero Ansible es ideal para scripts de implementación de infraestructura cero como este.

Esto se debe a que ya hay módulos destinados a resolver problemas como este, y las herramientas de administración de configuración tienen la idempotencia como un objetivo de diseño básico: esa es la propiedad de cambiar solo cuando es necesario, incluso si lo ejecuta accidentalmente (o intencionalmente) nuevamente.

En particular, el módulo cron de Ansible puede modificar crontabs de usuario. Como beneficio adicional, si luego desea ajustar para usar crontabs del sistema, será un ajuste muy fácil en lugar de una reescritura.


* descargo de responsabilidad: trabajo para Red Hat, y Ansible es un proyecto patrocinado por Red Hat.

mattdm
fuente
Sí, lo que pasa es que no sabía acerca de Ansible hace 2 meses, y ahora tenemos un script de despliegue masivo de Python (pero él es MAGNÍFICO, legible, mantenible,;)) La próxima vez, usaré Ansible pero ahora voy volver es imposible (dinero dinero dinero)
sliders_alpha
1

Si desea agregar un trabajo cron a través de la cuenta de destino, ejecute crontab -e. Este comando pasa el crontab a través de un editor. Dígale que use un comando de editor que modifique el crontab como desee. El comando del editor se ejecuta como un fragmento de shell con el nombre de un archivo temporal adjunto.

unset VISUAL
EDITOR='update_crontab () {
  set -e
  new=$(mktemp)
  if <"$1" grep -v "^#" | grep -w do_stuff; then
    # Remove existing entries containing do_stuff
    grep -v -w do_stuff "$1" >"$new"
  else
    cp "$1" "$new"
  fi
  # Add the new entry
  echo "1 2 3 4 5 do_stuff --new-options" >>"$new"
  mv "$new" "$1"
}
update_crontab' crontab -e

Este enfoque es más confiable que el nativo crontab -l | … | crontab -porque este es vulnerable a una condición de carrera si el crontab se edita simultáneamente: las modificaciones realizadas entre la llamada a crontab -ly la llamada a crontab -se deshacerían.

Gilles 'SO- deja de ser malvado'
fuente
1

Esta es una adaptación de lo que ofreció @ phillip-zyan-k-lee-stockmann , basado en su código de "Mejor idea hasta ahora".

Mis cambios de su (fragmento excelente y útil) son básicamente:

  • Regex no solo para el nombre del comando, sino también para toda la entrada, incluidas las cadenas de tiempo. De esa manera, podría admitir agregar un comando incluso si hay comandos con el mismo nombre o superpuestos en otras entradas. (Todavía no agregará el mismo comando en el mismo horario dos veces).
  • Un poco de registro
  • Cambié (y nombré) el mío por hora por varias razones; fácil de ajustar de nuevo por sintaxis crontab

Y aquí está mi código para lo que llamé crontab-add-hourly.sh:

#!/bin/bash

# PURPOSE:
# To allow simple, programmatic addition of commands/entries into the crontab (if not already present)

cmd=$1
entry="0 * * * * $cmd"
printf "we want to add this entry:\n$entry\n\n" 
escapedEntry=$(printf '%s\n' "$entry" | sed 's:[][\/.^$*]:\\&:g') #from: https://unix.stackexchange.com/a/129063/320236
printf "but first we'll see if it's already in there using this regex pattern:\n$escapedEntry\n\n"

if [[ $(crontab -l | egrep -v '^(#|$)' | grep -q "$escapedEntry"; echo $?) == 1 ]] # from: https://unix.stackexchange.com/a/297377/320236
then
    printf "all clear; pattern was not already present; adding command to crontab hourly:\n$cmd\n\n"
    (crontab -l ; printf "$entry\n\n") | crontab -
else
    printf "pattern already present; no action taken\n\n"
fi

Ejemplo de uso y salida:

$ ./crontab-add-hourly.sh my-script.bash

we want to add this entry:
0 * * * * my-script.bash

but first we'll see if it's already in there using this regex pattern:
0 \* \* \* \* my-script\.bash

all clear; pattern was not already present; adding command to crontab hourly:
my-script.bash
usuario1417853
fuente
0

TL; DR: Esto realmente funciona, probado en Bash 4.4.

if [[ $(crontab -l | egrep -v "^(#|$)" | grep -q 'some_command'; echo $?) == 1 ]]
then
    set -f
    printf "$(crontab -l ; echo '* * * * * some_command')\n" | crontab -
    set +f
fi

Como se señaló en los comentarios de @Phillip -Zyan K Lee- Stockmann, esa solución se expande *en todos los archivos en el directorio actual. No pude obtener la sugerencia de los comentarios para trabajar. set -f desactiva la expansión de comodines, consulte https://stackoverflow.com/a/11456496/915441 .

Yngvar Kristiansen
fuente