From 0a8abfbd7d4683efa8d409ab0305bba8463d81fa Mon Sep 17 00:00:00 2001 From: Navid Yaghoobi Date: Sun, 6 Oct 2024 16:07:48 +1100 Subject: [PATCH] image pull and list Signed-off-by: Navid Yaghoobi --- .gitignore | 2 +- Cargo.lock | 1873 ++++++++++++++++++++++++++++++++++++ Cargo.toml | 14 +- README.md | 2 + src/builder/dist_client.rs | 43 + src/builder/mod.rs | 2 + src/builder/pull.rs | 93 ++ src/builder/reset.rs | 9 +- src/commands/images.rs | 66 ++ src/commands/mod.rs | 1 + src/commands/pull.rs | 16 +- src/commands/reset.rs | 6 +- src/error/mod.rs | 44 +- src/image/config.rs | 43 + src/image/images.rs | 125 +++ src/image/manifest.rs | 45 + src/image/mod.rs | 3 + src/layer/blob.rs | 45 + src/layer/create.rs | 39 + src/layer/layers.rs | 79 ++ src/layer/mod.rs | 3 + src/layer/store.rs | 44 +- src/main.rs | 14 +- src/utils/digest.rs | 94 ++ src/utils/mod.rs | 2 + 25 files changed, 2685 insertions(+), 22 deletions(-) create mode 100644 Cargo.lock create mode 100644 src/builder/dist_client.rs create mode 100644 src/builder/pull.rs create mode 100644 src/commands/images.rs create mode 100644 src/image/config.rs create mode 100644 src/image/images.rs create mode 100644 src/image/manifest.rs create mode 100644 src/layer/blob.rs create mode 100644 src/layer/create.rs create mode 100644 src/layer/layers.rs create mode 100644 src/utils/digest.rs diff --git a/.gitignore b/.gitignore index 1890a1a..6c718ab 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ bin/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock +#Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..56fb6ea --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1873 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" + +[[package]] +name = "cc" +version = "1.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "clap" +version = "4.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_builder" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "docker_credential" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31951f49556e34d90ed28342e1df7e1cb7a229c4cab0aecc627b5d91edd41d07" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "flate2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +dependencies = [ + "crc32fast", + "libz-sys", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getset" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f636605b743120a8d32ed92fc27b6cde1a769f8f936c065151eb66f88ded513c" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-auth" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "150fa4a9462ef926824cf4519c84ed652ca8f4fbae34cb8af045b5cbcaf98822" +dependencies = [ + "memchr", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jwt" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" +dependencies = [ + "base64 0.13.1", + "crypto-common", + "digest", + "hmac", + "serde", + "serde_json", + "sha2", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + +[[package]] +name = "libz-sys" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "oci-client" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e050f8bab3f561aa5ab537e1a6ed24006d58b4ee93cd6bb2c0cb822726f306" +dependencies = [ + "bytes", + "chrono", + "futures-util", + "http", + "http-auth", + "jwt", + "lazy_static", + "oci-spec", + "olpc-cjson", + "regex", + "reqwest", + "serde", + "serde_json", + "sha2", + "thiserror", + "tokio", + "tracing", + "unicase", +] + +[[package]] +name = "oci-spec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cee185ce7cf1cce45e194e34cd87c0bad7ff0aa2e8917009a2da4f7b31fb363" +dependencies = [ + "derive_builder", + "getset", + "regex", + "serde", + "serde_json", + "strum", + "strum_macros", + "thiserror", +] + +[[package]] +name = "ocibuilder" +version = "0.1.0" +dependencies = [ + "base16ct", + "clap", + "docker_credential", + "env_logger", + "flate2", + "fs2", + "home", + "lazy_static", + "log", + "oci-client", + "regex", + "serde", + "serde_json", + "sha2", + "tabwriter", + "tar", + "thiserror", + "tokio", +] + +[[package]] +name = "olpc-cjson" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696183c9b5fe81a7715d074fd632e8bd46f4ccc0231a3ed7fc580a80de5f7083" +dependencies = [ + "serde", + "serde_json", + "unicode-normalization", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "tabwriter" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a327282c4f64f6dc37e3bba4c2b6842cc3a992f204fa58d917696a89f691e5f6" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "tar" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "wasm-streams" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys", + "rustix", +] diff --git a/Cargo.toml b/Cargo.toml index f8e03af..3c58192 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,21 @@ homepage = "https://github.com/navidys/ocibuilder" repository = "https://github.com/navidys/ocibuilder" [dependencies] +base16ct = { version = "0.2.0", features = ["alloc"] } clap = { version = "4.5.18", features = ["derive"] } +docker_credential = "1.3.1" env_logger = "0.11.5" +flate2 = { version = "1.0.34", features = ["zlib"] } fs2 = "0.4.3" home = "0.5.9" +lazy_static = "1.5.0" log = "0.4.22" -oci-spec = "0.7.0" +oci-client = "0.13.0" +regex = "1.10.6" +serde = { version = "1.0.210", features = ["derive"] } +serde_json = "1.0.128" +sha2 = "0.10.8" +tabwriter = "1.4.0" +tar = "0.4.42" thiserror = "1.0.63" -url = "2.5.2" +tokio = { version = "1.40.0", features = ["rt-multi-thread"] } diff --git a/README.md b/README.md index d8130fe..80f4f7c 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,6 @@ The project is under development and not ready for usage (feel free to contribut | Command | Description | | ---------- | ----------- | +| images | List images in local storage. +| pull | Pull an image from the specified registry. | reset | Reset local storage. diff --git a/src/builder/dist_client.rs b/src/builder/dist_client.rs new file mode 100644 index 0000000..f4de128 --- /dev/null +++ b/src/builder/dist_client.rs @@ -0,0 +1,43 @@ +use docker_credential::{CredentialRetrievalError, DockerCredential}; +use log::{debug, warn}; +use oci_client::{client, secrets::RegistryAuth, Reference}; + +use crate::error::BuilderResult; + +pub fn build_auth(reference: &Reference, anon: bool) -> BuilderResult { + let server = reference + .resolve_registry() + .strip_suffix('/') + .unwrap_or_else(|| reference.resolve_registry()); + + if anon { + return Ok(RegistryAuth::Anonymous); + } + + match docker_credential::get_credential(server) { + Err(CredentialRetrievalError::ConfigNotFound) => Ok(RegistryAuth::Anonymous), + Err(CredentialRetrievalError::NoCredentialConfigured) => Ok(RegistryAuth::Anonymous), + Err(e) => panic!("Error handling docker configuration file: {}", e), + Ok(DockerCredential::UsernamePassword(username, password)) => { + debug!("Found docker credentials"); + Ok(RegistryAuth::Basic(username, password)) + } + Ok(DockerCredential::IdentityToken(_)) => { + warn!("Cannot use contents of docker config, identity token not supported. Using anonymous auth"); + Ok(RegistryAuth::Anonymous) + } + } +} + +pub fn build_client_config(insecure: &bool) -> BuilderResult { + let protocol = if *insecure { + client::ClientProtocol::Http + } else { + client::ClientProtocol::Https + }; + + Ok(client::ClientConfig { + protocol, + ..Default::default() + }) +} diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 0ac1c31..7d937fb 100644 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -1,2 +1,4 @@ +pub mod dist_client; pub mod oci; +pub mod pull; pub mod reset; diff --git a/src/builder/pull.rs b/src/builder/pull.rs new file mode 100644 index 0000000..e5838b5 --- /dev/null +++ b/src/builder/pull.rs @@ -0,0 +1,93 @@ +use log::debug; +use oci_client::{Client, Reference}; + +use crate::{ + builder::dist_client, + error::{BuilderError, BuilderResult}, + utils::{self, digest}, +}; + +use super::oci::OCIBuilder; + +impl OCIBuilder { + pub async fn pull(&self, image_name: &str, insecure: &bool) -> BuilderResult { + self.lock()?; + + let reference: Reference = match image_name.parse() { + Ok(img_ref) => img_ref, + Err(err) => return Err(BuilderError::InvalidImageName(image_name.to_string(), err)), + }; + + match self.image_store().image_digest(&reference.to_string()) { + Ok(dg) => return Ok(dg), + Err(err) => { + if err.to_string() != BuilderError::ImageNotFound(reference.to_string()).to_string() + { + return Err(err); + } + } + } + + let auth = dist_client::build_auth(&reference, true)?; + let client_config = dist_client::build_client_config(insecure)?; + + let client = Client::new(client_config); + + println!("Trying pull image {}...", reference); + + let (manifest, digest, config) = + match client.pull_manifest_and_config(&reference, &auth).await { + Ok((manifest, digest, config)) => (manifest, digest, config), + Err(err) => return Err(BuilderError::OciDistError(err)), + }; + + let image_digest = utils::digest::Digest::new(&digest)?; + + for layer in &manifest.layers { + let mut blob: Vec = Vec::new(); + debug!("pull blob: {}", layer.digest); + + let layer_digest = utils::digest::Digest::new(&layer.digest)?; + println!("Copying blob {:.1$}", layer_digest.encoded, 12); + + match client.pull_blob(&reference, &layer, &mut blob).await { + Ok(_) => { + self.layer_store().write_blob(&layer_digest, &blob)?; + + // create layer overlay dir + self.layer_store().create_layer_overlay_dir(&layer_digest)?; + + // add pulled layer to layers + self.layer_store().add_layer_desc(layer)?; + + // extract content to overlay diff + let over_diff = self.layer_store().overlay_diff_path(&layer_digest); + let buf = flate2::read::GzDecoder::new(blob.as_slice()); + let mut blob_archive = tar::Archive::new(buf); + blob_archive.set_preserve_ownerships(false); + match blob_archive.unpack(over_diff) { + Ok(_) => {} + Err(err) => return Err(BuilderError::ArchiveError(err.to_string())), + } + } + Err(err) => return Err(BuilderError::OciDistError(err)), + } + } + + // write image config + println!("Copying config {:.1$}", image_digest.encoded, 12); + self.image_store().write_config(&image_digest, &config)?; + + // write image manifest + println!("Writing image manifest"); + self.image_store() + .write_manifest(&image_digest, &manifest)?; + + // update images + self.image_store().write_images(&reference, &image_digest)?; + + self.unlock()?; + + Ok(image_digest) + } +} diff --git a/src/builder/reset.rs b/src/builder/reset.rs index 54d8212..1d9ec34 100644 --- a/src/builder/reset.rs +++ b/src/builder/reset.rs @@ -5,7 +5,7 @@ use crate::error::{BuilderError, BuilderResult}; use super::oci::OCIBuilder; impl OCIBuilder { - pub fn reset(&self) -> BuilderResult<()> { + pub async fn reset(&self) -> BuilderResult<()> { self.lock()?; // remove image store directory content @@ -29,6 +29,13 @@ impl OCIBuilder { Err(err) => return Err(BuilderError::IoError(lstore_path.clone(), err)), } + // remove overlay content + let overlay_path = self.layer_store().overlay_path(); + match fs::remove_dir_all(overlay_path) { + Ok(_) => {} + Err(err) => return Err(BuilderError::IoError(overlay_path.clone(), err)), + } + self.unlock()?; Ok(()) diff --git a/src/commands/images.rs b/src/commands/images.rs new file mode 100644 index 0000000..a78d24d --- /dev/null +++ b/src/commands/images.rs @@ -0,0 +1,66 @@ +use std::ffi::OsString; +use std::io::Write; + +use clap::Parser; +use oci_client::Reference; +use tabwriter::TabWriter; + +use crate::{ + builder, + error::{BuilderError, BuilderResult}, + utils, +}; + +#[derive(Parser, Debug)] +pub struct Images { + /// Pretty-print containers to JSON + #[clap(short, long)] + json: bool, +} + +impl Images { + pub fn new(json: bool) -> Self { + Self { json } + } + + pub fn exec(&self, root_dir: Option) -> BuilderResult<()> { + let root_dir_path = utils::get_root_dir(root_dir); + let builder = builder::oci::OCIBuilder::new(root_dir_path)?; + + let images = builder.image_store().images()?; + if self.json { + match serde_json::to_string_pretty(&images) { + Ok(output) => println!("{}", output), + Err(err) => return Err(BuilderError::SerdeJsonError(err)), + } + + return Ok(()); + } + + let mut tw = TabWriter::new(std::io::stdout()); + let mut output = "REPOSITORY\tTAG\tIMAGE ID\n".to_string(); + + for img in images { + let img_ref = format!("{}:{}", img.repository(), img.tag()); + let reference: Reference = match img_ref.parse() { + Ok(img_ref) => img_ref, + Err(err) => return Err(BuilderError::InvalidImageName(img_ref, err)), + }; + + let img_name = format!("{}/{}", reference.registry(), reference.repository()); + output = format!("{}{}\t{}\t{:.12}\n", output, img_name, img.tag(), img.id()); + } + + match write!(&mut tw, "{}", output) { + Ok(_) => {} + Err(err) => return Err(BuilderError::TabWriterError(err.to_string())), + } + + match tw.flush() { + Ok(_) => {} + Err(err) => return Err(BuilderError::TabWriterError(err.to_string())), + } + + Ok(()) + } +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 9778ae8..0741ff4 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,2 +1,3 @@ +pub mod images; pub mod pull; pub mod reset; diff --git a/src/commands/pull.rs b/src/commands/pull.rs index a499a80..3e948a9 100644 --- a/src/commands/pull.rs +++ b/src/commands/pull.rs @@ -8,18 +8,26 @@ use crate::{builder, error::BuilderResult, utils}; #[derive(Parser, Debug)] pub struct Pull { image_name: String, + /// Using http insecure connection instead of https + #[clap(short, long)] + insecure: bool, } impl Pull { - pub fn new(image_name: String) -> Self { - Self { image_name } + pub fn new(image_name: String, insecure: bool) -> Self { + Self { + image_name, + insecure, + } } - pub fn exec(&self, root_dir: Option) -> BuilderResult<()> { + pub async fn exec(&self, root_dir: Option) -> BuilderResult<()> { debug!("pulling image..."); let root_dir_path = utils::get_root_dir(root_dir); - let _builder = builder::oci::OCIBuilder::new(root_dir_path)?; + let builder = builder::oci::OCIBuilder::new(root_dir_path)?; + + builder.pull(&self.image_name, &self.insecure).await?; Ok(()) } diff --git a/src/commands/reset.rs b/src/commands/reset.rs index a9cf237..73bd2d0 100644 --- a/src/commands/reset.rs +++ b/src/commands/reset.rs @@ -17,14 +17,14 @@ impl Reset { Self { force } } - pub fn exec(&self, root_dir: Option) -> BuilderResult<()> { + pub async fn exec(&self, root_dir: Option) -> BuilderResult<()> { debug!("resetting storage..."); let root_dir_path = utils::get_root_dir(root_dir); let builder = builder::oci::OCIBuilder::new(root_dir_path)?; if self.force { - builder.reset()?; + builder.reset().await?; return Ok(()); } @@ -38,7 +38,7 @@ impl Reset { .expect("Failed to read input"); if user_input.to_lowercase() == "y\n" { - builder.reset()? + builder.reset().await? } Ok(()) diff --git a/src/error/mod.rs b/src/error/mod.rs index 0d35a41..6125136 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -1,20 +1,54 @@ use std::{io, path::PathBuf}; +use oci_client::{errors::OciDistributionError, ParseError}; use thiserror::Error; pub type BuilderResult = std::result::Result; #[derive(Debug, Error)] pub enum BuilderError { - #[error("url {0} parse error: because {1}")] - UrlParseError(String, url::ParseError), - - #[error("client call error: {0}")] - ClientCallError(String), + #[error("tab writer: {0}")] + TabWriterError(String), #[error("io error {0}: {1}")] IoError(PathBuf, io::Error), + #[error("json error: {0}")] + SerdeJsonError(serde_json::Error), + #[error("builder lock error: {0}")] BuilderLockError(io::Error), + + #[error("archive error: {0}")] + ArchiveError(String), + + // general builder errors + #[error("oci distribution error: {0}")] + OciDistError(OciDistributionError), + + #[error("distribution error: {0}")] + DistributionError(String), + + #[error("invalid digest: {0}")] + InvalidDigest(String), + + // image store errors + #[error("image store error: {0}")] + ImageStoreError(String), + + #[error("image not found: {0}")] + ImageNotFound(String), + + #[error("invalid image name {0}: {1}")] + InvalidImageName(String, ParseError), + + #[error("invalid image reference: {0}")] + InvalidImageReference(String), + + // layers store error + #[error("layer store error: {0}")] + LayerStoreError(String), + + #[error("layer not found: {0}")] + LayerNotFound(String), } diff --git a/src/image/config.rs b/src/image/config.rs new file mode 100644 index 0000000..e6d462c --- /dev/null +++ b/src/image/config.rs @@ -0,0 +1,43 @@ +use std::{fs, path::PathBuf}; + +use log::debug; + +use crate::{ + error::{BuilderError, BuilderResult}, + utils::digest, +}; + +use super::store::ImageStore; + +const CONFIG_FILENAME: &str = "config.json"; + +impl ImageStore { + pub fn write_config(&self, digest: &digest::Digest, config: &str) -> BuilderResult<()> { + debug!("write image config: {}", digest); + + let mut config_dir = self.istore_path().clone(); + config_dir.push(&digest.encoded); + + match fs::create_dir_all(config_dir) { + Ok(_) => { + let config_file = self.config_path(digest); + match fs::write(&config_file, config) { + Ok(_) => Ok(()), + Err(err) => Err(BuilderError::IoError(config_file, err)), + } + } + Err(err) => Err(BuilderError::ImageStoreError(format!( + "blob directory: {}", + err, + ))), + } + } + + pub fn config_path(&self, digest: &digest::Digest) -> PathBuf { + let mut cpath = self.istore_path().clone(); + cpath.push(&digest.encoded); + cpath.push(CONFIG_FILENAME); + + cpath + } +} diff --git a/src/image/images.rs b/src/image/images.rs new file mode 100644 index 0000000..477263b --- /dev/null +++ b/src/image/images.rs @@ -0,0 +1,125 @@ +use std::{fs::File, path::PathBuf}; + +use log::debug; +use oci_client::Reference; +use serde::{Deserialize, Serialize}; + +use crate::{ + error::{BuilderError, BuilderResult}, + utils::digest, +}; + +use super::store::ImageStore; + +const IMAGES_FILENAME: &str = "images.json"; + +#[derive(Debug, Deserialize, Serialize)] +pub struct Image { + repository: String, + tag: String, + id: String, +} + +impl Image { + pub fn repository(&self) -> String { + self.repository.clone() + } + + pub fn tag(&self) -> String { + self.tag.clone() + } + + pub fn id(&self) -> String { + self.id.clone() + } +} + +impl ImageStore { + pub fn images(&self) -> BuilderResult> { + let images_path = self.images_path(); + + let images_file = match File::open(&images_path) { + Ok(f) => f, + Err(err) => { + if err.kind() == std::io::ErrorKind::NotFound { + return Ok(Vec::new()); + } + + return Err(BuilderError::ImageStoreError(format!( + "{:?}: {:?}", + images_path, + err.to_string(), + ))); + } + }; + + let images: Vec = match serde_json::from_reader(images_file) { + Ok(i) => i, + Err(err) => return Err(BuilderError::ImageStoreError(err.to_string())), + }; + + Ok(images) + } + + pub fn image_digest(&self, name_or_id: &str) -> BuilderResult { + let images = self.images()?; + + for img in images { + let input_id = name_or_id.to_string(); + if img.repository() == input_id + || (input_id.len() >= 12 && img.id[..12] == input_id[..12]) + { + let img_digest = digest::Digest::new(&format!("sha256:{}", img.id))?; + return Ok(img_digest); + } + } + + Err(BuilderError::ImageNotFound(name_or_id.to_string())) + } + + pub fn write_images(&self, img_ref: &Reference, dg: &digest::Digest) -> BuilderResult<()> { + debug!("write images: {}", dg); + + let mut images = self.images()?; + + let img_repo = format!("{}/{}", img_ref.registry(), img_ref.repository()); + + images.push(Image { + repository: img_repo, + tag: img_ref.tag().unwrap_or_default().to_string(), + id: dg.encoded.to_owned(), + }); + + let images_path = self.images_path(); + let images_file = match File::create(&images_path) { + Ok(f) => f, + Err(err) => { + return Err(BuilderError::ImageStoreError(format!( + "{:?}: {:?}", + images_path, + err.to_string(), + ))); + } + }; + + match serde_json::to_writer(images_file, &images) { + Ok(_) => {} + Err(err) => { + return Err(BuilderError::ImageStoreError(format!( + "{:?}: {:?}", + images_path, + err.to_string(), + ))); + } + } + + Ok(()) + } + + pub fn images_path(&self) -> PathBuf { + let mut images_file = self.istore_path().clone(); + images_file.push(IMAGES_FILENAME); + + images_file + } +} diff --git a/src/image/manifest.rs b/src/image/manifest.rs new file mode 100644 index 0000000..e3ebbac --- /dev/null +++ b/src/image/manifest.rs @@ -0,0 +1,45 @@ +use std::{fs::File, path::PathBuf}; + +use log::debug; +use oci_client::manifest::OciImageManifest; + +use crate::{ + error::{BuilderError, BuilderResult}, + utils::digest, +}; + +use super::store::ImageStore; + +const MANIFEST_FILENAME: &str = "manifest.json"; + +impl ImageStore { + pub fn write_manifest( + &self, + digest: &digest::Digest, + manifest: &OciImageManifest, + ) -> BuilderResult<()> { + let manifest_file_path = self.manifest_path(digest); + + debug!("write manifest: {:?}", manifest_file_path); + + let manifest_file = match File::create(&manifest_file_path) { + Ok(f) => f, + Err(err) => return Err(BuilderError::IoError(manifest_file_path, err)), + }; + + match serde_json::to_writer(manifest_file, manifest) { + Ok(_) => {} + Err(err) => return Err(BuilderError::SerdeJsonError(err)), + } + + Ok(()) + } + + pub fn manifest_path(&self, digest: &digest::Digest) -> PathBuf { + let mut manifest_file = self.istore_path().clone(); + manifest_file.push(&digest.encoded); + manifest_file.push(MANIFEST_FILENAME); + + manifest_file + } +} diff --git a/src/image/mod.rs b/src/image/mod.rs index 55c88cb..e38ed45 100644 --- a/src/image/mod.rs +++ b/src/image/mod.rs @@ -1 +1,4 @@ +pub mod config; +pub mod images; +pub mod manifest; pub mod store; diff --git a/src/layer/blob.rs b/src/layer/blob.rs new file mode 100644 index 0000000..76b39cd --- /dev/null +++ b/src/layer/blob.rs @@ -0,0 +1,45 @@ +use std::{fs, path::PathBuf}; + +use crate::{ + error::{BuilderError, BuilderResult}, + utils::digest, +}; + +use super::store::LayerStore; + +impl LayerStore { + pub fn write_blob(&self, digest: &digest::Digest, blobs: &Vec) -> BuilderResult<()> { + let mut blob_dir = self.lstore_path().clone(); + blob_dir.push(&digest.algorithm); + + let blob_file = self.blob_path(digest); + + match fs::create_dir_all(blob_dir) { + Ok(_) => match fs::write(blob_file, blobs) { + Ok(_) => {} + Err(err) => { + return Err(BuilderError::LayerStoreError(format!( + "blob write: {}", + err, + ))) + } + }, + Err(err) => { + return Err(BuilderError::LayerStoreError(format!( + "blob directory: {}", + err, + ))) + } + } + + Ok(()) + } + + pub fn blob_path(&self, digest: &digest::Digest) -> PathBuf { + let mut blob_file = self.lstore_path().clone(); + blob_file.push(&digest.algorithm); + blob_file.push(&digest.encoded); + + blob_file + } +} diff --git a/src/layer/create.rs b/src/layer/create.rs new file mode 100644 index 0000000..1e6d214 --- /dev/null +++ b/src/layer/create.rs @@ -0,0 +1,39 @@ +use std::fs; + +use log::debug; + +use crate::{ + error::{BuilderError, BuilderResult}, + utils::digest, +}; + +use super::store::LayerStore; + +impl LayerStore { + pub fn create_layer_overlay_dir(&self, dg: &digest::Digest) -> BuilderResult<()> { + let mut ovpath = self.overlay_path().clone(); + ovpath.push(&dg.encoded); + + debug!("create layer overlay directories: {:?}", ovpath); + + match fs::create_dir_all(&ovpath) { + Ok(_) => {} + Err(err) => return Err(BuilderError::IoError(ovpath, err)), + } + + let overlays_subdir = [ + &self.overlay_diff_path(dg), + &self.overlay_merged_path(dg), + &self.overlay_work_path(dg), + ]; + + for dir_path in overlays_subdir { + match fs::create_dir(dir_path) { + Ok(_) => {} + Err(err) => return Err(BuilderError::IoError(dir_path.to_owned(), err)), + } + } + + Ok(()) + } +} diff --git a/src/layer/layers.rs b/src/layer/layers.rs new file mode 100644 index 0000000..861a384 --- /dev/null +++ b/src/layer/layers.rs @@ -0,0 +1,79 @@ +use std::fs::File; + +use log::debug; +use oci_client::manifest::OciDescriptor; + +use crate::{ + error::{BuilderError, BuilderResult}, + utils::{self, digest}, +}; + +use super::store::LayerStore; + +const LAYERS_FILENAME: &str = "layers.json"; + +impl LayerStore { + pub fn add_layer_desc(&self, desc: &OciDescriptor) -> BuilderResult<()> { + debug!("write layer desc: {}", desc.digest); + + let leyer_digest = utils::digest::Digest::new(&desc.digest)?; + + // layer already exist and will not add + if self.get_layer_desc(&leyer_digest).is_ok() { + return Ok(()); + } + + let mut all_layers = self.get_all_layers_desc()?; + all_layers.push(desc.to_owned()); + + let mut layers_path = self.lstore_path().clone(); + layers_path.push(LAYERS_FILENAME); + + let layers_file = match File::create(&layers_path) { + Ok(f) => f, + Err(err) => return Err(BuilderError::LayerStoreError(err.to_string())), + }; + + match serde_json::to_writer(layers_file, &all_layers) { + Ok(_) => {} + Err(err) => return Err(BuilderError::SerdeJsonError(err)), + } + + Ok(()) + } + + pub fn get_layer_desc(&self, dg: &digest::Digest) -> BuilderResult { + let layers = self.get_all_layers_desc()?; + + for layer in layers { + if layer.digest == dg.to_string() { + return Ok(layer); + } + } + + Err(BuilderError::LayerNotFound(dg.to_string())) + } + + pub fn get_all_layers_desc(&self) -> BuilderResult> { + let mut layers_path = self.lstore_path().clone(); + layers_path.push(LAYERS_FILENAME); + + let layers_file = match File::open(&layers_path) { + Ok(f) => f, + Err(err) => { + if err.kind() == std::io::ErrorKind::NotFound { + return Ok(Vec::new()); + } + + return Err(BuilderError::LayerStoreError(err.to_string())); + } + }; + + let layers: Vec = match serde_json::from_reader(layers_file) { + Ok(c) => c, + Err(err) => return Err(BuilderError::SerdeJsonError(err)), + }; + + Ok(layers) + } +} diff --git a/src/layer/mod.rs b/src/layer/mod.rs index 55c88cb..db98aee 100644 --- a/src/layer/mod.rs +++ b/src/layer/mod.rs @@ -1 +1,4 @@ +pub mod blob; +pub mod create; +pub mod layers; pub mod store; diff --git a/src/layer/store.rs b/src/layer/store.rs index 199c264..405c212 100644 --- a/src/layer/store.rs +++ b/src/layer/store.rs @@ -1,9 +1,13 @@ use std::{fs, path::PathBuf}; -use crate::error::{BuilderError, BuilderResult}; +use crate::{ + error::{BuilderError, BuilderResult}, + utils::digest, +}; pub struct LayerStore { lstore_path: PathBuf, + overlay_path: PathBuf, } impl LayerStore { @@ -16,10 +20,46 @@ impl LayerStore { Err(err) => return Err(BuilderError::IoError(lstore_path, err)), } - Ok(Self { lstore_path }) + let mut overlay_path = PathBuf::from(&root_dir); + overlay_path.push("overlay/"); + + match fs::create_dir_all(&overlay_path) { + Ok(_) => {} + Err(err) => return Err(BuilderError::IoError(overlay_path, err)), + } + + Ok(Self { + lstore_path, + overlay_path, + }) } pub fn lstore_path(&self) -> &PathBuf { &self.lstore_path } + + pub fn overlay_path(&self) -> &PathBuf { + &self.overlay_path + } + + pub fn overlay_diff_path(&self, dg: &digest::Digest) -> PathBuf { + let mut diff_path = self.overlay_path.clone(); + diff_path.push(&dg.encoded); + diff_path.push("diff"); + diff_path + } + + pub fn overlay_merged_path(&self, dg: &digest::Digest) -> PathBuf { + let mut merged_path = self.overlay_path.clone(); + merged_path.push(&dg.encoded); + merged_path.push("merged"); + merged_path + } + + pub fn overlay_work_path(&self, dg: &digest::Digest) -> PathBuf { + let mut work_path = self.overlay_path.clone(); + work_path.push(&dg.encoded); + work_path.push("work"); + work_path + } } diff --git a/src/main.rs b/src/main.rs index 074eb66..c90e584 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::ffi::OsString; use clap::{Parser, Subcommand}; -use ocibuilder::commands::reset; +use ocibuilder::commands::{images, pull, reset}; #[derive(Parser, Debug)] #[clap(version = env!("CARGO_PKG_VERSION"), about)] @@ -18,21 +18,27 @@ struct Opts { #[allow(clippy::large_enum_variant)] #[derive(Subcommand, Debug)] enum SubCommand { + /// List images in local storage + Images(images::Images), + /// Pull an image from specified registry - // Pull(pull::Pull), + Pull(pull::Pull), /// Reset local storage Reset(reset::Reset), } -fn main() { +#[tokio::main] +async fn main() { env_logger::builder().format_timestamp(None).init(); let opts = Opts::parse(); let root_dir = opts.root; let result = match opts.subcmd { - SubCommand::Reset(reset) => reset.exec(root_dir), + SubCommand::Images(images) => images.exec(root_dir), + SubCommand::Pull(pull) => pull.exec(root_dir).await, + SubCommand::Reset(reset) => reset.exec(root_dir).await, }; match result { diff --git a/src/utils/digest.rs b/src/utils/digest.rs new file mode 100644 index 0000000..a87d588 --- /dev/null +++ b/src/utils/digest.rs @@ -0,0 +1,94 @@ +use regex::Regex; +use sha2::{Digest as _, Sha256}; +use std::{fmt, io, path::PathBuf}; + +use crate::error::{BuilderError, BuilderResult}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Digest { + pub algorithm: String, + pub encoded: String, +} + +lazy_static::lazy_static! { + static ref ENCODED_RE: Regex = Regex::new(r"[a-zA-Z0-9=_-]+").unwrap(); +} + +impl fmt::Display for Digest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}:{}", self.algorithm, self.encoded) + } +} + +impl Digest { + pub fn new(input: &str) -> BuilderResult { + let mut iter = input.split(':'); + match (iter.next(), iter.next(), iter.next()) { + (Some(algorithm), Some(encoded), None) => { + // FIXME: check algorithm part + if ENCODED_RE.is_match(encoded) { + Ok(Digest { + algorithm: algorithm.to_string(), + encoded: encoded.to_string(), + }) + } else { + Err(BuilderError::InvalidDigest(input.to_string())) + } + } + _ => Err(BuilderError::InvalidDigest(input.to_string())), + } + } + + /// As a path used in oci-archive + pub fn as_path(&self) -> PathBuf { + PathBuf::from(format!("blobs/{}/{}", self.algorithm, self.encoded)) + } + + /// Calc digest using SHA-256 algorithm + pub fn from_buf_sha256(buf: &[u8]) -> Self { + let hash = Sha256::digest(buf); + let digest = base16ct::lower::encode_string(&hash); + Self { + algorithm: "sha256".to_string(), + encoded: digest, + } + } +} + +/// Wrapper for calculating hash +pub struct DigestBuf { + inner: W, + hasher: Sha256, +} + +impl DigestBuf { + pub fn new(inner: W) -> Self { + DigestBuf { + inner, + hasher: Sha256::new(), + } + } + + pub fn finish(self) -> (W, Digest) { + let hash = self.hasher.finalize(); + let digest = base16ct::lower::encode_string(&hash); + ( + self.inner, + Digest { + algorithm: "sha256".to_string(), + encoded: digest, + }, + ) + } +} + +impl io::Write for DigestBuf { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.hasher.update(buf); + self.inner.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index de716f7..c3238d1 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,5 @@ +pub mod digest; + use std::{ ffi::OsString, path::{Path, PathBuf},