kdash 0.0.3

A fast and simple dashboard for Kubernetes
use super::utils::{self, UNKNOWN};
use k8s_openapi::{
  api::core::v1::{Service, ServicePort},
  chrono::Utc,
};

#[derive(Clone)]
pub struct KubeSvc {
  pub namespace: String,
  pub name: String,
  pub type_: String,
  pub cluster_ip: String,
  pub external_ip: String,
  pub ports: String,
  pub age: String,
}

impl KubeSvc {
  pub fn from_api(service: &Service) -> Self {
    let (type_, cluster_ip, external_ip, ports) = match &service.spec {
      Some(spec) => {
        let type_ = match &spec.type_ {
          Some(type_) => type_.clone(),
          _ => UNKNOWN.into(),
        };

        let external_ips = match type_.as_str() {
          "ClusterIP" | "NodePort" => spec.external_ips.clone(),
          "LoadBalancer" => get_lb_ext_ips(service, spec.external_ips.clone()),
          "ExternalName" => Some(vec![spec.external_name.clone().unwrap_or_default()]),
          _ => None,
        }
        .unwrap_or_else(|| {
          if type_ == "LoadBalancer" {
            vec!["<pending>".into()]
          } else {
            vec![String::default()]
          }
        });

        (
          type_,
          spec.cluster_ip.as_ref().unwrap_or(&"None".into()).clone(),
          external_ips.join(","),
          get_ports(spec.ports.clone()).join(" "),
        )
      }
      _ => (
        UNKNOWN.into(),
        String::default(),
        String::default(),
        String::default(),
      ),
    };

    KubeSvc {
      name: service.metadata.name.clone().unwrap_or_default(),
      type_,
      namespace: service.metadata.namespace.clone().unwrap_or_default(),
      cluster_ip,
      external_ip,
      ports,
      age: utils::to_age(service.metadata.creation_timestamp.as_ref(), Utc::now()),
    }
  }
}

fn get_ports(sports: Option<Vec<ServicePort>>) -> Vec<String> {
  match sports {
    Some(ports) => ports
      .iter()
      .map(|s| {
        let mut port = String::new();
        if s.name.is_some() {
          port = format!("{}:", s.name.clone().unwrap());
        }
        port = format!("{}{}{}", port, s.port, s.node_port.unwrap_or(0));
        if s.protocol.is_some() && s.protocol.clone().unwrap() == "TCP" {
          port = format!("{}/{}", port, s.protocol.clone().unwrap());
        }
        port
      })
      .collect(),
    None => vec![],
  }
}

fn get_lb_ext_ips(service: &Service, external_ips: Option<Vec<String>>) -> Option<Vec<String>> {
  let mut lb_ips = match &service.status {
    Some(ss) => match &ss.load_balancer {
      Some(lb) => {
        let ing = &lb.ingress;
        ing
          .clone()
          .unwrap_or_default()
          .iter()
          .map(|it| {
            if it.ip.is_some() {
              it.ip.clone().unwrap_or_default()
            } else if it.hostname.is_some() {
              it.hostname.clone().unwrap_or_default()
            } else {
              String::default()
            }
          })
          .collect::<Vec<String>>()
      }
      None => vec![],
    },
    None => vec![],
  };
  if external_ips.is_some() && !lb_ips.is_empty() {
    lb_ips.extend(external_ips.unwrap_or_default());
    Some(lb_ips)
  } else if !lb_ips.is_empty() {
    Some(lb_ips)
  } else {
    None
  }
}