Dieses Dokument wurde mithilfe automatisierter maschineller Übersetzungstechnologie übersetzt. Wir bemühen uns um korrekte Übersetzungen, übernehmen jedoch keine Gewähr für die Vollständigkeit, Richtigkeit oder Zuverlässigkeit der übersetzten Inhalte. Im Falle von Abweichungen ist die englische Originalversion maßgebend und stellt den verbindlichen Text dar.

Dies ist eine unveröffentlichte Dokumentation für Admission Controller 1.34-dev.

Erstellen einer neuen Mutationsrichtlinie

Mutationsrichtlinien sind ähnlich wie Validierungsrichtlinien, haben jedoch auch die Fähigkeit, ein eingehendes Objekt zu mutieren.

Sie können:

  • Eine Anfrage ablehnen

  • Eine Anfrage akzeptieren, ohne das eingehende Objekt zu ändern

  • Das eingehende Objekt nach Bedarf mutieren und die Anfrage akzeptieren

Das Schreiben einer SUSE Security Admission Controller Mutationsrichtlinie ist unkompliziert. Sie verwenden die in den vorherigen Abschnitten erstellte Validierungsrichtlinie und verwandeln sie mit einigen Änderungen in eine Mutationsrichtlinie.

Ihre Richtlinie verwendet die gleiche Validierungslogik, die zuvor definiert wurde, fügt jedoch allen Pods, die einen gültigen Namen haben, eine Annotation hinzu.

Der Versuch, einen Pod wie diesen zu erstellen:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
    - name: nginx
      image: nginx:latest

führt zur Erstellung dieses Pods:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  annotations:
    kubewarden.policy.demo/inspected: true
spec:
  containers:
    - name: nginx
      image: nginx:latest

Schreiben Sie den Mutationscode

Der Mutationscode befindet sich in der validate Funktion. Sie sollten diese Funktion ändern, um die Anfrage mit mutate_request anstelle von accept_request zu genehmigen.

So sollte die validate Funktion in lib.rs aussehen:

fn validate(payload: &[u8]) -> CallResult {
    let validation_request: ValidationRequest<Settings> = ValidationRequest::new(payload)?;

    info!(LOG_DRAIN, "starting validation");
    if validation_request.request.kind.kind != apicore::Pod::KIND {
        warn!(LOG_DRAIN, "Policy validates Pods only. Accepting resource"; "kind" => &validation_request.request.kind.kind);
        return kubewarden::accept_request();
    }

    match serde_json::from_value::<apicore::Pod>(validation_request.request.object) {
        // NOTE 1
        Ok(mut pod) => {
            let pod_name = pod.metadata.name.clone().unwrap_or_default();
            if validation_request
                .settings
                .invalid_names
                .contains(&pod_name)
            {
                kubewarden::reject_request(
                    Some(format!("pod name {:?} is not accepted", pod_name)),
                    None,
                    None,
                    None,
                )
            } else {
                // NOTE 2
                let mut new_annotations = pod.metadata.annotations.clone().unwrap_or_default();
                new_annotations.insert(
                    String::from("kubewarden.policy.demo/inspected"),
                    String::from("true"),
                );
                pod.metadata.annotations = Some(new_annotations);

                // NOTE 3
                let mutated_object = serde_json::to_value(pod)?;
                kubewarden::mutate_request(mutated_object)
            }
        }
        Err(_) => {
            // We were forwarded a request we cannot unmarshal or
            // understand, just accept it
            kubewarden::accept_request()
        }
    }
}

Im Vergleich zum vorherigen Code haben Sie drei Änderungen vorgenommen:

  1. Wir haben das pod Objekt als veränderbar definiert, siehe das mut Schlüsselwort. Dies ist notwendig, da wir das metadata.annotations-Attribut erweitern werden.

  2. Dies ist der Code, der das vorhandene annotations nimmt, das neue hinzufügt und schließlich das aktualisierte annotations-Objekt wieder in die ursprüngliche pod-Instanz einfügt.

  3. Serialisieren Sie das pod-Objekt in ein generisches serde_json::Value und geben Sie dann eine Mutationsantwort zurück.

Nachdem diese Änderungen vorgenommen wurden, ist es Zeit, die Unit-Tests erneut auszuführen:

$ cargo test
   Compiling demo-a v0.1.0 (/home/jhk/projects/suse/tmp/demo)
    Finished test [unoptimized + debuginfo] target(s) in 0.95s
     Running unittests src/lib.rs (target/debug/deps/demo_a-634b88b0dcb6e707)

running 5 tests
test settings::tests::reject_settings_without_a_list_of_invalid_names ... ok
test settings::tests::accept_settings_with_a_list_of_invalid_names ... ok
test tests::accept_request_with_non_pod_resource ... ok
test tests::reject_pod_with_invalid_name ... ok
test tests::accept_pod_with_valid_name ... FAILED

failures:

---- tests::accept_pod_with_valid_name stdout ----
{"column":5,"file":"src/lib.rs","level":"info","line":34,"message":"starting validation","policy":"sample-policy"}
thread 'tests::accept_pod_with_valid_name' panicked at src/lib.rs:98:9:
Something mutated with test case: Pod creation with valid name
note: run with `+RUST_BACKTRACE=1+` environment variable to display a backtrace


failures:
    tests::accept_pod_with_valid_name

test result: FAILED. 4 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Wie Sie sehen können, schlägt das accept_pod_with_valid_name fehl, da die Antwort ein mutiertes Objekt enthält. Es sieht so aus, als ob unser Code funktioniert.

Aktualisieren Sie die Unit-Tests

Sie können das accept_pod_with_valid_name in lib.rs so aktualisieren:

#[test]
fn accept_pod_with_valid_name() -> Result<(), ()> {
    let mut invalid_names = HashSet::new();
    invalid_names.insert(String::from("bad_name1"));
    let settings = Settings { invalid_names };

    let request_file = "test_data/pod_creation.json";
    let tc = Testcase {
        name: String::from("Pod creation with valid name"),
        fixture_file: String::from(request_file),
        expected_validation_result: true,
        settings,
    };

    let res = tc.eval(validate).unwrap();
    // NOTE 1
    assert!(
        res.mutated_object.is_some(),
        "Expected accepted object to be mutated",
    );

    // NOTE 2
    let final_pod =
        serde_json::from_value::<apicore::Pod>(res.mutated_object.unwrap()).unwrap();
    let final_annotations = final_pod.metadata.annotations.unwrap();
    assert_eq!(
        final_annotations.get_key_value("kubewarden.policy.demo/inspected"),
        Some((
            &String::from("kubewarden.policy.demo/inspected"),
            &String::from("true")
        )),
    );

    Ok(())
}

Im Vergleich zum ersten Test gibt es zwei Änderungen:

  1. Ändern Sie die assert!-Anweisung so, dass die Anfrage weiterhin akzeptiert wird, aber auch ein mutiertes Objekt enthält.

  2. Erstellte eine Pod-Instanz, die vom mutierten Objekt ausgeht, das Teil der Antwort ist. Stellen Sie sicher, dass das mutierte Pod-Objekt das richtige metadata.annotations hat.

Führen Sie die Tests erneut aus, diesmal sollen alle bestehen:

$ cargo test
   Compiling demo-a v0.1.0 (/home/jhk/projects/suse/tmp/demo)
    Finished test [unoptimized + debuginfo] target(s) in 1.25s
     Running unittests src/lib.rs (target/debug/deps/demo_a-634b88b0dcb6e707)

running 5 tests
test settings::tests::accept_settings_with_a_list_of_invalid_names ... ok
test settings::tests::reject_settings_without_a_list_of_invalid_names ... ok
test tests::accept_request_with_non_pod_resource ... ok
test tests::reject_pod_with_invalid_name ... ok
test tests::accept_pod_with_valid_name ... ok

test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Wie Sie sehen können, ist die Erstellung einer Mutationsrichtlinie unkompliziert.