עדכון משאב באמצעות מסכת שדות

כאן מוסבר איך לעדכן משאבים באופן סלקטיבי באמצעות מסכות שדות. כשמעדכנים משאב ב- Google Cloud, לפעמים רוצים לשנות רק שדות ספציפיים ולא להחליף את כל המשאב. ספריות הלקוח של Rust תומכות במסכות שדות, ומאפשרות לכם לשלוט בדיוק באילו שדות של משאב מתעדכנים, בלי לשנות את השדות האחרים.

דרישות מוקדמות

במדריך הזה נעשה שימוש ב-Secret Manager API, אבל המושגים רלוונטיים גם ל Google Cloud משאבים ולשירותים אחרים.

לפני שממשיכים, צריך לפעול לפי ההוראות במאמר יצירה וגישה לסוד באמצעות Secret Manager כדי להפעיל את Secret Manager API ולאמת אותו.

הוראות מלאות להגדרת ספריות Rust זמינות במאמר הגדרת סביבת הפיתוח.

תלויות

אחרי שמפעילים את Secret Manager, צריך להצהיר על יחסי התלות בקובץ Cargo.toml:

cargo add google-cloud-secretmanager-v1
cargo add tokio

התקנה של סוגים מוכרים

‫Crate‏ google_cloud_wkt מכיל סוגים מוכרים של ממשקי API. Google Cloud לסוגים האלה יש בדרך כלל קידוד JSON מותאם אישית, והם עשויים לספק פונקציות המרה לסוגים נפוצים של Rust וממנו. חבילת google_cloud_wkt מכילה את סוג מסכת השדות, FieldMask.

מוסיפים את ה-crate כתלות:

cargo add google-cloud-wkt

מסיכות שדות

FieldMask מייצג קבוצה של נתיבי שדות סמליים. מסכות שדות מציינות קבוצת משנה של שדות שפעולת עדכון משנה או שפעולת אחזור מחזירה.

בפעולת עדכון, מסכת שדות מציינת אילו שדות במשאב המטרה צריך לעדכן. ה-API משנה רק את הערכים של השדות שצוינו במסכה, ומשאיר את השאר ללא שינוי. אם מעבירים משאב כדי לתאר את הערכים המעודכנים, ה-API מתעלם מהערכים של כל השדות שלא נכללים במסכה. אם לא מציינים מסכת שדות בעדכון, הפעולה חלה על כל השדות.

כדי לאפס שדה לערך ברירת המחדל, צריך לכלול את השדה במסכה ולהגדיר את ערך ברירת המחדל במשאב שצוין. כדי לאפס את כל השדות של משאב, צריך לספק מופע ברירת מחדל של המשאב ולהגדיר את כל השדות במסכה, או לא לספק מסכה.

עדכון שדות במשאב

קודם כול, מאתחלים לקוח של Secret Manager ויוצרים סוד:

    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:?}");

הפלט של פעולת היצירה מראה שהשדות labels ו-annotations ריקים.

הקוד הבא מעדכן את השדות labels ו-annotations:

    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_etag מאפשרת להגדיר תג etag לסוד, וכך למנוע החלפה של עדכונים בו-זמניים.

אחרי שמגדירים תוויות והערות לסוד המעודכן, מעבירים מסכת שדות ל-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(())
}