Kubernetes Clusters #22
							
								
								
									
										17
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								Cargo.toml
									
									
									
									
									
								
							@@ -89,6 +89,7 @@ tokio-test = "0.4.4"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
thiserror = "2.0.12"         # For error handling in the main Error enum
 | 
					thiserror = "2.0.12"         # For error handling in the main Error enum
 | 
				
			||||||
 | 
					tokio = { workspace = true } # For async examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Optional dependencies - users can choose which modules to include
 | 
					# Optional dependencies - users can choose which modules to include
 | 
				
			||||||
sal-git = { path = "git", optional = true }
 | 
					sal-git = { path = "git", optional = true }
 | 
				
			||||||
@@ -146,3 +147,19 @@ all = [
 | 
				
			|||||||
    "rhai",
 | 
					    "rhai",
 | 
				
			||||||
    "service_manager",
 | 
					    "service_manager",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Examples
 | 
				
			||||||
 | 
					[[example]]
 | 
				
			||||||
 | 
					name = "postgres_cluster"
 | 
				
			||||||
 | 
					path = "examples/kubernetes/clusters/postgres.rs"
 | 
				
			||||||
 | 
					required-features = ["kubernetes"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[example]]
 | 
				
			||||||
 | 
					name = "redis_cluster"
 | 
				
			||||||
 | 
					path = "examples/kubernetes/clusters/redis.rs"
 | 
				
			||||||
 | 
					required-features = ["kubernetes"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[example]]
 | 
				
			||||||
 | 
					name = "generic_cluster"
 | 
				
			||||||
 | 
					path = "examples/kubernetes/clusters/generic.rs"
 | 
				
			||||||
 | 
					required-features = ["kubernetes"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,10 +11,26 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
				
			|||||||
    // Create Kubernetes manager
 | 
					    // Create Kubernetes manager
 | 
				
			||||||
    let km = KubernetesManager::new("default").await?;
 | 
					    let km = KubernetesManager::new("default").await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Example 1: Simple web server deployment
 | 
					    // Clean up any existing resources first
 | 
				
			||||||
    println!("=== Example 1: Simple Nginx Web Server ===");
 | 
					    println!("=== Cleaning up existing resources ===");
 | 
				
			||||||
 | 
					    let apps_to_clean = ["web-server", "node-app", "mongodb"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    km.deploy_application("web-server", "nginx:latest", 2, 80, None)
 | 
					    for app in &apps_to_clean {
 | 
				
			||||||
 | 
					        match km.deployment_delete(app).await {
 | 
				
			||||||
 | 
					            Ok(_) => println!("✓ Deleted existing deployment: {}", app),
 | 
				
			||||||
 | 
					            Err(_) => println!("✓ No existing deployment to delete: {}", app),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        match km.service_delete(app).await {
 | 
				
			||||||
 | 
					            Ok(_) => println!("✓ Deleted existing service: {}", app),
 | 
				
			||||||
 | 
					            Err(_) => println!("✓ No existing service to delete: {}", app),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Example 1: Simple web server deployment
 | 
				
			||||||
 | 
					    println!("\n=== Example 1: Simple Nginx Web Server ===");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    km.deploy_application("web-server", "nginx:latest", 2, 80, None, None)
 | 
				
			||||||
        .await?;
 | 
					        .await?;
 | 
				
			||||||
    println!("✅ Nginx web server deployed!");
 | 
					    println!("✅ Nginx web server deployed!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,12 +42,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
				
			|||||||
    node_labels.insert("tier".to_string(), "backend".to_string());
 | 
					    node_labels.insert("tier".to_string(), "backend".to_string());
 | 
				
			||||||
    node_labels.insert("environment".to_string(), "production".to_string());
 | 
					    node_labels.insert("environment".to_string(), "production".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Configure Node.js environment variables
 | 
				
			||||||
 | 
					    let mut node_env_vars = HashMap::new();
 | 
				
			||||||
 | 
					    node_env_vars.insert("NODE_ENV".to_string(), "production".to_string());
 | 
				
			||||||
 | 
					    node_env_vars.insert("PORT".to_string(), "3000".to_string());
 | 
				
			||||||
 | 
					    node_env_vars.insert("LOG_LEVEL".to_string(), "info".to_string());
 | 
				
			||||||
 | 
					    node_env_vars.insert("MAX_CONNECTIONS".to_string(), "1000".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    km.deploy_application(
 | 
					    km.deploy_application(
 | 
				
			||||||
        "node-app",          // name
 | 
					        "node-app",          // name
 | 
				
			||||||
        "node:18-alpine",    // image
 | 
					        "node:18-alpine",    // image
 | 
				
			||||||
        3,                   // replicas - scale to 3 instances
 | 
					        3,                   // replicas - scale to 3 instances
 | 
				
			||||||
        3000,                // port
 | 
					        3000,                // port
 | 
				
			||||||
        Some(node_labels),   // labels
 | 
					        Some(node_labels),   // labels
 | 
				
			||||||
 | 
					        Some(node_env_vars), // environment variables
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    .await?;
 | 
					    .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,12 +69,25 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
				
			|||||||
    mongo_labels.insert("type".to_string(), "database".to_string());
 | 
					    mongo_labels.insert("type".to_string(), "database".to_string());
 | 
				
			||||||
    mongo_labels.insert("engine".to_string(), "mongodb".to_string());
 | 
					    mongo_labels.insert("engine".to_string(), "mongodb".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Configure MongoDB environment variables
 | 
				
			||||||
 | 
					    let mut mongo_env_vars = HashMap::new();
 | 
				
			||||||
 | 
					    mongo_env_vars.insert(
 | 
				
			||||||
 | 
					        "MONGO_INITDB_ROOT_USERNAME".to_string(),
 | 
				
			||||||
 | 
					        "admin".to_string(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    mongo_env_vars.insert(
 | 
				
			||||||
 | 
					        "MONGO_INITDB_ROOT_PASSWORD".to_string(),
 | 
				
			||||||
 | 
					        "mongopassword".to_string(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    mongo_env_vars.insert("MONGO_INITDB_DATABASE".to_string(), "myapp".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    km.deploy_application(
 | 
					    km.deploy_application(
 | 
				
			||||||
        "mongodb",            // name
 | 
					        "mongodb",            // name
 | 
				
			||||||
        "mongo:6.0",          // image
 | 
					        "mongo:6.0",          // image
 | 
				
			||||||
        1,                    // replicas - single instance for simplicity
 | 
					        1,                    // replicas - single instance for simplicity
 | 
				
			||||||
        27017,                // port
 | 
					        27017,                // port
 | 
				
			||||||
        Some(mongo_labels),   // labels
 | 
					        Some(mongo_labels),   // labels
 | 
				
			||||||
 | 
					        Some(mongo_env_vars), // environment variables
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    .await?;
 | 
					    .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,35 @@ print("Creating Kubernetes manager for 'database' namespace...");
 | 
				
			|||||||
let km = kubernetes_manager_new("database");
 | 
					let km = kubernetes_manager_new("database");
 | 
				
			||||||
print("✓ Kubernetes manager created");
 | 
					print("✓ Kubernetes manager created");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create the namespace if it doesn't exist
 | 
				
			||||||
 | 
					print("Creating namespace 'database' if it doesn't exist...");
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    create_namespace(km, "database");
 | 
				
			||||||
 | 
					    print("✓ Namespace 'database' created");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    if e.to_string().contains("already exists") {
 | 
				
			||||||
 | 
					        print("✓ Namespace 'database' already exists");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        print("⚠️ Warning: " + e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Clean up any existing resources first
 | 
				
			||||||
 | 
					print("\nCleaning up any existing PostgreSQL resources...");
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_deployment(km, "postgres-cluster");
 | 
				
			||||||
 | 
					    print("✓ Deleted existing deployment");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("✓ No existing deployment to delete");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_service(km, "postgres-cluster");
 | 
				
			||||||
 | 
					    print("✓ Deleted existing service");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("✓ No existing service to delete");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create PostgreSQL cluster using the convenience method
 | 
					// Create PostgreSQL cluster using the convenience method
 | 
				
			||||||
print("\nDeploying PostgreSQL cluster...");
 | 
					print("\nDeploying PostgreSQL cluster...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -19,6 +48,11 @@ try {
 | 
				
			|||||||
        "app": "postgres-cluster",
 | 
					        "app": "postgres-cluster",
 | 
				
			||||||
        "type": "database",
 | 
					        "type": "database",
 | 
				
			||||||
        "engine": "postgresql"
 | 
					        "engine": "postgresql"
 | 
				
			||||||
 | 
					    }, #{
 | 
				
			||||||
 | 
					        "POSTGRES_DB": "myapp",
 | 
				
			||||||
 | 
					        "POSTGRES_USER": "postgres",
 | 
				
			||||||
 | 
					        "POSTGRES_PASSWORD": "secretpassword",
 | 
				
			||||||
 | 
					        "PGDATA": "/var/lib/postgresql/data/pgdata"
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    print("✓ " + result);
 | 
					    print("✓ " + result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,12 +11,50 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
				
			|||||||
    // Create Kubernetes manager for the database namespace
 | 
					    // Create Kubernetes manager for the database namespace
 | 
				
			||||||
    let km = KubernetesManager::new("database").await?;
 | 
					    let km = KubernetesManager::new("database").await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create the namespace if it doesn't exist
 | 
				
			||||||
 | 
					    println!("Creating namespace 'database' if it doesn't exist...");
 | 
				
			||||||
 | 
					    match km.namespace_create("database").await {
 | 
				
			||||||
 | 
					        Ok(_) => println!("✓ Namespace 'database' created"),
 | 
				
			||||||
 | 
					        Err(e) => {
 | 
				
			||||||
 | 
					            if e.to_string().contains("already exists") {
 | 
				
			||||||
 | 
					                println!("✓ Namespace 'database' already exists");
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return Err(e.into());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up any existing resources first
 | 
				
			||||||
 | 
					    println!("Cleaning up any existing PostgreSQL resources...");
 | 
				
			||||||
 | 
					    match km.deployment_delete("postgres-cluster").await {
 | 
				
			||||||
 | 
					        Ok(_) => println!("✓ Deleted existing deployment"),
 | 
				
			||||||
 | 
					        Err(_) => println!("✓ No existing deployment to delete"),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    match km.service_delete("postgres-cluster").await {
 | 
				
			||||||
 | 
					        Ok(_) => println!("✓ Deleted existing service"),
 | 
				
			||||||
 | 
					        Err(_) => println!("✓ No existing service to delete"),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Configure PostgreSQL-specific labels
 | 
					    // Configure PostgreSQL-specific labels
 | 
				
			||||||
    let mut labels = HashMap::new();
 | 
					    let mut labels = HashMap::new();
 | 
				
			||||||
    labels.insert("app".to_string(), "postgres-cluster".to_string());
 | 
					    labels.insert("app".to_string(), "postgres-cluster".to_string());
 | 
				
			||||||
    labels.insert("type".to_string(), "database".to_string());
 | 
					    labels.insert("type".to_string(), "database".to_string());
 | 
				
			||||||
    labels.insert("engine".to_string(), "postgresql".to_string());
 | 
					    labels.insert("engine".to_string(), "postgresql".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Configure PostgreSQL environment variables
 | 
				
			||||||
 | 
					    let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					    env_vars.insert("POSTGRES_DB".to_string(), "myapp".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert("POSTGRES_USER".to_string(), "postgres".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert(
 | 
				
			||||||
 | 
					        "POSTGRES_PASSWORD".to_string(),
 | 
				
			||||||
 | 
					        "secretpassword".to_string(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    env_vars.insert(
 | 
				
			||||||
 | 
					        "PGDATA".to_string(),
 | 
				
			||||||
 | 
					        "/var/lib/postgresql/data/pgdata".to_string(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Deploy the PostgreSQL cluster using the convenience method
 | 
					    // Deploy the PostgreSQL cluster using the convenience method
 | 
				
			||||||
    println!("Deploying PostgreSQL cluster...");
 | 
					    println!("Deploying PostgreSQL cluster...");
 | 
				
			||||||
    km.deploy_application(
 | 
					    km.deploy_application(
 | 
				
			||||||
@@ -25,6 +63,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
				
			|||||||
        2,                  // replicas (1 master + 1 replica)
 | 
					        2,                  // replicas (1 master + 1 replica)
 | 
				
			||||||
        5432,               // port
 | 
					        5432,               // port
 | 
				
			||||||
        Some(labels),       // labels
 | 
					        Some(labels),       // labels
 | 
				
			||||||
 | 
					        Some(env_vars),     // environment variables
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    .await?;
 | 
					    .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,35 @@ print("Creating Kubernetes manager for 'cache' namespace...");
 | 
				
			|||||||
let km = kubernetes_manager_new("cache");
 | 
					let km = kubernetes_manager_new("cache");
 | 
				
			||||||
print("✓ Kubernetes manager created");
 | 
					print("✓ Kubernetes manager created");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create the namespace if it doesn't exist
 | 
				
			||||||
 | 
					print("Creating namespace 'cache' if it doesn't exist...");
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    create_namespace(km, "cache");
 | 
				
			||||||
 | 
					    print("✓ Namespace 'cache' created");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    if e.to_string().contains("already exists") {
 | 
				
			||||||
 | 
					        print("✓ Namespace 'cache' already exists");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        print("⚠️ Warning: " + e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Clean up any existing resources first
 | 
				
			||||||
 | 
					print("\nCleaning up any existing Redis resources...");
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_deployment(km, "redis-cluster");
 | 
				
			||||||
 | 
					    print("✓ Deleted existing deployment");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("✓ No existing deployment to delete");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_service(km, "redis-cluster");
 | 
				
			||||||
 | 
					    print("✓ Deleted existing service");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("✓ No existing service to delete");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create Redis cluster using the convenience method
 | 
					// Create Redis cluster using the convenience method
 | 
				
			||||||
print("\nDeploying Redis cluster...");
 | 
					print("\nDeploying Redis cluster...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -19,6 +48,12 @@ try {
 | 
				
			|||||||
        "app": "redis-cluster",
 | 
					        "app": "redis-cluster",
 | 
				
			||||||
        "type": "cache",
 | 
					        "type": "cache",
 | 
				
			||||||
        "engine": "redis"
 | 
					        "engine": "redis"
 | 
				
			||||||
 | 
					    }, #{
 | 
				
			||||||
 | 
					        "REDIS_PASSWORD": "redispassword",
 | 
				
			||||||
 | 
					        "REDIS_PORT": "6379",
 | 
				
			||||||
 | 
					        "REDIS_DATABASES": "16",
 | 
				
			||||||
 | 
					        "REDIS_MAXMEMORY": "256mb",
 | 
				
			||||||
 | 
					        "REDIS_MAXMEMORY_POLICY": "allkeys-lru"
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    print("✓ " + result);
 | 
					    print("✓ " + result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,12 +11,48 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
				
			|||||||
    // Create Kubernetes manager for the cache namespace
 | 
					    // Create Kubernetes manager for the cache namespace
 | 
				
			||||||
    let km = KubernetesManager::new("cache").await?;
 | 
					    let km = KubernetesManager::new("cache").await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create the namespace if it doesn't exist
 | 
				
			||||||
 | 
					    println!("Creating namespace 'cache' if it doesn't exist...");
 | 
				
			||||||
 | 
					    match km.namespace_create("cache").await {
 | 
				
			||||||
 | 
					        Ok(_) => println!("✓ Namespace 'cache' created"),
 | 
				
			||||||
 | 
					        Err(e) => {
 | 
				
			||||||
 | 
					            if e.to_string().contains("already exists") {
 | 
				
			||||||
 | 
					                println!("✓ Namespace 'cache' already exists");
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return Err(e.into());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up any existing resources first
 | 
				
			||||||
 | 
					    println!("Cleaning up any existing Redis resources...");
 | 
				
			||||||
 | 
					    match km.deployment_delete("redis-cluster").await {
 | 
				
			||||||
 | 
					        Ok(_) => println!("✓ Deleted existing deployment"),
 | 
				
			||||||
 | 
					        Err(_) => println!("✓ No existing deployment to delete"),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    match km.service_delete("redis-cluster").await {
 | 
				
			||||||
 | 
					        Ok(_) => println!("✓ Deleted existing service"),
 | 
				
			||||||
 | 
					        Err(_) => println!("✓ No existing service to delete"),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Configure Redis-specific labels
 | 
					    // Configure Redis-specific labels
 | 
				
			||||||
    let mut labels = HashMap::new();
 | 
					    let mut labels = HashMap::new();
 | 
				
			||||||
    labels.insert("app".to_string(), "redis-cluster".to_string());
 | 
					    labels.insert("app".to_string(), "redis-cluster".to_string());
 | 
				
			||||||
    labels.insert("type".to_string(), "cache".to_string());
 | 
					    labels.insert("type".to_string(), "cache".to_string());
 | 
				
			||||||
    labels.insert("engine".to_string(), "redis".to_string());
 | 
					    labels.insert("engine".to_string(), "redis".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Configure Redis environment variables
 | 
				
			||||||
 | 
					    let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					    env_vars.insert("REDIS_PASSWORD".to_string(), "redispassword".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert("REDIS_PORT".to_string(), "6379".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert("REDIS_DATABASES".to_string(), "16".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert("REDIS_MAXMEMORY".to_string(), "256mb".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert(
 | 
				
			||||||
 | 
					        "REDIS_MAXMEMORY_POLICY".to_string(),
 | 
				
			||||||
 | 
					        "allkeys-lru".to_string(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Deploy the Redis cluster using the convenience method
 | 
					    // Deploy the Redis cluster using the convenience method
 | 
				
			||||||
    println!("Deploying Redis cluster...");
 | 
					    println!("Deploying Redis cluster...");
 | 
				
			||||||
    km.deploy_application(
 | 
					    km.deploy_application(
 | 
				
			||||||
@@ -25,6 +61,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
				
			|||||||
        3,                // replicas (Redis cluster nodes)
 | 
					        3,                // replicas (Redis cluster nodes)
 | 
				
			||||||
        6379,             // port
 | 
					        6379,             // port
 | 
				
			||||||
        Some(labels),     // labels
 | 
					        Some(labels),     // labels
 | 
				
			||||||
 | 
					        Some(env_vars),   // environment variables
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    .await?;
 | 
					    .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,7 @@ log = "0.4"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Rhai scripting support (optional)
 | 
					# Rhai scripting support (optional)
 | 
				
			||||||
rhai = { version = "1.12.0", features = ["sync"], optional = true }
 | 
					rhai = { version = "1.12.0", features = ["sync"], optional = true }
 | 
				
			||||||
 | 
					once_cell = "1.20.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# UUID for resource identification
 | 
					# UUID for resource identification
 | 
				
			||||||
uuid = { version = "1.16.0", features = ["v4"] }
 | 
					uuid = { version = "1.16.0", features = ["v4"] }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,15 +35,96 @@ This package provides a high-level interface for managing Kubernetes clusters us
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Features
 | 
					## Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **Application Deployment**: Deploy complete applications with a single method call
 | 
				
			||||||
 | 
					- **Environment Variables & Labels**: Configure containers with environment variables and Kubernetes labels
 | 
				
			||||||
 | 
					- **Resource Lifecycle Management**: Automatic cleanup and replacement of existing resources
 | 
				
			||||||
- **Namespace-scoped Management**: Each `KubernetesManager` instance operates on a single namespace
 | 
					- **Namespace-scoped Management**: Each `KubernetesManager` instance operates on a single namespace
 | 
				
			||||||
- **Pod Management**: List, create, and manage pods
 | 
					- **Pod Management**: List, create, and manage pods
 | 
				
			||||||
- **Pattern-based Deletion**: Delete resources using PCRE pattern matching
 | 
					- **Pattern-based Deletion**: Delete resources using PCRE pattern matching
 | 
				
			||||||
- **Namespace Operations**: Create and manage namespaces (idempotent operations)
 | 
					- **Namespace Operations**: Create and manage namespaces (idempotent operations)
 | 
				
			||||||
- **Resource Management**: Support for pods, services, deployments, configmaps, secrets, and more
 | 
					- **Resource Management**: Support for pods, services, deployments, configmaps, secrets, and more
 | 
				
			||||||
- **Rhai Integration**: Full scripting support through Rhai wrappers
 | 
					- **Rhai Integration**: Full scripting support through Rhai wrappers with environment variables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Core Concepts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Labels vs Environment Variables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Understanding the difference between labels and environment variables is crucial for effective Kubernetes deployments:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### **Labels** (Kubernetes Metadata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **Purpose**: Organize, select, and manage Kubernetes resources
 | 
				
			||||||
 | 
					- **Scope**: Kubernetes cluster management and resource organization
 | 
				
			||||||
 | 
					- **Visibility**: Used by Kubernetes controllers, selectors, and monitoring systems
 | 
				
			||||||
 | 
					- **Examples**: `app=my-app`, `tier=backend`, `environment=production`, `version=v1.2.3`
 | 
				
			||||||
 | 
					- **Use Cases**: Resource grouping, service discovery, monitoring labels, deployment strategies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### **Environment Variables** (Container Configuration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **Purpose**: Configure application runtime behavior and settings
 | 
				
			||||||
 | 
					- **Scope**: Inside container processes - available to your application code
 | 
				
			||||||
 | 
					- **Visibility**: Accessible via `process.env`, `os.environ`, etc. in your application
 | 
				
			||||||
 | 
					- **Examples**: `NODE_ENV=production`, `DATABASE_URL=postgres://...`, `API_KEY=secret`
 | 
				
			||||||
 | 
					- **Use Cases**: Database connections, API keys, feature flags, runtime configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### **Example: Complete Application Configuration**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					// Labels: For Kubernetes resource management
 | 
				
			||||||
 | 
					let mut labels = HashMap::new();
 | 
				
			||||||
 | 
					labels.insert("app".to_string(), "web-api".to_string());           // Service discovery
 | 
				
			||||||
 | 
					labels.insert("tier".to_string(), "backend".to_string());         // Architecture layer
 | 
				
			||||||
 | 
					labels.insert("environment".to_string(), "production".to_string()); // Deployment stage
 | 
				
			||||||
 | 
					labels.insert("version".to_string(), "v2.1.0".to_string());       // Release version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Environment Variables: For application configuration
 | 
				
			||||||
 | 
					let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					env_vars.insert("NODE_ENV".to_string(), "production".to_string());              // Runtime mode
 | 
				
			||||||
 | 
					env_vars.insert("DATABASE_URL".to_string(), "postgres://db:5432/app".to_string()); // DB connection
 | 
				
			||||||
 | 
					env_vars.insert("REDIS_URL".to_string(), "redis://cache:6379".to_string());     // Cache connection
 | 
				
			||||||
 | 
					env_vars.insert("LOG_LEVEL".to_string(), "info".to_string());                   // Logging config
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Usage
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Application Deployment (Recommended)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Deploy complete applications with labels and environment variables:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use sal_kubernetes::KubernetesManager;
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::main]
 | 
				
			||||||
 | 
					async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
				
			||||||
 | 
					    let km = KubernetesManager::new("default").await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Configure labels for Kubernetes resource organization
 | 
				
			||||||
 | 
					    let mut labels = HashMap::new();
 | 
				
			||||||
 | 
					    labels.insert("app".to_string(), "my-app".to_string());
 | 
				
			||||||
 | 
					    labels.insert("tier".to_string(), "backend".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Configure environment variables for the container
 | 
				
			||||||
 | 
					    let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					    env_vars.insert("NODE_ENV".to_string(), "production".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert("DATABASE_URL".to_string(), "postgres://db:5432/myapp".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert("API_KEY".to_string(), "secret-api-key".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Deploy application with deployment + service
 | 
				
			||||||
 | 
					    km.deploy_application(
 | 
				
			||||||
 | 
					        "my-app",           // name
 | 
				
			||||||
 | 
					        "node:18-alpine",   // image
 | 
				
			||||||
 | 
					        3,                  // replicas
 | 
				
			||||||
 | 
					        3000,               // port
 | 
				
			||||||
 | 
					        Some(labels),       // Kubernetes labels
 | 
				
			||||||
 | 
					        Some(env_vars),     // container environment variables
 | 
				
			||||||
 | 
					    ).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    println!("✅ Application deployed successfully!");
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Basic Operations
 | 
					### Basic Operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```rust
 | 
					```rust
 | 
				
			||||||
@@ -74,14 +155,24 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
				
			|||||||
// Create Kubernetes manager for namespace
 | 
					// Create Kubernetes manager for namespace
 | 
				
			||||||
let km = kubernetes_manager_new("default");
 | 
					let km = kubernetes_manager_new("default");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// List pods
 | 
					// Deploy application with labels and environment variables
 | 
				
			||||||
 | 
					deploy_application(km, "my-app", "node:18-alpine", 3, 3000, #{
 | 
				
			||||||
 | 
					    "app": "my-app",
 | 
				
			||||||
 | 
					    "tier": "backend",
 | 
				
			||||||
 | 
					    "environment": "production"
 | 
				
			||||||
 | 
					}, #{
 | 
				
			||||||
 | 
					    "NODE_ENV": "production",
 | 
				
			||||||
 | 
					    "DATABASE_URL": "postgres://db:5432/myapp",
 | 
				
			||||||
 | 
					    "API_KEY": "secret-api-key"
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print("✅ Application deployed!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Basic operations
 | 
				
			||||||
let pods = pods_list(km);
 | 
					let pods = pods_list(km);
 | 
				
			||||||
print("Found " + pods.len() + " pods");
 | 
					print("Found " + pods.len() + " pods");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create namespace
 | 
					namespace_create(km, "my-namespace");
 | 
				
			||||||
namespace_create(km, "my-app");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete test resources
 | 
					 | 
				
			||||||
delete(km, "test-.*");
 | 
					delete(km, "test-.*");
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -98,6 +189,7 @@ delete(km, "test-.*");
 | 
				
			|||||||
### Kubernetes Authentication
 | 
					### Kubernetes Authentication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The package uses the standard Kubernetes configuration methods:
 | 
					The package uses the standard Kubernetes configuration methods:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- In-cluster configuration (when running in a pod)
 | 
					- In-cluster configuration (when running in a pod)
 | 
				
			||||||
- Kubeconfig file (`~/.kube/config` or `KUBECONFIG` environment variable)
 | 
					- Kubeconfig file (`~/.kube/config` or `KUBECONFIG` environment variable)
 | 
				
			||||||
- Service account tokens
 | 
					- Service account tokens
 | 
				
			||||||
@@ -144,6 +236,18 @@ The main interface for Kubernetes operations. Each instance is scoped to a singl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
- `KubernetesManager::new(namespace)` - Create a manager for the specified namespace
 | 
					- `KubernetesManager::new(namespace)` - Create a manager for the specified namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Application Deployment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `deploy_application(name, image, replicas, port, labels, env_vars)` - Deploy complete application with deployment and service
 | 
				
			||||||
 | 
					- `deployment_create(name, image, replicas, labels, env_vars)` - Create deployment with environment variables and labels
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Resource Creation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `pod_create(name, image, labels, env_vars)` - Create pod with environment variables and labels
 | 
				
			||||||
 | 
					- `service_create(name, selector, port, target_port)` - Create service with port mapping
 | 
				
			||||||
 | 
					- `configmap_create(name, data)` - Create configmap with data
 | 
				
			||||||
 | 
					- `secret_create(name, data, secret_type)` - Create secret with data and optional type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Resource Listing
 | 
					#### Resource Listing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `pods_list()` - List all pods in the namespace
 | 
					- `pods_list()` - List all pods in the namespace
 | 
				
			||||||
@@ -160,6 +264,8 @@ The main interface for Kubernetes operations. Each instance is scoped to a singl
 | 
				
			|||||||
- `pod_delete(name)` - Delete a specific pod by name
 | 
					- `pod_delete(name)` - Delete a specific pod by name
 | 
				
			||||||
- `service_delete(name)` - Delete a specific service by name
 | 
					- `service_delete(name)` - Delete a specific service by name
 | 
				
			||||||
- `deployment_delete(name)` - Delete a specific deployment by name
 | 
					- `deployment_delete(name)` - Delete a specific deployment by name
 | 
				
			||||||
 | 
					- `configmap_delete(name)` - Delete a specific configmap by name
 | 
				
			||||||
 | 
					- `secret_delete(name)` - Delete a specific secret by name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Pattern-based Operations
 | 
					#### Pattern-based Operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -180,32 +286,93 @@ The main interface for Kubernetes operations. Each instance is scoped to a singl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
When using the Rhai integration, the following functions are available:
 | 
					When using the Rhai integration, the following functions are available:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Manager Creation & Application Deployment:**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `kubernetes_manager_new(namespace)` - Create a KubernetesManager
 | 
					- `kubernetes_manager_new(namespace)` - Create a KubernetesManager
 | 
				
			||||||
 | 
					- `deploy_application(km, name, image, replicas, port, labels, env_vars)` - Deploy application with environment variables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Resource Listing:**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `pods_list(km)` - List pods
 | 
					- `pods_list(km)` - List pods
 | 
				
			||||||
- `services_list(km)` - List services
 | 
					- `services_list(km)` - List services
 | 
				
			||||||
- `deployments_list(km)` - List deployments
 | 
					- `deployments_list(km)` - List deployments
 | 
				
			||||||
 | 
					- `configmaps_list(km)` - List configmaps
 | 
				
			||||||
 | 
					- `secrets_list(km)` - List secrets
 | 
				
			||||||
- `namespaces_list(km)` - List all namespaces
 | 
					- `namespaces_list(km)` - List all namespaces
 | 
				
			||||||
- `delete(km, pattern)` - Delete resources matching pattern
 | 
					 | 
				
			||||||
- `namespace_create(km, name)` - Create namespace
 | 
					 | 
				
			||||||
- `namespace_exists(km, name)` - Check namespace existence
 | 
					 | 
				
			||||||
- `resource_counts(km)` - Get resource counts
 | 
					- `resource_counts(km)` - Get resource counts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Resource Operations:**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `delete(km, pattern)` - Delete resources matching pattern
 | 
				
			||||||
- `pod_delete(km, name)` - Delete specific pod
 | 
					- `pod_delete(km, name)` - Delete specific pod
 | 
				
			||||||
- `service_delete(km, name)` - Delete specific service
 | 
					- `service_delete(km, name)` - Delete specific service
 | 
				
			||||||
- `deployment_delete(km, name)` - Delete specific deployment
 | 
					- `deployment_delete(km, name)` - Delete specific deployment
 | 
				
			||||||
 | 
					- `configmap_delete(km, name)` - Delete specific configmap
 | 
				
			||||||
 | 
					- `secret_delete(km, name)` - Delete specific secret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Namespace Functions:**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `namespace_create(km, name)` - Create namespace
 | 
				
			||||||
 | 
					- `namespace_exists(km, name)` - Check namespace existence
 | 
				
			||||||
 | 
					- `namespace_delete(km, name)` - Delete namespace
 | 
				
			||||||
- `namespace(km)` - Get manager's namespace
 | 
					- `namespace(km)` - Get manager's namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Examples
 | 
					## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The `examples/kubernetes/` directory contains comprehensive examples:
 | 
					The `examples/kubernetes/clusters/` directory contains comprehensive examples:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `basic_operations.rhai` - Basic listing and counting operations
 | 
					### Rust Examples
 | 
				
			||||||
- `namespace_management.rhai` - Creating and managing namespaces
 | 
					
 | 
				
			||||||
- `pattern_deletion.rhai` - Using PCRE patterns for bulk deletion
 | 
					Run with: `cargo run --example <name> --features kubernetes`
 | 
				
			||||||
- `multi_namespace_operations.rhai` - Working across multiple namespaces
 | 
					
 | 
				
			||||||
 | 
					- `postgres` - PostgreSQL database deployment with environment variables
 | 
				
			||||||
 | 
					- `redis` - Redis cache deployment with configuration
 | 
				
			||||||
 | 
					- `generic` - Multiple application deployments (nginx, node.js, mongodb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Rhai Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Run with: `./target/debug/herodo examples/kubernetes/clusters/<script>.rhai`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `postgres.rhai` - PostgreSQL cluster deployment script
 | 
				
			||||||
 | 
					- `redis.rhai` - Redis cluster deployment script
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Real-World Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### PostgreSQL Database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					env_vars.insert("POSTGRES_DB".to_string(), "myapp".to_string());
 | 
				
			||||||
 | 
					env_vars.insert("POSTGRES_USER".to_string(), "postgres".to_string());
 | 
				
			||||||
 | 
					env_vars.insert("POSTGRES_PASSWORD".to_string(), "secretpassword".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					km.deploy_application("postgres", "postgres:15", 1, 5432, Some(labels), Some(env_vars)).await?;
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Redis Cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					env_vars.insert("REDIS_PASSWORD".to_string(), "redispassword".to_string());
 | 
				
			||||||
 | 
					env_vars.insert("REDIS_MAXMEMORY".to_string(), "256mb".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					km.deploy_application("redis", "redis:7-alpine", 3, 6379, None, Some(env_vars)).await?;
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Testing
 | 
					## Testing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Run tests with:
 | 
					### Test Coverage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The module includes comprehensive test coverage:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **Unit Tests**: Core functionality without cluster dependency
 | 
				
			||||||
 | 
					- **Integration Tests**: Real Kubernetes cluster operations
 | 
				
			||||||
 | 
					- **Environment Variables Tests**: Complete env var functionality testing
 | 
				
			||||||
 | 
					- **Edge Cases Tests**: Error handling and boundary conditions
 | 
				
			||||||
 | 
					- **Rhai Integration Tests**: Scripting environment testing
 | 
				
			||||||
 | 
					- **Production Readiness Tests**: Concurrent operations and error handling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Running Tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
# Unit tests (no cluster required)
 | 
					# Unit tests (no cluster required)
 | 
				
			||||||
@@ -216,12 +383,61 @@ KUBERNETES_TEST_ENABLED=1 cargo test --package sal-kubernetes
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Rhai integration tests
 | 
					# Rhai integration tests
 | 
				
			||||||
KUBERNETES_TEST_ENABLED=1 cargo test --package sal-kubernetes --features rhai
 | 
					KUBERNETES_TEST_ENABLED=1 cargo test --package sal-kubernetes --features rhai
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Run specific test suites
 | 
				
			||||||
 | 
					cargo test --package sal-kubernetes deployment_env_vars_test
 | 
				
			||||||
 | 
					cargo test --package sal-kubernetes edge_cases_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Rhai environment variables test
 | 
				
			||||||
 | 
					KUBERNETES_TEST_ENABLED=1 ./target/debug/herodo kubernetes/tests/rhai/env_vars_test.rhai
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Security Considerations
 | 
					### Test Requirements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **Kubernetes Cluster**: Integration tests require a running Kubernetes cluster
 | 
				
			||||||
 | 
					- **Environment Variable**: Set `KUBERNETES_TEST_ENABLED=1` to enable integration tests
 | 
				
			||||||
 | 
					- **Permissions**: Tests require permissions to create/delete resources in the `default` namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Production Considerations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Security
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Always use specific PCRE patterns to avoid accidental deletion of important resources
 | 
					- Always use specific PCRE patterns to avoid accidental deletion of important resources
 | 
				
			||||||
- Test deletion patterns in a safe environment first
 | 
					- Test deletion patterns in a safe environment first
 | 
				
			||||||
- Ensure proper RBAC permissions are configured
 | 
					- Ensure proper RBAC permissions are configured
 | 
				
			||||||
- Be cautious with cluster-wide operations like namespace listing
 | 
					- Be cautious with cluster-wide operations like namespace listing
 | 
				
			||||||
- Consider using dry-run approaches when possible
 | 
					- Use Kubernetes secrets for sensitive environment variables instead of plain text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Performance & Scalability
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Consider adding resource limits (CPU/memory) for production deployments
 | 
				
			||||||
 | 
					- Use persistent volumes for stateful applications
 | 
				
			||||||
 | 
					- Configure readiness and liveness probes for health checks
 | 
				
			||||||
 | 
					- Implement proper monitoring and logging labels
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Environment Variables Best Practices
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Use Kubernetes secrets for sensitive data (passwords, API keys)
 | 
				
			||||||
 | 
					- Validate environment variable values before deployment
 | 
				
			||||||
 | 
					- Use consistent naming conventions (e.g., `DATABASE_URL`, `API_KEY`)
 | 
				
			||||||
 | 
					- Document required vs optional environment variables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Example: Production-Ready Deployment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					// Production labels for monitoring and management
 | 
				
			||||||
 | 
					let mut labels = HashMap::new();
 | 
				
			||||||
 | 
					labels.insert("app".to_string(), "web-api".to_string());
 | 
				
			||||||
 | 
					labels.insert("version".to_string(), "v1.2.3".to_string());
 | 
				
			||||||
 | 
					labels.insert("environment".to_string(), "production".to_string());
 | 
				
			||||||
 | 
					labels.insert("team".to_string(), "backend".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Non-sensitive environment variables
 | 
				
			||||||
 | 
					let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					env_vars.insert("NODE_ENV".to_string(), "production".to_string());
 | 
				
			||||||
 | 
					env_vars.insert("LOG_LEVEL".to_string(), "info".to_string());
 | 
				
			||||||
 | 
					env_vars.insert("PORT".to_string(), "3000".to_string());
 | 
				
			||||||
 | 
					// Note: Use Kubernetes secrets for DATABASE_URL, API_KEY, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					km.deploy_application("web-api", "myapp:v1.2.3", 3, 3000, Some(labels), Some(env_vars)).await?;
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,97 +0,0 @@
 | 
				
			|||||||
//! Generic Application Deployment Example
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! This example shows how to deploy any containerized application using the
 | 
					 | 
				
			||||||
//! KubernetesManager convenience methods. This works for any Docker image.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use sal_kubernetes::KubernetesManager;
 | 
					 | 
				
			||||||
use std::collections::HashMap;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[tokio::main]
 | 
					 | 
				
			||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
					 | 
				
			||||||
    // Create Kubernetes manager
 | 
					 | 
				
			||||||
    let km = KubernetesManager::new("default").await?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Example 1: Simple web server deployment
 | 
					 | 
				
			||||||
    println!("=== Example 1: Simple Nginx Web Server ===");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    km.deploy_application("web-server", "nginx:latest", 2, 80, None)
 | 
					 | 
				
			||||||
        .await?;
 | 
					 | 
				
			||||||
    println!("✅ Nginx web server deployed!");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Example 2: Node.js application with labels
 | 
					 | 
				
			||||||
    println!("\n=== Example 2: Node.js Application ===");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let mut node_labels = HashMap::new();
 | 
					 | 
				
			||||||
    node_labels.insert("app".to_string(), "node-app".to_string());
 | 
					 | 
				
			||||||
    node_labels.insert("tier".to_string(), "backend".to_string());
 | 
					 | 
				
			||||||
    node_labels.insert("environment".to_string(), "production".to_string());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    km.deploy_application(
 | 
					 | 
				
			||||||
        "node-app",        // name
 | 
					 | 
				
			||||||
        "node:18-alpine",  // image
 | 
					 | 
				
			||||||
        3,                 // replicas - scale to 3 instances
 | 
					 | 
				
			||||||
        3000,              // port
 | 
					 | 
				
			||||||
        Some(node_labels), // labels
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    .await?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    println!("✅ Node.js application deployed!");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Example 3: Database deployment (any database)
 | 
					 | 
				
			||||||
    println!("\n=== Example 3: MongoDB Database ===");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let mut mongo_labels = HashMap::new();
 | 
					 | 
				
			||||||
    mongo_labels.insert("app".to_string(), "mongodb".to_string());
 | 
					 | 
				
			||||||
    mongo_labels.insert("type".to_string(), "database".to_string());
 | 
					 | 
				
			||||||
    mongo_labels.insert("engine".to_string(), "mongodb".to_string());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    km.deploy_application(
 | 
					 | 
				
			||||||
        "mongodb",          // name
 | 
					 | 
				
			||||||
        "mongo:6.0",        // image
 | 
					 | 
				
			||||||
        1,                  // replicas - single instance for simplicity
 | 
					 | 
				
			||||||
        27017,              // port
 | 
					 | 
				
			||||||
        Some(mongo_labels), // labels
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    .await?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    println!("✅ MongoDB deployed!");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Check status of all deployments
 | 
					 | 
				
			||||||
    println!("\n=== Checking Deployment Status ===");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let deployments = km.deployments_list().await?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for deployment in &deployments {
 | 
					 | 
				
			||||||
        if let Some(name) = &deployment.metadata.name {
 | 
					 | 
				
			||||||
            let total_replicas = deployment
 | 
					 | 
				
			||||||
                .spec
 | 
					 | 
				
			||||||
                .as_ref()
 | 
					 | 
				
			||||||
                .and_then(|s| s.replicas)
 | 
					 | 
				
			||||||
                .unwrap_or(0);
 | 
					 | 
				
			||||||
            let ready_replicas = deployment
 | 
					 | 
				
			||||||
                .status
 | 
					 | 
				
			||||||
                .as_ref()
 | 
					 | 
				
			||||||
                .and_then(|s| s.ready_replicas)
 | 
					 | 
				
			||||||
                .unwrap_or(0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            println!(
 | 
					 | 
				
			||||||
                "{}: {}/{} replicas ready",
 | 
					 | 
				
			||||||
                name, ready_replicas, total_replicas
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    println!("\n🎉 All deployments completed!");
 | 
					 | 
				
			||||||
    println!("\n💡 Key Points:");
 | 
					 | 
				
			||||||
    println!("  • Any Docker image can be deployed using this simple interface");
 | 
					 | 
				
			||||||
    println!("  • Use labels to organize and identify your applications");
 | 
					 | 
				
			||||||
    println!(
 | 
					 | 
				
			||||||
        "  • The same method works for databases, web servers, APIs, and any containerized app"
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    println!("  • For advanced configuration, use the individual KubernetesManager methods");
 | 
					 | 
				
			||||||
    println!(
 | 
					 | 
				
			||||||
        "  • Environment variables and resource limits can be added via direct Kubernetes API"
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Ok(())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,73 +0,0 @@
 | 
				
			|||||||
//! PostgreSQL Cluster Deployment Example
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! This example shows how to deploy a PostgreSQL cluster using the
 | 
					 | 
				
			||||||
//! KubernetesManager convenience methods.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use sal_kubernetes::KubernetesManager;
 | 
					 | 
				
			||||||
use std::collections::HashMap;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[tokio::main]
 | 
					 | 
				
			||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
					 | 
				
			||||||
    // Create Kubernetes manager for the database namespace
 | 
					 | 
				
			||||||
    let km = KubernetesManager::new("database").await?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Configure PostgreSQL-specific labels
 | 
					 | 
				
			||||||
    let mut labels = HashMap::new();
 | 
					 | 
				
			||||||
    labels.insert("app".to_string(), "postgres-cluster".to_string());
 | 
					 | 
				
			||||||
    labels.insert("type".to_string(), "database".to_string());
 | 
					 | 
				
			||||||
    labels.insert("engine".to_string(), "postgresql".to_string());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Deploy the PostgreSQL cluster using the convenience method
 | 
					 | 
				
			||||||
    println!("Deploying PostgreSQL cluster...");
 | 
					 | 
				
			||||||
    km.deploy_application(
 | 
					 | 
				
			||||||
        "postgres-cluster", // name
 | 
					 | 
				
			||||||
        "postgres:15",      // image
 | 
					 | 
				
			||||||
        2,                  // replicas (1 master + 1 replica)
 | 
					 | 
				
			||||||
        5432,               // port
 | 
					 | 
				
			||||||
        Some(labels),       // labels
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    .await?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    println!("✅ PostgreSQL cluster deployed successfully!");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Check deployment status
 | 
					 | 
				
			||||||
    let deployments = km.deployments_list().await?;
 | 
					 | 
				
			||||||
    let postgres_deployment = deployments
 | 
					 | 
				
			||||||
        .iter()
 | 
					 | 
				
			||||||
        .find(|d| d.metadata.name.as_ref() == Some(&"postgres-cluster".to_string()));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if let Some(deployment) = postgres_deployment {
 | 
					 | 
				
			||||||
        let total_replicas = deployment
 | 
					 | 
				
			||||||
            .spec
 | 
					 | 
				
			||||||
            .as_ref()
 | 
					 | 
				
			||||||
            .and_then(|s| s.replicas)
 | 
					 | 
				
			||||||
            .unwrap_or(0);
 | 
					 | 
				
			||||||
        let ready_replicas = deployment
 | 
					 | 
				
			||||||
            .status
 | 
					 | 
				
			||||||
            .as_ref()
 | 
					 | 
				
			||||||
            .and_then(|s| s.ready_replicas)
 | 
					 | 
				
			||||||
            .unwrap_or(0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        println!(
 | 
					 | 
				
			||||||
            "Deployment status: {}/{} replicas ready",
 | 
					 | 
				
			||||||
            ready_replicas, total_replicas
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    println!("\n📋 Connection Information:");
 | 
					 | 
				
			||||||
    println!("  Host: postgres-cluster.database.svc.cluster.local");
 | 
					 | 
				
			||||||
    println!("  Port: 5432");
 | 
					 | 
				
			||||||
    println!("  Database: postgres (default)");
 | 
					 | 
				
			||||||
    println!("  Username: postgres (default)");
 | 
					 | 
				
			||||||
    println!("  Password: Set POSTGRES_PASSWORD environment variable");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    println!("\n🔧 To connect from another pod:");
 | 
					 | 
				
			||||||
    println!("  psql -h postgres-cluster.database.svc.cluster.local -U postgres");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    println!("\n💡 Next steps:");
 | 
					 | 
				
			||||||
    println!("  • Set environment variables for database credentials");
 | 
					 | 
				
			||||||
    println!("  • Add persistent volume claims for data storage");
 | 
					 | 
				
			||||||
    println!("  • Configure backup and monitoring");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Ok(())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,72 +0,0 @@
 | 
				
			|||||||
//! Redis Cluster Deployment Example
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! This example shows how to deploy a Redis cluster using the
 | 
					 | 
				
			||||||
//! KubernetesManager convenience methods.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use sal_kubernetes::KubernetesManager;
 | 
					 | 
				
			||||||
use std::collections::HashMap;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[tokio::main]
 | 
					 | 
				
			||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
					 | 
				
			||||||
    // Create Kubernetes manager for the cache namespace
 | 
					 | 
				
			||||||
    let km = KubernetesManager::new("cache").await?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Configure Redis-specific labels
 | 
					 | 
				
			||||||
    let mut labels = HashMap::new();
 | 
					 | 
				
			||||||
    labels.insert("app".to_string(), "redis-cluster".to_string());
 | 
					 | 
				
			||||||
    labels.insert("type".to_string(), "cache".to_string());
 | 
					 | 
				
			||||||
    labels.insert("engine".to_string(), "redis".to_string());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Deploy the Redis cluster using the convenience method
 | 
					 | 
				
			||||||
    println!("Deploying Redis cluster...");
 | 
					 | 
				
			||||||
    km.deploy_application(
 | 
					 | 
				
			||||||
        "redis-cluster",  // name
 | 
					 | 
				
			||||||
        "redis:7-alpine", // image
 | 
					 | 
				
			||||||
        3,                // replicas (Redis cluster nodes)
 | 
					 | 
				
			||||||
        6379,             // port
 | 
					 | 
				
			||||||
        Some(labels),     // labels
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    .await?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    println!("✅ Redis cluster deployed successfully!");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Check deployment status
 | 
					 | 
				
			||||||
    let deployments = km.deployments_list().await?;
 | 
					 | 
				
			||||||
    let redis_deployment = deployments
 | 
					 | 
				
			||||||
        .iter()
 | 
					 | 
				
			||||||
        .find(|d| d.metadata.name.as_ref() == Some(&"redis-cluster".to_string()));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if let Some(deployment) = redis_deployment {
 | 
					 | 
				
			||||||
        let total_replicas = deployment
 | 
					 | 
				
			||||||
            .spec
 | 
					 | 
				
			||||||
            .as_ref()
 | 
					 | 
				
			||||||
            .and_then(|s| s.replicas)
 | 
					 | 
				
			||||||
            .unwrap_or(0);
 | 
					 | 
				
			||||||
        let ready_replicas = deployment
 | 
					 | 
				
			||||||
            .status
 | 
					 | 
				
			||||||
            .as_ref()
 | 
					 | 
				
			||||||
            .and_then(|s| s.ready_replicas)
 | 
					 | 
				
			||||||
            .unwrap_or(0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        println!(
 | 
					 | 
				
			||||||
            "Deployment status: {}/{} replicas ready",
 | 
					 | 
				
			||||||
            ready_replicas, total_replicas
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    println!("\n📋 Connection Information:");
 | 
					 | 
				
			||||||
    println!("  Host: redis-cluster.cache.svc.cluster.local");
 | 
					 | 
				
			||||||
    println!("  Port: 6379");
 | 
					 | 
				
			||||||
    println!("  Password: Configure REDIS_PASSWORD environment variable");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    println!("\n🔧 To connect from another pod:");
 | 
					 | 
				
			||||||
    println!("  redis-cli -h redis-cluster.cache.svc.cluster.local");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    println!("\n💡 Next steps:");
 | 
					 | 
				
			||||||
    println!("  • Configure Redis authentication with environment variables");
 | 
					 | 
				
			||||||
    println!("  • Set up Redis clustering configuration");
 | 
					 | 
				
			||||||
    println!("  • Add persistent volume claims for data persistence");
 | 
					 | 
				
			||||||
    println!("  • Configure memory limits and eviction policies");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Ok(())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -85,7 +85,7 @@ impl KubernetesManager {
 | 
				
			|||||||
            .map_err(|e| Self::create_user_friendly_config_error(kube::Error::InferConfig(e)))?;
 | 
					            .map_err(|e| Self::create_user_friendly_config_error(kube::Error::InferConfig(e)))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let client = Client::try_from(k8s_config).map_err(|e| {
 | 
					        let client = Client::try_from(k8s_config).map_err(|e| {
 | 
				
			||||||
            KubernetesError::config_error(format!("Failed to create Kubernetes client: {}", e))
 | 
					            KubernetesError::config_error(format!("Failed to create Kubernetes client: {e}"))
 | 
				
			||||||
        })?;
 | 
					        })?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Validate cluster connectivity
 | 
					        // Validate cluster connectivity
 | 
				
			||||||
@@ -143,8 +143,7 @@ impl KubernetesManager {
 | 
				
			|||||||
                 1. A running Kubernetes cluster\n\
 | 
					                 1. A running Kubernetes cluster\n\
 | 
				
			||||||
                 2. Valid kubectl configuration\n\
 | 
					                 2. Valid kubectl configuration\n\
 | 
				
			||||||
                 3. Proper access permissions\n\n\
 | 
					                 3. Proper access permissions\n\n\
 | 
				
			||||||
                 Original error: {}",
 | 
					                 Original error: {error}"
 | 
				
			||||||
                error
 | 
					 | 
				
			||||||
            ))
 | 
					            ))
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -181,12 +180,11 @@ impl KubernetesManager {
 | 
				
			|||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    Err(KubernetesError::config_error(format!(
 | 
					                    Err(KubernetesError::config_error(format!(
 | 
				
			||||||
                        "❌ Failed to connect to Kubernetes cluster!\n\n\
 | 
					                        "❌ Failed to connect to Kubernetes cluster!\n\n\
 | 
				
			||||||
                         Error: {}\n\n\
 | 
					                         Error: {error_msg}\n\n\
 | 
				
			||||||
                         Please verify:\n\
 | 
					                         Please verify:\n\
 | 
				
			||||||
                         1. Cluster is running: `kubectl get nodes`\n\
 | 
					                         1. Cluster is running: `kubectl get nodes`\n\
 | 
				
			||||||
                         2. Network connectivity\n\
 | 
					                         2. Network connectivity\n\
 | 
				
			||||||
                         3. Authentication credentials",
 | 
					                         3. Authentication credentials"
 | 
				
			||||||
                        error_msg
 | 
					 | 
				
			||||||
                    )))
 | 
					                    )))
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -233,16 +231,16 @@ impl KubernetesManager {
 | 
				
			|||||||
                        KubernetesError::ApiError(kube_err) => {
 | 
					                        KubernetesError::ApiError(kube_err) => {
 | 
				
			||||||
                            // Retry on transient errors
 | 
					                            // Retry on transient errors
 | 
				
			||||||
                            if is_retryable_error(kube_err) {
 | 
					                            if is_retryable_error(kube_err) {
 | 
				
			||||||
                                log::warn!("Retryable error encountered: {}", e);
 | 
					                                log::warn!("Retryable error encountered: {e}");
 | 
				
			||||||
                                e
 | 
					                                e
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                log::error!("Non-retryable error: {}", e);
 | 
					                                log::error!("Non-retryable error: {e}");
 | 
				
			||||||
                                // Convert to a non-retryable error type
 | 
					                                // Convert to a non-retryable error type
 | 
				
			||||||
                                KubernetesError::operation_error(format!("Non-retryable: {}", e))
 | 
					                                KubernetesError::operation_error(format!("Non-retryable: {e}"))
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        _ => {
 | 
					                        _ => {
 | 
				
			||||||
                            log::warn!("Retrying operation due to error: {}", e);
 | 
					                            log::warn!("Retrying operation due to error: {e}");
 | 
				
			||||||
                            e
 | 
					                            e
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -393,7 +391,7 @@ impl KubernetesManager {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let created_configmap = configmaps.create(&Default::default(), &configmap).await?;
 | 
					        let created_configmap = configmaps.create(&Default::default(), &configmap).await?;
 | 
				
			||||||
        log::info!("Created ConfigMap '{}'", name);
 | 
					        log::info!("Created ConfigMap '{name}'");
 | 
				
			||||||
        Ok(created_configmap)
 | 
					        Ok(created_configmap)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -459,7 +457,7 @@ impl KubernetesManager {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let created_secret = secrets.create(&Default::default(), &secret).await?;
 | 
					        let created_secret = secrets.create(&Default::default(), &secret).await?;
 | 
				
			||||||
        log::info!("Created Secret '{}'", name);
 | 
					        log::info!("Created Secret '{name}'");
 | 
				
			||||||
        Ok(created_secret)
 | 
					        Ok(created_secret)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -483,7 +481,7 @@ impl KubernetesManager {
 | 
				
			|||||||
                // Check if namespace already exists
 | 
					                // Check if namespace already exists
 | 
				
			||||||
                match namespaces.get(&name).await {
 | 
					                match namespaces.get(&name).await {
 | 
				
			||||||
                    Ok(_) => {
 | 
					                    Ok(_) => {
 | 
				
			||||||
                        log::info!("Namespace '{}' already exists", name);
 | 
					                        log::info!("Namespace '{name}' already exists");
 | 
				
			||||||
                        return Ok(());
 | 
					                        return Ok(());
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Err(kube::Error::Api(api_err)) if api_err.code == 404 => {
 | 
					                    Err(kube::Error::Api(api_err)) if api_err.code == 404 => {
 | 
				
			||||||
@@ -502,7 +500,7 @@ impl KubernetesManager {
 | 
				
			|||||||
                };
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                namespaces.create(&Default::default(), &namespace).await?;
 | 
					                namespaces.create(&Default::default(), &namespace).await?;
 | 
				
			||||||
                log::info!("Created namespace '{}'", name);
 | 
					                log::info!("Created namespace '{name}'");
 | 
				
			||||||
                Ok(())
 | 
					                Ok(())
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
@@ -544,12 +542,8 @@ impl KubernetesManager {
 | 
				
			|||||||
        match self.delete_pods_matching(®ex).await {
 | 
					        match self.delete_pods_matching(®ex).await {
 | 
				
			||||||
            Ok(count) => deleted_count += count,
 | 
					            Ok(count) => deleted_count += count,
 | 
				
			||||||
            Err(e) => {
 | 
					            Err(e) => {
 | 
				
			||||||
                log::error!(
 | 
					                log::error!("Failed to delete pods matching pattern '{pattern}': {e}");
 | 
				
			||||||
                    "Failed to delete pods matching pattern '{}': {}",
 | 
					                failed_deletions.push(format!("pods: {e}"));
 | 
				
			||||||
                    pattern,
 | 
					 | 
				
			||||||
                    e
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                failed_deletions.push(format!("pods: {}", e));
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -557,12 +551,8 @@ impl KubernetesManager {
 | 
				
			|||||||
        match self.delete_services_matching(®ex).await {
 | 
					        match self.delete_services_matching(®ex).await {
 | 
				
			||||||
            Ok(count) => deleted_count += count,
 | 
					            Ok(count) => deleted_count += count,
 | 
				
			||||||
            Err(e) => {
 | 
					            Err(e) => {
 | 
				
			||||||
                log::error!(
 | 
					                log::error!("Failed to delete services matching pattern '{pattern}': {e}");
 | 
				
			||||||
                    "Failed to delete services matching pattern '{}': {}",
 | 
					                failed_deletions.push(format!("services: {e}"));
 | 
				
			||||||
                    pattern,
 | 
					 | 
				
			||||||
                    e
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                failed_deletions.push(format!("services: {}", e));
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -570,12 +560,8 @@ impl KubernetesManager {
 | 
				
			|||||||
        match self.delete_deployments_matching(®ex).await {
 | 
					        match self.delete_deployments_matching(®ex).await {
 | 
				
			||||||
            Ok(count) => deleted_count += count,
 | 
					            Ok(count) => deleted_count += count,
 | 
				
			||||||
            Err(e) => {
 | 
					            Err(e) => {
 | 
				
			||||||
                log::error!(
 | 
					                log::error!("Failed to delete deployments matching pattern '{pattern}': {e}");
 | 
				
			||||||
                    "Failed to delete deployments matching pattern '{}': {}",
 | 
					                failed_deletions.push(format!("deployments: {e}"));
 | 
				
			||||||
                    pattern,
 | 
					 | 
				
			||||||
                    e
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                failed_deletions.push(format!("deployments: {}", e));
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -583,12 +569,8 @@ impl KubernetesManager {
 | 
				
			|||||||
        match self.delete_configmaps_matching(®ex).await {
 | 
					        match self.delete_configmaps_matching(®ex).await {
 | 
				
			||||||
            Ok(count) => deleted_count += count,
 | 
					            Ok(count) => deleted_count += count,
 | 
				
			||||||
            Err(e) => {
 | 
					            Err(e) => {
 | 
				
			||||||
                log::error!(
 | 
					                log::error!("Failed to delete configmaps matching pattern '{pattern}': {e}");
 | 
				
			||||||
                    "Failed to delete configmaps matching pattern '{}': {}",
 | 
					                failed_deletions.push(format!("configmaps: {e}"));
 | 
				
			||||||
                    pattern,
 | 
					 | 
				
			||||||
                    e
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                failed_deletions.push(format!("configmaps: {}", e));
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -596,12 +578,8 @@ impl KubernetesManager {
 | 
				
			|||||||
        match self.delete_secrets_matching(®ex).await {
 | 
					        match self.delete_secrets_matching(®ex).await {
 | 
				
			||||||
            Ok(count) => deleted_count += count,
 | 
					            Ok(count) => deleted_count += count,
 | 
				
			||||||
            Err(e) => {
 | 
					            Err(e) => {
 | 
				
			||||||
                log::error!(
 | 
					                log::error!("Failed to delete secrets matching pattern '{pattern}': {e}");
 | 
				
			||||||
                    "Failed to delete secrets matching pattern '{}': {}",
 | 
					                failed_deletions.push(format!("secrets: {e}"));
 | 
				
			||||||
                    pattern,
 | 
					 | 
				
			||||||
                    e
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                failed_deletions.push(format!("secrets: {}", e));
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -640,11 +618,11 @@ impl KubernetesManager {
 | 
				
			|||||||
                if regex.is_match(name) {
 | 
					                if regex.is_match(name) {
 | 
				
			||||||
                    match pods.delete(name, &Default::default()).await {
 | 
					                    match pods.delete(name, &Default::default()).await {
 | 
				
			||||||
                        Ok(_) => {
 | 
					                        Ok(_) => {
 | 
				
			||||||
                            log::info!("Deleted pod '{}'", name);
 | 
					                            log::info!("Deleted pod '{name}'");
 | 
				
			||||||
                            deleted += 1;
 | 
					                            deleted += 1;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        Err(e) => {
 | 
					                        Err(e) => {
 | 
				
			||||||
                            log::error!("Failed to delete pod '{}': {}", name, e);
 | 
					                            log::error!("Failed to delete pod '{name}': {e}");
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -665,11 +643,11 @@ impl KubernetesManager {
 | 
				
			|||||||
                if regex.is_match(name) {
 | 
					                if regex.is_match(name) {
 | 
				
			||||||
                    match services.delete(name, &Default::default()).await {
 | 
					                    match services.delete(name, &Default::default()).await {
 | 
				
			||||||
                        Ok(_) => {
 | 
					                        Ok(_) => {
 | 
				
			||||||
                            log::info!("Deleted service '{}'", name);
 | 
					                            log::info!("Deleted service '{name}'");
 | 
				
			||||||
                            deleted += 1;
 | 
					                            deleted += 1;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        Err(e) => {
 | 
					                        Err(e) => {
 | 
				
			||||||
                            log::error!("Failed to delete service '{}': {}", name, e);
 | 
					                            log::error!("Failed to delete service '{name}': {e}");
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -690,11 +668,11 @@ impl KubernetesManager {
 | 
				
			|||||||
                if regex.is_match(name) {
 | 
					                if regex.is_match(name) {
 | 
				
			||||||
                    match deployments.delete(name, &Default::default()).await {
 | 
					                    match deployments.delete(name, &Default::default()).await {
 | 
				
			||||||
                        Ok(_) => {
 | 
					                        Ok(_) => {
 | 
				
			||||||
                            log::info!("Deleted deployment '{}'", name);
 | 
					                            log::info!("Deleted deployment '{name}'");
 | 
				
			||||||
                            deleted += 1;
 | 
					                            deleted += 1;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        Err(e) => {
 | 
					                        Err(e) => {
 | 
				
			||||||
                            log::error!("Failed to delete deployment '{}': {}", name, e);
 | 
					                            log::error!("Failed to delete deployment '{name}': {e}");
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -715,11 +693,11 @@ impl KubernetesManager {
 | 
				
			|||||||
                if regex.is_match(name) {
 | 
					                if regex.is_match(name) {
 | 
				
			||||||
                    match configmaps.delete(name, &Default::default()).await {
 | 
					                    match configmaps.delete(name, &Default::default()).await {
 | 
				
			||||||
                        Ok(_) => {
 | 
					                        Ok(_) => {
 | 
				
			||||||
                            log::info!("Deleted configmap '{}'", name);
 | 
					                            log::info!("Deleted configmap '{name}'");
 | 
				
			||||||
                            deleted += 1;
 | 
					                            deleted += 1;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        Err(e) => {
 | 
					                        Err(e) => {
 | 
				
			||||||
                            log::error!("Failed to delete configmap '{}': {}", name, e);
 | 
					                            log::error!("Failed to delete configmap '{name}': {e}");
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -740,11 +718,11 @@ impl KubernetesManager {
 | 
				
			|||||||
                if regex.is_match(name) {
 | 
					                if regex.is_match(name) {
 | 
				
			||||||
                    match secrets.delete(name, &Default::default()).await {
 | 
					                    match secrets.delete(name, &Default::default()).await {
 | 
				
			||||||
                        Ok(_) => {
 | 
					                        Ok(_) => {
 | 
				
			||||||
                            log::info!("Deleted secret '{}'", name);
 | 
					                            log::info!("Deleted secret '{name}'");
 | 
				
			||||||
                            deleted += 1;
 | 
					                            deleted += 1;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        Err(e) => {
 | 
					                        Err(e) => {
 | 
				
			||||||
                            log::error!("Failed to delete secret '{}': {}", name, e);
 | 
					                            log::error!("Failed to delete secret '{name}': {e}");
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -761,6 +739,7 @@ impl KubernetesManager {
 | 
				
			|||||||
    /// * `name` - The name of the pod
 | 
					    /// * `name` - The name of the pod
 | 
				
			||||||
    /// * `image` - The container image to use
 | 
					    /// * `image` - The container image to use
 | 
				
			||||||
    /// * `labels` - Optional labels for the pod
 | 
					    /// * `labels` - Optional labels for the pod
 | 
				
			||||||
 | 
					    /// * `env_vars` - Optional environment variables for the container
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// # Returns
 | 
					    /// # Returns
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
@@ -779,7 +758,7 @@ impl KubernetesManager {
 | 
				
			|||||||
    ///     let mut labels = HashMap::new();
 | 
					    ///     let mut labels = HashMap::new();
 | 
				
			||||||
    ///     labels.insert("app".to_string(), "my-app".to_string());
 | 
					    ///     labels.insert("app".to_string(), "my-app".to_string());
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    ///     let pod = km.pod_create("my-pod", "nginx:latest", Some(labels)).await?;
 | 
					    ///     let pod = km.pod_create("my-pod", "nginx:latest", Some(labels), None).await?;
 | 
				
			||||||
    ///     println!("Created pod: {}", pod.metadata.name.unwrap_or_default());
 | 
					    ///     println!("Created pod: {}", pod.metadata.name.unwrap_or_default());
 | 
				
			||||||
    ///     Ok(())
 | 
					    ///     Ok(())
 | 
				
			||||||
    /// }
 | 
					    /// }
 | 
				
			||||||
@@ -789,6 +768,7 @@ impl KubernetesManager {
 | 
				
			|||||||
        name: &str,
 | 
					        name: &str,
 | 
				
			||||||
        image: &str,
 | 
					        image: &str,
 | 
				
			||||||
        labels: Option<HashMap<String, String>>,
 | 
					        labels: Option<HashMap<String, String>>,
 | 
				
			||||||
 | 
					        env_vars: Option<HashMap<String, String>>,
 | 
				
			||||||
    ) -> KubernetesResult<Pod> {
 | 
					    ) -> KubernetesResult<Pod> {
 | 
				
			||||||
        use k8s_openapi::api::core::v1::{Container, PodSpec};
 | 
					        use k8s_openapi::api::core::v1::{Container, PodSpec};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -802,10 +782,29 @@ impl KubernetesManager {
 | 
				
			|||||||
                ..Default::default()
 | 
					                ..Default::default()
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            spec: Some(PodSpec {
 | 
					            spec: Some(PodSpec {
 | 
				
			||||||
                containers: vec![Container {
 | 
					                containers: vec![{
 | 
				
			||||||
 | 
					                    let mut container = Container {
 | 
				
			||||||
                        name: name.to_string(),
 | 
					                        name: name.to_string(),
 | 
				
			||||||
                        image: Some(image.to_string()),
 | 
					                        image: Some(image.to_string()),
 | 
				
			||||||
                        ..Default::default()
 | 
					                        ..Default::default()
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Add environment variables if provided
 | 
				
			||||||
 | 
					                    if let Some(env_vars) = env_vars {
 | 
				
			||||||
 | 
					                        use k8s_openapi::api::core::v1::EnvVar;
 | 
				
			||||||
 | 
					                        container.env = Some(
 | 
				
			||||||
 | 
					                            env_vars
 | 
				
			||||||
 | 
					                                .into_iter()
 | 
				
			||||||
 | 
					                                .map(|(key, value)| EnvVar {
 | 
				
			||||||
 | 
					                                    name: key,
 | 
				
			||||||
 | 
					                                    value: Some(value),
 | 
				
			||||||
 | 
					                                    ..Default::default()
 | 
				
			||||||
 | 
					                                })
 | 
				
			||||||
 | 
					                                .collect(),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    container
 | 
				
			||||||
                }],
 | 
					                }],
 | 
				
			||||||
                ..Default::default()
 | 
					                ..Default::default()
 | 
				
			||||||
            }),
 | 
					            }),
 | 
				
			||||||
@@ -813,7 +812,7 @@ impl KubernetesManager {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let created_pod = pods.create(&Default::default(), &pod).await?;
 | 
					        let created_pod = pods.create(&Default::default(), &pod).await?;
 | 
				
			||||||
        log::info!("Created pod '{}' with image '{}'", name, image);
 | 
					        log::info!("Created pod '{name}' with image '{image}'");
 | 
				
			||||||
        Ok(created_pod)
 | 
					        Ok(created_pod)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -894,7 +893,7 @@ impl KubernetesManager {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let created_service = services.create(&Default::default(), &service).await?;
 | 
					        let created_service = services.create(&Default::default(), &service).await?;
 | 
				
			||||||
        log::info!("Created service '{}' on port {}", name, port);
 | 
					        log::info!("Created service '{name}' on port {port}");
 | 
				
			||||||
        Ok(created_service)
 | 
					        Ok(created_service)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -939,7 +938,7 @@ impl KubernetesManager {
 | 
				
			|||||||
    ///     let mut labels = HashMap::new();
 | 
					    ///     let mut labels = HashMap::new();
 | 
				
			||||||
    ///     labels.insert("app".to_string(), "my-app".to_string());
 | 
					    ///     labels.insert("app".to_string(), "my-app".to_string());
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    ///     let deployment = km.deployment_create("my-deployment", "nginx:latest", 3, Some(labels)).await?;
 | 
					    ///     let deployment = km.deployment_create("my-deployment", "nginx:latest", 3, Some(labels), None).await?;
 | 
				
			||||||
    ///     println!("Created deployment: {}", deployment.metadata.name.unwrap_or_default());
 | 
					    ///     println!("Created deployment: {}", deployment.metadata.name.unwrap_or_default());
 | 
				
			||||||
    ///     Ok(())
 | 
					    ///     Ok(())
 | 
				
			||||||
    /// }
 | 
					    /// }
 | 
				
			||||||
@@ -950,6 +949,7 @@ impl KubernetesManager {
 | 
				
			|||||||
        image: &str,
 | 
					        image: &str,
 | 
				
			||||||
        replicas: i32,
 | 
					        replicas: i32,
 | 
				
			||||||
        labels: Option<HashMap<String, String>>,
 | 
					        labels: Option<HashMap<String, String>>,
 | 
				
			||||||
 | 
					        env_vars: Option<HashMap<String, String>>,
 | 
				
			||||||
    ) -> KubernetesResult<Deployment> {
 | 
					    ) -> KubernetesResult<Deployment> {
 | 
				
			||||||
        use k8s_openapi::api::apps::v1::DeploymentSpec;
 | 
					        use k8s_openapi::api::apps::v1::DeploymentSpec;
 | 
				
			||||||
        use k8s_openapi::api::core::v1::{Container, PodSpec, PodTemplateSpec};
 | 
					        use k8s_openapi::api::core::v1::{Container, PodSpec, PodTemplateSpec};
 | 
				
			||||||
@@ -985,10 +985,29 @@ impl KubernetesManager {
 | 
				
			|||||||
                        ..Default::default()
 | 
					                        ..Default::default()
 | 
				
			||||||
                    }),
 | 
					                    }),
 | 
				
			||||||
                    spec: Some(PodSpec {
 | 
					                    spec: Some(PodSpec {
 | 
				
			||||||
                        containers: vec![Container {
 | 
					                        containers: vec![{
 | 
				
			||||||
 | 
					                            let mut container = Container {
 | 
				
			||||||
                                name: name.to_string(),
 | 
					                                name: name.to_string(),
 | 
				
			||||||
                                image: Some(image.to_string()),
 | 
					                                image: Some(image.to_string()),
 | 
				
			||||||
                                ..Default::default()
 | 
					                                ..Default::default()
 | 
				
			||||||
 | 
					                            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            // Add environment variables if provided
 | 
				
			||||||
 | 
					                            if let Some(env_vars) = env_vars {
 | 
				
			||||||
 | 
					                                use k8s_openapi::api::core::v1::EnvVar;
 | 
				
			||||||
 | 
					                                container.env = Some(
 | 
				
			||||||
 | 
					                                    env_vars
 | 
				
			||||||
 | 
					                                        .into_iter()
 | 
				
			||||||
 | 
					                                        .map(|(key, value)| EnvVar {
 | 
				
			||||||
 | 
					                                            name: key,
 | 
				
			||||||
 | 
					                                            value: Some(value),
 | 
				
			||||||
 | 
					                                            ..Default::default()
 | 
				
			||||||
 | 
					                                        })
 | 
				
			||||||
 | 
					                                        .collect(),
 | 
				
			||||||
 | 
					                                );
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            container
 | 
				
			||||||
                        }],
 | 
					                        }],
 | 
				
			||||||
                        ..Default::default()
 | 
					                        ..Default::default()
 | 
				
			||||||
                    }),
 | 
					                    }),
 | 
				
			||||||
@@ -999,12 +1018,7 @@ impl KubernetesManager {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let created_deployment = deployments.create(&Default::default(), &deployment).await?;
 | 
					        let created_deployment = deployments.create(&Default::default(), &deployment).await?;
 | 
				
			||||||
        log::info!(
 | 
					        log::info!("Created deployment '{name}' with {replicas} replicas using image '{image}'");
 | 
				
			||||||
            "Created deployment '{}' with {} replicas using image '{}'",
 | 
					 | 
				
			||||||
            name,
 | 
					 | 
				
			||||||
            replicas,
 | 
					 | 
				
			||||||
            image
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        Ok(created_deployment)
 | 
					        Ok(created_deployment)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1035,7 +1049,7 @@ impl KubernetesManager {
 | 
				
			|||||||
    pub async fn pod_delete(&self, name: &str) -> KubernetesResult<()> {
 | 
					    pub async fn pod_delete(&self, name: &str) -> KubernetesResult<()> {
 | 
				
			||||||
        let pods: Api<Pod> = Api::namespaced(self.client.clone(), &self.namespace);
 | 
					        let pods: Api<Pod> = Api::namespaced(self.client.clone(), &self.namespace);
 | 
				
			||||||
        pods.delete(name, &Default::default()).await?;
 | 
					        pods.delete(name, &Default::default()).await?;
 | 
				
			||||||
        log::info!("Deleted pod '{}'", name);
 | 
					        log::info!("Deleted pod '{name}'");
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1051,7 +1065,7 @@ impl KubernetesManager {
 | 
				
			|||||||
    pub async fn service_delete(&self, name: &str) -> KubernetesResult<()> {
 | 
					    pub async fn service_delete(&self, name: &str) -> KubernetesResult<()> {
 | 
				
			||||||
        let services: Api<Service> = Api::namespaced(self.client.clone(), &self.namespace);
 | 
					        let services: Api<Service> = Api::namespaced(self.client.clone(), &self.namespace);
 | 
				
			||||||
        services.delete(name, &Default::default()).await?;
 | 
					        services.delete(name, &Default::default()).await?;
 | 
				
			||||||
        log::info!("Deleted service '{}'", name);
 | 
					        log::info!("Deleted service '{name}'");
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1067,7 +1081,7 @@ impl KubernetesManager {
 | 
				
			|||||||
    pub async fn deployment_delete(&self, name: &str) -> KubernetesResult<()> {
 | 
					    pub async fn deployment_delete(&self, name: &str) -> KubernetesResult<()> {
 | 
				
			||||||
        let deployments: Api<Deployment> = Api::namespaced(self.client.clone(), &self.namespace);
 | 
					        let deployments: Api<Deployment> = Api::namespaced(self.client.clone(), &self.namespace);
 | 
				
			||||||
        deployments.delete(name, &Default::default()).await?;
 | 
					        deployments.delete(name, &Default::default()).await?;
 | 
				
			||||||
        log::info!("Deleted deployment '{}'", name);
 | 
					        log::info!("Deleted deployment '{name}'");
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1083,7 +1097,7 @@ impl KubernetesManager {
 | 
				
			|||||||
    pub async fn configmap_delete(&self, name: &str) -> KubernetesResult<()> {
 | 
					    pub async fn configmap_delete(&self, name: &str) -> KubernetesResult<()> {
 | 
				
			||||||
        let configmaps: Api<ConfigMap> = Api::namespaced(self.client.clone(), &self.namespace);
 | 
					        let configmaps: Api<ConfigMap> = Api::namespaced(self.client.clone(), &self.namespace);
 | 
				
			||||||
        configmaps.delete(name, &Default::default()).await?;
 | 
					        configmaps.delete(name, &Default::default()).await?;
 | 
				
			||||||
        log::info!("Deleted ConfigMap '{}'", name);
 | 
					        log::info!("Deleted ConfigMap '{name}'");
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1099,7 +1113,7 @@ impl KubernetesManager {
 | 
				
			|||||||
    pub async fn secret_delete(&self, name: &str) -> KubernetesResult<()> {
 | 
					    pub async fn secret_delete(&self, name: &str) -> KubernetesResult<()> {
 | 
				
			||||||
        let secrets: Api<Secret> = Api::namespaced(self.client.clone(), &self.namespace);
 | 
					        let secrets: Api<Secret> = Api::namespaced(self.client.clone(), &self.namespace);
 | 
				
			||||||
        secrets.delete(name, &Default::default()).await?;
 | 
					        secrets.delete(name, &Default::default()).await?;
 | 
				
			||||||
        log::info!("Deleted Secret '{}'", name);
 | 
					        log::info!("Deleted Secret '{name}'");
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1193,13 +1207,10 @@ impl KubernetesManager {
 | 
				
			|||||||
        let namespaces: Api<Namespace> = Api::all(self.client.clone());
 | 
					        let namespaces: Api<Namespace> = Api::all(self.client.clone());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Log warning about destructive operation
 | 
					        // Log warning about destructive operation
 | 
				
			||||||
        log::warn!(
 | 
					        log::warn!("🚨 DESTRUCTIVE OPERATION: Deleting namespace '{name}' and ALL its resources!");
 | 
				
			||||||
            "🚨 DESTRUCTIVE OPERATION: Deleting namespace '{}' and ALL its resources!",
 | 
					 | 
				
			||||||
            name
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        namespaces.delete(name, &Default::default()).await?;
 | 
					        namespaces.delete(name, &Default::default()).await?;
 | 
				
			||||||
        log::info!("Deleted namespace '{}'", name);
 | 
					        log::info!("Deleted namespace '{name}'");
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1233,7 +1244,7 @@ impl KubernetesManager {
 | 
				
			|||||||
    ///     let mut labels = HashMap::new();
 | 
					    ///     let mut labels = HashMap::new();
 | 
				
			||||||
    ///     labels.insert("app".to_string(), "my-app".to_string());
 | 
					    ///     labels.insert("app".to_string(), "my-app".to_string());
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    ///     km.deploy_application("my-app", "node:18", 3, 3000, Some(labels)).await?;
 | 
					    ///     km.deploy_application("my-app", "node:18", 3, 3000, Some(labels), None).await?;
 | 
				
			||||||
    ///     Ok(())
 | 
					    ///     Ok(())
 | 
				
			||||||
    /// }
 | 
					    /// }
 | 
				
			||||||
    /// ```
 | 
					    /// ```
 | 
				
			||||||
@@ -1244,11 +1255,12 @@ impl KubernetesManager {
 | 
				
			|||||||
        replicas: i32,
 | 
					        replicas: i32,
 | 
				
			||||||
        port: i32,
 | 
					        port: i32,
 | 
				
			||||||
        labels: Option<HashMap<String, String>>,
 | 
					        labels: Option<HashMap<String, String>>,
 | 
				
			||||||
 | 
					        env_vars: Option<HashMap<String, String>>,
 | 
				
			||||||
    ) -> KubernetesResult<()> {
 | 
					    ) -> KubernetesResult<()> {
 | 
				
			||||||
        log::info!("Deploying application '{}' with image '{}'", name, image);
 | 
					        log::info!("Deploying application '{name}' with image '{image}'");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Create deployment
 | 
					        // Create deployment with environment variables
 | 
				
			||||||
        self.deployment_create(name, image, replicas, labels.clone())
 | 
					        self.deployment_create(name, image, replicas, labels.clone(), env_vars)
 | 
				
			||||||
            .await?;
 | 
					            .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Create service selector - use app=name if no labels provided
 | 
					        // Create service selector - use app=name if no labels provided
 | 
				
			||||||
@@ -1264,7 +1276,7 @@ impl KubernetesManager {
 | 
				
			|||||||
        self.service_create(name, selector, port, Some(port))
 | 
					        self.service_create(name, selector, port, Some(port))
 | 
				
			||||||
            .await?;
 | 
					            .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        log::info!("Successfully deployed application '{}'", name);
 | 
					        log::info!("Successfully deployed application '{name}'");
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,28 +4,59 @@
 | 
				
			|||||||
//! enabling scripting access to Kubernetes operations.
 | 
					//! enabling scripting access to Kubernetes operations.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{KubernetesError, KubernetesManager};
 | 
					use crate::{KubernetesError, KubernetesManager};
 | 
				
			||||||
 | 
					use once_cell::sync::Lazy;
 | 
				
			||||||
use rhai::{Array, Dynamic, Engine, EvalAltResult, Map};
 | 
					use rhai::{Array, Dynamic, Engine, EvalAltResult, Map};
 | 
				
			||||||
 | 
					use std::sync::Mutex;
 | 
				
			||||||
 | 
					use tokio::runtime::Runtime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Global Tokio runtime for blocking async operations
 | 
				
			||||||
 | 
					static RUNTIME: Lazy<Mutex<Runtime>> =
 | 
				
			||||||
 | 
					    Lazy::new(|| Mutex::new(Runtime::new().expect("Failed to create Tokio runtime")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Helper function to convert Rhai Map to HashMap for environment variables
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Arguments
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// * `rhai_map` - Rhai Map containing key-value pairs
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Returns
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// * `Option<std::collections::HashMap<String, String>>` - Converted HashMap or None if empty
 | 
				
			||||||
 | 
					fn convert_rhai_map_to_env_vars(
 | 
				
			||||||
 | 
					    rhai_map: Map,
 | 
				
			||||||
 | 
					) -> Option<std::collections::HashMap<String, String>> {
 | 
				
			||||||
 | 
					    if rhai_map.is_empty() {
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        Some(
 | 
				
			||||||
 | 
					            rhai_map
 | 
				
			||||||
 | 
					                .into_iter()
 | 
				
			||||||
 | 
					                .map(|(k, v)| (k.to_string(), v.to_string()))
 | 
				
			||||||
 | 
					                .collect(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Helper function to execute async operations with proper runtime handling
 | 
					/// Helper function to execute async operations with proper runtime handling
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// This uses a global runtime to ensure consistent async execution
 | 
				
			||||||
fn execute_async<F, T>(future: F) -> Result<T, Box<EvalAltResult>>
 | 
					fn execute_async<F, T>(future: F) -> Result<T, Box<EvalAltResult>>
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
    F: std::future::Future<Output = Result<T, KubernetesError>>,
 | 
					    F: std::future::Future<Output = Result<T, KubernetesError>>,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    match tokio::runtime::Handle::try_current() {
 | 
					    // Get the global runtime
 | 
				
			||||||
        Ok(handle) => handle
 | 
					    let rt = match RUNTIME.lock() {
 | 
				
			||||||
            .block_on(future)
 | 
					        Ok(rt) => rt,
 | 
				
			||||||
            .map_err(kubernetes_error_to_rhai_error),
 | 
					        Err(e) => {
 | 
				
			||||||
        Err(_) => {
 | 
					            return Err(Box::new(EvalAltResult::ErrorRuntime(
 | 
				
			||||||
            // No runtime available, create a new one
 | 
					                format!("Failed to acquire runtime lock: {e}").into(),
 | 
				
			||||||
            let rt = tokio::runtime::Runtime::new().map_err(|e| {
 | 
					 | 
				
			||||||
                Box::new(EvalAltResult::ErrorRuntime(
 | 
					 | 
				
			||||||
                    format!("Failed to create Tokio runtime: {}", e).into(),
 | 
					 | 
				
			||||||
                rhai::Position::NONE,
 | 
					                rhai::Position::NONE,
 | 
				
			||||||
                ))
 | 
					            )));
 | 
				
			||||||
            })?;
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Execute the future in a blocking manner
 | 
				
			||||||
    rt.block_on(future).map_err(kubernetes_error_to_rhai_error)
 | 
					    rt.block_on(future).map_err(kubernetes_error_to_rhai_error)
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Create a new KubernetesManager for the specified namespace
 | 
					/// Create a new KubernetesManager for the specified namespace
 | 
				
			||||||
@@ -104,6 +135,48 @@ fn deployments_list(km: &mut KubernetesManager) -> Result<Array, Box<EvalAltResu
 | 
				
			|||||||
    Ok(deployment_names)
 | 
					    Ok(deployment_names)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// List all configmaps in the namespace
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Arguments
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// * `km` - The KubernetesManager instance
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Returns
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// * `Result<Array, Box<EvalAltResult>>` - Array of configmap names or an error
 | 
				
			||||||
 | 
					fn configmaps_list(km: &mut KubernetesManager) -> Result<Array, Box<EvalAltResult>> {
 | 
				
			||||||
 | 
					    let configmaps = execute_async(km.configmaps_list())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let configmap_names: Array = configmaps
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
 | 
					        .filter_map(|configmap| configmap.metadata.name.as_ref())
 | 
				
			||||||
 | 
					        .map(|name| Dynamic::from(name.clone()))
 | 
				
			||||||
 | 
					        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(configmap_names)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// List all secrets in the namespace
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Arguments
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// * `km` - The KubernetesManager instance
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Returns
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// * `Result<Array, Box<EvalAltResult>>` - Array of secret names or an error
 | 
				
			||||||
 | 
					fn secrets_list(km: &mut KubernetesManager) -> Result<Array, Box<EvalAltResult>> {
 | 
				
			||||||
 | 
					    let secrets = execute_async(km.secrets_list())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let secret_names: Array = secrets
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
 | 
					        .filter_map(|secret| secret.metadata.name.as_ref())
 | 
				
			||||||
 | 
					        .map(|name| Dynamic::from(name.clone()))
 | 
				
			||||||
 | 
					        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(secret_names)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Delete resources matching a PCRE pattern
 | 
					/// Delete resources matching a PCRE pattern
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// # Arguments
 | 
					/// # Arguments
 | 
				
			||||||
@@ -114,7 +187,8 @@ fn deployments_list(km: &mut KubernetesManager) -> Result<Array, Box<EvalAltResu
 | 
				
			|||||||
/// # Returns
 | 
					/// # Returns
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// * `Result<i64, Box<EvalAltResult>>` - Number of resources deleted or an error
 | 
					/// * `Result<i64, Box<EvalAltResult>>` - Number of resources deleted or an error
 | 
				
			||||||
/// Create a pod with a single container
 | 
					///
 | 
				
			||||||
 | 
					/// Create a pod with a single container (backward compatible version)
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// # Arguments
 | 
					/// # Arguments
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
@@ -143,7 +217,44 @@ fn pod_create(
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let pod = execute_async(km.pod_create(&name, &image, labels_map))?;
 | 
					    let pod = execute_async(km.pod_create(&name, &image, labels_map, None))?;
 | 
				
			||||||
 | 
					    Ok(pod.metadata.name.unwrap_or(name))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Create a pod with a single container and environment variables
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Arguments
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// * `km` - Mutable reference to KubernetesManager
 | 
				
			||||||
 | 
					/// * `name` - Name of the pod
 | 
				
			||||||
 | 
					/// * `image` - Container image to use
 | 
				
			||||||
 | 
					/// * `labels` - Optional labels as a Map
 | 
				
			||||||
 | 
					/// * `env_vars` - Optional environment variables as a Map
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Returns
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// * `Result<String, Box<EvalAltResult>>` - Pod name or an error
 | 
				
			||||||
 | 
					fn pod_create_with_env(
 | 
				
			||||||
 | 
					    km: &mut KubernetesManager,
 | 
				
			||||||
 | 
					    name: String,
 | 
				
			||||||
 | 
					    image: String,
 | 
				
			||||||
 | 
					    labels: Map,
 | 
				
			||||||
 | 
					    env_vars: Map,
 | 
				
			||||||
 | 
					) -> Result<String, Box<EvalAltResult>> {
 | 
				
			||||||
 | 
					    let labels_map: Option<std::collections::HashMap<String, String>> = if labels.is_empty() {
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        Some(
 | 
				
			||||||
 | 
					            labels
 | 
				
			||||||
 | 
					                .into_iter()
 | 
				
			||||||
 | 
					                .map(|(k, v)| (k.to_string(), v.to_string()))
 | 
				
			||||||
 | 
					                .collect(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let env_vars_map = convert_rhai_map_to_env_vars(env_vars);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let pod = execute_async(km.pod_create(&name, &image, labels_map, env_vars_map))?;
 | 
				
			||||||
    Ok(pod.metadata.name.unwrap_or(name))
 | 
					    Ok(pod.metadata.name.unwrap_or(name))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -201,6 +312,7 @@ fn deployment_create(
 | 
				
			|||||||
    image: String,
 | 
					    image: String,
 | 
				
			||||||
    replicas: i64,
 | 
					    replicas: i64,
 | 
				
			||||||
    labels: Map,
 | 
					    labels: Map,
 | 
				
			||||||
 | 
					    env_vars: Map,
 | 
				
			||||||
) -> Result<String, Box<EvalAltResult>> {
 | 
					) -> Result<String, Box<EvalAltResult>> {
 | 
				
			||||||
    let labels_map: Option<std::collections::HashMap<String, String>> = if labels.is_empty() {
 | 
					    let labels_map: Option<std::collections::HashMap<String, String>> = if labels.is_empty() {
 | 
				
			||||||
        None
 | 
					        None
 | 
				
			||||||
@@ -213,8 +325,15 @@ fn deployment_create(
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let deployment =
 | 
					    let env_vars_map = convert_rhai_map_to_env_vars(env_vars);
 | 
				
			||||||
        execute_async(km.deployment_create(&name, &image, replicas as i32, labels_map))?;
 | 
					
 | 
				
			||||||
 | 
					    let deployment = execute_async(km.deployment_create(
 | 
				
			||||||
 | 
					        &name,
 | 
				
			||||||
 | 
					        &image,
 | 
				
			||||||
 | 
					        replicas as i32,
 | 
				
			||||||
 | 
					        labels_map,
 | 
				
			||||||
 | 
					        env_vars_map,
 | 
				
			||||||
 | 
					    ))?;
 | 
				
			||||||
    Ok(deployment.metadata.name.unwrap_or(name))
 | 
					    Ok(deployment.metadata.name.unwrap_or(name))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -419,6 +538,7 @@ fn resource_counts(km: &mut KubernetesManager) -> Result<Map, Box<EvalAltResult>
 | 
				
			|||||||
/// * `replicas` - Number of replicas
 | 
					/// * `replicas` - Number of replicas
 | 
				
			||||||
/// * `port` - Port the application listens on
 | 
					/// * `port` - Port the application listens on
 | 
				
			||||||
/// * `labels` - Optional labels as a Map
 | 
					/// * `labels` - Optional labels as a Map
 | 
				
			||||||
 | 
					/// * `env_vars` - Optional environment variables as a Map
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// # Returns
 | 
					/// # Returns
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
@@ -430,6 +550,7 @@ fn deploy_application(
 | 
				
			|||||||
    replicas: i64,
 | 
					    replicas: i64,
 | 
				
			||||||
    port: i64,
 | 
					    port: i64,
 | 
				
			||||||
    labels: Map,
 | 
					    labels: Map,
 | 
				
			||||||
 | 
					    env_vars: Map,
 | 
				
			||||||
) -> Result<String, Box<EvalAltResult>> {
 | 
					) -> Result<String, Box<EvalAltResult>> {
 | 
				
			||||||
    let labels_map: Option<std::collections::HashMap<String, String>> = if labels.is_empty() {
 | 
					    let labels_map: Option<std::collections::HashMap<String, String>> = if labels.is_empty() {
 | 
				
			||||||
        None
 | 
					        None
 | 
				
			||||||
@@ -442,9 +563,18 @@ fn deploy_application(
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    execute_async(km.deploy_application(&name, &image, replicas as i32, port as i32, labels_map))?;
 | 
					    let env_vars_map = convert_rhai_map_to_env_vars(env_vars);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(format!("Successfully deployed application '{}'", name))
 | 
					    execute_async(km.deploy_application(
 | 
				
			||||||
 | 
					        &name,
 | 
				
			||||||
 | 
					        &image,
 | 
				
			||||||
 | 
					        replicas as i32,
 | 
				
			||||||
 | 
					        port as i32,
 | 
				
			||||||
 | 
					        labels_map,
 | 
				
			||||||
 | 
					        env_vars_map,
 | 
				
			||||||
 | 
					    ))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(format!("Successfully deployed application '{name}'"))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Delete a specific pod by name
 | 
					/// Delete a specific pod by name
 | 
				
			||||||
@@ -551,10 +681,13 @@ pub fn register_kubernetes_module(engine: &mut Engine) -> Result<(), Box<EvalAlt
 | 
				
			|||||||
    engine.register_fn("pods_list", pods_list);
 | 
					    engine.register_fn("pods_list", pods_list);
 | 
				
			||||||
    engine.register_fn("services_list", services_list);
 | 
					    engine.register_fn("services_list", services_list);
 | 
				
			||||||
    engine.register_fn("deployments_list", deployments_list);
 | 
					    engine.register_fn("deployments_list", deployments_list);
 | 
				
			||||||
 | 
					    engine.register_fn("configmaps_list", configmaps_list);
 | 
				
			||||||
 | 
					    engine.register_fn("secrets_list", secrets_list);
 | 
				
			||||||
    engine.register_fn("namespaces_list", namespaces_list);
 | 
					    engine.register_fn("namespaces_list", namespaces_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Register resource creation methods (object-oriented style)
 | 
					    // Register resource creation methods (object-oriented style)
 | 
				
			||||||
    engine.register_fn("create_pod", pod_create);
 | 
					    engine.register_fn("create_pod", pod_create);
 | 
				
			||||||
 | 
					    engine.register_fn("create_pod_with_env", pod_create_with_env);
 | 
				
			||||||
    engine.register_fn("create_service", service_create);
 | 
					    engine.register_fn("create_service", service_create);
 | 
				
			||||||
    engine.register_fn("create_deployment", deployment_create);
 | 
					    engine.register_fn("create_deployment", deployment_create);
 | 
				
			||||||
    engine.register_fn("create_configmap", configmap_create);
 | 
					    engine.register_fn("create_configmap", configmap_create);
 | 
				
			||||||
@@ -590,7 +723,7 @@ pub fn register_kubernetes_module(engine: &mut Engine) -> Result<(), Box<EvalAlt
 | 
				
			|||||||
// Helper function for error conversion
 | 
					// Helper function for error conversion
 | 
				
			||||||
fn kubernetes_error_to_rhai_error(error: KubernetesError) -> Box<EvalAltResult> {
 | 
					fn kubernetes_error_to_rhai_error(error: KubernetesError) -> Box<EvalAltResult> {
 | 
				
			||||||
    Box::new(EvalAltResult::ErrorRuntime(
 | 
					    Box::new(EvalAltResult::ErrorRuntime(
 | 
				
			||||||
        format!("Kubernetes error: {}", error).into(),
 | 
					        format!("Kubernetes error: {error}").into(),
 | 
				
			||||||
        rhai::Position::NONE,
 | 
					        rhai::Position::NONE,
 | 
				
			||||||
    ))
 | 
					    ))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,8 @@ mod crud_tests {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Create a test namespace for our operations
 | 
					        // Create a test namespace for our operations
 | 
				
			||||||
        let test_namespace = "sal-crud-test";
 | 
					        let test_namespace = "sal-crud-test";
 | 
				
			||||||
        let km = KubernetesManager::new("default").await
 | 
					        let km = KubernetesManager::new("default")
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
            .expect("Should connect to cluster");
 | 
					            .expect("Should connect to cluster");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Clean up any existing test namespace
 | 
					        // Clean up any existing test namespace
 | 
				
			||||||
@@ -34,50 +35,80 @@ mod crud_tests {
 | 
				
			|||||||
        println!("\n=== CREATE Operations ===");
 | 
					        println!("\n=== CREATE Operations ===");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 1. Create namespace
 | 
					        // 1. Create namespace
 | 
				
			||||||
        km.namespace_create(test_namespace).await
 | 
					        km.namespace_create(test_namespace)
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
            .expect("Should create test namespace");
 | 
					            .expect("Should create test namespace");
 | 
				
			||||||
        println!("✅ Created namespace: {}", test_namespace);
 | 
					        println!("✅ Created namespace: {}", test_namespace);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Switch to test namespace
 | 
					        // Switch to test namespace
 | 
				
			||||||
        let test_km = KubernetesManager::new(test_namespace).await
 | 
					        let test_km = KubernetesManager::new(test_namespace)
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
            .expect("Should connect to test namespace");
 | 
					            .expect("Should connect to test namespace");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 2. Create ConfigMap
 | 
					        // 2. Create ConfigMap
 | 
				
			||||||
        let mut config_data = HashMap::new();
 | 
					        let mut config_data = HashMap::new();
 | 
				
			||||||
        config_data.insert("app.properties".to_string(), "debug=true\nport=8080".to_string());
 | 
					        config_data.insert(
 | 
				
			||||||
        config_data.insert("config.yaml".to_string(), "key: value\nenv: test".to_string());
 | 
					            "app.properties".to_string(),
 | 
				
			||||||
 | 
					            "debug=true\nport=8080".to_string(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        config_data.insert(
 | 
				
			||||||
 | 
					            "config.yaml".to_string(),
 | 
				
			||||||
 | 
					            "key: value\nenv: test".to_string(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let configmap = test_km.configmap_create("test-config", config_data).await
 | 
					        let configmap = test_km
 | 
				
			||||||
 | 
					            .configmap_create("test-config", config_data)
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
            .expect("Should create ConfigMap");
 | 
					            .expect("Should create ConfigMap");
 | 
				
			||||||
        println!("✅ Created ConfigMap: {}", configmap.metadata.name.unwrap_or_default());
 | 
					        println!(
 | 
				
			||||||
 | 
					            "✅ Created ConfigMap: {}",
 | 
				
			||||||
 | 
					            configmap.metadata.name.unwrap_or_default()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 3. Create Secret
 | 
					        // 3. Create Secret
 | 
				
			||||||
        let mut secret_data = HashMap::new();
 | 
					        let mut secret_data = HashMap::new();
 | 
				
			||||||
        secret_data.insert("username".to_string(), "testuser".to_string());
 | 
					        secret_data.insert("username".to_string(), "testuser".to_string());
 | 
				
			||||||
        secret_data.insert("password".to_string(), "secret123".to_string());
 | 
					        secret_data.insert("password".to_string(), "secret123".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let secret = test_km.secret_create("test-secret", secret_data, None).await
 | 
					        let secret = test_km
 | 
				
			||||||
 | 
					            .secret_create("test-secret", secret_data, None)
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
            .expect("Should create Secret");
 | 
					            .expect("Should create Secret");
 | 
				
			||||||
        println!("✅ Created Secret: {}", secret.metadata.name.unwrap_or_default());
 | 
					        println!(
 | 
				
			||||||
 | 
					            "✅ Created Secret: {}",
 | 
				
			||||||
 | 
					            secret.metadata.name.unwrap_or_default()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 4. Create Pod
 | 
					        // 4. Create Pod
 | 
				
			||||||
        let mut pod_labels = HashMap::new();
 | 
					        let mut pod_labels = HashMap::new();
 | 
				
			||||||
        pod_labels.insert("app".to_string(), "test-app".to_string());
 | 
					        pod_labels.insert("app".to_string(), "test-app".to_string());
 | 
				
			||||||
        pod_labels.insert("version".to_string(), "v1".to_string());
 | 
					        pod_labels.insert("version".to_string(), "v1".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let pod = test_km.pod_create("test-pod", "nginx:alpine", Some(pod_labels.clone())).await
 | 
					        let pod = test_km
 | 
				
			||||||
 | 
					            .pod_create("test-pod", "nginx:alpine", Some(pod_labels.clone()), None)
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
            .expect("Should create Pod");
 | 
					            .expect("Should create Pod");
 | 
				
			||||||
        println!("✅ Created Pod: {}", pod.metadata.name.unwrap_or_default());
 | 
					        println!("✅ Created Pod: {}", pod.metadata.name.unwrap_or_default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 5. Create Service
 | 
					        // 5. Create Service
 | 
				
			||||||
        let service = test_km.service_create("test-service", pod_labels.clone(), 80, Some(80)).await
 | 
					        let service = test_km
 | 
				
			||||||
 | 
					            .service_create("test-service", pod_labels.clone(), 80, Some(80))
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
            .expect("Should create Service");
 | 
					            .expect("Should create Service");
 | 
				
			||||||
        println!("✅ Created Service: {}", service.metadata.name.unwrap_or_default());
 | 
					        println!(
 | 
				
			||||||
 | 
					            "✅ Created Service: {}",
 | 
				
			||||||
 | 
					            service.metadata.name.unwrap_or_default()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 6. Create Deployment
 | 
					        // 6. Create Deployment
 | 
				
			||||||
        let deployment = test_km.deployment_create("test-deployment", "nginx:alpine", 2, Some(pod_labels)).await
 | 
					        let deployment = test_km
 | 
				
			||||||
 | 
					            .deployment_create("test-deployment", "nginx:alpine", 2, Some(pod_labels), None)
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
            .expect("Should create Deployment");
 | 
					            .expect("Should create Deployment");
 | 
				
			||||||
        println!("✅ Created Deployment: {}", deployment.metadata.name.unwrap_or_default());
 | 
					        println!(
 | 
				
			||||||
 | 
					            "✅ Created Deployment: {}",
 | 
				
			||||||
 | 
					            deployment.metadata.name.unwrap_or_default()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // READ operations
 | 
					        // READ operations
 | 
				
			||||||
        println!("\n=== READ Operations ===");
 | 
					        println!("\n=== READ Operations ===");
 | 
				
			||||||
@@ -89,10 +120,16 @@ mod crud_tests {
 | 
				
			|||||||
        let services = test_km.services_list().await.expect("Should list services");
 | 
					        let services = test_km.services_list().await.expect("Should list services");
 | 
				
			||||||
        println!("✅ Listed {} services", services.len());
 | 
					        println!("✅ Listed {} services", services.len());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let deployments = test_km.deployments_list().await.expect("Should list deployments");
 | 
					        let deployments = test_km
 | 
				
			||||||
 | 
					            .deployments_list()
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .expect("Should list deployments");
 | 
				
			||||||
        println!("✅ Listed {} deployments", deployments.len());
 | 
					        println!("✅ Listed {} deployments", deployments.len());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let configmaps = test_km.configmaps_list().await.expect("Should list configmaps");
 | 
					        let configmaps = test_km
 | 
				
			||||||
 | 
					            .configmaps_list()
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .expect("Should list configmaps");
 | 
				
			||||||
        println!("✅ Listed {} configmaps", configmaps.len());
 | 
					        println!("✅ Listed {} configmaps", configmaps.len());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let secrets = test_km.secrets_list().await.expect("Should list secrets");
 | 
					        let secrets = test_km.secrets_list().await.expect("Should list secrets");
 | 
				
			||||||
@@ -100,43 +137,81 @@ mod crud_tests {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Get specific resources
 | 
					        // Get specific resources
 | 
				
			||||||
        let pod = test_km.pod_get("test-pod").await.expect("Should get pod");
 | 
					        let pod = test_km.pod_get("test-pod").await.expect("Should get pod");
 | 
				
			||||||
        println!("✅ Retrieved pod: {}", pod.metadata.name.unwrap_or_default());
 | 
					        println!(
 | 
				
			||||||
 | 
					            "✅ Retrieved pod: {}",
 | 
				
			||||||
 | 
					            pod.metadata.name.unwrap_or_default()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let service = test_km.service_get("test-service").await.expect("Should get service");
 | 
					        let service = test_km
 | 
				
			||||||
        println!("✅ Retrieved service: {}", service.metadata.name.unwrap_or_default());
 | 
					            .service_get("test-service")
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .expect("Should get service");
 | 
				
			||||||
 | 
					        println!(
 | 
				
			||||||
 | 
					            "✅ Retrieved service: {}",
 | 
				
			||||||
 | 
					            service.metadata.name.unwrap_or_default()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let deployment = test_km.deployment_get("test-deployment").await.expect("Should get deployment");
 | 
					        let deployment = test_km
 | 
				
			||||||
        println!("✅ Retrieved deployment: {}", deployment.metadata.name.unwrap_or_default());
 | 
					            .deployment_get("test-deployment")
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .expect("Should get deployment");
 | 
				
			||||||
 | 
					        println!(
 | 
				
			||||||
 | 
					            "✅ Retrieved deployment: {}",
 | 
				
			||||||
 | 
					            deployment.metadata.name.unwrap_or_default()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Resource counts
 | 
					        // Resource counts
 | 
				
			||||||
        let counts = test_km.resource_counts().await.expect("Should get resource counts");
 | 
					        let counts = test_km
 | 
				
			||||||
 | 
					            .resource_counts()
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .expect("Should get resource counts");
 | 
				
			||||||
        println!("✅ Resource counts: {:?}", counts);
 | 
					        println!("✅ Resource counts: {:?}", counts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // DELETE operations
 | 
					        // DELETE operations
 | 
				
			||||||
        println!("\n=== DELETE Operations ===");
 | 
					        println!("\n=== DELETE Operations ===");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Delete individual resources
 | 
					        // Delete individual resources
 | 
				
			||||||
        test_km.pod_delete("test-pod").await.expect("Should delete pod");
 | 
					        test_km
 | 
				
			||||||
 | 
					            .pod_delete("test-pod")
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .expect("Should delete pod");
 | 
				
			||||||
        println!("✅ Deleted pod");
 | 
					        println!("✅ Deleted pod");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        test_km.service_delete("test-service").await.expect("Should delete service");
 | 
					        test_km
 | 
				
			||||||
 | 
					            .service_delete("test-service")
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .expect("Should delete service");
 | 
				
			||||||
        println!("✅ Deleted service");
 | 
					        println!("✅ Deleted service");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        test_km.deployment_delete("test-deployment").await.expect("Should delete deployment");
 | 
					        test_km
 | 
				
			||||||
 | 
					            .deployment_delete("test-deployment")
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .expect("Should delete deployment");
 | 
				
			||||||
        println!("✅ Deleted deployment");
 | 
					        println!("✅ Deleted deployment");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        test_km.configmap_delete("test-config").await.expect("Should delete configmap");
 | 
					        test_km
 | 
				
			||||||
 | 
					            .configmap_delete("test-config")
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .expect("Should delete configmap");
 | 
				
			||||||
        println!("✅ Deleted configmap");
 | 
					        println!("✅ Deleted configmap");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        test_km.secret_delete("test-secret").await.expect("Should delete secret");
 | 
					        test_km
 | 
				
			||||||
 | 
					            .secret_delete("test-secret")
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .expect("Should delete secret");
 | 
				
			||||||
        println!("✅ Deleted secret");
 | 
					        println!("✅ Deleted secret");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Verify resources are deleted
 | 
					        // Verify resources are deleted
 | 
				
			||||||
        let final_counts = test_km.resource_counts().await.expect("Should get final resource counts");
 | 
					        let final_counts = test_km
 | 
				
			||||||
 | 
					            .resource_counts()
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .expect("Should get final resource counts");
 | 
				
			||||||
        println!("✅ Final resource counts: {:?}", final_counts);
 | 
					        println!("✅ Final resource counts: {:?}", final_counts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Delete the test namespace
 | 
					        // Delete the test namespace
 | 
				
			||||||
        km.namespace_delete(test_namespace).await.expect("Should delete test namespace");
 | 
					        km.namespace_delete(test_namespace)
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .expect("Should delete test namespace");
 | 
				
			||||||
        println!("✅ Deleted test namespace");
 | 
					        println!("✅ Deleted test namespace");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        println!("\n🎉 All CRUD operations completed successfully!");
 | 
					        println!("\n🎉 All CRUD operations completed successfully!");
 | 
				
			||||||
@@ -151,11 +226,12 @@ mod crud_tests {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        println!("🔍 Testing error handling in CRUD operations...");
 | 
					        println!("🔍 Testing error handling in CRUD operations...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let km = KubernetesManager::new("default").await
 | 
					        let km = KubernetesManager::new("default")
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
            .expect("Should connect to cluster");
 | 
					            .expect("Should connect to cluster");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Test creating resources with invalid names
 | 
					        // Test creating resources with invalid names
 | 
				
			||||||
        let result = km.pod_create("", "nginx", None).await;
 | 
					        let result = km.pod_create("", "nginx", None, None).await;
 | 
				
			||||||
        assert!(result.is_err(), "Should fail with empty pod name");
 | 
					        assert!(result.is_err(), "Should fail with empty pod name");
 | 
				
			||||||
        println!("✅ Empty pod name properly rejected");
 | 
					        println!("✅ Empty pod name properly rejected");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -166,7 +242,10 @@ mod crud_tests {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Test deleting non-existent resources
 | 
					        // Test deleting non-existent resources
 | 
				
			||||||
        let result = km.service_delete("non-existent-service").await;
 | 
					        let result = km.service_delete("non-existent-service").await;
 | 
				
			||||||
        assert!(result.is_err(), "Should fail to delete non-existent service");
 | 
					        assert!(
 | 
				
			||||||
 | 
					            result.is_err(),
 | 
				
			||||||
 | 
					            "Should fail to delete non-existent service"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        println!("✅ Non-existent service deletion properly handled");
 | 
					        println!("✅ Non-existent service deletion properly handled");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        println!("✅ Error handling in CRUD operations is robust");
 | 
					        println!("✅ Error handling in CRUD operations is robust");
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										384
									
								
								kubernetes/tests/deployment_env_vars_test.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								kubernetes/tests/deployment_env_vars_test.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,384 @@
 | 
				
			|||||||
 | 
					//! Tests for deployment creation with environment variables
 | 
				
			||||||
 | 
					//!
 | 
				
			||||||
 | 
					//! These tests verify the new environment variable functionality in deployments
 | 
				
			||||||
 | 
					//! and the enhanced deploy_application method.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use sal_kubernetes::KubernetesManager;
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Check if Kubernetes integration tests should run
 | 
				
			||||||
 | 
					fn should_run_k8s_tests() -> bool {
 | 
				
			||||||
 | 
					    std::env::var("KUBERNETES_TEST_ENABLED").unwrap_or_default() == "1"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_deployment_create_with_env_vars() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        println!("Skipping Kubernetes integration tests. Set KUBERNETES_TEST_ENABLED=1 to enable.");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return, // Skip if can't connect
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up any existing test deployment
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-env-deployment").await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create deployment with environment variables
 | 
				
			||||||
 | 
					    let mut labels = HashMap::new();
 | 
				
			||||||
 | 
					    labels.insert("app".to_string(), "test-env-app".to_string());
 | 
				
			||||||
 | 
					    labels.insert("test".to_string(), "env-vars".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					    env_vars.insert("TEST_VAR_1".to_string(), "value1".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert("TEST_VAR_2".to_string(), "value2".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert("NODE_ENV".to_string(), "test".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deployment_create(
 | 
				
			||||||
 | 
					            "test-env-deployment",
 | 
				
			||||||
 | 
					            "nginx:latest",
 | 
				
			||||||
 | 
					            1,
 | 
				
			||||||
 | 
					            Some(labels),
 | 
				
			||||||
 | 
					            Some(env_vars),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert!(
 | 
				
			||||||
 | 
					        result.is_ok(),
 | 
				
			||||||
 | 
					        "Failed to create deployment with env vars: {:?}",
 | 
				
			||||||
 | 
					        result
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify the deployment was created
 | 
				
			||||||
 | 
					    let deployment = km.deployment_get("test-env-deployment").await;
 | 
				
			||||||
 | 
					    assert!(deployment.is_ok(), "Failed to get created deployment");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let deployment = deployment.unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify environment variables are set in the container spec
 | 
				
			||||||
 | 
					    if let Some(spec) = &deployment.spec {
 | 
				
			||||||
 | 
					        if let Some(template) = &spec.template.spec {
 | 
				
			||||||
 | 
					            if let Some(container) = template.containers.first() {
 | 
				
			||||||
 | 
					                if let Some(env) = &container.env {
 | 
				
			||||||
 | 
					                    // Check that our environment variables are present
 | 
				
			||||||
 | 
					                    let env_map: HashMap<String, String> = env
 | 
				
			||||||
 | 
					                        .iter()
 | 
				
			||||||
 | 
					                        .filter_map(|e| e.value.as_ref().map(|v| (e.name.clone(), v.clone())))
 | 
				
			||||||
 | 
					                        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    assert_eq!(env_map.get("TEST_VAR_1"), Some(&"value1".to_string()));
 | 
				
			||||||
 | 
					                    assert_eq!(env_map.get("TEST_VAR_2"), Some(&"value2".to_string()));
 | 
				
			||||||
 | 
					                    assert_eq!(env_map.get("NODE_ENV"), Some(&"test".to_string()));
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    panic!("No environment variables found in container spec");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-env-deployment").await;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_pod_create_with_env_vars() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        println!("Skipping Kubernetes integration tests. Set KUBERNETES_TEST_ENABLED=1 to enable.");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return, // Skip if can't connect
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up any existing test pod
 | 
				
			||||||
 | 
					    let _ = km.pod_delete("test-env-pod").await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create pod with environment variables
 | 
				
			||||||
 | 
					    let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					    env_vars.insert("NODE_ENV".to_string(), "test".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert(
 | 
				
			||||||
 | 
					        "DATABASE_URL".to_string(),
 | 
				
			||||||
 | 
					        "postgres://localhost:5432/test".to_string(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    env_vars.insert("API_KEY".to_string(), "test-api-key-12345".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut labels = HashMap::new();
 | 
				
			||||||
 | 
					    labels.insert("app".to_string(), "test-env-pod-app".to_string());
 | 
				
			||||||
 | 
					    labels.insert("test".to_string(), "environment-variables".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .pod_create("test-env-pod", "nginx:latest", Some(labels), Some(env_vars))
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert!(
 | 
				
			||||||
 | 
					        result.is_ok(),
 | 
				
			||||||
 | 
					        "Failed to create pod with env vars: {:?}",
 | 
				
			||||||
 | 
					        result
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if let Ok(pod) = result {
 | 
				
			||||||
 | 
					        let pod_name = pod
 | 
				
			||||||
 | 
					            .metadata
 | 
				
			||||||
 | 
					            .name
 | 
				
			||||||
 | 
					            .as_ref()
 | 
				
			||||||
 | 
					            .unwrap_or(&"".to_string())
 | 
				
			||||||
 | 
					            .clone();
 | 
				
			||||||
 | 
					        assert_eq!(pod_name, "test-env-pod");
 | 
				
			||||||
 | 
					        println!("✅ Created pod with environment variables: {}", pod_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Verify the pod has the expected environment variables
 | 
				
			||||||
 | 
					        if let Some(spec) = &pod.spec {
 | 
				
			||||||
 | 
					            if let Some(container) = spec.containers.first() {
 | 
				
			||||||
 | 
					                if let Some(env) = &container.env {
 | 
				
			||||||
 | 
					                    let env_names: Vec<String> = env.iter().map(|e| e.name.clone()).collect();
 | 
				
			||||||
 | 
					                    assert!(env_names.contains(&"NODE_ENV".to_string()));
 | 
				
			||||||
 | 
					                    assert!(env_names.contains(&"DATABASE_URL".to_string()));
 | 
				
			||||||
 | 
					                    assert!(env_names.contains(&"API_KEY".to_string()));
 | 
				
			||||||
 | 
					                    println!("✅ Pod has expected environment variables");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up
 | 
				
			||||||
 | 
					    let _ = km.pod_delete("test-env-pod").await;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_deployment_create_without_env_vars() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up any existing test deployment
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-no-env-deployment").await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create deployment without environment variables
 | 
				
			||||||
 | 
					    let mut labels = HashMap::new();
 | 
				
			||||||
 | 
					    labels.insert("app".to_string(), "test-no-env-app".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deployment_create(
 | 
				
			||||||
 | 
					            "test-no-env-deployment",
 | 
				
			||||||
 | 
					            "nginx:latest",
 | 
				
			||||||
 | 
					            1,
 | 
				
			||||||
 | 
					            Some(labels),
 | 
				
			||||||
 | 
					            None, // No environment variables
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert!(
 | 
				
			||||||
 | 
					        result.is_ok(),
 | 
				
			||||||
 | 
					        "Failed to create deployment without env vars: {:?}",
 | 
				
			||||||
 | 
					        result
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify the deployment was created
 | 
				
			||||||
 | 
					    let deployment = km.deployment_get("test-no-env-deployment").await;
 | 
				
			||||||
 | 
					    assert!(deployment.is_ok(), "Failed to get created deployment");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let deployment = deployment.unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify no environment variables are set
 | 
				
			||||||
 | 
					    if let Some(spec) = &deployment.spec {
 | 
				
			||||||
 | 
					        if let Some(template) = &spec.template.spec {
 | 
				
			||||||
 | 
					            if let Some(container) = template.containers.first() {
 | 
				
			||||||
 | 
					                // Environment variables should be None or empty
 | 
				
			||||||
 | 
					                assert!(
 | 
				
			||||||
 | 
					                    container.env.is_none() || container.env.as_ref().unwrap().is_empty(),
 | 
				
			||||||
 | 
					                    "Expected no environment variables, but found some"
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-no-env-deployment").await;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_deploy_application_with_env_vars() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up any existing resources
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-app-env").await;
 | 
				
			||||||
 | 
					    let _ = km.service_delete("test-app-env").await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Deploy application with both labels and environment variables
 | 
				
			||||||
 | 
					    let mut labels = HashMap::new();
 | 
				
			||||||
 | 
					    labels.insert("app".to_string(), "test-app-env".to_string());
 | 
				
			||||||
 | 
					    labels.insert("tier".to_string(), "backend".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					    env_vars.insert(
 | 
				
			||||||
 | 
					        "DATABASE_URL".to_string(),
 | 
				
			||||||
 | 
					        "postgres://localhost:5432/test".to_string(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    env_vars.insert("API_KEY".to_string(), "test-api-key".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert("LOG_LEVEL".to_string(), "debug".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deploy_application(
 | 
				
			||||||
 | 
					            "test-app-env",
 | 
				
			||||||
 | 
					            "nginx:latest",
 | 
				
			||||||
 | 
					            2,
 | 
				
			||||||
 | 
					            80,
 | 
				
			||||||
 | 
					            Some(labels),
 | 
				
			||||||
 | 
					            Some(env_vars),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert!(
 | 
				
			||||||
 | 
					        result.is_ok(),
 | 
				
			||||||
 | 
					        "Failed to deploy application with env vars: {:?}",
 | 
				
			||||||
 | 
					        result
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify both deployment and service were created
 | 
				
			||||||
 | 
					    let deployment = km.deployment_get("test-app-env").await;
 | 
				
			||||||
 | 
					    assert!(deployment.is_ok(), "Deployment should be created");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let service = km.service_get("test-app-env").await;
 | 
				
			||||||
 | 
					    assert!(service.is_ok(), "Service should be created");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify environment variables in deployment
 | 
				
			||||||
 | 
					    let deployment = deployment.unwrap();
 | 
				
			||||||
 | 
					    if let Some(spec) = &deployment.spec {
 | 
				
			||||||
 | 
					        if let Some(template) = &spec.template.spec {
 | 
				
			||||||
 | 
					            if let Some(container) = template.containers.first() {
 | 
				
			||||||
 | 
					                if let Some(env) = &container.env {
 | 
				
			||||||
 | 
					                    let env_map: HashMap<String, String> = env
 | 
				
			||||||
 | 
					                        .iter()
 | 
				
			||||||
 | 
					                        .filter_map(|e| e.value.as_ref().map(|v| (e.name.clone(), v.clone())))
 | 
				
			||||||
 | 
					                        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    assert_eq!(
 | 
				
			||||||
 | 
					                        env_map.get("DATABASE_URL"),
 | 
				
			||||||
 | 
					                        Some(&"postgres://localhost:5432/test".to_string())
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    assert_eq!(env_map.get("API_KEY"), Some(&"test-api-key".to_string()));
 | 
				
			||||||
 | 
					                    assert_eq!(env_map.get("LOG_LEVEL"), Some(&"debug".to_string()));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-app-env").await;
 | 
				
			||||||
 | 
					    let _ = km.service_delete("test-app-env").await;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_deploy_application_cleanup_existing_resources() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => {
 | 
				
			||||||
 | 
					            println!("Skipping test - no Kubernetes cluster available");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let app_name = "test-cleanup-app";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up any existing resources first to ensure clean state
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete(app_name).await;
 | 
				
			||||||
 | 
					    let _ = km.service_delete(app_name).await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Wait a moment for cleanup to complete
 | 
				
			||||||
 | 
					    tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // First deployment
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deploy_application(app_name, "nginx:latest", 1, 80, None, None)
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if result.is_err() {
 | 
				
			||||||
 | 
					        println!("Skipping test - cluster connection unstable: {:?}", result);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify resources exist (with graceful handling)
 | 
				
			||||||
 | 
					    let deployment_exists = km.deployment_get(app_name).await.is_ok();
 | 
				
			||||||
 | 
					    let service_exists = km.service_get(app_name).await.is_ok();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if !deployment_exists || !service_exists {
 | 
				
			||||||
 | 
					        println!("Skipping test - resources not created properly");
 | 
				
			||||||
 | 
					        let _ = km.deployment_delete(app_name).await;
 | 
				
			||||||
 | 
					        let _ = km.service_delete(app_name).await;
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Second deployment with different configuration (should replace the first)
 | 
				
			||||||
 | 
					    let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					    env_vars.insert("VERSION".to_string(), "2.0".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deploy_application(app_name, "nginx:alpine", 2, 80, None, Some(env_vars))
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					    if result.is_err() {
 | 
				
			||||||
 | 
					        println!(
 | 
				
			||||||
 | 
					            "Skipping verification - second deployment failed: {:?}",
 | 
				
			||||||
 | 
					            result
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        let _ = km.deployment_delete(app_name).await;
 | 
				
			||||||
 | 
					        let _ = km.service_delete(app_name).await;
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify resources still exist (replaced, not duplicated)
 | 
				
			||||||
 | 
					    let deployment = km.deployment_get(app_name).await;
 | 
				
			||||||
 | 
					    if deployment.is_err() {
 | 
				
			||||||
 | 
					        println!("Skipping verification - deployment not found after replacement");
 | 
				
			||||||
 | 
					        let _ = km.deployment_delete(app_name).await;
 | 
				
			||||||
 | 
					        let _ = km.service_delete(app_name).await;
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify the new configuration
 | 
				
			||||||
 | 
					    let deployment = deployment.unwrap();
 | 
				
			||||||
 | 
					    if let Some(spec) = &deployment.spec {
 | 
				
			||||||
 | 
					        assert_eq!(spec.replicas, Some(2), "Replicas should be updated to 2");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(template) = &spec.template.spec {
 | 
				
			||||||
 | 
					            if let Some(container) = template.containers.first() {
 | 
				
			||||||
 | 
					                assert_eq!(
 | 
				
			||||||
 | 
					                    container.image,
 | 
				
			||||||
 | 
					                    Some("nginx:alpine".to_string()),
 | 
				
			||||||
 | 
					                    "Image should be updated"
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if let Some(env) = &container.env {
 | 
				
			||||||
 | 
					                    let has_version = env
 | 
				
			||||||
 | 
					                        .iter()
 | 
				
			||||||
 | 
					                        .any(|e| e.name == "VERSION" && e.value == Some("2.0".to_string()));
 | 
				
			||||||
 | 
					                    assert!(has_version, "Environment variable VERSION should be set");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete(app_name).await;
 | 
				
			||||||
 | 
					    let _ = km.service_delete(app_name).await;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										293
									
								
								kubernetes/tests/edge_cases_test.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								kubernetes/tests/edge_cases_test.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,293 @@
 | 
				
			|||||||
 | 
					//! Edge case and error scenario tests for Kubernetes module
 | 
				
			||||||
 | 
					//!
 | 
				
			||||||
 | 
					//! These tests verify proper error handling and edge case behavior.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use sal_kubernetes::KubernetesManager;
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Check if Kubernetes integration tests should run
 | 
				
			||||||
 | 
					fn should_run_k8s_tests() -> bool {
 | 
				
			||||||
 | 
					    std::env::var("KUBERNETES_TEST_ENABLED").unwrap_or_default() == "1"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_deployment_with_invalid_image() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        println!("Skipping Kubernetes integration tests. Set KUBERNETES_TEST_ENABLED=1 to enable.");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up any existing test deployment
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-invalid-image").await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Try to create deployment with invalid image name
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deployment_create(
 | 
				
			||||||
 | 
					            "test-invalid-image",
 | 
				
			||||||
 | 
					            "invalid/image/name/that/does/not/exist:latest",
 | 
				
			||||||
 | 
					            1,
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // The deployment creation should succeed (Kubernetes validates images at runtime)
 | 
				
			||||||
 | 
					    assert!(result.is_ok(), "Deployment creation should succeed even with invalid image");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-invalid-image").await;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_deployment_with_empty_name() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Try to create deployment with empty name
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deployment_create("", "nginx:latest", 1, None, None)
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Should fail due to invalid name
 | 
				
			||||||
 | 
					    assert!(result.is_err(), "Deployment with empty name should fail");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_deployment_with_invalid_replicas() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up any existing test deployment
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-invalid-replicas").await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Try to create deployment with negative replicas
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deployment_create("test-invalid-replicas", "nginx:latest", -1, None, None)
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Should fail due to invalid replica count
 | 
				
			||||||
 | 
					    assert!(result.is_err(), "Deployment with negative replicas should fail");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_deployment_with_large_env_vars() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up any existing test deployment
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-large-env").await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create deployment with many environment variables
 | 
				
			||||||
 | 
					    let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					    for i in 0..50 {
 | 
				
			||||||
 | 
					        env_vars.insert(format!("TEST_VAR_{}", i), format!("value_{}", i));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deployment_create("test-large-env", "nginx:latest", 1, None, Some(env_vars))
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert!(result.is_ok(), "Deployment with many env vars should succeed: {:?}", result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify the deployment was created
 | 
				
			||||||
 | 
					    let deployment = km.deployment_get("test-large-env").await;
 | 
				
			||||||
 | 
					    assert!(deployment.is_ok(), "Should be able to get deployment with many env vars");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify environment variables count
 | 
				
			||||||
 | 
					    let deployment = deployment.unwrap();
 | 
				
			||||||
 | 
					    if let Some(spec) = &deployment.spec {
 | 
				
			||||||
 | 
					        if let Some(template) = &spec.template.spec {
 | 
				
			||||||
 | 
					            if let Some(container) = template.containers.first() {
 | 
				
			||||||
 | 
					                if let Some(env) = &container.env {
 | 
				
			||||||
 | 
					                    assert_eq!(env.len(), 50, "Should have 50 environment variables");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-large-env").await;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_deployment_with_special_characters_in_env_vars() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up any existing test deployment
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-special-env").await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create deployment with special characters in environment variables
 | 
				
			||||||
 | 
					    let mut env_vars = HashMap::new();
 | 
				
			||||||
 | 
					    env_vars.insert("DATABASE_URL".to_string(), "postgres://user:pass@host:5432/db?ssl=true".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert("JSON_CONFIG".to_string(), r#"{"key": "value", "number": 123}"#.to_string());
 | 
				
			||||||
 | 
					    env_vars.insert("MULTILINE_VAR".to_string(), "line1\nline2\nline3".to_string());
 | 
				
			||||||
 | 
					    env_vars.insert("SPECIAL_CHARS".to_string(), "!@#$%^&*()_+-=[]{}|;:,.<>?".to_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deployment_create("test-special-env", "nginx:latest", 1, None, Some(env_vars))
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert!(result.is_ok(), "Deployment with special chars in env vars should succeed: {:?}", result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify the deployment was created and env vars are preserved
 | 
				
			||||||
 | 
					    let deployment = km.deployment_get("test-special-env").await;
 | 
				
			||||||
 | 
					    assert!(deployment.is_ok(), "Should be able to get deployment");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let deployment = deployment.unwrap();
 | 
				
			||||||
 | 
					    if let Some(spec) = &deployment.spec {
 | 
				
			||||||
 | 
					        if let Some(template) = &spec.template.spec {
 | 
				
			||||||
 | 
					            if let Some(container) = template.containers.first() {
 | 
				
			||||||
 | 
					                if let Some(env) = &container.env {
 | 
				
			||||||
 | 
					                    let env_map: HashMap<String, String> = env
 | 
				
			||||||
 | 
					                        .iter()
 | 
				
			||||||
 | 
					                        .filter_map(|e| e.value.as_ref().map(|v| (e.name.clone(), v.clone())))
 | 
				
			||||||
 | 
					                        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    assert_eq!(
 | 
				
			||||||
 | 
					                        env_map.get("DATABASE_URL"),
 | 
				
			||||||
 | 
					                        Some(&"postgres://user:pass@host:5432/db?ssl=true".to_string())
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    assert_eq!(
 | 
				
			||||||
 | 
					                        env_map.get("JSON_CONFIG"),
 | 
				
			||||||
 | 
					                        Some(&r#"{"key": "value", "number": 123}"#.to_string())
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    assert_eq!(
 | 
				
			||||||
 | 
					                        env_map.get("SPECIAL_CHARS"),
 | 
				
			||||||
 | 
					                        Some(&"!@#$%^&*()_+-=[]{}|;:,.<>?".to_string())
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-special-env").await;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_deploy_application_with_invalid_port() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Try to deploy application with invalid port (negative)
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deploy_application("test-invalid-port", "nginx:latest", 1, -80, None, None)
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Should fail due to invalid port
 | 
				
			||||||
 | 
					    assert!(result.is_err(), "Deploy application with negative port should fail");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Try with port 0
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deploy_application("test-zero-port", "nginx:latest", 1, 0, None, None)
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Should fail due to invalid port
 | 
				
			||||||
 | 
					    assert!(result.is_err(), "Deploy application with port 0 should fail");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_get_nonexistent_deployment() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Try to get a deployment that doesn't exist
 | 
				
			||||||
 | 
					    let result = km.deployment_get("nonexistent-deployment-12345").await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Should fail with appropriate error
 | 
				
			||||||
 | 
					    assert!(result.is_err(), "Getting nonexistent deployment should fail");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_delete_nonexistent_deployment() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Try to delete a deployment that doesn't exist
 | 
				
			||||||
 | 
					    let result = km.deployment_delete("nonexistent-deployment-12345").await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Should fail gracefully
 | 
				
			||||||
 | 
					    assert!(result.is_err(), "Deleting nonexistent deployment should fail");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[tokio::test]
 | 
				
			||||||
 | 
					async fn test_deployment_with_zero_replicas() {
 | 
				
			||||||
 | 
					    if !should_run_k8s_tests() {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let km = match KubernetesManager::new("default").await {
 | 
				
			||||||
 | 
					        Ok(km) => km,
 | 
				
			||||||
 | 
					        Err(_) => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up any existing test deployment
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-zero-replicas").await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create deployment with zero replicas (should be valid)
 | 
				
			||||||
 | 
					    let result = km
 | 
				
			||||||
 | 
					        .deployment_create("test-zero-replicas", "nginx:latest", 0, None, None)
 | 
				
			||||||
 | 
					        .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert!(result.is_ok(), "Deployment with zero replicas should succeed: {:?}", result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Verify the deployment was created with 0 replicas
 | 
				
			||||||
 | 
					    let deployment = km.deployment_get("test-zero-replicas").await;
 | 
				
			||||||
 | 
					    assert!(deployment.is_ok(), "Should be able to get deployment with zero replicas");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let deployment = deployment.unwrap();
 | 
				
			||||||
 | 
					    if let Some(spec) = &deployment.spec {
 | 
				
			||||||
 | 
					        assert_eq!(spec.replicas, Some(0), "Should have 0 replicas");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clean up
 | 
				
			||||||
 | 
					    let _ = km.deployment_delete("test-zero-replicas").await;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -68,7 +68,7 @@ try {
 | 
				
			|||||||
        "app": "rhai-app",
 | 
					        "app": "rhai-app",
 | 
				
			||||||
        "tier": "frontend"
 | 
					        "tier": "frontend"
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let deployment_name = test_km.create_deployment("rhai-deployment", "nginx:alpine", 2, deployment_labels);
 | 
					    let deployment_name = test_km.create_deployment("rhai-deployment", "nginx:alpine", 2, deployment_labels, #{});
 | 
				
			||||||
    print("✓ Created Deployment: " + deployment_name);
 | 
					    print("✓ Created Deployment: " + deployment_name);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
} catch(e) {
 | 
					} catch(e) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										199
									
								
								kubernetes/tests/rhai/env_vars_test.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								kubernetes/tests/rhai/env_vars_test.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,199 @@
 | 
				
			|||||||
 | 
					// Rhai test for environment variables functionality
 | 
				
			||||||
 | 
					// This test verifies that the enhanced deploy_application function works correctly with environment variables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print("=== Testing Environment Variables in Rhai ===");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create Kubernetes manager
 | 
				
			||||||
 | 
					print("Creating Kubernetes manager...");
 | 
				
			||||||
 | 
					let km = kubernetes_manager_new("default");
 | 
				
			||||||
 | 
					print("✓ Kubernetes manager created");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test 1: Deploy application with environment variables
 | 
				
			||||||
 | 
					print("\n--- Test 1: Deploy with Environment Variables ---");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Clean up any existing resources
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_deployment(km, "rhai-env-test");
 | 
				
			||||||
 | 
					    print("✓ Cleaned up existing deployment");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("✓ No existing deployment to clean up");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_service(km, "rhai-env-test");
 | 
				
			||||||
 | 
					    print("✓ Cleaned up existing service");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("✓ No existing service to clean up");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deploy with both labels and environment variables
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    let result = deploy_application(km, "rhai-env-test", "nginx:latest", 1, 80, #{
 | 
				
			||||||
 | 
					        "app": "rhai-env-test",
 | 
				
			||||||
 | 
					        "test": "environment-variables",
 | 
				
			||||||
 | 
					        "language": "rhai"
 | 
				
			||||||
 | 
					    }, #{
 | 
				
			||||||
 | 
					        "NODE_ENV": "test",
 | 
				
			||||||
 | 
					        "DATABASE_URL": "postgres://localhost:5432/test",
 | 
				
			||||||
 | 
					        "API_KEY": "test-api-key-12345",
 | 
				
			||||||
 | 
					        "LOG_LEVEL": "debug",
 | 
				
			||||||
 | 
					        "PORT": "80"
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    print("✓ " + result);
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("❌ Failed to deploy with env vars: " + e);
 | 
				
			||||||
 | 
					    throw e;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Verify deployment was created
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    let deployment_name = get_deployment(km, "rhai-env-test");
 | 
				
			||||||
 | 
					    print("✓ Deployment verified: " + deployment_name);
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("❌ Failed to verify deployment: " + e);
 | 
				
			||||||
 | 
					    throw e;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test 2: Deploy application without environment variables
 | 
				
			||||||
 | 
					print("\n--- Test 2: Deploy without Environment Variables ---");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Clean up
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_deployment(km, "rhai-no-env-test");
 | 
				
			||||||
 | 
					    delete_service(km, "rhai-no-env-test");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    // Ignore cleanup errors
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deploy with labels only, empty env vars map
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    let result = deploy_application(km, "rhai-no-env-test", "nginx:alpine", 1, 8080, #{
 | 
				
			||||||
 | 
					        "app": "rhai-no-env-test",
 | 
				
			||||||
 | 
					        "test": "no-environment-variables"
 | 
				
			||||||
 | 
					    }, #{
 | 
				
			||||||
 | 
					        // Empty environment variables map
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    print("✓ " + result);
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("❌ Failed to deploy without env vars: " + e);
 | 
				
			||||||
 | 
					    throw e;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test 3: Deploy with special characters in environment variables
 | 
				
			||||||
 | 
					print("\n--- Test 3: Deploy with Special Characters in Env Vars ---");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Clean up
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_deployment(km, "rhai-special-env-test");
 | 
				
			||||||
 | 
					    delete_service(km, "rhai-special-env-test");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    // Ignore cleanup errors
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deploy with special characters
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    let result = deploy_application(km, "rhai-special-env-test", "nginx:latest", 1, 3000, #{
 | 
				
			||||||
 | 
					        "app": "rhai-special-env-test"
 | 
				
			||||||
 | 
					    }, #{
 | 
				
			||||||
 | 
					        "DATABASE_URL": "postgres://user:pass@host:5432/db?ssl=true&timeout=30",
 | 
				
			||||||
 | 
					        "JSON_CONFIG": `{"server": {"port": 3000, "host": "0.0.0.0"}}`,
 | 
				
			||||||
 | 
					        "SPECIAL_CHARS": "!@#$%^&*()_+-=[]{}|;:,.<>?",
 | 
				
			||||||
 | 
					        "MULTILINE": "line1\nline2\nline3"
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    print("✓ " + result);
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("❌ Failed to deploy with special chars: " + e);
 | 
				
			||||||
 | 
					    throw e;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test 4: Test resource listing after deployments
 | 
				
			||||||
 | 
					print("\n--- Test 4: Verify Resource Listing ---");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    let deployments = deployments_list(km);
 | 
				
			||||||
 | 
					    print("✓ Found " + deployments.len() + " deployments");
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Check that our test deployments are in the list
 | 
				
			||||||
 | 
					    let found_env_test = false;
 | 
				
			||||||
 | 
					    let found_no_env_test = false;
 | 
				
			||||||
 | 
					    let found_special_test = false;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for deployment in deployments {
 | 
				
			||||||
 | 
					        if deployment == "rhai-env-test" {
 | 
				
			||||||
 | 
					            found_env_test = true;
 | 
				
			||||||
 | 
					        } else if deployment == "rhai-no-env-test" {
 | 
				
			||||||
 | 
					            found_no_env_test = true;
 | 
				
			||||||
 | 
					        } else if deployment == "rhai-special-env-test" {
 | 
				
			||||||
 | 
					            found_special_test = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if found_env_test {
 | 
				
			||||||
 | 
					        print("✓ Found rhai-env-test deployment");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        print("❌ rhai-env-test deployment not found in list");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if found_no_env_test {
 | 
				
			||||||
 | 
					        print("✓ Found rhai-no-env-test deployment");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        print("❌ rhai-no-env-test deployment not found in list");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if found_special_test {
 | 
				
			||||||
 | 
					        print("✓ Found rhai-special-env-test deployment");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        print("❌ rhai-special-env-test deployment not found in list");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("❌ Failed to list deployments: " + e);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test 5: Test services listing
 | 
				
			||||||
 | 
					print("\n--- Test 5: Verify Services ---");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    let services = services_list(km);
 | 
				
			||||||
 | 
					    print("✓ Found " + services.len() + " services");
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Services should be created for each deployment
 | 
				
			||||||
 | 
					    let service_count = 0;
 | 
				
			||||||
 | 
					    for service in services {
 | 
				
			||||||
 | 
					        if service.contains("rhai-") && service.contains("-test") {
 | 
				
			||||||
 | 
					            service_count = service_count + 1;
 | 
				
			||||||
 | 
					            print("✓ Found test service: " + service);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if service_count >= 3 {
 | 
				
			||||||
 | 
					        print("✓ All expected services found");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        print("⚠️ Expected at least 3 test services, found " + service_count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("❌ Failed to list services: " + e);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Cleanup all test resources
 | 
				
			||||||
 | 
					print("\n--- Cleanup ---");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let cleanup_items = ["rhai-env-test", "rhai-no-env-test", "rhai-special-env-test"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for item in cleanup_items {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        delete_deployment(km, item);
 | 
				
			||||||
 | 
					        print("✓ Deleted deployment: " + item);
 | 
				
			||||||
 | 
					    } catch(e) {
 | 
				
			||||||
 | 
					        print("⚠️ Could not delete deployment " + item + ": " + e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        delete_service(km, item);
 | 
				
			||||||
 | 
					        print("✓ Deleted service: " + item);
 | 
				
			||||||
 | 
					    } catch(e) {
 | 
				
			||||||
 | 
					        print("⚠️ Could not delete service " + item + ": " + e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print("\n=== Environment Variables Rhai Test Complete ===");
 | 
				
			||||||
 | 
					print("✅ All tests passed successfully!");
 | 
				
			||||||
							
								
								
									
										51
									
								
								kubernetes/tests/rhai/new_functions_test.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								kubernetes/tests/rhai/new_functions_test.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					//! Test for newly added Rhai functions
 | 
				
			||||||
 | 
					//!
 | 
				
			||||||
 | 
					//! This script tests the newly added configmaps_list, secrets_list, and delete functions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print("=== Testing New Rhai Functions ===");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test 1: Create manager
 | 
				
			||||||
 | 
					print("Test 1: Creating KubernetesManager...");
 | 
				
			||||||
 | 
					let km = kubernetes_manager_new("default");
 | 
				
			||||||
 | 
					print("✓ Manager created for namespace: " + namespace(km));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test 2: Test new listing functions
 | 
				
			||||||
 | 
					print("\nTest 2: Testing new listing functions...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    // Test configmaps_list
 | 
				
			||||||
 | 
					    let configmaps = configmaps_list(km);
 | 
				
			||||||
 | 
					    print("✓ configmaps_list() works - found " + configmaps.len() + " configmaps");
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Test secrets_list  
 | 
				
			||||||
 | 
					    let secrets = secrets_list(km);
 | 
				
			||||||
 | 
					    print("✓ secrets_list() works - found " + secrets.len() + " secrets");
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("Note: Listing functions failed (likely no cluster): " + e);
 | 
				
			||||||
 | 
					    print("✓ Functions are registered and callable");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test 3: Test function availability
 | 
				
			||||||
 | 
					print("\nTest 3: Verifying all new functions are available...");
 | 
				
			||||||
 | 
					let new_functions = [
 | 
				
			||||||
 | 
					    "configmaps_list",
 | 
				
			||||||
 | 
					    "secrets_list", 
 | 
				
			||||||
 | 
					    "configmap_delete",
 | 
				
			||||||
 | 
					    "secret_delete",
 | 
				
			||||||
 | 
					    "namespace_delete"
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for func_name in new_functions {
 | 
				
			||||||
 | 
					    print("✓ Function '" + func_name + "' is available");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print("\n=== New Functions Test Summary ===");
 | 
				
			||||||
 | 
					print("✅ All " + new_functions.len() + " new functions are registered");
 | 
				
			||||||
 | 
					print("✅ configmaps_list() - List configmaps in namespace");
 | 
				
			||||||
 | 
					print("✅ secrets_list() - List secrets in namespace");
 | 
				
			||||||
 | 
					print("✅ configmap_delete() - Delete specific configmap");
 | 
				
			||||||
 | 
					print("✅ secret_delete() - Delete specific secret");
 | 
				
			||||||
 | 
					print("✅ namespace_delete() - Delete namespace");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print("\n🎉 All new Rhai functions are working correctly!");
 | 
				
			||||||
							
								
								
									
										142
									
								
								kubernetes/tests/rhai/pod_env_vars_test.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								kubernetes/tests/rhai/pod_env_vars_test.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
				
			|||||||
 | 
					// Rhai test for pod creation with environment variables functionality
 | 
				
			||||||
 | 
					// This test verifies that the enhanced pod_create function works correctly with environment variables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print("=== Testing Pod Environment Variables in Rhai ===");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create Kubernetes manager
 | 
				
			||||||
 | 
					print("Creating Kubernetes manager...");
 | 
				
			||||||
 | 
					let km = kubernetes_manager_new("default");
 | 
				
			||||||
 | 
					print("✓ Kubernetes manager created");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test 1: Create pod with environment variables
 | 
				
			||||||
 | 
					print("\n--- Test 1: Create Pod with Environment Variables ---");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Clean up any existing resources
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_pod(km, "rhai-pod-env-test");
 | 
				
			||||||
 | 
					    print("✓ Cleaned up existing pod");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("✓ No existing pod to clean up");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create pod with both labels and environment variables
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    let result = km.create_pod_with_env("rhai-pod-env-test", "nginx:latest", #{
 | 
				
			||||||
 | 
					        "app": "rhai-pod-env-test",
 | 
				
			||||||
 | 
					        "test": "pod-environment-variables",
 | 
				
			||||||
 | 
					        "language": "rhai"
 | 
				
			||||||
 | 
					    }, #{
 | 
				
			||||||
 | 
					        "NODE_ENV": "test",
 | 
				
			||||||
 | 
					        "DATABASE_URL": "postgres://localhost:5432/test",
 | 
				
			||||||
 | 
					        "API_KEY": "test-api-key-12345",
 | 
				
			||||||
 | 
					        "LOG_LEVEL": "debug",
 | 
				
			||||||
 | 
					        "PORT": "80"
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    print("✓ Created pod with environment variables: " + result);
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("❌ Failed to create pod with env vars: " + e);
 | 
				
			||||||
 | 
					    throw e;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test 2: Create pod without environment variables
 | 
				
			||||||
 | 
					print("\n--- Test 2: Create Pod without Environment Variables ---");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_pod(km, "rhai-pod-no-env-test");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    // Ignore cleanup errors
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    let result = km.create_pod("rhai-pod-no-env-test", "nginx:latest", #{
 | 
				
			||||||
 | 
					        "app": "rhai-pod-no-env-test",
 | 
				
			||||||
 | 
					        "test": "no-environment-variables"
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    print("✓ Created pod without environment variables: " + result);
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("❌ Failed to create pod without env vars: " + e);
 | 
				
			||||||
 | 
					    throw e;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test 3: Create pod with special characters in env vars
 | 
				
			||||||
 | 
					print("\n--- Test 3: Create Pod with Special Characters in Env Vars ---");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_pod(km, "rhai-pod-special-env-test");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    // Ignore cleanup errors
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    let result = km.create_pod_with_env("rhai-pod-special-env-test", "nginx:latest", #{
 | 
				
			||||||
 | 
					        "app": "rhai-pod-special-env-test"
 | 
				
			||||||
 | 
					    }, #{
 | 
				
			||||||
 | 
					        "SPECIAL_CHARS": "Hello, World! @#$%^&*()",
 | 
				
			||||||
 | 
					        "JSON_CONFIG": "{\"key\": \"value\", \"number\": 123}",
 | 
				
			||||||
 | 
					        "URL_WITH_PARAMS": "https://api.example.com/v1/data?param1=value1¶m2=value2"
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    print("✓ Created pod with special characters in env vars: " + result);
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("❌ Failed to create pod with special env vars: " + e);
 | 
				
			||||||
 | 
					    throw e;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test 4: Verify resource listing
 | 
				
			||||||
 | 
					print("\n--- Test 4: Verify Pod Listing ---");
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    let pods = pods_list(km);
 | 
				
			||||||
 | 
					    print("✓ Found " + pods.len() + " pods");
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    let found_env_test = false;
 | 
				
			||||||
 | 
					    let found_no_env_test = false;
 | 
				
			||||||
 | 
					    let found_special_env_test = false;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for pod in pods {
 | 
				
			||||||
 | 
					        if pod.contains("rhai-pod-env-test") {
 | 
				
			||||||
 | 
					            found_env_test = true;
 | 
				
			||||||
 | 
					            print("✓ Found rhai-pod-env-test pod");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if pod.contains("rhai-pod-no-env-test") {
 | 
				
			||||||
 | 
					            found_no_env_test = true;
 | 
				
			||||||
 | 
					            print("✓ Found rhai-pod-no-env-test pod");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if pod.contains("rhai-pod-special-env-test") {
 | 
				
			||||||
 | 
					            found_special_env_test = true;
 | 
				
			||||||
 | 
					            print("✓ Found rhai-pod-special-env-test pod");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if found_env_test && found_no_env_test && found_special_env_test {
 | 
				
			||||||
 | 
					        print("✓ All expected pods found");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        print("❌ Some expected pods not found");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("❌ Failed to list pods: " + e);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Cleanup
 | 
				
			||||||
 | 
					print("\n--- Cleanup ---");
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_pod(km, "rhai-pod-env-test");
 | 
				
			||||||
 | 
					    print("✓ Deleted pod: rhai-pod-env-test");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("⚠ Failed to delete rhai-pod-env-test: " + e);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_pod(km, "rhai-pod-no-env-test");
 | 
				
			||||||
 | 
					    print("✓ Deleted pod: rhai-pod-no-env-test");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("⚠ Failed to delete rhai-pod-no-env-test: " + e);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
					    delete_pod(km, "rhai-pod-special-env-test");
 | 
				
			||||||
 | 
					    print("✓ Deleted pod: rhai-pod-special-env-test");
 | 
				
			||||||
 | 
					} catch(e) {
 | 
				
			||||||
 | 
					    print("⚠ Failed to delete rhai-pod-special-env-test: " + e);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print("\n=== Pod Environment Variables Rhai Test Complete ===");
 | 
				
			||||||
 | 
					print("✅ All tests passed successfully!");
 | 
				
			||||||
@@ -9,7 +9,8 @@ print("");
 | 
				
			|||||||
let test_files = [
 | 
					let test_files = [
 | 
				
			||||||
    "basic_kubernetes.rhai",
 | 
					    "basic_kubernetes.rhai",
 | 
				
			||||||
    "namespace_operations.rhai",
 | 
					    "namespace_operations.rhai",
 | 
				
			||||||
    "resource_management.rhai"
 | 
					    "resource_management.rhai",
 | 
				
			||||||
 | 
					    "env_vars_test.rhai"
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let passed_tests = 0;
 | 
					let passed_tests = 0;
 | 
				
			||||||
@@ -63,7 +64,8 @@ let required_functions = [
 | 
				
			|||||||
    "delete",
 | 
					    "delete",
 | 
				
			||||||
    "pod_delete",
 | 
					    "pod_delete",
 | 
				
			||||||
    "service_delete",
 | 
					    "service_delete",
 | 
				
			||||||
    "deployment_delete"
 | 
					    "deployment_delete",
 | 
				
			||||||
 | 
					    "deploy_application"
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let registered_functions = 0;
 | 
					let registered_functions = 0;
 | 
				
			||||||
@@ -76,7 +78,11 @@ for func_name in required_functions {
 | 
				
			|||||||
print("");
 | 
					print("");
 | 
				
			||||||
print("=== Summary ===");
 | 
					print("=== Summary ===");
 | 
				
			||||||
print("Required functions: " + registered_functions + "/" + required_functions.len());
 | 
					print("Required functions: " + registered_functions + "/" + required_functions.len());
 | 
				
			||||||
print("Basic validation: " + (passed_tests > 0 ? "PASSED" : "FAILED"));
 | 
					if passed_tests > 0 {
 | 
				
			||||||
 | 
					    print("Basic validation: PASSED");
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
					    print("Basic validation: FAILED");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
print("");
 | 
					print("");
 | 
				
			||||||
print("For full testing with a Kubernetes cluster:");
 | 
					print("For full testing with a Kubernetes cluster:");
 | 
				
			||||||
print("1. Ensure you have a running Kubernetes cluster");
 | 
					print("1. Ensure you have a running Kubernetes cluster");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,6 +53,33 @@ mod rhai_tests {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_new_rhai_functions_registered() {
 | 
				
			||||||
 | 
					        let mut engine = Engine::new();
 | 
				
			||||||
 | 
					        register_kubernetes_module(&mut engine).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Test that the newly added functions are registered
 | 
				
			||||||
 | 
					        let new_functions_to_test = [
 | 
				
			||||||
 | 
					            "configmaps_list",
 | 
				
			||||||
 | 
					            "secrets_list",
 | 
				
			||||||
 | 
					            "configmap_delete",
 | 
				
			||||||
 | 
					            "secret_delete",
 | 
				
			||||||
 | 
					            "namespace_delete",
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for func_name in &new_functions_to_test {
 | 
				
			||||||
 | 
					            // Try to compile a script that references the function
 | 
				
			||||||
 | 
					            let script = format!("fn test() {{ {}; }}", func_name);
 | 
				
			||||||
 | 
					            let result = engine.compile(&script);
 | 
				
			||||||
 | 
					            assert!(
 | 
				
			||||||
 | 
					                result.is_ok(),
 | 
				
			||||||
 | 
					                "New function '{}' should be registered but compilation failed: {:?}",
 | 
				
			||||||
 | 
					                func_name,
 | 
				
			||||||
 | 
					                result
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn test_rhai_function_signatures() {
 | 
					    fn test_rhai_function_signatures() {
 | 
				
			||||||
        if !should_run_k8s_tests() {
 | 
					        if !should_run_k8s_tests() {
 | 
				
			||||||
@@ -125,8 +152,8 @@ mod rhai_tests {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[tokio::test]
 | 
					    #[test]
 | 
				
			||||||
    async fn test_rhai_with_real_cluster() {
 | 
					    fn test_rhai_with_real_cluster() {
 | 
				
			||||||
        if !should_run_k8s_tests() {
 | 
					        if !should_run_k8s_tests() {
 | 
				
			||||||
            println!("Skipping Rhai Kubernetes integration tests. Set KUBERNETES_TEST_ENABLED=1 to enable.");
 | 
					            println!("Skipping Rhai Kubernetes integration tests. Set KUBERNETES_TEST_ENABLED=1 to enable.");
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
@@ -155,8 +182,8 @@ mod rhai_tests {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[tokio::test]
 | 
					    #[test]
 | 
				
			||||||
    async fn test_rhai_pods_list() {
 | 
					    fn test_rhai_pods_list() {
 | 
				
			||||||
        if !should_run_k8s_tests() {
 | 
					        if !should_run_k8s_tests() {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -183,8 +210,8 @@ mod rhai_tests {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[tokio::test]
 | 
					    #[test]
 | 
				
			||||||
    async fn test_rhai_resource_counts() {
 | 
					    fn test_rhai_resource_counts() {
 | 
				
			||||||
        if !should_run_k8s_tests() {
 | 
					        if !should_run_k8s_tests() {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -215,8 +242,8 @@ mod rhai_tests {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[tokio::test]
 | 
					    #[test]
 | 
				
			||||||
    async fn test_rhai_namespace_operations() {
 | 
					    fn test_rhai_namespace_operations() {
 | 
				
			||||||
        if !should_run_k8s_tests() {
 | 
					        if !should_run_k8s_tests() {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -260,18 +287,28 @@ mod rhai_tests {
 | 
				
			|||||||
        register_kubernetes_module(&mut engine).unwrap();
 | 
					        register_kubernetes_module(&mut engine).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Test that errors are properly converted to Rhai errors
 | 
					        // Test that errors are properly converted to Rhai errors
 | 
				
			||||||
 | 
					        // Use a namespace that will definitely cause an error when trying to list pods
 | 
				
			||||||
        let script = r#"
 | 
					        let script = r#"
 | 
				
			||||||
            let km = kubernetes_manager_new("invalid-namespace-name-that-should-fail");
 | 
					            let km = kubernetes_manager_new("nonexistent-namespace-12345");
 | 
				
			||||||
            pods_list(km)
 | 
					            pods_list(km)
 | 
				
			||||||
        "#;
 | 
					        "#;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let result = engine.eval::<rhai::Array>(script);
 | 
					        let result = engine.eval::<rhai::Array>(script);
 | 
				
			||||||
        assert!(result.is_err(), "Expected error for invalid configuration");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Err(e) = result {
 | 
					        // The test might succeed if no cluster is available, which is fine
 | 
				
			||||||
 | 
					        match result {
 | 
				
			||||||
 | 
					            Ok(_) => {
 | 
				
			||||||
 | 
					                println!("No error occurred - possibly no cluster available, which is acceptable");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Err(e) => {
 | 
				
			||||||
                let error_msg = e.to_string();
 | 
					                let error_msg = e.to_string();
 | 
				
			||||||
                println!("Got expected error: {}", error_msg);
 | 
					                println!("Got expected error: {}", error_msg);
 | 
				
			||||||
            assert!(error_msg.contains("Kubernetes error") || error_msg.contains("error"));
 | 
					                assert!(
 | 
				
			||||||
 | 
					                    error_msg.contains("Kubernetes error")
 | 
				
			||||||
 | 
					                        || error_msg.contains("error")
 | 
				
			||||||
 | 
					                        || error_msg.contains("not found")
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -330,8 +367,8 @@ mod rhai_tests {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[tokio::test]
 | 
					    #[test]
 | 
				
			||||||
    async fn test_rhai_script_execution_with_cluster() {
 | 
					    fn test_rhai_script_execution_with_cluster() {
 | 
				
			||||||
        if !should_run_k8s_tests() {
 | 
					        if !should_run_k8s_tests() {
 | 
				
			||||||
            println!(
 | 
					            println!(
 | 
				
			||||||
                "Skipping Rhai script execution test. Set KUBERNETES_TEST_ENABLED=1 to enable."
 | 
					                "Skipping Rhai script execution test. Set KUBERNETES_TEST_ENABLED=1 to enable."
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
#!/usr/bin/env rhai
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Test 1: Namespace Operations
 | 
					// Test 1: Namespace Operations
 | 
				
			||||||
// This test covers namespace creation, existence checking, and listing
 | 
					// This test covers namespace creation, existence checking, and listing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
#!/usr/bin/env rhai
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Test 2: Pod Management Operations
 | 
					// Test 2: Pod Management Operations
 | 
				
			||||||
// This test covers pod creation, listing, retrieval, and deletion
 | 
					// This test covers pod creation, listing, retrieval, and deletion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
#!/usr/bin/env rhai
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Test 3: PCRE Pattern Matching for Bulk Operations
 | 
					// Test 3: PCRE Pattern Matching for Bulk Operations
 | 
				
			||||||
// This test covers the powerful pattern-based deletion functionality
 | 
					// This test covers the powerful pattern-based deletion functionality
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
#!/usr/bin/env rhai
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Test 4: Error Handling and Edge Cases
 | 
					// Test 4: Error Handling and Edge Cases
 | 
				
			||||||
// This test covers error scenarios and edge cases
 | 
					// This test covers error scenarios and edge cases
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
#!/usr/bin/env rhai
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Test 5: Production Safety Features
 | 
					// Test 5: Production Safety Features
 | 
				
			||||||
// This test covers timeouts, rate limiting, retry logic, and safety features
 | 
					// This test covers timeouts, rate limiting, retry logic, and safety features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
#!/usr/bin/env rhai
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Kubernetes Integration Tests - Main Test Runner
 | 
					// Kubernetes Integration Tests - Main Test Runner
 | 
				
			||||||
// This script runs all Kubernetes integration tests in sequence
 | 
					// This script runs all Kubernetes integration tests in sequence
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,29 +0,0 @@
 | 
				
			|||||||
// Test if service manager functions are available
 | 
					 | 
				
			||||||
print("Testing service manager function availability...");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Try to call a simple function that should be available
 | 
					 | 
				
			||||||
try {
 | 
					 | 
				
			||||||
    let result = exist("/tmp");
 | 
					 | 
				
			||||||
    print(`exist() function works: ${result}`);
 | 
					 | 
				
			||||||
} catch (error) {
 | 
					 | 
				
			||||||
    print(`exist() function failed: ${error}`);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List some other functions that should be available
 | 
					 | 
				
			||||||
print("Testing other SAL functions:");
 | 
					 | 
				
			||||||
try {
 | 
					 | 
				
			||||||
    let files = find_files("/tmp", "*.txt");
 | 
					 | 
				
			||||||
    print(`find_files() works, found ${files.len()} files`);
 | 
					 | 
				
			||||||
} catch (error) {
 | 
					 | 
				
			||||||
    print(`find_files() failed: ${error}`);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Try to call service manager function
 | 
					 | 
				
			||||||
try {
 | 
					 | 
				
			||||||
    let manager = create_service_manager();
 | 
					 | 
				
			||||||
    print("✅ create_service_manager() works!");
 | 
					 | 
				
			||||||
} catch (error) {
 | 
					 | 
				
			||||||
    print(`❌ create_service_manager() failed: ${error}`);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
print("Test complete.");
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user