use regent_sdk::ExpectedState;
use regent_sdk::hosts::handlers::{ConnectionMethod, TargetUser};
use regent_sdk::hosts::managed_host::ManagedHostBuilder;
use std::process::exit;
use std::{fs::File, io::Read};
use tokio::time::{Duration, sleep};
use tracing::{error, info, span, warn};
use tracing_subscriber::{Layer, fmt, layer::SubscriberExt, util::SubscriberInitExt};
mod config;
mod git;
use crate::config::{LogFormat, RegOpsConfig, RunningMode};
use crate::git::{git_clone, git_pull};
#[tokio::main]
async fn main() {
let config = match File::open("examples/config.toml") {
Ok(mut configuration_file) => {
let mut file_content: Vec<u8> = Vec::new();
if let Err(details) = configuration_file.read_to_end(&mut file_content) {
println!("Unable to read configuration file content ({})", details);
std::process::exit(1);
}
match toml::from_slice::<RegOpsConfig>(&file_content) {
Ok(valid_configuration) => valid_configuration,
Err(details) => {
println!("Invalid configuration file content ({})", details);
std::process::exit(1);
}
}
}
Err(details) => {
println!("Unable to open configuration file ({})", details);
std::process::exit(1);
}
};
let fmt_layer = match config.system_integration.log_format {
LogFormat::Raw => fmt::layer().boxed(),
LogFormat::Json => fmt::layer().json().boxed(),
};
tracing_subscriber::registry()
.with(config.system_integration.log_level.to_tracing_level())
.with(fmt_layer)
.init();
let hostname = match hostname::get() {
Ok(value) => value.to_string_lossy().to_string(),
Err(details) => {
warn!(%details, "Failed to get hostname");
"localhost".to_string()
}
};
let global_span = span!(tracing::Level::INFO, "RegOps", ?hostname);
let _guard = global_span.enter();
std::fs::create_dir_all(&config.git.local_path).unwrap();
let repository_url;
loop {
match &config.git.repo {
Some(repo_url) => match gix::url::parse(repo_url) {
Ok(repo_url) => {
repository_url = repo_url;
break;
}
Err(details) => {
warn!(%details, "Invalid git repository");
sleep(Duration::from_secs(10)).await;
}
},
None => {
warn!("No repository url set yet");
sleep(Duration::from_secs(10)).await;
}
}
}
info!("Git repository : {:?}", repository_url);
info!("Running mode : {:?}", config.behavior.mode);
let mut initial_cloning_required = false;
match gix::open(&config.git.local_path) {
Ok(already_existing_repository) => {
match already_existing_repository.find_remote("origin") {
Ok(remote) => {
match remote.url(gix::remote::Direction::Fetch) {
Some(current_remote_url) => {
if current_remote_url.to_bstring() == config.git.repo.unwrap().as_str()
{
} else {
error!(
"There is already a git repository in place, but not the expected one."
);
exit(1);
}
}
None => {
initial_cloning_required = true;
}
}
}
Err(_details) => {
initial_cloning_required = true;
}
}
}
Err(_details) => {
initial_cloning_required = true;
}
}
if initial_cloning_required {
git_clone(repository_url, &config.git.local_path, &config.git.branch).unwrap();
}
let mut managed_localhost = ManagedHostBuilder::new(
&hostname,
"localhost",
Some(ConnectionMethod::Localhost(TargetUser::current_user())),
)
.build(None)
.await
.unwrap();
managed_localhost.connect().await.unwrap();
loop {
git_pull(&config.git.local_path).unwrap();
let expected_state_description =
match std::fs::read_to_string(&config.git.expected_state_path) {
Ok(content) => content,
Err(details) => {
error!(?details, "Failed to get file content");
continue;
}
};
let expected_state = match ExpectedState::from_raw_yaml(&expected_state_description) {
Ok(state) => state,
Err(error_detail) => {
error!("Wrong yaml content : {:?}", error_detail);
sleep(Duration::from_secs(config.behavior.interval_sec)).await;
continue;
}
};
match &config.behavior.mode {
RunningMode::Assess => {
if let Err(details) = managed_localhost
.assess_compliance(&expected_state, true)
.await
{
warn!(?details, "Failed to assess compliance");
}
}
RunningMode::Enforce => {
if let Err(details) = managed_localhost.reach_compliance(&expected_state).await {
warn!(?details, "Failed to enforce compliance");
}
}
}
sleep(Duration::from_secs(config.behavior.interval_sec)).await;
}
}