RFC-0154:子套件

RFC-0154:子套件
狀態已接受
領域
  • 軟體推送
說明

允許套件宣告版本專屬子套件的依附元件,進而改善密封性和重新定位。

問題
更小鳥
作者
審查人員
提交日期 (年月分)2022-01-25
審查日期 (年-月-日)2022-03-23

摘要

這項提案是在 Fuchsia 中新增支援,用於定義及解決子套件。子套件是從一個套件 (「A」) 到另一個套件 (「B」) 的具名參照,用於指定單向依附元件 (從「A」到「B」)。子套件也可以包含子套件,從頂層套件建立一個有向非循環圖 (DAG) 的相關套件。如果使用子套件,套件「A」中的資源可以使用其子套件名稱 (由所含套件定義) 參照「B」。如果「B」是「A」的子套件,則任何提供「A」的系統 (例如 Fuchsia 封存,或是 Blob 存放區) 也必須提供「B」可用。因此,子套件會啟用兩個重要的系統屬性:

  • 隱密性 - 使用特定套件依附元件封裝程式 (或測試)。
  • 不可分割性 - 系統應保證,如果含有子套件的套件順利解析,則其所有子套件都會成功解析。(此 RFC 中提議的方法將保證這個狀況是先迅速解決整個子套件的整個階層,再從套件解決要求傳回成功結果)。
  • 可複製性 - 套件可以和所有必要資源一起發布,並放入單一封存 (例如下載) 或替代的 Fuuchsia 套件伺服器中。

提振精神

這個 RFC 提議表達及支援巢狀跨套件依附元件 (子套件) 的概念。儘管套件目前不允許依附其他套件,但此 RFC 修訂了該套件的限制,使該套件不得依附於其他套件 (除非透過隔離)。

請注意,雖然此 RFC 中討論的初始動機用途全都是與測試有關,但子套件在其他情況下可能也很有用。系統會先對子套件開放測試,因為我們能夠以不可在實際工作環境中不接受的方式預先準備測試角落。除了安排時間考量,這麼做是為了在測試和正式環境的情境中完整支援子套件。

為何現在發行?

需要跨套件依附元件最常見的用途是目前最普遍的應用實例,也就是測試情境。測試必須在密封環境中執行特定元件 (通常是模擬或模擬),以及要測試的元件。要避免全域系統狀態 (即即將安裝的一組套件) 影響測試的正確性,請務必根據並納入依附元件的確切版本,我們稱之為「密封包裝」

我們在單一套件中納入所有元件依附元件,將測試套件樹狀結構內。但樹狀結構外 (OOT) 卻視絕對套件網址而定。這些測試並非結構性封裝,會使平台套件網址成為隱含 ABI。

舉例來說,目前 Flutter 整合測試透過元件網址 fuchsia-pkg://fuchsia.com/scenic#meta/scenic.cm 宣告了 Fuchsia Scenic 元件的依附元件。這只是宣告 Fuchsia 平台會預期透過該網址參照提供「部分」景觀的執行個體。如果 View 或其其中一個依附元件 (測試開發人員也必須發現及宣告)、變更其介面、行為或移除,測試可能會在執行階段失敗。這是一種常見的測試中斷來源,而且只會隨著規模擴大,情況會越來越嚴重。

相較之下,如果使用子套件,Flutter 整合測試作者可以:

  1. 在建構時取得包含特定版本的 View 的套件。(這項機制不在說明範圍內)。
  2. 將該套件納入其測試套件的子套件。
  3. 在測試的元件資訊清單中 (而非 fuchsia-pkg://fuchsia.com/scenic#meta/scenic.cm) 宣告含有網址 scenic#meta/scenic.cm 的子元件。

這個版本的測試較為密封,且運作方式都會相同,無論在其執行的裝置上安裝哪個版本的圖像。

注意:子套件啟用這類 OOT 測試情境,但針對測試中重複使用的 Fuchsia 平台套件,這不在 RFC 範圍內。不過,在 RFC 核准後,接下來似乎是定義這項程序的 Fuchsia SDK 擴充功能。

我們鼓勵樹狀結構內結構與 OOT 元件之間提供一致的體驗:單一測試套件會明確指出其所有密封依附元件,並以不可分割的團體身分進行測試。

與其從樹狀結構內重新套用現有策略 (需要建構包含多個元件的新套件),而是改以子套件巢狀結構的形式支援跨套件依附元件。這讓我們可以獨立套件並依附元件,而無需考量過多的命名空間考量。

相關人員

講師:hjfreyer@google.com

審查者:

名稱 重點領域
wittrock@google.com SWD
jsankey@google.com SWD
geb@google.com 元件架構
shayba@google.com 建構
etryzelaar@google.com 工具
aaronwood@google.com 組裝/包裹移動
cgonyeo@google.com RealmBuilder
ampearce@google.com 安全性

諮詢對象:Computerdruid@google.com

社會化:

SWD 團隊在發布前曾參與此 RFC 的初始草稿。元件架構團隊持續向 Component Framework 團隊掌握作業進度,並在每週會議中針對範圍和功能提供意見回饋,並向 RFC 作者的 CF 團隊成員提供意見回饋。

設計

受到子套件影響的用途

  • 巢狀套件依附元件的套件內宣告
    • 套件「可能」包含列出子套件的中繼資料檔案,且建構系統必須能夠根據套件目標的依附元件填入這個中繼資料檔案。
  • 相對元件解析度
    • 舉例來說,開發人員可將元件 fuchsia-pkg://servername/some-package#meta/child_component.cm 的參照替換成相對參照 child_package#meta/child_component.cm;其中 child_packagesome-package 的具名參照,也就是在建構期間鎖定版本 (依套件雜湊) 的參照網址 (或「父項套件」)。
  • 套件依附元件樹狀結構遍歷
    • 舉例來說,發布單一頂層套件後,套件伺服器就能使用嵌入的套件參照,自動尋找並載入套件依附元件。
  • 套件樹狀結構封存
    • 如要建構單一檔案,請從指定的頂層套件取得,此套件包含套件和所有依附元件。

子套件表示法

套件會以套件範圍內的子套件宣告子套件的參照,這些子套件會對應至在同一套件存放區和相同套件組合中定義的套件套件雜湊。(舉例來說,如果是從「宇宙」套件集解析的父項,其子套件也必須透過「universe」套件集解析)。RFC 的其餘部分描述是將這些宣告稱為 meta/subpackages 的檔案,符合 meta/contents (=). 目前使用的格式。

如有的話,此類檔案必須至少包含子套件的名稱及其套件雜湊 (所參照子套件 meta.far 的「內容位址」)。

子套件名稱在單一 meta/subpackages 檔案中不得重複,但不同套件中的子套件名稱可以重複。因此,套件可完全控制其子套件的命名,而多個套件可能會以不同的本機名稱包含相同的子套件。

子套件參照會視為參照套件的「私人資料」。舉例來說,父項套件 A 可能包含兩個子套件:BC (由各自的套件雜湊參照)。B 套件也可能包含相同套件雜湊 C 的子套件參照。頂層父項 A 及其子套件 B 都不會知道通用依附元件。換句話說,父項套件只能解析其子套件,往下一層。父項無法--直接-解析為子套件的子套件的套件網址。事實上,這個 RFC 不會定義代表子套件子套件的語法)。

注意:個別子套件也可以獨立發布,並以頂層套件的形式提供。(例如,正式版元件可發布為 fuchsia-pkg 網址識別的頂層元件,但該元件的套件也可以納入為一或多個密封測試中的子套件)。解析頂層套件時,由 Fuchsia 套件 URI 執行,預設行為是擷取該套件最近發布的版本。另一方面,子套件一律以「套件雜湊」參照特定版本。

使用子套件建立套件

在套件中新增子套件與新增一般檔案的方式類似,不過有一些特殊注意事項。

各種 Fuchsia SDK 建構系統 (今天、GN 和 Bazel) 通常都支援建立「套件」目標,該目標可將依附元件放在其他目標上,以便納入套件中。在這些建構系統中,為套件新增依附元件會告知建構系統也產生相依套件,但目前並不會在產生的套件中對該依附元件進行編碼。舉例來說,下列 GN 程式碼只會在產生套件「A」時產生套件「B」。

# GN format
fuchsia_package("A") {
  deps = [
    ":B",
    other_deps,
    ...
  ]
}

fuchsia_package("B") {
  ...
}

根據 fuchsia.git 中現有的跨套件依附元件進行仔細的調查,在大部分的情況下,目標套件依附於另一個套件時,目標套件中的元件會預期載入相依套件。在這種情況下,相依套件可以與目標一併封裝,做為子套件。由於現有 GN 規則不會強制執行這項解釋,因此我們無法推論。

因此,這個 RFC 建議使用明確的變數--subpackages---來宣告子套件目標。subpackages 清單中的每個目標都必須在產生的 fuchsia_packagemeta/subpackages 檔案中產生對應項目。

這份清單中的目標也會推論建構依附元件,因此 subpackages 中的目標也不需要出現在 deps 中。如要將具有已宣告套件依附元件的套件轉換為內含子套件的套件,請將所有 (或已選取) 套件目標從 deps 移至 subpackages。通常,對 fuchsia_package 的變更可能會顯示為:

# GN format
fuchsia_package("A") {
  subpackages = [ ":B" ]

  deps = [
    other_deps,
    ...
  ]
}

fuchsia_package("B") {
  ...
}

建構系統應將子套件名稱預設為所含目標的名稱,但可以支援使用常見的慣用語覆寫這個名稱。

為協助使用者使用現有工具建構含有子套件的套件,您「必須」更新 Fuchsia 樹狀結構中的建構規則和指令碼,並應在 Fuchsia GN SDK 和下游建構環境中更新。 (如需瞭解受影響的建構環境和必要變更,請參閱「實作」一節)。

解決子套件

子套件解析度會解析已知套件中已命名子套件的相對參照。

fuchsia.pkg.PackageResolver 通訊協定的 Resolve 方法將修改為傳回 ResolutionContextResolve 只會解析絕對套件網址。系統會加入另一個方法 ResolveWithContext,藉此擷取額外的 context 引數 (ResolutionContext),也會為已解析的套件傳回新的 ResolutionContextResolveWithContext 會解析絕對套件網址 (忽略 context) 或相對套件網址。這些變更會以下列概念 FIDL 程式碼片段表示:

   library fuchsia.pkg;

   ...

   const MAX_RESOLUTION_CONTEXT_LENGTH = <TBD>;
   type ResolutionContext = bytes:MAX_RESOLUTION_CONTEXT_LENGTH;

   protocol PackageResolver {

     /// Resolves an absolute component URL.
     /// ...
     Resolve(resource struct {
         package_url string;
         dir server_end:fuchsia.io.Directory;
     }) -> (struct {
         resolved_context ResolutionContext;
     }) error ResolveError;

     /// Resolves a component URL, which may be absolute or relative. If
     /// relative, the component will be resolved relative to the supplied
     /// `context`.
     ResolveWithContext(resource struct {
         package_url string;
         context ResolutionContext;
         dir server_end:fuchsia.io.Directory;
     }) -> (struct {
         resolved_context ResolutionContext;
     }) error ResolveError;

     ...
   }

注意:結構定義的位元組陣列選擇是由多項限制、一些 FIDL 限制及精心評估的方言結果所導致。如需其他背景資訊,請參閱「使用情境專屬 Resolver」和「替代內容:FIDL 類型表示法」。

如果 package_url 中的套件會宣告子套件 (「父項」套件),則傳回的 resolved_context 必須在解析子套件時,以 context 輸入參數的形式傳遞給 ResolveWithContext 的後續呼叫。(如果呼叫端未解析已解決套件的子套件,呼叫端 MAY 會忽略傳回的 resolved_context)。

子套件實作「不得」降低現有服務和元件的穩定性和效力。換句話說,子套件不得導致非重要 (可重新啟動) 元件必須標示為重要,以及無狀態通訊協定 (例如 fuchsia.pkg.Resolve()) 以及提議的 ResolveWithContext,後者會取代部分對 Resolve 的呼叫。

context 值的實作可能會受這項限制影響。只要父項套件仍在系統中使用中,ResolveWithContext 就必須接受 ResolveResolveWithContext 傳回的任何 context。(概念上,父項套件的套件雜湊足以無狀態地解析同一個 Merkle-hash-indexed 套件存放區中的子套件)。

注意:判斷父項是否仍為「使用中」的套件垃圾收集作業,在套件解析器中實作子套件時必須解決。

針對絕對 package_url,系統會忽略傳送至 ResolveWithContextcontext 引數。

注意:由於這個 RFC 會將子套件解析限制為父項 (下一層) 宣告的子套件,因此相對路徑「不得」包含斜線 (/)。

從子套件載入元件

元件解析器會負責剖析元件網址、將其轉換為套件網址並處理完畢,然後在已解決的套件中載入元件資訊清單。載入的資訊清單和套件內容可用來建立新元件,包括透過相對路徑解析子套件的結構定義。

fuchsia.component.resolution.Resolver 通訊協定的 Resolve 方法只會解析絕對元件網址。系統會新增一個額外的 ResolveWithContext 方法,並納入額外的 context 引數 (fuchsia.component.resolution.Context)。ResolveWithContext 會解析絕對元件網址 (忽略 context) 或相對元件網址。這些變更會以下列概念 FIDL 程式碼片段表示:

   library fuchsia.component.resolution;

   ...

   // Note the context length, in bytes, must be at least the size of
   // fuchsia.pkg.MAX_RESOLUTION_CONTEXT_LENGTH, plus the size required to
   // accommodate additional component context information, if any.

   /// The maximum number of bytes for a `Context`.
   const MAX_RESOLUTION_CONTEXT_LENGTH uint32 = 8192;

   /// A byte array that persists a value used by the target `Resolver` to locate
   /// and resolve a component by relative URL (for example, by a subpackage
   /// name).
   alias Context = bytes:MAX_RESOLUTION_CONTEXT_LENGTH;

   protocol Resolver {

     /// Resolves a component with the given absolute URL.
     /// ...
     Resolve(struct {
         component_url string;
     }) -> (resource struct {
         component Component;
     }) error ResolverError;

     /// Resolves a component with the absolute or relative URL. If relative, the
     /// component will be resolved relative to the supplied `context`.
     ///
     /// `component_url` is the unescaped URL of the component to resolve, the
     /// format of which can be either:
     ///
     ///   * a fully-qualified absolute component URL
     ///   * a subpackaged-component reference, prefixed by a URI relative
     ///     path to its containing subpackage (for example,
     ///     `child_package#meta/some_component.cm`); or
     ///   * a URI fragment to a component in the current package (for
     ///     example,`#meta/other_component.cm`)
     ///
     /// `context` is the `resolution_context` of a previously-resolved
     /// `Component`, providing the context for resoving a relative URL.
     ResolveWithContext(struct {
         component_url string;
         context Context;
     }) -> (resource struct {
         component Component;
     }) error ResolverError;
   }

傳回的 Component 類型會修改為加入 resolution_context,以便在解析與此 Component 相關的其他元件時使用。

   type Component = resource table {

       ...

       /// The context used to resolve `component_url`s relative to this
       /// component.
       5: resolution_context Context;
   };

舉例來說,解析名為 parent 的元件後,後續呼叫 ResolveWithContext(subpackaged_url, parent.resolution_context) 即可解析子封裝元件。

針對絕對 component_url,系統會忽略傳送至 ResolveWithContextcontext 引數。

針對相對 component_url

  • 用戶端必須使用 ResolveWithContext。使用相對元件網址呼叫 Resolve 會傳回錯誤代碼 ResolveError::INVALID_ARGS

針對相對的子封裝元件網址:

  • component_url 以相對路徑開頭 (子套件名稱,後面接有特定元件的 # 片段,例如 child_package#meta/some_component.cm)。
  • 如果套件解析器傳回 PACKAGE_NOT_FOUND (或某些對等的套件存放區相關錯誤),元件解析器「必須」傳回 ResolveError:PACKAGE_NOT_FOUND

相對資源元件網址 (來自與其他元件相同的套件):

  • component_url 是 URI 片段 (例如 #meta/other_component.cm,如 RFC-0104:相對元件網址中所述):
  • 片段必須參照與先前解析的其他元件 (「對等」元件) 相同套件中的元件。
  • context 值必須參照與對等值相同的套件版本 (相同的套件雜湊)。

注意:使用 context 解析相對資源元件網址 (依 URI 片段) 可以處理相對解析器的目前行為。目前相對解析器會從父項元件建構絕對套件網址,並附加相對片段部分。這項行為無法保證系統會從相同的套件版本擷取父項元件和子項元件。使用子套件時,有時無法從父項或對等元件建構絕對套件網址。另一方面,如果將套件固定至用於解析套件中所有元件的特定套件雜湊值,或許是您希望的行為。在這種情況下,使用背景可以改善。

代表現有元件 (也就是「父項元件」) 使用相對 URI (路徑或片段) 解析元件網址時,元件管理員必須將解析度委派給用於解析父項元件的同一個 Resolver

結構定義值應視為元件解析器內部的實作詳細資料,用戶端不透明。Resolver 可以將 resolved_context 值 (由 PackageResolver::Resolve...() 傳回) 做為元件解析度 context 值。(API 不會阻止 Resolver 傳回其他或額外的結構定義值,但可能並非必要)。

以下概念範例:解析具有子封裝子項的元件 (假設在啟動後環境) 的程序:

  1. 如何從頂層套件 P 載入元件 A

    a. 元件管理服務會取得 fuchsia-pkg 配置的註冊 Resolver 能力並呼叫 Resolver::Resolve("fuchsia-pkg://fuchsia.com/package-p#meta/component-a.cm")

    b. Resolver 會擷取套件網址並呼叫 PackageResolver::Resolve("fuchsia-pkg://fuchsia.com/package-p", ...),傳回用於載入元件的 fuchsia.io.Directory,以及 resolved_context 值。

    c. Resolver::Resolve() 會建構並傳回解析的 Component (resolution_context),而元件管理服務會以該元件的狀態快取內容。

  2. 如要從子套件 child-package 載入相對於上述元件 A 的子元件 B

    a. 元件管理服務可取得用於解析 component-aResolver 能力,並呼叫 Resolver::ResolveWithContext("child-package#meta/component-b.cm", component_a.resolution_context)

    b. Resolver 會從元件網址擷取相對套件路徑 (子套件名稱),並從元件 context 輸入參數擷取 package_context,並呼叫 PackageResolver::ResolveWithContext("child-package", package_context),以傳回用於載入元件的 fuchsia.io.Directory,以及子套件本身的 resolved_context 值。

    c. Resolver::ResolveWithContext() 會使用新元件的 resolution_context 建構並傳回解析的 Component,而元件管理服務會以該元件的狀態快取內容。

未來工作

  • 合併 subpackagescontents 檔案 - 如果針對永久 FIDL 套件內容建議的 RFC 且獲得核准,子套件檔案應該更新為符合取代 contents 的新格式,或者使用展開欄位來區分內容項目與子套件項目。
  • 使用摘要統計資料為 subpackages 檔案項目加上註解 -「持續性 FIDL 套件內容」RFC 提供一項機制,可納入每個子套件至套件雜湊對應的額外資料。建議您納入內容大小等摘要統計資料 (包括每個子套件及其巢狀依附元件的總計大小)。比如,在載入含有大量依附元件組合的套件時,便可在套件擷取作業期間回報進度。
  • 改善執行階段依附元件解析功能:我們在考慮 (針對日後的 RFC) 中,這個概念也要支援使用絕對路徑 (開頭加上正斜線「/」的正斜線「/」) 的相對套件網址,從提供「目前」套件 (或目前元件) 的「相同套件伺服器」要求頂層套件。(如同此 RFC 所述,目前的套件伺服器可以知道,解析目前套件中的子套件網址的方式相同)。
  • 用於解析及載入資產套件內容的通用 FIDL 服務 - 目前 Fuchsia 軟體在載入套件內容的唯一方法,是在套件中加入元件。自訂元件將負責轉送目錄或提供 FIDL 通訊協定,以從套件佈建資產。我們未來的提案將考慮納入更易於存取的 FIDL API,以便載入子套件和/或該套件中的資產。舉例來說,這可用來發布視覺素材資源或可選取的使用者介面「主題」做為資源套裝組合,而不必採取額外步驟實作元件中介。
  • 延遲載入子套件 - 這個 RFC 建議在載入頂層套件 (也就是快速載入) 時,載入套件及其所有子套件 (以遞迴方式)。建議的未來擴充功能可將指定的子套件宣告為「可延遲載入」。這可用於避免將套件從遠端伺服器匯入受限裝置,直到特別要求特定套件為止。(詳情請參閱「加速套件載入」一文)。
  • 子封裝元件的熱重載:這個 RFC 提議嚴格實作跨套件版本管理:根據其內容的雜湊值計算套件雜湊,包括子套件雜湊。因此,如果子套件有所變更 (以子套件的套件雜湊值表示變更),父項套件也會一併變更。建議您在不重新載入父項的情況下重新載入子套件 (例如在開發時「熱重新載入」元件),反之亦然。允許這類行為可能會減少子套件的某些優勢,包括可靠性和安全性。此外,元件管理服務在解析並重新載入元件新版本時,已存在已知的落差 (請參閱 https://fxbug.dev/42145182)。如果這些已知問題已解決,您可以在日後的提案中修改子套件依附元件限制,讓父項套件根據較授權合約 (例如行為、API 和/或 ABI 保證) 宣告特定依附元件。

實作

Fuchsia 樹狀結構內結構建構系統 (GN 規則和指令碼)

建構規則、指令碼和檔案格式 (包括 Fuchsia 套件產生的中繼檔案格式) 必須更新,以便透過套件建構和/或封存的每個階段轉送 subpackages 清單,最終以產生與套件 contents 檔案一起的新 meta/subpackages 檔案。

對於 subpackages 中的每個套件目標,meta/subpackages 檔案「必須」將宣告的子套件名稱 (預設為子套件名稱目標名稱) 對應至子套件目標的套件雜湊 (也就是子套件 meta.far 的 blob 雜湊)。

有些檔案格式需要變更格式 (藉此新增子套件參照) 或同一階段的補充檔案 (例如 contents 如何與 subpackages 配對) 包括:

  • 「建構資訊清單」(或 archive_manifest) 套件,結尾通常為 .manifest 擴充功能
  • package_manifest.json

Fuchsia 樹狀結構外建構環境 (GN、Bazel 和支援指令碼)

建構規則和指令碼必須更新,做法與 Fuchsia 樹狀結構內的建構規則和指令碼類似,這樣才能在樹狀結構外存放區中啟用子套件。受影響的存放區包括 chromium 和 flutter/engine。(請注意,flutter/engine 會實作 GN SDK 建構規則和指令碼的修改副本,因此如果兩者不同,就必須在 GN SDK 存放區和 flutter/引擎中進行變更。

  • package.gni 會使用 --manifest-path 叫用 prepare_package_inputs.py,產生 ${package_name}.manifest (在指令碼中稱為 archive_manifest,或在 pm CLI 說明文件中稱為「建構資訊清單」)
  • pm_tool.gni 使用 -m ${archive_manifest}-output-package-manifest 叫用 pm build,以產生 package_manifest.json

套件管理員套件指令列介面 (CLI)

套件管理員指令會變更 ffx package,隨之修改。

注意:pm 指令已淘汰,並改用 ffx package。只有在工作流程需要,pm 才會變更 ,而且無法更新為使用 ffx package 替代指令。

  • ffx package build (原為 pm build)
    • 此指令必須變更為接受其他引數和檔案,或是修改輸入和輸出的檔案格式,以加入其他子套件宣告。
  • ffx package export (原為 pm archive)
    • 這個指令會讀取 contents 檔案並產生 .far 檔案,其中包含所有參照的 blob。
    • 針對含有 subpackages 檔案的套件,系統會擴充 export 行為,以便將這些子套件 (以遞迴方式) 組合到產生的封存中 (請參閱「用於發布的套件依附元件」一節)
  • 其他 ffx package 指令可能也會需要變更以配合 subpackages (例如 downloadimport)。我們會在實作時調查這些指令的影響。不過,由於 RFC-0124:去中心化產品整合:構件說明與傳播的一部分,這些變更可能會影響或受到其他預定變更的影響。

封裝套件依附元件以進行發布

子套件的關鍵用途之一,就是要支援套件發布作業,方法是確保套件能確保套件中的所有直接及間接依附元件都能以組合的形式重新分配。相較於開發遞迴週遊子套件依附元件的程序,組合格式的重要性更低。

ffx package 工具是邏輯工具,可識別及找出每個子套件的內容 (展開的套件目錄或 .far 封存檔)。

此 RFC 建議擴充套件工具,以支援子套件感知功能,以支援透過子套件依附元件發布套件的發布作業,而非增加對每個樹狀結構外使用者進行密封封裝的負擔。例如,ffx package 可擴大以將套件及其依附元件封裝成單一檔案封存。

將密封套件封存做為單一檔案的建議格式,會是提供所有套件的展開內容,進而產生所有套件中所有 blob 的索引扁平集合。

套件解析器

您必須更新 fuchsia.pkg.PackageResolver 通訊協定的實作方式,才能實作上述設計章節所述的行為。

正在載入套件

套件解析器「必須」在內部以週期性方式解析所有子套件,直到擷取所有子套件為止,才能傳回根套件的解決結果。這個方法可簡化不可分割屬性的實作,因此所有必要的子套件保證在元件啟動之前就已解決。

強制執行快速套件解析度可能也支援已核准的 RFC-0145:Eager Package Update 中的部分需求。值得注意的是,套件及其子套件樹狀結構可用於實作 Eager Package 更新 RFC 對「套件群組」的必要條件。

元件解析器

您必須更新 fuchsia.component.resolution.Resolver 通訊協定的實作內容,才能實作上述設計章節所述的行為。

RealmBuilder

您必須更新 RealmBuilder,以便納入宣告子套件,以及回應使用元件網址中相對路徑的元件解析度要求。使用 RealmBuilder 的複雜測試目前無法存取套件解析器。「必須」實作透過子套件宣告載入密封套件套件的支援功能,並提供給密封套件與系統測試。

效能

遍歷子套件可能會透過多個間接層級,增加識別所有待下載的 blob 的延遲,但實際上這在實務上不太可能具有顯著性。例如,比較載入具有 N 個 blob 的單一套件,以載入具有 N 個專屬 blob 的子套件:

  • 父項
    • N Blob

VS

  • 父項
    • N_0 blob
    • 子項 1
      • N_1 blob
      • Child2
      • N_2 blob
      • ...

在第一個案例中,所有 N Blob 都可以事先得知,且可以平行載入。在第二種情況下,套件解析器必須依序追蹤父項和子項之間的連結,以累積要載入的完整 N blob 組合。這種週遊會產生受樹狀結構深度限制的額外延遲時間。如果遞迴 blob 載入的延遲時間會造成問題,您可以使用數個實作選項來改善延遲。例如,子套件連結可以在載入 blob 時一併掃遍,或者您可以在建構套件時加入完整的子套件內容位址組合。

回溯相容性

相對資源元件網址的解析度行為

解析現有套件網址和元件網址的行為並未改變,但有一個例外:相對資源元件網址 (以 URI 片段表示,例如 #meta/child.cm) 需要元件 context (建議實作將會保證),但行為會稍有不同。目前的相對解析器會將片段串連至父項元件的套件網址,然後重新解析套件和元件。但解析器可能會載入新版本的套件。這屬於原始實作的潛在風險領域。context 可以保證,相對元件會透過解決「父項元件」的同一個套件解析。

解讀已解析元件的 fuchsia.component.resolution.Package

解析元件後,傳回的 fuchsia.component.resolution.Component 類型會包含 fuchsia.component.resolution.Package 類型的 package 欄位,其中包含對包含傳回 Component 的套件的 package_url 參照。

   type Package = resource table {
       /// The URL of the package itself.
       1: url string;

       /// The package's content directory.
       2: directory client_end:fuchsia.io.Directory;
   };

由於子套件一律具有相對關係,因此 package_url 的現有使用方式都可能會受到影響。如此 RFC 所述,子套件解析程序不會使用儲存在 Package 類型中的任何資訊,因此對 Package 的任何變更或該類型的解譯方式都不會影響子套件 RFC 設計。

實作時需要考慮的替代方案包括:

  1. 儲存僅參照套件伺服器和套件雜湊值 (例如 fuchsia-pkg://fuchsia.com?hash=123456789) 的 url,這足以重新解析子套件的內容,但不會保留父項元件或其子套件名稱的任何資訊。
  2. 將子套件路徑儲存在 url 中,並將選用的解析度內容欄位 (元件套件解析後的內容) 新增至 Package

FIDL 方法變更

fuchsia.pkg.PackageResolverfuchsia.component.resolution.Resolver 中,Resolve() 方法都會改變,藉此傳回新的解析內容,而也會新增名為 ResolveWithContext() 的其他方法以支援 context 輸入參數。這不必經過柔軟的轉換。

套件表示法變更

將子套件新增至 Fuchsia 不會變更不含子套件的套件的表示方式。現有的套件網址和元件網址不會受到影響。開發人員可以選擇將完整的元件網址 (例如使用 fuchsia-pkg://fuchsia.com/top-level-package#...) 替換為其其中一個子套件的參照 (使用 child-package#...,將 child-package 對應至 top-level-package 的套件雜湊)。

工具變更

舊版 Fuchsia、套件伺服器及部分主機端工具 (例如 pmffx package) 不支援使用子套件發布的套件,或使用與套件相對路徑的軟體。Fuchsia 系統和主機端工具需要更新及重新編譯。

套件名稱語法

子套件名稱的範圍限定於父項套件,並且是對於具有相同套件雜湊的任何頂層套件名稱的有效別名。這表示子套件名稱的語法不需要鏡射套件名稱的語法。

不過,如果將相同名稱直接稱為子套件或視為子套件,就可能經常使用相同的名稱。

子套件是階層式的,因此通常會將巢狀子套件視為可命名,或許會使用正斜線做為分隔符號。例如 fuchsia-pkg://fuchsia.com/parent/child/grandchild#gc-component.cmchild/grandchild#gc-component.cm。請注意,這項 RFC 不僅未受到製裁,也不接受這類表示法,但使用斜線分隔符號會造成潛在的陷阱。

套件名稱目前可包含單一斜線。之後的內容會視為「變化版本」,例如 /0,目前在 Fuchsia 中很常見。

值得注意的是,/0 的使用方式已經淘汰,從常見用途中移除後,或許可以釋出子套件的 / 含義 (不過目前並未建議這麼做,且必須遵循日後的 RFC)。

如果套件命名階層的應用方式相互競爭,您可以將替代分隔符號 (例如 :) 用於子套件巢狀結構;子套件則可使用 /,並將替代分隔符號用於其他意義。

此 RFC 建議從允許的子套件名稱語法保留至少 /:

安全性考量

可稽核性與可執行性

此提案的設計提案,從 Fuchsia Security 主管至今,將可能影響系統安全防護機制的變更情形納入考量,並簡化可能影響可稽核性與執行能力的變更審查結果。

舉例來說,父項及其所有子套件 (以遞迴方式) 必須來自同一個套件存放區,並被視為相同套件集的一部分 (例如來自「base」或「宇宙」的所有子套件)。如果使用相同的套件集,則子套件階層中的所有套件都必須遵循相同的執行性政策。

根據套件或元件網址的系統許可清單

目前在特殊權限許可清單中識別的套件或元件 (因為其機密功能不同),可納入子套件,但需要一些額外限制,才能保留必要權限。在測試之外,父項套件「不得」包含需要比父項權限更多權限的子元件子套件。在密封測試中執行的元件可能需要遵守這項規則的例外狀況,但如果列入許可清單的套件或元件在密封測試中用做子套件,就應該盡可能將特殊權限功能替換為模擬或同等的替代項目。

如果此限製造成其他系統改善造成的阻礙,應向安全團隊諮詢其他輔助設施。

針對特殊權限套件解析作業的控管存取權

以現在的套件命名空間來說,PackageResolver 用戶端可以讀取 blobfs 中的所有內容,因此能夠從套件伺服器要求套件並讀取其 BLOB,屬於特殊權限作業。

子套件只會提供指定子集的 blobfs 套件 (目前層級深入),但已宣告另一個套件依附元件的套件就不再具有讀取該套件內容的權限。這項 RFC 建議保持與非特殊權限用戶端從子套件要求功能或資產時可採取的相同限制。

從特定套件執行的元件可能無法查看子套件的內容,但若具有 PackageResolver 能力,可能會檢視 meta/subpackages 檔案並手動解析其內容。

相較於在單一套件中加入依附元件元件,這種設計可以提高安全性。在該解決方案下,每個元件都可以任意檢視彼此的內容。由於元件可能只會看到執行中的套件,而無法查看子套件的內容,因此這個設計會隱藏這些實作詳細資料。

導入風險:線阻斷層

如果我們實作深度優先套件解析度,巢狀子套件 DAG 可能會封鎖其他套件的解析度。這可能會導致解析器停滯,在以遞迴方式解析子套件時,持續保留父項的封鎖資源。

由於 RFC 狀態只會實作快速解析,實作者可確保在釋出解析器資源來解析子套件前,不必完全解析父項資源,即可降低這項風險。

隱私權注意事項

我們預計不會對隱私權狀態產生任何影響。

測試

將新增單元測試,以便驗證額外功能。現有測試有助於識別非預期的迴歸。

主機端封裝測試會在處理子套件時,驗證 ffx package 的行為 (及/或 pm,如有需要) 和相關工具。

系統會編寫密封整合測試,以驗證子套件中的元件解析度。

說明文件

下列已知文件需要更新:

  • 現有說明文件,說明 Fuchsia 套件網址和元件網址 (記錄如何參照子套件)、Software Delivery 說明文件,以及「元件架構」概念文件。目前顯示 CML 範例以及 fuchsia-pkg:// 網址參照的範例,可以使用子套件搭配範例進行增強。
  • 應更新相關元件參照的說明文件,藉此說明與子封裝元件之間的相似與不同之處。

缺點、替代項目和未知

請參閱「設計」一節中的「未來的工作」子章節,其中說明一些未來推出的強化功能,包括在縮小範圍至此 RFC 目前版本之前考慮採用的替代方法和其他功能。

此外,下列子節說明所建議方案的一些替代方案。

替代方法:在子元件網址中加入版本雜湊

與其將子套件導入 Software Delivery 堆疊,我們可以新增工具,讓元件能夠透過在子項元件網址中加入套件雜湊限定詞,藉此宣告具有版本化的依附元件。舉例來說,parent.cmchildren: 清單中經過嚴密封裝的依附元件會顯示為 url: "fuchsia-pkg://fuchsia.com/some_package?hash=1234#meta/a_component.cm"

只要工具和基礎架構能保證 some_package 版本位於套件存放區,該參照即可與現有的解析器搭配使用。您不一定要建立新檔案 (例如此 RFC 中描述的概念 subpackages 檔案) 以及 SWD 變更來解讀該檔案。

與建議的解決方案相比,這項替代方案已遭到拒絕,因為在執行這項作業所需的工具及建構基礎架構時,會變得更加複雜,尤其是在工具和建構工作流程方面。例如:

  • 此替代方案會假設套件雜湊並未嵌入在開發人員 CML 中,或在執行階段解析元件網址的程式碼中,例如將其新增至 collection。有些機制必須新增一些機制,才能修改元件資訊清單和靜態參照的編譯表示法;如果開發人員指出希望元件網址在建構時固定至特定雜湊,則需要在網址中插入 ?hash=<blobid> 限定詞。
  • 元件資訊清單中的密封依附元件可讓您啟用執行階段解析,但需要其他機制才能支援使用依附元件可重新定位的封裝。剖析修改後的資訊清單和程式碼可能並不實際,因此任何用來判斷修改位置的機制,都必須產生其他描述依附元件圖表的中繼資料。然後,工具可變更為讀取該中繼資料來讀取該中繼資料,例如封存包含其所有依附元件的套件,或將套件及其所有雜湊釘選依附元件發布至套件存放區。

替代方法:使用內容專屬的解析器

解析元件或套件時,您可以修改 Resolve 方法,加入 context_resolver 要求控制代碼 (server_end) 做為新的輸入參數,不用傳遞及傳回 resolution_context

從概念上來說,PackageResolver 和 Resolver 都可以建立此 FIDL 通訊協定模式的模型:

protocol Resolver {
  Resolve(struct {
      component_url string;
      context_resolver server_end:Resolver;
  }) -> (resource struct {
      component Component;
  }) error ResolverError;

  ...
}

在這個替代做法中,必須在後續呼叫中使用 context_resolver 的用戶端端,以代表傳回的 PackageComponent 解析元件參照 (例如解析 CML 宣告 children 的元件網址時)。

這個做法遭到拒絕的主要原因是,為解析器增加維護即時連線的複雜度。如果發生解析器伺服器重新啟動的狀況,您可能需要重新建立這些任何或所有管道,這會增加不必要的複雜度。

結構定義參數應對必要的資訊編碼,以便將特定子套件名稱對應至父項套件 meta/subpackages 檔案中的已固定套件雜湊,讓 PackageResolverResolver 實作在解析器重新啟動後仍然有效。

替代選項:解析度結構定義值的 FIDL 類型表示法

我們考慮到幾種表示解析度情境的方法,最終決定對 PackageResolverContextResolver 都使用一般位元組陣列。這種做法的一個優點是,位元組陣列是編碼值的常見類型,而且不會預先編碼任意內容 (包括空值位元組)。

由於 FIDL 團隊沒有其他明確的 FIDL 類型,因此建議使用位元組陣列 (在網路瀏覽器剖析中,這可視為「Cookie 模式」)。

最後,我們將這種類型的限制總結如下:

  • 這些類型應可以管理,而不在 fuchsia.pkg 和 fuchsia.component.resolution 之間導入 API 依附元件。
  • 元件管理員重新啟動後,結構定義值並不需要保留,但子套件實作應不需要將現有「非重要」(也就是可重新啟動) 的套件供應元件轉換為「關鍵」。(這項限制似乎可以使用服務的控制代碼排除)。請注意,如果元件管理員重新啟動,所有元件都會 (目前) 重新解析並重新啟動,其中包含新的結構定義值。結構定義值不需要在元件管理員重新啟動後持續保留,因此您可以單獨重新啟動或重新開機。
  • 結構定義可以很小 (關於套件伺服器名稱與套件雜湊的大小)。這表示針對每個子套件,處理 VMO 的控制代碼可能所費不貲。

替代做法:元件管理服務會使用 resolution_context 取得解析器

解析相對子封裝元件網址時,我們會將 URI 配置 (例如 fuchsia-pkg) 納入 Component::resolution_context 中,讓元件管理員能夠透過配置查詢,透過解析器登錄檔取得 Resolver。之所以遭到拒絕,是因為理論上是,允許 Resolver 傳回採用不同配置的結構定義,以便解決相對的子封裝元件,而這被視為架構風險。而是會追蹤用於解析元件的 Resolver。元件管理服務一律會使用元件的 Resolver,透過相對網址要求其他元件。

替代做法:相對元件,而非相對套件

在 Fuchsia 中,套件是軟體發布的單位,以及軟體安裝單位 (可執行的程式碼和其他檔案)。雖然 Fuchsia 元件可提供一些較熟悉的用途,但是在不涉及元件或元件到元件依附元件的套件之間可以存在跨套件依附元件。舉例來說,Fusia 殼層套件可能會依附於其他資產套件,而這些資產沒有專屬的元件。此外,在 SDK 中獨立發布預先建構元件也不是個簡便的方法。

替代方法:參照使用特殊 fuchsia-subpkg:// 配置的子套件

這個 RFC 建議將 URI 相對路徑 (以路徑片段開頭的 URI,省略配置和授權前置字串) 視為子套件中資源的參照。目前的提案也限制僅以立即的「子項」套件參照子套件,因此路徑「不得」包含斜線。(子套件參照中的斜線會保留給日後使用)。

另一個替代方法則是在參照子套件時,需要用特殊的配置前置字元 (例如 fuchsia-subpkg://),確保指定字串明確用於子套件解析。

此外,使用配置前置字串 fuchsia-subpkg 似乎表示依賴於處理 fuchsia-pkg 配置的相同解析器,可能會令人感到困惑。

URI 標準建議只從相對路徑開始。要求使用特殊的配置前置字串,可能會須依賴特定配置處理常式,進而限制一般性。無結構定義的相關路徑已廣泛實作且容易理解 (舉例來說,在 HTML 中,<a href="sub-path/page"> 會以隱含形式視為相對位置參照,不需要特殊配置)。

優先藝術與參考資料

標準

接受的 Fuchsia RFC

可能相關的草稿 Fuchsia RFC