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
|
use axum::{
extract::{ws::WebSocketUpgrade, Extension, Query, TypedHeader},
headers,
response::IntoResponse,
routing::{get, post},
Json, Router,
};
use std::{process::Command, sync::Arc};
use tracing::{info, instrument, warn};
use zone_core::{
CloneOptions, Container, ContainerOptions, Status, FilterContainer, Runtime,
Templates, Volumes,
};
use crate::{error::CommandError, ws, Result, State};
#[instrument()]
pub fn build_routes() -> Router {
Router::new()
.route("/test", get(test_endpoint))
.route("/container", post(clone_container))
.route("/container/list?<container..>", get(container_list))
.route("/container/attach", get(ws_upgrade))
}
/// # Test endpoint
///
/// Returns a list of containers based on the query.
#[instrument(ret, skip(_state))]
async fn test_endpoint(Extension(_state): Extension<Arc<State>>) -> Json<String> {
Json("All good!".into())
}
/// List containers
///
/// Returns a list of containers based on the query.
#[instrument(err, ret)]
async fn container_list(
Query(params): Query<ContainerOptions>,
Extension(state): Extension<Arc<State>>,
) -> Result<Json<Vec<Container>>> {
let containers = state
.nspawn
.list()?
.into_iter()
.filter_container(¶ms)
.into();
Ok(containers)
}
/// Create container
///
/// Creates a new container volume from the provided container json data
#[instrument(err, ret, skip(state))]
async fn clone_container(
Json(clone_options): Json<CloneOptions>,
Extension(state): Extension<Arc<State>>,
) -> Result<Json<(Container, Status)>> {
let container = state
.zfs
.clone_from(clone_options.template, clone_options.owner)?;
let volume = state.zfs.get_volume(&container)?;
if let Some(script) = &state.config.init_script {
Command::new(script)
.env("DIRECTORY", &volume)
.env("USER", &container.owner)
.env("TEMPLATE", &container.template)
.output()
.map_err(CommandError::from)?
.status
.code()
.map_or(Ok(()), |c| Err(CommandError::from(c)))?;
};
state.nspawn.create(volume, &container)?;
Ok(Json::from((container, Status::Running)))
}
/// Upgrade to websocket
///
/// Request to upgrade to a websocket connection
#[instrument(ret, skip_all)]
async fn ws_upgrade(
ws: WebSocketUpgrade,
user_agent: Option<TypedHeader<headers::UserAgent>>,
Extension(state): Extension<Arc<State>>,
) -> impl IntoResponse {
let agent = user_agent.map_or("Unknown".to_string(), |u| u.to_string());
info!(%agent, "Client connected");
ws.on_upgrade(|socket| ws::handler(socket, state))
}
#[cfg(test)]
mod tests {
#[test]
fn hello_world() {
// use super::*;
assert!("true" == "true");
}
}
|