feat(okd installation): Process works nicely all the way up to setting the bootstrap host binding in opnsense automatically! Next step : generate the mac address boot file for bootstrap host, install ignition files and the cluster will booooooooot
Some checks failed
Run Check Script / check (pull_request) Failing after 33s

This commit is contained in:
Jean-Gabriel Gill-Couture 2025-09-02 00:39:52 -04:00
parent f6e665f990
commit a353249eec
7 changed files with 78 additions and 16 deletions

View File

@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT host_id FROM host_role_mapping WHERE role = ?",
"describe": {
"columns": [
{
"name": "host_id",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "2ea29df2326f7c84bd4100ad510a3fd4878dc2e217dc83f9bf45a402dfd62a91"
}

View File

@ -16,7 +16,7 @@
{
"name": "data: Json<PhysicalHost>",
"ordinal": 2,
"type_info": "Null"
"type_info": "Blob"
}
],
"parameters": {

View File

@ -50,12 +50,12 @@ pub async fn get_topology() -> HAClusterTopology {
dhcp_server: opnsense.clone(),
dns_server: opnsense.clone(),
control_plane: vec![LogicalHost {
ip: ip!("10.100.8.20"),
ip: ip!("192.168.1.20"),
name: "cp0".to_string(),
}],
bootstrap_host: LogicalHost {
ip: ip!("10.100.8.20"),
name: "cp0".to_string(),
ip: ip!("192.168.1.20"),
name: "bootstrap".to_string(),
},
workers: vec![],
switch: vec![],

View File

@ -29,6 +29,7 @@ pub trait InventoryRepository: Send + Sync + 'static {
async fn save(&self, host: &PhysicalHost) -> Result<(), RepoError>;
async fn get_latest_by_id(&self, host_id: &str) -> Result<Option<PhysicalHost>, RepoError>;
async fn get_all_hosts(&self) -> Result<Vec<PhysicalHost>, RepoError>;
async fn get_host_for_role(&self, role: HostRole) -> Result<Vec<PhysicalHost>, RepoError>;
async fn save_role_mapping(
&self,
role: &HostRole,

View File

@ -46,14 +46,15 @@ impl InventoryRepository for SqliteInventoryRepository {
}
async fn get_latest_by_id(&self, host_id: &str) -> Result<Option<PhysicalHost>, RepoError> {
let _row = sqlx::query_as!(
let row = sqlx::query_as!(
DbHost,
r#"SELECT id, version_id, data as "data: Json<PhysicalHost>" FROM physical_hosts WHERE id = ? ORDER BY version_id DESC LIMIT 1"#,
host_id
)
.fetch_optional(&self.pool)
.await?;
todo!()
Ok(row.map(|r| r.data.0))
}
async fn get_all_hosts(&self) -> Result<Vec<PhysicalHost>, RepoError> {
@ -107,6 +108,36 @@ impl InventoryRepository for SqliteInventoryRepository {
Ok(())
}
async fn get_host_for_role(&self, role: HostRole) -> Result<Vec<PhysicalHost>, RepoError> {
struct HostIdRow {
host_id: String,
}
let role_str = format!("{:?}", role);
let host_id_rows = sqlx::query_as!(
HostIdRow,
"SELECT host_id FROM host_role_mapping WHERE role = ?",
role_str
)
.fetch_all(&self.pool)
.await?;
let mut hosts = Vec::with_capacity(host_id_rows.len());
for row in host_id_rows {
match self.get_latest_by_id(&row.host_id).await? {
Some(host) => hosts.push(host),
None => {
log::warn!(
"Found a role mapping for host_id '{}', but the host does not exist in the physical_hosts table. This may indicate a data integrity issue.",
row.host_id
);
}
}
}
Ok(hosts)
}
}
use sqlx::types::Json;
@ -115,4 +146,3 @@ struct DbHost {
id: String,
version_id: String,
}

View File

@ -1,7 +1,7 @@
use async_trait::async_trait;
use derive_new::new;
use harmony_types::id::Id;
use log::info;
use log::{info, trace};
use serde::Serialize;
use crate::{
@ -177,7 +177,7 @@ impl DhcpHostBindingInterpret {
.collect();
info!("DHCPStaticEntry : {:?}", dhcp_entries);
info!("DHCP server : {:?}", dhcp_server);
trace!("DHCP server : {:?}", dhcp_server);
let number_new_entries = dhcp_entries.len();

View File

@ -411,11 +411,22 @@ impl OKDSetup02BootstrapInterpret {
}
}
fn get_bootstrap_node<'a>(&self, inventory: &'a Inventory) -> &'a PhysicalHost {
inventory
.worker_host
.first()
.expect("At least one worker host is required to be used as bootstrap node")
async fn get_bootstrap_node(
&self,
_inventory: &Inventory,
) -> Result<PhysicalHost, InterpretError> {
let repo = InventoryRepositoryFactory::build().await?;
match repo
.get_host_for_role(HostRole::Bootstrap)
.await?
.into_iter()
.next()
{
Some(host) => Ok(host),
None => Err(InterpretError::new(
"No bootstrap node available".to_string(),
)),
}
}
async fn configure_host_binding(
@ -425,7 +436,7 @@ impl OKDSetup02BootstrapInterpret {
) -> Result<(), InterpretError> {
let binding = HostBinding {
logical_host: topology.bootstrap_host.clone(),
physical_host: self.get_bootstrap_node(inventory).clone(),
physical_host: self.get_bootstrap_node(inventory).await?,
};
info!("Configuring host binding for bootstrap node {binding:?}");
@ -444,7 +455,7 @@ impl OKDSetup02BootstrapInterpret {
) -> Result<(), InterpretError> {
// Placeholder: use Harmony templates to emit {MAC}.ipxe selecting SCOS live + bootstrap ignition.
info!("[Bootstrap] Rendering per-MAC PXE for bootstrap node");
let bootstrap_node = self.get_bootstrap_node(inventory);
let bootstrap_node = self.get_bootstrap_node(inventory).await?;
IPxeMacBootFileScore {
mac_address: bootstrap_node.get_mac_address(),
content: todo!("templace for bootstrap node"),