Skip to content

Commit b16bb99

Browse files
committed
packages/ak-common/mode: init (#21259)
Squashed commit of the following: commit 6ce8e12 Merge: 0edc38e 0079984 Author: Marc 'risson' Schmitt <marc.schmitt@risson.space> Date: Wed Apr 1 17:09:25 2026 +0200 Merge branch 'rust-lib-rename' into rust-lib-mode Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> commit 0079984 Author: Marc 'risson' Schmitt <marc.schmitt@risson.space> Date: Wed Apr 1 17:08:07 2026 +0200 packages/ak-common: rename from ak-lib Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> commit 0edc38e Merge: 775a971 a3fea5d Author: Marc 'risson' Schmitt <marc.schmitt@risson.space> Date: Wed Apr 1 16:52:25 2026 +0200 Merge branch 'fix-rustfmt-config' into rust-lib-mode commit a3fea5d Author: Marc 'risson' Schmitt <marc.schmitt@risson.space> Date: Wed Apr 1 16:32:16 2026 +0200 root: fix rustfmt config Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> commit 775a971 Author: Marc 'risson' Schmitt <marc.schmitt@risson.space> Date: Wed Apr 1 15:44:16 2026 +0200 add docs Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> commit 52d6fc1 Merge: e4ca5d9 55e555c Author: Marc 'risson' Schmitt <marc.schmitt@risson.space> Date: Tue Mar 31 13:54:07 2026 +0200 Merge branch 'main' into rust-lib-mode Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> commit e4ca5d9 Author: Marc 'risson' Schmitt <marc.schmitt@risson.space> Date: Mon Mar 30 20:16:24 2026 +0200 packages/ak-lib/mode: init Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> commit 524b788 Author: Marc 'risson' Schmitt <marc.schmitt@risson.space> Date: Mon Mar 30 19:57:51 2026 +0200 fixup Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> commit dc65ab1 Author: Marc 'risson' Schmitt <marc.schmitt@risson.space> Date: Mon Mar 30 16:33:49 2026 +0200 packages/ak-lib: init Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
1 parent 77a1c18 commit b16bb99

4 files changed

Lines changed: 201 additions & 1 deletion

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ tracing = "= 0.1.44"
6464
url = "= 2.5.8"
6565
uuid = { version = "= 1.23.0", features = ["serde", "v4"] }
6666

67-
ak-common = { package = "authentik-common", version = "2026.5.0-rc1", path = "./packages/ak-common" }
67+
ak-common = { package = "authentik-common", version = "2026.5.0-rc1", path = "./packages/ak-common", default-features = false }
6868

6969
[profile.dev.package.backtrace]
7070
opt-level = 3

packages/ak-common/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ repository.workspace = true
99
license-file.workspace = true
1010
publish.workspace = true
1111

12+
[features]
13+
default = ["core", "proxy"]
14+
core = []
15+
proxy = []
16+
1217
[dependencies]
1318
arc-swap.workspace = true
1419
axum-server.workspace = true

packages/ak-common/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
pub mod arbiter;
44
pub use arbiter::{Arbiter, Event, Tasks};
55
pub mod config;
6+
pub mod mode;
7+
pub use mode::Mode;
68
pub mod tokio;
79

810
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

packages/ak-common/src/mode.rs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
//! Utilities to manage the current execution mode.
2+
3+
use std::{
4+
env,
5+
path::PathBuf,
6+
sync::atomic::{AtomicU8, Ordering},
7+
};
8+
9+
use eyre::{Result, eyre};
10+
use tracing::debug;
11+
12+
/// Stores the current mode.
13+
static MODE: AtomicU8 = AtomicU8::new(0);
14+
15+
fn mode_path() -> PathBuf {
16+
env::temp_dir().join("authentik-mode")
17+
}
18+
19+
/// authentik execution mode.
20+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
21+
#[repr(u8)]
22+
pub enum Mode {
23+
/// Running both the server and the worker.
24+
#[cfg(feature = "core")]
25+
AllInOne = 0,
26+
/// Running the server.
27+
#[cfg(feature = "core")]
28+
Server = 1,
29+
/// Running the worker.
30+
#[cfg(feature = "core")]
31+
Worker = 2,
32+
/// Running the proxy outpost.
33+
#[cfg(feature = "proxy")]
34+
Proxy = 128,
35+
}
36+
37+
impl std::fmt::Display for Mode {
38+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39+
match self {
40+
#[cfg(feature = "core")]
41+
Self::AllInOne => write!(f, "allinone"),
42+
#[cfg(feature = "core")]
43+
Self::Server => write!(f, "server"),
44+
#[cfg(feature = "core")]
45+
Self::Worker => write!(f, "worker"),
46+
#[cfg(feature = "proxy")]
47+
Self::Proxy => write!(f, "proxy"),
48+
}
49+
}
50+
}
51+
52+
impl From<Mode> for u8 {
53+
#[expect(clippy::as_conversions, reason = "repr of enum is u8")]
54+
fn from(value: Mode) -> Self {
55+
value as Self
56+
}
57+
}
58+
59+
impl Mode {
60+
/// Get the current mode.
61+
pub fn get() -> Self {
62+
match MODE.load(Ordering::Relaxed) {
63+
#[cfg(feature = "core")]
64+
0 => Self::AllInOne,
65+
#[cfg(feature = "core")]
66+
1 => Self::Server,
67+
#[cfg(feature = "core")]
68+
2 => Self::Worker,
69+
#[cfg(feature = "proxy")]
70+
128 => Self::Proxy,
71+
_ => unreachable!(),
72+
}
73+
}
74+
75+
/// Set the current mode.
76+
pub fn set(mode: Self) -> Result<()> {
77+
std::fs::write(mode_path(), mode.to_string())?;
78+
MODE.store(mode.into(), Ordering::SeqCst);
79+
Ok(())
80+
}
81+
82+
/// Load the current mode from the filesystem.
83+
pub fn load() -> Result<()> {
84+
let mode = std::fs::read_to_string(mode_path())?;
85+
let mode = match mode.trim() {
86+
#[cfg(feature = "core")]
87+
"allinone" => Self::AllInOne,
88+
#[cfg(feature = "core")]
89+
"server" => Self::Server,
90+
#[cfg(feature = "core")]
91+
"worker" => Self::Worker,
92+
#[cfg(feature = "proxy")]
93+
"proxy" => Self::Proxy,
94+
_ => return Err(eyre!("Mode {mode} not supported")),
95+
};
96+
MODE.store(mode.into(), Ordering::SeqCst);
97+
Ok(())
98+
}
99+
100+
/// Cleanup the mode stored on the filesystem.
101+
pub fn cleanup() {
102+
let mode_path = mode_path();
103+
if let Err(err) = std::fs::remove_file(&mode_path) {
104+
debug!(%err, mode_path = %&mode_path.display(), "failed to remove mode file");
105+
}
106+
}
107+
108+
/// Check if the mode is one of the "core" modes, namely [`Mode::AllInOne`], [`Mode::Server`]
109+
/// or [`Mode::Worker`].
110+
#[must_use]
111+
pub fn is_core() -> bool {
112+
match Self::get() {
113+
#[cfg(feature = "core")]
114+
Self::AllInOne | Self::Server | Self::Worker => true,
115+
_ => false,
116+
}
117+
}
118+
}
119+
120+
#[cfg(test)]
121+
mod tests {
122+
use tempfile::{TempDir, tempdir};
123+
124+
use super::Mode;
125+
126+
fn prepare_temp_dir() -> TempDir {
127+
let tempdir = tempdir().expect("failed to create tempdir");
128+
#[expect(unsafe_code, reason = "testing")]
129+
// SAFETY: testing
130+
unsafe {
131+
std::env::set_var("TMPDIR", tempdir.path());
132+
}
133+
tempdir
134+
}
135+
136+
#[test]
137+
fn get_and_set() {
138+
let temp_dir = prepare_temp_dir();
139+
let mode_path = temp_dir.path().join("authentik-mode");
140+
for mode in [
141+
(Mode::AllInOne, "allinone"),
142+
(Mode::Server, "server"),
143+
(Mode::Worker, "worker"),
144+
(Mode::Proxy, "proxy"),
145+
] {
146+
Mode::set(mode.0).expect("failed to set mode");
147+
assert_eq!(Mode::get(), mode.0);
148+
assert_eq!(
149+
std::fs::read_to_string(&mode_path).expect("failed to read mode"),
150+
mode.1
151+
);
152+
}
153+
}
154+
155+
#[test]
156+
fn load() {
157+
let temp_dir = prepare_temp_dir();
158+
let mode_path = temp_dir.path().join("authentik-mode");
159+
for mode in [
160+
("allinone", Mode::AllInOne),
161+
("server", Mode::Server),
162+
("worker", Mode::Worker),
163+
("proxy", Mode::Proxy),
164+
] {
165+
std::fs::write(&mode_path, mode.0).expect("failed to write mode");
166+
Mode::load().expect("failed to load mode");
167+
assert_eq!(Mode::get(), mode.1);
168+
}
169+
}
170+
171+
#[test]
172+
fn cleanup() {
173+
let temp_dir = prepare_temp_dir();
174+
let mode_path = temp_dir.path().join("authentik-mode");
175+
Mode::set(Mode::AllInOne).expect("failed to set mode");
176+
Mode::cleanup();
177+
mode_path.metadata().expect_err("mode file still exists");
178+
}
179+
180+
#[test]
181+
fn is_core() {
182+
let _temp_dir = prepare_temp_dir();
183+
for mode in [
184+
(Mode::AllInOne, true),
185+
(Mode::Server, true),
186+
(Mode::Worker, true),
187+
(Mode::Proxy, false),
188+
] {
189+
Mode::set(mode.0).expect("failed to set mode");
190+
assert_eq!(Mode::is_core(), mode.1);
191+
}
192+
}
193+
}

0 commit comments

Comments
 (0)