From e11d09ee7a373917ea256f806b601b372a13cea7 Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Fri, 9 Aug 2024 16:04:00 -0400 Subject: [PATCH 01/16] manage gateway status label and disassociate EIP on node ready status Unknown --- eip_operator/src/controller/node.rs | 231 ++++++++++++++++++++++++---- eip_operator/src/main.rs | 3 + eip_operator_shared/src/lib.rs | 4 + 3 files changed, 205 insertions(+), 33 deletions(-) diff --git a/eip_operator/src/controller/node.rs b/eip_operator/src/controller/node.rs index 3de51cd..fa6486e 100644 --- a/eip_operator/src/controller/node.rs +++ b/eip_operator/src/controller/node.rs @@ -1,7 +1,8 @@ +use std::collections::{BTreeMap, BTreeSet}; use std::time::Duration; use k8s_openapi::api::core::v1::Node; -use kube::api::{Api, ListParams}; +use kube::api::{Api, ListParams, Patch, PatchParams}; use kube::error::ErrorResponse; use kube::{Client, ResourceExt}; use kube_runtime::controller::Action; @@ -11,6 +12,8 @@ use eip_operator_shared::Error; use crate::eip::v2::Eip; use crate::kube_ext::NodeExt; +use crate::EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY; +use crate::EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE; pub(crate) struct Context { ec2_client: aws_sdk_ec2::Client, @@ -47,8 +50,18 @@ impl k8s_controller::Context for Context { self.namespace.as_deref().unwrap_or("default"), ); + // Check for unresponsive egress nodes and cleanup their egress status. + // This check must be done before any checks for EIPS. The EIPS may be + // already unassociated from the node. + let node_api = Api::::all(client.clone()); + cleanup_old_egress_nodes(&eip_api, &node_api).await?; + let node_ip = node.ip().ok_or(Error::MissingNodeIp)?; let node_labels = node.labels(); + let node_conditon_ready_status = get_ready_status_from_node(node) + .await + .ok_or(Error::MissingNodeReadyCondition)?; + let provider_id = node.provider_id().ok_or(Error::MissingProviderId)?; let instance_id = provider_id .rsplit_once('/') @@ -83,41 +96,57 @@ impl k8s_controller::Context for Context { .swap_remove(0); let instance_description = crate::aws::describe_instance(&self.ec2_client, instance_id).await?; - let eni_id = crate::aws::get_eni_from_private_ip(&instance_description, node_ip) .ok_or(Error::NoInterfaceWithThatIp)?; - if eip_description.network_interface_id != Some(eni_id.to_owned()) - || eip_description.private_ip_address != Some(node_ip.to_owned()) - { - match crate::eip::set_status_should_attach(&eip_api, &eip, &eni_id, node_ip, &name) - .await - { - Ok(_) => { - info!("Found matching Eip, attaching it"); - let association_id = crate::aws::associate_eip( - &self.ec2_client, - allocation_id, - &eni_id, - node_ip, - ) - .await? - .association_id - .ok_or(Error::MissingAssociationId)?; - crate::eip::set_status_association_id(&eip_api, &eip_name, &association_id) - .await?; - } - Err(Error::Kube { - source: kube::Error::Api(ErrorResponse { reason, .. }), - }) if reason == "Conflict" => { - warn!( - "Node {} failed to claim eip {}, rescheduling to try another", - name, eip_name - ); - return Ok(Some(Action::requeue(Duration::from_secs(3)))); - } - Err(e) => return Err(e), - }; + + let network_interface_mismatch = + eip_description.network_interface_id != Some(eni_id.to_owned()); + let private_ip_mismatch = eip_description.private_ip_address != Some(node_ip.to_owned()); + let attach_result = match ( + network_interface_mismatch, + private_ip_mismatch, + node_conditon_ready_status.as_str(), + ) { + (true, false, "True") | (false, true, "True") | (true, true, "True") => { + crate::eip::set_status_should_attach(&eip_api, &eip, &eni_id, node_ip, &name).await + } + // Node is in a ready state of `unknown`. Disassociate the EIP. + (false, false, "Unknown") => { + // This code path runs a limited number of times once a node goes unresponsive. + // The EIP will be disassociated and then this apply() will exit early since no EIP is associated + warn!("Node {} is in Unknown state, disassociating EIP", &name); + crate::aws::disassociate_eip(&self.ec2_client, &eip_name).await?; + crate::eip::set_status_detached(&eip_api, &eip_name).await?; + + return Ok(None); + } + _ => return Ok(None), + }; + + match attach_result { + Ok(_) => { + info!("Found matching Eip, attaching it"); + let association_id = + crate::aws::associate_eip(&self.ec2_client, allocation_id, &eni_id, node_ip) + .await? + .association_id + .ok_or(Error::MissingAssociationId)?; + crate::eip::set_status_association_id(&eip_api, &eip_name, &association_id).await?; + + add_gateway_status_label(&Api::all(client.clone()), &name, "ready").await?; + } + Err(Error::Kube { + source: kube::Error::Api(ErrorResponse { reason, .. }), + }) if reason == "Conflict" => { + warn!( + "Node {} failed to claim eip {}, rescheduling to try another", + name, eip_name + ); + return Ok(Some(Action::requeue(Duration::from_secs(3)))); + } + Err(e) => return Err(e), } + Ok(None) } @@ -157,3 +186,139 @@ impl k8s_controller::Context for Context { Ok(None) } } + +/// Applies label to node specifying the status of the egress gateway node. +#[instrument(skip(api), err)] +async fn add_gateway_status_label( + api: &Api, + name: &str, + status: &str, +) -> Result { + info!( + "Adding gateway status label {} key {} to node {}", + crate::EGRESS_NODE_STATUS_LABEL, + status, + name + ); + let patch = serde_json::json!({ + "apiVersion": "v1", + "kind": "Node", + "metadata": { + "labels": { + // Ensure the status is lowercase to match conventions + crate::EGRESS_NODE_STATUS_LABEL: status.to_lowercase(), + } + } + }); + let patch = Patch::Apply(&patch); + let params = PatchParams::apply(crate::FIELD_MANAGER); + api.patch(name, ¶ms, &patch).await +} + +/// Retrieve all egress nodes in the cluster. +async fn get_egress_nodes(api: &Api) -> Result, kube::Error> { + let params = ListParams::default().labels( + format!( + "{}={}", + EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY, EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE + ) + .as_str(), + ); + + match api.list(¶ms).await { + Ok(node_list) => Ok(node_list.items), + Err(e) => Err(e), + } +} + +/// Get Ready status from the node status field. +async fn get_ready_status_from_node(node: &Node) -> Option { + node.status + .as_ref()? + .conditions + .as_ref()? + .iter() + .find(|c| c.type_ == "Ready") + .map(|condition| condition.status.clone()) +} + +/// Retrieve node names and ready status given a list of nodes. +async fn get_nodes_ready_status(node_list: Vec) -> Result, Error> { + let mut node_ready_status_map = BTreeMap::new(); + + for node in node_list { + if let Some(ref node_name) = node.metadata.name { + let ready_status = get_ready_status_from_node(&node) + .await + .ok_or(Error::MissingNodeReadyCondition)?; + + node_ready_status_map.insert(node_name.to_string(), ready_status); + } + } + + Ok(node_ready_status_map) +} + +/// Find old nodes in a ready state of `Unknown`. +/// Update their status label if another node is available. +/// Note: Egress traffic will be immediately dropped once the egress status label is changed away from "true" +#[instrument(skip(), err)] +async fn cleanup_old_egress_nodes(eip_api: &Api, node_api: &Api) -> Result<(), Error> { + // Gather a list of egress nodes and EIPs to check for potential cleanup. + let node_list = get_egress_nodes(&Api::all(node_api.clone().into())).await?; + match node_list.len() { + // Exit early and avoid extra network calls. + len if len < 2 => { + info!("Less than two egress nodes found, no cleanup is possible."); + return Ok(()); + } + // At least two nodes found, continue with cleanup. + _ => { + let node_names_and_status: BTreeMap = + get_nodes_ready_status(node_list).await?; + let (nodes_status_ready, nodes_status_unknown): (BTreeSet, BTreeSet) = + node_names_and_status.iter().fold( + (BTreeSet::new(), BTreeSet::new()), + |(mut ready, mut unknown), (name, status)| { + match status.as_str() { + "True" => { + ready.insert(name.clone()); + } + "Unknown" => { + unknown.insert(name.clone()); + } + // Ignore nodes in other states. + &_ => {} + } + (ready, unknown) + }, + ); + + // Ensure an egress node exists with an EIP and Ready state of `true`. + let eip_resource_ids: BTreeSet = eip_api + .list(&ListParams::default()) + .await? + .items + .into_iter() + .filter_map(|eip| eip.status.and_then(|s| s.resource_id)) + .collect(); + let matched_ready_nodes_with_eip: BTreeSet = nodes_status_ready + .intersection(&eip_resource_ids) + .cloned() + .collect(); + info!( + "Found ready nodes with EIPs: {:?}", + matched_ready_nodes_with_eip + ); + + // At least one node is ready with an EIP, update the status label of nodes in an unknown state. + if !matched_ready_nodes_with_eip.is_empty() { + for node_name in nodes_status_unknown { + add_gateway_status_label(node_api, &node_name, "unknown").await?; + } + } + } + } + + Ok(()) +} diff --git a/eip_operator/src/main.rs b/eip_operator/src/main.rs index 9b42844..9c9aadc 100644 --- a/eip_operator/src/main.rs +++ b/eip_operator/src/main.rs @@ -30,6 +30,9 @@ const FIELD_MANAGER: &str = "eip.materialize.cloud"; const AUTOCREATE_EIP_LABEL: &str = "eip.materialize.cloud/autocreate_eip"; const EIP_ALLOCATION_ID_ANNOTATION: &str = "eip.materialize.cloud/allocation_id"; const EXTERNAL_DNS_TARGET_ANNOTATION: &str = "external-dns.alpha.kubernetes.io/target"; +const EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY: &str = "workload"; +const EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE: &str = "materialize-egress"; +const EGRESS_NODE_STATUS_LABEL: &str = "egress-gateway.materialize.cloud/status"; // See https://us-east-1.console.aws.amazon.com/servicequotas/home/services/ec2/quotas // and filter in the UI for EC2 quotas like this, or use the CLI: diff --git a/eip_operator_shared/src/lib.rs b/eip_operator_shared/src/lib.rs index 90eafe5..3ac0daf 100644 --- a/eip_operator_shared/src/lib.rs +++ b/eip_operator_shared/src/lib.rs @@ -66,6 +66,10 @@ pub enum Error { MissingNodeName, #[error("Node does not have labels.")] MissingNodeLabels, + #[error("Node does not have a status.")] + MissingNodeStatus, + #[error("Node does not have a ready condition.")] + MissingNodeReadyCondition, #[error("Node does not have a provider_id in its spec.")] MissingProviderId, #[error("Node provider_id is not in expected format.")] From be070fb7a02b7d996d1267b77b293033ef4a5af5 Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Tue, 27 Aug 2024 19:11:22 -0400 Subject: [PATCH 02/16] update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f33ee5f..50bd6b9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +⚠️⚠️ **WARNING!** ⚠️⚠️ The Materialize K8s-eip-operator will soon be archived and no longer be under active development. # k8s-eip-operator Manage external connections to Kubernetes pods or nodes using AWS Elastic IPs (EIPs). From 901df8a32d9da53989f221f459fe84c49fe2485e Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Wed, 28 Aug 2024 19:44:47 -0400 Subject: [PATCH 03/16] update openssl and fix Dockerfile rust version --- Cargo.lock | 8 ++++---- Dockerfile | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ecf6d4e..d4a0a7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1625,9 +1625,9 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.61" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ "bitflags 2.4.0", "cfg-if", @@ -1666,9 +1666,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.97" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", diff --git a/Dockerfile b/Dockerfile index d7d48ba..d5526f2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM rust:1.74.0-slim-bookworm AS chef +FROM --platform=$BUILDPLATFORM rust:1.76.0-slim-bookworm AS chef RUN cargo install --locked cargo-chef ARG TARGETARCH RUN echo -n "$TARGETARCH" | sed 's#amd64#x86_64#;s#arm64#aarch64#' > /cargo_arch From 1b1d20403323796aaad28a08d04affd3bcf0cada Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Thu, 29 Aug 2024 15:51:48 -0400 Subject: [PATCH 04/16] clean up typos, remove unnecessary async, and simplify egress node cleanup --- eip_operator/src/controller/node.rs | 108 +++++++++++++--------------- 1 file changed, 50 insertions(+), 58 deletions(-) diff --git a/eip_operator/src/controller/node.rs b/eip_operator/src/controller/node.rs index fa6486e..9b3006e 100644 --- a/eip_operator/src/controller/node.rs +++ b/eip_operator/src/controller/node.rs @@ -58,9 +58,8 @@ impl k8s_controller::Context for Context { let node_ip = node.ip().ok_or(Error::MissingNodeIp)?; let node_labels = node.labels(); - let node_conditon_ready_status = get_ready_status_from_node(node) - .await - .ok_or(Error::MissingNodeReadyCondition)?; + let node_condition_ready_status = + get_ready_status_from_node(node).ok_or(Error::MissingNodeReadyCondition)?; let provider_id = node.provider_id().ok_or(Error::MissingProviderId)?; let instance_id = provider_id @@ -105,7 +104,7 @@ impl k8s_controller::Context for Context { let attach_result = match ( network_interface_mismatch, private_ip_mismatch, - node_conditon_ready_status.as_str(), + node_condition_ready_status.as_str(), ) { (true, false, "True") | (false, true, "True") | (true, true, "True") => { crate::eip::set_status_should_attach(&eip_api, &eip, &eni_id, node_ip, &name).await @@ -232,7 +231,7 @@ async fn get_egress_nodes(api: &Api) -> Result, kube::Error> { } /// Get Ready status from the node status field. -async fn get_ready_status_from_node(node: &Node) -> Option { +fn get_ready_status_from_node(node: &Node) -> Option { node.status .as_ref()? .conditions @@ -243,14 +242,13 @@ async fn get_ready_status_from_node(node: &Node) -> Option { } /// Retrieve node names and ready status given a list of nodes. -async fn get_nodes_ready_status(node_list: Vec) -> Result, Error> { +fn get_nodes_ready_status(node_list: Vec) -> Result, Error> { let mut node_ready_status_map = BTreeMap::new(); for node in node_list { if let Some(ref node_name) = node.metadata.name { - let ready_status = get_ready_status_from_node(&node) - .await - .ok_or(Error::MissingNodeReadyCondition)?; + let ready_status = + get_ready_status_from_node(&node).ok_or(Error::MissingNodeReadyCondition)?; node_ready_status_map.insert(node_name.to_string(), ready_status); } @@ -266,57 +264,51 @@ async fn get_nodes_ready_status(node_list: Vec) -> Result, node_api: &Api) -> Result<(), Error> { // Gather a list of egress nodes and EIPs to check for potential cleanup. let node_list = get_egress_nodes(&Api::all(node_api.clone().into())).await?; - match node_list.len() { - // Exit early and avoid extra network calls. - len if len < 2 => { - info!("Less than two egress nodes found, no cleanup is possible."); - return Ok(()); - } - // At least two nodes found, continue with cleanup. - _ => { - let node_names_and_status: BTreeMap = - get_nodes_ready_status(node_list).await?; - let (nodes_status_ready, nodes_status_unknown): (BTreeSet, BTreeSet) = - node_names_and_status.iter().fold( - (BTreeSet::new(), BTreeSet::new()), - |(mut ready, mut unknown), (name, status)| { - match status.as_str() { - "True" => { - ready.insert(name.clone()); - } - "Unknown" => { - unknown.insert(name.clone()); - } - // Ignore nodes in other states. - &_ => {} - } - (ready, unknown) - }, - ); - - // Ensure an egress node exists with an EIP and Ready state of `true`. - let eip_resource_ids: BTreeSet = eip_api - .list(&ListParams::default()) - .await? - .items - .into_iter() - .filter_map(|eip| eip.status.and_then(|s| s.resource_id)) - .collect(); - let matched_ready_nodes_with_eip: BTreeSet = nodes_status_ready - .intersection(&eip_resource_ids) - .cloned() - .collect(); - info!( - "Found ready nodes with EIPs: {:?}", - matched_ready_nodes_with_eip - ); + if node_list.len() < 2 { + info!("Less than two egress nodes found, no cleanup is possible."); + return Ok(()); + } - // At least one node is ready with an EIP, update the status label of nodes in an unknown state. - if !matched_ready_nodes_with_eip.is_empty() { - for node_name in nodes_status_unknown { - add_gateway_status_label(node_api, &node_name, "unknown").await?; + let node_names_and_status: BTreeMap = get_nodes_ready_status(node_list)?; + let (nodes_status_ready, nodes_status_unknown): (BTreeSet, BTreeSet) = + node_names_and_status.iter().fold( + (BTreeSet::new(), BTreeSet::new()), + |(mut ready, mut unknown), (name, status)| { + match status.as_str() { + "True" => { + ready.insert(name.clone()); + } + "Unknown" => { + unknown.insert(name.clone()); + } + // Ignore nodes in other states. + &_ => {} } - } + (ready, unknown) + }, + ); + + // Ensure an egress node exists with an EIP and Ready state of `true`. + let eip_resource_ids: BTreeSet = eip_api + .list(&ListParams::default()) + .await? + .items + .into_iter() + .filter_map(|eip| eip.status.and_then(|s| s.resource_id)) + .collect(); + let matched_ready_nodes_with_eip: BTreeSet = nodes_status_ready + .intersection(&eip_resource_ids) + .cloned() + .collect(); + info!( + "Found ready nodes with EIPs: {:?}", + matched_ready_nodes_with_eip + ); + + // At least one node is ready with an EIP, update the status label of nodes in an unknown state. + if !matched_ready_nodes_with_eip.is_empty() { + for node_name in nodes_status_unknown { + add_gateway_status_label(node_api, &node_name, "unknown").await?; } } From c0dd1035983713762379599343e5f41173426e38 Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Thu, 29 Aug 2024 17:23:45 -0400 Subject: [PATCH 05/16] add egress control loop for setting egress status label on unresponsive nodes --- eip_operator/src/controller/node.rs | 11 ++++------ eip_operator/src/main.rs | 33 ++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/eip_operator/src/controller/node.rs b/eip_operator/src/controller/node.rs index 9b3006e..c763a96 100644 --- a/eip_operator/src/controller/node.rs +++ b/eip_operator/src/controller/node.rs @@ -50,12 +50,6 @@ impl k8s_controller::Context for Context { self.namespace.as_deref().unwrap_or("default"), ); - // Check for unresponsive egress nodes and cleanup their egress status. - // This check must be done before any checks for EIPS. The EIPS may be - // already unassociated from the node. - let node_api = Api::::all(client.clone()); - cleanup_old_egress_nodes(&eip_api, &node_api).await?; - let node_ip = node.ip().ok_or(Error::MissingNodeIp)?; let node_labels = node.labels(); let node_condition_ready_status = @@ -261,7 +255,10 @@ fn get_nodes_ready_status(node_list: Vec) -> Result, node_api: &Api) -> Result<(), Error> { +pub(crate) async fn cleanup_old_egress_nodes( + eip_api: &Api, + node_api: &Api, +) -> Result<(), Error> { // Gather a list of egress nodes and EIPs to check for potential cleanup. let node_list = get_egress_nodes(&Api::all(node_api.clone().into())).await?; if node_list.len() < 2 { diff --git a/eip_operator/src/main.rs b/eip_operator/src/main.rs index 9c9aadc..827b273 100644 --- a/eip_operator/src/main.rs +++ b/eip_operator/src/main.rs @@ -1,4 +1,5 @@ use std::collections::{HashMap, HashSet}; +use std::pin::pin; use std::time::Duration; use aws_sdk_ec2::types::Filter; @@ -6,9 +7,10 @@ use aws_sdk_ec2::Client as Ec2Client; use aws_sdk_servicequotas::types::ServiceQuota; use aws_sdk_servicequotas::Client as ServiceQuotaClient; use futures::future::join_all; +use futures::TryStreamExt; use json_patch::{PatchOperation, RemoveOperation, TestOperation}; use k8s_controller::Controller; -use k8s_openapi::api::core::v1::Pod; +use k8s_openapi::api::core::v1::{Node, Pod}; use kube::api::{Api, ListParams, Patch, PatchParams}; use kube::{Client, ResourceExt}; use tokio::task; @@ -92,6 +94,9 @@ async fn run() -> Result<(), Error> { None => Api::::all(k8s_client.clone()), }; + debug!("Getting node api"); + let node_api = Api::::all(k8s_client.clone()); + debug!("Cleaning up any orphaned EIPs"); cleanup_orphan_eips( &ec2_client, @@ -152,6 +157,32 @@ async fn run() -> Result<(), Error> { task::spawn(eip_controller.run()) }); + tasks.push({ + let eip_api = eip_api.clone(); + let node_api = node_api.clone(); + let watch_config = + kube_runtime::watcher::Config::default().labels(EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY); + + task::spawn(async move { + let mut watcher = pin!(kube_runtime::watcher(node_api.clone(), watch_config)); + + while let Some(node) = watcher.try_next().await.unwrap_or_else(|e| { + event!(Level::ERROR, err = %e, "Error watching nodes"); + None + }) { + if let kube_runtime::watcher::Event::Applied(_) + | kube_runtime::watcher::Event::Deleted(_) = node + { + if let Err(err) = + controller::node::cleanup_old_egress_nodes(&eip_api, &node_api).await + { + event!(Level::ERROR, err = %err, "Node egress cleanup reporting error"); + } + } + } + }) + }); + join_all(tasks).await; debug!("exiting"); From e536898f810a41a71204987951d4d5b14f4ec524 Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Fri, 30 Aug 2024 12:29:04 -0400 Subject: [PATCH 06/16] move egress label setting of new nodes out of controller --- eip_operator/src/controller/node.rs | 260 ++++++++-------------------- eip_operator/src/egress.rs | 123 +++++++++++++ eip_operator/src/main.rs | 8 +- eip_operator/src/node.rs | 33 ++++ 4 files changed, 233 insertions(+), 191 deletions(-) create mode 100644 eip_operator/src/egress.rs create mode 100644 eip_operator/src/node.rs diff --git a/eip_operator/src/controller/node.rs b/eip_operator/src/controller/node.rs index c763a96..329913b 100644 --- a/eip_operator/src/controller/node.rs +++ b/eip_operator/src/controller/node.rs @@ -1,8 +1,7 @@ -use std::collections::{BTreeMap, BTreeSet}; use std::time::Duration; use k8s_openapi::api::core::v1::Node; -use kube::api::{Api, ListParams, Patch, PatchParams}; +use kube::api::{Api, ListParams}; use kube::error::ErrorResponse; use kube::{Client, ResourceExt}; use kube_runtime::controller::Action; @@ -12,8 +11,6 @@ use eip_operator_shared::Error; use crate::eip::v2::Eip; use crate::kube_ext::NodeExt; -use crate::EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY; -use crate::EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE; pub(crate) struct Context { ec2_client: aws_sdk_ec2::Client, @@ -52,14 +49,12 @@ impl k8s_controller::Context for Context { let node_ip = node.ip().ok_or(Error::MissingNodeIp)?; let node_labels = node.labels(); - let node_condition_ready_status = - get_ready_status_from_node(node).ok_or(Error::MissingNodeReadyCondition)?; - let provider_id = node.provider_id().ok_or(Error::MissingProviderId)?; let instance_id = provider_id .rsplit_once('/') .ok_or(Error::MalformedProviderId)? .1; + let matched_eips: Vec = eip_api .list(&ListParams::default()) .await? @@ -70,6 +65,41 @@ impl k8s_controller::Context for Context { if matched_eips.is_empty() { return Err(Error::NoEipResourceWithThatNodeSelector); } + + // Check the existing Node for EIP association and ready status. + // Node's that go in to a "NotReady" or "Unknown" state should have their EIP + // disassociated to allow a new node to spawn and use the EIP. + let node_condition_ready_status = crate::node::get_ready_status_from_node(node) + .ok_or(Error::MissingNodeReadyCondition)?; + match node_condition_ready_status.as_str() { + // Remove the EIP from nodes with an Unknown or NotReady ready status. + // An Unknown ready status could mean the node is unresponsive or experienced a hardware failure. + // A NotReady ready status could mean the node is experiencing a network issue. + "Unknown" | "False" => { + // Skip disassociation if no EIP is not associated with the node. + let node_eip = matched_eips.iter().find(|eip| { + eip.status.as_ref().is_some_and(|s| { + s.resource_id.is_some() + && s.resource_id.as_ref().map(|r| r == &name).unwrap_or(false) + }) + }); + if let Some(eip) = node_eip { + let node_eip_name = eip.name_unchecked(); + warn!( + "Node {} is in an unknown state, disassociating EIP {}", + &name.clone(), + &node_eip_name + ); + crate::aws::disassociate_eip(&self.ec2_client, &node_eip_name).await?; + crate::eip::set_status_detached(&eip_api, &node_eip_name).await?; + + return Ok(None); + } + } + // Skip Ready Status True and continue with EIP node association. + _ => {} + } + let eip = matched_eips.into_iter().find(|eip| { eip.status.as_ref().is_some_and(|s| { s.resource_id.is_none() @@ -89,57 +119,45 @@ impl k8s_controller::Context for Context { .swap_remove(0); let instance_description = crate::aws::describe_instance(&self.ec2_client, instance_id).await?; + let eni_id = crate::aws::get_eni_from_private_ip(&instance_description, node_ip) .ok_or(Error::NoInterfaceWithThatIp)?; - - let network_interface_mismatch = - eip_description.network_interface_id != Some(eni_id.to_owned()); - let private_ip_mismatch = eip_description.private_ip_address != Some(node_ip.to_owned()); - let attach_result = match ( - network_interface_mismatch, - private_ip_mismatch, - node_condition_ready_status.as_str(), - ) { - (true, false, "True") | (false, true, "True") | (true, true, "True") => { - crate::eip::set_status_should_attach(&eip_api, &eip, &eni_id, node_ip, &name).await - } - // Node is in a ready state of `unknown`. Disassociate the EIP. - (false, false, "Unknown") => { - // This code path runs a limited number of times once a node goes unresponsive. - // The EIP will be disassociated and then this apply() will exit early since no EIP is associated - warn!("Node {} is in Unknown state, disassociating EIP", &name); - crate::aws::disassociate_eip(&self.ec2_client, &eip_name).await?; - crate::eip::set_status_detached(&eip_api, &eip_name).await?; - - return Ok(None); - } - _ => return Ok(None), - }; - - match attach_result { - Ok(_) => { - info!("Found matching Eip, attaching it"); - let association_id = - crate::aws::associate_eip(&self.ec2_client, allocation_id, &eni_id, node_ip) - .await? - .association_id - .ok_or(Error::MissingAssociationId)?; - crate::eip::set_status_association_id(&eip_api, &eip_name, &association_id).await?; - - add_gateway_status_label(&Api::all(client.clone()), &name, "ready").await?; - } - Err(Error::Kube { - source: kube::Error::Api(ErrorResponse { reason, .. }), - }) if reason == "Conflict" => { - warn!( - "Node {} failed to claim eip {}, rescheduling to try another", - name, eip_name - ); - return Ok(Some(Action::requeue(Duration::from_secs(3)))); - } - Err(e) => return Err(e), + // Ensure only node's marked with ready status True are associated with an EIP. + // We don't want to associate an EIP with a node that is not ready and potentially remove it + // in the next reconciliation loop if the node is still coming online. + if (eip_description.network_interface_id != Some(eni_id.to_owned()) + || eip_description.private_ip_address != Some(node_ip.to_owned())) + && node_condition_ready_status == "True" + { + match crate::eip::set_status_should_attach(&eip_api, &eip, &eni_id, node_ip, &name) + .await + { + Ok(_) => { + info!("Found matching Eip, attaching it"); + let association_id = crate::aws::associate_eip( + &self.ec2_client, + allocation_id, + &eni_id, + node_ip, + ) + .await? + .association_id + .ok_or(Error::MissingAssociationId)?; + crate::eip::set_status_association_id(&eip_api, &eip_name, &association_id) + .await?; + } + Err(Error::Kube { + source: kube::Error::Api(ErrorResponse { reason, .. }), + }) if reason == "Conflict" => { + warn!( + "Node {} failed to claim eip {}, rescheduling to try another", + name, eip_name + ); + return Ok(Some(Action::requeue(Duration::from_secs(3)))); + } + Err(e) => return Err(e), + }; } - Ok(None) } @@ -179,135 +197,3 @@ impl k8s_controller::Context for Context { Ok(None) } } - -/// Applies label to node specifying the status of the egress gateway node. -#[instrument(skip(api), err)] -async fn add_gateway_status_label( - api: &Api, - name: &str, - status: &str, -) -> Result { - info!( - "Adding gateway status label {} key {} to node {}", - crate::EGRESS_NODE_STATUS_LABEL, - status, - name - ); - let patch = serde_json::json!({ - "apiVersion": "v1", - "kind": "Node", - "metadata": { - "labels": { - // Ensure the status is lowercase to match conventions - crate::EGRESS_NODE_STATUS_LABEL: status.to_lowercase(), - } - } - }); - let patch = Patch::Apply(&patch); - let params = PatchParams::apply(crate::FIELD_MANAGER); - api.patch(name, ¶ms, &patch).await -} - -/// Retrieve all egress nodes in the cluster. -async fn get_egress_nodes(api: &Api) -> Result, kube::Error> { - let params = ListParams::default().labels( - format!( - "{}={}", - EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY, EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE - ) - .as_str(), - ); - - match api.list(¶ms).await { - Ok(node_list) => Ok(node_list.items), - Err(e) => Err(e), - } -} - -/// Get Ready status from the node status field. -fn get_ready_status_from_node(node: &Node) -> Option { - node.status - .as_ref()? - .conditions - .as_ref()? - .iter() - .find(|c| c.type_ == "Ready") - .map(|condition| condition.status.clone()) -} - -/// Retrieve node names and ready status given a list of nodes. -fn get_nodes_ready_status(node_list: Vec) -> Result, Error> { - let mut node_ready_status_map = BTreeMap::new(); - - for node in node_list { - if let Some(ref node_name) = node.metadata.name { - let ready_status = - get_ready_status_from_node(&node).ok_or(Error::MissingNodeReadyCondition)?; - - node_ready_status_map.insert(node_name.to_string(), ready_status); - } - } - - Ok(node_ready_status_map) -} - -/// Find old nodes in a ready state of `Unknown`. -/// Update their status label if another node is available. -/// Note: Egress traffic will be immediately dropped once the egress status label is changed away from "true" -#[instrument(skip(), err)] -pub(crate) async fn cleanup_old_egress_nodes( - eip_api: &Api, - node_api: &Api, -) -> Result<(), Error> { - // Gather a list of egress nodes and EIPs to check for potential cleanup. - let node_list = get_egress_nodes(&Api::all(node_api.clone().into())).await?; - if node_list.len() < 2 { - info!("Less than two egress nodes found, no cleanup is possible."); - return Ok(()); - } - - let node_names_and_status: BTreeMap = get_nodes_ready_status(node_list)?; - let (nodes_status_ready, nodes_status_unknown): (BTreeSet, BTreeSet) = - node_names_and_status.iter().fold( - (BTreeSet::new(), BTreeSet::new()), - |(mut ready, mut unknown), (name, status)| { - match status.as_str() { - "True" => { - ready.insert(name.clone()); - } - "Unknown" => { - unknown.insert(name.clone()); - } - // Ignore nodes in other states. - &_ => {} - } - (ready, unknown) - }, - ); - - // Ensure an egress node exists with an EIP and Ready state of `true`. - let eip_resource_ids: BTreeSet = eip_api - .list(&ListParams::default()) - .await? - .items - .into_iter() - .filter_map(|eip| eip.status.and_then(|s| s.resource_id)) - .collect(); - let matched_ready_nodes_with_eip: BTreeSet = nodes_status_ready - .intersection(&eip_resource_ids) - .cloned() - .collect(); - info!( - "Found ready nodes with EIPs: {:?}", - matched_ready_nodes_with_eip - ); - - // At least one node is ready with an EIP, update the status label of nodes in an unknown state. - if !matched_ready_nodes_with_eip.is_empty() { - for node_name in nodes_status_unknown { - add_gateway_status_label(node_api, &node_name, "unknown").await?; - } - } - - Ok(()) -} diff --git a/eip_operator/src/egress.rs b/eip_operator/src/egress.rs new file mode 100644 index 0000000..4ea8653 --- /dev/null +++ b/eip_operator/src/egress.rs @@ -0,0 +1,123 @@ +use crate::eip::v2::Eip; +use crate::Error; +use k8s_openapi::api::core::v1::Node; +use kube::api::{Api, ListParams, Patch, PatchParams}; +use std::collections::{BTreeMap, BTreeSet}; +use tracing::{info, instrument}; + +use crate::EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY; +use crate::EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE; + +/// Applies label to node specifying the status of the egress gateway node. +#[instrument(skip(api), err)] +async fn add_gateway_status_label( + api: &Api, + name: &str, + status: &str, +) -> Result { + info!( + "Adding gateway status label {} value {} to node {}", + crate::EGRESS_NODE_STATUS_LABEL, + status, + name + ); + let patch = serde_json::json!({ + "apiVersion": "v1", + "kind": "Node", + "metadata": { + "labels": { + // Ensure the status is lowercase to match conventions + crate::EGRESS_NODE_STATUS_LABEL: status.to_lowercase(), + } + } + }); + let patch = Patch::Apply(&patch); + let params = PatchParams::apply(crate::FIELD_MANAGER); + api.patch(name, ¶ms, &patch).await +} + +/// Retrieve all egress nodes in the cluster. +async fn get_egress_nodes(api: &Api) -> Result, kube::Error> { + let params = ListParams::default().labels( + format!( + "{}={}", + EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY, EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE + ) + .as_str(), + ); + + match api.list(¶ms).await { + Ok(node_list) => Ok(node_list.items), + Err(e) => Err(e), + } +} + +/// Update state label on egress nodes. +/// Note: Egress traffic will be immediately dropped when the status label is changed away from "true". +#[instrument(skip(), err)] +pub(crate) async fn label_egress_nodes( + eip_api: &Api, + node_api: &Api, +) -> Result<(), Error> { + info!("Updating egress node status labels."); + let node_list = get_egress_nodes(&Api::all(node_api.clone().into())).await?; + if node_list.is_empty() { + info!("No egress nodes found. Skipping egress cleanup."); + return Ok(()); + } + + let node_names_and_status: BTreeMap = + crate::node::get_nodes_ready_status(node_list)?; + let (nodes_status_ready, nodes_status_unknown): (BTreeSet, BTreeSet) = + node_names_and_status.iter().fold( + (BTreeSet::new(), BTreeSet::new()), + |(mut ready, mut unknown), (name, status)| { + match status.as_str() { + "True" => { + ready.insert(name.clone()); + } + "Unknown" => { + unknown.insert(name.clone()); + } + // Ignore nodes in other states. + &_ => { + info!("Ignoring node {} with status {}", name, status); + } + } + (ready, unknown) + }, + ); + + // Ensure an egress node exists with an EIP and Ready state of `true`. + let eip_resource_ids: BTreeSet = eip_api + .list(&ListParams::default()) + .await? + .items + .into_iter() + .filter_map(|eip| eip.status.and_then(|s| s.resource_id)) + .collect(); + let matched_ready_nodes_with_eip: BTreeSet = nodes_status_ready + .intersection(&eip_resource_ids) + .cloned() + .collect(); + + if matched_ready_nodes_with_eip.is_empty() { + info!("No ready egress nodes found with EIPs. Skipping egress labeling."); + return Ok(()); + } + + info!( + "Found ready egress nodes with EIPs: {:?}", + matched_ready_nodes_with_eip + ); + // Set egress status for nodes ready with an EIP attached. + for node_name in nodes_status_ready { + add_gateway_status_label(node_api, &node_name, "ready").await?; + } + // Attempt cleanup of nodes in a ready state of `Unknown` if another node is ready with an EIP. + for node_name in nodes_status_unknown { + add_gateway_status_label(node_api, &node_name, "unknown").await?; + } + + Ok(()) +} diff --git a/eip_operator/src/main.rs b/eip_operator/src/main.rs index 827b273..375df85 100644 --- a/eip_operator/src/main.rs +++ b/eip_operator/src/main.rs @@ -22,8 +22,10 @@ use eip::v2::Eip; mod aws; mod controller; +mod egress; mod eip; mod kube_ext; +mod node; const LEGACY_MANAGE_EIP_LABEL: &str = "eip.aws.materialize.com/manage"; const LEGACY_POD_FINALIZER_NAME: &str = "eip.aws.materialize.com/disassociate"; @@ -173,10 +175,8 @@ async fn run() -> Result<(), Error> { if let kube_runtime::watcher::Event::Applied(_) | kube_runtime::watcher::Event::Deleted(_) = node { - if let Err(err) = - controller::node::cleanup_old_egress_nodes(&eip_api, &node_api).await - { - event!(Level::ERROR, err = %err, "Node egress cleanup reporting error"); + if let Err(err) = crate::egress::label_egress_nodes(&eip_api, &node_api).await { + event!(Level::ERROR, err = %err, "Node egress labeling reporting error"); } } } diff --git a/eip_operator/src/node.rs b/eip_operator/src/node.rs new file mode 100644 index 0000000..99c9bc3 --- /dev/null +++ b/eip_operator/src/node.rs @@ -0,0 +1,33 @@ +use std::collections::BTreeMap; + +use eip_operator_shared::Error; +use k8s_openapi::api::core::v1::Node; + +/// Get Ready status from the node status field. +pub(crate) fn get_ready_status_from_node(node: &Node) -> Option { + node.status + .as_ref()? + .conditions + .as_ref()? + .iter() + .find(|c| c.type_ == "Ready") + .map(|condition| condition.status.clone()) +} + +/// Retrieve node names and ready status given a list of nodes. +pub(crate) fn get_nodes_ready_status( + node_list: Vec, +) -> Result, Error> { + let mut node_ready_status_map = BTreeMap::new(); + + for node in node_list { + if let Some(ref node_name) = node.metadata.name { + let ready_status = + get_ready_status_from_node(&node).ok_or(Error::MissingNodeReadyCondition)?; + + node_ready_status_map.insert(node_name.to_string(), ready_status); + } + } + + Ok(node_ready_status_map) +} From 13c5c17c6a4c1f04f285da471df3791ff144a861 Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Tue, 3 Sep 2024 13:57:58 -0400 Subject: [PATCH 07/16] update packages and adjust egress node labeling --- Cargo.lock | 624 +++++++++++++++++++--------- Cargo.toml | 13 +- eip_operator/Cargo.toml | 1 + eip_operator/src/controller/node.rs | 7 +- eip_operator/src/egress.rs | 119 ++++-- eip_operator/src/main.rs | 30 +- eip_operator/src/node.rs | 33 -- 7 files changed, 533 insertions(+), 294 deletions(-) delete mode 100644 eip_operator/src/node.rs diff --git a/Cargo.lock b/Cargo.lock index d4a0a7d..028722e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,25 +46,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] -name = "android-tzdata" -version = "0.1.1" +name = "anyhow" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] -name = "android_system_properties" -version = "0.1.5" +name = "async-broadcast" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" dependencies = [ - "libc", + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", ] [[package]] -name = "anyhow" -version = "1.0.75" +name = "async-stream" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] [[package]] name = "async-trait" @@ -102,8 +121,8 @@ dependencies = [ "aws-types", "bytes", "fastrand 2.0.1", - "http", - "hyper", + "http 0.2.9", + "hyper 0.14.27", "time", "tokio", "tracing", @@ -131,8 +150,8 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "http", - "http-body", + "http 0.2.9", + "http-body 0.4.5", "pin-project-lite", "tracing", ] @@ -152,7 +171,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "fastrand 2.0.1", - "http", + "http 0.2.9", "percent-encoding", "tracing", "uuid", @@ -177,7 +196,7 @@ dependencies = [ "aws-smithy-xml", "aws-types", "fastrand 2.0.1", - "http", + "http 0.2.9", "regex", "tracing", ] @@ -199,7 +218,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "http", + "http 0.2.9", "regex", "tracing", ] @@ -222,7 +241,7 @@ dependencies = [ "aws-smithy-types", "aws-smithy-xml", "aws-types", - "http", + "http 0.2.9", "regex", "tracing", ] @@ -241,7 +260,7 @@ dependencies = [ "form_urlencoded", "hex", "hmac", - "http", + "http 0.2.9", "once_cell", "percent-encoding", "regex", @@ -272,8 +291,8 @@ dependencies = [ "bytes", "bytes-utils", "futures-core", - "http", - "http-body", + "http 0.2.9", + "http-body 0.4.5", "once_cell", "percent-encoding", "pin-project-lite", @@ -312,9 +331,9 @@ dependencies = [ "aws-smithy-types", "bytes", "fastrand 2.0.1", - "http", - "http-body", - "hyper", + "http 0.2.9", + "http-body 0.4.5", + "hyper 0.14.27", "once_cell", "pin-project-lite", "pin-utils", @@ -331,7 +350,7 @@ dependencies = [ "aws-smithy-async", "aws-smithy-types", "bytes", - "http", + "http 0.2.9", "pin-project-lite", "tokio", "tracing", @@ -347,8 +366,8 @@ dependencies = [ "bytes", "bytes-utils", "futures-core", - "http", - "http-body", + "http 0.2.9", + "http-body 0.4.5", "itoa", "num-integer", "pin-project-lite", @@ -379,7 +398,7 @@ dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", "aws-smithy-types", - "http", + "http 0.2.9", "rustc_version", "tracing", ] @@ -395,9 +414,9 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http", - "http-body", - "hyper", + "http 0.2.9", + "http-body 0.4.5", + "hyper 0.14.27", "itoa", "matchit", "memchr", @@ -421,8 +440,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 0.2.9", + "http-body 0.4.5", "mime", "rustversion", "tower-layer", @@ -461,6 +480,12 @@ version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64-simd" version = "0.8.0" @@ -534,15 +559,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ - "android-tzdata", - "iana-time-zone", "num-traits", "serde", - "winapi", ] [[package]] @@ -561,6 +583,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -690,6 +721,7 @@ dependencies = [ "eip-operator-shared", "futures", "json-patch", + "jsonptr", "k8s-controller", "k8s-openapi", "kube", @@ -714,7 +746,7 @@ dependencies = [ "aws-smithy-runtime", "aws-smithy-runtime-api", "futures", - "hyper", + "hyper 0.14.27", "hyper-tls", "kube", "kube-runtime", @@ -755,7 +787,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -768,6 +800,27 @@ dependencies = [ "libc", ] +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -783,6 +836,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "fluent-uri" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "fnv" version = "1.0.7" @@ -946,7 +1008,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.9", "indexmap 2.2.1", "slab", "tokio", @@ -972,9 +1034,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -997,7 +1059,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1011,6 +1073,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.5" @@ -1018,15 +1091,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", - "http", + "http 0.2.9", "pin-project-lite", ] [[package]] -name = "http-range-header" -version = "0.3.0" +name = "http-body" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] [[package]] name = "httparse" @@ -1051,35 +1141,55 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 0.2.9", + "http-body 0.4.5", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", "want", ] +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-openssl" -version = "0.9.2" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ee5d7a8f718585d1c3c61dfde28ef5b0bb14734b4db13f5ada856cdc6c612b" +checksum = "527d4d619ca2c2aafa31ec139a3d1d60bf557bf7578a1f20f743637eccd9ca19" dependencies = [ - "http", - "hyper", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", "linked_hash_set", "once_cell", "openssl", "openssl-sys", "parking_lot", - "tokio", - "tokio-openssl", + "pin-project", "tower-layer", + "tower-service", ] [[package]] @@ -1088,12 +1198,25 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper", + "hyper 0.14.27", "pin-project-lite", "tokio", "tokio-io-timeout", ] +[[package]] +name = "hyper-timeout" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +dependencies = [ + "hyper 1.4.1", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1101,33 +1224,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.27", "native-tls", "tokio", "tokio-native-tls", ] [[package]] -name = "iana-time-zone" -version = "0.1.57" +name = "hyper-util" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower", + "tower-service", + "tracing", ] [[package]] @@ -1173,7 +1293,7 @@ checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1213,32 +1333,47 @@ dependencies = [ [[package]] name = "json-patch" -version = "1.2.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ff1e1486799e3f64129f8ccad108b38290df9cd7015cd31bed17239f0789d6" +checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" dependencies = [ + "jsonptr", "serde", "serde_json", "thiserror", - "treediff", ] [[package]] -name = "jsonpath_lib" -version = "0.3.0" +name = "jsonpath-rust" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa63191d68230cccb81c5aa23abd53ed64d83337cacbb25a7b8c7979523774f" +checksum = "19d8fe85bd70ff715f31ce8c739194b423d79811a19602115d611a3ec85d6200" dependencies = [ - "log", + "lazy_static", + "once_cell", + "pest", + "pest_derive", + "regex", + "serde_json", + "thiserror", +] + +[[package]] +name = "jsonptr" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" +dependencies = [ + "fluent-uri", "serde", "serde_json", ] [[package]] name = "k8s-controller" -version = "0.2.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7257cd713364feef4f8c85f8a9363337f7038c44763b6ddf940e0724523e51f0" +checksum = "d5da4d3249c4ebd1e6de32134fb82a720db3e7a978ad38e576d64e6a22c6e308" dependencies = [ "async-trait", "futures", @@ -1252,12 +1387,11 @@ dependencies = [ [[package]] name = "k8s-openapi" -version = "0.20.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc3606fd16aca7989db2f84bb25684d0270c6d6fa1dbcd0025af7b4130523a6" +checksum = "19501afb943ae5806548bc3ebd7f3374153ca057a38f480ef30adfde5ef09755" dependencies = [ - "base64", - "bytes", + "base64 0.22.1", "chrono", "serde", "serde-value", @@ -1266,9 +1400,9 @@ dependencies = [ [[package]] name = "kube" -version = "0.87.1" +version = "0.92.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34392aea935145070dcd5b39a6dea689ac6534d7d117461316c3d157b1d0fc3" +checksum = "231c5a5392d9e2a9b0d923199760d3f1dd73b95288f2871d16c7c90ba4954506" dependencies = [ "k8s-openapi", "kube-client", @@ -1278,27 +1412,28 @@ dependencies = [ [[package]] name = "kube-client" -version = "0.87.1" +version = "0.92.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7266548b9269d9fa19022620d706697e64f312fb2ba31b93e6986453fcc82c92" +checksum = "8f4bf54135062ff60e2a0dfb3e7a9c8e931fc4a535b4d6bd561e0a1371321c61" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "chrono", "either", "futures", "home", - "http", - "http-body", - "hyper", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", "hyper-openssl", - "hyper-timeout", - "jsonpath_lib", + "hyper-timeout 0.5.1", + "hyper-util", + "jsonpath-rust", "k8s-openapi", "kube-core", "openssl", "pem", - "pin-project", "secrecy", "serde", "serde_json", @@ -1313,16 +1448,15 @@ dependencies = [ [[package]] name = "kube-core" -version = "0.87.1" +version = "0.92.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8321c315b96b59f59ef6b33f604b84b905ab8f9ff114a4f909d934c520227b1" +checksum = "40fb9bd8141cbc0fe6b0d9112d371679b4cb607b45c31dd68d92e40864a12975" dependencies = [ "chrono", "form_urlencoded", - "http", + "http 1.1.0", "json-patch", "k8s-openapi", - "once_cell", "schemars", "serde", "serde_json", @@ -1331,9 +1465,9 @@ dependencies = [ [[package]] name = "kube-derive" -version = "0.87.1" +version = "0.92.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54591e1f37fc329d412c0fdaced010cc1305b546a39f283fc51700f8fb49421" +checksum = "08fc86f70076921fdf2f433bbd2a796dc08ac537dc1db1f062cfa63ed4fa15fb" dependencies = [ "darling", "proc-macro2", @@ -1344,24 +1478,26 @@ dependencies = [ [[package]] name = "kube-runtime" -version = "0.87.1" +version = "0.92.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e511e2c1a368d9d4bf6e70db58197e535d818df355b5a2007a8aeb17a370a8ba" +checksum = "b7eb2fb986f81770eb55ec7f857e197019b31b38768d2410f6c1046ffac34225" dependencies = [ "ahash", + "async-broadcast", + "async-stream", "async-trait", "backoff", "derivative", "futures", "hashbrown 0.14.2", "json-patch", + "jsonptr", "k8s-openapi", "kube-client", "parking_lot", "pin-project", "serde", "serde_json", - "smallvec", "thiserror", "tokio", "tokio-util", @@ -1455,13 +1591,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1598,16 +1735,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" version = "0.31.1" @@ -1619,9 +1746,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" @@ -1701,7 +1828,7 @@ checksum = "f24cda83b20ed2433c68241f918d0f6fdec8b1d43b7a9590ab4420c5095ca930" dependencies = [ "async-trait", "futures-core", - "http", + "http 0.2.9", "opentelemetry", "opentelemetry-proto", "opentelemetry-semantic-conventions", @@ -1785,6 +1912,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + [[package]] name = "parking_lot" version = "0.12.1" @@ -1805,7 +1938,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.1", ] [[package]] @@ -1820,7 +1953,7 @@ version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" dependencies = [ - "base64", + "base64 0.21.4", "serde", ] @@ -1830,20 +1963,65 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "pest" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "pest_meta" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "pin-project" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", @@ -1852,9 +2030,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.10" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -2024,7 +2202,7 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2045,7 +2223,7 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2164,7 +2342,6 @@ version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ - "indexmap 2.2.1", "itoa", "ryu", "serde", @@ -2223,9 +2400,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" @@ -2237,6 +2414,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "strsim" version = "0.10.0" @@ -2288,7 +2475,7 @@ dependencies = [ "fastrand 1.9.0", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2349,21 +2536,19 @@ dependencies = [ [[package]] name = "tokio" -version = "1.29.1" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ - "autocfg", "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.7", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -2378,9 +2563,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", @@ -2397,18 +2582,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-openssl" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ffab79df67727f6acf57f1ff743091873c24c579b1e2ce4d8f53e47ded4d63d" -dependencies = [ - "futures-util", - "openssl", - "openssl-sys", - "tokio", -] - [[package]] name = "tokio-stream" version = "0.1.14" @@ -2443,15 +2616,15 @@ checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ "async-trait", "axum", - "base64", + "base64 0.21.4", "bytes", "futures-core", "futures-util", "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", + "http 0.2.9", + "http-body 0.4.5", + "hyper 0.14.27", + "hyper-timeout 0.4.1", "percent-encoding", "pin-project", "prost", @@ -2485,18 +2658,16 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "base64", + "base64 0.21.4", "bitflags 2.4.0", "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", "mime", "pin-project-lite", "tower-layer", @@ -2609,15 +2780,6 @@ dependencies = [ "tracing-serde", ] -[[package]] -name = "treediff" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303" -dependencies = [ - "serde_json", -] - [[package]] name = "try-lock" version = "0.2.4" @@ -2630,6 +2792,12 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "unicode-ident" version = "1.0.10" @@ -2780,21 +2948,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" +name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", ] [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2803,13 +2971,29 @@ version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2818,42 +3002,90 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "xmlparser" version = "0.13.5" diff --git a/Cargo.toml b/Cargo.toml index 99f8b7a..c7bc285 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,17 +19,18 @@ rust-version = "1.76.0" [workspace.dependencies] iptables = "0.5" -json-patch = "1.2.0" -k8s-controller = "0.2.0" -k8s-openapi = { version = "0.20", default-features = false, features = [ - "v1_26", +json-patch = "2.0.0" +jsonptr = "0.4.7" +k8s-controller = "0.3.0" +k8s-openapi = { version = "0.22", default-features = false, features = [ + "v1_29", ] } netlink-packet-route = "0.17" rand = "0.8" rtnetlink = "0.13" tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] } -kube = { version = "0.87.0", default-features = false, features = ["derive", "openssl-tls"] } -kube-runtime = "0.87.0" +kube = { version = "0.92.1", default-features = false, features = ["derive", "openssl-tls"] } +kube-runtime = "0.92.1" tracing = "0.1" async-trait = "0.1.77" futures = "0.3" diff --git a/eip_operator/Cargo.toml b/eip_operator/Cargo.toml index 0cfdb6e..be016c2 100644 --- a/eip_operator/Cargo.toml +++ b/eip_operator/Cargo.toml @@ -27,4 +27,5 @@ eip-operator-shared = { workspace = true } async-trait = "0.1.77" k8s-openapi = { workspace = true } json-patch = { workspace = true } +jsonptr = { workspace = true } k8s-controller = { workspace = true } diff --git a/eip_operator/src/controller/node.rs b/eip_operator/src/controller/node.rs index 329913b..0f199c9 100644 --- a/eip_operator/src/controller/node.rs +++ b/eip_operator/src/controller/node.rs @@ -69,7 +69,12 @@ impl k8s_controller::Context for Context { // Check the existing Node for EIP association and ready status. // Node's that go in to a "NotReady" or "Unknown" state should have their EIP // disassociated to allow a new node to spawn and use the EIP. - let node_condition_ready_status = crate::node::get_ready_status_from_node(node) + let node_condition_ready_status = node + .status + .as_ref() + .and_then(|status| status.conditions.as_ref()) + .and_then(|conditions| conditions.iter().find(|c| c.type_ == "Ready")) + .map(|condition| condition.status.clone()) .ok_or(Error::MissingNodeReadyCondition)?; match node_condition_ready_status.as_str() { // Remove the EIP from nodes with an Unknown or NotReady ready status. diff --git a/eip_operator/src/egress.rs b/eip_operator/src/egress.rs index 4ea8653..109160b 100644 --- a/eip_operator/src/egress.rs +++ b/eip_operator/src/egress.rs @@ -8,7 +8,7 @@ use tracing::{info, instrument}; use crate::EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY; use crate::EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE; -/// Applies label to node specifying the status of the egress gateway node. +/// Applies label specifying the ready status of the egress gateway node. #[instrument(skip(api), err)] async fn add_gateway_status_label( api: &Api, @@ -52,43 +52,21 @@ async fn get_egress_nodes(api: &Api) -> Result, kube::Error> { } } -/// Update state label on egress nodes. -/// Note: Egress traffic will be immediately dropped when the status label is changed away from "true". +/// Update the ready status label on egress nodes. +/// This controls whether traffic is allowed to egress through the node. +/// Note: Egress traffic will be immediately dropped when the ready status label value is changed away from "true". #[instrument(skip(), err)] pub(crate) async fn label_egress_nodes( eip_api: &Api, node_api: &Api, ) -> Result<(), Error> { - info!("Updating egress node status labels."); - let node_list = get_egress_nodes(&Api::all(node_api.clone().into())).await?; - if node_list.is_empty() { - info!("No egress nodes found. Skipping egress cleanup."); + let egress_nodes = get_egress_nodes(&Api::all(node_api.clone().into())).await?; + if egress_nodes.is_empty() { + info!("No egress nodes found. Skipping egress node ready status labeling."); return Ok(()); } - let node_names_and_status: BTreeMap = - crate::node::get_nodes_ready_status(node_list)?; - let (nodes_status_ready, nodes_status_unknown): (BTreeSet, BTreeSet) = - node_names_and_status.iter().fold( - (BTreeSet::new(), BTreeSet::new()), - |(mut ready, mut unknown), (name, status)| { - match status.as_str() { - "True" => { - ready.insert(name.clone()); - } - "Unknown" => { - unknown.insert(name.clone()); - } - // Ignore nodes in other states. - &_ => { - info!("Ignoring node {} with status {}", name, status); - } - } - (ready, unknown) - }, - ); - - // Ensure an egress node exists with an EIP and Ready state of `true`. + // Build up a list of EIP resourceId's to check EIP attachment against node names. let eip_resource_ids: BTreeSet = eip_api .list(&ListParams::default()) .await? @@ -96,27 +74,78 @@ pub(crate) async fn label_egress_nodes( .into_iter() .filter_map(|eip| eip.status.and_then(|s| s.resource_id)) .collect(); - let matched_ready_nodes_with_eip: BTreeSet = nodes_status_ready - .intersection(&eip_resource_ids) - .cloned() + + // Build up a map of egress node names along with their + // ready status and whether or not they have an EIP attached. + let mut node_map: BTreeMap = BTreeMap::new(); + for node in egress_nodes { + if let Some(ref node_name) = node.metadata.name { + let ready_status = node + .status + .as_ref() + .and_then(|status| status.conditions.as_ref()) + .and_then(|conditions| conditions.iter().find(|c| c.type_ == "Ready")) + .map(|condition| condition.status.clone()) + .ok_or(Error::MissingNodeReadyCondition)?; + let eip_attached_status = eip_resource_ids.contains(node_name.as_str()); + node_map.insert(node_name.clone(), (ready_status, eip_attached_status)); + } + } + + let egress_nodes_ready_with_eip: BTreeMap = node_map + .iter() + .filter(|(_, &(ref ready_status, eip_attached_status))| { + ready_status == "True" && eip_attached_status + }) + .map(|(node_name, &(ref ready_status, eip_attached_status))| { + ( + node_name.clone(), + (ready_status.clone(), eip_attached_status), + ) + }) .collect(); - if matched_ready_nodes_with_eip.is_empty() { - info!("No ready egress nodes found with EIPs. Skipping egress labeling."); + // Wait to label egress nodes until an EIP is attached. + // Setting the ready status label to "ready" routes egress traffic through the node + // Wait to label egress nodes until they are ready and have an EIP attached. + if egress_nodes_ready_with_eip.is_empty() { + info!( + "No egress nodes found with a ready status and attached EIP. Skipping egress labeling." + ); return Ok(()); } - info!( - "Found ready egress nodes with EIPs: {:?}", - matched_ready_nodes_with_eip - ); - // Set egress status for nodes ready with an EIP attached. - for node_name in nodes_status_ready { - add_gateway_status_label(node_api, &node_name, "ready").await?; + // At least one egress node should be ready with an EIP attached in this current implementation. + // This allows the ready status label to be set and traffic to be routed through the node. + assert!(egress_nodes_ready_with_eip.len() == 1); + if let Some((first_key, _)) = egress_nodes_ready_with_eip.first_key_value() { + let node_name = first_key.clone(); + add_gateway_status_label(node_api, node_name.as_str(), "true").await?; } - // Attempt cleanup of nodes in a ready state of `Unknown` if another node is ready with an EIP. - for node_name in nodes_status_unknown { - add_gateway_status_label(node_api, &node_name, "unknown").await?; + + // We immediately disassociate EIPs from egress nodes that are in an + // unresponsive state in the eip node controller. + // This allows the EIP to be re-associated with a new healthy node. + // However, unresponsive nodes may still exist with a ready-status egress label + // set to "true". This allows the old node to still serve traffic if possible until a + // new node is ready to take over. + // Clean up unresponsive egress nodes in a ready state of `Unknown` if another node is ready with an EIP. + // Egress nodes in a ready state of "False" are not re-labelled since they should be able to be cleaned + // up by normal methods. + let egress_nodes_not_ready_without_eip: BTreeMap = node_map + .iter() + .filter(|(_, &(ref ready_status, eip_attached_status))| { + (ready_status == "Unknown") && !eip_attached_status + }) + .map(|(node_name, &(ref ready_status, eip_attached_status))| { + ( + node_name.clone(), + (ready_status.clone(), eip_attached_status), + ) + }) + .collect(); + for node_name in egress_nodes_not_ready_without_eip.keys() { + add_gateway_status_label(node_api, node_name.as_str(), "false").await?; } Ok(()) diff --git a/eip_operator/src/main.rs b/eip_operator/src/main.rs index 375df85..1d1a95d 100644 --- a/eip_operator/src/main.rs +++ b/eip_operator/src/main.rs @@ -25,7 +25,6 @@ mod controller; mod egress; mod eip; mod kube_ext; -mod node; const LEGACY_MANAGE_EIP_LABEL: &str = "eip.aws.materialize.com/manage"; const LEGACY_POD_FINALIZER_NAME: &str = "eip.aws.materialize.com/disassociate"; @@ -36,7 +35,7 @@ const EIP_ALLOCATION_ID_ANNOTATION: &str = "eip.materialize.cloud/allocation_id" const EXTERNAL_DNS_TARGET_ANNOTATION: &str = "external-dns.alpha.kubernetes.io/target"; const EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY: &str = "workload"; const EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE: &str = "materialize-egress"; -const EGRESS_NODE_STATUS_LABEL: &str = "egress-gateway.materialize.cloud/status"; +const EGRESS_NODE_STATUS_LABEL: &str = "egress-gateway.materialize.cloud/ready-status"; // See https://us-east-1.console.aws.amazon.com/servicequotas/home/services/ec2/quotas // and filter in the UI for EC2 quotas like this, or use the CLI: @@ -146,7 +145,7 @@ async fn run() -> Result<(), Error> { let context = controller::node::Context::new(ec2_client.clone(), namespace.clone()); let watch_config = kube_runtime::watcher::Config::default().labels(MANAGE_EIP_LABEL); let node_controller = Controller::cluster(k8s_client.clone(), context, watch_config); - task::spawn(node_controller.run()) + task::spawn(node_controller.with_concurrency(1).run()) }); tasks.push({ @@ -162,18 +161,17 @@ async fn run() -> Result<(), Error> { tasks.push({ let eip_api = eip_api.clone(); let node_api = node_api.clone(); - let watch_config = - kube_runtime::watcher::Config::default().labels(EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY); + let watch_config = kube_runtime::watcher::Config::default(); task::spawn(async move { - let mut watcher = pin!(kube_runtime::watcher(node_api.clone(), watch_config)); + let mut watcher = pin!(kube_runtime::watcher(eip_api.clone(), watch_config)); - while let Some(node) = watcher.try_next().await.unwrap_or_else(|e| { - event!(Level::ERROR, err = %e, "Error watching nodes"); + while let Some(eip) = watcher.try_next().await.unwrap_or_else(|e| { + event!(Level::ERROR, err = %e, "Error watching eips"); None }) { - if let kube_runtime::watcher::Event::Applied(_) - | kube_runtime::watcher::Event::Deleted(_) = node + if let kube_runtime::watcher::Event::Apply(_) + | kube_runtime::watcher::Event::Delete(_) = eip { if let Err(err) = crate::egress::label_egress_nodes(&eip_api, &node_api).await { event!(Level::ERROR, err = %err, "Node egress labeling reporting error"); @@ -189,6 +187,13 @@ async fn run() -> Result<(), Error> { Ok(()) } +/// Generate a finalizer path. +fn finalizer_path(position: usize) -> jsonptr::Pointer { + format!("/metadata/finalizers/{}", position) + .parse() + .unwrap() +} + /// Finds all EIPs tagged for this cluster, then compares them to the pod UIDs. If the EIP is not /// tagged with a pod UID, or the UID does not exist in this cluster, it deletes the EIP. #[instrument(skip(ec2_client, eip_api, pod_api), err)] @@ -261,18 +266,17 @@ async fn cleanup_orphan_eips( .position(|s| s == LEGACY_POD_FINALIZER_NAME) { let pod_name = pod.name_unchecked(); - let finalizer_path = format!("/metadata/finalizers/{}", position); pod_api .patch::( &pod_name, &PatchParams::default(), &Patch::Json(json_patch::Patch(vec![ PatchOperation::Test(TestOperation { - path: finalizer_path.clone(), + path: finalizer_path(position), value: LEGACY_POD_FINALIZER_NAME.into(), }), PatchOperation::Remove(RemoveOperation { - path: finalizer_path, + path: finalizer_path(position), }), ])), ) diff --git a/eip_operator/src/node.rs b/eip_operator/src/node.rs deleted file mode 100644 index 99c9bc3..0000000 --- a/eip_operator/src/node.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::collections::BTreeMap; - -use eip_operator_shared::Error; -use k8s_openapi::api::core::v1::Node; - -/// Get Ready status from the node status field. -pub(crate) fn get_ready_status_from_node(node: &Node) -> Option { - node.status - .as_ref()? - .conditions - .as_ref()? - .iter() - .find(|c| c.type_ == "Ready") - .map(|condition| condition.status.clone()) -} - -/// Retrieve node names and ready status given a list of nodes. -pub(crate) fn get_nodes_ready_status( - node_list: Vec, -) -> Result, Error> { - let mut node_ready_status_map = BTreeMap::new(); - - for node in node_list { - if let Some(ref node_name) = node.metadata.name { - let ready_status = - get_ready_status_from_node(&node).ok_or(Error::MissingNodeReadyCondition)?; - - node_ready_status_map.insert(node_name.to_string(), ready_status); - } - } - - Ok(node_ready_status_map) -} From 344a5956c9d2808bbf53c2565bd76d0f4cf4ea4c Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Thu, 5 Sep 2024 16:37:39 -0400 Subject: [PATCH 08/16] move to rust 1.78 and upgrade more dependencies --- Cargo.lock | 383 +++++++++++++++++++-------------- Cargo.toml | 2 +- Dockerfile | 2 +- deny.toml | 16 ++ eip_operator/Cargo.toml | 14 +- eip_operator/src/main.rs | 10 +- eip_operator_shared/Cargo.toml | 28 ++- eip_operator_shared/src/lib.rs | 14 +- 8 files changed, 267 insertions(+), 202 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 028722e..5c28f39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,6 +96,12 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.1.0" @@ -104,12 +110,11 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "aws-config" -version = "0.101.0" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f9625b71b3ee4adbfbca369c6680d156e316ed86d2c7199a2a134563917414" +checksum = "4e95816a168520d72c0e7680c405a5a8c1fb6a035b4bc4b9d7b0de8e1a941697" dependencies = [ "aws-credential-types", - "aws-http", "aws-runtime", "aws-sdk-sts", "aws-smithy-async", @@ -122,17 +127,17 @@ dependencies = [ "bytes", "fastrand 2.0.1", "http 0.2.9", - "hyper 0.14.27", "time", "tokio", "tracing", + "url", ] [[package]] name = "aws-credential-types" -version = "0.101.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5924466398ac76ffd411d297b9d516dcebb0577f7344c0c15fd8e8e04d9c7895" +checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -140,51 +145,38 @@ dependencies = [ "zeroize", ] -[[package]] -name = "aws-http" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb9a3aa335a105a00975c971f1dad403c3175f2a210d98f39345c6af53923912" -dependencies = [ - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-types", - "bytes", - "http 0.2.9", - "http-body 0.4.5", - "pin-project-lite", - "tracing", -] - [[package]] name = "aws-runtime" -version = "0.101.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75844ecbdf3dc5e0f5ac5fd1088fb1623849990ea9445d2826258ce63be4de5" +checksum = "2424565416eef55906f9f8cece2072b6b6a76075e3ff81483ebe938a89a4c05f" dependencies = [ "aws-credential-types", - "aws-http", "aws-sigv4", "aws-smithy-async", "aws-smithy-http", + "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", "aws-types", + "bytes", "fastrand 2.0.1", "http 0.2.9", + "http-body 0.4.5", + "once_cell", "percent-encoding", + "pin-project-lite", "tracing", "uuid", ] [[package]] name = "aws-sdk-ec2" -version = "0.38.0" +version = "1.71.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eadf3b2bbaed2435729f6aadc5355d06a8e00cf819309a67d736594b04880ba" +checksum = "a32ce9e17a7edc78842d92539c9e3bca8b05b06141113f5e1680cf7f5c87f2f4" dependencies = [ "aws-credential-types", - "aws-http", "aws-runtime", "aws-smithy-async", "aws-smithy-http", @@ -197,18 +189,18 @@ dependencies = [ "aws-types", "fastrand 2.0.1", "http 0.2.9", - "regex", + "once_cell", + "regex-lite", "tracing", ] [[package]] name = "aws-sdk-servicequotas" -version = "0.38.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9177a64ca9f65cf293e2f3223b3e87594a3271688c741a61ff03574c6a0fdf4" +checksum = "2b161e345bee21a3b28462b3294ea564b970d0ba13695b3becb62864ed484c1c" dependencies = [ "aws-credential-types", - "aws-http", "aws-runtime", "aws-smithy-async", "aws-smithy-http", @@ -219,18 +211,18 @@ dependencies = [ "aws-types", "bytes", "http 0.2.9", - "regex", + "once_cell", + "regex-lite", "tracing", ] [[package]] name = "aws-sdk-sts" -version = "0.38.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e3958c43a78f6c3822e62009a35802af5cc7c120fbe8e60b98565604569aae" +checksum = "99c56bcd6a56cab7933980a54148b476a5a69a7694e3874d9aa2a566f150447d" dependencies = [ "aws-credential-types", - "aws-http", "aws-runtime", "aws-smithy-async", "aws-smithy-http", @@ -242,15 +234,16 @@ dependencies = [ "aws-smithy-xml", "aws-types", "http 0.2.9", - "regex", + "once_cell", + "regex-lite", "tracing", ] [[package]] name = "aws-sigv4" -version = "0.101.0" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06130e3686db3c5ae2fc44b3516fffe6b4d4eccebe09bd8ccc4067f3c9c183fb" +checksum = "5df1b0fa6be58efe9d4ccc257df0a53b89cd8909e86591a13ca54817c87517be" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -261,9 +254,9 @@ dependencies = [ "hex", "hmac", "http 0.2.9", + "http 1.1.0", "once_cell", "percent-encoding", - "regex", "sha2", "time", "tracing", @@ -271,9 +264,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "0.101.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d787b7e07925b450bed90d9d29ac8e57006c9c2ac907151d175ac0e376bfee0e" +checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" dependencies = [ "futures-util", "pin-project-lite", @@ -282,9 +275,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.59.0" +version = "0.60.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96daaad925331c72449423574fdc72b54af780d5a23ace3c0a6ad0ccbf378715" +checksum = "01dbcb6e2588fd64cfb6d7529661b06466419e4c54ed1c62d6510d2d0350a728" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -302,18 +295,18 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.59.0" +version = "0.60.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ff985bee3fe21046dc501fadc1d04a1161977c55a0cbbccd9b111c18206aa64" +checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" dependencies = [ "aws-smithy-types", ] [[package]] name = "aws-smithy-query" -version = "0.59.0" +version = "0.60.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4006503693766d34717efc5f58325062845fce26a683a71b70f23156d72e67" +checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" dependencies = [ "aws-smithy-types", "urlencoding", @@ -321,9 +314,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "0.101.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d28af854558601b4202a4273b9720aebe43d73e472143e6056f16e3bd90bc837" +checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -331,8 +324,11 @@ dependencies = [ "aws-smithy-types", "bytes", "fastrand 2.0.1", + "h2 0.3.26", "http 0.2.9", "http-body 0.4.5", + "http-body 1.0.1", + "httparse", "hyper 0.14.27", "once_cell", "pin-project-lite", @@ -343,31 +339,36 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "0.101.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1c68e17e754b86da350b43add38294189121a880e9c3fb454f83ff7044f5257" +checksum = "e086682a53d3aa241192aa110fa8dfce98f2f5ac2ead0de84d41582c7e8fdb96" dependencies = [ "aws-smithy-async", "aws-smithy-types", "bytes", "http 0.2.9", + "http 1.1.0", "pin-project-lite", "tokio", "tracing", + "zeroize", ] [[package]] name = "aws-smithy-types" -version = "0.101.0" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97b978d8a351ea5744206ecc643a1d3806628680e9f151b4d6b7a76fec1596f" +checksum = "273dcdfd762fae3e1650b8024624e7cd50e484e37abdab73a7a706188ad34543" dependencies = [ "base64-simd", "bytes", "bytes-utils", "futures-core", "http 0.2.9", + "http 1.1.0", "http-body 0.4.5", + "http-body 1.0.1", + "http-body-util", "itoa", "num-integer", "pin-project-lite", @@ -381,42 +382,40 @@ dependencies = [ [[package]] name = "aws-smithy-xml" -version = "0.59.0" +version = "0.60.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97500a0d0884b9576e65076075f81d899cfbb84f7db5ca1dd317f0582204e528" +checksum = "d123fbc2a4adc3c301652ba8e149bf4bc1d1725affb9784eb20c953ace06bf55" dependencies = [ "xmlparser", ] [[package]] name = "aws-types" -version = "0.101.0" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61065f0c6070cb0f9aaddfa614605fb1049908481da71ba5b39b2ffca12f57e4" +checksum = "5221b91b3e441e6675310829fd8984801b772cb1546ef6c0e54dec9f1ac13fef" dependencies = [ "aws-credential-types", "aws-smithy-async", "aws-smithy-runtime-api", "aws-smithy-types", - "http 0.2.9", "rustc_version", "tracing", ] [[package]] name = "axum" -version = "0.6.18" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core", - "bitflags 1.3.2", "bytes", "futures-util", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.27", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", "itoa", "matchit", "memchr", @@ -425,7 +424,7 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper", + "sync_wrapper 1.0.1", "tower", "tower-layer", "tower-service", @@ -433,17 +432,20 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" dependencies = [ "async-trait", "bytes", "futures-util", - "http 0.2.9", - "http-body 0.4.5", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", "mime", + "pin-project-lite", "rustversion", + "sync_wrapper 0.1.2", "tower-layer", "tower-service", ] @@ -617,16 +619,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -711,15 +703,18 @@ checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "eip-operator" -version = "0.3.0" +version = "0.4.0" dependencies = [ "async-trait", "aws-config", "aws-sdk-ec2", "aws-sdk-servicequotas", "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", "eip-operator-shared", "futures", + "hyper-tls 0.5.0", "json-patch", "jsonptr", "k8s-controller", @@ -736,18 +731,15 @@ dependencies = [ [[package]] name = "eip-operator-shared" -version = "0.3.0" +version = "0.4.0" dependencies = [ "async-trait", - "aws-config", "aws-sdk-ec2", "aws-sdk-servicequotas", - "aws-smithy-http", - "aws-smithy-runtime", - "aws-smithy-runtime-api", "futures", - "hyper 0.14.27", - "hyper-tls", + "hyper 1.4.1", + "hyper-tls 0.6.0", + "hyper-util", "kube", "kube-runtime", "native-tls", @@ -1016,6 +1008,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.2.1", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1140,7 +1151,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.9", "http-body 0.4.5", "httparse", @@ -1163,9 +1174,11 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -1192,18 +1205,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "hyper-timeout" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" -dependencies = [ - "hyper 0.14.27", - "pin-project-lite", - "tokio", - "tokio-io-timeout", -] - [[package]] name = "hyper-timeout" version = "0.5.1" @@ -1230,6 +1231,22 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.7" @@ -1256,6 +1273,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1427,7 +1454,7 @@ dependencies = [ "http-body-util", "hyper 1.4.1", "hyper-openssl", - "hyper-timeout 0.5.1", + "hyper-timeout", "hyper-util", "jsonpath-rust", "k8s-openapi", @@ -1603,11 +1630,10 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -1806,32 +1832,29 @@ dependencies = [ [[package]] name = "opentelemetry" -version = "0.21.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" +checksum = "4c365a63eec4f55b7efeceb724f1336f26a9cf3427b70e59e2cd2a5b947fba96" dependencies = [ "futures-core", "futures-sink", - "indexmap 2.2.1", "js-sys", "once_cell", "pin-project-lite", "thiserror", - "urlencoding", ] [[package]] name = "opentelemetry-otlp" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f24cda83b20ed2433c68241f918d0f6fdec8b1d43b7a9590ab4420c5095ca930" +checksum = "6b925a602ffb916fb7421276b86756027b37ee708f9dce2dbdcc51739f07e727" dependencies = [ "async-trait", "futures-core", - "http 0.2.9", + "http 1.1.0", "opentelemetry", "opentelemetry-proto", - "opentelemetry-semantic-conventions", "opentelemetry_sdk", "prost", "thiserror", @@ -1841,9 +1864,9 @@ dependencies = [ [[package]] name = "opentelemetry-proto" -version = "0.4.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2e155ce5cc812ea3d1dffbd1539aed653de4bf4882d60e6e04dcf0901d674e1" +checksum = "30ee9f20bff9c984511a02f082dc8ede839e4a9bf15cc2487c8d6fea5ad850d9" dependencies = [ "opentelemetry", "opentelemetry_sdk", @@ -1851,32 +1874,22 @@ dependencies = [ "tonic", ] -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5774f1ef1f982ef2a447f6ee04ec383981a3ab99c8e77a1a7b30182e65bbc84" -dependencies = [ - "opentelemetry", -] - [[package]] name = "opentelemetry_sdk" -version = "0.21.2" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f16aec8a98a457a52664d69e0091bac3a0abd18ead9b641cb00202ba4e0efe4" +checksum = "692eac490ec80f24a17828d49b40b60f5aeaccdfe6a503f939713afd22bc28df" dependencies = [ "async-trait", - "crossbeam-channel", "futures-channel", "futures-executor", "futures-util", "glob", "once_cell", "opentelemetry", - "ordered-float 4.2.0", "percent-encoding", "rand", + "serde_json", "thiserror", "tokio", "tokio-stream", @@ -1891,15 +1904,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "ordered-float" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" -dependencies = [ - "num-traits", -] - [[package]] name = "outref" version = "0.5.1" @@ -2063,9 +2067,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.11.9" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" dependencies = [ "bytes", "prost-derive", @@ -2073,15 +2077,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.9" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -2152,6 +2156,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.6.29" @@ -2310,7 +2320,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" dependencies = [ - "ordered-float 2.10.0", + "ordered-float", "serde", ] @@ -2464,6 +2474,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "tempfile" version = "3.6.0" @@ -2534,6 +2550,21 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.40.0" @@ -2551,16 +2582,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-macros" version = "2.4.0" @@ -2610,24 +2631,26 @@ dependencies = [ [[package]] name = "tonic" -version = "0.9.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +checksum = "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401" dependencies = [ + "async-stream", "async-trait", "axum", - "base64 0.21.4", + "base64 0.22.1", "bytes", - "futures-core", - "futures-util", - "h2", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.27", - "hyper-timeout 0.4.1", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-timeout", + "hyper-util", "percent-encoding", "pin-project", "prost", + "socket2 0.5.7", "tokio", "tokio-stream", "tower", @@ -2733,9 +2756,9 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.22.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c67ac25c5407e7b961fafc6f7e9aa5958fd297aada2d20fa2ae1737357e55596" +checksum = "a9784ed4da7d921bc8df6963f8c80a0e4ce34ba6ba76668acadd3edbd985ff3b" dependencies = [ "js-sys", "once_cell", @@ -2798,18 +2821,44 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "unsafe-libyaml" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "urlencoding" version = "2.1.2" @@ -2917,9 +2966,9 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-time" -version = "0.2.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index c7bc285..3c23c68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ [workspace.package] edition = "2021" -rust-version = "1.76.0" +rust-version = "1.78.0" # Use this section only to change the source of dependencies that might diff --git a/Dockerfile b/Dockerfile index d5526f2..567f733 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM rust:1.76.0-slim-bookworm AS chef +FROM --platform=$BUILDPLATFORM rust:1.78.0-slim-bookworm AS chef RUN cargo install --locked cargo-chef ARG TARGETARCH RUN echo -n "$TARGETARCH" | sed 's#amd64#x86_64#;s#arm64#aarch64#' > /cargo_arch diff --git a/deny.toml b/deny.toml index 25d42ae..c795d5b 100644 --- a/deny.toml +++ b/deny.toml @@ -24,6 +24,22 @@ skip = [ { name = "ordered-float", version = "2.10.0" }, { name = "fastrand", version = "2.0.1" }, { name = "regex-syntax", version = "0.6.29" }, + { name = "sync_wrapper", version = "0.1.2" }, + { name = "sync_wrapper", version = "1.0.1" }, + { name = "hyper-tls", version = "0.5.0" }, + { name = "hyper-tls", version = "0.6.0" }, + { name = "socket2", version = "0.4.9" }, + { name = "socket2", version = "0.5.7" }, + { name = "hyper", version = "0.14.27" }, + { name = "hyper", version = "1.4.1" }, + { name = "base64", version = "0.21.4" }, + { name = "base64", version = "0.22.1" }, + { name = "http-body", version = "0.4.5" }, + { name = "http-body", version = "1.0.1" }, + { name = "h2", version = "0.3.26"}, + { name = "h2", version = "0.4.6"}, + { name = "http", version = "0.2.9"}, + { name = "http", version = "1.1.0"}, ] # Use `tracing` instead. diff --git a/eip_operator/Cargo.toml b/eip_operator/Cargo.toml index be016c2..941ee72 100644 --- a/eip_operator/Cargo.toml +++ b/eip_operator/Cargo.toml @@ -1,19 +1,21 @@ [package] name = "eip-operator" -version = "0.3.0" +version = "0.4.0" edition = "2021" license = "Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -aws-config = { version = "0.101", default-features = false} -aws-sdk-ec2 = { version = "0.38", default-features = false, features = [ "rt-tokio" ] } -aws-sdk-servicequotas = { version = "0.38", default-features = false, features = [ "rt-tokio" ] } -aws-smithy-http = { version = "0.59", default-features = false, features = [ "rt-tokio" ] } +aws-config = { version = "1.5.5", default-features = false} +aws-sdk-ec2 = { version = "1.64.0", default-features = false, features = [ "rt-tokio" ] } +aws-sdk-servicequotas = { version = "1.37", default-features = false, features = [ "rt-tokio" ] } +aws-smithy-runtime-api = { version = "1", features = ["http-02x"] } +aws-smithy-runtime = { version = "1", features = ["connector-hyper-0-14-x"] } +aws-smithy-http = { version = "0.60.9", default-features = false, features = [ "rt-tokio" ] } +hyper-tls = "0.5.0" futures = { workspace = true } - kube = { workspace = true } kube-runtime = { workspace = true } rand = { workspace = true } diff --git a/eip_operator/src/main.rs b/eip_operator/src/main.rs index 1d1a95d..f93eace 100644 --- a/eip_operator/src/main.rs +++ b/eip_operator/src/main.rs @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; use std::pin::pin; use std::time::Duration; +use aws_config::{BehaviorVersion, ConfigLoader}; use aws_sdk_ec2::types::Filter; use aws_sdk_ec2::Client as Ec2Client; use aws_sdk_servicequotas::types::ServiceQuota; @@ -59,7 +60,7 @@ async fn run() -> Result<(), Error> { let k8s_client = Client::try_default().await?; debug!("Getting ec2_client..."); - let mut config_loader = eip_operator_shared::aws_config_loader_default(); + let mut config_loader = aws_config_loader_default(); if let Ok(endpoint) = std::env::var("AWS_ENDPOINT_URL") { config_loader = config_loader.endpoint_url(endpoint); @@ -314,3 +315,10 @@ fn set_abort_on_panic() { std::process::abort(); })); } + +fn aws_config_loader_default() -> ConfigLoader { + let connector = hyper_tls::HttpsConnector::new(); + let hyper_client_builder = + aws_smithy_runtime::client::http::hyper_014::HyperClientBuilder::new().build(connector); + aws_config::defaults(BehaviorVersion::latest()).http_client(hyper_client_builder) +} diff --git a/eip_operator_shared/Cargo.toml b/eip_operator_shared/Cargo.toml index 595f477..ed9ad43 100644 --- a/eip_operator_shared/Cargo.toml +++ b/eip_operator_shared/Cargo.toml @@ -1,37 +1,33 @@ [package] name = "eip-operator-shared" -version = "0.3.0" +version = "0.4.0" edition = "2021" license = "Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -aws-config = { version = "0.101", default-features = false} -aws-sdk-ec2 = { version = "0.38", default-features = false, features = [ "rt-tokio" ] } -aws-sdk-servicequotas = { version = "0.38", default-features = false, features = [ "rt-tokio" ] } -aws-smithy-http = { version = "0.59", default-features = false, features = [ "rt-tokio" ] } -aws-smithy-runtime-api = "0.101" -aws-smithy-runtime = { version = "0.101", features = ["connector-hyper-0-14-x"] } -hyper-tls = { version = "0.5.0" } - +aws-sdk-ec2 = { version = "1.64.0", default-features = false, features = [ "rt-tokio" ] } +aws-sdk-servicequotas = { version = "1.37", default-features = false, features = [ "rt-tokio" ] } +hyper-tls = { version = "0.6.0" } futures = "0.3" -hyper = { version = "0.14.27", features = ["http2"] } +hyper = { version = "1.4.1", features = ["http2"] } +hyper-util = { version = "0.1.7", features = ["client", "client-legacy", "tokio"] } kube = { workspace = true } kube-runtime = { workspace = true } -native-tls = { version = "0.2.11", features = ["alpn", "vendored"] } -opentelemetry = { version = "0.21", features = ["trace"] } -opentelemetry_sdk = { version = "0.21", features = ["trace", "rt-tokio"] } -opentelemetry-otlp = { version = "0.14" } +native-tls = { version = "0.2.12", features = ["alpn", "vendored"] } +opentelemetry = { version = "0.24", features = ["trace"] } +opentelemetry_sdk = { version = "0.24.1", features = ["trace", "rt-tokio"] } +opentelemetry-otlp = { version = "0.17" } serde = "1" serde_json = "1" thiserror = "1" tokio-native-tls = { version = "0.3.1" } tokio = { workspace = true } -tonic = { version = "0.9.2", features = ["transport"] } +tonic = { version = "0.12.1", features = ["transport"] } tracing = "0.1" -tracing-opentelemetry = "0.22" +tracing-opentelemetry = "0.25" tracing-subscriber = { version = "0.3", features = [ "registry", "env-filter", diff --git a/eip_operator_shared/src/lib.rs b/eip_operator_shared/src/lib.rs index 3ac0daf..920538f 100644 --- a/eip_operator_shared/src/lib.rs +++ b/eip_operator_shared/src/lib.rs @@ -4,9 +4,6 @@ use std::net::AddrParseError; use std::str::FromStr; use std::time::Duration; -use aws_config::{BehaviorVersion, ConfigLoader}; -use aws_smithy_runtime::client::http::hyper_014::HyperClientBuilder; - use aws_sdk_ec2::error::SdkError; use aws_sdk_ec2::operation::allocate_address::AllocateAddressError; use aws_sdk_ec2::operation::associate_address::AssociateAddressError; @@ -17,8 +14,9 @@ use aws_sdk_ec2::operation::release_address::ReleaseAddressError; use aws_sdk_servicequotas::error::SdkError as ServiceQuotaSdkError; use aws_sdk_servicequotas::operation::get_service_quota::GetServiceQuotaError; use futures::Future; -use hyper::client::HttpConnector; use hyper_tls::HttpsConnector; +use hyper_util::client::legacy::connect::HttpConnector; +use opentelemetry::trace::TracerProvider; use opentelemetry::KeyValue; use opentelemetry_sdk::trace::{Config, Sampler}; use opentelemetry_sdk::Resource as OtelResource; @@ -247,7 +245,7 @@ where .with_channel(channel) .with_metadata(mmap); - let tracer = opentelemetry_otlp::new_pipeline() + let tracer_provider = opentelemetry_otlp::new_pipeline() .tracing() .with_exporter(otlp_exporter) .with_trace_config( @@ -259,6 +257,7 @@ where ) .install_batch(opentelemetry_sdk::runtime::Tokio) .unwrap(); + let tracer = tracer_provider.tracer_builder(service_name).build(); let otel_layer = tracing_opentelemetry::layer() .with_tracer(tracer) .with_filter(otel_targets); @@ -279,8 +278,3 @@ where }; f().await } - -pub fn aws_config_loader_default() -> ConfigLoader { - aws_config::defaults(BehaviorVersion::latest()) - .http_client(HyperClientBuilder::new().build(HttpsConnector::new())) -} From f7abdbe887654e43cebb88c7de23f219eff31151 Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Fri, 6 Sep 2024 13:33:13 -0400 Subject: [PATCH 09/16] use replace_status on set_status_detached --- eip_operator/src/controller/eip.rs | 2 +- eip_operator/src/controller/node.rs | 4 ++-- eip_operator/src/controller/pod.rs | 2 +- eip_operator/src/eip.rs | 35 +++++++++++++---------------- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/eip_operator/src/controller/eip.rs b/eip_operator/src/controller/eip.rs index 0c414d5..f597d63 100644 --- a/eip_operator/src/controller/eip.rs +++ b/eip_operator/src/controller/eip.rs @@ -127,7 +127,7 @@ impl k8s_controller::Context for Context { if let Some(association_id) = status.association_id { crate::aws::disassociate_eip(&self.ec2_client, &association_id).await?; } - crate::eip::set_status_detached(&eip_api, &eip.name_unchecked()).await?; + crate::eip::set_status_detached(&eip_api, &eip).await?; } } diff --git a/eip_operator/src/controller/node.rs b/eip_operator/src/controller/node.rs index 0f199c9..b21b0a4 100644 --- a/eip_operator/src/controller/node.rs +++ b/eip_operator/src/controller/node.rs @@ -96,7 +96,7 @@ impl k8s_controller::Context for Context { &node_eip_name ); crate::aws::disassociate_eip(&self.ec2_client, &node_eip_name).await?; - crate::eip::set_status_detached(&eip_api, &node_eip_name).await?; + crate::eip::set_status_detached(&eip_api, &eip).await?; return Ok(None); } @@ -197,7 +197,7 @@ impl k8s_controller::Context for Context { crate::aws::disassociate_eip(&self.ec2_client, &association_id).await?; } } - crate::eip::set_status_detached(&eip_api, &eip.name_unchecked()).await?; + crate::eip::set_status_detached(&eip_api, &eip).await?; } Ok(None) } diff --git a/eip_operator/src/controller/pod.rs b/eip_operator/src/controller/pod.rs index f8c87a8..15c52cf 100644 --- a/eip_operator/src/controller/pod.rs +++ b/eip_operator/src/controller/pod.rs @@ -141,7 +141,7 @@ impl k8s_controller::Context for Context { crate::aws::disassociate_eip(&self.ec2_client, &association_id).await?; } } - crate::eip::set_status_detached(&eip_api, &eip.name_unchecked()).await?; + crate::eip::set_status_detached(&eip_api, &eip).await?; } if should_autocreate_eip(pod) { event!(Level::INFO, should_autocreate_eip = true); diff --git a/eip_operator/src/eip.rs b/eip_operator/src/eip.rs index 3982df9..be3ec1c 100644 --- a/eip_operator/src/eip.rs +++ b/eip_operator/src/eip.rs @@ -421,30 +421,27 @@ pub(crate) async fn set_status_should_attach( serde_json::to_vec(&eip.clone())?, ) .await?; - // let result = api.patch_status(name, ¶ms, &patch).await; event!(Level::INFO, "Done updating status before attaching EIP."); Ok(result) } /// Unsets the eni and privateIpAddress fields in the Eip status. #[instrument(skip(api), err)] -pub(crate) async fn set_status_detached(api: &Api, name: &str) -> Result { +pub(crate) async fn set_status_detached(api: &Api, eip: &Eip) -> Result { event!(Level::INFO, "Updating status for detached EIP."); - let patch = serde_json::json!({ - "apiVersion": Eip::version(), - "kind": "Eip", - "status": { - "eni": None::, - "privateIpAddress": None::, - "resourceId": None::, - "associationId": None::, - } - }); - let patch = Patch::Merge(&patch); - let params = PatchParams::default(); - let result = api.patch_status(name, ¶ms, &patch).await; - if result.is_ok() { - event!(Level::INFO, "Done updating status for detached EIP."); - } - result + let mut eip = eip.clone(); + let status = eip.status.as_mut().ok_or(Error::MissingEipStatus)?; + status.association_id = None::; + status.eni = None::; + status.private_ip_address = None::; + status.resource_id = None::; + let result = api + .replace_status( + &eip.name_unchecked(), + &PostParams::default(), + serde_json::to_vec(&eip.clone())?, + ) + .await?; + event!(Level::INFO, "Done updating status for detached EIP."); + Ok(result) } From b2ea11948aee36c7cb5ff3ab6c92b3d9871dd31e Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Fri, 6 Sep 2024 15:02:59 -0400 Subject: [PATCH 10/16] lint fixes and bump pr workflow to rust 1.78 --- .github/workflows/pr.yml | 10 +++++----- eip_operator/src/controller/eip.rs | 2 +- eip_operator/src/controller/node.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 6fff6a1..f4b0b97 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -12,28 +12,28 @@ jobs: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.74.0 + toolchain: 1.78.0 components: rustfmt, clippy - uses: Swatinem/rust-cache@v1.3.0 - uses: actions-rs/cargo@v1 with: command: fmt - toolchain: 1.74.0 + toolchain: 1.78.0 args: -- --check - uses: actions-rs/cargo@v1 with: command: clippy - toolchain: 1.74.0 + toolchain: 1.78.0 args: -- --deny warnings - uses: actions-rs/cargo@v1 with: command: install - toolchain: 1.74.0 + toolchain: 1.78.0 args: --locked cargo-deny - uses: actions-rs/cargo@v1 with: command: deny - toolchain: 1.74.0 + toolchain: 1.78.0 args: check test: runs-on: ubuntu-20.04 diff --git a/eip_operator/src/controller/eip.rs b/eip_operator/src/controller/eip.rs index f597d63..167c9f6 100644 --- a/eip_operator/src/controller/eip.rs +++ b/eip_operator/src/controller/eip.rs @@ -127,7 +127,7 @@ impl k8s_controller::Context for Context { if let Some(association_id) = status.association_id { crate::aws::disassociate_eip(&self.ec2_client, &association_id).await?; } - crate::eip::set_status_detached(&eip_api, &eip).await?; + crate::eip::set_status_detached(&eip_api, eip).await?; } } diff --git a/eip_operator/src/controller/node.rs b/eip_operator/src/controller/node.rs index b21b0a4..1a3d22e 100644 --- a/eip_operator/src/controller/node.rs +++ b/eip_operator/src/controller/node.rs @@ -96,7 +96,7 @@ impl k8s_controller::Context for Context { &node_eip_name ); crate::aws::disassociate_eip(&self.ec2_client, &node_eip_name).await?; - crate::eip::set_status_detached(&eip_api, &eip).await?; + crate::eip::set_status_detached(&eip_api, eip).await?; return Ok(None); } From b3ee8f27adec86800ad874d24fcc525668edc833 Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Fri, 6 Sep 2024 16:30:50 -0400 Subject: [PATCH 11/16] address remaining clippy warnings --- eip_operator/src/controller/eip.rs | 2 ++ eip_operator/src/controller/node.rs | 2 ++ eip_operator/src/controller/pod.rs | 2 ++ eip_operator/src/eip.rs | 3 ++- 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/eip_operator/src/controller/eip.rs b/eip_operator/src/controller/eip.rs index 167c9f6..9e96762 100644 --- a/eip_operator/src/controller/eip.rs +++ b/eip_operator/src/controller/eip.rs @@ -38,6 +38,7 @@ impl k8s_controller::Context for Context { const FINALIZER_NAME: &'static str = "eip.materialize.cloud/destroy"; + #[allow(clippy::blocks_in_conditions)] #[instrument(skip(self, client, eip), err)] async fn apply( &self, @@ -170,6 +171,7 @@ impl k8s_controller::Context for Context { Ok(None) } + #[allow(clippy::blocks_in_conditions)] #[instrument(skip(self, _client, eip), err)] async fn cleanup( &self, diff --git a/eip_operator/src/controller/node.rs b/eip_operator/src/controller/node.rs index 1a3d22e..af6c687 100644 --- a/eip_operator/src/controller/node.rs +++ b/eip_operator/src/controller/node.rs @@ -33,6 +33,7 @@ impl k8s_controller::Context for Context { const FINALIZER_NAME: &'static str = "eip.materialize.cloud/disassociate_node"; + #[allow(clippy::blocks_in_conditions)] #[instrument(skip(self, client, node), err)] async fn apply( &self, @@ -166,6 +167,7 @@ impl k8s_controller::Context for Context { Ok(None) } + #[allow(clippy::blocks_in_conditions)] #[instrument(skip(self, client, node), err)] async fn cleanup( &self, diff --git a/eip_operator/src/controller/pod.rs b/eip_operator/src/controller/pod.rs index 15c52cf..f0dcc7a 100644 --- a/eip_operator/src/controller/pod.rs +++ b/eip_operator/src/controller/pod.rs @@ -29,6 +29,7 @@ impl k8s_controller::Context for Context { const FINALIZER_NAME: &'static str = "eip.materialize.cloud/disassociate"; + #[allow(clippy::blocks_in_conditions)] #[instrument(skip(self, client, pod), err)] async fn apply( &self, @@ -112,6 +113,7 @@ impl k8s_controller::Context for Context { Ok(None) } + #[allow(clippy::blocks_in_conditions)] #[instrument(skip(self, client, pod), err)] async fn cleanup( &self, diff --git a/eip_operator/src/eip.rs b/eip_operator/src/eip.rs index be3ec1c..3e2d9c4 100644 --- a/eip_operator/src/eip.rs +++ b/eip_operator/src/eip.rs @@ -237,7 +237,8 @@ pub mod v2 { }, }, ); - eip.meta_mut().resource_version = eip_v1.metadata.resource_version.clone(); + let resource_version = eip_v1.metadata.resource_version.clone(); + eip.meta_mut().resource_version = resource_version; Ok(eip) } else { Err(None) From f3171ccff64f2a3ecbdb4a592089a8d8aa106e79 Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Mon, 9 Sep 2024 13:56:42 -0400 Subject: [PATCH 12/16] adjust node controller: * cleanup egress_ready label * only detach don't disassociate EIP on unresponsive nodes --- eip_operator/src/controller/node.rs | 19 +++++++++++-------- eip_operator/src/egress.rs | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/eip_operator/src/controller/node.rs b/eip_operator/src/controller/node.rs index af6c687..b112c92 100644 --- a/eip_operator/src/controller/node.rs +++ b/eip_operator/src/controller/node.rs @@ -78,11 +78,13 @@ impl k8s_controller::Context for Context { .map(|condition| condition.status.clone()) .ok_or(Error::MissingNodeReadyCondition)?; match node_condition_ready_status.as_str() { - // Remove the EIP from nodes with an Unknown or NotReady ready status. + // Skip Ready Status True and continue with EIP node association. + "True" => {} + // Detach the EIP from a node with an Unknown or False ready status. // An Unknown ready status could mean the node is unresponsive or experienced a hardware failure. // A NotReady ready status could mean the node is experiencing a network issue. - "Unknown" | "False" => { - // Skip disassociation if no EIP is not associated with the node. + _ => { + // Skip detachment if no EIP is associated with this node. let node_eip = matched_eips.iter().find(|eip| { eip.status.as_ref().is_some_and(|s| { s.resource_id.is_some() @@ -90,20 +92,16 @@ impl k8s_controller::Context for Context { }) }); if let Some(eip) = node_eip { - let node_eip_name = eip.name_unchecked(); warn!( "Node {} is in an unknown state, disassociating EIP {}", &name.clone(), - &node_eip_name + &eip.name_unchecked() ); - crate::aws::disassociate_eip(&self.ec2_client, &node_eip_name).await?; crate::eip::set_status_detached(&eip_api, eip).await?; return Ok(None); } } - // Skip Ready Status True and continue with EIP node association. - _ => {} } let eip = matched_eips.into_iter().find(|eip| { @@ -201,6 +199,11 @@ impl k8s_controller::Context for Context { } crate::eip::set_status_detached(&eip_api, &eip).await?; } + + let node_api = Api::::all(client); + crate::egress::add_gateway_status_label(&node_api, node.name_unchecked().as_str(), "false") + .await?; + Ok(None) } } diff --git a/eip_operator/src/egress.rs b/eip_operator/src/egress.rs index 109160b..970d9df 100644 --- a/eip_operator/src/egress.rs +++ b/eip_operator/src/egress.rs @@ -10,7 +10,7 @@ use crate::EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE; /// Applies label specifying the ready status of the egress gateway node. #[instrument(skip(api), err)] -async fn add_gateway_status_label( +pub(crate) async fn add_gateway_status_label( api: &Api, name: &str, status: &str, From ea2d3cb324e0e911fc3e09e9772aded61fa2a3ce Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Mon, 9 Sep 2024 18:09:20 -0400 Subject: [PATCH 13/16] refactor egress labeler --- eip_operator/src/controller/node.rs | 2 +- eip_operator/src/egress.rs | 121 +++++++++------------------- eip_operator/src/main.rs | 8 +- 3 files changed, 42 insertions(+), 89 deletions(-) diff --git a/eip_operator/src/controller/node.rs b/eip_operator/src/controller/node.rs index b112c92..af08a44 100644 --- a/eip_operator/src/controller/node.rs +++ b/eip_operator/src/controller/node.rs @@ -93,7 +93,7 @@ impl k8s_controller::Context for Context { }); if let Some(eip) = node_eip { warn!( - "Node {} is in an unknown state, disassociating EIP {}", + "Node {} is in an unresponsive state, detaching EIP {}", &name.clone(), &eip.name_unchecked() ); diff --git a/eip_operator/src/egress.rs b/eip_operator/src/egress.rs index 970d9df..f4d627d 100644 --- a/eip_operator/src/egress.rs +++ b/eip_operator/src/egress.rs @@ -2,7 +2,7 @@ use crate::eip::v2::Eip; use crate::Error; use k8s_openapi::api::core::v1::Node; use kube::api::{Api, ListParams, Patch, PatchParams}; -use std::collections::{BTreeMap, BTreeSet}; +use kube::ResourceExt; use tracing::{info, instrument}; use crate::EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY; @@ -56,97 +56,50 @@ async fn get_egress_nodes(api: &Api) -> Result, kube::Error> { /// This controls whether traffic is allowed to egress through the node. /// Note: Egress traffic will be immediately dropped when the ready status label value is changed away from "true". #[instrument(skip(), err)] -pub(crate) async fn label_egress_nodes( - eip_api: &Api, - node_api: &Api, -) -> Result<(), Error> { +pub(crate) async fn label_egress_nodes(eip: &Eip, node_api: &Api) -> Result<(), Error> { let egress_nodes = get_egress_nodes(&Api::all(node_api.clone().into())).await?; if egress_nodes.is_empty() { info!("No egress nodes found. Skipping egress node ready status labeling."); return Ok(()); } - // Build up a list of EIP resourceId's to check EIP attachment against node names. - let eip_resource_ids: BTreeSet = eip_api - .list(&ListParams::default()) - .await? - .items - .into_iter() - .filter_map(|eip| eip.status.and_then(|s| s.resource_id)) - .collect(); - - // Build up a map of egress node names along with their - // ready status and whether or not they have an EIP attached. - let mut node_map: BTreeMap = BTreeMap::new(); - for node in egress_nodes { - if let Some(ref node_name) = node.metadata.name { - let ready_status = node - .status - .as_ref() - .and_then(|status| status.conditions.as_ref()) - .and_then(|conditions| conditions.iter().find(|c| c.type_ == "Ready")) - .map(|condition| condition.status.clone()) - .ok_or(Error::MissingNodeReadyCondition)?; - let eip_attached_status = eip_resource_ids.contains(node_name.as_str()); - node_map.insert(node_name.clone(), (ready_status, eip_attached_status)); + // Note(Evan): find nodes that match eips we're reconciling + // if eip has a resource id, see if the node with the resoruce is ready + // if no, do nothing + // if yes, mark that node as egress_ready=true, and mark all other nodes as egress_ready=false + if let Some(resource_id) = eip.status.as_ref().and_then(|s| s.resource_id.as_ref()) { + let node = egress_nodes + .iter() + .find(|node| { + node.metadata + .name + .as_ref() + .map(|n| n == resource_id) + .unwrap_or(false) + }) + .ok_or(Error::MissingNodeName)?; + let node_ready_status = node + .status + .as_ref() + .and_then(|status| status.conditions.as_ref()) + .and_then(|conditions| conditions.iter().find(|c| c.type_ == "Ready")) + .map(|condition| condition.status.clone()) + .ok_or(Error::MissingNodeReadyCondition)?; + if node_ready_status != "True" { + return Ok(()); + } else { + // mark node with EIP as ready for egress traffic + add_gateway_status_label(node_api, node.name_unchecked().as_str(), "true").await?; + // mark all other nodes as not ready for egress traffic + for other_node in egress_nodes + .iter() + .filter(|n| n.name_unchecked() != node.name_unchecked()) + { + add_gateway_status_label(node_api, other_node.name_unchecked().as_str(), "false") + .await?; + } } } - let egress_nodes_ready_with_eip: BTreeMap = node_map - .iter() - .filter(|(_, &(ref ready_status, eip_attached_status))| { - ready_status == "True" && eip_attached_status - }) - .map(|(node_name, &(ref ready_status, eip_attached_status))| { - ( - node_name.clone(), - (ready_status.clone(), eip_attached_status), - ) - }) - .collect(); - - // Wait to label egress nodes until an EIP is attached. - // Setting the ready status label to "ready" routes egress traffic through the node - // Wait to label egress nodes until they are ready and have an EIP attached. - if egress_nodes_ready_with_eip.is_empty() { - info!( - "No egress nodes found with a ready status and attached EIP. Skipping egress labeling." - ); - return Ok(()); - } - - // At least one egress node should be ready with an EIP attached in this current implementation. - // This allows the ready status label to be set and traffic to be routed through the node. - assert!(egress_nodes_ready_with_eip.len() == 1); - if let Some((first_key, _)) = egress_nodes_ready_with_eip.first_key_value() { - let node_name = first_key.clone(); - add_gateway_status_label(node_api, node_name.as_str(), "true").await?; - } - - // We immediately disassociate EIPs from egress nodes that are in an - // unresponsive state in the eip node controller. - // This allows the EIP to be re-associated with a new healthy node. - // However, unresponsive nodes may still exist with a ready-status egress label - // set to "true". This allows the old node to still serve traffic if possible until a - // new node is ready to take over. - // Clean up unresponsive egress nodes in a ready state of `Unknown` if another node is ready with an EIP. - // Egress nodes in a ready state of "False" are not re-labelled since they should be able to be cleaned - // up by normal methods. - let egress_nodes_not_ready_without_eip: BTreeMap = node_map - .iter() - .filter(|(_, &(ref ready_status, eip_attached_status))| { - (ready_status == "Unknown") && !eip_attached_status - }) - .map(|(node_name, &(ref ready_status, eip_attached_status))| { - ( - node_name.clone(), - (ready_status.clone(), eip_attached_status), - ) - }) - .collect(); - for node_name in egress_nodes_not_ready_without_eip.keys() { - add_gateway_status_label(node_api, node_name.as_str(), "false").await?; - } - Ok(()) } diff --git a/eip_operator/src/main.rs b/eip_operator/src/main.rs index f93eace..d7a66b5 100644 --- a/eip_operator/src/main.rs +++ b/eip_operator/src/main.rs @@ -167,14 +167,14 @@ async fn run() -> Result<(), Error> { task::spawn(async move { let mut watcher = pin!(kube_runtime::watcher(eip_api.clone(), watch_config)); - while let Some(eip) = watcher.try_next().await.unwrap_or_else(|e| { + while let Some(eip_event) = watcher.try_next().await.unwrap_or_else(|e| { event!(Level::ERROR, err = %e, "Error watching eips"); None }) { - if let kube_runtime::watcher::Event::Apply(_) - | kube_runtime::watcher::Event::Delete(_) = eip + if let kube_runtime::watcher::Event::Apply(eip) + | kube_runtime::watcher::Event::Delete(eip) = eip_event { - if let Err(err) = crate::egress::label_egress_nodes(&eip_api, &node_api).await { + if let Err(err) = crate::egress::label_egress_nodes(&eip, &node_api).await { event!(Level::ERROR, err = %err, "Node egress labeling reporting error"); } } From 20f85c3da6eaf93113aee740e69821a85031c56c Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Thu, 12 Sep 2024 21:19:25 -0400 Subject: [PATCH 14/16] egress improvements --- eip_operator/src/egress.rs | 44 ++++++++++++-------------------------- eip_operator/src/main.rs | 2 -- 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/eip_operator/src/egress.rs b/eip_operator/src/egress.rs index f4d627d..4758b33 100644 --- a/eip_operator/src/egress.rs +++ b/eip_operator/src/egress.rs @@ -1,13 +1,10 @@ use crate::eip::v2::Eip; use crate::Error; use k8s_openapi::api::core::v1::Node; -use kube::api::{Api, ListParams, Patch, PatchParams}; +use kube::api::{Api, Patch, PatchParams}; use kube::ResourceExt; use tracing::{info, instrument}; -use crate::EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY; -use crate::EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE; - /// Applies label specifying the ready status of the egress gateway node. #[instrument(skip(api), err)] pub(crate) async fn add_gateway_status_label( @@ -36,37 +33,20 @@ pub(crate) async fn add_gateway_status_label( api.patch(name, ¶ms, &patch).await } -/// Retrieve all egress nodes in the cluster. -async fn get_egress_nodes(api: &Api) -> Result, kube::Error> { - let params = ListParams::default().labels( - format!( - "{}={}", - EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY, EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE - ) - .as_str(), - ); - - match api.list(¶ms).await { - Ok(node_list) => Ok(node_list.items), - Err(e) => Err(e), - } -} - /// Update the ready status label on egress nodes. /// This controls whether traffic is allowed to egress through the node. /// Note: Egress traffic will be immediately dropped when the ready status label value is changed away from "true". #[instrument(skip(), err)] pub(crate) async fn label_egress_nodes(eip: &Eip, node_api: &Api) -> Result<(), Error> { - let egress_nodes = get_egress_nodes(&Api::all(node_api.clone().into())).await?; + let egress_nodes = node_api.list(&eip.get_resource_list_params()).await?.items; if egress_nodes.is_empty() { info!("No egress nodes found. Skipping egress node ready status labeling."); return Ok(()); } - // Note(Evan): find nodes that match eips we're reconciling - // if eip has a resource id, see if the node with the resoruce is ready - // if no, do nothing - // if yes, mark that node as egress_ready=true, and mark all other nodes as egress_ready=false + // If EIP being reconciled has a resource id, find it's node, and check if it's ready. + // If not ready, return early. + // If ready, mark that node as egress_ready=true, and mark all other nodes as egress_ready=false. if let Some(resource_id) = eip.status.as_ref().and_then(|s| s.resource_id.as_ref()) { let node = egress_nodes .iter() @@ -91,12 +71,16 @@ pub(crate) async fn label_egress_nodes(eip: &Eip, node_api: &Api) -> Resul // mark node with EIP as ready for egress traffic add_gateway_status_label(node_api, node.name_unchecked().as_str(), "true").await?; // mark all other nodes as not ready for egress traffic - for other_node in egress_nodes - .iter() - .filter(|n| n.name_unchecked() != node.name_unchecked()) - { - add_gateway_status_label(node_api, other_node.name_unchecked().as_str(), "false") + for other_node in &egress_nodes { + // skip the node we just set to ready + if other_node.name_unchecked() != node.name_unchecked() { + add_gateway_status_label( + node_api, + other_node.name_unchecked().as_str(), + "false", + ) .await?; + } } } } diff --git a/eip_operator/src/main.rs b/eip_operator/src/main.rs index d7a66b5..852d862 100644 --- a/eip_operator/src/main.rs +++ b/eip_operator/src/main.rs @@ -34,8 +34,6 @@ const FIELD_MANAGER: &str = "eip.materialize.cloud"; const AUTOCREATE_EIP_LABEL: &str = "eip.materialize.cloud/autocreate_eip"; const EIP_ALLOCATION_ID_ANNOTATION: &str = "eip.materialize.cloud/allocation_id"; const EXTERNAL_DNS_TARGET_ANNOTATION: &str = "external-dns.alpha.kubernetes.io/target"; -const EGRESS_GATEWAY_NODE_SELECTOR_LABEL_KEY: &str = "workload"; -const EGRESS_GATEWAY_NODE_SELECTOR_LABEL_VALUE: &str = "materialize-egress"; const EGRESS_NODE_STATUS_LABEL: &str = "egress-gateway.materialize.cloud/ready-status"; // See https://us-east-1.console.aws.amazon.com/servicequotas/home/services/ec2/quotas From 74aa125eed08faa8dc5f2d6f181696b532367835 Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Thu, 12 Sep 2024 10:33:00 -0400 Subject: [PATCH 15/16] node improvements --- README.md | 2 +- eip_operator/src/controller/node.rs | 61 +++++++++++------------------ 2 files changed, 24 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 50bd6b9..5114d01 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -⚠️⚠️ **WARNING!** ⚠️⚠️ The Materialize K8s-eip-operator will soon be archived and no longer be under active development. +⚠️⚠️ **WARNING!** ⚠️⚠️ The Materialize K8s-eip-operator will soon be archived and is no longer be under active development. # k8s-eip-operator Manage external connections to Kubernetes pods or nodes using AWS Elastic IPs (EIPs). diff --git a/eip_operator/src/controller/node.rs b/eip_operator/src/controller/node.rs index af08a44..9da394d 100644 --- a/eip_operator/src/controller/node.rs +++ b/eip_operator/src/controller/node.rs @@ -67,9 +67,13 @@ impl k8s_controller::Context for Context { return Err(Error::NoEipResourceWithThatNodeSelector); } - // Check the existing Node for EIP association and ready status. - // Node's that go in to a "NotReady" or "Unknown" state should have their EIP - // disassociated to allow a new node to spawn and use the EIP. + let eip = matched_eips.into_iter().find(|eip| { + eip.status.as_ref().is_some_and(|s| { + s.resource_id.is_none() + || s.resource_id.as_ref().map(|r| r == &name).unwrap_or(false) + }) + }); + let node_condition_ready_status = node .status .as_ref() @@ -77,39 +81,24 @@ impl k8s_controller::Context for Context { .and_then(|conditions| conditions.iter().find(|c| c.type_ == "Ready")) .map(|condition| condition.status.clone()) .ok_or(Error::MissingNodeReadyCondition)?; - match node_condition_ready_status.as_str() { - // Skip Ready Status True and continue with EIP node association. - "True" => {} - // Detach the EIP from a node with an Unknown or False ready status. - // An Unknown ready status could mean the node is unresponsive or experienced a hardware failure. - // A NotReady ready status could mean the node is experiencing a network issue. - _ => { - // Skip detachment if no EIP is associated with this node. - let node_eip = matched_eips.iter().find(|eip| { - eip.status.as_ref().is_some_and(|s| { - s.resource_id.is_some() - && s.resource_id.as_ref().map(|r| r == &name).unwrap_or(false) - }) - }); - if let Some(eip) = node_eip { - warn!( - "Node {} is in an unresponsive state, detaching EIP {}", - &name.clone(), - &eip.name_unchecked() - ); - crate::eip::set_status_detached(&eip_api, eip).await?; - return Ok(None); - } + // Check the Node's EIP claim and ready status. + // Node's with a ready status of "False" or "Unknown" should not have a claim to an EIP. + if node_condition_ready_status != "True" { + // If an EIP has a resource id label pointing to this node, remove that label releasing this nodes claim to the EIP + if let Some(eip) = eip { + warn!( + "Node {} is in an unresponsive state, setting status detached on EIP {}", + &name.clone(), + &eip.name_unchecked() + ); + crate::eip::set_status_detached(&eip_api, &eip).await?; } + + dbg!("Node {} is not ready, skipping EIP claim", &name); + return Ok(None); } - let eip = matched_eips.into_iter().find(|eip| { - eip.status.as_ref().is_some_and(|s| { - s.resource_id.is_none() - || s.resource_id.as_ref().map(|r| r == &name).unwrap_or(false) - }) - }); let Some(eip) = eip else { info!("No un-associated eips found for node {}", &name); return Ok(None); @@ -126,12 +115,8 @@ impl k8s_controller::Context for Context { let eni_id = crate::aws::get_eni_from_private_ip(&instance_description, node_ip) .ok_or(Error::NoInterfaceWithThatIp)?; - // Ensure only node's marked with ready status True are associated with an EIP. - // We don't want to associate an EIP with a node that is not ready and potentially remove it - // in the next reconciliation loop if the node is still coming online. - if (eip_description.network_interface_id != Some(eni_id.to_owned()) - || eip_description.private_ip_address != Some(node_ip.to_owned())) - && node_condition_ready_status == "True" + if eip_description.network_interface_id != Some(eni_id.to_owned()) + || eip_description.private_ip_address != Some(node_ip.to_owned()) { match crate::eip::set_status_should_attach(&eip_api, &eip, &eni_id, node_ip, &name) .await From 31221c82142462b886280446aef73f91d579ce86 Mon Sep 17 00:00:00 2001 From: Evan Harmon Date: Fri, 13 Sep 2024 17:38:31 -0400 Subject: [PATCH 16/16] fix debug call --- eip_operator/src/controller/node.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eip_operator/src/controller/node.rs b/eip_operator/src/controller/node.rs index 9da394d..abe7b51 100644 --- a/eip_operator/src/controller/node.rs +++ b/eip_operator/src/controller/node.rs @@ -5,7 +5,7 @@ use kube::api::{Api, ListParams}; use kube::error::ErrorResponse; use kube::{Client, ResourceExt}; use kube_runtime::controller::Action; -use tracing::{event, info, instrument, warn, Level}; +use tracing::{debug, event, info, instrument, warn, Level}; use eip_operator_shared::Error; @@ -95,7 +95,7 @@ impl k8s_controller::Context for Context { crate::eip::set_status_detached(&eip_api, &eip).await?; } - dbg!("Node {} is not ready, skipping EIP claim", &name); + debug!("Node {} is not ready, skipping EIP claim", &name); return Ok(None); }