2022 的第一篇部落格,就從整理一則舊筆記開始吧!這次整理的舊筆記是關於在 Ansible 的 Varaible 中使用 boolean 的一些個人經驗。(本文使用的 Ansible 版本為 2.10。)
內文
在撰寫 Ansible Playbook 的時候,我們為了讓它能夠運用在多種情境,我們經常會使用到 when: 來控制各個 task 是否需要執行,例如:
# 當目標 Host 的 Linux 為 Ubuntu 時,才執行此 Task
- name: "Create nginx sites-enabled link | (Ubuntu)"
file:
src: "/etc/nginx/sites-available/{{ APP_NAME }}"
dest: "/etc/nginx/sites-enabled/{{ APP_NAME }}"
state: link
when: ansible_distribution == "Ubuntu"
而在剛開始學習 Ansible,對於 Ansible、Python 及 Jinja2 還不夠熟悉時,我們可能會寫出下面這種 when:。
# 希望做到當 Variable is_laravel 為 true 時,就執行 task
- name: Create .env
copy:
src: "app/.env.example"
dest: "deploy/.env"
when: is_laravel == "true"
# 希望做到當 Variable skip_env 為 false 時,就執行 task
- name: Create .env
copy:
src: "app/.env.example"
dest: "deploy/.env"
when: skip_env == "false"
這樣寫乍看之下沒有問題,但實際在執行 Ansible playbook 時,可能就會遇到非你預期的狀況,像是明明再三確認該 Variable 的值是 True 或 false,但 task 卻沒有按預期的執行或 skip,當年我也因此踩了幾次雷,還一度寫了類似下面的測試用 Playbook 去測試各種因素,想要釐清是因為 T 或 F 的大小寫,還是因為有沒有雙引號 " 或單引號 ',又或者是 Ansible 版本造成的問題。
- name: "test"
hosts: localhost
gather_facts: no
vars:
is_laravel_True: True
is_laravel_true: true
is_laravel_True_quotes: "True"
is_laravel_true_quotes: "true"
tasks:
- name: is_laravel_True
debug: msg="{{ is_laravel_True }}"
when: is_laravel_True == True
- name: is_laravel_true
debug: msg="{{ is_laravel_true }}"
when: is_laravel_True == True
- name: is_laravel_True_quotes
debug: msg="{{ is_laravel_True_quotes }}"
when: is_laravel_True == True
- name: is_laravel_true_quotes
debug: msg="{{ is_laravel_true_quotes }}"
when: is_laravel_True == True
## ... 中略 ... 別懷疑,我當時還真的是土法煉鋼的列出了各種組合。(暈)
經過反覆測試之後,最後得到幾個經驗,下面就逐一說明。
【補充】你可能會問為何上面說這些是「經驗」而不是肯定的答案,原因很簡單,因為我沒有追根究底的去追查 Ansible 的原始碼到底是怎麼寫的,只概略知道 Ansible 對於 boolean 會做出一些處理,因此這些內容完全都是根據實驗而得到的經驗。
- Ansible 版本一律升級到 2.10 以上。
這一點沒特別的原因,就是隨著 Ansible 一路升級到現在,目前 2.10 是我最熟悉且個人覺得使用起來最沒問題的版本。(謎之音:你還真的完全就是個人憑感覺的經驗談。)
- 不論是使用
True或False、Yes或No,一律全都用小寫,即是true、false、yes、no。 - 一律不使用雙引號或單引號。
這兩點很容易理解,就是全面統一寫法,建立撰寫 Playbook 的規範。
【補充】Ansible Variable 判斷 boolean,除了可以用
true或false之外,你也可以使用yes或no,甚至是1與0。
- 如果希望 Task 執行的判斷條件是「Variable 的值為
true」,不要用when: is_laravel == true這種寫法,改用when: is_laravel。 - 續上,反之如果希望 Task 執行的判斷條件是「Variable 的值為
false」,不要用when: is_laravel == false這種寫法,改用when: not is_laravel。
這兩點其實也與前兩點相同,一樣是統一寫法,同時也避開到底是 yes、no 還是 true 或 false 的問題。
- 如果 Variable 並非是以
vars:事先定義,而是隨著 task 產生,視狀況需補上|bool強制轉換為 boolean。
這點讓我們用一個實驗來說明,請試著執行下面的 Ansible playbook。
- name: "test"
hosts: localhost
gather_facts: no
vars:
is_true: true
is_false: false
tasks:
- name: echo false
command: echo false
register: echo_false
- name: debug echo_false
debug: msg="{{ echo_false.stdout }}"
- name: debug echo_false (var)
debug: var="echo_false.stdout"
- name: when echo_false
debug: msg="ok not"
when: not echo_false.stdout
- name: when echo_false
debug: msg="ok not"
when: not echo_false.stdout|bool
- name: echo true
command: echo xxxxxxxx
register: echo_true
- name: when echo_true
debug: msg="ok"
when: echo_true.stdout
- name: when echo_true
debug: msg="ok"
when: echo_true.stdout|bool
我們一段一段的解釋,首先是:
- name: echo false
command: echo false
register: echo_false
- name: debug echo_false
debug: msg="{{ echo_false.stdout }}"
- name: debug echo_false (var)
debug: var="echo_false.stdout"
- name: when echo_false
debug: msg="ok not"
when: not echo_false.stdout
- name: when echo_false
debug: msg="ok not"
when: not echo_false.stdout|bool
如下圖,從執行結果可以發現 echo_false.stdout 中紀錄的是 false 這個字串,並非 boolean,因此只有透過 |bool 將它轉換成 boolean 後才能正確的作用於 when:。

接著下一段:
- name: echo true
command: echo xxxxxxxx
register: echo_true
- name: when echo_true
debug: msg="ok"
when: echo_true.stdout
- name: when echo_true
debug: msg="ok"
when: echo_true.stdout|bool
如下圖,明明 echo_true.stdout 內的字串是 xxxxxxxx,但居然 when: echo_true.stdout 卻執行了該 task,一樣需要經過 |bool 將它轉換成 boolean 後它才能正確的作用於 when:。

在上面的實驗中,我是用 echo 搭配 register: 去產生新的 Variable,然後我故意要使用的是 .stdout,你也可以在 task 中試試用其他的方式取得 Variable,看看 Ansible 拿到的結果為何?
結語
本文來自當年留下的舊筆記,如果你已是 Ansible、Python 及 Jinja2 的高手,可能你早就知道本文描述的問題原因。而原因說穿了其實很簡單,即是在使用 Variable 做判斷時,我們必須要確保 Variable 內的值到底是 boolean 還是 string,如果不是正確的 boolean,就可能會導致 task 沒能正確的執行與 skip。
最後重新條列一次本文提到的各項經驗,其實就是幾個小小的原則,讓 Playbook 可以撰寫的更一致,避免再次遇到 boolean 判斷錯誤的狀況。
- Ansible 版本一律升級到
2.10以上。 - 不論是使用
True或False、Yes或No,一律全都用小寫,即是true、false、yes、no。 - 一律不使用雙引號或單引號。
- 如果希望 Task 執行的判斷條件是「Variable 的值為
true」,不要用when: is_laravel == true這種寫法,改用when: is_laravel - 續上,反之如果希望 Task 執行的判斷條件是「Variable 的值為
false」,不要用when: is_laravel == false這種寫法,改用when: not is_laravel - 如果 Variable 並非是以
vars:事先定義,而是隨著 task 產生,視狀況需補上|bool強制轉換為 boolean。
