aboutsummaryrefslogtreecommitdiff
path: root/src/config/runtime.rs
blob: 58c86b951a92861a704e395862e0b1f980bd2db4 (plain) (blame)
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
use std::time::Duration;

use anyhow::{Result, anyhow};

use crate::config::{ConfigOpts, ConfigOptsAcme, ConfigOptsBase, ConfigOptsConsul};

// This code is inspired by the Trunk crate (https://github.com/thedodd/trunk)

// In this file, we take ConfigOpts and transform them into ready-to-use RuntimeConfig.
// We apply default values and business logic.

#[derive(Debug)]
pub struct RuntimeConfigAcme {
	pub email: String,
}

#[derive(Debug)]
pub struct RuntimeConfigConsul {
	pub node_name: String,
	pub url: String,
}

#[derive(Debug)]
pub struct RuntimeConfigFirewall {
	pub refresh_time: Duration,
}

#[derive(Debug)]
pub struct RuntimeConfigIgd {
	pub private_ip: String,
	pub expiration_time: Duration,
	pub refresh_time: Duration,
}

#[derive(Debug)]
pub struct RuntimeConfig {
	pub acme: Option<RuntimeConfigAcme>,
	pub consul: RuntimeConfigConsul,
	pub firewall: RuntimeConfigFirewall,
	pub igd: RuntimeConfigIgd,
}

impl RuntimeConfig {
	pub fn new(opts: ConfigOpts) -> Result<Self> {
		let acme = RuntimeConfigAcme::new(opts.acme.clone())?;
		let consul = RuntimeConfigConsul::new(opts.consul.clone())?;
		let firewall = RuntimeConfigFirewall::new(opts.base.clone())?;
		let igd = RuntimeConfigIgd::new(opts.base.clone())?;

		Ok(Self {
			acme,
			consul,
			firewall,
			igd,
		})
	}
}

impl RuntimeConfigAcme {
	pub fn new(opts: ConfigOptsAcme) -> Result<Option<Self>> {
		if !opts.enable {
			return Ok(None);
		}

		let email = opts.email.expect(
			"'DIPLONAT_ACME_EMAIL' environment variable is required \
			if 'DIPLONAT_ACME_ENABLE' == 'true'");

		Ok(Some(Self {
			email,
		}))
	}
}

impl RuntimeConfigConsul {
	pub(super) fn new(opts: ConfigOptsConsul) -> Result<Self> {
		let node_name = opts.node_name.expect(
			"'DIPLONAT_CONSUL_NODE_NAME' environment variable is required");
		let url = opts.url.unwrap_or(super::CONSUL_URL.to_string());

		Ok(Self {
			node_name,
			url,
		})
	}
}

impl RuntimeConfigFirewall {
	pub(super) fn new(opts: ConfigOptsBase) -> Result<Self> {
		let refresh_time = Duration::from_secs(
				opts.refresh_time.unwrap_or(super::REFRESH_TIME).into());

		Ok(Self {
			refresh_time,
		})
	}
}

impl RuntimeConfigIgd {
	pub(super) fn new(opts: ConfigOptsBase) -> Result<Self> {
		let private_ip = opts.private_ip.expect(
			"'DIPLONAT_PRIVATE_IP' environment variable is required");
		let expiration_time = Duration::from_secs(
				opts.expiration_time.unwrap_or(super::EXPIRATION_TIME).into());
		let refresh_time = Duration::from_secs(
				opts.refresh_time.unwrap_or(super::REFRESH_TIME).into());

		if refresh_time.as_secs() * 2 > expiration_time.as_secs() {
	      return Err(anyhow!(
	        "IGD expiration time (currently: {}s) must be at least twice bigger than refresh time (currently: {}s)", 
	        expiration_time.as_secs(),
	        refresh_time.as_secs()));
	    }

		Ok(Self {
			private_ip,
			expiration_time,
			refresh_time,
		})
	}
}