Ce document a été traduit à l'aide d'une technologie de traduction automatique. Bien que nous nous efforcions de fournir des traductions exactes, nous ne fournissons aucune garantie quant à l'exhaustivité, l'exactitude ou la fiabilité du contenu traduit. En cas de divergence, la version originale anglaise prévaut et fait foi.

Il s'agit d'une documentation non publiée pour Admission Controller 1.34-dev.

Créer une nouvelle stratégie de mutation

Les stratégies de mutation sont similaires aux stratégies de validation, mais ont également la capacité de muter un objet entrant.

Elles peuvent :

  • Rejeter une demande

  • Accepter une demande sans changer l’objet entrant

  • Muter l’objet entrant selon leurs besoins et accepter la demande

Écrire une stratégie de mutation SUSE Security Admission Controller est simple. Vous utiliserez la stratégie de validation créée dans les sections précédentes, et avec quelques modifications, la transformerez en stratégie de mutation.

Votre stratégie utilise la même logique de validation définie précédemment, mais elle ajoute également une annotation à tous les Pods qui ont un nom valide.

Essayer de créer un Pod comme ceci :

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

Conduit à la création de ce Pod :

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

Écrire le code de mutation

Le code de mutation se trouve dans la fonction validate. Vous devez changer cette fonction pour approuver la demande en utilisant mutate_request au lieu de accept_request.

Voici à quoi la fonction validate dans lib.rs devrait ressembler :

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()
        }
    }
}

Comparé au code précédent, vous avez apporté trois modifications :

  1. Nous avons défini l’objet pod comme mutable, voir le mot-clé mut. Ceci est nécessaire car nous allons étendre son attribut metadata.annotations.

  2. Voici le code qui prend le annotations existant, ajoute le nouveau, et remet finalement l’objet annotations mis à jour dans l’instance pod d’origine.

  3. Sérialisez l’objet pod en un serde_json::Value générique et renvoyez ensuite une réponse de mutation.

Après avoir effectué ces modifications, il est temps de relancer les tests unitaires :

$ 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

Comme vous pouvez le voir, le accept_pod_with_valid_name échoue car la réponse contient un objet muté. Il semble que notre code fonctionne.

Mettez à jour les tests unitaires

Vous pouvez mettre à jour le accept_pod_with_valid_name dans lib.rs pour qu’il ressemble à ceci :

#[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(())
}

Comparé au premier test, il y a deux modifications :

  1. Modifiez l’instruction assert! afin que la demande soit toujours acceptée, mais qu’elle inclue également un objet muté.

  2. Créé une instance Pod à partir de l’objet muté qui fait partie de la réponse. Affirmez que l’objet Pod muté a le bon metadata.annotations.

Exécutez à nouveau les tests, cette fois tous doivent réussir :

$ 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

Comme vous pouvez le voir, la création d’une stratégie de mutation est simple.