A Software Contraption

With Docker Compose

Physical Objects

Docker Containers

$ docker pull redis
$ docker run -dP redis
f965bea89b312e77184a9b59b2d41827d99c34de97d179420d77...
$ docker ps -l
CONTAINER ID      IMAGE         PORTS
f965bea89b31      redis         0.0.0.0:32769->6379/tcp
version: '2'
services:
  rust:
    image: contraption/rust
    ports: 
    - 3500:3500
    links:
    - redis
    working_dir: /rust/projects/rusty_redis
    entrypoint: ./target/debug/rusty_redis
  clojure:
    image: contraption/clojure
    links:
    - rabbitmq
    - redis
    working_dir: /root/projects/bringing_clojure
    entrypoint: lein run
  golang:
    image: contraption/golang
    links:
    - rabbitmq
    working_dir: /projects/gofire
    entrypoint: go run gofire.go
  redis:
    image: redis
  rabbitmq:
    image: rabbitmq

Fast

Safe

Systems

$ docker run -it --entrypoint '/bin/sh' scorpil/rust
# container running...
$ mkdir -p projects/hello_world
$ cd projects/hello_world
$ nano main.rs

fn main() {
  println!("Hello, World!");
}

$ rustc main.rs
$ ./main
Hello, World!
extern crate redis;
use redis::Commands;
use std::net::{TcpListener, TcpStream};
use std::thread;
use std::io::prelude::*;
use std::io::BufReader;


fn handle_client(mut stream: TcpStream) -> redis::RedisResult<()> {

    let client = try!(redis::Client::open("redis://redis/"));
    let con = try!(client.get_connection());

    let mut buffer = String::new();
    let mut reader = BufReader::new(stream);

    while reader.read_line(&mut buffer).unwrap() > 0 {

        let s = format!("{}, Rust!", buffer.trim());
        println!("Received string: {} ..", s);
        buffer.clear();

        let _ = try!(con.set("rusty_key", s));
    }

    Ok(())
}

fn main() {

    let listener = TcpListener::bind("0.0.0.0:3500").unwrap();

    // accept connections and process them, spawning a new thread for each one
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                thread::spawn(move|| {
                    println!("Client connected!");
                    let _ = handle_client(stream);
                });
            }
            Err(e) => {
                println!("client errored! {}", e);
            }
        }
    }

    // close the socket server
    drop(listener);

Store Key

Publish

Functional

Simple

Fast

$ docker run -it clojure
# container running...
$ mkdir -p projects
$ cd projects
$ lein new app hello_world
$ cd hello_world
$ lein run
Hello, World!
(ns bringing-clojure.core
  (:gen-class))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))
(ns bringing-clojure.core
  (:require [taoensso.carmine :as car :refer (wcar)])
  (:require [langohr.core     :as rmq])
  (:require [langohr.channel  :as lch])
  (:require [langohr.queue    :as lq])
  (:require [langohr.basic    :as lb])
  (:gen-class))

;; redis setup
(def redis-conn {:pool {} :spec {:uri "redis://redis/"}})
(defmacro wcar* [& body] `(car/wcar redis-conn ~@body))

;; data passing
(defn feed-rabbit
  [v]
  (let [conn  (rmq/connect {:host "rabbitmq"})
        ch    (lch/open conn)
        qname "go-rabbit"]
    (lq/declare ch qname {:exclusive false :auto-delete true})
    (lb/publish ch "" qname (format "%s Clojure!" v) {:content-type "text/plain"})
    (rmq/close ch)
    (rmq/close conn)))

(defn fetch-rusty-key
  []
  (if-let [v (wcar* (car/get "rusty_key"))]
    (do
    (wcar* (car/set "rusty_key" nil))
    v)))

;; main
(defn -main
  "setup, poll redis, feed rabbit, rise-and-repeat."
  [& args]
  (while true
    (Thread/sleep 10)
    (if-let [v (fetch-rusty-key)]
      (do
      (println "found rusty value!" v)
      (feed-rabbit v)))))

Portable

Concurrent

Fast

$ docker run -it golang
# container running...
$ mkdir -p projects/hello_world
$ cd projects/hello_world
$ nano hello_world.go

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

$ go run hello_world.go
Hello, World!
package main

import (
  "log"
  "time"
  "github.com/streadway/amqp"
)

func main() {

    log.Printf(" [*] Go-ing to sleep ")
    time.Sleep(10000 * time.Millisecond)
    log.Printf(" [*] Time to get to work!")

    conn, _ := amqp.Dial("amqp://guest:guest@rabbitmq/")
    defer conn.Close()

    ch, _ := conn.Channel()
    defer ch.Close()

    q, _ := ch.QueueDeclare(
      "go-rabbit",  // name
      false,        // durable
      false,        // delete when unused
      false,        // exclusive
      false,        // no-wait
      nil,          // arguments
    )

    msgs, _ := ch.Consume(
      q.Name, // queue
      "",     // consumer
      true,   // auto-ack
      false,  // exclusive
      false,  // no-local
      false,  // no-wait
      nil,    // args
    )

    forever := make(chan bool)

    go func() {
      for d := range msgs {
        log.Printf("Received a message: %s", d.Body)
      }
    }()

    log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
    <-forever
}

Honorable Mentions

Software Contraption

By mazyod

Software Contraption

During this talk, we'll demo a contraption built with software, using the latest technologies available.

  • 839