Ansible Variable 的 True 與 False (boolean)

在使用 Ansible 時,如果你需要在變數中使用 Boolean,可能要稍微注意一下幾個小地方。
Table of Contents

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 的值是 Truefalse,但 task 卻沒有按預期的執行或 skip,當年我也因此踩了幾次雷,還一度寫了類似下面的測試用 Playbook 去測試各種因素,想要釐清是因為 TF 的大小寫,還是因為有沒有雙引號 " 或單引號 ',又或者是 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 是我最熟悉且個人覺得使用起來最沒問題的版本。(謎之音:你還真的完全就是個人憑感覺的經驗談。)

  • 不論是使用 TrueFalseYesNo,一律全都用小寫,即是 truefalseyesno
  • 一律不使用雙引號或單引號。

這兩點很容易理解,就是全面統一寫法,建立撰寫 Playbook 的規範。

【補充】Ansible Variable 判斷 boolean,除了可以用 truefalse 之外,你也可以使用 yesno,甚至是 10

  • 如果希望 Task 執行的判斷條件是「Variable 的值為 true」,不要用 when: is_laravel == true 這種寫法,改用 when: is_laravel
  • 續上,反之如果希望 Task 執行的判斷條件是「Variable 的值為 false」,不要用 when: is_laravel == false 這種寫法,改用 when: not is_laravel

這兩點其實也與前兩點相同,一樣是統一寫法,同時也避開到底是 yesno 還是 truefalse 的問題。

  • 如果 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 以上。
  • 不論是使用 TrueFalseYesNo,一律全都用小寫,即是 truefalseyesno
  • 一律不使用雙引號或單引號。
  • 如果希望 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。

參考資料

轉貼本文時禁止修改,禁止商業使用,並且必須註明來自「艦長,你有事嗎?」原創作者 Cheng Wei Chen,及附上原文連結。

工商服務

更多文章