Compare commits
	
		
			2 Commits
		
	
	
		
			29e74a2712
			...
			55143dcad4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 55143dcad4 | |||
| acfb93f1a2 | 
							
								
								
									
										7
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -1577,6 +1577,7 @@ dependencies = [
 | 
				
			|||||||
 "serde-value",
 | 
					 "serde-value",
 | 
				
			||||||
 "serde_json",
 | 
					 "serde_json",
 | 
				
			||||||
 "serde_yaml",
 | 
					 "serde_yaml",
 | 
				
			||||||
 | 
					 "similar",
 | 
				
			||||||
 "temp-dir",
 | 
					 "temp-dir",
 | 
				
			||||||
 "temp-file",
 | 
					 "temp-file",
 | 
				
			||||||
 "tokio",
 | 
					 "tokio",
 | 
				
			||||||
@ -4091,6 +4092,12 @@ 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"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										47
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								Cargo.toml
									
									
									
									
									
								
							@ -20,34 +20,23 @@ readme = "README.md"
 | 
				
			|||||||
license = "GNU AGPL v3"
 | 
					license = "GNU AGPL v3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[workspace.dependencies]
 | 
					[workspace.dependencies]
 | 
				
			||||||
log = "0.4.22"
 | 
					log = "0.4"
 | 
				
			||||||
env_logger = "0.11.5"
 | 
					env_logger = "0.11"
 | 
				
			||||||
derive-new = "0.7.0"
 | 
					derive-new = "0.7"
 | 
				
			||||||
async-trait = "0.1.82"
 | 
					async-trait = "0.1"
 | 
				
			||||||
tokio = { version = "1.40.0", features = [
 | 
					tokio = { version = "1.40", features = ["io-std", "fs", "macros", "rt-multi-thread"] }
 | 
				
			||||||
  "io-std",
 | 
					 | 
				
			||||||
  "fs",
 | 
					 | 
				
			||||||
  "macros",
 | 
					 | 
				
			||||||
  "rt-multi-thread",
 | 
					 | 
				
			||||||
] }
 | 
					 | 
				
			||||||
cidr = { features = ["serde"], version = "0.2" }
 | 
					cidr = { features = ["serde"], version = "0.2" }
 | 
				
			||||||
russh = "0.45.0"
 | 
					russh = "0.45"
 | 
				
			||||||
russh-keys = "0.45.0"
 | 
					russh-keys = "0.45"
 | 
				
			||||||
rand = "0.8.5"
 | 
					rand = "0.8"
 | 
				
			||||||
url = "2.5.4"
 | 
					url = "2.5"
 | 
				
			||||||
kube = "0.98.0"
 | 
					kube = "0.98"
 | 
				
			||||||
k8s-openapi = { version = "0.24.0", features = ["v1_30"] }
 | 
					k8s-openapi = { version = "0.24", features = ["v1_30"] }
 | 
				
			||||||
serde_yaml = "0.9.34"
 | 
					serde_yaml = "0.9"
 | 
				
			||||||
serde-value = "0.7.0"
 | 
					serde-value = "0.7"
 | 
				
			||||||
http = "1.2.0"
 | 
					http = "1.2"
 | 
				
			||||||
inquire = "0.7.5"
 | 
					inquire = "0.7"
 | 
				
			||||||
convert_case = "0.8.0"
 | 
					convert_case =  "0.8"
 | 
				
			||||||
chrono = "0.4"
 | 
					chrono = "0.4"
 | 
				
			||||||
 | 
					similar = "2"
 | 
				
			||||||
[workspace.dependencies.uuid]
 | 
					uuid = { version = "1.11", features = [ "v4", "fast-rng", "macro-diagnostics" ] }
 | 
				
			||||||
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
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -53,3 +53,4 @@ 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
 | 
				
			||||||
 | 
				
			|||||||
@ -10,4 +10,6 @@ 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));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -4,9 +4,11 @@ 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 {
 | 
				
			||||||
@ -48,8 +50,79 @@ 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))
 | 
					
 | 
				
			||||||
            .await
 | 
					        if *crate::config::DRY_RUN {
 | 
				
			||||||
 | 
					            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(¤t)
 | 
				
			||||||
 | 
					                        .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(¤t_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(¤t_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>
 | 
				
			||||||
 | 
				
			|||||||
@ -138,6 +138,7 @@ 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": {},
 | 
				
			||||||
@ -219,8 +220,29 @@ 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!(
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user