diff options
Diffstat (limited to 'apps/dreki')
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 |