use serde::{Deserialize, Serialize};
use chrono::{DateTime, Local};
use crate::metadata::medadata::Metadata;
use crate::pod::pod::Pod;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum EvenType {
Normal,
Warning,
Error,
Painic,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct Event {
#[serde(rename = "apiVersion")]
pub api_version: String,
pub kind: String,
pub metadata: Metadata,
#[serde(rename = "involvedObject")]
pub involved_object: Option<ObjectReference>,
pub reason: Option<String>,
pub message: Option<String>,
pub source: Option<EventSource>,
#[serde(rename = "lastTimestamp")]
pub last_timestamp: Option<DateTime<Local>>,
#[serde(rename = "type")]
pub event_type: Option<EvenType>,
#[serde(rename = "reportingInstance")]
pub reporting_instance: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct ObjectReference {
pub kind: String,
pub namespace: String,
pub name: String,
pub uid: String,
#[serde(rename = "apiVersion")]
pub api_version: Option<String>,
#[serde(rename = "resourceVersion")]
pub resource_version: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct EventSource {
pub component: String,
pub host: Option<String>,
}
impl Event {
#[allow(dead_code)]
fn new_pod_event(pod: Pod, event_type: EvenType, comment: String, reason: String, message: String) -> Self {
Event {
api_version: "v1".to_string(),
kind: "Event".to_string(),
metadata: Metadata {
name: format!("{}.{}", pod.metadata.name, crate::generate_uid_to_cutter()),
namespace: pod.metadata.namespace.clone(),
labels: None,
annotations: None,
uid: Some(crate::generate_uid()),
resource_version: Some(crate::generate_resource_version()),
creation_timestamp: Some(Local::now()),
},
involved_object: Some(ObjectReference {
kind: pod.kind,
namespace: pod.metadata.namespace,
name: pod.metadata.name,
uid: pod.metadata.uid.unwrap(),
api_version: Some(pod.api_version),
resource_version: pod.metadata.resource_version,
}),
reason: Some(reason),
message: Some(message),
source: Some(EventSource {
component: comment.clone(),
host: None,
}),
last_timestamp: Some(Local::now()),
event_type: Some(event_type),
reporting_instance: Some(comment),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_event_deserialization() {
let event_yaml = r#"
apiVersion: v1
kind: Event
count: 1460
involvedObject:
apiVersion: v1
fieldPath: spec.containers{test}
kind: Pod
name: test-6c8847f6c7-qffrj
namespace: default
resourceVersion: "853"
uid: 6be77998-98f6-4e68-b171-ebe7c51238d2
lastTimestamp: "2024-12-10T11:51:15Z"
message: Back-off pulling image "registry.cn-hangzhou.aliyuncs.com/liuliu-public/test:latest123"
metadata:
creationTimestamp: "2024-12-10T06:21:07Z"
name: test-6c8847f6c7-qffrj.180fbc869d46c773
namespace: default
resourceVersion: "7123"
uid: 3291a46a-e9a2-4bcf-86dd-48b923c51c10
reason: BackOff
reportingComponent: kubelet
reportingInstance: vm-8-15-centos
source:
component: kubelet
host: vm-8-15-centos
type: Normal
"#;
let event: Event = serde_yaml::from_str(event_yaml).expect("Failed to parse YAML");
assert_eq!(event.api_version, "v1");
assert_eq!(event.kind, "Event");
assert_eq!(event.metadata.name, "test-6c8847f6c7-qffrj.180fbc869d46c773");
assert_eq!(event.metadata.namespace, "default");
assert_eq!(event.involved_object.clone().unwrap().api_version.unwrap(), "v1");
assert_eq!(event.involved_object.clone().unwrap().kind, "Pod");
let creation_time = event.metadata.creation_timestamp;
assert_eq!(
creation_time.unwrap(),
"2024-12-10T06:21:07Z"
.parse::<DateTime<Local>>()
.unwrap()
);
println!("{:#?}", event);
}
#[test]
fn test_new_pod_event(){
let pod_yaml = r#"
apiVersion: v1
kind: Pod
metadata:
name: example-pod
namespace: default
labels:
app: my-app
creationTimestamp: 2024-12-10T08:00:00+08:00
spec:
nodeName: my-node
hostname: my-hostname
hostAliases:
- ip: "127.0.0.1"
hostnames:
- "my-local-host"
- "another-host"
containers:
- name: example-container
image: example-image:latest
imagePullPolicy: IfNotPresent
command: ["nginx"]
args: ["-g", "daemon off;"]
workingDir: /usr/share/nginx/html
ports:
- name: http
containerPort: 80
protocol: TCP
- name: https
containerPort: 443
protocol: TCP
env:
- name: ENV_MODE
value: production
- name: ENV_VERSION
valueFrom:
fieldRef:
fieldPath: metadata.name
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "1"
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d
readOnly: true
- name: data-volume
mountPath: /usr/share/nginx/html
- name: data-host-volume
mountPath: /usr/share/nginx/a.txt
volumeDevices:
- name: device-volume
devicePath: /dev/sdb
securityContext:
runAsUser: 1000
runAsGroup: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: true
privileged: true
volumes:
- name: example-volume
configMap:
name: nginx-config
- name: data-volume
emptyDir: {}
- name: device-volume
hostPath:
path: /dev/sdb
type: Directory
- name: device-volume
hostPath:
path: /dev/sdb
type: Directory
status:
phase: Pending
message: begin handle
podIP: 10.42.0.9
podIPs:
- ip: 10.42.0.9
"#;
let mut pod: Pod = serde_yaml::from_str(pod_yaml).expect("Failed to parse YAML");
pod.fill_pod_defaults();
let event = Event::new_pod_event(pod.clone(), EvenType::Warning, "fleet-scheduler".to_string(), "backOff".to_string(), "get node failed".to_string());
assert_eq!(event.metadata.name.starts_with(&pod.metadata.name), true);
assert_eq!(event.metadata.namespace, pod.metadata.namespace);
assert!(event.metadata.uid.is_some());
assert!(event.metadata.resource_version.is_some());
assert!(event.metadata.creation_timestamp.is_some());
assert!(event.involved_object.is_some());
let involved_object = event.involved_object.unwrap();
assert_eq!(involved_object.kind, pod.kind);
assert_eq!(involved_object.namespace, pod.metadata.namespace);
assert_eq!(involved_object.name, pod.metadata.name);
assert_eq!(involved_object.uid, pod.metadata.uid.unwrap());
assert_eq!(involved_object.api_version.unwrap(), pod.api_version);
assert_eq!(involved_object.resource_version.unwrap(), pod.metadata.resource_version.unwrap());
assert_eq!(event.reason.unwrap(), "backOff");
assert_eq!(event.message.unwrap(), "get node failed");
assert!(event.source.is_some());
let source = event.source.unwrap();
assert_eq!(source.component, "fleet-scheduler");
assert_eq!(source.host, None);
assert!(event.last_timestamp.is_some());
assert!(event.event_type.is_some());
assert!(matches!(event.event_type.unwrap(), EvenType::Warning));
assert_eq!(event.reporting_instance.unwrap(), "fleet-scheduler");
}
}