Como continuar uma tarefa quando o Fabric recebe um erro


94

Quando eu defino uma tarefa para ser executada em vários servidores remotos, se a tarefa for executada no servidor um e sair com um erro, o Fabric irá parar e abortar a tarefa. Mas eu quero fazer o fabric ignorar o erro e executar a tarefa no próximo servidor. Como posso fazer isso?

Por exemplo:

$ fab site1_service_gw
[site1rpt1] Executing task 'site1_service_gw'

[site1fep1] run: echo 'Nm123!@#' | sudo -S route
[site1fep1] err:
[site1fep1] err: We trust you have received the usual lecture from the local System
[site1fep1] err: Administrator. It usually boils down to these three things:
[site1fep1] err:
[site1fep1] err:     #1) Respect the privacy of others.
[site1fep1] err:     #2) Think before you type.
[site1fep1] err:     #3) With great power comes great responsibility.
[site1fep1] err: root's password:
[site1fep1] err: sudo: route: command not found

Fatal error: run() encountered an error (return code 1) while executing 'echo 'Nm123!@#' | sudo -S route '

Aborting.

Respostas:


146

Dos documentos :

... O padrão do Fabric é um padrão de comportamento “fail-fast”: se algo der errado, como um programa remoto retornando um valor de retorno diferente de zero ou o código Python do seu fabfile encontrando uma exceção, a execução será interrompida imediatamente.

Normalmente, esse é o comportamento desejado, mas há muitas exceções à regra, portanto, o Fabric fornece env.warn_only, uma configuração booleana. O padrão é False, o que significa que uma condição de erro resultará na interrupção imediata do programa. No entanto, se env.warn_only for definido como True no momento da falha - com, digamos, o gerenciador de contexto de configurações - o Fabric emitirá uma mensagem de aviso, mas continuará executando.

Parece que você pode exercer um controle refinado sobre onde os erros são ignorados usando o settingsgerenciador de contexto , algo como:

from fabric.api import settings

sudo('mkdir tmp') # can't fail
with settings(warn_only=True):
    sudo('touch tmp/test') # can fail
sudo('rm tmp') # can't fail

13
Não se esqueça de importarfrom fabric.api settings
cevaris

31

No Fabric 1.5, existe um ContextManager que torna isso mais fácil:

from fabric.api import sudo, warn_only

with warn_only():
    sudo('mkdir foo')

Atualização: eu reconfirmei que isso funciona em ipython usando o seguinte código.

from fabric.api import local, warn_only

#aborted with SystemExit after 'bad command'
local('bad command'); local('bad command 2')

#executes both commands, printing errors for each
with warn_only():
    local('bad command'); local('bad command 2')

Qual versão de tecido você está usando? Acabei de testar novamente com Fabric == 1.6.2 e funcionou bem.
Chris Marinos

Possivelmente, estou usando o Fabric == 1.9.0 e não funciona para mim
cevaris

Acabei de testar em 1.9.0 também. Qual é a sua saída quando você tenta o código de exemplo do meu comentário atualizado?
Chris Marinos de

Se não quiser imprimir os avisos / erros, você também pode usar o gerenciador de contexto de ocultação :with hide('everything'):
np8

13

Você também pode definir a configuração warn_only de todo o script para ser verdadeira com

def local():
    env.warn_only = True

10

Você deve definir a abort_exceptionvariável de ambiente e capturar a exceção.

Por exemplo:

from fabric.api        import env
from fabric.operations import sudo

class FabricException(Exception):
    pass

env.abort_exception = FabricException
# ... set up the rest of the environment...

try:
    sudo('reboot')
except FabricException:
    pass  # This is expected, we can continue.

Você também pode configurá-lo em um bloco com. Veja a documentação aqui .


Obrigado por isso, mas uma pergunta - é possível acessar / passar no ambiente de malha atual conforme definido quando a exceção ocorreu? (Posso imprimir algumas configurações específicas com a exceção.)
Brian,

@Brian: Você não poderia simplesmente verificar fabric.api.envdentro do seu exceptbloco?
ArtOfWarfare

@ArtOfWarefare Ahh, bobagem, eu estava tentando evitar envolver todas as minhas tarefas em um try / except e, em vez disso, apenas configurei o env.abort_exception=MyExceptionpara que pudesse executar minha própria falha. Ele meio que "funciona" se eu usar uma função em vez de uma classe (satisfaz a solicitação de chamada para abort_exception), mas ainda estou trabalhando em alguns outros problemas com essa abordagem.
Brian,

@Brian: Então, dentro do corpo dessa função, verifique o que fabric.api.envé.
ArtOfWarfare

7

No Fabric 1.3.2, pelo menos, você pode recuperar a exceção capturando a SystemExitexceção. Isso é útil se você tiver mais de um comando para executar em um lote (como uma implantação) e quiser limpar se um deles falhar.


+1: Testado - também funciona no Fabric 1.9.0. Depois de detectar isso, você pode verificar a SystemExitmensagem ou o código de para obter mais detalhes.
ArtOfWarfare

Melhor do que capturar SystemExit, defina abort_exceptionuma exceção diferente, para que você não pegue acidentalmente exceções que não têm nada a ver com o Fabric. Veja minha resposta para um exemplo: stackoverflow.com/a/27990242/901641
ArtOfWarfare

7

No Fabric 2.x, você pode apenas usar a execução de invoke com o argumento warn = True . De qualquer forma, invoke é uma dependência do Fabric 2.x :

from invoke import run
run('bad command', warn=True)

De dentro de uma tarefa:

from invoke import task

@task
def my_task(c):
    c.run('bad command', warn=True)

-5

No meu caso, no Fabric> = 1,4 essa resposta foi a correta.

Você pode pular hosts ruins adicionando este:

env.skip_bad_hosts = True

Ou passando a --skip-bad-hostsbandeira /

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.