重写成败与变更判定
命令类任务默认按退出码判断失败、按“执行过”判断变更,这两条默认规则经常不符合实际语义:某个命令返回非零只是“没找到匹配项”,某个查询每次都被记为变更。失败判定与变更判定两个关键字允许在任务结果返回后、由控制节点重新判定,把语义纠正过来。
基本信息
| 失败判定 | 覆盖默认退出码判断,条件成立即视为失败 |
|---|---|
| 变更判定 | 覆盖默认“执行过即变更”的行为 |
| 求值位置 | 在控制节点对返回结果求值,可引用输出内容与返回码 |
| 多条件写法 | 条件以列表给出时按“任一成立”处理,需要同时成立时写成单个复合表达式 |
| 忽略错误 | 忽略错误会让流程继续,但主机仍被标记为失败状态 |
| 日志抑制 | 失败信息可能含敏感内容,必要时抑制输出 |
典型使用场景
- 可接受的非零码:查询类命令无匹配时返回非零,不应判为故障
- 只读命令:用变更判定声明无变更,避免污染变更统计与触发处理器
- 有条件的变更:命令输出显示确实改了东西时才标记为变更
- 容忍已知错误:环境上确实可能出现的报错降级为警告而不中断流程
- 失败分级:按返回码区分致命错误与需要人工确认的中间状态
- 与断言配合:先重写判定,再用断言校验关键字段
常见写法
常见写法
- name: 查询类命令:非零码不算失败
ansible.builtin.command: grep -q enabled /etc/app/app.conf
register: enabled_check
failed_when: enabled_check.rc not in [0, 1] # 0 命中、1 未命中都算正常
changed_when: false
- name: 只读命令:显式声明无变更
ansible.builtin.command: /usr/local/bin/app --version
changed_when: false
- name: 按输出决定是否算变更
ansible.builtin.shell: /usr/local/bin/sync-data.sh
register: sync_out
changed_when: "'updated' in sync_out.stdout" # 没更新内容就不算变更
- name: 复合条件:两个条件都要满足才算失败
ansible.builtin.command: /usr/local/bin/app --verify
register: verify_out
failed_when: verify_out.rc != 0 and 'EXPECTED' not in verify_out.stdout
changed_when: false
- name: 失败判定重写为永不失败(谨慎)
ansible.builtin.command: /usr/local/bin/may-fail.sh
failed_when: false
changed_when: false用法与后果
| 写法 | 用途 | 风险 |
|---|---|---|
| 按返回码白名单判定 | 把可接受的非零码排除在失败之外 | 白名单放宽后真实错误被漏掉 |
| 变更判定为假 | 只读任务不产生变更 | 用在真实写操作上会掩盖变更 |
| 按输出判定变更 | 有实质改动才标记变更 | 输出措辞变化后判定失效 |
| 失败判定为假 | 临时绕过已知问题 | 长期保留会掩盖真实故障 |
| 忽略错误继续执行 | 不让单点失败打断整批 | 后续任务依赖上一步产物时会连锁失败 |
变更判定影响处理器触发:写成无变更后,依赖它的处理器不会再被触发。改动前先确认没有下游动作依赖这次变更信号,否则会出现“配置改了但服务没重载”的静默问题。
用永不失败或忽略错误来绕过报错属于应急手段:它让真实故障不再暴露,问题会在更晚、更难定位的位置出现。若必须临时使用,应留下显式的说明与后续处理计划,而不是长期保留在剧本里。
判断标准
重写判定前先问:这个命令的哪些返回码属于业务正常范围?它每次执行是否真的改了东西?把答案落到条件表达式里,再配合断言校验关键字段。重写是为了让判定贴近语义,而不是为了让流程看起来更顺利。