1use super::{CommandExecutor, CommandOutput, DockerCommand};
6use crate::error::Result;
7use async_trait::async_trait;
8
9#[derive(Debug, Clone)]
32pub struct UpdateCommand {
33 containers: Vec<String>,
35 memory: Option<String>,
37 memory_reservation: Option<String>,
39 memory_swap: Option<String>,
41 cpu_shares: Option<u64>,
43 cpu_period: Option<u64>,
45 cpu_quota: Option<i64>,
47 cpus: Option<String>,
49 cpuset_cpus: Option<String>,
51 cpuset_mems: Option<String>,
53 blkio_weight: Option<u16>,
55 kernel_memory: Option<String>,
57 restart: Option<String>,
59 pids_limit: Option<i64>,
61 pub executor: CommandExecutor,
63}
64
65impl UpdateCommand {
66 #[must_use]
76 pub fn new(container: impl Into<String>) -> Self {
77 Self {
78 containers: vec![container.into()],
79 memory: None,
80 memory_reservation: None,
81 memory_swap: None,
82 cpu_shares: None,
83 cpu_period: None,
84 cpu_quota: None,
85 cpus: None,
86 cpuset_cpus: None,
87 cpuset_mems: None,
88 blkio_weight: None,
89 kernel_memory: None,
90 restart: None,
91 pids_limit: None,
92 executor: CommandExecutor::new(),
93 }
94 }
95
96 #[must_use]
106 pub fn new_multiple(containers: Vec<impl Into<String>>) -> Self {
107 Self {
108 containers: containers.into_iter().map(Into::into).collect(),
109 memory: None,
110 memory_reservation: None,
111 memory_swap: None,
112 cpu_shares: None,
113 cpu_period: None,
114 cpu_quota: None,
115 cpus: None,
116 cpuset_cpus: None,
117 cpuset_mems: None,
118 blkio_weight: None,
119 kernel_memory: None,
120 restart: None,
121 pids_limit: None,
122 executor: CommandExecutor::new(),
123 }
124 }
125
126 #[must_use]
128 pub fn container(mut self, container: impl Into<String>) -> Self {
129 self.containers.push(container.into());
130 self
131 }
132
133 #[must_use]
144 pub fn memory(mut self, memory: impl Into<String>) -> Self {
145 self.memory = Some(memory.into());
146 self
147 }
148
149 #[must_use]
160 pub fn memory_reservation(mut self, memory_reservation: impl Into<String>) -> Self {
161 self.memory_reservation = Some(memory_reservation.into());
162 self
163 }
164
165 #[must_use]
176 pub fn memory_swap(mut self, memory_swap: impl Into<String>) -> Self {
177 self.memory_swap = Some(memory_swap.into());
178 self
179 }
180
181 #[must_use]
192 pub fn cpu_shares(mut self, cpu_shares: u64) -> Self {
193 self.cpu_shares = Some(cpu_shares);
194 self
195 }
196
197 #[must_use]
208 pub fn cpu_period(mut self, cpu_period: u64) -> Self {
209 self.cpu_period = Some(cpu_period);
210 self
211 }
212
213 #[must_use]
224 pub fn cpu_quota(mut self, cpu_quota: i64) -> Self {
225 self.cpu_quota = Some(cpu_quota);
226 self
227 }
228
229 #[must_use]
240 pub fn cpus(mut self, cpus: impl Into<String>) -> Self {
241 self.cpus = Some(cpus.into());
242 self
243 }
244
245 #[must_use]
256 pub fn cpuset_cpus(mut self, cpuset_cpus: impl Into<String>) -> Self {
257 self.cpuset_cpus = Some(cpuset_cpus.into());
258 self
259 }
260
261 #[must_use]
272 pub fn cpuset_mems(mut self, cpuset_mems: impl Into<String>) -> Self {
273 self.cpuset_mems = Some(cpuset_mems.into());
274 self
275 }
276
277 #[must_use]
288 pub fn blkio_weight(mut self, blkio_weight: u16) -> Self {
289 self.blkio_weight = Some(blkio_weight);
290 self
291 }
292
293 #[must_use]
304 pub fn kernel_memory(mut self, kernel_memory: impl Into<String>) -> Self {
305 self.kernel_memory = Some(kernel_memory.into());
306 self
307 }
308
309 #[must_use]
320 pub fn restart(mut self, restart: impl Into<String>) -> Self {
321 self.restart = Some(restart.into());
322 self
323 }
324
325 #[must_use]
336 pub fn pids_limit(mut self, pids_limit: i64) -> Self {
337 self.pids_limit = Some(pids_limit);
338 self
339 }
340
341 pub async fn run(&self) -> Result<UpdateResult> {
368 let output = self.execute().await?;
369
370 Ok(UpdateResult {
371 output,
372 containers: self.containers.clone(),
373 })
374 }
375}
376
377#[async_trait]
378impl DockerCommand for UpdateCommand {
379 type Output = CommandOutput;
380
381 fn build_command_args(&self) -> Vec<String> {
382 let mut args = vec!["update".to_string()];
383
384 if let Some(ref memory) = self.memory {
385 args.push("--memory".to_string());
386 args.push(memory.clone());
387 }
388
389 if let Some(ref memory_reservation) = self.memory_reservation {
390 args.push("--memory-reservation".to_string());
391 args.push(memory_reservation.clone());
392 }
393
394 if let Some(ref memory_swap) = self.memory_swap {
395 args.push("--memory-swap".to_string());
396 args.push(memory_swap.clone());
397 }
398
399 if let Some(cpu_shares) = self.cpu_shares {
400 args.push("--cpu-shares".to_string());
401 args.push(cpu_shares.to_string());
402 }
403
404 if let Some(cpu_period) = self.cpu_period {
405 args.push("--cpu-period".to_string());
406 args.push(cpu_period.to_string());
407 }
408
409 if let Some(cpu_quota) = self.cpu_quota {
410 args.push("--cpu-quota".to_string());
411 args.push(cpu_quota.to_string());
412 }
413
414 if let Some(ref cpus) = self.cpus {
415 args.push("--cpus".to_string());
416 args.push(cpus.clone());
417 }
418
419 if let Some(ref cpuset_cpus) = self.cpuset_cpus {
420 args.push("--cpuset-cpus".to_string());
421 args.push(cpuset_cpus.clone());
422 }
423
424 if let Some(ref cpuset_mems) = self.cpuset_mems {
425 args.push("--cpuset-mems".to_string());
426 args.push(cpuset_mems.clone());
427 }
428
429 if let Some(blkio_weight) = self.blkio_weight {
430 args.push("--blkio-weight".to_string());
431 args.push(blkio_weight.to_string());
432 }
433
434 if let Some(ref kernel_memory) = self.kernel_memory {
435 args.push("--kernel-memory".to_string());
436 args.push(kernel_memory.clone());
437 }
438
439 if let Some(ref restart) = self.restart {
440 args.push("--restart".to_string());
441 args.push(restart.clone());
442 }
443
444 if let Some(pids_limit) = self.pids_limit {
445 args.push("--pids-limit".to_string());
446 args.push(pids_limit.to_string());
447 }
448
449 args.extend(self.containers.clone());
450 args.extend(self.executor.raw_args.clone());
451 args
452 }
453
454 fn get_executor(&self) -> &CommandExecutor {
455 &self.executor
456 }
457
458 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
459 &mut self.executor
460 }
461
462 async fn execute(&self) -> Result<Self::Output> {
463 if self.containers.is_empty() {
464 return Err(crate::error::Error::invalid_config(
465 "No containers specified for update",
466 ));
467 }
468
469 let args = self.build_command_args();
470 let command_name = args[0].clone();
471 let command_args = args[1..].to_vec();
472 self.executor
473 .execute_command(&command_name, command_args)
474 .await
475 }
476}
477
478#[derive(Debug, Clone)]
480pub struct UpdateResult {
481 pub output: CommandOutput,
483 pub containers: Vec<String>,
485}
486
487impl UpdateResult {
488 #[must_use]
490 pub fn success(&self) -> bool {
491 self.output.success
492 }
493
494 #[must_use]
496 pub fn containers(&self) -> &[String] {
497 &self.containers
498 }
499
500 #[must_use]
502 pub fn output(&self) -> &CommandOutput {
503 &self.output
504 }
505
506 #[must_use]
508 pub fn container_count(&self) -> usize {
509 self.containers.len()
510 }
511}
512
513#[cfg(test)]
514mod tests {
515 use super::*;
516
517 #[test]
518 fn test_update_single_container() {
519 let cmd = UpdateCommand::new("test-container");
520 let args = cmd.build_command_args();
521 assert_eq!(args, vec!["update", "test-container"]);
522 }
523
524 #[test]
525 fn test_update_multiple_containers() {
526 let cmd = UpdateCommand::new_multiple(vec!["web", "db", "cache"]);
527 let args = cmd.build_command_args();
528 assert_eq!(args, vec!["update", "web", "db", "cache"]);
529 }
530
531 #[test]
532 fn test_update_add_container() {
533 let cmd = UpdateCommand::new("web").container("db").container("cache");
534 let args = cmd.build_command_args();
535 assert_eq!(args, vec!["update", "web", "db", "cache"]);
536 }
537
538 #[test]
539 fn test_update_memory_options() {
540 let cmd = UpdateCommand::new("test-container")
541 .memory("512m")
542 .memory_reservation("256m")
543 .memory_swap("1g");
544 let args = cmd.build_command_args();
545 assert_eq!(
546 args,
547 vec![
548 "update",
549 "--memory",
550 "512m",
551 "--memory-reservation",
552 "256m",
553 "--memory-swap",
554 "1g",
555 "test-container"
556 ]
557 );
558 }
559
560 #[test]
561 fn test_update_cpu_options() {
562 let cmd = UpdateCommand::new("test-container")
563 .cpu_shares(512)
564 .cpu_period(100_000)
565 .cpu_quota(50000)
566 .cpus("1.5")
567 .cpuset_cpus("0,1")
568 .cpuset_mems("0");
569 let args = cmd.build_command_args();
570 assert_eq!(
571 args,
572 vec![
573 "update",
574 "--cpu-shares",
575 "512",
576 "--cpu-period",
577 "100000",
578 "--cpu-quota",
579 "50000",
580 "--cpus",
581 "1.5",
582 "--cpuset-cpus",
583 "0,1",
584 "--cpuset-mems",
585 "0",
586 "test-container"
587 ]
588 );
589 }
590
591 #[test]
592 fn test_update_all_options() {
593 let cmd = UpdateCommand::new("test-container")
594 .memory("1g")
595 .cpu_shares(1024)
596 .blkio_weight(500)
597 .kernel_memory("128m")
598 .restart("unless-stopped")
599 .pids_limit(100);
600 let args = cmd.build_command_args();
601 assert_eq!(
602 args,
603 vec![
604 "update",
605 "--memory",
606 "1g",
607 "--cpu-shares",
608 "1024",
609 "--blkio-weight",
610 "500",
611 "--kernel-memory",
612 "128m",
613 "--restart",
614 "unless-stopped",
615 "--pids-limit",
616 "100",
617 "test-container"
618 ]
619 );
620 }
621
622 #[test]
623 fn test_update_result() {
624 let result = UpdateResult {
625 output: CommandOutput {
626 stdout: "test-container".to_string(),
627 stderr: String::new(),
628 exit_code: 0,
629 success: true,
630 },
631 containers: vec!["test-container".to_string()],
632 };
633
634 assert!(result.success());
635 assert_eq!(result.containers(), &["test-container"]);
636 assert_eq!(result.container_count(), 1);
637 }
638}