run () / sudo () veria de forma inteligente que você está indo para o localhost e apenas executaria local (). Isso provavelmente seria opcional.
Comentários de Jeff no IRC:
e sim, quero dizer que sempre haverá sobrecarga com ssh vs tubos diretos de improviso. Não acho que seria terrivelmente difícil atualizar run / sudo (especialmente no master, agora que eles foram refatorados) para chamar / retornar local ( ) de forma inteligente, não tenho certeza de que gostaria desse comportamento semágico no núcleo (mesmo com ele desativado por padrão com um optin para ativá-lo, embora isso ajudasse), mas mesmo assim, seria um experimento interessante. e se for tão simples quanto estou pensando, honestamente, não consigo encontrar um bom motivo para não o fazer (mais uma vez, desde que não seja o comportamento padrão)
Originalmente apresentado por Nick Welch ( mackstann ) em 2009-11-11 às 01:39 pm EST
James Pearson (xiong.chiamiov) postou:
Como também mencionei no irc, eu normalmente não executo o servidor ssh em uma máquina desktop, então não posso realmente executar ssh para localhost.
em 11/11/2009 às 15:13 EST
Travis Swicegood ( tswicegood ) postou:
Acabei de implementar algo semelhante esta noite na forma de uma nova função fabric.operations chamada do
. Ele olha para env.run_as
para ver se é igual a "local" e, ao fazer isso, muda para o método local
vez de run
(ou sudo
se sudo=True
for passado como um kwarg). Ele também lida com a prefixação de comandos locais com sudo
no caso de estarem executando localmente.
Esta é uma maneira diferente de contornar este problema, que funciona sem alterar o comportamento de run
ou sudo
. Essas mudanças estão disponíveis em meu repositório .
em 11/01/2010 às 12h22 EST
Morgan Goose ( goosemo ) postou:
Eu realmente não vejo isso sendo plausível. Qual é o objetivo de executar como local. Um dos requisitos do Fabric é o sshd rodando na máquina, remoto ou loopback. O outro problema é que apenas alterar o local não leva em consideração put, get, rsync_project e outros que ainda precisam do ssh. Tentar implementá-los realmente causaria mais problemas, já que agora está no reino de fazer fabfiles traduzir para o bash.
em 13/03/2011 às 23:14 EDT
Jeff Forcier ( bitprophet ) postou:
Embora eu também não esteja 100% convencido de que essa é uma ótima ideia, é claramente algo que vários usuários sentem necessidade - outra solicitação foi apresentada como # 364 com outra explicação do caso de uso.
Também adicionei o tíquete de simulação conforme relacionado a este, porque (presumo - se algum dos usuários solicitantes puder verificar isso, seria ótimo) o principal caso de uso para esse recurso é para teste / teste correndo.
em 23/06/2011 às 11h26 EDT
Conforme observado em # 538, se algum dia formos capazes de normalizar totalmente os três corredores para que possam ser usados de forma intercambiável, precisaremos nos certificar de que o escape do shell funcione de forma consistente entre eles. No momento, não temos o escape de shell local
, embora isso seja pelo menos em parte porque ele não está usando um invólucro de shell.
Se alguém está se perguntando "por que alguém faria isso?", A resposta é que se você tiver um pipeline de implantação, pode ser útil executar o mesmo script de implantação exato, não importa o ambiente, em vez de ter um script de configuração especial para localhost contra tudo o mais.
+1 para o recurso
+1
+10
+1
+1
Para atrasar você, certifique-se de ter o servidor OpenSSH em execução. Primeiro faça sudo apt-get install ssh
para ter certeza de que o instalou (mesmo se você achar que tem). Em seguida, faça sudo service ssh start
| stop
| restart
conforme necessário. Aprendi com este tópico .
+1
Meu caso de uso é simples: eu quero usar o mesmo script django-deploy para configurar instâncias ec2 com cloud-init por meio do CloudWatch (o caso para executar comandos locais) e usar o fab deploy_django -H foo@bar
regular.
+1
Isso seria muito útil. Um caso de uso que eu tenho é usar o vagrant shell provisioner para configurar um VM particular usando fabric e sem a necessidade de ssh localhost.
+1
Fiquei surpreso ao não ver isso ainda no Fabric.
Para sua informação: A implementação deste recurso fica mais complexa quando você pensa em funções de malha como reboot()
.
+1
Já deve fazer parte do núcleo!
+1
Isso faria sentido: de um ponto de vista abstrato, local
é apenas um caso especial de run
, onde nenhuma máquina SSH está envolvida.
Mais uma coisa a apontar (talvez óbvio): o tecido deve ser inteligente o suficiente para decidir se run
deve ser convertido em local
APÓS a leitura de / etc / hosts.
Quero dizer: se tivermos
env.host = [ 'mywebserver' ]
e em / etc / hosts temos:
127.0.0.1 mywebserver
então, quaisquer run
chamadas devem, na verdade, ser local
chamadas.
Levando este conceito um passo adiante, devemos também tratar run
como uma chamada local quando o host remoto resolve para um IP que é atribuído a uma interface de rede da máquina local.
Por exemplo:
fabfile:
env.host = [ 'mywebserver' ]
/ etc / hosts:
192.168.1.1 mywebserver
ip addr
:
[...]
eth0:
inet 192.168.1.1
[...]
+1
+1: +1:
: +1:
+1
+1
O Fabric 2 usará pyinvoke / invoke, então isso deve ser bem fácil de fazer lá. Eu esperaria pelo Fabric 2 por uma maneira não hacky de fazer isso.
: +1:
+1
: +1:
: +1: implemente isso, especialmente porque os computadores mac não são configurados automaticamente para ter túneis SSH configurados para acesso remoto ao servidor localhost.
+1
+1 :)
+1 por favor
: +1:
: +1:
: +1:
Estamos usando o Fab para construir pacotes debian e isso adiciona complexidade extra
pessoal, olá a todos
tento criar clone de tecido com diferença:
Você pode dar uma olhada se precisar deste recurso
https://github.com/Friz-zy/factory
Posso estar faltando alguma coisa nesta discussão, mas aqui está o que fiz para usar o mesmo código com o comando fab run
em ambos os locaishost e máquinas remotas.
env.use_ssh_config = True
no meu fabfile.pyIsso não resolve o seu problema se você não estiver executando o servidor ssh em sua máquina local
: +1:
+1
+1 Implemente este recurso :)
+1
Pode ser muito útil para inicializar imagens do Docker usando scripts existentes do Fabric. Este recurso evitaria a instalação de um servidor SSH no contêiner, o que vai contra as práticas recomendadas do
+1
+1
+1
Além da resposta fornecida por @AntoniosHadji , aqui estão as instruções completas para fazer este trabalho;
# Generate new SSH key for local usage
ssh-keygen -f ~/.ssh/id_rsa -N ''
# Add server keys to users known hosts (eliminates 'are you sure' messages);
ssh-keyscan -H localhost > ~/.ssh/known_hosts
# Allow user to ssh to itself
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
Na verdade, isso pode ser feito com a culinária . Você precisa alterar todas as run
execuções para fazer referência à função cuisine.run
, o que pode ser feito facilmente com uma importação, e alterar o modo para local:
from cuisine import run, mode_local
mode_local()
print run("echo Hello")
Otimo @cgarciaarano
Para casos de uso simples, isso funciona para mim:
from fabric.api import run, local
# ...
# in task:
if env.host is None or env.host == 'localhost':
run = local
: +1:
Eu quero que meu fabfile seja executado remotamente ou localmente quando o ssh não for uma opção. Isso inclui wrappers locais para obter / colocar / existir etc.
: +1: Eu tenho fabfiles que são executados tanto localmente quanto remotamente e acabei hackeando minhas próprias funções de wrapper para executar / local / conseguir lidar com todas as diferenças sutis, como captura de saída e tratamento de erros.
E se você tiver uma conexão ssh fazendo encaminhamento de porta dinâmico e vinculando em 127.0.0.2 (ainda tecnicamente localhost) na porta 2223. Posso ver como isso pode causar problemas, para esse fim combinando em localhost e resolvendo para 127.0.0.1 em vez de também suportar toda a classe 127.0.0.0/8 pode ser uma boa ideia de lidar.
@ blade2005 Sim, todo o intervalo 127 ._._. * aponta para o seu host local (exceto 127.0.0.0 e 127.255.255.255), mas quando você está realmente apontando para o seu host local, você não usa a porta certa?
Portanto, acredito que podemos assumir com segurança que 127.*.*.* == localhost
e ssh podem ser evitados, mas 127.*.*.*:*
aponta para uma porta encaminhada e ssh é necessário.
Honestamente, esse recurso provavelmente faria mais sentido como um plug-in de terceiros criado em tecido, semelhante à biblioteca de culinária. Em seguida, apenas importaríamos funções agrupadas para run / get / put / etc, que saberiam se executariam local ou remotamente com base em uma variável env. Pelo menos assim, alguém poderia começar isso para que todos usassem.
Implementei algo localmente e é muito mais trabalhoso do que apenas alternar entre local / execução. Você deve considerar prefixos, diretórios alterados, usuários sudo, etc.
Estava pensando brevemente sobre isso no contexto de outro tíquete relacionado ao 2.0, e percebi que há mais coisas surgindo além de apenas " run
torna-se uma nova ligação de local
":
local
e run
, ou put
/ get
, torna-se inerentemente problemática: operações com definição clara 'local' e 'remoto' "termina" agora ambos apontam localmente.run
ou sudo
aumenta DoesntMakeAnySenseError
"ou qualquer outra coisa.put
/ get
presumivelmente poderia se transformar em shutil.copy
ou similarlocal
presumivelmente não seria alterado (embora ao imprimir o que está acontecendo, provavelmente ainda queira que seja diferenciado do que run-except-locally
tem como prefixo ...?)prefix
, cd
etc, todos precisam de perguntas semelhantes respondidas.sudo
comandos, é uma metralhadora potencialmente enorme e provavelmente deseja verificações de segurança adicionais.local
, que é outra possibilidade. Embora não seja grande, qualquer comando sudo
que funcione localmente (ou seja, um está implantando e implantando a partir do Linux) presumivelmente precisaria permanecer privilegiado localmente (por exemplo, apt
/ yum
e amigos, ajustes de firewall, etc).sudo
também (como observado acima por Jon) precisa aumentar a possibilidade de configurar vetores de configuração locais vs remotos distintos, uma vez que o usuário sudo, a senha etc. provavelmente diferem entre os dois lados.localhost
simplesmente receberia os valores apropriados. (Além disso, como uma subclasse de Context
dedicada "para executar coisas remotas localmente", ele poderia fazer outras coisas também, se necessário).@ max-arnold estava tentando fazer isso na versão alfa v2 e encontrou problemas confusos, o que era de se esperar desde então - eu ainda não tinha chegado ao caso de uso deste tíquete em particular, além de garantir run
e local
têm APIs tão semelhantes quanto possível.
No momento, o grande problema é simplesmente a natureza e a API do objeto vinculado ao contexto de uma tarefa ( c
ou ctx
ou qualquer outro nome) posarg. No momento, e novamente, isso não pretende ser definitivo, é apenas como acabou até agora:
Executor
do Invoke ou por FabExecutor
Fab 2 quando nenhum host está presente, é invoke.Context
, que tem um run
executado localmente , e não tem um local
;fabric.Connection
, cujo run
é executado remotamente e cujo local
é uma religação de run
de Invoke É necessária uma reflexão mais específica, incluindo a análise de casos de uso aqui e em tickets vinculados. Brainstorm improvisado:
patchwork
(née contrib
) e / ou invocations
(versão de Invoke do mesmo), especialmente porque informa quanto compartilhamento de código eles podem Faz. Muitas tarefas e / ou sub-rotinas nesses tipos de bases de código podem ser executadas local ou remotamente.@task
e / ou kwargs para o mesmo, onde o usuário pode declarar suas expectativas (ou seja, "Eu realmente quero ter um contexto com capacidade remota", "por favor, nunca me forneça um contexto com capacidade remota", etc)@task
/ Task
; As bases de código do pure-Invoke apenas usariam seu @task
que sempre acionaria o recebimento de um vanilla Context
, enquanto as tarefas criadas por meio da versão do Fabric teriam pelo menos a opção de receber um Connection
, se não exigir um.ctx.run()
exista_.@task
, com o entendimento de que alguém do ponto de vista de invocação de Tecido (ou semelhante a Tecido) tem a opção de dar a eles executa a tarefa Connection
vez de Context
.local
quando não existe (código do Fabric / Connection-expecting executado via Invoke)run
seja executado localmente quando, em vez disso, for dado um contexto com um run
remoto (código de chamada / expectativa de contexto executado via Fabric)Connection('localhost').run('foo')
_não use SSH_, mas em vez disso atue exatamente como Connection('localhost').local('foo')
.ssh.localhost_becomes_subprocess = True
ou qualquer outra.)Meu único caso de uso aqui no momento seria upload_template()
ser capaz de renderizar um modelo localmente.
Claro que se poderia fazer assim:
#http://matthiaseisen.com/pp/patterns/p0198/
import os
import jinja2
def render(tpl_path, context):
path, filename = os.path.split(tpl_path)
return jinja2.Environment(
loader=jinja2.FileSystemLoader(path or './')
).get_template(filename).render(context)
Mas por que não ter a opção de renderizar localmente?
O principal uso desse recurso, no meu caso, seria implantar a configuração do aplicativo em minha máquina local para teste local.
Considere que você tem um settings.py.j2
que é renderizado para o servidor de destino na implantação e lá é denominado settings.py
e contém apenas código python, sem jinja.
Agora você deseja testar localmente, mas localmente não há settings.py
ainda, porque ele precisa ser renderizado a partir de settings.py.j2
.
Portanto, seu aplicativo não pode ser iniciado e você terá que criar um settings.py
separado manualmente para o seu teste local.
Isso é muito cansativo e deveria ser mais fácil.
Por exemplo, em Ansible, eu simplesmente diria à tarefa que ela usará "conexão local" e renderizaria no host local sem tentar fazer o ssh nele.
Até que esse recurso esteja disponível no Fabric, usarei a solução colada acima, é claro, pois são apenas algumas linhas de código. Deve ser mais fácil embora imho. Eu sinto que esse é realmente o tipo de material que o tecido deveria tornar mais fácil para mim.
@fninja Eu não upload_template
ainda, mas eu definitivamente concordo que se enquadra neste espaço de problema. Indiscutivelmente, pode-se lidar com isso apenas dividindo a etapa de renderização do Jinja-wrapping e a etapa de upload de alguma string, especialmente porque a última já existe na forma de "entregar um FLO para put
". Por exemplo:
from StringIO import StringIO # too lazy to remember the newer path offhand
from somewhere.jinja_wrapper import render
from invoke import task
<strong i="9">@task</strong>
def render_settings(c):
rendered = render('settings.py.j2', {'template': 'params'})
c.put(StringIO(rendered), 'remote/path/to/settings.py')
Mas provavelmente ainda há espaço para um análogo de 1 parada ainda mais curto para upload_template
que seria um método Connection
ou uma sub-rotina tomando um argumento Connection
.
De qualquer forma, isso levanta mais questões sobre: exatamente como tratar esse tipo de coisa - por exemplo, objetos Context
invocação não têm put
/ get
. Vale a pena adicioná-los? Faz muito sentido para usuários do Fabric no contexto deste tíquete (então upload_template
ou w / e pode simplesmente chamar put
em ambos os casos), mas para usuários do Invoke puro, é um bizarro e parte inútil da API.
+1 para tornar este um recurso central
Poste cruzado de # 1637. Apenas uma ideia:
from fabric import task, local
<strong i="6">@task</strong>
<strong i="7">@local</strong>
def build(ctx):
with ctx.cd('/project/dir'):
ctx.run('build > artifact.zip')
<strong i="8">@task</strong>
def deploy(conn):
build(local(conn))
with conn.cd('/remote/path'), local(conn).cd('/project/dir'):
conn.put(remote_path='build.zip', local_path='artifact.zip')
Basicamente local()
pode atuar como decorador / gerenciador de contexto / função e transformar Connection
em Context
.
Outro caso de uso que acho que não vi mencionado: Construindo uma biblioteca de funções reutilizáveis. No meu caso, são principalmente git
comandos. Eu escrevi um dorun
excessivamente simplista que esconde as diferenças entre os parâmetros de função run
e local
(na v1); qual função é escolhida é passada como um parâmetro. Aqui está git checkout
por exemplo:
def git_checkout(branch, remote='origin', run=run):
"""Checkout a branch if necessary."""
if branch == git_current_branch(run=run):
return
elif branch in git_local_branches(run=run):
dorun('git checkout ' + branch, run=run)
else:
dorun('git checkout -t -b {0} {1}/{0}'.format(branch, remote), run=run)
def git_current_branch(run=run):
"""Get the current branch (aka HEAD)"""
output = dorun('git name-rev --name-only HEAD', run=run)
return output.strip()
def git_local_branches(run=run):
"""Get a list of local branches; assumes in repo directory."""
output = dorun('git branch --no-color', run=run)
branches = {l.strip().split(' ')[-1]
for l in output.strip().split('\n')}
return branches
Se parece com isso:
from fabric.api import run as run_remote, local as run_local
def dorun(*args, **kwargs):
"""Work around the fact that "local" and "run" are very different."""
kwargs.setdefault('run', run_remote)
run = kwargs.pop('run')
if run == run_local:
kwargs.setdefault('capture', True)
elif 'capture' in kwargs:
del kwargs['capture']
return run(*args, **kwargs)
Não tenho ideia do que acontece com sudo
e existem problemas com os quais não posso lidar facilmente, como expandir ~remoteuser
para produzir um caminho.
Comentários muito úteis
Se alguém está se perguntando "por que alguém faria isso?", A resposta é que se você tiver um pipeline de implantação, pode ser útil executar o mesmo script de implantação exato, não importa o ambiente, em vez de ter um script de configuração especial para localhost contra tudo o mais.