A Cloud at the lowest level!

Purpose

CloudI provides a Cloud for software developers without requiring virtualization.

Usage

CloudI is for back-end server processing tasks that require soft-realtime transaction processing external to database usage (in ATS, C/C++, Erlang/Elixir, Go, Haskell, Java, JavaScript/node.js, OCaml, Perl, PHP, Python, Ruby and Rust). Examples include:

Quick Start

C
  1. A CloudI service written in C is called an "external" service because the service is ran inside an Operating System process (external to the Erlang VM). The example C service can be created by executing the following inside your shell:
    cat << EOF > hello_world.c
    #include "cloudi.h"
    #include <string.h>
    #include <assert.h>
    static void hello_world(int const request_type,
                            char const * const name,
                            char const * const pattern,
                            void const * const request_info,
                            uint32_t const request_info_size,
                            void const * const request,
                            uint32_t const request_size,
                            uint32_t timeout,
                            int8_t priority,
                            char const * const trans_id,
                            char const * const pid,
                            uint32_t const pid_size,
                            void * state,
                            cloudi_instance_t * api)
    {
        char const * const message = "Hello World!";
        uint32_t const message_size = strlen(message);
        cloudi_return(api, request_type, name, pattern, "", 0,
                      message, message_size,
                      timeout, trans_id, pid, pid_size);
    }
    int main(int argc, char ** argv)
    {
        unsigned int thread_count;
        int result = cloudi_initialize_thread_count(&thread_count);
        assert(result == cloudi_success);
        assert(thread_count == 1);
        cloudi_instance_t api;
        result = cloudi_initialize(&api, 0, 0);
        assert(result == cloudi_success);
        result = cloudi_subscribe(&api, "hello_world/get",
                                  &hello_world);
        assert(result == cloudi_success);
        result = cloudi_poll(&api, -1);
        cloudi_destroy(&api);
        return result;
    }
    EOF
    
  2. Compile the CloudI service executable:
    gcc -I/usr/local/lib/cloudi-2.0.7/api/c \
        -L/usr/local/lib/cloudi-2.0.7/api/c \
        -g -O0 -fexceptions hello_world.c -o hello_world_c -lcloudi
    
  3. Now it is necessary to create the CloudI service configuration that specifies both the initialization and fault-tolerance constraints the CloudI service should be executed with (with the proplist format to rely on defaults):
    export PWD=`pwd`
    cat << EOF > hello_world.conf
    [[{prefix, "/quickstart/c/"},
      {file_path, "$PWD/hello_world_c"},
      {env, [{"LD_LIBRARY_PATH",
              "/usr/local/lib/cloudi-2.0.7/api/c/"},
             {"DYLD_LIBRARY_PATH",
              "/usr/local/lib/cloudi-2.0.7/api/c/"}]}]]
    EOF
    
  4. To dynamically add the CloudI service configuration that starts the service's execution use:
    curl -X POST -d @hello_world.conf \
        http://localhost:6464/cloudi/api/rpc/services_add.erl
    
  5. The curl requests have been using the cowboy HTTP server that is running within the default CloudI configuration to allow the CloudI Service API to be used over HTTP. The same HTTP server can be used to make a CloudI service request to the hello_world service with:
    curl http://localhost:6464/quickstart/c/hello_world
    
  6. If there was a problem during the service creation there would be an ERROR entry within the /usr/local/var/log/cloudi/cloudi.log file. If an error occurred with a curl command it would be displayed in the shell. The available service configuration parameters are described in the services_add documentation. More complex C examples are listed here.
C++
  1. A CloudI service written in C++ is called an "external" service because the service is ran inside an Operating System process (external to the Erlang VM). The example C++ service can be created by executing the following inside your shell:
    cat << EOF > hello_world.cpp
    #include "cloudi.hpp"
    #include <string>
    #include <cassert>
    class Task
    {
        public:
            Task(unsigned int const thread_index) :
                m_api(thread_index)
            {
                int result = 0;
                result = m_api.subscribe("hello_world/get",
                                         *this, &Task::hello_world);
                assert(result == CloudI::API::return_value::success);
            }
    
            int run()
            {
                return m_api.poll();
            }
        private:
            void hello_world(CloudI::API const & api,
                             int const request_type,
                             std::string const & name,
                             std::string const & pattern,
                             void const * const /*request_info*/,
                             uint32_t const /*request_info_size*/,
                             void const * const request,
                             uint32_t const request_size,
                             uint32_t timeout,
                             int8_t /*priority*/,
                             char const * const trans_id,
                             char const * const pid,
                             uint32_t const pid_size)
            {
                std::string message("Hello World!");
                api.return_(request_type, name, pattern, "", 0,
                            message.c_str(), message.size(),
                            timeout, trans_id, pid, pid_size);
            }
            CloudI::API m_api;
    };
    int main(int, char **)
    {
        unsigned int const thread_count = CloudI::API::thread_count();
        assert(thread_count == 1);
        Task t(0);
        return t.run();
    }
    EOF
    
  2. Compile the CloudI service executable:
    g++ -I/usr/local/lib/cloudi-2.0.7/api/c \
        -L/usr/local/lib/cloudi-2.0.7/api/c \
        -g -O0 hello_world.cpp -o hello_world_cxx -lcloudi
    
  3. Now it is necessary to create the CloudI service configuration that specifies both the initialization and fault-tolerance constraints the CloudI service should be executed with (with the proplist format to rely on defaults):
    export PWD=`pwd`
    cat << EOF > hello_world.conf
    [[{prefix, "/quickstart/cxx/"},
      {file_path, "$PWD/hello_world_cxx"},
      {env, [{"LD_LIBRARY_PATH",
              "/usr/local/lib/cloudi-2.0.7/api/c/"},
             {"DYLD_LIBRARY_PATH",
              "/usr/local/lib/cloudi-2.0.7/api/c/"}]}]]
    EOF
    
  4. To dynamically add the CloudI service configuration that starts the service's execution use:
    curl -X POST -d @hello_world.conf \
        http://localhost:6464/cloudi/api/rpc/services_add.erl
    
  5. The curl requests have been using the cowboy HTTP server that is running within the default CloudI configuration to allow the CloudI Service API to be used over HTTP. The same HTTP server can be used to make a CloudI service request to the hello_world service with:
    curl http://localhost:6464/quickstart/cxx/hello_world
    
  6. If there was a problem during the service creation there would be an ERROR entry within the /usr/local/var/log/cloudi/cloudi.log file. If an error occurred with a curl command it would be displayed in the shell. The available service configuration parameters are described in the services_add documentation. More complex C++ examples are listed here.
Elixir
  1. A CloudI service written in Elixir is called an "internal" service because the service is ran inside the Erlang VM. The example Elixir service can be created by executing the following inside your shell:
    mkdir lib
    cat << EOF > lib/HelloWorld.ex
    defmodule HelloWorld do
    
        import CloudILogger
    
        def cloudi_service_init(_args, _prefix, _timeout,
                                dispatcher) do
            :cloudi_service.subscribe(dispatcher, 'hello_world/get')
            {:ok, :undefined}
        end
    
        def cloudi_service_handle_request(_request_type,
                                          _name, _pattern,
                                          _request_info, _request,
                                          _timeout, _priority,
                                          _transid, _pid, state,
                                          _dispatcher) do
            {:reply, "Hello World!", state}
        end
    
        def cloudi_service_handle_info(request, state, _dispatcher) do
            log_warn('Unknown info "~p"', [request])
            {:noreply, state}
        end
    
        def cloudi_service_terminate(_reason, _timeout, _state) do
            :ok
        end
    end
    EOF
    cat << EOF > mix.exs
    defmodule HelloWorld.Mixfile do
        use Mix.Project
    
        def project do
            [app: :'Elixir.HelloWorld',
             version: "2.0.7",
             elixirc_paths: ["lib/"],
             deps: []]
        end
    
        def application do
            [applications: [:cloudi_core]]
        end
    end
    EOF
    
  2. Now compile the CloudI service module. If the CloudI service needed to utilize other Elixir dependencies they would be added to the mix.exs file.
    mix compile
    
  3. You now have a CloudI service contained within a single Elixir module that may look familiar if you remember how a GenServer behavior works. Instead of using the GenServer behavior, we are using a cloudi_service behavior which provides more features with CloudI service requests. To allow the Erlang VM to find the CloudI service Elixir module that has been compiled, it is necessary to add the current directory to the code path:
    export PWD=`pwd`
    curl -X POST -d '"'$PWD'/_build/dev/lib/Elixir.HelloWorld/ebin"' \
        http://localhost:6464/cloudi/api/rpc/code_path_add.erl
    
  4. If Elixir isn't already in the Erlang VM code search path, add it:
    curl -X POST -d '"/usr/local/lib/elixir/lib/elixir/ebin"' \
        http://localhost:6464/cloudi/api/rpc/code_path_add.erl
    
  5. Now it is necessary to create the CloudI service configuration that specifies both the initialization and fault-tolerance constraints the CloudI service should be executed with (with the proplist format to rely on defaults):
    cat << EOF > hello_world.conf
    [[{prefix, "/quickstart/elixir/"},
      {module, 'Elixir.HelloWorld'}]]
    EOF
    
  6. To dynamically add the CloudI service configuration that starts the service's execution use:
    curl -X POST -d @hello_world.conf \
        http://localhost:6464/cloudi/api/rpc/services_add.erl
    
  7. The curl requests have been using the cowboy HTTP server that is running within the default CloudI configuration to allow the CloudI Service API to be used over HTTP. The same HTTP server can be used to make a CloudI service request to the hello_world service with:
    curl http://localhost:6464/quickstart/elixir/hello_world
    
  8. If there was a problem during the service creation there would be an ERROR entry within the /usr/local/var/log/cloudi/cloudi.log file. If an error occurred with a curl command it would be displayed in the shell. The available service configuration parameters are described in the services_add documentation.
Erlang
  1. A CloudI service written in Erlang (or using a language based on core Erlang like Elixir) is called an "internal" service because the service is ran inside the Erlang VM. The example Erlang service can be created by executing the following inside your shell:
    cat << EOF > hello_world.erl
    -module(hello_world).
    -behaviour(cloudi_service).
    
    %% cloudi_service callbacks
    -export([cloudi_service_init/4,
             cloudi_service_handle_request/11,
             cloudi_service_handle_info/3,
             cloudi_service_terminate/3]).
    
    -include_lib("cloudi_core/include/cloudi_logger.hrl").
    
    -record(state,
        {
        }).
    
    cloudi_service_init(_Args, _Prefix, _Timeout, Dispatcher) ->
        cloudi_service:subscribe(Dispatcher, "hello_world/get"),
        {ok, #state{}}.
    
    cloudi_service_handle_request(_RequestType, _Name, _Pattern,
                                  _RequestInfo, _Request,
                                  _Timeout, _Priority,
                                  _TransId, _Pid,
                                  #state{} = State, _Dispatcher) ->
        {reply, <<"Hello World!">>, State}.
    
    cloudi_service_handle_info(Request, State, _Dispatcher) ->
        ?LOG_WARN("Unknown info \"~p\"", [Request]),
        {noreply, State}.
    
    cloudi_service_terminate(_Reason, _Timeout, #state{}) ->
        ok.
    EOF
    
  2. Now compile the CloudI service module. If the CloudI service needed to utilize other Erlang dependencies an Erlang/OTP .app file would be added with the same filename (see the examples for more details).
    erlc -pz /usr/local/lib/cloudi-2.0.7/lib/cloudi_core-2.0.7/ebin \
        hello_world.erl
    
  3. You now have a CloudI service contained within a single Erlang module that may look familiar if you remember how a gen_server behavior works. Instead of using the gen_server behavior, we are using a cloudi_service behavior which provides more features with CloudI service requests. To allow the Erlang VM to find the CloudI service Erlang module that has been compiled, it is necessary to add the current directory to the code path:
    curl -X POST -d '"'`pwd`'"' \
        http://localhost:6464/cloudi/api/rpc/code_path_add.erl
    
  4. Now it is necessary to create the CloudI service configuration that specifies both the initialization and fault-tolerance constraints the CloudI service should be executed with (with the proplist format to rely on defaults):
    cat << EOF > hello_world.conf
    [[{prefix, "/quickstart/erlang/"},
      {module, hello_world}]]
    EOF
    
  5. To dynamically add the CloudI service configuration that starts the service's execution use:
    curl -X POST -d @hello_world.conf \
        http://localhost:6464/cloudi/api/rpc/services_add.erl
    
  6. The curl requests have been using the cowboy HTTP server that is running within the default CloudI configuration to allow the CloudI Service API to be used over HTTP. The same HTTP server can be used to make a CloudI service request to the hello_world service with:
    curl http://localhost:6464/quickstart/erlang/hello_world
    
  7. If there was a problem during the service creation there would be an ERROR entry within the /usr/local/var/log/cloudi/cloudi.log file. If an error occurred with a curl command it would be displayed in the shell. To get more details on CloudI Erlang integration (i.e., CloudI runtime usage with Erlang) see the examples in the source code repository. The available service configuration parameters are described in the services_add documentation. More complex Erlang examples are listed here.
Go
  1. CloudI must be compiled and installed after using the configure argument "--enable-go-support" before attempting the Go quickstart below:
  2. A CloudI service written in Go is called an "external" service because the service is ran inside an Operating System process (external to the Erlang VM). The example Go service can be created by executing the following inside your shell:
    cat << EOF > go.mod
    module hello_world_go
    
    replace (
        github.com/CloudI/cloudi_api_go/v2/cloudi => /usr/local/lib/cloudi-2.0.7/api/go/cloudi
        github.com/okeuday/erlang_go/v2/erlang => /usr/local/lib/cloudi-2.0.7/api/go/erlang
    )
    
    require (
        github.com/CloudI/cloudi_api_go/v2/cloudi v0.0.0-00000000000000-000000000000
        github.com/okeuday/erlang_go/v2/erlang v0.0.0-00000000000000-000000000000
    )
    EOF
    cat << EOF > main.go
    package main
    
    import (
        "github.com/CloudI/cloudi_api_go/v2/cloudi"
        "os"
        "sync"
    )
    
    func helloWorld(requestType int, name, pattern string, requestInfo, request []byte, timeout uint32, priority int8, transId [16]byte, pid cloudi.Source, state interface{}, api *cloudi.Instance) ([]byte, []byte, error) {
        return nil, []byte("Hello World!"), nil
    }
    
    func task(threadIndex uint32, execution *sync.WaitGroup) {
        defer execution.Done()
        api, err := cloudi.API(threadIndex, nil)
        if err != nil {
            cloudi.ErrorWrite(os.Stderr, err)
            return
        }
        err = api.Subscribe("hello_world/get", helloWorld)
        if err != nil {
            cloudi.ErrorWrite(os.Stderr, err)
            return
        }
        _, err = api.Poll(-1)
        if err != nil {
            cloudi.ErrorWrite(os.Stderr, err)
        }
    }
    
    func main() {
        threadCount, err := cloudi.ThreadCount()
        if err != nil {
            cloudi.ErrorExit(os.Stderr, err)
        }
        var execution sync.WaitGroup
        for threadIndex := uint32(0); threadIndex < threadCount; threadIndex++ {
            execution.Add(1)
            go task(threadIndex, &execution)
        }
        execution.Wait()
    }
    EOF
    
  3. Compile the CloudI service executable:
    GOBIN=`pwd` go install -x hello_world_go
    
  4. Now it is necessary to create the CloudI service configuration that specifies both the initialization and fault-tolerance constraints the CloudI service should be executed with (with the proplist format to rely on defaults):
    export PWD=`pwd`
    cat << EOF > hello_world.conf
    [[{prefix, "/quickstart/go/"},
      {file_path, "$PWD/hello_world_go"}]]
    EOF
    
  5. To dynamically add the CloudI service configuration that starts the service's execution use:
    curl -X POST -d @hello_world.conf \
        http://localhost:6464/cloudi/api/rpc/services_add.erl
    
  6. The curl requests have been using the cowboy HTTP server that is running within the default CloudI configuration to allow the CloudI Service API to be used over HTTP. The same HTTP server can be used to make a CloudI service request to the hello_world service with:
    curl http://localhost:6464/quickstart/go/hello_world
    
  7. If there was a problem during the service creation there would be an ERROR entry within the /usr/local/var/log/cloudi/cloudi.log file. If an error occurred with a curl command it would be displayed in the shell. The available service configuration parameters are described in the services_add documentation. More complex Go examples are listed here.
Java
  1. A CloudI service written in Java is called an "external" service because the service is ran inside an Operating System process (external to the Erlang VM). The example Java service can be created by executing the following inside your shell:
    mkdir -p org/cloudi/tests/hello_world/
    cat << EOF > org/cloudi/tests/hello_world/Main.java
    package org.cloudi.tests.hello_world;
    
    import org.cloudi.API;
    
    public class Main
    {
        public static void main(String[] args)
        {
            try
            {
                final int thread_count = API.thread_count();
                assert(thread_count == 1);
                Task t = new Task(0);
                t.run();
            }
            catch (API.InvalidInputException e)
            {
                e.printStackTrace(API.err);
            }
        }
    }
    EOF
    cat << EOF > org/cloudi/tests/hello_world/Task.java
    package org.cloudi.tests.hello_world;
    
    import com.ericsson.otp.erlang.OtpErlangPid;
    import org.cloudi.API;
    
    public class Task
    {
        private API api;
    
        public Task(final int thread_index)
        {
            try
            {
                this.api = new API(thread_index);
            }
            catch (API.InvalidInputException e)
            {
                e.printStackTrace(API.err);
                System.exit(1);
            }
            catch (API.MessageDecodingException e)
            {
                e.printStackTrace(API.err);
                System.exit(1);
            }
            catch (API.TerminateException e)
            {
                System.exit(1);
            }
        }
    
        public Object hello_world(Integer request_type,
                                  String name, String pattern,
                                  byte[] request_info,
                                  byte[] request,
                                  Integer timeout, Byte priority,
                                  byte[] trans_id, OtpErlangPid pid)
        {
            return ("Hello World!").getBytes();
        }
    
        public void run()
        {
            try
            {
                this.api.subscribe("hello_world/get",
                                   this, "hello_world");
                this.api.poll();
            }
            catch (API.TerminateException e)
            {
            }
            catch (Exception e)
            {
                e.printStackTrace(API.err);
            }
        }
    }
    
    EOF
    cat << EOF > manifest.txt
    Main-Class: org.cloudi.tests.hello_world.Main
    Class-Path: /usr/local/lib/cloudi-2.0.7/api/java/cloudi.jar
    
    EOF
    
  2. Compile the CloudI service jar:
    cd org/cloudi/tests/hello_world/
    CLASSPATH=/usr/local/lib/cloudi-2.0.7\
    /api/java/cloudi.jar:${CLASSPATH} javac Task.java Main.java
    cd ../../../../
    jar cvfm hello_world.jar manifest.txt org
    
  3. Now it is necessary to create the CloudI service configuration that specifies both the initialization and fault-tolerance constraints the CloudI service should be executed with (with the proplist format to rely on defaults):
    export JAVA=`which java`
    export PWD=`pwd`
    cat << EOF > hello_world.conf
    [[{prefix, "/quickstart/java/"},
      {file_path, "$JAVA"},
      {args, "-cp /usr/local/lib/cloudi-2.0.7/api/java/ "
             "-ea:org.cloudi... -jar $PWD/hello_world.jar"}]]
    EOF
    
  4. To dynamically add the CloudI service configuration that starts the service's execution use:
    curl -X POST -d @hello_world.conf \
        http://localhost:6464/cloudi/api/rpc/services_add.erl
    
  5. The curl requests have been using the cowboy HTTP server that is running within the default CloudI configuration to allow the CloudI Service API to be used over HTTP. The same HTTP server can be used to make a CloudI service request to the hello_world service with:
    curl http://localhost:6464/quickstart/java/hello_world
    
  6. If there was a problem during the service creation there would be an ERROR entry within the /usr/local/var/log/cloudi/cloudi.log file. If an error occurred with a curl command it would be displayed in the shell. The available service configuration parameters are described in the services_add documentation. More complex Java examples are listed here.
JavaScript
  1. A CloudI service written in JavaScript is called an "external" service because the service is ran inside an Operating System process (external to the Erlang VM). The example JavaScript service can be created by executing the following inside your shell:
    cat << EOF > hello_world.js
    var CloudI = require('/usr/local/lib/cloudi-2.0.7/' +
                         'api/javascript/CloudI.js').CloudI;
    var assert = require('assert');
    
    Task = function Task (thread_index) {
        var Task = this;
        Task._thread_index = thread_index;
    };
    Task.prototype.run = function () {
        var Task = this;
        try {
            new CloudI.API(Task._thread_index, function (api) {
            Task._api = api;
            Task._api.subscribe('hello_world/get', Task,
                                Task.hello_world,
                                function () {
            Task._api.poll(function (timeout) {
            });
            });});
        }
        catch (err) {
            if (typeof err.stack !== 'undefined') {
                process.stderr.write(err.stack + '\n');
            }
            else {
                process.stderr.write(err + '\n');
            }
        }
    };
    Task.prototype.hello_world = function (request_type, name, pattern,
                                           request_info, request,
                                           timeout, priority,
                                           trans_id, pid) {
        return 'Hello World!';
    };
    
    assert(CloudI.API.thread_count() == 1);
    var thread = new Task(0);
    thread.run();
    EOF
    
  2. Now it is necessary to create the CloudI service configuration that specifies both the initialization and fault-tolerance constraints the CloudI service should be executed with (with the proplist format to rely on defaults):
    export NODE=`which node`
    export PWD=`pwd`
    cat << EOF > hello_world.conf
    [[{prefix, "/quickstart/javascript/"},
      {file_path, "$NODE"},
      {args, "$PWD/hello_world.js"}]]
    EOF
    
  3. To dynamically add the CloudI service configuration that starts the service's execution use:
    curl -X POST -d @hello_world.conf \
        http://localhost:6464/cloudi/api/rpc/services_add.erl
    
  4. The curl requests have been using the cowboy HTTP server that is running within the default CloudI configuration to allow the CloudI Service API to be used over HTTP. The same HTTP server can be used to make a CloudI service request to the hello_world service with:
    curl http://localhost:6464/quickstart/javascript/hello_world
    
  5. If there was a problem during the service creation there would be an ERROR entry within the /usr/local/var/log/cloudi/cloudi.log file. If an error occurred with a curl command it would be displayed in the shell. The available service configuration parameters are described in the services_add documentation. More complex JavaScript examples are listed here.
Perl
  1. A CloudI service written in Perl is called an "external" service because the service is ran inside an Operating System process (external to the Erlang VM). The example Perl service can be created by executing the following inside your shell:
    cat << EOF > hello_world.pl
    use strict;
    use warnings;
    
    require CloudI::API;
    require CloudI::TerminateException;
    
    sub task
    {
        my (\$api) = @_;
        eval
        {
            my \$task_hello_world = sub
            {
                my (\$request_type, \$name, \$pattern,
                    \$request_info, \$request,
                    \$timeout, \$priority, \$trans_id, \$pid) = @_;
                return 'Hello World!';
            };
            \$api->subscribe('hello_world/get', \$task_hello_world);
            \$api->poll();
        };
        my \$e = \$@;
        if (\$e)
        {
            if (\$e->isa('CloudI::TerminateException'))
            {
                1;
            }
            else
            {
                print "\$e";
            }
        }
    }
    {
        CloudI::API->assert(CloudI::API->thread_count() == 1);
        task(CloudI::API->new(0));
    }
    EOF
    
  2. Now it is necessary to create the CloudI service configuration that specifies both the initialization and fault-tolerance constraints the CloudI service should be executed with (with the proplist format to rely on defaults):
    export PERL=`which perl`
    export PWD=`pwd`
    cat << EOF > hello_world.conf
    [[{prefix, "/quickstart/perl/"},
      {file_path, "$PERL"},
      {args, "$PWD/hello_world.pl"},
      {env, [{"PERL5LIB", "/usr/local/lib/cloudi-2.0.7/api/perl"}]}]]
    EOF
    
  3. To dynamically add the CloudI service configuration that starts the service's execution use:
    curl -X POST -d @hello_world.conf \
        http://localhost:6464/cloudi/api/rpc/services_add.erl
    
  4. The curl requests have been using the cowboy HTTP server that is running within the default CloudI configuration to allow the CloudI Service API to be used over HTTP. The same HTTP server can be used to make a CloudI service request to the hello_world service with:
    curl http://localhost:6464/quickstart/perl/hello_world
    
  5. If there was a problem during the service creation there would be an ERROR entry within the /usr/local/var/log/cloudi/cloudi.log file. If an error occurred with a curl command it would be displayed in the shell. The available service configuration parameters are described in the services_add documentation. More complex Perl examples are listed here.
PHP
  1. A CloudI service written in PHP is called an "external" service because the service is ran inside an Operating System process (external to the Erlang VM). The example PHP service can be created by executing the following inside your shell:
    cat << EOF > hello_world.php
    <?php 
    
    require '/usr/local/lib/cloudi-2.0.7/api/php/CloudI.php';
    
    class Task
    {
        private \$api;
    
        public function __construct(\$api)
        {
            \$this->api = \$api;
        }
    
        public function run()
        {
            try
            {
                \$this->api->subscribe('hello_world/get',
                                            \$this, 'hello_world');
                \$this->api->poll();
            }
            catch (\CloudI\TerminateException \$e)
            {
            }
            catch (Exception \$e)
            {
                error_log("{\$e->getMessage()}\n{\$e}\n");
            }
        }
    
        public function hello_world(\$request_type, \$name, \$pattern,
                                    \$request_info, \$request,
                                    \$timeout, \$priority,
                                    \$trans_id, \$pid)
        {
            return 'Hello World!';
        }
    }
    
    \$thread_count = \CloudI\API::thread_count();
    assert(\$thread_count == 1);
    \$main_thread = new Task(new \CloudI\API(0));
    \$main_thread->run();
    
    ?>
    EOF
    
  2. Now it is necessary to create the CloudI service configuration that specifies both the initialization and fault-tolerance constraints the CloudI service should be executed with (with the proplist format to rely on defaults):
    export PHP=`which php`
    export PWD=`pwd`
    cat << EOF > hello_world.conf
    [[{prefix, "/quickstart/php/"},
      {file_path, "$PHP"},
      {args, "$PWD/hello_world.php"}]]
    EOF
    
  3. To dynamically add the CloudI service configuration that starts the service's execution use:
    curl -X POST -d @hello_world.conf \
        http://localhost:6464/cloudi/api/rpc/services_add.erl
    
  4. The curl requests have been using the cowboy HTTP server that is running within the default CloudI configuration to allow the CloudI Service API to be used over HTTP. The same HTTP server can be used to make a CloudI service request to the hello_world service with:
    curl http://localhost:6464/quickstart/php/hello_world
    
  5. If there was a problem during the service creation there would be an ERROR entry within the /usr/local/var/log/cloudi/cloudi.log file. If an error occurred with a curl command it would be displayed in the shell. The available service configuration parameters are described in the services_add documentation. More complex PHP examples are listed here.
Python
  1. A CloudI service written in Python is called an "external" service because the service is ran inside an Operating System process (external to the Erlang VM). The example Python service can be created by executing the following inside your shell:
    cat << EOF > hello_world.py
    import sys
    sys.path.append('/usr/local/lib/cloudi-2.0.7/api/python/')
    import traceback
    from cloudi import API, terminate_exception
    
    class Task(object):
        def __init__(self):
            self.__api = API(0) # first/only thread == 0
    
        def run(self):
            try:
                self.__api.subscribe("hello_world/get",
                                     self.__hello_world)
                self.__api.poll()
            except terminate_exception:
                pass
            except:
                traceback.print_exc(file=sys.stderr)
    
        def __hello_world(self, request_type, name, pattern,
                          request_info, request,
                          timeout, priority, trans_id, pid):
            return b'Hello World!'
    
    if __name__ == '__main__':
        assert API.thread_count() == 1
        task = Task()
        task.run()
    EOF
    
  2. Now it is necessary to create the CloudI service configuration that specifies both the initialization and fault-tolerance constraints the CloudI service should be executed with (either python2 or python3 will work and the python executable name may instead be "python" on your system):
    export PYTHON=`which python3`
    export PWD=`pwd`
    cat << EOF > hello_world.conf
    [[{prefix, "/quickstart/python/"},
      {file_path, "$PYTHON"},
      {args, "$PWD/hello_world.py"}]]
    EOF
    
  3. To dynamically add the CloudI service configuration that starts the service's execution use:
    curl -X POST -d @hello_world.conf \
        http://localhost:6464/cloudi/api/rpc/services_add.erl
    
  4. The curl requests have been using the cowboy HTTP server that is running within the default CloudI configuration to allow the CloudI Service API to be used over HTTP. The same HTTP server can be used to make a CloudI service request to the hello_world service with:
    curl http://localhost:6464/quickstart/python/hello_world
    
  5. If there was a problem during the service creation there would be an ERROR entry within the /usr/local/var/log/cloudi/cloudi.log file. If an error occurred with a curl command it would be displayed in the shell. The available service configuration parameters are described in the services_add documentation. More complex Python examples are listed here.
Ruby
  1. A CloudI service written in Ruby is called an "external" service because the service is ran inside an Operating System process (external to the Erlang VM). The example Ruby service can be created by executing the following inside your shell:
    cat << EOF > hello_world.rb
    \$:.unshift '/usr/local/lib/cloudi-2.0.7/api/ruby'
    
    \$DEBUG = false
    
    require 'cloudi'
    
    if __FILE__ == \$PROGRAM_NAME
        thread_count = CloudI::API.thread_count()
        CloudI::API.assert{thread_count == 1}
    
        class Task
            def initialize(thread_index)
                @api = CloudI::API.new(thread_index)
            end
    
            def run
                begin
                    @api.subscribe('hello_world/get',
                                   method(:hello_world))
    
                    @api.poll
                rescue CloudI::TerminateException
                    #
                rescue
                    \$stderr.puts \$!.message
                    \$stderr.puts \$!.backtrace
                end
            end
    
            private
    
            def hello_world(request_type, name, pattern,
                            request_info, request,
                            timeout, priority, trans_id, pid)
                return 'Hello World!';
            end
        end
        begin
            object = Task.new(0)
            object.run
        rescue
            \$stderr.puts \$!.message
            \$stderr.puts \$!.backtrace
        end
    end
    EOF
    
  2. Now it is necessary to create the CloudI service configuration that specifies both the initialization and fault-tolerance constraints the CloudI service should be executed with (with the proplist format to rely on defaults):
    export RUBY=`which ruby` # must be ≥ 1.9
    export PWD=`pwd`
    cat << EOF > hello_world.conf
    [[{prefix, "/quickstart/ruby/"},
      {file_path, "$RUBY"},
      {args, "$PWD/hello_world.rb"}]]
    EOF
    
  3. To dynamically add the CloudI service configuration that starts the service's execution use:
    curl -X POST -d @hello_world.conf \
        http://localhost:6464/cloudi/api/rpc/services_add.erl
    
  4. The curl requests have been using the cowboy HTTP server that is running within the default CloudI configuration to allow the CloudI Service API to be used over HTTP. The same HTTP server can be used to make a CloudI service request to the hello_world service with:
    curl http://localhost:6464/quickstart/ruby/hello_world
    
  5. If there was a problem during the service creation there would be an ERROR entry within the /usr/local/var/log/cloudi/cloudi.log file. If an error occurred with a curl command it would be displayed in the shell. The available service configuration parameters are described in the services_add documentation. More complex Ruby examples are listed here.
 
  1. Get curl if you don't already have it
    • (Ubuntu) sudo apt-get install curl
    • (OSX) sudo port install curl

  2. Get CloudI running (need ./configure help?):
    curl -LOJ https://cloudi.org/download/cloudi-2.0.7.tar.gz
    
    tar zxvf cloudi-2.0.7.tar.gz
    
    cd cloudi-2.0.7/src
    
    ./configure
    
    make
    
    sudo make install
    
    cd ../..
    
    sudo cloudi start
    
  3. The CloudI integration tests are now running and consuming your available CPUs. The /usr/local/var/log/cloudi/cloudi.log file provides integration test output. You can now select a programming language above to create a CloudI service.