arcbox_migration/model.rs
1//! Normalized migration model types.
2
3use crate::docker_types::NetworkIpamConfig;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::path::PathBuf;
7
8/// Supported migration source runtimes.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "kebab-case")]
11pub enum SourceKind {
12 /// Docker Desktop on macOS.
13 DockerDesktop,
14 /// OrbStack on macOS.
15 OrbStack,
16}
17
18impl SourceKind {
19 /// Returns the stable CLI/protocol identifier for the source kind.
20 #[must_use]
21 pub const fn as_str(self) -> &'static str {
22 match self {
23 Self::DockerDesktop => "docker-desktop",
24 Self::OrbStack => "orbstack",
25 }
26 }
27}
28
29/// Source configuration used by the planner and executor.
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31pub struct SourceConfig {
32 /// Selected source runtime.
33 pub kind: SourceKind,
34 /// Source Docker Engine socket path.
35 pub socket_path: PathBuf,
36}
37
38/// Minimal source identity discovered during preflight.
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
40pub struct SourceInfo {
41 /// Selected source runtime.
42 pub kind: SourceKind,
43 /// Source Docker Engine socket path.
44 pub socket_path: PathBuf,
45 /// Docker daemon name.
46 pub daemon_name: String,
47 /// Reported server version.
48 pub server_version: String,
49 /// Reported operating system.
50 pub operating_system: String,
51 /// Reported architecture.
52 pub architecture: String,
53}
54
55/// A fully normalized migration plan.
56#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
57pub struct MigrationPlan {
58 /// Source identity.
59 pub source: SourceInfo,
60 /// Helper image reference used for temporary volume-mount containers.
61 pub helper_image: String,
62 /// Images that will be imported into ArcBox.
63 pub images: Vec<ImagePlan>,
64 /// Volumes that will be imported into ArcBox.
65 pub volumes: Vec<VolumePlan>,
66 /// Networks that will be recreated in ArcBox.
67 pub networks: Vec<NetworkPlan>,
68 /// Containers that will be recreated in ArcBox.
69 pub containers: Vec<ContainerPlan>,
70 /// Resources that are out of scope for v1.
71 pub unsupported_resources: Vec<String>,
72 /// Replace actions that require confirmation.
73 pub replacements: ReplacementSummary,
74 /// Source volume blockers caused by running containers.
75 pub blockers: Vec<RunningVolumeBlocker>,
76}
77
78/// Source image transfer description.
79#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
80pub struct ImagePlan {
81 /// Source image identifier.
82 pub image_id: String,
83 /// Image reference used for export.
84 pub export_reference: String,
85 /// Repo tags preserved by the source daemon.
86 pub repo_tags: Vec<String>,
87 /// Repo tags that will overwrite an existing ArcBox tag.
88 pub replace_tags: Vec<String>,
89}
90
91/// Source volume transfer description.
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
93pub struct VolumePlan {
94 /// Source volume name.
95 pub name: String,
96 /// Volume driver.
97 pub driver: String,
98 /// Volume labels preserved during recreate.
99 pub labels: HashMap<String, String>,
100 /// Driver options preserved during recreate.
101 pub options: HashMap<String, String>,
102 /// Whether the target volume will be replaced.
103 pub replace_existing: bool,
104 /// Source containers referencing the volume.
105 pub attached_containers: Vec<String>,
106}
107
108/// Source network transfer description.
109#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
110pub struct NetworkPlan {
111 /// Source network name.
112 pub name: String,
113 /// Source network identifier.
114 pub id: String,
115 /// Docker network driver.
116 pub driver: String,
117 /// Whether the network is internal.
118 pub internal: bool,
119 /// Whether IPv6 is enabled.
120 pub enable_ipv6: bool,
121 /// Whether the network is attachable.
122 pub attachable: bool,
123 /// Network labels preserved during recreate.
124 pub labels: HashMap<String, String>,
125 /// Network options preserved during recreate.
126 pub options: HashMap<String, String>,
127 /// IPAM subnet configuration preserved during recreate.
128 pub ipam: Vec<NetworkIpamConfig>,
129 /// Whether the target network will be replaced.
130 pub replace_existing: bool,
131}
132
133/// A recreated container definition.
134#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
135pub struct ContainerPlan {
136 /// Source container name without the leading slash.
137 pub name: String,
138 /// Source container identifier.
139 pub id: String,
140 /// Image reference used when recreating the container.
141 pub image_reference: String,
142 /// Normalized container creation spec.
143 pub spec: ContainerSpec,
144 /// Additional network attachments after create.
145 pub extra_networks: Vec<ContainerNetworkAttachment>,
146 /// Whether the target container will be replaced.
147 pub replace_existing: bool,
148}
149
150/// Container creation spec translated from inspect output.
151#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
152pub struct ContainerSpec {
153 /// Hostname.
154 pub hostname: Option<String>,
155 /// Domain name.
156 pub domainname: Option<String>,
157 /// User.
158 pub user: Option<String>,
159 /// Environment variables.
160 pub env: Vec<String>,
161 /// Labels.
162 pub labels: HashMap<String, String>,
163 /// Exposed ports.
164 pub exposed_ports: Vec<String>,
165 /// Whether tty mode is enabled.
166 pub tty: bool,
167 /// Whether stdin should stay open.
168 pub open_stdin: bool,
169 /// Working directory.
170 pub working_dir: Option<String>,
171 /// Entrypoint argv.
172 pub entrypoint: Vec<String>,
173 /// Command argv.
174 pub cmd: Vec<String>,
175 /// Mount definitions.
176 pub mounts: Vec<ContainerMount>,
177 /// Port publish rules.
178 pub publishes: Vec<PortPublish>,
179 /// Restart policy.
180 pub restart_policy: Option<RestartPolicySpec>,
181 /// Privileged mode.
182 pub privileged: bool,
183 /// Read-only root filesystem.
184 pub read_only_rootfs: bool,
185 /// Extra hosts.
186 pub extra_hosts: Vec<String>,
187 /// Auto-remove on exit.
188 pub auto_remove: bool,
189 /// Primary network attached during create.
190 pub primary_network: Option<ContainerNetworkAttachment>,
191}
192
193/// Supported mount definitions.
194#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
195pub enum ContainerMount {
196 /// Named volume mount.
197 Volume {
198 /// Volume name.
199 source: String,
200 /// Container destination path.
201 target: String,
202 /// Whether the mount is writable.
203 rw: bool,
204 },
205 /// Bind mount.
206 Bind {
207 /// Host path.
208 source: String,
209 /// Container destination path.
210 target: String,
211 /// Whether the mount is writable.
212 rw: bool,
213 },
214 /// Tmpfs mount.
215 Tmpfs {
216 /// Container destination path.
217 target: String,
218 /// Mount options string.
219 options: Option<String>,
220 },
221}
222
223/// Host port publish rule.
224#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
225pub struct PortPublish {
226 /// Port and protocol inside the container.
227 pub container_port: String,
228 /// Host IP, if present.
229 pub host_ip: Option<String>,
230 /// Host port.
231 pub host_port: Option<String>,
232}
233
234/// Restart policy.
235#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
236pub struct RestartPolicySpec {
237 /// Policy name.
238 pub name: String,
239 /// Maximum retry count.
240 pub maximum_retry_count: Option<i64>,
241}
242
243/// Container network attachment.
244#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
245pub struct ContainerNetworkAttachment {
246 /// Network name.
247 pub network: String,
248 /// Network-scoped aliases.
249 pub aliases: Vec<String>,
250}
251
252/// Aggregate replacement summary.
253#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
254pub struct ReplacementSummary {
255 /// Container names that will be replaced.
256 pub containers: Vec<String>,
257 /// Volume names that will be replaced.
258 pub volumes: Vec<String>,
259 /// Network names that will be replaced.
260 pub networks: Vec<String>,
261 /// Image tags that will be overwritten.
262 pub image_tags: Vec<String>,
263}
264
265impl ReplacementSummary {
266 /// Returns true when no replace action is required.
267 #[must_use]
268 pub fn is_empty(&self) -> bool {
269 self.containers.is_empty()
270 && self.volumes.is_empty()
271 && self.networks.is_empty()
272 && self.image_tags.is_empty()
273 }
274}
275
276/// A source volume blocked by running containers.
277#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
278pub struct RunningVolumeBlocker {
279 /// Volume name.
280 pub volume_name: String,
281 /// Running source containers using the volume.
282 pub containers: Vec<String>,
283}