Dodaj coś do crontab programowo (przez ssh)

13

Mam skrypt wdrażania, musi on coś dodać do użytkownika crontab(uruchomić skrypt, który czyści dzienniki co XXX dni), jednak można to zrobić tylko podczas pierwszego wdrożenia lub gdy trzeba go zaktualizować.

(Mogę biegać xxx.py deploy envlub xxx.py update env)

więc muszę to zrobić:

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

Nie widzę, jak dodać / sprawdzić / usunąć coś crontabbez użycia crontab -elub edycji crontabpliku (pobierz, przepisz, prześlij ponownie)

PS: jest to cronjob specyficzny dla użytkownika, „webadmin” to zrobi i nie powinien używać do tego sudo.

sliders_alpha
źródło
1
Czy musi to być crontab specyficzny dla użytkownika? Większość paczkowanych zadań cron trafia do jednego z katalogów /etc/cron.*.
CVn
Czy CentOS ma /etc/cron.d? Jeśli tak, umieść tam swój skrypt, używając nazwy unikalnej dla Twojej aplikacji
roaima
tak, jest specyficzny dla użytkownika. Nie mogę dodać go do /etc/cron.d, ponieważ jest to plik root, dlatego tylko praca roota jest w nim zawarta (mógłbym sudo, ale powiedziano mi, że to zła praktyka)
sliders_alpha
1
tak samo jak /etc/crontabpliki w /etc/cron.d/mają dodatkowe pole na nazwę użytkownika, bezpośrednio po specyfikacji harmonogramu. np * * * * * username /path/to/script. Zobacz man 5 crontabi wyszukaj SYSTEM CRON.
cas
Zobacz także stackoverflow.com/questions/610839/…
rogerdpack

Odpowiedzi:

15

jak dotąd mój najlepszy pomysł

najpierw sprawdź, czy treść pasuje do tego, co powinno tam być, i zaktualizuj tylko, jeśli nie:

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

ale komplikuje się to na tyle, aby zbudować osobny skrypt wokół tego zadania crona.

inne pomysły

możesz wysłać ciąg przez stdin do crontab (uwaga, to wyczyści wszystkie poprzednie wpisy crontab):

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

powinno to nawet działać bezpośrednio przez ssh:

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

jeśli chcesz dołączyć do pliku, możesz użyć tego:

# 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
źródło
to zależy: czy potrzebujesz tego, co by tam było? :)
Phillip -Zyan K Lee- Stockmann
awww ... nie działa jako root? ...
Przepiszę
eh, podoba mi się to: D
sliders_alpha
Miałem dwa problemy z tym rozwiązaniem: 1) echo '*...rozszerzyłem *listę plików. 2) zakończenia wierszy w crontab zostały usunięte.
Heath Raftery
Udało mi się rozwiązać te problemy poprzez: 1) zmianę na echo "*...i 2) usunięcie echo $z początku linii.
Heath Raftery
3

Dla przypomnienia proponuję użyć /etc/cron.d/. Tylko root może tutaj zapisywać pliki, ale wpisy można uruchamiać jako dowolny użytkownik (bez potrzeby sudo).

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

Można to zastosować wiele razy, w webadmin.cronrazie potrzeby aktualizując plik lokalny przed skopiowaniem go.

Możesz nawet usunąć obsługę administracyjną:

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

Zauważ, że w wielu przypadkach nie możesz podać hasła roota dla poleceń scp/ ssh. Zamiast tego musisz skonfigurować certyfikaty klucza publicznego / prywatnego. Ponadto, implikacja, konto lokalne (cokolwiek to jest) będzie miało pełny dostęp root do zdalnego serwera. W tej chwili nie jest jasne, czy byłoby to przeszkodą dla konkretnego scenariusza.

roaima
źródło
To mój serwer klienta, nie mogę zalogować się jako root, mogę sudo na nim, ALE, jeśli to zrobię, zabiją mnie. To zadanie webadlmin, więc powinno być tylko w webadminie, tak powiedział mi sysadmin.
sliders_alpha
@ slider_alpha zadanie działa tylko jako webadmin. To dostarczanie wymaga równoważności katalogu głównego. Jednak szukam również rozwiązania innego niż root.
roaima
1
+1. /etc/cron.d/istnieje właśnie w tym celu - aby pakiety / wdrożenia mogły po prostu upuścić tutaj plik crontab.
cas
3

Zdecydowanie zalecam używanie do tego Ansible *, a nie rozwijanie własnego. Lub Puppet lub Chef - ale Ansible doskonale nadaje się do skryptów wdrażania z zerową infrastrukturą, takich jak ten.

Dzieje się tak, ponieważ istnieją już moduły przeznaczone do rozwiązywania takich problemów, a narzędzia do zarządzania konfiguracją mają idempotencję jako podstawowy cel projektowania - właściwość polega na zmianie tylko wtedy, gdy trzeba, nawet jeśli przypadkowo (lub celowo) uruchomisz ją ponownie.

W szczególności moduł cron Ansible może modyfikować pliki crontab użytkownika. Jako bonus, jeśli chcesz później dostosować się do crontabs systemowych, będzie to bardzo łatwa modyfikacja niż przepisywanie.


* wyłączenie odpowiedzialności: Pracuję dla Red Hat, a Ansible to projekt sponsorowany przez Red Hat.

mattdm
źródło
Tak, rzecz w tym, że nie wiedziałem o ansible 2 miesiące temu, a teraz mamy ogromny skrypt wdrażający Pythona (ale jest on NAJWYŻSZY, czytelny, możliwy do utrzymania;)) Następnym razem skorzystam z ansible, ale teraz idę powrót jest niemożliwy (pieniądze pieniądze pieniądze)
sliders_alpha
1

Jeśli chcesz dodać zadanie cron za pośrednictwem konta docelowego, uruchom crontab -e. To polecenie przekazuje crontab przez edytor. Powiedz, aby używał polecenia edytora, które modyfikuje tabelę crontab według własnego uznania. Polecenie edytora jest wykonywane jako fragment powłoki z dołączoną nazwą pliku tymczasowego.

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

Podejście to jest bardziej niezawodne niż natywne, crontab -l | … | crontab -ponieważ jest podatne na warunki wyścigu, jeśli crontab jest edytowany jednocześnie: modyfikacje dokonane między wywołaniem crontab -la wezwaniem do crontab -zostaną cofnięte.

Gilles „SO- przestań być zły”
źródło
1

Jest to adaptacja tego, co oferował @ phillip-zyan-k-lee-stockmann , w oparciu o jego kod „Najlepszy pomysł do tej pory”.

Moje zmiany od jego (doskonałego i pomocnego fragmentu) są w zasadzie:

  • Ponownie wyszukaj nie tylko nazwę polecenia, ale także cały wpis, w tym ciągi czasowe. W ten sposób może obsługiwać dodawanie poleceń, nawet jeśli w innych wpisach są polecenia o tej samej nazwie lub nakładające się na siebie. (Nadal nie doda dwukrotnie tego samego polecenia w tym samym harmonogramie).
  • Trochę logowania
  • Z różnych powodów zmieniłem (i nazłem) mój na godzinny; łatwo go przywrócić zgodnie ze składnią crontab

Oto mój kod tego, co nazwałem 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

Przykładowe użycie i dane wyjściowe:

$ ./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
użytkownik1417853
źródło
0

TL; DR: To faktycznie działa, przetestowane w 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

Jak zauważono w komentarzach @Phillip -Zyan K Lee-Stockmann, rozwiązanie to rozszerza się *na wszystkie pliki w bieżącym katalogu. Nie udało mi się uzyskać sugestii komentarzy. zestaw -f wyłącza rozszerzenie symboli wieloznacznych, patrz https://stackoverflow.com/a/11456496/915441 .

Yngvar Kristiansen
źródło