1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::auth::AuthConfig;
use crate::errors::*;
use serde::Deserialize;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};

pub const IDLE_DELAY: u64 = 180;
pub const PING_DEADLINE: i64 = IDLE_DELAY as i64 + 20;
pub const PING_INTERVAL: u64 = 60;
pub const WORKER_DELAY: u64 = 3;
pub const API_ERROR_DELAY: u64 = 30;

pub fn load<P: AsRef<Path>>(path: Option<P>) -> Result<ConfigFile> {
    let mut config = ConfigFile::default();

    if let Some(c) = load_from("/etc/rebuilderd.conf")? {
        config.update(c);
    }

    if let Ok(path) = config_path() {
        if let Some(c) = load_from(path)? {
            config.update(c);
        }
    }

    if let Some(path) = path {
        let c = load_from(path)?
            .ok_or_else(|| format_err!("Failed to read config file"))?;
        config.update(c);
    }

    Ok(config)
}

fn config_path() -> Result<PathBuf> {
    let config_dir = dirs::config_dir()
        .ok_or_else(|| format_err!("Failed to find config dir"))?;
    Ok(config_dir.join("rebuilderd.conf"))
}

fn load_from<P: AsRef<Path>>(path: P) -> Result<Option<ConfigFile>> {
    if let Ok(buf) = fs::read(path.as_ref()) {
        debug!("loading config file {:?}", path.as_ref());
        let config = toml::from_slice(&buf)
            .context("Failed to load config")?;
        Ok(Some(config))
    } else {
        Ok(None)
    }
}

#[derive(Debug, Default, Deserialize)]
pub struct ConfigFile {
    #[serde(default)]
    pub http: HttpConfig,
    #[serde(default)]
    pub auth: AuthConfig,
    #[serde(default)]
    pub endpoints: HashMap<String, EndpointConfig>,
    #[serde(default)]
    pub worker: WorkerConfig,
}

impl ConfigFile {
    pub fn update(&mut self, c: ConfigFile) {
        self.http.update(c.http);
        self.auth.update(c.auth);
        for (k, v) in c.endpoints {
            if let Some(o) = self.endpoints.get_mut(&k) {
                o.update(v);
            } else {
                self.endpoints.insert(k, v);
            }
        }
        self.worker.update(c.worker);
    }
}

#[derive(Debug, Default, Deserialize)]
pub struct HttpConfig {
    pub bind_addr: Option<String>,
    pub endpoint: Option<String>,
}

impl HttpConfig {
    pub fn update(&mut self, c: HttpConfig) {
        if c.bind_addr.is_some() {
            self.bind_addr = c.bind_addr;
        }
        if c.endpoint.is_some() {
            self.endpoint = c.endpoint;
        }
    }
}

#[derive(Debug, Default, Clone, Deserialize)]
pub struct EndpointConfig {
    pub cookie: String,
}

impl EndpointConfig {
    pub fn update(&mut self, c: EndpointConfig) {
        self.cookie = c.cookie;
    }
}

#[derive(Debug, Default, Clone, Deserialize)]
pub struct WorkerConfig {
    #[serde(default)]
    pub authorized_workers: Vec<String>,
    pub signup_secret: Option<String>,
}

impl WorkerConfig {
    pub fn update(&mut self, c: WorkerConfig) {
        if !c.authorized_workers.is_empty() {
            self.authorized_workers = c.authorized_workers;
        }
        if c.signup_secret.is_some() {
            self.signup_secret = c.signup_secret;
        }
    }
}