custom to toml serializer for api

This commit is contained in:
mbecker20
2024-06-10 17:34:56 -07:00
parent 28dc030e2b
commit 883f54431d
8 changed files with 312 additions and 30 deletions

61
Cargo.lock generated
View File

@@ -1482,6 +1482,15 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
@@ -2203,7 +2212,7 @@ dependencies = [
"strum 0.26.2",
"thiserror",
"tokio",
"tokio-tungstenite 0.22.0",
"tokio-tungstenite 0.23.0",
"tokio-util",
"tracing",
"typeshare",
@@ -2235,6 +2244,7 @@ dependencies = [
"mongo_indexed",
"monitor_client",
"mungos",
"ordered_hash_map",
"parse_csl",
"partial_derive2",
"periphery_client",
@@ -2251,6 +2261,7 @@ dependencies = [
"tokio",
"tokio-util",
"toml",
"toml_pretty",
"tower",
"tower-http",
"tracing",
@@ -2509,6 +2520,16 @@ dependencies = [
"num-traits",
]
[[package]]
name = "ordered_hash_map"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab0e5f22bf6dd04abd854a8874247813a8fa2c8c1260eba6fbb150270ce7c176"
dependencies = [
"hashbrown 0.13.2",
"serde",
]
[[package]]
name = "outref"
version = "0.5.1"
@@ -3736,19 +3757,19 @@ dependencies = [
"futures-util",
"log",
"tokio",
"tungstenite",
"tungstenite 0.21.0",
]
[[package]]
name = "tokio-tungstenite"
version = "0.22.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d46baf930138837d65e25e3b33be49c9228579a6135dbf756b5cb9e4283e7cef"
checksum = "becd34a233e7e31a3dbf7c7241b38320f57393dcae8e7324b0167d21b8e320b0"
dependencies = [
"futures-util",
"log",
"tokio",
"tungstenite",
"tungstenite 0.23.0",
]
[[package]]
@@ -3799,6 +3820,18 @@ dependencies = [
"winnow",
]
[[package]]
name = "toml_pretty"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8df1befb0cfdeeec0adc0d34e25dec5466ea469a88f5cfd75bded7bb879ae7c0"
dependencies = [
"ordered_hash_map",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "tonic"
version = "0.11.0"
@@ -4042,6 +4075,24 @@ dependencies = [
"utf-8",
]
[[package]]
name = "tungstenite"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8"
dependencies = [
"byteorder",
"bytes",
"data-encoding",
"http 1.1.0",
"httparse",
"log",
"rand",
"sha1",
"thiserror",
"utf-8",
]
[[package]]
name = "typed-builder"
version = "0.10.0"

View File

@@ -34,6 +34,7 @@ partial_derive2 = "0.4.3"
derive_variants = "1.0.0"
mongo_indexed = "0.3.0"
resolver_api = "1.1.0"
toml_pretty = "0.2.0"
parse_csl = "0.1.0"
mungos = "0.5.6"
svi = "1.0.1"
@@ -50,9 +51,10 @@ axum = { version = "0.7.5", features = ["ws", "json"] }
axum-extra = { version = "0.9.3", features = ["typed-header"] }
tower = { version = "0.4.13", features = ["timeout"] }
tower-http = { version = "0.5.2", features = ["fs", "cors"] }
tokio-tungstenite = "0.22.0"
tokio-tungstenite = "0.23.0"
# SER/DE
ordered_hash_map = { version = "0.4.0", features = ["serde"] }
serde = { version = "1.0.203", features = ["derive"] }
strum = { version = "0.26.2", features = ["derive"] }
serde_json = "1.0.117"

View File

@@ -28,11 +28,13 @@ partial_derive2.workspace = true
derive_variants.workspace = true
mongo_indexed.workspace = true
resolver_api.workspace = true
toml_pretty.workspace = true
parse_csl.workspace = true
mungos.workspace = true
slack.workspace = true
svi.workspace = true
# external
ordered_hash_map.workspace = true
urlencoding.workspace = true
aws-sdk-ec2.workspace = true
aws-config.workspace = true

View File

@@ -14,7 +14,11 @@ use monitor_client::{
alerter::Alerter,
build::Build,
builder::{Builder, BuilderConfig},
deployment::{Deployment, DeploymentImage},
deployment::{
conversions_to_string, term_signal_labels_to_string,
Deployment, DeploymentImage,
},
environment_vars_to_string,
permission::{PermissionLevel, UserTarget},
procedure::Procedure,
repo::Repo,
@@ -30,8 +34,10 @@ use monitor_client::{
},
};
use mungos::find::find_collect;
use ordered_hash_map::OrderedHashMap;
use partial_derive2::PartialDiff;
use resolver_api::Resolve;
use serde_json::Value;
use crate::{
helpers::query::get_user_user_group_ids,
@@ -305,7 +311,7 @@ impl Resolve<ExportResourcesToToml, User> for State {
.context("failed to get variables from db")?;
}
let toml = toml::to_string(&res)
let toml = serialize_resources_toml(&res)
.context("failed to serialize resources to toml")?;
Ok(ExportResourcesToTomlResponse { toml })
@@ -530,3 +536,192 @@ fn convert_resource<R: MonitorResource>(
config,
}
}
fn serialize_resources_toml(
resources: &ResourcesToml,
) -> anyhow::Result<String> {
let mut res = String::new();
for server in &resources.servers {
if !res.is_empty() {
res.push_str("\n##\n");
}
res.push_str("[[server]]\n");
res.push_str(
&toml_pretty::to_string_custom_tab(&server, " ")
.context("failed to serialize servers to toml")?,
);
}
for deployment in &resources.deployments {
if !res.is_empty() {
res.push_str("\n##\n");
}
res.push_str("[[deployment]]\n");
let mut parsed: OrderedHashMap<String, Value> =
serde_json::from_str(&serde_json::to_string(&deployment)?)?;
let config = parsed
.get_mut("config")
.context("deployment has no config?")?
.as_object_mut()
.context("config is not object?")?;
if let Some(term_signal_labels) =
&deployment.config.term_signal_labels
{
config.insert(
"term_signal_labels".to_string(),
Value::String(term_signal_labels_to_string(
term_signal_labels,
)),
);
}
if let Some(ports) = &deployment.config.ports {
config.insert(
"ports".to_string(),
Value::String(conversions_to_string(ports)),
);
}
if let Some(volumes) = &deployment.config.volumes {
config.insert(
"volumes".to_string(),
Value::String(conversions_to_string(volumes)),
);
}
if let Some(environment) = &deployment.config.environment {
config.insert(
"environment".to_string(),
Value::String(environment_vars_to_string(environment)),
);
}
if let Some(labels) = &deployment.config.labels {
config.insert(
"labels".to_string(),
Value::String(environment_vars_to_string(labels)),
);
}
res.push_str(
&toml_pretty::to_string_custom_tab(&deployment, " ")
.context("failed to serialize deployments to toml")?,
);
}
for build in &resources.builds {
if !res.is_empty() {
res.push_str("\n##\n");
}
let mut parsed: OrderedHashMap<String, Value> =
serde_json::from_str(&serde_json::to_string(&build)?)?;
let config = parsed
.get_mut("config")
.context("build has no config?")?
.as_object_mut()
.context("config is not object?")?;
if let Some(build_args) = &build.config.build_args {
config.insert(
"build_args".to_string(),
Value::String(environment_vars_to_string(build_args)),
);
}
if let Some(labels) = &build.config.labels {
config.insert(
"labels".to_string(),
Value::String(environment_vars_to_string(labels)),
);
}
res.push_str("[[build]]\n");
res.push_str(
&toml_pretty::to_string_custom_tab(&build, " ")
.context("failed to serialize builds to toml")?,
);
}
for repo in &resources.repos {
if !res.is_empty() {
res.push_str("\n##\n");
}
res.push_str("[[repo]]\n");
res.push_str(
&toml_pretty::to_string_custom_tab(&repo, " ")
.context("failed to serialize repos to toml")?,
);
}
for procedure in &resources.procedures {
if !res.is_empty() {
res.push_str("\n##\n");
}
res.push_str("[[procedure]]\n");
res.push_str(
&toml_pretty::to_string_custom_tab(&procedure, " ")
.context("failed to serialize procedures to toml")?,
);
}
for alerter in &resources.alerters {
if !res.is_empty() {
res.push_str("\n##\n");
}
res.push_str("[[alerter]]\n");
res.push_str(
&toml_pretty::to_string_custom_tab(&alerter, " ")
.context("failed to serialize alerters to toml")?,
);
}
for builder in &resources.builders {
if !res.is_empty() {
res.push_str("\n##\n");
}
res.push_str("[[builder]]\n");
res.push_str(
&toml_pretty::to_string_custom_tab(&builder, " ")
.context("failed to serialize builders to toml")?,
);
}
for server_template in &resources.server_templates {
if !res.is_empty() {
res.push_str("\n##\n");
}
res.push_str("[[server_template]]\n");
res.push_str(
&toml_pretty::to_string_custom_tab(&server_template, " ")
.context("failed to serialize server_templates to toml")?,
);
}
for resource_sync in &resources.resource_syncs {
if !res.is_empty() {
res.push_str("\n##\n");
}
res.push_str("[[resource_sync]]\n");
res.push_str(
&toml_pretty::to_string_custom_tab(&resource_sync, " ")
.context("failed to serialize resource_syncs to toml")?,
);
}
for variable in &resources.variables {
if !res.is_empty() {
res.push_str("\n##\n");
}
res.push_str("[[variable]]\n");
res.push_str(
&toml_pretty::to_string_custom_tab(&variable, " ")
.context("failed to serialize variables to toml")?,
);
}
for user_group in &resources.user_groups {
if !res.is_empty() {
res.push_str("\n##\n");
}
res.push_str("[[user_group]]\n");
res.push_str(
&toml_pretty::to_string_custom_tab(&user_group, " ")
.context("failed to serialize user_groups to toml")?,
);
}
Ok(res)
}

View File

@@ -17,29 +17,29 @@ docker = ["dep:bollard"]
[dependencies]
# mogh
mongo_indexed = { workspace = true, optional = true }
serror.workspace = true
resolver_api.workspace = true
derive_default_builder.workspace = true
derive_empty_traits.workspace = true
async_timing_util.workspace = true
partial_derive2.workspace = true
derive_variants.workspace = true
derive_default_builder.workspace = true
async_timing_util.workspace = true
resolver_api.workspace = true
serror.workspace = true
# external
bollard = { workspace = true, optional = true }
reqwest.workspace = true
anyhow.workspace = true
thiserror.workspace = true
serde.workspace = true
serde_json.workspace = true
envy.workspace = true
tokio.workspace = true
tokio-util.workspace = true
tokio-tungstenite.workspace = true
futures.workspace = true
typeshare.workspace = true
strum.workspace = true
derive_builder.workspace = true
serde_json.workspace = true
tokio-util.workspace = true
thiserror.workspace = true
typeshare.workspace = true
futures.workspace = true
reqwest.workspace = true
tracing.workspace = true
anyhow.workspace = true
serde.workspace = true
tokio.workspace = true
strum.workspace = true
envy.workspace = true
uuid.workspace = true
clap.workspace = true
bson.workspace = true

View File

@@ -281,6 +281,16 @@ pub struct Conversion {
pub container: String,
}
pub fn conversions_to_string(conversions: &[Conversion]) -> String {
conversions
.iter()
.map(|Conversion { local, container }| {
format!("{local}={container}")
})
.collect::<Vec<_>>()
.join("\n")
}
pub fn conversions_from_str(
value: &str,
) -> anyhow::Result<Vec<Conversion>> {
@@ -567,6 +577,18 @@ pub struct TerminationSignalLabel {
pub label: String,
}
pub fn term_signal_labels_to_string(
labels: &[TerminationSignalLabel],
) -> String {
labels
.iter()
.map(|TerminationSignalLabel { signal, label }| {
format!("{signal}={label}")
})
.collect::<Vec<_>>()
.join("\n")
}
pub fn term_signal_labels_from_str(
value: &str,
) -> anyhow::Result<Vec<TerminationSignalLabel>> {

View File

@@ -334,6 +334,16 @@ pub struct EnvironmentVar {
pub value: String,
}
pub fn environment_vars_to_string(vars: &[EnvironmentVar]) -> String {
vars
.iter()
.map(|EnvironmentVar { variable, value }| {
format!("{variable}={value}")
})
.collect::<Vec<_>>()
.join("\n")
}
pub fn environment_vars_from_str(
value: &str,
) -> anyhow::Result<Vec<EnvironmentVar>> {

View File

@@ -48,13 +48,6 @@ pub struct ResourcesToml {
)]
pub procedures: Vec<ResourceToml<PartialProcedureConfig>>,
#[serde(
default,
rename = "builder",
skip_serializing_if = "Vec::is_empty"
)]
pub builders: Vec<ResourceToml<PartialBuilderConfig>>,
#[serde(
default,
rename = "alerter",
@@ -62,6 +55,13 @@ pub struct ResourcesToml {
)]
pub alerters: Vec<ResourceToml<PartialAlerterConfig>>,
#[serde(
default,
rename = "builder",
skip_serializing_if = "Vec::is_empty"
)]
pub builders: Vec<ResourceToml<PartialBuilderConfig>>,
#[serde(
default,
rename = "server_template",