RFC-0146:CML 中的結構化設定結構定義

RFC-0146:CML 中的結構化設定結構定義
狀態已接受
領域
  • 元件架構
說明

針對 CML 中的結構化設定結構定義進行設計、實作策略及其他決策

問題
毛皮變化
作者
審查人員
提交日期 (年-月-日)2021-10-18
審查日期 (年-月-日)2021-12-22

摘要

本文件以 RFC-0127 為基礎 CML 中結構化設定結構定義的其他決策。本提案將提供 設定結構定義的 JSON 語法,此為一組 FIDL 類型,用於元件中的編碼設定結構定義 資訊清單,以及將設定結構定義從 JSON 轉換為 FIDL 的程序。

請注意,「設定結構定義」就是「設定檔」 RFC-0127 中定義的。

提振精神

開發人員需要為元件宣告設定結構定義的方法,模型的 剖析設定結構定義,編譯並傳送至元件管理服務 。這項提案旨在解決這些疑慮。

設定結構定義是系統向使用者顯示的結構化設定主要介面。根據標準 RFC-0098RFC-0098 所設,必須完成 RFC 提案,才能使用這項功能。

Fuchsia SDK 中會包含您提議的資訊清單語法和 FIDL 結構。為了控制 如果使用這項功能,資訊清單檔案中的設定結構定義將受到 cmc 中的功能旗標管制。

相關人員

講師:pascallouis@google.com

審查員:adamperry@google.com、jsankey@google.com、geb@google.com、pascallouis@google.com、 cpu@google.com

諮詢:aaronwood@google.con、yifeit@google.com、surajmalhotra@google.com、shayba@google.com

社交:本設計是與元件架構團隊分享,同時也是 元件架構設計討論。

設計

範圍

本提案會將下列因素納入考量:

  • 支援 RFC-0127 指定的最低設定類型: 這些資料型別的布林值、整數、受限長度字串,以及受限長度向量
  • 對齊結構化設定的哲學 (由 RFC-0127 指定): 簡單、可靠、安全、可測試

本提案考量下列策略,屬於 RFC 規範的未來工作及範圍外:

CML 語法

我們將在元件資訊清單中引進全新的頂層鍵,以定義結構化 為元件設定個別元件對應的值會包含每個設定欄位的鍵。

結構化設定欄位需要類型系統。Fuchsia 團隊擁有豐富經驗 開發 FIDL 的型別系統,並定義可運用這項經驗 與 FIDL 相容,且具有相同理念例如 版面配置和限制條件可在結構化設定鍵中表示的類型組合為 目前可在 FIDL 中表示的類型子集。結構化設定 FIDL 會隨著時間演進,而我們現在認為結構化設定類型可能會隨時間增加 表現比 FIDL 類型更出色,例如將數字限制在特定範圍 只要設定限制

CML 樣式和語法的用途是確保一致性,如果具有相同概念,我們就會 命名和分解工作以 FIDL 類型為例 選擇性參數化,並視需要受限。這個分解存在於 提議的 CML 語法延伸 (類型具有選用屬性);原始版面配置,例如 int32 或更複雜的版面配置 (例如 vector),名稱都相同;限制條件可套用至不同類型 例如為向量或字串設定大小上限

元件資訊清單變更摘要如下:

{
    use: {
        ...,
    },
    ...
    config: {
        <key>: {
            type: "<type string>",
            <additional properties based on type>
        },
        ...
    }
}

config 是資訊清單中的頂層鍵,其值為 JSON 字典。「 字典是設定欄位,由索引鍵和類型組成。

設定鍵具備以下屬性:

  • 是元件設定結構定義中的專屬 ID
  • 這些函式將用於系統組合及處理覆寫作業
    • 已編譯資訊清單中的設定金鑰是由系統組合使用,以建立設定檔檔案
    • 設定鍵是可用於定義父項覆寫設定的固定 ID
  • 必須與規則運算式 [a-z]([a-z0-9_]*[a-z0-9])? 相符
    • 這項規則運算式與 FIDL、JSON 和潛在檔案中的 ID 相容 用戶端程式庫
    • 必要時也會保留鍵中編碼分隔符的空間。
    • 未來也可以擴大應用範圍。
  • 長度不得超過 64 個半形字元
    • 日後可以擴展這項功能。

設定欄位中的類型字串只能使用下列其中一個值:

  • bool
  • uint8uint16uint32uint64
  • int8int16int32int64
  • string
  • vector

這項設計可支援 enumfloatstructarray 等類型。 但不在 RFC 的範圍內如要瞭解這些複雜型別的語法,請參閱 部分。

bool 和整數沒有任何類型限制:

config: {
    enable_advanced_features: { type: "bool" },
    num_threads: { type: "uint64" },
}

string 必須含有 max_size 類型限制。max_size 會剖析為 uint32

config: {
    network_id: {
        type: "string",
        max_size: 100,
    }
}

vector 必須含有 max_count 類型限制和 element 類型引數。max_count 剖析為 uint32element 限制在 bool、整數或 string 中:

config: {
    tags: {
        type: "vector",
        max_count: 10,
        element: {
            type: "string",
            max_size: 20,
        }
    }
}

範例

請思考下列元件資訊清單,已加以調整,改為使用結構化設定。 這些範例會明確著重於資訊清單的 config 部分。

考古學家

目前的設定會分割在指令列引數之間 和封裝於元件中的 JSON 設定檔。 以下範例說明如何將所有設定來源繫結至結構化設定。

config: {
    // proxy the kernel logger
    enable_klog: { type: "bool" },

    // initializes syslog library with a log socket to itself
    consume_own_logs: { type: "bool" },

    // connects to the component event provider. This can be set to false when the
    // archivist won't consume events from the Component Framework v1 to remove log spam.
    enable_component_event_provider: { type: "bool" },

    ...

    // initializes logging to debuglog via fuchsia.boot.WriteOnlyLog
    log_to_debuglog: { type: "bool" },

    // number of threads the archivist has available to use.
    num_threads: { type: "uint32" }
}

detect

samplerpersistencedetect 等程式會編譯為「啟動器」二進位檔 節省空間由於啟動器中的每個程式都有專屬的資訊清單,因此 不同的結構化設定

考慮使用目前設定完成的 detect 程式 指令列引數

program: {
    ...
    args: [
         // The mode is passed over argv because it does not vary for this component manifest.
         // Launcher will use the mode to determine the program to run.
         "detect"
    ]
},
config: {
    // how often to scan Diagnostic data
    // unit: minutes
    //
    // NOTE: in detect's CLI parsing, this is optional with a default specified in code.
    // when using structured config, the default would be provided by a build template or
    // by product assembly.
    check_every: { type: "uint64" },

    // if true, minimum times will be ignored for testing purposes.
    // never check in code with this flag enabled.
    test_only: { type: "bool" },
}

遊戲主機

您可以使用指令列引數完成目前的設定。這個範例顯示 則需要在結構化設定中,使用字串向量等進階類型

config: {
    // Add a tag to the allow list. Log entries with matching tags will be output to
    // the console. If no tags are specified, all log entries will be printed.
    allowed_log_tags: {
        type: "vector",
        max_count: 40,
        element: {
            type: "string",
            max_size: 40,
        }
    },

    // Add a tag to the deny list. Log entries with matching tags will be prevented
    // from being output to the console. This takes precedence over the allow list.
    denied_log_tags: {
        type: "vector",
        max_count: 40,
        element: {
            type: "string",
            max_size: 40,
        }
    },
}

FIDL 規格

上述定義的 CML 語法必須由 cmc 編譯為對等 FIDL 物件,並由 元件管理服務,用於在後續實作階段中覆寫解析度。

在設定結構定義中使用 FIDL 是內部的選擇,可簡化實作程序 在 cmc 和元件管理服務中。FIDL 不是最終開發人員用於設定的介面 結構定義

ConfigSchema 物件包含:

  • 已排序的欄位清單 (ConfigField 個物件)
  • 結構定義總和檢查碼:所有設定欄位的雜湊

ConfigField FIDL 物件包含 keytype。這兩個欄位的意義 與 CML 語法相同:key 可明確識別設定欄位,type 則是 設定值必須符合的類型。

FIDL 編碼最多允許 2^32 - 1 個設定欄位。

cmc 會以確定順序排序設定欄位,但並未指定該順序 的附件。使用確定順序可以提供下游結構定義檢查碼的一致性 工具和執行階段設定解析功能如果未針對 日後進行最佳化

cmc 會使用每個欄位的鍵和值類型計算結構定義檢查碼。這個核對和 也會顯示在 config value 檔案中元件管理服務會檢查 確保結構定義與值檔案完全相同,以免版本出現偏差。

library fuchsia.component.decl;

// Config keys can only consist of these many bytes
const CONFIG_KEY_MAX_SIZE uint32 = 64;

// The string identifier for a config field.
alias ConfigKey = string:CONFIG_KEY_MAX_SIZE;

// The checksum produced for a configuration interface.
// Two configuration interfaces are the same if their checksums are the same.
type ConfigChecksum = flexible union {
    // A SHA-256 hash produced over a component's config interface.
    1: sha256 array<uint8>:32
};

/// The schema of a component's configuration interface.
type ConfigSchema = table {
    // Ordered fields of the component's configuration interface.
    1: fields vector<ConfigField>:MAX;

    // Checksum produced over a component's configuration interface.
    2: checksum ConfigChecksum;
};

// Declares a single config field (key + type)
type ConfigField = table {
    // The identifier for this config field.
    // This key will be used to match overrides.
    1: key ConfigKey;

    // The type of config values. Config values are verified
    // against this layout at build time and run time.
    2: type ConfigType;
};

// The type of a config value
type ConfigType = struct {
    layout ConfigTypeLayout;
    parameters vector<LayoutParameter>;
    constraints vector<LayoutConstraint>;
};

// Defines valid type ids for config fields.
type ConfigTypeLayout = flexible enum {
    BOOL = 1;
    UINT8 = 2;
    UINT16 = 3;
    UINT32 = 4;
    UINT64 = 5;
    INT8 = 6;
    INT16 = 7;
    INT32 = 8;
    INT64 = 9;
    STRING = 10;
    VECTOR = 11;
};

// Parameters of a given type layout
type LayoutParameter = table {
    // For vectors, this is the type of the nested element.
    1: nested_type ConfigType;
};

// Constraints on a given type layout
type LayoutConstraint = table {
    // For strings, this is the maximum number of bytes allowed.
    // For vectors, this is the maximum number of elements allowed.
    1: max_size uint32;
};

Component 是 CML 資訊清單的 FIDL,且現在必須包含 ConfigSchema FIDL 物件。

// *** component.fidl ***

library fuchsia.component.decl;
// NOTE: as long as the two libraries are supported, this change will also be made to
// library fuchsia.sys2;

/// A component declaration.
///
/// This information is typically encoded in the component manifest (.cm file)
/// if it has one or may be generated at runtime by a component resolver for
/// those that don't.
type Component = table {
    /// ... previous fields ...

    /// The schema of a component's configuration interface.
    10: config ConfigSchema;
};

cmc 的變更

我們將擴充 cmc,以便使用 config 區段反序列化 CML、驗證其內容,並加入 所產生的結構定義

我們會在 cmc 功能旗標後方實作這項功能。只有樹狀結構內明確物件上 允許許可清單可使用 config 個段落,而我們也會在導入其他主題時加以運用 結構化設定

元件管理服務變更

我們將變更元件管理服務,將結構化設定結構定義提供給中樞 在每個元件執行個體的 resolved/config 目錄下。登入中心位置的確切編碼方式 命名空間將不穩定,且不在這個 RFC 的範圍內。

這項變更可讓我們建立整合測試,驗證該設定 結構定義成功通過元件解析管道

ffx component 的變更

ffx component show 會使用中樞輸出元件執行個體的相關資訊。經過變更後 元件管理服務,這個外掛程式現在可以輸出每個元件執行個體的設定結構定義。

$ ffx component show netstack
Moniker: /core/network/netstack
URL: fuchsia-pkg://fuchsia.com/network#meta/netstack.cm
Type: CML static component
Component State: Resolved
...
Configuration:
  log_packets [bool]
  verbosity [string:10]
  socket_stats_sampling_interval [uint32]
  opaque_iids [bool]
  tags [vector<string:10>:20]
Execution State: Running
...

請注意,上述 ffx component show 的指令輸出內容隨時可能有所變更。指令 才會列印設定結構定義,直到實作實際值解析度為止。

實作

這項設計的實作會在三個階段逐步完成。

  1. 將新類型新增至 cm_rust,並在功能標記後方的 cmc 中剖析設定段落 (原型)
  2. 使用中樞、整合測試 (原型) 公開設定
  3. ffx component show 項變更

成效

  • 由於結構性的關係,我們將針對元件開始時間進行額外效能命中做為基準 此外還會從 0 自動調整資源配置 您完全不必調整資源調度設定此問題已有錯誤
  • 這項設計使用 FIDL 資料表來編碼結構定義,也就是說,調整後的結構定義會帶來一些額外負荷 比較 ComponentDecl FIDL 物件與宣告結構體。預估的負荷是 無法與整體元件開始時間做比較。
  • 這項設計會將設定鍵做為字串儲存在 ConfigField FIDL 物件中,而該物件會使用 額外磁碟空間,且在啟動元件時需要複製更多資料。
    • 這是非 ELF 執行器的必要條件,其中有些需要用到字串鍵來編碼 即可擷取設定值
    • 這也能讓透過 Hub 進行偵錯。
  • 如果是 N 設定鍵,則設定值比對作業應產生 O(N) 費用。系統不會設定 在未覆寫的情況下,檢查案件是否相等。
  • 我們與 cmc 等主機工具的效能無關。
  • 元件管理服務不負責對設定結構定義或設定進行雜湊處理 輕鬆分配獎金cmc 會預先進行雜湊處理。元件管理服務只需要檢查 結構定義和值檔案中的雜湊。
  • 元件管理服務會將設定結構定義儲存在中樞檔案系統中。這可能會 對元件管理服務的記憶體用量造成其他不可忽略的影響。
    • 若是將中樞提取式而非推送式架構來化解這個問題,A 罩杯 已針對這項功能要求提出 bug

安全性考量

作者未發現任何潛在疑慮。請注意,這項功能在 cmc 中受到使用 加入許可清單,可進一步降低安全性風險。

隱私權注意事項

設定鍵會顯示在元件資訊清單中。這些金鑰不得包括 專屬資訊。

作者並未看到任何其他潛在疑慮。

測試

包括:

  • cmc 的單元測試,用於測試 config 個段落的驗證和編譯作業 (包括失敗) 案件)
  • 元件管理服務工具的單元測試,這些測試會使用結構化設定測試中樞目錄結構
  • 元件管理服務工具的整合測試,這些測試會解析元件資訊清單,並驗證中樞 顯示其設定結構定義
  • ffx component show 的單元測試,可正確剖析中樞內的結構化設定。

說明文件

在結構化設定達到成熟度時,我們預計會提供下列文件:

  • 元件結構化設定結構定義的 CML 語法
  • 元件結構化設定結構定義的 FIDL 規格
  • 宣告設定結構定義的最佳做法:註解、命名慣例等
  • 範例/程式碼研究室:將 config 區段新增至元件的資訊清單、建構元件。 使用 ffx component show 驗證設定欄位

缺點、替代方案和未知

替代做法:config 區段指向 FIDL 檔案

資訊清單中的 config 部分可能指向會 說明設定結構定義RFC-0127 會在 詳細資料與缺點從 RFC-0127 得出的結論也適用於此。

替代做法:program 下的「config」部分

program: {
    config: {
        <key>: {
            type: "<type string>",
            <additional JSON fields based on type>
        },
    }
}
  • Pro:在 programconfig 之間建立關聯
  • 缺點:巢狀結構過多
  • 缺點:不含 program 區段的元件不可提供結構化設定。日後 路徑可能包含沒有程式的元件

替代組合:fields 個區段

config: {
    fields: {
        <key>: {
            type: "<type string>",
        }
        ...
    }
}
  • Pro:保留日後擴充至設定結構定義的空間。目前不清楚我們是否需要 因為現在的擴充性
  • 缺點:較簡潔

替代方案:田域的語意

選項 B:

config: [
    {
        key: "<key>",
        type: "<type string>",
    }
    ...
]
  • Pro:使用 JSON 陣列 (而非 JSON 物件)。與 資訊清單:useexpose
  • 缺點:這個選項的外觀與用於定義的結構/表格/地圖語意不同 設定欄位在 JSON 中,如果值具有字串鍵指向值,則通常會表示 依地圖/物件
  • Con:較為詳細

選項 C:

config: {
    <key>: "<type string>"
    ...
}
  • Pro:更簡潔
  • 缺點:不預留日後給欄位擴充空間的空間。如果是複雜的類型,可能需要提供這項資訊 和預設值

選項 D:

config: [
    {
        <type string>: "<key>",
        ...
    },
],
  • Pro:更簡潔。沒有樣板「type」或「key」關鍵字
  • 優點:與能力轉送語法一致
  • 缺點:必須明確檢查重複的鍵
  • 缺點:不確定如何與向量的 element 類型引數搭配運作

日後的工作

資訊清單資料分割中的結構化設定

我們將延後處理這項工作,直到能證明必要性為止。我們也沒有理想的策略 以處理合併衝突如果合併衝突停止編譯,則每個資料分割都需要命名 藉此定義可用性政策如果能解決合併衝突,各個資料分割就會 不小心共用相同的設定欄位

設定結構定義中的預設值

設定欄位可以支援預設值。系統會使用 JSON 類型RFC-0127 假設預設值應為設定的一部分 但目前更適合使用建構規則或子組合來提供預設值 產生設定檔

config: {
    enable_advanced_features: {
        type: "bool",
        default: false,
    }
    tags: {
        type: "vector",
        max_count: 10,
        element: {
            type: "string",
            max_size: 20,
        }
        default: [
            "foo",
            "bar",
            "baz",
        ]
    }
}

我們將延後這項工作,直到能證明子組件系統無法用於 預設值。

複雜資料類型

我們預計在日後支援 arrayenumfloatstruct。這些類型都應該在「vector」中提供支援,且可能需要進行額外驗證 步驟。

config: {
    fsck: {
        type: "struct",
        fields: {
            check_on_mount: { type: "bool" },
            verify_hard_links: { type: "bool" },
            repair_errors: { type: "bool" },
        }
    },
    compression_type: {
        type: "enum",
        variants: {
            uncompressed: 0,
            zstd_chunked: 1,
        }
    },
    // Vectors can store complex structures
    coordinates: {
        type: "vector",
        max_count: 10,
        element: {
            type: "struct",
            fields: {
                x: { type: "int32" },
                y: { type: "int32" },
            }
        }
    },
}

設定欄位註解的工具

上述範例的設定欄位含有描述設定欄位的 JSON 註解 一起來看看吧系統可以處理這些註解,並將註解新增至結構化設定的其他區域。 您可以想像,在 Rust 和 C++ 中產生的用戶端程式庫會有相同的說明。

如果開發人員編寫用戶端程式碼,就會在編輯器中取得這些說明做為提示。 系統組合等工具還能提供更詳細的說明和錯誤文字訊息。

這需要使用變更目前不會剖析 JSON 註解的 JSON 剖析程式庫。

config: {
      /// Add a tag to the allow list. Log entries with matching tags will be output to
      /// the console. If no tags are specified, all log entries will be printed.
      allowed_log_tags: {
          type: "vector",
          max_count: 40,
          element: {
              type: "string",
              max_size: 40,
          }
      },

      /// Add a tag to the deny list. Log entries with matching tags will be prevented
      /// from being output to the console. This takes precedence over the allow list.
      denied_log_tags: {
          type: "vector",
          max_count: 40,
          element: {
              type: "string",
              max_size: 40,
          }
      },
}

max_sizemax_count 支援的值上限

資訊清單建議採用下列語言的 max_sizemax_count 屬性支援的值: 向量和字串只要使用 MAX 字串或省略屬性即可。 以致於可能必須趕在最後一刻 大幅更動專案,甚至將專案全部取消

config: {
    network_id: {
        type: "string",
        max_size: "MAX",
    }
}
config: {
    tags: {
        type: "vector",
        element: {
            type: "string"
        }
    }
}

既有藝術與參考資料

元件資訊清單語法

FIDL 語言規格

JSON5 資料交換格式