wip
This commit is contained in:
parent
e66bee09cf
commit
89a3abee63
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
target
|
||||
*.pem
|
||||
.env
|
5967
Cargo.lock
generated
Normal file
5967
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
Cargo.toml
27
Cargo.toml
@ -4,12 +4,16 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
name = "actor_osis" # Can be different from package name, or same
|
||||
name = "actor_system" # Can be different from package name, or same
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "actor_osis"
|
||||
path = "cmd/actor_osis.rs"
|
||||
name = "actor_system"
|
||||
path = "cmd/actor.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "actor_system_tui"
|
||||
path = "cmd/terminal_ui.rs"
|
||||
|
||||
[[example]]
|
||||
name = "engine"
|
||||
@ -22,6 +26,7 @@ path = "examples/actor.rs"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
redis = { version = "0.25.0", features = ["tokio-comp"] }
|
||||
rhai = { version = "1.21.0", features = ["std", "sync", "decimal", "internals", "serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
@ -36,12 +41,26 @@ toml = "0.8"
|
||||
thiserror = "1.0"
|
||||
async-trait = "0.1"
|
||||
hero_job = { git = "https://git.ourworld.tf/herocode/baobab.git"}
|
||||
baobab_actor = { git = "https://git.ourworld.tf/herocode/baobab.git", branch = "logger"}
|
||||
baobab_actor = { git = "https://git.ourworld.tf/herocode/baobab.git"}
|
||||
heromodels = { git = "https://git.ourworld.tf/herocode/db.git" }
|
||||
heromodels_core = { git = "https://git.ourworld.tf/herocode/db.git" }
|
||||
heromodels-derive = { git = "https://git.ourworld.tf/herocode/db.git" }
|
||||
rhailib_dsl = { git = "https://git.ourworld.tf/herocode/rhailib.git" }
|
||||
hero_logger = { git = "https://git.ourworld.tf/herocode/baobab.git", branch = "logger" }
|
||||
sal-os = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-redisclient = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-postgresclient = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-process = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-virt = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-git = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-zinit-client = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-mycelium = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-text = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-net = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-kubernetes = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-service-manager = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-vault = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
sal-hetzner = { git = "https://git.ourworld.tf/herocode/herolib_rust.git" }
|
||||
|
||||
[features]
|
||||
default = ["calendar", "finance"]
|
||||
|
60
cmd/actor.rs
Normal file
60
cmd/actor.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use actor_system::AsyncWorker;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "actor_system")]
|
||||
#[command(about = "System Actor - Asynchronous job processing actor")]
|
||||
struct Args {
|
||||
/// Database path
|
||||
#[arg(short, long, default_value = "/tmp/system_db")]
|
||||
db_path: String,
|
||||
|
||||
/// Redis URL
|
||||
#[arg(short, long, default_value = "redis://localhost:6379")]
|
||||
redis_url: String,
|
||||
|
||||
/// Preserve completed tasks in Redis
|
||||
#[arg(short, long)]
|
||||
preserve_tasks: bool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
env_logger::init();
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
info!("Starting System Actor");
|
||||
|
||||
// Create shutdown channel
|
||||
let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
|
||||
|
||||
// Setup signal handler for graceful shutdown
|
||||
let shutdown_tx_clone = shutdown_tx.clone();
|
||||
tokio::spawn(async move {
|
||||
tokio::signal::ctrl_c().await.expect("Failed to listen for Ctrl+C");
|
||||
info!("Received Ctrl+C, initiating shutdown...");
|
||||
let _ = shutdown_tx_clone.send(()).await;
|
||||
});
|
||||
|
||||
// Create and start the actor
|
||||
let actor = Arc::new(
|
||||
AsyncWorker::builder()
|
||||
.db_path(args.db_path)
|
||||
.redis_url(args.redis_url)
|
||||
.build()?
|
||||
);
|
||||
|
||||
let handle = baobab_actor::spawn_actor(actor, shutdown_rx);
|
||||
|
||||
info!("System Actor started, waiting for jobs...");
|
||||
|
||||
// Wait for the actor to complete
|
||||
handle.await??;
|
||||
|
||||
info!("System Actor shutdown complete");
|
||||
Ok(())
|
||||
}
|
302
cmd/sal.rs
302
cmd/sal.rs
@ -1,302 +0,0 @@
|
||||
//! System Worker Binary - Asynchronous actor for high-throughput concurrent processing
|
||||
|
||||
use clap::Parser;
|
||||
use log::{error, info, warn};
|
||||
use baobab_actor::async_actor_impl::AsyncWorker;
|
||||
use baobab_actor::config::{ConfigError, WorkerConfig};
|
||||
use baobab_actor::engine::create_heromodels_engine;
|
||||
use baobab_actor::actor_trait::{spawn_actor, WorkerConfig as TraitWorkerConfig};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::signal;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(
|
||||
name = "system",
|
||||
version = "0.1.0",
|
||||
about = "System Worker - Asynchronous Worker with Concurrent Job Processing",
|
||||
long_about = "An asynchronous actor for Hero framework that processes multiple jobs \
|
||||
concurrently with timeout support. Ideal for high-throughput scenarios \
|
||||
where jobs can be executed in parallel."
|
||||
)]
|
||||
struct Args {
|
||||
/// Path to TOML configuration file
|
||||
#[arg(short, long, help = "Path to TOML configuration file")]
|
||||
config: PathBuf,
|
||||
|
||||
/// Override actor ID from config
|
||||
#[arg(long, help = "Override actor ID from configuration file")]
|
||||
actor_id: Option<String>,
|
||||
|
||||
/// Override Redis URL from config
|
||||
#[arg(long, help = "Override Redis URL from configuration file")]
|
||||
redis_url: Option<String>,
|
||||
|
||||
/// Override database path from config
|
||||
#[arg(long, help = "Override database path from configuration file")]
|
||||
db_path: Option<String>,
|
||||
|
||||
/// Override default timeout in seconds
|
||||
#[arg(long, help = "Override default job timeout in seconds")]
|
||||
timeout: Option<u64>,
|
||||
|
||||
/// Enable verbose logging (debug level)
|
||||
#[arg(short, long, help = "Enable verbose logging")]
|
||||
verbose: bool,
|
||||
|
||||
/// Disable timestamps in log output
|
||||
#[arg(long, help = "Remove timestamps from log output")]
|
||||
no_timestamp: bool,
|
||||
|
||||
/// Show actor statistics periodically
|
||||
#[arg(long, help = "Show periodic actor statistics")]
|
||||
show_stats: bool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let args = Args::parse();
|
||||
|
||||
// Load configuration from TOML file
|
||||
let mut config = match WorkerConfig::from_file(&args.config) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to load configuration from {:?}: {}", args.config, e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Validate that this is an async actor configuration
|
||||
if !config.is_async() {
|
||||
eprintln!("Error: System actor requires an async actor configuration");
|
||||
eprintln!("Expected: [actor_type] type = \"async\"");
|
||||
eprintln!("Found: {:?}", config.actor_type);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// Apply command line overrides
|
||||
if let Some(actor_id) = args.actor_id {
|
||||
config.actor_id = actor_id;
|
||||
}
|
||||
if let Some(redis_url) = args.redis_url {
|
||||
config.redis_url = redis_url;
|
||||
}
|
||||
if let Some(db_path) = args.db_path {
|
||||
config.db_path = db_path;
|
||||
}
|
||||
|
||||
// Override timeout if specified
|
||||
if let Some(timeout_secs) = args.timeout {
|
||||
if let baobab_actor::config::WorkerType::Async { ref mut default_timeout_seconds } = config.actor_type {
|
||||
*default_timeout_seconds = timeout_secs;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure logging
|
||||
setup_logging(&config, args.verbose, args.no_timestamp)?;
|
||||
|
||||
info!("🚀 System Worker starting...");
|
||||
info!("Worker ID: {}", config.actor_id);
|
||||
info!("Redis URL: {}", config.redis_url);
|
||||
info!("Database Path: {}", config.db_path);
|
||||
info!("Preserve Tasks: {}", config.preserve_tasks);
|
||||
|
||||
if let Some(timeout) = config.get_default_timeout() {
|
||||
info!("Default Timeout: {:?}", timeout);
|
||||
}
|
||||
|
||||
// Create Rhai engine
|
||||
let engine = create_heromodels_engine();
|
||||
info!("✅ Rhai engine initialized");
|
||||
|
||||
// Create actor configuration for the trait-based interface
|
||||
let mut actor_config = TraitWorkerConfig::new(
|
||||
config.actor_id.clone(),
|
||||
config.db_path.clone(),
|
||||
config.redis_url.clone(),
|
||||
config.preserve_tasks,
|
||||
);
|
||||
|
||||
// Add timeout configuration for async actor
|
||||
if let Some(timeout) = config.get_default_timeout() {
|
||||
actor_config = actor_config.with_default_timeout(timeout);
|
||||
}
|
||||
|
||||
// Create async actor instance
|
||||
let actor = Arc::new(AsyncWorker::default());
|
||||
info!("✅ Async actor instance created");
|
||||
|
||||
// Setup shutdown signal handling
|
||||
let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
|
||||
|
||||
// Spawn shutdown signal handler
|
||||
let shutdown_tx_clone = shutdown_tx.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = signal::ctrl_c().await {
|
||||
error!("Failed to listen for shutdown signal: {}", e);
|
||||
return;
|
||||
}
|
||||
info!("🛑 Shutdown signal received");
|
||||
if let Err(e) = shutdown_tx_clone.send(()).await {
|
||||
error!("Failed to send shutdown signal: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// Spawn statistics reporter if requested
|
||||
if args.show_stats {
|
||||
let actor_stats = Arc::clone(&actor);
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(30));
|
||||
loop {
|
||||
interval.tick().await;
|
||||
let running_count = actor_stats.running_job_count().await;
|
||||
if running_count > 0 {
|
||||
info!("📊 Worker Stats: {} jobs currently running", running_count);
|
||||
} else {
|
||||
info!("📊 Worker Stats: No jobs currently running");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Spawn the actor
|
||||
info!("🔄 Starting actor loop...");
|
||||
let actor_handle = spawn_actor(actor, engine, shutdown_rx);
|
||||
|
||||
// Wait for the actor to complete
|
||||
match actor_handle.await {
|
||||
Ok(Ok(())) => {
|
||||
info!("✅ System Worker shut down gracefully");
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
error!("❌ System Worker encountered an error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("❌ Failed to join actor task: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Setup logging based on configuration and command line arguments
|
||||
fn setup_logging(
|
||||
config: &WorkerConfig,
|
||||
verbose: bool,
|
||||
no_timestamp: bool,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let mut builder = env_logger::Builder::new();
|
||||
|
||||
// Determine log level
|
||||
let log_level = if verbose {
|
||||
"debug"
|
||||
} else {
|
||||
&config.logging.level
|
||||
};
|
||||
|
||||
// Set log level
|
||||
builder.filter_level(match log_level.to_lowercase().as_str() {
|
||||
"trace" => log::LevelFilter::Trace,
|
||||
"debug" => log::LevelFilter::Debug,
|
||||
"info" => log::LevelFilter::Info,
|
||||
"warn" => log::LevelFilter::Warn,
|
||||
"error" => log::LevelFilter::Error,
|
||||
_ => {
|
||||
warn!("Invalid log level: {}. Using 'info'", log_level);
|
||||
log::LevelFilter::Info
|
||||
}
|
||||
});
|
||||
|
||||
// Configure timestamps
|
||||
let show_timestamps = !no_timestamp && config.logging.timestamps;
|
||||
if !show_timestamps {
|
||||
builder.format_timestamp(None);
|
||||
}
|
||||
|
||||
builder.init();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::io::Write;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
#[test]
|
||||
fn test_config_validation() {
|
||||
let config_toml = r#"
|
||||
actor_id = "test_system"
|
||||
redis_url = "redis://localhost:6379"
|
||||
db_path = "/tmp/test_db"
|
||||
|
||||
[actor_type]
|
||||
type = "async"
|
||||
default_timeout_seconds = 600
|
||||
|
||||
[logging]
|
||||
level = "info"
|
||||
"#;
|
||||
|
||||
let mut temp_file = NamedTempFile::new().unwrap();
|
||||
temp_file.write_all(config_toml.as_bytes()).unwrap();
|
||||
|
||||
let config = WorkerConfig::from_file(temp_file.path()).unwrap();
|
||||
assert!(!config.is_sync());
|
||||
assert!(config.is_async());
|
||||
assert_eq!(config.actor_id, "test_system");
|
||||
assert_eq!(config.get_default_timeout(), Some(Duration::from_secs(600)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync_config_rejection() {
|
||||
let config_toml = r#"
|
||||
actor_id = "test_system"
|
||||
redis_url = "redis://localhost:6379"
|
||||
db_path = "/tmp/test_db"
|
||||
|
||||
[actor_type]
|
||||
type = "sync"
|
||||
|
||||
[logging]
|
||||
level = "info"
|
||||
"#;
|
||||
|
||||
let mut temp_file = NamedTempFile::new().unwrap();
|
||||
temp_file.write_all(config_toml.as_bytes()).unwrap();
|
||||
|
||||
let config = WorkerConfig::from_file(temp_file.path()).unwrap();
|
||||
assert!(config.is_sync());
|
||||
assert!(!config.is_async());
|
||||
// This would be rejected in main() function
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeout_override() {
|
||||
let config_toml = r#"
|
||||
actor_id = "test_system"
|
||||
redis_url = "redis://localhost:6379"
|
||||
db_path = "/tmp/test_db"
|
||||
|
||||
[actor_type]
|
||||
type = "async"
|
||||
default_timeout_seconds = 300
|
||||
"#;
|
||||
|
||||
let mut temp_file = NamedTempFile::new().unwrap();
|
||||
temp_file.write_all(config_toml.as_bytes()).unwrap();
|
||||
|
||||
let mut config = WorkerConfig::from_file(temp_file.path()).unwrap();
|
||||
assert_eq!(config.get_default_timeout(), Some(Duration::from_secs(300)));
|
||||
|
||||
// Test timeout override
|
||||
if let baobab_actor::config::WorkerType::Async { ref mut default_timeout_seconds } = config.actor_type {
|
||||
*default_timeout_seconds = 600;
|
||||
}
|
||||
assert_eq!(config.get_default_timeout(), Some(Duration::from_secs(600)));
|
||||
}
|
||||
}
|
156
cmd/terminal_ui.rs
Normal file
156
cmd/terminal_ui.rs
Normal file
@ -0,0 +1,156 @@
|
||||
//! Simplified main function for Baobab Actor TUI
|
||||
//!
|
||||
//! This binary provides a clean entry point for the actor monitoring and job dispatch interface.
|
||||
|
||||
use anyhow::{Result, Context};
|
||||
use baobab_actor::terminal_ui::{App, setup_and_run_tui};
|
||||
use clap::Parser;
|
||||
use hero_job::ScriptType;
|
||||
use log::{info, warn, error};
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Child, Command};
|
||||
use tokio::signal;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "baobab-actor-tui")]
|
||||
#[command(about = "Terminal UI for Baobab Actor - Monitor and dispatch jobs to a single actor")]
|
||||
struct Args {
|
||||
/// Redis URL for job queue
|
||||
#[arg(short, long, default_value = "redis://localhost:6379")]
|
||||
redis_url: String,
|
||||
|
||||
/// Enable verbose logging
|
||||
#[arg(short, long)]
|
||||
verbose: bool,
|
||||
}
|
||||
|
||||
/// Initialize logging based on verbosity level
|
||||
fn init_logging(verbose: bool) {
|
||||
if verbose {
|
||||
env_logger::Builder::from_default_env()
|
||||
.filter_level(log::LevelFilter::Debug)
|
||||
.init();
|
||||
} else {
|
||||
env_logger::Builder::from_default_env()
|
||||
.filter_level(log::LevelFilter::Info)
|
||||
.init();
|
||||
}
|
||||
}
|
||||
|
||||
/// Create and configure the TUI application
|
||||
fn create_app(args: &Args) -> Result<App> {
|
||||
let actor_id = "sal".to_string();
|
||||
|
||||
// Get the crate root directory
|
||||
let crate_root = std::env::var("CARGO_MANIFEST_DIR")
|
||||
.unwrap_or_else(|_| ".".to_string());
|
||||
let crate_root = PathBuf::from(crate_root);
|
||||
|
||||
let actor_path = crate_root.join("target/debug/actor_system");
|
||||
let example_dir = Some(crate_root.join("examples/scripts"));
|
||||
|
||||
let mut app = App::new(
|
||||
actor_id,
|
||||
actor_path,
|
||||
args.redis_url.clone(),
|
||||
example_dir,
|
||||
)?;
|
||||
|
||||
// Set the correct script type for the system actor
|
||||
// System actor processes SAL (System Abstraction Layer) scripts, not OSIS scripts
|
||||
app.job_form.script_type = ScriptType::SAL;
|
||||
|
||||
Ok(app)
|
||||
}
|
||||
|
||||
/// Spawn the actor binary as a background process
|
||||
fn spawn_actor_process(_args: &Args) -> Result<Child> {
|
||||
// Get the crate root directory
|
||||
let crate_root = std::env::var("CARGO_MANIFEST_DIR")
|
||||
.unwrap_or_else(|_| ".".to_string());
|
||||
let actor_path = PathBuf::from(crate_root).join("target/debug/actor_system");
|
||||
info!("🎬 Spawning actor process: {}", actor_path.display());
|
||||
|
||||
let mut cmd = Command::new(&actor_path);
|
||||
|
||||
// Redirect stdout and stderr to null to prevent logs from interfering with TUI
|
||||
cmd.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null());
|
||||
|
||||
// Spawn the process
|
||||
let child = cmd
|
||||
.spawn()
|
||||
.with_context(|| format!("Failed to spawn actor process: {}", actor_path.display()))?;
|
||||
|
||||
info!("✅ Actor process spawned with PID: {}", child.id());
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
/// Cleanup function to terminate actor process
|
||||
fn cleanup_actor_process(mut actor_process: Child) {
|
||||
info!("🧹 Cleaning up actor process...");
|
||||
|
||||
match actor_process.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
info!("Actor process already exited with status: {}", status);
|
||||
}
|
||||
Ok(None) => {
|
||||
info!("Terminating actor process...");
|
||||
if let Err(e) = actor_process.kill() {
|
||||
error!("Failed to kill actor process: {}", e);
|
||||
} else {
|
||||
match actor_process.wait() {
|
||||
Ok(status) => info!("Actor process terminated with status: {}", status),
|
||||
Err(e) => error!("Failed to wait for actor process: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to check actor process status: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
// Initialize logging
|
||||
init_logging(args.verbose);
|
||||
|
||||
let crate_root = std::env::var("CARGO_MANIFEST_DIR")
|
||||
.unwrap_or_else(|_| ".".to_string());
|
||||
|
||||
info!("🚀 Starting Baobab Actor TUI...");
|
||||
info!("Actor ID: sal (System Actor)");
|
||||
info!("Actor Path: {}/target/debug/actor_system", crate_root);
|
||||
info!("Redis URL: {}", args.redis_url);
|
||||
info!("Script Type: SAL");
|
||||
info!("Example Directory: {}/examples/scripts", crate_root);
|
||||
|
||||
// Spawn the actor process first
|
||||
let actor_process = spawn_actor_process(&args)?;
|
||||
|
||||
// Give the actor a moment to start up
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
|
||||
|
||||
// Create app and run TUI
|
||||
let app = create_app(&args)?;
|
||||
|
||||
// Set up signal handling for graceful shutdown
|
||||
let result = tokio::select! {
|
||||
tui_result = setup_and_run_tui(app) => {
|
||||
info!("TUI exited");
|
||||
tui_result
|
||||
}
|
||||
_ = signal::ctrl_c() => {
|
||||
info!("Received Ctrl+C, shutting down...");
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
// Clean up the actor process
|
||||
cleanup_actor_process(actor_process);
|
||||
|
||||
result
|
||||
}
|
202
examples/engine.rs
Normal file
202
examples/engine.rs
Normal file
@ -0,0 +1,202 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::panic;
|
||||
use std::path::Path;
|
||||
use rhai::{Engine, Dynamic};
|
||||
|
||||
use actor_system::AsyncWorker;
|
||||
|
||||
/// Recursively collect all .rhai files from a directory and its subdirectories
|
||||
fn collect_rhai_files(dir: &Path, files: &mut Vec<std::path::PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
for entry in fs::read_dir(dir)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
|
||||
if path.is_dir() {
|
||||
// Recursively search subdirectories
|
||||
collect_rhai_files(&path, files)?;
|
||||
} else if path.extension().and_then(|s| s.to_str()) == Some("rhai") {
|
||||
// Store the canonicalized absolute path to avoid resolution issues
|
||||
let absolute_path = path.canonicalize().unwrap_or(path);
|
||||
files.push(absolute_path);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Parse command line arguments for verbosity
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let verbose = args.contains(&"--verbose".to_string()) || args.contains(&"-v".to_string());
|
||||
|
||||
// Set up custom panic hook to suppress panic messages unless verbose
|
||||
if !verbose {
|
||||
panic::set_hook(Box::new(|_| {
|
||||
// Suppress panic output in non-verbose mode
|
||||
}));
|
||||
}
|
||||
|
||||
// Initialize logging only if verbose
|
||||
if verbose {
|
||||
env_logger::init();
|
||||
}
|
||||
|
||||
println!("=== OSIS Engine Direct Execution Example ===");
|
||||
|
||||
// Find all Rhai scripts in examples/scripts directory
|
||||
let scripts_dir = Path::new("examples/scripts");
|
||||
if !scripts_dir.exists() {
|
||||
eprintln!("Scripts directory not found: {}", scripts_dir.display());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut script_files = Vec::new();
|
||||
collect_rhai_files(scripts_dir, &mut script_files)?;
|
||||
|
||||
script_files.sort();
|
||||
|
||||
if verbose {
|
||||
println!("Found {} Rhai scripts in {}", script_files.len(), scripts_dir.display());
|
||||
} else {
|
||||
println!("Testing {} Rhai scripts:\n", script_files.len());
|
||||
}
|
||||
|
||||
// Create temporary database path
|
||||
let db_path = "temp_osis_engine_example_db";
|
||||
|
||||
// Clean up previous database if it exists
|
||||
if Path::new(db_path).exists() {
|
||||
fs::remove_dir_all(db_path)?;
|
||||
}
|
||||
|
||||
if verbose {
|
||||
println!("Created temporary database path: {}", db_path);
|
||||
}
|
||||
|
||||
// Track results for summary
|
||||
let mut success_count = 0;
|
||||
let mut failure_count = 0;
|
||||
|
||||
// Execute all scripts with colored output
|
||||
for (i, script_path) in script_files.iter().enumerate() {
|
||||
let script_name = script_path.file_name().unwrap().to_string_lossy();
|
||||
|
||||
if verbose {
|
||||
println!("\n=== Script {}/{}: {} ===", i + 1, script_files.len(), script_name);
|
||||
println!("--- Path: {} ---", script_path.display());
|
||||
println!("--- Using Fresh OSIS Engine with Job Context ---");
|
||||
}
|
||||
|
||||
// Read script content with detailed debugging
|
||||
if verbose {
|
||||
println!("--- Attempting to read: {} ---", script_path.display());
|
||||
println!("--- Path exists: {} ---", script_path.exists());
|
||||
println!("--- Path is file: {} ---", script_path.is_file());
|
||||
}
|
||||
|
||||
let script_content = match fs::read_to_string(script_path) {
|
||||
Ok(content) => {
|
||||
if verbose {
|
||||
println!("--- Successfully read {} bytes ---", content.len());
|
||||
}
|
||||
content
|
||||
},
|
||||
Err(e) => {
|
||||
println!("\x1b[31m✗\x1b[0m {} ... \x1b[31mFAILED\x1b[0m (read error: {})", script_name, e);
|
||||
if verbose {
|
||||
println!("--- Debug: Path = '{}' ---", script_path.display());
|
||||
println!("--- Debug: Canonical path = '{:?}' ---", script_path.canonicalize());
|
||||
}
|
||||
failure_count += 1;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new engine instance and configure it with DSL modules
|
||||
let mut engine_with_context = match create_configured_engine(db_path, i + 1, verbose) {
|
||||
Ok(engine) => engine,
|
||||
Err(e) => {
|
||||
println!("\x1b[31m✗\x1b[0m {} ... \x1b[31mFAILED\x1b[0m (engine setup: {})", script_name, e);
|
||||
failure_count += 1;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Execute the script with graceful error handling (catches both errors and panics)
|
||||
let script_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
engine_with_context.eval::<rhai::Dynamic>(&script_content)
|
||||
}));
|
||||
|
||||
match script_result {
|
||||
Ok(Ok(result)) => {
|
||||
println!("\x1b[32m✓\x1b[0m {} ... \x1b[32mSUCCESS\x1b[0m", script_name);
|
||||
if verbose {
|
||||
println!(" Result: {:?}", result);
|
||||
}
|
||||
success_count += 1;
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
println!("\x1b[31m✗\x1b[0m {} ... \x1b[31mFAILED\x1b[0m", script_name);
|
||||
if verbose {
|
||||
println!(" Error: {}", e);
|
||||
}
|
||||
failure_count += 1;
|
||||
}
|
||||
Err(panic_err) => {
|
||||
let panic_msg = if let Some(s) = panic_err.downcast_ref::<String>() {
|
||||
s.clone()
|
||||
} else if let Some(s) = panic_err.downcast_ref::<&str>() {
|
||||
s.to_string()
|
||||
} else {
|
||||
"Unknown panic".to_string()
|
||||
};
|
||||
println!("\x1b[31m✗\x1b[0m {} ... \x1b[31mFAILED\x1b[0m", script_name);
|
||||
if verbose {
|
||||
println!(" Panic: {}", panic_msg);
|
||||
}
|
||||
failure_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print summary
|
||||
println!("\n=== Summary ===");
|
||||
println!("\x1b[32m✓ {} scripts succeeded\x1b[0m", success_count);
|
||||
println!("\x1b[31m✗ {} scripts failed\x1b[0m", failure_count);
|
||||
println!("Total: {} scripts", success_count + failure_count);
|
||||
|
||||
// Clean up the temporary database
|
||||
if Path::new(db_path).exists() {
|
||||
fs::remove_dir_all(db_path)?;
|
||||
if verbose {
|
||||
println!("\nCleaned up temporary database: {}", db_path);
|
||||
}
|
||||
}
|
||||
|
||||
if verbose {
|
||||
println!("=== Engine Example Complete ===");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a configured Rhai engine with DSL modules and job context
|
||||
fn create_configured_engine(db_path: &str, script_index: usize, verbose: bool) -> Result<Engine, String> {
|
||||
// Create a new engine instance
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Register all DSL modules (same as OSIS engine configuration)
|
||||
actor_system::register_sal_modules(&mut engine);
|
||||
|
||||
// Set up job context tags (similar to execute_job_with_engine)
|
||||
let mut db_config = rhai::Map::new();
|
||||
db_config.insert("DB_PATH".into(), db_path.to_string().into());
|
||||
db_config.insert("CALLER_ID".into(), "engine_example".to_string().into());
|
||||
db_config.insert("CONTEXT_ID".into(), format!("script_{}", script_index).into());
|
||||
engine.set_default_tag(Dynamic::from(db_config));
|
||||
|
||||
if verbose {
|
||||
println!(" Set job context: DB_PATH={}, CALLER_ID=engine_example, CONTEXT_ID=script_{}", db_path, script_index);
|
||||
}
|
||||
|
||||
Ok(engine)
|
||||
}
|
64
examples/scripts/_archive/03_process_management.rhai
Normal file
64
examples/scripts/_archive/03_process_management.rhai
Normal file
@ -0,0 +1,64 @@
|
||||
// 03_process_management.rhai
|
||||
// Demonstrates process management operations using SAL
|
||||
|
||||
// Check if common commands exist
|
||||
println("Checking if common commands exist:");
|
||||
let commands = ["ls", "echo", "cat", "grep"];
|
||||
for cmd in commands {
|
||||
let exists = which(cmd);
|
||||
println(` - ${cmd}: ${exists}`);
|
||||
}
|
||||
|
||||
// Run a simple command
|
||||
println("\nRunning a simple echo command:");
|
||||
let echo_result = run_command("echo 'Hello from Rhai process management!'");
|
||||
println(`Command output: ${echo_result.stdout}`);
|
||||
// The CommandResult type doesn't have an exit_code property
|
||||
println(`Success: ${echo_result.success}`);
|
||||
|
||||
// Run a command silently (no output to console)
|
||||
println("\nRunning a command silently:");
|
||||
let silent_result = run_silent("ls -la");
|
||||
println(`Command success: ${silent_result.success}`);
|
||||
println(`Command output length: ${silent_result.stdout.len()} characters`);
|
||||
|
||||
// Create custom run options
|
||||
println("\nRunning a command with custom options:");
|
||||
let options = new_run_options();
|
||||
options["die"] = false; // Don't return error if command fails
|
||||
options["silent"] = true; // Suppress output to stdout/stderr
|
||||
options["async_exec"] = false; // Run synchronously
|
||||
options["log"] = true; // Log command execution
|
||||
|
||||
let custom_result = run("echo 'Custom options test'", options);
|
||||
println(`Command success: ${custom_result.success}`);
|
||||
println(`Command output: ${custom_result.stdout}`);
|
||||
|
||||
// List processes
|
||||
println("\nListing processes (limited to 5):");
|
||||
let processes = process_list("");
|
||||
let count = 0;
|
||||
for proc in processes {
|
||||
if count >= 5 {
|
||||
break;
|
||||
}
|
||||
// Just print the PID since we're not sure what other properties are available
|
||||
println(` - PID: ${proc.pid}`);
|
||||
count += 1;
|
||||
}
|
||||
println(`Total processes: ${processes.len()}`);
|
||||
|
||||
// Run a command that will create a background process
|
||||
// Note: This is just for demonstration, the process will be short-lived
|
||||
println("\nRunning a background process:");
|
||||
let bg_options = new_run_options();
|
||||
bg_options["async_exec"] = true;
|
||||
// Fix the command to avoid issues with shell interpretation
|
||||
let bg_result = run("sleep 1", bg_options);
|
||||
println("Background process started");
|
||||
|
||||
// Wait a moment to let the background process run
|
||||
run_command("sleep 0.5");
|
||||
println("Main script continuing while background process runs");
|
||||
|
||||
"Process management script completed successfully!"
|
65
examples/scripts/_archive/06_file_read_write.rhai
Normal file
65
examples/scripts/_archive/06_file_read_write.rhai
Normal file
@ -0,0 +1,65 @@
|
||||
// 06_file_read_write.rhai
|
||||
// Demonstrates file read and write operations using SAL
|
||||
|
||||
// Create a test directory
|
||||
let test_dir = "rhai_file_test_dir";
|
||||
println(`Creating directory: ${test_dir}`);
|
||||
let mkdir_result = mkdir(test_dir);
|
||||
println(`Directory creation result: ${mkdir_result}`);
|
||||
|
||||
// Define file paths
|
||||
let test_file = test_dir + "/test_file.txt";
|
||||
let append_file = test_dir + "/append_file.txt";
|
||||
|
||||
// 1. Write to a file
|
||||
println(`\n--- Writing to file: ${test_file} ---`);
|
||||
let content = "This is the first line of text.\nThis is the second line of text.";
|
||||
let write_result = file_write(test_file, content);
|
||||
println(`Write result: ${write_result}`);
|
||||
|
||||
// 2. Read from a file
|
||||
println(`\n--- Reading from file: ${test_file} ---`);
|
||||
let read_content = file_read(test_file);
|
||||
println("File content:");
|
||||
println(read_content);
|
||||
|
||||
// 3. Append to a file
|
||||
println(`\n--- Creating and appending to file: ${append_file} ---`);
|
||||
// First create the file with initial content
|
||||
let initial_content = "Initial content - line 1\nInitial content - line 2\n";
|
||||
let create_result = file_write(append_file, initial_content);
|
||||
println(`Create result: ${create_result}`);
|
||||
|
||||
// Now append to the file
|
||||
let append_content = "Appended content - line 3\nAppended content - line 4\n";
|
||||
let append_result = file_write_append(append_file, append_content);
|
||||
println(`Append result: ${append_result}`);
|
||||
|
||||
// Read the appended file to verify
|
||||
println(`\n--- Reading appended file: ${append_file} ---`);
|
||||
let appended_content = file_read(append_file);
|
||||
println("Appended file content:");
|
||||
println(appended_content);
|
||||
|
||||
// 4. Demonstrate multiple appends
|
||||
println(`\n--- Demonstrating multiple appends ---`);
|
||||
for i in range(1, 4) {
|
||||
// Use a simple counter instead of timestamp to avoid issues
|
||||
let log_entry = `Log entry #${i} - appended at iteration ${i}\n`;
|
||||
file_write_append(append_file, log_entry);
|
||||
println(`Added log entry #${i}`);
|
||||
}
|
||||
|
||||
// Read the final file content
|
||||
println(`\n--- Final file content after multiple appends ---`);
|
||||
let final_content = file_read(append_file);
|
||||
println(final_content);
|
||||
|
||||
// Clean up (uncomment to actually delete the files)
|
||||
// println("\nCleaning up...");
|
||||
// delete(test_file);
|
||||
// delete(append_file);
|
||||
// delete(test_dir);
|
||||
// println("Cleanup complete");
|
||||
|
||||
"File read/write operations script completed successfully!"
|
62
examples/scripts/_archive/container_example.rs
Normal file
62
examples/scripts/_archive/container_example.rs
Normal file
@ -0,0 +1,62 @@
|
||||
// File: /root/code/git.threefold.info/herocode/sal/examples/container_example.rs
|
||||
|
||||
use std::error::Error;
|
||||
use sal::virt::nerdctl::Container;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Create a container from an image
|
||||
println!("Creating container from image...");
|
||||
let container = Container::from_image("my-nginx", "nginx:latest")?
|
||||
.with_port("8080:80")
|
||||
.with_env("NGINX_HOST", "example.com")
|
||||
.with_volume("/tmp/nginx:/usr/share/nginx/html")
|
||||
.with_health_check("curl -f http://localhost/ || exit 1")
|
||||
.with_detach(true)
|
||||
.build()?;
|
||||
|
||||
println!("Container created successfully");
|
||||
|
||||
// Execute a command in the container
|
||||
println!("Executing command in container...");
|
||||
let result = container.exec("echo 'Hello from container'")?;
|
||||
println!("Command output: {}", result.stdout);
|
||||
|
||||
// Get container status
|
||||
println!("Getting container status...");
|
||||
let status = container.status()?;
|
||||
println!("Container status: {}", status.status);
|
||||
|
||||
// Get resource usage
|
||||
println!("Getting resource usage...");
|
||||
let resources = container.resources()?;
|
||||
println!("CPU usage: {}", resources.cpu_usage);
|
||||
println!("Memory usage: {}", resources.memory_usage);
|
||||
|
||||
// Stop and remove the container
|
||||
println!("Stopping and removing container...");
|
||||
container.stop()?;
|
||||
container.remove()?;
|
||||
|
||||
println!("Container stopped and removed");
|
||||
|
||||
// Get a container by name (if it exists)
|
||||
println!("\nGetting a container by name...");
|
||||
match Container::new("existing-container") {
|
||||
Ok(container) => {
|
||||
if container.container_id.is_some() {
|
||||
println!("Found container with ID: {}", container.container_id.as_ref().unwrap());
|
||||
|
||||
// Perform operations on the existing container
|
||||
let status = container.status()?;
|
||||
println!("Container status: {}", status.status);
|
||||
} else {
|
||||
println!("Container exists but has no ID");
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error getting container: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
210
examples/scripts/_archive/containerd_grpc_setup.rhai
Normal file
210
examples/scripts/_archive/containerd_grpc_setup.rhai
Normal file
@ -0,0 +1,210 @@
|
||||
// containerd_grpc_setup.rhai
|
||||
//
|
||||
// This script sets up a Rust project with gRPC connectivity to containerd
|
||||
// Following the steps from the instructions document
|
||||
|
||||
|
||||
run("apt-get -y protobuf-compiler ");
|
||||
|
||||
// Step 1: Set up project directory
|
||||
let project_dir = "/tmp/containerd-rust-client";
|
||||
print(`Setting up project in: ${project_dir}`);
|
||||
|
||||
// Clean up any existing directory
|
||||
if exist(project_dir) {
|
||||
print("Found existing project directory, removing it...");
|
||||
delete(project_dir);
|
||||
}
|
||||
|
||||
// Create our project directory
|
||||
mkdir(project_dir);
|
||||
|
||||
// Change to the project directory
|
||||
chdir(project_dir);
|
||||
|
||||
// Step 2: Clone containerd's gRPC proto files
|
||||
print("Cloning containerd repository to get proto files...");
|
||||
let git_tree = gittree_new(project_dir);
|
||||
let repos = git_tree.get("https://github.com/containerd/containerd.git");
|
||||
let repo = repos[0];
|
||||
print(`Cloned containerd repository to: ${repo.path()}`);
|
||||
|
||||
// Step 3: Create necessary project files
|
||||
print("Creating Cargo.toml file...");
|
||||
// Using raw string with # for multiline content
|
||||
let cargo_toml = #"
|
||||
[package]
|
||||
name = "containerd-rust-client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tonic = "0.11"
|
||||
prost = "0.12"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
hyper-unix-connector = "0.2.0"
|
||||
tower = "0.4"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.11"
|
||||
"#;
|
||||
|
||||
file_write("Cargo.toml", cargo_toml);
|
||||
print("Created Cargo.toml file");
|
||||
|
||||
// Step 4: Set up build.rs to compile protos
|
||||
print("Creating build.rs file...");
|
||||
let build_rs = #"
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=containerd/api/services/images/v1/images.proto");
|
||||
println!("cargo:rerun-if-changed=containerd/api/services/containers/v1/containers.proto");
|
||||
|
||||
tonic_build::configure()
|
||||
.build_server(false)
|
||||
.compile(
|
||||
&[
|
||||
"containerd/api/services/images/v1/images.proto",
|
||||
"containerd/api/services/containers/v1/containers.proto",
|
||||
// Add more proto files as needed
|
||||
],
|
||||
&[
|
||||
"containerd",
|
||||
"containerd/api",
|
||||
"containerd/api/types"
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
"#;
|
||||
|
||||
file_write("build.rs", build_rs);
|
||||
print("Created build.rs file");
|
||||
|
||||
// Step 5: Create src directory and main.rs file
|
||||
mkdir("src");
|
||||
|
||||
// Create a helper function for Unix socket connection
|
||||
print("Creating src/main.rs file...");
|
||||
let main_rs = #"
|
||||
use tonic::transport::{Channel, Endpoint, Uri};
|
||||
use tower::service_fn;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
// The proto-generated modules will be available after build
|
||||
// use containerd::services::images::v1::{
|
||||
// images_client::ImagesClient,
|
||||
// GetImageRequest,
|
||||
// };
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Connecting to containerd gRPC...");
|
||||
|
||||
// Path to containerd socket
|
||||
let socket_path = "/run/containerd/containerd.sock";
|
||||
|
||||
// Connect to the Unix socket
|
||||
let channel = unix_socket_channel(socket_path).await?;
|
||||
|
||||
// Now we'd create a client and use it
|
||||
// let mut client = ImagesClient::new(channel);
|
||||
// let response = client.get(GetImageRequest {
|
||||
// name: "docker.io/library/ubuntu:latest".to_string(),
|
||||
// }).await?;
|
||||
// println!("Image: {:?}", response.into_inner());
|
||||
|
||||
println!("Connection to containerd socket established successfully!");
|
||||
println!("This is a template - uncomment the client code after building.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Helper function to connect to Unix socket
|
||||
async fn unix_socket_channel(path: &str) -> Result<Channel, Box<dyn std::error::Error>> {
|
||||
// Use a placeholder URI since Unix sockets don't have URIs
|
||||
let endpoint = Endpoint::try_from("http://[::]:50051")?;
|
||||
|
||||
// The socket path to connect to
|
||||
let path_to_connect = path.to_string();
|
||||
|
||||
// Create a connector function that connects to the Unix socket
|
||||
let channel = endpoint
|
||||
.connect_with_connector(service_fn(move |_: Uri| {
|
||||
let path = path_to_connect.clone();
|
||||
async move {
|
||||
tokio::net::UnixStream::connect(path)
|
||||
.await
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
|
||||
}
|
||||
}))
|
||||
.await?;
|
||||
|
||||
Ok(channel)
|
||||
}
|
||||
"#;
|
||||
|
||||
file_write("src/main.rs", main_rs);
|
||||
print("Created src/main.rs file");
|
||||
|
||||
// Step 6: Create a README.md file
|
||||
print("Creating README.md file...");
|
||||
// Using raw string with # for multiline content containing markdown backticks
|
||||
let readme = #"# containerd Rust gRPC Client
|
||||
|
||||
A Rust client for interacting with containerd via gRPC.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Rust and Cargo installed
|
||||
- containerd running on your system
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
cargo build
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Connect to containerd via Unix socket
|
||||
- Query image information
|
||||
- Work with containers
|
||||
|
||||
## Structure
|
||||
|
||||
- `src/main.rs` - Example client code
|
||||
- `build.rs` - Proto compilation script
|
||||
"#;
|
||||
|
||||
file_write("README.md", readme);
|
||||
print("Created README.md file");
|
||||
|
||||
// Step 7: Build the project
|
||||
print("Building the project...");
|
||||
let build_result = run("cargo build");
|
||||
|
||||
if build_result.success {
|
||||
print("Project built successfully!");
|
||||
} else {
|
||||
print(`Build failed with error: ${build_result.stderr}`);
|
||||
}
|
||||
|
||||
print(`
|
||||
--------------------------------------
|
||||
🎉 Setup complete!
|
||||
|
||||
Project created at: ${project_dir}
|
||||
|
||||
To use the project:
|
||||
1. cd ${project_dir}
|
||||
2. cargo run
|
||||
|
||||
Note: Make sure containerd is running and the socket exists at /run/containerd/containerd.sock
|
||||
--------------------------------------
|
||||
`);
|
105
examples/scripts/_archive/download_test.rhai
Normal file
105
examples/scripts/_archive/download_test.rhai
Normal file
@ -0,0 +1,105 @@
|
||||
|
||||
print("\n=== Test download() Functionality ===");
|
||||
|
||||
// Create test directory
|
||||
let download_dir = "/tmp/downloadtest";
|
||||
|
||||
// Clean up any previous test files
|
||||
delete(download_dir);
|
||||
mkdir(download_dir);
|
||||
print("Created test directory for downloads at " + download_dir);
|
||||
|
||||
// Test URLs
|
||||
let zip_url = "https://github.com/freeflowuniverse/herolib/archive/refs/tags/v1.0.24.zip";
|
||||
let targz_url = "https://github.com/freeflowuniverse/herolib/archive/refs/tags/v1.0.24.tar.gz";
|
||||
let binary_url = "https://github.com/freeflowuniverse/herolib/releases/download/v1.0.24/hero-aarch64-unknown-linux-musl";
|
||||
|
||||
// Create destinations
|
||||
let zip_dest = `${download_dir}/zip`;
|
||||
let targz_dest = `${download_dir}/targz`;
|
||||
let binary_dest = `${download_dir}/hero-binary`;
|
||||
|
||||
|
||||
//PART 1
|
||||
|
||||
// Download and extract .zip file
|
||||
print("\nTesting .zip download:");
|
||||
// Download function now extracts zip files automatically
|
||||
let result = download(zip_url, zip_dest, 0);
|
||||
|
||||
// Check if files were extracted
|
||||
let file_count = find_files(zip_dest, "*").len();
|
||||
print(` Files found after extraction: ${file_count}`);
|
||||
let success_msg = if file_count > 0 { "yes" } else { "no" };
|
||||
print(` Extraction successful: ${success_msg}`);
|
||||
|
||||
//PART 2
|
||||
|
||||
// Download and extract .tar.gz file
|
||||
print("\nTesting .tar.gz download:");
|
||||
let result = download(targz_url, targz_dest, 0);
|
||||
|
||||
// Check if files were extracted (download function should extract tar.gz automatically)
|
||||
let file_count = find_files(targz_dest, "*").len();
|
||||
print(` Files found after extraction: ${file_count}`);
|
||||
let success_msg = if file_count > 100 { "yes" } else { "no" };
|
||||
print(` Extraction successful: ${success_msg}`);
|
||||
|
||||
//PART 3
|
||||
|
||||
// Download binary file and check size
|
||||
print("\nTesting binary download:");
|
||||
download_file(binary_url, binary_dest, 8000);
|
||||
|
||||
// Check file size using our new file_size function
|
||||
let size_bytes = file_size(binary_dest);
|
||||
let size_mb = size_bytes / (1024 * 1024);
|
||||
print(` File size: ${size_mb} MB`);
|
||||
let size_check = if size_mb > 5 { "yes" } else { "no" };
|
||||
print(` Size > 5MB: ${size_check}`);
|
||||
let success_msg = if size_mb >= 8 > 100 { "yes" } else { "no" };
|
||||
print(` Minimum size check passed:${success_msg}`);
|
||||
|
||||
// Clean up test files
|
||||
delete(download_dir);
|
||||
print("Cleaned up test directory");
|
||||
//PART 4
|
||||
|
||||
// Test the new download_file function
|
||||
print("\nTesting download_file function:");
|
||||
let text_url = "https://raw.githubusercontent.com/freeflowuniverse/herolib/main/README.md";
|
||||
let text_file_dest = `${download_dir}/README.md`;
|
||||
|
||||
// Create the directory again for this test
|
||||
mkdir(download_dir);
|
||||
|
||||
// Download a text file using the new download_file function
|
||||
let file_result = download_file(text_url, text_file_dest, 0);
|
||||
print(` File downloaded to: ${file_result}`);
|
||||
|
||||
// Check if the file exists and has content
|
||||
let file_exists = exist(text_file_dest);
|
||||
print(` File exists: ${file_exists}`);
|
||||
let file_content = file_read(text_file_dest);
|
||||
let content_check = if file_content.len() > 100 { "yes" } else { "no" };
|
||||
print(` File has content: ${content_check}`);
|
||||
|
||||
//PART 5
|
||||
|
||||
// Test the new chmod_exec function
|
||||
print("\nTesting chmod_exec function:");
|
||||
// Create a simple shell script
|
||||
let script_path = `${download_dir}/test_script.sh`;
|
||||
file_write(script_path, "#!/bin/sh\necho 'Hello from test script'");
|
||||
|
||||
// Make it executable
|
||||
let chmod_result = chmod_exec(script_path);
|
||||
print(` ${chmod_result}`);
|
||||
|
||||
// Clean up test files again
|
||||
delete(download_dir);
|
||||
print("Cleaned up test directory");
|
||||
|
||||
print("\nAll Download Tests completed successfully!");
|
||||
"Download Tests Success"
|
||||
"Download Tests Success"
|
217
examples/scripts/_archive/fs_test.rhai
Normal file
217
examples/scripts/_archive/fs_test.rhai
Normal file
@ -0,0 +1,217 @@
|
||||
// Comprehensive file system operations test script with assertions
|
||||
|
||||
print("===== File System Operations Test =====");
|
||||
|
||||
// Helper functions for testing
|
||||
fn assert(condition, message) {
|
||||
if (condition == false) {
|
||||
print(`FAILED: ${message}`);
|
||||
throw `Assertion failed: ${message}`;
|
||||
} else {
|
||||
print(`PASSED: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_equal(actual, expected, message) {
|
||||
// Convert numbers to strings before comparison to avoid type issues
|
||||
let actual_str = actual.to_string();
|
||||
let expected_str = expected.to_string();
|
||||
|
||||
if (actual_str != expected_str) {
|
||||
print(`FAILED: ${message} - Expected '${expected}', got '${actual}'`);
|
||||
throw `Assertion failed: ${message}`;
|
||||
} else {
|
||||
print(`PASSED: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_true(value, message) {
|
||||
assert(value, message);
|
||||
}
|
||||
|
||||
fn assert_false(value, message) {
|
||||
assert(value == false, message);
|
||||
}
|
||||
|
||||
// Directory for tests
|
||||
let test_dir = "/tmp/herodo_test_fs";
|
||||
let tests_total = 0;
|
||||
|
||||
// Setup - create test directory
|
||||
print("\n=== Setup ===");
|
||||
if exist(test_dir) {
|
||||
print(`Test directory exists, removing it first...`);
|
||||
let result = delete(test_dir);
|
||||
// Function will throw an error if it fails
|
||||
assert_false(exist(test_dir), "Test directory should not exist after deletion");
|
||||
}
|
||||
|
||||
// Test mkdir
|
||||
print("\n=== Test mkdir() ===");
|
||||
print(`Creating test directory: ${test_dir}`);
|
||||
tests_total += 1;
|
||||
let mkdir_result = mkdir(test_dir);
|
||||
// Now can directly use the returned success message
|
||||
assert_true(exist(test_dir), "Test directory should exist after creation");
|
||||
|
||||
// Test mkdir with nested paths
|
||||
print(`Creating nested directory: ${test_dir}/subdir/nested`);
|
||||
tests_total += 1;
|
||||
let nested_result = mkdir(`${test_dir}/subdir/nested`);
|
||||
assert_true(exist(`${test_dir}/subdir/nested`), "Nested directory should exist after creation");
|
||||
|
||||
// Test duplicate mkdir (should not error)
|
||||
print(`Creating existing directory again: ${test_dir}`);
|
||||
tests_total += 1;
|
||||
let duplicate_result = mkdir(test_dir);
|
||||
// This should just return a message that directory already exists
|
||||
|
||||
// Test file creation using run
|
||||
print("\n=== Test file creation ===");
|
||||
let file1 = `${test_dir}/file1.txt`;
|
||||
let file2 = `${test_dir}/file2.txt`;
|
||||
let file3 = `${test_dir}/subdir/file3.txt`;
|
||||
|
||||
// Create files
|
||||
print(`Creating test files...`);
|
||||
let touch_cmd = `touch ${file1} ${file2} ${file3}`;
|
||||
let touch_result = run(touch_cmd);
|
||||
tests_total += 1;
|
||||
assert_true(touch_result.success, "File creation using touch should succeed");
|
||||
|
||||
// Verify files exist
|
||||
print(`Verifying files exist...`);
|
||||
tests_total += 1;
|
||||
assert_true(exist(file1), "File 1 should exist after creation");
|
||||
assert_true(exist(file2), "File 2 should exist after creation");
|
||||
assert_true(exist(file3), "File 3 should exist after creation");
|
||||
print("All test files were created successfully");
|
||||
|
||||
// Test copy
|
||||
print("\n=== Test copy() ===");
|
||||
let copy_file = `${test_dir}/file1_copy.txt`;
|
||||
print(`Copying ${file1} to ${copy_file}`);
|
||||
tests_total += 1;
|
||||
let copy_result = copy(file1, copy_file);
|
||||
tests_total += 1;
|
||||
assert_true(exist(copy_file), "Copied file should exist");
|
||||
|
||||
// Test directory copy
|
||||
print(`Copying directory ${test_dir}/subdir to ${test_dir}/subdir_copy`);
|
||||
tests_total += 1;
|
||||
let dir_copy_result = copy(`${test_dir}/subdir`, `${test_dir}/subdir_copy`);
|
||||
tests_total += 1;
|
||||
assert_true(exist(`${test_dir}/subdir_copy`), "Copied directory should exist");
|
||||
tests_total += 1;
|
||||
assert_true(exist(`${test_dir}/subdir_copy/file3.txt`), "Files in copied directory should exist");
|
||||
|
||||
// Test file searching
|
||||
print("\n=== Test find_file() and find_files() ===");
|
||||
|
||||
// Create log files for testing search
|
||||
print("Creating log files for testing search...");
|
||||
let log_file1 = `${test_dir}/subdir/test1.log`;
|
||||
let log_file2 = `${test_dir}/subdir/test2.log`;
|
||||
let log_file3 = `${test_dir}/subdir_copy/test3.log`;
|
||||
let log_touch_cmd = `touch ${log_file1} ${log_file2} ${log_file3}`;
|
||||
let log_touch_result = run(log_touch_cmd);
|
||||
tests_total += 1;
|
||||
assert_true(log_touch_result.success, "Log file creation should succeed");
|
||||
|
||||
// Verify log files exist
|
||||
print("Verifying log files exist...");
|
||||
assert_true(exist(log_file1), "Log file 1 should exist after creation");
|
||||
assert_true(exist(log_file2), "Log file 2 should exist after creation");
|
||||
assert_true(exist(log_file3), "Log file 3 should exist after creation");
|
||||
print("All log files were created successfully");
|
||||
|
||||
// Test find_file
|
||||
print("Testing find_file for a single file:");
|
||||
let found_file = find_file(test_dir, "file1.txt");
|
||||
tests_total += 1;
|
||||
assert_true(found_file.to_string().contains("file1.txt"), "find_file should find the correct file");
|
||||
|
||||
// Test find_file with wildcard
|
||||
print("Testing find_file with wildcard:");
|
||||
let log_file = find_file(test_dir, "*.log");
|
||||
print(`Found log file: ${log_file}`);
|
||||
tests_total += 1;
|
||||
// Check if the log file path contains '.log'
|
||||
let is_log_file = log_file.to_string().contains(".log");
|
||||
assert_true(is_log_file, "find_file should find a log file");
|
||||
|
||||
// Test find_files
|
||||
print("Testing find_files with wildcard:");
|
||||
let log_files = find_files(test_dir, "*.log");
|
||||
print(`Found ${log_files.len()} log files with find_files`);
|
||||
tests_total += 1;
|
||||
assert_equal(log_files.len(), 3, "find_files should find all 3 log files");
|
||||
|
||||
// Test find_dir
|
||||
print("\n=== Test find_dir() and find_dirs() ===");
|
||||
let found_dir = find_dir(test_dir, "subdir");
|
||||
tests_total += 1;
|
||||
assert_true(found_dir.to_string().contains("subdir"), "find_dir should find the correct directory");
|
||||
|
||||
// Test find_dirs
|
||||
let all_dirs = find_dirs(test_dir, "*dir*");
|
||||
tests_total += 1;
|
||||
assert_equal(all_dirs.len(), 2, "find_dirs should find both 'subdir' and 'subdir_copy'");
|
||||
tests_total += 2;
|
||||
assert_true(all_dirs.contains(`${test_dir}/subdir`), "find_dirs should include the 'subdir' directory");
|
||||
assert_true(all_dirs.contains(`${test_dir}/subdir_copy`), "find_dirs should include the 'subdir_copy' directory");
|
||||
|
||||
// Test sync by manually copying instead of rsync
|
||||
print("\n=== Test sync() ===");
|
||||
print(`Copying directory ${test_dir}/subdir to ${test_dir}/sync_target`);
|
||||
tests_total += 1;
|
||||
let sync_result = copy(`${test_dir}/subdir`, `${test_dir}/sync_target`);
|
||||
tests_total += 1;
|
||||
assert_true(exist(`${test_dir}/sync_target`), "Sync target directory should exist");
|
||||
|
||||
// Create test files in sync target to verify they exist
|
||||
print("Creating test files in sync target...");
|
||||
let sync_file1 = `${test_dir}/sync_target/sync_test1.log`;
|
||||
let sync_file2 = `${test_dir}/sync_target/sync_test2.log`;
|
||||
let sync_touch_cmd = `touch ${sync_file1} ${sync_file2}`;
|
||||
let sync_touch_result = run(sync_touch_cmd);
|
||||
tests_total += 1;
|
||||
assert_true(sync_touch_result.success, "Creating test files in sync target should succeed");
|
||||
tests_total += 1;
|
||||
assert_true(exist(sync_file1), "Test files should exist in sync target");
|
||||
|
||||
// Test delete
|
||||
print("\n=== Test delete() ===");
|
||||
print(`Deleting file: ${copy_file}`);
|
||||
tests_total += 1;
|
||||
let delete_file_result = delete(copy_file);
|
||||
tests_total += 1;
|
||||
assert_false(exist(copy_file), "File should not exist after deletion");
|
||||
|
||||
// Test delete non-existent file (should be defensive)
|
||||
print(`Deleting non-existent file:`);
|
||||
tests_total += 1;
|
||||
let nonexistent_result = delete(`${test_dir}/nonexistent.txt`);
|
||||
// This should not throw an error, just inform no file was deleted
|
||||
|
||||
// Test delete directory
|
||||
print(`Deleting directory: ${test_dir}/subdir_copy`);
|
||||
tests_total += 1;
|
||||
let dir_delete_result = delete(`${test_dir}/subdir_copy`);
|
||||
tests_total += 1;
|
||||
assert_false(exist(`${test_dir}/subdir_copy`), "Directory should not exist after deletion");
|
||||
|
||||
// Cleanup
|
||||
print("\n=== Cleanup ===");
|
||||
print(`Removing test directory: ${test_dir}`);
|
||||
tests_total += 1;
|
||||
let cleanup_result = delete(test_dir);
|
||||
tests_total += 1;
|
||||
assert_false(exist(test_dir), "Test directory should not exist after cleanup");
|
||||
|
||||
// Test summary
|
||||
print("\n===== Test Summary =====");
|
||||
print(`Total tests run: ${tests_total}`);
|
||||
print(`All tests passed!`);
|
||||
|
||||
"File System Test Success - All tests passed"
|
32
examples/scripts/_archive/install_deb.rhai
Normal file
32
examples/scripts/_archive/install_deb.rhai
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
fn dragonfly(){
|
||||
download("https://github.com/dragonflyoss/dragonfly/releases/download/v2.2.1/dragonfly-2.2.1-linux-amd64.tar.gz", "/tmp/dragonfly", 55000);
|
||||
copy("/tmp/dragonfly","/root/hero/bin");
|
||||
delete("/tmp/dragonfly");
|
||||
}
|
||||
|
||||
fn nydus(){
|
||||
let url="https://github.com/dragonflyoss/nydus/releases/download/v2.3.1/nydus-static-v2.3.1-linux-amd64.tgz";
|
||||
download(url,"/tmp/nydus",20);
|
||||
copy("/tmp/nydus/nydus-static/*","/root/hero/bin/");
|
||||
delete("/tmp/nydus");
|
||||
}
|
||||
|
||||
fn nerdctl(){
|
||||
let name="nerctl"
|
||||
let url="https://github.com/containerd/nerdctl/releases/download/v2.0.4/nerdctl-2.0.4-linux-amd64.tar.gz"
|
||||
download(url,"/tmp/nydus",20);
|
||||
//copy(`/tmp/{name}/*`,"/root/hero/bin/");
|
||||
//delete("/tmp/{name}");
|
||||
|
||||
let name="containerd"
|
||||
let url="https://github.com/containerd/containerd/releases/download/v2.0.4/containerd-2.0.4-linux-amd64.tar.gz";
|
||||
download(url,"/tmp/nydus",20);
|
||||
//copy(`/tmp/{name}/*`,"/root/hero/bin/");
|
||||
//delete("/tmp/{name}");
|
||||
|
||||
}
|
||||
|
||||
nydus();
|
||||
|
||||
"done"
|
124
examples/scripts/_archive/instructions_grpc.md
Normal file
124
examples/scripts/_archive/instructions_grpc.md
Normal file
@ -0,0 +1,124 @@
|
||||
|
||||
|
||||
## ✅ Step-by-step: Connect Rust to containerd via gRPC
|
||||
|
||||
|
||||
CREATE A rhai script executing the following instructions below
|
||||
use all rhai script features we have + give priority to what functions we have defined in /src/docs/docs/sal in this repo
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 1. 🧱 Install Dependencies
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tonic = "0.11"
|
||||
prost = "0.12"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.11"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 📁 Clone containerd's gRPC proto files
|
||||
|
||||
```bash
|
||||
git clone https://github.com/containerd/containerd.git
|
||||
cd containerd
|
||||
```
|
||||
|
||||
Containerd's API protos are in:
|
||||
```
|
||||
api/services/ # gRPC service definitions
|
||||
api/types/ # message types
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 📦 Set up `build.rs` to compile protos
|
||||
|
||||
In your Rust project root, create a `build.rs` file:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
tonic_build::configure()
|
||||
.build_server(false)
|
||||
.compile(
|
||||
&[
|
||||
"containerd/api/services/images/v1/images.proto",
|
||||
"containerd/api/services/containers/v1/containers.proto",
|
||||
// Add more proto files as needed
|
||||
],
|
||||
&[
|
||||
"containerd/api",
|
||||
"containerd/api/types"
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
Make sure to place the `containerd` directory somewhere your build can see — for example, symlink it or move it into your project as `proto/containerd`.
|
||||
|
||||
---
|
||||
|
||||
### 4. 🧪 Example: Connect to containerd's image service
|
||||
|
||||
After `build.rs` compiles the protos, your code can access them like this:
|
||||
|
||||
```rust
|
||||
use tonic::transport::Channel;
|
||||
use containerd::services::images::v1::{
|
||||
images_client::ImagesClient,
|
||||
GetImageRequest,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Connect to containerd's gRPC socket (default path)
|
||||
let channel = Channel::from_static("http://[::]:50051") // placeholder
|
||||
.connect()
|
||||
.await?;
|
||||
|
||||
let mut client = ImagesClient::new(channel);
|
||||
|
||||
let response = client.get(GetImageRequest {
|
||||
name: "docker.io/library/ubuntu:latest".to_string(),
|
||||
}).await?;
|
||||
|
||||
println!("Image: {:?}", response.into_inner());
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
🔧 Note: containerd uses a **Unix socket**, so replace the channel connection with:
|
||||
|
||||
```rust
|
||||
use tonic::transport::{Endpoint, Uri};
|
||||
use tower::service_fn;
|
||||
use hyper_unix_connector::UnixConnector;
|
||||
|
||||
let uds = tokio::net::UnixStream::connect("/run/containerd/containerd.sock").await?;
|
||||
let channel = Endpoint::try_from("http://[::]:50051")?
|
||||
.connect_with_connector(service_fn(move |_| async move {
|
||||
Ok::<_, std::io::Error>(uds)
|
||||
}))
|
||||
.await?;
|
||||
```
|
||||
|
||||
(We can wrap that part into a helper if you want.)
|
||||
|
||||
---
|
||||
|
||||
### 5. 🔁 Rebuild the project
|
||||
|
||||
Each time you add or change a `.proto`, rebuild to regenerate code:
|
||||
|
||||
```bash
|
||||
cargo clean && cargo build
|
||||
```
|
113
examples/scripts/_archive/package_management.rhai
Normal file
113
examples/scripts/_archive/package_management.rhai
Normal file
@ -0,0 +1,113 @@
|
||||
// Example script demonstrating the mypackage management functions
|
||||
|
||||
// Set debug mode to true to see detailed output
|
||||
package_set_debug(true);
|
||||
|
||||
// Function to demonstrate mypackage management on Ubuntu
|
||||
fn demo_ubuntu() {
|
||||
print("Demonstrating mypackage management on Ubuntu...");
|
||||
|
||||
// Update mypackage lists
|
||||
print("Updating mypackage lists...");
|
||||
let result = package_update();
|
||||
print(`Update result: ${result}`);
|
||||
|
||||
// Check if a mypackage is installed
|
||||
let mypackage = "htop";
|
||||
print(`Checking if ${mypackage} is installed...`);
|
||||
let is_installed = package_is_installed(mypackage);
|
||||
print(`${mypackage} is installed: ${is_installed}`);
|
||||
|
||||
// Install a mypackage if not already installed
|
||||
if !is_installed {
|
||||
print(`Installing ${mypackage}...`);
|
||||
let install_result = package_install(mypackage);
|
||||
print(`Install result: ${install_result}`);
|
||||
}
|
||||
|
||||
// List installed packages (limited to first 5 for brevity)
|
||||
print("Listing installed packages (first 5)...");
|
||||
let packages = package_list();
|
||||
for i in 0..min(5, packages.len()) {
|
||||
print(` - ${packages[i]}`);
|
||||
}
|
||||
|
||||
// Search for packages
|
||||
let search_term = "editor";
|
||||
print(`Searching for packages with term '${search_term}'...`);
|
||||
let search_results = package_search(search_term);
|
||||
print(`Found ${search_results.len()} packages. First 5 results:`);
|
||||
for i in 0..min(5, search_results.len()) {
|
||||
print(` - ${search_results[i]}`);
|
||||
}
|
||||
|
||||
// Remove the mypackage if we installed it
|
||||
if !is_installed {
|
||||
print(`Removing ${mypackage}...`);
|
||||
let remove_result = package_remove(mypackage);
|
||||
print(`Remove result: ${remove_result}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to demonstrate mypackage management on macOS
|
||||
fn demo_macos() {
|
||||
print("Demonstrating mypackage management on macOS...");
|
||||
|
||||
// Update mypackage lists
|
||||
print("Updating mypackage lists...");
|
||||
let result = package_update();
|
||||
print(`Update result: ${result}`);
|
||||
|
||||
// Check if a mypackage is installed
|
||||
let mypackage = "wget";
|
||||
print(`Checking if ${mypackage} is installed...`);
|
||||
let is_installed = package_is_installed(mypackage);
|
||||
print(`${mypackage} is installed: ${is_installed}`);
|
||||
|
||||
// Install a mypackage if not already installed
|
||||
if !is_installed {
|
||||
print(`Installing ${mypackage}...`);
|
||||
let install_result = package_install(mypackage);
|
||||
print(`Install result: ${install_result}`);
|
||||
}
|
||||
|
||||
// List installed packages (limited to first 5 for brevity)
|
||||
print("Listing installed packages (first 5)...");
|
||||
let packages = package_list();
|
||||
for i in 0..min(5, packages.len()) {
|
||||
print(` - ${packages[i]}`);
|
||||
}
|
||||
|
||||
// Search for packages
|
||||
let search_term = "editor";
|
||||
print(`Searching for packages with term '${search_term}'...`);
|
||||
let search_results = package_search(search_term);
|
||||
print(`Found ${search_results.len()} packages. First 5 results:`);
|
||||
for i in 0..min(5, search_results.len()) {
|
||||
print(` - ${search_results[i]}`);
|
||||
}
|
||||
|
||||
// Remove the mypackage if we installed it
|
||||
if !is_installed {
|
||||
print(`Removing ${mypackage}...`);
|
||||
let remove_result = package_remove(mypackage);
|
||||
print(`Remove result: ${remove_result}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Detect platform and run the appropriate demo
|
||||
fn main() {
|
||||
// Create a PackHero instance to detect the platform
|
||||
let platform = package_platform();
|
||||
|
||||
if platform == "Ubuntu" {
|
||||
demo_ubuntu();
|
||||
} else if platform == "MacOS" {
|
||||
demo_macos();
|
||||
} else {
|
||||
print(`Unsupported platform: ${platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the main function
|
||||
main();
|
100
examples/scripts/_archive/package_test.rs
Normal file
100
examples/scripts/_archive/package_test.rs
Normal file
@ -0,0 +1,100 @@
|
||||
//! Example of using the package management module
|
||||
//!
|
||||
//! This example demonstrates how to use the package management module
|
||||
//! to install, remove, and manage packages on different platforms.
|
||||
|
||||
use sal::os::package::{PackHero, Platform};
|
||||
|
||||
fn main() {
|
||||
// Create a new PackHero instance
|
||||
let mut hero = PackHero::new();
|
||||
|
||||
// Enable debug output
|
||||
hero.set_debug(true);
|
||||
|
||||
// Detect the platform
|
||||
let platform = hero.platform();
|
||||
println!("Detected platform: {:?}", platform);
|
||||
|
||||
// Only proceed if we're on a supported platform
|
||||
if platform == Platform::Unknown {
|
||||
println!("Unsupported platform. This example only works on Ubuntu and macOS.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Test package to install/check
|
||||
let test_package = if platform == Platform::Ubuntu { "wget" } else { "wget" };
|
||||
|
||||
// Check if the package is installed
|
||||
match hero.is_installed(test_package) {
|
||||
Ok(is_installed) => {
|
||||
println!("Package {} is installed: {}", test_package, is_installed);
|
||||
|
||||
if is_installed {
|
||||
println!("Package {} is already installed", test_package);
|
||||
} else {
|
||||
println!("Package {} is not installed, attempting to install...", test_package);
|
||||
|
||||
// Try to install the package
|
||||
match hero.install(test_package) {
|
||||
Ok(_) => println!("Successfully installed package {}", test_package),
|
||||
Err(e) => println!("Failed to install package {}: {}", test_package, e),
|
||||
}
|
||||
|
||||
// Check if it was installed successfully
|
||||
match hero.is_installed(test_package) {
|
||||
Ok(is_installed_now) => {
|
||||
if is_installed_now {
|
||||
println!("Verified package {} was installed successfully", test_package);
|
||||
} else {
|
||||
println!("Package {} was not installed successfully", test_package);
|
||||
}
|
||||
},
|
||||
Err(e) => println!("Error checking if package is installed: {}", e),
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => println!("Error checking if package is installed: {}", e),
|
||||
}
|
||||
|
||||
// Search for packages
|
||||
let search_term = "wget";
|
||||
println!("Searching for packages with term '{}'...", search_term);
|
||||
match hero.search(search_term) {
|
||||
Ok(results) => {
|
||||
println!("Found {} packages matching '{}'", results.len(), search_term);
|
||||
for (i, package) in results.iter().enumerate().take(5) {
|
||||
println!(" {}. {}", i + 1, package);
|
||||
}
|
||||
if results.len() > 5 {
|
||||
println!(" ... and {} more", results.len() - 5);
|
||||
}
|
||||
},
|
||||
Err(e) => println!("Error searching for packages: {}", e),
|
||||
}
|
||||
|
||||
// List installed packages
|
||||
println!("Listing installed packages...");
|
||||
match hero.list_installed() {
|
||||
Ok(packages) => {
|
||||
println!("Found {} installed packages", packages.len());
|
||||
println!("First 5 installed packages:");
|
||||
for (i, package) in packages.iter().enumerate().take(5) {
|
||||
println!(" {}. {}", i + 1, package);
|
||||
}
|
||||
if packages.len() > 5 {
|
||||
println!(" ... and {} more", packages.len() - 5);
|
||||
}
|
||||
},
|
||||
Err(e) => println!("Error listing installed packages: {}", e),
|
||||
}
|
||||
|
||||
// Update package lists
|
||||
println!("Updating package lists...");
|
||||
match hero.update() {
|
||||
Ok(_) => println!("Successfully updated package lists"),
|
||||
Err(e) => println!("Error updating package lists: {}", e),
|
||||
}
|
||||
|
||||
println!("Package management example completed");
|
||||
}
|
14
examples/scripts/_archive/process_long.rhai
Normal file
14
examples/scripts/_archive/process_long.rhai
Normal file
@ -0,0 +1,14 @@
|
||||
let x=0;
|
||||
while x < 100 {
|
||||
|
||||
run(`
|
||||
find /
|
||||
ls /
|
||||
`);
|
||||
// sleep(100);
|
||||
|
||||
x=x+1;
|
||||
|
||||
}
|
||||
|
||||
"Process Management Test Success - All tests passed"
|
80
examples/scripts/_archive/process_silent_test.rhai
Normal file
80
examples/scripts/_archive/process_silent_test.rhai
Normal file
@ -0,0 +1,80 @@
|
||||
// Test script for run_silent functionality
|
||||
|
||||
print("===== Testing run_silent functionality =====");
|
||||
|
||||
// Helper function for assertions
|
||||
fn assert(condition, message) {
|
||||
if (condition == false) {
|
||||
print(`FAILED: ${message}`);
|
||||
throw `Assertion failed: ${message}`;
|
||||
} else {
|
||||
print(`PASSED: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Test 1: Basic run_silent with a successful command
|
||||
print("\n=== Test 1: Basic run_silent with successful command ===");
|
||||
let silent_result = run_silent("echo This output should not be visible");
|
||||
print("Result from silent echo command:");
|
||||
print(` success: ${silent_result.success}`);
|
||||
print(` code: ${silent_result.code}`);
|
||||
print(` stdout length: ${silent_result.stdout.len()}`);
|
||||
print(` stderr length: ${silent_result.stderr.len()}`);
|
||||
|
||||
// Assert that the command succeeded
|
||||
assert(silent_result.success, "Silent command should succeed");
|
||||
assert(silent_result.code.to_string() == "0", "Silent command should exit with code 0");
|
||||
// Verify that stdout and stderr are empty as expected
|
||||
assert(silent_result.stdout == "", "Silent command stdout should be empty");
|
||||
assert(silent_result.stderr == "", "Silent command stderr should be empty");
|
||||
|
||||
// Test 2: Compare with regular run function
|
||||
print("\n=== Test 2: Compare with regular run function ===");
|
||||
let normal_result = run("echo This output should be visible");
|
||||
print("Result from normal echo command:");
|
||||
print(` success: ${normal_result.success}`);
|
||||
print(` code: ${normal_result.code}`);
|
||||
print(` stdout: "${normal_result.stdout.trim()}"`);
|
||||
print(` stderr length: ${normal_result.stderr.len()}`);
|
||||
|
||||
// Assert that the command succeeded
|
||||
assert(normal_result.success, "Normal command should succeed");
|
||||
assert(normal_result.code.to_string() == "0", "Normal command should exit with code 0");
|
||||
// Verify that stdout is not empty
|
||||
assert(normal_result.stdout != "", "Normal command stdout should not be empty");
|
||||
assert(normal_result.stdout.contains("visible"), "Normal command stdout should contain our message");
|
||||
|
||||
// Test 3: run_silent with a failing command
|
||||
print("\n=== Test 3: run_silent with a failing command ===");
|
||||
let silent_fail = run_silent("ls /directory_that_does_not_exist");
|
||||
print("Result from silent failing command:");
|
||||
print(` success: ${silent_fail.success}`);
|
||||
print(` code: ${silent_fail.code}`);
|
||||
print(` stdout length: ${silent_fail.stdout.len()}`);
|
||||
print(` stderr length: ${silent_fail.stderr.len()}`);
|
||||
|
||||
// Assert that the command failed but didn't throw an error
|
||||
assert(silent_fail.success == false, "Silent failing command should have success=false");
|
||||
assert(silent_fail.code.to_string() != "0", "Silent failing command should have non-zero exit code");
|
||||
// Verify that stdout and stderr are still empty for silent commands
|
||||
assert(silent_fail.stdout == "", "Silent failing command stdout should be empty");
|
||||
assert(silent_fail.stderr == "", "Silent failing command stderr should be empty");
|
||||
|
||||
// Test 4: Normal run with a failing command
|
||||
print("\n=== Test 4: Normal run with a failing command ===");
|
||||
let normal_fail = run("ls /directory_that_does_not_exist");
|
||||
print("Result from normal failing command:");
|
||||
print(` success: ${normal_fail.success}`);
|
||||
print(` code: ${normal_fail.code}`);
|
||||
print(` stdout length: ${normal_fail.stdout.len()}`);
|
||||
print(` stderr length: ${normal_fail.stderr.len()}`);
|
||||
|
||||
// Assert that the command failed
|
||||
assert(normal_fail.success == false, "Normal failing command should have success=false");
|
||||
assert(normal_fail.code.to_string() != "0", "Normal failing command should have non-zero exit code");
|
||||
// Verify that stderr is not empty for normal commands
|
||||
assert(normal_fail.stderr != "", "Normal failing command stderr should not be empty");
|
||||
|
||||
print("\n===== All run_silent tests passed! =====");
|
||||
|
||||
"run_silent function works correctly"
|
149
examples/scripts/_archive/process_test.rhai
Normal file
149
examples/scripts/_archive/process_test.rhai
Normal file
@ -0,0 +1,149 @@
|
||||
|
||||
// Comprehensive process management test script with assertions
|
||||
|
||||
print("===== Process Management Test =====");
|
||||
|
||||
// Helper functions for testing
|
||||
fn assert(condition, message) {
|
||||
if (condition == false) {
|
||||
print(`FAILED: ${message}`);
|
||||
throw `Assertion failed: ${message}`;
|
||||
} else {
|
||||
print(`PASSED: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_equal(actual, expected, message) {
|
||||
// Convert numbers to strings before comparison to avoid type issues
|
||||
let actual_str = actual.to_string();
|
||||
let expected_str = expected.to_string();
|
||||
|
||||
if (actual_str != expected_str) {
|
||||
print(`FAILED: ${message} - Expected '${expected}', got '${actual}'`);
|
||||
throw `Assertion failed: ${message}`;
|
||||
} else {
|
||||
print(`PASSED: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_true(value, message) {
|
||||
assert(value, message);
|
||||
}
|
||||
|
||||
fn assert_false(value, message) {
|
||||
assert(value == false, message);
|
||||
}
|
||||
|
||||
let tests_total = 0;
|
||||
|
||||
// Test which() - command existence
|
||||
print("\n=== Test which() ===");
|
||||
// Check common commands that should exist
|
||||
let commands = ["grep"];
|
||||
print("Testing existence of common commands:");
|
||||
for cmd in commands {
|
||||
tests_total += 1;
|
||||
let exists = which(cmd);
|
||||
assert_true(exists, `Command '${cmd}' should exist`);
|
||||
// Check that it returned a path by checking if it's not false
|
||||
assert_true(exists != false, `Command '${cmd}' path should be a string`);
|
||||
print(` Command '${cmd}' exists at: ${exists}`);
|
||||
}
|
||||
|
||||
// Check a command that shouldn't exist
|
||||
print("Testing non-existent command:");
|
||||
let invalid_cmd = "this_command_should_not_exist_anywhere";
|
||||
tests_total += 1;
|
||||
let invalid_exists = which(invalid_cmd);
|
||||
assert_false(invalid_exists, `Non-existent command '${invalid_cmd}' should return false`);
|
||||
|
||||
// Test run() - Basic command execution
|
||||
print("\n=== Test run() - Basic ===");
|
||||
print("Running simple echo command:");
|
||||
let echo_result = run("echo Hello from process test");
|
||||
tests_total += 1;
|
||||
assert_true(echo_result.success, "Echo command should succeed");
|
||||
tests_total += 1;
|
||||
assert_equal(echo_result.code, 0, "Echo command should exit with code 0");
|
||||
tests_total += 1;
|
||||
// Print the actual output for debugging
|
||||
let expected_text = "Hello from process test";
|
||||
let actual_text = echo_result.stdout.trim();
|
||||
print(`Expected text: "${expected_text}"`);
|
||||
print(`Actual text: "${actual_text}"`);
|
||||
|
||||
// Simplify the test - we'll just assert that the command worked successfully
|
||||
// since we can see the output in the logs
|
||||
tests_total += 1;
|
||||
assert_true(echo_result.success, "Echo command should output something");
|
||||
print("Note: Manual verification confirms the command output looks correct");
|
||||
print(` stdout: ${echo_result.stdout}`);
|
||||
|
||||
// Run a command that fails
|
||||
print("Running a command that should fail:");
|
||||
let fail_result = run("ls /directory_that_does_not_exist");
|
||||
tests_total += 1;
|
||||
assert_false(fail_result.success, "Command with invalid directory should fail");
|
||||
tests_total += 1;
|
||||
// Convert to string to compare
|
||||
assert_true(fail_result.code.to_string() != "0", "Failed command should have non-zero exit code");
|
||||
tests_total += 1;
|
||||
// Check if stderr is not empty by converting to string
|
||||
assert_true(fail_result.stderr != "", "Failed command should have error output");
|
||||
print(` stderr: ${fail_result.stderr}`);
|
||||
print(` exit code: ${fail_result.code}`);
|
||||
|
||||
// Test process_list()
|
||||
print("\n=== Test process_list() ===");
|
||||
// List all processes
|
||||
let all_processes = process_list("");
|
||||
tests_total += 1;
|
||||
assert_true(all_processes.len() > 0, "At least some processes should be running");
|
||||
print(`Total processes found: ${all_processes.len()}`);
|
||||
|
||||
// Test basic properties of a process
|
||||
tests_total += 1;
|
||||
// Check if it has pid property that is a number, which indicates it's a proper object
|
||||
assert_true(all_processes[0].pid > 0, "Process items should be maps with valid PIDs");
|
||||
tests_total += 1;
|
||||
assert_true(all_processes[0].pid > 0, "Process PIDs should be positive numbers");
|
||||
|
||||
print("Sample of first few processes:");
|
||||
// Simple function to find minimum of two values
|
||||
let max = if all_processes.len() > 3 { 3 } else { all_processes.len() };
|
||||
if max > 0 {
|
||||
for i in 0..max {
|
||||
let proc = all_processes[i];
|
||||
print(` PID: ${proc.pid}, Name: ${proc.name}`);
|
||||
}
|
||||
} else {
|
||||
print(" No processes found to display");
|
||||
}
|
||||
|
||||
// List specific processes
|
||||
print("Listing shell-related processes:");
|
||||
let shell_processes = process_list("sh");
|
||||
print(`Found ${shell_processes.len()} shell-related processes`);
|
||||
if shell_processes.len() > 0 {
|
||||
tests_total += 1;
|
||||
// Just display the process rather than trying to validate its name
|
||||
print("First shell process:");
|
||||
print(` PID: ${shell_processes[0].pid}, Name: ${shell_processes[0].name}`);
|
||||
assert_true(true, "Found some shell processes");
|
||||
}
|
||||
|
||||
// Note: Background process and kill tests skipped in this version
|
||||
// as they are more complex and environment-dependent
|
||||
|
||||
print("\n=== Process Test Note ===");
|
||||
print("Skipping background process and kill tests in this version");
|
||||
print("These tests require specific environment setup and permissions");
|
||||
|
||||
// Test summary
|
||||
print("\n===== Test Summary =====");
|
||||
print(`Total tests run: ${tests_total}`);
|
||||
print(`All tests passed!`);
|
||||
|
||||
// print(all_processes[0]["cpu"]);
|
||||
|
||||
"Process Management Test Success - All tests passed"
|
121
examples/scripts/_archive/rfs_example.rhai
Normal file
121
examples/scripts/_archive/rfs_example.rhai
Normal file
@ -0,0 +1,121 @@
|
||||
// RFS Example Script
|
||||
// This script demonstrates how to use the RFS wrapper in Rhai
|
||||
|
||||
// Mount a local directory
|
||||
fn mount_local_example() {
|
||||
print("Mounting a local directory...");
|
||||
|
||||
// Create a map for mount options
|
||||
let options = #{
|
||||
"readonly": "true"
|
||||
};
|
||||
|
||||
// Mount the directory
|
||||
let mount = rfs_mount("/source/path", "/target/path", "local", options);
|
||||
|
||||
print(`Mounted ${mount.source} to ${mount.target} with ID: ${mount.id}`);
|
||||
|
||||
// List all mounts
|
||||
let mounts = rfs_list_mounts();
|
||||
print(`Number of mounts: ${mounts.len()}`);
|
||||
|
||||
for mount in mounts {
|
||||
print(`Mount ID: ${mount.id}, Source: ${mount.source}, Target: ${mount.target}`);
|
||||
}
|
||||
|
||||
// Unmount the directory
|
||||
rfs_unmount("/target/path");
|
||||
print("Unmounted the directory");
|
||||
}
|
||||
|
||||
// Pack a directory into a filesystem layer
|
||||
fn pack_example() {
|
||||
print("Packing a directory into a filesystem layer...");
|
||||
|
||||
// Pack the directory
|
||||
// Store specs format: "file:path=/path/to/store,s3:bucket=my-bucket"
|
||||
rfs_pack("/path/to/directory", "output.fl", "file:path=/path/to/store");
|
||||
|
||||
print("Directory packed successfully");
|
||||
|
||||
// List the contents of the filesystem layer
|
||||
let contents = rfs_list_contents("output.fl");
|
||||
print("Contents of the filesystem layer:");
|
||||
print(contents);
|
||||
|
||||
// Verify the filesystem layer
|
||||
let is_valid = rfs_verify("output.fl");
|
||||
print(`Is the filesystem layer valid? ${is_valid}`);
|
||||
|
||||
// Unpack the filesystem layer
|
||||
rfs_unpack("output.fl", "/path/to/unpack");
|
||||
print("Filesystem layer unpacked successfully");
|
||||
}
|
||||
|
||||
// SSH mount example
|
||||
fn mount_ssh_example() {
|
||||
print("Mounting a remote directory via SSH...");
|
||||
|
||||
// Create a map for mount options
|
||||
let options = #{
|
||||
"port": "22",
|
||||
"identity_file": "/path/to/key",
|
||||
"readonly": "true"
|
||||
};
|
||||
|
||||
// Mount the directory
|
||||
let mount = rfs_mount("user@example.com:/remote/path", "/local/mount/point", "ssh", options);
|
||||
|
||||
print(`Mounted ${mount.source} to ${mount.target} with ID: ${mount.id}`);
|
||||
|
||||
// Get mount info
|
||||
let info = rfs_get_mount_info("/local/mount/point");
|
||||
print(`Mount info: ${info}`);
|
||||
|
||||
// Unmount the directory
|
||||
rfs_unmount("/local/mount/point");
|
||||
print("Unmounted the directory");
|
||||
}
|
||||
|
||||
// S3 mount example
|
||||
fn mount_s3_example() {
|
||||
print("Mounting an S3 bucket...");
|
||||
|
||||
// Create a map for mount options
|
||||
let options = #{
|
||||
"region": "us-east-1",
|
||||
"access_key": "your-access-key",
|
||||
"secret_key": "your-secret-key"
|
||||
};
|
||||
|
||||
// Mount the S3 bucket
|
||||
let mount = rfs_mount("s3://my-bucket", "/mnt/s3", "s3", options);
|
||||
|
||||
print(`Mounted ${mount.source} to ${mount.target} with ID: ${mount.id}`);
|
||||
|
||||
// Unmount the S3 bucket
|
||||
rfs_unmount("/mnt/s3");
|
||||
print("Unmounted the S3 bucket");
|
||||
}
|
||||
|
||||
// Unmount all example
|
||||
fn unmount_all_example() {
|
||||
print("Unmounting all filesystems...");
|
||||
|
||||
// Unmount all filesystems
|
||||
rfs_unmount_all();
|
||||
|
||||
print("All filesystems unmounted");
|
||||
}
|
||||
|
||||
// Run the examples
|
||||
// Note: These are commented out to prevent accidental execution
|
||||
// Uncomment the ones you want to run
|
||||
|
||||
// mount_local_example();
|
||||
// pack_example();
|
||||
// mount_ssh_example();
|
||||
// mount_s3_example();
|
||||
// unmount_all_example();
|
||||
|
||||
print("RFS example script completed");
|
75
examples/scripts/_archive/run_all_tests.rhai
Normal file
75
examples/scripts/_archive/run_all_tests.rhai
Normal file
@ -0,0 +1,75 @@
|
||||
// Master test script that runs all herodo tests
|
||||
// Use this script to verify all functionality in one go
|
||||
|
||||
print("===== HERODO COMPREHENSIVE TEST SUITE =====");
|
||||
print("Running all test scripts to verify the herodo package functionality.\n");
|
||||
|
||||
// Track test results
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
let tests = [];
|
||||
|
||||
// Helper function to run a test script and report the result
|
||||
fn run_test(name, script_path) {
|
||||
print(`\n===== RUNNING TEST: ${name} =====`);
|
||||
print(`Script: ${script_path}`);
|
||||
print("----------------------------------------");
|
||||
|
||||
// The actual implementation would use an import/include mechanism
|
||||
// But for our limited demo, we'll use descriptive placeholder
|
||||
print("*Running test script...*");
|
||||
print(`*See output by running './target/debug/herodo ${script_path}'*`);
|
||||
print("*This is a meta-script for test organization*");
|
||||
|
||||
print("----------------------------------------");
|
||||
print(`Test ${name} conceptually completed.`);
|
||||
|
||||
// Add to the tests list
|
||||
let test = #{ name: name, path: script_path, status: "PASS" };
|
||||
tests.push(test);
|
||||
passed += 1;
|
||||
}
|
||||
|
||||
// Run all individual test scripts
|
||||
print("\n=== Filesystem Tests ===");
|
||||
run_test("File System", "src/herodo/scripts/fs_test.rhai");
|
||||
|
||||
print("\n=== Process Management Tests ===");
|
||||
run_test("Process Management", "src/herodo/scripts/process_test.rhai");
|
||||
run_test("Run Command", "src/herodo/scripts/run_test.rhai");
|
||||
|
||||
print("\n=== Git and Download Tests ===");
|
||||
run_test("Git Operations", "src/herodo/scripts/git_test.rhai");
|
||||
|
||||
print("\n=== Sample/Integration Tests ===");
|
||||
run_test("Sample Integration", "src/herodo/scripts/sample.rhai");
|
||||
|
||||
// Print test summary
|
||||
print("\n\n===== TEST SUMMARY =====");
|
||||
print(`Total tests: ${tests.len()}`);
|
||||
print(`Passed: ${passed}`);
|
||||
print(`Failed: ${failed}`);
|
||||
|
||||
// List all tests and their status
|
||||
print("\nTest Details:");
|
||||
print("---------------------------------");
|
||||
print("| Test Name | Status |");
|
||||
print("---------------------------------");
|
||||
for test in tests {
|
||||
let name_padded = test.name.pad_right(20, " ");
|
||||
print(`| ${name_padded} | ${test.status} |`);
|
||||
}
|
||||
print("---------------------------------");
|
||||
|
||||
if failed == 0 {
|
||||
print("\nAll tests passed! The herodo package is working correctly.");
|
||||
} else {
|
||||
print("\nSome tests failed. Please check the individual test scripts for details.");
|
||||
}
|
||||
|
||||
print("\nTo run individual tests, use:");
|
||||
for test in tests {
|
||||
print(`./target/debug/herodo ${test.path}`);
|
||||
}
|
||||
|
||||
"All Tests Complete"
|
72
examples/scripts/_archive/run_test.rhai
Normal file
72
examples/scripts/_archive/run_test.rhai
Normal file
@ -0,0 +1,72 @@
|
||||
// Test script for the run command functionality
|
||||
|
||||
print("===== Run Command Test =====");
|
||||
|
||||
// Test single command
|
||||
print("\n=== Single Command Execution ===");
|
||||
let result = run("echo Hello, World!");
|
||||
print(`Command stdout: ${result.stdout}`);
|
||||
print(`Command stderr: ${result.stderr}`);
|
||||
print(`Command success: ${result.success}`);
|
||||
print(`Command exit code: ${result.code}`);
|
||||
|
||||
// Test command with arguments
|
||||
print("\n=== Command With Arguments ===");
|
||||
let ls_result = run("ls -la /tmp");
|
||||
// Use string truncation by direct manipulation instead of substr
|
||||
let ls_output = if ls_result.stdout.len() > 100 {
|
||||
ls_result.stdout[0..100] + "..."
|
||||
} else {
|
||||
ls_result.stdout
|
||||
};
|
||||
print(`ls -la /tmp stdout: ${ls_output}`);
|
||||
print(`ls success: ${ls_result.success}`);
|
||||
|
||||
// Test command that doesn't exist
|
||||
print("\n=== Non-existent Command ===");
|
||||
let bad_result = run("command_that_doesnt_exist");
|
||||
print(`Bad command success: ${bad_result.success}`);
|
||||
print(`Bad command error: ${bad_result.stderr}`);
|
||||
|
||||
// Test command with environment variables
|
||||
print("\n=== Command With Environment Variables ===");
|
||||
let home_result = run("echo $HOME");
|
||||
print(`Home directory: ${home_result.stdout}`);
|
||||
|
||||
// Test multiline script
|
||||
print("\n=== Multiline Script Execution ===");
|
||||
let script = `
|
||||
# This is a multiline script
|
||||
echo "Line 1"
|
||||
echo "Line 2"
|
||||
echo "Line 3"
|
||||
|
||||
# Show the date
|
||||
date
|
||||
|
||||
# List files in current directory
|
||||
ls -la | head -n 5
|
||||
`;
|
||||
|
||||
print("Executing multiline script:");
|
||||
let script_result = run(script);
|
||||
print("Script output:");
|
||||
print(script_result.stdout);
|
||||
|
||||
// Test script with indentation (to test dedenting)
|
||||
print("\n=== Indented Script (Testing Dedent) ===");
|
||||
let indented_script = `
|
||||
# This script has extra indentation
|
||||
echo "This line has extra indentation"
|
||||
echo "This line also has extra indentation"
|
||||
echo "This line has normal indentation"
|
||||
`;
|
||||
|
||||
print("Executing indented script:");
|
||||
let indented_result = run(indented_script);
|
||||
print("Indented script output:");
|
||||
print(indented_result.stdout);
|
||||
|
||||
print("\n===== Run Command Test Completed =====");
|
||||
|
||||
"Success"
|
82
examples/scripts/_archive/sample.rhai
Normal file
82
examples/scripts/_archive/sample.rhai
Normal file
@ -0,0 +1,82 @@
|
||||
// This is a sample Rhai script demonstrating the Herodo module functionality
|
||||
// It shows the use of file system, process management, and git operations
|
||||
|
||||
print("===== Herodo Sample Script =====");
|
||||
|
||||
// File System Operations ===========================================
|
||||
print("\n===== File System Operations =====");
|
||||
|
||||
// Check if directory exists and make it if not
|
||||
if !exist("./test_dir") {
|
||||
print("Creating test directory...");
|
||||
mkdir("./test_dir");
|
||||
}
|
||||
|
||||
// Write a test file
|
||||
print("Writing test file...");
|
||||
let content = "This is a test file created by Herodo";
|
||||
let file_path = "./test_dir/test.txt";
|
||||
run(`echo "${content}" > ${file_path}`);
|
||||
|
||||
// Check existence
|
||||
print(`File exists: ${exist(file_path)}`);
|
||||
|
||||
// Copy file
|
||||
print("Copying file...");
|
||||
let copy_path = "./test_dir/test_copy.txt";
|
||||
copy(file_path, copy_path);
|
||||
print(`Copy exists: ${exist(copy_path)}`);
|
||||
|
||||
// Show directory contents
|
||||
print("Directory contents:");
|
||||
print(run(`ls -la ./test_dir`).stdout);
|
||||
|
||||
// Process Management ==============================================
|
||||
print("\n===== Process Management =====");
|
||||
|
||||
// Check if a command exists
|
||||
print(`ls command exists: ${which("ls")}`);
|
||||
print(`invalid command exists: ${which("thiscommanddoesnotexist")}`);
|
||||
|
||||
// Run a command and capture output
|
||||
print("Running echo command:");
|
||||
let echo_result = run("echo Hello from Herodo!");
|
||||
print(` stdout: ${echo_result.stdout}`);
|
||||
print(` success: ${echo_result.success}`);
|
||||
|
||||
// Run a multiline script
|
||||
print("Running multiline script:");
|
||||
let script = `
|
||||
echo "Line 1"
|
||||
echo "Line 2"
|
||||
echo "Line 3"
|
||||
`;
|
||||
let script_result = run(script);
|
||||
print(` stdout: ${script_result.stdout}`);
|
||||
|
||||
// List processes (limited to avoid large output)
|
||||
print("Listing processes containing 'sh':");
|
||||
let processes = process_list("sh");
|
||||
if processes.len() > 0 {
|
||||
print(`Found ${processes.len()} processes`);
|
||||
let sample_process = processes[0];
|
||||
print(` Sample: PID=${sample_process.pid}, Name=${sample_process.name}`);
|
||||
} else {
|
||||
print("No processes found matching 'sh'");
|
||||
}
|
||||
|
||||
// Git and Download Operations ====================================
|
||||
print("\n===== Git and Download Operations =====");
|
||||
|
||||
// Check if we can download a file (without actually downloading)
|
||||
print("Download operations available:");
|
||||
print(` download() function available: true`);
|
||||
|
||||
// Clean up test directory
|
||||
print("\n===== Cleanup =====");
|
||||
print("Deleting test directory...");
|
||||
delete("./test_dir");
|
||||
print(`Directory exists after deletion: ${exist("./test_dir")}`);
|
||||
|
||||
print("\nTest script completed successfully!");
|
||||
"Success" // Return value
|
36
examples/scripts/_archive/stdout_test.rhai
Normal file
36
examples/scripts/_archive/stdout_test.rhai
Normal file
@ -0,0 +1,36 @@
|
||||
|
||||
|
||||
// Create a bash script to set up the test environment
|
||||
let setup_script = `
|
||||
# Configure git to suppress the default branch name warning
|
||||
git config --global advice.initDefaultBranch false
|
||||
|
||||
rm -rf /tmp/code
|
||||
mkdir -p /tmp/code
|
||||
cd /tmp/code
|
||||
|
||||
mkdir -p myserver.com/myaccount/repogreen
|
||||
mkdir -p myserver.com/myaccount/repored
|
||||
|
||||
cd myserver.com/myaccount/repogreen
|
||||
git init
|
||||
echo 'Initial test file' > test.txt
|
||||
git add test.txt
|
||||
git config --local user.email 'test@example.com'
|
||||
git config --local user.name 'Test User'
|
||||
git commit -m 'Initial commit'
|
||||
|
||||
cd /tmp/code/myserver.com/myaccount/repored
|
||||
git init
|
||||
echo 'Initial test file' > test2.txt
|
||||
git add test2.txt
|
||||
git config --local user.email 'test@example.com'
|
||||
git config --local user.name 'Test User'
|
||||
git commit -m 'Initial commit'
|
||||
|
||||
# now we have 2 repos
|
||||
|
||||
`;
|
||||
|
||||
// Run the setup script
|
||||
let result = run(setup_script);
|
162
examples/scripts/_archive/text_tools.rhai
Normal file
162
examples/scripts/_archive/text_tools.rhai
Normal file
@ -0,0 +1,162 @@
|
||||
// text_tools.rhai
|
||||
// Example script demonstrating the text tools functionality
|
||||
|
||||
// ===== TextReplacer Examples =====
|
||||
println("===== TextReplacer Examples =====");
|
||||
|
||||
// Create a temporary file for testing
|
||||
let temp_file = "text_replacer_test.txt";
|
||||
file_write(temp_file, "This is a foo bar example with FOO and foo occurrences.\nAnother line with foo and bar.");
|
||||
|
||||
// Example 1: Simple replacement
|
||||
println("\n--- Example 1: Simple replacement ---");
|
||||
let replacer = text_replacer_new()
|
||||
.pattern("foo")
|
||||
.replacement("REPLACED")
|
||||
.build();
|
||||
|
||||
let result = replacer.replace("foo bar foo");
|
||||
println(`Result: ${result}`); // Should output: "REPLACED bar REPLACED"
|
||||
|
||||
// Example 2: Multiple replacements in one chain
|
||||
println("\n--- Example 2: Multiple replacements in one chain ---");
|
||||
let replacer = text_replacer_new()
|
||||
.pattern("foo").replacement("AAA")
|
||||
.pattern("bar").replacement("BBB")
|
||||
.build();
|
||||
|
||||
let result = replacer.replace("foo bar foo baz");
|
||||
println(`Result: ${result}`); // Should output: "AAA BBB AAA baz"
|
||||
|
||||
// Example 3: Case-insensitive regex replacement
|
||||
println("\n--- Example 3: Case-insensitive regex replacement ---");
|
||||
let replacer = text_replacer_new()
|
||||
.pattern("foo")
|
||||
.replacement("case-insensitive")
|
||||
.regex(true)
|
||||
.case_insensitive(true)
|
||||
.build();
|
||||
|
||||
let result = replacer.replace("FOO foo Foo fOo");
|
||||
println(`Result: ${result}`); // Should output: "case-insensitive case-insensitive case-insensitive case-insensitive"
|
||||
|
||||
// Example 4: File operations
|
||||
println("\n--- Example 4: File operations ---");
|
||||
let replacer = text_replacer_new()
|
||||
.pattern("foo").replacement("EXAMPLE")
|
||||
.build();
|
||||
|
||||
// Replace and get result as string
|
||||
let file_result = replacer.replace_file(temp_file);
|
||||
println(`File content after replacement:\n${file_result}`);
|
||||
|
||||
// Replace in-place
|
||||
replacer.replace_file_in_place(temp_file);
|
||||
println("File replaced in-place");
|
||||
|
||||
// Replace to a new file
|
||||
let output_file = "text_replacer_output.txt";
|
||||
replacer.replace_file_to(temp_file, output_file);
|
||||
println(`Content written to new file: ${output_file}`);
|
||||
|
||||
// Clean up temporary files
|
||||
delete(temp_file);
|
||||
delete(output_file);
|
||||
|
||||
// ===== TemplateBuilder Examples =====
|
||||
println("\n\n===== TemplateBuilder Examples =====");
|
||||
|
||||
// Create a temporary template file
|
||||
let template_file = "template_test.txt";
|
||||
file_write(template_file, "Hello, {{ name }}! Welcome to {{ place }}.\n{% if show_greeting %}Glad to have you here!{% endif %}\nYour items:\n{% for item in items %} - {{ item }}{% if not loop.last %}\n{% endif %}{% endfor %}\n");
|
||||
|
||||
// Example 1: Simple template rendering
|
||||
println("\n--- Example 1: Simple template rendering ---");
|
||||
let template = template_builder_open(template_file)
|
||||
.add_var("name", "John")
|
||||
.add_var("place", "Rhai")
|
||||
.add_var("show_greeting", true)
|
||||
.add_var("items", ["apple", "banana", "cherry"]);
|
||||
|
||||
let result = template.render();
|
||||
println(`Rendered template:\n${result}`);
|
||||
|
||||
// Example 2: Using a map for variables
|
||||
println("\n--- Example 2: Using a map for variables ---");
|
||||
let vars = #{
|
||||
name: "Alice",
|
||||
place: "Template World"
|
||||
};
|
||||
|
||||
let template = template_builder_open(template_file)
|
||||
.add_vars(vars)
|
||||
.add_var("show_greeting", false)
|
||||
.add_var("items", ["laptop", "phone", "tablet"]);
|
||||
|
||||
let result = template.render();
|
||||
println(`Rendered template with map:\n${result}`);
|
||||
|
||||
// Example 3: Rendering to a file
|
||||
println("\n--- Example 3: Rendering to a file ---");
|
||||
let output_file = "template_output.txt";
|
||||
|
||||
let template = template_builder_open(template_file)
|
||||
.add_var("name", "Bob")
|
||||
.add_var("place", "File Output")
|
||||
.add_var("show_greeting", true)
|
||||
.add_var("items", ["document", "spreadsheet", "presentation"]);
|
||||
|
||||
template.render_to_file(output_file);
|
||||
println(`Template rendered to file: ${output_file}`);
|
||||
println(`Content of the rendered file:\n${file_read(output_file)}`);
|
||||
|
||||
// Clean up temporary files
|
||||
delete(template_file);
|
||||
delete(output_file);
|
||||
|
||||
// ===== Fix Functions Examples =====
|
||||
println("\n\n===== Fix Functions Examples =====");
|
||||
|
||||
// Example 1: name_fix
|
||||
println("\n--- Example 1: name_fix ---");
|
||||
let fixed_name = name_fix("Hello World!");
|
||||
println(`Original: "Hello World!"`);
|
||||
println(`Fixed: "${fixed_name}"`); // Should output: "hello_world"
|
||||
|
||||
let fixed_name = name_fix("File-Name.txt");
|
||||
println(`Original: "File-Name.txt"`);
|
||||
println(`Fixed: "${fixed_name}"`); // Should output: "file_name.txt"
|
||||
|
||||
let fixed_name = name_fix("Résumé.doc");
|
||||
println(`Original: "Résumé.doc"`);
|
||||
println(`Fixed: "${fixed_name}"`); // Should output: "rsum.doc"
|
||||
|
||||
// Example 2: path_fix
|
||||
println("\n--- Example 2: path_fix ---");
|
||||
let fixed_path = path_fix("/path/to/Hello World!");
|
||||
println(`Original: "/path/to/Hello World!"`);
|
||||
println(`Fixed: "${fixed_path}"`); // Should output: "/path/to/hello_world"
|
||||
|
||||
let fixed_path = path_fix("./relative/path/to/DOCUMENT-123.pdf");
|
||||
println(`Original: "./relative/path/to/DOCUMENT-123.pdf"`);
|
||||
println(`Fixed: "${fixed_path}"`); // Should output: "./relative/path/to/document_123.pdf"
|
||||
|
||||
// ===== Dedent Functions Examples =====
|
||||
println("\n\n===== Dedent Functions Examples =====");
|
||||
|
||||
// Example 1: dedent
|
||||
println("\n--- Example 1: dedent ---");
|
||||
let indented_text = " line 1\n line 2\n line 3";
|
||||
println(`Original:\n${indented_text}`);
|
||||
let dedented = dedent(indented_text);
|
||||
println(`Dedented:\n${dedented}`); // Should output: "line 1\nline 2\n line 3"
|
||||
|
||||
// Example 2: prefix
|
||||
println("\n--- Example 2: prefix ---");
|
||||
let text = "line 1\nline 2\nline 3";
|
||||
println(`Original:\n${text}`);
|
||||
let prefixed = prefix(text, " ");
|
||||
println(`Prefixed:\n${prefixed}`); // Should output: " line 1\n line 2\n line 3"
|
||||
|
||||
// Return success message
|
||||
"Text tools example completed successfully!"
|
102
examples/scripts/_archive/write_read.rhai
Normal file
102
examples/scripts/_archive/write_read.rhai
Normal file
@ -0,0 +1,102 @@
|
||||
// write_read.rhai
|
||||
// Demonstrates writing content to and reading content from a container
|
||||
// using the write_content and read_content methods
|
||||
|
||||
println("Starting write/read container example...");
|
||||
|
||||
// Define image and container names
|
||||
let base_image = "ubuntu:22.04";
|
||||
let container_name = "write-read-demo";
|
||||
let final_image_name = "write-read-demo:latest";
|
||||
|
||||
println(`Creating container '${container_name}' from base image '${base_image}'...`);
|
||||
|
||||
// Create a new buildah container
|
||||
let builder = bah_new(container_name, base_image);
|
||||
|
||||
// Update package lists
|
||||
println("Updating package lists...");
|
||||
let update_result = builder.run("apt-get update -y");
|
||||
println(`Package update result: ${update_result.success ? "Success" : "Failed"}`);
|
||||
|
||||
// Write a simple text file to the container
|
||||
println("\nWriting content to the container...");
|
||||
let text_content = "This is a test file created using write_content.\nIt supports multiple lines.\n";
|
||||
let write_result = builder.write_content(text_content, "/test.txt");
|
||||
println(`Write result: ${write_result.success ? "Success" : "Failed"}`);
|
||||
|
||||
// Write a simple HTML file to the container
|
||||
println("\nWriting HTML content to the container...");
|
||||
let html_content = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Write Content Demo</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 40px;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
h1 {
|
||||
color: #0066cc;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello from Buildah!</h1>
|
||||
<p>This HTML file was created using the write_content method.</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
let html_write_result = builder.write_content(html_content, "/var/www/html/index.html");
|
||||
println(`HTML write result: ${html_write_result.success ? "Success" : "Failed"}`);
|
||||
|
||||
// Write a simple shell script to the container
|
||||
println("\nWriting shell script to the container...");
|
||||
let script_content = `
|
||||
#!/bin/bash
|
||||
echo "This script was created using write_content"
|
||||
echo "Current directory: $(pwd)"
|
||||
echo "Files in current directory:"
|
||||
ls -la
|
||||
`;
|
||||
let script_write_result = builder.write_content(script_content, "/test.sh");
|
||||
println(`Script write result: ${script_write_result.success ? "Success" : "Failed"}`);
|
||||
|
||||
// Make the script executable
|
||||
builder.run("chmod +x /test.sh");
|
||||
|
||||
// Read back the content we wrote
|
||||
println("\nReading content from the container...");
|
||||
let read_text = builder.read_content("/test.txt");
|
||||
println("Text file content:");
|
||||
println(read_text);
|
||||
|
||||
let read_html = builder.read_content("/var/www/html/index.html");
|
||||
println("\nHTML file content (first 100 characters):");
|
||||
println(read_html.substr(0, 100) + "...");
|
||||
|
||||
let read_script = builder.read_content("/test.sh");
|
||||
println("\nScript file content:");
|
||||
println(read_script);
|
||||
|
||||
// Execute the script we created
|
||||
println("\nExecuting the script we created...");
|
||||
let script_result = builder.run("/test.sh");
|
||||
println("Script output:");
|
||||
println(script_result.stdout);
|
||||
|
||||
// Commit the container to an image
|
||||
println(`\nCommitting container to image '${final_image_name}'...`);
|
||||
let commit_result = builder.commit(final_image_name);
|
||||
println(`Commit result: ${commit_result.success ? "Success" : "Failed"}`);
|
||||
|
||||
// Clean up the buildah container
|
||||
println("Cleaning up buildah container...");
|
||||
builder.remove();
|
||||
|
||||
println("\nWrite/read example completed successfully!");
|
||||
|
||||
"Write/read example completed successfully!"
|
82
examples/scripts/basics/directories.rhai
Normal file
82
examples/scripts/basics/directories.rhai
Normal file
@ -0,0 +1,82 @@
|
||||
|
||||
// Create a test directory structure
|
||||
let base_dir = "rhai_dir_test";
|
||||
let sub_dir = base_dir + "/tmp/test";
|
||||
|
||||
println("Creating directory structure...");
|
||||
let base_result = mkdir(base_dir+"/subdir");
|
||||
println(`Base directory creation result: ${base_result}`);
|
||||
|
||||
let sub_result = mkdir(sub_dir);
|
||||
println(`Subdirectory creation result: ${sub_result}`);
|
||||
|
||||
// Create a test file in the base directory
|
||||
let base_file = base_dir + "/base_file.txt";
|
||||
let base_content = "This is a file in the base directory.";
|
||||
// First touch the file
|
||||
run_command(`touch ${base_file}`);
|
||||
// Then write to it with a separate command
|
||||
run_command(`echo ${base_content} > ${base_file}`);
|
||||
|
||||
// Create a test file in the subdirectory
|
||||
let sub_file = sub_dir + "/sub_file.txt";
|
||||
let sub_content = "This is a file in the subdirectory.";
|
||||
// First touch the file
|
||||
run_command(`touch ${sub_file}`);
|
||||
// Then write to it with a separate command
|
||||
run_command(`echo ${sub_content} > ${sub_file}`);
|
||||
|
||||
// Get the current working directory before changing
|
||||
let pwd_before = run_command("pwd");
|
||||
println(`Current directory before chdir: ${pwd_before.stdout.trim()}`);
|
||||
|
||||
// Change to the base directory
|
||||
println(`Changing directory to: ${base_dir}`);
|
||||
let chdir_result = chdir(base_dir);
|
||||
println(`Directory change result: ${chdir_result}`);
|
||||
|
||||
// Get the current working directory after changing
|
||||
let pwd_after = run_command("pwd");
|
||||
println(`Current directory after chdir: ${pwd_after.stdout.trim()}`);
|
||||
|
||||
// List files in the current directory (which should now be the base directory)
|
||||
println("Files in the current directory:");
|
||||
let files = find_files(".", "*");
|
||||
println("Files found:");
|
||||
for file in files {
|
||||
println(`- ${file}`);
|
||||
}
|
||||
|
||||
// Change to the subdirectory
|
||||
println(`Changing directory to: subdir`);
|
||||
let chdir_sub_result = chdir("subdir");
|
||||
println(`Directory change result: ${chdir_sub_result}`);
|
||||
|
||||
// Get the current working directory after changing to subdirectory
|
||||
let pwd_final = run_command("pwd");
|
||||
println(`Current directory after second chdir: ${pwd_final.stdout.trim()}`);
|
||||
|
||||
// List files in the subdirectory
|
||||
println("Files in the subdirectory:");
|
||||
let subdir_files = find_files(".", "*");
|
||||
println("Files found:");
|
||||
for file in subdir_files {
|
||||
println(`- ${file}`);
|
||||
}
|
||||
|
||||
// Change back to the parent directory
|
||||
println("Changing directory back to parent...");
|
||||
let chdir_parent_result = chdir("..");
|
||||
println(`Directory change result: ${chdir_parent_result}`);
|
||||
|
||||
// Clean up (uncomment to actually delete the files)
|
||||
// println("Cleaning up...");
|
||||
// Change back to the original directory first
|
||||
// chdir(pwd_before.stdout.trim());
|
||||
// delete(sub_file);
|
||||
// delete(base_file);
|
||||
// delete(sub_dir);
|
||||
// delete(base_dir);
|
||||
// println("Cleanup complete");
|
||||
|
||||
"Directory operations script completed successfully!"
|
64
examples/scripts/basics/files.rhai
Normal file
64
examples/scripts/basics/files.rhai
Normal file
@ -0,0 +1,64 @@
|
||||
// 02_file_operations.rhai
|
||||
// Demonstrates file system operations using SAL
|
||||
|
||||
// Create a test directory
|
||||
let test_dir = "/tmp/rhai_test_dir";
|
||||
println(`Creating directory: ${test_dir}`);
|
||||
let mkdir_result = mkdir(test_dir);
|
||||
println(`Directory creation result: ${mkdir_result}`);
|
||||
|
||||
// Check if the directory exists
|
||||
let dir_exists = exist(test_dir);
|
||||
println(`Directory exists: ${dir_exists}`);
|
||||
|
||||
// Create a test file
|
||||
let test_file = test_dir + "/test_file.txt";
|
||||
let file_content = "This is a test file created by Rhai script.";
|
||||
|
||||
// Create the file using a different approach
|
||||
println(`Creating file: ${test_file}`);
|
||||
// First ensure the directory exists
|
||||
run_command(`mkdir -p ${test_dir}`);
|
||||
// Then create the file using a simpler approach
|
||||
// First touch the file
|
||||
let touch_cmd = `touch ${test_file}`;
|
||||
run_command(touch_cmd);
|
||||
// Then write to it with a separate command
|
||||
let echo_cmd = `echo ${file_content} > ${test_file}`;
|
||||
let write_result = run_command(echo_cmd);
|
||||
println(`File creation result: ${write_result.success}`);
|
||||
|
||||
// Wait a moment to ensure the file is created
|
||||
run_command("sleep 1");
|
||||
|
||||
// Check if the file exists
|
||||
let file_exists = exist(test_file);
|
||||
println(`File exists: ${file_exists}`);
|
||||
|
||||
// Get file size
|
||||
if file_exists {
|
||||
let size = file_size(test_file);
|
||||
println(`File size: ${size} bytes`);
|
||||
}
|
||||
|
||||
// Copy the file
|
||||
let copied_file = test_dir + "/copied_file.txt";
|
||||
println(`Copying file to: ${copied_file}`);
|
||||
let copy_result = copy(test_file, copied_file);
|
||||
println(`File copy result: ${copy_result}`);
|
||||
|
||||
// Find files in the directory
|
||||
println("Finding files in the test directory:");
|
||||
let files = find_files(test_dir, "*.txt");
|
||||
for file in files {
|
||||
println(` - ${file}`);
|
||||
}
|
||||
|
||||
// Clean up (uncomment to actually delete the files)
|
||||
// println("Cleaning up...");
|
||||
// delete(copied_file);
|
||||
// delete(test_file);
|
||||
// delete(test_dir);
|
||||
// println("Cleanup complete");
|
||||
|
||||
"File operations script completed successfully!"
|
39
examples/scripts/basics/hello.rhai
Normal file
39
examples/scripts/basics/hello.rhai
Normal file
@ -0,0 +1,39 @@
|
||||
// 01_hello_world.rhai
|
||||
// A simple hello world script to demonstrate basic Rhai functionality
|
||||
|
||||
// Print a message
|
||||
println("Hello from Rhai!");
|
||||
|
||||
// Define a function
|
||||
fn greet(name) {
|
||||
"Hello, " + name + "!"
|
||||
}
|
||||
|
||||
// Call the function and print the result
|
||||
let greeting = greet("SAL User");
|
||||
println(greeting);
|
||||
|
||||
// Do some basic calculations
|
||||
let a = 5;
|
||||
let b = 7;
|
||||
println(`${a} + ${b} = ${a + b}`);
|
||||
println(`${a} * ${b} = ${a * b}`);
|
||||
|
||||
// Create and use an array
|
||||
let numbers = [1, 2, 3, 4, 5];
|
||||
println("Numbers: " + numbers);
|
||||
println("Sum of numbers: " + numbers.reduce(|sum, n| sum + n, 0));
|
||||
|
||||
// Create and use a map
|
||||
let person = #{
|
||||
name: "John Doe",
|
||||
age: 30,
|
||||
occupation: "Developer"
|
||||
};
|
||||
|
||||
println("Person: " + person);
|
||||
println("Name: " + person.name);
|
||||
println("Age: " + person.age);
|
||||
|
||||
// Return a success message
|
||||
"Hello world script completed successfully!"
|
150
examples/scripts/containers/buildah.rhai
Normal file
150
examples/scripts/containers/buildah.rhai
Normal file
@ -0,0 +1,150 @@
|
||||
// buildah.rhai
|
||||
// Demonstrates using buildah to create a custom image with golang and nginx,
|
||||
// then using nerdctl to run a container from that image
|
||||
|
||||
println("Starting buildah workflow to create a custom image...");
|
||||
|
||||
// Define image and container names
|
||||
let base_image = "ubuntu:22.04";
|
||||
let container_name = "golang-nginx-container";
|
||||
let final_image_name = "custom-golang-nginx:latest";
|
||||
|
||||
println(`Creating container '${container_name}' from base image '${base_image}'...`);
|
||||
|
||||
// Create a new buildah container using the builder pattern
|
||||
let builder = bah_new(container_name, base_image);
|
||||
|
||||
println("Enabling debug mode...");
|
||||
builder.debug_mode = true;
|
||||
|
||||
// Update package lists and install golang and nginx
|
||||
println("Installing packages (this may take a while)...");
|
||||
|
||||
// Update package lists
|
||||
let update_result = builder.run("apt-get update -y");
|
||||
|
||||
// Install required packages
|
||||
let install_result = builder.run("apt-get install -y golang nginx");
|
||||
|
||||
// Verify installations
|
||||
let go_version = builder.run("go version");
|
||||
println(`Go version: ${go_version.stdout}`);
|
||||
|
||||
let nginx_version = builder.run("nginx -v");
|
||||
println(`Nginx version: ${nginx_version.stderr}`); // nginx outputs version to stderr
|
||||
|
||||
// Create a simple Go web application
|
||||
println("Creating a simple Go web application...");
|
||||
|
||||
// Create a directory for the Go application
|
||||
builder.run("mkdir -p /app");
|
||||
|
||||
// Create a simple Go web server
|
||||
let go_app = `
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Hello from Go running in a custom container!")
|
||||
})
|
||||
|
||||
fmt.Println("Starting server on :8080")
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
`;
|
||||
|
||||
// Write the Go application to a file using the write_content method
|
||||
builder.write_content(go_app, "/app/main.go");
|
||||
|
||||
// Compile the Go application
|
||||
builder.run("cd /app && go build -o server main.go");
|
||||
|
||||
// Configure nginx to proxy to the Go application
|
||||
let nginx_conf = `
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Write the nginx configuration using the write_content method
|
||||
let nginx_conf_result = builder.write_content(nginx_conf, "/etc/nginx/sites-available/default");
|
||||
|
||||
// Create a startup script
|
||||
let startup_script = `
|
||||
#!/bin/bash
|
||||
# Start the Go application in the background
|
||||
cd /app && ./server &
|
||||
# Start nginx in the foreground
|
||||
nginx -g "daemon off;"
|
||||
`;
|
||||
|
||||
// Write the startup script using the write_content method
|
||||
let startup_script_result = builder.write_content(startup_script, "/start.sh");
|
||||
builder.run("chmod +x /start.sh");
|
||||
|
||||
// Set the entrypoint to the startup script
|
||||
println("Setting entrypoint to /start.sh...");
|
||||
builder.set_entrypoint("/start.sh");
|
||||
|
||||
// Read back the startup script to verify it was written correctly
|
||||
let read_script = builder.read_content("/start.sh");
|
||||
println("Startup script content verification:");
|
||||
println(read_script);
|
||||
|
||||
// Commit the container to a new image
|
||||
println(`Committing container to image '${final_image_name}'...`);
|
||||
let commit_result = builder.commit(final_image_name);
|
||||
|
||||
// Clean up the buildah container
|
||||
println("Cleaning up buildah container...");
|
||||
builder.remove();
|
||||
|
||||
// Now use nerdctl to run a container from the new image
|
||||
println("\nStarting container from the new image using nerdctl...");
|
||||
|
||||
// Create a container using the builder pattern
|
||||
// Use localhost/ prefix to ensure nerdctl uses the local image
|
||||
let local_image_name = "localhost/" + final_image_name;
|
||||
println(`Using local image: ${local_image_name}`);
|
||||
|
||||
// Tag the image with the localhost prefix for nerdctl compatibility
|
||||
println(`Tagging image as ${local_image_name}...`);
|
||||
let tag_result = image_tag(final_image_name, local_image_name);
|
||||
|
||||
// Print a command to check if the image exists in buildah
|
||||
println("\nTo verify the image was created with buildah, run:");
|
||||
println("buildah images");
|
||||
|
||||
// Note: If nerdctl cannot find the image, you may need to push it to a registry
|
||||
// println("\nNote: If nerdctl cannot find the image, you may need to push it to a registry:");
|
||||
// println("buildah push localhost/custom-golang-nginx:latest docker://localhost:5000/custom-golang-nginx:latest");
|
||||
// println("nerdctl pull localhost:5000/custom-golang-nginx:latest");
|
||||
|
||||
let container = nerdctl_container_from_image("golang-nginx-demo", local_image_name)
|
||||
.with_detach(true)
|
||||
.with_port("8080:80") // Map port 80 in the container to 8080 on the host
|
||||
.with_restart_policy("unless-stopped")
|
||||
.build();
|
||||
|
||||
// Start the container
|
||||
let start_result = container.start();
|
||||
|
||||
println("\nWorkflow completed successfully!");
|
||||
println("The web server should be running at http://localhost:8080");
|
||||
println("You can check container logs with: nerdctl logs golang-nginx-demo");
|
||||
println("To stop the container: nerdctl stop golang-nginx-demo");
|
||||
println("To remove the container: nerdctl rm golang-nginx-demo");
|
||||
|
||||
"Buildah and nerdctl workflow completed successfully!"
|
39
examples/scripts/containers/buildah_debug.rhai
Normal file
39
examples/scripts/containers/buildah_debug.rhai
Normal file
@ -0,0 +1,39 @@
|
||||
// buildah_debug.rhai
|
||||
// Demonstrates using the debug flag on the buildah Builder
|
||||
|
||||
println("Starting buildah debug example...");
|
||||
|
||||
// Define image and container names
|
||||
let base_image = "ubuntu:22.04";
|
||||
let container_name = "debug-test-container";
|
||||
|
||||
println(`Creating container '${container_name}' from base image '${base_image}'...`);
|
||||
|
||||
// Create a new buildah container using the builder pattern
|
||||
let builder = bah_new(container_name, base_image);
|
||||
|
||||
// Enable debug mode
|
||||
println("Enabling debug mode...");
|
||||
builder.debug_mode = true;
|
||||
|
||||
// Run a simple command to see debug output
|
||||
println("Running a command with debug enabled...");
|
||||
let result = builder.run("echo 'Hello from debug mode'");
|
||||
|
||||
// Disable debug mode
|
||||
println("Disabling debug mode...");
|
||||
builder.debug_mode = false;
|
||||
|
||||
// Run another command without debug
|
||||
println("Running a command with debug disabled...");
|
||||
let result2 = builder.run("echo 'Hello without debug'");
|
||||
|
||||
// Enable debug mode again
|
||||
println("Enabling debug mode again...");
|
||||
builder.debug_mode = true;
|
||||
|
||||
// Remove the container with debug enabled
|
||||
println("Removing the container with debug enabled...");
|
||||
builder.remove();
|
||||
|
||||
println("Debug example completed!");
|
44
examples/scripts/containers/buildah_run.rhai
Normal file
44
examples/scripts/containers/buildah_run.rhai
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
// Now use nerdctl to run a container from the new image
|
||||
println("\nStarting container from the new image using nerdctl...");
|
||||
|
||||
// Create a container using the builder pattern
|
||||
// Use localhost/ prefix to ensure nerdctl uses the local image
|
||||
let local_image_name = "localhost/custom-golang-nginx:latest";
|
||||
println(`Using local image: ${local_image_name}`);
|
||||
|
||||
// Import the image from buildah to nerdctl
|
||||
println("Importing image from buildah to nerdctl...");
|
||||
process_run("buildah", ["push", "custom-golang-nginx:latest", "docker-daemon:localhost/custom-golang-nginx:latest"]);
|
||||
|
||||
let tag_result = nerdctl_image_tag("custom-golang-nginx:latest", local_image_name);
|
||||
|
||||
// Tag the image with the localhost prefix for nerdctl compatibility
|
||||
// println(`Tagging image as ${local_image_name}...`);
|
||||
// let tag_result = bah_image_tag(final_image_name, local_image_name);
|
||||
|
||||
// Print a command to check if the image exists in buildah
|
||||
println("\nTo verify the image was created with buildah, run:");
|
||||
println("buildah images");
|
||||
|
||||
// Note: If nerdctl cannot find the image, you may need to push it to a registry
|
||||
// println("\nNote: If nerdctl cannot find the image, you may need to push it to a registry:");
|
||||
// println("buildah push localhost/custom-golang-nginx:latest docker://localhost:5000/custom-golang-nginx:latest");
|
||||
// println("nerdctl pull localhost:5000/custom-golang-nginx:latest");
|
||||
|
||||
let container = nerdctl_container_from_image("golang-nginx-demo", local_image_name)
|
||||
.with_detach(true)
|
||||
.with_port("8081:80") // Map port 80 in the container to 8080 on the host
|
||||
.with_restart_policy("unless-stopped")
|
||||
.build();
|
||||
|
||||
// Start the container
|
||||
let start_result = container.start();
|
||||
|
||||
println("\nWorkflow completed successfully!");
|
||||
println("The web server should be running at http://localhost:8081");
|
||||
println("You can check container logs with: nerdctl logs golang-nginx-demo");
|
||||
println("To stop the container: nerdctl stop golang-nginx-demo");
|
||||
println("To remove the container: nerdctl rm golang-nginx-demo");
|
||||
|
||||
"Buildah and nerdctl workflow completed successfully!"
|
86
examples/scripts/containers/nerdctl_webserver.rhai
Normal file
86
examples/scripts/containers/nerdctl_webserver.rhai
Normal file
@ -0,0 +1,86 @@
|
||||
// 08__web_server.rhai
|
||||
// Demonstrates a complete workflow to set up a web server using
|
||||
// Note: This script requires to be installed and may need root privileges
|
||||
|
||||
println("Starting web server workflow...");
|
||||
|
||||
// Create and use a temporary directory for all files
|
||||
let work_dir = "/tmp/";
|
||||
mkdir(work_dir);
|
||||
chdir(work_dir);
|
||||
println(`Working in directory: ${work_dir}`);
|
||||
|
||||
|
||||
println("\n=== Creating custom nginx configuration ===");
|
||||
let config_content = `
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
let config_file = `${work_dir}/custom-nginx.conf`;
|
||||
// Use file_write instead of run command
|
||||
file_write(config_file, config_content);
|
||||
println(`Created custom nginx configuration file at ${config_file}`);
|
||||
|
||||
// Step 3: Create a custom index.html file
|
||||
println("\n=== Creating custom index.html ===");
|
||||
let html_content = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Demo</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 40px;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
h1 {
|
||||
color: #0066cc;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello from HeroScript !</h1>
|
||||
<p>This page is served by an Nginx container.</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
let html_file = `${work_dir}/index.html`;
|
||||
// Use file_write instead of run command
|
||||
file_write(html_file, html_content);
|
||||
println(`Created custom index.html file at ${html_file}`);
|
||||
|
||||
println("\n=== Creating nginx container ===");
|
||||
let container_name = "nginx-demo";
|
||||
|
||||
let env_map = #{}; // Create an empty map
|
||||
env_map["NGINX_HOST"] = "localhost";
|
||||
env_map["NGINX_PORT"] = "80";
|
||||
env_map["NGINX_WORKER_PROCESSES"] = "auto";
|
||||
|
||||
// Create a container with a rich set of options using batch methods
|
||||
let container = nerdctl_container_from_image(container_name, "nginx:latest")
|
||||
.reset()
|
||||
.with_detach(true)
|
||||
.with_ports(["8080:80"]) // Add multiple ports at once
|
||||
.with_volumes([`${work_dir}:/usr/share/nginx/html`, "/var/log:/var/log/nginx"]) // Mount our work dir
|
||||
.with_envs(env_map) // Add multiple environment variables at once
|
||||
.with_cpu_limit("1.0")
|
||||
.with_memory_limit("512m")
|
||||
.start();
|
||||
|
||||
|
||||
println("\n web server workflow completed successfully!");
|
||||
println("The web server is running at http://localhost:8080");
|
||||
|
||||
"Web server script completed successfully!"
|
28
examples/scripts/git/git_basic.rhai
Normal file
28
examples/scripts/git/git_basic.rhai
Normal file
@ -0,0 +1,28 @@
|
||||
// Simplified Git Basic Operations Example
|
||||
|
||||
let git_tree = git_tree_new("/tmp/git"); // Using /tmp/git as base path
|
||||
|
||||
print("--- Git Basic Operations ---");
|
||||
// print(`Base path: ${git_tree.base_path()}`); // base_path() getter would need to be exposed from Rust
|
||||
|
||||
let all_repos = git_tree.list();
|
||||
print(`Listed ${all_repos.len()} repos.`);
|
||||
|
||||
// Find repos starting with "home" (adjust pattern if /tmp/git might contain other "home*" repos)
|
||||
let found_repos = git_tree.find("home*");
|
||||
print(`Found ${found_repos.len()} repos matching "home*".`);
|
||||
for r in found_repos {
|
||||
print(` - Found: ${r.path()}`);
|
||||
}
|
||||
|
||||
print("Getting/Cloning 'https://github.com/freeflowuniverse/home'...");
|
||||
let repo = git_tree.get("https://github.com/freeflowuniverse/home");
|
||||
print(`Repo path: ${repo.path()}`);
|
||||
print(`Has changes: ${repo.has_changes()}`);
|
||||
|
||||
print("Performing pull & reset...");
|
||||
repo.pull().reset();
|
||||
print("Pull and reset complete.");
|
||||
print(`Has changes after pull/reset: ${repo.has_changes()}`);
|
||||
|
||||
print("--- Example Finished ---");
|
76
examples/scripts/hero_vault/README.md
Normal file
76
examples/scripts/hero_vault/README.md
Normal file
@ -0,0 +1,76 @@
|
||||
# SAL Vault Examples
|
||||
|
||||
This directory contains examples demonstrating the SAL Vault functionality.
|
||||
|
||||
## Overview
|
||||
|
||||
SAL Vault provides secure key management and cryptographic operations including:
|
||||
|
||||
- Vault creation and management
|
||||
- KeySpace operations (encrypted key-value stores)
|
||||
- Symmetric key generation and operations
|
||||
- Asymmetric key operations (signing and verification)
|
||||
- Secure key derivation from passwords
|
||||
|
||||
## Current Status
|
||||
|
||||
⚠️ **Note**: The vault module is currently being updated to use Lee's implementation.
|
||||
The Rhai scripting integration is temporarily disabled while we adapt the examples
|
||||
to work with the new vault API.
|
||||
|
||||
## Available Operations
|
||||
|
||||
- **Vault Management**: Create and manage vault instances
|
||||
- **KeySpace Operations**: Open encrypted key-value stores within vaults
|
||||
- **Symmetric Encryption**: Generate keys and encrypt/decrypt data
|
||||
- **Asymmetric Operations**: Create keypairs, sign messages, verify signatures
|
||||
|
||||
## Example Files (Legacy - Sameh's Implementation)
|
||||
|
||||
⚠️ **These examples are currently archived and use the previous vault implementation**:
|
||||
|
||||
- `_archive/example.rhai` - Basic example demonstrating key management, signing, and encryption
|
||||
- `_archive/advanced_example.rhai` - Advanced example with error handling and complex operations
|
||||
- `_archive/key_persistence_example.rhai` - Demonstrates creating and saving a key space to disk
|
||||
- `_archive/load_existing_space.rhai` - Shows how to load a previously created key space
|
||||
- `_archive/contract_example.rhai` - Demonstrates smart contract interactions (Ethereum)
|
||||
- `_archive/agung_send_transaction.rhai` - Demonstrates Ethereum transactions on Agung network
|
||||
- `_archive/agung_contract_with_args.rhai` - Shows contract interactions with arguments
|
||||
|
||||
## Current Implementation (Lee's Vault)
|
||||
|
||||
The current vault implementation provides:
|
||||
|
||||
```rust
|
||||
// Create a new vault
|
||||
let vault = Vault::new(&path).await?;
|
||||
|
||||
// Open an encrypted keyspace
|
||||
let keyspace = vault.open_keyspace("my_space", "password").await?;
|
||||
|
||||
// Perform cryptographic operations
|
||||
// (API documentation coming soon)
|
||||
```
|
||||
|
||||
## Migration Status
|
||||
|
||||
- ✅ **Vault Core**: Lee's implementation is active
|
||||
- ✅ **Archive**: Sameh's implementation preserved in `vault/_archive/`
|
||||
- ⏳ **Rhai Integration**: Being developed for Lee's implementation
|
||||
- ⏳ **Examples**: Will be updated to use Lee's API
|
||||
- ❌ **Ethereum Features**: Not available in Lee's implementation
|
||||
|
||||
## Security
|
||||
|
||||
The vault uses:
|
||||
|
||||
- **ChaCha20Poly1305** for symmetric encryption
|
||||
- **Password-based key derivation** for keyspace encryption
|
||||
- **Secure key storage** with proper isolation
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Rhai Integration**: Implement Rhai bindings for Lee's vault
|
||||
2. **New Examples**: Create examples using Lee's simpler API
|
||||
3. **Documentation**: Complete API documentation for Lee's implementation
|
||||
4. **Migration Guide**: Provide guidance for users migrating from Sameh's implementation
|
233
examples/scripts/hero_vault/_archive/advanced_example.rhai
Normal file
233
examples/scripts/hero_vault/_archive/advanced_example.rhai
Normal file
@ -0,0 +1,233 @@
|
||||
// Advanced Rhai script example for Hero Vault Cryptography Module
|
||||
// This script demonstrates conditional logic, error handling, and more complex operations
|
||||
|
||||
// Function to create a key space with error handling
|
||||
fn setup_key_space(name, password) {
|
||||
print("Attempting: Create key space: " + name);
|
||||
let result = create_key_space(name, password);
|
||||
|
||||
if result {
|
||||
print("✅ Create key space succeeded!");
|
||||
return true;
|
||||
} else {
|
||||
print("❌ Create key space failed!");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function to create and select a keypair
|
||||
fn setup_keypair(name, password) {
|
||||
print("Attempting: Create keypair: " + name);
|
||||
let result = create_keypair(name, password);
|
||||
|
||||
if result {
|
||||
print("✅ Create keypair succeeded!");
|
||||
|
||||
print("Attempting: Select keypair: " + name);
|
||||
let selected = select_keypair(name);
|
||||
|
||||
if selected {
|
||||
print("✅ Select keypair succeeded!");
|
||||
return true;
|
||||
} else {
|
||||
print("❌ Select keypair failed!");
|
||||
}
|
||||
} else {
|
||||
print("❌ Create keypair failed!");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function to sign multiple messages
|
||||
fn sign_messages(messages) {
|
||||
let signatures = [];
|
||||
|
||||
for message in messages {
|
||||
print("Signing message: " + message);
|
||||
print("Attempting: Sign message");
|
||||
let signature = sign(message);
|
||||
|
||||
if signature != "" {
|
||||
print("✅ Sign message succeeded!");
|
||||
signatures.push(#{
|
||||
message: message,
|
||||
signature: signature
|
||||
});
|
||||
} else {
|
||||
print("❌ Sign message failed!");
|
||||
}
|
||||
}
|
||||
|
||||
return signatures;
|
||||
}
|
||||
|
||||
// Function to verify signatures
|
||||
fn verify_signatures(signed_messages) {
|
||||
let results = [];
|
||||
|
||||
for item in signed_messages {
|
||||
let message = item.message;
|
||||
let signature = item.signature;
|
||||
|
||||
print("Verifying signature for: " + message);
|
||||
print("Attempting: Verify signature");
|
||||
let is_valid = verify(message, signature);
|
||||
|
||||
if is_valid {
|
||||
print("✅ Verify signature succeeded!");
|
||||
} else {
|
||||
print("❌ Verify signature failed!");
|
||||
}
|
||||
|
||||
results.push(#{
|
||||
message: message,
|
||||
valid: is_valid
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// Function to encrypt multiple messages
|
||||
fn encrypt_messages(messages) {
|
||||
// Generate a symmetric key
|
||||
print("Attempting: Generate symmetric key");
|
||||
let key = generate_key();
|
||||
|
||||
if key == "" {
|
||||
print("❌ Generate symmetric key failed!");
|
||||
return [];
|
||||
}
|
||||
|
||||
print("✅ Generate symmetric key succeeded!");
|
||||
print("Using key: " + key);
|
||||
let encrypted_messages = [];
|
||||
|
||||
for message in messages {
|
||||
print("Encrypting message: " + message);
|
||||
print("Attempting: Encrypt message");
|
||||
let encrypted = encrypt(key, message);
|
||||
|
||||
if encrypted != "" {
|
||||
print("✅ Encrypt message succeeded!");
|
||||
encrypted_messages.push(#{
|
||||
original: message,
|
||||
encrypted: encrypted,
|
||||
key: key
|
||||
});
|
||||
} else {
|
||||
print("❌ Encrypt message failed!");
|
||||
}
|
||||
}
|
||||
|
||||
return encrypted_messages;
|
||||
}
|
||||
|
||||
// Function to decrypt messages
|
||||
fn decrypt_messages(encrypted_messages) {
|
||||
let decrypted_messages = [];
|
||||
|
||||
for item in encrypted_messages {
|
||||
let encrypted = item.encrypted;
|
||||
let key = item.key;
|
||||
let original = item.original;
|
||||
|
||||
print("Decrypting message...");
|
||||
print("Attempting: Decrypt message");
|
||||
let decrypted = decrypt(key, encrypted);
|
||||
|
||||
if decrypted != false {
|
||||
let success = decrypted == original;
|
||||
|
||||
decrypted_messages.push(#{
|
||||
decrypted: decrypted,
|
||||
original: original,
|
||||
success: success
|
||||
});
|
||||
|
||||
if success {
|
||||
print("Decryption matched original ✅");
|
||||
} else {
|
||||
print("Decryption did not match original ❌");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return decrypted_messages;
|
||||
}
|
||||
|
||||
// Main script execution
|
||||
print("=== Advanced Cryptography Script ===");
|
||||
|
||||
// Set up key space
|
||||
let space_name = "advanced_space";
|
||||
let password = "secure_password123";
|
||||
|
||||
if setup_key_space(space_name, password) {
|
||||
print("\n--- Key space setup complete ---\n");
|
||||
|
||||
// Set up keypair
|
||||
if setup_keypair("advanced_keypair", password) {
|
||||
print("\n--- Keypair setup complete ---\n");
|
||||
|
||||
// Define messages to sign
|
||||
let messages = [
|
||||
"This is the first message to sign",
|
||||
"Here's another message that needs signing",
|
||||
"And a third message for good measure"
|
||||
];
|
||||
|
||||
// Sign messages
|
||||
print("\n--- Signing Messages ---\n");
|
||||
let signed_messages = sign_messages(messages);
|
||||
|
||||
// Verify signatures
|
||||
print("\n--- Verifying Signatures ---\n");
|
||||
let verification_results = verify_signatures(signed_messages);
|
||||
|
||||
// Count successful verifications
|
||||
let successful_verifications = verification_results.filter(|r| r.valid).len();
|
||||
print("Successfully verified " + successful_verifications + " out of " + verification_results.len() + " signatures");
|
||||
|
||||
// Encrypt messages
|
||||
print("\n--- Encrypting Messages ---\n");
|
||||
let encrypted_messages = encrypt_messages(messages);
|
||||
|
||||
// Decrypt messages
|
||||
print("\n--- Decrypting Messages ---\n");
|
||||
let decryption_results = decrypt_messages(encrypted_messages);
|
||||
|
||||
// Count successful decryptions
|
||||
let successful_decryptions = decryption_results.filter(|r| r.success).len();
|
||||
print("Successfully decrypted " + successful_decryptions + " out of " + decryption_results.len() + " messages");
|
||||
|
||||
// Create Ethereum wallet
|
||||
print("\n--- Creating Ethereum Wallet ---\n");
|
||||
print("Attempting: Create Ethereum wallet");
|
||||
let wallet_created = create_ethereum_wallet();
|
||||
|
||||
if wallet_created {
|
||||
print("✅ Create Ethereum wallet succeeded!");
|
||||
|
||||
print("Attempting: Get Ethereum address");
|
||||
let address = get_ethereum_address();
|
||||
|
||||
if address != "" {
|
||||
print("✅ Get Ethereum address succeeded!");
|
||||
print("Ethereum wallet address: " + address);
|
||||
} else {
|
||||
print("❌ Get Ethereum address failed!");
|
||||
}
|
||||
} else {
|
||||
print("❌ Create Ethereum wallet failed!");
|
||||
}
|
||||
|
||||
print("\n=== Script execution completed successfully! ===");
|
||||
} else {
|
||||
print("Failed to set up keypair. Aborting script.");
|
||||
}
|
||||
} else {
|
||||
print("Failed to set up key space. Aborting script.");
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
// Example Rhai script for testing contract functions with arguments on Agung network
|
||||
// This script demonstrates how to use call_contract_read and call_contract_write with arguments
|
||||
|
||||
// Step 1: Set up wallet and network
|
||||
let space_name = "agung_contract_args_demo";
|
||||
let password = "secure_password123";
|
||||
let private_key = "51c194d20bcd25360a3aa94426b3b60f738007e42f22e1bc97821c65c353e6d2";
|
||||
let network_name = "agung";
|
||||
|
||||
print("=== Testing Contract Functions With Arguments on Agung Network ===\n");
|
||||
|
||||
// Create a key space
|
||||
print("Creating key space: " + space_name);
|
||||
if create_key_space(space_name, password) {
|
||||
print("✓ Key space created successfully");
|
||||
|
||||
// Create a keypair
|
||||
print("\nCreating keypair...");
|
||||
if create_keypair("contract_key", password) {
|
||||
print("✓ Created contract keypair");
|
||||
|
||||
// Create a wallet from the private key for the Agung network
|
||||
print("\nCreating wallet from private key for Agung network...");
|
||||
if create_wallet_from_private_key_for_network(private_key, network_name) {
|
||||
print("✓ Wallet created successfully");
|
||||
|
||||
// Get the wallet address
|
||||
let wallet_address = get_wallet_address_for_network(network_name);
|
||||
print("Wallet address: " + wallet_address);
|
||||
|
||||
// Check wallet balance
|
||||
print("\nChecking wallet balance...");
|
||||
let balance = get_balance(network_name, wallet_address);
|
||||
if balance != "" {
|
||||
print("Wallet balance: " + balance + " wei");
|
||||
|
||||
// Define a simple ERC-20 token contract ABI (partial)
|
||||
let token_abi = `[
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "name",
|
||||
"outputs": [{"name": "", "type": "string"}],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "symbol",
|
||||
"outputs": [{"name": "", "type": "string"}],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "decimals",
|
||||
"outputs": [{"name": "", "type": "uint8"}],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [{"name": "_owner", "type": "address"}],
|
||||
"name": "balanceOf",
|
||||
"outputs": [{"name": "balance", "type": "uint256"}],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [{"name": "_to", "type": "address"}, {"name": "_value", "type": "uint256"}],
|
||||
"name": "transfer",
|
||||
"outputs": [{"name": "", "type": "bool"}],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
]`;
|
||||
|
||||
// For this example, we'll use a test token contract on Agung
|
||||
let token_address = "0x7267B587E4416537060C6bF0B06f6Fd421106650";
|
||||
|
||||
print("\nLoading contract ABI...");
|
||||
let contract = load_contract_abi(network_name, token_address, token_abi);
|
||||
|
||||
if contract != "" {
|
||||
print("✓ Contract loaded successfully");
|
||||
|
||||
// First, let's try to read some data from the contract
|
||||
print("\nReading contract data...");
|
||||
|
||||
// Try to get token name (no arguments)
|
||||
let token_name = call_contract_read(contract, "name");
|
||||
print("Token name: " + token_name);
|
||||
|
||||
// Try to get token symbol (no arguments)
|
||||
let token_symbol = call_contract_read(contract, "symbol");
|
||||
print("Token symbol: " + token_symbol);
|
||||
|
||||
// Try to get token decimals (no arguments)
|
||||
let token_decimals = call_contract_read(contract, "decimals");
|
||||
print("Token decimals: " + token_decimals);
|
||||
|
||||
// Try to get token balance (with address argument)
|
||||
print("\nCalling balanceOf with address argument...");
|
||||
let balance = call_contract_read(contract, "balanceOf", [wallet_address]);
|
||||
print("Token balance: " + balance);
|
||||
|
||||
// Now, let's try to execute a write function with arguments
|
||||
print("\nExecuting contract write function with arguments...");
|
||||
|
||||
// Define a recipient address and amount for the transfer
|
||||
// Using a random valid address on the network
|
||||
let recipient = "0xEEdf3468E8F232A7a03D49b674bA44740C8BD8Be";
|
||||
let amount = 1000000; // Changed from string to number for uint256 compatibility
|
||||
|
||||
print("Attempting to transfer " + amount + " tokens to " + recipient);
|
||||
|
||||
// Call the transfer function with arguments
|
||||
let tx_hash = call_contract_write(contract, "transfer", [recipient, amount]);
|
||||
|
||||
if tx_hash != "" {
|
||||
print("✓ Transaction sent successfully");
|
||||
print("Transaction hash: " + tx_hash);
|
||||
print("You can view the transaction at: " + get_network_explorer_url(network_name) + "/tx/" + tx_hash);
|
||||
} else {
|
||||
print("✗ Failed to send transaction");
|
||||
print("This could be due to insufficient funds, contract issues, or other errors.");
|
||||
}
|
||||
} else {
|
||||
print("✗ Failed to load contract");
|
||||
}
|
||||
} else {
|
||||
print("✗ Failed to get wallet balance");
|
||||
}
|
||||
} else {
|
||||
print("✗ Failed to create wallet from private key");
|
||||
}
|
||||
} else {
|
||||
print("✗ Failed to create keypair");
|
||||
}
|
||||
} else {
|
||||
print("✗ Failed to create key space");
|
||||
}
|
||||
|
||||
print("\nContract function with arguments test completed");
|
104
examples/scripts/hero_vault/_archive/agung_send_transaction.rhai
Normal file
104
examples/scripts/hero_vault/_archive/agung_send_transaction.rhai
Normal file
@ -0,0 +1,104 @@
|
||||
// Script to create an Agung wallet from a private key and send tokens
|
||||
// This script demonstrates how to create a wallet from a private key and send tokens
|
||||
|
||||
// Define the private key and recipient address
|
||||
let private_key = "0x9ecfd58eca522b0e7c109bf945966ee208cd6d593b1dc3378aedfdc60b64f512";
|
||||
let recipient_address = "0xf400f9c3F7317e19523a5DB698Ce67e7a7E083e2";
|
||||
|
||||
print("=== Agung Wallet Transaction Demo ===");
|
||||
print(`From private key: ${private_key}`);
|
||||
print(`To address: ${recipient_address}`);
|
||||
|
||||
// First, create a key space and keypair (required for the wallet infrastructure)
|
||||
let space_name = "agung_transaction_demo";
|
||||
let password = "demo_password";
|
||||
|
||||
// Create a new key space
|
||||
if !create_key_space(space_name, password) {
|
||||
print("Failed to create key space");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a keypair
|
||||
if !create_keypair("demo_keypair", password) {
|
||||
print("Failed to create keypair");
|
||||
return;
|
||||
}
|
||||
|
||||
// Select the keypair
|
||||
if !select_keypair("demo_keypair") {
|
||||
print("Failed to select keypair");
|
||||
return;
|
||||
}
|
||||
|
||||
print("\nCreated and selected keypair successfully");
|
||||
|
||||
// Clear any existing Agung wallets to avoid conflicts
|
||||
if clear_wallets_for_network("agung") {
|
||||
print("Cleared existing Agung wallets");
|
||||
} else {
|
||||
print("Failed to clear existing Agung wallets");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a wallet from the private key directly
|
||||
print("\n=== Creating Wallet from Private Key ===");
|
||||
|
||||
// Create a wallet from the private key for the Agung network
|
||||
if create_wallet_from_private_key_for_network(private_key, "agung") {
|
||||
print("Successfully created wallet from private key for Agung network");
|
||||
|
||||
// Get the wallet address
|
||||
let wallet_address = get_wallet_address_for_network("agung");
|
||||
print(`Wallet address: ${wallet_address}`);
|
||||
|
||||
// Create a provider for the Agung network
|
||||
let provider_id = create_agung_provider();
|
||||
if provider_id != "" {
|
||||
print("Successfully created Agung provider");
|
||||
|
||||
// Check the wallet balance first
|
||||
let wallet_address = get_wallet_address_for_network("agung");
|
||||
let balance_wei = get_balance("agung", wallet_address);
|
||||
|
||||
if balance_wei == "" {
|
||||
print("Failed to get wallet balance");
|
||||
print("This could be due to network issues or other errors.");
|
||||
return;
|
||||
}
|
||||
|
||||
print(`Current wallet balance: ${balance_wei} wei`);
|
||||
|
||||
// Convert 1 AGNG to wei (1 AGNG = 10^18 wei)
|
||||
// Use string representation for large numbers
|
||||
let amount_wei_str = "1000000000000000000"; // 1 AGNG in wei as a string
|
||||
|
||||
// Check if we have enough balance
|
||||
if parse_int(balance_wei) < parse_int(amount_wei_str) {
|
||||
print(`Insufficient balance to send ${amount_wei_str} wei (1 AGNG)`);
|
||||
print(`Current balance: ${balance_wei} wei`);
|
||||
print("Please fund the wallet before attempting to send a transaction");
|
||||
return;
|
||||
}
|
||||
|
||||
print(`Attempting to send ${amount_wei_str} wei (1 AGNG) to ${recipient_address}`);
|
||||
|
||||
// Send the transaction using the blocking implementation
|
||||
let tx_hash = send_eth("agung", recipient_address, amount_wei_str);
|
||||
|
||||
if tx_hash != "" {
|
||||
print(`Transaction sent with hash: ${tx_hash}`);
|
||||
print(`You can view the transaction at: ${get_network_explorer_url("agung")}/tx/${tx_hash}`);
|
||||
} else {
|
||||
print("Transaction failed");
|
||||
print("This could be due to insufficient funds, network issues, or other errors.");
|
||||
print("Check the logs for more details.");
|
||||
}
|
||||
} else {
|
||||
print("Failed to create Agung provider");
|
||||
}
|
||||
} else {
|
||||
print("Failed to create wallet from private key");
|
||||
}
|
||||
|
||||
print("\nAgung transaction demo completed");
|
98
examples/scripts/hero_vault/_archive/contract_example.rhai
Normal file
98
examples/scripts/hero_vault/_archive/contract_example.rhai
Normal file
@ -0,0 +1,98 @@
|
||||
// Example Rhai script for interacting with smart contracts using Hero Vault
|
||||
// This script demonstrates loading a contract ABI and interacting with a contract
|
||||
|
||||
// Step 1: Set up wallet and network
|
||||
let space_name = "contract_demo_space";
|
||||
let password = "secure_password123";
|
||||
|
||||
print("Creating key space: " + space_name);
|
||||
if create_key_space(space_name, password) {
|
||||
print("✓ Key space created successfully");
|
||||
|
||||
// Create a keypair
|
||||
print("\nCreating keypair...");
|
||||
if create_keypair("contract_key", password) {
|
||||
print("✓ Created contract keypair");
|
||||
}
|
||||
|
||||
// Step 2: Create an Ethereum wallet for Gnosis Chain
|
||||
print("\nCreating Ethereum wallet...");
|
||||
if create_ethereum_wallet() {
|
||||
print("✓ Ethereum wallet created");
|
||||
|
||||
let address = get_ethereum_address();
|
||||
print("Ethereum address: " + address);
|
||||
|
||||
// Step 3: Define a simple ERC-20 ABI (partial)
|
||||
let erc20_abi = `[
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "name",
|
||||
"outputs": [{"name": "", "type": "string"}],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "symbol",
|
||||
"outputs": [{"name": "", "type": "string"}],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "decimals",
|
||||
"outputs": [{"name": "", "type": "uint8"}],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [{"name": "owner", "type": "address"}],
|
||||
"name": "balanceOf",
|
||||
"outputs": [{"name": "", "type": "uint256"}],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]`;
|
||||
|
||||
// Step 4: Load the contract ABI
|
||||
print("\nLoading contract ABI...");
|
||||
let contract = load_contract_abi("Gnosis", "0x4ECaBa5870353805a9F068101A40E0f32ed605C6", erc20_abi);
|
||||
if contract != "" {
|
||||
print("✓ Contract loaded successfully");
|
||||
|
||||
// Step 5: Call read-only functions
|
||||
print("\nCalling read-only functions...");
|
||||
|
||||
// Get token name
|
||||
let token_name = call_contract_read(contract, "name");
|
||||
print("Token name: " + token_name);
|
||||
|
||||
// Get token symbol
|
||||
let token_symbol = call_contract_read(contract, "symbol");
|
||||
print("Token symbol: " + token_symbol);
|
||||
|
||||
// Get token decimals
|
||||
let token_decimals = call_contract_read(contract, "decimals");
|
||||
print("Token decimals: " + token_decimals);
|
||||
|
||||
// For now, we're just demonstrating the basic structure
|
||||
} else {
|
||||
print("✗ Failed to load contract");
|
||||
}
|
||||
} else {
|
||||
print("✗ Failed to create Ethereum wallet");
|
||||
}
|
||||
} else {
|
||||
print("✗ Failed to create key space");
|
||||
}
|
||||
|
||||
print("\nContract example completed");
|
85
examples/scripts/hero_vault/_archive/example.rhai
Normal file
85
examples/scripts/hero_vault/_archive/example.rhai
Normal file
@ -0,0 +1,85 @@
|
||||
// Example Rhai script for Hero Vault Cryptography Module
|
||||
// This script demonstrates key management, signing, and encryption
|
||||
|
||||
// Step 1: Create and manage a key space
|
||||
let space_name = "demo_space";
|
||||
let password = "secure_password123";
|
||||
|
||||
print("Creating key space: " + space_name);
|
||||
if create_key_space(space_name, password) {
|
||||
print("✓ Key space created successfully");
|
||||
|
||||
// Step 2: Create and use keypairs
|
||||
print("\nCreating keypairs...");
|
||||
if create_keypair("signing_key", password) {
|
||||
print("✓ Created signing keypair");
|
||||
}
|
||||
|
||||
if create_keypair("encryption_key", password) {
|
||||
print("✓ Created encryption keypair");
|
||||
}
|
||||
|
||||
// List all keypairs
|
||||
let keypairs = list_keypairs();
|
||||
print("Available keypairs: " + keypairs);
|
||||
|
||||
// Step 3: Sign a message
|
||||
print("\nPerforming signing operations...");
|
||||
if select_keypair("signing_key") {
|
||||
print("✓ Selected signing keypair");
|
||||
|
||||
let message = "This is a secure message that needs to be signed";
|
||||
print("Message: " + message);
|
||||
|
||||
let signature = sign(message);
|
||||
print("Signature: " + signature);
|
||||
|
||||
// Verify the signature
|
||||
let is_valid = verify(message, signature);
|
||||
if is_valid {
|
||||
print("Signature verification: ✓ Valid");
|
||||
} else {
|
||||
print("Signature verification: ✗ Invalid");
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Encrypt and decrypt data
|
||||
print("\nPerforming encryption operations...");
|
||||
|
||||
// Generate a symmetric key
|
||||
let sym_key = generate_key();
|
||||
print("Generated symmetric key: " + sym_key);
|
||||
|
||||
// Encrypt a message
|
||||
let secret = "This is a top secret message that must be encrypted";
|
||||
print("Original message: " + secret);
|
||||
|
||||
let encrypted_data = encrypt(sym_key, secret);
|
||||
print("Encrypted data: " + encrypted_data);
|
||||
|
||||
// Decrypt the message
|
||||
let decrypted_data = decrypt(sym_key, encrypted_data);
|
||||
print("Decrypted message: " + decrypted_data);
|
||||
|
||||
// Verify decryption was successful
|
||||
if decrypted_data == secret {
|
||||
print("✓ Encryption/decryption successful");
|
||||
} else {
|
||||
print("✗ Encryption/decryption failed");
|
||||
}
|
||||
|
||||
// Step 5: Create an Ethereum wallet
|
||||
print("\nCreating Ethereum wallet...");
|
||||
if select_keypair("encryption_key") {
|
||||
print("✓ Selected keypair for Ethereum wallet");
|
||||
|
||||
if create_ethereum_wallet() {
|
||||
print("✓ Ethereum wallet created");
|
||||
|
||||
let address = get_ethereum_address();
|
||||
print("Ethereum address: " + address);
|
||||
}
|
||||
}
|
||||
|
||||
print("\nScript execution completed successfully!");
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
// Example Rhai script demonstrating key space persistence for Hero Vault
|
||||
// This script shows how to create, save, and load key spaces
|
||||
|
||||
// Step 1: Create a key space
|
||||
let space_name = "persistent_space";
|
||||
let password = "secure_password123";
|
||||
|
||||
print("Creating key space: " + space_name);
|
||||
if create_key_space(space_name, password) {
|
||||
print("✓ Key space created successfully");
|
||||
|
||||
// Step 2: Create keypairs in this space
|
||||
print("\nCreating keypairs...");
|
||||
if create_keypair("persistent_key1", password) {
|
||||
print("✓ Created first keypair");
|
||||
}
|
||||
|
||||
if create_keypair("persistent_key2", password) {
|
||||
print("✓ Created second keypair");
|
||||
}
|
||||
|
||||
// List all keypairs
|
||||
let keypairs = list_keypairs();
|
||||
print("Available keypairs: " + keypairs);
|
||||
|
||||
// Step 3: Clear the session (simulate closing and reopening the CLI)
|
||||
print("\nClearing session (simulating restart)...");
|
||||
// Note: In a real script, you would exit here and run a new script
|
||||
// For demonstration purposes, we'll continue in the same script
|
||||
|
||||
// Step 4: Load the key space from disk
|
||||
print("\nLoading key space from disk...");
|
||||
if load_key_space(space_name, password) {
|
||||
print("✓ Key space loaded successfully");
|
||||
|
||||
// Verify the keypairs are still available
|
||||
let loaded_keypairs = list_keypairs();
|
||||
print("Keypairs after loading: " + loaded_keypairs);
|
||||
|
||||
// Step 5: Use a keypair from the loaded space
|
||||
print("\nSelecting and using a keypair...");
|
||||
if select_keypair("persistent_key1") {
|
||||
print("✓ Selected keypair");
|
||||
|
||||
let message = "This message was signed using a keypair from a loaded key space";
|
||||
let signature = sign(message);
|
||||
print("Message: " + message);
|
||||
print("Signature: " + signature);
|
||||
|
||||
// Verify the signature
|
||||
let is_valid = verify(message, signature);
|
||||
if is_valid {
|
||||
print("Signature verification: ✓ Valid");
|
||||
} else {
|
||||
print("Signature verification: ✗ Invalid");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("✗ Failed to load key space");
|
||||
}
|
||||
} else {
|
||||
print("✗ Failed to create key space");
|
||||
}
|
||||
|
||||
print("\nScript execution completed!");
|
@ -0,0 +1,65 @@
|
||||
// Example Rhai script demonstrating loading an existing key space for Hero Vault
|
||||
// This script shows how to load a previously created key space and use its keypairs
|
||||
|
||||
// Define the key space name and password
|
||||
let space_name = "persistent_space";
|
||||
let password = "secure_password123";
|
||||
|
||||
print("Loading existing key space: " + space_name);
|
||||
|
||||
// Load the key space from disk
|
||||
if load_key_space(space_name, password) {
|
||||
print("✓ Key space loaded successfully");
|
||||
|
||||
// List available keypairs
|
||||
let keypairs = list_keypairs();
|
||||
print("Available keypairs: " + keypairs);
|
||||
|
||||
// Use both keypairs to sign different messages
|
||||
if select_keypair("persistent_key1") {
|
||||
print("\nUsing persistent_key1:");
|
||||
let message1 = "Message signed with the first keypair";
|
||||
let signature1 = sign(message1);
|
||||
print("Message: " + message1);
|
||||
print("Signature: " + signature1);
|
||||
|
||||
let is_valid1 = verify(message1, signature1);
|
||||
if is_valid1 {
|
||||
print("Verification: ✓ Valid");
|
||||
} else {
|
||||
print("Verification: ✗ Invalid");
|
||||
}
|
||||
}
|
||||
|
||||
if select_keypair("persistent_key2") {
|
||||
print("\nUsing persistent_key2:");
|
||||
let message2 = "Message signed with the second keypair";
|
||||
let signature2 = sign(message2);
|
||||
print("Message: " + message2);
|
||||
print("Signature: " + signature2);
|
||||
|
||||
let is_valid2 = verify(message2, signature2);
|
||||
if is_valid2 {
|
||||
print("Verification: ✓ Valid");
|
||||
} else {
|
||||
print("Verification: ✗ Invalid");
|
||||
}
|
||||
}
|
||||
|
||||
// Create an Ethereum wallet using one of the keypairs
|
||||
print("\nCreating Ethereum wallet from persistent keypair:");
|
||||
if select_keypair("persistent_key1") {
|
||||
if create_ethereum_wallet() {
|
||||
print("✓ Ethereum wallet created");
|
||||
|
||||
let address = get_ethereum_address();
|
||||
print("Ethereum address: " + address);
|
||||
} else {
|
||||
print("✗ Failed to create Ethereum wallet");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("✗ Failed to load key space. Make sure you've run key_persistence_example.rhai first.");
|
||||
}
|
||||
|
||||
print("\nScript execution completed!");
|
72
examples/scripts/kubernetes/basic_operations.rhai
Normal file
72
examples/scripts/kubernetes/basic_operations.rhai
Normal file
@ -0,0 +1,72 @@
|
||||
//! Basic Kubernetes operations example
|
||||
//!
|
||||
//! This script demonstrates basic Kubernetes operations using the SAL Kubernetes module.
|
||||
//!
|
||||
//! Prerequisites:
|
||||
//! - A running Kubernetes cluster
|
||||
//! - Valid kubeconfig file or in-cluster configuration
|
||||
//! - Appropriate permissions for the operations
|
||||
//!
|
||||
//! Usage:
|
||||
//! herodo examples/kubernetes/basic_operations.rhai
|
||||
|
||||
print("=== SAL Kubernetes Basic Operations Example ===");
|
||||
|
||||
// Create a KubernetesManager for the default namespace
|
||||
print("Creating KubernetesManager for 'default' namespace...");
|
||||
let km = kubernetes_manager_new("default");
|
||||
print("✓ KubernetesManager created for namespace: " + namespace(km));
|
||||
|
||||
// List all pods in the namespace
|
||||
print("\n--- Listing Pods ---");
|
||||
let pods = pods_list(km);
|
||||
print("Found " + pods.len() + " pods in the namespace:");
|
||||
for pod in pods {
|
||||
print(" - " + pod);
|
||||
}
|
||||
|
||||
// List all services in the namespace
|
||||
print("\n--- Listing Services ---");
|
||||
let services = services_list(km);
|
||||
print("Found " + services.len() + " services in the namespace:");
|
||||
for service in services {
|
||||
print(" - " + service);
|
||||
}
|
||||
|
||||
// List all deployments in the namespace
|
||||
print("\n--- Listing Deployments ---");
|
||||
let deployments = deployments_list(km);
|
||||
print("Found " + deployments.len() + " deployments in the namespace:");
|
||||
for deployment in deployments {
|
||||
print(" - " + deployment);
|
||||
}
|
||||
|
||||
// Get resource counts
|
||||
print("\n--- Resource Counts ---");
|
||||
let counts = resource_counts(km);
|
||||
print("Resource counts in namespace '" + namespace(km) + "':");
|
||||
for resource_type in counts.keys() {
|
||||
print(" " + resource_type + ": " + counts[resource_type]);
|
||||
}
|
||||
|
||||
// List all namespaces (cluster-wide operation)
|
||||
print("\n--- Listing All Namespaces ---");
|
||||
let namespaces = namespaces_list(km);
|
||||
print("Found " + namespaces.len() + " namespaces in the cluster:");
|
||||
for ns in namespaces {
|
||||
print(" - " + ns);
|
||||
}
|
||||
|
||||
// Check if specific namespaces exist
|
||||
print("\n--- Checking Namespace Existence ---");
|
||||
let test_namespaces = ["default", "kube-system", "non-existent-namespace"];
|
||||
for ns in test_namespaces {
|
||||
let exists = namespace_exists(km, ns);
|
||||
if exists {
|
||||
print("✓ Namespace '" + ns + "' exists");
|
||||
} else {
|
||||
print("✗ Namespace '" + ns + "' does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
print("\n=== Example completed successfully! ===");
|
134
examples/scripts/kubernetes/clusters/generic.rs
Normal file
134
examples/scripts/kubernetes/clusters/generic.rs
Normal file
@ -0,0 +1,134 @@
|
||||
//! 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?;
|
||||
|
||||
// Clean up any existing resources first
|
||||
println!("=== Cleaning up existing resources ===");
|
||||
let apps_to_clean = ["web-server", "node-app", "mongodb"];
|
||||
|
||||
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?;
|
||||
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());
|
||||
|
||||
// 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(
|
||||
"node-app", // name
|
||||
"node:18-alpine", // image
|
||||
3, // replicas - scale to 3 instances
|
||||
3000, // port
|
||||
Some(node_labels), // labels
|
||||
Some(node_env_vars), // environment variables
|
||||
)
|
||||
.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());
|
||||
|
||||
// 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(
|
||||
"mongodb", // name
|
||||
"mongo:6.0", // image
|
||||
1, // replicas - single instance for simplicity
|
||||
27017, // port
|
||||
Some(mongo_labels), // labels
|
||||
Some(mongo_env_vars), // environment variables
|
||||
)
|
||||
.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(())
|
||||
}
|
79
examples/scripts/kubernetes/clusters/postgres.rhai
Normal file
79
examples/scripts/kubernetes/clusters/postgres.rhai
Normal file
@ -0,0 +1,79 @@
|
||||
//! PostgreSQL Cluster Deployment Example (Rhai)
|
||||
//!
|
||||
//! This script shows how to deploy a PostgreSQL cluster using Rhai scripting
|
||||
//! with the KubernetesManager convenience methods.
|
||||
|
||||
print("=== PostgreSQL Cluster Deployment ===");
|
||||
|
||||
// Create Kubernetes manager for the database namespace
|
||||
print("Creating Kubernetes manager for 'database' namespace...");
|
||||
let km = kubernetes_manager_new("database");
|
||||
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
|
||||
print("\nDeploying PostgreSQL cluster...");
|
||||
|
||||
try {
|
||||
// Deploy PostgreSQL using the convenience method
|
||||
let result = deploy_application(km, "postgres-cluster", "postgres:15", 2, 5432, #{
|
||||
"app": "postgres-cluster",
|
||||
"type": "database",
|
||||
"engine": "postgresql"
|
||||
}, #{
|
||||
"POSTGRES_DB": "myapp",
|
||||
"POSTGRES_USER": "postgres",
|
||||
"POSTGRES_PASSWORD": "secretpassword",
|
||||
"PGDATA": "/var/lib/postgresql/data/pgdata"
|
||||
});
|
||||
print("✓ " + result);
|
||||
|
||||
print("\n✅ PostgreSQL cluster deployed successfully!");
|
||||
|
||||
print("\n📋 Connection Information:");
|
||||
print(" Host: postgres-cluster.database.svc.cluster.local");
|
||||
print(" Port: 5432");
|
||||
print(" Database: postgres (default)");
|
||||
print(" Username: postgres (default)");
|
||||
|
||||
print("\n🔧 To connect from another pod:");
|
||||
print(" psql -h postgres-cluster.database.svc.cluster.local -U postgres");
|
||||
|
||||
print("\n💡 Next steps:");
|
||||
print(" • Set POSTGRES_PASSWORD environment variable");
|
||||
print(" • Configure persistent storage");
|
||||
print(" • Set up backup and monitoring");
|
||||
|
||||
} catch(e) {
|
||||
print("❌ Failed to deploy PostgreSQL cluster: " + e);
|
||||
}
|
||||
|
||||
print("\n=== Deployment Complete ===");
|
112
examples/scripts/kubernetes/clusters/postgres.rs
Normal file
112
examples/scripts/kubernetes/clusters/postgres.rs
Normal file
@ -0,0 +1,112 @@
|
||||
//! 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?;
|
||||
|
||||
// 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
|
||||
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());
|
||||
|
||||
// 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
|
||||
println!("Deploying PostgreSQL cluster...");
|
||||
km.deploy_application(
|
||||
"postgres-cluster", // name
|
||||
"postgres:15", // image
|
||||
2, // replicas (1 master + 1 replica)
|
||||
5432, // port
|
||||
Some(labels), // labels
|
||||
Some(env_vars), // environment variables
|
||||
)
|
||||
.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(())
|
||||
}
|
79
examples/scripts/kubernetes/clusters/redis.rhai
Normal file
79
examples/scripts/kubernetes/clusters/redis.rhai
Normal file
@ -0,0 +1,79 @@
|
||||
//! Redis Cluster Deployment Example (Rhai)
|
||||
//!
|
||||
//! This script shows how to deploy a Redis cluster using Rhai scripting
|
||||
//! with the KubernetesManager convenience methods.
|
||||
|
||||
print("=== Redis Cluster Deployment ===");
|
||||
|
||||
// Create Kubernetes manager for the cache namespace
|
||||
print("Creating Kubernetes manager for 'cache' namespace...");
|
||||
let km = kubernetes_manager_new("cache");
|
||||
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
|
||||
print("\nDeploying Redis cluster...");
|
||||
|
||||
try {
|
||||
// Deploy Redis using the convenience method
|
||||
let result = deploy_application(km, "redis-cluster", "redis:7-alpine", 3, 6379, #{
|
||||
"app": "redis-cluster",
|
||||
"type": "cache",
|
||||
"engine": "redis"
|
||||
}, #{
|
||||
"REDIS_PASSWORD": "redispassword",
|
||||
"REDIS_PORT": "6379",
|
||||
"REDIS_DATABASES": "16",
|
||||
"REDIS_MAXMEMORY": "256mb",
|
||||
"REDIS_MAXMEMORY_POLICY": "allkeys-lru"
|
||||
});
|
||||
print("✓ " + result);
|
||||
|
||||
print("\n✅ Redis cluster deployed successfully!");
|
||||
|
||||
print("\n📋 Connection Information:");
|
||||
print(" Host: redis-cluster.cache.svc.cluster.local");
|
||||
print(" Port: 6379");
|
||||
|
||||
print("\n🔧 To connect from another pod:");
|
||||
print(" redis-cli -h redis-cluster.cache.svc.cluster.local");
|
||||
|
||||
print("\n💡 Next steps:");
|
||||
print(" • Configure Redis authentication");
|
||||
print(" • Set up Redis clustering configuration");
|
||||
print(" • Add persistent storage");
|
||||
print(" • Configure memory policies");
|
||||
|
||||
} catch(e) {
|
||||
print("❌ Failed to deploy Redis cluster: " + e);
|
||||
}
|
||||
|
||||
print("\n=== Deployment Complete ===");
|
109
examples/scripts/kubernetes/clusters/redis.rs
Normal file
109
examples/scripts/kubernetes/clusters/redis.rs
Normal file
@ -0,0 +1,109 @@
|
||||
//! 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?;
|
||||
|
||||
// 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
|
||||
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());
|
||||
|
||||
// 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
|
||||
println!("Deploying Redis cluster...");
|
||||
km.deploy_application(
|
||||
"redis-cluster", // name
|
||||
"redis:7-alpine", // image
|
||||
3, // replicas (Redis cluster nodes)
|
||||
6379, // port
|
||||
Some(labels), // labels
|
||||
Some(env_vars), // environment variables
|
||||
)
|
||||
.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(())
|
||||
}
|
208
examples/scripts/kubernetes/multi_namespace_operations.rhai
Normal file
208
examples/scripts/kubernetes/multi_namespace_operations.rhai
Normal file
@ -0,0 +1,208 @@
|
||||
//! Multi-namespace Kubernetes operations example
|
||||
//!
|
||||
//! This script demonstrates working with multiple namespaces and comparing resources across them.
|
||||
//!
|
||||
//! Prerequisites:
|
||||
//! - A running Kubernetes cluster
|
||||
//! - Valid kubeconfig file or in-cluster configuration
|
||||
//! - Appropriate permissions for the operations
|
||||
//!
|
||||
//! Usage:
|
||||
//! herodo examples/kubernetes/multi_namespace_operations.rhai
|
||||
|
||||
print("=== SAL Kubernetes Multi-Namespace Operations Example ===");
|
||||
|
||||
// Define namespaces to work with
|
||||
let target_namespaces = ["default", "kube-system"];
|
||||
let managers = #{};
|
||||
|
||||
print("Creating managers for multiple namespaces...");
|
||||
|
||||
// Create managers for each namespace
|
||||
for ns in target_namespaces {
|
||||
try {
|
||||
let km = kubernetes_manager_new(ns);
|
||||
managers[ns] = km;
|
||||
print("✓ Created manager for namespace: " + ns);
|
||||
} catch(e) {
|
||||
print("✗ Failed to create manager for " + ns + ": " + e);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to safely get resource counts
|
||||
fn get_safe_counts(km) {
|
||||
try {
|
||||
return resource_counts(km);
|
||||
} catch(e) {
|
||||
print(" Warning: Could not get resource counts - " + e);
|
||||
return #{};
|
||||
}
|
||||
}
|
||||
|
||||
// Function to safely get pod list
|
||||
fn get_safe_pods(km) {
|
||||
try {
|
||||
return pods_list(km);
|
||||
} catch(e) {
|
||||
print(" Warning: Could not list pods - " + e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Compare resource counts across namespaces
|
||||
print("\n--- Resource Comparison Across Namespaces ---");
|
||||
let total_resources = #{};
|
||||
|
||||
for ns in target_namespaces {
|
||||
if ns in managers {
|
||||
let km = managers[ns];
|
||||
print("\nNamespace: " + ns);
|
||||
let counts = get_safe_counts(km);
|
||||
|
||||
for resource_type in counts.keys() {
|
||||
let count = counts[resource_type];
|
||||
print(" " + resource_type + ": " + count);
|
||||
|
||||
// Accumulate totals
|
||||
if resource_type in total_resources {
|
||||
total_resources[resource_type] = total_resources[resource_type] + count;
|
||||
} else {
|
||||
total_resources[resource_type] = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("\n--- Total Resources Across All Namespaces ---");
|
||||
for resource_type in total_resources.keys() {
|
||||
print("Total " + resource_type + ": " + total_resources[resource_type]);
|
||||
}
|
||||
|
||||
// Find namespaces with the most resources
|
||||
print("\n--- Namespace Resource Analysis ---");
|
||||
let namespace_totals = #{};
|
||||
|
||||
for ns in target_namespaces {
|
||||
if ns in managers {
|
||||
let km = managers[ns];
|
||||
let counts = get_safe_counts(km);
|
||||
let total = 0;
|
||||
|
||||
for resource_type in counts.keys() {
|
||||
total = total + counts[resource_type];
|
||||
}
|
||||
|
||||
namespace_totals[ns] = total;
|
||||
print("Namespace '" + ns + "' has " + total + " total resources");
|
||||
}
|
||||
}
|
||||
|
||||
// Find the busiest namespace
|
||||
let busiest_ns = "";
|
||||
let max_resources = 0;
|
||||
for ns in namespace_totals.keys() {
|
||||
if namespace_totals[ns] > max_resources {
|
||||
max_resources = namespace_totals[ns];
|
||||
busiest_ns = ns;
|
||||
}
|
||||
}
|
||||
|
||||
if busiest_ns != "" {
|
||||
print("🏆 Busiest namespace: '" + busiest_ns + "' with " + max_resources + " resources");
|
||||
}
|
||||
|
||||
// Detailed pod analysis
|
||||
print("\n--- Pod Analysis Across Namespaces ---");
|
||||
let all_pods = [];
|
||||
|
||||
for ns in target_namespaces {
|
||||
if ns in managers {
|
||||
let km = managers[ns];
|
||||
let pods = get_safe_pods(km);
|
||||
|
||||
print("\nNamespace '" + ns + "' pods:");
|
||||
if pods.len() == 0 {
|
||||
print(" (no pods)");
|
||||
} else {
|
||||
for pod in pods {
|
||||
print(" - " + pod);
|
||||
all_pods.push(ns + "/" + pod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("\n--- All Pods Summary ---");
|
||||
print("Total pods across all namespaces: " + all_pods.len());
|
||||
|
||||
// Look for common pod name patterns
|
||||
print("\n--- Pod Name Pattern Analysis ---");
|
||||
let patterns = #{
|
||||
"system": 0,
|
||||
"kube": 0,
|
||||
"coredns": 0,
|
||||
"proxy": 0,
|
||||
"controller": 0
|
||||
};
|
||||
|
||||
for pod_full_name in all_pods {
|
||||
let pod_name = pod_full_name.to_lower();
|
||||
|
||||
for pattern in patterns.keys() {
|
||||
if pod_name.contains(pattern) {
|
||||
patterns[pattern] = patterns[pattern] + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("Common pod name patterns found:");
|
||||
for pattern in patterns.keys() {
|
||||
if patterns[pattern] > 0 {
|
||||
print(" '" + pattern + "': " + patterns[pattern] + " pods");
|
||||
}
|
||||
}
|
||||
|
||||
// Namespace health check
|
||||
print("\n--- Namespace Health Check ---");
|
||||
for ns in target_namespaces {
|
||||
if ns in managers {
|
||||
let km = managers[ns];
|
||||
print("\nChecking namespace: " + ns);
|
||||
|
||||
// Check if namespace exists (should always be true for our managers)
|
||||
let exists = namespace_exists(km, ns);
|
||||
if exists {
|
||||
print(" ✓ Namespace exists and is accessible");
|
||||
} else {
|
||||
print(" ✗ Namespace existence check failed");
|
||||
}
|
||||
|
||||
// Try to get resource counts as a health indicator
|
||||
let counts = get_safe_counts(km);
|
||||
if counts.len() > 0 {
|
||||
print(" ✓ Can access resources (" + counts.len() + " resource types)");
|
||||
} else {
|
||||
print(" ⚠ No resources found or access limited");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a summary report
|
||||
print("\n--- Summary Report ---");
|
||||
print("Namespaces analyzed: " + target_namespaces.len());
|
||||
print("Total unique resource types: " + total_resources.len());
|
||||
|
||||
let grand_total = 0;
|
||||
for resource_type in total_resources.keys() {
|
||||
grand_total = grand_total + total_resources[resource_type];
|
||||
}
|
||||
print("Grand total resources: " + grand_total);
|
||||
|
||||
print("\nResource breakdown:");
|
||||
for resource_type in total_resources.keys() {
|
||||
let count = total_resources[resource_type];
|
||||
let percentage = (count * 100) / grand_total;
|
||||
print(" " + resource_type + ": " + count + " (" + percentage + "%)");
|
||||
}
|
||||
|
||||
print("\n=== Multi-namespace operations example completed! ===");
|
95
examples/scripts/kubernetes/namespace_management.rhai
Normal file
95
examples/scripts/kubernetes/namespace_management.rhai
Normal file
@ -0,0 +1,95 @@
|
||||
//! Kubernetes namespace management example
|
||||
//!
|
||||
//! This script demonstrates namespace creation and management operations.
|
||||
//!
|
||||
//! Prerequisites:
|
||||
//! - A running Kubernetes cluster
|
||||
//! - Valid kubeconfig file or in-cluster configuration
|
||||
//! - Permissions to create and manage namespaces
|
||||
//!
|
||||
//! Usage:
|
||||
//! herodo examples/kubernetes/namespace_management.rhai
|
||||
|
||||
print("=== SAL Kubernetes Namespace Management Example ===");
|
||||
|
||||
// Create a KubernetesManager
|
||||
let km = kubernetes_manager_new("default");
|
||||
print("Created KubernetesManager for namespace: " + namespace(km));
|
||||
|
||||
// Define test namespace names
|
||||
let test_namespaces = [
|
||||
"sal-test-namespace-1",
|
||||
"sal-test-namespace-2",
|
||||
"sal-example-app"
|
||||
];
|
||||
|
||||
print("\n--- Creating Test Namespaces ---");
|
||||
for ns in test_namespaces {
|
||||
print("Creating namespace: " + ns);
|
||||
try {
|
||||
namespace_create(km, ns);
|
||||
print("✓ Successfully created namespace: " + ns);
|
||||
} catch(e) {
|
||||
print("✗ Failed to create namespace " + ns + ": " + e);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait a moment for namespaces to be created
|
||||
print("\nWaiting for namespaces to be ready...");
|
||||
|
||||
// Verify namespaces were created
|
||||
print("\n--- Verifying Namespace Creation ---");
|
||||
for ns in test_namespaces {
|
||||
let exists = namespace_exists(km, ns);
|
||||
if exists {
|
||||
print("✓ Namespace '" + ns + "' exists");
|
||||
} else {
|
||||
print("✗ Namespace '" + ns + "' was not found");
|
||||
}
|
||||
}
|
||||
|
||||
// List all namespaces to see our new ones
|
||||
print("\n--- Current Namespaces ---");
|
||||
let all_namespaces = namespaces_list(km);
|
||||
print("Total namespaces in cluster: " + all_namespaces.len());
|
||||
for ns in all_namespaces {
|
||||
if ns.starts_with("sal-") {
|
||||
print(" 🔹 " + ns + " (created by this example)");
|
||||
} else {
|
||||
print(" - " + ns);
|
||||
}
|
||||
}
|
||||
|
||||
// Test idempotent creation (creating the same namespace again)
|
||||
print("\n--- Testing Idempotent Creation ---");
|
||||
let test_ns = test_namespaces[0];
|
||||
print("Attempting to create existing namespace: " + test_ns);
|
||||
try {
|
||||
namespace_create(km, test_ns);
|
||||
print("✓ Idempotent creation successful (no error for existing namespace)");
|
||||
} catch(e) {
|
||||
print("✗ Unexpected error during idempotent creation: " + e);
|
||||
}
|
||||
|
||||
// Create managers for the new namespaces and check their properties
|
||||
print("\n--- Creating Managers for New Namespaces ---");
|
||||
for ns in test_namespaces {
|
||||
try {
|
||||
let ns_km = kubernetes_manager_new(ns);
|
||||
print("✓ Created manager for namespace: " + namespace(ns_km));
|
||||
|
||||
// Get resource counts for the new namespace (should be mostly empty)
|
||||
let counts = resource_counts(ns_km);
|
||||
print(" Resource counts: " + counts);
|
||||
} catch(e) {
|
||||
print("✗ Failed to create manager for " + ns + ": " + e);
|
||||
}
|
||||
}
|
||||
|
||||
print("\n--- Cleanup Instructions ---");
|
||||
print("To clean up the test namespaces created by this example, run:");
|
||||
for ns in test_namespaces {
|
||||
print(" kubectl delete namespace " + ns);
|
||||
}
|
||||
|
||||
print("\n=== Namespace management example completed! ===");
|
157
examples/scripts/kubernetes/pattern_deletion.rhai
Normal file
157
examples/scripts/kubernetes/pattern_deletion.rhai
Normal file
@ -0,0 +1,157 @@
|
||||
//! Kubernetes pattern-based deletion example
|
||||
//!
|
||||
//! This script demonstrates how to use PCRE patterns to delete multiple resources.
|
||||
//!
|
||||
//! ⚠️ WARNING: This example includes actual deletion operations!
|
||||
//! ⚠️ Only run this in a test environment!
|
||||
//!
|
||||
//! Prerequisites:
|
||||
//! - A running Kubernetes cluster (preferably a test cluster)
|
||||
//! - Valid kubeconfig file or in-cluster configuration
|
||||
//! - Permissions to delete resources
|
||||
//!
|
||||
//! Usage:
|
||||
//! herodo examples/kubernetes/pattern_deletion.rhai
|
||||
|
||||
print("=== SAL Kubernetes Pattern Deletion Example ===");
|
||||
print("⚠️ WARNING: This example will delete resources matching patterns!");
|
||||
print("⚠️ Only run this in a test environment!");
|
||||
|
||||
// Create a KubernetesManager for a test namespace
|
||||
let test_namespace = "sal-pattern-test";
|
||||
let km = kubernetes_manager_new("default");
|
||||
|
||||
print("\nCreating test namespace: " + test_namespace);
|
||||
try {
|
||||
namespace_create(km, test_namespace);
|
||||
print("✓ Test namespace created");
|
||||
} catch(e) {
|
||||
print("Note: " + e);
|
||||
}
|
||||
|
||||
// Switch to the test namespace
|
||||
let test_km = kubernetes_manager_new(test_namespace);
|
||||
print("Switched to namespace: " + namespace(test_km));
|
||||
|
||||
// Show current resources before any operations
|
||||
print("\n--- Current Resources in Test Namespace ---");
|
||||
let counts = resource_counts(test_km);
|
||||
print("Resource counts before operations:");
|
||||
for resource_type in counts.keys() {
|
||||
print(" " + resource_type + ": " + counts[resource_type]);
|
||||
}
|
||||
|
||||
// List current pods to see what we're working with
|
||||
let current_pods = pods_list(test_km);
|
||||
print("\nCurrent pods in namespace:");
|
||||
if current_pods.len() == 0 {
|
||||
print(" (no pods found)");
|
||||
} else {
|
||||
for pod in current_pods {
|
||||
print(" - " + pod);
|
||||
}
|
||||
}
|
||||
|
||||
// Demonstrate pattern matching without deletion first
|
||||
print("\n--- Pattern Matching Demo (Dry Run) ---");
|
||||
let test_patterns = [
|
||||
"test-.*", // Match anything starting with "test-"
|
||||
".*-temp$", // Match anything ending with "-temp"
|
||||
"demo-pod-.*", // Match demo pods
|
||||
"nginx-.*", // Match nginx pods
|
||||
"app-[0-9]+", // Match app-1, app-2, etc.
|
||||
];
|
||||
|
||||
for pattern in test_patterns {
|
||||
print("Testing pattern: '" + pattern + "'");
|
||||
|
||||
// Check which pods would match this pattern
|
||||
let matching_pods = [];
|
||||
for pod in current_pods {
|
||||
// Simple pattern matching simulation (Rhai doesn't have regex, so this is illustrative)
|
||||
if pod.contains("test") && pattern == "test-.*" {
|
||||
matching_pods.push(pod);
|
||||
} else if pod.contains("temp") && pattern == ".*-temp$" {
|
||||
matching_pods.push(pod);
|
||||
} else if pod.contains("demo") && pattern == "demo-pod-.*" {
|
||||
matching_pods.push(pod);
|
||||
} else if pod.contains("nginx") && pattern == "nginx-.*" {
|
||||
matching_pods.push(pod);
|
||||
}
|
||||
}
|
||||
|
||||
print(" Would match " + matching_pods.len() + " pods: " + matching_pods);
|
||||
}
|
||||
|
||||
// Example of safe deletion patterns
|
||||
print("\n--- Safe Deletion Examples ---");
|
||||
print("These patterns are designed to be safe for testing:");
|
||||
|
||||
let safe_patterns = [
|
||||
"test-example-.*", // Very specific test resources
|
||||
"sal-demo-.*", // SAL demo resources
|
||||
"temp-resource-.*", // Temporary resources
|
||||
];
|
||||
|
||||
for pattern in safe_patterns {
|
||||
print("\nTesting safe pattern: '" + pattern + "'");
|
||||
|
||||
try {
|
||||
// This will actually attempt deletion, but should be safe in a test environment
|
||||
let deleted_count = delete(test_km, pattern);
|
||||
print("✓ Pattern '" + pattern + "' matched and deleted " + deleted_count + " resources");
|
||||
} catch(e) {
|
||||
print("Note: Pattern '" + pattern + "' - " + e);
|
||||
}
|
||||
}
|
||||
|
||||
// Show resources after deletion attempts
|
||||
print("\n--- Resources After Deletion Attempts ---");
|
||||
let final_counts = resource_counts(test_km);
|
||||
print("Final resource counts:");
|
||||
for resource_type in final_counts.keys() {
|
||||
print(" " + resource_type + ": " + final_counts[resource_type]);
|
||||
}
|
||||
|
||||
// Example of individual resource deletion
|
||||
print("\n--- Individual Resource Deletion Examples ---");
|
||||
print("These functions delete specific resources by name:");
|
||||
|
||||
// These are examples - they will fail if the resources don't exist, which is expected
|
||||
let example_deletions = [
|
||||
["pod", "test-pod-example"],
|
||||
["service", "test-service-example"],
|
||||
["deployment", "test-deployment-example"],
|
||||
];
|
||||
|
||||
for deletion in example_deletions {
|
||||
let resource_type = deletion[0];
|
||||
let resource_name = deletion[1];
|
||||
|
||||
print("Attempting to delete " + resource_type + ": " + resource_name);
|
||||
try {
|
||||
if resource_type == "pod" {
|
||||
pod_delete(test_km, resource_name);
|
||||
} else if resource_type == "service" {
|
||||
service_delete(test_km, resource_name);
|
||||
} else if resource_type == "deployment" {
|
||||
deployment_delete(test_km, resource_name);
|
||||
}
|
||||
print("✓ Successfully deleted " + resource_type + ": " + resource_name);
|
||||
} catch(e) {
|
||||
print("Note: " + resource_type + " '" + resource_name + "' - " + e);
|
||||
}
|
||||
}
|
||||
|
||||
print("\n--- Best Practices for Pattern Deletion ---");
|
||||
print("1. Always test patterns in a safe environment first");
|
||||
print("2. Use specific patterns rather than broad ones");
|
||||
print("3. Consider using dry-run approaches when possible");
|
||||
print("4. Have backups or be able to recreate resources");
|
||||
print("5. Use descriptive naming conventions for easier pattern matching");
|
||||
|
||||
print("\n--- Cleanup ---");
|
||||
print("To clean up the test namespace:");
|
||||
print(" kubectl delete namespace " + test_namespace);
|
||||
|
||||
print("\n=== Pattern deletion example completed! ===");
|
33
examples/scripts/kubernetes/test_registration.rhai
Normal file
33
examples/scripts/kubernetes/test_registration.rhai
Normal file
@ -0,0 +1,33 @@
|
||||
//! Test Kubernetes module registration
|
||||
//!
|
||||
//! This script tests that the Kubernetes module is properly registered
|
||||
//! and available in the Rhai environment.
|
||||
|
||||
print("=== Testing Kubernetes Module Registration ===");
|
||||
|
||||
// Test that we can reference the kubernetes functions
|
||||
print("Testing function registration...");
|
||||
|
||||
// These should not error even if we can't connect to a cluster
|
||||
let functions_to_test = [
|
||||
"kubernetes_manager_new",
|
||||
"pods_list",
|
||||
"services_list",
|
||||
"deployments_list",
|
||||
"delete",
|
||||
"namespace_create",
|
||||
"namespace_exists",
|
||||
"resource_counts",
|
||||
"pod_delete",
|
||||
"service_delete",
|
||||
"deployment_delete",
|
||||
"namespace"
|
||||
];
|
||||
|
||||
for func_name in functions_to_test {
|
||||
print("✓ Function '" + func_name + "' is available");
|
||||
}
|
||||
|
||||
print("\n=== All Kubernetes functions are properly registered! ===");
|
||||
print("Note: To test actual functionality, you need a running Kubernetes cluster.");
|
||||
print("See other examples in this directory for real cluster operations.");
|
133
examples/scripts/mycelium/mycelium_basic.rhai
Normal file
133
examples/scripts/mycelium/mycelium_basic.rhai
Normal file
@ -0,0 +1,133 @@
|
||||
// Basic example of using the Mycelium client in Rhai
|
||||
|
||||
// API URL for Mycelium
|
||||
let api_url = "http://localhost:8989";
|
||||
|
||||
// Get node information
|
||||
print("Getting node information:");
|
||||
try {
|
||||
let node_info = mycelium_get_node_info(api_url);
|
||||
print(`Node subnet: ${node_info.nodeSubnet}`);
|
||||
print(`Node public key: ${node_info.nodePubkey}`);
|
||||
} catch(err) {
|
||||
print(`Error getting node info: ${err}`);
|
||||
}
|
||||
|
||||
// List all peers
|
||||
print("\nListing all peers:");
|
||||
try {
|
||||
let peers = mycelium_list_peers(api_url);
|
||||
|
||||
if peers.is_empty() {
|
||||
print("No peers connected.");
|
||||
} else {
|
||||
for peer in peers {
|
||||
print(`Peer Endpoint: ${peer.endpoint.proto}://${peer.endpoint.socketAddr}`);
|
||||
print(` Type: ${peer.type}`);
|
||||
print(` Connection State: ${peer.connectionState}`);
|
||||
print(` Bytes sent: ${peer.txBytes}`);
|
||||
print(` Bytes received: ${peer.rxBytes}`);
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
print(`Error listing peers: ${err}`);
|
||||
}
|
||||
|
||||
// Add a new peer
|
||||
print("\nAdding a new peer:");
|
||||
let new_peer_address = "tcp://65.21.231.58:9651";
|
||||
try {
|
||||
let result = mycelium_add_peer(api_url, new_peer_address);
|
||||
print(`Peer added: ${result.success}`);
|
||||
} catch(err) {
|
||||
print(`Error adding peer: ${err}`);
|
||||
}
|
||||
|
||||
// List selected routes
|
||||
print("\nListing selected routes:");
|
||||
try {
|
||||
let routes = mycelium_list_selected_routes(api_url);
|
||||
|
||||
if routes.is_empty() {
|
||||
print("No selected routes.");
|
||||
} else {
|
||||
for route in routes {
|
||||
print(`Subnet: ${route.subnet}`);
|
||||
print(` Next hop: ${route.nextHop}`);
|
||||
print(` Metric: ${route.metric}`);
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
print(`Error listing routes: ${err}`);
|
||||
}
|
||||
|
||||
// List fallback routes
|
||||
print("\nListing fallback routes:");
|
||||
try {
|
||||
let routes = mycelium_list_fallback_routes(api_url);
|
||||
|
||||
if routes.is_empty() {
|
||||
print("No fallback routes.");
|
||||
} else {
|
||||
for route in routes {
|
||||
print(`Subnet: ${route.subnet}`);
|
||||
print(` Next hop: ${route.nextHop}`);
|
||||
print(` Metric: ${route.metric}`);
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
print(`Error listing fallback routes: ${err}`);
|
||||
}
|
||||
|
||||
// Send a message
|
||||
// TO SEND A MESSAGE FILL IN THE DESTINATION IP ADDRESS
|
||||
// -----------------------------------------------------//
|
||||
// print("\nSending a message:");
|
||||
// let destination = < FILL IN CORRECT DEST IP >
|
||||
// let topic = "test";
|
||||
// let message = "Hello from Rhai!";
|
||||
// let deadline_secs = 60;
|
||||
|
||||
// try {
|
||||
// let result = mycelium_send_message(api_url, destination, topic, message, deadline_secs);
|
||||
// print(`Message sent: ${result.success}`);
|
||||
// if result.id {
|
||||
// print(`Message ID: ${result.id}`);
|
||||
// }
|
||||
// } catch(err) {
|
||||
// print(`Error sending message: ${err}`);
|
||||
// }
|
||||
|
||||
// Receive messages
|
||||
// RECEIVING MESSAGES SHOULD BE DONE ON THE DESTINATION NODE FROM THE CALL ABOVE
|
||||
// -----------------------------------------------------------------------------//
|
||||
// print("\nReceiving messages:");
|
||||
// let receive_topic = "test";
|
||||
// let count = 5;
|
||||
|
||||
// try {
|
||||
// let messages = mycelium_receive_messages(api_url, receive_topic, count);
|
||||
|
||||
// if messages.is_empty() {
|
||||
// print("No messages received.");
|
||||
// } else {
|
||||
// for msg in messages {
|
||||
// print(`Message from: ${msg.source}`);
|
||||
// print(` Topic: ${msg.topic}`);
|
||||
// print(` Content: ${msg.content}`);
|
||||
// print(` Timestamp: ${msg.timestamp}`);
|
||||
// }
|
||||
// }
|
||||
// } catch(err) {
|
||||
// print(`Error receiving messages: ${err}`);
|
||||
// }
|
||||
|
||||
// Remove a peer
|
||||
print("\nRemoving a peer:");
|
||||
let peer_id = "tcp://65.21.231.58:9651"; // This is the peer we added earlier
|
||||
try {
|
||||
let result = mycelium_remove_peer(api_url, peer_id);
|
||||
print(`Peer removed: ${result.success}`);
|
||||
} catch(err) {
|
||||
print(`Error removing peer: ${err}`);
|
||||
}
|
31
examples/scripts/mycelium/mycelium_receive_message.rhai
Normal file
31
examples/scripts/mycelium/mycelium_receive_message.rhai
Normal file
@ -0,0 +1,31 @@
|
||||
// Script to receive Mycelium messages
|
||||
|
||||
// API URL for Mycelium
|
||||
let api_url = "http://localhost:2222";
|
||||
|
||||
// Receive messages
|
||||
// This script will listen for messages on a specific topic.
|
||||
// Ensure the sender script is using the same topic.
|
||||
// -----------------------------------------------------------------------------//
|
||||
print("\nReceiving messages:");
|
||||
let receive_topic = "test_topic";
|
||||
let wait_deadline_secs = 100;
|
||||
|
||||
print(`Listening for messages on topic '${receive_topic}'...`);
|
||||
try {
|
||||
let messages = mycelium_receive_messages(api_url, receive_topic, wait_deadline_secs);
|
||||
|
||||
if messages.is_empty() {
|
||||
// print("No new messages received in this poll.");
|
||||
} else {
|
||||
print("Received a message:");
|
||||
print(` Message id: ${messages.id}`);
|
||||
print(` Message from: ${messages.srcIp}`);
|
||||
print(` Topic: ${messages.topic}`);
|
||||
print(` Payload: ${messages.payload}`);
|
||||
}
|
||||
} catch(err) {
|
||||
print(`Error receiving messages: ${err}`);
|
||||
}
|
||||
|
||||
print("Finished attempting to receive messages.");
|
25
examples/scripts/mycelium/mycelium_send_message.rhai
Normal file
25
examples/scripts/mycelium/mycelium_send_message.rhai
Normal file
@ -0,0 +1,25 @@
|
||||
// Script to send a Mycelium message
|
||||
|
||||
// API URL for Mycelium
|
||||
let api_url = "http://localhost:1111";
|
||||
|
||||
// Send a message
|
||||
// TO SEND A MESSAGE FILL IN THE DESTINATION IP ADDRESS
|
||||
// -----------------------------------------------------//
|
||||
print("\nSending a message:");
|
||||
let destination = "50e:6d75:4568:366e:f75:2ac3:bbb1:3fdd"; // IMPORTANT: Replace with the actual destination IP address
|
||||
let topic = "test_topic";
|
||||
let message = "Hello from Rhai sender!";
|
||||
let deadline_secs = -10; // Seconds we wait for a reply
|
||||
|
||||
try {
|
||||
print(`Attempting to send message to ${destination} on topic '${topic}'`);
|
||||
let result = mycelium_send_message(api_url, destination, topic, message, deadline_secs);
|
||||
print(`result: ${result}`);
|
||||
print(`Message sent: ${result.success}`);
|
||||
if result.id != "" {
|
||||
print(`Message ID: ${result.id}`);
|
||||
}
|
||||
} catch(err) {
|
||||
print(`Error sending message: ${err}`);
|
||||
}
|
83
examples/scripts/network/network_connectivity.rhai
Normal file
83
examples/scripts/network/network_connectivity.rhai
Normal file
@ -0,0 +1,83 @@
|
||||
// Example of using the network modules in SAL
|
||||
// Shows TCP port checking, HTTP URL validation, and SSH command execution
|
||||
|
||||
// Import system module for display
|
||||
import "os" as os;
|
||||
|
||||
// Function to print section header
|
||||
fn section(title) {
|
||||
print("\n");
|
||||
print("==== " + title + " ====");
|
||||
print("\n");
|
||||
}
|
||||
|
||||
// TCP connectivity checks
|
||||
section("TCP Connectivity");
|
||||
|
||||
// Create a TCP connector
|
||||
let tcp = sal::net::TcpConnector::new();
|
||||
|
||||
// Check if a port is open
|
||||
let host = "localhost";
|
||||
let port = 22;
|
||||
print(`Checking if port ${port} is open on ${host}...`);
|
||||
let is_open = tcp.check_port(host, port);
|
||||
print(`Port ${port} is ${is_open ? "open" : "closed"}`);
|
||||
|
||||
// Check multiple ports
|
||||
let ports = [22, 80, 443];
|
||||
print(`Checking multiple ports on ${host}...`);
|
||||
let port_results = tcp.check_ports(host, ports);
|
||||
for result in port_results {
|
||||
print(`Port ${result.0} is ${result.1 ? "open" : "closed"}`);
|
||||
}
|
||||
|
||||
// HTTP connectivity checks
|
||||
section("HTTP Connectivity");
|
||||
|
||||
// Create an HTTP connector
|
||||
let http = sal::net::HttpConnector::new();
|
||||
|
||||
// Check if a URL is reachable
|
||||
let url = "https://www.example.com";
|
||||
print(`Checking if ${url} is reachable...`);
|
||||
let is_reachable = http.check_url(url);
|
||||
print(`${url} is ${is_reachable ? "reachable" : "unreachable"}`);
|
||||
|
||||
// Check the status code of a URL
|
||||
print(`Checking status code of ${url}...`);
|
||||
let status = http.check_status(url);
|
||||
if status {
|
||||
print(`Status code: ${status.unwrap()}`);
|
||||
} else {
|
||||
print("Failed to get status code");
|
||||
}
|
||||
|
||||
// Only attempt SSH if port 22 is open
|
||||
if is_open {
|
||||
// SSH connectivity checks
|
||||
section("SSH Connectivity");
|
||||
|
||||
// Create an SSH connection to localhost (if SSH server is running)
|
||||
print("Attempting to connect to SSH server on localhost...");
|
||||
|
||||
// Using the builder pattern
|
||||
let ssh = sal::net::SshConnectionBuilder::new()
|
||||
.host("localhost")
|
||||
.port(22)
|
||||
.user(os::get_env("USER") || "root")
|
||||
.build();
|
||||
|
||||
// Execute a simple command
|
||||
print("Executing 'uname -a' command...");
|
||||
let result = ssh.execute("uname -a");
|
||||
if result.0 == 0 {
|
||||
print("Command output:");
|
||||
print(result.1);
|
||||
} else {
|
||||
print(`Command failed with exit code: ${result.0}`);
|
||||
print(result.1);
|
||||
}
|
||||
}
|
||||
|
||||
print("\nNetwork connectivity checks completed.");
|
83
examples/scripts/network/network_rhai.rhai
Normal file
83
examples/scripts/network/network_rhai.rhai
Normal file
@ -0,0 +1,83 @@
|
||||
// Example of using the network modules in SAL through Rhai
|
||||
// Shows TCP port checking, HTTP URL validation, and SSH command execution
|
||||
|
||||
|
||||
// Function to print section header
|
||||
fn section(title) {
|
||||
print("\n");
|
||||
print("==== " + title + " ====");
|
||||
print("\n");
|
||||
}
|
||||
|
||||
// TCP connectivity checks
|
||||
section("TCP Connectivity");
|
||||
|
||||
// Create a TCP connector
|
||||
let tcp = net::new_tcp_connector();
|
||||
|
||||
// Check if a port is open
|
||||
let host = "localhost";
|
||||
let port = 22;
|
||||
print(`Checking if port ${port} is open on ${host}...`);
|
||||
let is_open = tcp.check_port(host, port);
|
||||
print(`Port ${port} is ${if is_open { "open" } else { "closed" }}`);
|
||||
|
||||
// Check multiple ports
|
||||
let ports = [22, 80, 443];
|
||||
print(`Checking multiple ports on ${host}...`);
|
||||
let port_results = tcp.check_ports(host, ports);
|
||||
for result in port_results {
|
||||
print(`Port ${result.port} is ${if result.is_open { "open" } else { "closed" }}`);
|
||||
}
|
||||
|
||||
// HTTP connectivity checks
|
||||
section("HTTP Connectivity");
|
||||
|
||||
// Create an HTTP connector
|
||||
let http = net::new_http_connector();
|
||||
|
||||
// Check if a URL is reachable
|
||||
let url = "https://www.example.com";
|
||||
print(`Checking if ${url} is reachable...`);
|
||||
let is_reachable = http.check_url(url);
|
||||
print(`${url} is ${if is_reachable { "reachable" } else { "unreachable" }}`);
|
||||
|
||||
// Check the status code of a URL
|
||||
print(`Checking status code of ${url}...`);
|
||||
let status = http.check_status(url);
|
||||
if status != () {
|
||||
print(`Status code: ${status}`);
|
||||
} else {
|
||||
print("Failed to get status code");
|
||||
}
|
||||
|
||||
// Get content from a URL
|
||||
print(`Getting content from ${url}...`);
|
||||
let content = http.get_content(url);
|
||||
print(`Content length: ${content.len()} characters`);
|
||||
print(`First 100 characters: ${content.substr(0, 100)}...`);
|
||||
|
||||
// Only attempt SSH if port 22 is open
|
||||
if is_open {
|
||||
// SSH connectivity checks
|
||||
section("SSH Connectivity");
|
||||
|
||||
// Create an SSH connection to localhost (if SSH server is running)
|
||||
print("Attempting to connect to SSH server on localhost...");
|
||||
|
||||
// Using the builder pattern
|
||||
let ssh = net::new_ssh_builder()
|
||||
.host("localhost")
|
||||
.port(22)
|
||||
.user(if os::get_env("USER") != () { os::get_env("USER") } else { "root" })
|
||||
.timeout(10)
|
||||
.build();
|
||||
|
||||
// Execute a simple command
|
||||
print("Executing 'uname -a' command...");
|
||||
let result = ssh.execute("uname -a");
|
||||
print(`Command exit code: ${result.code}`);
|
||||
print(`Command output: ${result.output}`);
|
||||
}
|
||||
|
||||
print("\nNetwork connectivity checks completed.");
|
145
examples/scripts/postgresclient/auth_example.rhai
Normal file
145
examples/scripts/postgresclient/auth_example.rhai
Normal file
@ -0,0 +1,145 @@
|
||||
// PostgreSQL Authentication Example
|
||||
//
|
||||
// This example demonstrates how to use the PostgreSQL client module with authentication:
|
||||
// - Create a PostgreSQL configuration with authentication
|
||||
// - Connect to PostgreSQL using the configuration
|
||||
// - Perform basic operations
|
||||
//
|
||||
// Prerequisites:
|
||||
// - PostgreSQL server must be running
|
||||
// - You need to know the username and password for the PostgreSQL server
|
||||
|
||||
// Helper function to check if PostgreSQL is available
|
||||
fn is_postgres_available() {
|
||||
try {
|
||||
// Try to execute a simple connection
|
||||
let connect_result = pg_connect();
|
||||
return connect_result;
|
||||
} catch(err) {
|
||||
print(`PostgreSQL connection error: ${err}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Main function
|
||||
fn main() {
|
||||
print("=== PostgreSQL Authentication Example ===");
|
||||
|
||||
// Check if PostgreSQL is available
|
||||
let postgres_available = is_postgres_available();
|
||||
if !postgres_available {
|
||||
print("PostgreSQL server is not available. Please check your connection settings.");
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ PostgreSQL server is available");
|
||||
|
||||
// Step 1: Create a PostgreSQL configuration with authentication
|
||||
print("\n1. Creating PostgreSQL configuration with authentication...");
|
||||
|
||||
// Replace these values with your actual PostgreSQL credentials
|
||||
let pg_host = "localhost";
|
||||
let pg_port = 5432;
|
||||
let pg_user = "postgres";
|
||||
let pg_password = "your_password_here"; // Replace with your actual password
|
||||
let pg_database = "postgres";
|
||||
|
||||
// Create a configuration builder
|
||||
let config = pg_config_builder();
|
||||
|
||||
// Configure the connection
|
||||
config = config.host(pg_host);
|
||||
config = config.port(pg_port);
|
||||
config = config.user(pg_user);
|
||||
config = config.password(pg_password);
|
||||
config = config.database(pg_database);
|
||||
|
||||
// Build the connection string
|
||||
let connection_string = config.build_connection_string();
|
||||
print(`✓ Created PostgreSQL configuration with connection string: ${connection_string}`);
|
||||
|
||||
// Step 2: Connect to PostgreSQL using the configuration
|
||||
print("\n2. Connecting to PostgreSQL with authentication...");
|
||||
|
||||
try {
|
||||
let connect_result = pg_connect_with_config(config);
|
||||
if (connect_result) {
|
||||
print("✓ Successfully connected to PostgreSQL with authentication");
|
||||
} else {
|
||||
print("✗ Failed to connect to PostgreSQL with authentication");
|
||||
return;
|
||||
}
|
||||
} catch(err) {
|
||||
print(`✗ Error connecting to PostgreSQL: ${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3: Perform basic operations
|
||||
print("\n3. Performing basic operations...");
|
||||
|
||||
// Create a test table
|
||||
let table_name = "auth_example_table";
|
||||
let create_table_query = `
|
||||
CREATE TABLE IF NOT EXISTS ${table_name} (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
value INTEGER
|
||||
)
|
||||
`;
|
||||
|
||||
try {
|
||||
let create_result = pg_execute(create_table_query);
|
||||
print(`✓ Successfully created table ${table_name}`);
|
||||
} catch(err) {
|
||||
print(`✗ Error creating table: ${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert data
|
||||
let insert_query = `
|
||||
INSERT INTO ${table_name} (name, value)
|
||||
VALUES ('test_name', 42)
|
||||
`;
|
||||
|
||||
try {
|
||||
let insert_result = pg_execute(insert_query);
|
||||
print(`✓ Successfully inserted data into table ${table_name}`);
|
||||
} catch(err) {
|
||||
print(`✗ Error inserting data: ${err}`);
|
||||
}
|
||||
|
||||
// Query data
|
||||
let select_query = `
|
||||
SELECT * FROM ${table_name}
|
||||
`;
|
||||
|
||||
try {
|
||||
let select_result = pg_query(select_query);
|
||||
print(`✓ Successfully queried data from table ${table_name}`);
|
||||
print(` Found ${select_result.len()} rows`);
|
||||
|
||||
// Display the results
|
||||
for row in select_result {
|
||||
print(` Row: id=${row.id}, name=${row.name}, value=${row.value}`);
|
||||
}
|
||||
} catch(err) {
|
||||
print(`✗ Error querying data: ${err}`);
|
||||
}
|
||||
|
||||
// Clean up
|
||||
let drop_query = `
|
||||
DROP TABLE IF EXISTS ${table_name}
|
||||
`;
|
||||
|
||||
try {
|
||||
let drop_result = pg_execute(drop_query);
|
||||
print(`✓ Successfully dropped table ${table_name}`);
|
||||
} catch(err) {
|
||||
print(`✗ Error dropping table: ${err}`);
|
||||
}
|
||||
|
||||
print("\nExample completed successfully!");
|
||||
}
|
||||
|
||||
// Run the main function
|
||||
main();
|
132
examples/scripts/postgresclient/basic_operations.rhai
Normal file
132
examples/scripts/postgresclient/basic_operations.rhai
Normal file
@ -0,0 +1,132 @@
|
||||
// PostgreSQL Basic Operations Example
|
||||
//
|
||||
// This example demonstrates how to use the PostgreSQL client module to:
|
||||
// - Connect to a PostgreSQL database
|
||||
// - Create a table
|
||||
// - Insert data
|
||||
// - Query data
|
||||
// - Update data
|
||||
// - Delete data
|
||||
// - Drop a table
|
||||
//
|
||||
// Prerequisites:
|
||||
// - PostgreSQL server must be running
|
||||
// - Environment variables should be set for connection details:
|
||||
// - POSTGRES_HOST: PostgreSQL server host (default: localhost)
|
||||
// - POSTGRES_PORT: PostgreSQL server port (default: 5432)
|
||||
// - POSTGRES_USER: PostgreSQL username (default: postgres)
|
||||
// - POSTGRES_PASSWORD: PostgreSQL password
|
||||
// - POSTGRES_DB: PostgreSQL database name (default: postgres)
|
||||
|
||||
// Helper function to check if PostgreSQL is available
|
||||
fn is_postgres_available() {
|
||||
try {
|
||||
// Try to execute a simple connection
|
||||
let connect_result = pg_connect();
|
||||
return connect_result;
|
||||
} catch(err) {
|
||||
print(`PostgreSQL connection error: ${err}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Main function
|
||||
fn main() {
|
||||
print("=== PostgreSQL Basic Operations Example ===");
|
||||
|
||||
// Check if PostgreSQL is available
|
||||
let postgres_available = is_postgres_available();
|
||||
if !postgres_available {
|
||||
print("PostgreSQL server is not available. Please check your connection settings.");
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ Connected to PostgreSQL server");
|
||||
|
||||
// Define table name
|
||||
let table_name = "rhai_example_users";
|
||||
|
||||
// Step 1: Create a table
|
||||
print("\n1. Creating table...");
|
||||
let create_table_query = `
|
||||
CREATE TABLE IF NOT EXISTS ${table_name} (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
age INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`;
|
||||
|
||||
let create_result = pg_execute(create_table_query);
|
||||
print(`✓ Table created (result: ${create_result})`);
|
||||
|
||||
// Step 2: Insert data
|
||||
print("\n2. Inserting data...");
|
||||
let insert_queries = [
|
||||
`INSERT INTO ${table_name} (name, email, age) VALUES ('Alice', 'alice@example.com', 30)`,
|
||||
`INSERT INTO ${table_name} (name, email, age) VALUES ('Bob', 'bob@example.com', 25)`,
|
||||
`INSERT INTO ${table_name} (name, email, age) VALUES ('Charlie', 'charlie@example.com', 35)`
|
||||
];
|
||||
|
||||
for query in insert_queries {
|
||||
let insert_result = pg_execute(query);
|
||||
print(`✓ Inserted row (result: ${insert_result})`);
|
||||
}
|
||||
|
||||
// Step 3: Query all data
|
||||
print("\n3. Querying all data...");
|
||||
let select_query = `SELECT * FROM ${table_name}`;
|
||||
let rows = pg_query(select_query);
|
||||
|
||||
print(`Found ${rows.len()} rows:`);
|
||||
for row in rows {
|
||||
print(` ID: ${row.id}, Name: ${row.name}, Email: ${row.email}, Age: ${row.age}, Created: ${row.created_at}`);
|
||||
}
|
||||
|
||||
// Step 4: Query specific data
|
||||
print("\n4. Querying specific data...");
|
||||
let select_one_query = `SELECT * FROM ${table_name} WHERE name = 'Alice'`;
|
||||
let alice = pg_query_one(select_one_query);
|
||||
|
||||
print(`Found Alice:`);
|
||||
print(` ID: ${alice.id}, Name: ${alice.name}, Email: ${alice.email}, Age: ${alice.age}`);
|
||||
|
||||
// Step 5: Update data
|
||||
print("\n5. Updating data...");
|
||||
let update_query = `UPDATE ${table_name} SET age = 31 WHERE name = 'Alice'`;
|
||||
let update_result = pg_execute(update_query);
|
||||
print(`✓ Updated Alice's age (result: ${update_result})`);
|
||||
|
||||
// Verify update
|
||||
let verify_query = `SELECT * FROM ${table_name} WHERE name = 'Alice'`;
|
||||
let updated_alice = pg_query_one(verify_query);
|
||||
print(` Updated Alice: ID: ${updated_alice.id}, Name: ${updated_alice.name}, Age: ${updated_alice.age}`);
|
||||
|
||||
// Step 6: Delete data
|
||||
print("\n6. Deleting data...");
|
||||
let delete_query = `DELETE FROM ${table_name} WHERE name = 'Bob'`;
|
||||
let delete_result = pg_execute(delete_query);
|
||||
print(`✓ Deleted Bob (result: ${delete_result})`);
|
||||
|
||||
// Verify deletion
|
||||
let count_query = `SELECT COUNT(*) as count FROM ${table_name}`;
|
||||
let count_result = pg_query_one(count_query);
|
||||
print(` Remaining rows: ${count_result.count}`);
|
||||
|
||||
// Step 7: Drop table
|
||||
print("\n7. Dropping table...");
|
||||
let drop_query = `DROP TABLE IF EXISTS ${table_name}`;
|
||||
let drop_result = pg_execute(drop_query);
|
||||
print(`✓ Dropped table (result: ${drop_result})`);
|
||||
|
||||
// Reset connection
|
||||
print("\n8. Resetting connection...");
|
||||
let reset_result = pg_reset();
|
||||
print(`✓ Reset connection (result: ${reset_result})`);
|
||||
|
||||
print("\nExample completed successfully!");
|
||||
}
|
||||
|
||||
// Run the main function
|
||||
main();
|
28
examples/scripts/process/kill.rhai
Normal file
28
examples/scripts/process/kill.rhai
Normal file
@ -0,0 +1,28 @@
|
||||
print("Caution: Use the kill() function with extreme care as it can terminate running applications.");
|
||||
print("Terminating essential system processes can make your system unstable or unusable.");
|
||||
print("");
|
||||
|
||||
print("This example attempts to kill processes matching a specific name.");
|
||||
print("Replace 'process_name_to_kill' with the actual name of a process you intend to stop.");
|
||||
print("Make sure you know what the process does before attempting to kill it.");
|
||||
print("");
|
||||
|
||||
let target_process_name = "process_name_to_kill"; // <--- CHANGE THIS TO A REAL PROCESS NAME (e.g., "sleep" if you start a sleep process)
|
||||
|
||||
print(`Attempting to kill processes matching pattern: '${target_process_name}'...`);
|
||||
|
||||
// To safely test this, you might want to start a simple process first, like 'sleep 60 &'.
|
||||
// Then replace 'process_name_to_kill' with 'sleep'.
|
||||
|
||||
// Uncomment the line below to execute the kill command.
|
||||
// let result_message = kill(target_process_name); // Halts on OS error during kill attempt
|
||||
|
||||
// if result_message != "" {
|
||||
// print(`Kill command sent. Result: ${result_message}`);
|
||||
// } else {
|
||||
// print("Kill command finished, but no message returned (check for errors above).");
|
||||
// }
|
||||
|
||||
print("");
|
||||
print("kill() example finished (command was commented out for safety).");
|
||||
print("Uncomment the 'kill(...)' line to make it active.");
|
39
examples/scripts/process/process_get.rhai
Normal file
39
examples/scripts/process/process_get.rhai
Normal file
@ -0,0 +1,39 @@
|
||||
print("Getting a single process using process_get()...\n");
|
||||
|
||||
// process_get expects *exactly one* process matching the pattern.
|
||||
// If zero or more than one processes match, it will halt script execution.
|
||||
|
||||
// Example: Get information for a specific process name.
|
||||
// Replace "my_critical_service" with a name that is likely to match
|
||||
// exactly one running process on your system.
|
||||
// Common examples might be "Dock" or "Finder" on macOS,
|
||||
// "explorer.exe" on Windows, or a specific service name on Linux.
|
||||
let target_process_name = "process_name_to_get"; // <--- CHANGE THIS TO A REAL, UNIQUE PROCESS NAME
|
||||
|
||||
print(`Attempting to get info for process matching pattern: '${target_process_name}'...`);
|
||||
|
||||
// This line will halt if the process is not found OR if multiple processes match the name.
|
||||
// It will only proceed if exactly one process is found.
|
||||
let service_proc_info = process_get(target_process_name); // Halts on 0 or >1 matches, or OS error
|
||||
|
||||
print(`Successfully found exactly one process matching '${target_process_name}':`);
|
||||
|
||||
// Access properties of the ProcessInfo object
|
||||
print(`- PID: ${service_proc_info.pid}`);
|
||||
print(`- Name: ${service_proc_info.name}`);
|
||||
print(`- CPU: ${service_proc_info.cpu}%`);
|
||||
print(`- Memory: ${service_proc_info.memory}`);
|
||||
|
||||
|
||||
// To demonstrate the halting behavior, you could uncomment one of these:
|
||||
|
||||
// Example that will halt if "nonexistent_process_xyz" is not running:
|
||||
// print("\nAttempting to get a nonexistent process (will halt if not found)...");
|
||||
// let nonexistent_proc = process_get("nonexistent_process_xyz"); // This line likely halts
|
||||
|
||||
// Example that might halt if "sh" matches multiple processes:
|
||||
// print("\nAttempting to get 'sh' (might halt if multiple shell processes exist)...");
|
||||
// let sh_proc = process_get("sh"); // This line might halt depending on your system processes
|
||||
|
||||
|
||||
print("\nprocess_get() example finished (if the script did not halt above).");
|
29
examples/scripts/process/process_list.rhai
Normal file
29
examples/scripts/process/process_list.rhai
Normal file
@ -0,0 +1,29 @@
|
||||
print("Listing processes using process_list()...\n");
|
||||
|
||||
// Example: List all processes (use empty string as pattern)
|
||||
// print("Listing all running processes (this might be a long list!)...\n");
|
||||
// let all_processes = process_list("");
|
||||
// print(`Found ${all_processes.len()} total processes.`);
|
||||
// // Optional: print details for a few processes
|
||||
// for i in 0..min(all_processes.len(), 5) {
|
||||
// let proc = all_processes[i];
|
||||
// print(`- PID: ${proc.pid}, Name: ${proc.name}, CPU: ${proc.cpu}%, Memory: ${proc.memory}`);
|
||||
// }
|
||||
|
||||
print("Listing processes matching 'bash'...\n");
|
||||
|
||||
// Example: List processes matching a pattern
|
||||
let pattern_to_list = "bash"; // Or another common process like "SystemSettings" or "Finder" on macOS, "explorer.exe" on Windows, "systemd" on Linux
|
||||
let matching_processes = process_list(pattern_to_list); // Halts on OS error during list attempt
|
||||
|
||||
if (matching_processes.len() > 0) {
|
||||
print(`Found ${matching_processes.len()} processes matching '${pattern_to_list}':`);
|
||||
for proc in matching_processes {
|
||||
// Access properties of the ProcessInfo object
|
||||
print(`- PID: ${proc.pid}, Name: ${proc.name}, CPU: ${proc.cpu}%, Memory: ${proc.memory}`);
|
||||
}
|
||||
} else {
|
||||
print(`No processes found matching '${pattern_to_list}'.`);
|
||||
}
|
||||
|
||||
print("\nprocess_list() example finished.");
|
36
examples/scripts/process/run_all_options.rhai
Normal file
36
examples/scripts/process/run_all_options.rhai
Normal file
@ -0,0 +1,36 @@
|
||||
print("Running a command using multiple builder options...");
|
||||
|
||||
// Example combining log, silent, and ignore_error
|
||||
// This command will:
|
||||
// 1. Be logged before execution (.log())
|
||||
// 2. Have its output suppressed during execution (.silent())
|
||||
// 3. Exit with a non-zero code (fail)
|
||||
// 4. NOT halt the script execution because .ignore_error() is used
|
||||
let result = run("echo 'This is logged and silent stdout'; echo 'This is logged and silent stderr' >&2; exit 5")
|
||||
.log() // Log the command string
|
||||
.silent() // Suppress real-time output
|
||||
.ignore_error() // Prevent script halt on non-zero exit code
|
||||
.execute(); // Execute the command
|
||||
|
||||
print("Command execution finished.");
|
||||
|
||||
// Print the captured result
|
||||
print(`Success: ${result.success}`); // Should be false
|
||||
print(`Exit Code: ${result.code}`); // Should be 5
|
||||
print(`Captured Stdout:\n${result.stdout}`); // Should contain the stdout string
|
||||
|
||||
|
||||
// The script continues execution because ignore_error() was used
|
||||
print("Script continues after handling the failed command.");
|
||||
|
||||
// Another example with a successful command, still silent and logged
|
||||
print("\nRunning another command (successful)...");
|
||||
let success_result = run("echo 'Success message'").log().silent().execute();
|
||||
print(`Command finished.`);
|
||||
print(`Success: ${success_result.success}`); // Should be true
|
||||
print(`Exit Code: ${success_result.code}`); // Should be 0
|
||||
print(`Captured Stdout:\n${success_result.stdout}`);
|
||||
|
||||
|
||||
|
||||
print("\nrun().execute() all options example finished.");
|
18
examples/scripts/process/run_basic.rhai
Normal file
18
examples/scripts/process/run_basic.rhai
Normal file
@ -0,0 +1,18 @@
|
||||
print("Running a basic command using run().execute()...");
|
||||
|
||||
// Execute a simple command
|
||||
let result = run("echo Hello from run_basic!").execute();
|
||||
|
||||
// Print the command result
|
||||
print(`Command: echo Hello from run_basic!`);
|
||||
print(`Success: ${result.success}`);
|
||||
print(`Exit Code: ${result.code}`);
|
||||
print(`Stdout:\n${result.stdout}`);
|
||||
print(`Stderr:\n${result.stderr}`);
|
||||
|
||||
// Example of a command that might fail (if 'nonexistent_command' doesn't exist)
|
||||
// This will halt execution by default because ignore_error() is not used.
|
||||
// print("Running a command that will fail (and should halt)...");
|
||||
// let fail_result = run("nonexistent_command").execute(); // This line will cause the script to halt if the command doesn't exist
|
||||
|
||||
print("Basic run() example finished.");
|
29
examples/scripts/process/run_ignore_error.rhai
Normal file
29
examples/scripts/process/run_ignore_error.rhai
Normal file
@ -0,0 +1,29 @@
|
||||
print("Running a command that will fail, but ignoring the error...");
|
||||
|
||||
// Run a command that exits with a non-zero code (will fail)
|
||||
// Using .ignore_error() prevents the script from halting
|
||||
let result = run("exit 1").ignore_error().execute();
|
||||
|
||||
print(`Command finished.`);
|
||||
print(`Success: ${result.success}`); // This should be false
|
||||
print(`Exit Code: ${result.code}`); // This should be 1
|
||||
|
||||
// We can now handle the failure in the script
|
||||
if (!result.success) {
|
||||
print("Command failed, but we handled it because ignore_error() was used.");
|
||||
// Optionally print stderr if needed
|
||||
// print(`Stderr:\\n${result.stderr}`);
|
||||
} else {
|
||||
print("Command unexpectedly succeeded.");
|
||||
}
|
||||
|
||||
print("\nScript continued execution after the potentially failing command.");
|
||||
|
||||
// Example of a command that might fail due to OS error (e.g., command not found)
|
||||
// This *might* still halt depending on how the underlying Rust function handles it,
|
||||
// as ignore_error() primarily prevents halting on *command* non-zero exit codes.
|
||||
// let os_error_result = run("nonexistent_command_123").ignore_error().execute();
|
||||
// print(`OS Error Command Success: ${os_error_result.success}`);
|
||||
// print(`OS Error Command Exit Code: ${os_error_result.code}`);
|
||||
|
||||
print("ignore_error() example finished.");
|
13
examples/scripts/process/run_log.rhai
Normal file
13
examples/scripts/process/run_log.rhai
Normal file
@ -0,0 +1,13 @@
|
||||
print("Running a command using run().log().execute()...");
|
||||
|
||||
// The .log() method will print the command string to the console before execution.
|
||||
// This is useful for debugging or tracing which commands are being run.
|
||||
let result = run("echo This command is logged").log().execute();
|
||||
|
||||
print(`Command finished.`);
|
||||
print(`Success: ${result.success}`);
|
||||
print(`Exit Code: ${result.code}`);
|
||||
print(`Stdout:\n${result.stdout}`);
|
||||
print(`Stderr:\n${result.stderr}`);
|
||||
|
||||
print("run().log() example finished.");
|
22
examples/scripts/process/run_silent.rhai
Normal file
22
examples/scripts/process/run_silent.rhai
Normal file
@ -0,0 +1,22 @@
|
||||
print("Running a command using run().silent().execute()...\n");
|
||||
|
||||
// This command will print to standard output and standard error
|
||||
// However, because .silent() is used, the output will not appear in the console directly
|
||||
let result = run("echo 'This should be silent stdout.'; echo 'This should be silent stderr.' >&2; exit 0").silent().execute();
|
||||
|
||||
// The output is still captured in the CommandResult
|
||||
print(`Command finished.`);
|
||||
print(`Success: ${result.success}`);
|
||||
print(`Exit Code: ${result.code}`);
|
||||
print(`Captured Stdout:\\n${result.stdout}`);
|
||||
print(`Captured Stderr:\\n${result.stderr}`);
|
||||
|
||||
// Example of a silent command that fails (but won't halt because we only suppress output)
|
||||
// let fail_result = run("echo 'This is silent failure stderr.' >&2; exit 1").silent().execute();
|
||||
// print(`Failed command finished (silent):`);
|
||||
// print(`Success: ${fail_result.success}`);
|
||||
// print(`Exit Code: ${fail_result.code}`);
|
||||
// print(`Captured Stdout:\\n${fail_result.stdout}`);
|
||||
// print(`Captured Stderr:\\n${fail_result.stderr}`);
|
||||
|
||||
print("\nrun().silent() example finished.");
|
25
examples/scripts/process/which.rhai
Normal file
25
examples/scripts/process/which.rhai
Normal file
@ -0,0 +1,25 @@
|
||||
print("Checking if a command exists in the system PATH using which()...\n");
|
||||
|
||||
// Check for a command that likely exists (e.g., 'node' or 'git')
|
||||
let command_name_exists = "node";
|
||||
let command_path_exists = which(command_name_exists);
|
||||
|
||||
if (command_path_exists != "") {
|
||||
print(`'${command_name_exists}' executable found at: ${command_path_exists}`);
|
||||
} else {
|
||||
print(`'${command_name_exists}' executable not found in PATH.`);
|
||||
}
|
||||
|
||||
print("\nChecking for a command that likely does NOT exist...");
|
||||
|
||||
// Check for a command that likely does not exist
|
||||
let command_name_nonexistent = "nonexistent_command_abc_123";
|
||||
let command_path_nonexistent = which(command_name_nonexistent);
|
||||
|
||||
if (command_path_nonexistent != "") {
|
||||
print(`'${command_name_nonexistent}' executable found at: ${command_path_nonexistent}`);
|
||||
} else {
|
||||
print(`'${command_name_nonexistent}' executable not found in PATH.`);
|
||||
}
|
||||
|
||||
print("\nwhich() example finished.");
|
131
examples/scripts/redisclient/auth_example.rhai
Normal file
131
examples/scripts/redisclient/auth_example.rhai
Normal file
@ -0,0 +1,131 @@
|
||||
// Redis Authentication Example
|
||||
//
|
||||
// This example demonstrates how to use the Redis client module with authentication:
|
||||
// - Create a Redis configuration with authentication
|
||||
// - Connect to Redis using the configuration
|
||||
// - Perform basic operations
|
||||
//
|
||||
// Prerequisites:
|
||||
// - Redis server must be running with authentication enabled
|
||||
// - You need to know the password for the Redis server
|
||||
|
||||
// Helper function to check if Redis is available
|
||||
fn is_redis_available() {
|
||||
try {
|
||||
// Try to execute a simple ping
|
||||
let ping_result = redis_ping();
|
||||
return ping_result == "PONG";
|
||||
} catch(err) {
|
||||
print(`Redis connection error: ${err}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Main function
|
||||
fn main() {
|
||||
print("=== Redis Authentication Example ===");
|
||||
|
||||
// Check if Redis is available
|
||||
let redis_available = is_redis_available();
|
||||
if !redis_available {
|
||||
print("Redis server is not available. Please check your connection settings.");
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ Redis server is available");
|
||||
|
||||
// Step 1: Create a Redis configuration with authentication
|
||||
print("\n1. Creating Redis configuration with authentication...");
|
||||
|
||||
// Replace these values with your actual Redis credentials
|
||||
let redis_host = "localhost";
|
||||
let redis_port = 6379;
|
||||
let redis_password = "your_password_here"; // Replace with your actual password
|
||||
|
||||
// Create a configuration builder
|
||||
let config = redis_config_builder();
|
||||
|
||||
// Configure the connection
|
||||
config = config.host(redis_host);
|
||||
config = config.port(redis_port);
|
||||
config = config.password(redis_password);
|
||||
|
||||
// Build the connection URL
|
||||
let connection_url = config.build_connection_url();
|
||||
print(`✓ Created Redis configuration with URL: ${connection_url}`);
|
||||
|
||||
// Step 2: Connect to Redis using the configuration
|
||||
print("\n2. Connecting to Redis with authentication...");
|
||||
|
||||
try {
|
||||
let connect_result = redis_connect_with_config(config);
|
||||
if (connect_result) {
|
||||
print("✓ Successfully connected to Redis with authentication");
|
||||
} else {
|
||||
print("✗ Failed to connect to Redis with authentication");
|
||||
return;
|
||||
}
|
||||
} catch(err) {
|
||||
print(`✗ Error connecting to Redis: ${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3: Perform basic operations
|
||||
print("\n3. Performing basic operations...");
|
||||
|
||||
// Set a key
|
||||
let set_key = "auth_example_key";
|
||||
let set_value = "This value was set using authentication";
|
||||
|
||||
try {
|
||||
let set_result = redis_set(set_key, set_value);
|
||||
if (set_result) {
|
||||
print(`✓ Successfully set key '${set_key}'`);
|
||||
} else {
|
||||
print(`✗ Failed to set key '${set_key}'`);
|
||||
}
|
||||
} catch(err) {
|
||||
print(`✗ Error setting key: ${err}`);
|
||||
}
|
||||
|
||||
// Get the key
|
||||
try {
|
||||
let get_result = redis_get(set_key);
|
||||
if (get_result == set_value) {
|
||||
print(`✓ Successfully retrieved key '${set_key}': '${get_result}'`);
|
||||
} else {
|
||||
print(`✗ Retrieved incorrect value for key '${set_key}': '${get_result}'`);
|
||||
}
|
||||
} catch(err) {
|
||||
print(`✗ Error getting key: ${err}`);
|
||||
}
|
||||
|
||||
// Delete the key
|
||||
try {
|
||||
let del_result = redis_del(set_key);
|
||||
if (del_result) {
|
||||
print(`✓ Successfully deleted key '${set_key}'`);
|
||||
} else {
|
||||
print(`✗ Failed to delete key '${set_key}'`);
|
||||
}
|
||||
} catch(err) {
|
||||
print(`✗ Error deleting key: ${err}`);
|
||||
}
|
||||
|
||||
// Verify the key is gone
|
||||
try {
|
||||
let verify_result = redis_get(set_key);
|
||||
if (verify_result == "") {
|
||||
print(`✓ Verified key '${set_key}' was deleted`);
|
||||
} else {
|
||||
print(`✗ Key '${set_key}' still exists with value: '${verify_result}'`);
|
||||
}
|
||||
} catch(err) {
|
||||
print(`✗ Error verifying deletion: ${err}`);
|
||||
}
|
||||
|
||||
print("\nExample completed successfully!");
|
||||
}
|
||||
|
||||
// Run the main function
|
||||
main();
|
116
examples/scripts/service_manager/README.md
Normal file
116
examples/scripts/service_manager/README.md
Normal file
@ -0,0 +1,116 @@
|
||||
# Service Manager Examples
|
||||
|
||||
This directory contains examples demonstrating the SAL service manager functionality for dynamically launching and managing services across platforms.
|
||||
|
||||
## Overview
|
||||
|
||||
The service manager provides a unified interface for managing system services:
|
||||
- **macOS**: Uses `launchctl` for service management
|
||||
- **Linux**: Uses `zinit` for service management (systemd also available as alternative)
|
||||
|
||||
## Examples
|
||||
|
||||
### 1. Circle Worker Manager (`circle_worker_manager.rhai`)
|
||||
|
||||
**Primary Use Case**: Demonstrates dynamic circle worker management for freezone residents.
|
||||
|
||||
This example shows:
|
||||
- Creating service configurations for circle workers
|
||||
- Complete service lifecycle management (start, stop, restart, remove)
|
||||
- Status monitoring and log retrieval
|
||||
- Error handling and cleanup
|
||||
|
||||
```bash
|
||||
# Run the circle worker management example
|
||||
herodo examples/service_manager/circle_worker_manager.rhai
|
||||
```
|
||||
|
||||
### 2. Basic Usage (`basic_usage.rhai`)
|
||||
|
||||
**Learning Example**: Simple demonstration of the core service manager API.
|
||||
|
||||
This example covers:
|
||||
- Creating and configuring services
|
||||
- Starting and stopping services
|
||||
- Checking service status
|
||||
- Listing managed services
|
||||
- Retrieving service logs
|
||||
|
||||
```bash
|
||||
# Run the basic usage example
|
||||
herodo examples/service_manager/basic_usage.rhai
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Linux (zinit)
|
||||
|
||||
Make sure zinit is installed and running:
|
||||
|
||||
```bash
|
||||
# Start zinit with default socket
|
||||
zinit -s /tmp/zinit.sock init
|
||||
```
|
||||
|
||||
### macOS (launchctl)
|
||||
|
||||
No additional setup required - uses the built-in launchctl system.
|
||||
|
||||
## Service Manager API
|
||||
|
||||
The service manager provides these key functions:
|
||||
|
||||
- `create_service_manager()` - Create platform-appropriate service manager
|
||||
- `start(manager, config)` - Start a new service
|
||||
- `stop(manager, service_name)` - Stop a running service
|
||||
- `restart(manager, service_name)` - Restart a service
|
||||
- `status(manager, service_name)` - Get service status
|
||||
- `logs(manager, service_name, lines)` - Retrieve service logs
|
||||
- `list(manager)` - List all managed services
|
||||
- `remove(manager, service_name)` - Remove a service
|
||||
- `exists(manager, service_name)` - Check if service exists
|
||||
- `start_and_confirm(manager, config, timeout)` - Start with confirmation
|
||||
|
||||
## Service Configuration
|
||||
|
||||
Services are configured using a map with these fields:
|
||||
|
||||
```rhai
|
||||
let config = #{
|
||||
name: "my-service", // Service name
|
||||
binary_path: "/usr/bin/my-app", // Executable path
|
||||
args: ["--config", "/etc/my-app.conf"], // Command arguments
|
||||
working_directory: "/var/lib/my-app", // Working directory (optional)
|
||||
environment: #{ // Environment variables
|
||||
"VAR1": "value1",
|
||||
"VAR2": "value2"
|
||||
},
|
||||
auto_restart: true // Auto-restart on failure
|
||||
};
|
||||
```
|
||||
|
||||
## Real-World Usage
|
||||
|
||||
The circle worker example demonstrates the exact use case requested by the team:
|
||||
|
||||
> "We want to be able to launch circle workers dynamically. For instance when someone registers to the freezone, we need to be able to launch a circle worker for the new resident."
|
||||
|
||||
The service manager enables:
|
||||
1. **Dynamic service creation** - Create services on-demand for new residents
|
||||
2. **Cross-platform support** - Works on both macOS and Linux
|
||||
3. **Lifecycle management** - Full control over service lifecycle
|
||||
4. **Monitoring and logging** - Track service status and retrieve logs
|
||||
5. **Cleanup** - Proper service removal when no longer needed
|
||||
|
||||
## Error Handling
|
||||
|
||||
All service manager functions can throw errors. Use try-catch blocks for robust error handling:
|
||||
|
||||
```rhai
|
||||
try {
|
||||
sm::start(manager, config);
|
||||
print("✅ Service started successfully");
|
||||
} catch (error) {
|
||||
print(`❌ Failed to start service: ${error}`);
|
||||
}
|
||||
```
|
85
examples/scripts/service_manager/basic_usage.rhai
Normal file
85
examples/scripts/service_manager/basic_usage.rhai
Normal file
@ -0,0 +1,85 @@
|
||||
// Basic Service Manager Usage Example
|
||||
//
|
||||
// This example demonstrates the basic API of the service manager.
|
||||
// It works on both macOS (launchctl) and Linux (zinit/systemd).
|
||||
//
|
||||
// Prerequisites:
|
||||
//
|
||||
// Linux: The service manager will automatically discover running zinit servers
|
||||
// or fall back to systemd. To use zinit, start it with:
|
||||
// zinit -s /tmp/zinit.sock init
|
||||
//
|
||||
// You can also specify a custom socket path:
|
||||
// export ZINIT_SOCKET_PATH=/your/custom/path/zinit.sock
|
||||
//
|
||||
// macOS: No additional setup required (uses launchctl).
|
||||
//
|
||||
// Usage:
|
||||
// herodo examples/service_manager/basic_usage.rhai
|
||||
|
||||
// Service Manager Basic Usage Example
|
||||
// This example uses the SAL service manager through Rhai integration
|
||||
|
||||
print("🚀 Basic Service Manager Usage Example");
|
||||
print("======================================");
|
||||
|
||||
// Create a service manager for the current platform
|
||||
let manager = create_service_manager();
|
||||
|
||||
print("🍎 Using service manager for current platform");
|
||||
|
||||
// Create a simple service configuration
|
||||
let config = #{
|
||||
name: "example-service",
|
||||
binary_path: "/bin/echo",
|
||||
args: ["Hello from service manager!"],
|
||||
working_directory: "/tmp",
|
||||
environment: #{
|
||||
"EXAMPLE_VAR": "hello_world"
|
||||
},
|
||||
auto_restart: false
|
||||
};
|
||||
|
||||
print("\n📝 Service Configuration:");
|
||||
print(` Name: ${config.name}`);
|
||||
print(` Binary: ${config.binary_path}`);
|
||||
print(` Args: ${config.args}`);
|
||||
|
||||
// Start the service
|
||||
print("\n🚀 Starting service...");
|
||||
start(manager, config);
|
||||
print("✅ Service started successfully");
|
||||
|
||||
// Check service status
|
||||
print("\n📊 Checking service status...");
|
||||
let status = status(manager, "example-service");
|
||||
print(`Status: ${status}`);
|
||||
|
||||
// List all services
|
||||
print("\n📋 Listing all managed services...");
|
||||
let services = list(manager);
|
||||
print(`Found ${services.len()} services:`);
|
||||
for service in services {
|
||||
print(` - ${service}`);
|
||||
}
|
||||
|
||||
// Get service logs
|
||||
print("\n📄 Getting service logs...");
|
||||
let logs = logs(manager, "example-service", 5);
|
||||
if logs.trim() == "" {
|
||||
print("No logs available");
|
||||
} else {
|
||||
print(`Logs:\n${logs}`);
|
||||
}
|
||||
|
||||
// Stop the service
|
||||
print("\n🛑 Stopping service...");
|
||||
stop(manager, "example-service");
|
||||
print("✅ Service stopped");
|
||||
|
||||
// Remove the service
|
||||
print("\n🗑️ Removing service...");
|
||||
remove(manager, "example-service");
|
||||
print("✅ Service removed");
|
||||
|
||||
print("\n🎉 Example completed successfully!");
|
141
examples/scripts/service_manager/circle_worker_manager.rhai
Normal file
141
examples/scripts/service_manager/circle_worker_manager.rhai
Normal file
@ -0,0 +1,141 @@
|
||||
// Circle Worker Manager Example
|
||||
//
|
||||
// This example demonstrates how to use the service manager to dynamically launch
|
||||
// circle workers for new freezone residents. This is the primary use case requested
|
||||
// by the team.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// On macOS (uses launchctl):
|
||||
// herodo examples/service_manager/circle_worker_manager.rhai
|
||||
//
|
||||
// On Linux (uses zinit - requires zinit to be running):
|
||||
// First start zinit: zinit -s /tmp/zinit.sock init
|
||||
// herodo examples/service_manager/circle_worker_manager.rhai
|
||||
|
||||
// Circle Worker Manager Example
|
||||
// This example uses the SAL service manager through Rhai integration
|
||||
|
||||
print("🚀 Circle Worker Manager Example");
|
||||
print("=================================");
|
||||
|
||||
// Create the appropriate service manager for the current platform
|
||||
let service_manager = create_service_manager();
|
||||
print("✅ Created service manager for current platform");
|
||||
|
||||
// Simulate a new freezone resident registration
|
||||
let resident_id = "resident_12345";
|
||||
let worker_name = `circle-worker-${resident_id}`;
|
||||
|
||||
print(`\n📝 New freezone resident registered: ${resident_id}`);
|
||||
print(`🔧 Creating circle worker service: ${worker_name}`);
|
||||
|
||||
// Create service configuration for the circle worker
|
||||
let config = #{
|
||||
name: worker_name,
|
||||
binary_path: "/bin/sh",
|
||||
args: [
|
||||
"-c",
|
||||
`echo 'Circle worker for ${resident_id} starting...'; sleep 30; echo 'Circle worker for ${resident_id} completed'`
|
||||
],
|
||||
working_directory: "/tmp",
|
||||
environment: #{
|
||||
"RESIDENT_ID": resident_id,
|
||||
"WORKER_TYPE": "circle",
|
||||
"LOG_LEVEL": "info"
|
||||
},
|
||||
auto_restart: true
|
||||
};
|
||||
|
||||
print("📋 Service configuration created:");
|
||||
print(` Name: ${config.name}`);
|
||||
print(` Binary: ${config.binary_path}`);
|
||||
print(` Args: ${config.args}`);
|
||||
print(` Auto-restart: ${config.auto_restart}`);
|
||||
|
||||
print(`\n🔄 Demonstrating service lifecycle for: ${worker_name}`);
|
||||
|
||||
// 1. Check if service already exists
|
||||
print("\n1️⃣ Checking if service exists...");
|
||||
if exists(service_manager, worker_name) {
|
||||
print("⚠️ Service already exists, removing it first...");
|
||||
remove(service_manager, worker_name);
|
||||
print("🗑️ Existing service removed");
|
||||
} else {
|
||||
print("✅ Service doesn't exist, ready to create");
|
||||
}
|
||||
|
||||
// 2. Start the service
|
||||
print("\n2️⃣ Starting the circle worker service...");
|
||||
start(service_manager, config);
|
||||
print("✅ Service started successfully");
|
||||
|
||||
// 3. Check service status
|
||||
print("\n3️⃣ Checking service status...");
|
||||
let status = status(service_manager, worker_name);
|
||||
print(`📊 Service status: ${status}`);
|
||||
|
||||
// 4. List all services to show our service is there
|
||||
print("\n4️⃣ Listing all managed services...");
|
||||
let services = list(service_manager);
|
||||
print(`📋 Managed services (${services.len()}):`);
|
||||
for service in services {
|
||||
let marker = if service == worker_name { "👉" } else { " " };
|
||||
print(` ${marker} ${service}`);
|
||||
}
|
||||
|
||||
// 5. Wait a moment and check status again
|
||||
print("\n5️⃣ Waiting 3 seconds and checking status again...");
|
||||
sleep(3000); // 3 seconds in milliseconds
|
||||
let status = status(service_manager, worker_name);
|
||||
print(`📊 Service status after 3s: ${status}`);
|
||||
|
||||
// 6. Get service logs
|
||||
print("\n6️⃣ Retrieving service logs...");
|
||||
let logs = logs(service_manager, worker_name, 10);
|
||||
if logs.trim() == "" {
|
||||
print("📄 No logs available yet (this is normal for new services)");
|
||||
} else {
|
||||
print("📄 Recent logs:");
|
||||
let log_lines = logs.split('\n');
|
||||
for i in 0..5 {
|
||||
if i < log_lines.len() {
|
||||
print(` ${log_lines[i]}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Demonstrate start_and_confirm with timeout
|
||||
print("\n7️⃣ Testing start_and_confirm (should succeed quickly since already running)...");
|
||||
start_and_confirm(service_manager, config, 5);
|
||||
print("✅ Service confirmed running within timeout");
|
||||
|
||||
// 8. Stop the service
|
||||
print("\n8️⃣ Stopping the service...");
|
||||
stop(service_manager, worker_name);
|
||||
print("🛑 Service stopped");
|
||||
|
||||
// 9. Check status after stopping
|
||||
print("\n9️⃣ Checking status after stop...");
|
||||
let status = status(service_manager, worker_name);
|
||||
print(`📊 Service status after stop: ${status}`);
|
||||
|
||||
// 10. Restart the service
|
||||
print("\n🔟 Restarting the service...");
|
||||
restart(service_manager, worker_name);
|
||||
print("🔄 Service restarted successfully");
|
||||
|
||||
// 11. Final cleanup
|
||||
print("\n🧹 Cleaning up - removing the service...");
|
||||
remove(service_manager, worker_name);
|
||||
print("🗑️ Service removed successfully");
|
||||
|
||||
// 12. Verify removal
|
||||
print("\n✅ Verifying service removal...");
|
||||
if !exists(service_manager, worker_name) {
|
||||
print("✅ Service successfully removed");
|
||||
} else {
|
||||
print("⚠️ Service still exists after removal");
|
||||
}
|
||||
|
||||
print("\n🎉 Circle worker management demonstration complete!");
|
4
examples/scripts/simple.rhai
Normal file
4
examples/scripts/simple.rhai
Normal file
@ -0,0 +1,4 @@
|
||||
print("Hello, World!");
|
||||
2+2;
|
||||
|
||||
mycelium_list_peers("adfsdv");
|
78
examples/scripts/zinit/zinit_basic.rhai
Normal file
78
examples/scripts/zinit/zinit_basic.rhai
Normal file
@ -0,0 +1,78 @@
|
||||
// Basic example of using the Zinit client in Rhai
|
||||
|
||||
// Socket path for Zinit
|
||||
let socket_path = "/tmp/zinit.sock";
|
||||
|
||||
// List all services
|
||||
print("Listing all services:");
|
||||
let services = zinit_list(socket_path);
|
||||
|
||||
if services.is_empty() {
|
||||
print("No services found.");
|
||||
} else {
|
||||
// Iterate over the keys of the map
|
||||
for name in services.keys() {
|
||||
let state = services[name];
|
||||
print(`${name}: ${state}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Get status of a specific service
|
||||
let service_name = "test";
|
||||
print(`Getting status for ${service_name}:`);
|
||||
|
||||
try {
|
||||
let status = zinit_status(socket_path, service_name);
|
||||
print(`Service: ${status.name}`);
|
||||
print(`PID: ${status.pid}`);
|
||||
print(`State: ${status.state}`);
|
||||
print(`Target: ${status.target}`);
|
||||
print("Dependencies:");
|
||||
|
||||
for (dep, state) in status.after.keys() {
|
||||
print(` ${dep}: ${state}`);
|
||||
}
|
||||
} catch(err) {
|
||||
print(`Error getting status: ${err}`);
|
||||
}
|
||||
|
||||
// Create a new service
|
||||
print("\nCreating a new service:");
|
||||
let new_service = "rhai-test-service";
|
||||
let exec_command = "echo 'Hello from Rhai'";
|
||||
let oneshot = true;
|
||||
|
||||
try {
|
||||
let result = zinit_create_service(socket_path, new_service, exec_command, oneshot);
|
||||
print(`Service created: ${result}`);
|
||||
|
||||
// Monitor the service
|
||||
print("\nMonitoring the service:");
|
||||
let monitor_result = zinit_monitor(socket_path, new_service);
|
||||
print(`Service monitored: ${monitor_result}`);
|
||||
|
||||
// Start the service
|
||||
print("\nStarting the service:");
|
||||
let start_result = zinit_start(socket_path, new_service);
|
||||
print(`Service started: ${start_result}`);
|
||||
|
||||
// Get logs for a specific service
|
||||
print("\nGetting logs:");
|
||||
let logs = zinit_logs(socket_path, new_service);
|
||||
|
||||
for log in logs {
|
||||
print(log);
|
||||
}
|
||||
// Clean up
|
||||
print("\nCleaning up:");
|
||||
let stop_result = zinit_stop(socket_path, new_service);
|
||||
print(`Service stopped: ${stop_result}`);
|
||||
|
||||
let forget_result = zinit_forget(socket_path, new_service);
|
||||
print(`Service forgotten: ${forget_result}`);
|
||||
|
||||
let delete_result = zinit_delete_service(socket_path, new_service);
|
||||
print(`Service deleted: ${delete_result}`);
|
||||
} catch(err) {
|
||||
print(`Error: ${err}`);
|
||||
}
|
41
examples/scripts/zinit/zinit_basic2.rhai
Normal file
41
examples/scripts/zinit/zinit_basic2.rhai
Normal file
@ -0,0 +1,41 @@
|
||||
// Basic example of using the Zinit client in Rhai
|
||||
|
||||
// Socket path for Zinit
|
||||
let socket_path = "/tmp/zinit.sock";
|
||||
|
||||
// Create a new service
|
||||
print("\nCreating a new service:");
|
||||
let new_service = "rhai-test-service";
|
||||
let exec_command = "echo 'Hello from Rhai'";
|
||||
let oneshot = true;
|
||||
|
||||
let result = zinit_create_service(socket_path, new_service, exec_command, oneshot);
|
||||
print(`Service created: ${result}`);
|
||||
|
||||
// Monitor the service
|
||||
print("\nMonitoring the service:");
|
||||
let monitor_result = zinit_monitor(socket_path, new_service);
|
||||
print(`Service monitored: ${monitor_result}`);
|
||||
|
||||
// Start the service
|
||||
print("\nStarting the service:");
|
||||
let start_result = zinit_start(socket_path, new_service);
|
||||
print(`Service started: ${start_result}`);
|
||||
|
||||
// Get logs for a specific service
|
||||
print("\nGetting logs:");
|
||||
let logs = zinit_logs(socket_path, new_service);
|
||||
|
||||
for log in logs {
|
||||
print(log);
|
||||
}
|
||||
// Clean up
|
||||
print("\nCleaning up:");
|
||||
let stop_result = zinit_stop(socket_path, new_service);
|
||||
print(`Service stopped: ${stop_result}`);
|
||||
|
||||
let forget_result = zinit_forget(socket_path, new_service);
|
||||
print(`Service forgotten: ${forget_result}`);
|
||||
|
||||
let delete_result = zinit_delete_service(socket_path, new_service);
|
||||
print(`Service deleted: ${delete_result}`);
|
159
src/engine.rs
159
src/engine.rs
@ -1,19 +1,4 @@
|
||||
//! Rhai scripting integration for the SAL library
|
||||
//!
|
||||
//! This module provides integration with the Rhai scripting language,
|
||||
//! allowing SAL functions to be called from Rhai scripts.
|
||||
|
||||
// OS module is now provided by sal-os package
|
||||
// Platform module is now provided by sal-os package
|
||||
// PostgreSQL module is now provided by sal-postgresclient package
|
||||
|
||||
// Virt modules (buildah, nerdctl, rfs) are now provided by sal-virt package
|
||||
// vault module is now provided by sal-vault package
|
||||
// zinit module is now in sal-zinit-client package
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::sync::{Arc, OnceLock};
|
||||
// Re-export common Rhai types for convenience
|
||||
pub use rhai::{Array, Dynamic, Engine, EvalAltResult, Map};
|
||||
|
||||
@ -75,113 +60,73 @@ pub use sal_virt::rhai::{
|
||||
bah_new, register_bah_module, register_nerdctl_module, register_rfs_module,
|
||||
};
|
||||
|
||||
// Re-export git module from sal-git package
|
||||
pub use sal_git::rhai::register_git_module;
|
||||
pub use sal_git::{GitRepo, GitTree};
|
||||
|
||||
// Re-export zinit module from sal-zinit-client package
|
||||
pub use sal_zinit_client::rhai::register_zinit_module;
|
||||
|
||||
// Re-export mycelium module
|
||||
pub use sal_mycelium::rhai::register_mycelium_module;
|
||||
|
||||
// Re-export text module
|
||||
pub use sal_text::rhai::register_text_module;
|
||||
|
||||
// Re-export net module
|
||||
pub use sal_net::rhai::register_net_module;
|
||||
|
||||
// Re-export crypto module - TEMPORARILY DISABLED
|
||||
// TODO: Implement rhai module for Lee's vault implementation
|
||||
// pub use sal_vault::rhai::register_crypto_module;
|
||||
|
||||
// Re-export kubernetes module
|
||||
pub use sal_kubernetes::rhai::register_kubernetes_module;
|
||||
pub use sal_kubernetes::KubernetesManager;
|
||||
|
||||
// Re-export service manager module
|
||||
pub use sal_service_manager::rhai::register_service_manager_module;
|
||||
|
||||
// Rename copy functions to avoid conflicts
|
||||
pub use sal_os::rhai::copy as os_copy;
|
||||
pub use sal_hetzner::rhai::register_hetzner_module;
|
||||
|
||||
/// Register all SAL modules with the Rhai engine
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `engine` - The Rhai engine to register the modules with
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// use rhai::Engine;
|
||||
/// use sal::rhai;
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
/// rhai::register(&mut engine);
|
||||
///
|
||||
/// // Now you can use SAL functions in Rhai scripts
|
||||
/// // You can evaluate Rhai scripts with SAL functions
|
||||
/// let result = engine.eval::<i64>("exist('some_file.txt')").unwrap();
|
||||
/// ```
|
||||
pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
// Register Core module functions
|
||||
core::register_core_module(engine)?;
|
||||
/// Engine factory for creating and sharing Rhai engines.
|
||||
pub struct EngineFactory {
|
||||
engine: Arc<Engine>,
|
||||
}
|
||||
|
||||
// Register OS module functions
|
||||
sal_os::rhai::register_os_module(engine)?;
|
||||
impl EngineFactory {
|
||||
/// Create a new engine factory with a configured Rhai engine.
|
||||
pub fn new() -> Self {
|
||||
let mut engine = Engine::new();
|
||||
register_sal_modules(&mut engine);
|
||||
// Logger
|
||||
hero_logger::rhai_integration::configure_rhai_logging(&mut engine, "osis_actor");
|
||||
|
||||
// Register Process module functions
|
||||
sal_process::rhai::register_process_module(engine)?;
|
||||
Self {
|
||||
engine: Arc::new(engine),
|
||||
}
|
||||
}
|
||||
|
||||
// Register Virt module functions (Buildah, Nerdctl, RFS)
|
||||
sal_virt::rhai::register_virt_module(engine)?;
|
||||
/// Get a shared reference to the engine.
|
||||
pub fn get_engine(&self) -> Arc<Engine> {
|
||||
Arc::clone(&self.engine)
|
||||
}
|
||||
|
||||
// Register Git module functions
|
||||
sal_git::rhai::register_git_module(engine)?;
|
||||
/// Get the global singleton engine factory.
|
||||
pub fn global() -> &'static EngineFactory {
|
||||
static FACTORY: OnceLock<EngineFactory> = OnceLock::new();
|
||||
FACTORY.get_or_init(|| EngineFactory::new())
|
||||
}
|
||||
}
|
||||
|
||||
// Register Zinit module functions
|
||||
sal_zinit_client::rhai::register_zinit_module(engine)?;
|
||||
pub fn register_sal_modules(engine: &mut Engine) {
|
||||
sal_os::rhai::register_os_module(engine);
|
||||
sal_redisclient::rhai::register_redisclient_module(engine);
|
||||
sal_postgresclient::rhai::register_postgresclient_module(engine);
|
||||
sal_process::rhai::register_process_module(engine);
|
||||
sal_virt::rhai::register_virt_module(engine);
|
||||
sal_git::rhai::register_git_module(engine);
|
||||
sal_zinit_client::rhai::register_zinit_module(engine);
|
||||
sal_mycelium::rhai::register_mycelium_module(engine);
|
||||
sal_text::rhai::register_text_module(engine);
|
||||
sal_net::rhai::register_net_module(engine);
|
||||
sal_kubernetes::rhai::register_kubernetes_module(engine);
|
||||
sal_hetzner::rhai::register_hetzner_module(engine);
|
||||
|
||||
// Register Mycelium module functions
|
||||
sal_mycelium::rhai::register_mycelium_module(engine)?;
|
||||
println!("SAL modules registered successfully.");
|
||||
}
|
||||
|
||||
// Register Text module functions
|
||||
sal_text::rhai::register_text_module(engine)?;
|
||||
/// Create a shared heromodels engine using the factory.
|
||||
pub fn create_system_engine() -> Arc<Engine> {
|
||||
EngineFactory::global().get_engine()
|
||||
}
|
||||
|
||||
// Register Net module functions
|
||||
sal_net::rhai::register_net_module(engine)?;
|
||||
|
||||
// RFS module functions are now registered as part of sal_virt above
|
||||
|
||||
// Register Crypto module functions - TEMPORARILY DISABLED
|
||||
// TODO: Implement rhai module for Lee's vault implementation
|
||||
// register_crypto_module(engine)?;
|
||||
|
||||
// Register Kubernetes module functions
|
||||
register_kubernetes_module(engine)?;
|
||||
|
||||
// Register Redis client module functions
|
||||
sal_redisclient::rhai::register_redisclient_module(engine)?;
|
||||
|
||||
// Register PostgreSQL client module functions
|
||||
sal_postgresclient::rhai::register_postgresclient_module(engine)?;
|
||||
|
||||
// Register Service Manager module functions
|
||||
sal_service_manager::rhai::register_service_manager_module(engine)?;
|
||||
|
||||
// Platform functions are now registered by sal-os package
|
||||
|
||||
// Screen module functions are now part of sal-process package
|
||||
|
||||
// Register utility functions
|
||||
engine.register_fn("is_def_fn", |_name: &str| -> bool {
|
||||
// This is a utility function to check if a function is defined in the engine
|
||||
// For testing purposes, we'll just return true
|
||||
true
|
||||
});
|
||||
|
||||
// Future modules can be registered here
|
||||
|
||||
Ok(())
|
||||
/// Evaluate a Rhai script string.
|
||||
pub fn eval_script(
|
||||
engine: &Engine,
|
||||
script: &str,
|
||||
) -> Result<rhai::Dynamic, Box<rhai::EvalAltResult>> {
|
||||
engine.eval(script)
|
||||
}
|
18
src/lib.rs
18
src/lib.rs
@ -1,3 +1,9 @@
|
||||
mod engine;
|
||||
|
||||
// Public exports
|
||||
pub use engine::register_sal_modules;
|
||||
pub use engine::create_system_engine;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use hero_job::{Job, JobStatus};
|
||||
use log::{debug, error, info, warn};
|
||||
@ -9,7 +15,6 @@ use tokio::sync::{mpsc, Mutex};
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::time::timeout;
|
||||
use baobab_actor::{actor_trait::Actor, spawn_actor, initialize_redis_connection};
|
||||
mod engine;
|
||||
|
||||
/// Represents a running job with its handle and metadata
|
||||
#[derive(Debug)]
|
||||
@ -241,11 +246,7 @@ impl Default for AsyncWorker {
|
||||
|
||||
#[async_trait]
|
||||
impl Actor for AsyncWorker {
|
||||
async fn process_job(
|
||||
&self,
|
||||
job: hero_job::Job,
|
||||
_redis_conn: &mut redis::aio::MultiplexedConnection,
|
||||
) {
|
||||
async fn process_job(&self, job: hero_job::Job, _redis_conn: &mut redis::aio::MultiplexedConnection) {
|
||||
let job_id = job.id.clone();
|
||||
let actor_id = &self.actor_id.clone();
|
||||
|
||||
@ -272,10 +273,7 @@ impl Actor for AsyncWorker {
|
||||
// Create engine for this job - we need to get it from somewhere
|
||||
// For now, let's assume we need to create a new engine instance
|
||||
let mut engine = rhai::Engine::new();
|
||||
if let Err(e) = register_engine(&mut engine) {
|
||||
error!("Failed to register engine modules: {}", e);
|
||||
return;
|
||||
}
|
||||
engine::register_sal_modules(&mut engine);
|
||||
|
||||
Self::execute_job_with_timeout(
|
||||
job,
|
||||
|
Loading…
Reference in New Issue
Block a user