From f548655e04b8bf60d45c77401073763a74602a8f Mon Sep 17 00:00:00 2001 From: Danil-Grigorev Date: Sat, 7 Sep 2024 23:45:55 +0200 Subject: [PATCH 1/4] Patch implementation for unstable client Signed-off-by: Danil-Grigorev --- kube-client/src/client/client_ext.rs | 79 ++++++++++++++++++++++++++-- kube-client/src/error.rs | 6 +++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/kube-client/src/client/client_ext.rs b/kube-client/src/client/client_ext.rs index ced2df626..2eef94d3e 100644 --- a/kube-client/src/client/client_ext.rs +++ b/kube-client/src/client/client_ext.rs @@ -1,13 +1,16 @@ use crate::{Client, Error, Result}; use k8s_openapi::{ - api::core::v1::{LocalObjectReference, Namespace as k8sNs, ObjectReference}, + api::{ + core::v1::{LocalObjectReference, Namespace as k8sNs, ObjectReference}, + }, apimachinery::pkg::apis::meta::v1::OwnerReference, }; use kube_core::{ object::ObjectList, - params::{GetParams, ListParams}, + params::{GetParams, ListParams, Patch, PatchParams}, request::Request, - ApiResource, ClusterResourceScope, DynamicResourceScope, NamespaceResourceScope, Resource, + ApiResource, ClusterResourceScope, DynamicResourceScope, NamespaceResourceScope, ObjectMeta, Resource, + ResourceExt, }; use serde::{de::DeserializeOwned, Serialize}; use std::fmt::Debug; @@ -383,6 +386,76 @@ impl Client { req.extensions_mut().insert("list"); self.request::>(req).await } + + /// Perform apply patch on the provided `Resource` implementing type `K` + /// + /// ```no_run + /// # use k8s_openapi::api::core::v1::; + /// # use k8s_openapi::api::core::v1::Service; + /// # use kube::client::scope::Namespace; + /// # use kube::prelude::*; + /// # use kube::api::{PatchParams, Patch}; + /// # async fn wrapper() -> Result<(), Box> { + /// # let client: kube::Client = todo!(); + /// let pod: Pod = client.get("some_pod", &Namespace::from("default")).await?; + /// let pp = &PatchParams::apply("controller").force(); + /// // Perform an apply patch on the resource + /// client.apply(pod, pp).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn apply(&self, resource: &K, pp: &PatchParams) -> Result + where + K: ResourceExt + Serialize + DeserializeOwned + Clone + Debug, + ::DynamicType: Default, + { + let meta = resource.meta(); + let name = meta.name.as_ref().ok_or(Error::NameResolve)?; + let url = K::url_path(&Default::default(), meta.namespace.as_deref()); + let req = Request::new(url); + + let mut resource = resource.clone(); + resource.meta_mut().managed_fields = None; + let patch = &Patch::Apply(resource); + let req = req.patch(name, pp, patch).map_err(Error::BuildRequest)?; + self.request::(req).await + } + + /// Perform apply patch on the provided `Resource` status, implementing type `K` + /// + /// ```no_run + /// # use k8s_openapi::api::core::v1::Pod; + /// # use k8s_openapi::api::core::v1::Service; + /// # use kube::client::scope::Namespace; + /// # use kube::prelude::*; + /// # use kube::api::{PatchParams, Patch}; + /// # async fn wrapper() -> Result<(), Box> { + /// # let client: kube::Client = todo!(); + /// let pod: Pod = client.get("some_pod", &Namespace::from("default")).await?; + /// let pp = &PatchParams::apply("controller").force(); + /// // Perform an apply patch on the resource status + /// client.apply(pod, pp).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn apply_status(&self, resource: &K, pp: &PatchParams) -> Result + where + K: ResourceExt + Serialize + DeserializeOwned + Clone + Debug, + ::DynamicType: Default, + { + let meta = resource.meta(); + let name = meta.name.as_ref().ok_or(Error::NameResolve)?; + let url = K::url_path(&Default::default(), meta.namespace.as_deref()); + let req = Request::new(url); + + let mut resource = resource.clone(); + resource.meta_mut().managed_fields = None; + let patch = &Patch::Apply(resource); + let req = req + .patch_subresource("status", name, pp, patch) + .map_err(Error::BuildRequest)?; + self.request::(req).await + } } // Resource url_path resolver diff --git a/kube-client/src/error.rs b/kube-client/src/error.rs index 23bd2a6a9..e8cb91328 100644 --- a/kube-client/src/error.rs +++ b/kube-client/src/error.rs @@ -92,6 +92,12 @@ pub enum Error { #[cfg_attr(docsrs, doc(cfg(feature = "unstable-client")))] #[error("Reference resolve error: {0}")] RefResolve(String), + + /// Error resolving resource name + #[cfg(feature = "unstable-client")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable-client")))] + #[error("Resource has no name")] + NameResolve, } #[derive(Error, Debug)] From f7e57217e27b23a0e925b89b60c47cc0caa29bd9 Mon Sep 17 00:00:00 2001 From: Danil-Grigorev Date: Sun, 6 Oct 2024 14:52:24 +0200 Subject: [PATCH 2/4] WIP: prototyping apply patch wrapper Signed-off-by: Danil-Grigorev --- kube-client/src/client/client_ext.rs | 54 +++++++++++++++++++--------- kube-core/src/metadata.rs | 6 ++-- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/kube-client/src/client/client_ext.rs b/kube-client/src/client/client_ext.rs index 2eef94d3e..30eeba82e 100644 --- a/kube-client/src/client/client_ext.rs +++ b/kube-client/src/client/client_ext.rs @@ -1,16 +1,14 @@ use crate::{Client, Error, Result}; use k8s_openapi::{ - api::{ - core::v1::{LocalObjectReference, Namespace as k8sNs, ObjectReference}, - }, + api::core::v1::{LocalObjectReference, Namespace as k8sNs, ObjectReference}, apimachinery::pkg::apis::meta::v1::OwnerReference, }; use kube_core::{ object::ObjectList, params::{GetParams, ListParams, Patch, PatchParams}, request::Request, - ApiResource, ClusterResourceScope, DynamicResourceScope, NamespaceResourceScope, ObjectMeta, Resource, - ResourceExt, + ApiResource, ClusterResourceScope, DynamicResourceScope, NamespaceResourceScope, Resource, ResourceExt, + TypeMeta, }; use serde::{de::DeserializeOwned, Serialize}; use std::fmt::Debug; @@ -233,6 +231,18 @@ pub enum NamespaceError { MissingName, } +#[derive(Serialize, Clone, Debug)] +/// ApplyObject allows to wrap an object into Patch::Apply compatible structure, +/// with populated TypeMeta. +pub struct ApplyObject { + /// Contains the API version and type of the request. + #[serde(flatten)] + pub types: TypeMeta, + /// Contains the object data. + #[serde(flatten)] + pub data: R, +} + /// Generic client extensions for the `unstable-client` feature /// /// These methods allow users to query across a wide-array of resources without needing @@ -390,7 +400,7 @@ impl Client { /// Perform apply patch on the provided `Resource` implementing type `K` /// /// ```no_run - /// # use k8s_openapi::api::core::v1::; + /// # use k8s_openapi::api::core::v1::Pod; /// # use k8s_openapi::api::core::v1::Service; /// # use kube::client::scope::Namespace; /// # use kube::prelude::*; @@ -404,7 +414,7 @@ impl Client { /// # Ok(()) /// # } /// ``` - pub async fn apply(&self, resource: &K, pp: &PatchParams) -> Result + pub async fn apply(&self, resource: &K, pp: &PatchParams) -> Result where K: ResourceExt + Serialize + DeserializeOwned + Clone + Debug, ::DynamicType: Default, @@ -414,10 +424,17 @@ impl Client { let url = K::url_path(&Default::default(), meta.namespace.as_deref()); let req = Request::new(url); - let mut resource = resource.clone(); - resource.meta_mut().managed_fields = None; - let patch = &Patch::Apply(resource); - let req = req.patch(name, pp, patch).map_err(Error::BuildRequest)?; + let apply = ApplyObject:: { + types: TypeMeta::resource::(), + data: { + let mut resource = resource.clone(); + resource.meta_mut().managed_fields = None; + resource + }, + }; + let req = req + .patch(name, pp, &Patch::Apply(apply)) + .map_err(Error::BuildRequest)?; self.request::(req).await } @@ -438,7 +455,7 @@ impl Client { /// # Ok(()) /// # } /// ``` - pub async fn apply_status(&self, resource: &K, pp: &PatchParams) -> Result + pub async fn apply_status(&self, resource: &K, pp: &PatchParams) -> Result where K: ResourceExt + Serialize + DeserializeOwned + Clone + Debug, ::DynamicType: Default, @@ -448,11 +465,16 @@ impl Client { let url = K::url_path(&Default::default(), meta.namespace.as_deref()); let req = Request::new(url); - let mut resource = resource.clone(); - resource.meta_mut().managed_fields = None; - let patch = &Patch::Apply(resource); + let apply = ApplyObject:: { + types: TypeMeta::resource::(), + data: { + let mut resource = resource.clone(); + resource.meta_mut().managed_fields = None; + resource + }, + }; let req = req - .patch_subresource("status", name, pp, patch) + .patch_subresource("status", name, pp, &Patch::Apply(apply)) .map_err(Error::BuildRequest)?; self.request::(req).await } diff --git a/kube-core/src/metadata.rs b/kube-core/src/metadata.rs index 67edf6e16..be7dd5b6c 100644 --- a/kube-core/src/metadata.rs +++ b/kube-core/src/metadata.rs @@ -45,10 +45,10 @@ impl TypeMeta { /// assert_eq!(type_meta.kind, "Pod"); /// assert_eq!(type_meta.api_version, "v1"); /// ``` - pub fn resource>() -> Self { + pub fn resource>() -> Self { TypeMeta { - api_version: K::api_version(&()).into(), - kind: K::kind(&()).into(), + api_version: K::api_version(&Default::default()).into(), + kind: K::kind(&Default::default()).into(), } } } From a2067e73de03a322e3e6ef26e341240626969663 Mon Sep 17 00:00:00 2001 From: Danil-Grigorev Date: Sun, 6 Oct 2024 23:02:25 +0200 Subject: [PATCH 3/4] Fix invocation in methods docs Signed-off-by: Danil-Grigorev --- kube-client/src/client/client_ext.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kube-client/src/client/client_ext.rs b/kube-client/src/client/client_ext.rs index 30eeba82e..9ed48d447 100644 --- a/kube-client/src/client/client_ext.rs +++ b/kube-client/src/client/client_ext.rs @@ -410,7 +410,7 @@ impl Client { /// let pod: Pod = client.get("some_pod", &Namespace::from("default")).await?; /// let pp = &PatchParams::apply("controller").force(); /// // Perform an apply patch on the resource - /// client.apply(pod, pp).await?; + /// client.apply(&pod, pp).await?; /// # Ok(()) /// # } /// ``` @@ -451,7 +451,7 @@ impl Client { /// let pod: Pod = client.get("some_pod", &Namespace::from("default")).await?; /// let pp = &PatchParams::apply("controller").force(); /// // Perform an apply patch on the resource status - /// client.apply(pod, pp).await?; + /// client.apply_status(&pod, pp).await?; /// # Ok(()) /// # } /// ``` From 770b954db3231a544f04df06b700dc127094fa2f Mon Sep 17 00:00:00 2001 From: Danil-Grigorev Date: Tue, 15 Oct 2024 14:47:54 +0200 Subject: [PATCH 4/4] Unify type meta creation for default constraint Signed-off-by: Danil-Grigorev --- kube-core/src/metadata.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kube-core/src/metadata.rs b/kube-core/src/metadata.rs index be7dd5b6c..300b25e1c 100644 --- a/kube-core/src/metadata.rs +++ b/kube-core/src/metadata.rs @@ -28,10 +28,10 @@ impl TypeMeta { /// assert_eq!(type_meta.kind, "PodList"); /// assert_eq!(type_meta.api_version, "v1"); /// ``` - pub fn list>() -> Self { + pub fn list>() -> Self { TypeMeta { - api_version: K::api_version(&()).into(), - kind: K::kind(&()).to_string() + "List", + api_version: K::api_version(&Default::default()).into(), + kind: K::kind(&Default::default()).to_string() + "List", } }