Fabric: v2 兼容的“角色”或类似的

创建于 2017-04-22  ·  7评论  ·  资料来源: fabric/fabric

概要

在撰写本文时,v2 分支有一个Group类,该类应该能够作为以前称为“角色”的单位,也就是“一群主机来做事”。

但是,目前还没有组织或标记Group对象的特定方法; 对于想要推出自己的特定创建方式的高级用户的纯 API 用例来说,它已经“完成”了,但对于面向 CLI 的用户或想要构建框架的中级用户来说,它缺乏任何东西。

换句话说,除非您纯粹使用 API,否则如果 CLI 或任务调用位无法找到 Group 对象,那么将 Group 对象放在某处是没有用的!

背景

在 v1 中,角色实际上是一个单一的平面命名空间,将简单的字符串标签映射到 v2 中的组,并且可以在运行时在 CLI 上选择它们 ( fab --roles=web,db ) 和/或注册为任务的默认目标 ( @task('db') \n def migrate(): ),很像主机。

用户在env.roledefs定义了它们,一个简单的字典; 任何中级到高级功能都围绕着修改它,通常是在运行时(通过任务前或子程序),有时是在模块加载时。

特定用例/需求/子功能

  • 用于系统中其他任何地方的使用/引用的基本、幼稚的映射:输入名称,取回Group s 和/或Connection s 的一些可迭代对象。

    • 别名通常希望与之相伴,例如Lexicon而不是dict

    • 甚至更深层次的构造,例如“捆绑”,例如,您有名为dbweblb直接映射,然后是名为prod的第二层名称Lexicon 。 可能还有其他地图子类也已经这样做了。

    • 另外/或者,像通配符或其他字符串语法之类的东西,尽管我个人更喜欢利用 Python 不是“字符串类型”的事实......

  • 有用的“反向映射”,以便您可以识别给定连接所属的组。

    • 有问题:因为目前没有全局共享状态,所以对这个问题的天真答案 - 使用身份 - 失败了,因为您可以在技术上创建多个相同的 Connection 对象。

    • 特别是因为如果您只给它速记主机字符串,Group 可以代表您隐式创建它们,尽管这只是一个方便的选择。

    • 然而,鉴于没有全局状态的约束,我看不到使用相等测试的明显问题,所以这应该是可行的,例如,即使cxn是一个不同的对象, if cxn in group也会起作用来自group内的相等成员。

    • 唯一想到的是,是否存在从 Connection 到 Group(必须是组,复数)的强大的、有状态的链接持有它,而不是反之亦然,但我看不出有什么好的理由。

  • 与前一个密切相关:能够检查/显示“当前正在运行的角色”是什么(人们在 v1 中长期以来一直想要的东西,由于其设计而并非微不足道)

    • 主要问题是,这实际上是两个半不同的问题:“一般来说,当前的主机部分是什么角色”(基本上,反向查找的先前用例)以及“什么角色是专门要求对抗的执行机器”。

    • 换句话说,给定属于角色 A、B 和 C 的主机“foo”:在上下文为“foo”的给定任务中,但由于“对角色 A 执行”的请求而运行,是正在寻找的用户答案是“A、B 和 C”(角色 'foo' 在整体中​​)还是只是“A”(当前正在执行的角色)?

    • 这真的感觉像是两个不同的 API 调用,即使我记得的功能请求将两者混为一谈。

  • CLI 上的目标选择,全局和/或每个任务

    • Invoke 的 CLI 系统的扩展以解释“所有任务都在其定义之上的标志”可能对此有用或需要。 事实上,它牢牢地落入了 pyinvoke/invoke#205 领域,所以它的优先级比现在更高(相当高。)

  • 同上任务级别的默认值

    • 尽管任务级目标默认值确实希望是以下任何一个:连接、连接、组 obj、组 obj 或评估 _to_ 组 obj 的名称(最后一个是与此票证直接相关的唯一内容,可以说)

  • 同上集合级别的默认值(v2 中的新功能!)

    • 即“$submodule 中的所有任务默认针对db角色运行”

    • 与前一点相同的交易 - 此默认值希望允许多个不同的值,而不仅仅是字符串键。

  • 通过 OO 方法实现的任何其他新的和令人兴奋的东西真的想与它一起使用吗? 请记住,重点应该放在构建块和支持高级用户上,而不是像 Chef 或 Ansible 那样完全重新发明系统。

实施思路/关注点

  • 如果我们使用配置系统作为主要的存储向量,值“希望”成为原语,以便它们可以存储在 yaml、json 等中,但这是一堆以“将所有组/连接 kwargs 存储在一个大的 ol”中结尾的蠕虫list-o-dicts”等。
  • 如果我们期望定义主要在 Python 中,我们可以简单地说“实例化组对象”,然后我们可以选择将该数据合并到配置系统中或以某种方式使其独立。

    • 我想我更喜欢后者,因为把所有东西都塞进嵌套的配置 dicts 感觉就像它会导致坏消息。

  • 像别名和捆绑这样的更深层次的结构增加了复杂性和排序问题(即想象一个简单的别名设置,其中 key1 的值是一个组,但 key2 的值​​是 key1;现在你必须爬行两次结构来解决或检查 key2)

    • 但是,如果我们主要采用“在 Python 中执行”的方法,它就会变得很像配置系统的 API,您可以从声明性结构开始,但在初始设置之后通过方法调用启用更多功能。 我不觉得这很可怕吗? 编辑:我认为这正是 Lexicon 的工作方式。

  • 无论格式如何,我们都必须弄清楚高级用户希望如何从外部来源或类似来源即时生成它; 这加上别名等问题,意味着我们可能不希望将其存储在“存储”在某处的幼稚结构中,而是作为某个对象或被调用以生成它的对象上的 API。

    • 我怀疑我们可能希望从角色/组的选择“向下”工作,达到最高级别的 API 是“将用户提供的内容转换为可操作的目标单元”,因为最高级的用户必然想要完全控制该 API 调用的实现。 然后我们可以像往常一样提供一个有用的常见案例,但明确标记为“只是一种方法”。

    • @RedKrieg有一个很好的想法,我们有@group就像@task ,这些函数不是可执行的工作单元,而是产生 Group 对象。

    • 这种方法本身重用了任务层次结构(集合),这是实用的(为什么要重新发明轮子)和优雅的(因为在现实世界中,角色/组定义经常与使用它们的任务非常紧密地映射!)



      • 即使您的组不映射到您的任务,它也能很好地工作,因为您可以简单地在根集合级别编写定义。 十分简单。



    • 我不清楚这是否最好从每个函数返回一个组,或者我们是否希望能够产生多个组(或连接),或者最好不要将它作为装饰函数而只是作为 API 调用集合(例如集合级配置的存储方式)。

    • 例如,组/角色数据是动态的并且在 Fabric 之外的用例仍然需要在这里解决(这就是为什么我之前指出我们必须首先确定这个空间的最高级别的 API;然后我们需要看看它是如何网格化的有了这个中级想法。)

Feature

最有用的评论

嗨,我不知道多年后这个软件发生了什么,但我真的很想念[email protected]的“角色”概念,尤其是在运行$ fab -R dev

所有7条评论

来自邮件列表:

我们实现了我们自己的内部 REST API,它根据正在部署的项目动态填充 env.roledefs,并且严重依赖于不将主机字符串嵌入到项目的 fabfile 中或在 CLI 中指定它们。

我们的用例是:

  1. 无环境代码库https://12factor.net/config。 环境(角色)及其各自的主机字符串存储在集中式数据库中。 每个 fabfile.py 都有这样的东西(它在导入文件时填充 env.roledefs):
EnvironmentDatabaseAPIClient(
    'https://rest.api.url/schema/',
    env.service_name,
).apply_env()
  1. 服务器环境的数量 - 多个测试环境(其中一些是私有的,一些是公共的)和多个生产环境(针对不同的客户端)。 每个环境由一台或多台主机组成,并映射到结构角色。

  2. 每个服务(上例中的env.service_name )都有不同的环境集。

  3. 我们还有元角色(角色组)。 它们以group-为前缀: group-productiongroup-testgroup-externalgroup-internalgroup-all 。 这允许我们部署到多个服务器角色而无需一一指定它们,例如group-all部署到所有角色,包括生产和测试。

  4. 我们有特殊的结构任务来打印有关角色组、角色和主机的信息。

  5. 我们还严重依赖将主机字符串反向映射回角色名称(每个 service_name 的主机字符串都是唯一的)。 这用于部署日志记录和通知。 基本上,我们将服务部署记录到每个主机,并在服务已部署到角色中的所有主机时发送 Slack 通知。 EnvironmentDatabaseAPI 服务器对此负责(它保留日志和部署状态)。 这是通过使用装饰器装饰结构任务来完成的,该装饰器将env.hostenv.portenv.service_name (加上提交信息)提交回 API 服务器。

  6. 我们计划在未来添加部署身份验证,也很可能从服务器中提取更多env变量以使其在任务上下文中可用。

谢谢@max-arnold! 我也从我过去的用例中认出了其中的许多。 特别是反向映射位,我记得在 v1 中出现过几次,所以我将它添加到列表中。

为了让 Fabric v2 对我有用,我需要一种方法来告诉fab执行任务的主机集。

以前我定义了角色,然后运行fab -R ... 。 (实际上,角色是使用 IP 地址范围以编程方式定义的,但这不是必需的,YAML 文件中的静态列表就可以了。)

我在 Fabric v2 中找不到等效项,而且我也无法使用以下方法模拟此功能:

  • fabric.yaml配置文件包含
active_hostset: null
hostsets:
  myhostset:
  - ...
  • active_hostset = config["hostsets"][config["active_hostset"]]fabfile.py
  • env INVOKE_ACTIVE_HOSTSET=myhostset fab ...

我得到的不是预期的主机列表KeyError: 'active_hostset'

我们为 fabric v1 中的每个环境将不同的主机集映射到每个角色,并且通过运行role.environment:staging任务来指定环境来设置环境。 因此,此任务会影响以下任务使用的主机。

在 v2 中,我们尝试使用自定义任务,但问题是Executor.expand_calls在我们的role.environment任务运行之前运行,因此以下任务都不知道环境以动态构建其主机列表。

使Executor.expand_calls成为生成器允许任务执行影响以后的任务执行。 所以我上面的例子有效,我们有一个自定义的Task需要知道它的环境才能正确地将角色扩展到主机。 例如fab role.environment dev deploy.app - role.environment任务现在在deploy.app扩展之前运行,因此deploy.app知道环境并且可以配置它的主机,然后扩展为正确的任务集。

我在我的叉子中制作了这个原型:
https://github.com/pyinvoke/invoke/compare/master...rectalogic :expand-generator
https://github.com/fabric/fabric/compare/master...rectalogic :expand-generator

嗨,我不知道多年后这个软件发生了什么,但我真的很想念[email protected]的“角色”概念,尤其是在运行$ fab -R dev

我们还使用角色来表示不同环境中的同一组操作。 也许将命名角色和命名环境的概念分开会很有用? 就像在开发环境中的 Web 角色一样。

此页面是否有帮助?
0 / 5 - 0 等级