Added config reload
Build and Publish / Build and Publish Docker Image (push) Successful in 3m7s

This commit is contained in:
Ultradesu
2026-06-29 20:49:41 +03:00
parent 311a05789e
commit 0be4512293
4 changed files with 133 additions and 8 deletions
+7
View File
@@ -93,16 +93,23 @@ translations! {
configs_loading_status: "Loading status..." , "Загрузка статуса...";
configs_no_pods: "No AmneziaWG pods." , "Нет подов AmneziaWG.";
configs_server: "Server" , "Сервер";
configs_endpoint: "Endpoint" , "Endpoint";
configs_message: "Message" , "Сообщение";
configs_pod: "Pod" , "Pod";
configs_node: "Node" , "Нода";
configs_rollout: "Rollout" , "Применение";
configs_ready: "Ready" , "Готов";
configs_not_ready: "Not ready" , "Не готов";
configs_phase: "Phase" , "Фаза";
configs_uptime: "Uptime" , "Аптайм";
configs_restarts: "Restarts" , "Рестарты";
configs_config_applied: "Config applied" , "Конфиг применён";
configs_reload_status: "Reload status" , "Статус reload";
configs_config_hash: "Config hash" , "Хэш конфига";
configs_status_applied: "applied" , "применён";
configs_status_starting: "starting" , "стартует";
configs_status_pending_restart: "pending restart" , "ждёт рестарт";
configs_status_error: "reload error" , "ошибка reload";
configs_status_unknown: "unknown" , "неизвестно";
configs_never: "n/a" , "н/д";
configs_servers: "Servers" , "Серверы";
+57 -2
View File
@@ -399,6 +399,12 @@ fn qcompress(data: &[u8]) -> Result<Vec<u8>, String> {
const CLIENT_SECRET_UPDATED_AT_ANNOTATION: &str =
"amnezia-fellow.hexor.cy/client-secret-updated-at-ms";
const CLIENT_SECRET_APPLIED_AT_ANNOTATION: &str =
"amnezia-fellow.hexor.cy/client-secret-applied-at-ms";
const CLIENT_SECRET_APPLIED_HASH_ANNOTATION: &str =
"amnezia-fellow.hexor.cy/client-secret-applied-hash";
const CLIENT_SECRET_RELOAD_STATUS_ANNOTATION: &str =
"amnezia-fellow.hexor.cy/client-secret-reload-status";
const AMNEZIAWG_POD_LABEL_SELECTOR: &str = "app=amneziawg";
#[derive(Debug, Clone, Serialize, JsonSchema)]
@@ -613,6 +619,9 @@ pub struct VpnPodRolloutStatus {
pub ready: bool,
pub restart_count: i32,
pub started_at_ms: Option<i64>,
pub config_applied_at_ms: Option<i64>,
pub config_applied_hash: Option<String>,
pub config_reload_status: Option<String>,
pub uptime_seconds: Option<u64>,
pub rollout_status: String,
pub message: Option<String>,
@@ -705,6 +714,7 @@ fn pod_rollout_status(
config_updated_at_ms: Option<i64>,
now_ms: i64,
) -> VpnPodRolloutStatus {
let annotations = pod.metadata.annotations.clone().unwrap_or_default();
let name = pod.metadata.name.unwrap_or_default();
let node_name = pod
.spec
@@ -728,11 +738,26 @@ fn pod_rollout_status(
let started_at_ms = status
.and_then(|status| status.start_time.as_ref())
.map(time_to_millis);
let config_applied_at_ms = annotation_i64(&annotations, CLIENT_SECRET_APPLIED_AT_ANNOTATION);
let config_applied_hash = annotations
.get(CLIENT_SECRET_APPLIED_HASH_ANNOTATION)
.filter(|value| !value.is_empty())
.cloned();
let config_reload_status = annotations
.get(CLIENT_SECRET_RELOAD_STATUS_ANNOTATION)
.filter(|value| !value.is_empty())
.cloned();
let uptime_seconds = started_at_ms.map(|started| {
let elapsed_ms = now_ms.saturating_sub(started);
(elapsed_ms / 1000) as u64
});
let rollout_status = rollout_status(config_updated_at_ms, started_at_ms, ready);
let rollout_status = rollout_status(
config_updated_at_ms,
started_at_ms,
config_applied_at_ms,
config_reload_status.as_deref(),
ready,
);
let message = status.and_then(|status| status.message.clone().or(status.reason.clone()));
VpnPodRolloutStatus {
@@ -744,6 +769,9 @@ fn pod_rollout_status(
ready,
restart_count,
started_at_ms,
config_applied_at_ms,
config_applied_hash,
config_reload_status,
uptime_seconds,
rollout_status,
message,
@@ -781,9 +809,16 @@ fn amneziawg_restart_count(status: &k8s_openapi::api::core::v1::PodStatus) -> i3
fn rollout_status(
config_updated_at_ms: Option<i64>,
started_at_ms: Option<i64>,
config_applied_at_ms: Option<i64>,
config_reload_status: Option<&str>,
ready: bool,
) -> String {
match (config_updated_at_ms, started_at_ms) {
if config_reload_status == Some("error") {
return "error".to_owned();
}
let effective_applied_at_ms = started_at_ms.into_iter().chain(config_applied_at_ms).max();
match (config_updated_at_ms, effective_applied_at_ms) {
(None, _) => "unknown".to_owned(),
(Some(_), None) => "pending_restart".to_owned(),
(Some(updated), Some(started)) if started >= updated && ready => "applied".to_owned(),
@@ -802,6 +837,10 @@ fn client_secret_updated_at_ms(secret: &Secret) -> Option<i64> {
.ok()
}
fn annotation_i64(annotations: &BTreeMap<String, String>, key: &str) -> Option<i64> {
annotations.get(key)?.parse().ok()
}
fn time_to_millis(time: &Time) -> i64 {
time.0.as_millisecond()
}
@@ -1037,6 +1076,22 @@ mod tests {
assert!(!overrides.contains_key("empty"));
}
#[test]
fn rollout_status_uses_hot_reload_annotations() {
assert_eq!(
rollout_status(Some(2_000), Some(1_000), Some(3_000), Some("applied"), true),
"applied"
);
assert_eq!(
rollout_status(Some(2_000), Some(1_000), None, Some("error"), true),
"error"
);
assert_eq!(
rollout_status(Some(2_000), Some(1_000), None, None, true),
"pending_restart"
);
}
#[test]
fn qcompress_uses_qt_wire_format() {
let payload = br#"{"description":"de-fsn1"}"#;