Skip to content

Commit 7f51d2f

Browse files
authored
Allow specifying configuration params in env vars (#83)
1 parent 5ffd179 commit 7f51d2f

File tree

6 files changed

+191
-44
lines changed

6 files changed

+191
-44
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ thiserror = "2.0"
1414
futures = "^0.3.25"
1515
serde_json = "^1.0"
1616
serde = { version = "^1.0.147", features = ["derive"] }
17-
clap = { version = "^4.5.6", features = ["string"] }
17+
clap = { version = "^4.5.6", features = ["string", "env"] }
1818
log = "^0.4.17"
1919
env_logger = "^0.11.5"
2020
rusqlite = { version = "0.32", features = ["bundled"] }
2121
chrono = { version = "^0.4.38", features = ["serde"] }
2222
actix-rt = "2"
2323
tempfile = "3"
2424
pretty_assertions = "1"
25+
temp-env = "0.3"

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,18 @@ The server is configured with command-line options. See
7373

7474
The `--listen` option specifies the interface and port the server listens on.
7575
It must contain an IP-Address or a DNS name and a port number. This option is
76-
mandatory, but can be repeated to specify multiple interfaces or ports.
76+
mandatory, but can be repeated to specify multiple interfaces or ports. This
77+
value can be specified in environment variable `LISTEN`, as a comma-separated
78+
list of values.
7779

78-
The `--data-dir` option specifies where the server should store its data.
80+
The `--data-dir` option specifies where the server should store its data. This
81+
value can be specified in the environment variable `DATA_DIR`.
7982

8083
By default, the server allows all client IDs. To limit the accepted client IDs,
81-
such as when running a personal server, use `--allow-client-id <client-id>`.
84+
specify them in the environment variable `CLIENT_ID`, as a comma-separated list
85+
of UUIDs. Client IDs can be specified with `--allow-client-id`, but this should
86+
not be used on shared systems, as command line arguments are visible to all
87+
users on the system.
8288

8389
The server only logs errors by default. To add additional logging output, set
8490
environment variable `RUST_LOG` to `info` to get a log message for every

docker-compose.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ services:
5656
volume:
5757
nocopy: true
5858
subpath: tss
59-
command: --data-dir /tss/taskchampion-sync-server --listen 0.0.0.0:8080
6059
environment:
61-
- RUST_LOG=info
60+
- "RUST_LOG=info"
61+
- "DATA_DIR=/tss/taskchampion-sync-server"
62+
- "LISTEN=0.0.0.0:8080"
6263
depends_on:
6364
mkdir:
6465
condition: service_completed_successfully

server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ chrono.workspace = true
2424
actix-rt.workspace = true
2525
tempfile.workspace = true
2626
pretty_assertions.workspace = true
27+
temp-env.workspace = true

server/src/bin/taskchampion-sync-server.rs

Lines changed: 166 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,36 @@ fn command() -> Command {
2323
.arg(
2424
arg!(-l --listen <ADDRESS>)
2525
.help("Address and Port on which to listen on. Can be an IP Address or a DNS name followed by a colon and a port e.g. localhost:8080")
26+
.value_delimiter(',')
2627
.value_parser(ValueParser::string())
28+
.env("LISTEN")
2729
.action(ArgAction::Append)
2830
.required(true),
2931
)
3032
.arg(
3133
arg!(-d --"data-dir" <DIR> "Directory in which to store data")
3234
.value_parser(ValueParser::os_string())
35+
.env("DATA_DIR")
3336
.default_value("/var/lib/taskchampion-sync-server"),
3437
)
3538
.arg(
3639
arg!(-C --"allow-client-id" <CLIENT_ID> "Client IDs to allow (can be repeated; if not specified, all clients are allowed)")
40+
.value_delimiter(',')
3741
.value_parser(value_parser!(Uuid))
42+
.env("CLIENT_ID")
3843
.action(ArgAction::Append)
3944
.required(false),
4045
)
4146
.arg(
4247
arg!(--"snapshot-versions" <NUM> "Target number of versions between snapshots")
4348
.value_parser(value_parser!(u32))
49+
.env("SNAPSHOT_VERSIONS")
4450
.default_value(default_snapshot_versions),
4551
)
4652
.arg(
4753
arg!(--"snapshot-days" <NUM> "Target number of days between snapshots")
4854
.value_parser(value_parser!(i64))
55+
.env("SNAPSHOT_DAYS")
4956
.default_value(default_snapshot_days),
5057
)
5158
}
@@ -95,6 +102,7 @@ mod test {
95102
use actix_web::{self, App};
96103
use clap::ArgMatches;
97104
use taskchampion_sync_server_core::InMemoryStorage;
105+
use temp_env::{with_var, with_var_unset, with_vars, with_vars_unset};
98106

99107
/// Get the list of allowed client IDs
100108
fn allowed(matches: &ArgMatches) -> Option<Vec<Uuid>> {
@@ -103,60 +111,180 @@ mod test {
103111
.map(|ids| ids.copied().collect::<Vec<_>>())
104112
}
105113

114+
#[test]
115+
fn command_listen_two() {
116+
with_var_unset("LISTEN", || {
117+
let matches = command().get_matches_from([
118+
"tss",
119+
"--listen",
120+
"localhost:8080",
121+
"--listen",
122+
"otherhost:9090",
123+
]);
124+
assert_eq!(
125+
matches
126+
.get_many::<String>("listen")
127+
.unwrap()
128+
.cloned()
129+
.collect::<Vec<String>>(),
130+
vec!["localhost:8080".to_string(), "otherhost:9090".to_string()]
131+
);
132+
});
133+
}
134+
135+
#[test]
136+
fn command_listen_two_env() {
137+
with_var("LISTEN", Some("localhost:8080,otherhost:9090"), || {
138+
let matches = command().get_matches_from(["tss"]);
139+
assert_eq!(
140+
matches
141+
.get_many::<String>("listen")
142+
.unwrap()
143+
.cloned()
144+
.collect::<Vec<String>>(),
145+
vec!["localhost:8080".to_string(), "otherhost:9090".to_string()]
146+
);
147+
});
148+
}
149+
106150
#[test]
107151
fn command_allowed_client_ids_none() {
108-
let matches = command().get_matches_from(["tss", "--listen", "localhost:8080"]);
109-
assert_eq!(allowed(&matches), None);
152+
with_var_unset("CLIENT_ID", || {
153+
let matches = command().get_matches_from(["tss", "--listen", "localhost:8080"]);
154+
assert_eq!(allowed(&matches), None);
155+
});
110156
}
111157

112158
#[test]
113159
fn command_allowed_client_ids_one() {
114-
let matches = command().get_matches_from([
115-
"tss",
116-
"--listen",
117-
"localhost:8080",
118-
"-C",
119-
"711d5cf3-0cf0-4eb8-9eca-6f7f220638c0",
120-
]);
121-
assert_eq!(
122-
allowed(&matches),
123-
Some(vec![Uuid::parse_str(
124-
"711d5cf3-0cf0-4eb8-9eca-6f7f220638c0"
125-
)
126-
.unwrap()])
160+
with_var_unset("CLIENT_ID", || {
161+
let matches = command().get_matches_from([
162+
"tss",
163+
"--listen",
164+
"localhost:8080",
165+
"-C",
166+
"711d5cf3-0cf0-4eb8-9eca-6f7f220638c0",
167+
]);
168+
assert_eq!(
169+
allowed(&matches),
170+
Some(vec![Uuid::parse_str(
171+
"711d5cf3-0cf0-4eb8-9eca-6f7f220638c0"
172+
)
173+
.unwrap()])
174+
);
175+
});
176+
}
177+
178+
#[test]
179+
fn command_allowed_client_ids_one_env() {
180+
with_var(
181+
"CLIENT_ID",
182+
Some("711d5cf3-0cf0-4eb8-9eca-6f7f220638c0"),
183+
|| {
184+
let matches = command().get_matches_from(["tss", "--listen", "localhost:8080"]);
185+
assert_eq!(
186+
allowed(&matches),
187+
Some(vec![Uuid::parse_str(
188+
"711d5cf3-0cf0-4eb8-9eca-6f7f220638c0"
189+
)
190+
.unwrap()])
191+
);
192+
},
127193
);
128194
}
129195

130196
#[test]
131197
fn command_allowed_client_ids_two() {
132-
let matches = command().get_matches_from([
133-
"tss",
134-
"--listen",
135-
"localhost:8080",
136-
"-C",
137-
"711d5cf3-0cf0-4eb8-9eca-6f7f220638c0",
138-
"-C",
139-
"bbaf4b61-344a-4a39-a19e-8caa0669b353",
140-
]);
141-
assert_eq!(
142-
allowed(&matches),
143-
Some(vec![
144-
Uuid::parse_str("711d5cf3-0cf0-4eb8-9eca-6f7f220638c0").unwrap(),
145-
Uuid::parse_str("bbaf4b61-344a-4a39-a19e-8caa0669b353").unwrap()
146-
])
198+
with_var_unset("CLIENT_ID", || {
199+
let matches = command().get_matches_from([
200+
"tss",
201+
"--listen",
202+
"localhost:8080",
203+
"-C",
204+
"711d5cf3-0cf0-4eb8-9eca-6f7f220638c0",
205+
"-C",
206+
"bbaf4b61-344a-4a39-a19e-8caa0669b353",
207+
]);
208+
assert_eq!(
209+
allowed(&matches),
210+
Some(vec![
211+
Uuid::parse_str("711d5cf3-0cf0-4eb8-9eca-6f7f220638c0").unwrap(),
212+
Uuid::parse_str("bbaf4b61-344a-4a39-a19e-8caa0669b353").unwrap()
213+
])
214+
);
215+
});
216+
}
217+
218+
#[test]
219+
fn command_allowed_client_ids_two_env() {
220+
with_var(
221+
"CLIENT_ID",
222+
Some("711d5cf3-0cf0-4eb8-9eca-6f7f220638c0,bbaf4b61-344a-4a39-a19e-8caa0669b353"),
223+
|| {
224+
let matches = command().get_matches_from(["tss", "--listen", "localhost:8080"]);
225+
assert_eq!(
226+
allowed(&matches),
227+
Some(vec![
228+
Uuid::parse_str("711d5cf3-0cf0-4eb8-9eca-6f7f220638c0").unwrap(),
229+
Uuid::parse_str("bbaf4b61-344a-4a39-a19e-8caa0669b353").unwrap()
230+
])
231+
);
232+
},
147233
);
148234
}
149235

150236
#[test]
151237
fn command_data_dir() {
152-
let matches = command().get_matches_from([
153-
"tss",
154-
"--data-dir",
155-
"/foo/bar",
156-
"--listen",
157-
"localhost:8080",
158-
]);
159-
assert_eq!(matches.get_one::<OsString>("data-dir").unwrap(), "/foo/bar");
238+
with_var_unset("DATA_DIR", || {
239+
let matches = command().get_matches_from([
240+
"tss",
241+
"--data-dir",
242+
"/foo/bar",
243+
"--listen",
244+
"localhost:8080",
245+
]);
246+
assert_eq!(matches.get_one::<OsString>("data-dir").unwrap(), "/foo/bar");
247+
});
248+
}
249+
250+
#[test]
251+
fn command_data_dir_env() {
252+
with_var("DATA_DIR", Some("/foo/bar"), || {
253+
let matches = command().get_matches_from(["tss", "--listen", "localhost:8080"]);
254+
assert_eq!(matches.get_one::<OsString>("data-dir").unwrap(), "/foo/bar");
255+
});
256+
}
257+
258+
#[test]
259+
fn command_snapshot() {
260+
with_vars_unset(["SNAPSHOT_DAYS", "SNAPSHOT_VERSIONS"], || {
261+
let matches = command().get_matches_from([
262+
"tss",
263+
"--listen",
264+
"localhost:8080",
265+
"--snapshot-days",
266+
"13",
267+
"--snapshot-versions",
268+
"20",
269+
]);
270+
assert_eq!(*matches.get_one::<i64>("snapshot-days").unwrap(), 13i64);
271+
assert_eq!(*matches.get_one::<u32>("snapshot-versions").unwrap(), 20u32);
272+
});
273+
}
274+
275+
#[test]
276+
fn command_snapshot_env() {
277+
with_vars(
278+
[
279+
("SNAPSHOT_DAYS", Some("13")),
280+
("SNAPSHOT_VERSIONS", Some("20")),
281+
],
282+
|| {
283+
let matches = command().get_matches_from(["tss", "--listen", "localhost:8080"]);
284+
assert_eq!(*matches.get_one::<i64>("snapshot-days").unwrap(), 13i64);
285+
assert_eq!(*matches.get_one::<u32>("snapshot-versions").unwrap(), 20u32);
286+
},
287+
);
160288
}
161289

162290
#[actix_rt::test]

0 commit comments

Comments
 (0)