push_from_cache_manifest_demo/
push_from_cache_manifest_demo.rs1use docker_image_pusher::{
7 AuthConfig, cli::operation_mode::OperationMode, error::Result,
8 image::image_manager::ImageManager, registry::RegistryClientBuilder,
9};
10use std::env;
11
12#[tokio::main]
13async fn main() -> Result<()> {
14 println!("🚀 Docker Image Pusher - Push from Cache (Manifest) Demo");
15 println!("============================================================");
16
17 let source_repository = "yoce/cblt"; let source_reference = "yoce";
20 let target_registry = "registry.cn-beijing.aliyuncs.com";
21 let target_repository = "yoce/cblt"; let target_reference = "test-push"; let cache_dir = ".cache_demo";
24
25 println!("📥 Configuration:");
26 println!(
27 " Source (Cache): {}/{}",
28 source_repository, source_reference
29 );
30 println!(" Target Registry: {}", target_registry);
31 println!(" Target Repository: {}", target_repository);
32 println!(" Target Reference: {}", target_reference);
33 println!(" Cache Directory: {}", cache_dir);
34 println!();
35
36 check_cache_exists(cache_dir, source_repository, source_reference).await?;
38
39 println!("🔧 Creating ImageManager...");
41 let mut image_manager = ImageManager::new(Some(cache_dir), true)?;
42 println!("✅ ImageManager created successfully");
43
44 println!("🌐 Building Registry Client for Aliyun registry...");
46 let client = RegistryClientBuilder::new(format!("https://{}", target_registry))
47 .with_timeout(3600)
48 .with_skip_tls(false) .with_verbose(true)
50 .build()?;
51 println!("✅ Registry Client built successfully");
52
53 println!("🔐 Authenticating with Aliyun registry...");
55 let username = env::var("ALIYUN_USERNAME").unwrap_or_else(|_| "canny_best@163.com".to_string());
56 let password = env::var("ALIYUN_PASSWORD").unwrap_or_else(|_| "ra201222".to_string());
57
58 let auth_config = AuthConfig::new(username.clone(), password.clone());
59 let auth_token = client
60 .authenticate_for_repository(&auth_config, target_repository)
61 .await?;
62 println!("✅ Authentication successful with user: {}", username);
63 println!("🔑 Token scope: repository:{}:pull,push", target_repository);
64
65 let mode = OperationMode::PushFromCacheUsingManifest {
67 repository: target_repository.to_string(),
68 reference: target_reference.to_string(),
69 };
70
71 println!("📋 Operation Mode: {}", mode.description());
72 println!();
73
74 println!("🔄 Starting push from cache operation...");
76 match image_manager
77 .execute_operation(&mode, Some(&client), auth_token.as_deref())
78 .await
79 {
80 Ok(()) => {
81 println!("✅ Push from cache operation completed successfully!");
82 println!();
83 println!(
84 "🎯 Image pushed to: {}/{}/{}",
85 target_registry, target_repository, target_reference
86 );
87 println!("🔍 You can now verify the upload:");
88 println!(
89 " curl -H \"Authorization: Bearer <token>\" https://{}/v2/{}/manifests/{}",
90 target_registry, target_repository, target_reference
91 );
92 println!(
93 " curl -H \"Authorization: Bearer <token>\" https://{}/v2/{}/tags/list",
94 target_registry, target_repository
95 );
96 }
97 Err(e) => {
98 eprintln!("❌ Push from cache operation failed: {}", e);
99 eprintln!("💡 Possible solutions:");
100 eprintln!(
101 " - Check if source image exists in cache: {}/{}",
102 source_repository, source_reference
103 );
104 eprintln!(" - Verify Aliyun registry credentials");
105 eprintln!(" - Check network connectivity to Aliyun registry");
106 std::process::exit(1);
107 }
108 }
109
110 verify_push_result(&client, target_repository, target_reference, &auth_token).await;
112
113 Ok(())
114}
115
116async fn check_cache_exists(cache_dir: &str, repository: &str, reference: &str) -> Result<()> {
117 println!("🔍 Checking cache for source image...");
118
119 if !std::path::Path::new(cache_dir).exists() {
121 eprintln!("❌ Cache directory not found: {}", cache_dir);
122 eprintln!("💡 Please run extract_and_cache_demo.rs or pull_and_cache_demo.rs first");
123 std::process::exit(1);
124 }
125
126 let index_path = format!("{}/index.json", cache_dir);
128 if !std::path::Path::new(&index_path).exists() {
129 eprintln!("❌ Cache index not found: {}", index_path);
130 eprintln!("💡 Cache appears to be empty or corrupted");
131 std::process::exit(1);
132 }
133
134 let manifest_path = format!("{}/manifests/{}/{}", cache_dir, repository, reference);
136 if !std::path::Path::new(&manifest_path).exists() {
137 eprintln!("❌ Manifest not found in cache: {}", manifest_path);
138 eprintln!("💡 Available images in cache:");
139
140 if let Ok(index_content) = std::fs::read_to_string(&index_path) {
142 if let Ok(index) = serde_json::from_str::<serde_json::Value>(&index_content) {
143 if let Some(obj) = index.as_object() {
144 for key in obj.keys() {
145 eprintln!(" - {}", key);
146 }
147 }
148 }
149 }
150 std::process::exit(1);
151 }
152
153 println!(
154 "✅ Source image found in cache: {}/{}",
155 repository, reference
156 );
157 Ok(())
158}
159
160async fn verify_push_result(
161 client: &docker_image_pusher::registry::RegistryClient,
162 repository: &str,
163 reference: &str,
164 auth_token: &Option<String>,
165) {
166 println!();
167 println!("🔍 Verifying push result...");
168
169 match client
171 .pull_manifest(repository, reference, auth_token)
172 .await
173 {
174 Ok(manifest_data) => {
175 println!("✅ Manifest successfully retrieved from target registry");
176 println!("📊 Manifest size: {} bytes", manifest_data.len());
177
178 if let Ok(manifest) = serde_json::from_slice::<serde_json::Value>(&manifest_data) {
180 if let Some(layers) = manifest.get("layers").and_then(|v| v.as_array()) {
181 println!("📦 Number of layers: {}", layers.len());
182 }
183 }
184 }
185 Err(e) => {
186 eprintln!("⚠️ Could not verify manifest: {}", e);
187 }
188 }
189
190 match client.list_tags(repository, auth_token).await {
192 Ok(tags) => {
193 println!("🏷️ Available tags: {:?}", tags);
194 }
195 Err(e) => {
196 eprintln!("⚠️ Could not list tags: {}", e);
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[tokio::test]
206 async fn test_push_from_cache_creation() {
207 let result = ImageManager::new(Some(".test_push_cache"), false);
209 assert!(result.is_ok());
210
211 let _ = std::fs::remove_dir_all(".test_push_cache");
213 }
214
215 #[test]
216 fn test_operation_mode_description() {
217 let mode = OperationMode::PushFromCacheUsingManifest {
218 repository: "demo/test".to_string(),
219 reference: "v1.0".to_string(),
220 };
221 assert_eq!(mode.description(), "Push from cache using manifest");
222 }
223
224 #[test]
225 fn test_cache_path_construction() {
226 let cache_dir = ".test_cache";
227 let repository = "local/hello-world";
228 let reference = "latest";
229 let manifest_path = format!("{}/manifests/{}/{}", cache_dir, repository, reference);
230 assert_eq!(
231 manifest_path,
232 ".test_cache/manifests/local/hello-world/latest"
233 );
234 }
235}