use kube::CustomResource;
use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(CustomResource, Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[kube(
group = "cloudnative.amberjs.io",
version = "v1",
kind = "AmberCluster",
plural = "amberjsclusters",
shortname = "bjc",
namespaced
)]
#[cfg_attr(test, derive(Default))]
pub struct AmberClusterSpec {
pub version: String,
pub nodes: u32,
pub image: String,
pub resources: ResourceRequirements,
pub security: SecurityConfig,
pub distributed: DistributedConfig,
pub node_selector: Option<HashMap<String, String>>,
pub tolerations: Option<Vec<Toleration>>,
pub affinity: Option<Affinity>,
pub monitoring: Option<MonitoringConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct ResourceRequirements {
pub cpu: String,
pub memory: String,
pub disk: String,
pub gpu: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct SecurityConfig {
pub sandbox_enabled: bool,
pub rbac_enabled: bool,
pub encryption_enabled: bool,
pub security_context: Option<SecurityContext>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct SecurityContext {
pub run_as_non_root: bool,
pub run_as_user: Option<u32>,
pub run_as_group: Option<u32>,
pub read_only_root_filesystem: bool,
pub allow_privilege_escalation: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct DistributedConfig {
pub cluster_name: String,
pub service_discovery: bool,
pub load_balancer: bool,
pub auto_scaling: bool,
pub discovery_config: Option<ServiceDiscoveryConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct ServiceDiscoveryConfig {
pub gossip_interval_ms: Option<u64>,
pub node_timeout_sec: Option<u64>,
pub backend: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct Toleration {
pub key: String,
pub operator: Option<String>,
pub value: Option<String>,
pub effect: Option<String>,
pub toleration_seconds: Option<i64>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct Affinity {
pub pod_affinity: Option<PodAffinity>,
pub pod_anti_affinity: Option<PodAntiAffinity>,
pub node_affinity: Option<NodeAffinity>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct PodAffinity {
pub required_during_scheduling: Option<Vec<PodAffinityTerm>>,
pub preferred_during_scheduling: Option<Vec<WeightedPodAffinityTerm>>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct PodAffinityTerm {
pub label_selector: Option<LabelSelector>,
pub namespace_selector: Option<LabelSelector>,
pub topology_key: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct WeightedPodAffinityTerm {
pub weight: i32,
pub pod_affinity_term: PodAffinityTerm,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct LabelSelector {
pub match_labels: Option<HashMap<String, String>>,
pub match_expressions: Option<Vec<LabelSelectorRequirement>>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct LabelSelectorRequirement {
pub key: String,
pub operator: String,
pub values: Option<Vec<String>>,
}
pub type PodAntiAffinity = PodAffinity;
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct NodeAffinity {
pub required_during_scheduling: Option<NodeSelector>,
pub preferred_during_scheduling: Option<Vec<PreferredSchedulingTerm>>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct NodeSelector {
pub node_selector_terms: Vec<NodeSelectorTerm>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct NodeSelectorTerm {
pub match_expressions: Option<Vec<NodeSelectorRequirement>>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct NodeSelectorRequirement {
pub key: String,
pub operator: String,
pub values: Option<Vec<String>>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct PreferredSchedulingTerm {
pub weight: i32,
pub preference: NodeSelectorTerm,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct MonitoringConfig {
pub prometheus_enabled: bool,
pub grafana_enabled: bool,
pub tracing_enabled: bool,
pub service_monitor: Option<ServiceMonitorConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[cfg_attr(test, derive(Default))]
pub struct ServiceMonitorConfig {
pub interval: Option<String>,
pub path: Option<String>,
pub port: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_amberjs_cluster_crd_creation() {
let cluster: _ = AmberCluster::new(
"test-cluster",
AmberClusterSpec {
version: "v1.0.0".to_string(),
nodes: 3,
image: "amberjs:latest".to_string(),
resources: ResourceRequirements {
cpu: "2".to_string(),
memory: "4Gi".to_string(),
disk: "20Gi".to_string(),
gpu: None,
},
security: SecurityConfig {
sandbox_enabled: true,
rbac_enabled: true,
encryption_enabled: true,
security_context: None,
},
distributed: DistributedConfig {
cluster_name: "test-cluster".to_string(),
service_discovery: true,
load_balancer: true,
auto_scaling: true,
discovery_config: None,
},
node_selector: None,
tolerations: None,
affinity: None,
monitoring: None,
},
);
assert_eq!(cluster.spec.version, "v1.0.0");
assert_eq!(cluster.spec.nodes, 3);
assert!(cluster.spec.security.sandbox_enabled);
assert!(cluster.spec.distributed.service_discovery);
}
#[test]
fn test_resource_requirements() {
let resources: _ = ResourceRequirements {
cpu: "4".to_string(),
memory: "8Gi".to_string(),
disk: "100Gi".to_string(),
gpu: Some("nvidia-tesla-v100".to_string()),
};
assert_eq!(resources.cpu, "4");
assert_eq!(resources.memory, "8Gi");
assert_eq!(resources.disk, "100Gi");
assert_eq!(resources.gpu, Some("nvidia-tesla-v100".to_string()));
}
#[test]
fn test_security_context() {
let security_context: _ = SecurityContext {
run_as_non_root: true,
run_as_user: Some(1000),
run_as_group: Some(1000),
read_only_root_filesystem: true,
allow_privilege_escalation: false,
};
assert!(security_context.run_as_non_root);
assert_eq!(security_context.run_as_user, Some(1000));
assert!(security_context.read_only_root_filesystem);
assert!(!security_context.allow_privilege_escalation);
}
}
use std::collections::{BTreeMap, HashMap};