-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: create and manage the .wws.toml file and its metadata
- Loading branch information
1 parent
764a4ad
commit e2bcf3f
Showing
6 changed files
with
208 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,5 @@ target | |
!tests/**/*.wasm | ||
examples/*.toml | ||
.DS_Store | ||
.wws | ||
.wws | ||
.wws.toml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,109 +1,108 @@ | ||
// Copyright 2022 VMware, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::data::kv::KVConfigData; | ||
use crate::runtimes::metadata::Runtime; | ||
use anyhow::{anyhow, Result}; | ||
use serde::{Deserialize, Deserializer}; | ||
use std::collections::HashMap; | ||
use std::path::PathBuf; | ||
use std::{env, fs}; | ||
use toml::from_str; | ||
use serde::{Deserialize, Serialize}; | ||
use std::{ | ||
fs, | ||
path::{Path, PathBuf}, | ||
}; | ||
|
||
/// Workers configuration. These files are optional when no configuration change is required. | ||
#[derive(Deserialize, Clone)] | ||
pub struct Config { | ||
/// Worker name. For logging purposes | ||
pub name: Option<String>, | ||
/// Mandatory version of the file | ||
pub version: String, | ||
/// Optional data configuration | ||
pub data: Option<ConfigData>, | ||
/// Optional environment configuration | ||
#[serde(deserialize_with = "read_environment_variables", default)] | ||
pub vars: HashMap<String, String>, | ||
} | ||
/// Config file name | ||
const CONFIG_FILENAME: &str = ".wws.toml"; | ||
|
||
/// Configure a data plugin for the worker | ||
#[derive(Deserialize, Clone)] | ||
pub struct ConfigData { | ||
/// Creates a Key/Value store associated to the given worker | ||
pub kv: Option<KVConfigData>, | ||
/// Loads the data from the Project definition file or .wws.toml. | ||
/// This file contains information about the different runtimes | ||
/// required for this project. You can think of those as dependencies. | ||
/// | ||
/// If your project requires to run workers using any interpreted | ||
/// language (except Js, which it's embedded), you will need to install | ||
/// a language runtime. | ||
/// | ||
/// For reproducibility, this file can be commited to the project | ||
/// repository so other developers can download them directly. | ||
#[derive(Deserialize, Serialize)] | ||
pub struct Config { | ||
/// Version of the .wws file | ||
version: u32, | ||
/// List of repositories | ||
repositories: Vec<ConfigRepository>, | ||
} | ||
|
||
// TODO: Remove it when start adding the new subcommands | ||
#[allow(dead_code)] | ||
impl Config { | ||
/// Try to read the configuration from a TOML file. The path contains the local path | ||
/// to the worker configuration. The file should use the same name as the worker, | ||
/// with the .toml extension | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// name = "todos" | ||
/// version = "1" | ||
/// | ||
/// [data] | ||
/// | ||
/// [data.kv] | ||
/// namespace = "todos" | ||
/// ``` | ||
pub fn try_from_file(path: PathBuf) -> Result<Self> { | ||
let contents = fs::read_to_string(&path)?; | ||
/// Load the config file if it's present. It not, it will create a | ||
/// new empty config. | ||
pub fn load(project_root: &Path) -> Result<Self> { | ||
let config_path = Self::config_path(project_root); | ||
|
||
let try_config: Result<Config, toml::de::Error> = from_str(&contents); | ||
if config_path.exists() { | ||
toml::from_str(&fs::read_to_string(config_path)?).map_err(|_| { | ||
anyhow!("Error opening the .wws.toml file. The file format is not correct") | ||
}) | ||
} else { | ||
let new_repo = ConfigRepository { | ||
name: "wlr".to_string(), | ||
runtimes: Vec::new(), | ||
}; | ||
|
||
match try_config { | ||
Ok(c) => Ok(c), | ||
Err(err) => Err(anyhow!( | ||
"Error reading the configuration file at {}: {}", | ||
&path.to_str().unwrap_or("?"), | ||
err | ||
)), | ||
Ok(Self { | ||
version: 1, | ||
repositories: vec![new_repo], | ||
}) | ||
} | ||
} | ||
|
||
/// Returns a data Key/Value configuration if available | ||
pub fn data_kv_config(&self) -> Option<&KVConfigData> { | ||
self.data.as_ref()?.kv.as_ref() | ||
} | ||
/// Save a new installed runtime | ||
pub fn save_runtime(&mut self, repository: &str, runtime: &Runtime) { | ||
let repo = self.repositories.iter_mut().find(|r| r.name == repository); | ||
|
||
/// Returns the data Key/Value namespace if available | ||
pub fn data_kv_namespace(&self) -> Option<String> { | ||
Some(self.data_kv_config()?.namespace.clone()) | ||
// Shadow to init an empty one if required | ||
match repo { | ||
Some(r) => r.runtimes.push(runtime.clone()), | ||
None => { | ||
let new_repo = ConfigRepository { | ||
name: repository.to_string(), | ||
runtimes: vec![runtime.clone()], | ||
}; | ||
|
||
self.repositories.push(new_repo); | ||
} | ||
}; | ||
} | ||
} | ||
|
||
/// Deserialize the HashMap of variables. By default, this | ||
/// function won't modify the K or the V of the HashMap. If | ||
/// V starts with $, its value will be read from the server | ||
/// environment variables | ||
fn read_environment_variables<'de, D>(deserializer: D) -> Result<HashMap<String, String>, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
let result: Result<Option<HashMap<String, String>>, D::Error> = | ||
Deserialize::deserialize(deserializer); | ||
/// Remove an existing runtime if it's present. | ||
pub fn remove_runtime(&mut self, repository: &str, runtime: &Runtime) { | ||
let repo = self.repositories.iter_mut().find(|r| r.name == repository); | ||
|
||
match result { | ||
Ok(value) => match value { | ||
Some(mut options) => { | ||
for (_, value) in options.iter_mut() { | ||
// Read the value from the environment variables if available. | ||
// If not, it will default to an empty string | ||
if value.starts_with('$') && !value.contains(' ') { | ||
// Remove the $ | ||
value.remove(0); | ||
// Shadow to init an empty one if required | ||
if let Some(repo) = repo { | ||
repo.runtimes.retain(|r| r != runtime); | ||
}; | ||
} | ||
|
||
match env::var(&value) { | ||
Ok(env_value) => *value = env_value, | ||
Err(_) => *value = String::new(), | ||
} | ||
} | ||
} | ||
/// Write the current configuration into the `.wws.toml` file. It will | ||
/// store it in the project root folder | ||
pub fn save(&self, project_root: &Path) -> Result<()> { | ||
let contents = toml::to_string_pretty(self)?; | ||
|
||
Ok(options) | ||
} | ||
None => Ok(HashMap::new()), | ||
}, | ||
Err(err) => Err(err), | ||
fs::write(Self::config_path(project_root), contents) | ||
.map_err(|_| anyhow!("Error saving the .wws.toml file")) | ||
} | ||
|
||
/// Retrieve the configuration path from the project root | ||
fn config_path(project_root: &Path) -> PathBuf { | ||
project_root.join(CONFIG_FILENAME) | ||
} | ||
} | ||
|
||
#[derive(Deserialize, Serialize)] | ||
pub struct ConfigRepository { | ||
/// Local name to identify the repository. It avoids collisions when installing | ||
/// language runtimes | ||
name: String, | ||
/// Installed runtimes | ||
runtimes: Vec<Runtime>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
// Copyright 2022 VMware, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::data::kv::KVConfigData; | ||
use anyhow::{anyhow, Result}; | ||
use serde::{Deserialize, Deserializer}; | ||
use std::collections::HashMap; | ||
use std::path::PathBuf; | ||
use std::{env, fs}; | ||
use toml::from_str; | ||
|
||
/// Workers configuration. These files are optional when no configuration change is required. | ||
#[derive(Deserialize, Clone)] | ||
pub struct Config { | ||
/// Worker name. For logging purposes | ||
pub name: Option<String>, | ||
/// Mandatory version of the file | ||
pub version: String, | ||
/// Optional data configuration | ||
pub data: Option<ConfigData>, | ||
/// Optional environment configuration | ||
#[serde(deserialize_with = "read_environment_variables", default)] | ||
pub vars: HashMap<String, String>, | ||
} | ||
|
||
/// Configure a data plugin for the worker | ||
#[derive(Deserialize, Clone)] | ||
pub struct ConfigData { | ||
/// Creates a Key/Value store associated to the given worker | ||
pub kv: Option<KVConfigData>, | ||
} | ||
|
||
impl Config { | ||
/// Try to read the configuration from a TOML file. The path contains the local path | ||
/// to the worker configuration. The file should use the same name as the worker, | ||
/// with the .toml extension | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// name = "todos" | ||
/// version = "1" | ||
/// | ||
/// [data] | ||
/// | ||
/// [data.kv] | ||
/// namespace = "todos" | ||
/// ``` | ||
pub fn try_from_file(path: PathBuf) -> Result<Self> { | ||
let contents = fs::read_to_string(&path)?; | ||
|
||
let try_config: Result<Config, toml::de::Error> = from_str(&contents); | ||
|
||
match try_config { | ||
Ok(c) => Ok(c), | ||
Err(err) => Err(anyhow!( | ||
"Error reading the configuration file at {}: {}", | ||
&path.to_str().unwrap_or("?"), | ||
err | ||
)), | ||
} | ||
} | ||
|
||
/// Returns a data Key/Value configuration if available | ||
pub fn data_kv_config(&self) -> Option<&KVConfigData> { | ||
self.data.as_ref()?.kv.as_ref() | ||
} | ||
|
||
/// Returns the data Key/Value namespace if available | ||
pub fn data_kv_namespace(&self) -> Option<String> { | ||
Some(self.data_kv_config()?.namespace.clone()) | ||
} | ||
} | ||
|
||
/// Deserialize the HashMap of variables. By default, this | ||
/// function won't modify the K or the V of the HashMap. If | ||
/// V starts with $, its value will be read from the server | ||
/// environment variables | ||
fn read_environment_variables<'de, D>(deserializer: D) -> Result<HashMap<String, String>, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
let result: Result<Option<HashMap<String, String>>, D::Error> = | ||
Deserialize::deserialize(deserializer); | ||
|
||
match result { | ||
Ok(value) => match value { | ||
Some(mut options) => { | ||
for (_, value) in options.iter_mut() { | ||
// Read the value from the environment variables if available. | ||
// If not, it will default to an empty string | ||
if value.starts_with('$') && !value.contains(' ') { | ||
// Remove the $ | ||
value.remove(0); | ||
|
||
match env::var(&value) { | ||
Ok(env_value) => *value = env_value, | ||
Err(_) => *value = String::new(), | ||
} | ||
} | ||
} | ||
|
||
Ok(options) | ||
} | ||
None => Ok(HashMap::new()), | ||
}, | ||
Err(err) => Err(err), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters