summaryrefslogtreecommitdiff
path: root/src/cloudinit.rs
blob: 6c52effee8be046468f4bf40dc47a4763fe2544a (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
use std::default::Default;
use std::fs::write;
use std::path::Path;
use std::process::Command;
use tempfile::tempdir;

#[derive(Debug, thiserror::Error)]
pub enum CloudInitError {
    #[error("failed to create ISO image with genisoimage: {0}")]
    IsoFailed(String),

    #[error(transparent)]
    IoError(#[from] std::io::Error),

    #[error(transparent)]
    StringError(#[from] std::string::FromUtf8Error),
}

#[derive(Default, Debug, Clone)]
pub struct CloudInitConfig {
    hostname: String,
    authorized_keys: String,
}

impl CloudInitConfig {
    pub fn hostname(&self) -> &str {
        &self.hostname
    }

    pub fn set_hostname(&mut self, hostname: &str) {
        self.hostname = hostname.to_string();
    }

    pub fn authorized_keys(&self) -> &str {
        &self.authorized_keys
    }

    pub fn set_authorized_keys(&mut self, keys: &str) {
        self.authorized_keys = keys.to_string();
    }

    fn write(&self, dir: &Path) -> std::io::Result<()> {
        let metadata = dir.join("meta-data");
        let userdata = dir.join("user-data");

        write(
            &metadata,
            format!(
                "# Amazon EC2 style metadata\nlocal-hostname: {}\n",
                self.hostname()
            ),
        )?;
        write(
            &userdata,
            format!(
                "#cloud-config\nssh_authorized_keys:\n- {}",
                self.authorized_keys()
            ),
        )?;

        Ok(())
    }

    pub fn create_iso(&self, filename: &Path) -> Result<(), CloudInitError> {
        let dir = tempdir()?;
        self.write(dir.path())?;

        let r = Command::new("genisoimage")
            .arg("-quiet")
            .arg("-volid")
            .arg("cidata")
            .arg("-joliet")
            .arg("-rock")
            .arg("-output")
            .arg(filename)
            .arg(dir.path())
            .output()?;
        if !r.status.success() {
            let stderr = String::from_utf8(r.stderr)?;
            return Err(CloudInitError::IsoFailed(stderr));
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::CloudInitConfig;

    #[test]
    fn is_empty_by_default() {
        let init = CloudInitConfig::default();
        assert_eq!(init.hostname(), "");
        assert_eq!(init.authorized_keys(), "");
    }

    #[test]
    fn sets_hostname() {
        let mut init = CloudInitConfig::default();
        init.set_hostname("foo");
        assert_eq!(init.hostname(), "foo");
    }

    #[test]
    fn sets_authorized_keys() {
        let mut init = CloudInitConfig::default();
        init.set_authorized_keys("auth");
        assert_eq!(init.authorized_keys(), "auth");
    }
}