mirror of
https://github.com/valitydev/sequences.git
synced 2024-11-06 02:05:20 +00:00
HG-253: Sequence service created (#1)
This commit is contained in:
parent
06c06f2830
commit
5bc3c65824
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# general
|
||||
log
|
||||
/_build/
|
||||
/_checkouts/
|
||||
*~
|
||||
erl_crash.dump
|
||||
.tags*
|
||||
*.sublime-workspace
|
||||
.DS_Store
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
/.idea/
|
||||
*.beam
|
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
[submodule "build_utils"]
|
||||
path = build_utils
|
||||
url = git@github.com:rbkmoney/build_utils.git
|
||||
branch = master
|
25
Dockerfile.sh
Executable file
25
Dockerfile.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
cat <<EOF
|
||||
FROM $BASE_IMAGE
|
||||
MAINTAINER Natalia Pulina <n.pulina@rbkmoney.com>
|
||||
COPY ./_build/prod/rel/sequences /opt/sequences
|
||||
CMD /opt/sequences/bin/sequences foreground
|
||||
EXPOSE 8022
|
||||
LABEL base_image_tag=$BASE_IMAGE_TAG
|
||||
LABEL build_image_tag=$BUILD_IMAGE_TAG
|
||||
# A bit of magic to get a proper branch name
|
||||
# even when the HEAD is detached (Hey Jenkins!
|
||||
# BRANCH_NAME is available in Jenkins env).
|
||||
LABEL branch=$( \
|
||||
if [ "HEAD" != $(git rev-parse --abbrev-ref HEAD) ]; then \
|
||||
echo $(git rev-parse --abbrev-ref HEAD); \
|
||||
elif [ -n "$BRANCH_NAME" ]; then \
|
||||
echo $BRANCH_NAME; \
|
||||
else \
|
||||
echo $(git name-rev --name-only HEAD); \
|
||||
fi)
|
||||
LABEL commit=$(git rev-parse HEAD)
|
||||
LABEL commit_number=$(git rev-list --count HEAD)
|
||||
WORKDIR /opt/sequences
|
||||
EOF
|
||||
|
64
Jenkinsfile
vendored
64
Jenkinsfile
vendored
@ -1 +1,65 @@
|
||||
#!groovy
|
||||
// -*- mode: groovy -*-
|
||||
|
||||
def finalHook = {
|
||||
runStage('store CT logs') {
|
||||
archive '_build/test/logs/'
|
||||
}
|
||||
}
|
||||
|
||||
build('sequences', 'docker-host', finalHook) {
|
||||
checkoutRepo()
|
||||
loadBuildUtils()
|
||||
|
||||
def pipeDefault
|
||||
def withWsCache
|
||||
runStage('load pipeline') {
|
||||
env.JENKINS_LIB = "build_utils/jenkins_lib"
|
||||
pipeDefault = load("${env.JENKINS_LIB}/pipeDefault.groovy")
|
||||
withWsCache = load("${env.JENKINS_LIB}/withWsCache.groovy")
|
||||
}
|
||||
|
||||
pipeDefault() {
|
||||
if (env.BRANCH_NAME != 'master') {
|
||||
runStage('compile') {
|
||||
withGithubPrivkey {
|
||||
sh 'make wc_compile'
|
||||
}
|
||||
}
|
||||
runStage('lint') {
|
||||
sh 'make wc_lint'
|
||||
}
|
||||
runStage('xref') {
|
||||
sh 'make wc_xref'
|
||||
}
|
||||
runStage('dialyze') {
|
||||
withWsCache("_build/default/rebar3_19.1_plt") {
|
||||
sh 'make wc_dialyze'
|
||||
}
|
||||
}
|
||||
runStage('test') {
|
||||
sh "make wdeps_test"
|
||||
}
|
||||
}
|
||||
runStage('make release') {
|
||||
withGithubPrivkey {
|
||||
sh "make wc_release"
|
||||
}
|
||||
}
|
||||
runStage('build image') {
|
||||
sh "make build_image"
|
||||
}
|
||||
|
||||
try {
|
||||
if (env.BRANCH_NAME == 'master' || env.BRANCH_NAME.startsWith('epic')) {
|
||||
runStage('push image') {
|
||||
sh "make push_image"
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
runStage('rm local image') {
|
||||
sh 'make rm_local_image'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
73
Makefile
Normal file
73
Makefile
Normal file
@ -0,0 +1,73 @@
|
||||
REBAR := $(shell which rebar3 2>/dev/null || which ./rebar3)
|
||||
SUBMODULES = build_utils
|
||||
SUBTARGETS = $(patsubst %,%/.git,$(SUBMODULES))
|
||||
|
||||
UTILS_PATH := build_utils
|
||||
TEMPLATES_PATH := .
|
||||
|
||||
# Name of the service
|
||||
SERVICE_NAME := sequences
|
||||
# Service image default tag
|
||||
SERVICE_IMAGE_TAG ?= $(shell git rev-parse HEAD)
|
||||
# The tag for service image to be pushed with
|
||||
SERVICE_IMAGE_PUSH_TAG ?= $(SERVICE_IMAGE_TAG)
|
||||
|
||||
# Base image for the service
|
||||
BASE_IMAGE_NAME := service_erlang
|
||||
BASE_IMAGE_TAG := 13454a94990acb72f753623ec13599a9f6f4f852
|
||||
|
||||
# Build image tag to be used
|
||||
BUILD_IMAGE_TAG := 317d28640a5dd2ec6e732d81283628d8ad3f3f52
|
||||
|
||||
CALL_ANYWHERE := all submodules rebar-update compile xref lint dialyze start devrel release clean distclean
|
||||
|
||||
CALL_W_CONTAINER := $(CALL_ANYWHERE) test
|
||||
|
||||
all: compile
|
||||
|
||||
-include $(UTILS_PATH)/make_lib/utils_container.mk
|
||||
-include $(UTILS_PATH)/make_lib/utils_image.mk
|
||||
|
||||
.PHONY: $(CALL_W_CONTAINER)
|
||||
|
||||
# CALL_ANYWHERE
|
||||
$(SUBTARGETS): %/.git: %
|
||||
git submodule update --init $<
|
||||
touch $@
|
||||
|
||||
submodules: $(SUBTARGETS)
|
||||
|
||||
rebar-update:
|
||||
$(REBAR) update
|
||||
|
||||
compile: submodules rebar-update
|
||||
$(REBAR) compile
|
||||
|
||||
xref: submodules
|
||||
$(REBAR) xref
|
||||
|
||||
lint:
|
||||
elvis rock
|
||||
|
||||
dialyze:
|
||||
$(REBAR) dialyzer
|
||||
|
||||
start: submodules
|
||||
$(REBAR) run
|
||||
|
||||
devrel: submodules
|
||||
$(REBAR) release
|
||||
|
||||
release: distclean
|
||||
$(REBAR) as prod release
|
||||
|
||||
clean:
|
||||
$(REBAR) clean
|
||||
|
||||
distclean:
|
||||
$(REBAR) clean
|
||||
rm -rf _build
|
||||
|
||||
# CALL_W_CONTAINER
|
||||
test: submodules
|
||||
$(REBAR) ct
|
1
build_utils
Submodule
1
build_utils
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 8dd1d30e97e4db20c9da98a5ddb261b199402bc0
|
21
config/sys.config
Normal file
21
config/sys.config
Normal file
@ -0,0 +1,21 @@
|
||||
[
|
||||
{sequences, [
|
||||
{automaton_service_url, "http://machinegun:8022/v1/automaton"},
|
||||
{ip, "::"},
|
||||
{port, 8022},
|
||||
{net_opts, [
|
||||
{timeout, 60000}
|
||||
]}
|
||||
]},
|
||||
{lager, [
|
||||
{error_logger_redirect, true},
|
||||
{log_root, "/var/log/sequences"},
|
||||
{handlers, [
|
||||
{lager_file_backend, [
|
||||
{file, "log.json"},
|
||||
{level, debug},
|
||||
{formatter, lager_logstash_formatter}
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
].
|
6
config/vm.args
Normal file
6
config/vm.args
Normal file
@ -0,0 +1,6 @@
|
||||
-sname sequences
|
||||
|
||||
-setcookie sequences_cookie
|
||||
|
||||
+K true
|
||||
+A 10
|
35
docker-compose.sh
Executable file
35
docker-compose.sh
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
cat <<EOF
|
||||
version: '2.1'
|
||||
services:
|
||||
|
||||
${SERVICE_NAME}:
|
||||
image: ${BUILD_IMAGE}
|
||||
volumes:
|
||||
- .:$PWD
|
||||
- $HOME/.cache:/home/$UNAME/.cache
|
||||
working_dir: $PWD
|
||||
command: /sbin/init
|
||||
depends_on:
|
||||
machinegun:
|
||||
condition: service_healthy
|
||||
|
||||
machinegun:
|
||||
image: dr.rbkmoney.com/rbkmoney/machinegun:1844dff663c24acdcd32f30ae3ea208f5d05a008
|
||||
command: /opt/machinegun/bin/machinegun foreground
|
||||
volumes:
|
||||
- ./test/machinegun/config.yaml:/opt/machinegun/etc/config.yaml
|
||||
healthcheck:
|
||||
test: "curl http://localhost:8022/"
|
||||
interval: 5s
|
||||
timeout: 1s
|
||||
retries: 12
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
driver_opts:
|
||||
com.docker.network.enable_ipv6: "true"
|
||||
com.docker.network.bridge.enable_ip_masquerade: "true"
|
||||
EOF
|
||||
|
89
elvis.config
Normal file
89
elvis.config
Normal file
@ -0,0 +1,89 @@
|
||||
[
|
||||
{elvis, [
|
||||
{config, [
|
||||
#{
|
||||
dirs => ["src"],
|
||||
filter => "*.erl",
|
||||
rules => [
|
||||
{elvis_style, line_length, #{limit => 120, skip_comments => false}},
|
||||
{elvis_style, no_tabs},
|
||||
{elvis_style, no_trailing_whitespace},
|
||||
{elvis_style, macro_module_names},
|
||||
{elvis_style, operator_spaces, #{rules => [{right, ","}, {right, "++"}, {left, "++"}]}},
|
||||
{elvis_style, nesting_level, #{level => 3}},
|
||||
{elvis_style, god_modules, #{limit => 25}},
|
||||
{elvis_style, no_if_expression},
|
||||
{elvis_style, invalid_dynamic_call, #{ignore => [elvis]}},
|
||||
{elvis_style, used_ignored_variable},
|
||||
{elvis_style, no_behavior_info},
|
||||
{elvis_style, module_naming_convention, #{regex => "^([a-z][a-z0-9]*_?)*(_SUITE)?$"}},
|
||||
{elvis_style, function_naming_convention, #{regex => "^([a-z][a-z0-9]*_?)*$"}},
|
||||
{elvis_style, state_record_and_type},
|
||||
{elvis_style, no_spec_with_records},
|
||||
{elvis_style, dont_repeat_yourself, #{min_complexity => 10}},
|
||||
{elvis_style, no_debug_call, #{ignore => [elvis, elvis_utils]}}
|
||||
]
|
||||
},
|
||||
#{
|
||||
dirs => ["test"],
|
||||
filter => "*.erl",
|
||||
rules => [
|
||||
{elvis_style, line_length, #{limit => 120, skip_comments => false}},
|
||||
{elvis_style, no_tabs},
|
||||
{elvis_style, no_trailing_whitespace},
|
||||
{elvis_style, macro_module_names},
|
||||
{elvis_style, operator_spaces, #{rules => [{right, ","}, {right, "++"}, {left, "++"}]}},
|
||||
{elvis_style, nesting_level, #{level => 3}},
|
||||
{elvis_style, god_modules, #{limit => 25}},
|
||||
{elvis_style, no_if_expression},
|
||||
{elvis_style, invalid_dynamic_call, #{ignore => [elvis]}},
|
||||
{elvis_style, used_ignored_variable},
|
||||
{elvis_style, no_behavior_info},
|
||||
{elvis_style, module_naming_convention, #{regex => "^([a-z][a-z0-9]*_?)*(_SUITE)?$"}},
|
||||
{elvis_style, function_naming_convention, #{regex => "^([a-z][a-z0-9]*_?)*$"}},
|
||||
{elvis_style, state_record_and_type},
|
||||
{elvis_style, no_spec_with_records},
|
||||
{elvis_style, dont_repeat_yourself, #{min_complexity => 10}},
|
||||
{elvis_style, no_debug_call, #{ignore => [elvis, elvis_utils]}}
|
||||
]
|
||||
},
|
||||
#{
|
||||
dirs => ["."],
|
||||
filter => "Makefile",
|
||||
ruleset => makefiles
|
||||
},
|
||||
#{
|
||||
dirs => ["."],
|
||||
filter => "elvis.config",
|
||||
ruleset => elvis_config
|
||||
},
|
||||
#{
|
||||
dirs => ["."],
|
||||
filter => "rebar.config",
|
||||
rules => [
|
||||
{elvis_style, line_length, #{limit => 120, skip_comments => false}},
|
||||
{elvis_style, no_tabs},
|
||||
{elvis_style, no_trailing_whitespace}
|
||||
]
|
||||
},
|
||||
#{
|
||||
dirs => ["."],
|
||||
filter => "rebar.config",
|
||||
rules => [
|
||||
{elvis_style, line_length, #{limit => 120, skip_comments => false}},
|
||||
{elvis_style, no_tabs},
|
||||
{elvis_style, no_trailing_whitespace}
|
||||
]
|
||||
},
|
||||
#{
|
||||
dirs => ["src"],
|
||||
filter => "*.app.src",
|
||||
rules => [
|
||||
{elvis_style, line_length, #{limit => 120, skip_comments => false}},
|
||||
{elvis_style, no_tabs},
|
||||
{elvis_style, no_trailing_whitespace}
|
||||
]
|
||||
}
|
||||
]}
|
||||
]}
|
||||
].
|
108
rebar.config
Normal file
108
rebar.config
Normal file
@ -0,0 +1,108 @@
|
||||
%% Common project erlang options.
|
||||
{erl_opts, [
|
||||
{parse_transform, lager_transform},
|
||||
|
||||
% mandatory
|
||||
debug_info,
|
||||
warnings_as_errors,
|
||||
warn_export_all,
|
||||
warn_missing_spec,
|
||||
warn_untyped_record,
|
||||
warn_export_vars,
|
||||
|
||||
% by default
|
||||
warn_unused_record,
|
||||
warn_bif_clash,
|
||||
warn_obsolete_guard,
|
||||
warn_unused_vars,
|
||||
warn_shadow_vars,
|
||||
warn_unused_import,
|
||||
warn_unused_function,
|
||||
warn_deprecated_function
|
||||
|
||||
% at will
|
||||
% bin_opt_info
|
||||
% no_auto_import
|
||||
% warn_missing_spec_all
|
||||
]}.
|
||||
|
||||
%% Common project dependencies.
|
||||
{deps, [
|
||||
|
||||
{woody , {git, "git@github.com:rbkmoney/woody_erlang.git", {branch, "master"}}},
|
||||
{dmsl , {git, "git@github.com:rbkmoney/damsel.git", {branch, "release/erlang/master"}}},
|
||||
{mg_proto , {git, "git@github.com:rbkmoney/machinegun_proto.git", {branch, master}}},
|
||||
{lager , "3.2.1"},
|
||||
{lager_logstash_formatter, {git, "git@github.com:rbkmoney/lager_logstash_formatter.git", {branch, "master"}}}
|
||||
|
||||
]}.
|
||||
|
||||
%% XRef checks
|
||||
{xref_checks, [
|
||||
undefined_function_calls,
|
||||
undefined_functions,
|
||||
deprecated_functions_calls,
|
||||
deprecated_functions
|
||||
]}.
|
||||
|
||||
% at will
|
||||
% {xref_warnings, true}.
|
||||
|
||||
%% Tests
|
||||
{cover_enabled, true}.
|
||||
|
||||
%% Relx configuration
|
||||
{relx, [
|
||||
{release, {sequences, "0.1.0"}, [
|
||||
sequences
|
||||
]},
|
||||
{sys_config, "./config/sys.config"},
|
||||
{vm_args, "./config/vm.args"},
|
||||
{dev_mode, true},
|
||||
{include_erts, false},
|
||||
{extended_start_script, true}
|
||||
]}.
|
||||
|
||||
%% Dialyzer static analyzing
|
||||
{dialyzer, [
|
||||
{warnings, [
|
||||
% mandatory
|
||||
unmatched_returns,
|
||||
error_handling,
|
||||
race_conditions
|
||||
% unknown %% need fix
|
||||
]},
|
||||
{plt_apps, all_deps}
|
||||
]}.
|
||||
|
||||
{profiles, [
|
||||
|
||||
{test, [
|
||||
{deps, [
|
||||
]}
|
||||
]},
|
||||
|
||||
{prod, [
|
||||
{deps, [
|
||||
% for introspection on production
|
||||
{recon, "2.3.2"}
|
||||
]},
|
||||
{relx, [
|
||||
{release, {sequences, "0.1.0"}, [
|
||||
{recon , load }, % tools for introspection
|
||||
{runtime_tools, load }, % debugger
|
||||
{tools , load }, % profiler
|
||||
sasl,
|
||||
sequences
|
||||
]},
|
||||
{dev_mode, false},
|
||||
{include_erts, true}
|
||||
]}
|
||||
]}
|
||||
|
||||
]}.
|
||||
|
||||
{plugins, [
|
||||
rebar3_run
|
||||
]}.
|
||||
|
58
rebar.lock
Normal file
58
rebar.lock
Normal file
@ -0,0 +1,58 @@
|
||||
{"1.1.0",
|
||||
[{<<"certifi">>,{pkg,<<"certifi">>,<<"0.7.0">>},2},
|
||||
{<<"cowboy">>,{pkg,<<"cowboy">>,<<"1.0.4">>},1},
|
||||
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.0.2">>},2},
|
||||
{<<"dmsl">>,
|
||||
{git,"git@github.com:rbkmoney/damsel.git",
|
||||
{ref,"66d5da21a29df00dc2b7b2a5700e41d70228c9f4"}},
|
||||
0},
|
||||
{<<"genlib">>,
|
||||
{git,"https://github.com/rbkmoney/genlib.git",
|
||||
{ref,"82ff16f4314fc406dd90752467a08fe401b009ef"}},
|
||||
1},
|
||||
{<<"goldrush">>,{pkg,<<"goldrush">>,<<"0.1.8">>},1},
|
||||
{<<"hackney">>,{pkg,<<"hackney">>,<<"1.6.2">>},1},
|
||||
{<<"idna">>,{pkg,<<"idna">>,<<"1.2.0">>},2},
|
||||
{<<"jsx">>,{pkg,<<"jsx">>,<<"2.8.2">>},1},
|
||||
{<<"lager">>,{pkg,<<"lager">>,<<"3.2.1">>},0},
|
||||
{<<"lager_logstash_formatter">>,
|
||||
{git,"git@github.com:rbkmoney/lager_logstash_formatter.git",
|
||||
{ref,"d7370337d4d55b37915a2c3202f5c39047674bb3"}},
|
||||
0},
|
||||
{<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},2},
|
||||
{<<"mg_proto">>,
|
||||
{git,"git@github.com:rbkmoney/machinegun_proto.git",
|
||||
{ref,"35a23af91ee4245b6faffda4ed66a926df087bdf"}},
|
||||
0},
|
||||
{<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.0.2">>},2},
|
||||
{<<"ranch">>,{pkg,<<"ranch">>,<<"1.4.0">>},2},
|
||||
{<<"rfc3339">>,{pkg,<<"rfc3339">>,<<"0.9.0">>},1},
|
||||
{<<"snowflake">>,
|
||||
{git,"https://github.com/rbkmoney/snowflake.git",
|
||||
{ref,"0a598108f6582affe3b4ae550fc5b9f2062e318a"}},
|
||||
1},
|
||||
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},2},
|
||||
{<<"thrift">>,
|
||||
{git,"https://github.com/rbkmoney/thrift_erlang.git",
|
||||
{ref,"240bbc842f6e9b90d01bd07838778cf48752b510"}},
|
||||
1},
|
||||
{<<"woody">>,
|
||||
{git,"git@github.com:rbkmoney/woody_erlang.git",
|
||||
{ref,"2d00bda10454534e230d452b7338debafaf0a869"}},
|
||||
0}]}.
|
||||
[
|
||||
{pkg_hash,[
|
||||
{<<"certifi">>, <<"861A57F3808F7EB0C2D1802AFEAAE0FA5DE813B0DF0979153CBAFCD853ABABAF">>},
|
||||
{<<"cowboy">>, <<"A324A8DF9F2316C833A470D918AAF73AE894278B8AA6226CE7A9BF699388F878">>},
|
||||
{<<"cowlib">>, <<"9D769A1D062C9C3AC753096F868CA121E2730B9A377DE23DEC0F7E08B1DF84EE">>},
|
||||
{<<"goldrush">>, <<"2024BA375CEEA47E27EA70E14D2C483B2D8610101B4E852EF7F89163CDB6E649">>},
|
||||
{<<"hackney">>, <<"96A0A5E7E65B7ACAD8031D231965718CC70A9B4131A8B033B7543BBD673B8210">>},
|
||||
{<<"idna">>, <<"AC62EE99DA068F43C50DC69ACF700E03A62A348360126260E87F2B54ECED86B2">>},
|
||||
{<<"jsx">>, <<"7ACC7D785B5ABE8A6E9ADBDE926A24E481F29956DD8B4DF49E3E4E7BCC92A018">>},
|
||||
{<<"lager">>, <<"EEF4E18B39E4195D37606D9088EA05BF1B745986CF8EC84F01D332456FE88D17">>},
|
||||
{<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>},
|
||||
{<<"mimerl">>, <<"993F9B0E084083405ED8252B99460C4F0563E41729AB42D9074FD5E52439BE88">>},
|
||||
{<<"ranch">>, <<"10272F95DA79340FA7E8774BA7930B901713D272905D0012B06CA6D994F8826B">>},
|
||||
{<<"rfc3339">>, <<"2075653DC9407541C84B1E15F8BDA2ABE95FB17C9694025E079583F2D19C1060">>},
|
||||
{<<"ssl_verify_fun">>, <<"28A4D65B7F59893BC2C7DE786DEC1E1555BD742D336043FE644AE956C3497FBE">>}]}
|
||||
].
|
20
src/seq_handler.erl
Normal file
20
src/seq_handler.erl
Normal file
@ -0,0 +1,20 @@
|
||||
-module(seq_handler).
|
||||
|
||||
%% Woody handler
|
||||
|
||||
-behaviour(woody_server_thrift_handler).
|
||||
|
||||
-export([handle_function/4]).
|
||||
|
||||
%%
|
||||
|
||||
-spec handle_function(woody:func(), woody:args(), woody_context:ctx(), woody:options()) ->
|
||||
{ok, woody:result()}.
|
||||
|
||||
handle_function('GetCurrent', [SequenceId], Context, _Opts) ->
|
||||
Value = seq_machine:get_current(SequenceId, Context),
|
||||
{ok, Value};
|
||||
|
||||
handle_function('GetNext', [SequenceId], Context, _Opts) ->
|
||||
Value = seq_machine:get_next(SequenceId, Context),
|
||||
{ok, Value}.
|
121
src/seq_machine.erl
Normal file
121
src/seq_machine.erl
Normal file
@ -0,0 +1,121 @@
|
||||
-module(seq_machine).
|
||||
|
||||
-include_lib("mg_proto/include/mg_proto_state_processing_thrift.hrl").
|
||||
|
||||
-export([get_current/2]).
|
||||
-export([get_next/2]).
|
||||
|
||||
%% State processor
|
||||
|
||||
-behaviour(woody_server_thrift_handler).
|
||||
|
||||
-export([handle_function/4]).
|
||||
|
||||
%%
|
||||
|
||||
-define(NS, <<"sequences">>).
|
||||
-define(NIL, {nl, #msgpack_Nil{}}).
|
||||
-define(INIT, 0).
|
||||
|
||||
-type id() :: mg_proto_base_thrift:'ID'().
|
||||
-type context() :: woody_context:ctx().
|
||||
|
||||
%%
|
||||
|
||||
-spec get_next(id(), context()) ->
|
||||
integer().
|
||||
|
||||
get_next(Id, Context) ->
|
||||
{ok, AuxState} = call_automaton_with_lazy_start('Call', Id, [?NIL], Context),
|
||||
get_sequence_value(AuxState).
|
||||
|
||||
-spec get_current(id(), context()) ->
|
||||
integer().
|
||||
|
||||
get_current(Id, Context) ->
|
||||
{ok, #'Machine'{aux_state = AuxState}} = call_automaton_with_lazy_start('GetMachine', Id, Context),
|
||||
get_sequence_value(AuxState).
|
||||
|
||||
get_sequence_value(AuxState) ->
|
||||
unmarshal(AuxState).
|
||||
|
||||
%%
|
||||
|
||||
ensure_started(Id, Context) ->
|
||||
case call_automaton('Start', [?NS, Id, ?NIL], Context) of
|
||||
{ok, _} ->
|
||||
ok;
|
||||
{exception, #'MachineAlreadyExists'{}} ->
|
||||
ok
|
||||
end.
|
||||
|
||||
call_automaton(Function, Id, Args, Context) ->
|
||||
Descriptor = construct_descriptor({id, Id}),
|
||||
call_automaton(Function, [Descriptor|Args], Context).
|
||||
|
||||
call_automaton(Function, Args, Context) ->
|
||||
Request = {{mg_proto_state_processing_thrift, 'Automaton'}, Function, Args},
|
||||
{ok, URL} = application:get_env(sequences, automaton_service_url),
|
||||
Opts = #{url => URL, event_handler => {woody_event_handler_default, undefined}},
|
||||
woody_client:call(Request, Opts, Context).
|
||||
|
||||
call_automaton_with_lazy_start(Function, Id, Context) ->
|
||||
call_automaton_with_lazy_start(Function, Id, [], Context).
|
||||
|
||||
call_automaton_with_lazy_start(Function, Id, Args, Context) ->
|
||||
case call_automaton(Function, Id, Args, Context) of
|
||||
{ok, _} = Ok ->
|
||||
Ok;
|
||||
{exception, #'MachineNotFound'{}} ->
|
||||
ok = ensure_started(Id, Context),
|
||||
call_automaton(Function, Id, Args, Context)
|
||||
end.
|
||||
|
||||
construct_descriptor(Ref) ->
|
||||
#'MachineDescriptor'{
|
||||
ns = ?NS,
|
||||
ref = Ref,
|
||||
range = #'HistoryRange'{}
|
||||
}.
|
||||
|
||||
%%
|
||||
|
||||
-type func() :: 'ProcessSignal' | 'ProcessCall'.
|
||||
|
||||
-spec handle_function(func(), woody:args(), context(), woody:options()) ->
|
||||
{ok, term()}.
|
||||
|
||||
handle_function('ProcessSignal', [#'SignalArgs'{signal = {init, _}}], _Context, _Opts) ->
|
||||
{ok, #'SignalResult'{
|
||||
change = construct_change(init()),
|
||||
action = #'ComplexAction'{}
|
||||
}};
|
||||
|
||||
handle_function('ProcessCall', [#'CallArgs'{machine = #'Machine'{aux_state = CurrentAuxState}}], _Context, _Opts) ->
|
||||
NextAuxState = process_call(CurrentAuxState),
|
||||
{ok, #'CallResult'{
|
||||
change = construct_change(NextAuxState),
|
||||
action = #'ComplexAction'{},
|
||||
response = NextAuxState
|
||||
}}.
|
||||
|
||||
construct_change(State) ->
|
||||
#'MachineStateChange'{
|
||||
events = [],
|
||||
aux_state = State
|
||||
}.
|
||||
|
||||
init() ->
|
||||
marshal(?INIT).
|
||||
|
||||
process_call(CurrentValue) ->
|
||||
NextValue = unmarshal(CurrentValue) + 1,
|
||||
marshal(NextValue).
|
||||
|
||||
%% Marshalling
|
||||
|
||||
marshal(Int) when is_integer(Int) ->
|
||||
{arr, [{i, 1}, {i, Int}]}.
|
||||
|
||||
unmarshal({arr, [{i, 1}, {i, Int}]}) ->
|
||||
Int.
|
16
src/sequences.app.src
Normal file
16
src/sequences.app.src
Normal file
@ -0,0 +1,16 @@
|
||||
{application, sequences, [
|
||||
{description, "Sequence service"},
|
||||
{vsn, "0.1.0"},
|
||||
{registered, []},
|
||||
{applications, [
|
||||
kernel,
|
||||
stdlib,
|
||||
lager,
|
||||
lager_logstash_formatter,
|
||||
woody,
|
||||
dmsl,
|
||||
mg_proto
|
||||
]},
|
||||
{mod, {sequences, []}},
|
||||
{env, []}
|
||||
]}.
|
76
src/sequences.erl
Normal file
76
src/sequences.erl
Normal file
@ -0,0 +1,76 @@
|
||||
-module(sequences).
|
||||
-behaviour(supervisor).
|
||||
-behaviour(application).
|
||||
|
||||
%% API
|
||||
-export([start/0]).
|
||||
-export([stop/0]).
|
||||
|
||||
%% Supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
%% Application callbacks
|
||||
-export([start/2]).
|
||||
-export([stop/1]).
|
||||
|
||||
|
||||
%% API
|
||||
|
||||
-spec start() ->
|
||||
{ok, _}.
|
||||
start() ->
|
||||
application:ensure_all_started(?MODULE).
|
||||
|
||||
-spec stop() ->
|
||||
ok.
|
||||
stop() ->
|
||||
application:stop(?MODULE).
|
||||
|
||||
%% Supervisor callbacks
|
||||
|
||||
-spec init([]) ->
|
||||
{ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
|
||||
|
||||
init([]) ->
|
||||
{ok, Ip} = inet:parse_address(genlib_app:env(?MODULE, ip, "::")),
|
||||
ChildSpec = woody_server:child_spec(
|
||||
?MODULE,
|
||||
#{
|
||||
ip => Ip,
|
||||
port => genlib_app:env(?MODULE, port, 8022),
|
||||
net_opts => genlib_app:env(?MODULE, net_opts, []),
|
||||
event_handler => woody_event_handler_default,
|
||||
handlers => [
|
||||
get_handler_spec(sequences),
|
||||
get_handler_spec(state_processor)
|
||||
]
|
||||
}
|
||||
),
|
||||
{ok, {
|
||||
#{strategy => one_for_all, intensity => 6, period => 30},
|
||||
[ChildSpec]
|
||||
}}.
|
||||
|
||||
get_handler_spec(sequences) ->
|
||||
{"/v1/sequences", {
|
||||
{dmsl_sequences_thrift, 'Sequences'},
|
||||
seq_handler
|
||||
}};
|
||||
|
||||
get_handler_spec(state_processor) ->
|
||||
{"/v1/stateproc", {
|
||||
{mg_proto_state_processing_thrift, 'Processor'},
|
||||
seq_machine
|
||||
}}.
|
||||
|
||||
%% Application callbacks
|
||||
|
||||
-spec start(normal, any()) ->
|
||||
{ok, pid()} | {error, any()}.
|
||||
start(_StartType, _StartArgs) ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
-spec stop(any()) ->
|
||||
ok.
|
||||
stop(_State) ->
|
||||
ok.
|
6
test/machinegun/config.yaml
Normal file
6
test/machinegun/config.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
namespaces:
|
||||
sequences:
|
||||
processor:
|
||||
url: http://sequences:8022/v1/stateproc
|
||||
storage:
|
||||
type: memory
|
35
test/seq_client.erl
Normal file
35
test/seq_client.erl
Normal file
@ -0,0 +1,35 @@
|
||||
-module(seq_client).
|
||||
|
||||
-export([new/0]).
|
||||
-export([get_current/2]).
|
||||
-export([get_next/2]).
|
||||
|
||||
-type client() :: woody_context:ctx().
|
||||
|
||||
-spec get_current(binary(), client()) -> integer().
|
||||
|
||||
get_current(SeqId, Client) ->
|
||||
call('GetCurrent', [SeqId], Client).
|
||||
|
||||
-spec get_next(binary(), client()) -> integer().
|
||||
|
||||
get_next(SeqId, Client) ->
|
||||
call('GetNext', [SeqId], Client).
|
||||
|
||||
-spec new() -> client().
|
||||
|
||||
new() ->
|
||||
woody_context:new().
|
||||
|
||||
call(Function, Args, Client) ->
|
||||
Call = {{dmsl_sequences_thrift, 'Sequences'}, Function, Args},
|
||||
Opts = #{
|
||||
url => <<"http://sequences:8022/v1/sequences">>,
|
||||
event_handler => {woody_event_handler_default, undefined}
|
||||
},
|
||||
case woody_client:call(Call, Opts, Client) of
|
||||
{ok, Response} ->
|
||||
Response;
|
||||
{exception, Exception} ->
|
||||
throw(Exception)
|
||||
end.
|
79
test/sequences_tests_SUITE.erl
Normal file
79
test/sequences_tests_SUITE.erl
Normal file
@ -0,0 +1,79 @@
|
||||
-module(sequences_tests_SUITE).
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-export([all/0]).
|
||||
-export([init_per_suite/1]).
|
||||
-export([end_per_suite/1]).
|
||||
-export([init_per_testcase/2]).
|
||||
-export([end_per_testcase/2]).
|
||||
|
||||
-export([get_current/1]).
|
||||
-export([get_next/1]).
|
||||
|
||||
%% tests descriptions
|
||||
|
||||
-type config() :: [{atom(), term()}].
|
||||
|
||||
-define(config(Key, C), (element(2, lists:keyfind(Key, 1, C)))).
|
||||
|
||||
-spec all() -> [atom()].
|
||||
all() ->
|
||||
[
|
||||
get_current,
|
||||
get_next
|
||||
].
|
||||
|
||||
%% starting/stopping
|
||||
|
||||
-spec init_per_suite(config()) -> config().
|
||||
init_per_suite(C) ->
|
||||
Apps = genlib_app:start_application_with(lager, [
|
||||
{async_threshold, 1},
|
||||
{async_threshold_window, 0},
|
||||
{error_logger_hwm, 600},
|
||||
{suppress_application_start_stop, true},
|
||||
{handlers, [
|
||||
{lager_common_test_backend, warning}
|
||||
]}
|
||||
]) ++ genlib_app:start_application_with(sequences, [
|
||||
{automaton_service_url, "http://machinegun:8022/v1/automaton"}
|
||||
]),
|
||||
[{suite_apps, Apps} | C].
|
||||
|
||||
-spec end_per_suite(config()) -> term().
|
||||
end_per_suite(C) ->
|
||||
genlib_app:stop_unload_applications(?config(suite_apps, C)).
|
||||
|
||||
%% tests
|
||||
|
||||
-spec init_per_testcase(atom(), config()) -> config().
|
||||
|
||||
init_per_testcase(_Name, C) ->
|
||||
Client = seq_client:new(),
|
||||
[{client, Client} | C].
|
||||
|
||||
-spec end_per_testcase(atom(), config()) -> config().
|
||||
|
||||
end_per_testcase(_Name, _C) ->
|
||||
ok.
|
||||
|
||||
-spec get_current(term()) -> term().
|
||||
get_current(C) ->
|
||||
Client = proplists:get_value(client, C),
|
||||
SeqId = get_sequence_id(),
|
||||
0 = seq_client:get_current(SeqId, Client),
|
||||
0 = seq_client:get_current(SeqId, Client),
|
||||
_ = seq_client:get_next(SeqId, Client),
|
||||
1 = seq_client:get_current(SeqId, Client).
|
||||
|
||||
-spec get_next(term()) -> term().
|
||||
get_next(C) ->
|
||||
Client = proplists:get_value(client, C),
|
||||
SeqId = get_sequence_id(),
|
||||
1 = seq_client:get_next(SeqId, Client),
|
||||
2 = seq_client:get_next(SeqId, Client).
|
||||
|
||||
%%
|
||||
|
||||
get_sequence_id() ->
|
||||
integer_to_binary(erlang:system_time(micro_seconds)).
|
Loading…
Reference in New Issue
Block a user