Skip to main content

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}