run()/sudo() 会智能地看到您将访问 localhost 并改为运行 local() 。 这可能是一个可选的事情。
Jeff 对 IRC 的评论:
是的,我的意思是 ssh 和直管总是会产生开销,我认为更新 run/sudo(尤其是在 master 中,因为它们已经被重构)来调用/返回本地( ) 聪明地,我并不肯定我希望在核心中具有这种半神奇的行为(即使默认情况下关闭它并选择启用它,这会有所帮助)但即便如此,这仍将是一个有趣的实验。 如果它像我想的那么简单,老实说,我想不出一个很好的理由不这样做(再次假设它不是默认行为)
最初由尼克·韦尔奇(提交mackstann )上2009-11-11在下午1时39 EST
James Pearson (xiong.chiamiov) 发布:
正如在 irc 上提到的,我通常不会在台式机上运行 ssh 服务器,所以我实际上无法 ssh 到本地主机。
就在2009-11-11下午3时13 EST
Travis Swicegood ( tswicegood ) 发布:
我刚刚在今晚以一个名为do
的新 fabric.operations 函数的形式实现了类似的东西。 它查看env.run_as
以查看它是否等于“local”,然后切换到local
方法而不是run
(或sudo
如果sudo=True
作为 kwarg 传入)。 如果本地命令在本地运行,它还会处理带有sudo
前缀的本地命令。
这是解决这个问题的一种不同方式,它可以在不改变run
或sudo
。 这些更改在我的存储库中可用。
在2010-01-11 12:22在
摩根鹅( goosemo ) 发布:
我真的不认为这是合理的。 以本地身份运行有什么意义。 Fabric 的要求之一是在机器上运行 sshd,远程或环回。 另一个问题是,仅更改 local 并没有考虑到 put、get、rsync_project 和其他仍然需要 ssh 的内容。 试图实现这些,只会导致更多的问题,因为它现在处于将 fabfiles 转换为 bash 的领域。
于2011-03-13 11:14pm EDT
Jeff Forcier ( bitprophet ) 发布:
虽然我也不是 100% 相信这是一个好主意,但显然许多用户认为需要它 - 另一个请求已提交为 #364,并附有用例的另一个解释。
我还添加了与此相关的试运行票,因为(我假设 - 如果任何提出请求的用户可以验证这一点,那就太好了)此功能的主要用例是用于测试/试运行 -跑步。
于2011-06-23于11:26am EDT
如#538 中所述,如果我们能够完全标准化三个运行器,以便它们可以互换使用,我们需要确保外壳转义在它们之间一致地工作。 现在我们没有 shell 转义local
,尽管这至少部分是因为它没有使用 shell 包装器。
如果有人想知道“为什么会有人这样做?”,答案是,如果您有部署管道,那么运行完全相同的部署脚本会很有帮助,无论在哪种环境中,而不是为 localhost 使用特殊的安装脚本与其他一切。
+1 功能
+1
+10
+1
+1
为了阻止您,您只需确保 OpenSSH 服务器正在运行。 首先执行sudo apt-get install ssh
以确保您已安装它(即使您认为已安装)。 然后做sudo service ssh start
| stop
| restart
根据需要。 从这个线程中学到的。
+1
我的用例很简单:我想使用相同的django-deploy 脚本通过 CloudWatch 使用cloud-init (运行本地命令的情况)和使用常规fab deploy_django -H foo@bar
配置 ec2 实例。
+1
这将非常有用。 我的一个用例是使用 vagrant shell 配置器来配置特定的虚拟机,而无需 ssh localhost。
+1
我很惊讶没有在 Fabric 中看到这个。
仅供参考:当您考虑像reboot()
这样的结构函数时,此功能的实现会变得更加复杂。
+1
应该已经是核心的一部分了!
+1
这完全有道理:从抽象的角度来看, local
只是run
一个特例,其中不涉及 SSH 机制。
还有一件事要指出(也许很明显):Fabric 应该足够聪明,可以决定是否在读取 /etc/hosts 后将run
转换为local
。
我的意思是:如果我们有
env.host = [ 'mywebserver' ]
在 /etc/hosts 我们有:
127.0.0.1 mywebserver
那么,任何run
调用实际上应该是local
调用。
将这个概念更进一步,当远程主机解析为分配给本地机器的网络接口的 IP 时,我们还应该将run
视为本地调用。
例如:
工厂文件:
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 将使用 pyinvoke/invoke,所以这应该很容易在那里完成。 我会等待 Fabric 2 以一种非黑客的方式来做到这一点。
:+1:
+1
:+1:
:+1: 请执行此操作,尤其是因为 mac 计算机不会自动设置为配置 SSH 隧道以远程访问本地主机服务器。
+1
+1 :)
+1 请
:+1:
:+1:
:+1:
我们正在使用 Fab 来构建 debian 包,这增加了额外的复杂性
伙计们,大家好
我尝试创建不同的织物克隆:
需要这个功能的可以看看
https://github.com/Friz-zy/factory
在这个讨论中我可能遗漏了一些东西,但这是我在本地主机和远程机器上使用相同的代码与 fab run
命令所做的。
env.use_ssh_config = True
如果您没有在本地机器上运行 ssh 服务器,这并不能解决您的问题
:+1:
+1
+1 请实现此功能:)
+1
使用现有的 Fabric 脚本引导 Docker 镜像可能非常有用。 此功能将避免在容器上安装 SSH 服务器,这违背了Docker 最佳实践
+1
+1
+1
除了@AntoniosHadji提供的答案
# 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
实际上,这可以使用food来完成。 您需要将所有run
执行更改为引用cuisine.run
函数,这可以通过导入轻松完成,并将模式更改为本地:
from cuisine import run, mode_local
mode_local()
print run("echo Hello")
伟大的@cgarciaarano
对于简单的用例,这对我有用:
from fabric.api import run, local
# ...
# in task:
if env.host is None or env.host == 'localhost':
run = local
:+1:
当 ssh 不是一个选项时,我希望我的 fabfile 远程或本地运行。 这包括用于 get/put/exists 等的本地包装器。
:+1: 我有在本地和远程运行的 fabfiles,我最终破解了我自己的 run/local/get 包装函数来处理所有细微的差异,例如输出捕获和错误处理。
如果您有一个 ssh 连接在 2223 端口上的 127.0.0.2(技术上仍然是本地主机)上执行动态端口转发和绑定怎么办。我可以看到这会如何导致问题,为此在本地主机上匹配并解析为 127.0.0.1 而不是同时支持整个 127.0.0.0/8 类可能是一个好主意。
@blade2005 是的,整个 127._._.* 范围都指向您的本地主机(127.0.0.0 和 127.255.255.255 除外),但是当您实际指向本地主机时,您不会使用端口,对吗?
所以我相信我们可以安全地假设127.*.*.* == localhost
和 ssh 可以避免,但127.*.*.*:*
指向转发端口并且需要 ssh。
老实说,这个功能可能更有意义,因为它是一个基于结构的 3rd 方插件,类似于美食库。 然后我们只需为 run/get/put/etc 导入包装函数,这些函数将根据环境变量知道是在本地还是远程运行。 至少这样,有人可以让每个人都开始使用它。
我在本地实现了一些东西,它比在本地/运行之间切换要多得多。 您必须考虑前缀、更改的目录、sudo 用户等。
在另一个与 2.0 相关的票证的上下文中简要地考虑了这一点,并意识到除了“ run
成为local
的重新绑定”之外还有更多:
local
和run
或put
/ get
任何一种的真正混合模式任务,都会变得固有问题:具有明确定义的操作'local' 和 'remote' “ends” 现在都指向本地。run
或sudo
引发DoesntMakeAnySenseError
" 或其他什么。put
/ get
大概可以变成shutil.copy
或类似的local
大概不会改变(尽管在打印正在发生的事情时,可能仍然希望它与run-except-locally
以...为前缀的内容区分开来?)prefix
、 cd
等都需要回答类似的问题。sudo
命令,是一个潜在的巨大的footgun,可能需要额外的安全检查。local
另一个绑定,这是另一种可能性。 虽然不是很大,但任何在本地工作的sudo
命令(即部署到 Linux 和从 Linux 部署)大概都需要在本地保持特权(例如apt
/ yum
和朋友,防火墙修补等)。sudo
也(如 Jon 上面提到的)需要增加配置不同的 local-vs-remote 配置向量的可能性,因为 sudo 用户、密码等可能在双方之间有所不同。localhost
上下文将简单地传递适当的值。 (另外,作为专用的“用于在本地运行远程事物”的Context
子类,如果需要,它也可以做其他事情)。@max-arnold 正在 v2 alpha 中尝试这个并遇到了令人困惑的问题,这是可以预料的,因为 - 除了确保run
之外,我还没有了解这个特定票证的用例和local
具有尽可能相似的 API。
目前,最大的问题只是绑定到任务上下文( c
或ctx
或任何名称)posarg 的对象的性质和 API。 目前,再一次,这不是最终的,它只是到目前为止的结果:
Executor
或由 Fab 2 的FabExecutor
在没有主机存在时执行时,它是invoke.Context
,它有一个run
在本地运行, 并且缺少local
;fabric.Connection
,其run
远程运行,其local
是 Invoke 的run
的重新绑定需要更具体的思考,包括查看此处和链接票证中的用例。 临时头脑风暴:
patchwork
(née contrib
) 和/或invocations
(Invoke 的版本)的 v2 兼容版本,特别是因为它告知他们可以共享多少代码做。 这类代码库中的许多任务和/或子程序可能希望在本地或远程运行。@task
和/或 kwargs 相同,用户可以在其中声明他们的期望(即“我真的想获得一个远程上下文”, “请永远不要给我一个远程上下文”等)@task
/ Task
自己的轻量级包装器; pure-Invoke 代码库只会使用它的@task
,它总是会触发给一个 vanilla Context
,而通过 Fabric 版本创建的任务至少可以选择给一个Connection
,如果不需要的话。ctx.run()
_。@task
的“我只需要一个基本的、普通的上下文”版本来装饰,并理解从 Fabric(或类似 Fabric)调用的角度来看,有人可以选择给那些任务Connection
而不是Context
。local
不存在时期望它(通过 Invoke 运行的结构/连接期望代码)run
在本地运行,而不是给一个带有远程run
上下文(通过 Fabric 运行的调用/上下文预期代码)Connection('localhost').run('foo')
_不使用 SSH_,而是完全像Connection('localhost').local('foo')
。ssh.localhost_becomes_subprocess = True
或其他。)我目前唯一的用例是upload_template()
能够在本地呈现模板。
当然可以这样做:
#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)
但是为什么没有在本地渲染的选项呢?
就我而言,此功能的主要用途是将应用程序配置部署到我的本地机器以进行本地测试。
考虑到您有一个settings.py.j2
在部署时被渲染到目标服务器,它被命名为settings.py
并且只包含 python 代码,没有 jinja。
现在你想在本地测试,但本地还没有settings.py
,因为它需要从settings.py.j2
渲染。
因此您的应用程序无法启动,您必须手动创建一个单独的settings.py
用于本地测试。
这很累人,应该更容易。
例如,在 Ansible 中,我只是告诉任务它将使用“本地连接”,它会在本地主机上呈现,而不会尝试通过 ssh 连接到它。
在 Fabric 中提供此功能之前,我将使用上面粘贴的解决方案,当然,因为它只是几行代码。 不过恕我直言,它应该更容易。 我觉得这真的是面料应该让我轻松的那种东西。
@fninja我还没有移植upload_template
本身,但我绝对同意它属于这个问题空间。 可以说,可以通过将 Jinja 包装渲染步骤和上传一些字符串上传步骤分开来处理这个问题,尤其是因为后者已经以“将 FLO 交给put
”的形式存在。 例如:
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')
但是可能仍然有一个更短的 1-stop 模拟upload_template
,它可以是Connection
方法或采用Connection
参数的子程序。
无论哪种方式,它都会引发更多问题:究竟如何处理这类事情 - 例如,仅调用Context
对象没有put
/ get
。 值得添加它们吗? 在这张票的上下文中,对于 Fabric 用户来说很有意义(然后upload_template
或 w/e 在任何一种情况下都可以简单地调用put
),但对于纯 Invoke 用户来说,这很奇怪和 API 的无用部分。
+1 使其成为核心功能
来自 #1637 的交叉点。 只是一个想法:
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')
基本上local()
可以充当装饰器/上下文管理器/函数并将Connection
为Context
。
我认为我没有看到提到的另一个用例:构建可重用函数库。 就我而言,它主要是git
命令。 我写了一个过于简单的dorun
隐藏了run
和local
函数参数之间的差异(在 v1 上); 选择哪个函数作为参数传递。 例如,这是一个git checkout
:
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
它看起来像这样:
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)
我不知道sudo
会发生什么,并且有些问题我无法轻松处理,例如扩展~remoteuser
以生成路径。
最有用的评论
如果有人想知道“为什么会有人这样做?”,答案是,如果您有部署管道,那么运行完全相同的部署脚本会很有帮助,无论在哪种环境中,而不是为 localhost 使用特殊的安装脚本与其他一切。