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。