Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start Refining Errors #686

Merged
merged 9 commits into from
Nov 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ UNRELEASED
===================
* see https://github.com/kube-rs/kube-rs/compare/0.63.2...master

* BREAKING: The following breaking changes were made as a part of an effort to refine errors (#688).
- Removed `kube::core::Error` and `kube::core::Result`. `kube::core::Error` was replaced by more specific errors as described below.
- Replaced `kube::core::Error::InvalidGroupVersion` with `kube::core::gvk::ParseGroupVersionError`.
- Changed the error returned from `kube::core::admission::AdmissionRequest::with_patch` to `kube::core::admission::SerializePatchError` (was `kube::core::Error::SerdeError`).
- Changed the error associated with `TryInto<AdmissionRequest<T>` to `kube::core::admission::ConvertAdmissionReviewError` (was `kube::core::Error::RequestValidation`).
- Changed the error returned from methods of `kube::core::Request` to `kube::core::request::Error` (was `kube::core::Error`). `kube::core::request::Error` represents possible errors when building an HTTP request. The removed `kube::core::Error` had `RequestValidation(String)`, `SerdeError(serde_json::Error)`, and `HttpError(http::Error)` variants. They are now `Validation(String)`, `SerializeBody(serde_json::Error)`, and `BuildRequest(http::Error)` respectively in `kube::core::request::Error`.
- Replaced `kube::Error::RequestValidation(String)` variant with `kube::Error::BuildRequest(kube::core::request::Error)`. This variant includes possible errors when building an HTTP request as described above, and contains errors that was previously grouped under `kube::Error::SerdeError` and `kube::Error::HttpError`.
- Removed `impl From<T> for kube::Error` for the following types: `std::io::Error`, `hyper::Error`, `tower::BoxError`, `std::string::FromUtf8Error`, `http::Error`, `http::uri::InvalidUri`, `serde_json::Error`, `openssl::error::ErrorStack`, `kube::core::Error`, `kube::error::ConfigError`, `kube::error::DisoveryError`, `kube::error::OAuthError`.
- Changed variants of error enums in `kube::runtime`. Replaced `snafu` with `thiserror`.

0.63.2 / 2021-10-28
===================
* `kube::runtime::events`: fix build and hide module on kubernetes < 1.19 (events/v1 missing there) - [#685](https://github.com/kube-rs/kube-rs/issues/685)
Expand Down
2 changes: 1 addition & 1 deletion examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ serde_json = "1.0.68"
serde_yaml = "0.8.21"
tokio = { version = "1.12.0", features = ["full"] }
color-eyre = "0.5.10"
snafu = { version = "0.6.10", features = ["futures"] }
# Some Api::delete methods use Either
either = "1.6.1"
schemars = "0.8.6"
Expand All @@ -54,6 +53,7 @@ json-patch = "0.2.6"
tower = { version = "0.4.6" }
tower-http = { version = "0.1.0", features = ["trace", "decompression-gzip"] }
hyper = { version = "0.14.13", features = ["client", "http1", "stream", "tcp"] }
thiserror = "1.0.29"

[[example]]
name = "configmapgen_controller"
Expand Down
42 changes: 18 additions & 24 deletions examples/configmapgen_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,16 @@ use kube::{
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use snafu::{Backtrace, OptionExt, ResultExt, Snafu};
use std::{collections::BTreeMap, io::BufRead};
use thiserror::Error;
use tokio::time::Duration;

#[derive(Debug, Snafu)]
#[derive(Debug, Error)]
enum Error {
#[snafu(display("Failed to create ConfigMap: {}", source))]
ConfigMapCreationFailed {
source: kube::Error,
backtrace: Backtrace,
},
MissingObjectKey {
name: &'static str,
backtrace: Backtrace,
},
#[error("Failed to create ConfigMap: {0}")]
ConfigMapCreationFailed(#[source] kube::Error),
#[error("MissingObjectKey: {0}")]
MissingObjectKey(&'static str),
}

#[derive(CustomResource, Debug, Clone, Deserialize, Serialize, JsonSchema)]
Expand All @@ -42,12 +37,8 @@ fn object_to_owner_reference<K: Resource<DynamicType = ()>>(
Ok(OwnerReference {
api_version: K::api_version(&()).to_string(),
kind: K::kind(&()).to_string(),
name: meta.name.context(MissingObjectKey {
name: ".metadata.name",
})?,
uid: meta.uid.context(MissingObjectKey {
name: ".metadata.uid",
})?,
name: meta.name.ok_or(Error::MissingObjectKey(".metadata.name"))?,
uid: meta.uid.ok_or(Error::MissingObjectKey(".metadata.uid"))?,
..OwnerReference::default()
})
}
Expand Down Expand Up @@ -76,20 +67,23 @@ async fn reconcile(generator: ConfigMapGenerator, ctx: Context<Data>) -> Result<
};
let cm_api = Api::<ConfigMap>::namespaced(
client.clone(),
generator.metadata.namespace.as_ref().context(MissingObjectKey {
name: ".metadata.namespace",
})?,
generator
.metadata
.namespace
.as_ref()
.ok_or(Error::MissingObjectKey(".metadata.namespace"))?,
);
cm_api
.patch(
cm.metadata.name.as_ref().context(MissingObjectKey {
name: ".metadata.name",
})?,
cm.metadata
.name
.as_ref()
.ok_or(Error::MissingObjectKey(".metadata.name"))?,
&PatchParams::apply("configmapgenerator.kube-rt.nullable.se"),
&Patch::Apply(&cm),
)
.await
.context(ConfigMapCreationFailed)?;
.map_err(Error::ConfigMapCreationFailed)?;
Ok(ReconcilerAction {
requeue_after: Some(Duration::from_secs(300)),
})
Expand Down
23 changes: 15 additions & 8 deletions examples/secret_syncer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,27 @@ use kube::{
finalizer::{finalizer, Event},
},
};
use snafu::{OptionExt, ResultExt, Snafu};
use std::time::Duration;
use thiserror::Error;

#[derive(Debug, Snafu)]
#[derive(Debug, Error)]
enum Error {
#[error("NoName")]
NoName,
#[error("NoNamespace")]
NoNamespace,
UpdateSecret { source: kube::Error },
DeleteSecret { source: kube::Error },
#[error("UpdateSecret: {0}")]
UpdateSecret(#[source] kube::Error),
#[error("DeleteSecret: {0}")]
DeleteSecret(#[source] kube::Error),
}
type Result<T, E = Error> = std::result::Result<T, E>;

fn secret_name_for_configmap(cm: &ConfigMap) -> Result<String> {
Ok(format!("cm---{}", cm.metadata.name.as_deref().context(NoName)?))
Ok(format!(
"cm---{}",
cm.metadata.name.as_deref().ok_or(Error::NoName)?
))
}

async fn apply(cm: ConfigMap, secrets: &kube::Api<Secret>) -> Result<ReconcilerAction> {
Expand All @@ -48,7 +55,7 @@ async fn apply(cm: ConfigMap, secrets: &kube::Api<Secret>) -> Result<ReconcilerA
}),
)
.await
.context(UpdateSecret)?;
.map_err(Error::UpdateSecret)?;
Ok(ReconcilerAction { requeue_after: None })
}

Expand All @@ -63,7 +70,7 @@ async fn cleanup(cm: ConfigMap, secrets: &kube::Api<Secret>) -> Result<Reconcile
kube::Error::Api(ErrorResponse { code: 404, .. }) => Ok(()),
err => Err(err),
})
.context(DeleteSecret)?;
.map_err(Error::DeleteSecret)?;
Ok(ReconcilerAction { requeue_after: None })
}

Expand All @@ -78,7 +85,7 @@ async fn main() -> color_eyre::Result<()> {
)
.run(
|cm, _| {
let ns = cm.meta().namespace.as_deref().context(NoNamespace).unwrap();
let ns = cm.meta().namespace.as_deref().ok_or(Error::NoNamespace).unwrap();
let cms: Api<ConfigMap> = Api::namespaced(kube.clone(), ns);
let secrets: Api<Secret> = Api::namespaced(kube.clone(), ns);
async move {
Expand Down
42 changes: 24 additions & 18 deletions kube-client/src/api/core_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use futures::Stream;
use serde::{de::DeserializeOwned, Serialize};
use std::fmt::Debug;

use crate::{api::Api, Result};
use crate::{api::Api, Error, Result};
use kube_core::{object::ObjectList, params::*, response::Status, WatchEvent};

/// PUSH/PUT/POST/GET abstractions
Expand All @@ -17,15 +17,15 @@ where
/// use kube::{Api, Client};
/// use k8s_openapi::api::core::v1::Pod;
/// #[tokio::main]
/// async fn main() -> Result<(), kube::Error> {
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let client = Client::try_default().await?;
/// let pods: Api<Pod> = Api::namespaced(client, "apps");
/// let p: Pod = pods.get("blog").await?;
/// Ok(())
/// }
/// ```
pub async fn get(&self, name: &str) -> Result<K> {
let mut req = self.request.get(name)?;
let mut req = self.request.get(name).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("get");
self.client.request::<K>(req).await
}
Expand All @@ -38,7 +38,7 @@ where
/// use kube::{api::{Api, ListParams, ResourceExt}, Client};
/// use k8s_openapi::api::core::v1::Pod;
/// #[tokio::main]
/// async fn main() -> Result<(), kube::Error> {
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let client = Client::try_default().await?;
/// let pods: Api<Pod> = Api::namespaced(client, "apps");
/// let lp = ListParams::default().labels("app=blog"); // for this app only
Expand All @@ -49,7 +49,7 @@ where
/// }
/// ```
pub async fn list(&self, lp: &ListParams) -> Result<ObjectList<K>> {
let mut req = self.request.list(lp)?;
let mut req = self.request.list(lp).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("list");
self.client.request::<ObjectList<K>>(req).await
}
Expand All @@ -74,8 +74,8 @@ where
where
K: Serialize,
{
let bytes = serde_json::to_vec(&data)?;
let mut req = self.request.create(pp, bytes)?;
let bytes = serde_json::to_vec(&data).map_err(Error::SerdeError)?;
let mut req = self.request.create(pp, bytes).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("create");
self.client.request::<K>(req).await
}
Expand All @@ -93,7 +93,7 @@ where
/// use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1 as apiexts;
/// use apiexts::CustomResourceDefinition;
/// #[tokio::main]
/// async fn main() -> Result<(), kube::Error> {
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let client = Client::try_default().await?;
/// let crds: Api<CustomResourceDefinition> = Api::all(client);
/// crds.delete("foos.clux.dev", &DeleteParams::default()).await?
Expand All @@ -103,7 +103,7 @@ where
/// }
/// ```
pub async fn delete(&self, name: &str, dp: &DeleteParams) -> Result<Either<K, Status>> {
let mut req = self.request.delete(name, dp)?;
let mut req = self.request.delete(name, dp).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("delete");
self.client.request_status::<K>(req).await
}
Expand All @@ -120,7 +120,7 @@ where
/// use kube::{api::{Api, DeleteParams, ListParams, ResourceExt}, Client};
/// use k8s_openapi::api::core::v1::Pod;
/// #[tokio::main]
/// async fn main() -> Result<(), kube::Error> {
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let client = Client::try_default().await?;
/// let pods: Api<Pod> = Api::namespaced(client, "apps");
/// match pods.delete_collection(&DeleteParams::default(), &ListParams::default()).await? {
Expand All @@ -140,7 +140,10 @@ where
dp: &DeleteParams,
lp: &ListParams,
) -> Result<Either<ObjectList<K>, Status>> {
let mut req = self.request.delete_collection(dp, lp)?;
let mut req = self
.request
.delete_collection(dp, lp)
.map_err(Error::BuildRequest)?;
req.extensions_mut().insert("delete_collection");
self.client.request_status::<ObjectList<K>>(req).await
}
Expand All @@ -153,7 +156,7 @@ where
/// use kube::{api::{Api, PatchParams, Patch, Resource}, Client};
/// use k8s_openapi::api::core::v1::Pod;
/// #[tokio::main]
/// async fn main() -> Result<(), kube::Error> {
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let client = Client::try_default().await?;
/// let pods: Api<Pod> = Api::namespaced(client, "apps");
/// let patch = serde_json::json!({
Expand All @@ -180,7 +183,7 @@ where
pp: &PatchParams,
patch: &Patch<P>,
) -> Result<K> {
let mut req = self.request.patch(name, pp, patch)?;
let mut req = self.request.patch(name, pp, patch).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("patch");
self.client.request::<K>(req).await
}
Expand All @@ -197,7 +200,7 @@ where
/// use kube::{api::{Api, PostParams, ResourceExt}, Client};
/// use k8s_openapi::api::batch::v1::Job;
/// #[tokio::main]
/// async fn main() -> Result<(), kube::Error> {
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let client = Client::try_default().await?;
/// let jobs: Api<Job> = Api::namespaced(client, "apps");
/// let j = jobs.get("baz").await?;
Expand Down Expand Up @@ -233,8 +236,11 @@ where
where
K: Serialize,
{
let bytes = serde_json::to_vec(&data)?;
let mut req = self.request.replace(name, pp, bytes)?;
let bytes = serde_json::to_vec(&data).map_err(Error::SerdeError)?;
let mut req = self
.request
.replace(name, pp, bytes)
.map_err(Error::BuildRequest)?;
req.extensions_mut().insert("replace");
self.client.request::<K>(req).await
}
Expand All @@ -255,7 +261,7 @@ where
/// use k8s_openapi::api::batch::v1::Job;
/// use futures::{StreamExt, TryStreamExt};
/// #[tokio::main]
/// async fn main() -> Result<(), kube::Error> {
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let client = Client::try_default().await?;
/// let jobs: Api<Job> = Api::namespaced(client, "apps");
/// let lp = ListParams::default()
Expand All @@ -281,7 +287,7 @@ where
lp: &ListParams,
version: &str,
) -> Result<impl Stream<Item = Result<WatchEvent<K>>>> {
let mut req = self.request.watch(lp, version)?;
let mut req = self.request.watch(lp, version).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("watch");
self.client.request_events::<K>(req).await
}
Expand Down
Loading