feat(k8sanywhere):added functions to ensure deployment has ready replicas, as well as a function to patch resource by merge"
feat(certificate): completed functions to update the default okd ingress class to use a specfied ca-cert, tls.key, and tls.crt
This commit is contained in:
		
							parent
							
								
									41f1bca69c
								
							
						
					
					
						commit
						84ae60fbbf
					
				| @ -144,6 +144,25 @@ impl K8sClient { | ||||
|         Ok(pods.get_opt(name).await?) | ||||
|     } | ||||
| 
 | ||||
|     pub async fn patch_resource_by_merge( | ||||
|         &self, | ||||
|         name: &str, | ||||
|         namespace: Option<&str>, | ||||
|         gvk: &GroupVersionKind, | ||||
|         patch: Value, | ||||
|     ) -> Result<(), Error> { | ||||
|         let gvk = ApiResource::from_gvk(gvk); | ||||
|         let resource: Api<DynamicObject> = if let Some(ns) = namespace { | ||||
|             Api::namespaced_with(self.client.clone(), ns, &gvk) | ||||
|         } else { | ||||
|             Api::default_namespaced_with(self.client.clone(), &gvk) | ||||
|         }; | ||||
|         let pp = PatchParams::default(); | ||||
|         let merge = Patch::Merge(&patch); | ||||
|         resource.patch(name, &pp, &merge).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     pub async fn scale_deployment( | ||||
|         &self, | ||||
|         name: &str, | ||||
|  | ||||
| @ -1,124 +0,0 @@ | ||||
| use std::{path::PathBuf, sync::Arc}; | ||||
| 
 | ||||
| use fqdn::FQDN; | ||||
| use harmony_types::id::Id; | ||||
| 
 | ||||
| use crate::{ | ||||
|     data::Version, | ||||
|     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, | ||||
|     inventory::Inventory, | ||||
|     score::Score, | ||||
|     topology::{k8s::K8sClient, K8sclient, Topology}, | ||||
| }; | ||||
| 
 | ||||
| pub struct UpdateDefaultOkdIngressScore { | ||||
|     ca_name: String, | ||||
|     domain: FQDN, | ||||
| } | ||||
| 
 | ||||
| impl<T: Topology> Score<T> for UpdateDefaultOkdIngressScore { | ||||
|     fn name(&self) -> String { | ||||
|         "UpdateDefaultOkdIngressScore".to_string() | ||||
|     } | ||||
| 
 | ||||
|     #[doc(hidden)] | ||||
|     fn create_interpret(&self) -> Box<dyn Interpret<T>> { | ||||
|         Box::new(UpdateDefaultOkdIngressInterpret { | ||||
|             score: self.clone(), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct UpdateDefaultOkdIngressInterpret { | ||||
|     score: UpdateDefaultOkdIngressScore, | ||||
| } | ||||
| 
 | ||||
| #[async_trait] | ||||
| impl<T: Topology + K8sclient> Interpret<T> for UpdateDefaultOkdIngressInterpret { | ||||
|     async fn execute(&self, inventory: &Inventory, topology: &T) -> Result<Outcome, InterpretError> { | ||||
|         let client = topology.k8s_client().await?; | ||||
|         let ca_name = self.score.ca_name.clone(); | ||||
|         let domain = self.score.domain.clone(); | ||||
|         let secret_name = "ingress_ca_secret"; | ||||
|         self.ensure_ingress_operator(&client).await?; | ||||
|         self.create_ca_cm(&client, &domain, &ca_name).await?; | ||||
|         self.patch_proxy(&client, &ca_name).await?; | ||||
|         self.create_tls_secret(&client, &secret_name).await?; | ||||
|         self.patch_ingress(&client, &secret_name).await?; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     fn get_name(&self) -> InterpretName { | ||||
|         InterpretName::Custom("UpdateDefaultOkd") | ||||
|     } | ||||
| 
 | ||||
|     fn get_version(&self) -> Version { | ||||
|         todo!() | ||||
|     } | ||||
| 
 | ||||
|     fn get_status(&self) -> InterpretStatus { | ||||
|         todo!() | ||||
|     } | ||||
| 
 | ||||
|     fn get_children(&self) -> Vec<Id> { | ||||
|         todo!() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl UpdateDefaultOkdIngressInterpret { | ||||
|     async fn ensure_ingress_operator( | ||||
|         &self, | ||||
|         client: &Arc<K8sClient>, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|         let operator_name = "ingress-operator"; | ||||
|         let operator_namespace = "openshift-ingress-operator"; | ||||
|         client | ||||
|             .ensure_deployment(operator_name, Some(operator_namespace)) | ||||
|             .await? | ||||
|     } | ||||
| 
 | ||||
|     async fn create_ca_cm( | ||||
|         &self, | ||||
|         client: &Arc<K8sClient>, | ||||
|         fqdn: &FQDN, | ||||
|         ca_name: &str, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|         let cm = format!( | ||||
|             r"#
 | ||||
| apiVersion: v1 | ||||
| kind: ConfigMap | ||||
| metadata: | ||||
|   name: custom-ca | ||||
|   namespace: openshift-config | ||||
| data: | ||||
|   ca-bundle.crt: {} | ||||
|         #", fqdn
 | ||||
|         ); | ||||
|         client.apply_yaml(serde_yaml::to_value(&cm), None).await?; | ||||
|         Ok(Outcome::success(format!( | ||||
|             "successfully created cm : {} in default namespace", | ||||
|             ca_name | ||||
|         ))) | ||||
|     } | ||||
| 
 | ||||
|     async fn patch_proxy( | ||||
|         &self, | ||||
|         client: &Arc<K8sClient>, | ||||
|         ca_name: &str, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|     } | ||||
| 
 | ||||
|     async fn create_tls_secret( | ||||
|         &self, | ||||
|         client: &Arc<K8sClient>, | ||||
|         secret_name: &str, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|     } | ||||
| 
 | ||||
|     async fn patch_ingress( | ||||
|         &self, | ||||
|         client: &Arc<K8sClient>, | ||||
|         secret_name: &str, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,222 @@ | ||||
| use std::{ | ||||
|     fs::File, | ||||
|     io::Read, | ||||
|     path::{Path, PathBuf}, | ||||
|     sync::Arc, | ||||
| }; | ||||
| 
 | ||||
| use base64::{Engine, prelude::BASE64_STANDARD}; | ||||
| use fqdn::Path; | ||||
| use harmony_types::id::Id; | ||||
| use kube::api::GroupVersionKind; | ||||
| use serde_json::json; | ||||
| 
 | ||||
| use crate::{ | ||||
|     data::Version, | ||||
|     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, | ||||
|     inventory::Inventory, | ||||
|     score::Score, | ||||
|     topology::{K8sclient, Topology, k8s::K8sClient}, | ||||
| }; | ||||
| 
 | ||||
| pub struct UpdateDefaultOkdIngressScore { | ||||
|     operator_name: String, | ||||
|     operator_namespace: String, | ||||
|     ca_name: String, | ||||
|     path_to_tls_crt: Path, | ||||
|     path_to_tls_key: Path, | ||||
|     path_to_ca_cert: Path, | ||||
| } | ||||
| 
 | ||||
| impl<T: Topology> Score<T> for UpdateDefaultOkdIngressScore { | ||||
|     fn name(&self) -> String { | ||||
|         "UpdateDefaultOkdIngressScore".to_string() | ||||
|     } | ||||
| 
 | ||||
|     #[doc(hidden)] | ||||
|     fn create_interpret(&self) -> Box<dyn Interpret<T>> { | ||||
|         Box::new(UpdateDefaultOkdIngressInterpret { | ||||
|             score: self.clone(), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct UpdateDefaultOkdIngressInterpret { | ||||
|     score: UpdateDefaultOkdIngressScore, | ||||
| } | ||||
| 
 | ||||
| #[async_trait] | ||||
| impl<T: Topology + K8sclient> Interpret<T> for UpdateDefaultOkdIngressInterpret { | ||||
|     async fn execute( | ||||
|         &self, | ||||
|         inventory: &Inventory, | ||||
|         topology: &T, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|         let client = topology.k8s_client().await?; | ||||
|         let secret_name = "ingress_ca_secret"; | ||||
|         self.ensure_ingress_operator( | ||||
|             &client, | ||||
|             &self.score.operator_name, | ||||
|             &self.score.operator_namespace, | ||||
|         ) | ||||
|         .await?; | ||||
|         self.create_ca_cm(&client, self.score.path_to_ca_cert, &self.score.ca_name) | ||||
|             .await?; | ||||
|         self.patch_proxy(&client, &self.score.ca_name).await?; | ||||
|         self.create_tls_secret( | ||||
|             &client, | ||||
|             self.score.path_to_tls_crt, | ||||
|             self.score.path_to_tls_key, | ||||
|             &self.score.operator_namespace, | ||||
|             &secret_name, | ||||
|         ) | ||||
|         .await?; | ||||
|         self.patch_ingress(&client, &self.score.operator_namespace, &secret_name) | ||||
|             .await?; | ||||
|     } | ||||
| 
 | ||||
|     fn get_name(&self) -> InterpretName { | ||||
|         InterpretName::Custom("UpdateDefaultOkdIngress") | ||||
|     } | ||||
| 
 | ||||
|     fn get_version(&self) -> Version { | ||||
|         todo!() | ||||
|     } | ||||
| 
 | ||||
|     fn get_status(&self) -> InterpretStatus { | ||||
|         todo!() | ||||
|     } | ||||
| 
 | ||||
|     fn get_children(&self) -> Vec<Id> { | ||||
|         todo!() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl UpdateDefaultOkdIngressInterpret { | ||||
|     async fn ensure_ingress_operator( | ||||
|         &self, | ||||
|         client: &Arc<K8sClient>, | ||||
|         operator_name: &str, | ||||
|         operator_namespace: &str, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|         client | ||||
|             .ensure_deployment(operator_name, Some(operator_namespace)) | ||||
|             .await? | ||||
|     } | ||||
| 
 | ||||
|     fn open_path(&self, path: Path) -> Result<String, InterpretError> { | ||||
|         let mut file = match File::open(&path) { | ||||
|             Ok(file) => file, | ||||
|             Err(e) => InterpretError::new(format!("Could not open file {}", e)), | ||||
|         }; | ||||
|         let s = String::new(); | ||||
|         match file.read_to_string(&mut s) { | ||||
|             Ok(s) => Ok(s), | ||||
|             Err(e) => InterpretError::new(format!("Could not read file {}", e)), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async fn create_ca_cm( | ||||
|         &self, | ||||
|         client: &Arc<K8sClient>, | ||||
|         path_to_ca_cert: Path, | ||||
|         ca_name: &str, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|         let ca_bundle = BASE64_STANDARD.encode(self.open_path(path_to_ca_cert).unwrap().as_bytes()); | ||||
| 
 | ||||
|         let cm = format!( | ||||
|             r"#
 | ||||
| apiVersion: v1 | ||||
| kind: ConfigMap | ||||
| metadata: | ||||
|   name: custom-ca | ||||
| data: | ||||
|   ca-bundle.crt: {ca_bundle} | ||||
|         #" | ||||
|         ); | ||||
|         client.apply_yaml(serde_yaml::to_value(&cm), None).await?; | ||||
|         Ok(Outcome::success(format!( | ||||
|             "successfully created cm : {} in default namespace", | ||||
|             ca_name | ||||
|         ))) | ||||
|     } | ||||
| 
 | ||||
|     async fn patch_proxy( | ||||
|         &self, | ||||
|         client: &Arc<K8sClient>, | ||||
|         ca_name: &str, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|         let gvk = GroupVersionKind { | ||||
|             group: "config.openshift.io".to_string(), | ||||
|             version: "v1".to_string(), | ||||
|             kind: "Proxy".to_string(), | ||||
|         }; | ||||
|         let patch = json!({ | ||||
|             "spec": { | ||||
|                 "trustedCA": { | ||||
|                     "name": ca_name | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         client | ||||
|             .patch_resource_by_merge("cluster", None, &gvk, patch) | ||||
|             .await?; | ||||
|         Ok(Outcome::success(format!( | ||||
|             "successfully merged trusted ca to cluster proxy" | ||||
|         ))) | ||||
|     } | ||||
| 
 | ||||
|     async fn create_tls_secret( | ||||
|         &self, | ||||
|         client: &Arc<K8sClient>, | ||||
|         tls_crt: Path, | ||||
|         tls_key: Path, | ||||
|         operator_namespace: &str, | ||||
|         secret_name: &str, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|         let base64_tls_crt = BASE64_STANDARD.encode(self.open_path(tls_crt).unwrap().as_bytes()); | ||||
|         let base64_tls_key = BASE64_STANDARD.encode(self.open_path(tls_key).unwrap().as_bytes()); | ||||
|         let secret = format!( | ||||
|             r#" | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: secret-tls | ||||
|   namespace: {operator_namespace} | ||||
| type: kubernetes.io/tls | ||||
| data: | ||||
|   # values are base64 encoded, which obscures them but does NOT provide | ||||
|   # any useful level of confidentiality | ||||
|   # Replace the following values with your own base64-encoded certificate and key. | ||||
|   tls.crt: "{base64_tls_crt}" 
 | ||||
|   tls.key: "{base64_tls_key}" | ||||
|             "#
 | ||||
|         ); | ||||
|         client | ||||
|             .apply_yaml(serde_yaml::to_value(secret), Some(operator_namespace)) | ||||
|             .await?; | ||||
|         Ok(Outcome::success(format!( | ||||
|             "successfully created tls secret trusted ca to cluster proxy" | ||||
|         ))) | ||||
|     } | ||||
| 
 | ||||
|     async fn patch_ingress( | ||||
|         &self, | ||||
|         client: &Arc<K8sClient>, | ||||
|         operator_namespace: &str, | ||||
|         secret_name: &str, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|         let gvk = GroupVersionKind { | ||||
|             group: "operator.openshift.io".to_string(), | ||||
|             version: "v1".to_string(), | ||||
|             kind: "IngressController".to_string(), | ||||
|         }; | ||||
|         let patch = json!( | ||||
|             {"spec":{"defaultCertificate": {"name": secret_name}}}); | ||||
|         client | ||||
|             .patch_resource_by_merge("default", Some(operator_namespace), &gvk, patch) | ||||
|             .await?; | ||||
| 
 | ||||
|         Ok(Outcome::success("successfully pathed ingress operator to")) | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user