Adicione algo ao crontab programaticamente (sobre ssh)

13

Eu tenho um script de implantação, ele deve adicionar algo a um usuário crontab(acionar um script que limpe os logs a cada XXX dias); no entanto, isso só deve ser feito durante a primeira implantação ou quando precisar ser atualizado.

(Eu posso correr xxx.py deploy envou xxx.py update env)

então eu tenho que fazer isso:

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

Não vejo como adicionar / verificar / remover algo crontabsem usar crontab -eou editar o crontabarquivo (faça o download, reescreva-o, faça o upload novamente)

PS: este é um cronjob específico do usuário, "webadmin" fará isso e ele não deve usar o sudo para fazer isso.

sliders_alpha
fonte
1
Ele precisa estar em um crontab específico do usuário? A maioria dos trabalhos cron pré-empacotados vão para um dos diretórios /etc/cron.*.
a CVn 21/07
O CentOS possui /etc/cron.d? Se assim for, colocar o script lá usando um nome único para a sua aplicação
roaima
sim, é específico do usuário. Eu não pode adicioná-lo à /etc/cron.d porque é um arquivo de raiz, dentro, portanto, só trabalho de raiz são alowed (eu poderia sudo, mas que uma prática ruim que eu tenho dito)
sliders_alpha
1
igual a /etc/crontab, os arquivos /etc/cron.d/têm um campo extra para o nome de usuário, imediatamente após a especificação da programação. por exemplo * * * * * username /path/to/script. Veja man 5 crontabe pesquise SYSTEM CRON.
cas 21/07
Veja também stackoverflow.com/questions/610839/…
rogerdpack

Respostas:

15

minha melhor ideia até agora

para verificar primeiro se o conteúdo corresponde ao que deveria estar lá e somente atualizar se não:

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

mas isso é complicado o suficiente para criar um script separado em torno dessa tarefa cron.

outras idéias

você pode enviar a string via stdin para o crontab (cuidado, isso limpa todas as entradas anteriores do crontab):

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

isso deve funcionar mesmo através do ssh:

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

se você deseja anexar ao arquivo, você pode usar isto:

# 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
fonte
isso depende: você precisa do que estaria lá? :)
Phillip -Zyan K Lee- Stockmann
awww ... não está funcionando como root? ... vou reescrevê-lo ...
Phillip -Zyan K Lee- Stockmann
eh, eu gosto disso: D
sliders_alpha
Eu tive dois problemas com esta solução: 1) echo '*...expandiu o *arquivo para uma lista de arquivos. 2) as terminações de linha no crontab foram excluídas.
Heath Raftery,
Consegui corrigir esses problemas: 1) altere para echo "*...e 2) remova echo $do início da linha.
Heath Raftery
3

Para o registro, vou sugerir o uso /etc/cron.d/. Somente o root pode gravar arquivos aqui, mas as entradas podem ser executadas como qualquer usuário (sem necessidade sudo).

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

Isso pode ser aplicado várias vezes, atualizando o webadmin.cronarquivo local conforme necessário antes de copiá-lo.

Você pode até remover o provisionamento:

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

Observe que, em muitos casos, você não pode fornecer a senha do root para os comandos scp/ ssh. Em vez disso, você precisa configurar certificados de chave pública / privada. Além disso, implicitamente, a conta local (seja ela qual for) terá acesso root completo ao servidor remoto. Não está claro no momento se isso seria uma barreira para o seu cenário específico.

roaima
fonte
É o meu servidor cliente, não consigo logar como root, posso fazer o sudo MAS, se fizer isso, eles vão me matar. Este é um trabalho webadlmin, portanto, ele deve estar apenas no material webadmin, foi o que o administrador do sistema me disse.
Slider_alpha 21/07
@sliders_alpha o trabalho é executado apenas como administrador da web. É o provisionamento que requer equivalência de raiz. No entanto, também procurarei uma solução não raiz.
roaima 21/07
1
+1. /etc/cron.d/existe exatamente para esse fim - para que pacotes / implantações possam simplesmente colocar um arquivo crontab aqui.
cas
3

Eu recomendo usar o Ansible * para isso, em vez de usar o seu. Ou Puppet ou Chef - mas o Ansible é adequado para scripts de implantação de infraestrutura zero como este.

Isso ocorre porque já existem módulos destinados a resolver problemas como esse, e as ferramentas de gerenciamento de configurações têm a idempotência como um objetivo básico de design - essa é a propriedade de mudar apenas quando necessário, mesmo que você acidentalmente (ou intencionalmente) execute novamente.

Em particular, o módulo cron do Ansible pode modificar as crontabs do usuário. Como bônus, se você quiser se ajustar mais tarde para usar os crontabs do sistema, será um ajuste muito fácil, e não uma reescrita.


* aviso: trabalho para a Red Hat, e o Ansible é um projeto patrocinado pela Red Hat.

mattdm
fonte
Sim, o fato é que eu não sabia sobre o ansible há 2 meses e agora temos um enorme script de implementador python (mas ele é MAGNIFICIENTE, legível, manutenível,;)) Na próxima vez, usarei o ansible, mas agora vou volta é impossível (dinheiro dinheiro)
sliders_alpha
1

Se você deseja adicionar um trabalho cron através da conta de destino, execute crontab -e. Este comando passa o crontab através de um editor. Diga a ele para usar um comando de editor que modifique o crontab como desejar. O comando do editor é executado como um snippet de shell com o nome de um arquivo temporário anexado.

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

Essa abordagem é mais confiável que a nativa, crontab -l | … | crontab -porque esta é vulnerável a uma condição de corrida se o crontab for editado simultaneamente: as modificações feitas entre a chamada crontab -le a chamada crontab -serão desfeitas.

Gilles 'SO- parar de ser mau'
fonte
1

Esta é uma adaptação do que o @ phillip-zyan-k-lee-stockmann ofereceu, com base no código "Melhor idéia até agora".

Minhas alterações a partir dele (excelente e útil trecho) são basicamente:

  • Regex não apenas para o nome do comando, mas também para toda a entrada, incluindo as seqüências de tempo. Dessa forma, ele poderia oferecer suporte à adição de um comando, mesmo se houver comandos com o mesmo nome ou com sobreposição em outras entradas. (Ele ainda não adicionará o mesmo comando na mesma programação duas vezes.)
  • Um pouco de log
  • Mudei (e nomeei) o meu para o horário por várias razões; fácil ajustá-lo de volta por sintaxe crontab

E aqui está o meu código para o que eu chamei 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

Exemplo de uso e saída:

$ ./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
fonte
0

TL; DR: Isso realmente funciona, testado no 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

Conforme observado nos comentários da resposta @Phillip -Zyan K Lee- Stockmann, essa solução se expande *em todos os arquivos no diretório atual. Não consegui que a sugestão dos comentários funcionasse. set -f desativa a expansão de curinga, consulte https://stackoverflow.com/a/11456496/915441 .

Yngvar Kristiansen
fonte