From 472540e1f00f51ac5bc26631a67cf2adcdfc03e3 Mon Sep 17 00:00:00 2001 From: Legokichi Duckscallion Date: Mon, 19 Jun 2023 16:54:39 +0900 Subject: [PATCH 1/3] add docker pull test --- Cargo.lock | 175 +++++++++++++++++-- Cargo.toml | 2 + src/test.rs | 484 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 651 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 34860e5..0445582 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,7 +36,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -47,7 +47,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -126,6 +126,19 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "dirs" version = "5.0.1" @@ -170,10 +183,12 @@ dependencies = [ "nix", "openssl", "rand", + "rstest", "rustls", "rustls-pemfile", "serde", "serde_json", + "serial_test", "tar", "thiserror", "tokio", @@ -262,6 +277,7 @@ checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -284,6 +300,17 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.28" @@ -298,7 +325,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -313,6 +340,12 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + [[package]] name = "futures-util" version = "0.3.28" @@ -583,6 +616,16 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.18" @@ -704,7 +747,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -731,6 +774,29 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets 0.48.0", +] + [[package]] name = "percent-encoding" version = "2.3.0" @@ -754,7 +820,7 @@ checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -873,6 +939,41 @@ dependencies = [ "winapi", ] +[[package]] +name = "rstest" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de1bb486a691878cd320c2f0d319ba91eeaa2e894066d8b5f8f117c000e9d962" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290ca1a1c8ca7edb7c3283bd44dc35dd54fdec6253a3912e201ba1072018fca8" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", + "unicode-ident", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.37.19" @@ -945,6 +1046,12 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "sct" version = "0.7.0" @@ -978,6 +1085,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + [[package]] name = "serde" version = "1.0.163" @@ -995,7 +1108,7 @@ checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -1009,6 +1122,31 @@ dependencies = [ "serde", ] +[[package]] +name = "serial_test" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" +dependencies = [ + "dashmap", + "futures", + "lazy_static", + "log", + "parking_lot", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + [[package]] name = "slab" version = "0.4.8" @@ -1018,6 +1156,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + [[package]] name = "socket2" version = "0.4.9" @@ -1040,6 +1184,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.18" @@ -1092,7 +1247,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -1135,7 +1290,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -1296,7 +1451,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.18", "wasm-bindgen-shared", ] @@ -1318,7 +1473,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index f82880f..279d67f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,8 @@ thiserror = "1" [dev-dependencies] rand = "0.8" +rstest = "0.17" +serial_test = "2" [target.'cfg(unix)'.dependencies] hyperlocal = "0.8" diff --git a/src/test.rs b/src/test.rs index a39c264..637f6e5 100644 --- a/src/test.rs +++ b/src/test.rs @@ -158,3 +158,487 @@ fn get_stats_response() -> http::Response { let body = include_str!("fixtures/stats_stream.json").to_string(); response.body(hyper::Body::from(body)).unwrap() } + +// docker run -d -p 5000:5000 registry +// docker push localhost:5000/hello-world:latest +static MANIFEST: &str = r###"{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 1470, + "digest": "sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 2457, + "digest": "sha256:719385e32844401d57ecfd3eacab360bf551a1491c05b85806ed8f1b08d792f6" + } + ] +}"###; +static CONFIG: &str = r###"eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJIb3N0bmFtZSI6IiIsIkRvbWFpbm5h +bWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNl +LCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRp +bk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46 +L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpbIi9oZWxsbyJdLCJJbWFnZSI6 +InNoYTI1Njo2MmExNTYxOTAzN2YzYzRmYjRlNmJhOWJkMjI0Y2JhMzU0MGUzOTNhNTVkYzUyZjZi +ZWJlMjEyY2E3YjVlMWE3IiwiVm9sdW1lcyI6bnVsbCwiV29ya2luZ0RpciI6IiIsIkVudHJ5cG9p +bnQiOm51bGwsIk9uQnVpbGQiOm51bGwsIkxhYmVscyI6bnVsbH0sImNvbnRhaW5lciI6IjM0N2Nh +Njg4NzJlZTkyNGM0ZjkzOTRiMTk1ZGNhZGFmNTkxZDM4N2E0NWQ2MjQyMjUyNTFlZmM2Y2I3YTM0 +OGUiLCJjb250YWluZXJfY29uZmlnIjp7Ikhvc3RuYW1lIjoiMzQ3Y2E2ODg3MmVlIiwiRG9tYWlu +bmFtZSI6IiIsIlVzZXIiOiIiLCJBdHRhY2hTdGRpbiI6ZmFsc2UsIkF0dGFjaFN0ZG91dCI6ZmFs +c2UsIkF0dGFjaFN0ZGVyciI6ZmFsc2UsIlR0eSI6ZmFsc2UsIk9wZW5TdGRpbiI6ZmFsc2UsIlN0 +ZGluT25jZSI6ZmFsc2UsIkVudiI6WyJQQVRIPS91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2Jp +bjovdXNyL3NiaW46L3Vzci9iaW46L3NiaW46L2JpbiJdLCJDbWQiOlsiL2Jpbi9zaCIsIi1jIiwi +Iyhub3ApICIsIkNNRCBbXCIvaGVsbG9cIl0iXSwiSW1hZ2UiOiJzaGEyNTY6NjJhMTU2MTkwMzdm +M2M0ZmI0ZTZiYTliZDIyNGNiYTM1NDBlMzkzYTU1ZGM1MmY2YmViZTIxMmNhN2I1ZTFhNyIsIlZv +bHVtZXMiOm51bGwsIldvcmtpbmdEaXIiOiIiLCJFbnRyeXBvaW50IjpudWxsLCJPbkJ1aWxkIjpu +dWxsLCJMYWJlbHMiOnt9fSwiY3JlYXRlZCI6IjIwMjMtMDUtMDRUMTc6Mzc6MDMuODcyOTU4NzEy +WiIsImRvY2tlcl92ZXJzaW9uIjoiMjAuMTAuMjMiLCJoaXN0b3J5IjpbeyJjcmVhdGVkIjoiMjAy +My0wNS0wNFQxNzozNzowMy44MDE4NDA4MjNaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhu +b3ApIENPUFkgZmlsZToyMDFmOGYxODQ5ZTg5ZDUzYmU5ZjZhYTc2OTM3ZjVlMjA5ZDc0NWFiZmQx +NWE4NTUyZmNmMmJhNDVhYjI2N2Y5IGluIC8gIn0seyJjcmVhdGVkIjoiMjAyMy0wNS0wNFQxNzoz +NzowMy44NzI5NTg3MTJaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApICBDTUQgW1wi +L2hlbGxvXCJdIiwiZW1wdHlfbGF5ZXIiOnRydWV9XSwib3MiOiJsaW51eCIsInJvb3RmcyI6eyJ0 +eXBlIjoibGF5ZXJzIiwiZGlmZl9pZHMiOlsic2hhMjU2OjAxYmI0ZmNlM2ViMWI1NmIwNWFkZjk5 +NTA0ZGFmZDMxOTA3YTVhYWRhYzczNmUzNmIyNzU5NWM4YjkyZjA3ZjEiXX19"###; +static LAYER: &str = r###"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 +lWQAOgAA +"###; +fn v2_version(req: &hyper::Request) -> Option> { + match (req.method(), req.uri().path()) { + (&hyper::Method::GET, "/v2/") => { + // curl -X GET -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -s -D - http://localhost:5000/v2/ + let res = hyper::Response::builder() + .status(hyper::StatusCode::OK) + .header( + "Content-Type", + "Content-Type: application/json; charset=utf-8", + ) + .header("Docker-Distribution-Api-Version", "registry/2.0") + .header("X-Content-Type-Options", "nosniff") + .header("Content-Length", "2") + .body(hyper::Body::from("{}")) + .unwrap(); + Some(res) + } + _ => None, + } +} +fn v2_manifest_latest(req: &hyper::Request) -> Option> { + match (req.method(), req.uri().path()) { + (&hyper::Method::HEAD, "/v2/hello-world/manifests/latest") => { + // curl -X HEAD -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -s -D - http://localhost:5000/v2/hello-world/manifests/latest + let res = hyper::Response::builder() + .status(hyper::StatusCode::OK) + .header( + "Content-Type", + "application/vnd.docker.distribution.manifest.v2+json", + ) + .header( + "Docker-Content-Digest", + "sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3", + ) + .header("Docker-Distribution-Api-Version", "registry/2.0") + .header("X-Content-Type-Options", "nosniff") + .header("Content-Length", "525") + .body(hyper::Body::empty()) + .unwrap(); + Some(res) + } + _ => None, + } +} +fn v2_manifest_sha256( + req: &hyper::Request, + conn_shutdown: bool, +) -> Option> { + match (req.method(), req.uri().path()) { + (&hyper::Method::GET, "/v2/hello-world/manifests/sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3") => { + let body = MANIFEST.as_bytes(); + assert_eq!(body.len(), 525); + let body = async_stream::stream!{ + for (_i, bytes) in body.chunks(16).enumerate() { + if conn_shutdown { + // simulate a connection shutdown + return; + } + tokio::time::sleep(std::time::Duration::from_millis(10)).await; + yield Ok(bytes.to_vec()) as Result<_, std::io::Error>; + } + }; + // curl -X GET -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -s -D - http://localhost:5000/v2/hello-world/manifests/sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3 + let res = hyper::Response::builder() + .status(hyper::StatusCode::OK) + .header("Content-Type", "application/vnd.docker.distribution.manifest.v2+json") + .header("Docker-Content-Digest", "sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3") + .header("Docker-Distribution-Api-Version", "registry/2.0") + .header("X-Content-Type-Options", "nosniff") + .header("Content-Length", "525") + .body(hyper::Body::wrap_stream(body)) + .unwrap(); + Some(res) + }, + _ => { None } + } +} +fn v2_blob_config( + req: &hyper::Request, + conn_shutdown: bool, +) -> Option> { + match (req.method(), req.uri().path()) { + (&hyper::Method::GET, "/v2/hello-world/blobs/sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d") => { + use base64::Engine as _; + let body = CONFIG.lines().collect::(); + let body = base64::engine::general_purpose::STANDARD_NO_PAD.decode(body).unwrap(); + assert_eq!(body.len(), 1470); + let body = async_stream::stream!{ + for (_i, bytes) in body.chunks(16).enumerate() { + if conn_shutdown { + // simulate a connection shutdown + return; + } + tokio::time::sleep(std::time::Duration::from_millis(10)).await; + yield Ok(bytes.to_vec()) as Result<_, std::io::Error>; + } + }; + // curl -X GET -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -s -D - http://localhost:5000/v2/hello-world/blobs/sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d + let res = hyper::Response::builder() + .status(hyper::StatusCode::OK) + .header("Accept-Ranges", "bytes") + .header("Content-Type", "application/octet-stream") + .header("Docker-Content-Digest", "sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d") + .header("Docker-Distribution-Api-Version", "registry/2.0") + .header("X-Content-Type-Options", "nosniff") + .header("Content-Length", "1470") + .body(hyper::Body::wrap_stream(body)) + .unwrap(); + Some(res) + }, + _ => { None } + } +} +fn v2_blob_layer( + req: &hyper::Request, + conn_shutdown_count: Option, +) -> Option> { + match (req.method(), req.uri().path()) { + (&hyper::Method::GET, "/v2/hello-world/blobs/sha256:719385e32844401d57ecfd3eacab360bf551a1491c05b85806ed8f1b08d792f6") => { + dbg!(&req); + use base64::Engine as _; + let body = LAYER.lines().collect::(); + let body = base64::engine::general_purpose::STANDARD_NO_PAD.decode(body).unwrap(); + assert_eq!(body.len(), 2457); + let body = async_stream::stream!{ + for (i, bytes) in body.chunks(16).enumerate() { + if let Some(count) = conn_shutdown_count { + if i > count { + // simulate a connection shutdown + return; + } + } + tokio::time::sleep(std::time::Duration::from_millis(10)).await; + yield Ok(bytes.to_vec()) as Result<_, std::io::Error>; + } + }; + // curl -X GET -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -s -D - http://localhost:5000/v2/hello-world/blobs/sha256:719385e32844401d57ecfd3eacab360bf551a1491c05b85806ed8f1b08d792f6 + let res = hyper::Response::builder() + .status(hyper::StatusCode::OK) + .header("Content-Type", "application/octet-stream") + .header("Docker-Content-Digest", "sha256:719385e32844401d57ecfd3eacab360bf551a1491c05b85806ed8f1b08d792f6") + .header("Docker-Distribution-Api-Version", "registry/2.0") + .header("X-Content-Type-Options", "nosniff") + .header("Content-Length", "2457") + .body(hyper::Body::wrap_stream(body)) + .unwrap(); + Some(res) + }, + _ => { None } + } +} +#[tokio::test] +#[serial_test::serial] +async fn pull_succ() { + let dw = crate::Docker::connect_with_defaults().unwrap(); + let pass = crate::credentials::UserPassword::new( + "".to_string(), + "".to_string(), + "".to_string(), + "".to_string(), + ); + let cred = crate::credentials::Credential::with_password(pass); + dw.set_credential(cred); + let image_id = "localhost:3000/hello-world:latest"; + dw.remove_image(image_id, Some(true), Some(true)).await.ok(); + let (server_ready_tx, server_ready_rx) = tokio::sync::oneshot::channel::<()>(); + let (shutdown_server_tx, shutdown_server_rx) = tokio::sync::oneshot::channel::<()>(); + let fut1 = async { + let app = hyper::service::make_service_fn(|_conn| async move { + let svc = + hyper::service::service_fn(move |req: hyper::Request| async move { + v2_version(&req) + .or_else(|| v2_manifest_latest(&req)) + .or_else(|| v2_manifest_sha256(&req, false)) + .or_else(|| v2_blob_config(&req, false)) + .or_else(|| v2_blob_layer(&req, None)) + .ok_or_else(|| format!("abort: {req:?}")) + }); + Ok(svc) as Result<_, std::convert::Infallible> + }); + let fut = hyper::Server::bind(&std::net::SocketAddr::from(([0, 0, 0, 0], 3000))).serve(app); + let fut = futures::future::join( + async { + fut.await.unwrap(); + }, + async { + server_ready_tx.send(()).unwrap(); + }, + ); + tokio::select! { + _ = fut => { unreachable!() }, + _ = shutdown_server_rx => {}, + } + }; + let fut2 = async { + server_ready_rx.await.unwrap(); + // manifest の取得までは POST /images/create の response header を返す前に行われる + // manifest は containerd が /var/lib/containerd 以下にキャッシュしており、 docker rmi + // では削除できない + // その後 response body の json stream で blob の download が行われる + let res = dw.create_image(image_id, "").await.unwrap(); + use futures::StreamExt; + res.enumerate() + .for_each(|(i, msg)| async move { + println!("{i}:{msg:?}"); + }) + .await; + shutdown_server_tx.send(()).unwrap(); + }; + futures::future::join(fut1, fut2).await; + dw.remove_image(image_id, Some(true), Some(true)).await.ok(); +} +#[rstest::rstest] +#[case( + "cannot_lookup", + "Get https://unexist.actcast.io/v2/: dial tcp: lookup unexist.actcast.io: no such host" +)] +#[case( + "cannot_connect", + "Get http://localhost:3001/v2/: dial tcp 127.0.0.1:3001: connect: connection refused" +)] +#[case("cannot_get_version", "Get http://localhost:3000/v2/: EOF")] +#[case( + "cannot_get_manifest", + "Head http://localhost:3000/v2/hello-world/manifests/latest: EOF" +)] +#[case("cannot_get_manifest_sha256", "Get http://localhost:3000/v2/hello-world/manifests/sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3: EOF")] +// this test sometimes failed if manifest file is cached +#[case("cannot_download_manifest_sha256", "Get http://localhost:3000/v2/hello-world/manifests/sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3: EOF")] +#[serial_test::serial] +#[tokio::test] +async fn pull_failed_before_stream_response( + #[case] case: &'static str, + #[case] message: &'static str, +) { + let dw = crate::Docker::connect_with_defaults().unwrap(); + let pass = crate::credentials::UserPassword::new( + "".to_string(), + "".to_string(), + "".to_string(), + "".to_string(), + ); + let cred = crate::credentials::Credential::with_password(pass); + dw.set_credential(cred); + let image_id = match case { + "cannot_lookup" => "unexist.actcast.io/hello-world:latest", + "cannot_connect" => "localhost:3001/hello-world:latest", + _ => "localhost:3000/hello-world:latest", + }; + dw.remove_image(image_id, Some(true), Some(true)).await.ok(); + let (server_ready_tx, server_ready_rx) = tokio::sync::oneshot::channel::<()>(); + let (shutdown_server_tx, shutdown_server_rx) = tokio::sync::oneshot::channel::<()>(); + let fut1 = async { + let app = hyper::service::make_service_fn(|_conn| async move { + let svc = + hyper::service::service_fn(move |req: hyper::Request| async move { + match case { + "cannot_get_version" => Err("abort"), + "cannot_get_manifest" => v2_version(&req).ok_or("abort"), + "cannot_get_manifest_sha256" => v2_version(&req) + .or_else(|| v2_manifest_latest(&req)) + .ok_or("abort"), + "cannot_download_manifest_sha256" => v2_version(&req) + .or_else(|| v2_manifest_latest(&req)) + .or_else(|| v2_manifest_sha256(&req, true)) + .ok_or("abort"), + _ => unreachable!(), + } + }); + Ok(svc) as Result<_, std::convert::Infallible> + }); + let fut = hyper::Server::bind(&std::net::SocketAddr::from(([0, 0, 0, 0], 3000))).serve(app); + let fut = futures::future::join( + async { + fut.await.unwrap(); + }, + async { + server_ready_tx.send(()).unwrap(); + }, + ); + tokio::select! { + _ = fut => { unreachable!() }, + _ = shutdown_server_rx => {}, + } + }; + let fut2 = async { + server_ready_rx.await.unwrap(); + // manifest の取得までは POST /images/create の response header を返す前に行われる + // manifest は containerd が /var/lib/containerd 以下にキャッシュしており、 docker rmi + // では削除できない + // その後 response body の json stream で blob の download が行われる + let res = dw.create_image(image_id, "").await; + if let Err(crate::errors::Error::Docker(err)) = res { + dbg!(&err); + assert_eq!(err.message, message); + } else { + panic!("assertion failed"); + } + shutdown_server_tx.send(()).unwrap(); + }; + futures::future::join(fut1, fut2).await; + dw.remove_image(image_id, Some(true), Some(true)).await.ok(); +} + +#[rstest::rstest] +#[case(true, 999, "error pulling image configuration: Get http://localhost:3000/v2/hello-world/blobs/sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d: EOF", "error pulling image configuration: Get http://localhost:3000/v2/hello-world/blobs/sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d: EOF")] +#[case(false, 0, "unexpected EOF", "unexpected EOF")] +#[case( + false, + 1, + "expected HTTP 206 from byte range request", + "expected HTTP 206 from byte range request" +)] +#[serial_test::serial] +#[tokio::test] +async fn pull_failed_on_response_stream( + #[case] v2_blob_config_flag: bool, + #[case] v2_blob_layer_count: usize, + #[case] error: &str, + #[case] message: &str, +) { + let dw = crate::Docker::connect_with_defaults().unwrap(); + let pass = crate::credentials::UserPassword::new( + "".to_string(), + "".to_string(), + "".to_string(), + "".to_string(), + ); + let cred = crate::credentials::Credential::with_password(pass); + dw.set_credential(cred); + let image_id = "localhost:3000/hello-world:latest"; + dw.remove_image(image_id, Some(true), Some(true)).await.ok(); + let (server_ready_tx, server_ready_rx) = tokio::sync::oneshot::channel::<()>(); + let (shutdown_server_tx, shutdown_server_rx) = tokio::sync::oneshot::channel::<()>(); + let fut1 = async { + let app = hyper::service::make_service_fn(|_conn| async move { + let svc = + hyper::service::service_fn(move |req: hyper::Request| async move { + v2_version(&req) + .or_else(|| v2_manifest_latest(&req)) + .or_else(|| v2_manifest_sha256(&req, false)) + .or_else(|| v2_blob_config(&req, v2_blob_config_flag)) + .or_else(|| v2_blob_layer(&req, Some(v2_blob_layer_count))) + .ok_or("abort") + }); + Ok(svc) as Result<_, std::convert::Infallible> + }); + let fut = hyper::Server::bind(&std::net::SocketAddr::from(([0, 0, 0, 0], 3000))).serve(app); + let fut = futures::future::join( + async { + fut.await.unwrap(); + }, + async { + server_ready_tx.send(()).unwrap(); + }, + ); + tokio::select! { + _ = fut => { unreachable!() }, + _ = shutdown_server_rx => {}, + } + }; + let fut2 = async { + server_ready_rx.await.unwrap(); + let res = dw.create_image(image_id, "").await.unwrap(); + use futures::StreamExt; + let results = res + .enumerate() + .map(move |(i, msg)| { + println!("{i}:{msg:?}"); + msg + }) + .collect::>() + .await; + let ret = results.into_iter().any(|msg| { + use crate::response::Error; + use crate::response::ErrorDetail; + use crate::response::Response; + if let Ok(msg) = msg { + msg == Response::Error(Error { + error: error.to_string(), + errorDetail: ErrorDetail { + message: message.to_string(), + }, + }) + } else { + false + } + }); + assert!(ret); + shutdown_server_tx.send(()).unwrap(); + }; + futures::future::join(fut1, fut2).await; + dw.remove_image(image_id, Some(true), Some(true)).await.ok(); +} From 94c89cd794b526bf9798216527605700e22948ca Mon Sep 17 00:00:00 2001 From: Legokichi Duckscallion Date: Mon, 19 Jun 2023 16:58:33 +0900 Subject: [PATCH 2/3] add comment --- src/test.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test.rs b/src/test.rs index 637f6e5..ab1ec33 100644 --- a/src/test.rs +++ b/src/test.rs @@ -161,6 +161,10 @@ fn get_stats_response() -> http::Response { // docker run -d -p 5000:5000 registry // docker push localhost:5000/hello-world:latest +// original +// - https://hub.docker.com/_/hello-world +// - https://github.com/docker-library/hello-world/ +// - in MIT License static MANIFEST: &str = r###"{ "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.v2+json", From b8e0a3ba4484d9fe1c4974b56722d88e4b3c11ee Mon Sep 17 00:00:00 2001 From: Legokichi Duckscallion Date: Mon, 19 Jun 2023 17:42:55 +0900 Subject: [PATCH 3/3] fix test --- .circleci/config.yml | 27 ++++++++++++------- src/test.rs | 64 +++++++++++++++++++++++++++++--------------- 2 files changed, 61 insertions(+), 30 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 16e8f14..e8cd09a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,32 +21,34 @@ jobs: build: working_directory: /tmp/dockworker - docker: - - image: rust:1.49.0 - environment: - DOCKER_VERSION: 18.03.1-ce + machine: + image: ubuntu-2204:2023.04.2 + environment: + DOCKER_VERSION: 18.03.1-ce steps: - checkout - - setup_remote_docker - attach_workspace: at: /tmp/dockworker - run: name: Setup Packages command: | - apt-get update - apt-get install -y ca-certificates curl libssl-dev + sudo apt-get update + sudo apt-get install -y ca-certificates curl libssl-dev - run: name: Install Docker Client command: | # client only - curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz | tar -zxf - --strip 1 -C /usr/local/bin docker/docker + curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz | sudo tar -zxf - --strip 1 -C /usr/local/bin docker/docker - run: name: Install rustfmt command: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + source "$HOME/.cargo/env" rustup component add rustfmt - run: name: Generate cache key command: | + source "$HOME/.cargo/env" cat Cargo.toml Cargo.lock > /tmp/build-dep rustc --version >> /tmp/build-dep rustfmt --version >> /tmp/build-dep @@ -56,29 +58,36 @@ jobs: key: cache-cargo-target-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLECI_CACHE_VERSION }}-{{ checksum "/tmp/build-dep" }} - run: name: Checking source code style - command: cargo fmt -- --check + command: | + source "$HOME/.cargo/env" + cargo fmt -- --check - run: name: Build project command: | + source "$HOME/.cargo/env" RUST_BACKTRACE=1 cargo build --verbose --features ssl RUST_BACKTRACE=1 cargo build --examples -j 8 - run: name: Unit testing with ssl feature command: | + source "$HOME/.cargo/env" RUST_BACKTRACE=1 cargo test -j 8 --verbose --features ssl - run: name: Unit testing with ssl-rustls feature command: | + source "$HOME/.cargo/env" RUST_BACKTRACE=1 cargo test -j 8 --verbose --features ssl-rustls - run: name: More unit testing command: | + source "$HOME/.cargo/env" docker load -i test-iostream docker load -i test-signal RUST_BACKTRACE=1 cargo test --verbose --features ssl -- --ignored - run: name: More unit testing with ssl-rustls command: | + source "$HOME/.cargo/env" RUST_BACKTRACE=1 cargo test --verbose --features ssl-rustls -- --ignored - save_cache: key: cache-cargo-target-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLECI_CACHE_VERSION }}-{{ checksum "/tmp/build-dep" }} diff --git a/src/test.rs b/src/test.rs index ab1ec33..18cd144 100644 --- a/src/test.rs +++ b/src/test.rs @@ -377,7 +377,7 @@ fn v2_blob_layer( let body = base64::engine::general_purpose::STANDARD_NO_PAD.decode(body).unwrap(); assert_eq!(body.len(), 2457); let body = async_stream::stream!{ - for (i, bytes) in body.chunks(16).enumerate() { + for (i, bytes) in body.chunks(8).enumerate() { if let Some(count) = conn_shutdown_count { if i > count { // simulate a connection shutdown @@ -415,7 +415,7 @@ async fn pull_succ() { ); let cred = crate::credentials::Credential::with_password(pass); dw.set_credential(cred); - let image_id = "localhost:3000/hello-world:latest"; + let image_id = "localhost:37564/hello-world:latest"; dw.remove_image(image_id, Some(true), Some(true)).await.ok(); let (server_ready_tx, server_ready_rx) = tokio::sync::oneshot::channel::<()>(); let (shutdown_server_tx, shutdown_server_rx) = tokio::sync::oneshot::channel::<()>(); @@ -432,7 +432,8 @@ async fn pull_succ() { }); Ok(svc) as Result<_, std::convert::Infallible> }); - let fut = hyper::Server::bind(&std::net::SocketAddr::from(([0, 0, 0, 0], 3000))).serve(app); + let fut = + hyper::Server::bind(&std::net::SocketAddr::from(([0, 0, 0, 0], 37564))).serve(app); let fut = futures::future::join( async { fut.await.unwrap(); @@ -448,6 +449,7 @@ async fn pull_succ() { }; let fut2 = async { server_ready_rx.await.unwrap(); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; // manifest の取得までは POST /images/create の response header を返す前に行われる // manifest は containerd が /var/lib/containerd 以下にキャッシュしており、 docker rmi // では削除できない @@ -464,23 +466,23 @@ async fn pull_succ() { futures::future::join(fut1, fut2).await; dw.remove_image(image_id, Some(true), Some(true)).await.ok(); } + #[rstest::rstest] #[case( "cannot_lookup", - "Get https://unexist.actcast.io/v2/: dial tcp: lookup unexist.actcast.io: no such host" + "Get \"https://unexist.actcast.io/v2/\": dial tcp: lookup unexist.actcast.io: no such host" )] #[case( "cannot_connect", - "Get http://localhost:3001/v2/: dial tcp 127.0.0.1:3001: connect: connection refused" + "Get \"http://localhost:13001/v2/\": dial tcp 127.0.0.1:13001: connect: connection refused" )] -#[case("cannot_get_version", "Get http://localhost:3000/v2/: EOF")] +#[case("cannot_get_version", "Get \"http://localhost:37564/v2/\": EOF")] #[case( "cannot_get_manifest", - "Head http://localhost:3000/v2/hello-world/manifests/latest: EOF" + "Head \"http://localhost:37564/v2/hello-world/manifests/latest\": EOF" )] -#[case("cannot_get_manifest_sha256", "Get http://localhost:3000/v2/hello-world/manifests/sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3: EOF")] -// this test sometimes failed if manifest file is cached -#[case("cannot_download_manifest_sha256", "Get http://localhost:3000/v2/hello-world/manifests/sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3: EOF")] +#[case("cannot_get_manifest_sha256", "Get \"http://localhost:37564/v2/hello-world/manifests/sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3\": EOF")] +#[case("cannot_download_manifest_sha256", "Get \"http://localhost:37564/v2/hello-world/manifests/sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3\": EOF")] #[serial_test::serial] #[tokio::test] async fn pull_failed_before_stream_response( @@ -498,8 +500,8 @@ async fn pull_failed_before_stream_response( dw.set_credential(cred); let image_id = match case { "cannot_lookup" => "unexist.actcast.io/hello-world:latest", - "cannot_connect" => "localhost:3001/hello-world:latest", - _ => "localhost:3000/hello-world:latest", + "cannot_connect" => "localhost:13001/hello-world:latest", + _ => "localhost:37564/hello-world:latest", }; dw.remove_image(image_id, Some(true), Some(true)).await.ok(); let (server_ready_tx, server_ready_rx) = tokio::sync::oneshot::channel::<()>(); @@ -523,7 +525,8 @@ async fn pull_failed_before_stream_response( }); Ok(svc) as Result<_, std::convert::Infallible> }); - let fut = hyper::Server::bind(&std::net::SocketAddr::from(([0, 0, 0, 0], 3000))).serve(app); + let fut = + hyper::Server::bind(&std::net::SocketAddr::from(([0, 0, 0, 0], 37564))).serve(app); let fut = futures::future::join( async { fut.await.unwrap(); @@ -539,6 +542,7 @@ async fn pull_failed_before_stream_response( }; let fut2 = async { server_ready_rx.await.unwrap(); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; // manifest の取得までは POST /images/create の response header を返す前に行われる // manifest は containerd が /var/lib/containerd 以下にキャッシュしており、 docker rmi // では削除できない @@ -557,14 +561,20 @@ async fn pull_failed_before_stream_response( } #[rstest::rstest] -#[case(true, 999, "error pulling image configuration: Get http://localhost:3000/v2/hello-world/blobs/sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d: EOF", "error pulling image configuration: Get http://localhost:3000/v2/hello-world/blobs/sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d: EOF")] -#[case(false, 0, "unexpected EOF", "unexpected EOF")] #[case( - false, - 1, - "expected HTTP 206 from byte range request", - "expected HTTP 206 from byte range request" + true, + 999, + "error pulling image configuration: download failed after attempts=6: EOF", + "error pulling image configuration: download failed after attempts=6: EOF" )] +// thoses tests sometimes failed if blob file is cached +//#[case(false, 0, "unexpected EOF", "unexpected EOF")] +//#[case( +// false, +// 1, +// "expected HTTP 206 from byte range request", +// "expected HTTP 206 from byte range request" +//)] #[serial_test::serial] #[tokio::test] async fn pull_failed_on_response_stream( @@ -582,8 +592,18 @@ async fn pull_failed_on_response_stream( ); let cred = crate::credentials::Credential::with_password(pass); dw.set_credential(cred); - let image_id = "localhost:3000/hello-world:latest"; + let image_id = "localhost:37564/hello-world:latest"; dw.remove_image(image_id, Some(true), Some(true)).await.ok(); + let output = std::process::Command::new("sudo") + .args([ + "rm", + "-rf", + "/var/lib/containerd/io.containerd.content.v1.content/*", + ]) + .output() + .unwrap(); + println!("{}", String::from_utf8(output.stdout).unwrap()); + println!("{}", String::from_utf8(output.stderr).unwrap()); let (server_ready_tx, server_ready_rx) = tokio::sync::oneshot::channel::<()>(); let (shutdown_server_tx, shutdown_server_rx) = tokio::sync::oneshot::channel::<()>(); let fut1 = async { @@ -599,7 +619,8 @@ async fn pull_failed_on_response_stream( }); Ok(svc) as Result<_, std::convert::Infallible> }); - let fut = hyper::Server::bind(&std::net::SocketAddr::from(([0, 0, 0, 0], 3000))).serve(app); + let fut = + hyper::Server::bind(&std::net::SocketAddr::from(([0, 0, 0, 0], 37564))).serve(app); let fut = futures::future::join( async { fut.await.unwrap(); @@ -615,6 +636,7 @@ async fn pull_failed_on_response_stream( }; let fut2 = async { server_ready_rx.await.unwrap(); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; let res = dw.create_image(image_id, "").await.unwrap(); use futures::StreamExt; let results = res