run()/sudo() verrait intelligemment que vous allez sur localhost et exécutez simplement local() à la place. Ce serait probablement une chose facultative.
Commentaires de Jeff sur IRC :
et oui, je veux dire qu'il y aura toujours des frais généraux avec ssh vs tuyaux droits à la main, je ne pense pas qu'il serait très difficile de mettre à jour run/sudo (surtout dans master maintenant qu'ils ont été refactorisés) pour appeler/retourner local ( ) intelligemment, je ne suis pas certain que je voudrais ce comportement semi-magique dans le noyau (même s'il est désactivé par défaut avec une option pour l'activer, mais cela aiderait) mais même ainsi, ce serait une expérience intéressante. et si c'est aussi simple que je le pense, honnêtement, je ne peux pas trouver une bonne raison de ne pas le faire (encore une fois, à condition que ce ne soit pas le comportement par défaut)
Présentée à l' origine par Nick Welch ( mackstann ) sur 2009-11-11 à 13:39 EST
James Pearson (xiong.chiamiov) a posté :
Comme mentionné également sur irc, je n'exécute normalement pas de serveur ssh sur un ordinateur de bureau, je ne peux donc pas réellement ssh vers localhost.
sur 2009-11-11 à 15:13 EST
Travis Swicegood ( tswicegood ) a posté :
Je viens d'implémenter quelque chose de similaire ce soir sous la forme d'une nouvelle fonction fabric.operations appelée do
. Il regarde env.run_as
pour voir s'il est égal à "local", et ce faisant passe à la méthode local
au lieu de la méthode run
(ou sudo
si sudo=True
est passé en tant que kwarg). Il gère également le préfixe des commandes locales avec sudo
dans le cas où elles s'exécutent en local.
C'est une manière différente de contourner ce problème qui fonctionne sans changer le comportement de run
ou sudo
. Ces modifications sont disponibles dans mon référentiel .
le 2010-01-11 à 00h22 HNE
Morgan Goose ( goosemo ) a posté :
Je ne vois vraiment pas cela comme plausible. Quel est l'intérêt de faire fonctionner en tant que local. L'une des exigences de Fabric est que sshd s'exécute sur la machine, à distance ou en boucle. L'autre problème étant que seul le changement de local ne prend pas en compte put, get, rsync_project et d'autres qui auraient tous encore besoin de ssh. Essayer de les implémenter causerait vraiment plus de problèmes, car il s'agit maintenant de traduire les fabfiles en bash.
le 13/03/2011 à 23h14 HAE
Jeff Forcier ( bitprophet ) a posté :
Bien que je ne sois pas non plus convaincu à 100% que c'est une excellente idée, c'est clairement quelque chose dont un certain nombre d'utilisateurs ressentent le besoin - une autre demande a été déposée en tant que #364 avec une autre explication du cas d'utilisation.
J'ai également ajouté le ticket de tirage à sec en rapport avec celui-ci, car (je suppose - si l'un des utilisateurs demandeurs peut le vérifier, ce serait formidable) le principal cas d'utilisation de cette fonctionnalité est le test / le séchage. fonctionnement.
le 23/06/2011 à 11h26 HAE
Comme indiqué dans le #538, si nous parvenons un jour à normaliser complètement les trois coureurs afin qu'ils puissent être utilisés de manière interchangeable, nous devrons nous assurer que l'échappement du shell fonctionne de manière cohérente sur eux. Pour le moment, nous n'utilisons pas d'échappement de shell local
, bien que ce soit au moins en partie parce qu'il n'utilise pas de shell wrapper.
Si quelqu'un se demande "pourquoi quelqu'un ferait-il cela ?", la réponse est que si vous avez un pipeline de déploiement, il peut être utile d'exécuter exactement le même script de déploiement, quel que soit l'environnement, plutôt que d'avoir un script de configuration spécial pour localhost contre tout le reste.
+1 pour la fonctionnalité
+1
+10
+1
+1
Pour vous retenir, vous pouvez simplement vous assurer que le serveur OpenSSH est en cours d'exécution. Faites d'abord sudo apt-get install ssh
pour vous assurer que vous l'avez installé (même si vous pensez l'avoir). Ensuite, faites sudo service ssh start
| stop
| restart
au besoin. Appris de ce fil .
+1
Mon cas d'utilisation est simple : je souhaite utiliser le même script django-deploy pour configurer les instances ec2 à la fois avec cloud-init via CloudWatch (le cas pour exécuter des commandes locales) et en utilisant le fab deploy_django -H foo@bar
.
+1
Ce serait vraiment utile. Un cas d'utilisation que j'ai consiste à utiliser un fournisseur de shell vagrant pour configurer une machine virtuelle particulière à l'aide de Fabric et sans avoir besoin de ssh localhost.
+1
J'ai été surpris de ne pas voir cela déjà dans Fabric.
Pour info : la mise en œuvre de cette fonctionnalité devient plus complexe lorsque vous pensez à des fonctions de structure telles que reboot()
.
+1
Devrait déjà faire partie du noyau !
+1
Cela serait parfaitement logique : d'un point de vue abstrait, local
n'est qu'un cas particulier de run
, où aucune machinerie SSH n'est impliquée.
Une dernière chose à souligner (peut-être évidente) : Fabric devrait être suffisamment intelligent pour décider si un run
doit être converti en local
APRÈS avoir lu /etc/hosts.
Je veux dire : si nous avons
env.host = [ 'mywebserver' ]
et dans /etc/hosts nous avons :
127.0.0.1 mywebserver
alors, tous les appels run
devraient en fait être des appels local
.
En poussant ce concept un peu plus loin, nous devrions également traiter run
comme un appel local lorsque l'hôte distant se résout en une adresse IP qui est attribuée à une interface réseau de la machine locale.
Par exemple:
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 utilisera pyinvoke/invoke donc cela devrait être assez facile à faire là-bas. J'attendrais Fabric 2 pour un moyen non piraté de le faire.
:+1:
+1
:+1:
:+1: Veuillez implémenter ceci, d'autant plus que les ordinateurs mac ne sont pas automatiquement configurés pour avoir des tunnels SSH configurés pour l'accès à distance au serveur localhost.
+1
+1 :)
+1 s'il vous plait
:+1:
:+1:
:+1:
Nous utilisons Fab pour construire des paquets Debian et cela ajoute une complexité supplémentaire
les gars, bonjour à tous
J'essaie de créer un clone de tissu avec différence :
Vous pouvez jeter un oeil si vous avez besoin de cette fonctionnalité
https://github.com/Frizz-zy/factory
Il me manque peut-être quelque chose dans cette discussion, mais voici ce que j'ai fait pour utiliser le même code avec la commande fab run
sur les machines locales et distantes.
env.use_ssh_config = True
dans mon fabfile.pyCela ne résout pas votre problème si vous n'exécutez pas de serveur ssh sur votre machine locale
:+1:
+1
+1 Veuillez implémenter cette fonctionnalité :)
+1
Cela pourrait être très utile pour amorcer des images Docker à l'aide de scripts Fabric existants. Cette fonctionnalité éviterait d'installer un serveur SSH sur le conteneur, ce qui va à l'encontre des bonnes pratiques de Docker
+1
+1
+1
Suite à la réponse fournie par @AntoniosHadji , voici les instructions complètes pour que cela fonctionne;
# 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 fait, cela peut être fait en utilisant la cuisine . Vous devez modifier toutes les exécutions de run
pour référencer la fonction cuisine.run
, ce qui peut être fait facilement avec une importation, et changer le mode en local :
from cuisine import run, mode_local
mode_local()
print run("echo Hello")
Super @cgarciaarano
Pour les cas d'utilisation simples, cela fonctionne pour moi :
from fabric.api import run, local
# ...
# in task:
if env.host is None or env.host == 'localhost':
run = local
:+1:
Je veux que mon fabfile s'exécute à distance ou localement lorsque ssh n'est pas une option. Cela inclut les wrappers locaux pour get/put/exists, etc.
:+1: J'ai des fabfiles qui s'exécutent à la fois localement et à distance et j'ai fini par pirater mes propres fonctions de wrapper pour run/local/get afin de gérer toutes les différences subtiles telles que la capture de sortie et la gestion des erreurs.
Que faire si vous avez une connexion ssh faisant un transfert de port dynamique et une liaison sur 127.0.0.2 (toujours techniquement localhost) sur le port 2223. Je peux voir comment cela pourrait causer des problèmes, à cette fin correspondant à localhost et résolu à 127.0.0.1 plutôt qu'aussi la prise en charge de l'ensemble de la classe 127.0.0.0/8 peut être une bonne idée à gérer.
@blade2005 Oui, toute la plage 127._._.* pointe vers votre hôte local (sauf 127.0.0.0 et 127.255.255.255) mais lorsque vous pointez réellement vers votre hôte local, vous n'utiliserez pas le port, n'est-ce pas ?
Je pense donc que nous pouvons supposer en toute sécurité que 127.*.*.* == localhost
et ssh peuvent être évités, mais que 127.*.*.*:*
pointe vers un port transféré et ssh est nécessaire.
Honnêtement, cette fonctionnalité aurait probablement plus de sens en tant que plugin tiers construit sur du tissu, similaire à la bibliothèque de cuisine. Ensuite, nous importerions simplement des fonctions encapsulées pour run/get/put/etc, qui sauraient s'il faut s'exécuter localement ou à distance en fonction d'une variable d'environnement. Au moins de cette façon, quelqu'un pourrait démarrer cela pour que tout le monde puisse l'utiliser.
J'ai implémenté quelque chose localement, et c'est beaucoup plus de travail que de simplement basculer entre local/run. Vous devez tenir compte des préfixes, des répertoires modifiés, des utilisateurs sudo, etc.
J'y réfléchissais brièvement dans le contexte d'un autre ticket lié à la 2.0, et j'ai réalisé qu'il y avait autre chose que " run
devient une reliure de local
" :
local
et run
, ou l'un des put
/ get
, devient intrinsèquement problématique : des opérations avec des 'local' et 'remote' "se termine" maintenant tous les deux localement.run
ou sudo
augmente DoesntMakeAnySenseError
" ou autre.put
/ get
pourrait vraisemblablement se transformer en shutil.copy
ou similairelocal
ne serait vraisemblablement pas modifié (bien que lors de l'impression de ce qui se passe, vous voulez probablement toujours qu'il soit différencié de ce que run-except-locally
est préfixé avec...?)prefix
, cd
etc. ont tous besoin de réponses à des questions similaires.sudo
est une arme potentiellement énorme et nécessite probablement des contrôles de sécurité supplémentaires.local
, ce qui est une autre possibilité. Bien qu'elles ne soient pas importantes, toutes les commandes sudo
qui fonctionnent même localement (c'est-à-dire qu'elles sont déployées vers et à partir de Linux) devraient vraisemblablement rester privilégiées localement (par exemple, apt
/ yum
et amis, bricolage du pare-feu, etc.).sudo
(comme indiqué ci-dessus par Jon) doit également augmenter la possibilité de configurer des vecteurs de configuration locaux vs distants distincts, car l'utilisateur sudo, le mot de passe, etc. sont susceptibles de différer entre les deux côtés.localhost
recevrait simplement les valeurs appropriées. (De plus, en tant que sous-classe Context
dédiée "pour exécuter des choses distantes localement", elle pourrait également faire d'autres choses, si nécessaire).@max-arnold essayait cela dans l'alpha v2 et s'est heurté à des problèmes déroutants, ce à quoi il faut s'attendre à ce stade puisque - je n'étais pas encore arrivé au cas d'utilisation de ce ticket particulier, à part assurer run
et local
ont des API aussi similaires que possible.
Pour le moment, le gros problème est simplement la nature et l'API de l'objet lié au contexte d'une tâche ( c
ou ctx
ou quel que soit le nom qu'on lui donne) posarg. À l'heure actuelle, et encore une fois, ce n'est pas destiné à être définitif, c'est juste comment cela s'est terminé jusqu'à présent :
Executor
d'Invoke ou par FabExecutor
de Fab 2 lorsqu'aucun hôte n'est présent, c'est invoke.Context
, qui a un run
qui s'exécute localement , et il manque un local
;fabric.Connection
, dont le run
s'exécute à distance et dont le local
est une reliure du run
d'Invoke Une réflexion plus spécifique est nécessaire, notamment en examinant les cas d'utilisation ici et dans les tickets liés. Remue-méninges désinvolte :
patchwork
(née contrib
) et/ou invocations
(la version d'Invoke de la même), d'autant plus qu'elle indique combien de partage de code ils peuvent faire. De nombreuses tâches et/ou sous-routines dans ces types de bases de code peuvent vouloir s'exécuter localement ou à distance.@task
et/ou de kwargs, où l'utilisateur peut déclarer ses attentes (c'est-à-dire "Je veux vraiment qu'on me donne un contexte distant", "s'il vous plaît, ne me donnez jamais un contexte à distance", etc.)@task
/ Task
; les bases de code pure-Invoke utiliseraient simplement son @task
qui déclencherait toujours l'attribution d'un Context
vanille, tandis que les tâches créées via la version de Fabric auraient au moins la possibilité de se voir attribuer un Connection
, sinon en demander un.ctx.run()
existe_.@task
, étant entendu que quelqu'un du point de vue de l'invocation Fabric (ou Fabric-like) a la possibilité de leur donner tâches un Connection
au lieu d'un Context
.local
quand il n'existe pas (Fabric/Connection-attendant le code exécuté via Invoke)run
s'exécute localement alors qu'on a plutôt reçu un contexte avec un run
distant (Invoke/Context-attending code exécuté via Fabric)Connection('localhost').run('foo')
_n'utilise pas SSH_ mais agisse exactement comme Connection('localhost').local('foo')
.ssh.localhost_becomes_subprocess = True
ou autre.)Mon seul cas d'utilisation ici pour le moment serait que upload_template()
soit capable de rendre un modèle localement.
Bien sûr, on pourrait le faire comme ceci :
#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)
Mais pourquoi ne pas avoir une option pour rendre localement ?
L'utilisation principale de cette fonctionnalité, dans mon cas, serait de déployer la configuration de l'application sur ma machine locale pour les tests locaux.
Considérez que vous avez un settings.py.j2
qui est rendu au serveur de destination lors du déploiement et qu'il s'appelle settings.py
et ne contient que du code python, pas de jinja.
Maintenant, vous voulez tester localement, mais localement il n'y a pas encore de settings.py
, car il doit être rendu à partir de settings.py.j2
.
Votre application ne peut donc pas démarrer et vous devrez créer manuellement un settings.py
séparé pour vos tests locaux.
C'est très fatiguant, et ça devrait être plus facile.
Par exemple, dans Ansible, je dirais simplement à la tâche qu'elle va utiliser une "connexion locale", et elle sera rendue sur l'hôte local sans essayer de s'y connecter.
Jusqu'à ce que cette fonctionnalité soit disponible dans Fabric, j'utiliserai bien sûr la solution collée ci-dessus, car il ne s'agit que de quelques lignes de code. Cela devrait être plus facile à mon humble avis. J'ai l'impression que c'est vraiment le genre de tissu qui devrait me faciliter la tâche.
@fninja Je n'ai pas encore porté upload_template
lui-même mais je suis tout à fait d'accord pour dire que cela relève de cet espace problématique. On pourrait sans doute gérer cela en divisant simplement l'étape de rendu Jinja-wrapping et l'étape de téléchargement upload-some-string, d'autant plus que cette dernière existe déjà sous la forme de "donner un FLO à put
". Par exemple:
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')
Mais il y a probablement encore de la place pour un analogue 1-stop encore plus court à upload_template
qui serait soit une méthode Connection
soit un sous-programme prenant un argument Connection
.
Quoi qu'il en soit, cela soulève plus de questions sur : comment traiter exactement ce genre de chose - par exemple, les objets Context
appel uniquement n'ont pas de put
/ get
. Cela vaut-il la peine de les ajouter ? Cela a beaucoup de sens pour les utilisateurs de Fabric dans le contexte de ce ticket (alors upload_template
ou w/e peut simplement appeler put
dans les deux cas), mais pour les utilisateurs pur-Invoke, c'est un et une partie inutile de l'API.
+1 pour en faire une fonctionnalité principale
Crosspost de #1637. Juste une idée:
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')
Fondamentalement, local()
peut agir comme décorateur/gestionnaire de contexte/fonction et transformer Connection
en Context
.
Un autre cas d'utilisation que je ne pense pas avoir vu mentionné : Construire une bibliothèque de fonctions réutilisables. Dans mon cas, il s'agit principalement de commandes git
. J'ai écrit un dorun
trop simpliste qui masque les différences entre les paramètres de fonction run
et local
(sur v1) ; quelle fonction est choisie est passé en paramètre. Voici un git checkout
par exemple :
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
Cela ressemble à ceci :
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)
Je n'ai aucune idée de ce qui se passe avec sudo
et il y a des problèmes que je ne peux pas facilement résoudre, comme étendre ~remoteuser
pour produire un chemin.
Commentaire le plus utile
Si quelqu'un se demande "pourquoi quelqu'un ferait-il cela ?", la réponse est que si vous avez un pipeline de déploiement, il peut être utile d'exécuter exactement le même script de déploiement, quel que soit l'environnement, plutôt que d'avoir un script de configuration spécial pour localhost contre tout le reste.