Skip to content

Commit

Permalink
Make VersionMatch follow upstream + configure list semantics in `wa…
Browse files Browse the repository at this point in the history
…tcher::Config` (#1171)

* Make `VersionMatch` follow upstream + decouple enum from `watcher`

Effectively makes the `VersionMatch` an exact duplicate of the upstream,
and adds some builders here and there to compensate for the missing `Any`.

Have changed the watcher interface and made a new enum that maps onto `VersionMatch` instead,
because it does not make sense to run a watcher against a pinned resource version; it will always use 0.

(at least unless/until we build pagination into it).

We could keep the enum that wraps resource version, but this is awkward;
it would be the only one that bundles resourceVersion that way;

- watch api calls get it as a str (outside watchparams)
- get api calls (in a follow-up pr) will also not use the enum

so altogether we are just making it awkward for ourselves by trying to make it nice :(

Signed-off-by: clux <[email protected]>

* better enum variant names in kube-runtime + less aggressive docs

Signed-off-by: clux <[email protected]>

* rename enum variant, clean up tests and helper methods

Signed-off-by: clux <[email protected]>

* use verbs to be more consistent

Signed-off-by: clux <[email protected]>

* Apply suggestions from code review

Co-authored-by: Natalie <[email protected]>
Signed-off-by: Eirik A <[email protected]>

* use match rather than if/else on enum

Signed-off-by: clux <[email protected]>

* fix tests

Signed-off-by: clux <[email protected]>

* remove unset variant, replace with Option

Signed-off-by: clux <[email protected]>

* fix deny

Signed-off-by: clux <[email protected]>

* also fix kube-runtime tests

Signed-off-by: clux <[email protected]>

* ws

Signed-off-by: clux <[email protected]>

* use kubernetes terminology for enum and avoid changing default behaviour

Signed-off-by: clux <[email protected]>

* leftover any

Signed-off-by: clux <[email protected]>

* put caveats on MostRecent

Signed-off-by: clux <[email protected]>

---------

Signed-off-by: clux <[email protected]>
Signed-off-by: Eirik A <[email protected]>
Co-authored-by: Natalie <[email protected]>
  • Loading branch information
clux and nightkr authored Apr 3, 2023
1 parent aed07be commit 4f633ee
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 129 deletions.
4 changes: 4 additions & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,7 @@ name = "syn"
# waiting for pem to bump base64
# https://github.com/jcreekmore/pem-rs/blob/master/Cargo.toml#L16
name = "base64"

[[bans.skip]]
# deep in dependency tree, only dual use via dev dependency
name = "redox_syscall"
109 changes: 70 additions & 39 deletions kube-core/src/params.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
//! A port of request parameter *Optionals from apimachinery/types.go
use std::fmt;

use crate::request::Error;
use serde::Serialize;

/// Controls how the resourceVersion parameter is applied
/// Controls how the resource version parameter is applied for list calls
///
/// Not specifying a `VersionMatch` strategy will give you different semantics
/// depending on what `resource_version`, `limit`, `continue_token` you include with the list request.
///
/// This embeds the resource version when using the `NotOlderThan` or `Exact` variants.
/// See <https://kubernetes.io/docs/reference/using-api/api-concepts/#semantics-for-get-and-list> for details.
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub enum VersionMatch {
/// Matches data with the latest version available in the kube-apiserver database (etcd) (quorum read required).
#[default]
MostRecent,
/// Matches data with the latest version available in the kube-apiserver cache.
Any,
/// Matches data at least as new as the provided resourceVersion.
NotOlderThan(String),
/// Matches data at the exact resourceVersion provided.
Exact(String),
}
/// Returns data at least as new as the provided resource version.
///
/// The newest available data is preferred, but any data not older than the provided resource version may be served.
/// This guarantees that the collection's resource version is not older than the requested resource version,
/// but does not make any guarantee about the resource version of any of the items in that collection.
///
/// ### Any Version
/// A degenerate, but common sub-case of `NotOlderThan` is when used together with `resource_version` "0".
///
/// It is possible for a "0" resource version request to return data at a much older resource version
/// than the client has previously observed, particularly in HA configurations, due to partitions or stale caches.
/// Clients that cannot tolerate this should not use this semantic.
NotOlderThan,

impl fmt::Display for VersionMatch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
VersionMatch::MostRecent => write!(f, "MostRecent"),
VersionMatch::Any => write!(f, "Any"),
VersionMatch::NotOlderThan(s) => write!(f, "NotOlderThan[{}]", s),
VersionMatch::Exact(s) => write!(f, "Exact[{}]", s),
}
}
/// Return data at the exact resource version provided.
///
/// If the provided resource version is unavailable, the server responds with HTTP 410 "Gone".
/// For list requests to servers that honor the resource version Match parameter, this guarantees that the collection's
/// resource version is the same as the resource version you requested in the query string.
/// That guarantee does not apply to the resource version of any items within that collection.
///
/// Note that `Exact` cannot be used with resource version "0". For the most up-to-date list; use `Unset`.
Exact,
}

/// Common query parameters used in list/delete calls on collections
Expand Down Expand Up @@ -62,23 +65,27 @@ pub struct ListParams {
/// After listing results with a limit, a continue token can be used to fetch another page of results.
pub continue_token: Option<String>,

/// Determines how resourceVersion is applied to list calls.
/// See <https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions> for
/// details.
pub version_match: VersionMatch,
/// Determines how resourceVersion is matched applied to list calls.
pub version_match: Option<VersionMatch>,

/// An explicit resourceVersion using the given `VersionMatch` strategy
///
/// See <https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions> for details.
pub resource_version: Option<String>,
}

impl ListParams {
pub(crate) fn validate(&self) -> Result<(), Error> {
match &self.version_match {
VersionMatch::Exact(resource_version) | VersionMatch::NotOlderThan(resource_version) => {
if resource_version == "0" {
return Err(Error::Validation(
"ListParams::version_match cannot be equal to \"0\" for Exact and NotOlderThan variants.".into(),
));
}
if let Some(rv) = &self.resource_version {
if self.version_match == Some(VersionMatch::Exact) && rv == "0" {
return Err(Error::Validation(
"A non-zero resource_version is required when using an Exact match".into(),
));
}
_ => (),
} else if self.version_match.is_some() {
return Err(Error::Validation(
"A resource_version is required when using an explicit match".into(),
));
}
Ok(())
}
Expand All @@ -90,6 +97,7 @@ impl ListParams {
/// ```
/// use kube::api::ListParams;
/// let lp = ListParams::default()
/// .match_any()
/// .timeout(60)
/// .labels("kubernetes.io/lifecycle=spot");
/// ```
Expand Down Expand Up @@ -139,12 +147,35 @@ impl ListParams {
self
}

/// Sets resource version and resource version match.
/// Sets the resource version
#[must_use]
pub fn version_match(mut self, version_match: VersionMatch) -> Self {
self.version_match = version_match;
pub fn at(mut self, resource_version: &str) -> Self {
self.resource_version = Some(resource_version.into());
self
}

/// Sets an arbitary resource version match strategy
///
/// A non-default strategy such as `VersionMatch::Exact` or `VersionMatch::NotGreaterThan`
/// requires an explicit `resource_version` set to pass request validation.
#[must_use]
pub fn matching(mut self, version_match: VersionMatch) -> Self {
self.version_match = Some(version_match);
self
}

/// Use the semantic "any" resource version strategy
///
/// This is a less taxing variant of the default list, returning data at any resource version.
/// It will prefer the newest avialable resource version, but strong consistency is not required;
/// data at any resource version may be served.
/// It is possible for the request to return data at a much older resource version than the client
/// has previously observed, particularly in high availability configurations, due to partitions or stale caches.
/// Clients that cannot tolerate this should not use this semantic.
#[must_use]
pub fn match_any(self) -> Self {
self.matching(VersionMatch::NotOlderThan).at("0")
}
}

/// The validation directive to use for `fieldValidation` when using server-side apply.
Expand Down
Loading

0 comments on commit 4f633ee

Please sign in to comment.