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.

Validierungslogik schreiben

Es ist Zeit, den tatsächlichen Validierungscode zu schreiben. Es ist in der src/lib.rs Datei definiert. In dieser Datei finden Sie eine Funktion namens validate.

Dies ist die im Scaffolding bereitgestellte Funktion:

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

    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();
    }
    // TODO: you can unmarshal any Kubernetes API type you are interested in (2)
    match serde_json::from_value::<apicore::Pod>(validation_request.request.object) {
        Ok(pod) => {
            // TODO: your logic goes here (3)
            if pod.metadata.name == Some("invalid-pod-name".to_string()) {
                let pod_name = pod.metadata.name.unwrap();
                info!(
                    LOG_DRAIN,
                    "rejecting pod";
                    "pod_name" => &pod_name
                );
                kubewarden::reject_request(
                    Some(format!("pod name {} is not accepted", &pod_name)),
                    None,
                    None,
                    None,
                )
            } else {
                info!(LOG_DRAIN, "accepting resource");
                kubewarden::accept_request()
            }
        }
        Err(_) => {
            // TODO: handle as you wish
            // We were forwarded a request we cannot unmarshal or
            // understand, just accept it
            warn!(LOG_DRAIN, "cannot unmarshal resource: this policy does not know how to evaluate this resource; accept it");
            kubewarden::accept_request() (4)
        }
    }
}

Gehen Sie das Code-Listing durch:

  • In der Zeile, die mit ➀ markiert ist. Analysieren Sie das eingehende payload in ein ValidationRequest<Setting> Objekt. Dies füllt automatisch die Settings Instanz innerhalb des ValidationRequest mit den vom Benutzer bereitgestellten Parametern.

  • In der Zeile, die mit ➁ markiert ist. Konvertieren Sie das in die Anfrage eingebettete Kubernetes-Raw-JSON-Objekt in eine Instanz des Pod struct

  • In der Zeile, die mit ➂ markiert ist. Die Anfrage hat ein Pod-Objekt, der Code genehmigt nur die Anfragen, die nicht metadata.name gleich dem fest codierten Wert invalid-pod-name haben.

  • In der Zeile, die mit ➃ markiert ist. Die Anfrage enthält kein Pod-Objekt, daher akzeptiert die Richtlinie die Anfrage.

Wie Sie sehen können, führt der Code bereits eine Validierung durch, die derjenigen ähnelt, die Sie implementieren möchten. Sie müssen nur den fest codierten Wert entfernen und die vom Benutzer über die Richtlinieneinstellungen bereitgestellten Werte verwenden.

Sie können dies tun, indem Sie die validate Funktion in src/lib.rs durch diese ersetzen:

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) {
        Ok(pod) => {
            let pod_name = pod.metadata.name.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 {
                kubewarden::accept_request()
            }
        }
        Err(_) => {
            // We were forwarded a request we cannot unmarshal or
            // understand, just accept it
            kubewarden::accept_request()
        }
    }
}

Unit-Tests

Schließlich können Sie Unit-Tests erstellen, um zu überprüfen, ob der Validierungscode wie erwartet funktioniert.

Die lib.rs Datei hat bereits Tests am Ende der Datei definiert, und wie Sie sehen können, bietet das Rust SDK von Admission Controller auch Testhilfen an.

Darüber hinaus wird das Scaffold-Projekt bereits mit standardmäßigen Testfixtures im test_data Verzeichnis ausgeliefert. Sie werden diese aufgezeichneten Zulassungsanfragen verwenden, um Ihre Unit-Tests zu schreiben.

Ändern Sie den Inhalt des Testabschnitts am Ende von src/lib.rs, damit er so aussieht:

#[cfg(test)]
mod tests {
    use super::*;

    use kubewarden_policy_sdk::test::Testcase;
    use std::collections::HashSet;

    #[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();
        assert!(
            res.mutated_object.is_none(),
            "Something mutated with test case: {}",
            tc.name,
        );

        Ok(())
    }

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

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

        let res = tc.eval(validate).unwrap();
        assert!(
            res.mutated_object.is_none(),
            "Something mutated with test case: {}",
            tc.name,
        );

        Ok(())
    }

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

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

        let res = tc.eval(validate).unwrap();
        assert!(
            res.mutated_object.is_none(),
            "Something mutated with test case: {}",
            tc.name,
        );

        Ok(())
    }
}

Sie haben jetzt drei Unit-Tests in lib.rs definiert:

  • accept_pod_with_valid_name: akzeptiert ein Pod mit einem gültigen Namen

  • reject_pod_with_invalid_name: lehnt ein Pod mit einem ungültigen Namen ab

  • accept_request_with_non_pod_resource: akzeptiert Anfragen, die kein Pod als Objekt haben

Sie können die Unit-Tests erneut ausführen:

$ cargo test
   Compiling demo v0.1.0 (/home/flavio/hacking/kubernetes/kubewarden/demo)
    Finished test [unoptimized + debuginfo] target(s) in 3.45s
     Running target/debug/deps/demo-24670dd6a538fd72

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::accept_pod_with_valid_name ... ok
test tests::reject_pod_with_invalid_name ... ok

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

Das ist alles, was erforderlich ist, wenn Sie eine einfache Validierungsrichtlinie schreiben müssen.