在创建适用于linux和macOS的剧本时,我遇到两个问题。
我的剧本中有很多类似的步骤:
- name: install something
package:
name: [something_1, something_2, ...]
state: present
become: yes
它对apt和yum都很好用,但是当我尝试在macOS上运行它时,自制软件会抱怨:
Running Homebrew as root is extremely dangerous and no longer supported.
我在许多地方都找不到解决此问题的优雅方法。复制所有任务,并使用 when 子句对我来说不堪重负。也许我可以根据分发情况使用将变为_user变量设置为root / local_user,但这也有很多更改。
第二个问题是纯头公式(只能用--head标志安装的自制软件包)。如果需要使用此标志安装something_2怎么办?同样,我可以复制任务并将 package 模块更改为 homebrew ,但这是很多样板。
有帮助吗?
答案 0 :(得分:2)
如果您希望一组任务足够灵活以适应多个Linux 软件包管理器和macOS brew,选择是更多逻辑还是更多 重复。
这三种模式应该会有所帮助。他们仍然有重复和 样板代码,但这就是我们与Ansible合作的领域 用于跨平台。
become: yes
(root)when
所需的特定于平台的处理
--head
的{{1}},或brew
设置了PPA等apt
,brew install ncurses
和apt install libncurses5-dev
都是同一个库。dnf install ncurses-devel
(root)对于Linux主机,切换到root进行安装是预期的行为。
对于macOS a la Homebrew,以root身份安装不是很好。因此,使用become: yes
时需要become: yes
(true
),否则需要brew
(become: no
)(对于
Linux)。
在您的示例中,false
指令嵌套在每个任务(“步骤”)中。至
防止重复,在执行任务之前以更高的词汇范围调用become
开始。随后的任务将继承become
的状态,即
根据条件表达式进行设置。
不幸的是,根剧本范围中become
的变量将是
未定义并在运行第一个任务之前引发错误:
become
要解决此问题,我们可以将任务存储在另一个文件中并import,或者
将任务包装在block中。这些模式都可以提供
有机会及时用我们的自定义变量值声明# playbook.yml
- name: Demo
hosts: localhost
connection: local
# This works
become: True
# This doesn't - the variable is undefined
become: "{{ False if ansible_pkg_mgr == 'brew' else True }}"
# Nor does this - also undefined
become: "{{ False if ansible_os_family == 'Darwin' else True }}"
tasks:
# ...
捡起来的任务:
become
现在# playbook.yml
---
- name: Demo
hosts: localhost
connection: local
vars:
# This variable gives us a boolean for deciding whether or not to become
# root. It cascades down to any subsequent tasks unless overwritten.
should_be_root: "{{ true if ansible_pkg_mgr != 'brew' else false }}"
# It could also be based on the OS type, but since brew is the main cause
# it's probably better this way.
# should_be_root: "{{ False if ansible_os_family == 'Darwin' else True }}"
tasks:
# Import the tasks from another file, which gives us a chance to pass along
# a `become` context with our variable:
- import_tasks: test_tasks.yml
become: "{{ should_be_root }}"
# Wrapping the tasks in a block will also work:
- block:
- name: ncurses is present
package:
name: [libncurses5-dev, libncursesw5-dev]
state: present
- name: cmatrix is present
package:
name: cmatrix
state: present
become: "{{ should_be_root }}"
有一个逻辑检查,而brew
一个指令
(取决于上面使用的任务模式)。所有任务将以
超级用户,除非正在使用的程序包管理器为before
。
brew
Package Module十分方便,但是非常有限。通过
本身仅适用于理想情况;意思是,一个没有
需要基础软件包管理器的任何特殊处理或标志。所有
它可以通过传递要安装的软件包的文字字符串,when
和
可选参数,用于强制使用特定的程序包管理器可执行文件。
下面是一个示例,该示例安装了state
并完成了很短的任务,并且仅
与wget
一起安装时,处理ffmpeg
的特殊情况的详细信息:
brew
上面的播放将针对# playbook.yml
# ...
tasks:
# wget is the same among package managers, nothing to see here
- name: wget is present
when: ansible_pkg_mgr != 'brew'
package:
name: wget
state: present
# This will only run on hosts that do not use `brew`, like linux
- name: ffmpeg is present
when: ansible_pkg_mgr != 'brew'
package:
name: ffmpeg
state: present
# This will only run on hosts that use `brew`, i.e. macOS
- name: ffmpeg is present (brew)
when: ansible_pkg_mgr == 'brew'
homebrew:
name: ffmpeg
# head flag
state: head
# --with-chromaprint --with-fdk-aac --with-etc-etc
install_options: with-chromaprint, with-fdk-aac, with-etc-etc
在Linux机器上产生以下输出:
ffmpeg
这不是您的问题的一部分,但很可能会在接下来出现。
Package Module文档还提到:
软件包名称也随软件包管理器而不同;该模块不会“翻译” 他们每个发行版。例如libyaml-dev,libyaml-devel。
因此,我们可以自行处理相同软件使用不同软件的情况 软件包管理器平台之间的名称。这很常见。
有多种模式,例如:
他们都不是很愉快。这是使用role的方法。角色做 涉及更多样板和目录杂耍,但作为交换,它们提供了 模块化和局部变量环境。当一组任务中的一个角色 需要更多的努力才能正确,它不会最终污染其他任务 设置。
TASK [youtube-dl : ffmpeg is present] ******************************************
ok: [localhost]
TASK [youtube-dl : ffmpeg is present (brew)] ***********************************
skipping: [localhost]
# playbook.yml
---
- name: Demo
hosts: localhost
connection: local
roles:
- cmatrix
# roles/cmatrix/defaults/main.yml
---
ncurses:
default:
- ncurses
# Important: these keys need to exactly match the name of package managers for
# our logic to hold up
apt:
- libncurses5-dev
- libncursesw5-dev
brew:
- pkg-config
- ncurses
# roles/cmatrix/tasks/main.yml
---
- name: cmatix and its dependencies are present
become: "{{ should_be_root }}"
block:
- name: ncurses is present
package:
name: '{{ item }}'
state: latest
loop: "{{ ncurses[ansible_pkg_mgr] | default(ncurses['default']) }}"
- name: cmatrix is present
when: ansible_pkg_mgr != 'brew'
package:
name: cmatrix
state: present
的任务寻找一组要循环输入的项目,由
相应的程序包管理器。如果未定义正在使用的程序包管理器
在变量对象中,使用Jinja默认过滤器来引用
我们设置的ncurses
值。
使用此模式,添加对另一个程序包管理器或其他程序的支持 依赖关系仅涉及更新变量对象:
default
这是一个涵盖所有三个方面的完整示例。该剧本有两个作用
每个变量都基于单个变量使用正确的# roles/cmatrix/defaults/main.yml
---
ncurses:
default:
- ncurses
apt:
- libncurses5-dev
- libncursesw5-dev
# add a new dependency for Debian
- imaginarycurses-dep
brew:
- pkg-config
- ncurses
# add support for Fedora
dnf:
- ncurses-devel
值。它也是
与become
和cmatrix
一起安装时的特殊情况
ffmpeg
,并在程序包管理器之间处理ncurses的备用名称。
brew
# playbook.yml
---
- name: Demo
hosts: localhost
connection: local
vars:
should_be_root: "{{ true if ansible_pkg_mgr != 'brew' else false }}"
roles:
- cmatrix
- youtube-dl
# roles/cmatrix/defaults/main.yml
ncurses:
default:
- ncurses
apt:
- libncurses5-dev
- libncursesw5-dev
brew:
- pkg-config
- ncurses
dnf:
- ncurses-devel
# roles/cmatrix/tasks/main.yml
---
- name: cmatrix and dependencies are present
# A var from above, in the playbook
become: "{{ should_be_root }}"
block:
- name: ncurses is present
package:
name: '{{ item }}'
state: latest
# Get an array of the correct package names to install from the map in our
# default variables file
loop: "{{ ncurses[ansible_pkg_mgr] | default(ncurses['default']) }}"
# Install as usual if this is not a brew system
- name: cmatrix is present
when: ansible_pkg_mgr != 'brew'
package:
name: cmatrix
state: present
# If it is a brew system, use this instead
- name: cmatrix is present (brew)
when: ansible_pkg_mgr == 'brew'
homebrew:
name: cmatrix
state: head
install_options: with-some-option
Ubuntu的结果:
# roles/youtube-dl/tasks/main.yml
---
- name: youtube-dl and dependencies are present
become: "{{ should_be_root }}"
block:
- name: ffmpeg is present
when: ansible_pkg_mgr != 'brew'
package:
name: ffmpeg
state: latest
- name: ffmpeg is present (brew)
when: ansible_pkg_mgr == 'brew'
homebrew:
name: ffmpeg
state: head
install_options: with-chromaprint, with-fdk-aac, with-etc-etc
- name: atomicparsley is present
package:
name: atomicparsley
state: latest
- name: youtube-dl is present
package:
name: youtube-dl
state: latest