diff --git a/edge-modules/api-proxy-module/src/main.rs b/edge-modules/api-proxy-module/src/main.rs index 4e8190f4f76..0159f889587 100644 --- a/edge-modules/api-proxy-module/src/main.rs +++ b/edge-modules/api-proxy-module/src/main.rs @@ -194,7 +194,7 @@ pub fn nginx_controller_start( // This delay controls how fast the main loop can reconnect once nginx crashed. // Without this, nginx crashes and restart continuously. Rapidly increasing the size of the logs. - time::sleep(MAX_LOOP_INTERVAL_SECONDS); + time::sleep(MAX_LOOP_INTERVAL_SECONDS).await; } } }); diff --git a/edge-modules/edgehub-proxy/Cargo.lock b/edge-modules/edgehub-proxy/Cargo.lock index 2b4f342ade0..a72dec0002c 100644 --- a/edge-modules/edgehub-proxy/Cargo.lock +++ b/edge-modules/edgehub-proxy/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "aho-corasick" version = "0.7.18" @@ -69,21 +54,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" -[[package]] -name = "backtrace" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "base64" version = "0.13.0" @@ -197,10 +167,10 @@ name = "edgelet-utils" version = "0.1.0" dependencies = [ "config", - "failure", "log", "serde", "serde_json", + "thiserror", "yaml-rust", ] @@ -217,28 +187,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "fnv" version = "1.0.7" @@ -321,12 +269,6 @@ dependencies = [ "slab", ] -[[package]] -name = "gimli" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" - [[package]] name = "hermit-abi" version = "0.1.19" @@ -501,16 +443,6 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - [[package]] name = "mio" version = "0.7.13" @@ -578,15 +510,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.8.0" @@ -723,12 +646,6 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" -[[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - [[package]] name = "ryu" version = "1.0.5" @@ -805,18 +722,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "synstructure" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "474aaa926faa1603c40b7885a9eaea29b444d1cb2850cb7c0e37bb1a4182f4fa" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - [[package]] name = "termcolor" version = "1.1.2" diff --git a/edgelet/Cargo.lock b/edgelet/Cargo.lock index 51e58f280d7..0f641864a0a 100644 --- a/edgelet/Cargo.lock +++ b/edgelet/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" -dependencies = [ - "gimli", -] - [[package]] name = "adler" version = "1.0.2" @@ -71,9 +62,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "aziot-cert-client-async" @@ -82,10 +73,10 @@ source = "git+https://github.com/Azure/iot-identity-service?branch=main#df08bfaa dependencies = [ "aziot-cert-common-http", "aziot-key-common", - "http 0.2.5", + "http", "http-common", "hyper", - "percent-encoding 2.1.0", + "percent-encoding", ] [[package]] @@ -103,13 +94,13 @@ version = "0.1.0" source = "git+https://github.com/Azure/iot-identity-service?branch=main#df08bfaad7d2c2657ddc41cedc32fe0cc04561b1" dependencies = [ "cert-renewal", - "hex 0.4.3", + "hex", "http-common", "libc", "openssl", "serde", "serde_with", - "url 2.2.2", + "url", ] [[package]] @@ -119,7 +110,7 @@ dependencies = [ "aziot-identity-client-async", "aziot-identity-common", "aziot-identity-common-http", - "base64 0.13.0", + "base64", "clap", "edgelet-core", "edgelet-docker", @@ -135,7 +126,7 @@ dependencies = [ "serde_json", "sha2", "tokio", - "url 2.2.2", + "url", ] [[package]] @@ -146,10 +137,10 @@ dependencies = [ "aziot-cert-common-http", "aziot-identity-common", "aziot-identity-common-http", - "http 0.2.5", + "http", "http-common", "hyper", - "percent-encoding 2.1.0", + "percent-encoding", ] [[package]] @@ -186,7 +177,7 @@ dependencies = [ "http-common", "libc", "serde", - "url 2.2.2", + "url", ] [[package]] @@ -196,10 +187,10 @@ source = "git+https://github.com/Azure/iot-identity-service?branch=main#df08bfaa dependencies = [ "aziot-key-common", "aziot-key-common-http", - "http 0.2.5", + "http", "http-common", "httparse", - "percent-encoding 2.1.0", + "percent-encoding", "serde", "serde_json", ] @@ -211,10 +202,10 @@ source = "git+https://github.com/Azure/iot-identity-service?branch=main#df08bfaa dependencies = [ "aziot-key-common", "aziot-key-common-http", - "http 0.2.5", + "http", "http-common", "hyper", - "percent-encoding 2.1.0", + "percent-encoding", ] [[package]] @@ -242,7 +233,7 @@ source = "git+https://github.com/Azure/iot-identity-service?branch=main#df08bfaa dependencies = [ "aziot-key-client", "aziot-key-common", - "base64 0.13.0", + "base64", "foreign-types-shared", "log", "openssl", @@ -270,7 +261,7 @@ source = "git+https://github.com/Azure/iot-identity-service?branch=main#df08bfaa dependencies = [ "pkcs11", "serde", - "url 2.2.2", + "url", ] [[package]] @@ -293,7 +284,7 @@ dependencies = [ "aziot-keyd-config", "aziot-keys-common", "aziot-tpmd-config", - "base64 0.13.0", + "base64", "cert-renewal", "http-common", "log", @@ -301,41 +292,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "url 2.2.2", -] - -[[package]] -name = "backtrace" -version = "0.3.62" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091bcdf2da9950f96aa522681ce805e6857f6ca8df73833d35736ab2dc78e152" -dependencies = [ - "addr2line", - "cc", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -dependencies = [ - "byteorder", - "safemem", -] - -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", + "url", ] [[package]] @@ -371,16 +328,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "iovec", -] - [[package]] name = "bytes" version = "1.1.0" @@ -430,12 +377,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -506,6 +447,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a079bddaa385eab2a88dc5816a378921852ab3af6646748da14681b2facf502" +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "cpufeatures" version = "0.2.1" @@ -521,7 +468,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -530,7 +477,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", ] @@ -540,7 +487,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] @@ -551,7 +498,7 @@ version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", "lazy_static", "memoffset", @@ -564,7 +511,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "lazy_static", ] @@ -612,41 +559,34 @@ dependencies = [ "generic-array", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "docker" version = "0.1.0" dependencies = [ "async-trait", - "base64 0.9.3", - "futures 0.1.31", + "base64", + "futures", "http-common", "hyper", "serde", "serde_derive", "serde_json", - "typed-headers", - "url 1.7.2", + "url", ] [[package]] name = "edgelet-core" version = "0.1.0" dependencies = [ + "anyhow", "async-trait", "aziotctl-common", - "base64 0.13.0", - "bytes 1.1.0", + "base64", + "bytes", "chrono", "consistenttime", "edgelet-settings", - "failure", - "futures 0.3.17", + "futures", "humantime", "hyper", "lazy_static", @@ -658,44 +598,41 @@ dependencies = [ "serde_json", "serde_with", "test-case", + "thiserror", "tokio", "tokio-util", - "url 2.2.2", + "url", ] [[package]] name = "edgelet-docker" version = "0.1.0" dependencies = [ + "anyhow", "async-trait", "aziot-cert-client-async", "aziot-cert-common-http", - "base64 0.9.3", - "bytes 1.1.0", + "base64", + "bytes", "chrono", "config-common", "docker", "edgelet-core", "edgelet-settings", "edgelet-utils", - "failure", - "futures 0.3.17", - "hex 0.3.2", + "futures", + "hex", "http-common", "hyper", "libc", "log", - "maplit", "serde", "serde_derive", "serde_json", "sysinfo", - "tempdir", - "tempfile", - "time", + "thiserror", "tokio", - "typed-headers", - "url 2.2.2", + "url", ] [[package]] @@ -708,11 +645,11 @@ dependencies = [ "edgelet-settings", "edgelet-test-utils", "futures-util", - "http 0.2.5", + "http", "http-common", "libc", "log", - "percent-encoding 2.1.0", + "percent-encoding", "serde", "serde_json", "tokio", @@ -731,20 +668,20 @@ dependencies = [ "edgelet-settings", "edgelet-test-utils", "futures-util", - "http 0.2.5", + "http", "http-common", "hyper", "libc", "log", "nix", - "percent-encoding 2.1.0", + "percent-encoding", "regex", "serde", "serde_json", "support-bundle", "test-common", "tokio", - "url 2.2.2", + "url", ] [[package]] @@ -762,27 +699,27 @@ dependencies = [ "aziot-key-common", "aziot-key-common-http", "aziot-key-openssl-engine", - "base64 0.13.0", + "base64", "chrono", "edgelet-core", "edgelet-http", "edgelet-settings", "edgelet-test-utils", "futures-util", - "http 0.2.5", + "http", "http-common", "hyper", "libc", "log", "nix", "openssl", - "percent-encoding 2.1.0", + "percent-encoding", "regex", "serde", "serde_json", "test-common", "tokio", - "url 2.2.2", + "url", ] [[package]] @@ -795,23 +732,24 @@ dependencies = [ "serde", "serde_json", "test-case", - "url 2.2.2", + "url", ] [[package]] name = "edgelet-test-utils" version = "0.1.0" dependencies = [ + "anyhow", "async-trait", "aziot-identity-common", "aziot-key-client", "aziot-key-common", - "bytes 1.1.0", + "bytes", "edgelet-core", "edgelet-settings", - "futures 0.3.17", + "futures", "futures-util", - "http 0.2.5", + "http", "hyper", "nix", "openssl", @@ -823,11 +761,11 @@ name = "edgelet-utils" version = "0.1.0" dependencies = [ "config", - "failure", "log", "serde", "serde_derive", "serde_json", + "thiserror", "tokio", "yaml-rust", ] @@ -848,7 +786,7 @@ dependencies = [ "humantime", "log", "regex", - "termcolor 1.1.2", + "termcolor", ] [[package]] @@ -860,35 +798,13 @@ dependencies = [ "serde", ] -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "flate2" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crc32fast", "libc", "miniz_oxide", @@ -922,21 +838,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" dependencies = [ "matches", - "percent-encoding 2.1.0", + "percent-encoding", ] -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "futures" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - [[package]] name = "futures" version = "0.3.17" @@ -1047,29 +951,23 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi", ] -[[package]] -name = "gimli" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" - [[package]] name = "h2" -version = "0.3.7" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd819562fcebdac5afc5c113c3ec36f902840b70fd4fc458799c8ce4607ae55" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" dependencies = [ - "bytes 1.1.0", + "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http 0.2.5", + "http", "indexmap", "slab", "tokio", @@ -1089,11 +987,11 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c4eb0471fcb85846d8b0690695ef354f9afb11cb03cac2e1d7c9253351afb0" dependencies = [ - "base64 0.13.0", + "base64", "bitflags", - "bytes 1.1.0", + "bytes", "headers-core", - "http 0.2.5", + "http", "httpdate", "mime", "sha-1", @@ -1105,7 +1003,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" dependencies = [ - "http 0.2.5", + "http", ] [[package]] @@ -1117,12 +1015,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hex" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" - [[package]] name = "hex" version = "0.4.3" @@ -1131,22 +1023,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" -dependencies = [ - "bytes 0.4.12", - "fnv", - "itoa", -] - -[[package]] -name = "http" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" +checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" dependencies = [ - "bytes 1.1.0", + "bytes", "fnv", "itoa", ] @@ -1157,8 +1038,8 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" dependencies = [ - "bytes 1.1.0", - "http 0.2.5", + "bytes", + "http", "pin-project-lite", ] @@ -1168,10 +1049,10 @@ version = "0.1.0" source = "git+https://github.com/Azure/iot-identity-service?branch=main#df08bfaad7d2c2657ddc41cedc32fe0cc04561b1" dependencies = [ "async-trait", - "base64 0.13.0", + "base64", "futures-util", "headers", - "http 0.2.5", + "http", "hyper", "hyper-openssl", "hyper-proxy", @@ -1180,19 +1061,19 @@ dependencies = [ "nix", "openssl", "openssl-sys", - "percent-encoding 2.1.0", + "percent-encoding", "serde", "serde_json", "tokio", "tracing", - "url 2.2.2", + "url", ] [[package]] name = "httparse" -version = "1.5.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +checksum = "6330e8a36bd8c859f3fa6d9382911fbb7147ec39807f63b923933a247240b9ba" [[package]] name = "httpdate" @@ -1208,16 +1089,16 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.14" +version = "0.14.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b91bb1f221b6ea1f1e4371216b70f40748774c2fb5971b450c07773fb92d26b" +checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" dependencies = [ - "bytes 1.1.0", + "bytes", "futures-channel", "futures-core", "futures-util", "h2", - "http 0.2.5", + "http", "http-body", "httparse", "httpdate", @@ -1232,11 +1113,11 @@ dependencies = [ [[package]] name = "hyper-openssl" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d52322a69f0a93f177d76ca82073fcec8d5b4eb6e28525d5b3142fa718195c" +checksum = "d6ee5d7a8f718585d1c3c61dfde28ef5b0bb14734b4db13f5ada856cdc6c612b" dependencies = [ - "http 0.2.5", + "http", "hyper", "linked_hash_set", "once_cell", @@ -1254,10 +1135,10 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" dependencies = [ - "bytes 1.1.0", - "futures 0.3.17", + "bytes", + "futures", "headers", - "http 0.2.5", + "http", "hyper", "openssl", "tokio", @@ -1271,17 +1152,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.2.3" @@ -1303,19 +1173,11 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if 1.0.0", -] - [[package]] name = "iotedge" version = "0.1.0" dependencies = [ + "anyhow", "async-trait", "atty", "aziot-certd-config", @@ -1327,9 +1189,9 @@ dependencies = [ "aziot-keys-common", "aziot-tpmd-config", "aziotctl-common", - "base64 0.9.3", + "base64", "byte-unit", - "bytes 0.4.12", + "bytes", "chrono", "chrono-humanize", "clap", @@ -1342,9 +1204,8 @@ dependencies = [ "edgelet-test-utils", "edgelet-utils", "erased-serde", - "failure", - "futures 0.1.31", - "hex 0.3.2", + "futures", + "hex", "http-common", "hyper", "lazy_static", @@ -1354,34 +1215,24 @@ dependencies = [ "openssl", "regex", "serde", - "serde_derive", "serde_json", "support-bundle", "sysinfo", "tabwriter", "tempfile", - "termcolor 0.3.6", + "termcolor", + "thiserror", "tokio", "toml", - "typed-headers", - "url 2.2.2", + "url", "zip", ] -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", -] - [[package]] name = "itoa" -version = "0.4.8" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "lazy_static" @@ -1397,16 +1248,16 @@ checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ "arrayvec", "bitflags", - "cfg-if 1.0.0", + "cfg-if", "ryu", "static_assertions", ] [[package]] name = "libc" -version = "0.2.105" +version = "0.2.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013" +checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" [[package]] name = "linked-hash-map" @@ -1425,10 +1276,11 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ + "autocfg", "scopeguard", ] @@ -1438,7 +1290,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1450,12 +1302,6 @@ dependencies = [ "log", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "matches" version = "0.1.9" @@ -1523,7 +1369,7 @@ checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" dependencies = [ "bitflags", "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", "memoffset", ] @@ -1577,15 +1423,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.8.0" @@ -1605,7 +1442,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "foreign-types", "libc", "once_cell", @@ -1667,35 +1504,27 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.11.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" dependencies = [ - "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" dependencies = [ - "cfg-if 1.0.0", - "instant", + "cfg-if", "libc", "redox_syscall", "smallvec", - "winapi", + "windows-sys", ] -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" - [[package]] name = "percent-encoding" version = "2.1.0" @@ -1727,7 +1556,7 @@ dependencies = [ "openssl-sys", "openssl-sys2", "openssl2", - "percent-encoding 2.1.0", + "percent-encoding", "pkcs11-sys", ] @@ -1778,19 +1607,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi", -] - [[package]] name = "rand" version = "0.8.4" @@ -1799,7 +1615,7 @@ checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" dependencies = [ "libc", "rand_chacha", - "rand_core 0.6.3", + "rand_core", "rand_hc", ] @@ -1810,24 +1626,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.6.3" @@ -1843,7 +1644,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" dependencies = [ - "rand_core 0.6.3", + "rand_core", ] [[package]] @@ -1871,15 +1672,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" version = "0.2.10" @@ -1915,12 +1707,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - [[package]] name = "rustversion" version = "1.0.5" @@ -1933,12 +1719,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - [[package]] name = "scopeguard" version = "1.1.0" @@ -1967,9 +1747,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.68" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ "itoa", "ryu", @@ -2006,7 +1786,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ "block-buffer", - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest", "opaque-debug", @@ -2019,7 +1799,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ "block-buffer", - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest", "opaque-debug", @@ -2078,14 +1858,15 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" name = "support-bundle" version = "0.1.0" dependencies = [ + "anyhow", "chrono", "edgelet-core", "edgelet-settings", - "failure", - "futures 0.3.17", + "futures", "hyper", "regex", "tempfile", + "thiserror", "tokio", "zip", ] @@ -2101,26 +1882,14 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - [[package]] name = "sysinfo" -version = "0.14.15" +version = "0.23.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2983daff11a197c7c406b130579bc362177aa54cf2cc1f34d6ac88fccaa6a5e1" +checksum = "4eea2ed6847da2e0c7289f72cb4f285f0bd704694ca067d32be811b2a45ea858" dependencies = [ - "cfg-if 0.1.10", - "doc-comment", + "cfg-if", + "core-foundation-sys", "libc", "ntapi", "once_cell", @@ -2137,39 +1906,20 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "tempdir" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -dependencies = [ - "rand 0.4.6", - "remove_dir_all", -] - [[package]] name = "tempfile" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", - "rand 0.8.4", + "rand", "redox_syscall", "remove_dir_all", "winapi", ] -[[package]] -name = "termcolor" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83" -dependencies = [ - "wincolor", -] - [[package]] name = "termcolor" version = "1.1.2" @@ -2185,7 +1935,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b114ece25254e97bf48dd4bfc2a12bad0647adacfe4cae1247a9ca6ad302cec" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "proc-macro2", "quote", "syn", @@ -2270,7 +2020,7 @@ version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a" dependencies = [ - "bytes 1.1.0", + "bytes", "libc", "memchr", "mio", @@ -2307,16 +2057,16 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" dependencies = [ - "bytes 1.1.0", + "bytes", "futures-core", "futures-sink", - "log", "pin-project-lite", "tokio", + "tracing", ] [[package]] @@ -2346,7 +2096,7 @@ version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -2379,19 +2129,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" -[[package]] -name = "typed-headers" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6f5af532d859106afe9077c8f95bcaa09af272d5d9b338ec1ff05830b5803c" -dependencies = [ - "base64 0.10.1", - "bytes 0.4.12", - "chrono", - "http 0.1.21", - "mime", -] - [[package]] name = "typenum" version = "1.14.0" @@ -2425,17 +2162,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" -[[package]] -name = "url" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -dependencies = [ - "idna 0.1.5", - "matches", - "percent-encoding 1.0.1", -] - [[package]] name = "url" version = "2.2.2" @@ -2443,9 +2169,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" dependencies = [ "form_urlencoded", - "idna 0.2.3", + "idna", "matches", - "percent-encoding 2.1.0", + "percent-encoding", "serde", ] @@ -2515,14 +2241,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "wincolor" -version = "0.1.6" +name = "windows-sys" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" +checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" dependencies = [ - "winapi", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/edgelet/aziot-edged/src/workload_manager.rs b/edgelet/aziot-edged/src/workload_manager.rs index ee635448f83..219aaa223be 100644 --- a/edgelet/aziot-edged/src/workload_manager.rs +++ b/edgelet/aziot-edged/src/workload_manager.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use edgelet_core::{module::ModuleAction, ErrorKind, UrlExt}; +use edgelet_core::{module::ModuleAction, Error, UrlExt}; use edgelet_settings::uri::Listen; use crate::error::Error as EdgedError; @@ -88,7 +88,7 @@ where signal_socket_created.send(()).map_err(|()| { EdgedError::from_err( "Could not send socket created signal to module runtime", - ErrorKind::WorkloadManager, + Error::WorkloadManager, ) })?; } @@ -156,7 +156,7 @@ where || { Err(EdgedError::from_err( "No home dir found", - ErrorKind::WorkloadManager, + Error::WorkloadManager, )) }, |home_dir| { @@ -265,7 +265,7 @@ where ); EdgedError::from_err( "Could not notify back runtime, stop listener", - ErrorKind::WorkloadManager, + Error::WorkloadManager, ) })?; } diff --git a/edgelet/docker-rs/Cargo.toml b/edgelet/docker-rs/Cargo.toml index 3159840be0c..35e3fe37626 100644 --- a/edgelet/docker-rs/Cargo.toml +++ b/edgelet/docker-rs/Cargo.toml @@ -7,14 +7,13 @@ version = "0.1.0" [dependencies] async-trait = "0.1" -base64 = "0.9" -futures = "0.1" +base64 = "0.13" +futures = "0.3" hyper = "0.14" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -typed-headers = "0.1" -url = "1.5" +url = "2" -http-common = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} +http-common = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } diff --git a/edgelet/docker-rs/src/apis/client.rs b/edgelet/docker-rs/src/apis/client.rs index bec1e6fc646..791424cd915 100644 --- a/edgelet/docker-rs/src/apis/client.rs +++ b/edgelet/docker-rs/src/apis/client.rs @@ -4,7 +4,6 @@ use std::sync::Arc; use futures::{Future, Stream}; use serde_json; -use typed_headers::{self, mime, HeaderMapExt}; use http_common::{Connector, ErrorBody, HttpRequest}; use hyper::{Body, Client, Uri}; diff --git a/edgelet/docker-rs/src/lib.rs b/edgelet/docker-rs/src/lib.rs index 40865688011..abbb42d94da 100644 --- a/edgelet/docker-rs/src/lib.rs +++ b/edgelet/docker-rs/src/lib.rs @@ -12,6 +12,5 @@ #![cfg(not(tarpaulin_include))] pub mod apis; pub mod models; -pub mod utils; pub use apis::{DockerApi, DockerApiClient}; diff --git a/edgelet/docker-rs/src/utils.rs b/edgelet/docker-rs/src/utils.rs deleted file mode 100644 index e99e0c13288..00000000000 --- a/edgelet/docker-rs/src/utils.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -use serde_json; -use std::borrow::Cow; -use std::str::FromStr; -use typed_headers::{self, http}; - -use crate::models::ContainerCreateBody; - -impl FromStr for ContainerCreateBody { - type Err = serde_json::Error; - - fn from_str(s: &str) -> Result { - serde_json::from_str(s) - } -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct UserAgent<'a>(pub Cow<'a, str>); - -impl<'a> typed_headers::Header for UserAgent<'a> { - fn name() -> &'static http::header::HeaderName { - &http::header::USER_AGENT - } - - fn from_values<'b>( - values: &mut http::header::ValueIter<'b, http::header::HeaderValue>, - ) -> Result, typed_headers::Error> - where - Self: Sized, - { - match values.next() { - Some(value) => { - let value = value - .to_str() - .map_err(|_| typed_headers::Error::invalid_value())? - .trim() - .to_string() - .into(); - Ok(Some(UserAgent(value))) - } - - None => Ok(None), - } - } - - fn to_values(&self, values: &mut typed_headers::ToValues<'_>) { - typed_headers::util::encode_single_value(&self.0, values); - } -} diff --git a/edgelet/edgelet-core/Cargo.toml b/edgelet/edgelet-core/Cargo.toml index 37232404053..8fab4207df2 100644 --- a/edgelet/edgelet-core/Cargo.toml +++ b/edgelet/edgelet-core/Cargo.toml @@ -6,12 +6,12 @@ publish = false edition = "2021" [dependencies] +anyhow = "1" async-trait = "0.1" base64 = "0.13" bytes = "1" chrono = { version = "0.4", features = ["serde"] } consistenttime = "0.2" -failure = "0.1" futures = "0.3" humantime = "2" hyper = "0.14" @@ -23,8 +23,9 @@ serde = "1" serde_derive = "1" serde_json = "1" serde_with = "1" +thiserror = "1" tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal", "sync", "time"] } -tokio-util = { version = "0.6", features = ["codec"] } +tokio-util = { version = "0.7", features = ["codec"] } url = { version = "2", features = ["serde"] } aziotctl-common = { git = "https://github.com/Azure/iot-identity-service.git", branch = "main" } diff --git a/edgelet/edgelet-core/src/authorization.rs b/edgelet/edgelet-core/src/authorization.rs index b4c1674b244..a9b629ec5b5 100644 --- a/edgelet/edgelet-core/src/authorization.rs +++ b/edgelet/edgelet-core/src/authorization.rs @@ -41,7 +41,7 @@ impl fmt::Display for AuthId { match self { AuthId::None => write!(f, "none"), AuthId::Any => write!(f, "any"), - AuthId::Value(auth_id) => write!(f, "{}", auth_id), + AuthId::Value(auth_id) => write!(f, "{auth_id}"), } } } diff --git a/edgelet/edgelet-core/src/certificate_properties.rs b/edgelet/edgelet-core/src/certificate_properties.rs deleted file mode 100644 index 8814b1bbb60..00000000000 --- a/edgelet/edgelet-core/src/certificate_properties.rs +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -/// Enumerator for `CERTIFICATE_TYPE` -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum CertificateType { - Unknown, - Client, - Server, - Ca, -} - -/// Enumerator for `CERTIFICATE_ISSUER` -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum CertificateIssuer { - DefaultCa, - DeviceCa, -} - -/// Globally supported properties of certificates in the Edge. -#[derive(Debug, Clone)] -pub struct CertificateProperties { - common_name: String, - certificate_type: CertificateType, - alias: String, - issuer: CertificateIssuer, - dns_san_entries: Option>, - ip_entries: Option>, -} - -impl CertificateProperties { - pub fn new(common_name: String, certificate_type: CertificateType, alias: String) -> Self { - CertificateProperties { - common_name, - certificate_type, - alias, - issuer: CertificateIssuer::DefaultCa, - dns_san_entries: None, - ip_entries: None, - } - } - - pub fn common_name(&self) -> &str { - &self.common_name - } - - #[must_use] - pub fn with_common_name(mut self, common_name: String) -> Self { - self.common_name = common_name; - self - } - - pub fn certificate_type(&self) -> &CertificateType { - &self.certificate_type - } - - #[must_use] - pub fn with_certificate_type(mut self, certificate_type: CertificateType) -> Self { - self.certificate_type = certificate_type; - self - } - - pub fn alias(&self) -> &str { - &self.alias - } - - #[must_use] - pub fn with_alias(mut self, alias: String) -> Self { - self.alias = alias; - self - } - - pub fn issuer(&self) -> &CertificateIssuer { - &self.issuer - } - - #[must_use] - pub fn with_issuer(mut self, issuer: CertificateIssuer) -> Self { - self.issuer = issuer; - self - } - - pub fn dns_san_entries(&self) -> Option<&[String]> { - self.dns_san_entries.as_ref().map(AsRef::as_ref) - } - - #[must_use] - pub fn with_dns_san_entries(mut self, entries: Vec) -> Self { - self.dns_san_entries = Some(entries); - self - } - - pub fn ip_entries(&self) -> Option<&[String]> { - self.ip_entries.as_ref().map(AsRef::as_ref) - } - - #[must_use] - pub fn with_ip_entries(mut self, entries: Vec) -> Self { - self.ip_entries = Some(entries); - self - } -} - -#[cfg(test)] -mod tests { - use super::{CertificateIssuer, CertificateProperties, CertificateType}; - - #[test] - fn test_default() { - let c = CertificateProperties::new( - "common_name".to_string(), - CertificateType::Client, - "alias".to_string(), - ); - - assert_eq!("common_name", c.common_name()); - assert_eq!(&CertificateType::Client, c.certificate_type()); - assert_eq!("alias", c.alias()); - assert_eq!(&CertificateIssuer::DefaultCa, c.issuer()); - assert!(c.dns_san_entries().is_none()); - } - - #[test] - fn test_default_with_settings() { - let input_sans = vec![String::from("serif"), String::from("sar")]; - let c = CertificateProperties::new( - "common_name".to_string(), - CertificateType::Client, - "alias".to_string(), - ) - .with_certificate_type(CertificateType::Ca) - .with_common_name("bafflegab".to_string()) - .with_alias("Andrew Johnson".to_string()) - .with_issuer(CertificateIssuer::DeviceCa) - .with_dns_san_entries(input_sans.clone()); - assert_eq!("bafflegab", c.common_name()); - assert_eq!(&CertificateType::Ca, c.certificate_type()); - assert_eq!("Andrew Johnson", c.alias()); - assert_eq!(&CertificateIssuer::DeviceCa, c.issuer()); - assert_eq!(&*input_sans, c.dns_san_entries().unwrap()); - } -} diff --git a/edgelet/edgelet-core/src/crypto.rs b/edgelet/edgelet-core/src/crypto.rs deleted file mode 100644 index 46e7616c085..00000000000 --- a/edgelet/edgelet-core/src/crypto.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -use bytes::Bytes; -use chrono::{DateTime, Utc}; -use consistenttime::ct_u8_slice_eq; - -use crate::certificate_properties::{CertificateIssuer, CertificateProperties}; -use crate::error::Error; - -pub trait Signature { - fn as_bytes(&self) -> &[u8]; -} - -impl Signature for T -where - T: AsRef<[u8]>, -{ - fn as_bytes(&self) -> &[u8] { - self.as_ref() - } -} - -#[derive(Debug)] -pub enum KeyBytes> { - Pem(T), -} - -impl Clone for KeyBytes -where - T: AsRef<[u8]> + Clone, -{ - fn clone(&self) -> Self { - match *self { - KeyBytes::Pem(ref val) => KeyBytes::Pem(val.clone()), - } - } -} - -#[derive(Debug)] -pub enum PrivateKey> { - Ref(String), - Key(KeyBytes), -} - -impl Clone for PrivateKey -where - T: AsRef<[u8]> + Clone, -{ - fn clone(&self) -> Self { - match *self { - PrivateKey::Ref(ref sref) => PrivateKey::Ref(sref.clone()), - PrivateKey::Key(ref val) => PrivateKey::Key(val.clone()), - } - } -} - -pub trait GetIssuerAlias { - fn get_issuer_alias(&self, issuer: CertificateIssuer) -> Result; -} - -pub trait GetDeviceIdentityCertificate { - type Certificate: Certificate; - type Buffer: AsRef<[u8]>; - - fn get(&self) -> Result; - fn sign_with_private_key(&self, data: &[u8]) -> Result; -} - -pub trait CreateCertificate { - type Certificate: Certificate; - - fn create_certificate( - &self, - properties: &CertificateProperties, - ) -> Result; - - fn destroy_certificate(&self, alias: String) -> Result<(), Error>; - - fn get_certificate(&self, alias: String) -> Result; -} - -pub trait Certificate { - type Buffer: AsRef<[u8]>; - type KeyBuffer: AsRef<[u8]>; - - fn pem(&self) -> Result; - fn get_private_key(&self) -> Result>, Error>; - fn get_valid_to(&self) -> Result, Error>; - fn get_common_name(&self) -> Result; -} - -#[derive(Debug)] -pub struct Digest { - bytes: Bytes, -} - -impl PartialEq for Digest { - fn eq(&self, other: &Self) -> bool { - ct_u8_slice_eq(self.bytes.as_ref(), other.bytes.as_ref()) - } -} - -impl Signature for Digest { - fn as_bytes(&self) -> &[u8] { - self.bytes.as_ref() - } -} - -impl Digest { - pub fn new(bytes: Bytes) -> Self { - Digest { bytes } - } -} diff --git a/edgelet/edgelet-core/src/error.rs b/edgelet/edgelet-core/src/error.rs index 63d82ac94d9..1339718737b 100644 --- a/edgelet/edgelet-core/src/error.rs +++ b/edgelet/edgelet-core/src/error.rs @@ -1,144 +1,92 @@ // Copyright (c) Microsoft. All rights reserved. -use std::fmt; -use std::fmt::Display; - -use failure::{Backtrace, Context, Fail}; - -pub type Result = ::std::result::Result; - -#[derive(Debug)] -pub struct Error { - inner: Context, -} - -#[derive(Debug, Fail)] -pub enum ErrorKind { +#[derive(Debug, thiserror::Error)] +pub enum Error { // Only used by edgelet-test-utils #[cfg(test)] - #[fail(display = "Identity error")] + #[error("Identity error")] Certificate, - #[fail(display = "An error occurred obtaining the certificate contents")] + #[error("An error occurred obtaining the certificate contents")] CertificateContent, - #[fail(display = "An error occurred creating the certificate")] + #[error("An error occurred creating the certificate")] CertificateCreate, - #[fail(display = "An error occurred destroying the certificate")] + #[error("An error occurred destroying the certificate")] CertificateDestroy, - #[fail(display = "An error occurred obtaining the certificate's details")] + #[error("An error occurred obtaining the certificate's details")] CertificateDetail, - #[fail(display = "An error occurred getting the certificate")] + #[error("An error occurred getting the certificate")] CertificateGet, - #[fail(display = "An error occurred obtaining the certificate's key")] + #[error("An error occurred obtaining the certificate's key")] CertificateKey, - #[fail(display = "An error occurred when obtaining the device identity certificate.")] + #[error("An error occurred when obtaining the device identity certificate.")] DeviceIdentityCertificate, - #[fail(display = "An error occurred when signing using the device identity private key.")] + #[error("An error occurred when signing using the device identity private key.")] DeviceIdentitySign, - #[fail( - display = "Edge runtime module has not been created in IoT Hub. Please make sure this device is an IoT Edge capable device." + #[error( + "Edge runtime module has not been created in IoT Hub. Please make sure this device is an IoT Edge capable device." )] EdgeRuntimeIdentityNotFound, - #[fail(display = "The timer that checks the edge runtime status encountered an error.")] + #[error("The timer that checks the edge runtime status encountered an error.")] EdgeRuntimeStatusCheckerTimer, - #[fail(display = "Unable to get the virtualization status.")] + #[error("Unable to get the virtualization status.")] GetVirtualizationStatus, - #[fail(display = "An error occurred when obtaining the HSM version")] + #[error("An error occurred when obtaining the HSM version")] HsmVersion, - #[fail(display = "An identity manager error occurred.")] + #[error("An identity manager error occurred.")] IdentityManager, - #[fail(display = "Invalid image pull policy configuration {:?}", _0)] + #[error("Invalid image pull policy configuration {0:?}")] InvalidImagePullPolicy(String), - #[fail(display = "Invalid or unsupported certificate issuer.")] + #[error("Invalid or unsupported certificate issuer.")] InvalidIssuer, - #[fail(display = "Invalid log tail {:?}", _0)] + #[error("Invalid log tail {0:?}")] InvalidLogTail(String), - #[fail(display = "Invalid module name {:?}", _0)] + #[error("Invalid module name {0:?}")] InvalidModuleName(String), - #[fail(display = "Invalid module type {:?}", _0)] + #[error("Invalid module type {0:?}")] InvalidModuleType(String), - #[fail(display = "Invalid URL {:?}", _0)] + #[error("Invalid URL {0:?}")] InvalidUrl(String), - #[fail(display = "An error occurred in the key store.")] + #[error("An error occurred in the key store.")] KeyStore, - #[fail(display = "Item not found.")] + #[error("Item not found.")] KeyStoreItemNotFound, - #[fail(display = "An error occured when generating a random number.")] + #[error("An error occured when generating a random number.")] MakeRandom, - #[fail(display = "A module runtime error occurred.")] + #[error("A module runtime error occurred.")] ModuleRuntime, - #[fail(display = "Unable to parse since.")] + #[error("Unable to parse since.")] ParseSince, - #[fail(display = "Signing error occurred.")] + #[error("Signing error occurred.")] Sign, - #[fail(display = "Signing error occurred. Invalid key length: {}", _0)] + #[error("Signing error occurred. Invalid key length: {0}")] SignInvalidKeyLength(usize), - #[fail(display = "The workload manager encountered an error")] + #[error("The workload manager encountered an error")] WorkloadManager, } - -impl Fail for Error { - fn cause(&self) -> Option<&dyn Fail> { - self.inner.cause() - } - - fn backtrace(&self) -> Option<&Backtrace> { - self.inner.backtrace() - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.inner, f) - } -} - -impl Error { - pub fn new(inner: Context) -> Self { - Error { inner } - } - - pub fn kind(&self) -> &ErrorKind { - self.inner.get_context() - } -} - -impl From for Error { - fn from(kind: ErrorKind) -> Self { - Error { - inner: Context::new(kind), - } - } -} - -impl From> for Error { - fn from(inner: Context) -> Self { - Error { inner } - } -} diff --git a/edgelet/edgelet-core/src/identity.rs b/edgelet/edgelet-core/src/identity.rs deleted file mode 100644 index f7af9cb0d9e..00000000000 --- a/edgelet/edgelet-core/src/identity.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -use std::fmt; - -use failure::Fail; - -#[derive(Clone, Copy, Debug, serde_derive::Deserialize, PartialEq, serde_derive::Serialize)] -pub enum AuthType { - None, - Sas, - X509, -} - -impl fmt::Display for AuthType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = match *self { - AuthType::None => "None", - AuthType::Sas => "Sas", - AuthType::X509 => "X509", - }; - write!(f, "{}", s) - } -} - -pub trait Identity { - fn module_id(&self) -> &str; - fn managed_by(&self) -> &str; - fn generation_id(&self) -> &str; - fn auth_type(&self) -> AuthType; -} - -pub struct IdentitySpec { - module_id: String, - generation_id: Option, - managed_by: Option, -} - -impl IdentitySpec { - pub fn new(module_id: String) -> Self { - IdentitySpec { - module_id, - generation_id: None, - managed_by: None, - } - } - - pub fn module_id(&self) -> &str { - &self.module_id - } - - pub fn generation_id(&self) -> Option<&str> { - self.generation_id.as_ref().map(AsRef::as_ref) - } - - #[must_use] - pub fn with_generation_id(mut self, generation_id: String) -> Self { - self.generation_id = Some(generation_id); - self - } - - pub fn managed_by(&self) -> Option<&str> { - self.managed_by.as_ref().map(AsRef::as_ref) - } - - #[must_use] - pub fn with_managed_by(mut self, managed_by: String) -> Self { - self.managed_by = Some(managed_by); - self - } -} - -#[async_trait::async_trait] -pub trait IdentityManager { - type Identity: Identity; - type Error: Fail; - - async fn create(&mut self, id: IdentitySpec) -> Result; - async fn update(&mut self, id: IdentitySpec) -> Result; - async fn list(&self) -> Result, Self::Error>; - async fn get(&self, id: IdentitySpec) -> Result, Self::Error>; - async fn delete(&mut self, id: IdentitySpec) -> Result<(), Self::Error>; -} - -// Useful for error contexts -#[derive(Clone, Debug)] -pub enum IdentityOperation { - CreateIdentity(String), - DeleteIdentity(String), - GetIdentity(String), - ListIdentities, - UpdateIdentity(String), -} - -impl fmt::Display for IdentityOperation { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - IdentityOperation::CreateIdentity(name) => { - write!(f, "Could not create identity {}", name) - } - IdentityOperation::DeleteIdentity(name) => { - write!(f, "Could not delete identity {}", name) - } - IdentityOperation::GetIdentity(name) => write!(f, "Could not get identity {}", name), - IdentityOperation::ListIdentities => write!(f, "Could not list identities"), - IdentityOperation::UpdateIdentity(name) => { - write!(f, "Could not update identity {}", name) - } - } - } -} diff --git a/edgelet/edgelet-core/src/lib.rs b/edgelet/edgelet-core/src/lib.rs index a0ae0334d10..60e73501cef 100644 --- a/edgelet/edgelet-core/src/lib.rs +++ b/edgelet/edgelet-core/src/lib.rs @@ -11,41 +11,24 @@ clippy::use_self )] -use std::path::{Path, PathBuf}; - -use lazy_static::lazy_static; -use url::Url; - -mod authentication; -mod authorization; -mod certificate_properties; -pub mod crypto; pub mod error; -mod identity; -mod logs; pub mod module; + mod parse_since; mod virtualization; -pub mod workload; - -pub use authentication::Authenticator; -pub use authorization::{AuthId, ModuleId, Policy}; -pub use certificate_properties::{CertificateIssuer, CertificateProperties, CertificateType}; -pub use crypto::{ - Certificate, CreateCertificate, GetDeviceIdentityCertificate, GetIssuerAlias, KeyBytes, - PrivateKey, -}; -pub use error::{Error, ErrorKind}; -pub use identity::{AuthType, Identity, IdentityManager, IdentityOperation, IdentitySpec}; -//pub use logs::{Chunked, LogChunk, LogDecode}; + +pub use error::Error; pub use module::{ - DiskInfo, LogOptions, LogTail, MakeModuleRuntime, Module, ModuleOperation, ModuleRegistry, - ModuleRuntime, ModuleRuntimeErrorReason, ModuleRuntimeState, ModuleStatus, ProvisioningInfo, - RegistryOperation, RuntimeOperation, SystemInfo, SystemResources, + DiskInfo, LogOptions, LogTail, MakeModuleRuntime, Module, ModuleAction, ModuleOperation, + ModuleRegistry, ModuleRuntime, ModuleRuntimeErrorReason, ModuleRuntimeState, ModuleStatus, + ProvisioningInfo, RegistryOperation, RuntimeOperation, SystemInfo, SystemResources, }; pub use parse_since::parse_since; -pub use virtualization::is_virtualized_env; -pub use workload::WorkloadConfig; + +use std::path::{Path, PathBuf}; + +use lazy_static::lazy_static; +use url::Url; lazy_static! { static ref VERSION: &'static str = diff --git a/edgelet/edgelet-core/src/module.rs b/edgelet/edgelet-core/src/module.rs index 76ca36c1ec5..97c2d5531c1 100644 --- a/edgelet/edgelet-core/src/module.rs +++ b/edgelet/edgelet-core/src/module.rs @@ -8,17 +8,17 @@ use std::str::FromStr; use std::string::ToString; use std::time::Duration; +use anyhow::Context; use chrono::prelude::*; -use failure::{Fail, ResultExt}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; +use tokio::sync::mpsc::UnboundedSender; use aziotctl_common::host_info::{DmiInfo, OsInfo}; use edgelet_settings::module::Settings as ModuleSpec; use edgelet_settings::RuntimeSettings; -use tokio::sync::mpsc::UnboundedSender; -use crate::error::{Error, ErrorKind, Result as EdgeletResult}; +use crate::error::Error; #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "lowercase")] @@ -166,15 +166,15 @@ impl Default for LogTail { } impl FromStr for LogTail { - type Err = Error; + type Err = anyhow::Error; - fn from_str(s: &str) -> EdgeletResult { + fn from_str(s: &str) -> anyhow::Result { let tail = if s == "all" { LogTail::All } else { let num = s .parse::() - .with_context(|_| ErrorKind::InvalidLogTail(s.to_string()))?; + .with_context(|| Error::InvalidLogTail(s.to_string()))?; LogTail::Num(num) }; Ok(tail) @@ -264,21 +264,19 @@ impl LogOptions { #[async_trait::async_trait] pub trait Module { type Config; - type Error: Fail; fn name(&self) -> &str; fn type_(&self) -> &str; fn config(&self) -> &Self::Config; - async fn runtime_state(&self) -> Result; + async fn runtime_state(&self) -> anyhow::Result; } #[async_trait::async_trait] pub trait ModuleRegistry { type Config; - type Error: Fail; - async fn pull(&self, config: &Self::Config) -> Result<(), Self::Error>; - async fn remove(&self, name: &str) -> Result<(), Self::Error>; + async fn pull(&self, config: &Self::Config) -> anyhow::Result<()>; + async fn remove(&self, name: &str) -> anyhow::Result<()>; } #[skip_serializing_none] @@ -460,38 +458,33 @@ pub trait MakeModuleRuntime { type Config: Clone + Send; type Settings: RuntimeSettings; type ModuleRuntime: ModuleRuntime; - type Error: Fail; async fn make_runtime( settings: &Self::Settings, create_socket_channel: UnboundedSender, - ) -> Result; + ) -> anyhow::Result; } #[async_trait::async_trait] pub trait ModuleRuntime: Sized { - type Error: Fail; - type Config: Clone + Send + serde::Serialize; type Module: Module + Send; - type ModuleRegistry: ModuleRegistry + Send + Sync; - - async fn create(&self, module: ModuleSpec) -> Result<(), Self::Error>; - async fn get(&self, id: &str) -> Result<(Self::Module, ModuleRuntimeState), Self::Error>; - async fn start(&self, id: &str) -> Result<(), Self::Error>; - async fn stop(&self, id: &str, wait_before_kill: Option) -> Result<(), Self::Error>; - async fn restart(&self, id: &str) -> Result<(), Self::Error>; - async fn remove(&self, id: &str) -> Result<(), Self::Error>; - async fn system_info(&self) -> Result; - async fn system_resources(&self) -> Result; - async fn list(&self) -> Result, Self::Error>; - async fn list_with_details( - &self, - ) -> Result, Self::Error>; - async fn logs(&self, id: &str, options: &LogOptions) -> Result; - async fn remove_all(&self) -> Result<(), Self::Error>; - async fn stop_all(&self, wait_before_kill: Option) -> Result<(), Self::Error>; - async fn module_top(&self, id: &str) -> Result, Self::Error>; + type ModuleRegistry: ModuleRegistry + Send + Sync; + + async fn create(&self, module: ModuleSpec) -> anyhow::Result<()>; + async fn get(&self, id: &str) -> anyhow::Result<(Self::Module, ModuleRuntimeState)>; + async fn start(&self, id: &str) -> anyhow::Result<()>; + async fn stop(&self, id: &str, wait_before_kill: Option) -> anyhow::Result<()>; + async fn restart(&self, id: &str) -> anyhow::Result<()>; + async fn remove(&self, id: &str) -> anyhow::Result<()>; + async fn system_info(&self) -> anyhow::Result; + async fn system_resources(&self) -> anyhow::Result; + async fn list(&self) -> anyhow::Result>; + async fn list_with_details(&self) -> anyhow::Result>; + async fn logs(&self, id: &str, options: &LogOptions) -> anyhow::Result; + async fn remove_all(&self) -> anyhow::Result<()>; + async fn stop_all(&self, wait_before_kill: Option) -> anyhow::Result<()>; + async fn module_top(&self, id: &str) -> anyhow::Result>; fn registry(&self) -> &Self::ModuleRegistry; } diff --git a/edgelet/edgelet-core/src/parse_since.rs b/edgelet/edgelet-core/src/parse_since.rs index 97e0a7339bb..1fc20ec6efc 100644 --- a/edgelet/edgelet-core/src/parse_since.rs +++ b/edgelet/edgelet-core/src/parse_since.rs @@ -1,27 +1,27 @@ use std::convert::TryInto; +use anyhow::Context; use chrono::{DateTime, Duration, Local}; -use failure::ResultExt; use humantime::parse_duration; -use crate::error::{Error, ErrorKind}; +use crate::error::Error; -pub fn parse_since(since: &str) -> Result { +pub fn parse_since(since: &str) -> anyhow::Result { if let Ok(datetime) = DateTime::parse_from_rfc3339(since) { let temp: Result = datetime.timestamp().try_into(); - Ok(temp.context(ErrorKind::ParseSince)?) + Ok(temp.context(Error::ParseSince)?) } else if let Ok(epoch) = since.parse() { Ok(epoch) } else if let Ok(duration) = parse_duration(since) { let nano: Result = duration.as_nanos().try_into(); - let nano = nano.context(ErrorKind::ParseSince)?; + let nano = nano.context(Error::ParseSince)?; let temp: Result = (Local::now() - Duration::nanoseconds(nano)) .timestamp() .try_into(); - Ok(temp.context(ErrorKind::ParseSince)?) + Ok(temp.context(Error::ParseSince)?) } else { - Err(Error::from(ErrorKind::ParseSince)) + Err(Error::ParseSince.into()) } } diff --git a/edgelet/edgelet-core/src/virtualization.rs b/edgelet/edgelet-core/src/virtualization.rs index 304a12171fa..726747ef90d 100644 --- a/edgelet/edgelet-core/src/virtualization.rs +++ b/edgelet/edgelet-core/src/virtualization.rs @@ -1,14 +1,16 @@ // Copyright (c) Microsoft. All rights reserved. -use crate::error::{Error, ErrorKind}; -use failure::ResultExt; use std::process::Command; -pub fn is_virtualized_env() -> Result, Error> { +use anyhow::Context; + +use crate::error::Error; + +pub fn is_virtualized_env() -> anyhow::Result> { if cfg!(target_os = "linux") { let status = Command::new("systemd-detect-virt") .status() - .context(ErrorKind::GetVirtualizationStatus)?; + .context(Error::GetVirtualizationStatus)?; Ok(Some(status.code() == Some(0))) } else { diff --git a/edgelet/edgelet-core/src/workload.rs b/edgelet/edgelet-core/src/workload.rs deleted file mode 100644 index 161b4dd06ee..00000000000 --- a/edgelet/edgelet-core/src/workload.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -use crate::certificate_properties::CertificateType; - -/// Trait to obtain configuration data needed by any implementation of the workload interface -/// for module identity and certificate management. -pub trait WorkloadConfig { - fn iot_hub_name(&self) -> &str; - fn device_id(&self) -> &str; - fn edge_ca_cert(&self) -> &str; - fn edge_ca_key(&self) -> &str; - fn trust_bundle_cert(&self) -> &str; - fn manifest_trust_bundle_cert(&self) -> &str; - fn get_cert_max_duration(&self, cert_type: CertificateType) -> i64; -} diff --git a/edgelet/edgelet-docker/Cargo.toml b/edgelet/edgelet-docker/Cargo.toml index 9802fdacd18..3c97ab2e4e3 100644 --- a/edgelet/edgelet-docker/Cargo.toml +++ b/edgelet/edgelet-docker/Cargo.toml @@ -6,35 +6,29 @@ publish = false version = "0.1.0" [dependencies] +anyhow = "1" async-trait = "0.1" -base64 = "0.9" +base64 = "0.13" bytes = "1" -chrono = {version = "0.4", features = ["serde"]} -failure = "0.1" +chrono = { version = "0.4", features = ["serde"] } futures = "0.3" -hex="0.3" +hex = "0.4" hyper = "0.14" log = "0.4" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -sysinfo = "0.14.10" -tokio = {version = "1", features = ["process", "macros"]} -url = {version = "2", features = ["serde"]} +sysinfo = "0.23" +thiserror = "1" +tokio = { version = "1", features = ["macros", "process"] } +url = { version = "2", features = ["serde"] } -aziot-cert-client-async = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -aziot-cert-common-http = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -config-common = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -docker = {path = "../docker-rs"} -edgelet-core = {path = "../edgelet-core"} -edgelet-settings = {path = "../edgelet-settings", features = ["settings-docker"]} -edgelet-utils = {path = "../edgelet-utils"} -http-common = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} +aziot-cert-client-async = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +aziot-cert-common-http = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +config-common = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +docker = { path = "../docker-rs" } +edgelet-core = { path = "../edgelet-core" } +edgelet-settings = { path = "../edgelet-settings", features = ["settings-docker"] } +edgelet-utils = { path = "../edgelet-utils" } +http-common = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } libc = "0.2.66" - -[dev_dependencies] -maplit = "1.0" -tempdir = "0.3.7" -tempfile = "3" -time = "0.1" -typed-headers = "0.1" diff --git a/edgelet/edgelet-docker/src/error.rs b/edgelet/edgelet-docker/src/error.rs index cf776216b36..6c911eb7830 100644 --- a/edgelet/edgelet-docker/src/error.rs +++ b/edgelet/edgelet-docker/src/error.rs @@ -1,129 +1,63 @@ // Copyright (c) Microsoft. All rights reserved. -use std::fmt; -use std::fmt::Display; +use edgelet_core::{ModuleOperation, RegistryOperation, RuntimeOperation}; -use failure::{Backtrace, Context, Fail}; - -use edgelet_core::{ - ModuleOperation, ModuleRuntimeErrorReason, RegistryOperation, RuntimeOperation, -}; - -pub type Result = ::std::result::Result; - -#[derive(Debug)] -pub struct Error { - inner: Context, -} - -#[derive(Debug, Fail)] -pub enum ErrorKind { - #[fail(display = "Could not clone create options")] +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Could not clone create options")] CloneCreateOptions, - #[fail(display = "Conflict with current operation")] + #[error("Conflict with current operation")] Conflict, - #[fail(display = "Container runtime error")] + #[error("Container runtime error")] Docker, - #[fail(display = "Container runtime error - {:?}", _0)] + #[error("Container runtime error - {0}")] DockerRuntime(String), - #[fail(display = "{}", _0)] + #[error("{0}")] FormattedDockerRuntime(String), - #[fail(display = "Could not initialize module runtime - {}", _0)] + #[error("Could not initialize module runtime - {0}")] Initialization(String), - #[fail(display = "Could not initialize Notary configuration: {}", _0)] + #[error("Could not initialize Notary configuration: {0}")] InitializeNotary(String), - #[fail(display = "Invalid docker image {:?}", _0)] + #[error("Invalid docker image {0:?}")] InvalidImage(String), - #[fail(display = "Invalid module name {:?}", _0)] + #[error("Invalid module name {0:?}")] InvalidModuleName(String), - #[fail(display = "Invalid module type {:?}", _0)] + #[error("Invalid module type {0:?}")] InvalidModuleType(String), - #[fail(display = "Invalid socket URI: {:?}", _0)] + #[error("Invalid socket URI: {0:?}")] InvalidSocketUri(String), - #[fail(display = "{}", _0)] + #[error("{0}")] LaunchNotary(String), - #[fail(display = "{}", _0)] + #[error("{0}")] ModuleOperation(ModuleOperation), - #[fail(display = "{}", _0)] + #[error("{0}")] NotaryDigestMismatch(String), - #[fail(display = "{}", _0)] + #[error("{0}")] NotaryRootCAReadError(String), - #[fail(display = "{}", _0)] + #[error("{0}")] NotFound(String), - #[fail(display = "Target of operation already in this state")] + #[error("Target of operation already in this state")] NotModified, - #[fail(display = "{}", _0)] + #[error("{0}")] RegistryOperation(RegistryOperation), - #[fail(display = "{}", _0)] + #[error("{0}")] RuntimeOperation(RuntimeOperation), } - -impl Fail for Error { - fn cause(&self) -> Option<&dyn Fail> { - self.inner.cause() - } - - fn backtrace(&self) -> Option<&Backtrace> { - self.inner.backtrace() - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.inner, f) - } -} - -impl Error { - pub fn kind(&self) -> &ErrorKind { - self.inner.get_context() - } - - #[allow(clippy::needless_pass_by_value)] - pub fn from_docker_error(err: Box, context: ErrorKind) -> Self { - Error::from(ErrorKind::DockerRuntime(err.to_string())) - .context(context) - .into() - } -} - -impl From for Error { - fn from(kind: ErrorKind) -> Self { - Error { - inner: Context::new(kind), - } - } -} - -impl From> for Error { - fn from(inner: Context) -> Self { - Error { inner } - } -} - -impl<'a> From<&'a Error> for ModuleRuntimeErrorReason { - fn from(err: &'a Error) -> Self { - match ::find_root_cause(err).downcast_ref::() { - Some(ErrorKind::NotFound(_)) => ModuleRuntimeErrorReason::NotFound, - _ => ModuleRuntimeErrorReason::Other, - } - } -} diff --git a/edgelet/edgelet-docker/src/lib.rs b/edgelet/edgelet-docker/src/lib.rs index 048ac45bfb9..efdd25baaab 100644 --- a/edgelet/edgelet-docker/src/lib.rs +++ b/edgelet/edgelet-docker/src/lib.rs @@ -17,6 +17,6 @@ mod module; mod notary; mod runtime; -pub use error::{Error, ErrorKind}; +pub use error::Error; pub use module::{DockerModule, MODULE_TYPE}; pub use runtime::{init_client, DockerModuleRuntime}; diff --git a/edgelet/edgelet-docker/src/module.rs b/edgelet/edgelet-docker/src/module.rs index 0c32b34a560..a4efcf61d73 100644 --- a/edgelet/edgelet-docker/src/module.rs +++ b/edgelet/edgelet-docker/src/module.rs @@ -2,16 +2,16 @@ use std::str::FromStr; +use anyhow::Context; use chrono::prelude::*; use docker::apis::{DockerApi, DockerApiClient}; use docker::models::InlineResponse200State; use edgelet_core::{Module, ModuleOperation, ModuleRuntimeState, ModuleStatus}; use edgelet_settings::DockerConfig; +use edgelet_utils::ensure_not_empty; -use edgelet_utils::ensure_not_empty_with_context; - -use crate::error::{Error, ErrorKind, Result}; +use crate::error::Error; pub const MODULE_TYPE: &str = "docker"; pub const MIN_DATE: &str = "0001-01-01T00:00:00Z"; @@ -29,8 +29,12 @@ impl std::fmt::Debug for DockerModule { } impl DockerModule { - pub fn new(client: DockerApiClient, name: String, config: DockerConfig) -> Result { - ensure_not_empty_with_context(&name, || ErrorKind::InvalidModuleName(name.clone()))?; + pub fn new( + client: DockerApiClient, + name: String, + config: DockerConfig, + ) -> anyhow::Result { + ensure_not_empty(&name).with_context(|| Error::InvalidModuleName(name.clone()))?; Ok(DockerModule { client, @@ -89,7 +93,6 @@ pub fn runtime_state( #[async_trait::async_trait] impl Module for DockerModule { type Config = DockerConfig; - type Error = Error; fn name(&self) -> &str { &self.name @@ -103,17 +106,13 @@ impl Module for DockerModule { &self.config } - async fn runtime_state(&self) -> Result { + async fn runtime_state(&self) -> anyhow::Result { let inspect = self .client .container_inspect(&self.name, false) .await - .map_err(|e| { - Error::from_docker_error( - e, - ErrorKind::ModuleOperation(ModuleOperation::RuntimeState), - ) - })?; + .map_err(|e| Error::DockerRuntime(e.to_string())) + .context(Error::ModuleOperation(ModuleOperation::RuntimeState))?; Ok(runtime_state(inspect.id(), inspect.state())) } diff --git a/edgelet/edgelet-docker/src/notary.rs b/edgelet/edgelet-docker/src/notary.rs index 0106ade78b9..ad52adce979 100644 --- a/edgelet/edgelet-docker/src/notary.rs +++ b/edgelet/edgelet-docker/src/notary.rs @@ -4,26 +4,26 @@ use std::fs; use std::fs::File; use std::path::{Path, PathBuf}; -use failure::ResultExt; +use anyhow::Context; use log::debug; use serde_json::json; use tokio::process::Command; -use crate::{Error, ErrorKind}; +use crate::Error; pub fn notary_init( home_dir: &Path, registry_server_hostname: &str, cert_buf: &[u8], -) -> Result { +) -> anyhow::Result { // Validate inputs if registry_server_hostname.is_empty() { - return Err(ErrorKind::InitializeNotary("hostname is empty".to_owned()).into()); + return Err(Error::InitializeNotary("hostname is empty".to_owned()).into()); } if cert_buf.is_empty() { return Err( - ErrorKind::InitializeNotary("root ca pem string content is empty".to_owned()).into(), + Error::InitializeNotary("root ca pem string content is empty".to_owned()).into(), ); } @@ -67,7 +67,7 @@ pub fn notary_init( // Delete Notary directory for a clean start. if let Err(err) = fs::remove_dir_all(¬ary_dir) { if err.kind() != std::io::ErrorKind::NotFound { - return Err(ErrorKind::InitializeNotary(format!( + return Err(Error::InitializeNotary(format!( "could not delete notary directory {}", hostname_dir.display() )) @@ -76,16 +76,16 @@ pub fn notary_init( } // Create trust directory - fs::create_dir_all(&trust_dir).with_context(|_| { - ErrorKind::InitializeNotary(format!( + fs::create_dir_all(&trust_dir).with_context(|| { + Error::InitializeNotary(format!( "could not create trust directory {}", trust_dir.display() )) })?; // Create certs directory - fs::create_dir_all(&certs_dir).with_context(|_| { - ErrorKind::InitializeNotary(format!( + fs::create_dir_all(&certs_dir).with_context(|| { + Error::InitializeNotary(format!( "could not create certs directory {}", certs_dir.display() )) @@ -95,8 +95,8 @@ pub fn notary_init( let root_ca_cert_name = sanitized_hostname + "_root_ca.pem"; let root_ca_file_path = certs_dir.join(root_ca_cert_name); - fs::write(&root_ca_file_path, cert_buf).with_context(|_| { - ErrorKind::InitializeNotary(format!( + fs::write(&root_ca_file_path, cert_buf).with_context(|| { + Error::InitializeNotary(format!( "could not create root CA cert for notary hostname directory {}", hostname_dir.display() )) @@ -125,8 +125,8 @@ pub fn notary_init( let mut config_file_path = hostname_dir.join("config"); // Create config directory - fs::create_dir_all(&config_file_path).with_context(|_| { - ErrorKind::InitializeNotary(format!( + fs::create_dir_all(&config_file_path).with_context(|| { + Error::InitializeNotary(format!( "could not config directory {}", config_file_path.display() )) @@ -136,14 +136,14 @@ pub fn notary_init( debug!("Config file path {}", config_file_path.display()); // Create Notary config file - let file = File::create(&config_file_path).with_context(|_| { - ErrorKind::InitializeNotary(format!( + let file = File::create(&config_file_path).with_context(|| { + Error::InitializeNotary(format!( "could not create notary config file in {}", config_file_path.display() )) })?; - serde_json::to_writer(file, &config_contents).with_context(|_| { - ErrorKind::InitializeNotary(format!( + serde_json::to_writer(file, &config_contents).with_context(|| { + Error::InitializeNotary(format!( "could not write contents to notary config file in {}", config_file_path.display() )) @@ -157,7 +157,7 @@ pub async fn notary_lookup( image_gun: &str, tag: &str, config_path: &Path, -) -> Result { +) -> anyhow::Result { let mut notary_cmd = Command::new("notary"); if let Some(notary_auth) = notary_auth { notary_cmd.env("NOTARY_AUTH", notary_auth); @@ -169,18 +169,16 @@ pub async fn notary_lookup( .arg(config_path) .output() .await - .with_context(|_| ErrorKind::LaunchNotary("could not spawn notary process".to_owned()))?; + .with_context(|| Error::LaunchNotary("could not spawn notary process".to_owned()))?; - let notary_output_string = String::from_utf8(notary_output.stdout).with_context(|_| { - ErrorKind::LaunchNotary("could not retrieve notary output as string".to_owned()) + let notary_output_string = String::from_utf8(notary_output.stdout).with_context(|| { + Error::LaunchNotary("could not retrieve notary output as string".to_owned()) })?; debug!("Notary output string is {}", notary_output_string); let split_array: Vec<&str> = notary_output_string.split_whitespace().collect(); if split_array.len() < 2 { - return Err( - ErrorKind::LaunchNotary("notary digest split array is empty".to_owned()).into(), - ); + return Err(Error::LaunchNotary("notary digest split array is empty".to_owned()).into()); } // Notary Server output on lookup is of the format [tag, digest, bytes] diff --git a/edgelet/edgelet-docker/src/runtime.rs b/edgelet/edgelet-docker/src/runtime.rs index 80eefdceac6..7bda8e14704 100644 --- a/edgelet/edgelet-docker/src/runtime.rs +++ b/edgelet/edgelet-docker/src/runtime.rs @@ -7,18 +7,18 @@ use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::{mem, process, str}; -use edgelet_core::module::ModuleAction; -use failure::ResultExt; +use anyhow::Context; use hyper::Uri; use log::{debug, error, info, warn, Level}; -use sysinfo::{DiskExt, ProcessExt, ProcessorExt, System, SystemExt}; +use sysinfo::{DiskExt, PidExt, ProcessExt, ProcessorExt, System, SystemExt}; +use tokio::sync::mpsc::UnboundedSender; use tokio::sync::Mutex; use url::Url; use docker::apis::{Configuration, DockerApi, DockerApiClient}; use docker::models::{ContainerCreateBody, HostConfig, InlineResponse2001, Ipam, NetworkConfig}; use edgelet_core::{ - DiskInfo, LogOptions, MakeModuleRuntime, Module, ModuleRegistry, ModuleRuntime, + DiskInfo, LogOptions, MakeModuleRuntime, Module, ModuleAction, ModuleRegistry, ModuleRuntime, ModuleRuntimeState, RegistryOperation, RuntimeOperation, SystemInfo as CoreSystemInfo, SystemResources, UrlExt, }; @@ -26,11 +26,10 @@ use edgelet_settings::{ ContentTrust, DockerConfig, Ipam as CoreIpam, MobyNetwork, ModuleSpec, RuntimeSettings, Settings, }; -use edgelet_utils::{ensure_not_empty_with_context, log_failure}; +use edgelet_utils::{ensure_not_empty, log_failure}; use http_common::Connector; -use tokio::sync::mpsc::UnboundedSender; -use crate::error::{Error, ErrorKind, Result}; +use crate::error::Error; use crate::module::{runtime_state, DockerModule, MODULE_TYPE as DOCKER_MODULE_TYPE}; use crate::notary; @@ -75,7 +74,9 @@ impl DockerModuleRuntime { .collect() } - async fn get_notary_registries(settings: &Settings) -> Result> { + async fn get_notary_registries( + settings: &Settings, + ) -> anyhow::Result> { if let Some(content_trust_map) = settings .moby_runtime() .content_trust() @@ -86,20 +87,19 @@ impl DockerModuleRuntime { let certd_url = settings.endpoints().aziot_certd_url().clone(); let cert_client = aziot_cert_client_async::Client::new( aziot_cert_common_http::ApiVersion::V2020_09_01, - http_common::Connector::new(&certd_url) - .map_err(|_| Error::from(ErrorKind::Docker))?, // TODO: Error Fix + http_common::Connector::new(&certd_url).map_err(|_| Error::Docker)?, // TODO: Error Fix 1, ); let mut notary_registries = BTreeMap::new(); for (registry_server_hostname, cert_id) in content_trust_map { let cert_buf = cert_client.get_cert(cert_id).await.map_err(|_| { - ErrorKind::NotaryRootCAReadError("Notary root CA read error".to_owned()) + Error::NotaryRootCAReadError("Notary root CA read error".to_owned()) })?; let config_path = notary::notary_init(&home_dir, registry_server_hostname, &cert_buf).map_err( - |_| ErrorKind::NotaryRootCAReadError("Notary init error".to_owned()), + |_| Error::NotaryRootCAReadError("Notary init error".to_owned()), )?; notary_registries.insert(registry_server_hostname.clone(), config_path); } @@ -111,7 +111,10 @@ impl DockerModuleRuntime { } } - async fn check_for_notary_image(&self, config: &DockerConfig) -> Result<(String, bool)> { + async fn check_for_notary_image( + &self, + config: &DockerConfig, + ) -> anyhow::Result<(String, bool)> { if let Some((notary_auth, gun, tag, config_path)) = self.get_notary_parameters(config) { let lock = self.notary_lock.clone(); let mut notary_map = lock.lock().await; @@ -141,9 +144,10 @@ impl DockerModuleRuntime { "Digest from notary : {} and Digest from manifest : {} does not match", digest_from_notary, digest_from_manifest ); - Err(Error::from(ErrorKind::NotaryDigestMismatch( + Err(Error::NotaryDigestMismatch( "notary digest mismatch with the manifest".to_owned(), - ))) + ) + .into()) } } else { info!("No Digest from the manifest"); @@ -199,10 +203,9 @@ impl std::fmt::Debug for DockerModuleRuntime { #[async_trait::async_trait] impl ModuleRegistry for DockerModuleRuntime { - type Error = Error; type Config = DockerConfig; - async fn pull(&self, config: &Self::Config) -> Result<()> { + async fn pull(&self, config: &Self::Config) -> anyhow::Result<()> { let (image, is_content_trust_enabled) = self.check_for_notary_image(config).await?; if is_content_trust_enabled { info!("Pulling image via digest {}...", image); @@ -212,8 +215,8 @@ impl ModuleRegistry for DockerModuleRuntime { let creds = match config.auth() { Some(a) => { - let json = serde_json::to_string(&a).with_context(|_| { - ErrorKind::RegistryOperation(RegistryOperation::PullImage(image.clone())) + let json = serde_json::to_string(&a).with_context(|| { + Error::RegistryOperation(RegistryOperation::PullImage(image.clone())) })?; base64::encode_config(&json, base64::URL_SAFE) } @@ -223,36 +226,32 @@ impl ModuleRegistry for DockerModuleRuntime { self.client .image_create(&image, "", "", "", "", &creds, "") .await - .map_err(|err| { - Error::from_docker_error( - err, - ErrorKind::RegistryOperation(RegistryOperation::PullImage(image.clone())), - ) + .map_err(|err| Error::DockerRuntime(err.to_string())) + .with_context(|| { + Error::RegistryOperation(RegistryOperation::PullImage(image.clone())) })?; info!("Successfully pulled image {}", image); Ok(()) } - async fn remove(&self, name: &str) -> Result<()> { + async fn remove(&self, name: &str) -> anyhow::Result<()> { info!("Removing image {}...", name); - if let Err(err) = ensure_not_empty_with_context(name, || { - ErrorKind::RegistryOperation(RegistryOperation::RemoveImage(name.to_string())) - }) { - return Err(Error::from(err)); - } + ensure_not_empty(name).with_context(|| { + Error::RegistryOperation(RegistryOperation::RemoveImage(name.to_string())) + })?; self.client .image_delete(name, false, false) .await .map_err(|e| { - let err = Error::from_docker_error( - e, - ErrorKind::RegistryOperation(RegistryOperation::RemoveImage(name.to_string())), - ); + let err = Error::DockerRuntime(e.to_string()); log_failure(Level::Warn, &err); err + }) + .with_context(|| { + Error::RegistryOperation(RegistryOperation::RemoveImage(name.to_string())) })?; info!("Successfully removed image {}", name); @@ -265,12 +264,11 @@ impl MakeModuleRuntime for DockerModuleRuntime { type Config = DockerConfig; type Settings = Settings; type ModuleRuntime = Self; - type Error = Error; async fn make_runtime( settings: &Settings, create_socket_channel: UnboundedSender, - ) -> Result { + ) -> anyhow::Result { info!("Initializing module runtime..."); let client = init_client(settings.moby_runtime().uri())?; @@ -296,17 +294,16 @@ impl MakeModuleRuntime for DockerModuleRuntime { } } -pub fn init_client(docker_url: &Url) -> Result { +pub fn init_client(docker_url: &Url) -> anyhow::Result { // build the hyper client - let connector = Connector::new(docker_url) - .map_err(|e| Error::from(ErrorKind::Initialization(e.to_string())))?; + let connector = Connector::new(docker_url).map_err(|e| Error::Initialization(e.to_string()))?; // extract base path - the bit that comes after the scheme let base_path = docker_url .to_base_path() - .context(ErrorKind::Initialization("".to_owned()))? + .with_context(|| Error::Initialization("".to_owned()))? .to_str() - .ok_or_else(|| ErrorKind::Initialization("".to_owned()))? + .ok_or_else(|| Error::Initialization("".to_owned()))? .to_string(); let uri_composer = Box::new(|base_path: &str, path: &str| { // https://docs.rs/hyperlocal/0.6.0/src/hyperlocal/lib.rs.html#59 @@ -326,17 +323,24 @@ pub fn init_client(docker_url: &Url) -> Result { Ok(DockerApiClient::new(connector).with_configuration(configuration)) } -async fn create_network_if_missing(settings: &Settings, client: &DockerApiClient) -> Result<()> { +async fn create_network_if_missing( + settings: &Settings, + client: &DockerApiClient, +) -> anyhow::Result<()> { let (enable_i_pv6, ipam) = get_ipv6_settings(settings.moby_runtime().network()); let network_id = settings.moby_runtime().network().name(); info!("Using runtime network id {}", network_id); let filter = format!(r#"{{"name":{{"{}":true}}}}"#, network_id); - let existing_iotedge_networks = client.network_list(&filter).await.map_err(|err| { - let e = Error::from_docker_error(err, ErrorKind::RuntimeOperation(RuntimeOperation::Init)); - log_failure(Level::Warn, &e); - e - })?; + let existing_iotedge_networks = client + .network_list(&filter) + .await + .map_err(|err| { + let e = Error::DockerRuntime(err.to_string()); + log_failure(Level::Warn, &e); + e + }) + .context(Error::RuntimeOperation(RuntimeOperation::Init))?; if existing_iotedge_networks.is_empty() { let mut network_config = @@ -346,12 +350,15 @@ async fn create_network_if_missing(settings: &Settings, client: &DockerApiClient network_config.set_IPAM(ipam_config); }; - client.network_create(network_config).await.map_err(|err| { - let e = - Error::from_docker_error(err, ErrorKind::RuntimeOperation(RuntimeOperation::Init)); - log_failure(Level::Warn, &e); - e - })?; + client + .network_create(network_config) + .await + .map_err(|err| { + let e = Error::DockerRuntime(err.to_string()); + log_failure(Level::Warn, &e); + e + }) + .context(Error::RuntimeOperation(RuntimeOperation::Init))?; } Ok(()) @@ -393,19 +400,16 @@ fn get_ipv6_settings(network_configuration: &MobyNetwork) -> (bool, Option #[async_trait::async_trait] impl ModuleRuntime for DockerModuleRuntime { - type Error = Error; type Config = DockerConfig; type Module = DockerModule; type ModuleRegistry = Self; - async fn create(&self, mut module: ModuleSpec) -> Result<()> { + async fn create(&self, mut module: ModuleSpec) -> anyhow::Result<()> { info!("Creating module {}...", module.name()); // we only want "docker" modules if module.r#type() != DOCKER_MODULE_TYPE { - return Err(Error::from(ErrorKind::InvalidModuleType( - module.r#type().to_string(), - ))); + return Err(Error::InvalidModuleType(module.r#type().to_string()).into()); } unset_privileged( @@ -448,39 +452,29 @@ impl ModuleRuntime for DockerModuleRuntime { self.client .container_create(create_options, module.name()) .await - .map_err(|e| { - Error::from_docker_error( - e, - ErrorKind::RuntimeOperation(RuntimeOperation::CreateModule( - module.name().to_string(), - )), - ) + .map_err(|e| Error::DockerRuntime(e.to_string())) + .with_context(|| { + Error::RuntimeOperation(RuntimeOperation::CreateModule(module.name().to_string())) })?; Ok(()) } - async fn get(&self, id: &str) -> Result<(Self::Module, ModuleRuntimeState)> { + async fn get(&self, id: &str) -> anyhow::Result<(Self::Module, ModuleRuntimeState)> { debug!("Getting module {}...", id); - ensure_not_empty_with_context(id, || { - ErrorKind::RuntimeOperation(RuntimeOperation::GetModule(id.to_owned())) - }) - .map_err(Error::from)?; + ensure_not_empty(id) + .with_context(|| Error::RuntimeOperation(RuntimeOperation::GetModule(id.to_owned())))?; let response = self .client .container_inspect(id, false) .await - .map_err(|_| { - Error::from(ErrorKind::RuntimeOperation(RuntimeOperation::GetModule( - id.to_owned(), - ))) - })?; + .map_err(|_| Error::RuntimeOperation(RuntimeOperation::GetModule(id.to_owned())))?; - let name = response.name().ok_or_else(|| { - ErrorKind::RuntimeOperation(RuntimeOperation::GetModule(id.to_string())) - })?; + let name = response + .name() + .ok_or_else(|| Error::RuntimeOperation(RuntimeOperation::GetModule(id.to_string())))?; let name = name.trim_start_matches('/').to_owned(); let mut create_options = ContainerCreateBody::new(); @@ -510,27 +504,26 @@ impl ModuleRuntime for DockerModuleRuntime { None, self.allow_elevated_docker_permissions, ) - .map_err(|_| ErrorKind::RuntimeOperation(RuntimeOperation::GetModule(id.to_string())))?; + .map_err(|_| Error::RuntimeOperation(RuntimeOperation::GetModule(id.to_string())))?; if let Some(image_hash) = response.image() { config = config.with_image_hash(image_hash.to_string()); } - let module = DockerModule::new(self.client.clone(), name, config).with_context(|_| { - ErrorKind::RuntimeOperation(RuntimeOperation::GetModule(id.to_string())) + let module = DockerModule::new(self.client.clone(), name, config).with_context(|| { + Error::RuntimeOperation(RuntimeOperation::GetModule(id.to_string())) })?; let state = runtime_state(response.id(), response.state()); Ok((module, state)) } - async fn start(&self, id: &str) -> Result<()> { + async fn start(&self, id: &str) -> anyhow::Result<()> { info!("Starting module {}...", id); - ensure_not_empty_with_context(id, || { - ErrorKind::RuntimeOperation(RuntimeOperation::StartModule(id.to_owned())) - }) - .map_err(Error::from)?; + ensure_not_empty(id).with_context(|| { + Error::RuntimeOperation(RuntimeOperation::StartModule(id.to_owned())) + })?; let (sender, receiver) = tokio::sync::oneshot::channel::<()>(); @@ -538,9 +531,7 @@ impl ModuleRuntime for DockerModuleRuntime { .send(ModuleAction::Start(id.to_string(), sender)) .map_err(|_| { error!("Could not notify workload manager, start of module: {}", id); - Error::from(ErrorKind::RuntimeOperation(RuntimeOperation::StartModule( - id.to_string(), - ))) + Error::RuntimeOperation(RuntimeOperation::StartModule(id.to_string())) })?; receiver.await.map_err(|_| { @@ -548,28 +539,26 @@ impl ModuleRuntime for DockerModuleRuntime { "Could not wait on workload manager response, start of module: {}", id ); - Error::from(ErrorKind::RuntimeOperation(RuntimeOperation::StartModule( - id.to_owned(), - ))) + Error::RuntimeOperation(RuntimeOperation::StartModule(id.to_owned())) })?; - self.client.container_start(id, "").await.map_err(|e| { - let err = Error::from_docker_error( - e, - ErrorKind::RuntimeOperation(RuntimeOperation::StartModule(id.to_owned())), - ); - log_failure(Level::Warn, &err); - err - }) + self.client + .container_start(id, "") + .await + .map_err(|e| { + let err = Error::DockerRuntime(e.to_string()); + log_failure(Level::Warn, &err); + err + }) + .with_context(|| Error::RuntimeOperation(RuntimeOperation::StartModule(id.to_owned()))) } - async fn stop(&self, id: &str, wait_before_kill: Option) -> Result<()> { + async fn stop(&self, id: &str, wait_before_kill: Option) -> anyhow::Result<()> { info!("Stopping module {}...", id); - ensure_not_empty_with_context(id, || { - ErrorKind::RuntimeOperation(RuntimeOperation::StopModule(id.to_owned())) - }) - .map_err(Error::from)?; + ensure_not_empty(id).with_context(|| { + Error::RuntimeOperation(RuntimeOperation::StopModule(id.to_owned())) + })?; #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] let wait_timeout = wait_before_kill.map(|s| match s.as_secs() { @@ -581,48 +570,45 @@ impl ModuleRuntime for DockerModuleRuntime { .send(ModuleAction::Stop(id.to_string())) .map_err(|_| { error!("Could not notify workload manager, stop of module: {}", id); - Error::from(ErrorKind::RuntimeOperation(RuntimeOperation::GetModule( - id.to_string(), - ))) + Error::RuntimeOperation(RuntimeOperation::GetModule(id.to_string())) })?; self.client .container_stop(id, wait_timeout) .await .map_err(|e| { - let err = Error::from_docker_error( - e, - ErrorKind::RuntimeOperation(RuntimeOperation::StopModule(id.to_owned())), - ); + let err = Error::DockerRuntime(e.to_string()); log_failure(Level::Warn, &err); err }) + .with_context(|| Error::RuntimeOperation(RuntimeOperation::StopModule(id.to_owned()))) } - async fn restart(&self, id: &str) -> Result<()> { + async fn restart(&self, id: &str) -> anyhow::Result<()> { info!("Restarting module {}...", id); - ensure_not_empty_with_context(id, || { - ErrorKind::RuntimeOperation(RuntimeOperation::RestartModule(id.to_owned())) - }) - .map_err(Error::from)?; + ensure_not_empty(id).with_context(|| { + Error::RuntimeOperation(RuntimeOperation::RestartModule(id.to_owned())) + })?; - self.client.container_restart(id, None).await.map_err(|e| { - let err = Error::from_docker_error( - e, - ErrorKind::RuntimeOperation(RuntimeOperation::RestartModule(id.to_owned())), - ); - log_failure(Level::Warn, &err); - err - }) + self.client + .container_restart(id, None) + .await + .map_err(|e| { + let err = Error::DockerRuntime(e.to_string()); + log_failure(Level::Warn, &err); + err + }) + .with_context(|| { + Error::RuntimeOperation(RuntimeOperation::RestartModule(id.to_owned())) + }) } - async fn remove(&self, id: &str) -> Result<()> { + async fn remove(&self, id: &str) -> anyhow::Result<()> { info!("Removing module {}...", id); - ensure_not_empty_with_context(id, || { - ErrorKind::RuntimeOperation(RuntimeOperation::RemoveModule(id.to_owned())) - }) - .map_err(Error::from)?; + ensure_not_empty(id).with_context(|| { + Error::RuntimeOperation(RuntimeOperation::RemoveModule(id.to_owned())) + })?; self.client .container_delete( @@ -631,12 +617,12 @@ impl ModuleRuntime for DockerModuleRuntime { ) .await .map_err(|e| { - let err = Error::from_docker_error( - e, - ErrorKind::RuntimeOperation(RuntimeOperation::RemoveModule(id.to_owned())), - ); + let err = Error::DockerRuntime(e.to_string()); log_failure(Level::Warn, &err); err + }) + .with_context(|| { + Error::RuntimeOperation(RuntimeOperation::RemoveModule(id.to_owned())) })?; // Remove the socket to avoid having socket files polluting the home folder. @@ -647,17 +633,15 @@ impl ModuleRuntime for DockerModuleRuntime { "Could not notify workload manager, remove of module: {}", id ); - Error::from(ErrorKind::RuntimeOperation(RuntimeOperation::GetModule( - id.to_string(), - ))) + Error::RuntimeOperation(RuntimeOperation::GetModule(id.to_string())).into() }) } - async fn system_info(&self) -> Result { + async fn system_info(&self) -> anyhow::Result { info!("Querying system info..."); let mut system_info = CoreSystemInfo::from_system() - .map_err(|_| ErrorKind::RuntimeOperation(RuntimeOperation::SystemInfo))?; + .map_err(|_| Error::RuntimeOperation(RuntimeOperation::SystemInfo))?; system_info.merge_additional(self.additional_info.clone()); @@ -665,7 +649,7 @@ impl ModuleRuntime for DockerModuleRuntime { Ok(system_info) } - async fn system_resources(&self) -> Result { + async fn system_resources(&self) -> anyhow::Result { info!("Querying system resources..."); let uptime: u64 = { @@ -682,14 +666,9 @@ impl ModuleRuntime for DockerModuleRuntime { let mut system_resources = self.system_resources.as_ref().lock().await; system_resources.refresh_all(); - let start_time = process::id() - .try_into() - .map(|id| { - system_resources - .get_process(id) - .map(ProcessExt::start_time) - .unwrap_or_default() - }) + let start_time = system_resources + .process(sysinfo::Pid::from_u32(process::id())) + .map(ProcessExt::start_time) .unwrap_or_default(); let current_time = SystemTime::now() @@ -697,20 +676,20 @@ impl ModuleRuntime for DockerModuleRuntime { .unwrap_or_default() .as_secs(); - let used_cpu = system_resources.get_global_processor_info().get_cpu_usage(); - let total_memory = system_resources.get_total_memory() * 1000; - let used_memory = system_resources.get_used_memory() * 1000; + let used_cpu = system_resources.global_processor_info().cpu_usage(); + let total_memory = system_resources.total_memory() * 1000; + let used_memory = system_resources.used_memory() * 1000; let disks = system_resources - .get_disks() + .disks() .iter() .map(|disk| { DiskInfo::new( - disk.get_name().to_string_lossy().into_owned(), - disk.get_available_space(), - disk.get_total_space(), - String::from_utf8_lossy(disk.get_file_system()).into_owned(), - format!("{:?}", disk.get_type()), + disk.name().to_string_lossy().into_owned(), + disk.available_space(), + disk.total_space(), + String::from_utf8_lossy(disk.file_system()).into_owned(), + format!("{:?}", disk.type_()), ) }) .collect(); @@ -719,26 +698,18 @@ impl ModuleRuntime for DockerModuleRuntime { // Note a for_each loop is used for simplicity with async operations // While a stream could be used for parallel operations, it isn't necessary here let modules = self.list().await?; - let mut docker_stats: Vec = Vec::with_capacity(modules.len()); + let mut docker_stats = Vec::::with_capacity(modules.len()); for module in modules { let stats = self .client .container_stats(module.name(), false) .await - .map_err(|e| { - Error::from_docker_error( - e, - ErrorKind::RuntimeOperation(RuntimeOperation::SystemResources), - ) - })?; + .map_err(|e| Error::DockerRuntime(e.to_string()))?; docker_stats.push(stats); } - let docker_stats = serde_json::to_string(&docker_stats).map_err(|_| { - Error::from(ErrorKind::RuntimeOperation( - RuntimeOperation::SystemResources, - )) - })?; + let docker_stats = serde_json::to_string(&docker_stats) + .map_err(|_| Error::RuntimeOperation(RuntimeOperation::SystemResources))?; Ok(SystemResources::new( uptime, @@ -751,14 +722,13 @@ impl ModuleRuntime for DockerModuleRuntime { )) } - async fn list(&self) -> Result> { + async fn list(&self) -> anyhow::Result> { debug!("Listing modules..."); let mut filters = HashMap::new(); filters.insert("label", LABELS); let filters = serde_json::to_string(&filters) - .context(ErrorKind::RuntimeOperation(RuntimeOperation::ListModules)) - .map_err(Error::from)?; + .context(Error::RuntimeOperation(RuntimeOperation::ListModules))?; let containers = self .client @@ -769,12 +739,7 @@ impl ModuleRuntime for DockerModuleRuntime { &filters, ) .await - .map_err(|e| { - Error::from_docker_error( - e, - ErrorKind::RuntimeOperation(RuntimeOperation::ListModules), - ) - })?; + .map_err(|e| Error::DockerRuntime(e.to_string()))?; let result = containers .iter() @@ -816,7 +781,7 @@ impl ModuleRuntime for DockerModuleRuntime { Ok(result) } - async fn list_with_details(&self) -> Result> { + async fn list_with_details(&self) -> anyhow::Result> { let mut result = Vec::new(); for module in self.list().await? { // Note, if error calling just drop module from list @@ -828,7 +793,7 @@ impl ModuleRuntime for DockerModuleRuntime { Ok(result) } - async fn logs(&self, id: &str, options: &LogOptions) -> Result { + async fn logs(&self, id: &str, options: &LogOptions) -> anyhow::Result { info!("Getting logs for module {}...", id); self.client @@ -844,12 +809,9 @@ impl ModuleRuntime for DockerModuleRuntime { ) .await .map_err(|e| { - let err = Error::from_docker_error( - e, - ErrorKind::RuntimeOperation(RuntimeOperation::GetModuleLogs(id.to_owned())), - ); + let err = Error::DockerRuntime(e.to_string()); log_failure(Level::Warn, &err); - err + err.into() }) } @@ -857,7 +819,7 @@ impl ModuleRuntime for DockerModuleRuntime { self } - async fn remove_all(&self) -> Result<()> { + async fn remove_all(&self) -> anyhow::Result<()> { let modules = self.list().await?; let mut remove = vec![]; @@ -874,7 +836,7 @@ impl ModuleRuntime for DockerModuleRuntime { Ok(()) } - async fn stop_all(&self, wait_before_kill: Option) -> Result<()> { + async fn stop_all(&self, wait_before_kill: Option) -> anyhow::Result<()> { let modules = self.list().await?; let mut stop = vec![]; @@ -891,19 +853,20 @@ impl ModuleRuntime for DockerModuleRuntime { Ok(()) } - async fn module_top(&self, id: &str) -> Result> { - let top_response = self.client.container_top(id, "").await.map_err(|e| { - let err = Error::from_docker_error( - e, - ErrorKind::RuntimeOperation(RuntimeOperation::TopModule(id.to_owned())), - ); - log_failure(Level::Warn, &err); - err - })?; + async fn module_top(&self, id: &str) -> anyhow::Result> { + let top_response = self + .client + .container_top(id, "") + .await + .map_err(|e| { + let err = Error::DockerRuntime(e.to_string()); + log_failure(Level::Warn, &err); + err + }) + .with_context(|| Error::RuntimeOperation(RuntimeOperation::TopModule(id.to_owned())))?; - let pids = parse_top_response::(&top_response).with_context(|_| { - ErrorKind::RuntimeOperation(RuntimeOperation::TopModule(id.to_owned())) - })?; + let pids = parse_top_response::(&top_response) + .with_context(|| Error::RuntimeOperation(RuntimeOperation::TopModule(id.to_owned())))?; Ok(pids) } diff --git a/edgelet/edgelet-docker/tests/runtime.rs b/edgelet/edgelet-docker/tests/runtime.rs deleted file mode 100644 index 5cfc6ea3dd1..00000000000 --- a/edgelet/edgelet-docker/tests/runtime.rs +++ /dev/null @@ -1,2018 +0,0 @@ -// // Copyright (c) Microsoft. All rights reserved. - -// #![deny(rust_2018_idioms, warnings)] -// #![deny(clippy::all, clippy::pedantic)] -// #![allow(clippy::default_trait_access, clippy::too_many_lines)] - -// use std::collections::{BTreeMap, HashMap}; -// use std::str; -// use std::sync::{Arc, RwLock}; -// use std::time::Duration; - -// use failure::Fail; -// use futures::future; -// use futures::prelude::*; -// use hyper::{Body, Method, Request, Response, StatusCode}; -// use maplit::btreemap; -// use serde_json::{self, json}; -// use tempfile::NamedTempFile; -// use typed_headers::{mime, ContentLength, ContentType, HeaderMapExt}; -// use url::form_urlencoded::parse as parse_query; - -// use docker::models::{ -// AuthConfig, ContainerCreateBody, ContainerHostConfig, ContainerNetworkSettings, -// ContainerSummary, HostConfig, HostConfigPortBindings, ImageDeleteResponseItem, NetworkConfig, -// }; - -// use edgelet_core::{ -// ImagePullPolicy, LogOptions, LogTail, MakeModuleRuntime, Module, ModuleRegistry, ModuleRuntime, -// ModuleSpec, RegistryOperation, RuntimeOperation, -// }; -// use edgelet_docker::{DockerConfig, DockerModuleRuntime, Settings}; -// use edgelet_docker::{Error, ErrorKind}; -// use edgelet_test_utils::web::{ -// make_req_dispatcher, HttpMethod, RequestHandler, RequestPath, ResponseFuture, -// }; -// use edgelet_test_utils::{routes, run_tcp_server}; -// use hyper::Error as HyperError; - -// const IMAGE_NAME: &str = "nginx:latest"; - -// const INVALID_IMAGE_NAME: &str = "invalidname:latest"; -// const INVALID_IMAGE_HOST: &str = "invalidhost.com/nginx:latest"; - -// fn make_settings(moby_runtime: &str) -> Settings { -// use std::io::Write; - -// lazy_static::lazy_static! { -// static ref ENV_LOCK: std::sync::Mutex<()> = Default::default(); -// } - -// let _env_lock = ENV_LOCK.lock().expect("env lock poisoned"); - -// let mut config_file = NamedTempFile::new().expect("could not create tempfile for config"); - -// config_file -// .write_all( -// r#" -// hostname = "zoo" -// homedir = "/var/lib/aziot/edged" - -// [agent] -// name = "edgeAgent" -// type = "docker" - -// [agent.config] -// image = "microsoft/azureiotedge-agent:1.0" - -// [connect] -// workload_uri = "unix:///var/lib/iotedge/workload.sock" -// management_uri = "unix:///var/lib/iotedge/mgmt.sock" - -// [listen] -// workload_uri = "unix:///var/lib/iotedge/workload.sock" -// management_uri = "unix:///var/lib/iotedge/mgmt.sock" - -// "# -// .as_bytes(), -// ) -// .expect("could not write to config file"); - -// config_file -// .write_all(moby_runtime.as_bytes()) -// .expect("could not write to config file"); - -// std::env::set_var("AZIOT_EDGED_CONFIG", config_file.path()); - -// Settings::new().unwrap() -// } - -// fn make_get_networks_handler( -// on_get: impl Fn() -> String + Clone + Send + 'static, -// ) -> impl Fn(Request) -> ResponseFuture + Clone { -// move |_| { -// let response = on_get(); -// let response_len = response.len(); - -// let mut response = Response::new(response.into()); -// response -// .headers_mut() -// .typed_insert(&ContentLength(response_len as u64)); -// response -// .headers_mut() -// .typed_insert(&ContentType(mime::APPLICATION_JSON)); -// Box::new(future::ok(response)) as ResponseFuture -// } -// } - -// fn make_create_network_handler( -// on_post: impl Fn(Request) + Clone + Send + 'static, -// ) -> impl Fn(Request) -> ResponseFuture + Clone { -// move |req| { -// on_post(req); - -// let response = json!({ -// "Id": "12345", -// "Warnings": "" -// }) -// .to_string(); -// let response_len = response.len(); - -// let mut response = Response::new(response.into()); -// response -// .headers_mut() -// .typed_insert(&ContentLength(response_len as u64)); -// response -// .headers_mut() -// .typed_insert(&ContentType(mime::APPLICATION_JSON)); -// Box::new(future::ok(response)) as ResponseFuture -// } -// } - -// fn not_found_handler(_: Request) -> ResponseFuture { -// let response = Response::builder() -// .status(StatusCode::NOT_FOUND) -// .body(Body::default()) -// .unwrap(); - -// Box::new(future::ok(response)) -// } - -// fn make_network_handler( -// on_get: impl Fn() -> String + Clone + Send + 'static, -// on_post: impl Fn(Request) + Clone + Send + 'static, -// ) -> impl Fn(Request) -> Box, Error = HyperError> + Send> + Clone -// { -// let dispatch_table = routes!( -// GET "/networks" => make_get_networks_handler(on_get), -// POST "/networks/create" => make_create_network_handler(on_post), -// ); - -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)) -// } - -// fn default_get_networks_handler() -> impl Fn(Request) -> ResponseFuture + Clone { -// make_get_networks_handler(|| json!([]).to_string()) -// } - -// fn default_create_network_handler() -> impl Fn(Request) -> ResponseFuture + Clone { -// make_create_network_handler(|_| ()) -// } - -// fn default_network_handler( -// ) -> impl Fn(Request) -> Box, Error = HyperError> + Send> + Clone -// { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// ); - -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)) -// } - -// #[allow(clippy::needless_pass_by_value)] -// fn invalid_image_name_pull_handler(req: Request) -> ResponseFuture { -// // verify that path is /images/create and that the "fromImage" query -// // parameter has the image name we expect -// assert_eq!(req.uri().path(), "/images/create"); - -// let query_map: HashMap = parse_query(req.uri().query().unwrap().as_bytes()) -// .into_owned() -// .collect(); -// assert!(query_map.contains_key("fromImage")); -// assert_eq!( -// query_map.get("fromImage").map(AsRef::as_ref), -// Some(INVALID_IMAGE_NAME) -// ); - -// let response = format!( -// r#"{{ -// "message": "manifest for {} not found" -// }} -// "#, -// INVALID_IMAGE_NAME -// ); - -// let response_len = response.len(); - -// let mut response = Response::new(response.into()); -// response -// .headers_mut() -// .typed_insert(&ContentLength(response_len as u64)); -// response -// .headers_mut() -// .typed_insert(&ContentType(mime::APPLICATION_JSON)); -// *response.status_mut() = hyper::StatusCode::NOT_FOUND; - -// Box::new(future::ok(response)) -// } - -// #[test] -// fn image_pull_with_invalid_image_name_fails() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// POST "/images/create" => invalid_image_name_pull_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = DockerModuleRuntime::make_runtime(settings).and_then(|runtime| { -// let auth = AuthConfig::new() -// .with_username("u1".to_string()) -// .with_password("bleh".to_string()) -// .with_email("u1@bleh.com".to_string()) -// .with_serveraddress("svr1".to_string()); -// let config = DockerConfig::new( -// INVALID_IMAGE_NAME.to_string(), -// ContainerCreateBody::new(), -// None, -// Some(auth), -// ) -// .unwrap(); - -// runtime.pull(&config) -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); - -// // Assert -// let err = runtime -// .block_on(task) -// .expect_err("Expected runtime pull method to fail due to invalid image name."); - -// match (err.kind(), err.cause().and_then(Fail::downcast_ref)) { -// ( -// edgelet_docker::ErrorKind::RegistryOperation( -// edgelet_core::RegistryOperation::PullImage(name), -// ), -// Some(edgelet_docker::ErrorKind::NotFound(message)), -// ) if name == INVALID_IMAGE_NAME => { -// assert_eq!( -// &format!("manifest for {} not found", INVALID_IMAGE_NAME), -// message -// ); -// } - -// _ => panic!( -// "Specific docker runtime message is expected for invalid image name. Got {:?}", -// err.kind() -// ), -// } -// } - -// #[allow(clippy::needless_pass_by_value)] -// fn invalid_image_host_pull_handler(req: Request) -> ResponseFuture { -// // verify that path is /images/create and that the "fromImage" query -// // parameter has the image name we expect -// assert_eq!(req.uri().path(), "/images/create"); - -// let query_map: HashMap = parse_query(req.uri().query().unwrap().as_bytes()) -// .into_owned() -// .collect(); -// assert!(query_map.contains_key("fromImage")); -// assert_eq!( -// query_map.get("fromImage").map(AsRef::as_ref), -// Some(INVALID_IMAGE_HOST) -// ); - -// let response = format!( -// r#" -// {{ -// "message":"Get https://invalidhost.com: dial tcp: lookup {} on X.X.X.X: no such host" -// }} -// "#, -// INVALID_IMAGE_HOST -// ); -// let response_len = response.len(); - -// let mut response = Response::new(response.into()); -// response -// .headers_mut() -// .typed_insert(&ContentLength(response_len as u64)); -// response -// .headers_mut() -// .typed_insert(&ContentType(mime::APPLICATION_JSON)); -// *response.status_mut() = hyper::StatusCode::INTERNAL_SERVER_ERROR; -// Box::new(future::ok(response)) -// } - -// #[test] -// fn image_pull_with_invalid_image_host_fails() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// POST "/images/create" => invalid_image_host_pull_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = DockerModuleRuntime::make_runtime(settings).and_then(|runtime| { -// let auth = AuthConfig::new() -// .with_username("u1".to_string()) -// .with_password("bleh".to_string()) -// .with_email("u1@bleh.com".to_string()) -// .with_serveraddress("svr1".to_string()); -// let config = DockerConfig::new( -// INVALID_IMAGE_HOST.to_string(), -// ContainerCreateBody::new(), -// None, -// Some(auth), -// ) -// .unwrap(); - -// runtime.pull(&config) -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); - -// // Assert -// let err = runtime -// .block_on(task) -// .expect_err("Expected runtime pull method to fail due to invalid image host."); - -// match (err.kind(), err.cause().and_then(Fail::downcast_ref)) { -// ( -// edgelet_docker::ErrorKind::RegistryOperation( -// edgelet_core::RegistryOperation::PullImage(name), -// ), -// Some(edgelet_docker::ErrorKind::FormattedDockerRuntime(message)), -// ) if name == INVALID_IMAGE_HOST => { -// assert_eq!( -// &format!( -// "Get https://invalidhost.com: dial tcp: lookup {} on X.X.X.X: no such host", -// INVALID_IMAGE_HOST -// ), -// message -// ); -// } - -// _ => panic!( -// "Specific docker runtime message is expected for invalid image host. Got {:?}", -// err.kind() -// ), -// } -// } - -// #[allow(clippy::needless_pass_by_value)] -// fn image_pull_with_invalid_creds_handler(req: Request) -> ResponseFuture { -// // verify that path is /images/create and that the "fromImage" query -// // parameter has the image name we expect -// assert_eq!(req.uri().path(), "/images/create"); - -// let query_map: HashMap = parse_query(req.uri().query().unwrap().as_bytes()) -// .into_owned() -// .collect(); -// assert!(query_map.contains_key("fromImage")); -// assert_eq!(query_map.get("fromImage"), Some(&IMAGE_NAME.to_string())); - -// // verify registry creds -// let auth_str = req -// .headers() -// .get_all("X-Registry-Auth") -// .into_iter() -// .map(|bytes| base64::decode_config(bytes, base64::URL_SAFE).unwrap()) -// .map(|raw| str::from_utf8(&raw).unwrap().to_owned()) -// .collect::(); -// let auth_config: AuthConfig = serde_json::from_str(&auth_str).unwrap(); -// assert_eq!(auth_config.username(), Some("us1")); -// assert_eq!(auth_config.password(), Some("ac?ac~aaac???")); -// assert_eq!(auth_config.email(), Some("u1@bleh.com")); -// assert_eq!(auth_config.serveraddress(), Some("svr1")); - -// let response = format!( -// r#" -// {{ -// "message":"Get {}: unauthorized: authentication required" -// }} -// "#, -// IMAGE_NAME -// ); -// let response_len = response.len(); - -// let mut response = Response::new(response.into()); -// response -// .headers_mut() -// .typed_insert(&ContentLength(response_len as u64)); -// response -// .headers_mut() -// .typed_insert(&ContentType(mime::APPLICATION_JSON)); -// *response.status_mut() = hyper::StatusCode::INTERNAL_SERVER_ERROR; -// Box::new(future::ok(response)) -// } - -// #[test] -// fn image_pull_with_invalid_creds_fails() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// POST "/images/create" => image_pull_with_invalid_creds_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = DockerModuleRuntime::make_runtime(settings).and_then(|runtime| { -// // password is written to guarantee base64 encoding has '-' and/or '_' -// let auth = AuthConfig::new() -// .with_username("us1".to_string()) -// .with_password("ac?ac~aaac???".to_string()) -// .with_email("u1@bleh.com".to_string()) -// .with_serveraddress("svr1".to_string()); -// let config = DockerConfig::new( -// IMAGE_NAME.to_string(), -// ContainerCreateBody::new(), -// None, -// Some(auth), -// ) -// .unwrap(); - -// runtime.pull(&config) -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); - -// // Assert -// let err = runtime -// .block_on(task) -// .expect_err("Expected runtime pull method to fail due to unauthentication."); - -// match (err.kind(), err.cause().and_then(Fail::downcast_ref)) { -// ( -// edgelet_docker::ErrorKind::RegistryOperation( -// edgelet_core::RegistryOperation::PullImage(name), -// ), -// Some(edgelet_docker::ErrorKind::FormattedDockerRuntime(message)), -// ) if name == IMAGE_NAME => { -// assert_eq!( -// &format!( -// "Get {}: unauthorized: authentication required", -// &IMAGE_NAME.to_string() -// ), -// message -// ); -// } - -// _ => panic!( -// "Specific docker runtime message is expected for unauthentication. Got {:?}", -// err.kind() -// ), -// } -// } - -// #[allow(clippy::needless_pass_by_value)] -// fn image_pull_handler(req: Request) -> ResponseFuture { -// // verify that path is /images/create and that the "fromImage" query -// // parameter has the image name we expect -// assert_eq!(req.uri().path(), "/images/create"); - -// let query_map: HashMap = parse_query(req.uri().query().unwrap().as_bytes()) -// .into_owned() -// .collect(); -// assert!(query_map.contains_key("fromImage")); -// assert_eq!(query_map.get("fromImage"), Some(&IMAGE_NAME.to_string())); - -// let response = r#" -// { -// "Id": "img1", -// "Warnings": [] -// } -// "#; -// let response_len = response.len(); - -// let mut response = Response::new(response.into()); -// response -// .headers_mut() -// .typed_insert(&ContentLength(response_len as u64)); -// response -// .headers_mut() -// .typed_insert(&ContentType(mime::APPLICATION_JSON)); -// Box::new(future::ok(response)) -// } - -// #[test] -// fn image_pull_succeeds() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// POST "/images/create" => image_pull_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = DockerModuleRuntime::make_runtime(settings).and_then(|runtime| { -// let auth = AuthConfig::new() -// .with_username("u1".to_string()) -// .with_password("bleh".to_string()) -// .with_email("u1@bleh.com".to_string()) -// .with_serveraddress("svr1".to_string()); -// let config = DockerConfig::new( -// IMAGE_NAME.to_string(), -// ContainerCreateBody::new(), -// None, -// Some(auth), -// ) -// .unwrap(); - -// runtime.pull(&config) -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[allow(clippy::needless_pass_by_value)] -// fn image_pull_with_creds_handler(req: Request) -> ResponseFuture { -// // verify that path is /images/create and that the "fromImage" query -// // parameter has the image name we expect -// assert_eq!(req.uri().path(), "/images/create"); - -// let query_map: HashMap = parse_query(req.uri().query().unwrap().as_bytes()) -// .into_owned() -// .collect(); -// assert!(query_map.contains_key("fromImage")); -// assert_eq!(query_map.get("fromImage"), Some(&IMAGE_NAME.to_string())); - -// // verify registry creds -// let auth_str = req -// .headers() -// .get_all("X-Registry-Auth") -// .into_iter() -// .map(|bytes| base64::decode_config(bytes, base64::URL_SAFE).unwrap()) -// .map(|raw| str::from_utf8(&raw).unwrap().to_owned()) -// .collect::(); -// let auth_config: AuthConfig = serde_json::from_str(&auth_str).unwrap(); -// assert_eq!(auth_config.username(), Some("u1")); -// assert_eq!(auth_config.password(), Some("bleh")); -// assert_eq!(auth_config.email(), Some("u1@bleh.com")); -// assert_eq!(auth_config.serveraddress(), Some("svr1")); - -// let response = r#" -// { -// "Id": "img1", -// "Warnings": [] -// } -// "#; -// let response_len = response.len(); - -// let mut response = Response::new(response.into()); -// response -// .headers_mut() -// .typed_insert(&ContentLength(response_len as u64)); -// response -// .headers_mut() -// .typed_insert(&ContentType(mime::APPLICATION_JSON)); -// Box::new(future::ok(response)) -// } - -// #[test] -// fn image_pull_with_creds_succeeds() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// POST "/images/create" => image_pull_with_creds_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = DockerModuleRuntime::make_runtime(settings).and_then(|runtime| { -// let auth = AuthConfig::new() -// .with_username("u1".to_string()) -// .with_password("bleh".to_string()) -// .with_email("u1@bleh.com".to_string()) -// .with_serveraddress("svr1".to_string()); -// let config = DockerConfig::new( -// IMAGE_NAME.to_string(), -// ContainerCreateBody::new(), -// None, -// Some(auth), -// ) -// .unwrap(); - -// runtime.pull(&config) -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[allow(clippy::needless_pass_by_value)] -// fn image_remove_handler(req: Request) -> ResponseFuture { -// assert_eq!(req.method(), &Method::DELETE); -// assert_eq!(req.uri().path(), &format!("/images/{}", IMAGE_NAME)); - -// let response = serde_json::to_string(&vec![ -// ImageDeleteResponseItem::new().with_deleted(IMAGE_NAME.to_string()) -// ]) -// .unwrap(); -// let response_len = response.len(); - -// let mut response = Response::new(response.into()); -// response -// .headers_mut() -// .typed_insert(&ContentLength(response_len as u64)); -// response -// .headers_mut() -// .typed_insert(&ContentType(mime::APPLICATION_JSON)); -// Box::new(future::ok(response)) -// } - -// #[test] -// fn image_remove_succeeds() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// DELETE format!("/images/{}", IMAGE_NAME) => image_remove_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| ModuleRegistry::remove(&runtime, IMAGE_NAME)); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// fn container_create_handler(req: Request) -> ResponseFuture { -// assert_eq!(req.method(), &Method::POST); -// assert_eq!(req.uri().path(), "/containers/create"); - -// let response = json!({ -// "Id": "12345", -// "Warnings": [] -// }) -// .to_string(); -// let response_len = response.len(); - -// Box::new( -// req.into_body() -// .concat2() -// .and_then(|body| { -// let create_options: ContainerCreateBody = -// serde_json::from_slice(body.as_ref()).unwrap(); - -// assert_eq!("nginx:latest", create_options.image().unwrap()); - -// for &v in &["/do/the/custom/command", "with these args"] { -// assert!(create_options.cmd().unwrap().contains(&v.to_string())); -// } - -// for &v in &["/also/do/the/entrypoint", "and this"] { -// assert!(create_options -// .entrypoint() -// .unwrap() -// .contains(&v.to_string())); -// } - -// for &v in &["k1=v1", "k2=v2", "k3=v3", "k4=v4", "k5=v5"] { -// assert!(create_options.env().unwrap().contains(&v.to_string())); -// } - -// let port_bindings = create_options -// .host_config() -// .unwrap() -// .port_bindings() -// .unwrap(); -// assert_eq!( -// "8080", -// port_bindings -// .get("80/tcp") -// .unwrap() -// .iter() -// .next() -// .unwrap() -// .host_port() -// .unwrap() -// ); -// assert_eq!( -// "11022", -// port_bindings -// .get("22/tcp") -// .unwrap() -// .iter() -// .next() -// .unwrap() -// .host_port() -// .unwrap() -// ); - -// let volumes = create_options.volumes().unwrap(); -// let mut expected = ::std::collections::BTreeMap::new(); -// expected.insert("test1".to_string(), json!({})); -// assert_eq!(*volumes, expected); - -// Ok(()) -// }) -// .map(move |_| { -// let mut response = Response::new(response.into()); -// response -// .headers_mut() -// .typed_insert(&ContentLength(response_len as u64)); -// response -// .headers_mut() -// .typed_insert(&ContentType(mime::APPLICATION_JSON)); -// response -// }), -// ) -// } - -// #[test] -// fn container_create_succeeds() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// POST "/containers/create" => container_create_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = DockerModuleRuntime::make_runtime(settings).and_then(|runtime| { -// let mut env = BTreeMap::new(); -// env.insert("k1".to_string(), "v1".to_string()); -// env.insert("k2".to_string(), "v2".to_string()); -// env.insert("k3".to_string(), "v3".to_string()); - -// // add some create options -// let mut port_bindings = BTreeMap::new(); -// port_bindings.insert( -// "22/tcp".to_string(), -// vec![HostConfigPortBindings::new().with_host_port("11022".to_string())], -// ); -// port_bindings.insert( -// "80/tcp".to_string(), -// vec![HostConfigPortBindings::new().with_host_port("8080".to_string())], -// ); -// let memory: i64 = 3_221_225_472; -// let mut volumes = ::std::collections::BTreeMap::new(); -// volumes.insert("test1".to_string(), json!({})); -// let create_options = ContainerCreateBody::new() -// .with_host_config( -// HostConfig::new() -// .with_port_bindings(port_bindings) -// .with_memory(memory), -// ) -// .with_cmd(vec![ -// "/do/the/custom/command".to_string(), -// "with these args".to_string(), -// ]) -// .with_entrypoint(vec![ -// "/also/do/the/entrypoint".to_string(), -// "and this".to_string(), -// ]) -// .with_env(vec!["k4=v4".to_string(), "k5=v5".to_string()]) -// .with_volumes(volumes); - -// let module_config = ModuleSpec::new( -// "m1".to_string(), -// "docker".to_string(), -// DockerConfig::new("nginx:latest".to_string(), create_options, None, None).unwrap(), -// env, -// ImagePullPolicy::default(), -// ) -// .unwrap(); - -// runtime.create(module_config) -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[allow(clippy::needless_pass_by_value)] -// fn container_start_handler(req: Request) -> ResponseFuture { -// assert_eq!(req.method(), &Method::POST); -// assert_eq!(req.uri().path(), "/containers/m1/start"); - -// Box::new(future::ok(Response::new(Body::empty()))) -// } - -// #[test] -// fn container_start_succeeds() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// POST "/containers/m1/start" => container_start_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = DockerModuleRuntime::make_runtime(settings).and_then(|runtime| runtime.start("m1")); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[allow(clippy::needless_pass_by_value)] -// fn container_stop_handler(req: Request) -> ResponseFuture { -// assert_eq!(req.method(), &Method::POST); -// assert_eq!(req.uri().path(), "/containers/m1/stop"); - -// Box::new(future::ok(Response::new(Body::empty()))) -// } - -// #[test] -// fn container_stop_succeeds() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// POST "/containers/m1/stop" => container_stop_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = -// DockerModuleRuntime::make_runtime(settings).and_then(|runtime| runtime.stop("m1", None)); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[allow(clippy::needless_pass_by_value)] -// fn container_stop_with_timeout_handler(req: Request) -> ResponseFuture { -// assert_eq!(req.method(), &Method::POST); -// assert_eq!(req.uri().path(), "/containers/m1/stop"); -// assert_eq!(req.uri().query().unwrap(), "t=600"); - -// Box::new(future::ok(Response::new(Body::empty()))) -// } - -// #[test] -// fn container_stop_with_timeout_succeeds() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// POST "/containers/m1/stop" => container_stop_with_timeout_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| runtime.stop("m1", Some(Duration::from_secs(600)))); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[allow(clippy::needless_pass_by_value)] -// fn container_remove_handler(req: Request) -> ResponseFuture { -// assert_eq!(req.method(), &Method::DELETE); -// assert_eq!(req.uri().path(), "/containers/m1"); - -// Box::new(future::ok(Response::new(Body::empty()))) -// } - -// #[test] -// fn container_remove_succeeds() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// DELETE "/containers/m1" => container_remove_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| ModuleRuntime::remove(&runtime, "m1")); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[allow(clippy::needless_pass_by_value)] -// fn container_list_handler(req: Request) -> ResponseFuture { -// assert_eq!(req.method(), &Method::GET); -// assert_eq!(req.uri().path(), "/containers/json"); - -// let query_map: HashMap = parse_query(req.uri().query().unwrap().as_bytes()) -// .into_owned() -// .collect(); -// assert!(query_map.contains_key("filters")); -// assert_eq!( -// query_map.get("filters"), -// Some( -// &json!({ -// "label": vec!["net.azure-devices.edge.owner=Microsoft.Azure.Devices.Edge.Agent"] -// }) -// .to_string() -// ) -// ); - -// let mut labels = HashMap::new(); -// labels.insert("l1".to_string(), "v1".to_string()); -// labels.insert("l2".to_string(), "v2".to_string()); -// labels.insert("l3".to_string(), "v3".to_string()); - -// let modules = vec![ -// ContainerSummary::new( -// "m1".to_string(), -// vec!["/m1".to_string()], -// "nginx:latest".to_string(), -// "img1".to_string(), -// "".to_string(), -// 10, -// vec![], -// 10, -// 10, -// labels.clone(), -// "".to_string(), -// "".to_string(), -// ContainerHostConfig::new(""), -// ContainerNetworkSettings::new(HashMap::new()), -// vec![], -// ), -// ContainerSummary::new( -// "m2".to_string(), -// vec!["/m2".to_string()], -// "ubuntu:latest".to_string(), -// "img2".to_string(), -// "".to_string(), -// 10, -// vec![], -// 10, -// 10, -// labels.clone(), -// "".to_string(), -// "".to_string(), -// ContainerHostConfig::new(""), -// ContainerNetworkSettings::new(HashMap::new()), -// vec![], -// ), -// ContainerSummary::new( -// "m3".to_string(), -// vec!["/m3".to_string()], -// "mongo:latest".to_string(), -// "img3".to_string(), -// "".to_string(), -// 10, -// vec![], -// 10, -// 10, -// labels, -// "".to_string(), -// "".to_string(), -// ContainerHostConfig::new(""), -// ContainerNetworkSettings::new(HashMap::new()), -// vec![], -// ), -// ]; - -// let response = serde_json::to_string(&modules).unwrap(); -// let response_len = response.len(); - -// let mut response = Response::new(response.into()); -// response -// .headers_mut() -// .typed_insert(&ContentLength(response_len as u64)); -// response -// .headers_mut() -// .typed_insert(&ContentType(mime::APPLICATION_JSON)); -// Box::new(future::ok(response)) -// } - -// #[test] -// fn container_list_succeeds() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// GET "/containers/json" => container_list_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = DockerModuleRuntime::make_runtime(settings).and_then(|runtime| runtime.list()); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// let modules = runtime.block_on(task).unwrap(); - -// assert_eq!(3, modules.len()); - -// assert_eq!("m1", modules[0].name()); -// assert_eq!("m2", modules[1].name()); -// assert_eq!("m3", modules[2].name()); - -// assert_eq!("img1", modules[0].config().image_id().unwrap()); -// assert_eq!("img2", modules[1].config().image_id().unwrap()); -// assert_eq!("img3", modules[2].config().image_id().unwrap()); - -// assert_eq!("nginx:latest", modules[0].config().image()); -// assert_eq!("ubuntu:latest", modules[1].config().image()); -// assert_eq!("mongo:latest", modules[2].config().image()); - -// for module in modules { -// for i in 0..3 { -// assert_eq!( -// module -// .config() -// .create_options() -// .labels() -// .unwrap() -// .get(&format!("l{}", i + 1)), -// Some(&format!("v{}", i + 1)) -// ); -// } -// } -// } - -// #[allow(clippy::needless_pass_by_value)] -// fn container_logs_handler(req: Request) -> ResponseFuture { -// assert_eq!(req.method(), &Method::GET); -// assert_eq!(req.uri().path(), "/containers/mod1/logs"); - -// let query_map: HashMap = parse_query(req.uri().query().unwrap().as_bytes()) -// .into_owned() -// .collect(); -// assert!(query_map.contains_key("stdout")); -// assert!(query_map.contains_key("stderr")); -// assert!(query_map.contains_key("follow")); -// assert!(query_map.contains_key("tail")); -// assert_eq!("true", query_map["follow"]); -// assert_eq!("all", query_map["tail"]); -// assert_eq!("100000", query_map["since"]); - -// let body = vec![ -// 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x52, 0x6f, 0x73, 0x65, 0x73, 0x20, 0x61, -// 0x72, 0x65, 0x20, 0x72, 0x65, 0x64, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x76, -// 0x69, 0x6f, 0x6c, 0x65, 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x62, 0x6c, 0x75, 0x65, -// ]; - -// Box::new(future::ok(Response::new(body.into()))) -// } - -// #[test] -// fn container_logs_succeeds() { -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// GET "/containers/mod1/logs" => container_logs_handler, -// ); - -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = DockerModuleRuntime::make_runtime(settings).and_then(|runtime| { -// let options = LogOptions::new() -// .with_follow(true) -// .with_tail(LogTail::All) -// .with_since(100_000) -// .with_until(200_000); - -// runtime.logs("mod1", &options) -// }); - -// let expected_body = [ -// 0x01_u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x52, 0x6f, 0x73, 0x65, 0x73, 0x20, -// 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x64, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, -// 0x76, 0x69, 0x6f, 0x6c, 0x65, 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x62, 0x6c, 0x75, -// 0x65, -// ]; - -// let assert = task.and_then(Stream::concat2).and_then(|b| { -// assert_eq!(&expected_body[..], b.as_ref()); -// Ok(()) -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(assert).unwrap(); -// } - -// #[test] -// fn image_remove_with_white_space_name_fails() { -// let (server, port) = run_tcp_server("127.0.0.1", default_network_handler()); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let image_name = " "; - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| ModuleRegistry::remove(&runtime, image_name)) -// .then(|res| match res { -// Ok(_) => Err("Expected error but got a result.".to_string()), -// Err(err) => match err.kind() { -// ErrorKind::RegistryOperation(RegistryOperation::RemoveImage(s)) -// if s == image_name => -// { -// Ok(()) -// } -// kind => panic!( -// "Expected `RegistryOperation(RemoveImage)` error but got {:?}.", -// kind -// ), -// }, -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[test] -// fn create_fails_for_non_docker_type() { -// let (server, port) = run_tcp_server("127.0.0.1", default_network_handler()); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let name = "not_docker"; - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| { -// let module_config = ModuleSpec::new( -// "m1".to_string(), -// name.to_string(), -// DockerConfig::new( -// "nginx:latest".to_string(), -// ContainerCreateBody::new(), -// None, -// None, -// ) -// .unwrap(), -// BTreeMap::new(), -// ImagePullPolicy::default(), -// ) -// .unwrap(); - -// runtime.create(module_config) -// }) -// .then(|result| match result { -// Ok(_) => panic!("Expected test to fail but it didn't!"), -// Err(err) => match err.kind() { -// ErrorKind::InvalidModuleType(s) if s == name => Ok::<_, Error>(()), -// kind => panic!("Expected `InvalidModuleType` error but got {:?}.", kind), -// }, -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[test] -// fn start_fails_for_empty_id() { -// let (server, port) = run_tcp_server("127.0.0.1", default_network_handler()); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let name = ""; - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| runtime.start(name)) -// .then(|result| match result { -// Ok(_) => panic!("Expected test to fail but it didn't!"), -// Err(err) => match err.kind() { -// ErrorKind::RuntimeOperation(RuntimeOperation::StartModule(s)) if s == name => { -// Ok::<_, Error>(()) -// } -// kind => panic!( -// "Expected `RuntimeOperation(StartModule)` error but got {:?}.", -// kind -// ), -// }, -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[test] -// fn start_fails_for_white_space_id() { -// let (server, port) = run_tcp_server("127.0.0.1", default_network_handler()); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let name = " "; - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| runtime.start(name)) -// .then(|result| match result { -// Ok(_) => panic!("Expected test to fail but it didn't!"), -// Err(err) => match err.kind() { -// ErrorKind::RuntimeOperation(RuntimeOperation::StartModule(s)) if s == name => { -// Ok::<_, Error>(()) -// } -// kind => panic!( -// "Expected `RuntimeOperation(StartModule)` error but got {:?}.", -// kind -// ), -// }, -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[test] -// fn stop_fails_for_empty_id() { -// let (server, port) = run_tcp_server("127.0.0.1", default_network_handler()); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let name = ""; - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| runtime.stop(name, None)) -// .then(|result| match result { -// Ok(_) => panic!("Expected test to fail but it didn't!"), -// Err(err) => match err.kind() { -// ErrorKind::RuntimeOperation(RuntimeOperation::StopModule(s)) if s == name => { -// Ok::<_, Error>(()) -// } -// kind => panic!( -// "Expected `RuntimeOperation(StopModule)` error but got {:?}.", -// kind -// ), -// }, -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[test] -// fn stop_fails_for_white_space_id() { -// let (server, port) = run_tcp_server("127.0.0.1", default_network_handler()); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let name = " "; - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| runtime.stop(name, None)) -// .then(|result| match result { -// Ok(_) => panic!("Expected test to fail but it didn't!"), -// Err(err) => match err.kind() { -// ErrorKind::RuntimeOperation(RuntimeOperation::StopModule(s)) if s == name => { -// Ok::<_, Error>(()) -// } -// kind => panic!( -// "Expected `RuntimeOperation(StopModule)` error but got {:?}.", -// kind -// ), -// }, -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[test] -// fn restart_fails_for_empty_id() { -// let (server, port) = run_tcp_server("127.0.0.1", default_network_handler()); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let name = ""; - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| runtime.restart(name)) -// .then(|result| match result { -// Ok(_) => panic!("Expected test to fail but it didn't!"), -// Err(err) => match err.kind() { -// ErrorKind::RuntimeOperation(RuntimeOperation::RestartModule(s)) if s == name => { -// Ok::<_, Error>(()) -// } -// kind => panic!( -// "Expected `RuntimeOperation(RestartModule)` error but got {:?}.", -// kind -// ), -// }, -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[test] -// fn restart_fails_for_white_space_id() { -// let (server, port) = run_tcp_server("127.0.0.1", default_network_handler()); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let name = " "; - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| runtime.restart(name)) -// .then(|result| match result { -// Ok(_) => panic!("Expected test to fail but it didn't!"), -// Err(err) => match err.kind() { -// ErrorKind::RuntimeOperation(RuntimeOperation::RestartModule(s)) if s == name => { -// Ok::<_, Error>(()) -// } -// kind => panic!( -// "Expected `RuntimeOperation(RestartModule)` error but got {:?}.", -// kind -// ), -// }, -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[test] -// fn remove_fails_for_empty_id() { -// let (server, port) = run_tcp_server("127.0.0.1", default_network_handler()); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let name = ""; - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| ModuleRuntime::remove(&runtime, name)) -// .then(|result| match result { -// Ok(_) => panic!("Expected test to fail but it didn't!"), -// Err(err) => match err.kind() { -// ErrorKind::RuntimeOperation(RuntimeOperation::RemoveModule(s)) if s == name => { -// Ok::<_, Error>(()) -// } -// kind => panic!( -// "Expected `RuntimeOperation(RemoveModule)` error but got {:?}.", -// kind -// ), -// }, -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[test] -// fn remove_fails_for_white_space_id() { -// let (server, port) = run_tcp_server("127.0.0.1", default_network_handler()); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let name = " "; - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| ModuleRuntime::remove(&runtime, name)) -// .then(|result| match result { -// Ok(_) => panic!("Expected test to fail but it didn't!"), -// Err(err) => match err.kind() { -// ErrorKind::RuntimeOperation(RuntimeOperation::RemoveModule(s)) if s == name => { -// Ok::<_, Error>(()) -// } -// kind => panic!( -// "Expected `RuntimeOperation(RemoveModule)` error but got {:?}.", -// kind -// ), -// }, -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[test] -// fn get_fails_for_empty_id() { -// let (server, port) = run_tcp_server("127.0.0.1", default_network_handler()); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let name = ""; - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| runtime.get(name)) -// .then(|result| match result { -// Ok(_) => panic!("Expected test to fail but it didn't!"), -// Err(err) => match err.kind() { -// ErrorKind::RuntimeOperation(RuntimeOperation::GetModule(s)) if s == name => { -// Ok::<_, Error>(()) -// } -// kind => panic!( -// "Expected `RuntimeOperation(GetModule)` error but got {:?}.", -// kind -// ), -// }, -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[test] -// fn get_fails_for_white_space_id() { -// let (server, port) = run_tcp_server("127.0.0.1", default_network_handler()); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let name = " "; - -// let task = DockerModuleRuntime::make_runtime(settings) -// .and_then(|runtime| runtime.get(name)) -// .then(|result| match result { -// Ok(_) => panic!("Expected test to fail but it didn't!"), -// Err(err) => match err.kind() { -// ErrorKind::RuntimeOperation(RuntimeOperation::GetModule(s)) if s == name => { -// Ok::<_, Error>(()) -// } -// kind => panic!( -// "Expected `RuntimeOperation(GetModule)` error but got {:?}.", -// kind -// ), -// }, -// }); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); -// } - -// #[test] -// fn runtime_init_network_does_not_exist_create() { -// let list_got_called_lock = Arc::new(RwLock::new(false)); -// let list_got_called_lock_cloned = list_got_called_lock.clone(); - -// let create_got_called_lock = Arc::new(RwLock::new(false)); -// let create_got_called_lock_cloned = create_got_called_lock.clone(); - -// let network_handler = make_network_handler( -// move || { -// let mut list_got_called_w = list_got_called_lock.write().unwrap(); -// *list_got_called_w = true; - -// json!([]).to_string() -// }, -// move |_| { -// let mut create_got_called_w = create_got_called_lock.write().unwrap(); -// *create_got_called_w = true; -// }, -// ); - -// let (server, port) = run_tcp_server("127.0.0.1", network_handler); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// //act -// let task = DockerModuleRuntime::make_runtime(settings); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); - -// //assert -// assert_eq!(true, *list_got_called_lock_cloned.read().unwrap()); -// assert_eq!(true, *create_got_called_lock_cloned.read().unwrap()); -// } - -// #[test] -// fn network_ipv6_create() { -// let list_got_called_lock = Arc::new(RwLock::new(false)); -// let list_got_called_lock_cloned = list_got_called_lock.clone(); - -// let create_got_called_lock = Arc::new(RwLock::new(false)); -// let create_got_called_lock_cloned = create_got_called_lock.clone(); - -// let network_handler = make_network_handler( -// move || { -// let mut list_got_called_w = list_got_called_lock.write().unwrap(); -// *list_got_called_w = true; - -// json!([]).to_string() -// }, -// move |req| { -// let mut create_got_called_w = create_got_called_lock.write().unwrap(); -// *create_got_called_w = true; - -// let task = req -// .into_body() -// .concat2() -// .map(|body| { -// let network: NetworkConfig = serde_json::from_slice(&body).unwrap(); -// assert_eq!("my-network", network.name().as_str()); -// let ipam_config = network.IPAM().unwrap().config().unwrap(); - -// let ipam_config_0 = ipam_config.get(0).unwrap(); -// assert_eq!(ipam_config_0["Gateway"], "172.18.0.1"); -// assert_eq!(ipam_config_0["Subnet"], "172.18.0.0/16"); -// assert_eq!(ipam_config_0["IPRange"], "172.18.0.0/16"); - -// let ipam_config_1 = ipam_config.get(1).unwrap(); -// assert_eq!(ipam_config_1["Gateway"], "172.20.0.1"); -// assert_eq!(ipam_config_1["Subnet"], "172.20.0.0/16"); -// assert_eq!(ipam_config_1["IPRange"], "172.20.0.0/24"); -// }) -// .map_err(|err| panic!("{:?}", err)); - -// tokio::spawn(task).into_future().wait().unwrap(); -// }, -// ); - -// let (server, port) = run_tcp_server("127.0.0.1", network_handler); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" - -// [moby_runtime.network] -// name = "my-network" -// ipv6 = true - -// [[moby_runtime.network.ipam.config]] -// gateway = "172.18.0.1" -// subnet = "172.18.0.0/16" -// ip_range = "172.18.0.0/16" - -// [[moby_runtime.network.ipam.config]] -// gateway = "172.20.0.1" -// subnet = "172.20.0.0/16" -// ip_range = "172.20.0.0/24" -// "#, -// port -// )); - -// //act -// let task = DockerModuleRuntime::make_runtime(settings); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); - -// //assert -// assert_eq!(true, *list_got_called_lock_cloned.read().unwrap()); -// assert_eq!(true, *create_got_called_lock_cloned.read().unwrap()); -// } - -// #[test] -// fn runtime_init_network_exist_do_not_create() { -// let list_got_called_lock = Arc::new(RwLock::new(false)); -// let list_got_called_lock_cloned = list_got_called_lock.clone(); - -// let create_got_called_lock = Arc::new(RwLock::new(false)); -// let create_got_called_lock_cloned = create_got_called_lock.clone(); - -// let network_handler = make_network_handler( -// move || { -// let mut list_got_called_w = list_got_called_lock.write().unwrap(); -// *list_got_called_w = true; - -// json!([ -// { -// "Name": "azure-iot-edge", -// "Id": "8e3209d08ed5e73d1c9c8e7580ddad232b6dceb5bf0c6d74cadbed75422eef0e", -// "Created": "0001-01-01T00:00:00Z", -// "Scope": "local", -// "Driver": "bridge", -// "EnableIPv6": false, -// "Internal": false, -// "Attachable": false, -// "Ingress": false, -// "IPAM": { -// "Driver": "bridge", -// "Config": [] -// }, -// "Containers": {}, -// "Options": {} -// } -// ]) -// .to_string() -// }, -// move |_| { -// let mut create_got_called_w = create_got_called_lock.write().unwrap(); -// *create_got_called_w = true; -// }, -// ); - -// let (server, port) = run_tcp_server("127.0.0.1", network_handler); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// //act -// let task = DockerModuleRuntime::make_runtime(settings); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// runtime.block_on(task).unwrap(); - -// //assert -// assert_eq!(true, *list_got_called_lock_cloned.read().unwrap()); -// assert_eq!(false, *create_got_called_lock_cloned.read().unwrap()); -// } - -// #[test] -// fn runtime_system_info_succeeds() { -// let system_info_got_called_lock = Arc::new(RwLock::new(false)); -// let system_info_got_called_lock_cloned = system_info_got_called_lock.clone(); - -// let on_system_info = move |req: Request| { -// let mut system_info_got_called_w = system_info_got_called_lock.write().unwrap(); -// *system_info_got_called_w = true; - -// assert_eq!(req.uri().path(), "/info"); - -// let response = json!( -// { -// "OSType": "linux", -// "Architecture": "x86_64", -// } -// ) -// .to_string(); -// let response_len = response.len(); - -// let mut response = Response::new(response.into()); -// response -// .headers_mut() -// .typed_insert(&ContentLength(response_len as u64)); -// response -// .headers_mut() -// .typed_insert(&ContentType(mime::APPLICATION_JSON)); - -// Box::new(future::ok(response)) as ResponseFuture -// }; - -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// GET "/info" => on_system_info, -// ); - -// //act -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = -// DockerModuleRuntime::make_runtime(settings).and_then(|runtime| runtime.system_info()); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// let system_info = runtime.block_on(task).unwrap(); - -// //assert -// assert_eq!(true, *system_info_got_called_lock_cloned.read().unwrap()); -// assert_eq!("linux", system_info.os_type); -// assert_eq!("x86_64", system_info.architecture); -// } - -// #[test] -// fn runtime_system_info_none_returns_unkown() { -// let system_info_got_called_lock = Arc::new(RwLock::new(false)); -// let system_info_got_called_lock_cloned = system_info_got_called_lock.clone(); - -// let on_system_info = move |req: Request| { -// let mut system_info_got_called_w = system_info_got_called_lock.write().unwrap(); -// *system_info_got_called_w = true; - -// assert_eq!(req.uri().path(), "/info"); - -// let response = json!({}).to_string(); -// let response_len = response.len(); - -// let mut response = Response::new(response.into()); -// response -// .headers_mut() -// .typed_insert(&ContentLength(response_len as u64)); -// response -// .headers_mut() -// .typed_insert(&ContentType(mime::APPLICATION_JSON)); - -// Box::new(future::ok(response)) as ResponseFuture -// }; - -// let dispatch_table = routes!( -// GET "/networks" => default_get_networks_handler(), -// POST "/networks/create" => default_create_network_handler(), -// GET "/info" => on_system_info, -// ); - -// //act -// let (server, port) = run_tcp_server( -// "127.0.0.1", -// make_req_dispatcher(dispatch_table, Box::new(not_found_handler)), -// ); -// let server = server.map_err(|err| panic!(err)); - -// let settings = make_settings(&format!( -// r#" -// [moby_runtime] -// uri = "http://localhost:{}" -// network = "azure-iot-edge" -// "#, -// port -// )); - -// let task = -// DockerModuleRuntime::make_runtime(settings).and_then(|runtime| runtime.system_info()); - -// let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap(); -// runtime.spawn(server); -// let system_info = runtime.block_on(task).unwrap(); - -// //assert -// assert_eq!(true, *system_info_got_called_lock_cloned.read().unwrap()); -// assert_eq!("Unknown", system_info.os_type); -// assert_eq!("Unknown", system_info.architecture); -// } diff --git a/edgelet/edgelet-test-utils/Cargo.toml b/edgelet/edgelet-test-utils/Cargo.toml index e5b0d524745..fd73a44f7e5 100644 --- a/edgelet/edgelet-test-utils/Cargo.toml +++ b/edgelet/edgelet-test-utils/Cargo.toml @@ -6,6 +6,7 @@ publish = false edition = "2021" [dependencies] +anyhow = "1" async-trait = "0.1" bytes = "1" futures = "0.3" diff --git a/edgelet/edgelet-test-utils/src/lib.rs b/edgelet/edgelet-test-utils/src/lib.rs index a1ac4d21764..776db664ad7 100644 --- a/edgelet/edgelet-test-utils/src/lib.rs +++ b/edgelet/edgelet-test-utils/src/lib.rs @@ -8,6 +8,6 @@ pub use settings::Settings; /// Generic test error. Most users of ModuleRuntime don't act on the error other /// than passing it up the call stack, so it's fine to return any error. -fn test_error() -> std::io::Error { - std::io::Error::new(std::io::ErrorKind::Other, "test error") +fn test_error() -> anyhow::Error { + anyhow::Error::msg("test error") } diff --git a/edgelet/edgelet-test-utils/src/runtime.rs b/edgelet/edgelet-test-utils/src/runtime.rs index 4caa28932f9..db7dffea82e 100644 --- a/edgelet/edgelet-test-utils/src/runtime.rs +++ b/edgelet/edgelet-test-utils/src/runtime.rs @@ -23,7 +23,6 @@ impl Default for Module { #[async_trait::async_trait] impl edgelet_core::Module for Module { type Config = Config; - type Error = std::io::Error; fn name(&self) -> &str { &self.name @@ -39,7 +38,7 @@ impl edgelet_core::Module for Module { // The functions below aren't used in tests. - async fn runtime_state(&self) -> Result { + async fn runtime_state(&self) -> anyhow::Result { unimplemented!() } } @@ -49,15 +48,14 @@ pub struct ModuleRegistry {} #[async_trait::async_trait] impl edgelet_core::ModuleRegistry for ModuleRegistry { type Config = Config; - type Error = std::io::Error; // The fuctions below aren't used in tests. - async fn pull(&self, _config: &Self::Config) -> Result<(), Self::Error> { + async fn pull(&self, _config: &Self::Config) -> anyhow::Result<()> { unimplemented!() } - async fn remove(&self, _name: &str) -> Result<(), Self::Error> { + async fn remove(&self, _name: &str) -> anyhow::Result<()> { unimplemented!() } } @@ -83,13 +81,11 @@ impl Default for Runtime { #[async_trait::async_trait] impl edgelet_core::ModuleRuntime for Runtime { - type Error = std::io::Error; - type Config = Config; type Module = Module; type ModuleRegistry = ModuleRegistry; - async fn module_top(&self, id: &str) -> Result, Self::Error> { + async fn module_top(&self, id: &str) -> anyhow::Result> { if id == "runtimeError" { Err(crate::test_error()) } else { @@ -110,18 +106,18 @@ impl edgelet_core::ModuleRuntime for Runtime { async fn create( &self, _module: edgelet_settings::ModuleSpec, - ) -> Result<(), Self::Error> { + ) -> anyhow::Result<()> { unimplemented!() } async fn get( &self, _id: &str, - ) -> Result<(Self::Module, edgelet_core::ModuleRuntimeState), Self::Error> { + ) -> anyhow::Result<(Self::Module, edgelet_core::ModuleRuntimeState)> { unimplemented!() } - async fn start(&self, _id: &str) -> Result<(), Self::Error> { + async fn start(&self, _id: &str) -> anyhow::Result<()> { unimplemented!() } @@ -129,33 +125,33 @@ impl edgelet_core::ModuleRuntime for Runtime { &self, _id: &str, _wait_before_kill: Option, - ) -> Result<(), Self::Error> { + ) -> anyhow::Result<()> { unimplemented!() } - async fn restart(&self, _id: &str) -> Result<(), Self::Error> { + async fn restart(&self, _id: &str) -> anyhow::Result<()> { unimplemented!() } - async fn remove(&self, _id: &str) -> Result<(), Self::Error> { + async fn remove(&self, _id: &str) -> anyhow::Result<()> { unimplemented!() } - async fn system_info(&self) -> Result { + async fn system_info(&self) -> anyhow::Result { unimplemented!() } - async fn system_resources(&self) -> Result { + async fn system_resources(&self) -> anyhow::Result { unimplemented!() } - async fn list(&self) -> Result, Self::Error> { + async fn list(&self) -> anyhow::Result> { unimplemented!() } async fn list_with_details( &self, - ) -> Result, Self::Error> { + ) -> anyhow::Result> { unimplemented!() } @@ -163,18 +159,15 @@ impl edgelet_core::ModuleRuntime for Runtime { &self, _id: &str, _options: &edgelet_core::LogOptions, - ) -> Result { + ) -> anyhow::Result { unimplemented!() } - async fn remove_all(&self) -> Result<(), Self::Error> { + async fn remove_all(&self) -> anyhow::Result<()> { unimplemented!() } - async fn stop_all( - &self, - _wait_before_kill: Option, - ) -> Result<(), Self::Error> { + async fn stop_all(&self, _wait_before_kill: Option) -> anyhow::Result<()> { unimplemented!() } diff --git a/edgelet/edgelet-utils/Cargo.toml b/edgelet/edgelet-utils/Cargo.toml index 29a1496a9f9..83adc975328 100644 --- a/edgelet/edgelet-utils/Cargo.toml +++ b/edgelet/edgelet-utils/Cargo.toml @@ -7,10 +7,10 @@ edition = "2021" [dependencies] config = { version = "0.11", default-features = false } -failure = "0.1" log = "0.4" serde = "1.0" serde_json = "1.0" +thiserror = "1" yaml-rust = "0.4" [dev_dependencies] diff --git a/edgelet/edgelet-utils/src/error.rs b/edgelet/edgelet-utils/src/error.rs index f85cc5664be..28aa495562b 100644 --- a/edgelet/edgelet-utils/src/error.rs +++ b/edgelet/edgelet-utils/src/error.rs @@ -1,67 +1,21 @@ // Copyright (c) Microsoft. All rights reserved. -use std::fmt; -use std::fmt::Display; - -use failure::{Backtrace, Context, Fail}; - pub type Result = ::std::result::Result; -#[derive(Debug)] -pub struct Error { - inner: Context, -} - -#[derive(Clone, Debug, Fail)] -pub enum ErrorKind { - #[fail(display = "Invalid argument - [{}]", _0)] +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Invalid argument - [{0}]")] Argument(String), - #[fail(display = "Argument {} out of range [{}, {}) ", _0, _1, _2)] + #[error("Argument {0} out of range [{1}, {2})")] ArgumentOutOfRange(String, String, String), - #[fail(display = "Argument {} should be greater than {}", _0, _1)] + #[error("Argument {0} should be greater than {1}")] ArgumentTooLow(String, String), - #[fail(display = "Argument is empty or only has whitespace - [{}]", _0)] + #[error("Argument is empty or only has whitespace - [{0}]")] ArgumentEmpty(String), - #[fail(display = "Could not clone value via serde")] - SerdeClone, -} - -impl Fail for Error { - fn cause(&self) -> Option<&dyn Fail> { - self.inner.cause() - } - - fn backtrace(&self) -> Option<&Backtrace> { - self.inner.backtrace() - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.inner, f) - } -} - -impl Error { - pub fn kind(&self) -> &ErrorKind { - self.inner.get_context() - } -} - -impl From for Error { - fn from(kind: ErrorKind) -> Self { - Error { - inner: Context::new(kind), - } - } -} - -impl From> for Error { - fn from(inner: Context) -> Self { - Error { inner } - } + #[error("Could not clone value via serde")] + SerdeClone(#[from] serde_json::Error), } diff --git a/edgelet/edgelet-utils/src/lib.rs b/edgelet/edgelet-utils/src/lib.rs index f3188faf958..34d8e564d07 100644 --- a/edgelet/edgelet-utils/src/lib.rs +++ b/edgelet/edgelet-utils/src/lib.rs @@ -14,18 +14,25 @@ mod error; mod logging; -pub mod macros; mod ser_de; mod yaml_file_source; use std::{collections::HashMap, net::IpAddr, str::FromStr}; -pub use crate::error::{Error, ErrorKind}; +pub use crate::error::Error; pub use crate::logging::log_failure; -pub use crate::macros::ensure_not_empty_with_context; pub use crate::ser_de::{serde_clone, string_or_struct}; pub use crate::yaml_file_source::YamlFileSource; +#[inline] +pub fn ensure_not_empty(value: &str) -> Result<(), Error> { + if value.trim().is_empty() { + return Err(Error::ArgumentEmpty(String::new())); + } + + Ok(()) +} + pub fn parse_query(query: &str) -> HashMap<&str, &str> { query .split('&') diff --git a/edgelet/edgelet-utils/src/logging.rs b/edgelet/edgelet-utils/src/logging.rs index 2eb6e724dee..721d1b46df1 100644 --- a/edgelet/edgelet-utils/src/logging.rs +++ b/edgelet/edgelet-utils/src/logging.rs @@ -1,11 +1,12 @@ // Copyright (c) Microsoft. All rights reserved. -use failure::Fail; use log::{log, Level}; -pub fn log_failure(level: Level, fail: &dyn Fail) { +pub fn log_failure(level: Level, fail: &dyn std::error::Error) { log!(level, "{}", fail); - for cause in fail.iter_causes() { + let mut cur = fail; + while let Some(cause) = cur.source() { log!(level, "\tcaused by: {}", cause); + cur = cause; } } diff --git a/edgelet/edgelet-utils/src/macros.rs b/edgelet/edgelet-utils/src/macros.rs deleted file mode 100644 index 500635bfb98..00000000000 --- a/edgelet/edgelet-utils/src/macros.rs +++ /dev/null @@ -1,458 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -//! Utility macros -//! -//! This module contains helper macros for implementing argument validation in -//! functions. In order to be able to use the macros in this crate, you _must_ -//! have an implementation of `std::convert::From` that converts from the `Error` -//! type defined in `edgelet-utils` to the error type being returned from the -//! function where the macro is being used. -//! -//! # Examples -//! -//!

Custom error types with implicit conversion:

-//! -//! ``` -//! #[macro_use] extern crate edgelet_utils; -//! extern crate failure; -//! -//! use failure::Fail; -//! -//! use edgelet_utils::Error as UtilsError; -//! -//! struct BooError { -//! inner: Box, -//! } -//! -//! impl From for BooError { -//! fn from(err: UtilsError) -> Self { -//! BooError { inner: Box::new(err) } -//! } -//! } -//! -//! struct TheThing { -//! val: i32, -//! } -//! -//! impl TheThing { -//! fn new(val: i32) -> Result { -//! Ok(TheThing { -//! val: ensure_range!(val, 10, 100), -//! }) -//! } -//! } -//! -//! let _thing = TheThing::new(5); -//! ``` - -use std::fmt; - -use failure::{Context, Fail}; - -use crate::error::ErrorKind; - -/// Exits a function early with an `Error`. -/// -/// The `bail!` macro provides an easy way to exit a function. It takes an error -/// as an argument and wraps that in an `edgelet_utils::Error` type instance and -/// invokes `From::from` to convert the error to the type that is being returned -/// from the function where it is being called. -/// -/// ``` -/// #[macro_use] extern crate edgelet_utils; -/// -/// use edgelet_utils::{ErrorKind, Error}; -/// -/// fn do_the_thing(some_expected_value: bool) -> Result<(), Error> { -/// if !some_expected_value { -/// bail!(ErrorKind::Argument("boo".to_string())); -/// } else { -/// Ok(()) -/// } -/// } -/// -/// fn main() { -/// let result = do_the_thing(false); -/// println!("{:?}", result); -/// } -/// ``` -/// -/// `bail!(err)` expands to: -/// -/// ```ignore -/// return Err(From::from(Error::from(err))) -/// ``` -#[macro_export] -macro_rules! bail { - ($err:expr) => { - return Err(::std::convert::From::from($crate::Error::from($err))); - }; -} - -/// Internal macro used for implementing other validation macros. -/// -/// Not to be directly invoked. Use one of the other `ensure*` macros. -#[macro_export] -macro_rules! ensure_impl { - ($val:expr, $cond:expr, $err:expr, $bail:tt) => {{ - let cond = $cond; - if cond { - $val - } else { - $bail!($err); - } - }}; -} - -/// Check if a condition evaluates to `true` and call the `bail!` macro with an -/// error if it doesn't. -/// -/// # Examples -/// -/// ``` -/// # #[macro_use] extern crate edgelet_utils; -/// # use edgelet_utils::{ErrorKind, Error}; -/// fn do_thing() -> Result<(), Error> { -/// assert_eq!(10, ensure!(10, 10 > 0)); -/// Ok(()) -/// } -/// # fn main() { -/// # do_thing().unwrap(); -/// # } -/// ``` -/// -/// ``` -/// # #[macro_use] extern crate edgelet_utils; -/// # use edgelet_utils::{ErrorKind, Error}; -/// #[derive(Debug)] -/// struct Foo { -/// ival: i32, -/// fval: f32, -/// } -/// -/// impl Foo { -/// fn new(ival: i32, fval: f32) -> Result { -/// Ok(Foo { -/// ival: ensure!(ival, ival > 0), -/// fval: ensure!( -/// fval, fval > 10f32, -/// ErrorKind::Argument("fval too small".to_string()) -/// ), -/// }) -/// } -/// } -/// -/// fn main() { -/// // prints argument error -/// println!("{:?}", Foo::new(0, 20f32)); -/// -/// // prints argument error with the message "fval too small" -/// println!("{:?}", Foo::new(5, 5f32)); -/// } -/// ``` -#[macro_export] -macro_rules! ensure { - ($val:expr, $cond:expr, $err:expr) => { - ensure_impl!($val, $cond, $err, bail) - }; - ($val:expr, $cond:expr) => { - ensure!($val, $cond, $crate::ErrorKind::Argument("".to_string())) - }; - ($cond:expr) => { - ensure!((), $cond); - }; -} - -/// Internal macro used for implementing other validation macros. -/// -/// Not to be directly invoked. Use one of the other `ensure*` macros. -#[macro_export] -macro_rules! ensure_range_impl { - ($val:expr, $low:expr, $high:expr, $ensure:tt) => { - match (&$val, &$low, &$high) { - (val_val, low_val, high_val) => $ensure!( - *val_val, - *val_val > *low_val && *val_val <= *high_val, - $crate::ErrorKind::ArgumentOutOfRange( - format!("{}", val_val), - format!("{}", low_val), - format!("{}", high_val), - ) - ), - } - }; -} - -/// Check if a value falls within the range (low, high]. -/// -/// # Examples -/// -/// ``` -/// # #[macro_use] extern crate edgelet_utils; -/// # use edgelet_utils::Error; -/// struct Foo { -/// ival: i32 -/// } -/// -/// impl Foo { -/// fn new(ival: i32) -> Result { -/// Ok(Foo { -/// ival: ensure_range!(ival, 10, 25), -/// }) -/// } -/// } -/// -/// # fn main() {} -/// ``` -#[macro_export] -macro_rules! ensure_range { - ($val:expr, $low:expr, $high:expr) => { - ensure_range_impl!($val, $low, $high, ensure) - }; -} - -/// Internal macro used for implementing other validation macros. -/// -/// Not to be directly invoked. Use one of the other `ensure*` macros. -#[macro_export] -macro_rules! ensure_greater_impl { - ($val:expr, $low:expr, $ensure:tt) => { - match (&$val, &$low) { - (val_val, low_val) => $ensure!( - *val_val, - *val_val > *low_val, - $crate::ErrorKind::ArgumentTooLow(format!("{}", val_val), format!("{}", low_val)) - ), - } - }; -} - -/// Check if a value is greater than a minimum and bail with an error if it is not. -/// -/// # Examples -/// -/// ``` -/// # #[macro_use] extern crate edgelet_utils; -/// # use edgelet_utils::Error; -/// #[derive(Debug)] -/// struct Foo { -/// val: i32, -/// } -/// -/// impl Foo { -/// fn new(val: i32) -> Result { -/// Ok(Foo { -/// val: ensure_greater!(val, 10), -/// }) -/// } -/// } -/// -/// fn main() { -/// // prints ArgumentTooLow error -/// let foo = Foo::new(10); -/// println!("{:?}", foo); -/// } -/// ``` -#[macro_export] -macro_rules! ensure_greater { - ($val:expr, $low:expr) => { - ensure_greater_impl!($val, $low, ensure) - }; -} - -/// Internal macro used for implementing other validation macros. -/// -/// Not to be directly invoked. Use one of the other `ensure*` macros. -#[macro_export] -macro_rules! ensure_not_empty_impl { - ($val:expr, $msg:expr, $ensure:tt) => { - $ensure!( - $val, - !($val.trim().is_empty()), - $crate::ErrorKind::ArgumentEmpty($msg.to_string()) - ) - }; -} - -/// Check if a string is empty and bail with an error if it is. -/// -/// # Examples -/// -/// ``` -/// # #[macro_use] extern crate edgelet_utils; -/// # use edgelet_utils::Error; -/// #[derive(Debug)] -/// struct Foo { -/// sval1: String, -/// sval2: String, -/// } -/// -/// impl Foo { -/// fn new(sval1: &str, sval2: String) -> Result { -/// Ok(Foo { -/// sval1: ensure_not_empty!(sval1.to_string()), -/// sval2: ensure_not_empty!(sval2, "sval2 cannot be empty"), -/// }) -/// } -/// } -/// -/// # fn main() { -/// # } -/// ``` -#[macro_export] -macro_rules! ensure_not_empty { - ($val:expr, $msg:expr) => { - ensure_not_empty_impl!($val, $msg, ensure) - }; - ($val:expr) => { - ensure_not_empty!($val, "".to_string()) - }; -} - -pub fn ensure_not_empty_with_context(value: &str, context: F) -> Result<(), Context> -where - D: fmt::Display + Send + Sync, - F: FnOnce() -> D, -{ - if value.trim().is_empty() { - return Err(ErrorKind::ArgumentEmpty(String::new()).context(context())); - } - - Ok(()) -} - -#[cfg(test)] -#[allow(clippy::semicolon_if_nothing_returned)] -mod tests { - use crate::error::{Error, ErrorKind}; - - macro_rules! check_ok { - ($expected:expr, $f:block) => { - let result: Result<_, Error> = async { $f }.await; - assert_eq!($expected, result.unwrap()); - }; - - ($expected:expr, $f:tt) => { - let result: Result<_, Error> = $f(); - assert_eq!($expected, result.unwrap()); - }; - } - - macro_rules! check_err { - ($expected:ident, $f:block) => { - let result: Result<_, Error> = async { $f }.await; - let err = result.expect_err("expected error but found value"); - - match err.kind() { - ErrorKind::$expected(..) => (), - _ => panic!("Unexpected error encountered {:#?}", err), - } - }; - - ($expected:ident, $f:tt) => { - let result: Result<_, Error> = $f(); - let err = result.expect_err("expected error but found value"); - - match err.kind() { - ErrorKind::$expected(..) => (), - _ => panic!("Unexpected error encountered {:#?}", err), - } - }; - } - - #[test] - fn validate_ensure() { - check_ok!(15, (|| Ok(ensure!(15, 15 > 10)))); - - check_err!(Argument, (|| Ok(ensure!(10, 9 > 10)))); - } - - #[tokio::test] - async fn validate_ensure_async() { - check_ok!(15, { Ok(ensure!(15, 15 > 10)) }); - - check_err!(Argument, { Ok(ensure!(10, 9 > 10)) }); - } - - #[test] - fn validate_ensure_range() { - check_ok!(10, (|| Ok(ensure_range!(10, 5, 15)))); - check_ok!(15, (|| Ok(ensure_range!(15, 5, 15)))); - - check_err!(ArgumentOutOfRange, (|| Ok(ensure_range!(3, 5, 15)))); - check_err!(ArgumentOutOfRange, (|| Ok(ensure_range!(5, 5, 15)))); - check_err!(ArgumentOutOfRange, (|| Ok(ensure_range!(25, 5, 15)))); - } - - #[tokio::test] - async fn validate_ensure_range_async() { - check_ok!(10, { Ok(ensure_range!(10, 5, 15)) }); - check_ok!(15, { Ok(ensure_range!(15, 5, 15)) }); - - check_err!(ArgumentOutOfRange, { Ok(ensure_range!(3, 5, 15)) }); - check_err!(ArgumentOutOfRange, { Ok(ensure_range!(5, 5, 15)) }); - check_err!(ArgumentOutOfRange, { Ok(ensure_range!(25, 5, 15)) }); - } - - #[test] - fn validate_ensure_greater() { - check_ok!(10, (|| Ok(ensure_greater!(10, 5)))); - - check_err!(ArgumentTooLow, (|| Ok(ensure_greater!(10, 25)))); - } - - #[tokio::test] - async fn validate_ensure_greater_async() { - check_ok!(10, { Ok(ensure_greater!(10, 5)) }); - - check_err!(ArgumentTooLow, { Ok(ensure_greater!(10, 25)) }); - } - - #[test] - fn validate_ensure_not_empty() { - check_err!(ArgumentEmpty, (|| Ok(ensure_not_empty!("")))); - check_err!(ArgumentEmpty, (|| Ok(ensure_not_empty!("", "empty str")))); - check_err!( - ArgumentEmpty, - (|| Ok(ensure_not_empty!(" ", "white space str"))) - ); - - check_err!(ArgumentEmpty, (|| Ok(ensure_not_empty!("".to_string())))); - check_err!( - ArgumentEmpty, - (|| Ok(ensure_not_empty!("".to_string(), "empty String"))) - ); - check_err!( - ArgumentEmpty, - (|| Ok(ensure_not_empty!(" ".to_string(), "white space String"))) - ); - - check_ok!(" not empty ", (|| Ok(ensure_not_empty!(" not empty ")))); - check_ok!( - " not empty ".to_string(), - (|| Ok(ensure_not_empty!(" not empty ".to_string()))) - ); - } - - #[tokio::test] - async fn validate_ensure_not_empty_async() { - check_err!(ArgumentEmpty, { Ok(ensure_not_empty!("")) }); - check_err!(ArgumentEmpty, { Ok(ensure_not_empty!("", "empty str")) }); - check_err!(ArgumentEmpty, { - Ok(ensure_not_empty!(" ", "white space str")) - }); - - check_err!(ArgumentEmpty, { Ok(ensure_not_empty!("".to_string())) }); - check_err!(ArgumentEmpty, { - Ok(ensure_not_empty!("".to_string(), "empty String")) - }); - check_err!(ArgumentEmpty, { - Ok(ensure_not_empty!(" ".to_string(), "white space String")) - }); - - check_ok!(" not empty ", { Ok(ensure_not_empty!(" not empty ")) }); - check_ok!(" not empty ".to_string(), { - Ok(ensure_not_empty!(" not empty ".to_string())) - }); - } -} diff --git a/edgelet/edgelet-utils/src/ser_de.rs b/edgelet/edgelet-utils/src/ser_de.rs index 626b2bbf060..00315d4678e 100644 --- a/edgelet/edgelet-utils/src/ser_de.rs +++ b/edgelet/edgelet-utils/src/ser_de.rs @@ -5,11 +5,10 @@ use std::marker::PhantomData; use std::result::Result as StdResult; use std::str::FromStr; -use failure::ResultExt; use serde::de::{self, Deserialize, DeserializeOwned, Deserializer, MapAccess, Visitor}; use serde::ser::Serialize; -use crate::error::{ErrorKind, Result}; +use crate::error::Result; // This implementation has been adapted from: https://serde.rs/string-or-struct.html @@ -61,9 +60,7 @@ pub fn serde_clone(inp: &T) -> Result where T: Serialize + DeserializeOwned, { - Ok(serde_json::to_string(inp) - .and_then(|s| serde_json::from_str(&s)) - .context(ErrorKind::SerdeClone)?) + Ok(serde_json::to_string(inp).and_then(|s| serde_json::from_str(&s))?) } #[cfg(test)] diff --git a/edgelet/iotedge/Cargo.toml b/edgelet/iotedge/Cargo.toml index cc70f99b558..dcedd4c7ed4 100644 --- a/edgelet/iotedge/Cargo.toml +++ b/edgelet/iotedge/Cargo.toml @@ -6,20 +6,21 @@ The iotedge tool is used to manage the IoT Edge runtime. edition = "2021" name = "iotedge" version = "0.1.0" + [dependencies] +anyhow = "1" async-trait = "0.1" atty = "0.2" -base64 = "0.9" +base64 = "0.13" byte-unit = "3.0.3" -bytes = "0.4" -chrono = {version = "0.4.7", features = ["serde"]} +bytes = "1" +chrono = { version = "0.4.7", features = ["serde"] } chrono-humanize = "0.0.11" clap = "2.31" -config = {version = "0.11", default-features = false} +config = { version = "0.11", default-features = false } erased-serde = "0.3.12" -failure = "0.1" -futures = "0.1" -hex = "0.3" +futures = "0.3" +hex = "0.4" hyper = "0.14" lazy_static = "1" libc = "0.2" @@ -27,36 +28,35 @@ log = "0.4" nix = "0.23" openssl = "0.10" regex = "1" -serde = "1.0" -serde_derive = "1.0" -serde_json = "1.0" -sysinfo = "0.14.10" -tabwriter = "1.0" -termcolor = "0.3" -tokio = {version = "1", features = ["process", "macros"]} +serde = { version = "1.0", features = ["derive"] } +serde_json = "1" +sysinfo = "0.23" +tabwriter = "1" +termcolor = "1" +thiserror = "1" +tokio = { version = "1", features = ["macros", "process"] } toml = "0.5" -typed-headers = "0.1.1" url = "2" zip = "0.5.3" -aziot-certd-config = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -aziot-identity-client-async = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -aziot-identity-common = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -aziot-identity-common-http = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -aziot-identityd-config = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -aziot-keyd-config = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -aziot-keys-common = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -aziot-tpmd-config = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -aziotctl-common = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -config-common = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -docker = {path = "../docker-rs"} -edgelet-core = {path = "../edgelet-core"} -edgelet-http = {path = "../edgelet-http"} -edgelet-settings = {path = "../edgelet-settings", features = ["settings-docker"]} -edgelet-utils = {path = "../edgelet-utils"} -http-common = {git = "https://github.com/Azure/iot-identity-service", branch = "main"} -support-bundle = {path = "../support-bundle"} +aziot-certd-config = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +aziot-identity-client-async = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +aziot-identity-common = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +aziot-identity-common-http = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +aziot-identityd-config = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +aziot-keyd-config = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +aziot-keys-common = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +aziot-tpmd-config = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +aziotctl-common = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +config-common = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +docker = { path = "../docker-rs" } +edgelet-core = { path = "../edgelet-core" } +edgelet-http = { path = "../edgelet-http" } +edgelet-settings = { path = "../edgelet-settings", features = ["settings-docker"] } +edgelet-utils = { path = "../edgelet-utils" } +http-common = { git = "https://github.com/Azure/iot-identity-service", branch = "main" } +support-bundle = { path = "../support-bundle" } [dev-dependencies] -edgelet-test-utils = {path = "../edgelet-test-utils"} +edgelet-test-utils = { path = "../edgelet-test-utils" } tempfile = "3.1.0" diff --git a/edgelet/iotedge/src/check/additional_info.rs b/edgelet/iotedge/src/check/additional_info.rs index e19bd6012a3..3cc8dca4ffe 100644 --- a/edgelet/iotedge/src/check/additional_info.rs +++ b/edgelet/iotedge/src/check/additional_info.rs @@ -7,7 +7,7 @@ use byte_unit::{Byte, ByteUnit}; use sysinfo::{DiskExt, SystemExt}; /// Additional info for the JSON output of `iotedge check` -#[derive(Clone, Debug, serde_derive::Serialize)] +#[derive(Clone, Debug, serde::Serialize)] pub(super) struct AdditionalInfo { pub(super) docker_version: Option, pub(super) aziot_edged_version: Option, @@ -42,7 +42,7 @@ impl AdditionalInfo { /// ``` /// /// Ref: -#[derive(Clone, Debug, serde_derive::Serialize)] +#[derive(Clone, Debug, serde::Serialize)] pub(super) struct OsInfo { id: Option, version_id: Option, @@ -115,7 +115,7 @@ fn parse_os_release_line(line: &str) -> Option<(&str, &str)> { Some((key, value)) } -#[derive(Clone, Debug, Default, serde_derive::Serialize)] +#[derive(Clone, Debug, Default, serde::Serialize)] struct SystemInfo { used_ram: String, total_ram: String, @@ -132,12 +132,12 @@ impl SystemInfo { let mut system = sysinfo::System::new(); system.refresh_all(); SystemInfo { - total_ram: pretty_kbyte(system.get_total_memory()), - used_ram: pretty_kbyte(system.get_used_memory()), - total_swap: pretty_kbyte(system.get_total_swap()), - used_swap: pretty_kbyte(system.get_used_swap()), + total_ram: pretty_kbyte(system.total_memory()), + used_ram: pretty_kbyte(system.used_memory()), + total_swap: pretty_kbyte(system.total_swap()), + used_swap: pretty_kbyte(system.used_swap()), - disks: system.get_disks().iter().map(DiskInfo::new).collect(), + disks: system.disks().iter().map(DiskInfo::new).collect(), } } @@ -146,7 +146,7 @@ impl SystemInfo { } } -#[derive(Clone, Debug, Default, serde_derive::Serialize)] +#[derive(Clone, Debug, Default, serde::Serialize)] struct DiskInfo { name: String, percent_free: String, @@ -162,8 +162,8 @@ impl DiskInfo { where T: DiskExt, { - let available_space = disk.get_available_space(); - let total_space = disk.get_total_space(); + let available_space = disk.available_space(); + let total_space = disk.total_space(); #[allow(clippy::cast_precision_loss)] let percent_free = format!( "{:.1}%", @@ -171,7 +171,7 @@ impl DiskInfo { ); DiskInfo { - name: disk.get_name().to_string_lossy().into_owned(), + name: disk.name().to_string_lossy().into_owned(), percent_free, available_space: Byte::from_bytes(u128::from(available_space)) .get_appropriate_unit(true) @@ -179,8 +179,8 @@ impl DiskInfo { total_space: Byte::from_bytes(u128::from(total_space)) .get_appropriate_unit(true) .format(2), - file_system: String::from_utf8_lossy(disk.get_file_system()).into_owned(), - file_type: format!("{:?}", disk.get_type()), + file_system: String::from_utf8_lossy(disk.file_system()).into_owned(), + file_type: format!("{:?}", disk.type_()), } } } diff --git a/edgelet/iotedge/src/check/checks/aziot_edged_version.rs b/edgelet/iotedge/src/check/checks/aziot_edged_version.rs index 027e961bb88..962e73d4a39 100644 --- a/edgelet/iotedge/src/check/checks/aziot_edged_version.rs +++ b/edgelet/iotedge/src/check/checks/aziot_edged_version.rs @@ -1,10 +1,10 @@ -use failure::{self, Context, Fail, ResultExt}; +use anyhow::{anyhow, Context}; use regex::Regex; use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -use crate::error::{Error, ErrorKind, FetchLatestVersionsReason}; +use crate::error::{Error, FetchLatestVersionsReason}; -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub(crate) struct AziotEdgedVersion { actual_version: Option, expected_version: Option, @@ -29,16 +29,13 @@ impl Checker for AziotEdgedVersion { } impl AziotEdgedVersion { - async fn get_version( - &mut self, - check: &Check, - ) -> Result { + async fn get_version(&mut self, check: &Check) -> anyhow::Result { let proxy = check .proxy_uri .as_ref() .map(|proxy| proxy.parse::()) .transpose() - .context(ErrorKind::FetchLatestVersions( + .context(Error::FetchLatestVersions( FetchLatestVersionsReason::CreateClient, ))?; @@ -59,7 +56,7 @@ impl AziotEdgedVersion { let res = client .request(req) .await - .context(ErrorKind::FetchLatestVersions( + .context(Error::FetchLatestVersions( FetchLatestVersionsReason::GetResponse, ))?; match res.status() { @@ -67,18 +64,18 @@ impl AziotEdgedVersion { uri = res .headers() .get(hyper::header::LOCATION) - .ok_or(ErrorKind::FetchLatestVersions( + .ok_or(Error::FetchLatestVersions( FetchLatestVersionsReason::InvalidOrMissingLocationHeader, ))? .to_str() .map_err(|_| { - ErrorKind::FetchLatestVersions( + Error::FetchLatestVersions( FetchLatestVersionsReason::InvalidOrMissingLocationHeader, ) })? .parse() .map_err(|_| { - ErrorKind::FetchLatestVersions( + Error::FetchLatestVersions( FetchLatestVersionsReason::InvalidOrMissingLocationHeader, ) })?; @@ -94,9 +91,9 @@ impl AziotEdgedVersion { } status_code => { - return Err(Error::from(ErrorKind::FetchLatestVersions( + return Err(Error::FetchLatestVersions( FetchLatestVersionsReason::ResponseStatusCode(status_code), - )) + ) .into()) } } @@ -105,7 +102,7 @@ impl AziotEdgedVersion { Ok(latest_versions) } - async fn inner_execute(&mut self, check: &mut Check) -> Result { + async fn inner_execute(&mut self, check: &mut Check) -> anyhow::Result { let latest_versions = if let Some(expected_aziot_edged_version) = &check.expected_aziot_edged_version { crate::LatestVersions { @@ -131,13 +128,12 @@ impl AziotEdgedVersion { .await .context("Could not spawn aziot-edged process")?; if !output.status.success() { - return Err(Context::new(format!( + return Err(anyhow!( "aziot-edged returned {}, stderr = {}", output.status, String::from_utf8_lossy(&*output.stderr), - )) - .context("Could not spawn aziot-edged process") - .into()); + ) + .context("Could not spawn aziot-edged process")); } let output = String::from_utf8(output.stdout) @@ -148,11 +144,8 @@ impl AziotEdgedVersion { let captures = aziot_edged_version_regex .captures(output.trim()) .ok_or_else(|| { - Context::new(format!( - "output {:?} does not match expected format", - output, - )) - .context("Could not parse output of aziot-edged --version") + anyhow!("output {:?} does not match expected format", output,) + .context("Could not parse output of aziot-edged --version") })?; let version = captures .get(1) @@ -164,12 +157,11 @@ impl AziotEdgedVersion { if version != latest_versions.aziot_edge { return Ok(CheckResult::Warning( - Context::new(format!( + anyhow!( "Installed IoT Edge daemon has version {} but {} is the latest stable version available.\n\ Please see https://aka.ms/iotedge-update-runtime for update instructions.", version, latest_versions.aziot_edge, - )) - .into(), + ), )); } diff --git a/edgelet/iotedge/src/check/checks/check_agent_image.rs b/edgelet/iotedge/src/check/checks/check_agent_image.rs index f228a06f062..7f6ea1901af 100644 --- a/edgelet/iotedge/src/check/checks/check_agent_image.rs +++ b/edgelet/iotedge/src/check/checks/check_agent_image.rs @@ -1,10 +1,11 @@ -use edgelet_settings::RuntimeSettings; -use failure::{Context, ResultExt}; +use anyhow::Context; use regex::Regex; +use edgelet_settings::RuntimeSettings; + use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub(crate) struct CheckAgentImage {} #[async_trait::async_trait] @@ -25,7 +26,7 @@ impl Checker for CheckAgentImage { impl CheckAgentImage { #[allow(clippy::unused_self)] - async fn inner_execute(&mut self, check: &mut Check) -> Result { + async fn inner_execute(&mut self, check: &mut Check) -> anyhow::Result { let settings = if let Some(settings) = &mut check.settings { settings } else { @@ -125,10 +126,9 @@ fn check_agent_image_version_nested(agent_image: &str) -> CheckResult { if let (Some(major), Some(minor)) = (major, minor) { if major < 1 || (major == 1) && (minor < 2) { - return CheckResult::Failed( - Context::new("In nested Edge, edgeAgent version need to be 1.2 or above") - .into(), - ); + return CheckResult::Failed(anyhow::anyhow!( + "In nested Edge, edgeAgent version need to be 1.2 or above", + )); } } } diff --git a/edgelet/iotedge/src/check/checks/connect_management_uri.rs b/edgelet/iotedge/src/check/checks/connect_management_uri.rs index 486be681da0..2c21a035b18 100644 --- a/edgelet/iotedge/src/check/checks/connect_management_uri.rs +++ b/edgelet/iotedge/src/check/checks/connect_management_uri.rs @@ -1,14 +1,14 @@ use std::borrow::Cow; use std::ffi::{OsStr, OsString}; -use failure::{self, Context, ResultExt}; +use anyhow::{anyhow, Context}; use edgelet_core::{self, UrlExt}; use edgelet_settings::RuntimeSettings; use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub(crate) struct ConnectManagementUri { connect_management_uri: Option, listen_management_uri: Option, @@ -31,7 +31,7 @@ impl Checker for ConnectManagementUri { } impl ConnectManagementUri { - async fn inner_execute(&mut self, check: &mut Check) -> Result { + async fn inner_execute(&mut self, check: &mut Check) -> anyhow::Result { let settings = if let Some(settings) = &check.settings { settings } else { @@ -84,21 +84,19 @@ impl ConnectManagementUri { let socket_path = socket_path.to_str() - .ok_or_else(|| Context::new("Could not parse connect.management_uri: file path is not valid utf-8"))?; + .ok_or_else(|| anyhow!("Could not parse connect.management_uri: file path is not valid utf-8"))?; args.push(Cow::Owned(format!("{}:{}", socket_path, socket_path).into())); }, - (scheme1, scheme2) if scheme1 != scheme2 => return Err(Context::new( - format!( + (scheme1, scheme2) if scheme1 != scheme2 => return Err(anyhow!( "configuration has invalid combination of schemes for connect.management_uri ({:?}) and listen.management_uri ({:?})", scheme1, scheme2, - )) - .into()), + )), - (scheme, _) => return Err(Context::new( - format!("Could not parse connect.management_uri: scheme {} is invalid", scheme), - ).into()), + (scheme, _) => return Err(anyhow!( + "Could not parse connect.management_uri: scheme {} is invalid", scheme, + )), } args.extend(vec![ @@ -112,8 +110,8 @@ impl ConnectManagementUri { match super::docker(docker_host_arg, args).await { Ok(_) => Ok(CheckResult::Ok), - Err((Some(stderr), err)) => Err(err.context(stderr).into()), - Err((None, err)) => Err(err.context("Could not spawn docker process").into()), + Err((Some(stderr), err)) => Err(err.context(stderr)), + Err((None, err)) => Err(err.context("Could not spawn docker process")), } } } diff --git a/edgelet/iotedge/src/check/checks/container_connect_upstream.rs b/edgelet/iotedge/src/check/checks/container_connect_upstream.rs index c858e18137c..92f4fcc52b1 100644 --- a/edgelet/iotedge/src/check/checks/container_connect_upstream.rs +++ b/edgelet/iotedge/src/check/checks/container_connect_upstream.rs @@ -50,7 +50,7 @@ pub(crate) fn get_host_container_upstream_tests() -> Vec> { ] } -#[derive(serde_derive::Serialize)] +#[derive(serde::Serialize)] pub(crate) struct ContainerConnectUpstream { upstream_port: UpstreamProtocolPort, upstream_hostname: Option, @@ -179,18 +179,16 @@ impl ContainerConnectUpstream { } if let Err((_, err)) = super::docker(docker_host_arg, args).await { - let err = err - .context(format!( - "Container on the {} network could not connect to {}:{}", - if self.use_container_runtime_network { - network_name - } else { - "default" - }, - upstream_hostname, - port, - )) - .into(); + let err = err.context(format!( + "Container on the {} network could not connect to {}:{}", + if self.use_container_runtime_network { + network_name + } else { + "default" + }, + upstream_hostname, + port, + )); return CheckResult::Failed(err); } diff --git a/edgelet/iotedge/src/check/checks/container_engine_dns.rs b/edgelet/iotedge/src/check/checks/container_engine_dns.rs index 0724abc52a3..83afaabef36 100644 --- a/edgelet/iotedge/src/check/checks/container_engine_dns.rs +++ b/edgelet/iotedge/src/check/checks/container_engine_dns.rs @@ -1,10 +1,10 @@ use std::fs::File; -use failure::{self, Context, ResultExt}; +use anyhow::Context; use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub(crate) struct ContainerEngineDns { container_engine_config_path: Option, dns: Option>, @@ -26,7 +26,7 @@ impl Checker for ContainerEngineDns { } impl ContainerEngineDns { - fn inner_execute(&mut self, check: &mut Check) -> Result { + fn inner_execute(&mut self, check: &mut Check) -> anyhow::Result { const MESSAGE: &str = "Container engine is not configured with DNS server setting, which may impact connectivity to IoT Hub.\n\ Please see https://aka.ms/iotedge-prod-checklist-dns for best practices.\n\ @@ -40,7 +40,7 @@ impl ContainerEngineDns { ); let daemon_config_file = File::open(&check.container_engine_config_path) - .with_context(|_| { + .with_context(|| { format!( "Could not open container engine config file {}", check.container_engine_config_path.display(), @@ -50,11 +50,11 @@ impl ContainerEngineDns { let daemon_config_file = match daemon_config_file { Ok(daemon_config_file) => daemon_config_file, Err(err) => { - return Ok(CheckResult::Warning(err.into())); + return Ok(CheckResult::Warning(err)); } }; let daemon_config: DaemonConfig = serde_json::from_reader(daemon_config_file) - .with_context(|_| { + .with_context(|| { format!( "Could not parse container engine config file {}", check.container_engine_config_path.display(), @@ -64,14 +64,14 @@ impl ContainerEngineDns { self.dns = daemon_config.dns.clone(); if daemon_config.dns.map_or(true, |e| e.is_empty()) { - return Ok(CheckResult::Warning(Context::new(MESSAGE).into())); + return Ok(CheckResult::Warning(anyhow::anyhow!(MESSAGE))); } Ok(CheckResult::Ok) } } -#[derive(serde_derive::Deserialize)] +#[derive(serde::Deserialize)] struct DaemonConfig { dns: Option>, } diff --git a/edgelet/iotedge/src/check/checks/container_engine_installed.rs b/edgelet/iotedge/src/check/checks/container_engine_installed.rs index 862cc452d30..4fa78dad0fe 100644 --- a/edgelet/iotedge/src/check/checks/container_engine_installed.rs +++ b/edgelet/iotedge/src/check/checks/container_engine_installed.rs @@ -1,7 +1,8 @@ -use failure::Context; - use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -#[derive(Default, serde_derive::Serialize)] + +use anyhow::anyhow; + +#[derive(Default, serde::Serialize)] pub(crate) struct ContainerEngineInstalled { docker_host_arg: Option, docker_server_version: Option, @@ -24,7 +25,7 @@ impl Checker for ContainerEngineInstalled { } impl ContainerEngineInstalled { - async fn inner_execute(&mut self, check: &mut Check) -> Result { + async fn inner_execute(&mut self, check: &mut Check) -> anyhow::Result { let settings = if let Some(settings) = &check.settings { settings } else { @@ -43,11 +44,11 @@ impl ContainerEngineInstalled { } scheme => { - return Err(Context::new(format!( + return Err(anyhow!( "Could not communicate with container engine at {}. The scheme {} is invalid.", - uri, scheme, - )) - .into()); + uri, + scheme, + )); } }; @@ -68,11 +69,11 @@ impl ContainerEngineInstalled { if let Some(message) = message { if message.contains("Got permission denied") { error_message += "\nYou might need to run this command as root."; - return Ok(CheckResult::Fatal(err.context(error_message).into())); + return Ok(CheckResult::Fatal(err.context(error_message))); } } - return Err(err.context(error_message).into()); + return Err(err.context(error_message)); } }; diff --git a/edgelet/iotedge/src/check/checks/container_engine_ipv6.rs b/edgelet/iotedge/src/check/checks/container_engine_ipv6.rs index 7b746cfc228..fd10c9fa052 100644 --- a/edgelet/iotedge/src/check/checks/container_engine_ipv6.rs +++ b/edgelet/iotedge/src/check/checks/container_engine_ipv6.rs @@ -1,12 +1,12 @@ use std::fs::File; -use failure::{self, Context, Fail, ResultExt}; +use anyhow::Context; use edgelet_settings::MobyNetwork; use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub(crate) struct ContainerEngineIPv6 { expected_use_ipv6: Option, actual_use_ipv6: Option, @@ -28,7 +28,7 @@ impl Checker for ContainerEngineIPv6 { } impl ContainerEngineIPv6 { - fn inner_execute(&mut self, check: &mut Check) -> Result { + fn inner_execute(&mut self, check: &mut Check) -> anyhow::Result { const MESSAGE: &str = "Container engine is not configured for IPv6 communication.\n\ Please see https://aka.ms/iotedge-docker-ipv6 for a guide on how to enable IPv6 support."; @@ -44,7 +44,7 @@ impl ContainerEngineIPv6 { self.expected_use_ipv6 = Some(is_edge_ipv6_configured); let daemon_config_file = File::open(&check.container_engine_config_path) - .with_context(|_| { + .with_context(|| { format!( "Could not open container engine config file {}", check.container_engine_config_path.display(), @@ -55,14 +55,14 @@ impl ContainerEngineIPv6 { Ok(daemon_config_file) => daemon_config_file, Err(err) => { return if is_edge_ipv6_configured { - Err(err.context(MESSAGE).into()) + Err(err.context(MESSAGE)) } else { Ok(CheckResult::Ignored) } } }; let daemon_config: DaemonConfig = serde_json::from_reader(daemon_config_file) - .with_context(|_| { + .with_context(|| { format!( "Could not parse container engine config file {}", check.container_engine_config_path.display(), @@ -74,14 +74,14 @@ impl ContainerEngineIPv6 { if daemon_config.ipv6.unwrap_or_default() { Ok(CheckResult::Ok) } else if is_edge_ipv6_configured { - Err(Context::new(MESSAGE).into()) + Err(anyhow::anyhow!(MESSAGE)) } else { Ok(CheckResult::Ignored) } } } -#[derive(serde_derive::Deserialize)] +#[derive(serde::Deserialize)] struct DaemonConfig { ipv6: Option, } diff --git a/edgelet/iotedge/src/check/checks/container_engine_logrotate.rs b/edgelet/iotedge/src/check/checks/container_engine_logrotate.rs index 852cbef1caa..c63f19f26bf 100644 --- a/edgelet/iotedge/src/check/checks/container_engine_logrotate.rs +++ b/edgelet/iotedge/src/check/checks/container_engine_logrotate.rs @@ -1,10 +1,10 @@ use std::fs::File; -use failure::{self, Context, ResultExt}; +use anyhow::{anyhow, Context}; use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub(crate) struct ContainerEngineLogrotate { daemon_config: Option, } @@ -25,14 +25,14 @@ impl Checker for ContainerEngineLogrotate { } impl ContainerEngineLogrotate { - fn inner_execute(&mut self, check: &mut Check) -> Result { + fn inner_execute(&mut self, check: &mut Check) -> anyhow::Result { const MESSAGE: &str = "Container engine is not configured to rotate module logs which may cause it run out of disk space.\n\ Please see https://aka.ms/iotedge-prod-checklist-logs for best practices.\n\ You can ignore this warning if you are setting log policy per module in the Edge deployment."; let daemon_config_file = File::open(&check.container_engine_config_path) - .with_context(|_| { + .with_context(|| { format!( "Could not open container engine config file {}", check.container_engine_config_path.display(), @@ -42,11 +42,11 @@ impl ContainerEngineLogrotate { let daemon_config_file = match daemon_config_file { Ok(daemon_config_file) => daemon_config_file, Err(err) => { - return Ok(CheckResult::Warning(err.into())); + return Ok(CheckResult::Warning(err)); } }; let daemon_config: DaemonConfig = serde_json::from_reader(daemon_config_file) - .with_context(|_| { + .with_context(|| { format!( "Could not parse container engine config file {}", check.container_engine_config_path.display(), @@ -56,26 +56,26 @@ impl ContainerEngineLogrotate { self.daemon_config = Some(daemon_config.clone()); if daemon_config.log_driver.is_none() { - return Ok(CheckResult::Warning(Context::new(MESSAGE).into())); + return Ok(CheckResult::Warning(anyhow!(MESSAGE))); } if let Some(log_opts) = &daemon_config.log_opts { if log_opts.max_file.is_none() { - return Ok(CheckResult::Warning(Context::new(MESSAGE).into())); + return Ok(CheckResult::Warning(anyhow!(MESSAGE))); } if log_opts.max_size.is_none() { - return Ok(CheckResult::Warning(Context::new(MESSAGE).into())); + return Ok(CheckResult::Warning(anyhow!(MESSAGE))); } } else { - return Ok(CheckResult::Warning(Context::new(MESSAGE).into())); + return Ok(CheckResult::Warning(anyhow!(MESSAGE))); } Ok(CheckResult::Ok) } } -#[derive(serde_derive::Deserialize, serde_derive::Serialize, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Clone)] struct DaemonConfig { #[serde(rename = "log-driver")] log_driver: Option, @@ -84,7 +84,7 @@ struct DaemonConfig { log_opts: Option, } -#[derive(serde_derive::Deserialize, serde_derive::Serialize, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Clone)] struct DaemonConfigLogOpts { #[serde(rename = "max-file")] max_file: Option, diff --git a/edgelet/iotedge/src/check/checks/container_local_time.rs b/edgelet/iotedge/src/check/checks/container_local_time.rs index bc82156c6c9..9a91e965b35 100644 --- a/edgelet/iotedge/src/check/checks/container_local_time.rs +++ b/edgelet/iotedge/src/check/checks/container_local_time.rs @@ -1,9 +1,10 @@ -use failure::{self, Context, ResultExt}; use std::time::Duration; +use anyhow::Context; + use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub(crate) struct ContainerLocalTime { expected_duration: Option, actual_duration: Option, @@ -27,7 +28,7 @@ impl Checker for ContainerLocalTime { } impl ContainerLocalTime { - async fn inner_execute(&mut self, check: &mut Check) -> Result { + async fn inner_execute(&mut self, check: &mut Check) -> anyhow::Result { let docker_host_arg = if let Some(docker_host_arg) = &check.docker_host_arg { docker_host_arg } else { @@ -62,7 +63,7 @@ impl ContainerLocalTime { .context("Could not query local time inside container")?; let output = std::str::from_utf8(&output) - .map_err(failure::Error::from) + .map_err(anyhow::Error::from) .and_then(|output| output.trim_end().parse::().map_err(Into::into)) .context("Could not parse container output")?; @@ -79,7 +80,9 @@ impl ContainerLocalTime { self.diff = Some(diff.as_secs()); if diff.as_secs() >= 10 { - return Err(Context::new("Detected time drift between host and container").into()); + return Err(anyhow::anyhow!( + "Detected time drift between host and container", + )); } Ok(CheckResult::Ok) diff --git a/edgelet/iotedge/src/check/checks/container_resolve_parent_hostname.rs b/edgelet/iotedge/src/check/checks/container_resolve_parent_hostname.rs index 06e6c06bd14..6c4f8a27173 100644 --- a/edgelet/iotedge/src/check/checks/container_resolve_parent_hostname.rs +++ b/edgelet/iotedge/src/check/checks/container_resolve_parent_hostname.rs @@ -1,10 +1,12 @@ use std::{net::IpAddr, str::FromStr}; -use crate::check::{Check, CheckResult, Checker, CheckerMeta}; +use anyhow::Context; + use edgelet_settings::RuntimeSettings; -use failure::ResultExt; -#[derive(Default, serde_derive::Serialize)] +use crate::check::{Check, CheckResult, Checker, CheckerMeta}; + +#[derive(Default, serde::Serialize)] pub(crate) struct ContainerResolveParentHostname {} #[async_trait::async_trait] @@ -25,7 +27,7 @@ impl Checker for ContainerResolveParentHostname { impl ContainerResolveParentHostname { #[allow(clippy::unused_self)] - async fn inner_execute(&mut self, check: &mut Check) -> Result { + async fn inner_execute(&mut self, check: &mut Check) -> anyhow::Result { let settings = if let Some(settings) = &check.settings { settings } else { diff --git a/edgelet/iotedge/src/check/checks/mod.rs b/edgelet/iotedge/src/check/checks/mod.rs index aa058e680bc..319dfe74283 100644 --- a/edgelet/iotedge/src/check/checks/mod.rs +++ b/edgelet/iotedge/src/check/checks/mod.rs @@ -32,14 +32,12 @@ pub(crate) use self::well_formed_config::WellFormedConfig; use std::ffi::OsStr; -use failure::{self, Context, Fail}; - use super::Checker; pub(crate) async fn docker( docker_host_arg: &str, args: I, -) -> Result, (Option, failure::Error)> +) -> Result, (Option, anyhow::Error)> where I: IntoIterator, ::Item: AsRef, @@ -53,17 +51,13 @@ where let output = process.output().await.map_err(|err| { ( None, - err.context(format!("could not run {:?}", process)).into(), + anyhow::Error::from(err).context(format!("could not run {:?}", process)), ) })?; if !output.status.success() { let stderr = String::from_utf8_lossy(&*output.stderr).into_owned(); - let err = Context::new(format!( - "docker returned {}, stderr = {}", - output.status, stderr, - )) - .into(); + let err = anyhow::anyhow!("docker returned {}, stderr = {}", output.status, stderr,); return Err((Some(stderr), err)); } diff --git a/edgelet/iotedge/src/check/checks/parent_hostname.rs b/edgelet/iotedge/src/check/checks/parent_hostname.rs index 018aef43a4b..7f97712dc2b 100644 --- a/edgelet/iotedge/src/check/checks/parent_hostname.rs +++ b/edgelet/iotedge/src/check/checks/parent_hostname.rs @@ -1,10 +1,10 @@ use std::{net::IpAddr, str::FromStr}; -use failure::{self, Context}; +use anyhow::anyhow; use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub(crate) struct ParentHostname { config_parent_hostname: Option, } @@ -26,7 +26,7 @@ impl Checker for ParentHostname { impl ParentHostname { #[allow(clippy::unnecessary_wraps)] - fn inner_execute(&mut self, check: &mut Check) -> Result { + fn inner_execute(&mut self, check: &mut Check) -> anyhow::Result { let config_parent_hostname = if let Some(config_parent_hostname) = check.parent_hostname.as_ref() { config_parent_hostname @@ -45,7 +45,7 @@ impl ParentHostname { // Some software like the IoT Hub SDKs for downstream clients require the device hostname to follow RFC 1035. // For example, the IoT Hub C# SDK cannot connect to a hostname that contains an `_`. if !aziotctl_common::is_rfc_1035_valid(config_parent_hostname) { - return Ok(CheckResult::Warning(Context::new(format!( + return Ok(CheckResult::Warning(anyhow!( "configuration has parent_hostname {} which does not comply with RFC 1035.\n\ \n\ - Hostname must be between 1 and 255 octets inclusive.\n\ @@ -55,18 +55,14 @@ impl ParentHostname { \n\ Not complying with RFC 1035 may cause errors during the TLS handshake with modules and downstream devices.", config_parent_hostname, - )) - .into())); + ))); } if !aziotctl_common::check_length_for_local_issuer(config_parent_hostname) { - return Ok(CheckResult::Failed( - Context::new(format!( - "configuration parent_hostname {} is too long to be used as a certificate issuer", - config_parent_hostname, - )) - .into(), - )); + return Ok(CheckResult::Failed(anyhow!( + "configuration parent_hostname {} is too long to be used as a certificate issuer", + config_parent_hostname, + ))); } Ok(CheckResult::Ok) diff --git a/edgelet/iotedge/src/check/checks/proxy_settings.rs b/edgelet/iotedge/src/check/checks/proxy_settings.rs index b1946c58fd4..4df65b2de88 100644 --- a/edgelet/iotedge/src/check/checks/proxy_settings.rs +++ b/edgelet/iotedge/src/check/checks/proxy_settings.rs @@ -1,8 +1,6 @@ -use failure::{self, Context}; - use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub(crate) struct ProxySettings {} #[async_trait::async_trait] @@ -23,7 +21,7 @@ impl Checker for ProxySettings { } impl ProxySettings { - async fn inner_execute(&mut self, check: &mut Check) -> Result { + async fn inner_execute(&mut self, check: &mut Check) -> anyhow::Result { let settings = if let Some(settings) = &mut check.settings { settings } else { @@ -59,15 +57,14 @@ impl ProxySettings { { Ok(CheckResult::Ok) } else { - return Ok(CheckResult::Warning(Context::new( - format!( + return Ok(CheckResult::Warning(anyhow::anyhow!( "The proxy setting for IoT Edge Agent {:?}, IoT Edge Daemon {:?}, IoT Identity Daemon {:?}, and Moby {:?} may need to be identical.", edge_agent_proxy_uri, edge_daemon_proxy_uri, identity_daemon_proxy_uri, moby_proxy_uri ) - ).into())); + )); } } } diff --git a/edgelet/iotedge/src/check/checks/storage_mounted_from_host.rs b/edgelet/iotedge/src/check/checks/storage_mounted_from_host.rs index 872b90eed84..ecab300e852 100644 --- a/edgelet/iotedge/src/check/checks/storage_mounted_from_host.rs +++ b/edgelet/iotedge/src/check/checks/storage_mounted_from_host.rs @@ -2,12 +2,12 @@ use std::path::{Path, PathBuf}; -use failure::{self, Context, ResultExt}; +use anyhow::Context; use regex::Regex; use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub(crate) struct EdgeAgentStorageMounted { storage_directory: Option, container_directories: Option>, @@ -35,7 +35,7 @@ impl Checker for EdgeAgentStorageMounted { } } -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub struct EdgeHubStorageMounted { storage_directory: Option, container_directories: Option>, @@ -69,7 +69,7 @@ async fn storage_mounted_from_host<'a>( storage_directory_name: &'static str, storage_directory_out: &'a mut Option, container_directories_out: &'a mut Option>, -) -> Result { +) -> anyhow::Result { lazy_static::lazy_static! { static ref STORAGE_FOLDER_ENV_VAR_KEY_REGEX: Regex = Regex::new("(?i)^storagefolder=(.*)") @@ -124,15 +124,13 @@ async fn storage_mounted_from_host<'a>( .into_iter() .any(|container_directory| storage_directory.starts_with(container_directory)) { - return Ok(CheckResult::Warning( - Context::new(format!( - "The {} module is not configured to persist its {} directory on the host filesystem.\n\ + return Ok(CheckResult::Warning(anyhow::Error::msg(format!( + "The {} module is not configured to persist its {} directory on the host filesystem.\n\ Data might be lost if the module is deleted or updated.\n\ Please see https://aka.ms/iotedge-storage-host for best practices.", - container_name, - storage_directory.display(), - )).into(), - )); + container_name, + storage_directory.display(), + )))); } Ok(CheckResult::Ok) @@ -141,8 +139,8 @@ async fn storage_mounted_from_host<'a>( async fn inspect_container( docker_host_arg: &str, name: &str, -) -> Result { - Ok(super::docker(docker_host_arg, &["inspect", name]) +) -> anyhow::Result { + super::docker(docker_host_arg, &["inspect", name]) .await .map_err(|(_, err)| err) .and_then(|output| { @@ -151,5 +149,5 @@ async fn inspect_container( .context("Could not parse result of docker inspect")?; Ok(inspect_result) }) - .with_context(|_| format!("Could not check current state of {} container", name))?) + .with_context(|| format!("Could not check current state of {} container", name)) } diff --git a/edgelet/iotedge/src/check/checks/up_to_date_config.rs b/edgelet/iotedge/src/check/checks/up_to_date_config.rs index 551fc8c900a..3e69b627f3f 100644 --- a/edgelet/iotedge/src/check/checks/up_to_date_config.rs +++ b/edgelet/iotedge/src/check/checks/up_to_date_config.rs @@ -1,10 +1,8 @@ -use failure::{self, Context}; - use aziotctl_common::check_last_modified::{check_last_modified, LastModifiedError}; use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub(crate) struct UpToDateConfig {} #[async_trait::async_trait] @@ -23,12 +21,12 @@ impl Checker for UpToDateConfig { impl UpToDateConfig { #[allow(clippy::unnecessary_wraps)] - fn inner_execute(_check: &mut Check) -> Result { + fn inner_execute(_check: &mut Check) -> anyhow::Result { let check_result = match check_last_modified(&["edged"]) { Ok(()) => CheckResult::Ok, Err(LastModifiedError::Ignored) => CheckResult::Ignored, Err(LastModifiedError::Warning(message)) => { - CheckResult::Warning(Context::new(message).into()) + CheckResult::Warning(anyhow::anyhow!(message)) } Err(LastModifiedError::Failed(error)) => CheckResult::Failed(error.into()), }; diff --git a/edgelet/iotedge/src/check/checks/well_formed_config.rs b/edgelet/iotedge/src/check/checks/well_formed_config.rs index b1b9cbf4803..0cd3d47d4fc 100644 --- a/edgelet/iotedge/src/check/checks/well_formed_config.rs +++ b/edgelet/iotedge/src/check/checks/well_formed_config.rs @@ -1,13 +1,11 @@ use std::fs::File; -use failure::{self, Fail}; - use edgelet_settings::{Settings, CONFIG_FILE_DEFAULT, UPSTREAM_PARENT_KEYWORD}; use crate::check::{Check, CheckResult, Checker, CheckerMeta}; -use crate::{Error, ErrorKind}; +use crate::error::Error; -#[derive(Default, serde_derive::Serialize)] +#[derive(Default, serde::Serialize)] pub(crate) struct WellFormedConfig {} #[async_trait::async_trait] @@ -25,7 +23,7 @@ impl Checker for WellFormedConfig { } impl WellFormedConfig { - fn inner_execute(check: &mut Check) -> Result { + fn inner_execute(check: &mut Check) -> anyhow::Result { // The config crate just returns a "file not found" error when it can't open the file for any reason, // even if the real error was a permissions issue. // @@ -35,13 +33,11 @@ impl WellFormedConfig { if let Err(err) = File::open(CONFIG_FILE_DEFAULT) { if err.kind() == std::io::ErrorKind::PermissionDenied { return Ok(CheckResult::Fatal( - err.context("Could not open IoT Edge configuration. You might need to run this command as root.") - .into(), + anyhow::Error::from(err).context("Could not open IoT Edge configuration. You might need to run this command as root."), )); } else if err.kind() != std::io::ErrorKind::NotFound { - return Err(err - .context(format!("Could not open file {}", CONFIG_FILE_DEFAULT)) - .into()); + return Err(anyhow::Error::from(err) + .context(format!("Could not open file {}", CONFIG_FILE_DEFAULT))); } } @@ -57,7 +53,7 @@ impl WellFormedConfig { } else { "The IoT Edge daemon's configuration file is not well-formed.".to_string() }; - return Err(Error::from(ErrorKind::Check(message)).into()); + return Err(Error::Check(message).into()); } }; diff --git a/edgelet/iotedge/src/check/mod.rs b/edgelet/iotedge/src/check/mod.rs index 5b5e7df04ba..1b93d5522bc 100644 --- a/edgelet/iotedge/src/check/mod.rs +++ b/edgelet/iotedge/src/check/mod.rs @@ -3,11 +3,9 @@ use std::collections::{BTreeMap, BTreeSet}; use std::io::Write; use std::path::PathBuf; - use std::process::Command; -use failure::Fail; -use failure::{self, ResultExt}; +use anyhow::Context; use edgelet_settings::{RuntimeSettings, Settings}; @@ -16,7 +14,7 @@ use aziotctl_common::{ CheckResultsSerializable, CheckerMetaSerializable, }; -use crate::error::{Error, ErrorKind}; +use crate::error::Error; mod additional_info; use self::additional_info::AdditionalInfo; @@ -104,7 +102,7 @@ impl Check { } } - pub async fn print_list(aziot_bin: &str) -> Result<(), Error> { + pub async fn print_list(aziot_bin: &str) -> anyhow::Result<()> { let mut all_checks: Vec<(String, Vec)> = Vec::new(); // get all the aziot checks by shelling-out to aziot @@ -118,7 +116,7 @@ impl Check { match aziot_check_out { Ok(out) => { let aziot_checks: BTreeMap> = - serde_json::from_slice(&out.stdout).context(ErrorKind::Aziot)?; + serde_json::from_slice(&out.stdout).context(Error::Aziot)?; all_checks.extend(aziot_checks.into_iter().map(|(section_name, checks)| { (section_name + " (aziot-identity-service)", checks) @@ -213,7 +211,7 @@ impl Check { } } - pub async fn execute(&mut self) -> Result<(), Error> { + pub async fn execute(&mut self) -> anyhow::Result<()> { // heterogeneous type representing the output of a check, regardless of // whether or not it is built-in, or parsed from `aziot check` #[derive(Debug)] @@ -234,208 +232,206 @@ impl Check { let mut num_fatal = 0_usize; let mut num_errors = 0_usize; - let mut output_check = |check: CheckOutput, - verbose: bool, - warnings_as_errors: bool| - -> Result { - if num_fatal > 0 { - return Ok(true); - } + let mut output_check = + |check: CheckOutput, verbose: bool, warnings_as_errors: bool| -> anyhow::Result { + if num_fatal > 0 { + return Ok(true); + } - let CheckOutput { - id: check_id, - description: check_name, - result: check_result, - additional_info, - .. - } = check; - - match check_result { - CheckResult::Ok => { - num_successful += 1; - - checks.insert( - check_id, - CheckOutputSerializable { - result: CheckResultSerializable::Ok, - additional_info, - }, - ); + let CheckOutput { + id: check_id, + description: check_name, + result: check_result, + additional_info, + .. + } = check; + + match check_result { + CheckResult::Ok => { + num_successful += 1; + + checks.insert( + check_id, + CheckOutputSerializable { + result: CheckResultSerializable::Ok, + additional_info, + }, + ); - stdout.write_success(|stdout| { - writeln!(stdout, "\u{221a} {} - OK", check_name)?; - Ok(()) - }); - } + stdout.write_success(|stdout| { + writeln!(stdout, "\u{221a} {} - OK", check_name)?; + Ok(()) + }); + } - CheckResult::Warning(ref warning) if !warnings_as_errors => { - num_warnings += 1; + CheckResult::Warning(ref warning) if !warnings_as_errors => { + num_warnings += 1; - checks.insert( - check_id, - CheckOutputSerializable { - result: CheckResultSerializable::Warning { - details: warning.iter_chain().map(ToString::to_string).collect(), + checks.insert( + check_id, + CheckOutputSerializable { + result: CheckResultSerializable::Warning { + details: warning.chain().map(ToString::to_string).collect(), + }, + additional_info, }, - additional_info, - }, - ); + ); - stdout.write_warning(|stdout| { - writeln!(stdout, "\u{203c} {} - Warning", check_name)?; + stdout.write_warning(|stdout| { + writeln!(stdout, "\u{203c} {} - Warning", check_name)?; - let message = warning.to_string(); + let message = warning.to_string(); - write_lines(stdout, " ", " ", message.lines())?; + write_lines(stdout, " ", " ", message.lines())?; - if verbose { - for cause in warning.iter_causes() { - write_lines( - stdout, - " caused by: ", - " ", - cause.to_string().lines(), - )?; + if verbose { + for cause in warning.chain() { + write_lines( + stdout, + " caused by: ", + " ", + cause.to_string().lines(), + )?; + } } - } - Ok(()) - }); - } + Ok(()) + }); + } - CheckResult::Ignored => { - checks.insert( - check_id, - CheckOutputSerializable { - result: CheckResultSerializable::Ignored, - additional_info, - }, - ); - } + CheckResult::Ignored => { + checks.insert( + check_id, + CheckOutputSerializable { + result: CheckResultSerializable::Ignored, + additional_info, + }, + ); + } - CheckResult::Skipped => { - num_skipped += 1; + CheckResult::Skipped => { + num_skipped += 1; - checks.insert( - check_id, - CheckOutputSerializable { - result: CheckResultSerializable::Skipped, - additional_info, - }, - ); + checks.insert( + check_id, + CheckOutputSerializable { + result: CheckResultSerializable::Skipped, + additional_info, + }, + ); - if verbose { - stdout.write_warning(|stdout| { - writeln!(stdout, "\u{203c} {} - Warning", check_name)?; - writeln!(stdout, " skipping because of previous failures")?; - Ok(()) - }); + if verbose { + stdout.write_warning(|stdout| { + writeln!(stdout, "\u{203c} {} - Warning", check_name)?; + writeln!(stdout, " skipping because of previous failures")?; + Ok(()) + }); + } } - } - CheckResult::SkippedDueTo(reason) => { - num_skipped += 1; + CheckResult::SkippedDueTo(reason) => { + num_skipped += 1; - checks.insert( - check_id, - CheckOutputSerializable { - result: CheckResultSerializable::Skipped, - additional_info, - }, - ); + checks.insert( + check_id, + CheckOutputSerializable { + result: CheckResultSerializable::Skipped, + additional_info, + }, + ); - if verbose { - stdout.write_success(|stdout| { - writeln!(stdout, "\u{221a} {} - OK", check_name)?; - writeln!(stdout, " skipping because of {}", reason)?; - Ok(()) - }); + if verbose { + stdout.write_success(|stdout| { + writeln!(stdout, "\u{221a} {} - OK", check_name)?; + writeln!(stdout, " skipping because of {}", reason)?; + Ok(()) + }); + } } - } - CheckResult::Fatal(err) => { - num_fatal += 1; + CheckResult::Fatal(err) => { + num_fatal += 1; - checks.insert( - check_id, - CheckOutputSerializable { - result: CheckResultSerializable::Fatal { - details: err.iter_chain().map(ToString::to_string).collect(), + checks.insert( + check_id, + CheckOutputSerializable { + result: CheckResultSerializable::Fatal { + details: err.chain().map(ToString::to_string).collect(), + }, + additional_info, }, - additional_info, - }, - ); + ); - stdout.write_error(|stdout| { - writeln!(stdout, "\u{00d7} {} - Error", check_name)?; + stdout.write_error(|stdout| { + writeln!(stdout, "\u{00d7} {} - Error", check_name)?; - let message = err.to_string(); + let message = err.to_string(); - write_lines(stdout, " ", " ", message.lines())?; + write_lines(stdout, " ", " ", message.lines())?; - if verbose { - for cause in err.iter_causes() { - write_lines( - stdout, - " caused by: ", - " ", - cause.to_string().lines(), - )?; + if verbose { + for cause in err.chain() { + write_lines( + stdout, + " caused by: ", + " ", + cause.to_string().lines(), + )?; + } } - } - Ok(()) - }); - } + Ok(()) + }); + } - CheckResult::Warning(err) | CheckResult::Failed(err) => { - num_errors += 1; + CheckResult::Warning(err) | CheckResult::Failed(err) => { + num_errors += 1; - checks.insert( - check_id, - CheckOutputSerializable { - result: CheckResultSerializable::Error { - details: err.iter_chain().map(ToString::to_string).collect(), + checks.insert( + check_id, + CheckOutputSerializable { + result: CheckResultSerializable::Error { + details: err.chain().map(ToString::to_string).collect(), + }, + additional_info, }, - additional_info, - }, - ); + ); - stdout.write_error(|stdout| { - writeln!(stdout, "\u{00d7} {} - Error", check_name)?; + stdout.write_error(|stdout| { + writeln!(stdout, "\u{00d7} {} - Error", check_name)?; - let message = err.to_string(); + let message = err.to_string(); - write_lines(stdout, " ", " ", message.lines())?; + write_lines(stdout, " ", " ", message.lines())?; - if verbose { - for cause in err.iter_causes() { - write_lines( - stdout, - " caused by: ", - " ", - cause.to_string().lines(), - )?; + if verbose { + for cause in err.chain() { + write_lines( + stdout, + " caused by: ", + " ", + cause.to_string().lines(), + )?; + } } - } - Ok(()) - }); + Ok(()) + }); + } } - } - Ok(false) - }; + Ok(false) + }; // run the aziot checks first, as certain bits of `additional_info` from // aziot are required to run iotedge checks. e.g: the "iothub_hostname". { fn to_check_result(res: CheckResultSerializable) -> CheckResult { - fn vec_to_err(mut v: Vec) -> failure::Error { + fn vec_to_err(mut v: Vec) -> anyhow::Error { let mut err = - failure::err_msg(v.pop().expect("errors always have at least one source")); + anyhow::anyhow!(v.pop().expect("errors always have at least one source"),); while let Some(s) = v.pop() { - err = err.context(s).into(); + err = err.context(s); } err } @@ -486,7 +482,7 @@ impl Check { for val in serde_json::Deserializer::from_reader(child.stdout.unwrap()).into_iter() { - let val = val.context(ErrorKind::Aziot)?; + let val = val.context(Error::Aziot)?; match val { CheckOutputSerializableStreaming::Section { name } => { self.output_section(&format!("{} (aziot-identity-service)", name)); @@ -539,7 +535,7 @@ impl Check { "aziot-identity-service checks unavailable - could not communicate with '{}' binary.", &self.aziot_bin.to_str().expect("aziot_bin should be valid UTF-8") ), - result: CheckResult::Failed(err.context(ErrorKind::Aziot).into()), + result: CheckResult::Failed(anyhow::Error::from(err).context(Error::Aziot)), additional_info: serde_json::Value::Null, }, self.verbose, @@ -619,7 +615,7 @@ impl Check { } let result = if num_fatal + num_errors > 0 { - Err(ErrorKind::Diagnostics.into()) + Err(Error::Diagnostics.into()) } else { Ok(()) }; @@ -632,7 +628,7 @@ impl Check { if let Err(err) = serde_json::to_writer(std::io::stdout(), &check_results) { eprintln!("Could not write JSON output: {}", err,); - return Err(ErrorKind::Diagnostics.into()); + return Err(Error::Diagnostics.into()); } println!(); diff --git a/edgelet/iotedge/src/check/shared.rs b/edgelet/iotedge/src/check/shared.rs index 188c5d0dc77..868a2321741 100644 --- a/edgelet/iotedge/src/check/shared.rs +++ b/edgelet/iotedge/src/check/shared.rs @@ -26,7 +26,7 @@ pub enum CheckResult { Ok, /// Check failed with a warning. - Warning(failure::Error), + Warning(anyhow::Error), /// Check is not applicable and was ignored. Should be treated as success. Ignored, @@ -38,8 +38,8 @@ pub enum CheckResult { SkippedDueTo(String), /// Check failed, and further checks should be performed. - Failed(failure::Error), + Failed(anyhow::Error), /// Check failed, and further checks should not be performed. - Fatal(failure::Error), + Fatal(anyhow::Error), } diff --git a/edgelet/iotedge/src/check/upstream_protocol_port.rs b/edgelet/iotedge/src/check/upstream_protocol_port.rs index 2958f58ac40..6db405eec93 100644 --- a/edgelet/iotedge/src/check/upstream_protocol_port.rs +++ b/edgelet/iotedge/src/check/upstream_protocol_port.rs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, serde_derive::Serialize)] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize)] pub(super) enum UpstreamProtocolPort { Amqp, Https, diff --git a/edgelet/iotedge/src/client.rs b/edgelet/iotedge/src/client.rs index ec321721363..65c9a76316b 100644 --- a/edgelet/iotedge/src/client.rs +++ b/edgelet/iotedge/src/client.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use failure::ResultExt; +use anyhow::Context; use hyper::Uri; use url::Url; @@ -12,8 +12,7 @@ use edgelet_http::{ListModulesResponse, ModuleDetails}; use edgelet_settings::module::Settings as ModuleSpec; use http_common::{Connector, ErrorBody, HttpRequest}; -use crate::{Error, ErrorKind}; -type Result = std::result::Result; +use crate::error::Error; const API_VERSION: &str = "2020-07-07"; @@ -31,25 +30,24 @@ pub struct MgmtClient { } impl MgmtClient { - pub fn new(url: &Url) -> Result { - let connector = - Connector::new(url).map_err(|e| Error::from(ErrorKind::Misc(e.to_string())))?; + pub fn new(url: &Url) -> anyhow::Result { + let connector = Connector::new(url).map_err(|e| Error::Misc(e.to_string()))?; let base_path = url .to_base_path() - .context(ErrorKind::ModuleRuntime)? + .context(Error::ModuleRuntime)? .to_str() - .ok_or(ErrorKind::ModuleRuntime)? + .ok_or(Error::ModuleRuntime)? .to_string(); let host = hex::encode(base_path.as_bytes()); Ok(Self { connector, host }) } - fn get_uri(&self, path: &str) -> Result { + fn get_uri(&self, path: &str) -> anyhow::Result { let host_str = format!("unix://{}:0{}", self.host, path); let uri: std::result::Result = host_str.parse(); - let uri = uri.context(ErrorKind::ModuleRuntime)?; + let uri = uri.context(Error::ModuleRuntime)?; let uri = uri.to_string(); Ok(uri) @@ -58,12 +56,11 @@ impl MgmtClient { #[async_trait::async_trait] impl ModuleRuntime for MgmtClient { - type Error = Error; type Config = MgmtConfig; type Module = MgmtModule; type ModuleRegistry = Self; - async fn restart(&self, id: &str) -> Result<()> { + async fn restart(&self, id: &str) -> anyhow::Result<()> { let path = format!("/modules/{}/restart?api-version={}", id, API_VERSION); let uri = self.get_uri(&path)?; @@ -72,12 +69,12 @@ impl ModuleRuntime for MgmtClient { request .no_content_response() .await - .context(ErrorKind::ModuleRuntime)?; + .context(Error::ModuleRuntime)?; Ok(()) } - async fn list(&self) -> Result> { + async fn list(&self) -> anyhow::Result> { let path = format!("/modules?api-version={}", API_VERSION); let uri = self.get_uri(&path)?; @@ -86,16 +83,16 @@ impl ModuleRuntime for MgmtClient { let response = request .json_response() .await - .context(ErrorKind::ModuleRuntime)?; + .context(Error::ModuleRuntime)?; let response = response .parse_expect_ok::>() - .context(ErrorKind::ModuleRuntime)?; + .context(Error::ModuleRuntime)?; let modules = response.modules.into_iter().map(MgmtModule::new).collect(); Ok(modules) } - async fn list_with_details(&self) -> Result> { + async fn list_with_details(&self) -> anyhow::Result> { let modules = self .list() .await? @@ -106,7 +103,7 @@ impl ModuleRuntime for MgmtClient { Ok(modules) } - async fn logs(&self, id: &str, options: &LogOptions) -> Result { + async fn logs(&self, id: &str, options: &LogOptions) -> anyhow::Result { let uri = { let mut query = ::url::form_urlencoded::Serializer::new(String::new()); query @@ -129,50 +126,44 @@ impl ModuleRuntime for MgmtClient { .body(hyper::Body::empty()) .expect("could not build hyper::Request"); let client = self.connector.clone().into_client(); - let resp = client - .request(req) - .await - .context(ErrorKind::ModuleRuntime)?; + let resp = client.request(req).await.context(Error::ModuleRuntime)?; let (hyper::http::response::Parts { status, .. }, body) = resp.into_parts(); if status.is_success() { Ok(body) } else { - Err(Error::from(ErrorKind::Misc(format!( - "Bad status code when calling logs: {}", - status - )))) + Err(Error::Misc(format!("Bad status code when calling logs: {}", status)).into()) } } - async fn create(&self, _module: ModuleSpec) -> Result<()> { + async fn create(&self, _module: ModuleSpec) -> anyhow::Result<()> { unimplemented!() } - async fn get(&self, _id: &str) -> Result<(Self::Module, ModuleRuntimeState)> { + async fn get(&self, _id: &str) -> anyhow::Result<(Self::Module, ModuleRuntimeState)> { unimplemented!() } - async fn start(&self, _id: &str) -> Result<()> { + async fn start(&self, _id: &str) -> anyhow::Result<()> { unimplemented!() } - async fn stop(&self, _id: &str, _wait_before_kill: Option) -> Result<()> { + async fn stop(&self, _id: &str, _wait_before_kill: Option) -> anyhow::Result<()> { unimplemented!() } - async fn remove(&self, _id: &str) -> Result<()> { + async fn remove(&self, _id: &str) -> anyhow::Result<()> { unimplemented!() } - async fn system_info(&self) -> Result { + async fn system_info(&self) -> anyhow::Result { unimplemented!() } - async fn system_resources(&self) -> Result { + async fn system_resources(&self) -> anyhow::Result { unimplemented!() } - async fn remove_all(&self) -> Result<()> { + async fn remove_all(&self) -> anyhow::Result<()> { unimplemented!() } - async fn stop_all(&self, _wait_before_kill: Option) -> Result<()> { + async fn stop_all(&self, _wait_before_kill: Option) -> anyhow::Result<()> { unimplemented!() } - async fn module_top(&self, _id: &str) -> Result> { + async fn module_top(&self, _id: &str) -> anyhow::Result> { unimplemented!() } @@ -184,7 +175,6 @@ impl ModuleRuntime for MgmtClient { #[async_trait::async_trait] impl Module for MgmtModule { type Config = MgmtConfig; - type Error = Error; fn name(&self) -> &str { &self.details.name @@ -196,21 +186,20 @@ impl Module for MgmtModule { &MgmtConfig {} } - async fn runtime_state(&self) -> Result { + async fn runtime_state(&self) -> anyhow::Result { unimplemented!(); } } #[async_trait::async_trait] impl ModuleRegistry for MgmtClient { - type Error = Error; type Config = MgmtConfig; - async fn pull(&self, _config: &Self::Config) -> Result<()> { + async fn pull(&self, _config: &Self::Config) -> anyhow::Result<()> { Ok(()) } - async fn remove(&self, _name: &str) -> Result<()> { + async fn remove(&self, _name: &str) -> anyhow::Result<()> { Ok(()) } } diff --git a/edgelet/iotedge/src/config/import/old_config/agent.rs b/edgelet/iotedge/src/config/import/old_config/agent.rs index cbd5fe8f0dc..4b67e6d6585 100644 --- a/edgelet/iotedge/src/config/import/old_config/agent.rs +++ b/edgelet/iotedge/src/config/import/old_config/agent.rs @@ -4,7 +4,7 @@ use std::collections::BTreeMap; use docker::models::{AuthConfig, ContainerCreateBody}; -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] pub(crate) struct ModuleSpec { pub(crate) name: String, @@ -21,7 +21,7 @@ pub(crate) struct ModuleSpec { pub(crate) image_pull_policy: ImagePullPolicy, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(rename_all = "camelCase")] pub(crate) struct DockerConfig { pub(crate) image: String, @@ -40,7 +40,7 @@ pub(crate) struct DockerConfig { pub(crate) auth: Option, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(rename_all = "lowercase")] pub(crate) enum ImagePullPolicy { #[serde(rename = "on-create")] diff --git a/edgelet/iotedge/src/config/import/old_config/moby_runtime.rs b/edgelet/iotedge/src/config/import/old_config/moby_runtime.rs index 9f8afbb50ca..76507eec992 100644 --- a/edgelet/iotedge/src/config/import/old_config/moby_runtime.rs +++ b/edgelet/iotedge/src/config/import/old_config/moby_runtime.rs @@ -5,21 +5,21 @@ use std::path::PathBuf; use url::Url; -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] pub(crate) struct MobyRuntime { pub(crate) uri: Url, pub(crate) network: MobyNetwork, pub(crate) content_trust: Option, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(untagged)] pub(crate) enum MobyNetwork { Network(Network), Name(String), } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] pub(crate) struct Network { pub(crate) name: String, @@ -30,13 +30,13 @@ pub(crate) struct Network { pub(crate) ipam: Option, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] pub(crate) struct Ipam { #[serde(rename = "config", skip_serializing_if = "Option::is_none")] pub(crate) config: Option>, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] pub(crate) struct IpamConfig { #[serde(rename = "gateway", skip_serializing_if = "Option::is_none")] pub(crate) gateway: Option, @@ -48,7 +48,7 @@ pub(crate) struct IpamConfig { pub(crate) ip_range: Option, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] pub(crate) struct ContentTrust { pub(crate) ca_certs: Option>, } diff --git a/edgelet/iotedge/src/config/import/old_config/mod.rs b/edgelet/iotedge/src/config/import/old_config/mod.rs index 994612e4865..fed5dbecc99 100644 --- a/edgelet/iotedge/src/config/import/old_config/mod.rs +++ b/edgelet/iotedge/src/config/import/old_config/mod.rs @@ -13,7 +13,7 @@ use std::path::PathBuf; use url::Url; -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[allow(dead_code)] pub(crate) struct Config { pub(crate) provisioning: Provisioning, @@ -36,13 +36,13 @@ pub(crate) struct Config { pub(crate) moby_runtime: MobyRuntime, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] pub(crate) struct Connect { pub(crate) management_uri: Url, pub(crate) workload_uri: Url, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] pub(crate) struct Listen { pub(crate) management_uri: Url, pub(crate) workload_uri: Url, @@ -99,7 +99,7 @@ impl<'de> serde::Deserialize<'de> for Protocol { } } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] pub(crate) struct Certificates { #[serde(flatten)] pub(crate) device_cert: Option, @@ -108,7 +108,7 @@ pub(crate) struct Certificates { pub(crate) auto_generated_ca_lifetime_days: u16, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] pub(crate) struct DeviceCertificate { #[serde(deserialize_with = "deserialize_file_uri_or_path")] pub(crate) device_ca_cert: Url, @@ -161,13 +161,13 @@ where deserializer.deserialize_str(Visitor) } -#[derive(Debug, Default, serde_derive::Deserialize)] +#[derive(Debug, Default, serde::Deserialize)] pub(crate) struct WatchdogSettings { #[serde(default)] pub(crate) max_retries: RetryLimit, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(untagged)] pub(crate) enum RetryLimit { Infinite, diff --git a/edgelet/iotedge/src/config/import/old_config/provisioning.rs b/edgelet/iotedge/src/config/import/old_config/provisioning.rs index 146f7cddcf9..6c53a1f51ec 100644 --- a/edgelet/iotedge/src/config/import/old_config/provisioning.rs +++ b/edgelet/iotedge/src/config/import/old_config/provisioning.rs @@ -3,7 +3,7 @@ use regex::Regex; use url::Url; -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(rename_all = "lowercase")] pub(crate) struct Provisioning { #[serde(flatten)] @@ -13,7 +13,7 @@ pub(crate) struct Provisioning { pub(crate) dynamic_reprovisioning: bool, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(rename_all = "lowercase", tag = "source")] pub(crate) enum ProvisioningType { Manual(Manual), @@ -31,7 +31,7 @@ impl<'de> serde::Deserialize<'de> for Manual { where D: serde::Deserializer<'de>, { - #[derive(Debug, serde_derive::Deserialize)] + #[derive(Debug, serde::Deserialize)] struct Inner { #[serde(skip_serializing_if = "Option::is_none")] device_connection_string: Option, @@ -63,7 +63,7 @@ impl<'de> serde::Deserialize<'de> for Manual { Ok(Manual { authentication }) } } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(rename_all = "lowercase", tag = "method")] pub(crate) enum ManualAuthMethod { #[serde(rename = "device_connection_string")] @@ -154,7 +154,7 @@ impl<'de> serde::Deserialize<'de> for ManualDeviceConnectionString { where D: serde::Deserializer<'de>, { - #[derive(Debug, serde_derive::Deserialize)] + #[derive(Debug, serde::Deserialize)] #[serde(rename_all = "lowercase")] struct Inner { device_connection_string: String, @@ -170,7 +170,7 @@ impl<'de> serde::Deserialize<'de> for ManualDeviceConnectionString { } } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(rename_all = "lowercase")] pub(crate) struct ManualX509Auth { pub(crate) iothub_hostname: String, @@ -192,7 +192,7 @@ impl<'de> serde::Deserialize<'de> for Dps { where D: serde::Deserializer<'de>, { - #[derive(Debug, serde_derive::Deserialize)] + #[derive(Debug, serde::Deserialize)] struct Inner { global_endpoint: Url, scope_id: String, @@ -230,7 +230,7 @@ impl<'de> serde::Deserialize<'de> for Dps { } } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(rename_all = "lowercase", tag = "method")] pub(crate) enum AttestationMethod { Tpm(TpmAttestationInfo), @@ -241,13 +241,13 @@ pub(crate) enum AttestationMethod { X509(X509AttestationInfo), } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(rename_all = "lowercase")] pub(crate) struct TpmAttestationInfo { pub(crate) registration_id: String, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(rename_all = "lowercase")] pub(crate) struct SymmetricKeyAttestationInfo { pub(crate) registration_id: String, @@ -255,7 +255,7 @@ pub(crate) struct SymmetricKeyAttestationInfo { pub(crate) symmetric_key: Vec, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(rename_all = "lowercase")] pub(crate) struct X509AttestationInfo { #[serde(skip_serializing_if = "Option::is_none")] @@ -266,7 +266,7 @@ pub(crate) struct X509AttestationInfo { pub(crate) identity_pk: Url, } -#[derive(Debug, serde_derive::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(rename_all = "lowercase")] #[allow(dead_code)] pub(crate) struct External { diff --git a/edgelet/iotedge/src/config/super_config.rs b/edgelet/iotedge/src/config/super_config.rs index b64edec55d0..f2fdd4f3657 100644 --- a/edgelet/iotedge/src/config/super_config.rs +++ b/edgelet/iotedge/src/config/super_config.rs @@ -75,7 +75,7 @@ pub fn default_agent() -> edgelet_settings::ModuleSpec>, } diff --git a/edgelet/iotedge/src/error.rs b/edgelet/iotedge/src/error.rs index 115a75b3759..41d2e01882a 100644 --- a/edgelet/iotedge/src/error.rs +++ b/edgelet/iotedge/src/error.rs @@ -1,105 +1,64 @@ // Copyright (c) Microsoft. All rights reserved. use std::fmt; -use std::fmt::Display; -use failure::{Backtrace, Context, Fail}; - -#[derive(Debug)] -pub struct Error { - inner: Context, -} - -#[derive(Clone, Debug, Fail)] -pub enum ErrorKind { - #[fail(display = "Invalid value for --host parameter")] +#[derive(Clone, Debug, thiserror::Error)] +pub enum Error { + #[error("Invalid value for --host parameter")] BadHostParameter, - #[fail(display = "Invalid value for --since parameter")] + #[error("Invalid value for --since parameter")] BadSinceParameter, - #[fail(display = "Invalid value for --tail parameter")] + #[error("Invalid value for --tail parameter")] BadTailParameter, - #[fail(display = "")] + #[error("")] Diagnostics, - #[fail( - display = "Error while fetching latest versions of edge components: {}", - _0 - )] + #[error("Error while fetching latest versions of edge components: {0}")] FetchLatestVersions(FetchLatestVersionsReason), - #[fail(display = "Command failed: {}", _0)] + #[error("Command failed: {0}")] Config(std::borrow::Cow<'static, str>), - #[fail(display = "Could not initialize tokio runtime")] + #[error("Could not initialize tokio runtime")] InitializeTokio, - #[fail(display = "Missing --host parameter")] + #[error("Missing --host parameter")] MissingHostParameter, - #[fail(display = "A module runtime error occurred")] + #[error("A module runtime error occurred")] ModuleRuntime, - #[fail(display = "Could not generate support bundle")] + #[error("Could not generate support bundle")] SupportBundle, - #[fail(display = "Could not write to stdout")] + #[error("Could not write to stdout")] WriteToStdout, - #[fail(display = "Could not write to file")] + #[error("Could not write to file")] WriteToFile, - #[fail(display = "Unable to bundle iotedge check")] + #[error("Unable to bundle iotedge check")] BundleCheck, - #[fail(display = "Unable to call docker {}", _0)] + #[error("Unable to call docker: {0}")] Docker(String), - #[fail(display = "Error communicating with 'aziotctl' binary")] + #[error("Error communicating with 'aziotctl' binary")] Aziot, - #[fail(display = "Error running system command")] + #[error("Error running system command")] System, - #[fail(display = "Error running check: {}", _0)] + #[error("Error running check: {0}")] Check(String), - #[fail(display = "{}", _0)] + #[error("{0}")] Misc(String), } -impl Fail for Error { - fn cause(&self) -> Option<&dyn Fail> { - self.inner.cause() - } - - fn backtrace(&self) -> Option<&Backtrace> { - self.inner.backtrace() - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.inner, f) - } -} - -impl From for Error { - fn from(kind: ErrorKind) -> Self { - Error { - inner: Context::new(kind), - } - } -} - -impl From> for Error { - fn from(inner: Context) -> Self { - Error { inner } - } -} - #[derive(Clone, Copy, Debug)] pub enum FetchLatestVersionsReason { CreateClient, @@ -108,7 +67,7 @@ pub enum FetchLatestVersionsReason { ResponseStatusCode(hyper::StatusCode), } -impl Display for FetchLatestVersionsReason { +impl fmt::Display for FetchLatestVersionsReason { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { FetchLatestVersionsReason::CreateClient => write!(f, "could not create HTTP client"), diff --git a/edgelet/iotedge/src/lib.rs b/edgelet/iotedge/src/lib.rs index f1a50a4e097..665c31573a8 100644 --- a/edgelet/iotedge/src/lib.rs +++ b/edgelet/iotedge/src/lib.rs @@ -17,7 +17,7 @@ clippy::use_self )] -use serde_derive::Deserialize; +use serde::Deserialize; mod check; mod client; @@ -32,7 +32,7 @@ mod version; pub use crate::check::{Check, OutputFormat}; pub use crate::client::{MgmtClient, MgmtModule}; -pub use crate::error::{Error, ErrorKind, FetchLatestVersionsReason}; +pub use crate::error::{Error, FetchLatestVersionsReason}; pub use crate::list::List; pub use crate::logs::Logs; pub use crate::restart::Restart; diff --git a/edgelet/iotedge/src/list.rs b/edgelet/iotedge/src/list.rs index d9c800aa664..49a277618d5 100644 --- a/edgelet/iotedge/src/list.rs +++ b/edgelet/iotedge/src/list.rs @@ -4,15 +4,15 @@ use std::io::Write; use std::str::FromStr; use std::sync::{Arc, Mutex}; +use anyhow::Context; use chrono::{DateTime, Duration, TimeZone, Utc}; use chrono_humanize::{Accuracy, HumanTime, Tense}; -use failure::{Fail, ResultExt}; use tabwriter::TabWriter; use edgelet_core::{Module, ModuleRuntime, ModuleStatus as ModuleStatusEnum}; use edgelet_http::ModuleStatus; -use crate::error::{Error, ErrorKind}; +use crate::error::Error; use crate::MgmtModule; pub struct List { @@ -38,18 +38,18 @@ where M: ModuleRuntime, W: Write, { - pub async fn execute(self) -> Result<(), Error> { + pub async fn execute(self) -> anyhow::Result<()> { let write = self.output.clone(); let mut result = self .runtime .list_with_details() .await - .map_err(|err| Error::from(err.context(ErrorKind::ModuleRuntime)))?; + .context(Error::ModuleRuntime)?; result.sort_by(|(mod1, _), (mod2, _)| mod1.name().cmp(mod2.name())); let mut w = write.lock().unwrap(); - writeln!(w, "NAME\tSTATUS\tDESCRIPTION\tConfig").context(ErrorKind::WriteToStdout)?; + writeln!(w, "NAME\tSTATUS\tDESCRIPTION\tConfig").context(Error::WriteToStdout)?; for (module, _state) in result { writeln!( w, @@ -59,9 +59,9 @@ where humanize_status(&module.details.status), module.image ) - .context(ErrorKind::WriteToStdout)?; + .context(Error::WriteToStdout)?; } - w.flush().context(ErrorKind::WriteToStdout)?; + w.flush().context(Error::WriteToStdout)?; Ok(()) } diff --git a/edgelet/iotedge/src/logs.rs b/edgelet/iotedge/src/logs.rs index b68da4e425b..5432c543cd6 100644 --- a/edgelet/iotedge/src/logs.rs +++ b/edgelet/iotedge/src/logs.rs @@ -2,12 +2,12 @@ use std::io::stdout; -use failure::ResultExt; +use anyhow::Context; use edgelet_core::{LogOptions, ModuleRuntime}; use support_bundle::write_logs; -use crate::error::{Error, ErrorKind}; +use crate::error::Error; pub struct Logs { id: String, @@ -29,11 +29,11 @@ impl Logs where M: ModuleRuntime, { - pub async fn execute(self) -> Result<(), Error> { + pub async fn execute(self) -> anyhow::Result<()> { let id = self.id.clone(); write_logs(&self.runtime, &id, &self.options, &mut stdout()) .await - .context(ErrorKind::ModuleRuntime)?; + .context(Error::ModuleRuntime)?; Ok(()) } diff --git a/edgelet/iotedge/src/main.rs b/edgelet/iotedge/src/main.rs index 128c8e3ddae..983d83b87e1 100644 --- a/edgelet/iotedge/src/main.rs +++ b/edgelet/iotedge/src/main.rs @@ -9,26 +9,26 @@ use std::io; use std::process; use std::str::FromStr; +use anyhow::Context; use clap::{crate_description, crate_name, App, AppSettings, Arg, SubCommand}; -use failure::{Fail, ResultExt}; use url::Url; use edgelet_core::{parse_since, LogOptions, LogTail}; use support_bundle::OutputLocation; use iotedge::{ - Check, Error, ErrorKind, List, Logs, MgmtClient, OutputFormat, Restart, SupportBundleCommand, - System, Version, + Check, Error, List, Logs, MgmtClient, OutputFormat, Restart, SupportBundleCommand, System, + Version, }; #[tokio::main] async fn main() { if let Err(ref error) = run().await { - let fail: &dyn Fail = error; + let mut chain = error.chain(); - eprintln!("{}", error); + eprintln!("{}", chain.next().unwrap()); - for cause in fail.iter_causes() { + for cause in chain { eprintln!("\tcaused by: {}", cause); } @@ -39,7 +39,7 @@ async fn main() { } #[allow(clippy::too_many_lines)] -async fn run() -> Result<(), Error> { +async fn run() -> anyhow::Result<()> { let aziot_bin = option_env!("AZIOT_BIN").unwrap_or("aziotctl"); let default_mgmt_uri = option_env!("IOTEDGE_CONNECT_MANAGEMENT_URI") @@ -384,14 +384,10 @@ async fn run() -> Result<(), Error> { .subcommand(SubCommand::with_name("version").about("Show the version information")) .get_matches(); - let runtime = || -> Result<_, Error> { + let runtime = || -> anyhow::Result<_> { let url = matches.value_of("host").map_or_else( - || Err(Error::from(ErrorKind::MissingHostParameter)), - |h| { - Url::parse(h) - .context(ErrorKind::BadHostParameter) - .map_err(Error::from) - }, + || Err(Error::MissingHostParameter.into()), + |h| Url::parse(h).context(Error::BadHostParameter), )?; MgmtClient::new(&url) }; @@ -442,7 +438,7 @@ async fn run() -> Result<(), Error> { .expect("arg has a default value"); let config_file = std::path::Path::new(config_file); - let () = iotedge::config::apply::execute(config_file).map_err(ErrorKind::Config)?; + let () = iotedge::config::apply::execute(config_file).map_err(Error::Config)?; Ok(()) } ("import", Some(args)) => { @@ -459,7 +455,7 @@ async fn run() -> Result<(), Error> { let force = args.is_present("force"); let () = iotedge::config::import::execute(old_config_file, new_config_file, force) - .map_err(ErrorKind::Config)?; + .map_err(Error::Config)?; Ok(()) } ("mp", Some(args)) => { @@ -476,7 +472,7 @@ async fn run() -> Result<(), Error> { let force = args.is_present("force"); let () = iotedge::config::mp::execute(connection_string, out_config_file, force) - .map_err(ErrorKind::Config)?; + .map_err(Error::Config)?; Ok(()) } (command, _) => { @@ -501,15 +497,13 @@ async fn run() -> Result<(), Error> { .value_of("tail") .map(str::parse) .transpose() - .map_err(|err: edgelet_core::Error| { - Error::from(err.context(ErrorKind::BadTailParameter)) - })? + .context(Error::BadTailParameter)? .expect("arg has a default value"); let since = args .value_of("since") .map(parse_since) .transpose() - .context(ErrorKind::BadSinceParameter)? + .context(Error::BadSinceParameter)? .expect("arg has a default value"); let mut options = LogOptions::new() .with_follow(follow) @@ -519,14 +513,14 @@ async fn run() -> Result<(), Error> { .value_of("until") .map(parse_since) .transpose() - .context(ErrorKind::BadSinceParameter)? + .context(Error::BadSinceParameter)? { options = options.with_until(until); } Logs::new(id, options, runtime()?).execute().await } - ("system", Some(args)) => match args.subcommand() { + ("system", Some(args)) => (match args.subcommand() { ("logs", Some(args)) => { let jctl_args: Vec<&OsStr> = args .values_of_os("args") @@ -547,14 +541,15 @@ async fn run() -> Result<(), Error> { eprintln!("Unknown system subcommand {:?}", command); std::process::exit(1); } - }, + }) + .map_err(anyhow::Error::from), ("support-bundle", Some(args)) => { let location = args.value_of_os("output").expect("arg has a default value"); let since = args .value_of("since") .map(parse_since) .transpose() - .context(ErrorKind::BadSinceParameter)? + .context(Error::BadSinceParameter)? .expect("arg has a default value"); let mut options = LogOptions::new() .with_follow(false) @@ -564,7 +559,7 @@ async fn run() -> Result<(), Error> { .value_of("until") .map(parse_since) .transpose() - .context(ErrorKind::BadSinceParameter)? + .context(Error::BadSinceParameter)? { options = options.with_until(until); } diff --git a/edgelet/iotedge/src/restart.rs b/edgelet/iotedge/src/restart.rs index 40b18fe8016..8e417a9b804 100644 --- a/edgelet/iotedge/src/restart.rs +++ b/edgelet/iotedge/src/restart.rs @@ -3,11 +3,11 @@ use std::io::Write; use std::sync::{Arc, Mutex}; -use failure::ResultExt; +use anyhow::Context; use edgelet_core::ModuleRuntime; -use crate::error::{Error, ErrorKind}; +use crate::error::Error; pub struct Restart { id: String, @@ -25,18 +25,17 @@ impl Restart { } } -impl Restart +impl Restart where - M: ModuleRuntime, - Error: From, + M: ModuleRuntime, W: Write + Send, { - pub async fn execute(&self) -> Result<(), Error> { + pub async fn execute(&self) -> anyhow::Result<()> { let write = self.output.clone(); self.runtime.restart(&self.id).await?; let mut w = write.lock().unwrap(); - writeln!(w, "{}", self.id).context(ErrorKind::WriteToStdout)?; + writeln!(w, "{}", self.id).context(Error::WriteToStdout)?; Ok(()) } } diff --git a/edgelet/iotedge/src/support_bundle.rs b/edgelet/iotedge/src/support_bundle.rs index b6d76c525e6..bb4fc88d98d 100644 --- a/edgelet/iotedge/src/support_bundle.rs +++ b/edgelet/iotedge/src/support_bundle.rs @@ -3,12 +3,12 @@ use std::io::{copy, stdout}; use std::path::PathBuf; -use failure::Fail; +use anyhow::Context; use edgelet_core::{LogOptions, ModuleRuntime}; use support_bundle::{make_bundle, OutputLocation}; -use crate::error::{Error, ErrorKind}; +use crate::error::Error; pub struct SupportBundleCommand { runtime: M, @@ -41,7 +41,7 @@ where } } - pub async fn execute(self) -> Result<(), Error> { + pub async fn execute(self) -> anyhow::Result<()> { println!("Making support bundle"); let output_location = self.output_location.clone(); @@ -54,7 +54,7 @@ where &self.runtime, ) .await - .map_err(|_| Error::from(ErrorKind::SupportBundle))?; + .context(Error::SupportBundle)?; match output_location { OutputLocation::File(location) => { @@ -67,8 +67,7 @@ where Ok(()) } OutputLocation::Memory => { - copy(&mut bundle, &mut stdout()) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + copy(&mut bundle, &mut stdout()).context(Error::SupportBundle)?; Ok(()) } diff --git a/edgelet/iotedge/src/system.rs b/edgelet/iotedge/src/system.rs index f30305e41e3..fa8e9d7a09e 100644 --- a/edgelet/iotedge/src/system.rs +++ b/edgelet/iotedge/src/system.rs @@ -12,7 +12,7 @@ use aziotctl_common::system::{ use aziot_identity_client_async::Client as IdentityClient; use aziot_identity_common_http::ApiVersion; -use crate::error::{Error, ErrorKind}; +use crate::error::Error; lazy_static! { static ref IOTEDGED: ServiceDefinition = { @@ -50,35 +50,35 @@ impl System { logs(&services, args).map_err(|err| { eprintln!("{:#?}", err); - Error::from(ErrorKind::System) + Error::System }) } pub fn system_restart() -> Result<(), Error> { restart(&SERVICE_DEFINITIONS).map_err(|err| { eprintln!("{:#?}", err); - Error::from(ErrorKind::System) + Error::System }) } pub fn system_stop() -> Result<(), Error> { stop(&SERVICE_DEFINITIONS).map_err(|err| { eprintln!("{:#?}", err); - Error::from(ErrorKind::System) + Error::System }) } pub fn set_log_level(level: log::Level) -> Result<(), Error> { log_level(&SERVICE_DEFINITIONS, level).map_err(|err| { eprintln!("{:#?}", err); - Error::from(ErrorKind::System) + Error::System }) } pub fn get_system_status() -> Result<(), Error> { get_status(&SERVICE_DEFINITIONS).map_err(|err| { eprintln!("{:#?}", err); - Error::from(ErrorKind::System) + Error::System }) } @@ -89,21 +89,21 @@ impl System { ApiVersion::V2020_09_01, http_common::Connector::new(&uri).map_err(|err| { eprintln!("Failed to make identity client: {}", err); - Error::from(ErrorKind::System) + Error::System })?, 1, ); client.reprovision().await.map_err(|err| { eprintln!("Failed to reprovision: {}", err); - Error::from(ErrorKind::System) + Error::System })?; println!("Successfully reprovisioned with IoT Hub."); restart(&[&IOTEDGED]).map_err(|err| { eprintln!("{:#?}", err); - Error::from(ErrorKind::System) + Error::System })?; Ok(()) diff --git a/edgelet/support-bundle/Cargo.toml b/edgelet/support-bundle/Cargo.toml index 09fc6244fd6..b527e5d60cd 100644 --- a/edgelet/support-bundle/Cargo.toml +++ b/edgelet/support-bundle/Cargo.toml @@ -5,10 +5,11 @@ name = "support-bundle" version = "0.1.0" [dependencies] +anyhow = "1" chrono = "0.4" -failure = "0.1" futures = "0.3" hyper = { version = "0.14", features = ["stream"] } +thiserror = "1" tokio = { version = "1", features = ["process", "time"] } zip = "0.5" diff --git a/edgelet/support-bundle/src/error.rs b/edgelet/support-bundle/src/error.rs index 9e7e4f3f9fc..b74ffc440c8 100644 --- a/edgelet/support-bundle/src/error.rs +++ b/edgelet/support-bundle/src/error.rs @@ -1,53 +1,13 @@ // Copyright (c) Microsoft. All rights reserved. -use std::fmt; -use std::fmt::Display; - -use failure::{Backtrace, Context, Fail}; - -#[derive(Debug)] -pub struct Error { - inner: Context, -} - -#[derive(Clone, Debug, Fail)] -pub enum ErrorKind { - #[fail(display = "A module runtime error occurred")] +#[derive(Clone, Debug, thiserror::Error)] +pub enum Error { + #[error("A module runtime error occurred")] ModuleRuntime, - #[fail(display = "Could not generate support bundle")] + #[error("Could not generate support bundle")] SupportBundle, - #[fail(display = "Could not write")] + #[error("Could not write")] Write, } - -impl Fail for Error { - fn cause(&self) -> Option<&dyn Fail> { - self.inner.cause() - } - - fn backtrace(&self) -> Option<&Backtrace> { - self.inner.backtrace() - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.inner, f) - } -} - -impl From for Error { - fn from(kind: ErrorKind) -> Self { - Error { - inner: Context::new(kind), - } - } -} - -impl From> for Error { - fn from(inner: Context) -> Self { - Error { inner } - } -} diff --git a/edgelet/support-bundle/src/lib.rs b/edgelet/support-bundle/src/lib.rs index abd1a722e93..bed607ef233 100644 --- a/edgelet/support-bundle/src/lib.rs +++ b/edgelet/support-bundle/src/lib.rs @@ -9,6 +9,6 @@ mod runtime_util; mod shell_util; mod support_bundle; -pub use crate::error::{Error, ErrorKind}; +pub use crate::error::Error; pub use crate::runtime_util::write_logs; pub use crate::support_bundle::{make_bundle, OutputLocation}; diff --git a/edgelet/support-bundle/src/runtime_util.rs b/edgelet/support-bundle/src/runtime_util.rs index a58f9cb7e59..227f07611fc 100644 --- a/edgelet/support-bundle/src/runtime_util.rs +++ b/edgelet/support-bundle/src/runtime_util.rs @@ -2,12 +2,12 @@ use std::io::Write; -use failure::Fail; +use anyhow::Context; use futures::TryStreamExt; use edgelet_core::{LogOptions, Module, ModuleRuntime}; -use crate::error::{Error, ErrorKind}; +use crate::error::Error; pub async fn get_modules(runtime: &impl ModuleRuntime, include_ms_only: bool) -> Vec { const MS_MODULES: &[&str] = &["edgeAgent", "edgeHub"]; @@ -38,24 +38,18 @@ pub async fn write_logs( module_name: &str, options: &LogOptions, writer: &mut (impl Write + Send), -) -> Result<(), Error> { +) -> anyhow::Result<()> { // Collect Logs let mut logs = runtime .logs(module_name, options) .await - .map_err(|err| Error::from(err.context(ErrorKind::Write)))?; + .context(Error::Write)?; - while let Some(bytes) = logs - .try_next() - .await - .map_err(|err| Error::from(err.context(ErrorKind::Write)))? - { + while let Some(bytes) = logs.try_next().await.context(Error::Write)? { // First 4 bytes represent stderr vs stdout, we currently don't display differently based on that. // Next 4 bytes represent length of chunk, rust already encodes this information in the slice. if bytes.len() > 8 { - writer - .write_all(&bytes[8..]) - .map_err(|err| Error::from(err.context(ErrorKind::Write)))?; + writer.write_all(&bytes[8..]).context(Error::Write)?; } } diff --git a/edgelet/support-bundle/src/shell_util.rs b/edgelet/support-bundle/src/shell_util.rs index 8357dba1ee1..3dce9619292 100644 --- a/edgelet/support-bundle/src/shell_util.rs +++ b/edgelet/support-bundle/src/shell_util.rs @@ -4,17 +4,17 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use std::io::{Seek, Write}; use tokio::process::Command; -use failure::Fail; +use anyhow::Context; use zip::{write::FileOptions, ZipWriter}; -use crate::error::{Error, ErrorKind}; +use crate::error::Error; use edgelet_core::LogOptions; pub async fn write_check( writer: &mut impl Write, iothub_hostname: Option, verbose: bool, -) -> Result<(), Error> { +) -> anyhow::Result<()> { print_verbose("Calling iotedge check", verbose); let mut iotedge = std::env::current_exe().unwrap(); @@ -32,17 +32,14 @@ pub async fn write_check( if let Some(host_name) = iothub_hostname { check.args(&["--iothub-hostname", &host_name]); } - let check = check - .output() - .await - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + let check = check.output().await.context(Error::SupportBundle)?; writer .write_all(&check.stdout) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + .context(Error::SupportBundle)?; writer .write_all(&check.stderr) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + .context(Error::SupportBundle)?; print_verbose("Wrote check output to file", verbose); Ok(()) @@ -53,7 +50,7 @@ pub async fn write_inspect( zip_writer: &mut ZipWriter, file_options: &FileOptions, verbose: bool, -) -> Result<(), Error> +) -> anyhow::Result<()> where W: Write + Seek, { @@ -86,11 +83,11 @@ where zip_writer .start_file(file_name, *file_options) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + .context(Error::SupportBundle)?; zip_writer .write_all(&output) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + .context(Error::SupportBundle)?; print_verbose(&format!("Got docker inspect for {}", module_name), verbose); @@ -127,7 +124,7 @@ pub async fn write_network_inspect( zip_writer: &mut ZipWriter, file_options: &FileOptions, verbose: bool, -) -> Result<(), Error> +) -> anyhow::Result<()> where W: Write + Seek, { @@ -160,11 +157,11 @@ where zip_writer .start_file(file_name, *file_options) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + .context(Error::SupportBundle)?; zip_writer .write_all(&output) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + .context(Error::SupportBundle)?; print_verbose( &format!("Got docker network inspect for {}", network_name), @@ -180,7 +177,7 @@ pub async fn write_system_log( zip_writer: &mut ZipWriter, file_options: &FileOptions, verbose: bool, -) -> Result<(), Error> +) -> anyhow::Result<()> where W: Write + Seek, { @@ -230,11 +227,11 @@ where zip_writer .start_file(file_name, *file_options) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + .context(Error::SupportBundle)?; zip_writer .write_all(&output) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + .context(Error::SupportBundle)?; print_verbose(format!("Got logs for {}", name).as_str(), verbose); Ok(()) diff --git a/edgelet/support-bundle/src/support_bundle.rs b/edgelet/support-bundle/src/support_bundle.rs index cc461d05e58..ecace74569e 100644 --- a/edgelet/support-bundle/src/support_bundle.rs +++ b/edgelet/support-bundle/src/support_bundle.rs @@ -5,12 +5,12 @@ use std::fs::File; use std::io::{Cursor, Read, Seek, SeekFrom, Write}; use std::path::Path; -use failure::Fail; +use anyhow::Context; use zip::{write::FileOptions, CompressionMethod, ZipWriter}; use edgelet_core::{LogOptions, ModuleRuntime}; -use crate::error::{Error, ErrorKind}; +use crate::error::Error; use crate::runtime_util::{get_modules, write_logs}; use crate::shell_util::{ get_docker_networks, write_check, write_inspect, write_network_inspect, write_system_log, @@ -34,11 +34,10 @@ pub async fn make_bundle( verbose: bool, iothub_hostname: Option, runtime: &impl ModuleRuntime, -) -> Result<(Box, u64), Error> { +) -> anyhow::Result<(Box, u64)> { match output_location { OutputLocation::File(location) => { - let buffer = File::create(Path::new(&location)) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + let buffer = File::create(Path::new(&location)).context(Error::SupportBundle)?; let mut zip_writer = ZipWriter::new(buffer); let (reader, size) = write_all( @@ -79,7 +78,7 @@ async fn write_all( verbose: bool, iothub_hostname: Option, runtime: &impl ModuleRuntime, -) -> Result<(W, u64), Error> +) -> anyhow::Result<(W, u64)> where W: Write + Seek + Send, { @@ -88,7 +87,7 @@ where // Get Check zip_writer .start_file("check.json", file_options) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + .context(Error::SupportBundle)?; write_check(&mut zip_writer, iothub_hostname, verbose).await?; // Get all modules @@ -96,7 +95,7 @@ where // Write module logs zip_writer .start_file(format!("logs/{}_log.txt", module_name), file_options) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + .context(Error::SupportBundle)?; write_logs(runtime, &module_name, &log_options, &mut zip_writer).await?; // write module inspect @@ -114,15 +113,13 @@ where } // Finilize buffer and set cursur to 0 for reading. - let mut buffer = zip_writer - .finish() - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + let mut buffer = zip_writer.finish().context(Error::SupportBundle)?; let len = buffer .seek(SeekFrom::Current(0)) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + .context(Error::SupportBundle)?; buffer .seek(SeekFrom::Start(0)) - .map_err(|err| Error::from(err.context(ErrorKind::SupportBundle)))?; + .context(Error::SupportBundle)?; let result = (buffer, len); Ok(result) diff --git a/tools/check_submodules/Cargo.lock b/tools/check_submodules/Cargo.lock index a84cf27d575..792c4a121e4 100644 --- a/tools/check_submodules/Cargo.lock +++ b/tools/check_submodules/Cargo.lock @@ -20,6 +20,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" + [[package]] name = "arrayvec" version = "0.5.2" @@ -42,28 +48,6 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" -[[package]] -name = "backtrace" -version = "0.3.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" -dependencies = [ - "backtrace-sys", - "cfg-if 0.1.10", - "libc", - "rustc-demangle", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "bitflags" version = "1.2.1" @@ -92,13 +76,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "check_submodules" version = "0.1.1" dependencies = [ + "anyhow", "clap", "edgelet-utils", "env_logger", - "failure", "git2", "hex", "log", + "thiserror", ] [[package]] @@ -147,10 +132,10 @@ name = "edgelet-utils" version = "0.1.0" dependencies = [ "config", - "failure", "log", "serde", "serde_json", + "thiserror", "yaml-rust", ] @@ -167,28 +152,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "git2" version = "0.7.5" @@ -409,12 +372,6 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" -[[package]] -name = "rustc-demangle" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" - [[package]] name = "ryu" version = "1.0.2" @@ -461,24 +418,12 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7bedb3320d0f3035594b0b723c8a28d7d336a3eda3881db79e61d676fb644c" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "synstructure" -version = "0.12.1" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" +checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" dependencies = [ "proc-macro2", "quote", - "syn", "unicode-xid", ] @@ -500,6 +445,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-bidi" version = "0.3.4" diff --git a/tools/check_submodules/Cargo.toml b/tools/check_submodules/Cargo.toml index 25c503b1281..4d0361a6b47 100644 --- a/tools/check_submodules/Cargo.toml +++ b/tools/check_submodules/Cargo.toml @@ -6,11 +6,12 @@ edition = "2018" [dependencies] +anyhow = "1" clap = "2.32" env_logger = "0.9" -failure = "0.1" git2 = "0.7" hex = "0.3" log = "0.4" +thiserror = "1" edgelet-utils = { path = "../../edgelet/edgelet-utils" } diff --git a/tools/check_submodules/src/error.rs b/tools/check_submodules/src/error.rs index 1a06b9b5614..f804264f67b 100644 --- a/tools/check_submodules/src/error.rs +++ b/tools/check_submodules/src/error.rs @@ -1,57 +1,9 @@ // Copyright (c) Microsoft. All rights reserved. -use std::fmt; -use std::fmt::Display; - -use failure::{Backtrace, Context, Fail}; - -#[derive(Debug)] -pub struct Error { - inner: Context, -} - -#[derive(Debug, Fail)] -pub enum ErrorKind { - #[fail(display = "Found submodule inconsistencies.")] +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Found submodule inconsistencies.")] Count(i64), - #[fail(display = "Git library error")] + #[error("Git library error")] Git, } - -impl Fail for Error { - fn cause(&self) -> Option<&dyn Fail> { - self.inner.cause() - } - - fn backtrace(&self) -> Option<&Backtrace> { - self.inner.backtrace() - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.inner, f) - } -} - -impl From for Error { - fn from(kind: ErrorKind) -> Error { - Error { - inner: Context::new(kind), - } - } -} - -impl From> for Error { - fn from(inner: Context) -> Error { - Error { inner } - } -} - -impl From for Error { - fn from(count: i64) -> Error { - Error { - inner: Context::new(ErrorKind::Count(count)), - } - } -} diff --git a/tools/check_submodules/src/logging.rs b/tools/check_submodules/src/logging.rs index 63ae1dfaf83..6dfc9ffa5ee 100644 --- a/tools/check_submodules/src/logging.rs +++ b/tools/check_submodules/src/logging.rs @@ -4,11 +4,8 @@ use std::env; use std::io::Write; use edgelet_utils::log_failure; -use env_logger; use log::{Level, LevelFilter}; -use crate::error::Error; - const ENV_LOG: &str = "IOTEDGE_LOG"; fn syslog_level(level: Level) -> i8 { @@ -16,8 +13,7 @@ fn syslog_level(level: Level) -> i8 { Level::Error => 3, Level::Warn => 4, Level::Info => 6, - Level::Debug => 7, - Level::Trace => 7, + Level::Debug | Level::Trace => 7, } } @@ -59,6 +55,6 @@ pub fn init_logger() { .init(); } -pub fn log_error(error: &Error) { +pub fn log_error(error: &dyn std::error::Error) { log_failure(Level::Error, error); } diff --git a/tools/check_submodules/src/main.rs b/tools/check_submodules/src/main.rs index f853a1bb34e..96cb6c7eba4 100644 --- a/tools/check_submodules/src/main.rs +++ b/tools/check_submodules/src/main.rs @@ -10,18 +10,19 @@ mod tree; use std::env; use std::path::Path; +use anyhow::Context; use clap::{App, Arg}; use log::info; -fn run_check(starting_path: &Path) -> Result<(), error::Error> { +fn run_check(starting_path: &Path) -> anyhow::Result<()> { // Create a submodule tree. - let tree = tree::Git2Tree::new(starting_path)?; + let tree = tree::Git2Tree::new(starting_path).context(error::Error::Git)?; let count = tree.count_flagged(); // display the tree. println!("{}", tree); match count { 0 => Ok(()), - _ => Err(count)?, + _ => Err(error::Error::Count(count).into()), } } @@ -40,7 +41,7 @@ fn main() { info!("Starting check with path {:?}", starting_path); if let Err(e) = run_check(starting_path) { - logging::log_error(&e); + logging::log_error(e.as_ref()); std::process::exit(1); } else { info!("No inconsistencies detected"); diff --git a/tools/check_submodules/src/tree.rs b/tools/check_submodules/src/tree.rs index 36df3390162..6dbc02db325 100644 --- a/tools/check_submodules/src/tree.rs +++ b/tools/check_submodules/src/tree.rs @@ -4,12 +4,12 @@ use std::collections::HashMap; use std::fmt; use std::path::Path; -use failure::Fail; +use anyhow::Context; use git2::Repository; use hex::encode; use log::debug; -use crate::error::{Error, ErrorKind}; +use crate::error::Error; type RemoteUrl = String; type CommitId = String; @@ -45,13 +45,14 @@ impl GitModule { } } +#[allow(clippy::module_name_repetitions)] pub struct Git2Tree { root: GitModule, children: Vec, } -fn sanitize_url(url: String) -> String { - url.trim_end_matches(".git").replace("www.", "").to_string() +fn sanitize_url(url: &str) -> String { + url.trim_end_matches(".git").replace("www.", "") } impl Git2Tree { @@ -66,42 +67,39 @@ impl Git2Tree { write!(f, " ")?; } write!(f, "|- ")?; - write!(f, "{}\n", self.root)?; - for child in self.children.iter() { + writeln!(f, "{}", self.root)?; + for child in &self.children { child.format(level + 1, f)?; } Ok(()) } - fn new_as_subtree(path: &Path, mut remotes: &mut RemoteMap) -> Result { + fn new_as_subtree(path: &Path, remotes: &mut RemoteMap) -> anyhow::Result { debug!("repo path {:?}", path); - let repo = - Repository::open(path).map_err(|err| Error::from(err.context(ErrorKind::Git)))?; + let repo = Repository::open(path).context(Error::Git)?; let remote = sanitize_url( repo.find_remote("origin") - .map_err(|err| Error::from(err.context(ErrorKind::Git)))? + .context(Error::Git)? .url() - .unwrap() - .to_string(), + .unwrap(), ); debug!("remote = {:?}", remote); let commit = encode( repo.head() - .map_err(|err| Error::from(err.context(ErrorKind::Git)))? + .context(Error::Git)? .peel_to_commit() - .map_err(|err| Error::from(err.context(ErrorKind::Git)))? + .context(Error::Git)? .id(), ); debug!("commit = {:?}", commit); let flag = remotes.get(&remote).map_or(false, |c| &commit != c); - remotes.entry(remote.clone()).or_insert(commit.clone()); + remotes + .entry(remote.clone()) + .or_insert_with(|| commit.clone()); let mut children: Vec = Vec::new(); - for sm in repo - .submodules() - .map_err(|err| Error::from(err.context(ErrorKind::Git)))? - { - let child = Git2Tree::new_as_subtree(path.join(sm.path()).as_path(), &mut remotes)?; + for sm in repo.submodules().context(Error::Git)? { + let child = Git2Tree::new_as_subtree(path.join(sm.path()).as_path(), remotes)?; children.push(child); } Ok(Git2Tree { @@ -110,7 +108,7 @@ impl Git2Tree { }) } - pub fn new(path: &Path) -> Result { + pub fn new(path: &Path) -> anyhow::Result { let mut remotes: RemoteMap = HashMap::new(); Git2Tree::new_as_subtree(path, &mut remotes) } @@ -121,7 +119,7 @@ impl Git2Tree { + self .children .iter() - .fold(0, |acc, ref x| acc + x.count_flagged()) + .fold(0, |acc, x| acc + x.count_flagged()) } }