RFC-0071:OTA 後援 | |
---|---|
狀態 | 已接受 |
區域 |
|
說明 | 防止裝置在版本界線之間進行 OTA 回溯。 |
問題 | |
Gerrit 變更 | |
作者 | |
審查人員 | |
提交日期 (年-月-日) | 2021-02-03 |
審查日期 (年-月-日) | 2021-02-24 |
摘要
本文件提出的計畫可防止裝置在版本界限內安裝無線更新 (OTA)。
提振精神
當儲存空間堆疊對檔案系統格式進行重大變更時,會回溯格式的主要版本號碼,以免在舊版系統上執行的驅動程式嘗試掛載及使用新格式的映像檔。
如果系統更新堆疊中含有等同的版本號碼,使用者就無法嘗試將 OTA 回溯至不支援裝置所含檔案系統映像檔的驅動程式庫版本。換句話說,這可讓我們在裝置變成磚塊之前,讓「回溯 OTA」作業失敗。
這麼做可帶來以下好處:
- 對於任何會保留狀態的應用程式,這項功能都非常實用。舉例來說,應用程式會維護 sqlite 資料庫,而資料庫的結構定義可能會隨時間變更。
- 具體來說,這對儲存空間團隊來說非常實用,因為他們過去必須花費大量時間,處理因跨版本邊界執行反向 OTA 而產生的問題。
- 這會強化 Fuchsia 不支援回溯 OTA 的事實,因為這類 OTA 僅能盡力執行。
請注意,這項提案不會變更支援哪些 OTA 序列,以及哪些不支援。只是讓這項支援功能更明確。OTA 備援的主要目的,是避免開發人員的裝置進入無效狀態。對於實際生產裝置,應主要由版本管理系統強制執行「不回溯 OTA」不變量。
如果沒有這項建議,在開發人員嘗試啟動裝置時,嘗試在相容性不一致的界限上進行回溯 OTA 作業,就會導致問題 (例如,驅動程式庫可能不支援檔案系統格式)。有了這個提案,開發人員就能在執行 OTA 前發現這項問題 (且錯誤會更清楚顯示),進而提供更好的開發人員體驗。
背景
術語
OTA 是升級基礎作業系統的機制。Fuchsia 裝置可接收並安裝系統和應用程式軟體的 OTA 更新。
Stepping Stone 版本是指無法在 OTA 中略過的版本。舉例來說,假設有三個連續版本 A、B 和 C。傳統上,我們需要支援 A->B
、B->C
和 A->C
的 OTA。如果我們將 B
宣告為踏腳石版本,這會移除 A->C
邊緣,因此 A 升級至 C 的唯一方法就是 OTA A->B
,然後再升級至 B->C
。在實際操作中,這項功能可用於風險較高的遷移作業,以及減少需要測試的推送 OTA 數量。
OTA 備援機制與踏腳石的關係
OTA 備援和階梯式版本都是我們必須進行安全遷移 (例如儲存格式遷移) 的基礎元素。至於如何使用 OTA 備援和階梯式版本,具體的應對策略超出本 RFC 的範圍。我們在此提供範例,說明如何使用這些基本元素來支援安全的遷移作業。
建議您考慮遷移儲存格式。我們可能會採取下列步驟:
- 新增支援新格式,但尚未啟用/遷移。提交 OTA 後援。
- 請稍候片刻。
- 使用上述其中一種遷移策略啟用新格式。
如果我們確實遷移裝置,可以採取兩個進一步的步驟來啟用清理作業:
- 推出包含 (3) 的踏板版本。
- 移除遷移程式碼,並停止支援舊格式。
透過這個過渡版本,我們可以假設裝置會經過含有遷移程式碼的版本,因此我們可以移除舊版格式的讀取支援功能。
在 (1) 中提高 OTA 備援機制,可確保裝置不會降級至不支援新格式的版本。
撞擊後擋板的政策
應視需要一次性調整後備計劃。絕大多數變更都不需要使用回溯升級功能。如果這項 RFC 獲得核准,應發布官方應對手冊文件,說明如何提升後盾的具體步驟。在此期間,我們將概略說明這項政策。
提出 CL 以提升後援時,作者應:
- 提供 bugs.fuchsia.dev 問題的連結,說明為何需要升級,以及開發人員在絕對需要透過回溯點降級裝置時,該如何繼續操作 (例如,答案可能是 flash 或 pave)。
- 取得 //src/sys/pkg/OWNERS 核准。
設計
讓我們引入 epoch.json
檔案,讓該檔案同時出現在更新套件和系統中。應為含有兩個字串索引鍵的 JSON 檔案:
- 「version」:應包含
epoch.json
結構定義版本的單一字串值。實際上,系統在執行更新時不會檢查這個值,這個鍵只是為了讓您清楚知道epoch.json
結構定義在正式版中是否有變更。 - 「epoch」,應為 OTA 備援機制的單一整數值。如果更新套件的紀元 < 系統的紀元,我們應在準備階段使用
UNSUPPORTED_DOWNGRADE
讓 OTA 失敗。
例如,epoch.json
可能如下所示:
{
"version": "1",
"epoch": 5
}
為了安全地提升紀元,我們也要介紹一個 epoch_history
檔案,這個檔案會透過建構系統編譯成 epoch.json
。epoch_history
檔案的格式可能為:
0=Initial epoch (https://fxbug.dev/42144857)
1=Storage format migration (https://fxbug.dev/XXXXX)
...
N=Most recent change (https://fxbug.dev/YYYYY)
每次引入回溯不相容的變更時,都應手動升級 epoch_history
檔案。
雖然中介 epoch_history
檔案會增加一層複雜度,但這種做法仍有以下優點:
- 它會提供所有版本升級變更的記錄 (強制說明文件)
- 如果有兩個人基於不同原因嘗試提前 Epoch,就會產生合併衝突。
實作
變更將完全發生在平台 (具體來說是系統更新堆疊) 中。
為了實施變更,我們需要:
- 將
epoch_history
新增至 //src/sys/pkg/bin/system-updater。- 此外,請建立指令碼,將
epoch_history
轉換為epoch.json
。 - 請讓建構系統使用這個指令碼,將
epoch.json
新增至系統更新器的 out 目錄。
- 此外,請建立指令碼,將
- 修改 BUILD,讓
epoch.json
也能放入更新套件。 - 系統更新器應在 Prepare 階段結束時檢查
epoch.json
。- 如果更新套件中沒有
epoch.json
,或是反序列化時發生問題,請假設紀元是 0。我們刻意忽略錯誤,以便在epoch.json
結構定義變更時仍可進行 OTA。 - 如果系統更新器的 out 目錄中沒有
epoch.json
,或是反序列化時發生問題,則會失敗,因為這不是預期的情況。建議您使用include_str
巨集從 out 目錄讀取資料。 - 如果更新套件中的 epoch 小於系統更新器中的 epoch,則會以
UNSUPPORTED_DOWNGRADE
為原因失敗準備。我們需要為UNSUPPORTED_DOWNGRADE
建立新的 PrepareFailureReason。
- 如果更新套件中沒有
安全性
這並非安全防護功能。不過,它可能會與安全性功能互動,以改善開發人員的工作流程。舉例來說,復原保護功能會拒絕啟動 N
以下版本的映像檔。如果我們在發布映像檔版本 N
時增加了紀元,這將可防止開發人員降級無法啟動的版本,因為這些降級作業會在 OTA 備援機制中失敗。
此外,我們選擇將 epoch.json
嵌入系統更新器二進位檔 (而非設定資料),讓 OTA 能夠抵禦設定資料毀損的情況。
隱私權和效能考量
無
測試
我們可以使用 //src/sys/pkg 中的現有系統更新測試架構,該架構結合了單元和整合測試。
此外,OTA e2e 測試會確保備份不會遞減,且格式有效。例如:
- 如果
N
版本降低 OTA 備援機制,我們就會在從N-1
版本改為N
的 CI 到 OTA 作業中失敗。 - 如果建構
N
在系統更新器中產生無效的epoch.json
,我們將無法從 CI 進行 OTA 更新。 - 將
N
建構為N'
。
說明文件
我們需要建立文件,說明更新 epoch_history
的政策。
此外,我們還需要修改:
- 更新套件說明文件。
- OTA 說明文件 (尚未發布在 fuchsia.dev 上)。
缺點、替代方案和未知事項
實施這項提案的成本為何?
實作這項提案的主要成本是增加平台複雜度,因為我們會在平台中新增另一個版本 ID。
還有哪些策略可以解決同樣的問題?
另一種策略是正式支援所有回溯 OTA。這麼做並不實際,因為如果我們不知道變更內容,就無法編寫能因應未來變更的程式碼。
另一種策略是明確禁止所有回溯 OTA (即使在其他情況下也可能發生)。舉例來說,我們可以自動在每次新建構時提高後盾。我們決定不這麼做,因為在實際情況下,有些開發人員確實會依賴這些舊版 OTA,我們不希望這些開發人員因此無法正常運作。
另一種方法是直接整合 Fuchsia 平台版本資訊 (請參閱 RFC-0002)。不過,這項功能有幾個模糊的問題。舉例來說,是否應禁止 API 級別中的所有回溯 OTA,還是應挑選特定級別?我們會打破誰?由於 Fuchsia 先前曾為系統的不同部分使用不同的版本 ID (例如檔案系統有自己的版本 ID),因此這似乎是更簡單的做法。
既有技術與參考資料
如需瞭解 OTA 的更多資訊,請參閱 Android 說明。
特別銘謝
James Sullivan 協助撰寫動機和墊腳石部分。原始設計文件是由 Zach Kirschenbaum 撰寫,並由 Dan Johnson 審查。