Skip to content

Commit b911317

Browse files
committed
feat(example): add an example use
1 parent cc0936d commit b911317

File tree

9 files changed

+260
-0
lines changed

9 files changed

+260
-0
lines changed

example/.gitignore

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.rebar3
2+
_*
3+
.eunit
4+
*.o
5+
*.beam
6+
*.plt
7+
*.swp
8+
*.swo
9+
.erlang.cookie
10+
ebin
11+
log
12+
erl_crash.dump
13+
.rebar
14+
logs
15+
_build
16+
.idea
17+
*.iml
18+
rebar3.crashdump
19+
*~
20+
bin/hocon
21+
rebar.lock

example/README.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# example
2+
3+
An exapmple OTP application to make use of HOCON for configuration management
4+
5+
## Quick demo
6+
7+
```
8+
$ rebar3 release
9+
$ ./_build/default/rel/example/bin/entrypoint.sh console
10+
Erlang/OTP 23 [erts-11.1.8] [emqx] [64-bit] [smp:20:20] [ds:20:20:10] [async-threads:1]
11+
12+
Eshell V11.1.8 (abort with ^G)
13+
1> application:get_all_env(example).
14+
[{listeners,#{<<"http">> => #{<<"port">> => 8080},
15+
<<"https">> =>
16+
#{<<"port">> => 8083,
17+
<<"ssl">> =>
18+
#{<<"cacertfile">> => "config/ssl/ca.pem",
19+
<<"certfile">> => "config/ssl/server.pem",
20+
<<"keyfile">> => "config/ssl/server.key"}}}}]
21+
``
22+
23+
## Steps to create
24+
25+
* If not an existing project, create one with `rebar3 new umbrella <name>`
26+
* Add HOCON config file `config/example.hocon`
27+
* Add HOCON schema module `apps/example/src/example_schema.erl`
28+
* Add an entrypoint script (which calls generated boot script) `bin/entrypoint.sh`
29+
this script helps to generate sys.config and vm.args before calling the default
30+
(generated by `rebar3 release` command) boot script.
31+
* Include the config file and the entrypoint script in `rebar3.config`'s release overlays
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{application, example,
2+
[{description, "An OTP application"},
3+
{vsn, "0.1.0"},
4+
{registered, []},
5+
{mod, {example_app, []}},
6+
{applications,
7+
[kernel,
8+
stdlib
9+
]},
10+
{env,[]},
11+
{modules, []},
12+
13+
{licenses, ["Apache 2.0"]},
14+
{links, []}
15+
]}.
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
%%%-------------------------------------------------------------------
2+
%% @doc example public API
3+
%% @end
4+
%%%-------------------------------------------------------------------
5+
6+
-module(example_app).
7+
8+
-behaviour(application).
9+
10+
-export([start/2, stop/1]).
11+
12+
start(_StartType, _StartArgs) ->
13+
example_sup:start_link().
14+
15+
stop(_State) ->
16+
ok.
17+
18+
%% internal functions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-module(example_schema).
2+
3+
-export([roots/0, fields/1]).
4+
5+
roots() -> [myserver].
6+
7+
fields(myserver) ->
8+
[ {listeners,
9+
hoconsc:mk(
10+
hoconsc:map(name,
11+
hoconsc:union([hoconsc:ref(http_listener),
12+
hoconsc:ref(https_listener)])),
13+
#{mapping => "example.listeners"})
14+
}
15+
, {concurrent_users_limit, typerefl:integer()}
16+
];
17+
fields(http_listener) ->
18+
[ {port, typerefl:integer()}
19+
];
20+
fields(https_listener) ->
21+
[ {port, typerefl:integer()}
22+
, {ssl, {ref, ssl}}
23+
];
24+
fields(ssl) ->
25+
[ {cacertfile, typerefl:string()}
26+
, {keyfile, typerefl:string()}
27+
, {certfile, typerefl:string()}
28+
].
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
%%%-------------------------------------------------------------------
2+
%% @doc example top level supervisor.
3+
%% @end
4+
%%%-------------------------------------------------------------------
5+
6+
-module(example_sup).
7+
8+
-behaviour(supervisor).
9+
10+
-export([start_link/0]).
11+
12+
-export([init/1]).
13+
14+
-define(SERVER, ?MODULE).
15+
16+
start_link() ->
17+
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
18+
19+
%% sup_flags() = #{strategy => strategy(), % optional
20+
%% intensity => non_neg_integer(), % optional
21+
%% period => pos_integer()} % optional
22+
%% child_spec() = #{id => child_id(), % mandatory
23+
%% start => mfargs(), % mandatory
24+
%% restart => restart(), % optional
25+
%% shutdown => shutdown(), % optional
26+
%% type => worker(), % optional
27+
%% modules => modules()} % optional
28+
init([]) ->
29+
SupFlags = #{strategy => one_for_all,
30+
intensity => 0,
31+
period => 1},
32+
ChildSpecs = [],
33+
{ok, {SupFlags, ChildSpecs}}.
34+
35+
%% internal functions

example/bin/entrypoint.sh

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/bin/bash
2+
3+
set -euo pipefail
4+
5+
# OSX does not support readlink '-f' flag, work
6+
# around that
7+
# shellcheck disable=SC2039
8+
case $OSTYPE in
9+
darwin*)
10+
SCRIPT=$(readlink "$0" || true)
11+
;;
12+
*)
13+
SCRIPT=$(readlink -f "$0" || true)
14+
;;
15+
esac
16+
17+
ERTS_VSN="{{ erts_vsn }}"
18+
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT")" && pwd -P)"
19+
RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd -P)"
20+
CONFIG_DIR="$RELEASE_ROOT_DIR/config"
21+
CONFIG_FILE="$CONFIG_DIR/example.hocon"
22+
23+
find_erts_dir() {
24+
__erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
25+
if [ -d "$__erts_dir" ]; then
26+
ERTS_DIR="$__erts_dir";
27+
else
28+
__erl="$(command -v erl)"
29+
code="io:format(\"~s\", [code:root_dir()]), halt()."
30+
__erl_root="$("$__erl" -boot no_dot_erlang -sasl errlog_type error -noshell -eval "$code")"
31+
ERTS_DIR="$__erl_root/erts-$ERTS_VSN"
32+
if [ ! -d "$ERTS_DIR" ]; then
33+
erts_version_code="io:format(\"~s\", [erlang:system_info(version)]), halt()."
34+
__erts_version="$("$__erl" -boot no_dot_erlang -sasl errlog_type error -noshell -eval "$erts_version_code")"
35+
ERTS_DIR="${__erl_root}/erts-${__erts_version}"
36+
if [ -d "$ERTS_DIR" ]; then
37+
ERTS_VSN=${__erts_version}
38+
echo "Exact ERTS version (${ERTS_VSN}) match not found, instead using ${__erts_version}. The release may fail to run." 1>&2
39+
else
40+
echo "Can not run the release. There is no ERTS bundled with the release or found on the system."
41+
exit 1
42+
fi
43+
fi
44+
fi
45+
}
46+
47+
## generate sys.config and vm.args
48+
if [ "$CONFIG_FILE" -nt "$RELEASE_ROOT_DIR/sys.conf" ]; then
49+
find_erts_dir
50+
51+
TIME="$("$ERTS_DIR/bin/escript" "$RELEASE_ROOT_DIR/bin/hocon" generate \
52+
--schema_module example_schema \
53+
--conf_file "$CONFIG_FILE" \
54+
--dest_dir "$CONFIG_DIR" \
55+
--pa "$RELEASE_ROOT_DIR/lib/example-0.1.0/ebin")"
56+
57+
cp "$CONFIG_DIR/app.$TIME.config" "$RELEASE_ROOT_DIR/sys.config"
58+
cp "$CONFIG_DIR/vm.$TIME.args" "$RELEASE_ROOT_DIR/vm.args"
59+
fi
60+
61+
exec "$SCRIPT_DIR/example" "$@"

example/config/example.hocon

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
myserver.listeners {
2+
http {
3+
port = 8080
4+
}
5+
https {
6+
port = 8083
7+
ssl {
8+
cacertfile = "config/ssl/ca.pem"
9+
keyfile = "config/ssl/server.key"
10+
certfile = "config/ssl/server.pem"
11+
}
12+
}
13+
}
14+
myserver.concurrent_users_limit=1000

example/rebar.config

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{erl_opts, [debug_info]}.
2+
{deps, [
3+
{hocon, {git, "https://github.com/emqx/hocon", {tag, "0.17.0"}}}
4+
]}.
5+
6+
{relx, [{release, {example, "0.1.0"},
7+
[example,
8+
sasl,
9+
hocon
10+
]},
11+
12+
{mode, dev},
13+
14+
{sys_config, false}, %% generated by hocon
15+
{vm_args, false}, %% generated by hocon
16+
{overlay, [
17+
{mkdir, "config/"},
18+
{copy, "config/example.hocon", "config/"},
19+
{copy, "bin/hocon", "bin/"},
20+
{template, "bin/entrypoint.sh", "bin/entrypoint.sh"}
21+
]}
22+
]}.
23+
24+
{pre_hooks,
25+
[ {release, "bash -c 'make -C _build/default/lib/hocon es && cp _build/default/lib/hocon/_build/es/bin/hocon bin/'"}
26+
]}.
27+
28+
{profiles, [{prod, [{relx,
29+
[%% prod is the default mode when prod
30+
%% profile is used, so does not have
31+
%% to be explicitly included like this
32+
{mode, prod}
33+
34+
%% use minimal mode to exclude ERTS
35+
%% {mode, minimal}
36+
]
37+
}]}]}.

0 commit comments

Comments
 (0)