Ajouter quelque chose à crontab par programme (via ssh)

13

J'ai un script de déploiement, il doit ajouter quelque chose à un utilisateur crontab(déclencher un script qui nettoie les journaux tous les XXX jours), mais cela ne doit être fait que lors du premier déploiement, ou lorsqu'il doit être mis à jour.

(Je peux courir xxx.py deploy envou xxx.py update env)

donc je dois faire ceci:

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

Je ne vois pas comment ajouter / vérifier / supprimer quelque chose crontabsans utiliser crontab -eou modifier le crontabfichier (le télécharger, le réécrire, le télécharger à nouveau)

PS: il s'agit d'un cronjob spécifique à l'utilisateur, "webadmin" va le faire et il ne devrait pas utiliser sudo pour le faire.

sliders_alpha
la source
1
Doit-il être dans une crontab spécifique à l'utilisateur? La plupart des tâches cron préemballées vont dans l'un des répertoires /etc/cron.*.
un CVn
Est-ce que CentOS a /etc/cron.d? Si c'est le cas, mettez votre script là-dedans en utilisant un nom unique à votre application
roaima
oui c'est spécifique à l'utilisateur. Je ne peux pas l'ajouter à /etc/cron.d parce que c'est un fichier racine, donc seul le travail de root est autorisé à l'intérieur (je pourrais sudo mais c'est une mauvaise pratique qu'on m'a dit)
sliders_alpha
1
de la même manière que les /etc/crontabfichiers /etc/cron.d/contiennent un champ supplémentaire pour le nom d'utilisateur, immédiatement après la spécification de planification. par exemple * * * * * username /path/to/script. Voir man 5 crontabet rechercher SYSTEM CRON.
cas

Réponses:

15

ma meilleure idée jusqu'ici

pour vérifier d'abord si le contenu correspond à ce qui devrait y être et ne le mettre à jour que si ce n'est pas le cas:

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

mais cela devient assez compliqué pour construire un script séparé autour de cette tâche cron.

d'autres idées

vous pouvez envoyer la chaîne via stdin à crontab (attention, cela efface toutes les entrées crontab précédentes):

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

cela devrait même fonctionner directement via ssh:

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

si vous souhaitez ajouter au fichier, vous pouvez utiliser ceci:

# 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
la source
cela dépend: avez-vous besoin de ce qui aurait été là-dedans? :)
Phillip -Zyan K Lee- Stockmann
awww ... ne fonctionne pas en tant que root? ... je vais le réécrire ...
Phillip -Zyan K Lee- Stockmann
eh, j'aime ça: D
sliders_alpha
J'ai eu deux problèmes avec cette solution: 1) echo '*...étendu le *à une liste de fichiers. 2) les fins de ligne dans la crontab ont été supprimées.
Heath Raftery
J'ai pu résoudre ces problèmes en: 1) modifiant echo "*...et 2) supprimant echo $le début de la ligne.
Heath Raftery
3

Pour mémoire, je vais suggérer d'utiliser /etc/cron.d/. Seul root peut écrire des fichiers ici mais les entrées peuvent être exécutées comme n'importe quel utilisateur (sans besoin de sudo).

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

Cela peut être appliqué plusieurs fois, en mettant à jour le webadmin.cronfichier local si nécessaire avant de le copier.

Vous pouvez même supprimer le provisionnement:

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

Notez que dans de nombreux cas, vous ne pouvez pas fournir le mot de passe root pour les commandes scp/ ssh. Au lieu de cela, vous devez avoir configuré des certificats de clé publique / privée. En outre, par implication, le compte local (quel qu'il soit) aura un accès root complet au serveur distant. Il n'est pas clair pour le moment si ce serait une solution pour votre scénario spécifique.

roaima
la source
C'est mon serveur client, je ne peux pas me connecter en tant que root, je peux y entrer en mode MAIS, si je fais cela, ils me tueront. C'est un travail webadlmin donc il ne devrait être que dans les trucs webadmin, c'est ce que le sysadmin m'a dit.
sliders_alpha
@sliders_alpha, le travail s'exécute uniquement en tant que webadmin. C'est l'approvisionnement qui nécessite l'équivalence racine. Cependant, je chercherai également une solution non root.
roaima
1
+1. /etc/cron.d/existe exactement dans ce but - pour que les packages / déploiements puissent simplement déposer un fichier crontab ici.
cas
3

Je recommande fortement d'utiliser Ansible * pour cela plutôt que de lancer le vôtre. Ou Puppet ou Chef - mais Ansible est bien adapté pour des scripts de déploiement sans infrastructure comme celui-ci.

En effet, il existe déjà des modules destinés à résoudre des problèmes comme celui-ci, et les outils de gestion de la configuration ont l' idempotence comme objectif de conception de base - c'est la propriété de ne changer que lorsque cela est nécessaire, même si vous le réexécutez accidentellement (ou intentionnellement).

En particulier, le module cron d'Ansible peut modifier les crontabs utilisateur. En prime, si vous souhaitez vous ajuster ultérieurement pour utiliser les crontabs du système, ce sera un ajustement très facile plutôt qu'une réécriture.


* Avertissement: je travaille pour Red Hat et Ansible est un projet sponsorisé par Red Hat.

mattdm
la source
Ouais, la chose est, je ne connaissais pas ansible il y a 2 mois, et maintenant nous avons un script de déployeur de python massif (mais il est MAGNIFIQUE, lisible, maintenable,;)) La prochaine fois, j'utiliserai ansible mais je vais maintenant le dos est impossible (argent argent argent)
sliders_alpha
1

Si vous souhaitez ajouter un travail cron via le compte cible, exécutez crontab -e. Cette commande passe la crontab via un éditeur. Dites-lui d'utiliser une commande d'éditeur qui modifie la crontab comme vous le souhaitez. La commande de l'éditeur est exécutée comme un extrait de shell avec le nom d'un fichier temporaire ajouté.

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

Cette approche est plus fiable que la native crontab -l | … | crontab -car celle-ci est vulnérable à une condition de concurrence si la crontab est éditée simultanément: les modifications apportées entre l'appel à crontab -let l'appel à crontab -seraient annulées.

Gilles 'SO- arrête d'être méchant'
la source
1

Il s'agit d'une adaptation de ce que @ phillip-zyan-k-lee-stockmann a proposé, sur la base de son code "Meilleure idée à ce jour".

Mes changements par rapport à son (extrait excellent et utile) sont essentiellement:

  • Regex pour non seulement le nom de la commande, mais aussi l'entrée entière, y compris les chaînes de temps. De cette façon, il pourrait prendre en charge l'ajout d'une commande même s'il existe des commandes portant le même nom ou se chevauchant dans d'autres entrées. (Il n'ajoutera toujours pas la même commande deux fois sur la même planification.)
  • Un peu de journalisation
  • J'ai changé (et nommé) le mien en horaire pour diverses raisons; facile à ajuster par la syntaxe crontab

Et voici donc mon code pour ce que j'ai appelé 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

Exemple d'utilisation et de sortie:

$ ./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
user1417853
la source
0

TL; DR: Cela fonctionne réellement, testé dans 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

Comme indiqué dans les commentaires de @Phillip -Zyan K Lee-Stockmann, cette solution se développe *dans tous les fichiers du répertoire actuel. Je n'ai pas réussi à faire fonctionner la suggestion des commentaires. set -f désactive l'extension générique, voir https://stackoverflow.com/a/11456496/915441 .

Yngvar Kristiansen
la source