feat(secret): added get_or_prompt functionality and debuggable ipxe chainloading boot file and some misc stuff
Some checks failed
Run Check Script / check (pull_request) Failing after 29s
Some checks failed
Run Check Script / check (pull_request) Failing after 29s
This commit is contained in:
parent
160939de21
commit
b765e9b7dc
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@ private_repos/
|
||||
|
||||
### Harmony ###
|
||||
harmony.log
|
||||
data/okd/installation_files*
|
||||
|
||||
### Helm ###
|
||||
# Chart dependencies
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2427,6 +2427,7 @@ dependencies = [
|
||||
"harmony_secret_derive",
|
||||
"http 1.3.1",
|
||||
"infisical",
|
||||
"inquire",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"pretty_assertions",
|
||||
|
@ -22,7 +22,7 @@ pub async fn get_topology() -> HAClusterTopology {
|
||||
name: String::from("opnsense-1"),
|
||||
};
|
||||
|
||||
let config = SecretManager::get::<OPNSenseFirewallConfig>().await;
|
||||
let config = SecretManager::get_or_prompt::<OPNSenseFirewallConfig>().await;
|
||||
let config = config.unwrap();
|
||||
|
||||
let opnsense = Arc::new(
|
||||
|
@ -16,7 +16,7 @@ pub async fn get_topology() -> HAClusterTopology {
|
||||
name: String::from("opnsense-1"),
|
||||
};
|
||||
|
||||
let config = SecretManager::get::<OPNSenseFirewallCredentials>().await;
|
||||
let config = SecretManager::get_or_prompt::<OPNSenseFirewallCredentials>().await;
|
||||
let config = config.unwrap();
|
||||
|
||||
let opnsense = Arc::new(
|
||||
|
@ -425,7 +425,8 @@ impl OKDSetup02BootstrapInterpret {
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<(), InterpretError> {
|
||||
let okd_bin_path = PathBuf::from("./data/okd/bin");
|
||||
let okd_installation_path_str = "./data/okd/installation_files";
|
||||
let okd_installation_path_str =
|
||||
format!("./data/okd/installation_files_{}", inventory.location.name);
|
||||
let okd_images_path = &PathBuf::from("./data/okd/installer_image/");
|
||||
let okd_installation_path = &PathBuf::from(okd_installation_path_str);
|
||||
|
||||
@ -450,8 +451,8 @@ impl OKDSetup02BootstrapInterpret {
|
||||
);
|
||||
}
|
||||
|
||||
let redhat_secret = SecretManager::get::<RedhatSecret>().await?;
|
||||
let ssh_key = SecretManager::get::<SshKeyPair>().await?;
|
||||
let redhat_secret = SecretManager::get_or_prompt::<RedhatSecret>().await?;
|
||||
let ssh_key = SecretManager::get_or_prompt::<SshKeyPair>().await?;
|
||||
|
||||
let install_config_yaml = InstallConfigYaml {
|
||||
cluster_name: &topology.get_cluster_name(),
|
||||
@ -562,38 +563,14 @@ impl OKDSetup02BootstrapInterpret {
|
||||
.interpret(inventory, topology)
|
||||
.await?;
|
||||
|
||||
let run_command =
|
||||
async |cmd: &str, args: Vec<&str>| -> Result<std::process::Output, InterpretError> {
|
||||
let output = Command::new(cmd).args(&args).output().await.map_err(|e| {
|
||||
InterpretError::new(format!("Failed to launch command {cmd} : {e}"))
|
||||
})?;
|
||||
let stdout = String::from_utf8(output.stdout.clone()).unwrap();
|
||||
info!("{cmd} stdout :\n\n{}", stdout);
|
||||
let stderr = String::from_utf8(output.stderr.clone()).unwrap();
|
||||
info!("{cmd} stderr :\n\n{}", stderr);
|
||||
info!("{cmd} exit status : {}", output.status);
|
||||
if !output.status.success() {
|
||||
return Err(InterpretError::new(format!(
|
||||
"Command execution failed, exit code {} : {} {}",
|
||||
output.status,
|
||||
cmd,
|
||||
args.join(" ")
|
||||
)));
|
||||
}
|
||||
Ok(output)
|
||||
};
|
||||
|
||||
info!("Successfully prepared ignition files for OKD installation");
|
||||
// ignition_files_http_path // = PathBuf::from("okd_ignition_files");
|
||||
info!(
|
||||
r#"Uploading images, they can be refreshed with a command similar to this one: openshift-install coreos print-stream-json | grep -Eo '"https.*(kernel.|initramfs.|rootfs.)\w+(\.img)?"' | grep x86_64 | xargs -n 1 curl -LO"#
|
||||
);
|
||||
|
||||
warn!(
|
||||
"TODO push installer image files with `scp -r data/okd/installer_image/* root@192.168.1.1:/usr/local/http/scos/` until performance issue is resolved"
|
||||
);
|
||||
inquire::Confirm::new(
|
||||
"push installer image files with `scp -r data/okd/installer_image/* root@192.168.1.1:/usr/local/http/scos/` until performance issue is resolved").prompt().expect("Prompt error");
|
||||
&format!("push installer image files with `scp -r {}/* root@{}:/usr/local/http/scos/` until performance issue is resolved", okd_images_path.to_string_lossy(), topology.http_server.get_ip())).prompt().expect("Prompt error");
|
||||
|
||||
// let scos_http_path = PathBuf::from("scos");
|
||||
// StaticFilesHttpScore {
|
||||
@ -636,10 +613,12 @@ impl OKDSetup02BootstrapInterpret {
|
||||
) -> Result<(), InterpretError> {
|
||||
let content = BootstrapIpxeTpl {
|
||||
http_ip: &topology.http_server.get_ip().to_string(),
|
||||
scos_path: "scos", // TODO use some constant
|
||||
installation_device: "/dev/sda", // TODO do something smart based on the host drives
|
||||
// topology. Something like use the smallest device
|
||||
// above 200G that is an ssd
|
||||
scos_path: "scos", // TODO use some constant
|
||||
ignition_http_path: "okd_ignition_files", // TODO use proper variable
|
||||
installation_device: "/dev/sda",
|
||||
// TODO do something smart based on the host drives
|
||||
// topology. Something like use the smallest device
|
||||
// above 200G that is an ssd
|
||||
}
|
||||
.to_string();
|
||||
|
||||
@ -735,7 +714,7 @@ impl Interpret<HAClusterTopology> for OKDSetup02BootstrapInterpret {
|
||||
self.prepare_ignition_files(inventory, topology).await?;
|
||||
self.render_per_mac_pxe(inventory, topology).await?;
|
||||
self.setup_bootstrap_load_balancer(inventory, topology)
|
||||
.await?;
|
||||
.await?;
|
||||
|
||||
// TODO https://docs.okd.io/latest/installing/installing_bare_metal/upi/installing-bare-metal.html#installation-user-provisioned-validating-dns_installing-bare-metal
|
||||
// self.validate_dns_config(inventory, topology).await?;
|
||||
|
@ -15,4 +15,5 @@ pub struct BootstrapIpxeTpl<'a> {
|
||||
pub http_ip: &'a str,
|
||||
pub scos_path: &'a str,
|
||||
pub installation_device: &'a str,
|
||||
pub ignition_http_path: &'a str,
|
||||
}
|
||||
|
@ -1,6 +1,122 @@
|
||||
#!ipxe
|
||||
# Default chainloader with optional debug mode.
|
||||
# - Press any key within 3 seconds at start to enable debug mode.
|
||||
# - In debug mode: confirmations and extra sleeps are enabled.
|
||||
# - In production (no key pressed): continues without prompts.
|
||||
|
||||
# Config
|
||||
set base-url http://{{ gateway_ip }}:8080
|
||||
set hostfile ${base-url}/byMAC/01-${mac:hexhyp}.ipxe
|
||||
set macfile 01-${mac:hexhyp}
|
||||
set hostfile ${base-url}/byMAC/${macfile}.ipxe
|
||||
set fallback ${base-url}/fallback.ipxe
|
||||
|
||||
chain ${hostfile} || chain ${base-url}/fallback.ipxe
|
||||
# Verbosity (1..4)
|
||||
set debug 2
|
||||
|
||||
# State
|
||||
set debugmode 0
|
||||
|
||||
echo
|
||||
echo === iPXE chainload stage (default) ===
|
||||
echo MAC: ${mac}
|
||||
echo Base URL: ${base-url}
|
||||
echo Host file: ${hostfile}
|
||||
echo Fallback : ${fallback}
|
||||
echo ======================================
|
||||
echo
|
||||
echo Press any key within 3 seconds to enter DEBUG MODE...
|
||||
prompt --timeout 3 Entering debug mode... && set debugmode 1 || set debugmode 0
|
||||
|
||||
iseq ${debugmode} 1 && goto :debug_enabled || goto :debug_disabled
|
||||
|
||||
:debug_enabled
|
||||
echo DEBUG MODE: ON (confirmations and extra sleeps enabled)
|
||||
sleep 1
|
||||
goto :start
|
||||
|
||||
:debug_disabled
|
||||
echo DEBUG MODE: OFF (no confirmations; production behavior)
|
||||
sleep 1
|
||||
goto :start
|
||||
|
||||
:start
|
||||
# Show network status briefly in both modes
|
||||
ifstat
|
||||
iseq ${debugmode} 1 && sleep 2 || sleep 0
|
||||
|
||||
# Probe host-specific script via HTTP HEAD
|
||||
echo
|
||||
echo Probing host-specific script: ${hostfile}
|
||||
http --head ${hostfile}
|
||||
iseq ${rc} 0 && goto :has_hostfile || goto :no_hostfile
|
||||
|
||||
:has_hostfile
|
||||
echo Found host-specific script: ${hostfile}
|
||||
iseq ${debugmode} 1 && goto :confirm_host || goto :chain_host
|
||||
|
||||
:confirm_host
|
||||
prompt --timeout 8 Press Enter to chain host script, Esc to abort... && goto :chain_host || goto :abort
|
||||
|
||||
:chain_host
|
||||
echo Chaining ${hostfile} ...
|
||||
iseq ${debugmode} 1 && sleep 2 || sleep 0
|
||||
chain ${hostfile} || goto :host_chain_fail
|
||||
# On success, control does not return.
|
||||
|
||||
:host_chain_fail
|
||||
echo ERROR: chain to ${hostfile} failed (rc=${rc})
|
||||
iseq ${debugmode} 1 && sleep 5 || sleep 1
|
||||
goto :try_fallback
|
||||
|
||||
:no_hostfile
|
||||
echo NOT FOUND or unreachable: ${hostfile} (rc=${rc})
|
||||
iseq ${debugmode} 1 && sleep 2 || sleep 0
|
||||
|
||||
:try_fallback
|
||||
echo
|
||||
echo Probing fallback script: ${fallback}
|
||||
http --head ${fallback}
|
||||
iseq ${rc} 0 && goto :has_fallback || goto :fallback_missing
|
||||
|
||||
:has_fallback
|
||||
iseq ${debugmode} 1 && goto :confirm_fallback || goto :chain_fallback
|
||||
|
||||
:confirm_fallback
|
||||
prompt --timeout 8 Press Enter to chain fallback, Esc to shell... && goto :chain_fallback || goto :shell
|
||||
|
||||
:chain_fallback
|
||||
echo Chaining ${fallback} ...
|
||||
iseq ${debugmode} 1 && sleep 2 || sleep 0
|
||||
chain ${fallback} || goto :fallback_chain_fail
|
||||
# On success, control does not return.
|
||||
|
||||
:fallback_chain_fail
|
||||
echo ERROR: chain to fallback failed (rc=${rc})
|
||||
iseq ${debugmode} 1 && sleep 5 || sleep 1
|
||||
goto :shell
|
||||
|
||||
:fallback_missing
|
||||
echo ERROR: Fallback script not reachable: ${fallback} (rc=${rc})
|
||||
iseq ${debugmode} 1 && sleep 5 || sleep 1
|
||||
goto :shell
|
||||
|
||||
:abort
|
||||
echo Aborted by user.
|
||||
iseq ${debugmode} 1 && sleep 2 || sleep 1
|
||||
goto :shell
|
||||
|
||||
:shell
|
||||
echo
|
||||
echo === iPXE debug shell ===
|
||||
echo Try:
|
||||
echo dhcp
|
||||
echo ifstat
|
||||
echo ping {{ gateway_ip }}
|
||||
echo http ${hostfile}
|
||||
echo http ${fallback}
|
||||
echo chain ${hostfile}
|
||||
echo chain ${fallback}
|
||||
sleep 1
|
||||
shell
|
||||
|
||||
exit
|
||||
|
@ -2,6 +2,6 @@ set base-url http://{{ http_ip }}:8080
|
||||
set scos-base-url = ${base-url}/{{ scos_path }}
|
||||
set installation-device = {{ installation_device }}
|
||||
|
||||
kernel ${scos-base-url}/scos-live-kernel.x86_64 initrd=main coreos.live.rootfs_url=${scos-base-url}/scos-live-rootfs.x86_64.img coreos.inst.install_dev=${installation-device} coreos.inst.ignition_url=${base-url}/bootstrap.ign
|
||||
kernel ${scos-base-url}/scos-live-kernel.x86_64 initrd=main coreos.live.rootfs_url=${scos-base-url}/scos-live-rootfs.x86_64.img coreos.inst.install_dev=${installation-device} coreos.inst.ignition_url=${base-url}/{{ ignition_http_path }}/bootstrap.ign
|
||||
initrd --name main ${scos-base-url}/scos-live-initramfs.x86_64.img
|
||||
boot
|
||||
|
@ -18,6 +18,7 @@ infisical = { git = "https://github.com/jggc/rust-sdk.git", branch = "patch-1" }
|
||||
tokio.workspace = true
|
||||
async-trait.workspace = true
|
||||
http.workspace = true
|
||||
inquire.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions.workspace = true
|
||||
|
@ -110,6 +110,42 @@ impl SecretManager {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_or_prompt<T: Secret>() -> Result<T, SecretStoreError> {
|
||||
let secret = Self::get::<T>().await;
|
||||
let manager = get_secret_manager().await;
|
||||
let prompted = secret.is_err();
|
||||
|
||||
let secret = secret.or_else(|e| -> Result<T, SecretStoreError> {
|
||||
debug!("Could not get secret : {e}");
|
||||
|
||||
let ns = &manager.namespace;
|
||||
let key = T::KEY;
|
||||
let secret_json = inquire::Text::new(&format!(
|
||||
"Secret not found for {} {}, paste the JSON here :",
|
||||
ns, key
|
||||
))
|
||||
.prompt()
|
||||
.map_err(|e| {
|
||||
SecretStoreError::Store(format!("Failed to prompt secret {ns} {key} : {e}").into())
|
||||
})?;
|
||||
|
||||
let secret: T = serde_json::from_str(&secret_json).map_err(|e| {
|
||||
SecretStoreError::Deserialization {
|
||||
key: T::KEY.to_string(),
|
||||
source: e,
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(secret)
|
||||
})?;
|
||||
|
||||
if prompted {
|
||||
Self::set(&secret).await?;
|
||||
}
|
||||
|
||||
Ok(secret)
|
||||
}
|
||||
|
||||
/// Serializes and stores a secret.
|
||||
pub async fn set<T: Secret>(secret: &T) -> Result<(), SecretStoreError> {
|
||||
let manager = get_secret_manager().await;
|
||||
|
Loading…
Reference in New Issue
Block a user