chore: Move ADR helper files into folders with their corresponding ADR number
This commit is contained in:
314
adr/003-abstractions/topology/src/main_claude37_2.rs
Normal file
314
adr/003-abstractions/topology/src/main_claude37_2.rs
Normal file
@@ -0,0 +1,314 @@
|
||||
mod main_gemini25pro;
|
||||
use std::process::Command;
|
||||
|
||||
pub trait Capability {}
|
||||
|
||||
pub trait CommandCapability: Capability {
|
||||
fn execute_command(&self, command: &str, args: &[&str]) -> Result<String, String>;
|
||||
}
|
||||
|
||||
pub trait KubernetesCapability: Capability {
|
||||
fn apply_manifest(&self, manifest: &str) -> Result<(), String>;
|
||||
fn get_resource(&self, resource_type: &str, name: &str) -> Result<String, String>;
|
||||
}
|
||||
|
||||
pub trait Topology {
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
pub trait Score<T: Topology> {
|
||||
fn compile(&self) -> Result<Box<dyn Interpret<T>>, String>;
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
pub struct LinuxHostTopology {
|
||||
name: String,
|
||||
host: String,
|
||||
}
|
||||
|
||||
impl Capability for LinuxHostTopology {}
|
||||
|
||||
impl LinuxHostTopology {
|
||||
pub fn new(name: String, host: String) -> Self {
|
||||
Self { name, host }
|
||||
}
|
||||
}
|
||||
|
||||
impl Topology for LinuxHostTopology {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandCapability for LinuxHostTopology {
|
||||
fn execute_command(&self, command: &str, args: &[&str]) -> Result<String, String> {
|
||||
println!("Executing on {}: {} {:?}", self.host, command, args);
|
||||
// In a real implementation, this would SSH to the host and execute the command
|
||||
let output = Command::new(command)
|
||||
.args(args)
|
||||
.output()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(String::from_utf8_lossy(&output.stdout).to_string())
|
||||
} else {
|
||||
Err(String::from_utf8_lossy(&output.stderr).to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct K3DTopology {
|
||||
name: String,
|
||||
linux_host: LinuxHostTopology,
|
||||
cluster_name: String,
|
||||
}
|
||||
|
||||
impl Capability for K3DTopology {}
|
||||
|
||||
impl K3DTopology {
|
||||
pub fn new(name: String, linux_host: LinuxHostTopology, cluster_name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
linux_host,
|
||||
cluster_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Topology for K3DTopology {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandCapability for K3DTopology {
|
||||
fn execute_command(&self, command: &str, args: &[&str]) -> Result<String, String> {
|
||||
self.linux_host.execute_command(command, args)
|
||||
}
|
||||
}
|
||||
|
||||
impl KubernetesCapability for K3DTopology {
|
||||
fn apply_manifest(&self, manifest: &str) -> Result<(), String> {
|
||||
println!("Applying manifest to K3D cluster '{}'", self.cluster_name);
|
||||
// Write manifest to a temporary file
|
||||
let temp_file = format!("/tmp/manifest-harmony-temp.yaml");
|
||||
|
||||
// Use the linux_host directly to avoid capability trait bounds
|
||||
self.linux_host
|
||||
.execute_command("bash", &["-c", &format!("cat > {}", temp_file)])?;
|
||||
|
||||
// Apply with kubectl
|
||||
self.linux_host.execute_command("kubectl", &[
|
||||
"--context",
|
||||
&format!("k3d-{}", self.cluster_name),
|
||||
"apply",
|
||||
"-f",
|
||||
&temp_file,
|
||||
])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_resource(&self, resource_type: &str, name: &str) -> Result<String, String> {
|
||||
println!(
|
||||
"Getting resource {}/{} from K3D cluster '{}'",
|
||||
resource_type, name, self.cluster_name
|
||||
);
|
||||
self.linux_host.execute_command("kubectl", &[
|
||||
"--context",
|
||||
&format!("k3d-{}", self.cluster_name),
|
||||
"get",
|
||||
resource_type,
|
||||
name,
|
||||
"-o",
|
||||
"yaml",
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommandScore {
|
||||
name: String,
|
||||
command: String,
|
||||
args: Vec<String>,
|
||||
}
|
||||
|
||||
impl CommandScore {
|
||||
pub fn new(name: String, command: String, args: Vec<String>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
command,
|
||||
args,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Interpret<T: Topology> {
|
||||
fn execute(&self, topology: &T) -> Result<String, String>;
|
||||
}
|
||||
|
||||
struct CommandInterpret;
|
||||
|
||||
impl<T> Interpret<T> for CommandInterpret
|
||||
where
|
||||
T: Topology + CommandCapability,
|
||||
{
|
||||
fn execute(&self, topology: &T) -> Result<String, String> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Score<T> for CommandScore
|
||||
where
|
||||
T: Topology + CommandCapability,
|
||||
{
|
||||
fn compile(&self) -> Result<Box<dyn Interpret<T>>, String> {
|
||||
Ok(Box::new(CommandInterpret {}))
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct K8sResourceScore {
|
||||
name: String,
|
||||
manifest: String,
|
||||
}
|
||||
|
||||
impl K8sResourceScore {
|
||||
pub fn new(name: String, manifest: String) -> Self {
|
||||
Self { name, manifest }
|
||||
}
|
||||
}
|
||||
|
||||
struct K8sResourceInterpret {
|
||||
score: K8sResourceScore,
|
||||
}
|
||||
|
||||
impl<T: Topology + KubernetesCapability> Interpret<T> for K8sResourceInterpret {
|
||||
fn execute(&self, topology: &T) -> Result<String, String> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Score<T> for K8sResourceScore
|
||||
where
|
||||
T: Topology + KubernetesCapability,
|
||||
{
|
||||
fn compile(&self) -> Result<Box<(dyn Interpret<T> + 'static)>, String> {
|
||||
Ok(Box::new(K8sResourceInterpret {
|
||||
score: self.clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Maestro<T: Topology> {
|
||||
topology: T,
|
||||
scores: Vec<Box<dyn Score<T>>>,
|
||||
}
|
||||
|
||||
|
||||
impl<T: Topology> Maestro<T> {
|
||||
pub fn new(topology: T) -> Self {
|
||||
Self {
|
||||
topology,
|
||||
scores: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_score<S>(&mut self, score: S)
|
||||
where
|
||||
S: Score<T> + 'static,
|
||||
{
|
||||
println!(
|
||||
"Registering score '{}' for topology '{}'",
|
||||
score.name(),
|
||||
self.topology.name()
|
||||
);
|
||||
self.scores.push(Box::new(score));
|
||||
}
|
||||
|
||||
pub fn orchestrate(&self) -> Result<(), String> {
|
||||
println!("Orchestrating topology '{}'", self.topology.name());
|
||||
for score in &self.scores {
|
||||
let interpret = score.compile()?;
|
||||
interpret.execute(&self.topology)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let linux_host = LinuxHostTopology::new("dev-machine".to_string(), "localhost".to_string());
|
||||
|
||||
let mut linux_maestro = Maestro::new(linux_host);
|
||||
|
||||
linux_maestro.register_score(CommandScore::new(
|
||||
"check-disk".to_string(),
|
||||
"df".to_string(),
|
||||
vec!["-h".to_string()],
|
||||
));
|
||||
linux_maestro.orchestrate().unwrap();
|
||||
|
||||
// This would fail to compile if we tried to register a K8sResourceScore
|
||||
// because LinuxHostTopology doesn't implement KubernetesCapability
|
||||
//linux_maestro.register_score(K8sResourceScore::new(
|
||||
// "...".to_string(),
|
||||
// "...".to_string(),
|
||||
//));
|
||||
|
||||
// Create a K3D topology which has both Command and Kubernetes capabilities
|
||||
let k3d_host = LinuxHostTopology::new("k3d-host".to_string(), "localhost".to_string());
|
||||
|
||||
let k3d_topology = K3DTopology::new(
|
||||
"dev-cluster".to_string(),
|
||||
k3d_host,
|
||||
"devcluster".to_string(),
|
||||
);
|
||||
|
||||
// Create a maestro for the K3D topology
|
||||
let mut k3d_maestro = Maestro::new(k3d_topology);
|
||||
|
||||
// We can register both command scores and kubernetes scores
|
||||
k3d_maestro.register_score(CommandScore::new(
|
||||
"check-nodes".to_string(),
|
||||
"kubectl".to_string(),
|
||||
vec!["get".to_string(), "nodes".to_string()],
|
||||
));
|
||||
|
||||
k3d_maestro.register_score(K8sResourceScore::new(
|
||||
"deploy-nginx".to_string(),
|
||||
r#"
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
ports:
|
||||
- containerPort: 80
|
||||
"#
|
||||
.to_string(),
|
||||
));
|
||||
|
||||
// Orchestrate both topologies
|
||||
linux_maestro.orchestrate().unwrap();
|
||||
k3d_maestro.orchestrate().unwrap();
|
||||
}
|
||||
Reference in New Issue
Block a user