Skip to content

Commit 0d40bd9

Browse files
feat(host): gate builtins with feature flags
Signed-off-by: Brooks Townsend <brooksmtownsend@gmail.com>
1 parent 2aa0240 commit 0d40bd9

File tree

4 files changed

+96
-2
lines changed

4 files changed

+96
-2
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use tracing::warn;
2+
3+
/// Feature flags to enable experimental functionality in the host. Flags are disabled
4+
/// by default and must be explicitly enabled.
5+
#[derive(Copy, Clone, Debug, Default)]
6+
pub struct Features {
7+
/// Enable the built-in HTTP server capability provider
8+
/// that can be started with the reference wasmcloud+builtin://http-server
9+
pub(crate) builtin_http: bool,
10+
/// Enable the built-in NATS Messaging capability provider
11+
/// that can be started with the reference wasmcloud+builtin://messaging-nats
12+
pub(crate) builtin_messaging: bool,
13+
}
14+
15+
impl Features {
16+
/// Enable the built-in HTTP server capability provider
17+
pub fn with_builtin_http() -> Self {
18+
Self {
19+
builtin_http: true,
20+
..Default::default()
21+
}
22+
}
23+
24+
/// Enable the built-in NATS messaging capability provider
25+
pub fn with_builtin_messaging() -> Self {
26+
Self {
27+
builtin_messaging: true,
28+
..Default::default()
29+
}
30+
}
31+
}
32+
33+
/// This enables unioning feature flags together
34+
impl std::ops::Add for Features {
35+
type Output = Self;
36+
37+
fn add(self, rhs: Self) -> Self::Output {
38+
Self {
39+
builtin_http: self.builtin_http || rhs.builtin_http,
40+
builtin_messaging: self.builtin_messaging || rhs.builtin_messaging,
41+
}
42+
}
43+
}
44+
45+
/// Allow for summing over a collection of feature flags
46+
impl std::iter::Sum for Features {
47+
fn sum<I: Iterator<Item = Self>>(mut iter: I) -> Self {
48+
// Grab the first set of flags, fall back on defaults (all disabled)
49+
let first = iter.next().unwrap_or_default();
50+
iter.fold(first, |a, b| a + b)
51+
}
52+
}
53+
54+
/// Parse a feature flag from a string, enabling the feature if the string matches
55+
impl From<&str> for Features {
56+
fn from(s: &str) -> Self {
57+
match &*s.to_ascii_lowercase() {
58+
"builtin-http" | "builtin_http" => Self::with_builtin_http(),
59+
"builtin-messaging" | "builtin_messaging" => Self::with_builtin_messaging(),
60+
_ => {
61+
warn!(%s, "unknown feature flag");
62+
Features::default()
63+
}
64+
}
65+
}
66+
}

crates/host/src/wasmbus/host_config.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use url::Url;
99
use wasmcloud_core::{logging::Level as LogLevel, OtelConfig};
1010
use wasmcloud_runtime::{MAX_COMPONENTS, MAX_COMPONENT_SIZE, MAX_LINEAR_MEMORY};
1111

12+
use crate::wasmbus::experimental::Features;
13+
1214
/// wasmCloud Host configuration
1315
#[allow(clippy::struct_excessive_bools)]
1416
#[derive(Clone, Debug)]
@@ -72,6 +74,8 @@ pub struct Host {
7274
pub max_components: u32,
7375
/// The interval at which the Host will send heartbeats
7476
pub heartbeat_interval: Option<Duration>,
77+
/// Experimental features that can be enabled in the host
78+
pub experimental_features: Features,
7579
}
7680

7781
/// Configuration for wasmCloud policy service
@@ -121,6 +125,7 @@ impl Default for Host {
121125
max_component_size: MAX_COMPONENT_SIZE,
122126
max_components: MAX_COMPONENTS,
123127
heartbeat_interval: None,
128+
experimental_features: Features::default(),
124129
}
125130
}
126131
}

crates/host/src/wasmbus/mod.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ use crate::{
6363
};
6464

6565
mod event;
66+
mod experimental;
6667
mod handler;
6768
mod providers;
6869

@@ -73,6 +74,7 @@ pub mod host_config;
7374
pub use self::host_config::Host as HostConfig;
7475

7576
use self::config::{BundleGenerator, ConfigBundle};
77+
pub use self::experimental::Features;
7678
use self::handler::Handler;
7779

7880
const MAX_INVOCATION_CHANNEL_SIZE: usize = 5000;
@@ -389,6 +391,8 @@ pub struct Host {
389391
max_execution_time: Duration,
390392
messaging_links:
391393
Arc<RwLock<HashMap<Arc<str>, Arc<RwLock<HashMap<Box<str>, async_nats::Client>>>>>>,
394+
/// Experimental features to enable in the host that gate functionality
395+
experimental_features: Features,
392396
}
393397

394398
#[derive(Debug, Serialize, Deserialize, Default)]
@@ -884,6 +888,8 @@ impl Host {
884888

885889
let max_execution_time_ms = config.max_execution_time;
886890

891+
debug!("Feature flags: {:?}", config.experimental_features);
892+
887893
let host = Host {
888894
components: Arc::default(),
889895
event_builder,
@@ -896,6 +902,7 @@ impl Host {
896902
labels: RwLock::new(labels),
897903
ctl_nats,
898904
rpc_nats: Arc::new(rpc_nats),
905+
experimental_features: config.experimental_features,
899906
host_config: config,
900907
data: data.clone(),
901908
data_watch: data_watch_abort.clone(),
@@ -2547,7 +2554,7 @@ impl Host {
25472554
.await?
25482555
}
25492556
(None, ResourceRef::Builtin(name)) => match *name {
2550-
"http-server" => {
2557+
"http-server" if self.experimental_features.builtin_http => {
25512558
self.start_http_server_provider(
25522559
&mut tasks,
25532560
link_definitions,
@@ -2558,7 +2565,8 @@ impl Host {
25582565
)
25592566
.await?
25602567
}
2561-
"messaging-nats" => {
2568+
"http-server" => bail!("feature `builtin-http` is not enabled, denying start"),
2569+
"messaging-nats" if self.experimental_features.builtin_messaging => {
25622570
self.start_messaging_nats_provider(
25632571
&mut tasks,
25642572
link_definitions,
@@ -2569,6 +2577,9 @@ impl Host {
25692577
)
25702578
.await?
25712579
}
2580+
"messaging-nats" => {
2581+
bail!("feature `messaging-nats` is not enabled, denying start")
2582+
}
25722583
_ => bail!("unknown builtin name: {name}"),
25732584
},
25742585
_ => bail!("invalid provider reference"),

src/main.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use wasmcloud_core::{OtelConfig, OtelProtocol};
1717
use wasmcloud_host::oci::Config as OciConfig;
1818
use wasmcloud_host::url::Url;
1919
use wasmcloud_host::wasmbus::host_config::PolicyService as PolicyServiceConfig;
20+
use wasmcloud_host::wasmbus::Features;
2021
use wasmcloud_host::WasmbusHostConfig;
2122
use wasmcloud_tracing::configure_observability;
2223

@@ -327,6 +328,15 @@ struct Args {
327328
#[arg(long = "heartbeat-interval-seconds", env = "WASMCLOUD_HEARTBEAT_INTERVAL", value_parser = parse_duration_secs, hide = true)]
328329
heartbeat_interval: Option<Duration>,
329330

331+
/// Experimental features to enable in the host. This is a repeatable option.
332+
#[arg(
333+
long = "feature",
334+
env = "WASMCLOUD_EXPERIMENTAL_FEATURES",
335+
value_delimiter = ',',
336+
hide = true
337+
)]
338+
experimental_features: Vec<Features>,
339+
330340
#[clap(
331341
long = "help-markdown",
332342
action=ArgAction::SetTrue,
@@ -498,6 +508,8 @@ async fn main() -> anyhow::Result<()> {
498508
max_component_size: args.max_component_size,
499509
max_components: args.max_components,
500510
heartbeat_interval: args.heartbeat_interval,
511+
// NOTE(brooks): Summing the feature flags "OR"s the multiple flags together.
512+
experimental_features: args.experimental_features.into_iter().sum(),
501513
}))
502514
.await
503515
.context("failed to initialize host")?;

0 commit comments

Comments
 (0)