Fabric: Opcionalmente, evite usar ssh si va a localhost

Creado en 19 ago. 2011  ·  59Comentarios  ·  Fuente: fabric/fabric

Descripción

run () / sudo () vería inteligentemente que va a localhost y simplemente ejecutar local () en su lugar. Probablemente esto sea algo opcional.

Comentarios de Jeff en IRC:

y sí, quiero decir que siempre habrá una sobrecarga con ssh frente a tuberías rectas de forma imprevista, no creo que sea terriblemente difícil actualizar run / sudo (especialmente en el maestro ahora que se han refactorizado) para llamar / devolver local ( ) inteligentemente, no estoy seguro de querer ese comportamiento semi mágico en el núcleo (incluso si está desactivado de forma predeterminada con una opción para habilitarlo, aunque eso ayudaría) pero aun así, sería un experimento interesante. y si es tan simple como estoy pensando, honestamente, no puedo encontrar una buena razón para no hacerlo (nuevamente siempre que no sea el comportamiento predeterminado)

Enviado originalmente por Nick Welch ( mackstann ) el 2009-11-11 a las 01:39 pm EST

Relaciones

  • Duplicado por # 364: Permitir que la operación local omita la capa SSH
  • Relacionado con el n. ° 26: Implementar la función de "ejecución en seco"
Feature Network

Comentario más útil

Si alguien se pregunta "¿por qué alguien haría esto?", La respuesta es que si tiene una canalización de implementación, puede ser útil ejecutar exactamente la misma secuencia de comandos de implementación, sin importar el entorno, en lugar de tener una secuencia de comandos de configuración especial para localhost. contra todo lo demás.

Todos 59 comentarios

James Pearson (xiong.chiamiov) publicó:


Como también se mencionó en irc, normalmente no ejecuto el servidor ssh en una máquina de escritorio, por lo que en realidad no puedo usar ssh en localhost.


el 2009-11-11 a las 03:13 pm EST

Travis Swicegood ( tswicegood ) publicó:


Acabo de implementar algo similar esta noche en la forma de una nueva función fabric.operations llamada do . Mira env.run_as para ver si es igual a "local" y, al hacerlo, cambia al método local lugar del run (o sudo si sudo=True se pasa como un kwarg). También maneja el prefijo de los comandos locales con sudo en caso de que se estén ejecutando localmente.

Esta es una forma diferente de solucionar este problema que funciona sin cambiar el comportamiento de run o sudo . Estos cambios están disponibles en mi repositorio .


el 2010-01-11 a las 12:22 am EST

Morgan Goose ( goosemo ) publicó:


Realmente no veo que esto sea plausible. ¿Cuál es el punto de ejecutar como local? Uno de los requisitos de Fabric es que sshd se ejecute en la máquina, de forma remota o en bucle invertido. El otro problema es que solo cambiar local no tiene en cuenta put, get, rsync_project y otros que todavía necesitarían ssh. Tratar de implementarlos, realmente causaría más problemas, ya que ahora está en el ámbito de hacer que los fabfiles se traduzcan a bash.


el 2011-03-13 a las 11:14 pm EDT

Jeff Forcier (


Si bien tampoco estoy 100% convencido de que esta sea una gran idea, claramente es algo que varios usuarios sienten la necesidad: se ha presentado otra solicitud como # 364 con otra explicación del caso de uso.

También agregué el boleto de ejecución en seco relacionado con este, porque (supongo que si alguno de los usuarios solicitantes puede verificar esto, sería genial) el caso de uso principal de esta función es para probar / en seco- corriendo.


el 2011-06-23 a las 11:26 am EDT

Como se señaló en el n. ° 538, si alguna vez podemos normalizar completamente los tres corredores para que se puedan usar indistintamente, tendremos que asegurarnos de que el escape de shell funcione de manera consistente entre ellos. En este momento no escapamos de shell local , aunque eso es al menos en parte porque no está usando un contenedor de shell.

Si alguien se pregunta "¿por qué alguien haría esto?", La respuesta es que si tiene una canalización de implementación, puede ser útil ejecutar exactamente la misma secuencia de comandos de implementación, sin importar el entorno, en lugar de tener una secuencia de comandos de configuración especial para localhost. contra todo lo demás.

+1 para la función

+1

+10

+1

+1

Para detenerlo, puede asegurarse de tener el servidor OpenSSH en ejecución. Primero haz sudo apt-get install ssh para asegurarte de que lo tienes instalado (incluso si crees que lo tienes). Luego haga sudo service ssh start | stop | restart según sea necesario. Aprendí de este hilo .

+1

Mi caso de uso es simple: quiero usar el mismo script django-deploy para configurar instancias ec2 tanto con cloud-init a través de CloudWatch (el caso para ejecutar comandos locales) como usando el estándar fab deploy_django -H foo@bar .

+1

Esto sería realmente útil. Un caso de uso que tengo es el de usar el aprovisionador de shell vagabundo para configurar una máquina virtual particular usando fabric y sin la necesidad de ssh localhost.

+1

Me sorprendió no ver esto ya en Fabric.

FYI: La implementación de esta característica se vuelve más compleja cuando piensa en funciones de estructura como reboot() .

+1

¡Debería ser parte del núcleo ya!

+1

Tendría perfectamente sentido: desde un punto de vista abstracto, local es solo un caso especial de run , donde no hay maquinaria SSH involucrada.

Una cosa más para señalar (quizás obvia): Fabric debe ser lo suficientemente inteligente como para decidir si un run debe convertirse en local DESPUÉS de leer / etc / hosts.

Quiero decir: si tenemos

env.host = [ 'mywebserver' ]

y en / etc / hosts tenemos:

127.0.0.1 mywebserver

entonces, cualquier llamada run debería ser realmente llamada local .

Llevando este concepto un paso más allá, también deberíamos tratar run como una llamada local cuando el host remoto se resuelve en una IP que está asignada a una interfaz de red de la máquina local.
P.ej:
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

Fabric 2 usará pyinvoke / invoke, por lo que debería ser bastante fácil de hacer allí. Esperaría a Fabric 2 para una forma no pirateada de hacer esto.

: +1:

+1

: +1:

: +1: implemente esto, especialmente porque las computadoras mac no están configuradas automáticamente para tener túneles SSH configurados para el acceso remoto al servidor localhost.

+1

+1 :)

+1 por favor

: +1:

: +1:

: +1:

Estamos usando Fab para construir paquetes debian y esto agrega complejidad adicional

chicos, hola a todos
Intento crear un clon de tela con diferencia:

  • La función run () funciona de la misma manera con subprocess.popen en localhost que en ssh connect to remote host
  • Factory usa openssh o cualquier otro cliente ssh (debe modificar la configuración para esto), por lo que puede usar todo el poder de los sockets ssh
  • Factory utiliza la biblioteca gevent para la ejecución asincrónica

Puede echar un vistazo si necesita esta función
https://github.com/Friz-zy/factory

Puede que me esté perdiendo algo en esta discusión, pero esto es lo que hice para usar el mismo código con el comando fab run tanto en máquinas locales como remotas.

  1. Configuré env.use_ssh_config = True en mi fabfile.py
  2. ssh-copy-id localhost

Esto no resuelve su problema si no está ejecutando el servidor ssh en su máquina local

: +1:

+1

+1 Implemente esta función :)

+1

Podría ser muy útil para arrancar imágenes de Docker utilizando scripts de Fabric existentes. Esta función evitaría instalar un servidor SSH en el contenedor, lo que va en contra de las mejores prácticas de Docker.

+1

+1

+1

Además de la respuesta proporcionada por @AntoniosHadji , aquí están las instrucciones completas para hacer que esto funcione;

# 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

En realidad, esto se puede hacer usando la cocina . Debe cambiar todas las ejecuciones run para hacer referencia a la función cuisine.run , que se puede hacer fácilmente con una importación, y cambiar el modo a local:

from cuisine import run, mode_local

mode_local()
print run("echo Hello")

Genial @cgarciaarano

Para casos de uso simples, esto funciona para mí:

from fabric.api import run, local
# ...
# in task:
  if env.host is None or env.host == 'localhost':
    run = local

: +1:

Quiero que mi fabfile se ejecute de forma remota o local cuando ssh no sea una opción. Esto incluye envoltorios locales para get / put / exist, etc.

: +1: Tengo fabfiles que se ejecutan tanto de forma local como remota y terminé pirateando mis propias funciones de contenedor para ejecutar / local / llegar a lidiar con todas las diferencias sutiles, como la captura de salida y el manejo de errores.

¿Qué sucede si tiene una conexión ssh que realiza el reenvío dinámico de puertos y la vinculación en 127.0.0.2 (todavía técnicamente localhost) en el puerto 2223? Puedo ver cómo esto podría causar problemas, con el fin de hacer coincidir en localhost y resolver 127.0.0.1 en lugar de también admitir toda la clase 127.0.0.0/8 podría ser una buena idea de manejar.

@ blade2005 Sí, todo el rango de 127 ._._. * apunta a su localhost (excepto 127.0.0.0 y 127.255.255.255) pero cuando en realidad está apuntando a su localhost, no usará el puerto, ¿verdad?
Así que creo que podemos asumir con seguridad que 127.*.*.* == localhost y ssh pueden evitarse, pero 127.*.*.*:* apuntan a un puerto reenviado y se necesita ssh.

Honestamente, esta característica probablemente tendría más sentido como un complemento de terceros construido en tela, similar a la biblioteca de cocina. Luego, solo importaríamos funciones envueltas para ejecutar / obtener / poner / etc., que sabrían si se deben ejecutar de forma local o remota en función de una variable env. Al menos de esta manera, alguien podría comenzar esto para que todos lo usen.

Implementé algo localmente, y es mucho más trabajo que simplemente cambiar entre local / ejecutar. Debe considerar prefijos, directorios modificados, usuarios de sudo, etc.

Estuve pensando brevemente en esto en el contexto de otro ticket relacionado con 2.0, y me di cuenta de que hay más que surgen además de solo " run convierte en un reenlace de local ":

  • Cualquier tipo de tarea de modo verdaderamente mixto que utilice local y run , o cualquiera de put / get , se vuelve intrínsecamente problemático: operaciones con operaciones claramente definidas Los "extremos" 'local' y 'remoto' ahora apuntan localmente.

    • Asumiría que es un caso de uso minoritario (si es que lo es), pero aún debe resolverse, incluso si está "llamando a cualquier operación pero run o sudo aumenta DoesntMakeAnySenseError "o lo que sea.

    • put / get presumiblemente podría convertirse en shutil.copy o similar

    • local presumiblemente no se cambiaría (aunque al imprimir lo que está sucediendo, probablemente aún quiera diferenciarlo de lo que run-except-locally tiene como prefijo ...?)

    • Como se mencionó anteriormente, los diversos métodos / administradores de contexto de manipulación de contexto como prefix , cd etc., necesitan respuestas similares.

  • Aparte de eso, ejecutar localmente comandos sudo en absoluto, es una pistola potencialmente enorme y probablemente quiera controles de seguridad adicionales.

    • A menos que también se convierta en otra vinculación a local , que es otra posibilidad. Aunque no es uno grande, cualquier comando sudo que incluso funcione localmente (es decir, uno se está implementando y se está implementando desde Linux) presumiblemente necesitaría permanecer privilegiado localmente (p apt Ej., yum y amigos, retoques del firewall, etc.).

  • sudo también (como lo señaló Jon anteriormente) necesita aumentar la posibilidad de configurar distintos vectores de configuración local vs remoto, ya que es probable que el usuario sudo, la contraseña, etc. difieran entre los dos lados.

    • Aunque como estoy pensando en todo esto en el contexto de Fab 2, las anulaciones de configuración por host esperadas probablemente resolverían esa parte de las cosas al menos: el contexto localhost simplemente recibiría los valores apropiados. (Además, como una subclase dedicada "para ejecutar cosas remotas localmente" Context también podría hacer otras cosas, si fuera necesario).

@ max-arnold estaba probando esto en la versión alfa v2 y se encontró con problemas confusos, lo cual es de esperar en este punto, ya que todavía no había llegado al caso de uso de este boleto en particular, aparte de asegurar run y local tienen API tan similares como sea posible.

Por el momento, el gran problema es simplemente la naturaleza y la API del objeto vinculado al contexto de una tarea ( c o ctx o como se llame) posarg. En la actualidad, y nuevamente, esto no pretende ser definitivo, es solo cómo terminó hasta ahora:

  • de forma predeterminada, cuando se ejecuta por Executor de Invoke, o por FabExecutor de Fab 2 cuando no hay hosts presentes, es invoke.Context , que tiene un run que se ejecuta localmente y carece de local ;
  • cuando Fab 2 tiene host (s) para ejecutarse, crea un fabric.Connection , cuyo run ejecuta de forma remota, y cuyo local es un reenlace de run de Invoke

Se necesita un pensamiento más específico, incluido el análisis de casos de uso aquí y en tickets vinculados. Lluvia de ideas espontánea:

  • Una solución útil (o al menos la documentación) para esto debería existir casi definitivamente en el núcleo (según el chat anterior sobre que vive fuera del núcleo) porque:

    • es un caso de uso bastante común

    • es fácil estropear

    • se requiere implementar de manera útil versiones compatibles v2 de patchwork (née contrib ) y / o invocations (Invoke's version of same), especialmente porque informa cuánto código pueden compartir hacer. Muchas tareas y / o subrutinas en esos tipos de bases de código pueden querer ejecutarse de forma local o remota.

  • En el fondo, se trata de qué API esperar de los objetos de contexto donde es posible que la tarea no sepa con certeza "cómo" se está invocando.
  • Podría depender de cómo se genera la tarea, es decir, diferentes versiones de @task y / o kwargs a la misma, donde el usuario puede declarar sus expectativas (es decir, "Realmente quiero que se me brinde un contexto con capacidad remota", "por favor, nunca me des un contexto con capacidad remota", etc.)

    • Es posible que deseemos _requitar_ esto para evitar la ambigüedad ( ZoP # 12 )

    • Cuanto más lo pienso, más claro parece que queremos que Fabric desarrolle su propia envoltura ligera alrededor de @task / Task ; Las bases de código pure-Invoke solo usarían su @task lo que siempre provocaría que se le otorgue un Context vainilla, mientras que las tareas creadas a través de la versión de Fabric tendrían al menos la opción de recibir un Connection , si no requiere uno.

    • Una desventaja es el tipo de tarea "Puedo ser útil localmente XOR remotamente" mencionado anteriormente; una tarea que solo quiere una única opción "ejecutar comandos, por favor" y no mezcla ambos modos simultáneamente. Esto _no_ funciona bien con las soluciones "el decorador declara el tipo de contexto" porque _necesita_ 'alternar' el tipo de contexto dependiendo de quién lo llame y cómo.

    • Aunque ese es en realidad un punto completo de la API actual; esas tareas _no se preocupan_ por la subclase de contexto, _ siempre que ctx.run() exista_.

    • Entonces, aquellos que presumiblemente quieren estar decorados con la versión "Solo necesito una base, contexto vainilla" de @task , con el entendimiento de que alguien desde un punto de vista de invocación de Fabric (o similar a Fabric) tiene la opción de dar esos Tareas un Connection lugar de un Context .



      • Lo que nos lleva de vuelta a preguntarnos exactamente cómo ejecutar tareas, también conocido como pyinvoke / invoke # 170



  • Independientemente de la implementación, tenemos que asegurarnos de minimizar el potencial de pistolas con respecto a los usuarios que hacen cosas como:

    • Esperando local cuando no existe (el código de espera de conexión / tejido se ejecuta a través de Invoke)

    • Esperando que run ejecute localmente cuando a uno se le dio un contexto con un run remoto (Invocar / Código de espera de contexto ejecutado a través de Fabric)

    • Cualquier otra cosa del comentario adicional de Max aquí.

  • Como se ve en comentarios mucho más antiguos, un sub-caso de uso aquí son los usuarios que esperan que el equivalente v2 de Connection('localhost').run('foo') _no use SSH_ sino que actúe exactamente como Connection('localhost').local('foo') .

    • Supongo que en realidad no queremos hacer esto, ya que se siente como un arma de fuego desagradable para cualquiera que intente hacer comprobaciones de cordura de localhost. Simplemente se siente demasiado mágico para mí. Pero estoy abierto a argumentos, probablemente de forma voluntaria (por ejemplo, establezca una opción de configuración como ssh.localhost_becomes_subprocess = True o lo que sea).

Mi único caso de uso aquí en este momento sería upload_template() poder representar una plantilla localmente.

Por supuesto, uno podría hacerlo así:

#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)

Pero, ¿por qué no tener la opción de renderizar localmente?

El uso principal de esta función, en mi caso, sería implementar la configuración de la aplicación en mi máquina local para realizar pruebas locales.

Considere que tiene un settings.py.j2 que se procesa en el servidor de destino después de la implementación y allí se llama settings.py y contiene solo código Python, no jinja.
Ahora quiere probar localmente, pero localmente no hay settings.py todavía, porque necesita ser renderizado desde settings.py.j2 .
Por lo tanto, su aplicación no puede iniciarse y tendrá que crear un settings.py por separado manualmente para sus pruebas locales.

Esto es muy agotador y debería ser más fácil.

Por ejemplo, en Ansible simplemente le diría a la tarea que va a usar "conexión local", y se representará en el host local sin intentar ssh en él.

Hasta que esta función esté disponible en Fabric, usaré la solución pegada anteriormente, por supuesto, ya que son solo unas pocas líneas de código. Aunque debería ser más fácil. Siento que ese es realmente el tipo de cosas que la tela debería facilitarme.

@fninja No he portado upload_template todavía, pero definitivamente estoy de acuerdo en que se incluye en este espacio de problemas. Podría decirse que uno podría manejar esto simplemente dividiendo el paso de renderizado de envoltura de Jinja y el paso de carga de carga de alguna cadena, especialmente porque este último ya existe en la forma de "entregar un FLO a put ". P.ej:

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')

Pero probablemente todavía hay espacio para un análogo de 1 paso aún más corto a upload_template que sería un método Connection o una subrutina que toma un argumento Connection .

De cualquier manera, plantea más preguntas sobre cómo tratar exactamente este tipo de cosas; por ejemplo, los objetos Context invocación no tienen put / get . ¿Vale la pena agregarlos? Tiene mucho sentido para los usuarios de Fabric en el contexto de este ticket (entonces upload_template o w / e simplemente pueden llamar a put en cualquier caso), pero para los usuarios de Invoke puro, es extraño y parte inútil de la API.

+1 para hacer de esta una característica principal

Poste transversal del # 1637. Solo una idea:

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')

Básicamente, local() puede actuar como decorador / administrador de contexto / función y transformar Connection en Context .

Otro caso de uso que no creo haber visto mencionado: Creación de una biblioteca de funciones reutilizables. En mi caso, son principalmente comandos git . Escribí un dorun demasiado simplista que oculta las diferencias entre los parámetros de función run y local (en v1); qué función se elige se pasa como parámetro. Aquí hay un git checkout por ejemplo:

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 a esto:

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)

No tengo idea de lo que sucede con sudo y hay problemas que no puedo resolver fácilmente, como expandir ~remoteuser para producir una ruta.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

acdha picture acdha  ·  4Comentarios

bitprophet picture bitprophet  ·  6Comentarios

omzev picture omzev  ·  6Comentarios

jamesob picture jamesob  ·  3Comentarios

peteruhnak picture peteruhnak  ·  4Comentarios