use std::collections::HashMap;
use std::fmt::{self, Display};
use std::fmt::Debug;
use std::marker::PhantomData;
use serde::de::{DeserializeOwned, Deserializer};
use serde::Deserialize;
use serde::Serialize;
use crate::Spec;
pub const DEFAULT_NS: &str = "default";
pub const TYPE_OPAQUE: &str = "Opaque";
pub trait K8Meta {
fn name(&self) -> &str;
fn namespace(&self) -> &str;
}
pub trait LabelProvider: Sized {
fn set_label_map(self, labels: HashMap<String, String>) -> Self;
fn set_labels<T: ToString>(self, labels: Vec<(T, T)>) -> Self {
let mut label_map = HashMap::new();
for (key, value) in labels {
label_map.insert(key.to_string(), value.to_string());
}
self.set_label_map(label_map)
}
}
#[derive(Deserialize, Serialize, Eq, PartialEq, Debug, Default, Clone)]
#[serde(rename_all = "camelCase", default)]
pub struct ObjectMeta {
pub name: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub namespace: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub uid: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub creation_timestamp: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub generation: Option<i32>,
#[serde(skip_serializing_if = "String::is_empty")]
#[serde(default)]
pub resource_version: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub cluster_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deletion_timestamp: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deletion_grace_period_seconds: Option<u32>,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub labels: HashMap<String, String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub owner_references: Vec<OwnerReferences>,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub annotations: HashMap<String, String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub finalizers: Vec<String>,
}
impl LabelProvider for ObjectMeta {
fn set_label_map(mut self, labels: HashMap<String, String>) -> Self {
self.labels = labels;
self
}
}
impl K8Meta for ObjectMeta {
fn name(&self) -> &str {
&self.name
}
fn namespace(&self) -> &str {
&self.namespace
}
}
impl ObjectMeta {
pub fn new<S>(name: S, name_space: S) -> Self
where
S: Into<String>,
{
Self {
name: name.into(),
namespace: name_space.into(),
..Default::default()
}
}
pub fn set_labels<T: Into<String>>(mut self, labels: Vec<(T, T)>) -> Self {
let mut label_map = HashMap::new();
for (key, value) in labels {
label_map.insert(key.into(), value.into());
}
self.labels = label_map;
self
}
pub fn named<S>(name: S) -> Self
where
S: Into<String>,
{
Self {
name: name.into(),
..Default::default()
}
}
pub fn make_owner_reference<S: Spec>(&self) -> OwnerReferences {
OwnerReferences {
api_version: S::api_version(),
kind: S::kind(),
name: self.name.clone(),
uid: self.uid.clone(),
..Default::default()
}
}
pub fn namespace(&self) -> &str {
&self.namespace
}
pub fn make_child_input_metadata<S: Spec>(&self, childname: String) -> InputObjectMeta {
let owner_references: Vec<OwnerReferences> = vec![self.make_owner_reference::<S>()];
InputObjectMeta {
name: childname,
namespace: self.namespace().to_owned(),
owner_references,
..Default::default()
}
}
pub fn as_input(&self) -> InputObjectMeta {
InputObjectMeta {
name: self.name.clone(),
namespace: self.namespace.clone(),
owner_references: self.owner_references.clone(),
..Default::default()
}
}
pub fn as_item(&self) -> ItemMeta {
ItemMeta {
name: self.name.clone(),
namespace: self.namespace.clone(),
}
}
pub fn as_update(&self) -> UpdateItemMeta {
UpdateItemMeta {
name: self.name.clone(),
namespace: self.namespace.clone(),
resource_version: self.resource_version.clone(),
annotations: self.annotations.clone(),
owner_references: self.owner_references.clone(),
finalizers: self.finalizers.clone(),
labels: self.labels.clone(),
}
}
}
#[derive(Deserialize, Serialize, Debug, Default, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InputObjectMeta {
pub name: String,
pub labels: HashMap<String, String>,
pub namespace: String,
pub owner_references: Vec<OwnerReferences>,
pub finalizers: Vec<String>,
pub annotations: HashMap<String, String>,
}
impl LabelProvider for InputObjectMeta {
fn set_label_map(mut self, labels: HashMap<String, String>) -> Self {
self.labels = labels;
self
}
}
impl fmt::Display for InputObjectMeta {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}:{}", self.name, self.namespace)
}
}
impl K8Meta for InputObjectMeta {
fn name(&self) -> &str {
&self.name
}
fn namespace(&self) -> &str {
&self.namespace
}
}
impl InputObjectMeta {
pub fn named<S: Into<String>>(name: S, namespace: S) -> Self {
InputObjectMeta {
name: name.into(),
namespace: namespace.into(),
..Default::default()
}
}
}
impl From<ObjectMeta> for InputObjectMeta {
fn from(meta: ObjectMeta) -> Self {
Self {
name: meta.name,
namespace: meta.namespace,
..Default::default()
}
}
}
#[derive(Deserialize, Serialize, Debug, Default, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ItemMeta {
pub name: String,
pub namespace: String,
}
impl From<ObjectMeta> for ItemMeta {
fn from(meta: ObjectMeta) -> Self {
Self {
name: meta.name,
namespace: meta.namespace,
}
}
}
#[derive(Deserialize, Serialize, Debug, Default, Clone)]
#[serde(rename_all = "camelCase")]
pub struct UpdateItemMeta {
pub name: String,
pub namespace: String,
pub labels: HashMap<String, String>,
pub resource_version: String,
pub annotations: HashMap<String, String>,
pub owner_references: Vec<OwnerReferences>,
pub finalizers: Vec<String>,
}
impl From<ObjectMeta> for UpdateItemMeta {
fn from(meta: ObjectMeta) -> Self {
Self {
name: meta.name,
labels: meta.labels,
namespace: meta.namespace,
resource_version: meta.resource_version,
annotations: meta.annotations,
owner_references: meta.owner_references,
finalizers: meta.finalizers,
}
}
}
impl K8Meta for UpdateItemMeta {
fn name(&self) -> &str {
&self.name
}
fn namespace(&self) -> &str {
&self.namespace
}
}
#[derive(Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OwnerReferences {
pub api_version: String,
#[serde(default)]
pub block_owner_deletion: bool,
pub controller: Option<bool>,
pub kind: String,
pub name: String,
pub uid: String,
}
impl Default for OwnerReferences {
fn default() -> Self {
Self {
api_version: "v1".to_owned(),
block_owner_deletion: false,
controller: None,
kind: "".to_owned(),
uid: "".to_owned(),
name: "".to_owned(),
}
}
}
#[derive(Debug, Clone)]
pub enum DeleteStatus<S>
where
S: Spec,
{
Deleted(MetaStatus),
ForegroundDelete(K8Obj<S>),
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct MetaStatus {
pub api_version: String,
pub code: Option<u16>,
pub details: Option<StatusDetails>,
pub kind: String,
pub message: Option<String>,
pub reason: Option<String>,
pub status: StatusEnum,
}
impl Display for MetaStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.status)?;
if let Some(ref code) = self.code {
write!(f, " ({})", code)?;
}
if let Some(ref message) = self.message {
write!(f, ":{}.", message)?;
}
Ok(())
}
}
impl std::error::Error for MetaStatus {}
#[allow(clippy::upper_case_acronyms)]
#[derive(Deserialize, Debug, Eq, PartialEq, Clone)]
pub enum StatusEnum {
#[serde(rename = "Success")]
SUCCESS,
#[serde(rename = "Failure")]
FAILURE,
}
impl Display for StatusEnum {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::SUCCESS => write!(f, "Success"),
Self::FAILURE => write!(f, "Failure"),
}
}
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct StatusDetails {
pub name: String,
pub group: Option<String>,
pub kind: String,
pub uid: Option<String>,
}
#[derive(Deserialize, Serialize, Debug, Default, Clone)]
#[serde(rename_all = "camelCase")]
#[serde(bound(serialize = "S: Serialize"))]
#[serde(bound(deserialize = "S: DeserializeOwned"))]
pub struct K8Obj<S>
where
S: Spec,
{
#[serde(default = "S::api_version")]
pub api_version: String,
#[serde(default = "S::kind")]
pub kind: String,
#[serde(default)]
pub metadata: ObjectMeta,
#[serde(default)]
pub spec: S,
#[serde(flatten)]
pub header: S::Header,
#[serde(default)]
pub status: S::Status,
}
impl<S> K8Obj<S>
where
S: Spec,
{
#[allow(dead_code)]
pub fn new<N>(name: N, spec: S) -> Self
where
N: Into<String>,
{
Self {
api_version: S::api_version(),
kind: S::kind(),
metadata: ObjectMeta::named(name),
spec,
..Default::default()
}
}
#[allow(dead_code)]
pub fn set_status(mut self, status: S::Status) -> Self {
self.status = status;
self
}
pub fn as_status_update(&self, status: S::Status) -> UpdateK8ObjStatus<S> {
UpdateK8ObjStatus {
api_version: S::api_version(),
kind: S::kind(),
metadata: self.metadata.as_update(),
status,
..Default::default()
}
}
}
impl<S> K8Obj<S>
where
S: Spec,
{
pub fn as_input(&self) -> InputK8Obj<S> {
K8SpecObj {
api_version: self.api_version.clone(),
kind: self.kind.clone(),
metadata: self.metadata.as_input(),
spec: self.spec.clone(),
..Default::default()
}
}
pub fn as_update(&self) -> K8SpecObj<S, UpdateItemMeta> {
K8SpecObj {
api_version: self.api_version.clone(),
kind: self.kind.clone(),
metadata: self.metadata.as_update(),
spec: self.spec.clone(),
..Default::default()
} as K8SpecObj<S, UpdateItemMeta>
}
}
#[derive(Deserialize, Serialize, Debug, Default, Clone)]
#[serde(rename_all = "camelCase")]
#[serde(bound(serialize = "S: Serialize, M: Serialize"))]
#[serde(bound(deserialize = "S: DeserializeOwned, M: DeserializeOwned"))]
pub struct K8SpecObj<S, M>
where
S: Spec,
{
pub api_version: String,
pub kind: String,
pub metadata: M,
pub spec: S,
#[serde(flatten)]
pub header: S::Header,
}
impl<S, M> K8SpecObj<S, M>
where
S: Spec,
{
pub fn new(spec: S, metadata: M) -> Self
where
M: Default,
{
Self {
api_version: S::api_version(),
kind: S::kind(),
metadata,
spec,
..Default::default()
}
}
}
pub type InputK8Obj<S> = K8SpecObj<S, InputObjectMeta>;
#[deprecated(note = "use UpdatedK8Obj instead")]
pub type UpdateK8Obj<S> = K8SpecObj<S, ItemMeta>;
pub type UpdatedK8Obj<S> = K8SpecObj<S, UpdateItemMeta>;
#[derive(Deserialize, Serialize, Debug, Default, Clone)]
#[serde(rename_all = "camelCase")]
pub struct UpdateK8ObjStatus<S>
where
S: Spec,
{
pub api_version: String,
pub kind: String,
pub metadata: UpdateItemMeta,
pub status: S::Status,
pub data: PhantomData<S>,
}
impl<S> UpdateK8ObjStatus<S>
where
S: Spec,
{
pub fn new(status: S::Status, metadata: UpdateItemMeta) -> Self {
Self {
api_version: S::api_version(),
kind: S::kind(),
metadata,
status,
..Default::default()
}
}
}
#[allow(deprecated)]
impl<S> From<UpdateK8Obj<S>> for InputK8Obj<S>
where
S: Spec,
{
fn from(update: UpdateK8Obj<S>) -> Self {
Self {
api_version: update.api_version,
kind: update.kind,
metadata: update.metadata.into(),
spec: update.spec,
..Default::default()
}
}
}
impl From<ItemMeta> for InputObjectMeta {
fn from(update: ItemMeta) -> Self {
Self {
name: update.name,
namespace: update.namespace,
..Default::default()
}
}
}
#[derive(Deserialize, Serialize, Debug, Default, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase", default)]
pub struct TemplateMeta {
pub name: Option<String>,
pub creation_timestamp: Option<String>,
pub labels: HashMap<String, String>,
pub annotations: HashMap<String, String>,
}
impl LabelProvider for TemplateMeta {
fn set_label_map(mut self, labels: HashMap<String, String>) -> Self {
self.labels = labels;
self
}
}
impl TemplateMeta {
pub fn named<S>(name: S) -> Self
where
S: Into<String>,
{
Self {
name: Some(name.into()),
..Default::default()
}
}
}
#[derive(Deserialize, Serialize, Debug, Default, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TemplateSpec<S> {
pub metadata: Option<TemplateMeta>,
pub spec: S,
}
impl<S> TemplateSpec<S> {
pub fn new(spec: S) -> Self {
TemplateSpec {
metadata: None,
spec,
}
}
}
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[serde(bound(serialize = "K8Obj<S>: Serialize"))]
#[serde(bound(deserialize = "K8Obj<S>: DeserializeOwned"))]
pub struct K8List<S>
where
S: Spec,
{
pub api_version: String,
pub kind: String,
pub metadata: ListMetadata,
pub items: Vec<K8Obj<S>>,
}
impl<S> K8List<S>
where
S: Spec,
{
#[allow(dead_code)]
pub fn new() -> Self {
K8List {
api_version: S::api_version(),
items: vec![],
kind: S::kind(),
metadata: ListMetadata {
_continue: None,
resource_version: S::api_version(),
},
}
}
}
impl<S> Default for K8List<S>
where
S: Spec,
{
fn default() -> Self {
Self::new()
}
}
pub trait DeserializeWith: Sized {
fn deserialize_with<'de, D>(de: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>;
}
#[allow(clippy::upper_case_acronyms)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type", content = "object")]
#[serde(bound(serialize = "K8Obj<S>: Serialize"))]
#[serde(bound(deserialize = "K8Obj<S>: DeserializeOwned"))]
pub enum K8Watch<S>
where
S: Spec,
{
ADDED(K8Obj<S>),
MODIFIED(K8Obj<S>),
DELETED(K8Obj<S>),
}
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ListMetadata {
pub _continue: Option<String>,
#[serde(default)]
pub resource_version: String,
}
#[derive(Deserialize, Serialize, Default, Debug, Eq, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct LabelSelector {
pub match_labels: HashMap<String, String>,
}
impl LabelSelector {
pub fn new_labels<T: Into<String>>(labels: Vec<(T, T)>) -> Self {
let mut match_labels = HashMap::new();
for (key, value) in labels {
match_labels.insert(key.into(), value.into());
}
LabelSelector { match_labels }
}
}
#[derive(Deserialize, Serialize, Default, Debug, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Env {
pub name: String,
pub value: Option<String>,
pub value_from: Option<EnvVarSource>,
}
impl Env {
pub fn key_value<T: Into<String>>(name: T, value: T) -> Self {
Self {
name: name.into(),
value: Some(value.into()),
value_from: None,
}
}
pub fn key_field_ref<T: Into<String>>(name: T, field_path: T) -> Self {
Self {
name: name.into(),
value: None,
value_from: Some(EnvVarSource::FieldRef(ObjectFieldSelector {
field_path: field_path.into(),
})),
}
}
}
#[derive(Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum EnvVarSource {
ConfigMapKeyRef(KeySelector),
FieldRef(ObjectFieldSelector),
ResourceFieldRef(ResourceFieldSelector),
SecretKeyRef(KeySelector),
}
#[derive(Deserialize, Serialize, Default, Debug, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KeySelector {
pub key: String,
pub name: String,
#[serde(default)]
pub optional: bool,
}
#[derive(Deserialize, Serialize, Default, Debug, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ObjectFieldSelector {
pub field_path: String,
}
#[derive(Deserialize, Serialize, Default, Debug, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ResourceFieldSelector {
pub container_name: Option<String>,
pub divisor: Option<String>,
pub resource: String,
}
#[cfg(test)]
mod test {
use super::Env;
use super::ObjectMeta;
#[test]
fn test_metadata_label() {
let metadata =
ObjectMeta::default().set_labels(vec![("app".to_owned(), "test".to_owned())]);
let maps = metadata.labels;
assert_eq!(maps.len(), 1);
assert_eq!(maps.get("app").unwrap(), "test");
}
#[test]
fn test_env() {
let env = Env::key_value("lang", "english");
assert_eq!(env.name, "lang");
assert_eq!(env.value, Some("english".to_owned()));
}
}