Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wicketd] Add refresh-config subcommand instead of using curl #4606

Merged
merged 5 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion smf/wicketd/manifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
it expected https).
-->
<exec_method type='method' name='refresh'
exec='curl -X POST http://%{config/address}/reload-config'
exec='/opt/oxide/wicketd/bin/wicketd refresh-config /var/svc/manifest/site/wicketd/config.toml --address %{config/address}'
timeout_seconds='0' />

<property_group name='startd' type='framework'>
Expand Down
1 change: 1 addition & 0 deletions wicketd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ sled-hardware.workspace = true
tufaceous-lib.workspace = true
update-engine.workspace = true
wicket-common.workspace = true
wicketd-client.workspace = true
jgallagher marked this conversation as resolved.
Show resolved Hide resolved
omicron-workspace-hack.workspace = true

[[bin]]
Expand Down
32 changes: 31 additions & 1 deletion wicketd/src/bin/wicketd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ enum Args {
#[clap(name = "CONFIG_FILE_PATH", action)]
config_file_path: PathBuf,

/// The address for the technician port
/// The address on which the main wicketd dropshot server should listen
#[clap(short, long, action)]
address: SocketAddrV6,

Expand Down Expand Up @@ -57,6 +57,19 @@ enum Args {
#[clap(long, action, conflicts_with("read_smf_config"))]
rack_subnet: Option<Ipv6Addr>,
},

/// Instruct a running wicketd server to refresh its config
///
/// Mechanically, this hits a specific endpoint served by wicketd's dropshot
/// server
RefreshConfig {
#[clap(name = "CONFIG_FILE_PATH", action)]
config_file_path: PathBuf,
jgallagher marked this conversation as resolved.
Show resolved Hide resolved

/// The address of the server to refresh
#[clap(short, long, action)]
address: SocketAddrV6,
},
}

#[tokio::main]
Expand Down Expand Up @@ -140,5 +153,22 @@ async fn do_run() -> Result<(), CmdError> {
.await
.map_err(|err| CmdError::Failure(anyhow!(err)))
}
Args::RefreshConfig { config_file_path, address } => {
let config = Config::from_file(&config_file_path)
.with_context(|| {
format!("failed to parse {}", config_file_path.display())
})
.map_err(CmdError::Failure)?;

let log = config
.log
.to_logger("wicketd")
.context("failed to initialize logger")
.map_err(CmdError::Failure)?;

Server::refresh_config(log, address)
.await
.map_err(CmdError::Failure)
jgallagher marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
68 changes: 64 additions & 4 deletions wicketd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ mod preflight_check;
mod rss_config;
mod update_tracker;

use anyhow::{anyhow, Result};
use anyhow::{anyhow, Context, Result};
use artifacts::{WicketdArtifactServer, WicketdArtifactStore};
use bootstrap_addrs::BootstrapPeers;
pub use config::Config;
pub(crate) use context::ServerContext;
use display_error_chain::DisplayErrorChain;
use dropshot::{ConfigDropshot, HandlerTaskMode, HttpServer};
pub use installinator_progress::{IprUpdateTracker, RunningUpdateState};
use internal_dns::resolver::Resolver;
Expand All @@ -34,6 +35,7 @@ use preflight_check::PreflightCheckerHandler;
use sled_hardware::Baseboard;
use slog::{debug, error, o, Drain};
use std::sync::{Mutex, OnceLock};
use std::time::Duration;
use std::{
net::{SocketAddr, SocketAddrV6},
sync::Arc,
Expand Down Expand Up @@ -70,7 +72,6 @@ pub struct SmfConfigValues {
impl SmfConfigValues {
#[cfg(target_os = "illumos")]
pub fn read_current() -> Result<Self> {
use anyhow::Context;
use illumos_utils::scf::ScfHandle;

const CONFIG_PG: &str = "config";
Expand Down Expand Up @@ -259,11 +260,70 @@ impl Server {
res = self.artifact_server => {
match res {
Ok(()) => Err("artifact server exited unexpectedly".to_owned()),
// The artifact server returns an anyhow::Error, which has a `Debug` impl that
// prints out the chain of errors.
// The artifact server returns an anyhow::Error, which has a
// `Debug` impl that prints out the chain of errors.
Err(err) => Err(format!("running artifact server: {err:?}")),
}
}
}
}

/// Instruct a running server at the specified address to reload its config
/// parameters
pub async fn refresh_config(
log: slog::Logger,
address: SocketAddrV6,
) -> Result<()> {
// It's possible we're being told to refresh a server's config before
// it's ready to receive such a request, so we'll give it a healthy
// amount of time before we give up: we'll set a client timeout and also
// retry a few times. See
// https://github.com/oxidecomputer/omicron/issues/4604.
const CLIENT_TIMEOUT: Duration = Duration::from_secs(5);
const SLEEP_BETWEEN_RETRIES: Duration = Duration::from_secs(10);
const NUM_RETRIES: usize = 3;

let client = reqwest::Client::builder()
.connect_timeout(CLIENT_TIMEOUT)
.timeout(CLIENT_TIMEOUT)
.build()
.context("failed to construct reqwest Client")?;

let client = wicketd_client::Client::new_with_client(
&format!("http://{address}"),
client,
log,
);
let log = client.inner();

let mut attempt = 0;
loop {
attempt += 1;

// If we succeed, we're done.
let Err(err) = client.post_reload_config().await else {
return Ok(());
};

// If we failed, either warn+sleep and try again, or fail.
if attempt < NUM_RETRIES {
slog::warn!(
log,
"failed to refresh wicketd config \
(attempt {attempt} of {NUM_RETRIES}); \
will retry after {CLIENT_TIMEOUT:?}";
"err" => %DisplayErrorChain::new(&err),
);
tokio::time::sleep(SLEEP_BETWEEN_RETRIES).await;
} else {
slog::error!(
log,
"failed to refresh wicketd config \
(tried {NUM_RETRIES} times)";
"err" => %DisplayErrorChain::new(&err),
);
return Err(err).context("failed to contact wicketd");
}
}
}
}
Loading