kube_core/
response.rs

1//! Generic api response types
2use serde::{Deserialize, Serialize};
3
4/// A Kubernetes status object
5#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
6pub struct Status {
7    /// Status of the operation
8    ///
9    /// One of: `Success` or `Failure` - [more info](https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status)
10    #[serde(default, skip_serializing_if = "Option::is_none")]
11    pub status: Option<StatusSummary>,
12
13    /// Suggested HTTP return code (0 if unset)
14    #[serde(default, skip_serializing_if = "is_u16_zero")]
15    pub code: u16,
16
17    /// A human-readable  description of the status of this operation
18    #[serde(default, skip_serializing_if = "String::is_empty")]
19    pub message: String,
20
21    /// A machine-readable description of why this operation is in the “Failure” status.
22    ///
23    /// If this value is empty there is no information available.
24    /// A Reason clarifies an HTTP status code but does not override it.
25    #[serde(default, skip_serializing_if = "String::is_empty")]
26    pub reason: String,
27
28    /// Extended data associated with the reason.
29    ///
30    /// Each reason may define its own extended details.
31    /// This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.
32    #[serde(default, skip_serializing_if = "Option::is_none")]
33    pub details: Option<StatusDetails>,
34}
35
36impl Status {
37    /// Returns a successful `Status`
38    pub fn success() -> Self {
39        Status {
40            status: Some(StatusSummary::Success),
41            code: 0,
42            message: String::new(),
43            reason: String::new(),
44            details: None,
45        }
46    }
47
48    /// Returns an unsuccessful `Status`
49    pub fn failure(message: &str, reason: &str) -> Self {
50        Status {
51            status: Some(StatusSummary::Failure),
52            code: 0,
53            message: message.to_string(),
54            reason: reason.to_string(),
55            details: None,
56        }
57    }
58
59    /// Sets an explicit HTTP status code
60    pub fn with_code(mut self, code: u16) -> Self {
61        self.code = code;
62        self
63    }
64
65    /// Adds details to the `Status`
66    pub fn with_details(mut self, details: StatusDetails) -> Self {
67        self.details = Some(details);
68        self
69    }
70
71    /// Checks if this `Status` represents success
72    ///
73    /// Note that it is possible for `Status` to be in indeterminate state
74    /// when both `is_success` and `is_failure` return false.
75    pub fn is_success(&self) -> bool {
76        self.status == Some(StatusSummary::Success)
77    }
78
79    /// Checks if this `Status` represents failure
80    ///
81    /// Note that it is possible for `Status` to be in indeterminate state
82    /// when both `is_success` and `is_failure` return false.
83    pub fn is_failure(&self) -> bool {
84        self.status == Some(StatusSummary::Failure)
85    }
86}
87
88/// Overall status of the operation - whether it succeeded or not
89#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
90pub enum StatusSummary {
91    /// Operation succeeded
92    Success,
93    /// Operation failed
94    Failure,
95}
96
97/// Status details object on the [`Status`] object
98#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
99#[serde(rename_all = "camelCase")]
100pub struct StatusDetails {
101    /// The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described)
102    #[serde(default, skip_serializing_if = "String::is_empty")]
103    pub name: String,
104
105    /// The group attribute of the resource associated with the status StatusReason
106    #[serde(default, skip_serializing_if = "String::is_empty")]
107    pub group: String,
108
109    /// The kind attribute of the resource associated with the status StatusReason
110    ///
111    /// On some operations may differ from the requested resource Kind - [more info](https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds)
112    #[serde(default, skip_serializing_if = "String::is_empty")]
113    pub kind: String,
114
115    /// UID of the resource (when there is a single resource which can be described)
116    ///
117    /// [More info](http://kubernetes.io/docs/user-guide/identifiers#uids)
118    #[serde(default, skip_serializing_if = "String::is_empty")]
119    pub uid: String,
120
121    /// The Causes vector includes more details associated with the failure
122    ///
123    /// Not all StatusReasons may provide detailed causes.
124    #[serde(default, skip_serializing_if = "Vec::is_empty")]
125    pub causes: Vec<StatusCause>,
126
127    /// If specified, the time in seconds before the operation should be retried.
128    ///
129    /// Some errors may indicate the client must take an alternate action -
130    /// for those errors this field may indicate how long to wait before taking the alternate action.
131    #[serde(default, skip_serializing_if = "is_u32_zero")]
132    pub retry_after_seconds: u32,
133}
134
135/// Status cause object on the [`StatusDetails`] object
136#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
137pub struct StatusCause {
138    /// A machine-readable description of the cause of the error. If this value is empty there is no information available.
139    #[serde(default, skip_serializing_if = "String::is_empty")]
140    pub reason: String,
141
142    /// A human-readable description of the cause of the error. This field may be presented as-is to a reader.
143    #[serde(default, skip_serializing_if = "String::is_empty")]
144    pub message: String,
145
146    /// The field of the resource that has caused this error, as named by its JSON serialization
147    ///
148    /// May include dot and postfix notation for nested attributes. Arrays are zero-indexed.
149    /// Fields may appear more than once in an array of causes due to fields having multiple errors.
150    #[serde(default, skip_serializing_if = "String::is_empty")]
151    pub field: String,
152}
153
154fn is_u16_zero(&v: &u16) -> bool {
155    v == 0
156}
157
158fn is_u32_zero(&v: &u32) -> bool {
159    v == 0
160}
161
162#[cfg(test)]
163mod test {
164    use super::Status;
165
166    // ensure our status schema is sensible
167    #[test]
168    fn delete_deserialize_test() {
169        let statusresp = r#"{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Success","details":{"name":"some-app","group":"clux.dev","kind":"foos","uid":"1234-some-uid"}}"#;
170        let s: Status = serde_json::from_str::<Status>(statusresp).unwrap();
171        assert_eq!(s.details.unwrap().name, "some-app");
172
173        let statusnoname = r#"{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Success","details":{"group":"clux.dev","kind":"foos","uid":"1234-some-uid"}}"#;
174        let s2: Status = serde_json::from_str::<Status>(statusnoname).unwrap();
175        assert_eq!(s2.details.unwrap().name, ""); // optional probably better..
176    }
177}