aboutsummaryrefslogtreecommitdiff
path: root/src/config/runtime.rs
blob: ee7f682538c5779a0e083e4643a4fa7da62248e1 (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
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::time::Duration;

use anyhow::{Result, anyhow};

use crate::config::{ConfigOpts, ConfigOptsConsul, ConfigOptsAcme, ConfigOptsFirewall, ConfigOptsIgd};

// 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.

// Consul config is mandatory, all the others are optional.

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

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

#[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 consul: RuntimeConfigConsul,
	pub acme: Option<RuntimeConfigAcme>,
	pub firewall: Option<RuntimeConfigFirewall>,
	pub igd: Option<RuntimeConfigIgd>,
}

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

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

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

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

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

		let email = opts.email.expect(
			"'DIPLONAT_ACME_EMAIL' is required if ACME is enabled");
		let refresh_time = Duration::from_secs(
				opts.refresh_time.unwrap_or(super::REFRESH_TIME).into());

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

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

		let refresh_time = Duration::from_secs(
				opts.refresh_time.unwrap_or(super::REFRESH_TIME).into());

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

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

		let private_ip = opts.private_ip.expect(
			"'DIPLONAT_IGD_PRIVATE_IP' is required if IGD is enabled");
		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(Some(Self {
			private_ip,
			expiration_time,
			refresh_time,
		}))
	}
}