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}