use clap::{Arg, Command as ClapCommand};
use k8s_openapi::{
apimachinery::pkg::apis::meta::v1::ObjectMeta, http, ListOptional, ListResponse,
ListableResource, Metadata, NamespaceResourceScope, RequestError, Resource, Response,
ResponseBody, ResponseError,
};
use serde_json::{value::from_value, Error, Value};
use crate::{
command::command_def::{exec_match, show_arg, sort_arg, start_clap, Cmd},
command::{get_list_request_for_url, get_read_request_for_url, run_list_command, Extractor},
completer,
env::Env,
kobj::{KObj, ObjType},
output::ClickWriter,
table::CellSpec,
values::val_num,
};
use std::cell::RefCell;
use std::collections::HashMap;
use std::io::Write;
lazy_static! {
static ref RO_EXTRACTORS: HashMap<String, Extractor<RolloutValue>> = {
let mut m: HashMap<String, Extractor<RolloutValue>> = HashMap::new();
m.insert("Current".to_owned(), ro_current);
m.insert("Desired".to_owned(), ro_desired);
m.insert("Up To Date".to_owned(), ro_uptodate);
m.insert("Available".to_owned(), ro_available);
m
};
}
const COL_MAP: &[(&str, &str)] = &[
("name", "Name"),
("desired", "Desired"),
("current", "Current"),
("uptodate", "Up To Date"),
("available", "Available"),
("age", "Age"),
];
const COL_FLAGS: &[&str] = &{ extract_first!(COL_MAP) };
const EXTRA_COL_MAP: &[(&str, &str)] = &[("labels", "Labels"), ("namespace", "Namespace")];
const EXTRA_COL_FLAGS: &[&str] = &{ extract_first!(EXTRA_COL_MAP) };
fn ro_to_kobj(rollout: &RolloutValue) -> KObj {
let meta = &rollout.metadata;
KObj {
name: meta.name.clone().unwrap_or_else(|| "<Unknown>".into()),
namespace: meta.namespace.clone(),
typ: ObjType::Rollout,
}
}
fn ro_current(rollout: &RolloutValue) -> Option<CellSpec<'_>> {
Some(val_num("/status/replicas", &rollout.value, "0").into())
}
fn ro_desired(rollout: &RolloutValue) -> Option<CellSpec<'_>> {
Some(val_num("/spec/replicas", &rollout.value, "0").into())
}
fn ro_uptodate(rollout: &RolloutValue) -> Option<CellSpec<'_>> {
Some(val_num("/status/updatedReplicas", &rollout.value, "0").into())
}
fn ro_available(rollout: &RolloutValue) -> Option<CellSpec<'_>> {
Some(val_num("/status/availableReplicas", &rollout.value, "0").into())
}
list_command!(
Rollouts,
"rollouts",
"Get argo rollouts (in current namespace if set)",
super::COL_FLAGS,
super::EXTRA_COL_FLAGS,
|clap: ClapCommand<'static>| clap
.arg(
Arg::new("labels")
.short('L')
.long("labels")
.help("Show statefulsets labels (deprecated, use --show labels)")
.takes_value(false)
)
.arg(
Arg::new("regex")
.short('r')
.long("regex")
.help("Filter statefulsets by the specified regex")
.takes_value(true)
)
.arg(show_arg(EXTRA_COL_FLAGS, true))
.arg(sort_arg(COL_FLAGS, Some(EXTRA_COL_FLAGS)))
.arg(
Arg::new("reverse")
.short('R')
.long("reverse")
.help("Reverse the order of the returned list")
.takes_value(false),
),
vec!["rollouts"],
noop_complete!(),
[].into_iter(),
|matches, env, writer| {
let (request, _response_body) = match &env.namespace {
Some(ns) => RolloutValue::list_namespaced_rollout(ns, Default::default())?,
None => RolloutValue::list_rollout_for_all_namespaces(Default::default())?,
};
let cols: Vec<&str> = COL_MAP.iter().map(|(_, col)| *col).collect();
run_list_command(
matches,
env,
writer,
cols,
request,
COL_MAP,
Some(EXTRA_COL_MAP),
Some(&RO_EXTRACTORS),
ro_to_kobj,
)
}
);
#[derive(Debug)]
pub struct RolloutValue {
pub metadata: ObjectMeta,
value: Value,
}
impl ListableResource for RolloutValue {
const LIST_KIND: &'static str = "RolloutList";
}
impl<'de> serde::Deserialize<'de> for RolloutValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = Value::deserialize(deserializer)?;
let metavalue = value.pointer("/metadata").unwrap();
let metadata = from_value(metavalue.clone()).unwrap();
Ok(RolloutValue { value, metadata })
}
}
impl serde::Serialize for RolloutValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.value.serialize(serializer)
}
}
impl Metadata for RolloutValue {
type Ty = ObjectMeta;
fn metadata(&self) -> &<Self as Metadata>::Ty {
&self.metadata
}
fn metadata_mut(&mut self) -> &mut <Self as Metadata>::Ty {
&mut self.metadata
}
}
impl Resource for RolloutValue {
const API_VERSION: &'static str = "argoproj.io/v1alpha1";
const GROUP: &'static str = "";
const KIND: &'static str = "Rollout";
const VERSION: &'static str = "v1alpha1";
const URL_PATH_SEGMENT: &'static str = "rollouts";
type Scope = NamespaceResourceScope;
}
#[derive(Debug)]
pub enum ReadNamespacedRolloutValueResponse {
Ok(Box<RolloutValue>),
Other(Result<Option<Value>, Error>),
}
impl Response for ReadNamespacedRolloutValueResponse {
fn try_from_parts(
status_code: http::StatusCode,
buf: &[u8],
) -> Result<(Self, usize), ResponseError> {
match status_code {
http::StatusCode::OK => {
let result = match serde_json::from_slice(buf) {
Ok(value) => value,
Err(err) if err.is_eof() => return Err(ResponseError::NeedMoreData),
Err(err) => return Err(ResponseError::Json(err)),
};
Ok((ReadNamespacedRolloutValueResponse::Ok(result), buf.len()))
}
_ => {
let (result, read) = if buf.is_empty() {
(Ok(None), 0)
} else {
match serde_json::from_slice(buf) {
Ok(value) => (Ok(Some(value)), buf.len()),
Err(err) if err.is_eof() => return Err(ResponseError::NeedMoreData),
Err(err) => (Err(err), 0),
}
};
Ok((ReadNamespacedRolloutValueResponse::Other(result), read))
}
}
}
}
impl RolloutValue {
#[allow(clippy::type_complexity)] pub fn list_namespaced_rollout(
namespace: &str,
optional: ListOptional<'_>,
) -> Result<
(
http::Request<Vec<u8>>,
fn(http::StatusCode) -> ResponseBody<ListResponse<Self>>,
),
RequestError,
> {
let url = format!(
"/apis/argoproj.io/v1alpha1/namespaces/{}/rollouts",
namespace
);
get_list_request_for_url(url, optional)
}
#[allow(clippy::type_complexity)] pub fn list_rollout_for_all_namespaces(
optional: ListOptional<'_>,
) -> Result<
(
http::Request<Vec<u8>>,
fn(http::StatusCode) -> ResponseBody<ListResponse<Self>>,
),
RequestError,
> {
let url = "/apis/argoproj.io/v1alpha1/rollouts".to_string();
get_list_request_for_url(url, optional)
}
#[allow(clippy::type_complexity)] pub fn read_namespaced_rollout(
name: &str,
namespace: &str,
_optional: ListOptional<'_>,
) -> Result<
(
http::Request<Vec<u8>>,
fn(_: http::StatusCode) -> ResponseBody<ReadNamespacedRolloutValueResponse>,
),
RequestError,
> {
let url = format!(
"/apis/argoproj.io/v1alpha1/namespaces/{}/rollouts/{}",
namespace, name
);
get_read_request_for_url(url)
}
}