123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- use crate::github::GithubClient;
- use failure::Error;
- use std::collections::HashMap;
- use std::fmt;
- use std::sync::{Arc, RwLock};
- use std::time::{Duration, Instant};
- static CONFIG_FILE_NAME: &str = "triagebot.toml";
- const REFRESH_EVERY: Duration = Duration::from_secs(2 * 60); // Every two minutes
- lazy_static::lazy_static! {
- static ref CONFIG_CACHE:
- RwLock<HashMap<String, (Result<Arc<Config>, ConfigurationError>, Instant)>> =
- RwLock::new(HashMap::new());
- }
- #[derive(Debug, serde::Deserialize)]
- pub(crate) struct Config {
- pub(crate) relabel: Option<RelabelConfig>,
- pub(crate) assign: Option<AssignConfig>,
- }
- #[derive(Debug, serde::Deserialize)]
- pub(crate) struct AssignConfig {
- #[serde(default)]
- _empty: (),
- }
- #[derive(Debug, serde::Deserialize)]
- #[serde(rename_all = "kebab-case")]
- pub(crate) struct RelabelConfig {
- #[serde(default)]
- pub(crate) allow_unauthenticated: Vec<String>,
- }
- pub(crate) async fn get(gh: &GithubClient, repo: &str) -> Result<Arc<Config>, ConfigurationError> {
- if let Some(config) = get_cached_config(repo) {
- log::trace!("returning config for {} from cache", repo);
- config
- } else {
- log::trace!("fetching fresh config for {}", repo);
- let res = get_fresh_config(gh, repo).await;
- CONFIG_CACHE
- .write()
- .unwrap()
- .insert(repo.to_string(), (res.clone(), Instant::now()));
- res
- }
- }
- fn get_cached_config(repo: &str) -> Option<Result<Arc<Config>, ConfigurationError>> {
- let cache = CONFIG_CACHE.read().unwrap();
- cache.get(repo).and_then(|(config, fetch_time)| {
- if fetch_time.elapsed() < REFRESH_EVERY {
- Some(config.clone())
- } else {
- None
- }
- })
- }
- async fn get_fresh_config(
- gh: &GithubClient,
- repo: &str,
- ) -> Result<Arc<Config>, ConfigurationError> {
- let contents = gh
- .raw_file(repo, "master", CONFIG_FILE_NAME)
- .await
- .map_err(|e| ConfigurationError::Http(Arc::new(e)))?
- .ok_or(ConfigurationError::Missing)?;
- let config = Arc::new(toml::from_slice::<Config>(&contents).map_err(ConfigurationError::Toml)?);
- log::debug!("fresh configuration for {}: {:?}", repo, config);
- Ok(config)
- }
- #[derive(Clone, Debug)]
- pub enum ConfigurationError {
- Missing,
- Toml(toml::de::Error),
- Http(Arc<Error>),
- }
- impl std::error::Error for ConfigurationError {}
- impl fmt::Display for ConfigurationError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- ConfigurationError::Missing => write!(
- f,
- "This repository is not enabled to use triagebot.\n\
- Add a `triagebot.toml` in the root of the master branch to enable it."
- ),
- ConfigurationError::Toml(e) => {
- write!(f, "Malformed `triagebot.toml` in master branch.\n{}", e)
- }
- ConfigurationError::Http(_) => {
- write!(f, "Failed to query configuration for this repository.")
- }
- }
- }
- }
|