diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 0000000..fa1050b --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +Dockerfile diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..f8697af --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "metaparticle" +version = "0.0.1" + +[[example]] +name = "hello" +path = "examples/hello.rs" + +[[example]] +name = "web" +path = "examples/web.rs" + +[dependencies] \ No newline at end of file diff --git a/rust/README.md b/rust/README.md new file mode 100644 index 0000000..80f44d4 --- /dev/null +++ b/rust/README.md @@ -0,0 +1,52 @@ +# Metaparticle for Rust +Metaparticle/Package is a collection of libraries intended to +make building and deploying containers a seamless and idiomatic +experience for developers. + +This is the implementation for Rust. + +## Introduction +Metaparticle/Package simplifies and centralizes the task of +building and deploying a container image. + +Here is a quick example. + +Consider this simple Rust application: +```rust +fn main() { + println!("Hello World!"); +} +``` + +To containerize this application, you need to use the `metaparticle` crate and +the `containerize` wrapper function like this: + +```rust +fn run() { + println!("Hello World!"); +} + +fn main() { + let runtime = metaparticle::Runtime{ + ..Default::default() + }; + let package = metaparticle::Package{ + name: "hello".to_string(), + ..Default::default() + }; + metaparticle::containerize(run, runtime, package) +} +``` + +When you run this application, instead of printing "Hello world", it first packages itself as a container, and +then (optionally) deploys itself inside that container. + +## Tutorial + +```bash +git clone https://github.com/metaparticle-io/package/ +cd package/rust + +cargo build --example hello +./target/debug/examples/hello +``` \ No newline at end of file diff --git a/rust/examples/hello.rs b/rust/examples/hello.rs new file mode 100644 index 0000000..8d0145b --- /dev/null +++ b/rust/examples/hello.rs @@ -0,0 +1,17 @@ +extern crate metaparticle; + +fn run() { + println!("Hello World!"); +} + +fn main() { + let runtime = metaparticle::Runtime{ + ..Default::default() + }; + let package = metaparticle::Package{ + name: "hello".to_string(), + repository: "brendanburns".to_string(), + ..Default::default() + }; + metaparticle::containerize(run, runtime, package) +} \ No newline at end of file diff --git a/rust/examples/web.rs b/rust/examples/web.rs new file mode 100644 index 0000000..8a9645a --- /dev/null +++ b/rust/examples/web.rs @@ -0,0 +1,61 @@ +extern crate metaparticle; + +use std::net::{TcpStream, TcpListener}; +use std::io::{Read, Write}; +use std::thread; + + +fn handle_read(mut stream: &TcpStream) { + let mut buf = [0u8 ;4096]; + match stream.read(&mut buf) { + Ok(_) => { + let req_str = String::from_utf8_lossy(&buf); + println!("{}", req_str); + }, + Err(e) => println!("Unable to read stream: {}", e), + } +} + +fn handle_write(mut stream: TcpStream) { + let response = b"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
Hello world\r\n"; + match stream.write(response) { + Ok(_) => println!("Response sent"), + Err(e) => println!("Failed sending response: {}", e), + } +} + +fn handle_client(stream: TcpStream) { + handle_read(&stream); + handle_write(stream); +} + +fn run() { + let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); + println!("Listening for connections on port {}", 8080); + + for stream in listener.incoming() { + match stream { + Ok(stream) => { + thread::spawn(|| { + handle_client(stream) + }); + } + Err(e) => { + println!("Unable to connect: {}", e); + } + } + } +} + +fn main() { + let runtime = metaparticle::Runtime{ + ports: Some(8080), + ..Default::default() + }; + let package = metaparticle::Package{ + name: "web".to_string(), + repository: "brendanburns".to_string(), + ..Default::default() + }; + metaparticle::containerize(run, runtime, package) +} \ No newline at end of file diff --git a/rust/src/builder/docker.rs b/rust/src/builder/docker.rs new file mode 100644 index 0000000..83f6e7e --- /dev/null +++ b/rust/src/builder/docker.rs @@ -0,0 +1,13 @@ +use super::run_docker_process; +use Builder; + +pub struct DockerBuilder{} + +impl Builder for DockerBuilder { + fn build(&self, dir: &str, image: &str) { + run_docker_process(vec!["docker", "build", &*format!("-t{}", image), dir]); + } + fn push(&self, image: &str) { + run_docker_process(vec!["docker", "push", image]); + } +} \ No newline at end of file diff --git a/rust/src/builder/mod.rs b/rust/src/builder/mod.rs new file mode 100644 index 0000000..17e443f --- /dev/null +++ b/rust/src/builder/mod.rs @@ -0,0 +1,8 @@ +pub mod docker; +use super::run_docker_process; + + +pub trait Builder { + fn build(&self, dir: &str, image: &str); + fn push(&self, image: &str); +} \ No newline at end of file diff --git a/rust/src/executor/docker.rs b/rust/src/executor/docker.rs new file mode 100644 index 0000000..31610af --- /dev/null +++ b/rust/src/executor/docker.rs @@ -0,0 +1,31 @@ +use super::run_docker_process; +use super::Runtime; +use Executor; + +pub struct DockerExecutor{} + +impl Executor for DockerExecutor { + fn cancel(&self, name: &str) { + run_docker_process(vec!["docker", "stop", name]); + run_docker_process(vec!["docker", "rm", "-f", name]); + } + + fn logs(&self, name: &str) { + run_docker_process(vec!["docker", "logs", "-f", name]); + } + + fn run(&self, image: &str, name: &str, config: Runtime) { + let mut ports = String::new(); + let mut args = vec!["docker", "run", "-d", "--rm", "--name", name]; + + if let Some(port) = config.ports { + ports.push_str(&format!("-p {port}", port=port)); + args.push(&ports); + } + + args.push(image); + + run_docker_process(args); + } +} + diff --git a/rust/src/executor/mod.rs b/rust/src/executor/mod.rs new file mode 100644 index 0000000..9221027 --- /dev/null +++ b/rust/src/executor/mod.rs @@ -0,0 +1,9 @@ +pub mod docker; +use super::Runtime; +use super::run_docker_process; + +pub trait Executor { + fn cancel(&self, name: &str); + fn logs(&self, name: &str); + fn run(&self, image: &str, name: &str, config: Runtime); +} \ No newline at end of file diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..532752f --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,166 @@ + +mod builder; +mod executor; + +use builder::Builder; +use executor::Executor; + +use std::env; +use std::error::Error; +use std::fs::File; +use std::ffi::OsStr; +use std::io::prelude::*; +use std::path::Path; +use std::process; + +#[derive(Debug)] +pub struct Runtime { + pub replicas: Option