我有以下 Ansible 剧本:
- hosts: <host>
become: true
vars:
dest_dir: <dir>
roles:
- {role: '<role>', tags: '<tag>'}
被调用的
<role>
由单个任务文件组成:
- name: Copy and check file.
block:
- name: Copy
ansible.builtin.copy:
src: <source file>
dest: "{{dest_dir}}"
mode: 0666
owner: root
group: root
backup: true
register: copy_result
- name: Print result
ansible.builtin.debug:
var: copy_result
- name: Validate
ansible.builtin.shell: <script> 1
when: copy_result is changed
rescue:
- name: Revert
ansible.builtin.copy:
remote_src: true
src: "{{copy_result.backup_file}}"
dest: "{{dest_dir}}"
when: copy_result.backup_file is defined
被调用的
<script>
是一个虚拟的,只是以与提供的参数匹配的退出代码退出:
exit $1
(此代码基于 Ansible 常见问题解答 https://docs.ansible.com/ansible/devel/reference_appendices/faq.html#the-validate-option-is-not-enough-for-my-needs -我做什么。)
如果我在目标文件已经存在但与源文件内容不同时运行此命令,Ansible 会正确地将源文件复制到目标,并且我希望“验证”节会导致“救援”块,然后被调用,这应该“回滚”文件副本。 然而,“救援”块没有被调用;相反,剧本会从“验证”节中中止,并显示以下消息:
TASK [<task> : Validate] ******************************************************
fatal: [<host>]: FAILED! => {"changed": true, "cmd": "<script> 1", "delta": "0:00:00.010525", "end": "2024-09-17 10:49:23.559180", "msg": "non-zero return code", "rc": 1, "start": "2024-09-17 10:49:23.548655", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
然后进入调试器。
我确实想知道“rescue”块的“when”参数是否有问题,但将其注释掉会导致相同的行为。
任何人都可以找出我在这里做错了什么吗?
首先,请注意
ansible.builtin.copy
模块有一个 validate
选项。使用它,您可以像这样重写您的剧本:
- hosts: localhost
gather_facts: false
vars:
dest_dir: /tmp
tasks:
- name: Copy
ansible.builtin.copy:
src: testfile
dest: "{{dest_dir}}/testfile"
mode: 0666
owner: root
group: root
backup: true
validate: sh -c 'exit 1' %s
您可以用更少的代码有效地获得相同的行为。
关于您的问题,我无法重现您显示的错误。您没有在问题中提供,因此我制作了以下最小的、可重现的示例:
- hosts: localhost
gather_facts: false
vars:
dest_dir: /tmp
tasks:
- block:
- name: Copy
ansible.builtin.copy:
src: testfile
dest: "{{dest_dir}}"
mode: 0666
owner: root
group: root
backup: true
register: copy_result
- name: Print result
ansible.builtin.debug:
var: copy_result
- name: Validate
ansible.builtin.shell: "exit 1"
when: copy_result is changed
rescue:
- name: Revert
ansible.builtin.copy:
remote_src: true
src: "{{copy_result.backup_file}}"
dest: "{{dest_dir}}"
when: copy_result.backup_file is defined
unixy
stdout 回调来减少输出):
- localhost on hosts: localhost -
Copy...
localhost done
Print result...
localhost ok: {
"changed": false,
"copy_result": {
"backup_file": "/tmp/testfile.893626.2024-09-17@07:11:55~",
"changed": true,
"checksum": "22596363b3de40b06f981fb85d82312e8c0ed511",
"dest": "/tmp/testfile",
"diff": [],
"failed": false,
"gid": 0,
"group": "root",
"md5sum": "6f5902ac237024bdd0c176cb93063dc4",
"mode": "0666",
"owner": "root",
"secontext": "unconfined_u:object_r:user_tmp_t:s0",
"size": 12,
"src": "/home/lars/.ansible/tmp/ansible-tmp-1726571514.9080791-893587-119392786896945/.source",
"state": "file",
"uid": 0
}
}
Validate...
localhost failed | msg: non-zero return code
Revert...
localhost ok
- Play recap -
localhost : ok=3 changed=1 unreachable=0 failed=0 rescued=1 ignored=0
我们可以清楚地看到“Revert”任务正在运行。但是,请注意“恢复”任务不会执行您想要的操作。在上面的例子中,我们的目标文件是
/tmp/testfile
,这意味着备份文件的名称类似于/tmp/testfile.893626.2024-09-17@07:11:55~
。这使得“恢复”任务相当于:
- name: Revert
ansible.builtin.copy:
remote_src: true
src: "/tmp/testfile.893626.2024-09-17@07:11:55~"
dest: "/tmp"
when: copy_result.backup_file is defined
这是一个禁止操作;您正在将备份文件复制到其自身上。如果要替换目标文件,则需要在
dest
参数中提供明确的文件名,而不是目录;这将使剧本看起来像这样:
- hosts: localhost
gather_facts: false
vars:
dest_dir: /tmp
tasks:
- block:
- name: Copy
ansible.builtin.copy:
src: testfile
dest: "{{dest_dir}}/testfile"
mode: 0666
owner: root
group: root
backup: true
register: copy_result
- name: Print result
ansible.builtin.debug:
var: copy_result
- name: Validate
ansible.builtin.shell: "exit 1"
when: copy_result is changed
rescue:
- name: Revert
ansible.builtin.copy:
remote_src: true
src: "{{copy_result.backup_file}}"
dest: "{{dest_dir}}/testfile"
when: copy_result.backup_file is defined
当“验证”任务失败时,运行成功恢复文件。