aboutsummaryrefslogtreecommitdiffstats
path: root/zone_nspawn/src/container.rs
blob: 5a1dc71ed62c99f5ea78c5275223e98fb6e14658 (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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use crate::Config;
use crate::NetworkConfig;
use crate::{Error, Result};
use derive_builder::Builder;
use serde::{Deserialize, Serialize};
use serde_ini::de::from_bufread;
use std::fs::File;
use std::io::BufReader;
use std::{collections::HashMap, ffi::OsString, fs, path::PathBuf, process::Command};

#[derive(Builder, Debug, Serialize, Deserialize, Clone)]
#[builder(build_fn(private, name = "build_container"), derive(Debug))]
pub struct Container {
    pub machine: OsString,

    #[builder(setter(skip))]
    pub class: OsString,

    #[builder(setter(skip))]
    pub service: OsString,

    #[builder(setter(skip))]
    pub os: OsString,

    #[builder(setter(skip))]
    pub version: OsString,

    #[builder(setter(skip))]
    pub addresses: OsString,

    #[builder(setter(each = "add_network_config"))]
    #[serde(skip_deserializing)]
    pub network_configs: HashMap<PathBuf, NetworkConfig>,

    #[builder(setter(each = "add_option"))]
    #[serde(skip_deserializing)]
    pub options: Vec<String>,
}

impl Container {
    fn create(&self) -> Result<Container> {
        // TODO
        // exec systemd-nspawn --settings=trusted --quiet --console=interactive
        // --link-journal=no --resolv-conf=off --timezone=off --capability=all
        // --boot --directory=${SRVRPATH} --private-users=false --bind-ro=/sys/module
        // --bind-ro=/lib/modules --network-zone=${USERNAME}
        // --network-veth-extra=vn-${SRVRNAME}:host9
        Command::new("systemd-nspawn")
            .args(&self.options)
            .status()?
            .success()
            .then(|| self)
            .ok_or_else(|| Error::NSpawn(format!("Failed to create container: {:?}", self)))?
            .network_configuration()
            .map(|_| self.to_owned())
    }

    fn network_configuration(&self) -> Result<()> {
        for (filebuf, network_config) in self.network_configs.iter() {
            fs::write(filebuf, serde_ini::ser::to_vec(network_config)?)?;
        }

        Ok(())
    }
}

impl ContainerBuilder {
    pub fn build(&self) -> Result<Container> {
        self.build_container()?.create()
    }

    pub fn build_with_config(&mut self, config: &Config) -> Result<Container> {
        self.add_network_configs_from_config(config)?
            .build_container()?
            .create()
    }

    fn add_network_configs_from_config(&mut self, config: &Config) -> Result<&mut Self> {
        let dir = match &config.network_configs_path {
            Some(it) => it,
            None => {
                return Err(Error::FileError(format!(
                    "Failed to create container: {:?}",
                    self
                )))
            }
        };

        if dir.is_dir() {
            for entry in fs::read_dir(dir)? {
                if let Ok(entry) = entry {
                    let filename = entry.file_name();
                    let file = File::open(entry.path())?;
                    let file_contents =
                        match from_bufread(
                            BufReader::new(file),
                        ) {
                            Ok(it) => it,
                            Err(_) => {
                                return Err(Error::FileError(format!(
                                    "Failed to create container: {:?}",
                                    self
                                )))
                            }
                        };
                    self.add_network_config((PathBuf::from(filename), file_contents));
                }
            }
        }

        Ok(self)
    }
}