使用欄位遮罩更新資源

在 Google Cloud中更新資源時,您通常會想修改特定欄位,而不是替換整個資源。Rust 適用的 Cloud 用戶端程式庫支援欄位遮罩,因此您可以指定更新作業要修改哪些欄位,並保留所有其他欄位。

事前準備

本文件使用 Secret Manager API,但同樣的概念也適用於其他 Google Cloud 服務。

執行本文件中的程式碼前,請先完成下列必要條件:

依附元件

google-cloud-wkt crate 為 Google CloudAPI 提供知名類型,包括 FieldMask。將 google-cloud-secretmanager-v1、google-cloud-wkt、anyhow 和 tokio 新增至 Cargo.toml 檔案:

cargo add google-cloud-secretmanager-v1 google-cloud-wkt anyhow
cargo add tokio --features macros,rt-multi-thread

欄位遮罩的運作方式

FieldMask 包含一組符號欄位路徑,可識別更新作業修改的欄位,或讀取作業傳回的欄位。

在更新作業中,服務只會變更遮罩中列出的欄位,並忽略要求訊息中任何未列出欄位的值。如果您在更新要求中省略欄位遮罩,作業會套用至所有可變動的欄位。

如要將特定欄位重設為預設值,請在遮罩中加入欄位路徑,並將該欄位保留在資源結構體的預設值。

更新資源欄位

如要建立密鑰,並使用 FieldMask 選擇性更新其 labels 和 annotations 欄位,請完成下列步驟:

  1. 初始化 SecretManagerService 用戶端,並建立 labels 和 annotations 為空的密鑰:

    let client = SecretManagerService::builder().build().await?;
    
    let secret = client
        .create_secret()
        .set_parent(format!("projects/{project_id}"))
        .set_secret_id("your-secret")
        .set_secret(
            Secret::new().set_replication(Replication::new().set_automatic(Automatic::new())),
        )
        .send()
        .await?;
    println!("CREATE = {secret:?}");

  2. 傳送 update_secret 要求,設定 labels、annotations、目前的 etag (防止覆寫並行更新),以及涵蓋 "annotations" 和 "labels" 的更新遮罩:

    let tag = |mut labels: HashMap<_, _>, msg: &str| {
        labels.insert("updated".to_string(), msg.to_string());
        labels
    };
    
    let update = client
        .update_secret()
        .set_secret(
            Secret::new()
                .set_name(&secret.name)
                .set_etag(secret.etag)
                .set_labels(tag(secret.labels, "your-label"))
                .set_annotations(tag(secret.annotations, "your-annotations")),
        )
        .set_update_mask(FieldMask::default().set_paths(["annotations", "labels"]))
        .send()
        .await?;
    println!("UPDATE = {update:?}");

    set_update_mask 呼叫會指定要修改的確切欄位路徑:

    .set_update_mask(FieldMask::default().set_paths(["annotations", "labels"]))

輸出內容會確認作業已更新這兩個欄位:

labels: {"updated": "your-label"},
...
annotations: {"updated": "your-annotations"},

完整程式碼

下列函式會結合密鑰建立和欄位遮罩更新:

use google_cloud_secretmanager_v1::client::SecretManagerService;
use google_cloud_secretmanager_v1::model::replication::Automatic;
use google_cloud_secretmanager_v1::model::{Replication, Secret};
use google_cloud_wkt::FieldMask;
use std::collections::HashMap;

pub async fn sample(project_id: &str) -> anyhow::Result<()> {
    let client = SecretManagerService::builder().build().await?;

    let secret = client
        .create_secret()
        .set_parent(format!("projects/{project_id}"))
        .set_secret_id("your-secret")
        .set_secret(
            Secret::new().set_replication(Replication::new().set_automatic(Automatic::new())),
        )
        .send()
        .await?;
    println!("CREATE = {secret:?}");

    let tag = |mut labels: HashMap<_, _>, msg: &str| {
        labels.insert("updated".to_string(), msg.to_string());
        labels
    };

    let update = client
        .update_secret()
        .set_secret(
            Secret::new()
                .set_name(&secret.name)
                .set_etag(secret.etag)
                .set_labels(tag(secret.labels, "your-label"))
                .set_annotations(tag(secret.annotations, "your-annotations")),
        )
        .set_update_mask(FieldMask::default().set_paths(["annotations", "labels"]))
        .send()
        .await?;
    println!("UPDATE = {update:?}");

    Ok(())
}

清除所用資源

為避免系統向您的 Google Cloud 帳戶收取費用,並防止後續執行時發生 ALREADY_EXISTS 錯誤,請在完成後刪除 your-secret 密鑰。