Skip to content

Commit 2c74064

Browse files
committed
Tidy up main
1 parent 5061595 commit 2c74064

3 files changed

Lines changed: 319 additions & 277 deletions

File tree

src/main.rs

Lines changed: 7 additions & 277 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,21 @@
11
mod checkers;
22
mod helpers;
3+
mod postinstall_cmd;
4+
mod preinstall_cmd;
35
mod recorders;
46

5-
use checkers::preinstall::{
6-
byo_kernel::BYOKernelChecks, iommu::IOMMUChecks, kernel::KernelChecks, numa::NUMAChecks,
7-
pvh::PVHChecks, system::SystemChecks,
8-
};
9-
10-
use checkers::postinstall::{
11-
guest_type::GuestTypeChecks, kernel::PostinstallKernelChecks, kube::KubeChecks,
12-
services::ServiceChecks,
13-
};
14-
157
use clap::{Parser, Subcommand};
168
use console::{Emoji, style};
17-
use helpers::{
18-
CheckGroup, CheckGroupCategory, CheckGroupResult,
19-
CheckResultValue::{Errored, Failed, Passed},
20-
host_executor::HostNamespaceExecutor,
21-
};
22-
use recorders::postinstall::system::SystemRecorder as postrecorder;
23-
use recorders::preinstall::system::SystemRecorder as prerecorder;
9+
use helpers::{CheckGroup, CheckGroupResult, host_executor::HostNamespaceExecutor};
2410

25-
use anyhow::{Context, Result, anyhow, bail};
11+
use anyhow::{Context, Result};
2612
use chrono::Utc;
2713
use flate2::{Compression, write::GzEncoder};
2814
use log::debug;
2915
use std::{
30-
collections::HashSet,
31-
env, fs,
16+
fs,
3217
fs::File,
3318
path::{Path, PathBuf},
34-
process,
3519
};
3620
use tokio::task::JoinHandle;
3721

@@ -95,268 +79,14 @@ async fn main() -> Result<()> {
9579
only_checks,
9680
report_dir,
9781
} => {
98-
// If we are in a privileged container running in the host pid namespace,
99-
// this creates a tokio thread pool that runs stuff outside of the container context,
100-
// directly on the host.
101-
// If we are in a regular old `sudo`'d binary running naked on the host,
102-
// this is effectively a silent no-op.
103-
let host_executor = HostNamespaceExecutor::new();
104-
105-
// See if we are already booted under Edera. If so, error out and suggest `postinstall`
106-
// as the command to run.
107-
match host_executor
108-
.spawn_in_host_ns(async {
109-
if !Path::new("/var/lib/edera/protect/.install-completed").exists() {
110-
return false;
111-
}
112-
let xen = Path::new("/sys/hypervisor/type");
113-
xen.exists() && fs::read_to_string(xen).unwrap_or_default().trim() == "xen"
114-
})
82+
preinstall_cmd::do_preinstall(byo_kernel, record_hostinfo, only_checks, report_dir)
11583
.await
116-
{
117-
// TODO(bml) later we may add a `postinstall` command,
118-
// but for now all we have is `preinstall` and running it under an active Edera boot
119-
// is not supported or useful.
120-
Ok(true) => {
121-
println!(
122-
"{}",
123-
style("Edera is already installed. Run `edera-check postinstall` instead.")
124-
.red()
125-
.bold()
126-
);
127-
process::exit(1);
128-
}
129-
Ok(false) => (),
130-
Err(e) => {
131-
bail!("Error: {}", e);
132-
}
133-
};
134-
135-
let mut groups: Vec<Box<dyn CheckGroup>> = vec![
136-
Box::new(SystemChecks::new(host_executor.clone())),
137-
Box::new(PVHChecks::new(host_executor.clone())),
138-
Box::new(KernelChecks::new(host_executor.clone())),
139-
Box::new(IOMMUChecks::new(host_executor.clone())),
140-
Box::new(NUMAChecks::new(host_executor.clone())),
141-
];
142-
143-
if record_hostinfo {
144-
println!(
145-
"Collecting information about the current host as part of locally-generated preinstall report."
146-
);
147-
println!("The information collected will remain on this host.");
148-
groups.push(Box::new(prerecorder::new(host_executor.clone())));
149-
}
150-
151-
if byo_kernel {
152-
groups.push(Box::new(BYOKernelChecks::new(host_executor.clone())));
153-
}
154-
155-
// If only-checks is specified, only include checks that match the provided ID.
156-
if !only_checks.is_empty() {
157-
let valid_ids: HashSet<_> = groups.iter().map(|g| g.id().to_string()).collect();
158-
only_checks.iter().for_each(|id| {
159-
if !valid_ids.contains(id) {
160-
println!("{} '{}'", style("Unknown Check:").yellow(), style(id).red());
161-
}
162-
});
163-
groups.retain(|group| only_checks.contains(&group.id().to_string()));
164-
}
165-
166-
groups.sort_by_key(|g| g.category());
167-
168-
let mut required_groups_result = Passed;
169-
let mut all_groups_result = Passed;
170-
171-
let hostname = host_executor
172-
.spawn_in_host_ns(async { std::fs::read_to_string("/etc/hostname").unwrap() })
173-
.await?;
174-
175-
let base_dir = if let Some(dir) = report_dir {
176-
PathBuf::from(dir)
177-
} else {
178-
env::temp_dir()
179-
};
180-
181-
let base_path = create_base_path(base_dir, hostname.trim(), "preinstall")
182-
.map_err(|e| anyhow!("failed to create bundle base path: {e}"))?;
183-
// Run each check group
184-
for group in groups {
185-
println!(
186-
"{} {} [{}] - {}",
187-
style("Running Group").cyan(),
188-
style(group.name()).cyan().bold(),
189-
style(group.category()).white().bold(),
190-
group.description()
191-
);
192-
193-
let check_group_result = group.run().await;
194-
195-
check_group_result.log_individual_checks();
196-
197-
check_group_result.log_group(group.category());
198-
199-
// Set final result to Failed if we failed and aren't already in an Errored state
200-
// However, do not allow Optional groups to count towards Errored or Failed state.
201-
if matches!(check_group_result.result, Failed(_)) {
202-
if matches!(group.category(), CheckGroupCategory::Required)
203-
&& !matches!(required_groups_result, Errored(_))
204-
{
205-
required_groups_result = Failed(String::from("group failed"));
206-
} else if !matches!(all_groups_result, Errored(_)) {
207-
all_groups_result = Failed(String::from("group failed"));
208-
}
209-
}
210-
211-
if matches!(check_group_result.result, Errored(_)) {
212-
if matches!(group.category(), CheckGroupCategory::Required) {
213-
required_groups_result = Errored(String::from("group errored"));
214-
} else {
215-
all_groups_result = Errored(String::from("group errored"));
216-
}
217-
}
218-
219-
write_group_report(group, &check_group_result, &base_path)?;
220-
}
221-
222-
create_gzip_from(base_path, host_executor.clone()).await?;
223-
224-
match required_groups_result {
225-
Errored(_) | Failed(_) => bail!("Required preinstall checks did not pass"),
226-
_ => Ok(()),
227-
}
22884
}
22985
Commands::Postinstall {
23086
record_hostinfo,
23187
only_checks,
23288
report_dir,
233-
} => {
234-
// If we are in a privileged container running in the host pid namespace,
235-
// this creates a tokio thread pool that runs stuff outside of the container context,
236-
// directly on the host.
237-
// If we are in a regular old `sudo`'d binary running naked on the host,
238-
// this is effectively a silent no-op.
239-
let host_executor = HostNamespaceExecutor::new();
240-
241-
// See if we are already booted under Edera. If so, error out and suggest `postinstall`
242-
// as the command to run.
243-
match host_executor
244-
.spawn_in_host_ns(async {
245-
if !Path::new("/var/lib/edera/protect/.install-completed").exists() {
246-
return false;
247-
}
248-
let xen = Path::new("/sys/hypervisor/type");
249-
xen.exists() && fs::read_to_string(xen).unwrap_or_default().trim() == "xen"
250-
})
251-
.await
252-
{
253-
// TODO(bml) later we may add a `postinstall` command,
254-
// but for now all we have is `preinstall` and running it under an active Edera boot
255-
// is not supported or useful.
256-
Ok(true) => {}
257-
Ok(false) => {
258-
println!(
259-
"{}",
260-
style("Edera not installed. Run `edera-check preinstall` instead.")
261-
.red()
262-
.bold()
263-
);
264-
process::exit(1);
265-
}
266-
Err(e) => {
267-
bail!("Error: {}", e);
268-
}
269-
};
270-
271-
let mut groups: Vec<Box<dyn CheckGroup>> = vec![
272-
Box::new(GuestTypeChecks::new(host_executor.clone())),
273-
Box::new(PostinstallKernelChecks::new(host_executor.clone())),
274-
Box::new(ServiceChecks::new(host_executor.clone())),
275-
Box::new(KubeChecks::new(host_executor.clone())),
276-
];
277-
278-
if record_hostinfo {
279-
println!(
280-
"Collecting information about the current host as part of locally-generated preinstall report."
281-
);
282-
println!("The information collected will remain on this host.");
283-
groups.push(Box::new(postrecorder::new(host_executor.clone())));
284-
}
285-
286-
// If only-checks is specified, only include checks that match the provided ID.
287-
if !only_checks.is_empty() {
288-
let valid_ids: HashSet<_> = groups.iter().map(|g| g.id().to_string()).collect();
289-
only_checks.iter().for_each(|id| {
290-
if !valid_ids.contains(id) {
291-
println!("{} '{}'", style("Unknown Check:").yellow(), style(id).red());
292-
}
293-
});
294-
groups.retain(|group| only_checks.contains(&group.id().to_string()));
295-
}
296-
297-
groups.sort_by_key(|g| g.category());
298-
299-
let mut required_groups_result = Passed;
300-
let mut all_groups_result = Passed;
301-
302-
let hostname = host_executor
303-
.spawn_in_host_ns(async { std::fs::read_to_string("/etc/hostname").unwrap() })
304-
.await?;
305-
306-
let base_dir = if let Some(dir) = report_dir {
307-
PathBuf::from(dir)
308-
} else {
309-
env::temp_dir()
310-
};
311-
312-
let base_path = create_base_path(base_dir, hostname.trim(), "postinstall")
313-
.map_err(|e| anyhow!("failed to create bundle base path: {e}"))?;
314-
// Run each check group
315-
for group in groups {
316-
println!(
317-
"{} {} [{}] - {}",
318-
style("Running Group").cyan(),
319-
style(group.name()).cyan().bold(),
320-
style(group.category()).white().bold(),
321-
group.description()
322-
);
323-
324-
let check_group_result = group.run().await;
325-
326-
check_group_result.log_individual_checks();
327-
328-
check_group_result.log_group(group.category());
329-
330-
// Set final result to Failed if we failed and aren't already in an Errored state
331-
// However, do not allow Optional groups to count towards Errored or Failed state.
332-
if matches!(check_group_result.result, Failed(_)) {
333-
if matches!(group.category(), CheckGroupCategory::Required)
334-
&& !matches!(required_groups_result, Errored(_))
335-
{
336-
required_groups_result = Failed(String::from("group failed"));
337-
} else if !matches!(all_groups_result, Errored(_)) {
338-
all_groups_result = Failed(String::from("group failed"));
339-
}
340-
}
341-
342-
if matches!(check_group_result.result, Errored(_)) {
343-
if matches!(group.category(), CheckGroupCategory::Required) {
344-
required_groups_result = Errored(String::from("group errored"));
345-
} else {
346-
all_groups_result = Errored(String::from("group errored"));
347-
}
348-
}
349-
350-
write_group_report(group, &check_group_result, &base_path)?;
351-
}
352-
353-
create_gzip_from(base_path, host_executor.clone()).await?;
354-
355-
match required_groups_result {
356-
Errored(_) | Failed(_) => bail!("Required preinstall checks did not pass"),
357-
_ => Ok(()),
358-
}
359-
}
89+
} => postinstall_cmd::do_postinstall(record_hostinfo, only_checks, report_dir).await,
36090
}
36191
}
36292

0 commit comments

Comments
 (0)