@@ -6,197 +6,172 @@ use super::manager::{OsManager, PackageManager};
66pub struct DockerInstaller ;
77
88impl DockerInstaller {
9- pub async fn install ( _version : & str ) -> Result < ( ) , Box < dyn Error + Send + Sync > > {
9+ pub async fn install ( version : & str ) -> Result < ( ) , Box < dyn Error + Send + Sync > > {
1010 println ! ( "Starting Docker installation..." ) ;
11- // Note: Docker version argument is often ignored in standard repo installs unless specifically requested.
12- // For simplicity, we install the latest stable version from official repos.
11+
12+ // 1. Install System Dependencies
13+ Self :: install_system_dependencies ( ) . await ?;
14+
15+ // 2. Download Static Binaries
16+ // If version is "latest" or empty, we default to a known stable version to ensure URL validity
17+ // Or we could try to resolve "latest", but for static builds, explicit versions are safer.
18+ let docker_version = if version. is_empty ( ) || version == "latest" {
19+ "27.3.1" // Hardcoded recent stable version
20+ } else {
21+ version
22+ } ;
1323
14- let pm = OsManager :: detect_package_manager ( ) ;
15- match pm {
16- PackageManager :: Yum => Self :: install_yum ( ) . await ?,
17- PackageManager :: Apt => Self :: install_apt ( ) . await ?,
18- PackageManager :: Unknown => return Err ( "Unsupported OS for Docker installation" . into ( ) ) ,
24+ println ! ( "Downloading Docker {} static binaries..." , docker_version) ;
25+ let download_url = format ! ( "https://download.docker.com/linux/static/stable/x86_64/docker-{}.tgz" , docker_version) ;
26+
27+ let temp_dir = std:: env:: temp_dir ( ) . join ( "rustpanel_docker" ) ;
28+ if temp_dir. exists ( ) {
29+ fs:: remove_dir_all ( & temp_dir) . await ?;
30+ }
31+ fs:: create_dir_all ( & temp_dir) . await ?;
32+
33+ let tarball_path = temp_dir. join ( format ! ( "docker-{}.tgz" , docker_version) ) ;
34+ OsManager :: download_file ( & download_url, & tarball_path) . await ?;
35+
36+ // 3. Extract
37+ OsManager :: extract_tarball ( & tarball_path, & temp_dir) . await ?;
38+ // Structure is temp_dir/docker/{dockerd, docker, ...}
39+
40+ // 4. Install to Server Directory
41+ let current_dir = std:: env:: current_dir ( ) ?;
42+ let install_base = current_dir. join ( "server" ) . join ( "docker" ) ; // e.g., /rust/RustPanel/server/docker
43+ let bin_dir = install_base. join ( "bin" ) ;
44+ let data_dir = install_base. join ( "data" ) ;
45+ let config_dir = install_base. join ( "config" ) ;
46+
47+ // Clean old if exists
48+ if install_base. exists ( ) {
49+ // Stop service first just in case
50+ let _ = Command :: new ( "systemctl" ) . arg ( "stop" ) . arg ( "docker" ) . status ( ) . await ;
51+ fs:: remove_dir_all ( & install_base) . await ?;
1952 }
2053
21- // Enable and Start Docker
22- println ! ( "Starting Docker service..." ) ;
23- let _ = Command :: new ( "systemctl" ) . arg ( "enable" ) . arg ( "docker" ) . status ( ) . await ;
24- let status = Command :: new ( "systemctl" ) . arg ( "start" ) . arg ( "docker" ) . status ( ) . await ?;
25-
26- if !status. success ( ) {
27- return Err ( "Failed to start Docker service" . into ( ) ) ;
54+ fs:: create_dir_all ( & bin_dir) . await ?;
55+ fs:: create_dir_all ( & data_dir) . await ?;
56+ fs:: create_dir_all ( & config_dir) . await ?;
57+
58+ // Move binaries
59+ let extracted_docker_dir = temp_dir. join ( "docker" ) ;
60+ let mut entries = fs:: read_dir ( & extracted_docker_dir) . await ?;
61+ while let Some ( entry) = entries. next_entry ( ) . await ? {
62+ let path = entry. path ( ) ;
63+ if path. is_file ( ) {
64+ let file_name = path. file_name ( ) . unwrap ( ) ;
65+ fs:: rename ( & path, bin_dir. join ( file_name) ) . await ?;
66+ }
2867 }
2968
30- println ! ( "Docker installed successfully!" ) ;
69+ // 5. Configure daemon.json
70+ println ! ( "Configuring Docker..." ) ;
71+ let daemon_json_path = config_dir. join ( "daemon.json" ) ;
72+ let daemon_config = format ! ( r#"
73+ {{
74+ "data-root": "{}"
75+ }}
76+ "# , data_dir. display( ) . to_string( ) . replace( "\\ " , "/" ) ) ; // JSON needs forward slashes or escaped backslashes
77+
78+ fs:: write ( & daemon_json_path, daemon_config) . await ?;
79+
80+ // 6. Setup Service
81+ Self :: setup_service ( & bin_dir, & daemon_json_path) . await ?;
82+
83+ // 7. Cleanup
84+ let _ = fs:: remove_dir_all ( & temp_dir) . await ;
85+
86+ println ! ( "Docker installed successfully to {}!" , install_base. display( ) ) ;
3187 Ok ( ( ) )
3288 }
3389
3490 pub async fn uninstall ( ) -> Result < ( ) , Box < dyn Error + Send + Sync > > {
3591 println ! ( "Uninstalling Docker..." ) ;
3692
37- let pm = OsManager :: detect_package_manager ( ) ;
38-
3993 // Stop service
4094 let _ = Command :: new ( "systemctl" ) . arg ( "stop" ) . arg ( "docker" ) . status ( ) . await ;
4195 let _ = Command :: new ( "systemctl" ) . arg ( "disable" ) . arg ( "docker" ) . status ( ) . await ;
42-
43- match pm {
44- PackageManager :: Yum => {
45- let pkgs = vec ! [
46- "docker-ce" , "docker-ce-cli" , "containerd.io" , "docker-buildx-plugin" , "docker-compose-plugin" ,
47- "docker" , "docker-client" , "docker-client-latest" , "docker-common" , "docker-latest" , "docker-latest-logrotate" , "docker-logrotate" , "docker-engine"
48- ] ;
49- Command :: new ( "yum" ) . arg ( "remove" ) . arg ( "-y" ) . args ( & pkgs) . status ( ) . await ?;
50- }
51- PackageManager :: Apt => {
52- let pkgs = vec ! [
53- "docker-ce" , "docker-ce-cli" , "containerd.io" , "docker-buildx-plugin" , "docker-compose-plugin" ,
54- "docker.io" , "docker-doc" , "docker-compose" , "podman-docker" , "containerd" , "runc"
55- ] ;
56- Command :: new ( "apt-get" ) . arg ( "purge" ) . arg ( "-y" ) . args ( & pkgs) . status ( ) . await ?;
57- }
58- PackageManager :: Unknown => return Err ( "Unsupported OS" . into ( ) ) ,
96+
97+ let service_file = std:: path:: Path :: new ( "/etc/systemd/system/docker.service" ) ;
98+ if service_file. exists ( ) {
99+ fs:: remove_file ( service_file) . await ?;
100+ let _ = Command :: new ( "systemctl" ) . arg ( "daemon-reload" ) . status ( ) . await ;
59101 }
60102
61- // Cleanup directories
62- let paths = vec ! [ "/var/lib/docker" , "/var/lib/containerd" , "/etc/docker" ] ;
63- for path in paths {
64- let p = std:: path:: Path :: new ( path) ;
65- if p. exists ( ) {
66- let _ = fs:: remove_dir_all ( p) . await ;
67- }
103+ // Remove files
104+ let current_dir = std:: env:: current_dir ( ) ?;
105+ let install_base = current_dir. join ( "server" ) . join ( "docker" ) ;
106+ if install_base. exists ( ) {
107+ fs:: remove_dir_all ( install_base) . await ?;
68108 }
69109
70110 println ! ( "Docker uninstalled." ) ;
71111 Ok ( ( ) )
72112 }
73113
74- async fn install_yum ( ) -> Result < ( ) , Box < dyn Error + Send + Sync > > {
75- // 1. Remove old versions
76- let old_pkgs = vec ! [
77- "docker" , "docker-client" , "docker-client-latest" , "docker-common" , "docker-latest" , "docker-latest-logrotate" , "docker-logrotate" , "docker-engine"
78- ] ;
79- let _ = Command :: new ( "yum" ) . arg ( "remove" ) . arg ( "-y" ) . args ( & old_pkgs) . status ( ) . await ;
80-
81- // 2. Install utils
82- OsManager :: install_dependencies ( & [ "yum-utils" ] ) . await ?;
83-
84- // 3. Add Repo
85- println ! ( "Adding Docker repo..." ) ;
86- let status = Command :: new ( "yum-config-manager" )
87- . arg ( "--add-repo" )
88- . arg ( "https://download.docker.com/linux/centos/docker-ce.repo" )
89- . status ( )
90- . await ?;
91-
92- if !status. success ( ) {
93- // Try to handle RedHat/Fedora if CentOS repo fails, or just proceed hoping it worked or user has repo.
94- // But let's assume standard CentOS/RHEL/Alinux/etc compatibility.
95- return Err ( "Failed to add Docker repository" . into ( ) ) ;
96- }
97-
98- // 4. Install Docker
99- println ! ( "Installing Docker packages..." ) ;
100- let pkgs = vec ! [ "docker-ce" , "docker-ce-cli" , "containerd.io" , "docker-buildx-plugin" , "docker-compose-plugin" ] ;
101- OsManager :: install_dependencies ( & pkgs) . await ?;
102-
103- Ok ( ( ) )
114+ async fn install_system_dependencies ( ) -> Result < ( ) , Box < dyn Error + Send + Sync > > {
115+ let pm = OsManager :: detect_package_manager ( ) ;
116+ let pkgs = match pm {
117+ PackageManager :: Yum => vec ! [
118+ "iptables" , "git" , "xz" , "procps" // Basic requirements
119+ ] ,
120+ PackageManager :: Apt => vec ! [
121+ "iptables" , "git" , "xz-utils" , "procps" , "iproute2"
122+ ] ,
123+ PackageManager :: Unknown => return Err ( "Unsupported OS" . into ( ) ) ,
124+ } ;
125+ OsManager :: install_dependencies ( & pkgs) . await
104126 }
105127
106- async fn install_apt ( ) -> Result < ( ) , Box < dyn Error + Send + Sync > > {
107- // 1. Remove old versions
108- let old_pkgs = vec ! [
109- "docker.io" , "docker-doc" , "docker-compose" , "podman-docker" , "containerd" , "runc"
110- ] ;
111- let _ = Command :: new ( "apt-get" ) . arg ( "remove" ) . arg ( "-y" ) . args ( & old_pkgs) . status ( ) . await ;
112-
113- // 2. Install utils
114- OsManager :: install_dependencies ( & [ "ca-certificates" , "curl" , "gnupg" ] ) . await ?;
115-
116- // 3. Add GPG Key
117- println ! ( "Adding Docker GPG key..." ) ;
118- fs:: create_dir_all ( "/etc/apt/keyrings" ) . await ?;
119- let keyring_path = "/etc/apt/keyrings/docker.gpg" ;
120- // Remove old if exists
121- if std:: path:: Path :: new ( keyring_path) . exists ( ) {
122- let _ = fs:: remove_file ( keyring_path) . await ;
123- }
124-
125- // Curl pipe to gpg --dearmor
126- // Since we are in Rust, let's use Command chain or just download and shell out for simplicity of pipe
127- let status = Command :: new ( "bash" )
128- . arg ( "-c" )
129- . arg ( "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg" )
130- . status ( )
131- . await ?;
128+ async fn setup_service ( bin_dir : & std:: path:: Path , config_path : & std:: path:: Path ) -> Result < ( ) , Box < dyn Error + Send + Sync > > {
129+ let dockerd_path = bin_dir. join ( "dockerd" ) ;
132130
133- if !status. success ( ) {
134- // Fallback for Debian if ubuntu fails?
135- // Ideally we should detect distro codename.
136- // But let's try generic approach or check /etc/os-release
137- // For now, assume Ubuntu/Debian compatible.
138- return Err ( "Failed to add Docker GPG key" . into ( ) ) ;
139- }
140- let _ = Command :: new ( "chmod" ) . arg ( "a+r" ) . arg ( keyring_path) . status ( ) . await ;
141-
142- // 4. Add Repo
143- println ! ( "Adding Docker repository..." ) ;
144- // Get codename
145- let output = Command :: new ( "lsb_release" ) . arg ( "-cs" ) . output ( ) . await ;
146- let codename = if let Ok ( out) = output {
147- String :: from_utf8_lossy ( & out. stdout ) . trim ( ) . to_string ( )
148- } else {
149- // Fallback: cat /etc/os-release
150- "jammy" . to_string ( ) // unsafe assumption, but standard on many. Ideally parse /etc/os-release.
151- } ;
131+ // Add bin_dir to PATH in environment or just absolute path
132+ // We need to make sure 'containerd' and 'runc' are found.
133+ // Docker static bundle puts them all in same dir.
134+ // dockerd needs them in PATH.
152135
153- // Better codename detection
154- let codename = Self :: get_distro_codename ( ) . await . unwrap_or ( "jammy" . to_string ( ) ) ;
155-
156- // Determine if ubuntu or debian
157- let os_release = fs:: read_to_string ( "/etc/os-release" ) . await . unwrap_or_default ( ) . to_lowercase ( ) ;
158- let repo_url = if os_release. contains ( "debian" ) {
159- "https://download.docker.com/linux/debian"
160- } else {
161- "https://download.docker.com/linux/ubuntu"
162- } ;
163-
164- let repo_line = format ! (
165- "deb [arch=\" amd64\" signed-by={}] {} {} stable" ,
166- keyring_path, repo_url, codename
167- ) ;
136+ let service_content = format ! ( r#"
137+ [Unit]
138+ Description=Docker Application Container Engine
139+ Documentation=https://docs.docker.com
140+ After=network-online.target firewalld.service
141+ Wants=network-online.target
142+
143+ [Service]
144+ Type=notify
145+ Environment="PATH={}:/usr/bin:/usr/local/bin"
146+ ExecStart={} --config-file {}
147+ ExecReload=/bin/kill -s HUP $MAINPID
148+ TimeoutSec=0
149+ RestartSec=2
150+ Restart=always
151+ StartLimitBurst=3
152+ StartLimitInterval=60s
153+ LimitNOFILE=infinity
154+ LimitNPROC=infinity
155+ LimitCORE=infinity
156+ TasksMax=infinity
157+ Delegate=yes
158+ KillMode=process
159+ OOMScoreAdjust=-500
160+
161+ [Install]
162+ WantedBy=multi-user.target
163+ "# , bin_dir. display( ) , dockerd_path. display( ) , config_path. display( ) ) ;
164+
165+ fs:: write ( "/etc/systemd/system/docker.service" , service_content) . await ?;
168166
169- fs:: write ( "/etc/apt/sources.list.d/docker.list" , repo_line) . await ?;
170-
171- // 5. Update and Install
172- println ! ( "Updating apt cache..." ) ;
173- let _ = Command :: new ( "apt-get" ) . arg ( "update" ) . status ( ) . await ;
174-
175- println ! ( "Installing Docker packages..." ) ;
176- let pkgs = vec ! [ "docker-ce" , "docker-ce-cli" , "containerd.io" , "docker-buildx-plugin" , "docker-compose-plugin" ] ;
177- OsManager :: install_dependencies ( & pkgs) . await ?;
178-
179- Ok ( ( ) )
180- }
181-
182- async fn get_distro_codename ( ) -> Option < String > {
183- // Try lsb_release
184- if let Ok ( output) = Command :: new ( "lsb_release" ) . arg ( "-cs" ) . output ( ) . await {
185- if output. status . success ( ) {
186- return Some ( String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ) ;
187- }
188- }
167+ let _ = Command :: new ( "systemctl" ) . arg ( "daemon-reload" ) . status ( ) . await ;
168+ let _ = Command :: new ( "systemctl" ) . arg ( "enable" ) . arg ( "docker" ) . status ( ) . await ;
169+ let status = Command :: new ( "systemctl" ) . arg ( "start" ) . arg ( "docker" ) . status ( ) . await ?;
189170
190- // Try parsing /etc/os-release
191- if let Ok ( content) = fs:: read_to_string ( "/etc/os-release" ) . await {
192- for line in content. lines ( ) {
193- if line. starts_with ( "VERSION_CODENAME=" ) {
194- return Some ( line. trim_start_matches ( "VERSION_CODENAME=" ) . trim_matches ( '"' ) . to_string ( ) ) ;
195- }
196- }
197- // Fallback for systems without VERSION_CODENAME (like older CentOS, though we are in apt block)
198- // Debian usually has VERSION_CODENAME
171+ if !status. success ( ) {
172+ return Err ( "Failed to start Docker service" . into ( ) ) ;
199173 }
200- None
174+
175+ Ok ( ( ) )
201176 }
202177}
0 commit comments