-
Notifications
You must be signed in to change notification settings - Fork 110
/
settings.rs
238 lines (214 loc) · 8.05 KB
/
settings.rs
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
use crate::{
api::GatewayStakingMode, keypair, region, releases, Error, KeyedUri, Keypair, Region, Result,
};
use config::{Config, Environment, File};
use http::uri::Uri;
pub use log_method::LogMethod;
use serde::Deserialize;
use std::{collections::HashMap, fmt, path::Path, str::FromStr, sync::Arc};
pub fn version() -> semver::Version {
semver::Version::parse(env!("CARGO_PKG_VERSION")).expect("unable to parse version")
}
/// Settings are all the configuration parameters the service needs to operate.
#[derive(Debug, Deserialize)]
pub struct Settings {
/// The listen address to use for listening for the semtech UDP packet forwarder.
/// Default "127.0.0.1:1680"
#[serde(default = "default_listen")]
pub listen: String,
/// The listening network port for the grpc / jsonrpc API.
/// Default 4467
#[serde(default = "default_api")]
pub api: u16,
/// The location of the keypair binary file for the gateway. Defaults to
/// "/etc/helium_gateway/keypair.bin". If the keyfile is not found there a new
/// one is generated and saved in that location.
#[serde(deserialize_with = "keypair::deserialize")]
pub keypair: Arc<Keypair>,
/// The lorawan region to use. This value should line up with the configured
/// region of the semtech packet forwarder. Defaults to "US915"
#[serde(deserialize_with = "region::deserialize")]
pub region: Region,
/// Log settings
pub log: LogSettings,
/// Update settings
pub update: UpdateSettings,
/// The router to deliver packets to when no routers are found while
/// processing a packet.
pub router: HashMap<String, KeyedUri>,
/// The validator(s) to query for chain related state. Defaults to a Helium
/// validator.
pub gateways: Vec<KeyedUri>,
/// Cache settings
pub cache: CacheSettings,
}
/// Settings for log method and level to be used by the running service.
#[derive(Debug, Deserialize)]
pub struct LogSettings {
/// Log level to show (default info)
#[serde(deserialize_with = "log_level::deserialize")]
pub level: log_level::Level,
/// Which log method to use (stdio or syslog, default stdio)
pub method: log_method::LogMethod,
/// Whehter to show timestamps in the stdio output stream (default false)
pub timestamp: bool,
}
/// Settings for log method and level to be used by the running service.
#[derive(Debug, Deserialize)]
pub struct UpdateSettings {
/// Whether the auto-update system is enabled (default: true)
pub enabled: bool,
/// How often to check for updates (in minutes, default: 10)
pub interval: u32,
/// Which udpate channel to use (alpha, beta, release, semver).
/// Defaults to semver which is the channel specified in the running app.
#[serde(deserialize_with = "releases::deserialize_channel")]
pub channel: releases::Channel,
/// The platform identifier to use for released packages (default: klkgw)
pub platform: String,
/// The github release url to use (default
/// https://api.github.com/repos/helium/gateway-rs/releases)
#[serde(with = "http_serde::uri")]
pub uri: Uri,
/// The command to use to install an update. There will be just one
/// parameter which is the path to the new package to install.
pub command: String,
}
/// Settings for cache storage
#[derive(Debug, Deserialize, Clone)]
pub struct CacheSettings {
// Maximum number of packets to queue up per router client
pub max_packets: u16,
}
impl Settings {
/// Load Settings from a given path. Settings are loaded from a default.toml
/// file in the given path, followed by merging in an optional settings.toml
/// in the same folder.
///
/// Environemnt overrides have the same name as the entries in the settings
/// file in uppercase and prefixed with "GW_". For example "GW_KEY" will
/// override the key file location.
pub fn new(path: &Path) -> Result<Self> {
let default_file = path.join("default.toml");
let settings_file = path.join("settings.toml");
Config::builder()
// Source default config
.add_source(File::with_name(default_file.to_str().expect("file name")))
// Add optional settings file
.add_source(File::with_name(settings_file.to_str().expect("file name")).required(false))
// Add in settings from the environment (with a prefix of APP)
// Eg.. `GW_DEBUG=1 ./target/app` would set the `debug` key
.add_source(Environment::with_prefix("gw").separator("_"))
.build()
.and_then(|config| config.try_deserialize())
.map_err(|e| e.into())
}
pub fn default_router(&self) -> &KeyedUri {
&self.router[&self.update.channel.to_string()]
}
}
fn default_listen() -> String {
"127.0.0.1:1680".to_string()
}
fn default_api() -> u16 {
4467
}
#[derive(Debug)]
#[repr(u8)]
pub enum StakingMode {
DataOnly = 0,
Light = 1,
Full = 2,
}
impl From<GatewayStakingMode> for StakingMode {
fn from(v: GatewayStakingMode) -> Self {
match v {
GatewayStakingMode::Dataonly => StakingMode::DataOnly,
GatewayStakingMode::Full => StakingMode::Full,
GatewayStakingMode::Light => StakingMode::Light,
}
}
}
impl From<&StakingMode> for GatewayStakingMode {
fn from(v: &StakingMode) -> Self {
match v {
StakingMode::DataOnly => GatewayStakingMode::Dataonly,
StakingMode::Full => GatewayStakingMode::Full,
StakingMode::Light => GatewayStakingMode::Light,
}
}
}
impl fmt::Display for StakingMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
StakingMode::DataOnly => f.write_str("dataonly"),
StakingMode::Full => f.write_str("full"),
StakingMode::Light => f.write_str("light"),
}
}
}
impl FromStr for StakingMode {
type Err = Error;
fn from_str(v: &str) -> Result<Self> {
match v.to_lowercase().as_ref() {
"light" => Ok(Self::Light),
"full" => Ok(Self::Full),
"dataonly" => Ok(Self::DataOnly),
_ => Err(Error::custom(format!("invalid staking mode {v}"))),
}
}
}
pub mod log_level {
use serde::{de, Deserialize, Deserializer};
pub type Level = slog::Level;
pub fn deserialize<'de, D>(d: D) -> std::result::Result<slog::Level, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(d)?;
s.parse()
.map_err(|_| de::Error::custom(format!("invalid log level \"{s}\"")))
}
}
pub mod log_method {
use serde::de::{self, Deserialize, Deserializer, Visitor};
use std::fmt;
/// The method to use for logging.
#[derive(Debug)]
pub enum LogMethod {
/// Display logging information on stdout
Stdio,
/// Send logging information to syslog
Syslog,
}
impl<'de> Deserialize<'de> for LogMethod {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct LogMethodVisitor;
impl<'de> Visitor<'de> for LogMethodVisitor {
type Value = LogMethod;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("log method")
}
fn visit_str<E>(self, value: &str) -> std::result::Result<LogMethod, E>
where
E: de::Error,
{
let method = match value.to_lowercase().as_str() {
"stdio" => LogMethod::Stdio,
"syslog" => LogMethod::Syslog,
unsupported => {
return Err(de::Error::custom(format!(
"unsupported log method: \"{unsupported}\""
)))
}
};
Ok(method)
}
}
deserializer.deserialize_str(LogMethodVisitor)
}
}
}