pull_and_cache_demo/
pull_and_cache_demo.rs

1//! Example: Pull and Cache - 从 registry 拉取镜像并缓存
2//!
3//! 此示例展示如何从远程registry拉取Docker镜像并将其缓存到本地。
4//! 这是4种核心操作模式中的第1种:PullAndCache
5
6use 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 - Pull and Cache Demo");
15    println!("==================================================");
16
17    // 配置参数 - 支持环境变量
18    let registry = env::var("DOCKER_REGISTRY")
19        .unwrap_or_else(|_| "https://registry.cn-beijing.aliyuncs.com".to_string());
20    let repository = env::var("DOCKER_REPOSITORY").unwrap_or_else(|_| "yoce/cblt".to_string());
21    let reference = env::var("DOCKER_REFERENCE").unwrap_or_else(|_| "yoce".to_string());
22    let cache_dir = ".cache_demo";
23
24    println!("📥 Configuration:");
25    println!("  Registry: {}", registry);
26    println!("  Repository: {}", repository);
27    println!("  Reference: {}", reference);
28    println!("  Cache Directory: {}", cache_dir);
29    println!();
30
31    // 1. 创建 ImageManager
32    println!("🔧 Creating ImageManager...");
33    let mut image_manager = ImageManager::new(Some(cache_dir), true)?;
34    println!("✅ ImageManager created successfully");
35
36    // 2. 构建 Registry Client
37    println!("🌐 Building Registry Client...");
38    let client = RegistryClientBuilder::new(registry.to_string())
39        .with_timeout(3600)
40        .with_verbose(true)
41        .build()?;
42    println!("✅ Registry Client built successfully");
43
44    // 3. 获取认证(总是尝试,支持匿名token)
45    println!("🔐 Attempting authentication...");
46    let auth_token = if let (Ok(username), Ok(password)) =
47        (env::var("DOCKER_USERNAME"), env::var("DOCKER_PASSWORD"))
48    {
49        println!("  Using provided credentials for user: {}", username);
50        let auth_config = AuthConfig::new(username, password);
51        client
52            .authenticate_for_repository(&auth_config, &repository)
53            .await?
54    } else {
55        println!("  No credentials provided, trying anonymous authentication...");
56        // 使用直接认证方法尝试获取匿名token
57        let auth = docker_image_pusher::registry::auth::Auth::new();
58        let output = docker_image_pusher::logging::Logger::new(true);
59        auth.authenticate_with_registry(&registry, &repository, None, None, &output)
60            .await?
61    };
62
63    if auth_token.is_some() {
64        println!("✅ Authentication successful");
65    } else {
66        println!("ℹ️  No authentication required");
67    }
68
69    // 4. 定义操作模式
70    let mode = OperationMode::PullAndCache {
71        repository: repository.to_string(),
72        reference: reference.to_string(),
73    };
74
75    println!("📋 Operation Mode: {}", mode.description());
76    println!();
77
78    // 5. 执行拉取和缓存操作
79    println!("🔄 Starting pull and cache operation...");
80    match image_manager
81        .execute_operation(&mode, Some(&client), auth_token.as_deref())
82        .await
83    {
84        Ok(()) => {
85            println!("✅ Pull and cache operation completed successfully!");
86            println!();
87            println!("📂 Image cached to: {}", cache_dir);
88            println!("🔍 You can now inspect the cache contents:");
89            println!(
90                "   - Manifests: {}/manifests/{}/{}",
91                cache_dir, repository, reference
92            );
93            println!("   - Blobs: {}/blobs/sha256/", cache_dir);
94            println!("   - Index: {}/index.json", cache_dir);
95        }
96        Err(e) => {
97            eprintln!("❌ Pull and cache operation failed: {}", e);
98            std::process::exit(1);
99        }
100    }
101
102    // 6. 显示缓存统计
103    show_cache_stats(cache_dir).await;
104
105    Ok(())
106}
107
108async fn show_cache_stats(cache_dir: &str) {
109    println!();
110    println!("📊 Cache Statistics:");
111
112    // 检查缓存目录
113    if let Ok(entries) = std::fs::read_dir(format!("{}/blobs/sha256", cache_dir)) {
114        let blob_count = entries.count();
115        println!("  📦 Cached blobs: {}", blob_count);
116    }
117
118    // 检查索引文件
119    if let Ok(index_content) = std::fs::read_to_string(format!("{}/index.json", cache_dir)) {
120        if let Ok(index) = serde_json::from_str::<serde_json::Value>(&index_content) {
121            if let Some(obj) = index.as_object() {
122                println!("  📋 Cached images: {}", obj.len());
123            }
124        }
125    }
126
127    // 显示缓存目录大小
128    if let Ok(metadata) = std::fs::metadata(cache_dir) {
129        if metadata.is_dir() {
130            println!("  💾 Cache directory exists and is ready");
131        }
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[tokio::test]
140    async fn test_pull_and_cache_creation() {
141        // 测试 ImageManager 创建
142        let result = ImageManager::new(Some(".test_cache"), false);
143        assert!(result.is_ok());
144
145        // 清理测试缓存
146        let _ = std::fs::remove_dir_all(".test_cache");
147    }
148
149    #[tokio::test]
150    async fn test_registry_client_creation() {
151        // 测试 RegistryClient 创建
152        let result = RegistryClientBuilder::new("https://registry-1.docker.io".to_string())
153            .with_timeout(60)
154            .build();
155        assert!(result.is_ok());
156    }
157
158    #[test]
159    fn test_operation_mode_description() {
160        let mode = OperationMode::PullAndCache {
161            repository: "test/repo".to_string(),
162            reference: "latest".to_string(),
163        };
164        assert_eq!(mode.description(), "Pull from registry and cache locally");
165    }
166}