[#6] designing the plugin system

This commit is contained in:
Felipe Ripoll 2018-05-07 17:13:41 -06:00
parent e27aad9618
commit f7b9d077e6
11 changed files with 234 additions and 11 deletions

View File

@ -1 +1,16 @@
use Mix.Config
# configuration for collectors. The format for each collector is {collector_process_id, module, [target_transfers], label, args}
config :poa_agent,
:collectors,
[
{:my_collector, POAAgent.Plugins.Collectors.MyCollector, [:my_transfer], :my_metrics, [host: "localhost", port: 1234]}
]
# configuration for transfers. The format for each collector is {collector_process_id, module, args}
config :poa_agent,
:transfers,
[
{:my_transfer, POAAgent.Plugins.Transfers.MyTransfer, [ws_key: "mykey", other_stuff: "hello"]}
]

View File

@ -1,9 +0,0 @@
defmodule POAAgent do
@moduledoc """
Documentation for PoaAgent.
"""
def hello do
:world
end
end

View File

@ -0,0 +1,17 @@
defmodule POAAgent.Application do
@moduledoc false
use Application
def start(_type, _args) do
import Supervisor.Spec
children = [
supervisor(POAAgent.Plugins.Transfers.Supervisor, []),
supervisor(POAAgent.Plugins.Collectors.Supervisor, [])
]
opts = [strategy: :one_for_one, name: POAAgent.Supervisor]
Supervisor.start_link(children, opts)
end
end

View File

@ -0,0 +1,66 @@
defmodule POAAgent.Plugins.Collector do
@callback init_collector(args :: term()) ::
{:ok, any()}
@callback collect(state :: any()) :: {:ok, data :: any(), state :: any()}
@callback terminate(reason, state :: term()) :: term()
when reason: :normal | :shutdown | {:shutdown, term()}
defmacro __using__(_opt) do
quote do
@behaviour POAAgent.Plugins.Collector
@doc false
def start_link(%{name: name} = state) do
GenServer.start_link(__MODULE__, state, name: name)
end
@doc false
def init(state) do
{:ok, internal_state} = init_collector(state[:args])
set_collector_timer()
{:ok, Map.put(state, :internal_state, internal_state)}
end
@doc false
def handle_call(_msg, _from, state) do
{:noreply, state}
end
@doc false
def handle_info(:collect, state) do
{:ok, data, internal_state} = collect(state.internal_state)
transfer(data, state.label, state.transfers)
set_collector_timer()
{:noreply, %{state | internal_state: internal_state}}
end
def handle_info(_msg, state) do
{:noreply, state}
end
@doc false
def handle_cast(msg, state) do
{:noreply, state}
end
@doc false
def code_change(_old, state, _extra) do
{:ok, state}
end
@doc false
def transfer(data, label, transfers) do
Enum.each(transfers, &GenServer.cast(&1, %{label: label, data: data}))
:ok
end
defp set_collector_timer() do
Process.send_after(self(), :collect, 5000) # TODO timeout must be configurable
end
end
end
end

View File

@ -0,0 +1,18 @@
defmodule POAAgent.Plugins.Collectors.MyCollector do
use POAAgent.Plugins.Collector
def init_collector(args) do
IO.puts "init_collector args = #{inspect args}"
{:ok, :no_state}
end
def collect(:no_state) do
IO.puts "I am collecting data!"
{:ok, "data retrieved", :no_state}
end
def terminate(_reason, _state) do
:ok
end
end

View File

@ -0,0 +1,22 @@
defmodule POAAgent.Plugins.Collectors.Supervisor do
@moduledoc false
def start_link do
Supervisor.start_link(__MODULE__, :ok, name: __MODULE__)
end
def init(:ok) do
import Supervisor.Spec
# create the children from the config file
collectors = Application.get_env(:poa_agent, :collectors)
children = for {name, module, transfers, label, args} <- collectors do
worker(module, [%{name: name, transfers: transfers, label: label, args: args}])
end
opts = [strategy: :one_for_one]
supervise(children, opts)
end
end

View File

@ -0,0 +1,53 @@
defmodule POAAgent.Plugins.Transfer do
@callback init_transfer(args :: term()) ::
{:ok, any()}
@callback data_received(label :: atom(), data :: any(), state :: any()) :: {:ok, any()}
@callback terminate(reason, state :: term()) :: term()
when reason: :normal | :shutdown | {:shutdown, term()}
defmacro __using__(_opt) do
quote do
@behaviour POAAgent.Plugins.Transfer
@doc false
def start_link(%{name: name} = state) do
GenServer.start_link(__MODULE__, state, name: name)
end
@doc false
def init(state) do
{:ok, internal_state} = init_transfer(state[:args])
{:ok, Map.put(state, :internal_state, internal_state)}
end
@doc false
def handle_call(_msg, _from, state) do
{:noreply, state}
end
@doc false
def handle_info(_msg, state) do
{:noreply, state}
end
@doc false
def handle_cast(%{label: label, data: data}, state) do
{:ok, internal_state} = data_received(label, data, state.internal_state)
{:noreply, %{state | internal_state: internal_state}}
end
def handle_cast(msg, state) do
{:noreply, state}
end
@doc false
def code_change(_old, state, _extra) do
{:ok, state}
end
end
end
end

View File

@ -0,0 +1,18 @@
defmodule POAAgent.Plugins.Transfers.MyTransfer do
use POAAgent.Plugins.Transfer
def init_transfer(args) do
IO.puts "init_transfer args = #{inspect args}"
{:ok, :no_state}
end
def data_received(label, data, state) do
IO.puts "Received data with label #{inspect label}, data #{inspect data} and internal_state #{inspect state}"
{:ok, :no_state}
end
def terminate(_reason, _state) do
:ok
end
end

View File

@ -0,0 +1,22 @@
defmodule POAAgent.Plugins.Transfers.Supervisor do
@moduledoc false
def start_link do
Supervisor.start_link(__MODULE__, :ok, name: __MODULE__)
end
def init(:ok) do
import Supervisor.Spec
# create the children from the config file
transfers = Application.get_env(:poa_agent, :transfers)
children = for {name, module, args} <- transfers do
worker(module, [%{name: name, args: args}])
end
opts = [strategy: :one_for_one]
supervise(children, opts)
end
end

View File

@ -13,7 +13,8 @@ defmodule POAAgent.MixProject do
def application do
[
extra_applications: [:logger]
extra_applications: [:logger],
mod: {POAAgent.Application, []}
]
end

View File

@ -3,6 +3,6 @@ defmodule POAAgentTest do
doctest POAAgent
test "greets the world" do
assert POAAgent.hello() == :world
# assert POAAgent.hello() == :world
end
end