Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
Signed-off-by: Dan Norris <protochron@users.noreply.github.com>
  • Loading branch information
protochron committed Jun 17, 2024
1 parent 88a0257 commit 0fd10a5
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 22 deletions.
121 changes: 114 additions & 7 deletions crates/wadm-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub const LINK_TRAIT: &str = "link";
/// for a manifest
pub const LATEST_VERSION: &str = "latest";

pub const SECRET_TYPE: &str = "v1.secret.wasmcloud.dev";

/// An OAM manifest
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, utoipa::ToSchema)]
#[serde(deny_unknown_fields)]
Expand Down Expand Up @@ -135,13 +137,14 @@ pub struct Specification {
pub policies: Vec<Policy>,
}

/// A policy definition
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct Policy {
/// The name of the policy
/// The name of this policy
pub name: String,
/// The properties for this policy
pub properties: BTreeMap<String, String>,
/// The type of the policy
#[serde(rename = "type")]
pub policy_type: String,
}
Expand Down Expand Up @@ -186,6 +189,96 @@ pub struct ComponentProperties {
/// these values at runtime using `wasi:runtime/config.`
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub config: Vec<ConfigProperty>,
/// Named secret references to pass to the component. The component will be able to retrieve
/// these values at runtime using `wasmcloud:secrets/store`.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub secrets: Vec<SecretProperty>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct ConfigDefinition {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub config: Vec<ConfigProperty>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub secrets: Vec<SecretProperty>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
pub struct SecretProperty {
/// The name of the secret. This is used by a reference by the component or capability to
/// get the secret value as a resource.
pub name: String,
/// The source of the secret. This indicates how to retrieve the secret value from a secrets
/// backend and which backend to actually query.
pub source: SecretSourceProperty,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
pub struct SecretSourceProperty {
/// The backend to use for retrieving the secret.
pub backend: String,
/// The key to use for retrieving the secret from the backend.
pub key: String,
/// The version of the secret to retrieve. If not supplied, the latest version will be used.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
}

//{
// "type": "v1.secret.wasmcloud.dev",
// "backend": "whatever"
// "key": "abc123"
// "version": 1,
//}
impl TryFrom<HashMap<String, String>> for SecretSourceProperty {
type Error = anyhow::Error;

// TODO should this actually just wrap serde_json?
fn try_from(value: HashMap<String, String>) -> Result<Self, Self::Error> {
let secret_type = value
.get("type")
.ok_or_else(|| anyhow::anyhow!("Secret source must have a type"))?;

// Do we actually care? Feels like we should use a proto or something if we do since
// versioning would be a lot easier
if secret_type != SECRET_TYPE {
return Err(anyhow::anyhow!(
"Secret source type must be {}",
SECRET_TYPE
));
}

let backend = value
.get("backend")
.ok_or_else(|| anyhow::anyhow!("Secret source must have a backend"))?;

let key = value
.get("key")
.ok_or_else(|| anyhow::anyhow!("Secret source must have a key"))?;

let version = value.get("version").cloned();

Ok(Self {
backend: backend.clone(),
key: key.clone(),
version,
})
}
}

impl TryInto<HashMap<String, String>> for SecretSourceProperty {
type Error = anyhow::Error;

fn try_into(self) -> Result<HashMap<String, String>, Self::Error> {
let mut map = HashMap::new();
map.insert("type".to_string(), SECRET_TYPE.to_string());
map.insert("backend".to_string(), self.backend);
map.insert("key".to_string(), self.key);
if let Some(version) = self.version {
map.insert("version".to_string(), version);
}
Ok(map)
}
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -312,24 +405,38 @@ impl PartialEq<ConfigProperty> for String {
#[serde(deny_unknown_fields)]
pub struct LinkProperty {
/// The target this link applies to. This should be the name of a component in the manifest
pub target: String,
//pub target: String,
/// WIT namespace for the link
pub namespace: String,
/// WIT package for the link
pub package: String,
/// WIT interfaces for the link
pub interfaces: Vec<String>,
/// Configuration to apply to the source of the link
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub source_config: Vec<ConfigProperty>,
pub source: ConfigDefinition,
/// Configuration to apply to the target of the link
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub target_config: Vec<ConfigProperty>,
pub target: TargetConfig,
/// The name of this link
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct TargetConfig {
/// The target this link applies to. This should be the name of a component in the manifest
pub target: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub config: Vec<ConfigProperty>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub secrets: Vec<SecretProperty>,
}

impl PartialEq<TargetConfig> for String {
fn eq(&self, other: &TargetConfig) -> bool {
self == &other.target
}
}

/// Properties for spread scalers
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
Expand Down
7 changes: 4 additions & 3 deletions crates/wadm-types/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,12 +385,13 @@ fn check_dangling_links(manifest: &Manifest) -> Vec<ValidationFailure> {
let link_identifier = name
.as_ref()
.map(|n| format!("(name [{n}])"))
.unwrap_or_else(|| format!("(target [{target}])"));
if !lookup.contains_key(target) {
.unwrap_or_else(|| format!("(target [{}])", target.target));
if !lookup.contains_key(&target.target) {
failures.push(ValidationFailure::new(
ValidationFailureLevel::Warning,
format!(
"link {link_identifier} target [{target}] is not a listed component"
"link {link_identifier} target [{}] is not a listed component",
target.target
),
))
}
Expand Down
47 changes: 38 additions & 9 deletions crates/wadm/src/scaler/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,23 @@ use tokio::{
use tracing::{debug, error, instrument, trace, warn};
use wadm_types::{
api::StatusInfo, CapabilityProperties, Component, ComponentProperties, ConfigProperty,
Manifest, Properties, SpreadScalerProperty, Trait, TraitProperty, DAEMONSCALER_TRAIT,
LINK_TRAIT, SPREADSCALER_TRAIT,
Manifest, Properties, SecretProperty, SpreadScalerProperty, Trait, TraitProperty,
DAEMONSCALER_TRAIT, LINK_TRAIT, SPREADSCALER_TRAIT,
};

use crate::{
events::Event,
publisher::Publisher,
scaler::{spreadscaler::ActorSpreadScaler, Command, Scaler},
storage::{snapshot::SnapshotStore, ReadStore},
workers::{CommandPublisher, ConfigSource, LinkSource, StatusPublisher},
workers::{CommandPublisher, ConfigSource, LinkSource, SecretSource, StatusPublisher},
DEFAULT_LINK_NAME,
};

use super::{
configscaler::ConfigScaler,
daemonscaler::{provider::ProviderDaemonScaler, ActorDaemonScaler},
secretscaler::SecretScaler,
spreadscaler::{
link::{LinkScaler, LinkScalerConfig},
provider::{ProviderSpreadConfig, ProviderSpreadScaler},
Expand Down Expand Up @@ -133,7 +134,7 @@ impl<StateStore, P, L> ScalerManager<StateStore, P, L>
where
StateStore: ReadStore + Send + Sync + Clone + 'static,
P: Publisher + Clone + Send + Sync + 'static,
L: LinkSource + ConfigSource + Clone + Send + Sync + 'static,
L: LinkSource + ConfigSource + SecretSource + Clone + Send + Sync + 'static,
{
/// Creates a new ScalerManager configured to notify messages to `wadm.notify.{lattice_id}`
/// using the given jetstream client. Also creates an ephemeral consumer for notifications on
Expand Down Expand Up @@ -574,7 +575,7 @@ pub(crate) fn components_to_scalers<S, P, L>(
where
S: ReadStore + Send + Sync + Clone + 'static,
P: Publisher + Clone + Send + Sync + 'static,
L: LinkSource + ConfigSource + Clone + Send + Sync + 'static,
L: LinkSource + ConfigSource + SecretSource + Clone + Send + Sync + 'static,
{
let mut scalers: ScalerList = Vec::new();
for component in components.iter() {
Expand Down Expand Up @@ -630,12 +631,18 @@ where
let (mut config_scalers, source_config) = config_to_scalers(
snapshot_data.clone(),
name,
&p.source_config,
&p.source.config,
);
let (target_config_scalers, target_config) = config_to_scalers(
snapshot_data.clone(),
name,
&p.target_config,
&p.target.config,
);

let (target_secret_scalers, target_secrets) = secrets_to_scalers(
snapshot_data.clone(),
name,
&p.target.secrets,
);
config_scalers.extend(target_config_scalers);
match &component.properties {
Expand Down Expand Up @@ -745,12 +752,12 @@ where
let (mut config_scalers, source_config) = config_to_scalers(
snapshot_data.clone(),
name,
&p.source_config,
&p.source.config,
);
let (target_config_scalers, target_config) = config_to_scalers(
snapshot_data.clone(),
name,
&p.target_config,
&p.target.config,
);
config_scalers.extend(target_config_scalers);
match &component.properties {
Expand Down Expand Up @@ -857,6 +864,19 @@ fn config_to_scalers<C: ConfigSource + Send + Sync + Clone>(
.unzip()
}

fn secrets_to_scalers<S: SecretSource + Send + Sync + Clone>(
config_source: S,
model_name: &str,
secrets: &[SecretProperty],
) -> (Vec<SecretScaler<S>>, Vec<String>) {
secrets.iter().map(|s| {
let name = compute_secret_id(model_name, None, &s.name);
(SecretScaler::new(s.name, s.source, config_source), name)
});

todo!()
}

/// Based on the name of the model and the optionally provided ID, returns a unique ID for the
/// component that is a sanitized version of the component reference and model name, separated
/// by a dash.
Expand All @@ -880,6 +900,15 @@ pub(crate) fn compute_component_id(
}
}

pub(crate) fn compute_secret_id(
model_name: &str,
component_id: Option<&String>,
component_name: &str,
) -> String {
let name = compute_component_id(model_name, component_id, component_name);
format!("secret_{}", name)
}

#[cfg(test)]
mod test {
use crate::scaler::manager::compute_component_id;
Expand Down
1 change: 1 addition & 0 deletions crates/wadm/src/scaler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::{
pub mod configscaler;
pub mod daemonscaler;
pub mod manager;
pub mod secretscaler;
pub mod spreadscaler;

use manager::Notifications;
Expand Down
Loading

0 comments on commit 0fd10a5

Please sign in to comment.