Prepare, configure, and manage Firecracker microVMs in seconds!
virtualization
linux
microvm
firecracker
1use std::{process, thread};
2
3use anyhow::{Context, Error};
4
5use crate::{command::run_command, mqttc, types::VmOptions};
6
7pub const COREDNS_CONFIG_PATH: &str = "/etc/coredns/Corefile";
8pub const COREDNS_SERVICE_TEMPLATE: &str = include_str!("./systemd/coredns.service");
9
10pub fn setup_coredns(config: &VmOptions) -> Result<(), Error> {
11 let api_socket = config.api_socket.clone();
12 if !coredns_is_installed()? {
13 println!("[✗] CoreDNS is not installed. Please install it first to /usr/sbin.");
14 process::exit(1);
15 }
16
17 if !etcd_is_installed()? {
18 println!("[+] Installing etcd...");
19 run_command(
20 "apt-get",
21 &["install", "-y", "etcd-server", "etcd-client"],
22 true,
23 )?;
24 }
25
26 run_command(
27 "sh",
28 &[
29 "-c",
30 &format!(
31 "echo '{}' > {}",
32 include_str!("./coredns/Corefile"),
33 COREDNS_CONFIG_PATH
34 ),
35 ],
36 true,
37 )?;
38
39 run_command(
40 "sh",
41 &[
42 "-c",
43 &format!(
44 "echo '{}' > /etc/systemd/system/coredns.service",
45 COREDNS_SERVICE_TEMPLATE
46 ),
47 ],
48 true,
49 )?;
50 restart_coredns()?;
51
52 let etcd_args = match config.etcd.clone() {
53 Some(etcd) => {
54 let mut args = vec![];
55 if let Some(endpoints) = &etcd.endpoints {
56 args.push(format!("--endpoints={}", endpoints.join(",")));
57 }
58 if let Some(user) = &etcd.user {
59 args.push(format!("--user={}", user));
60 }
61 if let Some(password) = &etcd.password {
62 args.push(format!("--password={}", password));
63 }
64 if let Some(cacert) = &etcd.cacert {
65 args.push(format!("--cacert={}", cacert));
66 }
67 if let Some(cert) = &etcd.cert {
68 args.push(format!("--cert={}", cert));
69 }
70 args
71 }
72 None => vec![],
73 };
74
75 thread::spawn(move || {
76 let runtime = tokio::runtime::Runtime::new().unwrap();
77 match runtime.block_on(async {
78 let message = mqttc::wait_for_mqtt_message("REQUEST").await?;
79 let ip_addr = message
80 .split_whitespace()
81 .nth(2)
82 .ok_or_else(|| anyhow::anyhow!("Failed to extract IP address from MQTT message"))?;
83
84 let name = api_socket
85 .split('/')
86 .last()
87 .ok_or_else(|| anyhow::anyhow!("Failed to extract VM name from API socket path"))?
88 .replace("firecracker-", "")
89 .replace(".sock", "");
90
91 std::fs::write(format!("/tmp/firecracker-{}.ip", name), ip_addr)
92 .with_context(|| "Failed to write IP address to file")?;
93
94 println!(
95 "[+] Assigning DNS entry: {}.firecracker -> {}",
96 name, ip_addr
97 );
98
99 let etcd_key = format!("/skydns/firecracker/{}", name);
100 let etcd_value = format!("{{\"host\":\"{}\"}}", ip_addr);
101 let mut args = vec!["put", &etcd_key, &etcd_value];
102 args.extend(etcd_args.iter().map(String::as_str));
103
104 run_command("etcdctl", &args, false)?;
105
106 Ok::<(), Error>(())
107 }) {
108 Ok(_) => {}
109 Err(e) => {
110 eprintln!("[✗] Error setting up CoreDNS: {}", e);
111 process::exit(1);
112 }
113 }
114 Ok::<(), Error>(())
115 });
116
117 Ok(())
118}
119
120pub fn restart_coredns() -> Result<(), Error> {
121 println!("[+] Starting CoreDNS...");
122 run_command("systemctl", &["enable", "coredns"], true)?;
123 run_command("systemctl", &["daemon-reload"], true)?;
124 run_command("systemctl", &["restart", "coredns"], true)?;
125 println!("[✓] CoreDNS started successfully.");
126 Ok(())
127}
128
129pub fn coredns_is_installed() -> Result<bool, Error> {
130 let output = run_command("which", &["coredns"], false)?;
131 Ok(output.status.success())
132}
133
134pub fn etcd_is_installed() -> Result<bool, Error> {
135 let output = run_command("ls", &["/usr/bin/etcd"], false)?;
136 Ok(output.status.success())
137}