diff --git a/Cargo.lock b/Cargo.lock index af624ff..4b3c37c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -265,9 +265,9 @@ checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" [[package]] name = "aho-corasick" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f56c476256dc249def911d6f7580b5fc7e875895b5d7ee88f5d602208035744" +checksum = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811" dependencies = [ "memchr", ] @@ -371,9 +371,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.43" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f80256bc78f67e7df7e36d77366f636ed976895d91fe2ab9efa3973e8fe8c4f" +checksum = "e4036b9bf40f3cf16aba72a3d65e8a520fc4bafcdc7079aea8f848c58c5b5536" dependencies = [ "backtrace-sys", "cfg-if", @@ -463,9 +463,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" -version = "1.3.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "bytes" @@ -595,9 +595,9 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.2" +version = "0.99.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2159be042979966de68315bce7034bb000c775f22e3e834e1c52ff78f041cae8" +checksum = "a806e96c59a76a5ba6e18735b6cf833344671e61e7863f2edb5c518ea2cac95c" dependencies = [ "proc-macro2", "quote", @@ -688,9 +688,9 @@ dependencies = [ [[package]] name = "enum-as-inner" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900a6c7fbe523f4c2884eaf26b57b81bb69b6810a01a236390a7ac021d09492e" +checksum = "eaeb00c3d7e5eed0e7c15a2ff045d76800a2e34b93f790bc38c8e3f9bfafef2b" dependencies = [ "heck", "proc-macro2", @@ -789,9 +789,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda826c2f9351e68bc87b9037d91d818f24384993ecbb37f711e1f71a83182b5" +checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" dependencies = [ "futures-channel", "futures-core", @@ -804,9 +804,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92c2137e8e1ebf1ac99453550ab46eb4f35c5c53476d57d75eb782fb4d71e84" +checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" dependencies = [ "futures-core", "futures-sink", @@ -814,15 +814,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccfb301b0b09e940a67376cf40d1b0ac4db9366ee737f65c02edea225057e91e" +checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" [[package]] name = "futures-executor" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f085a4c6508baef1f70ec2e0232a09edc649a3b86551fe92555e3a9e43939e4c" +checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" dependencies = [ "futures-core", "futures-task", @@ -831,15 +831,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff098c09c30cc42b88a67d9fb27435e8456fb8b2483c904340ed499736931c" +checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" [[package]] name = "futures-macro" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebecc4204c719ca7140a3253676f1321e6fa17b1752a94a4a436d308be293e87" +checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -849,21 +849,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0485279d763e8a3669358f500e805339138b7bbe90f5718c80eedfdcb2ea36a4" +checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" [[package]] name = "futures-task" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefffab2aacc73845afd3f202e09fc775a55e2e96f46c8b1a46c117ae1c126ca" +checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" [[package]] name = "futures-util" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3f8c59707f898b8b6f0b54c2aef5408ae90a560b7bf0fbf1b95b3c652b0171" +checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" dependencies = [ "futures-channel", "futures-core", @@ -977,16 +977,16 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" +checksum = "e2c55f143919fbc0bc77e427fe2d74cf23786d7c1875666f2fde3ac3c659bb67" dependencies = [ "libc", ] [[package]] name = "hogan" -version = "0.7.1" +version = "0.7.3" dependencies = [ "actix-rt", "actix-service", @@ -1003,6 +1003,7 @@ dependencies = [ "json-patch", "log", "lru_time_cache", + "parking_lot", "predicates", "regex", "serde", @@ -1057,9 +1058,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b54058f0a6ff80b6803da8faf8997cde53872b38f4023728f6830b06cd3c0dc" +checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" dependencies = [ "autocfg 1.0.0", ] @@ -1247,9 +1248,9 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "memchr" -version = "2.3.0" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" +checksum = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978" [[package]] name = "mime" @@ -1539,9 +1540,9 @@ dependencies = [ [[package]] name = "proc-macro-error" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875077759af22fa20b610ad4471d8155b321c89c3f2785526c9839b099be4e0a" +checksum = "052b3c9af39c7e5e94245f820530487d19eb285faedcb40e0c3275132293f242" dependencies = [ "proc-macro-error-attr", "proc-macro2", @@ -1552,9 +1553,9 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5717d9fa2664351a01ed73ba5ef6df09c01a521cb42cb65a061432a826f3c7a" +checksum = "d175bef481c7902e63e3165627123fff3502f06ac043d3ef42d08c1246da9253" dependencies = [ "proc-macro2", "quote", @@ -1754,9 +1755,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" @@ -1780,9 +1781,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.46" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b01d7f0288608a01dca632cf1df859df6fd6ffa885300fc275ce2ba6221953" +checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" dependencies = [ "itoa", "ryu", diff --git a/Cargo.toml b/Cargo.toml index c99ac70..e53b089 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ doc = false [package] name = 'hogan' -version = '0.7.2' +version = '0.7.3' authors = ['Jonathan Morley '] edition = '2018' @@ -31,6 +31,7 @@ actix-web = '2' actix-rt = '1' actix-service = '1' futures = '0.3' +parking_lot = '0.10' [dependencies.regex] version = '1' diff --git a/src/app/datadogstatsd.rs b/src/app/datadogstatsd.rs index db0c1c4..d4895e7 100644 --- a/src/app/datadogstatsd.rs +++ b/src/app/datadogstatsd.rs @@ -2,75 +2,78 @@ use dogstatsd::{Client, Options}; use std::env; pub struct DdMetrics { - default_tags: [String; 2], + default_tags: Vec, client: Client, } impl Default for DdMetrics { fn default() -> Self { - let dd_options = Options::default(); - DdMetrics { - default_tags: [String::from("service:hogan"), "env:unknown".to_string()], - client: Client::new(dd_options).unwrap(), - } + DdMetrics::new() } } impl DdMetrics { pub fn new() -> Self { let dd_options = Options::default(); - let mut env_tag = String::from("env: "); let key = "ENV"; - match env::var(key) { - Ok(val) => { - info!("{}: {}", key, val); - env_tag.push_str(&val); - } - Err(e) => { - info!("couldn't interpret {}: {}", key, e); - env_tag.push_str("unknown"); - } - } + let env_name = env::var(key).unwrap_or_else(|_| "unknown".to_string()); - let dd_tags = [String::from("service:hogan"), env_tag]; + info!("Setting up datadog with environment {}", env_name); + + let dd_tags = vec![String::from("service:hogan"), format!("env:{}", env_name)]; DdMetrics { default_tags: dd_tags, client: Client::new(dd_options).unwrap(), } } - pub fn incr(&self, name: &str, url: &str) { + pub fn incr(&self, name: &str, additional_tags: Option>) { self.client - .incr(name, self.append_url_tag(url).iter()) + .incr( + name, + additional_tags + .unwrap_or_default() + .iter() + .chain(self.default_tags.iter()), + ) .unwrap_or_else(|err| self.error_msg(name, &err.to_string())); } - pub fn decr(&self, name: &str, url: &str) { + pub fn decr(&self, name: &str, additional_tags: Option>) { self.client - .decr(name, self.append_url_tag(url).iter()) + .decr( + name, + additional_tags + .unwrap_or_default() + .iter() + .chain(self.default_tags.iter()), + ) .unwrap_or_else(|err| self.error_msg(name, &err.to_string())); } - pub fn gauge(&self, name: &str, url: &str, value: &str) { + pub fn gauge(&self, name: &str, additional_tags: Option>, value: &str) { self.client - .gauge(name, value, self.append_url_tag(url).iter()) + .gauge( + name, + value, + additional_tags + .unwrap_or_default() + .iter() + .chain(self.default_tags.iter()), + ) .unwrap_or_else(|err| self.error_msg(name, &err.to_string())); } - pub fn time(&self, name: &str, url: &str, value: i64) { + pub fn time(&self, name: &str, additional_tags: Option>, value: i64) { self.client - .timing(name, value, self.append_url_tag(url).iter()) + .timing( + name, + value, + additional_tags + .unwrap_or_default() + .iter() + .chain(self.default_tags.iter()), + ) .unwrap_or_else(|err| self.error_msg(name, &err.to_string())); } - fn append_url_tag(&self, url: &str) -> Vec { - let mut dd_tags = Vec::new(); - dd_tags.extend_from_slice(&self.default_tags); - - let mut url_tag = String::from("request_url: "); - url_tag.push_str(url); - - dd_tags.push(url_tag); - dd_tags - } - fn error_msg(&self, name: &str, err: &str) { warn!("{} dd metrics failed with error {}", name, err) } @@ -87,7 +90,7 @@ impl CustomMetrics { match self { CustomMetrics::CacheMiss => "hogan.cache_miss.counter", CustomMetrics::CacheHit => "hogan.cache_hit.counter", - CustomMetrics::RequestTime => "hogan.request_time.gauge", + CustomMetrics::RequestTime => "hogan.requests", } } } diff --git a/src/app/server.rs b/src/app/server.rs index 2371786..4f89e5d 100644 --- a/src/app/server.rs +++ b/src/app/server.rs @@ -7,11 +7,11 @@ use futures::future::FutureExt; use hogan; use hogan::config::ConfigDir; use lru_time_cache::LruCache; +use parking_lot::Mutex; use regex::Regex; use serde::Deserialize; use serde::Serialize; use std::sync::Arc; -use std::sync::Mutex; use std::time::SystemTime; pub fn start_up_server( @@ -82,6 +82,10 @@ struct ServerState { dd_metrics: Option, } +fn contextualize_path(path: &str) -> &str { + path.split('/').nth(1).unwrap_or_else(|| &"route") +} + #[actix_rt::main] async fn start_server( address: String, @@ -102,31 +106,43 @@ async fn start_server( None }; srv.call(req).map(move |res| { - if let Some(time) = start_time { - if let Ok(duration) = time.elapsed() { - let ms = duration.as_millis(); - debug!("Request duration: {}", ms); - if dd_enabled { - let metrics = DdMetrics::new(); - metrics.time( - CustomMetrics::RequestTime.metrics_name(), - "route", //TODO: Normalize the matched URI - ms as i64, + if let Ok(result) = res { + if let Some(time) = start_time { + if let Ok(duration) = time.elapsed() { + let path = contextualize_path(result.request().path()); + let method = result.request().method().as_str(); + let ms = duration.as_millis(); + let status = result.status(); + debug!( + "Request for {} {} duration: {} status: {}", + method, path, ms, status ); - }; + if dd_enabled { + let metrics = DdMetrics::new(); + metrics.time( + CustomMetrics::RequestTime.metrics_name(), + Some(vec![ + format!("url:{}", path), + format!("method:{}", method), + format!("status:{}", status.as_str()), + ]), + ms as i64, + ); + }; + } } + Ok(result) + } else { + res } - res }) }) - .service( - web::scope("/transform") - .service(transform_env) - .service(transform_all_envs), - ) - .service(web::scope("/envs").service(get_envs)) - .service(web::scope("/configs").service(get_config_by_env)) - .service(web::scope("/heads").service(get_branch_sha)) + .service(transform_route_sha_env) + .service(transform_branch_head) + .service(transform_all_envs) + .service(get_envs) + .service(get_config_by_env) + .service(get_branch_sha) .route("/ok", web::to(|| HttpResponse::Ok().finish())) }) .bind(binding)? @@ -142,30 +158,43 @@ struct TransformEnvParams { env: String, } -#[post("/{sha}/{env}")] -fn transform_env( +#[post("transform/{sha}/{env}")] +fn transform_route_sha_env( data: String, params: web::Path, state: web::Data, ) -> HttpResponse { - let sha = format_sha(¶ms.sha); - let uri = format!("/transform/{}/{}", &sha, ¶ms.env); - match get_env(&state, None, sha, ¶ms.env, &uri) { + match transform_from_sha(data, ¶ms.sha, ¶ms.env, &state) { + Ok(result) => HttpResponse::Ok().body(result), + Err(e) => { + warn!( + "Error templating request {} {} {}", + e, params.sha, params.env + ); + HttpResponse::BadRequest().finish() + } + } +} + +fn transform_from_sha( + data: String, + sha: &str, + env: &str, + state: &ServerState, +) -> Result { + let sha = format_sha(sha); + match get_env(&state, None, sha, env) { Some(env) => { let handlebars = hogan::transform::handlebars(state.strict); - match handlebars.render_template(&data, &env.config_data) { - Ok(result) => HttpResponse::Ok().body(result), - Err(e) => { - warn!("Error templating request {} {} {}", e, sha, params.env); - HttpResponse::BadRequest().finish() - } - } + handlebars + .render_template(&data, &env.config_data) + .map_err(|e| e.into()) } - None => HttpResponse::NotFound().finish(), + None => Err(format_err!("Could not find env {}", env)), } } -#[post("/{sha}?{filename}")] +#[post("transform/{sha}?{filename}")] fn transform_all_envs() -> HttpResponse { HttpResponse::Gone().finish() } @@ -175,12 +204,9 @@ struct GetEnvsParams { sha: String, } -#[get("/{sha}")] +#[get("envs/{sha}")] fn get_envs(params: web::Path, state: web::Data) -> HttpResponse { - let uri = format!("/envs/{}", ¶ms.sha); - info!("uri: {}", uri); - - match get_env_listing(&state, None, ¶ms.sha, &uri) { + match get_env_listing(&state, None, ¶ms.sha) { Some(envs) => HttpResponse::Ok().json(envs), None => HttpResponse::NotFound().finish(), } @@ -192,14 +218,13 @@ struct ConfigByEnvState { env: String, } -#[get("/{sha}/{env}")] +#[get("configs/{sha}/{env}")] fn get_config_by_env( params: web::Path, state: web::Data, ) -> HttpResponse { let sha = format_sha(¶ms.sha); - let uri = format!("/config/{}/{}", &sha, ¶ms.env); - match get_env(&state, None, sha, ¶ms.env, &uri) { + match get_env(&state, None, sha, ¶ms.env) { Some(env) => HttpResponse::Ok().json(env), None => HttpResponse::NotFound().finish(), } @@ -217,26 +242,61 @@ struct BranchShaParams { branch_name: String, } -#[get("/{branch_name:.*}")] +fn find_branch_head(branch_name: &str, state: &ServerState) -> Option { + let config_dir = state.config_dir.lock(); + if let Some(head_sha) = config_dir.find_branch_head(&"origin", branch_name) { + Some(head_sha) + } else { + None + } +} + +#[get("heads/{branch_name:.*}")] fn get_branch_sha( params: web::Path, state: web::Data, ) -> HttpResponse { - //let branch_name = branch_name.intersperse("/").collect::(); let branch_name = ¶ms.branch_name; debug!("Looking up branch name {}", branch_name); - if let Ok(config_dir) = state.config_dir.lock() { - if let Some(head_sha) = config_dir.find_branch_head(&"origin", branch_name) { - HttpResponse::Ok().json(ShaResponse { - head_sha, - branch_name: branch_name.to_string(), - }) - } else { - HttpResponse::NotFound().finish() + + if let Some(head_sha) = find_branch_head(branch_name, &state) { + HttpResponse::Ok().json(ShaResponse { + head_sha, + branch_name: branch_name.to_string(), + }) + } else { + HttpResponse::NotFound().finish() + } +} + +#[derive(Deserialize)] +struct BranchHeadTransformParams { + branch_name: String, + environment: String, +} + +#[post("branch/{branch_name:.*}/transform/{environment}")] +fn transform_branch_head( + data: String, + params: web::Path, + state: web::Data, +) -> HttpResponse { + if let Some(head_sha) = find_branch_head(¶ms.branch_name, &state) { + match transform_from_sha(data, &head_sha, ¶ms.environment, &state) { + Ok(result) => HttpResponse::Ok().body(result), + Err(e) => { + warn!( + "Error templating request {} {} {}", + e, head_sha, params.environment + ); + HttpResponse::BadRequest().body(format!( + "Unable to template request on branch {} for environment {} - {:?}", + params.branch_name, params.environment, e + )) + } } } else { - warn!("Error locking git repo"); - HttpResponse::InternalServerError().finish() + HttpResponse::NotFound().body(format!("Unknown branch {}", params.branch_name)) } } @@ -249,47 +309,33 @@ fn get_env( remote: Option<&str>, sha: &str, env: &str, - request_url: &str, ) -> Option> { let key = format_key(sha, env); - let mut cache = match state.environments.lock() { - Ok(cache) => cache, - Err(e) => { - warn!("Unable to lock cache {}", e); - return None; - } - }; + let mut cache = state.environments.lock(); if let Some(env) = cache.get(&key) { info!("Cache Hit {}", key); if let Some(custom_metrics) = &state.dd_metrics { - custom_metrics.incr(CustomMetrics::CacheHit.metrics_name(), request_url); + custom_metrics.incr(CustomMetrics::CacheHit.metrics_name(), None); } Some(env.clone()) } else { info!("Cache Miss {}", key); if let Some(custom_metrics) = &state.dd_metrics { - custom_metrics.incr(CustomMetrics::CacheMiss.metrics_name(), request_url); + custom_metrics.incr(CustomMetrics::CacheMiss.metrics_name(), None); } - match state.config_dir.lock() { - Ok(repo) => { - if let Some(sha) = repo.refresh(remote, Some(sha)) { - match repo - .find(state.environments_regex.clone()) - .iter() - .find(|e| e.environment == env) - { - Some(env) => cache.insert(key.clone(), Arc::new(env.clone())), - None => { - debug!("Unable to find the env {} in {}", env, sha); - return None; - } - }; - }; - } - Err(e) => { - warn!("Unable to lock repository {}", e); - return None; - } + let repo = state.config_dir.lock(); + if let Some(sha) = repo.refresh(remote, Some(sha)) { + match repo + .find(state.environments_regex.clone()) + .iter() + .find(|e| e.environment == env) + { + Some(env) => cache.insert(key.clone(), Arc::new(env.clone())), + None => { + debug!("Unable to find the env {} in {}", env, sha); + return None; + } + }; }; if let Some(envs) = cache.get(&key) { Some(envs.clone()) @@ -304,45 +350,33 @@ fn get_env_listing( state: &ServerState, remote: Option<&str>, sha: &str, - request_url: &str, ) -> Option>> { let sha = format_sha(sha); - let mut cache = match state.environment_listings.lock() { - Ok(cache) => cache, - Err(e) => { - warn!("Unable to lock cache {}", e); - return None; - } - }; + let mut cache = state.environment_listings.lock(); if let Some(env) = cache.get(sha) { info!("Cache Hit {}", sha); if let Some(custom_metrics) = &state.dd_metrics { - custom_metrics.incr(CustomMetrics::CacheHit.metrics_name(), request_url); + custom_metrics.incr(CustomMetrics::CacheHit.metrics_name(), None); } Some(env.clone()) } else { info!("Cache Miss {}", sha); if let Some(custom_metrics) = &state.dd_metrics { - custom_metrics.incr(CustomMetrics::CacheMiss.metrics_name(), request_url); + custom_metrics.incr(CustomMetrics::CacheMiss.metrics_name(), None); } - match state.config_dir.lock() { - Ok(repo) => { - if let Some(sha) = repo.refresh(remote, Some(sha)) { - let envs = format_envs(&repo.find(state.environments_regex.clone())); - if !envs.is_empty() { - info!("Loading envs for {}", sha); - cache.insert(sha, Arc::new(envs)); - } else { - info!("No envs found for {}", sha); - return None; - } - }; - } - Err(e) => { - warn!("Unable to lock repository {}", e); + let repo = state.config_dir.lock(); + + if let Some(sha) = repo.refresh(remote, Some(sha)) { + let envs = format_envs(&repo.find(state.environments_regex.clone())); + if !envs.is_empty() { + info!("Loading envs for {}", sha); + cache.insert(sha, Arc::new(envs)); + } else { + info!("No envs found for {}", sha); return None; } }; + if let Some(envs) = cache.get(sha) { Some(envs.clone()) } else {