1use thiserror::Error;
3
4use crate::params::GetParams;
5
6use super::params::{DeleteParams, ListParams, Patch, PatchParams, PostParams, WatchParams};
7
8pub(crate) const JSON_MIME: &str = "application/json";
9pub(crate) const JSON_METADATA_MIME: &str = "application/json;as=PartialObjectMetadata;g=meta.k8s.io;v=v1";
17
18pub(crate) const JSON_METADATA_LIST_MIME: &str =
19 "application/json;as=PartialObjectMetadataList;g=meta.k8s.io;v=v1";
20
21#[derive(Debug, Error)]
23pub enum Error {
24 #[error("failed to build request: {0}")]
26 BuildRequest(#[source] http::Error),
27 #[error("failed to serialize body: {0}")]
29 SerializeBody(#[source] serde_json::Error),
30 #[error("failed to validate request: {0}")]
32 Validation(String),
33}
34
35#[derive(Debug, Clone)]
40pub struct Request {
41 pub url_path: String,
43}
44
45impl Request {
46 pub fn new<S: Into<String>>(url_path: S) -> Self {
48 Self {
49 url_path: url_path.into(),
50 }
51 }
52}
53
54impl Request {
58 pub fn list(&self, lp: &ListParams) -> Result<http::Request<Vec<u8>>, Error> {
60 let target = format!("{}?", self.url_path);
61 let mut qp = form_urlencoded::Serializer::new(target);
62 lp.validate()?;
63 lp.populate_qp(&mut qp);
64 let urlstr = qp.finish();
65 let req = http::Request::get(urlstr);
66 req.body(vec![]).map_err(Error::BuildRequest)
67 }
68
69 pub fn watch(&self, wp: &WatchParams, ver: &str) -> Result<http::Request<Vec<u8>>, Error> {
71 let target = format!("{}?", self.url_path);
72 let mut qp = form_urlencoded::Serializer::new(target);
73 wp.validate()?;
74 wp.populate_qp(&mut qp);
75 qp.append_pair("resourceVersion", ver);
76 let urlstr = qp.finish();
77 let req = http::Request::get(urlstr);
78 req.body(vec![]).map_err(Error::BuildRequest)
79 }
80
81 pub fn get(&self, name: &str, gp: &GetParams) -> Result<http::Request<Vec<u8>>, Error> {
83 validate_name(name)?;
84 let urlstr = if let Some(rv) = &gp.resource_version {
85 let target = format!("{}/{}?", self.url_path, name);
86 form_urlencoded::Serializer::new(target)
87 .append_pair("resourceVersion", rv)
88 .finish()
89 } else {
90 let target = format!("{}/{}", self.url_path, name);
91 form_urlencoded::Serializer::new(target).finish()
92 };
93 let req = http::Request::get(urlstr);
94 req.body(vec![]).map_err(Error::BuildRequest)
95 }
96
97 pub fn create(&self, pp: &PostParams, data: Vec<u8>) -> Result<http::Request<Vec<u8>>, Error> {
99 pp.validate()?;
100 let target = format!("{}?", self.url_path);
101 let mut qp = form_urlencoded::Serializer::new(target);
102 pp.populate_qp(&mut qp);
103 let urlstr = qp.finish();
104 let req = http::Request::post(urlstr).header(http::header::CONTENT_TYPE, JSON_MIME);
105 req.body(data).map_err(Error::BuildRequest)
106 }
107
108 pub fn delete(&self, name: &str, dp: &DeleteParams) -> Result<http::Request<Vec<u8>>, Error> {
110 validate_name(name)?;
111 let target = format!("{}/{}?", self.url_path, name);
112 let mut qp = form_urlencoded::Serializer::new(target);
113 let urlstr = qp.finish();
114 let body = serde_json::to_vec(&dp).map_err(Error::SerializeBody)?;
115 let req = http::Request::delete(urlstr).header(http::header::CONTENT_TYPE, JSON_MIME);
116 req.body(body).map_err(Error::BuildRequest)
117 }
118
119 pub fn delete_collection(
121 &self,
122 dp: &DeleteParams,
123 lp: &ListParams,
124 ) -> Result<http::Request<Vec<u8>>, Error> {
125 let target = format!("{}?", self.url_path);
126 let mut qp = form_urlencoded::Serializer::new(target);
127 if let Some(fields) = &lp.field_selector {
128 qp.append_pair("fieldSelector", fields);
129 }
130 if let Some(labels) = &lp.label_selector {
131 qp.append_pair("labelSelector", labels);
132 }
133 let urlstr = qp.finish();
134
135 let data = if dp.is_default() {
136 vec![] } else {
138 serde_json::to_vec(&dp).map_err(Error::SerializeBody)?
139 };
140
141 let req = http::Request::delete(urlstr).header(http::header::CONTENT_TYPE, JSON_MIME);
142 req.body(data).map_err(Error::BuildRequest)
143 }
144
145 pub fn patch<P: serde::Serialize>(
149 &self,
150 name: &str,
151 pp: &PatchParams,
152 patch: &Patch<P>,
153 ) -> Result<http::Request<Vec<u8>>, Error> {
154 validate_name(name)?;
155 pp.validate(patch)?;
156 let target = format!("{}/{}?", self.url_path, name);
157 let mut qp = form_urlencoded::Serializer::new(target);
158 pp.populate_qp(&mut qp);
159 let urlstr = qp.finish();
160
161 http::Request::patch(urlstr)
162 .header(http::header::ACCEPT, JSON_MIME)
163 .header(http::header::CONTENT_TYPE, patch.content_type())
164 .body(patch.serialize().map_err(Error::SerializeBody)?)
165 .map_err(Error::BuildRequest)
166 }
167
168 pub fn replace(
172 &self,
173 name: &str,
174 pp: &PostParams,
175 data: Vec<u8>,
176 ) -> Result<http::Request<Vec<u8>>, Error> {
177 validate_name(name)?;
178 let target = format!("{}/{}?", self.url_path, name);
179 let mut qp = form_urlencoded::Serializer::new(target);
180 pp.populate_qp(&mut qp);
181 let urlstr = qp.finish();
182 let req = http::Request::put(urlstr).header(http::header::CONTENT_TYPE, JSON_MIME);
183 req.body(data).map_err(Error::BuildRequest)
184 }
185}
186
187impl Request {
189 pub fn get_subresource(
191 &self,
192 subresource_name: &str,
193 name: &str,
194 ) -> Result<http::Request<Vec<u8>>, Error> {
195 validate_name(name)?;
196 let target = format!("{}/{}/{}", self.url_path, name, subresource_name);
197 let mut qp = form_urlencoded::Serializer::new(target);
198 let urlstr = qp.finish();
199 let req = http::Request::get(urlstr);
200 req.body(vec![]).map_err(Error::BuildRequest)
201 }
202
203 pub fn create_subresource(
205 &self,
206 subresource_name: &str,
207 name: &str,
208 pp: &PostParams,
209 data: Vec<u8>,
210 ) -> Result<http::Request<Vec<u8>>, Error> {
211 validate_name(name)?;
212 let target = format!("{}/{}/{}?", self.url_path, name, subresource_name);
213 let mut qp = form_urlencoded::Serializer::new(target);
214 pp.populate_qp(&mut qp);
215 let urlstr = qp.finish();
216 let req = http::Request::post(urlstr).header(http::header::CONTENT_TYPE, JSON_MIME);
217 req.body(data).map_err(Error::BuildRequest)
218 }
219
220 pub fn patch_subresource<P: serde::Serialize>(
222 &self,
223 subresource_name: &str,
224 name: &str,
225 pp: &PatchParams,
226 patch: &Patch<P>,
227 ) -> Result<http::Request<Vec<u8>>, Error> {
228 validate_name(name)?;
229 pp.validate(patch)?;
230 let target = format!("{}/{}/{}?", self.url_path, name, subresource_name);
231 let mut qp = form_urlencoded::Serializer::new(target);
232 pp.populate_qp(&mut qp);
233 let urlstr = qp.finish();
234
235 http::Request::patch(urlstr)
236 .header(http::header::ACCEPT, JSON_MIME)
237 .header(http::header::CONTENT_TYPE, patch.content_type())
238 .body(patch.serialize().map_err(Error::SerializeBody)?)
239 .map_err(Error::BuildRequest)
240 }
241
242 pub fn replace_subresource(
244 &self,
245 subresource_name: &str,
246 name: &str,
247 pp: &PostParams,
248 data: Vec<u8>,
249 ) -> Result<http::Request<Vec<u8>>, Error> {
250 validate_name(name)?;
251 let target = format!("{}/{}/{}?", self.url_path, name, subresource_name);
252 let mut qp = form_urlencoded::Serializer::new(target);
253 pp.populate_qp(&mut qp);
254 let urlstr = qp.finish();
255 let req = http::Request::put(urlstr).header(http::header::CONTENT_TYPE, JSON_MIME);
256 req.body(data).map_err(Error::BuildRequest)
257 }
258}
259
260impl Request {
265 pub fn get_metadata(&self, name: &str, gp: &GetParams) -> Result<http::Request<Vec<u8>>, Error> {
267 validate_name(name)?;
268 let urlstr = if let Some(rv) = &gp.resource_version {
269 let target = format!("{}/{}?", self.url_path, name);
270 form_urlencoded::Serializer::new(target)
271 .append_pair("resourceVersion", rv)
272 .finish()
273 } else {
274 let target = format!("{}/{}", self.url_path, name);
275 form_urlencoded::Serializer::new(target).finish()
276 };
277 let req = http::Request::get(urlstr)
278 .header(http::header::ACCEPT, JSON_METADATA_MIME)
279 .header(http::header::CONTENT_TYPE, JSON_MIME);
280 req.body(vec![]).map_err(Error::BuildRequest)
281 }
282
283 pub fn list_metadata(&self, lp: &ListParams) -> Result<http::Request<Vec<u8>>, Error> {
285 let target = format!("{}?", self.url_path);
286 let mut qp = form_urlencoded::Serializer::new(target);
287 lp.validate()?;
288 lp.populate_qp(&mut qp);
289 let urlstr = qp.finish();
290 let req = http::Request::get(urlstr)
291 .header(http::header::ACCEPT, JSON_METADATA_LIST_MIME)
292 .header(http::header::CONTENT_TYPE, JSON_MIME);
293
294 req.body(vec![]).map_err(Error::BuildRequest)
295 }
296
297 pub fn watch_metadata(&self, wp: &WatchParams, ver: &str) -> Result<http::Request<Vec<u8>>, Error> {
299 let target = format!("{}?", self.url_path);
300 let mut qp = form_urlencoded::Serializer::new(target);
301 wp.validate()?;
302 wp.populate_qp(&mut qp);
303 qp.append_pair("resourceVersion", ver);
304
305 let urlstr = qp.finish();
306 http::Request::get(urlstr)
307 .header(http::header::ACCEPT, JSON_METADATA_MIME)
308 .header(http::header::CONTENT_TYPE, JSON_MIME)
309 .body(vec![])
310 .map_err(Error::BuildRequest)
311 }
312
313 pub fn patch_metadata<P: serde::Serialize>(
317 &self,
318 name: &str,
319 pp: &PatchParams,
320 patch: &Patch<P>,
321 ) -> Result<http::Request<Vec<u8>>, Error> {
322 validate_name(name)?;
323 pp.validate(patch)?;
324 let target = format!("{}/{}?", self.url_path, name);
325 let mut qp = form_urlencoded::Serializer::new(target);
326 pp.populate_qp(&mut qp);
327 let urlstr = qp.finish();
328
329 http::Request::patch(urlstr)
330 .header(http::header::ACCEPT, JSON_METADATA_MIME)
331 .header(http::header::CONTENT_TYPE, patch.content_type())
332 .body(patch.serialize().map_err(Error::SerializeBody)?)
333 .map_err(Error::BuildRequest)
334 }
335}
336
337fn validate_name(name: &str) -> Result<(), Error> {
339 if name.is_empty() {
340 return Err(Error::Validation("A non-empty name is required".into()));
341 }
342 Ok(())
343}
344
345#[cfg(test)]
349mod test {
350 use crate::{
351 params::{GetParams, PostParams, VersionMatch, WatchParams},
352 request::{Error, Request},
353 resource::Resource,
354 };
355 use http::header;
356 use k8s::{
357 admissionregistration::v1 as adregv1, apps::v1 as appsv1, authorization::v1 as authv1,
358 autoscaling::v1 as autoscalingv1, batch::v1 as batchv1, core::v1 as corev1,
359 networking::v1 as networkingv1, rbac::v1 as rbacv1, storage::v1 as storagev1,
360 };
361 use k8s_openapi::api as k8s;
362
363 use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1 as apiextsv1;
365
366 #[test]
368 fn api_url_secret() {
369 let url = corev1::Secret::url_path(&(), Some("ns"));
370 let req = Request::new(url).create(&PostParams::default(), vec![]).unwrap();
371 assert_eq!(req.uri(), "/api/v1/namespaces/ns/secrets?");
372 assert_eq!(req.headers().get(header::CONTENT_TYPE).unwrap(), super::JSON_MIME);
373 }
374
375 #[test]
376 fn api_url_rs() {
377 let url = appsv1::ReplicaSet::url_path(&(), Some("ns"));
378 let req = Request::new(url).create(&PostParams::default(), vec![]).unwrap();
379 assert_eq!(req.uri(), "/apis/apps/v1/namespaces/ns/replicasets?");
380 }
381 #[test]
382 fn api_url_role() {
383 let url = rbacv1::Role::url_path(&(), Some("ns"));
384 let req = Request::new(url).create(&PostParams::default(), vec![]).unwrap();
385 assert_eq!(
386 req.uri(),
387 "/apis/rbac.authorization.k8s.io/v1/namespaces/ns/roles?"
388 );
389 }
390
391 #[test]
392 fn api_url_cj() {
393 let url = batchv1::CronJob::url_path(&(), Some("ns"));
394 let req = Request::new(url).create(&PostParams::default(), vec![]).unwrap();
395 assert_eq!(req.uri(), "/apis/batch/v1/namespaces/ns/cronjobs?");
396 }
397 #[test]
398 fn api_url_hpa() {
399 let url = autoscalingv1::HorizontalPodAutoscaler::url_path(&(), Some("ns"));
400 let req = Request::new(url).create(&PostParams::default(), vec![]).unwrap();
401 assert_eq!(
402 req.uri(),
403 "/apis/autoscaling/v1/namespaces/ns/horizontalpodautoscalers?"
404 );
405 }
406
407 #[test]
408 fn api_url_np() {
409 let url = networkingv1::NetworkPolicy::url_path(&(), Some("ns"));
410 let req = Request::new(url).create(&PostParams::default(), vec![]).unwrap();
411 assert_eq!(
412 req.uri(),
413 "/apis/networking.k8s.io/v1/namespaces/ns/networkpolicies?"
414 );
415 }
416 #[test]
417 fn api_url_ingress() {
418 let url = networkingv1::Ingress::url_path(&(), Some("ns"));
419 let req = Request::new(url).create(&PostParams::default(), vec![]).unwrap();
420 assert_eq!(req.uri(), "/apis/networking.k8s.io/v1/namespaces/ns/ingresses?");
421 }
422
423 #[test]
424 fn api_url_vattach() {
425 let url = storagev1::VolumeAttachment::url_path(&(), None);
426 let req = Request::new(url).create(&PostParams::default(), vec![]).unwrap();
427 assert_eq!(req.uri(), "/apis/storage.k8s.io/v1/volumeattachments?");
428 }
429
430 #[test]
431 fn api_url_admission() {
432 let url = adregv1::ValidatingWebhookConfiguration::url_path(&(), None);
433 let req = Request::new(url).create(&PostParams::default(), vec![]).unwrap();
434 assert_eq!(
435 req.uri(),
436 "/apis/admissionregistration.k8s.io/v1/validatingwebhookconfigurations?"
437 );
438 }
439
440 #[test]
441 fn api_auth_selfreview() {
442 let url = authv1::SelfSubjectRulesReview::url_path(&(), None);
445 let req = Request::new(url).create(&PostParams::default(), vec![]).unwrap();
446 assert_eq!(
447 req.uri(),
448 "/apis/authorization.k8s.io/v1/selfsubjectrulesreviews?"
449 );
450 }
451
452 #[test]
453 fn api_apiextsv1_crd() {
454 let url = apiextsv1::CustomResourceDefinition::url_path(&(), None);
455 let req = Request::new(url).create(&PostParams::default(), vec![]).unwrap();
456 assert_eq!(
457 req.uri(),
458 "/apis/apiextensions.k8s.io/v1/customresourcedefinitions?"
459 );
460 }
461
462 use crate::params::{DeleteParams, ListParams, Patch, PatchParams};
465
466 #[test]
467 fn get_metadata_path() {
468 let url = appsv1::Deployment::url_path(&(), Some("ns"));
469 let req = Request::new(url)
470 .get_metadata("mydeploy", &GetParams::default())
471 .unwrap();
472 println!("{}", req.uri());
473 assert_eq!(req.uri(), "/apis/apps/v1/namespaces/ns/deployments/mydeploy");
474 assert_eq!(req.method(), "GET");
475 assert_eq!(req.headers().get(header::CONTENT_TYPE).unwrap(), super::JSON_MIME);
476 assert_eq!(
477 req.headers().get(header::ACCEPT).unwrap(),
478 super::JSON_METADATA_MIME
479 );
480 }
481
482 #[test]
483 fn get_path_with_rv() {
484 let url = appsv1::Deployment::url_path(&(), Some("ns"));
485 let req = Request::new(url).get("mydeploy", &GetParams::any()).unwrap();
486 assert_eq!(
487 req.uri(),
488 "/apis/apps/v1/namespaces/ns/deployments/mydeploy?&resourceVersion=0"
489 );
490 }
491
492 #[test]
493 fn get_meta_path_with_rv() {
494 let url = appsv1::Deployment::url_path(&(), Some("ns"));
495 let req = Request::new(url)
496 .get_metadata("mydeploy", &GetParams::at("665"))
497 .unwrap();
498 assert_eq!(
499 req.uri(),
500 "/apis/apps/v1/namespaces/ns/deployments/mydeploy?&resourceVersion=665"
501 );
502
503 assert_eq!(req.method(), "GET");
504 assert_eq!(req.headers().get(header::CONTENT_TYPE).unwrap(), super::JSON_MIME);
505 assert_eq!(
506 req.headers().get(header::ACCEPT).unwrap(),
507 super::JSON_METADATA_MIME
508 );
509 }
510
511 #[test]
512 fn get_empty_name() {
513 let url = appsv1::Deployment::url_path(&(), Some("ns"));
514 let req = Request::new(url).get("", &GetParams::any());
515 assert!(matches!(req, Err(Error::Validation(_))));
516 }
517
518 #[test]
519 fn list_path() {
520 let url = appsv1::Deployment::url_path(&(), Some("ns"));
521 let lp = ListParams::default();
522 let req = Request::new(url).list(&lp).unwrap();
523 assert_eq!(req.uri(), "/apis/apps/v1/namespaces/ns/deployments");
524 }
525 #[test]
526 fn list_metadata_path() {
527 let url = appsv1::Deployment::url_path(&(), Some("ns"));
528 let lp = ListParams::default().matching(VersionMatch::NotOlderThan).at("5");
529 let req = Request::new(url).list_metadata(&lp).unwrap();
530 assert_eq!(
531 req.uri(),
532 "/apis/apps/v1/namespaces/ns/deployments?&resourceVersion=5&resourceVersionMatch=NotOlderThan"
533 );
534 assert_eq!(req.headers().get(header::CONTENT_TYPE).unwrap(), super::JSON_MIME);
535 assert_eq!(
536 req.headers().get(header::ACCEPT).unwrap(),
537 super::JSON_METADATA_LIST_MIME
538 );
539 }
540 #[test]
541 fn watch_path() {
542 let url = corev1::Pod::url_path(&(), Some("ns"));
543 let wp = WatchParams::default();
544 let req = Request::new(url).watch(&wp, "0").unwrap();
545 assert_eq!(
546 req.uri(),
547 "/api/v1/namespaces/ns/pods?&watch=true&timeoutSeconds=290&allowWatchBookmarks=true&resourceVersion=0"
548 );
549 }
550
551 #[test]
552 fn watch_streaming_list() {
553 let url = corev1::Pod::url_path(&(), Some("ns"));
554 let wp = WatchParams::default().initial_events();
555 let req = Request::new(url).watch(&wp, "0").unwrap();
556 assert_eq!(
557 req.uri(),
558 "/api/v1/namespaces/ns/pods?&watch=true&timeoutSeconds=290&allowWatchBookmarks=true&sendInitialEvents=true&resourceVersionMatch=NotOlderThan&resourceVersion=0"
559 );
560 }
561
562 #[test]
563 fn watch_metadata_path() {
564 let url = corev1::Pod::url_path(&(), Some("ns"));
565 let wp = WatchParams::default();
566 let req = Request::new(url).watch_metadata(&wp, "0").unwrap();
567 assert_eq!(
568 req.uri(),
569 "/api/v1/namespaces/ns/pods?&watch=true&timeoutSeconds=290&allowWatchBookmarks=true&resourceVersion=0"
570 );
571 assert_eq!(req.headers().get(header::CONTENT_TYPE).unwrap(), super::JSON_MIME);
572 assert_eq!(
573 req.headers().get(header::ACCEPT).unwrap(),
574 super::JSON_METADATA_MIME
575 );
576 }
577 #[test]
578 fn replace_path() {
579 let url = appsv1::DaemonSet::url_path(&(), None);
580 let pp = PostParams {
581 dry_run: true,
582 ..Default::default()
583 };
584 let req = Request::new(url).replace("myds", &pp, vec![]).unwrap();
585 assert_eq!(req.uri(), "/apis/apps/v1/daemonsets/myds?&dryRun=All");
586 assert_eq!(req.headers().get(header::CONTENT_TYPE).unwrap(), super::JSON_MIME);
587 }
588
589 #[test]
590 fn delete_path() {
591 let url = appsv1::ReplicaSet::url_path(&(), Some("ns"));
592 let dp = DeleteParams::default();
593 let req = Request::new(url).delete("myrs", &dp).unwrap();
594 assert_eq!(req.uri(), "/apis/apps/v1/namespaces/ns/replicasets/myrs");
595 assert_eq!(req.method(), "DELETE");
596 assert_eq!(req.headers().get(header::CONTENT_TYPE).unwrap(), super::JSON_MIME);
597 }
598
599 #[test]
600 fn delete_collection_path() {
601 let url = appsv1::ReplicaSet::url_path(&(), Some("ns"));
602 let lp = ListParams::default().labels("app=myapp");
603 let dp = DeleteParams::default();
604 let req = Request::new(url).delete_collection(&dp, &lp).unwrap();
605 assert_eq!(
606 req.uri(),
607 "/apis/apps/v1/namespaces/ns/replicasets?&labelSelector=app%3Dmyapp"
608 );
609 assert_eq!(req.method(), "DELETE");
610 assert_eq!(req.headers().get(header::CONTENT_TYPE).unwrap(), super::JSON_MIME);
611 }
612
613 #[test]
614 fn namespace_path() {
615 let url = corev1::Namespace::url_path(&(), None);
616 let gp = ListParams::default();
617 let req = Request::new(url).list(&gp).unwrap();
618 assert_eq!(req.uri(), "/api/v1/namespaces")
619 }
620
621 #[test]
623 fn patch_status_path() {
624 let url = corev1::Node::url_path(&(), None);
625 let pp = PatchParams::default();
626 let req = Request::new(url)
627 .patch_subresource("status", "mynode", &pp, &Patch::Merge(()))
628 .unwrap();
629 assert_eq!(req.uri(), "/api/v1/nodes/mynode/status?");
630 assert_eq!(
631 req.headers().get("Content-Type").unwrap().to_str().unwrap(),
632 Patch::Merge(()).content_type()
633 );
634 assert_eq!(req.method(), "PATCH");
635 }
636 #[test]
637 fn patch_pod_metadata() {
638 let url = corev1::Pod::url_path(&(), Some("ns"));
639 let pp = PatchParams::default();
640 let req = Request::new(url)
641 .patch_metadata("mypod", &pp, &Patch::Merge(()))
642 .unwrap();
643 assert_eq!(req.uri(), "/api/v1/namespaces/ns/pods/mypod?");
644 assert_eq!(
645 req.headers().get(header::CONTENT_TYPE).unwrap(),
646 Patch::Merge(()).content_type()
647 );
648 assert_eq!(
649 req.headers().get(header::ACCEPT).unwrap(),
650 super::JSON_METADATA_MIME
651 );
652 assert_eq!(req.method(), "PATCH");
653 }
654 #[test]
655 fn replace_status_path() {
656 let url = corev1::Node::url_path(&(), None);
657 let pp = PostParams::default();
658 let req = Request::new(url)
659 .replace_subresource("status", "mynode", &pp, vec![])
660 .unwrap();
661 assert_eq!(req.uri(), "/api/v1/nodes/mynode/status?");
662 assert_eq!(req.method(), "PUT");
663 assert_eq!(req.headers().get(header::CONTENT_TYPE).unwrap(), super::JSON_MIME);
664 }
665
666 #[test]
667 fn create_ingress() {
668 let url = networkingv1::Ingress::url_path(&(), Some("ns"));
670 let pp = PostParams::default();
671 let req = Request::new(&url).create(&pp, vec![]).unwrap();
672
673 assert_eq!(req.uri(), "/apis/networking.k8s.io/v1/namespaces/ns/ingresses?");
674 let patch_params = PatchParams::default();
675 let req = Request::new(url)
676 .patch("baz", &patch_params, &Patch::Merge(()))
677 .unwrap();
678 assert_eq!(
679 req.uri(),
680 "/apis/networking.k8s.io/v1/namespaces/ns/ingresses/baz?"
681 );
682 assert_eq!(req.method(), "PATCH");
683 }
684
685 #[test]
686 fn replace_status() {
687 let url = apiextsv1::CustomResourceDefinition::url_path(&(), None);
688 let pp = PostParams::default();
689 let req = Request::new(url)
690 .replace_subresource("status", "mycrd.domain.io", &pp, vec![])
691 .unwrap();
692 assert_eq!(
693 req.uri(),
694 "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/mycrd.domain.io/status?"
695 );
696 }
697 #[test]
698 fn get_scale_path() {
699 let url = corev1::Node::url_path(&(), None);
700 let req = Request::new(url).get_subresource("scale", "mynode").unwrap();
701 assert_eq!(req.uri(), "/api/v1/nodes/mynode/scale");
702 assert_eq!(req.method(), "GET");
703 }
704 #[test]
705 fn patch_scale_path() {
706 let url = corev1::Node::url_path(&(), None);
707 let pp = PatchParams::default();
708 let req = Request::new(url)
709 .patch_subresource("scale", "mynode", &pp, &Patch::Merge(()))
710 .unwrap();
711 assert_eq!(req.uri(), "/api/v1/nodes/mynode/scale?");
712 assert_eq!(req.method(), "PATCH");
713 }
714 #[test]
715 fn replace_scale_path() {
716 let url = corev1::Node::url_path(&(), None);
717 let pp = PostParams::default();
718 let req = Request::new(url)
719 .replace_subresource("scale", "mynode", &pp, vec![])
720 .unwrap();
721 assert_eq!(req.uri(), "/api/v1/nodes/mynode/scale?");
722 assert_eq!(req.method(), "PUT");
723 }
724
725 #[test]
726 fn create_subresource_path() {
727 let url = corev1::ServiceAccount::url_path(&(), Some("ns"));
728 let pp = PostParams::default();
729 let data = vec![];
730 let req = Request::new(url)
731 .create_subresource("token", "sa", &pp, data)
732 .unwrap();
733 assert_eq!(req.uri(), "/api/v1/namespaces/ns/serviceaccounts/sa/token");
734 }
735
736 #[test]
744 fn list_pods_from_cache() {
745 let url = corev1::Pod::url_path(&(), Some("ns"));
746 let gp = ListParams::default().match_any();
747 let req = Request::new(url).list(&gp).unwrap();
748 assert_eq!(
749 req.uri().query().unwrap(),
750 "&resourceVersion=0&resourceVersionMatch=NotOlderThan"
751 );
752 }
753
754 #[test]
755 fn list_most_recent_pods() {
756 let url = corev1::Pod::url_path(&(), Some("ns"));
757 let gp = ListParams::default();
758 let req = Request::new(url).list(&gp).unwrap();
759 assert_eq!(
760 req.uri().query().unwrap(),
761 "" );
763 }
764
765 #[test]
766 fn list_invalid_resource_version_combination() {
767 let url = corev1::Pod::url_path(&(), Some("ns"));
768 let gp = ListParams::default().at("0").matching(VersionMatch::Exact);
769 let err = Request::new(url).list(&gp).unwrap_err();
770 assert!(format!("{err}").contains("non-zero resource_version is required when using an Exact match"));
771 }
772
773 #[test]
774 fn list_paged_any_semantic() {
775 let url = corev1::Pod::url_path(&(), Some("ns"));
776 let gp = ListParams::default().limit(50).match_any();
777 let req = Request::new(url).list(&gp).unwrap();
778 assert_eq!(req.uri().query().unwrap(), "&limit=50");
779 }
780
781 #[test]
782 fn list_paged_with_continue_any_semantic() {
783 let url = corev1::Pod::url_path(&(), Some("ns"));
784 let gp = ListParams::default().limit(50).continue_token("1234").match_any();
785 let req = Request::new(url).list(&gp).unwrap();
786 assert_eq!(req.uri().query().unwrap(), "&limit=50&continue=1234");
787 }
788
789 #[test]
790 fn list_paged_with_continue_starting_at() {
791 let url = corev1::Pod::url_path(&(), Some("ns"));
792 let gp = ListParams::default()
793 .limit(50)
794 .continue_token("1234")
795 .at("9999")
796 .matching(VersionMatch::Exact);
797 let req = Request::new(url).list(&gp).unwrap();
798 assert_eq!(req.uri().query().unwrap(), "&limit=50&continue=1234");
799 }
800
801 #[test]
802 fn list_exact_match() {
803 let url = corev1::Pod::url_path(&(), Some("ns"));
804 let gp = ListParams::default().at("500").matching(VersionMatch::Exact);
805 let req = Request::new(url).list(&gp).unwrap();
806 let query = req.uri().query().unwrap();
807 assert_eq!(query, "&resourceVersion=500&resourceVersionMatch=Exact");
808 }
809
810 #[test]
811 fn watch_params() {
812 let url = corev1::Pod::url_path(&(), Some("ns"));
813 let wp = WatchParams::default()
814 .disable_bookmarks()
815 .fields("metadata.name=pod=1")
816 .labels("app=web");
817 let req = Request::new(url).watch(&wp, "0").unwrap();
818 assert_eq!(
819 req.uri().query().unwrap(),
820 "&watch=true&timeoutSeconds=290&fieldSelector=metadata.name%3Dpod%3D1&labelSelector=app%3Dweb&resourceVersion=0"
821 );
822 }
823
824 #[test]
825 fn watch_timeout_error() {
826 let url = corev1::Pod::url_path(&(), Some("ns"));
827 let wp = WatchParams::default().timeout(100000);
828 let err = Request::new(url).watch(&wp, "").unwrap_err();
829 assert!(format!("{err}").contains("timeout must be < 295s"));
830 }
831}