aboutsummaryrefslogtreecommitdiffstats
path: root/zoned/src/api.rs
blob: 523676418372204f6e35e6b4f9dc98ef34d6e76f (plain)
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
use axum::{
    extract::{ws::WebSocketUpgrade, Extension, Query, TypedHeader},
    headers,
    response::IntoResponse,
    routing::{get, post},
    Json, Router,
};
use std::sync::Arc;
use tracing::{info, instrument, warn};
use zone_core::{Container, ContainerOptions, FilterContainer};
use zone_nspawn::NSpawn;

use crate::{ws, Error, 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("/ws", 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(state.zfs.config.pool_name.to_owned())
}

/// List containers
///
/// Returns a list of containers based on the query.
#[instrument(err, ret)]
async fn container_list(
    container: Option<Query<ContainerOptions>>,
) -> Result<Json<Vec<Container>>> {
    let mut containers = NSpawn::get_containers()?.into_iter().filter_map(|c| {
        Container::try_from(c)
            .map_err(|err| warn!("Ignoring invalid nspawn container {:?}", err))
            .ok()
    });

    match container {
        Some(Query(params)) => Ok(containers.filter_container(params).into()),
        _ => Ok(containers.collect::<Vec<_>>().into()),
    }
}

/// Create container
///
/// Creates a new container volume from the provided container json data
#[instrument(err, ret, skip(state))]
async fn clone_container(
    Json(container): Json<Container>,
    Extension(state): Extension<Arc<State>>,
) -> Result<Json<Container>> {
    state
        .zfs
        .clone_from_latest(
            format!("{}-{}", container.user, container.id).into(),
            container.template.into(),
        )?
        .try_into()
        .map_err(Error::from)
        .map(Container::into)
}

/// Upgrade to websocket
///
/// Creates a new container volume from the provided container json data
#[instrument(ret, skip_all)]
async fn ws_upgrade(
    ws: WebSocketUpgrade,
    user_agent: Option<TypedHeader<headers::UserAgent>>,
    Extension(state): Extension<Arc<State>>,
) -> impl IntoResponse {
    let ua = user_agent.map_or("Unknown".to_string(), |u| u.to_string());
    info!(%ua, "Client connected");

    ws.on_upgrade(|socket| ws::handler(socket, state))
}

#[cfg(test)]
mod tests {
    #[test]
    fn hello_world() {
        // use super::*;
        assert!("true" == "true");
    }
}