diff --git a/Cargo.toml b/Cargo.toml index 3bd92d0eb..219d65bfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,8 @@ hyper-openssl = "0.10.2" hyper-rustls = { version = "0.27.0", default-features = false } hyper-socks2 = { version = "0.9.0", default-features = false } hyper-timeout = "0.5.1" -json-patch = "1.0.0" +json-patch = "2.0.0" +jsonptr = "0.4.7" jsonpath-rust = "0.5.0" k8s-openapi = { version = "0.22.0", default-features = false } openssl = "0.10.36" diff --git a/examples/Cargo.toml b/examples/Cargo.toml index cce3dccf8..e735a1f3d 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -28,6 +28,7 @@ garde = { version = "0.18.0", default-features = false, features = ["derive"] } anyhow.workspace = true futures = { workspace = true, features = ["async-await"] } jsonpath-rust.workspace = true +jsonptr.workspace = true kube = { path = "../kube", version = "^0.91.0", default-features = false, features = ["admission"] } kube-derive = { path = "../kube-derive", version = "^0.91.0", default-features = false } # only needed to opt out of schema k8s-openapi.workspace = true diff --git a/examples/admission_controller.rs b/examples/admission_controller.rs index 50cc87d69..10b9c6573 100644 --- a/examples/admission_controller.rs +++ b/examples/admission_controller.rs @@ -1,8 +1,11 @@ +use jsonptr::Pointer; use kube::core::{ admission::{AdmissionRequest, AdmissionResponse, AdmissionReview}, DynamicObject, Resource, ResourceExt, }; -use std::{convert::Infallible, error::Error}; + +use kube::runtime::finalizer; +use std::{convert::Infallible, error::Error, str::FromStr}; use tracing::*; use warp::{reply, Filter, Reply}; @@ -75,13 +78,13 @@ fn mutate(res: AdmissionResponse, obj: &DynamicObject) -> Result<AdmissionRespon // Ensure labels exist before adding a key to it if obj.meta().labels.is_none() { patches.push(json_patch::PatchOperation::Add(json_patch::AddOperation { - path: "/metadata/labels".into(), + path: Pointer::new(["metadata", "labels"]), value: serde_json::json!({}), })); } // Add our label patches.push(json_patch::PatchOperation::Add(json_patch::AddOperation { - path: "/metadata/labels/admission".into(), + path: Pointer::new(["metadata", "labels", "admission"]), value: serde_json::Value::String("modified-by-admission-controller".into()), })); Ok(res.with_patch(json_patch::Patch(patches))?) diff --git a/kube-core/Cargo.toml b/kube-core/Cargo.toml index 9b38f0335..66f528e25 100644 --- a/kube-core/Cargo.toml +++ b/kube-core/Cargo.toml @@ -41,3 +41,4 @@ k8s-openapi = { workspace = true, features = ["latest"] } assert-json-diff.workspace = true kube = { path = "../kube", version = "<1.0.0, >=0.53.0" } serde_yaml.workspace = true +jsonptr.workspace = true diff --git a/kube-core/src/admission.rs b/kube-core/src/admission.rs index f800e20ff..afcff1587 100644 --- a/kube-core/src/admission.rs +++ b/kube-core/src/admission.rs @@ -223,11 +223,12 @@ pub enum Operation { /// .into_review(); /// /// use json_patch::{AddOperation, Patch, PatchOperation}; +/// use jsonptr::Pointer; /// /// // A response adding a label to the resource. /// let _: AdmissionReview<_> = AdmissionResponse::from(&req) /// .with_patch(Patch(vec![PatchOperation::Add(AddOperation { -/// path: "/metadata/labels/my-label".to_owned(), +/// path: Pointer::new(["metadata","labels","my-label"]), /// value: serde_json::Value::String("my-value".to_owned()), /// })])) /// .unwrap() diff --git a/kube-runtime/Cargo.toml b/kube-runtime/Cargo.toml index 176013d8f..4d53c217a 100644 --- a/kube-runtime/Cargo.toml +++ b/kube-runtime/Cargo.toml @@ -41,6 +41,7 @@ tokio = { workspace = true, features = ["time"] } tokio-util = { workspace = true, features = ["time"] } tracing.workspace = true json-patch.workspace = true +jsonptr.workspace = true serde_json.workspace = true thiserror.workspace = true backoff.workspace = true diff --git a/kube-runtime/src/finalizer.rs b/kube-runtime/src/finalizer.rs index 4800e3a84..32cbaa70d 100644 --- a/kube-runtime/src/finalizer.rs +++ b/kube-runtime/src/finalizer.rs @@ -2,12 +2,14 @@ use crate::controller::Action; use futures::{TryFuture, TryFutureExt}; use json_patch::{AddOperation, PatchOperation, RemoveOperation, TestOperation}; +use jsonptr::Pointer; use kube_client::{ api::{Patch, PatchParams}, Api, Resource, ResourceExt, }; + use serde::{de::DeserializeOwned, Serialize}; -use std::{error::Error as StdError, fmt::Debug, sync::Arc}; +use std::{error::Error as StdError, fmt::Debug, str::FromStr, sync::Arc}; use thiserror::Error; #[derive(Debug, Error)] @@ -25,6 +27,8 @@ where RemoveFinalizer(#[source] kube_client::Error), #[error("object has no name")] UnnamedObject, + #[error("invalid finalizer")] + InvalidFinalizer, } struct FinalizerState { @@ -138,10 +142,14 @@ where // `Test` ensures that we fail instead of deleting someone else's finalizer // (in which case a new `Cleanup` event will be sent) PatchOperation::Test(TestOperation { - path: finalizer_path.clone(), + path: Pointer::from_str(finalizer_path.as_str()) + .map_err(|_err| Error::InvalidFinalizer)?, value: finalizer_name.into(), }), - PatchOperation::Remove(RemoveOperation { path: finalizer_path }), + PatchOperation::Remove(RemoveOperation { + path: Pointer::from_str(finalizer_path.as_str()) + .map_err(|_err| Error::InvalidFinalizer)?, + }), ])), ) .await @@ -156,11 +164,13 @@ where let patch = json_patch::Patch(if obj.finalizers().is_empty() { vec![ PatchOperation::Test(TestOperation { - path: "/metadata/finalizers".to_string(), + path: Pointer::from_str("/metadata/finalizers") + .map_err(|_err| Error::InvalidFinalizer)?, value: serde_json::Value::Null, }), PatchOperation::Add(AddOperation { - path: "/metadata/finalizers".to_string(), + path: Pointer::from_str("/metadata/finalizers") + .map_err(|_err| Error::InvalidFinalizer)?, value: vec![finalizer_name].into(), }), ] @@ -170,11 +180,13 @@ where // https://github.com/kube-rs/kube/issues/964#issuecomment-1197311254), // so we need to fail and retry if anyone else has added the finalizer in the meantime PatchOperation::Test(TestOperation { - path: "/metadata/finalizers".to_string(), + path: Pointer::from_str("/metadata/finalizers") + .map_err(|_err| Error::InvalidFinalizer)?, value: obj.finalizers().into(), }), PatchOperation::Add(AddOperation { - path: "/metadata/finalizers/-".to_string(), + path: Pointer::from_str("/metadata/finalizers/-") + .map_err(|_err| Error::InvalidFinalizer)?, value: finalizer_name.into(), }), ]