【ルール説明】run-once

こちらの記事は [Ansible lint Advent Calendar 2022 カレンダー2](https://qiita.com/advent-calendar/2022/ansible-lint) 20日目の記事になります。

今回はルール [run-once](https://ansible-lint.readthedocs.io/rules/run-once/) について説明します。

### run-once

[run-once](https://ansible-lint.readthedocs.io/rules/avoid-implicit/) は [ansible.builtin.free](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/free_strategy.html) が指定された時にタスクで`run_once`が有効になっている時に警告を出力します。

Ansible は標準ではインベントリファイルに指定されたホストの順番通りに処理を実行しますが [ansible.builtin.free](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/free_strategy.html) が指定されると Ansible が管理対象ノード（ Managed node ）への接続の速い順に処理を行います。

 [run_once](https://docs.ansible.com/ansible/2.9_ja/user_guide/playbooks_delegation.html) はインベントリファイルに指定されている最初のホストのみ処理を実行します。また [ansible.builtin.free](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/free_strategy.html) と [run_once](https://docs.ansible.com/ansible/2.9_ja/user_guide/playbooks_delegation.html) は同時に指定できません。指定するとエラーになります。
 
#### 実行例

```yaml:inventory.yml
all:
  hosts:
    18.183.124.4:
    35.77.218.231:
    35.78.95.41:
```

```yaml:site.yml
---
- hosts: all
  become: true
  strategy: free
  serial: 3 

  tasks:
    - name: Get OS version
      command: cat /etc/redhat-release
      register: os_release

    - name: Debug
      debug:
        var: os_release
        run_once: true
```

```bash
ansible-playbook -u centos -i inventory.yml site.yml 

PLAY [all] *******************************************************************************

TASK [Gathering Facts] *******************************************************************
ok: [35.78.95.41]
ok: [35.77.218.231]
ok: [18.183.124.4]

TASK [Get OS version] ********************************************************************
changed: [18.183.124.4]
changed: [35.77.218.231]
changed: [35.78.95.41]

TASK [Debug] *****************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: NoneType: None
fatal: [18.183.124.4]: FAILED! => {"msg": "Invalid options for debug: run_once"}
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: NoneType: None
fatal: [35.77.218.231]: FAILED! => {"msg": "Invalid options for debug: run_once"}
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: NoneType: None
fatal: [35.78.95.41]: FAILED! => {"msg": "Invalid options for debug: run_once"}

PLAY RECAP *******************************************************************************
18.183.124.4               : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
35.77.218.231              : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
35.78.95.41                : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
```

#### 問題のあるコード

```yaml
---
- name: "Example with run_once"
  hosts: all
  strategy: free # <-- strategy に free が設定されている
  gather_facts: false
  tasks:
    - name: Task with run_once
      ansible.builtin.debug:
        msg: "Test"
      run_once: true # <-- タスクで run_once が設定されている
```

#### 修正されたコードその１

```yaml
- name: "Example without run_once"
  hosts: all
  gather_facts: false
  tasks:
    - name: Task without run_once
      ansible.builtin.debug:
        msg: "Test"
```

#### 修正されたコードその２

```yaml
- name: "Example of using run_once with strategy other than free"
  hosts: all
  strategy: linear # <-- strategy に linear を設定する
  gather_facts: false
  tasks: # <-- unoqa を設定し Ansible lint の処理をスキップする
    - name: Task with run_once  # noqa: run_once[task]
      ansible.builtin.debug:
        msg: "Test"
      run_once: true
```

### 参考サイト

* [run-once — Ansible Lint Documentation](https://ansible-lint.readthedocs.io/rules/run-once/)