Skip to content
This repository has been archived by the owner on Sep 4, 2020. It is now read-only.

Commit

Permalink
Merge pull request #264 from fibonacci1729/master
Browse files Browse the repository at this point in the history
resolve variables
  • Loading branch information
fibonacci1729 committed Oct 3, 2019
2 parents ac66579 + 4b1e9a7 commit f662f60
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 43 deletions.
23 changes: 15 additions & 8 deletions src/instigator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::{
component_instance::KubeComponentInstance,
configuration::ApplicationConfiguration,
configuration::ComponentConfiguration,
variable::{resolve_variables, get_variable_values},
parameter::{resolve_parameters, resolve_values, ParameterValue},
traits::{self, Autoscaler, Empty, HydraTrait, Ingress, ManualScaler, TraitBinding},
HydraStatus, Status,
Expand Down Expand Up @@ -131,11 +132,18 @@ impl Instigator {
}

component_updated = true;
// Resolve parameters
let parent = get_values(event.spec.parameter_values.clone());
let child = get_values(component.parameter_values.clone());
let merged_vals = resolve_values(child, parent.clone())?;
let params = resolve_parameters(comp_def.spec.parameters.clone(), merged_vals)?;
// Resolve variables/parameters
let variables = event.spec.variables.clone().unwrap_or(vec![]);
let parent = get_variable_values(Some(variables.clone()));

let child = component.parameter_values.clone()
.map(|values| resolve_variables(values, variables))
.unwrap_or(Ok(vec![]))?;

let params = resolve_parameters(
comp_def.spec.parameters.clone(),
resolve_values(child, vec![])?,
)?;

let inst_name = component.instance_name.clone();
let new_owner_ref = match phase {
Expand Down Expand Up @@ -246,9 +254,8 @@ impl Instigator {
component.name.clone(),
self.client.clone(),
)?;
// Resolve parameters
let parent = get_values(event.spec.parameter_values.clone());

// Resolve variables/parameters
let parent = get_variable_values(event.spec.variables.clone());
let inst_name = component.instance_name.clone();
// Load all of the traits related to this component.
let mut trait_manager = TraitManager {
Expand Down
1 change: 1 addition & 0 deletions src/schematic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod component;
pub mod component_instance;
pub mod configuration;
pub mod parameter;
pub mod variable;
pub mod traits;

#[cfg(test)]
Expand Down
8 changes: 4 additions & 4 deletions src/schematic/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::schematic::{parameter::ParameterValue, traits::TraitBinding};
use crate::schematic::{parameter::ParameterValue, variable::Variable, traits::TraitBinding};

/// Configuration creates an instance of a specified component, and attaches configuration to it.
///
Expand All @@ -10,7 +10,7 @@ use crate::schematic::{parameter::ParameterValue, traits::TraitBinding};
pub struct ComponentConfiguration {
/// The name of the component to instantiate
pub name: String,
// The name of the instance that is to be created
/// The name of the instance that is to be created
pub instance_name: String,
/// Values to substitute into the component
pub parameter_values: Option<Vec<ParameterValue>>,
Expand All @@ -25,7 +25,7 @@ pub struct ComponentConfiguration {
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ApplicationConfiguration {
pub parameter_values: Option<Vec<ParameterValue>>,
pub variables: Option<Vec<Variable>>,
pub scopes: Option<Vec<ScopeBinding>>,
pub components: Option<Vec<ComponentConfiguration>>,
}
Expand All @@ -39,4 +39,4 @@ pub struct ScopeBinding {
pub scope_type: String,

pub parameter_values: Option<Vec<ParameterValue>>,
}
}
19 changes: 19 additions & 0 deletions src/schematic/configuration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,22 @@ fn test_component_configuration() {
assert!(conf.parameter_values.is_some());
assert!(conf.traits.is_none());
}

#[test]
fn test_application_configuration() {
// Test that an application configuration deserializes correctly.

let conf: ApplicationConfiguration = serde_json::from_str(
r#"{
"variables": [
{
"name": "var1",
"value": 1234
}
]
}"#,
)
.expect("JSON must parse");

assert!(conf.variables.is_some());
}
9 changes: 0 additions & 9 deletions src/schematic/parameter.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use failure::Error;
use regex::Regex;
use std::collections::BTreeMap;

pub type ParameterList = Vec<Parameter>;
Expand Down Expand Up @@ -189,11 +188,3 @@ pub struct ParameterValue {
pub value: Option<serde_json::Value>,
pub from_param: Option<String>,
}

pub fn parse_from_variable(input: String) -> Option<String> {
lazy_static! {
static ref RE: Regex = Regex::new(r"^\[fromVariable\((?P<var>[[:word:]]+)\)\]$").unwrap();
}
RE.captures(&input)
.and_then(|cap| cap.name("var").map(|var| var.as_str().to_owned()))
}
22 changes: 0 additions & 22 deletions src/schematic/parameter_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,28 +159,6 @@ fn test_resolve_values() {
assert_eq!(Some("house"), merged.get("abode").expect("abode").as_str());
}

#[test]
fn test_parse_from_variable() {
assert_eq!(
Some("VAR_42".to_owned()),
parse_from_variable("[fromVariable(VAR_42)]".into())
);
assert_eq!(
Some("_".to_owned()),
parse_from_variable("[fromVariable(_)]".into())
);
assert_eq!(
Some("42".to_owned()),
parse_from_variable("[fromVariable(42)]".into())
);
assert_eq!(
Some("VAR".to_owned()),
parse_from_variable("[fromVariable(VAR)]".into())
);
assert_eq!(None, parse_from_variable("[fromVariable (VAR)]".into())); // illegal
assert_eq!(None, parse_from_variable("[fromVariable()]".into()));
}

#[test]
fn test_resolve_value() {
let mut params: ResolvedVals = BTreeMap::new();
Expand Down
166 changes: 166 additions & 0 deletions src/schematic/variable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
use super::parameter::ParameterValue;
use std::collections::BTreeMap;
use std::cmp::Ordering;
use failure::Error;
use regex::Regex;

fn parse_from_variable(input: String) -> Option<String> {
lazy_static! {
static ref RE: Regex = Regex::new(r#"^\[fromVariable\((?P<var>[[:word:]]+)\)\]$"#).unwrap();
}
RE.captures(&input)
.and_then(|cap| cap.name("var").map(|var| var.as_str().to_owned()))
}

/// Variables are common values that can be substituted into
/// predefined locations within an application configuration
/// using the [fromVariable(VARNAME)] syntax.
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Variable {
/// The variable's name (must be unique per configuration).
pub name: String,
/// The variable's name scalar value.
pub value: serde_json::Value,
}

impl From<Variable> for ParameterValue {
fn from(var: Variable) -> Self {
ParameterValue {
name: var.name.clone(),
value: Some(var.value.clone()),
from_param: None,
}
}
}

impl PartialOrd for Variable {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.name.cmp(&other.name))
}
}

impl PartialEq for Variable {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}

impl Ord for Variable {
fn cmp(&self, other: &Self) -> Ordering {
self.name.cmp(&other.name)
}
}

impl Eq for Variable {}

/// Expand any variables in current values using the variables defined in the configuration.
///
/// If a variable is referenced but undefined, the parameter value will be set to None.
pub fn expand_variables(values: &mut Vec<ParameterValue>, vars: BTreeMap<String, serde_json::Value>) -> Result<(), Error> {
for param in values.iter_mut() {
if let Some(value) = &param.value {
if let serde_json::Value::String(s) = value {
if let Some(ref var) = parse_from_variable(s.clone()) {
param.value = vars.get(var).map(|var| var.clone());
ensure!(param.value.is_some(), format!(
"parameter `{:?}` references undefined variable `{:?}`",
&param.name,
&var,
));
}
}
}
}
Ok(())
}

/// Resolve parameter values containing variables.
pub fn resolve_variables(values: Vec<ParameterValue>, vars: Vec<Variable>) -> Result<Vec<ParameterValue>, Error> {
expand_variables(
&mut values.clone(),
vars
.into_iter()
.map(|var| (var.name.clone(), var.value.clone()))
.collect::<BTreeMap<String, serde_json::Value>>())?;
Ok(values.to_vec())

}

/// Transform a vector of variables into parameter values.
pub fn get_variable_values(vars: Option<Vec<Variable>>) -> Vec<ParameterValue> {
let mut vars = vars.unwrap_or(vec![]);
dedup(&mut vars);
vars.into_iter()
.map(|var| var.into())
.collect()
}

// TODO: variables are unique per config should redefinition should be an error.
pub fn dedup<T: Ord>(values: &mut Vec<T>) {
values.sort_unstable();
values.dedup();
}

#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;

#[test]
fn test_resolve_variables() {
resolve_variables(
vec![
ParameterValue {
name: "dinner1".into(),
value: Some(json!("[fromVariable(pet1)]")),
from_param: None,
},
ParameterValue {
name: "dinner2".into(),
value: Some(json!("[fromVariable(pet2)]")),
from_param: None,
},
],
vec![
Variable{name: "pet1".into(), value: json!("cat")},
Variable{name: "pet2".into(), value: json!("dog")},
],
)
.expect("resolve variables");

// test parameter value referencing undefine variable should error.
resolve_variables(
vec![
ParameterValue {
name: "dinner".into(),
value: Some(json!("[fromVariable(cereal)]")),
from_param: None,
},
],
vec![],
).expect_err(r#"undefined variable `"cereal"`"#);
}

#[test]
fn test_parse_from_variable() {
assert_eq!(
Some("VAR_42".to_owned()),
parse_from_variable("[fromVariable(VAR_42)]".into())
);
assert_eq!(
Some("_".to_owned()),
parse_from_variable("[fromVariable(_)]".into())
);
assert_eq!(
Some("42".to_owned()),
parse_from_variable("[fromVariable(42)]".into())
);
assert_eq!(
Some("VAR".to_owned()),
parse_from_variable("[fromVariable(VAR)]".into())
);
assert_eq!(None, parse_from_variable("[fromVariable (VAR)]".into())); // illegal
assert_eq!(None, parse_from_variable("[fromVariable()]".into()));
}
}

0 comments on commit f662f60

Please sign in to comment.