soma_infra/
compose_down.rs1use async_trait::async_trait;
2use serde::{Deserialize, Serialize};
3use soma_fleet::{HostId, HostRecord, TopologyRevision};
4use soma_ops::{MutationSendState, OperationId, OperationName, Timestamp, VerificationStatus};
5use tokio_util::sync::CancellationToken;
6
7use crate::{
8 ComposeInspector, ComposeProjectRef, ComposeRecreateFingerprint, ComposeStatus, InfraError,
9 InfraResult, MutationResult, MutationVerification,
10};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct ComposeDownRequest {
15 operation_id: OperationId,
16 operation: OperationName,
17 project: ComposeProjectRef,
18 expected: ComposeRecreateFingerprint,
19 force: bool,
20 remove_volumes: bool,
21 deadline: Timestamp,
22}
23
24impl ComposeDownRequest {
25 pub fn new(
27 operation_id: OperationId,
28 operation: OperationName,
29 project: ComposeProjectRef,
30 expected: ComposeRecreateFingerprint,
31 force: bool,
32 remove_volumes: bool,
33 deadline: Timestamp,
34 ) -> InfraResult<Self> {
35 if remove_volumes && !force {
36 return Err(InfraError::InvalidRequest {
37 domain: "compose-down",
38 message: "remove_volumes=true requires force=true".into(),
39 });
40 }
41 Ok(Self {
42 operation_id,
43 operation,
44 project,
45 expected,
46 force,
47 remove_volumes,
48 deadline,
49 })
50 }
51
52 #[must_use]
54 pub fn operation_id(&self) -> &OperationId {
55 &self.operation_id
56 }
57 #[must_use]
59 pub fn operation(&self) -> &OperationName {
60 &self.operation
61 }
62 #[must_use]
64 pub const fn project(&self) -> &ComposeProjectRef {
65 &self.project
66 }
67 #[must_use]
69 pub const fn expected(&self) -> &ComposeRecreateFingerprint {
70 &self.expected
71 }
72 #[must_use]
74 pub const fn force(&self) -> bool {
75 self.force
76 }
77 #[must_use]
79 pub const fn remove_volumes(&self) -> bool {
80 self.remove_volumes
81 }
82 #[must_use]
84 pub const fn deadline(&self) -> Timestamp {
85 self.deadline
86 }
87}
88
89#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
91pub struct ComposeDownReceipt {
92 pub host: HostId,
94 pub topology_revision: TopologyRevision,
96 pub project: String,
98 pub remove_volumes: bool,
100 pub send_state: MutationSendState,
102 pub stdout: String,
104 pub stderr: String,
106 pub output_truncated: bool,
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
112pub struct ComposeDownOutcome {
113 pub host: HostId,
115 pub topology_revision: TopologyRevision,
117 pub project: String,
119 pub before: ComposeStatus,
121 pub after: ComposeStatus,
123 pub changed: bool,
125 pub receipt: ComposeDownReceipt,
127 pub verification_status: VerificationStatus,
129 pub verification: MutationVerification,
131}
132
133#[async_trait]
135pub trait ComposeDownMutator: Send + Sync {
136 async fn down_compose(
138 &self,
139 host: &HostRecord,
140 request: &ComposeDownRequest,
141 cancellation: &CancellationToken,
142 ) -> MutationResult<ComposeDownReceipt>;
143}
144
145pub trait ComposeDownClient: ComposeInspector + ComposeDownMutator {}
147impl<T> ComposeDownClient for T where T: ComposeInspector + ComposeDownMutator {}
148
149#[cfg(test)]
150#[path = "compose_down_tests.rs"]
151mod tests;