aboutsummaryrefslogtreecommitdiff
path: root/apps/dreki
diff options
context:
space:
mode:
Diffstat (limited to 'apps/dreki')
-rw-r--r--apps/dreki/include/dreki_otel.hrl11
-rw-r--r--apps/dreki/include/dreki_plum.hrl6
-rw-r--r--apps/dreki/src/dreki.app.src6
-rw-r--r--apps/dreki/src/dreki_app.erl8
-rw-r--r--apps/dreki/src/dreki_config.erl26
-rw-r--r--apps/dreki/src/dreki_freebsd_schemas.erl59
-rw-r--r--apps/dreki/src/dreki_plum.erl30
-rw-r--r--apps/dreki/src/dreki_sup.erl10
-rw-r--r--apps/dreki/src/drekid.erl95
-rw-r--r--apps/dreki/src/drekid_tasks_store.erl71
-rw-r--r--apps/dreki/src/funs/dreki_fun_exec.erl28
-rw-r--r--apps/dreki/src/funs/dreki_funs.erl31
-rw-r--r--apps/dreki/src/node/dreki_node.erl (renamed from apps/dreki/src/dreki_node.erl)69
-rw-r--r--apps/dreki/src/node/dreki_node_server.erl (renamed from apps/dreki/src/dreki_node_server.erl)0
-rw-r--r--apps/dreki/src/node/rebar.conf0
-rw-r--r--apps/dreki/src/requests/dreki_requests.erl97
-rw-r--r--apps/dreki/src/runs/dreki_runs.erl1
-rw-r--r--apps/dreki/src/storages/dreki_storages.erl98
-rw-r--r--apps/dreki/src/storages/dreki_storages_zfs.erl2
-rw-r--r--apps/dreki/src/store/dreki_dets_store.erl (renamed from apps/dreki/src/dreki_dets_store.erl)5
-rw-r--r--apps/dreki/src/store/dreki_mnesia_store.erl94
-rw-r--r--apps/dreki/src/store/dreki_store.erl (renamed from apps/dreki/src/dreki_store.erl)144
-rw-r--r--apps/dreki/src/store/dreki_store_backend.erl (renamed from apps/dreki/src/dreki_store_backend.erl)0
-rw-r--r--apps/dreki/src/store/dreki_store_namespace.erl (renamed from apps/dreki/src/dreki_store_namespace.erl)0
-rw-r--r--apps/dreki/src/tasks/dreki_task.erl (renamed from apps/dreki/src/dreki_task.erl)0
-rw-r--r--apps/dreki/src/tasks/dreki_tasks.erl (renamed from apps/dreki/src/dreki_tasks.erl)41
-rw-r--r--apps/dreki/src/tasks/dreki_tasks_cloyster.erl (renamed from apps/dreki/src/dreki_tasks_cloyster.erl)0
-rw-r--r--apps/dreki/src/tasks/dreki_tasks_script.erl (renamed from apps/dreki/src/dreki_tasks_script.erl)0
-rw-r--r--apps/dreki/src/world/dreki_world.erl (renamed from apps/dreki/src/dreki_world.erl)0
-rw-r--r--apps/dreki/src/world/dreki_world_dns.erl (renamed from apps/dreki/src/dreki_world_dns.erl)0
-rw-r--r--apps/dreki/src/world/dreki_world_plum_events.erl (renamed from apps/dreki/src/dreki_world_plum_events.erl)0
-rw-r--r--apps/dreki/src/world/dreki_world_server.erl (renamed from apps/dreki/src/dreki_world_server.erl)0
-rw-r--r--apps/dreki/src/world/dreki_world_store.erl (renamed from apps/dreki/src/dreki_world_store.erl)4
-rw-r--r--apps/dreki/src/world/dreki_world_tasks.erl (renamed from apps/dreki/src/dreki_world_tasks.erl)0
34 files changed, 825 insertions, 111 deletions
diff --git a/apps/dreki/include/dreki_otel.hrl b/apps/dreki/include/dreki_otel.hrl
new file mode 100644
index 0000000..e784874
--- /dev/null
+++ b/apps/dreki/include/dreki_otel.hrl
@@ -0,0 +1,11 @@
+-include_lib("opentelemetry_api/include/opentelemetry.hrl").
+-include_lib("opentelemetry_api/include/otel_tracer.hrl").
+
+-define(FUN_NAME, binary:list_to_bin([
+ atom_to_binary(?MODULE), <<":">>,
+ atom_to_binary(?FUNCTION_NAME), <<"/">>,
+ integer_to_binary(?FUNCTION_ARITY)
+ ])
+).
+
+-define(FUN_NAME(D), binary:list_to_bin([?FUN_NAME, atom_to_binary(D)])).
diff --git a/apps/dreki/include/dreki_plum.hrl b/apps/dreki/include/dreki_plum.hrl
index bea65aa..aed2e05 100644
--- a/apps/dreki/include/dreki_plum.hrl
+++ b/apps/dreki/include/dreki_plum.hrl
@@ -5,14 +5,13 @@
%% dreki_store tabs
-define(PLUM_DB_STORE_TASKS_TAB, dreki_tasks).
-
%% indices tabs
-define(PLUM_DB_IDX_ROLE_TAB, 'dreki_idx:roles').
-define(PLUM_DB_IDX_TAGS_TAB, 'dreki_idx:tags').
-define(PLUM_DB_PREFIXES, [
- {?PLUM_DB_REGIONS_TAB, ram_disk},
- {?PLUM_DB_NODES_TAB, ram_disk},
+ {?PLUM_DB_REGIONS_TAB, disk},
+ {?PLUM_DB_NODES_TAB, disk},
{?PLUM_DB_PATHS_TAB, ram_disk},
{?PLUM_DB_STORES_TAB, ram_disk},
@@ -23,4 +22,3 @@
{?PLUM_DB_IDX_ROLE_TAB, disk},
{?PLUM_DB_IDX_TAGS_TAB, disk}
]).
-
diff --git a/apps/dreki/src/dreki.app.src b/apps/dreki/src/dreki.app.src
index 78009bf..2e152a7 100644
--- a/apps/dreki/src/dreki.app.src
+++ b/apps/dreki/src/dreki.app.src
@@ -8,9 +8,15 @@
stdlib,
mnesia,
logger_colorful,
+ ipee,
opentelemetry,
opentelemetry_api,
opentelemetry_exporter,
+ opentelemetry_logger_metadata,
+ telemetry,
+ opentelemetry_telemetry,
+ prometheus,
+ genlib,
uuid,
mnesia_rocksdb,
plum_db,
diff --git a/apps/dreki/src/dreki_app.erl b/apps/dreki/src/dreki_app.erl
index a1259a7..0442b4c 100644
--- a/apps/dreki/src/dreki_app.erl
+++ b/apps/dreki/src/dreki_app.erl
@@ -31,6 +31,8 @@ before_start(Type, Args) ->
logger:set_application_level(dreki_web, debug),
logger:set_application_level(partisan, info),
logger:set_application_level(plum_db, info),
+ logger:set_handler_config(default, level, debug),
+ opentelemetry_logger_metadata:setup(),
?LOG_NOTICE(#{message => "Dreki starting...."}),
application:stop(partisan),
ok = dreki_config:init(Args),
@@ -48,6 +50,7 @@ after_start() ->
ok = setup_event_manager(),
ok = dreki_store:start(),
?LOG_NOTICE(#{message => "Dreki Ready"}),
+ ok = dreki_config:set([dreki, status], ready),
dreki_event_manager:notify(dreki_ready),
ok.
@@ -70,12 +73,11 @@ setup_event_manager() ->
Mod = partisan_peer_service:manager(),
Mod:on_up('_', fun(Node) ->
- dreki_event_manager:notify({peer_up, Node})
+ dreki_event_manager:notify({partisan_peer_service, peer_up, Node})
end),
Mod:on_down('_', fun(Node) ->
- dreki_event_manager:notify({peer_down, Node})
+ dreki_event_manager:notify({partisan_peer_service, peer_down, Node})
end),
ok.
-
diff --git a/apps/dreki/src/dreki_config.erl b/apps/dreki/src/dreki_config.erl
index 69df2d6..457f559 100644
--- a/apps/dreki/src/dreki_config.erl
+++ b/apps/dreki/src/dreki_config.erl
@@ -2,8 +2,14 @@
-include_lib("kernel/include/logger.hrl").
-include_lib("partisan/include/partisan.hrl").
-include("dreki_plum.hrl").
+-include("dreki_otel.hrl").
+
+-compile({no_auto_import,[get/0]}).
+
-export([init/1]).
+-export([get/1]).
+-export([set/2]).
-define(CONFIG, [
%% All stolen from bondy as partisan isn't that well documented, eh.
@@ -44,15 +50,29 @@
-define(PT, dreki_config_cache).
init(_Args) ->
- persistent_term:put(?PT, application:get_all_env(dreki)),
+ persistent_term:put(?PT, [{dreki, application:get_all_env(dreki)},
+ {dreki_web, application:get_all_env(dreki_web)
+ }]),
ok = set_app_configs(?CONFIG),
- ?LOG_INFO(#{message => "Configured Dreki and dependencies"}),
+ ?LOG_NOTICE(#{message => "Configured Dreki and dependencies"}),
ok = partisan_config:init(),
ok.
+get() ->
+ persistent_term:get(?PT).
+
+get(Key) ->
+ ?with_span(?FUN_NAME, #{}, fun (_) -> key_value:get(Key, get()) end).
+
+set(Key, Value) ->
+ ?with_span(?FUN_NAME, #{},
+ fun (_) ->
+ persistent_term:put(?PT, key_value:put(Key, Value, get())),
+ ok
+ end).
+
set_app_configs(Configs) ->
lists:foreach(fun ({App, Params}) ->
[application:set_env(App, Key, Value) || {Key, Value} <- Params]
end, Configs),
ok.
-
diff --git a/apps/dreki/src/dreki_freebsd_schemas.erl b/apps/dreki/src/dreki_freebsd_schemas.erl
new file mode 100644
index 0000000..de97ded
--- /dev/null
+++ b/apps/dreki/src/dreki_freebsd_schemas.erl
@@ -0,0 +1,59 @@
+-module(dreki_freebsd_schemas).
+
+-export([schema/1]).
+
+schema(<<"dreki.v1.freebsd.jails.list">>) ->
+ #{
+ <<"@schema">> => "dreki.v1.freebsd.jails.list",
+ version => 'draft-06',
+ title => <<"List running jails ID/Names">>,
+ properties => #{}
+ };
+schema(<<"dreki.v1.freebsd.jails.get">>) ->
+ #{
+ <<"@schema">> => "dreki.v1.freebsd.jails.get",
+ version => 'draft-06',
+ title => <<"Get jail information">>,
+ properties => #{
+ jail_id => #{type => integer}
+ },
+ required => [jail_id]
+ };
+schema(<<"dreki.v1.freebsd.jails.start">>) ->
+ #{
+ <<"@schema">> => "dreki.v1.freebsd.jails.start",
+ version => 'draft-06',
+ title => <<"Start a jail">>,
+ properties => #{
+ name => #{type => string},
+ path => #{type => string},
+ hostname => #{type => string},
+ params => #{type => object}
+ },
+ required => [name, path, hostname, params]
+ };
+schema(<<"dreki.v1.freebsd.jails.exec">>) ->
+ #{
+ <<"@schema">> => "dreki.v1.freebsd.jails.exec",
+ version => 'draft-06',
+ title => <<"Execute a command in a jail">>,
+ properties => #{
+ jail_id => #{type => string},
+ command => #{type => string},
+ args => #{type => array},
+ env => #{type => object}
+ },
+ required => [id, command, args, env]
+ };
+schema(<<"dreki.v1.exec">>) ->
+ #{
+ <<"@schema">> => "dreki.v1.exec",
+ version => 'draft-06',
+ title => <<"Execute a command">>,
+ properties => #{
+ command => #{type => string},
+ args => #{type => array},
+ env => #{type => object}
+ },
+ required => [command, args, env]
+ }.
diff --git a/apps/dreki/src/dreki_plum.erl b/apps/dreki/src/dreki_plum.erl
index 9c03e46..dccdad7 100644
--- a/apps/dreki/src/dreki_plum.erl
+++ b/apps/dreki/src/dreki_plum.erl
@@ -8,7 +8,6 @@ before_start() ->
%% We temporarily disable plum_db's AAE to avoid rebuilding hashtrees
%% until we are ready to do it
ok = suspend_aae(),
- logger:debug("-- DISABLED AAE ! --"),
_ = application:ensure_all_started(plum_db, permanent),
ok.
@@ -27,9 +26,7 @@ suspend_aae() ->
true ->
ok = application:set_env(plum_db, priv_aae_enabled, true),
ok = application:set_env(plum_db, aae_enabled, false),
- ?LOG_NOTICE(#{
- description => "Temporarily disabled active anti-entropy (AAE) during initialisation"
- }),
+ ?LOG_NOTICE(#{message => "Temporarily disabled plum_db aae during initialisation"}),
ok;
false ->
ok
@@ -40,9 +37,7 @@ restore_aae() ->
true ->
%% plum_db should have started so we call plum_db_config
ok = plum_db_config:set(aae_enabled, true),
- ?LOG_NOTICE(#{
- description => "Active anti-entropy (AAE) re-enabled"
- }),
+ ?LOG_NOTICE(#{message => "plum_db aae re-enabled"}),
ok;
false ->
ok
@@ -51,11 +46,10 @@ restore_aae() ->
maybe_wait_for_plum_db_partitions() ->
case wait_for_partitions() of
true ->
- %% We block until all partitions are initialised
- ?LOG_NOTICE(#{
- description => "Application master is waiting for plum_db partitions to be initialised"
- }),
- plum_db_startup_coordinator:wait_for_partitions();
+ ?LOG_NOTICE(#{domain => [dreki_plum], message => "Waiting for plum_db partitions to be initialised"}),
+ ok = plum_db_startup_coordinator:wait_for_partitions(),
+ ?LOG_NOTICE(#{domain => [dreki_plum], message => "plum_db partitions initialised"}),
+ ok;
false ->
ok
end.
@@ -64,10 +58,10 @@ maybe_wait_for_plum_db_hashtrees() ->
case wait_for_hashtrees() of
true ->
%% We block until all hashtrees are built
- ?LOG_NOTICE(#{
- description => "Application master is waiting for plum_db hashtrees to be built"
- }),
- plum_db_startup_coordinator:wait_for_hashtrees();
+ ?LOG_NOTICE(#{domain => [dreki_plum], message => "Waiting for plum_db hashtrees to be built"}),
+ ok = plum_db_startup_coordinator:wait_for_hashtrees(),
+ ?LOG_NOTICE(#{domain => [dreki_plum], message => "plum_db hashtrees built"}),
+ ok;
false ->
ok
end,
@@ -88,9 +82,7 @@ maybe_wait_for_aae_exchange() ->
%% We have not yet joined a cluster, so we finish
ok;
Peers ->
- ?LOG_NOTICE(#{
- description => "Application master is waiting for plum_db AAE to perform exchange"
- }),
+ ?LOG_NOTICE(#{domain => [plum_db], message => "Waiting for plum_db AAE to perform exchange"}),
%% We are in a cluster, we randomnly pick a peer and
%% perform an AAE exchange
[Peer|_] = lists_utils:shuffle(Peers),
diff --git a/apps/dreki/src/dreki_sup.erl b/apps/dreki/src/dreki_sup.erl
index c1aa636..60b33e5 100644
--- a/apps/dreki/src/dreki_sup.erl
+++ b/apps/dreki/src/dreki_sup.erl
@@ -16,20 +16,12 @@
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
-%% sup_flags() = #{strategy => strategy(), % optional
-%% intensity => non_neg_integer(), % optional
-%% period => pos_integer()} % optional
-%% child_spec() = #{id => child_id(), % mandatory
-%% start => mfargs(), % mandatory
-%% restart => restart(), % optional
-%% shutdown => shutdown(), % optional
-%% type => worker(), % optional
-%% modules => modules()} % optional
init([]) ->
SupFlags = #{strategy => one_for_all,
intensity => 10,
period => 5},
ChildSpecs = [
+ #{id => drekid, start => {drekid, start_link, [[]]}},
#{id => dreki_event_manager, start => {dreki_event_manager, start_link, [[]]}},
#{id => dreki_world_server, start => {dreki_world_server, start_link, [[]]}},
#{id => dreki_node_server, start => {dreki_node_server, start_link, [[]]}}
diff --git a/apps/dreki/src/drekid.erl b/apps/dreki/src/drekid.erl
new file mode 100644
index 0000000..73beffd
--- /dev/null
+++ b/apps/dreki/src/drekid.erl
@@ -0,0 +1,95 @@
+-module(drekid).
+-behaviour(partisan_gen_fsm).
+-include_lib("kernel/include/logger.hrl").
+-include_lib("dreki_otel.hrl").
+-compile({no_auto_import, [get/0]}).
+-export([get/0]).
+-export([request/1, request/2, request/3]).
+-export([start_link/1]).
+-export([init/1, waiting/2, handle_info/3, handle_event/3, handle_sync_event/4]).
+-define(DREKID_PT, drekid).
+-record(drekid, {node, pid}).
+
+-spec get() -> {ok, pid()} | {error, drekid_unavailable}.
+get() ->
+ persistent_term:get(drekid, {error, drekid_unavailable}).
+
+request(Fun) ->
+ request(Fun, []).
+
+request(Fun, Args) ->
+ request(Fun, Args, 5000).
+
+request(Fun, Args, Timeout) when is_binary(Fun) ->
+ ?with_span(?FUN_NAME, fun(_SpanCtx) ->
+ send_request(Fun, Args, Timeout)
+ end).
+
+send_request(Fun, Args, Timeout) ->
+ ?set_attributes(#{function => Fun, args => Args, timeout => Timeout}),
+ case get() of
+ {ok, Pid} ->
+ Ref = make_ref(),
+ logger:info("drekid_request: ~p ~p to ~p", [Fun, Args, Pid]),
+ Pid ! {drekid_request_v1, self(), Ref, Fun, Args, Timeout},
+ ?with_span(?FUN_NAME(await_request), fun(_) ->
+ await_request(Ref, Timeout)
+ end);
+ Error ->
+ Error
+ end.
+
+await_request(Ref, Timeout) ->
+ receive
+ {drekid_response_v1, Ref, Result} ->
+ case Result of
+ {ok, Data = {_, _}} ->
+ Data;
+ {ok, Data} ->
+ {ok, Data};
+ {error, Error} ->
+ {error, {drekid_error, Error}}
+ end
+ after Timeout ->
+ {error, {drekid_error, timeout}}
+ end.
+
+start_link(Args) ->
+ partisan_gen_fsm:start_link({local, ?MODULE}, ?MODULE, Args, []).
+
+%%
+
+init(_Args) ->
+ ?LOG_DEBUG("drekid initialized"),
+ {ok, waiting, #drekid{}, 0}.
+
+waiting(timeout, Data) ->
+ persistent_term:put(drekid, {error, drekid_unavailable}),
+ {next_state, waiting, Data};
+waiting({drekid, {connect, Node, Pid}}, Data) ->
+ Data0 = Data#drekid{node = Node, pid = Pid},
+ monitor_node(Node, true),
+ Pid ! {drekid, hello, node(), self()},
+ ?LOG_NOTICE("drekid ready, node: ~p ~p", [Node, Pid]),
+ persistent_term:put(drekid, {ok, Pid}),
+ {next_state, ready, Data}.
+
+down(timeout, {Reason, State, Data}) ->
+ ?LOG_ERROR(#{code => drekid_down, reason => Reason, state => State}),
+ {next_state, waiting, Data#drekid{node = undefined, pid = undefined}}.
+
+handle_info({drekid, Msg}, waiting, Data) ->
+ waiting({drekid, Msg}, Data);
+handle_info({nodedown, Node}, State, Data = #drekid{node = Node}) ->
+ {next_state, down, {nodedown, State, Data}, 0};
+handle_info(Info, State, Data) ->
+ ?LOG_ERROR(#{code => unhandled_info, state => State, info => Info}),
+ {stop, badinfo, Data}.
+
+handle_event(Event, State, Data) ->
+ ?LOG_ERROR(#{code => unhandled_event, state => State, event => Event}),
+ {stop, badevent, Data}.
+
+handle_sync_event(Event, From, State, Data) ->
+ ?LOG_ERROR(#{code => unhandled_sync_event, state => State, event => Event}),
+ {stop, badevent, badevent, Data}.
diff --git a/apps/dreki/src/drekid_tasks_store.erl b/apps/dreki/src/drekid_tasks_store.erl
new file mode 100644
index 0000000..e16182a
--- /dev/null
+++ b/apps/dreki/src/drekid_tasks_store.erl
@@ -0,0 +1,71 @@
+-module(drekid_tasks_store).
+-behaviour(dreki_store_backend).
+
+-export([install/0]).
+-export([start/0, start/5, checkout/1, checkin/1, stop/0, stop/1]).
+-export([valid_store/5]).
+-export([list/1, count/1, exists/2, get/2, create/2, update/2, delete/2]).
+
+tasks() ->
+ Tasks = #{<<"dreki.v1.exec">> => #{id => <<"dreki.v1.exec">>, module => drekid_task_handler, params => #{}}},
+ FreeBSD = [<<"dreki.v1.freebsd.jails.list">>,
+ <<"dreki.v1.freebsd.jails.get">>,
+ <<"dreki.v1.freebsd.jails.start">>,
+ <<"dreki.v1.freebsd.jails.exec">>
+ ],
+ Tasks0 = lists:foldr(fun (Id, Acc) ->
+ Task = #{id => Id, module => drekid_task_handler, params => dreki_freebsd_schemas:schema(Id)},
+ maps:put(Id, Task, Acc)
+ end, Tasks, FreeBSD),
+ Tasks0.
+
+urn() ->
+ Urn = dreki_node:urn(),
+ <<Urn/binary, "::tasks:drekid">>.
+
+install() ->
+ dreki_store:create_store(urn(), ?MODULE, #{}, #{}).
+
+valid_store(<<"tasks">>, _Loc, _Name, _NSMod, _Args) ->
+ ok;
+valid_store(_Ns, _Loc, _Name, _NSMod, _Args) ->
+ error.
+
+start() ->
+ ok.
+
+start(_Ns, _NsMod, _Loc, _XUrn, Args) ->
+ {ok, Args}.
+
+checkout(Args) ->
+ {ok, Args}.
+
+checkin(Args) ->
+ ok.
+
+stop() ->
+ ok.
+
+stop(Args) ->
+ ok.
+
+list(_) ->
+ {ok, [V || {K, V} <- maps:to_list(tasks())]}.
+
+get(_, Id) ->
+ maps:get(Id, tasks(), not_found).
+
+count(_) ->
+ {ok, maps:size(tasks())}.
+
+exists(_, Id) ->
+ {ok, maps:is_key(Id, tasks())}.
+
+create(_, _) ->
+ {error, not_supported}.
+
+update(_, _) ->
+ {error, not_supported}.
+
+delete(_, _) ->
+ {error, not_supported}.
diff --git a/apps/dreki/src/funs/dreki_fun_exec.erl b/apps/dreki/src/funs/dreki_fun_exec.erl
new file mode 100644
index 0000000..d704e80
--- /dev/null
+++ b/apps/dreki/src/funs/dreki_fun_exec.erl
@@ -0,0 +1,28 @@
+-module(dreki_fun_exec).
+-behaviour(dreki_funs).
+-export([schemas/0]).
+
+schemas() ->
+ #{<<"exec">> => #{default_version => <<"1.0">>, <<"1.0">> => schemas('1.0')}}.
+
+schemas('1.0') ->
+ #{
+ version => 'draft-06',
+ title => <<"Execute command">>,
+ type => object,
+ properties => #{
+ <<"id">> => #{type => string,
+ <<"dreki:form">> => #{default => generate_id}},
+ <<"name">> => #{type => string},
+ <<"description">> => #{type => string,
+ <<"dreki:form">> => #{
+ input => textarea,
+ textarea_mode => markdown
+ }},
+ <<"command">> => #{type => string, <<"dreki:form">> => #{
+ input_style => monospace,
+ placeholder => <<"/bin/true">>
+ }}
+ },
+ required => [id, name, command]
+ }.
diff --git a/apps/dreki/src/funs/dreki_funs.erl b/apps/dreki/src/funs/dreki_funs.erl
new file mode 100644
index 0000000..1d15f21
--- /dev/null
+++ b/apps/dreki/src/funs/dreki_funs.erl
@@ -0,0 +1,31 @@
+-module(dreki_funs).
+-include("dreki.hrl").
+
+-behaviour(dreki_store_namespace).
+-export([start/0, valid_store/4, format_item/1, schemas/0]).
+
+-callback(schemas() -> #{}).
+
+start() ->
+ ok.
+
+valid_store(_Namespace, _Location, _Name, _BackendMod) ->
+ ok.
+
+format_item(Item) ->
+ ok.
+
+handlers() ->
+ [dreki_fun_exec].
+
+-record(?MODULE, {
+ id,
+ version,
+ name,
+ handler,
+ content
+ }).
+
+schemas() ->
+ Schemas = lists:foldr(fun (Handler, Acc) -> maps:merge(Acc, Handler:schemas()) end, #{}, handlers()),
+ maps:put(default, <<"exec:1.0">>, Schemas).
diff --git a/apps/dreki/src/dreki_node.erl b/apps/dreki/src/node/dreki_node.erl
index 87dbc73..5ed7fac 100644
--- a/apps/dreki/src/dreki_node.erl
+++ b/apps/dreki/src/node/dreki_node.erl
@@ -1,10 +1,11 @@
-module(dreki_node).
-include("dreki.hrl").
--include_lib("opentelemetry_api/include/otel_tracer.hrl").
+-include_lib("kernel/include/logger.hrl").
+-include("dreki_otel.hrl").
-behaviour(partisan_gen_fsm).
-compile({no_auto_import,[get/0]}).
-export([get/0, urn/0, stores/0]).
--export([rpc/4, rpc/5]).
+-export([rpc/4, rpc/5, remote_rpc/7]).
-export([uri/0]). % deprecated
-export([parents/0, parents/1, parent/0, parent/1]).
-export([neighbours/0, neighbours/1]).
@@ -18,17 +19,64 @@ rpc(Path, Mod, Fun, Args) ->
-spec rpc(dreki_urn(), module(), function(), Args :: [], #{timeout => non_neg_integer()}) -> {ok, any()} | {error, rpc_error()}.
rpc(Path, Mod, Fun, Args, #{timeout := Timeout}) ->
- ?with_span(<<"dreki_node:rpc">>, #{}, fun(ChildSpanCtx) ->
+ Myself = self(),
+ ?with_span(?FUN_NAME, fun(SpanCtx) ->
+ ?set_attributes(#{mod => Mod, fn => Fun, remote_node_path => Path}),
+ ?LOG_INFO("Ctx ~p ~p ~p ~p", [?FUN_NAME, otel_ctx:get_current(), otel_tracer:current_span_ctx(), SpanCtx]),
case dreki_world_dns:node_param(dreki_world:path_to_domain(Path), node_name) of
- {ok, NodeName} ->
- case partisan_rpc_backend:call(NodeName, Mod, Fun, Args, Timeout) of
- {badrpc, Error} -> {error, {rpc_error, Path, Error}};
- Result -> Result
- end;
- Error -> Error
- end
+ {ok, NodeName} ->
+ ?set_attribute(remote_node, NodeName),
+ Ctx = otel_ctx:get_current(),
+ ChildSpanCtx = ?start_span(?FUN_NAME(remote), #{}),
+ TraceId = otel_span:hex_trace_id(SpanCtx),
+ SpanId = otel_span:hex_span_id(SpanCtx),
+ case partisan_rpc_backend:call(NodeName, ?MODULE, remote_rpc, [Myself, Mod, Fun, Args, Timeout, TraceId, SpanId], Timeout) of
+ {badrpc, RpcError} ->
+ ?set_status(error, <<"RPC_ERROR">>),
+ {error, {rpc, RpcError}};
+ Error = {error, _} ->
+ ?set_status(error, <<"RESULT">>),
+ Error;
+ Result ->
+ ?set_status(ok),
+ Result
+ end;
+ Error ->
+ ?set_status(error, <<"NODE_NOT_FOUND">>),
+ Error
+ end
end).
+remote_rpc(Pid, Mod, Fun, Args, Timeout, TraceId, SpanId) ->
+ ParentCtx = otel_tracer:from_remote_span(TraceId, SpanId, 1),
+ otel_tracer:with_span(ParentCtx, opentelemetry:get_tracer(), ?FUN_NAME, #{},
+ fun (Ctx) -> remote_rpc(Pid, Mod, Fun, Args, Timeout, Ctx) end).
+
+remote_rpc(Pid, Mod, Fun, Args, Timeout, Ctx) ->
+ ?set_attributes(#{remote_node => node(), module => Mod, function => Fun}),
+ try apply(Mod, Fun, Args) of
+ {badrpc, RpcError} ->
+ ?set_status(error, <<"RPC_ERROR">>),
+ {error, {rpc, RpcError}};
+ Error = {error, _} ->
+ ?set_status(error, <<"RESULT">>),
+ Error;
+ Result ->
+ Result
+ catch
+ throw:Term:Stacktrace ->
+ ?set_status(error, <<"THROW">>),
+ {error, {throw, Term, Stacktrace}};
+ exit:Reason:Stacktrace ->
+ ?set_status(error, <<"EXIT">>),
+ {error, {exit, Reason, Stacktrace}};
+ error:Reason:Stacktrace ->
+ ?set_status(error, <<"RUNTIME">>),
+ {error, {runtime_error, Reason, Stacktrace}}
+ after
+ ?end_span()
+ end.
+
get() ->
{ok, Node} = dreki_world:get_node(dreki_world:node()),
Node.
@@ -100,4 +148,3 @@ ensure_local_node() ->
create_local_node() ->
dreki_world:create_node(uri(), #{}).
-
diff --git a/apps/dreki/src/dreki_node_server.erl b/apps/dreki/src/node/dreki_node_server.erl
index c8bca51..c8bca51 100644
--- a/apps/dreki/src/dreki_node_server.erl
+++ b/apps/dreki/src/node/dreki_node_server.erl
diff --git a/apps/dreki/src/node/rebar.conf b/apps/dreki/src/node/rebar.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/apps/dreki/src/node/rebar.conf
diff --git a/apps/dreki/src/requests/dreki_requests.erl b/apps/dreki/src/requests/dreki_requests.erl
new file mode 100644
index 0000000..d970dbd
--- /dev/null
+++ b/apps/dreki/src/requests/dreki_requests.erl
@@ -0,0 +1,97 @@
+-module(dreki_requests).
+-behaviour(dreki_store_namespace).
+-export([start/0, version/0, valid_store/4, format_item/1, actions/0, actions/1, schemas/0, new/0, as_tuple/1, as_map/1]).
+-export([record_name/0, record_attributes/0]).
+-export([after_create/1]).
+-export([new/1, create/2]).
+-export([install/0]).
+
+-record(?MODULE, {
+ id,
+ version,
+ schema,
+ task_urn,
+ identity_urn,
+ node,
+ params
+ }).
+
+create(#{'@ns' := <<"tasks">>, '@location' := Loc, '@id' := TaskUrn}, Params0) ->
+ LocalStore = <<Loc/binary, "::requests:local">>,
+ Params = Params0#{task_urn => TaskUrn},
+ dreki_store:create(LocalStore, Params).
+
+install() ->
+ NodeUrn = dreki_node:urn(),
+ Urn = <<NodeUrn/binary, "::requests:local">>,
+ dreki_store:create_store(Urn, dreki_mnesia_store, #{}, #{}).
+
+record_name() ->
+ ?MODULE.
+
+record_attributes() ->
+ record_info(fields, ?MODULE).
+
+version() ->
+ 1.
+
+start() ->
+ ok.
+
+valid_store(_Ns, _Loc, _Name, _BackendMod) ->
+ ok.
+
+format_item(Item) ->
+ ok.
+
+actions() ->
+ [].
+
+actions(_) ->
+ [].
+
+schemas() ->
+ #{default => <<"request">>,
+ <<"request">> => #{
+ default_version => <<"1.0">>,
+ <<"1.0">> => schemas(<<"request">>, <<"1.0">>)
+ }
+ }.
+
+schemas(<<"request">>, <<"1.0">>) ->
+ #{
+ version => 'draft-06',
+ title => <<"Run Request">>,
+ type => object,
+ properties => #{
+ id => #{type => string, <<"dreki:form">> => #{readonly => true}},
+ node => #{type => string},
+ params => #{type => object}
+ },
+ required => [node, params]
+ }.
+
+new() ->
+ schemas(<<"request">>, <<"1.0">>).
+
+new(#{'@ns' := <<"tasks">>, '@id' := TaskUrn, id := TaskId, module := Module, params := TaskParams}) ->
+ Schema0 = schemas(<<"request">>, <<"1.0">>),
+ Schema = key_value:set([properties, params], TaskParams, Schema0),
+ {ok, Schema}.
+
+as_tuple(#{id := Id, task_urn := TaskUrn, node := Node, params := Params}) ->
+ #?MODULE{id = Id, version = undefined, schema = undefined, task_urn = TaskUrn,
+ node = Node, params = Params}.
+
+as_map(R = #?MODULE{}) ->
+ #{
+ id => R#?MODULE.id,
+ version => R#?MODULE.version,
+ schema => R#?MODULE.schema,
+ task_urn => R#?MODULE.task_urn,
+ node => R#?MODULE.node,
+ params => R#?MODULE.params
+ }.
+
+after_create(Request) ->
+ ok.
diff --git a/apps/dreki/src/runs/dreki_runs.erl b/apps/dreki/src/runs/dreki_runs.erl
new file mode 100644
index 0000000..9509fa6
--- /dev/null
+++ b/apps/dreki/src/runs/dreki_runs.erl
@@ -0,0 +1 @@
+-module(dreki_runs).
diff --git a/apps/dreki/src/storages/dreki_storages.erl b/apps/dreki/src/storages/dreki_storages.erl
new file mode 100644
index 0000000..3a87347
--- /dev/null
+++ b/apps/dreki/src/storages/dreki_storages.erl
@@ -0,0 +1,98 @@
+-module(dreki_storages).
+-behaviour(dreki_store_namespace).
+-export([start/0, version/0, valid_store/4, format_item/1, actions/0, actions/1, schemas/0, as_tuple/1, as_map/1]).
+-export([record_name/0, record_attributes/0]).
+-export([after_create/1]).
+-export([install_local_store/0, create_local_storage/4]).
+
+-record(?MODULE, {
+ id,
+ version,
+ schema,
+ name,
+ type,
+ handler,
+ params,
+ tags = []
+ }).
+
+install_local_store() ->
+ NodeUrn = dreki_node:urn(),
+ Urn = <<NodeUrn/binary, "::storages:local">>,
+ dreki_store:create_store(Urn, dreki_mnesia_store, #{}, #{}).
+
+create_local_storage(Type, Handler, Params, Tags) ->
+ NodeUrn = dreki_node:urn(),
+ LocalStoreUrn = <<NodeUrn/binary, "::storages:local">>,
+ Storage = #{
+ type => Type,
+ handler => Handler,
+ params => Params,
+ tags => Tags
+ },
+ dreki_store:create(LocalStoreUrn, Storage).
+
+record_name() -> ?MODULE.
+record_attributes() -> record_info(fields, ?MODULE).
+version() -> 1.
+
+start() ->
+ ok.
+
+valid_store(_Ns, _Loc, _Name, _BackendMod) ->
+ ok.
+
+format_item(Item) ->
+ ok.
+
+actions() ->
+ [].
+
+actions(_) ->
+ [].
+
+schemas() ->
+ #{default => <<"storage">>,
+ <<"storage">> => #{
+ default_version => <<"1.0">>,
+ <<"1.0">> => schemas(<<"storage">>, <<"1.0">>)
+ }
+ }.
+
+schemas(<<"storage">>, <<"1.0">>) ->
+ #{
+ version => 'draft-06',
+ title => <<"Storage">>,
+ type => object,
+ required => [id, type, handler, params],
+ properties => #{
+ id => #{type => string, <<"dreki:form">> => #{}},
+ name => #{type => string},
+ type => #{type => string, enum => [<<"fs">>, <<"os">>, <<"bs">>]},
+ handler => #{type => string, enum => [<<"zfs">>, <<"fs">>]},
+ params => #{type => object},
+ tags => #{type => array, items => #{type => string}}
+ }
+ }.
+
+as_tuple(Map = #{id := Id, type := Type, handler := Handler, params := Params, tags := Tags}) ->
+ #?MODULE{id = Id,
+ name = maps:get(name, Map, undefined),
+ type = Type,
+ handler = Handler,
+ params = Params,
+ tags = maps:get(tags, Map, [])
+ }.
+
+as_map(R = #?MODULE{}) ->
+ #{
+ id => R#?MODULE.id,
+ name => R#?MODULE.name,
+ type => R#?MODULE.type,
+ handler => R#?MODULE.handler,
+ params => R#?MODULE.params,
+ tags => R#?MODULE.tags
+ }.
+
+after_create(_Storage) ->
+ ok.
diff --git a/apps/dreki/src/storages/dreki_storages_zfs.erl b/apps/dreki/src/storages/dreki_storages_zfs.erl
new file mode 100644
index 0000000..fc8ce00
--- /dev/null
+++ b/apps/dreki/src/storages/dreki_storages_zfs.erl
@@ -0,0 +1,2 @@
+-module(dreki_storages_zfs).
+
diff --git a/apps/dreki/src/dreki_dets_store.erl b/apps/dreki/src/store/dreki_dets_store.erl
index 05cf9fb..aaa4c23 100644
--- a/apps/dreki/src/dreki_dets_store.erl
+++ b/apps/dreki/src/store/dreki_dets_store.erl
@@ -4,7 +4,7 @@
-behaviour(dreki_store_backend).
--export([start/0, start/4, checkout/1, checkin/1, stop/0, stop/1]).
+-export([start/0, start/5, checkout/1, checkin/1, stop/0, stop/1]).
-export([valid_store/5]).
-export([list/1, count/1, exists/2, get/2, create/2, update/2, delete/2]).
@@ -14,7 +14,7 @@ start() -> ok.
valid_store(_Namespace, _Location, _Name, _NSMod, _Args) -> ok.
-start(Namespace, Name, _XUrn, Args) ->
+start(Namespace, _NsMod, Name, _XUrn, Args) ->
StoreName = {?MODULE, Namespace, Name},
File = maps:get(file_name, Args, <<Namespace/binary, ".", Name/binary, ".dets">>),
FileName = binary:bin_to_list(File),
@@ -71,4 +71,3 @@ update({dreki_dets_store_ref, Tab}, Task = #dreki_task{persisted=true, dirty=tru
ok -> {ok, Task#dreki_task{dirty=false}};
{error, Error} -> {error, Error}
end.
-
diff --git a/apps/dreki/src/store/dreki_mnesia_store.erl b/apps/dreki/src/store/dreki_mnesia_store.erl
new file mode 100644
index 0000000..aceae09
--- /dev/null
+++ b/apps/dreki/src/store/dreki_mnesia_store.erl
@@ -0,0 +1,94 @@
+-module(dreki_mnesia_store).
+-include("dreki.hrl").
+-include("dreki_otel.hrl").
+-include_lib("stdlib/include/ms_transform.hrl").
+-include_lib("kernel/include/logger.hrl").
+-behaviour(dreki_store_backend).
+
+-export([start/0, start/5, checkout/1, checkin/1, stop/0, stop/1]).
+-export([valid_store/5]).
+-export([list/1, count/1, exists/2, get/2, create/2, update/2, delete/2]).
+
+start() ->
+ ok.
+
+valid_store(_NS, _Loc, _Name, _NSMod, _Args) ->
+ ok.
+
+start(Namespace, NsMod, Name, _XUrn, Args) ->
+ TabName = binary_to_atom(iolist_to_binary(["dreki_mnesia_store", "-", Namespace, "-", Name])),
+ case lists:member(TabName, mnesia:system_info(tables)) of
+ true ->
+ {ok, TabName};
+ false ->
+ DefaultOptions = [{rocksdb_copies, [node()]}],
+ Options0 = maps:get(mnesia_table_options, Args, DefaultOptions),
+ Options = [{attributes, NsMod:record_attributes()}, {record_name, NsMod:record_name()} | Options0],
+ {atomic, ok} = mnesia:create_table(TabName, Options),
+ ?LOG_NOTICE(#{message => "Created mnesia table for store", mnesia_table => TabName, store_namespace => Namespace, store_name => Name}),
+ {ok, TabName}
+ end.
+
+checkout(TabName) ->
+ {ok, TabName}.
+
+checkin(_) ->
+ ok.
+
+stop() ->
+ ok.
+
+stop(_) ->
+ ok.
+
+list(TabName) ->
+ transaction(fun () ->
+ [get(TabName, Id) || Id <- iter_all(mnesia:first(TabName), TabName)]
+ end).
+
+count(TabName) ->
+ mnesia:table_info(TabName, size).
+
+get(TabName, Id) ->
+ transaction(fun() ->
+ case mnesia:read(TabName, Id) of
+ [Tuple] ->
+ Tuple;
+ [] ->
+ not_found
+ end
+ end).
+
+exists(TabName, Id) ->
+ case get(TabName, Id) of
+ not_found ->
+ false;
+ _ ->
+ true
+ end.
+
+create(Table, Tuple) ->
+ transaction(fun() ->
+ mnesia:write(Table, Tuple, write),
+ get(Table, element(2, Tuple))
+ end).
+
+update(Table, Tuple) ->
+ create(Table, Tuple).
+
+delete(TabName, Id) ->
+ transaction(fun() ->
+ mnesia:delete(TabName, Id, write)
+ end).
+
+transaction(Fun) ->
+ ?with_span(?FUN_NAME, #{}, fun(_) -> {atomic, Result} = mnesia:transaction(Fun), Result end).
+
+iter_all(Id, TabName) ->
+ ?with_span(?FUN_NAME, #{}, fun(_) -> iter_all(Id, TabName, []) end).
+
+iter_all('$end_of_table', _, Acc) ->
+ Acc;
+iter_all(Id, TabName, Acc) ->
+ [Tuple] = mnesia:read(TabName, Id),
+ iter_all(mnesia:next(TabName, Id), TabName, [Tuple | Acc]).
diff --git a/apps/dreki/src/dreki_store.erl b/apps/dreki/src/store/dreki_store.erl
index ae6fd66..63d87a8 100644
--- a/apps/dreki/src/dreki_store.erl
+++ b/apps/dreki/src/store/dreki_store.erl
@@ -1,7 +1,7 @@
-module(dreki_store).
-include("dreki.hrl").
-include("dreki_plum.hrl").
--include_lib("opentelemetry_api/include/otel_tracer.hrl").
+-include("dreki_otel.hrl").
-define(BACKENDS_PT, {dreki_stores, backends}).
-compile({no_auto_import,[get/1]}).
-export([backends/0]).
@@ -16,13 +16,20 @@
-export([callback/3, list_/2, get_/2, new_/2, create_/3, update_/3, delete_/2]).
--record(store, {urn, xurn, namespace, name, namespace_mod, backend_mod, backend_params}).
+-record(store, {urn, xurn, namespace, name, namespace_mod, format, backend_mod, backend_params}).
-type t() :: #store{}.
-type store() :: t() | dreki_uri() | dreki_expanded_uri().
-backends() -> [dreki_dets_store, dreki_world_store].
-namespaces() -> [{<<"tasks">>, dreki_tasks, #{}}].
+backends() ->
+ [dreki_dets_store, dreki_world_store].
+
+namespaces() ->
+ [
+ {<<"tasks">>, dreki_tasks, #{}},
+ {<<"requests">>, dreki_requests, #{}},
+ {<<"storages">>, dreki_storages, #{}}
+ ].
namespace(Name) ->
lists:keyfind(Name, 1, namespaces()).
@@ -80,7 +87,7 @@ start_store__(Store) ->
_Started -> {error, {store_already_started, Store#store.urn}}
end.
start_store___(Store = #store{backend_mod = Mod}) ->
- case Mod:start(Store#store.namespace, Store#store.name, Store#store.urn, Store#store.backend_params) of
+ case Mod:start(Store#store.namespace, Store#store.namespace_mod, Store#store.name, Store#store.urn, Store#store.backend_params) of
{ok, Backend} -> {ok, Backend};
{error, Error} -> {error, {store_start_failed, Store#store.urn, Error}}
end.
@@ -163,10 +170,11 @@ get_store(Location, Namespace, Name) ->
end.
as_record([Map]) -> as_record(Map);
-as_record(#{urn := Urn, name := Name, namespace := Namespace, module := Module, module_params := ModuleParams}) ->
+as_record(Store = #{urn := Urn, name := Name, namespace := Namespace, module := Module, module_params := ModuleParams}) ->
{ok, XUrn} = dreki_urn:expand(Urn),
{_, NSMod, _} = lists:keyfind(Namespace, 1, namespaces()),
- #store{urn = Urn, xurn = XUrn, name = Name, namespace = Namespace, namespace_mod = NSMod, backend_mod = Module, backend_params = ModuleParams}.
+ Format = maps:get(format, Store, tuple),
+ #store{urn = Urn, xurn = XUrn, name = Name, namespace = Namespace, namespace_mod = NSMod, format = Format, backend_mod = Module, backend_params = ModuleParams}.
store_as_map(#store{urn = Urn, name = Name, namespace = Namespace, namespace_mod = NSMod, backend_mod = Module, backend_params = ModuleParams}) ->
#{urn => Urn, name => Name, namespace => Namespace, namespace_mod => NSMod, backend_mod => Module, backend_params => ModuleParams}.
@@ -191,14 +199,14 @@ delete(SArg) ->
get_store_(SArg, delete_, []).
list_(#{resource := #{directory := _}}, Store) ->
- handle_result(Store, collection, callback(Store, list, []));
+ handle_result(Store, collection, callback(Store, list, []), list);
list_(#{schema := _}, Store) ->
{todo, schema};
list_(_, _) ->
not_supported.
get_(#{resource := #{resource := #{id := Id}}}, Store) ->
- handle_result(Store, single, callback(Store, get, [Id]));
+ handle_result(Store, single, callback(Store, get, [Id]), get);
get_(#{schema := _}, Store) ->
{todo, get_schema};
get_(_, _) -> not_supported.
@@ -208,21 +216,36 @@ new_(#{resource := #{directory := _}}, Store) ->
New = NSMod:new(),
{ok, New}.
-create_(XUrn = #{directory := _}, Store, Data) ->
- case validate(XUrn, Data) of
- {ok, _} ->
- handle_result(Store, single, callback(Store, create, [Data]));
- {error, Errors} -> {error, #{code => validation_failed, status => 422, errors => jesse_error:to_json({error, Errors}, [])}}
- end;
+create_(XUrn = #{resource := #{directory := _}}, Store, Data0) ->
+ Id = maps:get(id, Data0, ksuid:gen_id()),
+ Data1 = Data0#{id => Id},
+ case validate(XUrn, Data1) of
+ {ok, _} ->
+ Data = case Store#store.format of
+ tuple ->
+ Mod = Store#store.namespace_mod,
+ Mod:as_tuple(Data1);
+ map -> Data1
+ end,
+ handle_result(Store, single, callback(Store, create, [Data]), create);
+ {error, Errors} ->
+ {error, #{code => validation_failed, status => 422, errors => jesse_error:to_json({error, Errors}, [])}}
+ end;
create_(_, _, _) ->
- not_supported.
+ not_supported.
-update_(XUrn = #{resource := _}, Store, Data) ->
+update_(XUrn = #{resource := #{resource := _}}, Store, Data0) ->
case get(XUrn) of
{ok, Prev} ->
- case validate(XUrn, Data) of
+ case validate(XUrn, Data0) of
{ok, _} ->
- handle_result(Store, single, callback(Store, create, [Data]));
+ Data = case Store#store.format of
+ tuple ->
+ Mod = Store#store.namespace_mod,
+ Mod:as_tuple(Data0);
+ map -> Data0
+ end,
+ handle_result(Store, single, callback(Store, update, [XUrn, Data]), update);
{error, Errors} ->
{error, #{code => validation_failed, status => 422, errors => jesse_error:to_json({error, Errors}, [])}}
end;
@@ -240,21 +263,27 @@ get_store_(StoreArg, Fun, Args) when is_binary(StoreArg) ->
logger:debug("Expanding in get_store_ :) ~p", [StoreArg]),
case dreki_urn:expand(StoreArg) of
Error = {error, _} -> Error;
- {ok, XUrn = #{resource := #{schemas := _}}} -> get_schema_(XUrn, Fun, Args);
- {ok, XUrn = #{resource := #{schema := _}}} -> get_schema_(XUrn, Fun, Args);
{ok, XUrn} -> get_store_(XUrn, Fun, Args)
end;
+get_store_(XUrn = #{resource := #{shemas := _}}, Fun, Args) ->
+ get_schema_(XUrn, Fun, Args);
+get_store_(XUrn = #{resource := #{schema := _}}, Fun, Args) ->
+ get_schema_(XUrn, Fun, Args);
get_store_(XUrn = #{resource := _}, Fun, Args) ->
- ?with_span(<<"dreki_store:get_store_">>, #{}, fun(_Ctx) ->
- logger:debug("Getting store usually! ~p", [XUrn]),
- case get_store(XUrn) of
+ %%T = otel_tracer:start_span(opentelemetry:get_tracer(), <<"dreki_store">>, #{}),
+ %%?set_current_span(T),
+ ?with_span(opentelemetry:get_tracer(), #{}, fun(SpanCtx) ->
+ logger:debug("Getting store usually! ~p / Ctx: ~p / Span Ctx ~p / Cur Span Ctx ~p", [XUrn, otel_ctx:get_current(), SpanCtx, otel_tracer:current_span_ctx()]),
+ Result = case get_store(XUrn) of
Error = {error, _} -> Error;
{ok, Store} ->
case apply(?MODULE, Fun, [XUrn, Store | Args]) of
not_supported -> {error, {not_supported, Fun, maps:get(urn, XUrn)}};
Out -> Out
end
- end
+ end,
+ ?end_span(),
+ Result
end).
is_local(#store{xurn = #{kind := region}}) -> true;
@@ -279,38 +308,50 @@ callback(true, #store{urn = Urn, backend_mod = Mod, backend_params = Params}, Fu
Res
end.
-handle_result(_, _, {error, Err}) -> {error, Err};
-handle_result(Store, collection, {ok, Collection}) when is_list(Collection) ->
- handle_result(Store, collection, Collection);
-handle_result(Store, collection, Collection) when is_list(Collection) ->
- {ok, handle_collection_result(Collection, Store, [])};
-handle_result(Store, single, {ok, Item}) when is_map(Item) ->
- {ok, handle_single_result(Item, Store)}.
-
-handle_collection_result([Item | Rest], Store, Acc0) ->
- Acc = case handle_single_result(Item, Store) of
+handle_result(_, _, {error, Err}, _) -> {error, Err};
+handle_result(Store, collection, {ok, Collection}, PostCallback) when is_list(Collection) ->
+ handle_result(Store, collection, Collection, PostCallback);
+handle_result(Store, collection, Collection, PostCallback) when is_list(Collection) ->
+ {ok, handle_collection_result(Collection, Store, [], PostCallback)};
+handle_result(Store, single, Item, PostCallback) ->
+ handle_single_result(Item, Store, PostCallback);
+handle_result(Store, single, {ok, Item}, PostCallback)->
+ handle_single_result(Item, Store, PostCallback).
+
+handle_collection_result([Item | Rest], Store, Acc0, PostCallback) ->
+ Acc = case handle_single_result(Item, Store, PostCallback) of
ignore -> Acc0;
{ok, I} -> [I | Acc0]
end,
- handle_collection_result(Rest, Store, Acc);
-handle_collection_result([], Store, Acc) ->
+ handle_collection_result(Rest, Store, Acc, PostCallback);
+handle_collection_result([], Store, Acc, _PostCallback) ->
format_collection(Acc, Store).
-handle_single_result(Item = #{id := LId}, Store = #store{namespace_mod = Mod}) ->
+handle_single_result(Tuple, Store = #store{format = tuple, namespace_mod = Mod}, PostCallback) when is_tuple(Tuple) ->
+ handle_single_result(Mod:as_map(Tuple), Store, PostCallback);
+handle_single_result(Item = #{id := LId}, Store = #store{namespace_mod = Mod}, PostCallback) ->
case Mod:format_item(Item) of
- ok -> format_item(Item, Store);
- {ok, M} -> format_item(M, Store);
+ ok -> format_item(Item, Item, Store, PostCallback);
+ {ok, M} -> format_item(M, Item, Store, PostCallback);
Err -> Err
end.
-format_item(Item = #{id := LId}, Store = #store{urn = Urn}) ->
+format_item(Item = #{id := LId}, OriginalItem, Store = #store{urn = Urn, namespace = NS, namespace_mod = Mod}, PostCallback) ->
+ {ok, XUrn} = dreki_urn:expand(Urn),
+ SelfUrn = <<Urn/binary, ":", LId/binary>>,
AtLinks = #{
- self => <<Urn/binary, ":", LId>>,
- parent => maps:get(location, Urn)
+ self => SelfUrn,
+ parent => Urn
},
AllAtLinks = maps:merge(AtLinks, maps:get('@links', Item, #{})),
- I = #{'@id' => <<Urn/binary, ":", LId>>, '@links' => AllAtLinks},
- {ok, maps:merge(I, Item)}.
+ Actions = [#{id => Id, title => Title, new => New, create => Create} || {Id, Title, New, Create} <- Mod:actions(OriginalItem)],
+ I = #{'@id' => SelfUrn, '@ns' => NS, '@location' => maps:get(location, XUrn), '@links' => AllAtLinks, '@actions' => Actions},
+ FullItem = maps:merge(I, Item),
+ case PostCallback of
+ create -> Mod:after_create(FullItem);
+ _ -> undefined
+ end,
+ {ok, FullItem}.
format_collection(Data, Store = #store{urn = Urn}) ->
#{'@links' => #{self => Urn},
@@ -362,11 +403,12 @@ get_schema_(#{urn := Urn, resource := #{namespace := NS, schema := #{schema := d
case maps:get(default_version, Schema, not_found) of
not_found -> not_found;
SchemaVer ->
- NewUrn = binary:replace(Urn, <<"schemas::">>, <<"schemas:", SchemaName/binary, ":", SchemaVer/binary>>),
+ NewUrn0 = binary:replace(Urn, <<"::schemas::">>, <<>>),
+ NewUrn = <<NewUrn0/binary, "::schemas:", SchemaName/binary, ":", SchemaVer/binary>>,
{ok, XUrn} = dreki_urn:expand(NewUrn),
- logger:debug("Looking up default schema as ~p", [NewUrn, XUrn]),
+ logger:debug("Looking up default schema as ~p (~p)", [NewUrn, XUrn]),
get_schema_(XUrn, get_, Args)
- end
+ end
end;
get_schema_(XUrn = #{resource := #{namespace := NS, schema := #{schema := SchemaName, version := Version}}}, get_, _) ->
{_, NSMod, _} = namespace(NS),
@@ -423,7 +465,11 @@ get_xurn_namespace(#{resource := #{namespace := NS}}) -> NS;
get_xurn_namespace(#{resource := #{directory := #{namespace := NS}}}) -> NS;
get_xurn_namespace(#{resource := #{resource := #{namespace := NS}}}) -> NS.
+schema_loader(SchemaRef) ->
+ logger:debug("Trying to load schema ~p", [SchemaRef]),
+ get(SchemaRef).
+
validate_(XUrn, Schema, Data) ->
JesseOpts = [{allowed_errors, infinity},
- {schema_loader_fun, fun get/1}],
+ {schema_loader_fun, fun schema_loader/1}],
jesse:validate_with_schema(Schema, Data, JesseOpts).
diff --git a/apps/dreki/src/dreki_store_backend.erl b/apps/dreki/src/store/dreki_store_backend.erl
index 55db90e..55db90e 100644
--- a/apps/dreki/src/dreki_store_backend.erl
+++ b/apps/dreki/src/store/dreki_store_backend.erl
diff --git a/apps/dreki/src/dreki_store_namespace.erl b/apps/dreki/src/store/dreki_store_namespace.erl
index 3095ef7..3095ef7 100644
--- a/apps/dreki/src/dreki_store_namespace.erl
+++ b/apps/dreki/src/store/dreki_store_namespace.erl
diff --git a/apps/dreki/src/dreki_task.erl b/apps/dreki/src/tasks/dreki_task.erl
index b762aea..b762aea 100644
--- a/apps/dreki/src/dreki_task.erl
+++ b/apps/dreki/src/tasks/dreki_task.erl
diff --git a/apps/dreki/src/dreki_tasks.erl b/apps/dreki/src/tasks/dreki_tasks.erl
index 0899386..750aed0 100644
--- a/apps/dreki/src/dreki_tasks.erl
+++ b/apps/dreki/src/tasks/dreki_tasks.erl
@@ -2,7 +2,7 @@
-include("dreki.hrl").
-behaviour(dreki_store_namespace).
--export([start/0, version/0, valid_store/4, format_item/1, schemas/0, new/0]).
+-export([start/0, version/0, valid_store/4, format_item/1, actions/0, actions/1, schemas/0, new/0, as_tuple/1, as_map/1]).
%% old stuff
-export([resolve/1, exists/1, read_uri/2]).
@@ -13,14 +13,14 @@ valid_store(_Namespace, _Location, _Name, _BackendMod) -> ok.
format_item(Item) -> ok.
-handlers() -> [dreki_tasks_script, dreki_tasks_cloyster].
+handlers() -> [dreki_tasks_script, dreki_tasks_cloyster, drekid_function].
--record(?MODULE, {
+-record(t, {
id,
version,
schema,
- handler,
- handler_manifest
+ module,
+ params
}).
version() -> 1.
@@ -29,13 +29,25 @@ new() ->
#{
<<"@schema">> => <<"task:1.0">>,
<<"id">> => ksuid:gen_id(),
- <<"handler">> => <<"dreki_tasks_cloyster">>,
- <<"handler_manifest">> => #{
+ <<"module">> => <<"dreki_tasks_cloyster">>,
+ <<"params">> => #{
<<"@schema">> => <<"cloyster-task:1.0">>,
<<"script">> => <<>>
}
}.
+actions() ->
+ [
+ {new, <<"New task">>, {dreki_tasks, new, []}, {dreki_store, create, []}}
+ ].
+
+actions(_) ->
+ [
+ {request, <<"Request run">>,
+ {dreki_requests, new, []},
+ {dreki_store, create, []}}
+ ].
+
schemas() ->
Subs = lists:foldr(fun (Handler, Acc) -> maps:merge(Acc, Handler:schemas()) end,
#{}, handlers()),
@@ -80,6 +92,19 @@ schemas(task, <<"1.0">>) ->
required => [handler, handler_manifest]
}.
+as_tuple(#{id := Id, module := Module, params := Params}) ->
+ #t{id = Id, version = undefined, schema = undefined,
+ module = Module, params = Params}.
+
+as_map(Task = #t{}) ->
+ #{
+ id => Task#t.id,
+ version => Task#t.version,
+ schema => Task#t.schema,
+ module => Task#t.module,
+ params => Task#t.params
+ }.
+
%% old stuff
read_uri(undefined, Uri) ->
@@ -136,6 +161,6 @@ load_local_stores() ->
env => Env,
name => Name
},
- maps:put(Name, Store, Acc)
+ maps:put(Name, Store, Acc)
end,
lists:foldr(MapFn, #{}, Val).
diff --git a/apps/dreki/src/dreki_tasks_cloyster.erl b/apps/dreki/src/tasks/dreki_tasks_cloyster.erl
index 3fb045d..3fb045d 100644
--- a/apps/dreki/src/dreki_tasks_cloyster.erl
+++ b/apps/dreki/src/tasks/dreki_tasks_cloyster.erl
diff --git a/apps/dreki/src/dreki_tasks_script.erl b/apps/dreki/src/tasks/dreki_tasks_script.erl
index 8eeb563..8eeb563 100644
--- a/apps/dreki/src/dreki_tasks_script.erl
+++ b/apps/dreki/src/tasks/dreki_tasks_script.erl
diff --git a/apps/dreki/src/dreki_world.erl b/apps/dreki/src/world/dreki_world.erl
index 437b6c8..437b6c8 100644
--- a/apps/dreki/src/dreki_world.erl
+++ b/apps/dreki/src/world/dreki_world.erl
diff --git a/apps/dreki/src/dreki_world_dns.erl b/apps/dreki/src/world/dreki_world_dns.erl
index 058c9db..058c9db 100644
--- a/apps/dreki/src/dreki_world_dns.erl
+++ b/apps/dreki/src/world/dreki_world_dns.erl
diff --git a/apps/dreki/src/dreki_world_plum_events.erl b/apps/dreki/src/world/dreki_world_plum_events.erl
index b32ae09..b32ae09 100644
--- a/apps/dreki/src/dreki_world_plum_events.erl
+++ b/apps/dreki/src/world/dreki_world_plum_events.erl
diff --git a/apps/dreki/src/dreki_world_server.erl b/apps/dreki/src/world/dreki_world_server.erl
index 2bc41ed..2bc41ed 100644
--- a/apps/dreki/src/dreki_world_server.erl
+++ b/apps/dreki/src/world/dreki_world_server.erl
diff --git a/apps/dreki/src/dreki_world_store.erl b/apps/dreki/src/world/dreki_world_store.erl
index 71ef2ce..050efd0 100644
--- a/apps/dreki/src/dreki_world_store.erl
+++ b/apps/dreki/src/world/dreki_world_store.erl
@@ -8,7 +8,7 @@
-type db() :: #store{}.
-type args() :: #{}.
--export([start/0, start/4, checkout/1, checkin/1, stop/0, stop/1]).
+-export([start/0, start/5, checkout/1, checkin/1, stop/0, stop/1]).
-export([valid_store/5]).
-export([list/1, count/1, exists/2, get/2, create/2, update/2, delete/2]).
@@ -19,7 +19,7 @@ valid_store(_Namespace, Location, _Name, _NSMod, _Args) ->
end.
start() -> ok.
-start(_, _, _, _) -> {ok, dreki_world_store}.
+start(_, _, _, _, _) -> {ok, dreki_world_store}.
checkout(_) -> {ok, #store{}}.
checkin(_) -> ok.
diff --git a/apps/dreki/src/dreki_world_tasks.erl b/apps/dreki/src/world/dreki_world_tasks.erl
index c27a7dc..c27a7dc 100644
--- a/apps/dreki/src/dreki_world_tasks.erl
+++ b/apps/dreki/src/world/dreki_world_tasks.erl