Compare commits

..

No commits in common. "55143dcad42b68a601697500ea15564fe2916892" and "29e74a2712de5747951b029b3fb07bcb630778b5" have entirely different histories.

6 changed files with 32 additions and 126 deletions

7
Cargo.lock generated
View File

@ -1577,7 +1577,6 @@ dependencies = [
"serde-value", "serde-value",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"similar",
"temp-dir", "temp-dir",
"temp-file", "temp-file",
"tokio", "tokio",
@ -4092,12 +4091,6 @@ dependencies = [
"rand_core 0.6.4", "rand_core 0.6.4",
] ]
[[package]]
name = "similar"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
[[package]] [[package]]
name = "simple_asn1" name = "simple_asn1"
version = "0.6.3" version = "0.6.3"

View File

@ -20,23 +20,34 @@ readme = "README.md"
license = "GNU AGPL v3" license = "GNU AGPL v3"
[workspace.dependencies] [workspace.dependencies]
log = "0.4" log = "0.4.22"
env_logger = "0.11" env_logger = "0.11.5"
derive-new = "0.7" derive-new = "0.7.0"
async-trait = "0.1" async-trait = "0.1.82"
tokio = { version = "1.40", features = ["io-std", "fs", "macros", "rt-multi-thread"] } tokio = { version = "1.40.0", features = [
"io-std",
"fs",
"macros",
"rt-multi-thread",
] }
cidr = { features = ["serde"], version = "0.2" } cidr = { features = ["serde"], version = "0.2" }
russh = "0.45" russh = "0.45.0"
russh-keys = "0.45" russh-keys = "0.45.0"
rand = "0.8" rand = "0.8.5"
url = "2.5" url = "2.5.4"
kube = "0.98" kube = "0.98.0"
k8s-openapi = { version = "0.24", features = ["v1_30"] } k8s-openapi = { version = "0.24.0", features = ["v1_30"] }
serde_yaml = "0.9" serde_yaml = "0.9.34"
serde-value = "0.7" serde-value = "0.7.0"
http = "1.2" http = "1.2.0"
inquire = "0.7" inquire = "0.7.5"
convert_case = "0.8" convert_case = "0.8.0"
chrono = "0.4" chrono = "0.4"
similar = "2"
uuid = { version = "1.11", features = [ "v4", "fast-rng", "macro-diagnostics" ] } [workspace.dependencies.uuid]
version = "1.11.0"
features = [
"v4", # Lets you generate random UUIDs
"fast-rng", # Use a faster (but still sufficiently random) RNG
"macro-diagnostics", # Enable better diagnostics for compile-time UUIDs
]

View File

@ -53,4 +53,3 @@ fqdn = { version = "0.4.6", features = [
] } ] }
temp-dir = "0.1.14" temp-dir = "0.1.14"
dyn-clone = "1.0.19" dyn-clone = "1.0.19"
similar.workspace = true

View File

@ -10,6 +10,4 @@ lazy_static! {
std::env::var("HARMONY_REGISTRY_URL").unwrap_or_else(|_| "hub.nationtech.io".to_string()); std::env::var("HARMONY_REGISTRY_URL").unwrap_or_else(|_| "hub.nationtech.io".to_string());
pub static ref REGISTRY_PROJECT: String = pub static ref REGISTRY_PROJECT: String =
std::env::var("HARMONY_REGISTRY_PROJECT").unwrap_or_else(|_| "harmony".to_string()); std::env::var("HARMONY_REGISTRY_PROJECT").unwrap_or_else(|_| "harmony".to_string());
pub static ref DRY_RUN: bool =
std::env::var("HARMONY_DRY_RUN").map_or(true, |value| value.parse().unwrap_or(true));
} }

View File

@ -4,11 +4,9 @@ use kube::{
Api, Client, Config, Error, Resource, Api, Client, Config, Error, Resource,
api::{Patch, PatchParams}, api::{Patch, PatchParams},
config::{KubeConfigOptions, Kubeconfig}, config::{KubeConfigOptions, Kubeconfig},
core::ErrorResponse,
}; };
use log::{debug, error, trace}; use log::{debug, error, trace};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use similar::TextDiff;
#[derive(new)] #[derive(new)]
pub struct K8sClient { pub struct K8sClient {
@ -50,79 +48,8 @@ impl K8sClient {
.name .name
.as_ref() .as_ref()
.expect("K8s Resource should have a name"); .expect("K8s Resource should have a name");
api.patch(name, &patch_params, &Patch::Apply(resource))
if *crate::config::DRY_RUN { .await
match api.get(name).await {
Ok(current) => {
trace!("Received current value {current:#?}");
// The resource exists, so we calculate and display a diff.
println!("\nPerforming dry-run for resource: '{}'", name);
let mut current_yaml = serde_yaml::to_value(&current)
.expect(&format!("Could not serialize current value : {current:#?}"));
if current_yaml.is_mapping() && current_yaml.get("status").is_some() {
let map = current_yaml.as_mapping_mut().unwrap();
let removed = map.remove_entry("status");
trace!("Removed status {:?}", removed);
} else {
trace!(
"Did not find status entry for current object {}/{}",
current.meta().namespace.as_ref().unwrap_or(&"".to_string()),
current.meta().name.as_ref().unwrap_or(&"".to_string())
);
}
let current_yaml = serde_yaml::to_string(&current_yaml)
.unwrap_or_else(|_| "Failed to serialize current resource".to_string());
let new_yaml = serde_yaml::to_string(resource)
.unwrap_or_else(|_| "Failed to serialize new resource".to_string());
if current_yaml == new_yaml {
println!("No changes detected.");
// Return the current resource state as there are no changes.
return Ok(current);
}
println!("Changes detected:");
let diff = TextDiff::from_lines(&current_yaml, &new_yaml);
// Iterate over the changes and print them in a git-like diff format.
for change in diff.iter_all_changes() {
let sign = match change.tag() {
similar::ChangeTag::Delete => "-",
similar::ChangeTag::Insert => "+",
similar::ChangeTag::Equal => " ",
};
print!("{}{}", sign, change);
}
// In a dry run, we return the new resource state that would have been applied.
Ok(resource.clone())
}
Err(Error::Api(ErrorResponse { code: 404, .. })) => {
// The resource does not exist, so the "diff" is the entire new resource.
println!("\nPerforming dry-run for new resource: '{}'", name);
println!(
"Resource does not exist. It would be created with the following content:"
);
let new_yaml = serde_yaml::to_string(resource)
.unwrap_or_else(|_| "Failed to serialize new resource".to_string());
// Print each line of the new resource with a '+' prefix.
for line in new_yaml.lines() {
println!("+{}", line);
}
// In a dry run, we return the new resource state that would have been created.
Ok(resource.clone())
}
Err(e) => {
// Another API error occurred.
error!("Failed to get resource '{}': {}", name, e);
Err(e)
}
}
} else {
return api
.patch(name, &patch_params, &Patch::Apply(resource))
.await;
}
} }
pub async fn apply_many<K>(&self, resource: &Vec<K>, ns: Option<&str>) -> Result<Vec<K>, Error> pub async fn apply_many<K>(&self, resource: &Vec<K>, ns: Option<&str>) -> Result<Vec<K>, Error>

View File

@ -138,7 +138,6 @@ impl K8sTenantManager {
"kind": "NetworkPolicy", "kind": "NetworkPolicy",
"metadata": { "metadata": {
"name": format!("{}-network-policy", config.name), "name": format!("{}-network-policy", config.name),
"namespace": self.get_namespace_name(config),
}, },
"spec": { "spec": {
"podSelector": {}, "podSelector": {},
@ -220,29 +219,8 @@ impl K8sTenantManager {
}) })
}) })
.collect(); .collect();
let ports: Option<Vec<NetworkPolicyPort>> =
c.1.as_ref().map(|spec| match &spec.data {
super::PortSpecData::SinglePort(port) => vec![NetworkPolicyPort {
port: Some(IntOrString::Int(port.clone().into())),
..Default::default()
}],
super::PortSpecData::PortRange(start, end) => vec![NetworkPolicyPort {
port: Some(IntOrString::Int(start.clone().into())),
end_port: Some(end.clone().into()),
protocol: None, // Not currently supported by Harmony
}],
super::PortSpecData::ListOfPorts(items) => items
.iter()
.map(|i| NetworkPolicyPort {
port: Some(IntOrString::Int(i.clone().into())),
..Default::default()
})
.collect(),
});
let rule = serde_json::from_value::<NetworkPolicyIngressRule>(json!({ let rule = serde_json::from_value::<NetworkPolicyIngressRule>(json!({
"from": cidr_list, "from": cidr_list
"ports": ports,
})) }))
.map_err(|e| { .map_err(|e| {
ExecutorError::ConfigurationError(format!( ExecutorError::ConfigurationError(format!(