Compare commits

...

529 Commits

Author SHA1 Message Date
tigercl 2ed5e01054
Merge branch 'main-v4.4' into sync-from-v4.3 2021-12-09 18:49:38 +08:00
Zaiming (Stone) Shi a81f2ce315
Merge pull request #6414 from tigercl/chore/ehttpc-vsn
perf(http): bump ehttpc version
2021-12-09 09:38:30 +01:00
tigercl 42cfc6c5e6
Merge pull request #6272 from Rory-Z/ci/add-push-ecr-for-4.4
ci: add new steps for push ecr image when release
2021-12-09 16:05:17 +08:00
William Yang f72002949b
Merge pull request #6403 from qzhuyan/backport/william/4.3/systemd-restart-on-failure
Backport/william/4.3/systemd restart on failure
2021-12-09 08:38:39 +01:00
zhouzb 63a6ac2f5f perf(http): bump ehttpc version 2021-12-09 15:26:41 +08:00
Thales Macedo Garitezi d2b2a4ea99
Merge pull request #6404 from emqx/bugfix-live-chan-count
fix(live_conn): fix live connection count on race condition

When multiple clients try to connect concurrently using the same
client ID, they all call `emqx_channel:ensure_connected`, increasing
the live connection count, but only one will successfully acquire the
lock for that client ID.  This means that all other clients that
increased the live connection count will not get to call neither
`emqx_channel:ensure_disconnected` nor be monitored for `DOWN`
messages, effectively causing a count leak.

By moving the increment to `emqx_cm:register_channel`, which is only
called inside the lock, we can remove this leakage.

Also, during the handling of `DOWN` messages, we now iterate over all
channel PIDs returned by `eqmx_misc:drain_down`, since it could be
that one or more PIDs are not contained in the `pmon` state.
2021-12-08 15:10:24 -03:00
Thales Macedo Garitezi d435f1211e
fix(live_conn): fix live connection count on race condition
When multiple clients try to connect concurrently using the same
client ID, they all call `emqx_channel:ensure_connected`, increasing
the live connection count, but only one will successfully acquire the
lock for that client ID.  This means that all other clients that
increased the live connection count will not get to call neither
`emqx_channel:ensure_disconnected` nor be monitored for `DOWN`
messages, effectively causing a count leak.

By moving the increment to `emqx_cm:register_channel`, which is only
called inside the lock, we can remove this leakage.

Also, during the handling of `DOWN` messages, we now iterate over all
channel PIDs returned by `eqmx_misc:drain_down`, since it could be
that one or more PIDs are not contained in the `pmon` state.
2021-12-08 10:33:57 -03:00
Tobias Lindahl 756a256137
Merge pull request #6396 from emqx/fix-takeover-race-on-enqueued-messages-v4.3 2021-12-08 11:01:25 +01:00
Zaiming (Stone) Shi c2cc3c78f1
Merge pull request #6395 from zhongwencool/sync-from-4.3
Sync from 4.3
2021-12-08 10:57:55 +01:00
Rory Z cdc70d8ccc
Merge pull request #6271 from Rory-Z/ci/add-push-ecr-for-4.3
ci: add new steps for push ecr image when release
2021-12-08 17:30:44 +08:00
Rory Z 29a9bbe5d5
Merge pull request #6386 from Rory-Z/chore/helm-support-cluster-discovery-by-dns
chore(helm): support cluster discovery by dns
2021-12-08 17:30:18 +08:00
lafirest 99989ecca6
Merge pull request #6400 from lafirest/fix/emqx_slow_subs
fix(emqx_slow_subs): fix threshold related bugs
2021-12-08 15:54:29 +08:00
lafirest e651becd99 fix(emqx_slow_subs): fix threshold related bugs
1. limit the interval between calling hooks
2. improve the code of update threshold
2021-12-08 15:05:39 +08:00
tigercl a92712e429
Merge pull request #6399 from tigercl/fix/webhook-path
fix(webhook): fix the issue that the path field does not support rule engine variables
2021-12-08 14:22:51 +08:00
zhouzb 83981e7c87 fix(webhook): fix the issue that the path field does not support rule engine variables 2021-12-08 11:27:58 +08:00
William Yang ac5307a3a5
Merge pull request #6389 from qzhuyan/backport/william/4.3/systemd-restart-on-failure
Backport/william/4.3/systemd restart on failure
2021-12-07 19:54:43 +01:00
zhongwencool 2c2145dfe6 Merge branch 'main-v4.3' into sync-from-4.3 2021-12-07 23:20:01 +08:00
Tobias Lindahl 2348e612fa fix(emqx_channel): fix race condition in session takeover
Sessions must not enqueue messages when another process is taking over
the client id, since it already passed on the message queue in the
session state.

Without this fix, messages arriving after `{takeover, 'begin'} to a
channel with no connection (i.e., a persistent session) would be lost.
2021-12-07 16:05:49 +01:00
zhongwencool 9965288947
Merge pull request #6391 from zhongwencool/trace-bug-fix
fix: trace handler start time not correct
2021-12-07 22:58:55 +08:00
William Yang bcbb0822cf build(deb): fix postrm for checking systemd 2021-12-07 13:30:08 +01:00
zhongwencool be6160f5bd fix: trace handler start time not correct 2021-12-07 20:24:56 +08:00
William Yang 55893dbf5c ci(deb): remove systemV test 2021-12-07 12:49:40 +01:00
William Yang 517aa39542 build(deb): deb pkg enable/disable emqx system 2021-12-07 12:48:51 +01:00
William Yang be8284666a build(deb): remove System V init 2021-12-07 12:43:07 +01:00
William Yang 16e39ffee7 build: rpm and deb use the same systemd service file 2021-12-07 12:42:59 +01:00
William Yang 79f1b8651a build(deb): drop systemV init 2021-12-07 12:42:49 +01:00
William Yang 51f1a48983 fix(systemd-start): /usr/bin/emqx is bash not sh 2021-12-07 12:42:44 +01:00
William Yang eba8c08853 fix(systemd): add restart on failure 2021-12-07 12:42:32 +01:00
JimMoen 72a7f353c6
Merge pull request #6379 from JimMoen/v4.3/fix-vm-mem-info
fix(vm): memory info calc and display
2021-12-07 17:54:39 +08:00
zhanghongtong e7c765aaa3 chore(helm): support cluster discovery by dns 2021-12-07 16:23:19 +08:00
JimMoen dc2c17826e chore(appup): update appup.src 2021-12-07 14:23:40 +08:00
JimMoen 14aa408b5e style: make elvis happy 2021-12-07 14:17:33 +08:00
JimMoen c5e28bd32b fix(vm): add literal_alloc memory calculation 2021-12-07 14:17:33 +08:00
JimMoen ceed9678e3 fix(mgmt): node memory usage info 2021-12-07 14:17:33 +08:00
zhongwencool 26fa06b071
Merge pull request #6380 from zhongwencool/v4.4-trace-bug-fix
fix: trace_name format [A-Za-z0-9-_];retry if trace_file not find
2021-12-07 09:25:13 +08:00
Thales Macedo Garitezi 462955e6bc
Merge pull request #6381 from emqx/bump-ekka-0-8-1-6-v43
chore(ekka): bump ekka to 0.8.1.6

We're doing this to remove some missing change warnings when updating
emqx v4.3.10.  Ekka's appup was updated in emqx/ekka#140 .

<details>
<summary> `update_appup.escript` output after update (running against 4.3.10) </summary>

```
Found the previous appup file: _build/emqx/rel/emqx/lib/ekka-0.8.1.6/ebin/ekka.appup
INFO: Application 'ekka' has been updated: "0.8.1.4" -> "0.8.1.6"
... elided ...
ERROR: Appup file for the external dependency 'ehttpc' is not complete.
       Missing changes: #{down =>
                              [{"0.1.10",
                                [{load_module,ehttpc_pool,brutal_purge,soft_purge,[]},
                                 {load_module,ehttpc,brutal_purge,soft_purge,[]}]},
                               {<<"0\\.1\\.0">>,
                                [{load_module,ehttpc_pool,brutal_purge,soft_purge,[]},
                                 {load_module,ehttpc,brutal_purge,soft_purge,[]}]},
                               {<<"0\\.1\\.[1-7]">>,
                                [{load_module,ehttpc_pool,brutal_purge,soft_purge,[]},
                                 {load_module,ehttpc,brutal_purge,soft_purge,[]}]}],
                          up =>
                              [{"0.1.10",
                                [{load_module,ehttpc_pool,brutal_purge,soft_purge,[]},
                                 {load_module,ehttpc,brutal_purge,soft_purge,[]}]},
                               {<<"0\\.1\\.[0-7]">>,
                                [{load_module,ehttpc_pool,brutal_purge,soft_purge,[]},
                                 {load_module,ehttpc,brutal_purge,soft_purge,[]}]}]}
NOTE: Some changes above might be already covered by regexes.

ERROR: Incomplete appups found. Please inspect the output for more details.
```

</details>
2021-12-06 13:18:03 -03:00
zhongwencool d4dd4a124c fix: trace_name format [A-Za-z0-9-_];waiting status if create time to closed" 2021-12-06 20:51:42 +08:00
Thales Macedo Garitezi 3d308cf5a2
chore(ekka): bump ekka to 0.8.1.6
We're doing this to remove some missing change warnings when updating
emqx v4.3.10.  Ekka's appup was updated in emqx/ekka#140 .
2021-12-06 09:21:13 -03:00
lafirest acb63eeb7c
Merge pull request #6366 from lafirest/feat/dynamic_threshold
feat(emqx_slow_subs): add dyanamic threshold
2021-12-06 17:42:08 +08:00
lafirest 3f49e3186c fix(emqx_slow_subs): add default threshold macro 2021-12-06 17:22:36 +08:00
lafirest 48f8c735ea feat(emqx_slow_subs): add dyanamic threshold 2021-12-03 18:11:07 +08:00
zhongwencool fb1bfcac8e
Merge pull request #6363 from zhongwencool/v4.4-trace-api-crash
fix: trace len > 1 return 500
2021-12-03 11:31:53 +08:00
zhongwencool 28e76e498c fix: trace len > 1 return 500 2021-12-03 10:24:36 +08:00
zhongwencool 9f7db2eacb
Merge pull request #6357 from zhongwencool/4.3-sn-registry
fix: e4.4.0 failed to join e4.3.0 cluster
2021-12-03 09:32:46 +08:00
Zaiming (Stone) Shi f7d6cc12ce
Merge pull request #6358 from HJianBo/change_terminate_log_level_to_debug
chore(conn): change terminated print to debug level
2021-12-02 13:10:58 +01:00
JianBo He ef6f867304 chore(conn): change terminated print to debug level 2021-12-02 18:01:36 +08:00
zhongwencool f14a9d7da6 chore: bump emqx_sn to 4.3.4 2021-12-02 16:46:57 +08:00
zhongwencool 41a547d36d fix: e4.4.0 failed to join e4.3.0 cluster 2021-12-02 16:19:36 +08:00
JianBo He fdacb9040d
Merge pull request #6343 from HJianBo/drop-empty-retain-msg
[4.3][Retainer] Allow to stop publish clear message
2021-12-02 11:16:30 +08:00
Shawn 0cec0cff95
Merge pull request #6323 from terry-xiaoyu/stop_ecpool_failed_v4.3
chore(appup): bump ecpool to 0.5.2
2021-12-01 18:37:21 +08:00
Shawn 0d1b194906
Merge pull request #6286 from terry-xiaoyu/mqtt_bridge_hangs
MQTT bridge hangs with an unreachable IP
2021-12-01 18:33:42 +08:00
JianBo He 10ffe11ba0 chore(retainer): update appup.src 2021-12-01 17:11:40 +08:00
JianBo He e323b66285 test(retainer): testcase for stop_publish_clear_msg 2021-12-01 17:11:40 +08:00
JianBo He 8aaa2e8333 feat(retainer): add option to stop publish clear message 2021-12-01 17:11:38 +08:00
zhongwencool 20a98bee62
Merge pull request #6324 from zhongwencool/create-trace-schema-runtime
fix: create trace schema at runtime
2021-11-30 09:12:48 +08:00
Thales Macedo Garitezi c16d5e4bb5
Merge pull request #6307 from emqx/update-appup-ext-deps-diff
chore(appup): make update_appup.escript output only differences for external dependencies

Currently, the update_appup.escript outputs as an error the full appup
file for external dependencies, even if all the changes are already
contained in the depency.  Here, we make it only output the missing
actions to be inserted, to aid in seeing what are the differences.

<details>
<summary> Output before: </summary>

```
ERROR: Appup file for the external dependency 'ekka' is not complete.
       Missing changes: [{"0.8.1.4",
                          [{load_module,ekka_cluster_dns,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_node_monitor,brutal_purge,
                               soft_purge,[]}]},
                         {"0.8.1.3",
                          [{load_module,ekka_node_monitor,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_autocluster,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_autoheal,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_locker,brutal_purge,soft_purge,
                               []},
                           {load_module,ekka_cluster_dns,brutal_purge,
                               soft_purge,[]}]},
                         {"0.8.1.2",
                          [{load_module,ekka_ring,brutal_purge,soft_purge,[]},
                           {load_module,ekka_cluster_dns,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_node_monitor,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_autocluster,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_autoheal,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_locker,brutal_purge,soft_purge,
                               []},
                           {load_module,ekka_httpc,brutal_purge,soft_purge,
                               []}]},
                         {"0.8.1.1",
                          [{load_module,ekka_cluster_dns,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_ring,brutal_purge,soft_purge,[]},
                           {load_module,ekka_node_monitor,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_httpc,brutal_purge,soft_purge,[]},
                           {load_module,ekka_autocluster,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_autoheal,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_locker,brutal_purge,soft_purge,
                               []}]},
                         {"0.8.1",
                          [{load_module,ekka_cluster_dns,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_ring,brutal_purge,soft_purge,[]},
                           {load_module,ekka_node_monitor,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_autocluster,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_autoheal,brutal_purge,
                               soft_purge,[]},
                           {load_module,ekka_locker,brutal_purge,soft_purge,
                               []},
                           {load_module,ekka_httpc,brutal_purge,soft_purge,[]},
                           {load_module,ekka_mnesia,brutal_purge,soft_purge,
                               []}]}]
ERROR: Appup file for the external dependency 'ehttpc' is not complete.
       Missing changes: [{"0.1.10",
                          [{load_module,ehttpc_pool,brutal_purge,soft_purge,
                               []},
                           {load_module,ehttpc,brutal_purge,soft_purge,[]}]},
                         {<<"0\\.1\\.[0-7]">>,
                          [{load_module,ehttpc_pool,brutal_purge,soft_purge,
                               []},
                           {load_module,ehttpc,brutal_purge,soft_purge,[]},
                           {update,ehttpc,{advanced,[]}}]},
                         {<<"0\\.1\\.([8-9]|(1[0-1]))">>,
                          [{load_module,ehttpc,brutal_purge,soft_purge,[]},
                           {load_module,ehttpc_pool,brutal_purge,soft_purge,
                               []}]},
                         {<<".*">>,[]}]
```

</details>


<details>
<summary> Output after: </summary>

```
ERROR: Appup file for the external dependency 'ekka' is not complete.
       Missing changes: #{down =>
                              [{"0.8.1.4",
                                [{load_module,ekka_cluster_dns,brutal_purge,soft_purge,[]}]}],
                          up => []}
NOTE: Some changes above might be already covered by regexes.ERROR: Appup file for the external dependency 'ehttpc' is not complete.
       Missing changes: #{down =>
                              [{"0.1.10",
                                [{load_module,ehttpc_pool,brutal_purge,soft_purge,[]},
                                 {load_module,ehttpc,brutal_purge,soft_purge,[]}]},
                               {<<"0\\.1\\.0">>,
                                [{load_module,ehttpc_pool,brutal_purge,soft_purge,[]},
                                 {load_module,ehttpc,brutal_purge,soft_purge,[]}]},
                               {<<"0\\.1\\.[1-7]">>,
                                [{load_module,ehttpc_pool,brutal_purge,soft_purge,[]},
                                 {load_module,ehttpc,brutal_purge,soft_purge,[]}]}],
                          up =>
                              [{"0.1.10",
                                [{load_module,ehttpc_pool,brutal_purge,soft_purge,[]},
                                 {load_module,ehttpc,brutal_purge,soft_purge,[]}]},
                               {<<"0\\.1\\.[0-7]">>,
                                [{load_module,ehttpc_pool,brutal_purge,soft_purge,[]},
                                 {load_module,ehttpc,brutal_purge,soft_purge,[]}]}]}
NOTE: Some changes above might be already covered by regexes.
ERROR: Incomplete appups found. Please inspect the output for more details.
```

</details>
2021-11-29 16:38:53 -03:00
zhongwencool 0d218df14d fix: replace ct:sleep/1 by filesync/2 2021-11-29 23:08:56 +08:00
zhongwencool 1b14b79286 fix: create trace schema at runtime 2021-11-29 22:15:31 +08:00
Thales Macedo Garitezi 93caddd448
refactor(review): factor out common functionality 2021-11-29 10:23:54 -03:00
tigercl 25215244be
Merge pull request #6192 from turtleDeng/publish-api-user-properties
feat(publish-api): Publish api support user-properties parameters
2021-11-29 15:00:04 +08:00
Shawn 6eb919ea66 chore(appup): bump ecpool to 0.5.2 2021-11-29 14:20:57 +08:00
lafirest 8dd4d88d5b
fix(emx_slow_updates): fix the error of topk update (#6312) 2021-11-26 14:57:25 +08:00
lafirest fef3fc27cb
refactor(emqx_slow_subs): refactor use moving average (#6287)
* refactor(emqx_slow_subs): refactor use moving average

* fix(emqx_slow_subs): change elapsed to latency, and fix some error

* fix(emqx_slow_subs): fix emqx_mgmt_api.erl indent

* fix(emqx_slow_subs): change api name

* fix(emqx_slow_subs): fix and improve some code

* fix(emqx_slow_subs): move clienid filed from latency_stats to session
2021-11-26 10:42:15 +08:00
Thales Macedo Garitezi 2e93ec0f3d
Merge pull request #6290 from emqx/sys-mon-more-info-43
feat(sys_mon): Add proc_lib:initial_call/1 and current_stacktrace (4.3)

(Same as #6289 )

This adds the information from `proc_lib:initial_call/1` and the
current stacktrace from the process info to `emqx_sys_mon:procinfo/1`
to aid in debugging some warnings with no context such as the
following:

```
2021-11-23T12:33:59.387818+00:00 [warning] info: [{old_heap_block_size,45988046},{heap_block_size,22177879},{mbuf_size,0},{stack_size,40},{old_heap_size,22354134},{heap_size,7106339}], line: 130, mfa: emqx_sys_mon:handle_info/2, msg: large_heap, procinfo: [{pid,<0.2667.0>},{memory,579763664},{total_heap_size,68510672},{heap_size,22177879},{stack_size,40},{min_heap_size,233},{initial_call,{proc_lib,init_p,5}},{current_function,{gen,do_call,4}},{registered_name,[]},{status,running},{message_queue_len,360945},{group_leader,<0.1660.0>},{priority,normal},{trap_exit,false},{reductions,16493271},{last_calls,false},{catchlevel,4},{trace,0},{suspending,[]},{sequential_trace_token,[]},{error_handler,error_handler}]
```
2021-11-25 17:06:16 -03:00
Thales Macedo Garitezi 0932920d36
chore(appup): make update_appup.escript output only differences for
external dependencies

Currently, the update_appup.escript outputs as an error the full appup
file for external dependencies, even if all the changes are already
contained in the depency.  Here, we make it only output the missing
actions to be inserted, to aid in seeing what are the differences.
2021-11-25 15:21:25 -03:00
Thales Macedo Garitezi 5c693beadd
chore(appup): remove `load_module` instructions if `restart_application`
> the restart_application instruction will be translated into module
load instructions by otp release handler.
2021-11-25 12:08:02 -03:00
Thales Macedo Garitezi 64ce2eea1c
chore(appup): run ./scripts/update_appup.escript to bump vsns 2021-11-25 12:08:02 -03:00
Thales Macedo Garitezi 0260db6640
feat(sys_mon): Add proc_lib:initial_call/1 and current_stacktrace (4.3)
(Same as #6289 )

This adds the information from `proc_lib:initial_call/1` and the
current stacktrace from the process info to `emqx_sys_mon:procinfo/1`
to aid in debugging some warnings with no context such as the
following:

```
2021-11-23T12:33:59.387818+00:00 [warning] info: [{old_heap_block_size,45988046},{heap_block_size,22177879},{mbuf_size,0},{stack_size,40},{old_heap_size,22354134},{heap_size,7106339}], line: 130, mfa: emqx_sys_mon:handle_info/2, msg: large_heap, procinfo: [{pid,<0.2667.0>},{memory,579763664},{total_heap_size,68510672},{heap_size,22177879},{stack_size,40},{min_heap_size,233},{initial_call,{proc_lib,init_p,5}},{current_function,{gen,do_call,4}},{registered_name,[]},{status,running},{message_queue_len,360945},{group_leader,<0.1660.0>},{priority,normal},{trap_exit,false},{reductions,16493271},{last_calls,false},{catchlevel,4},{trace,0},{suspending,[]},{sequential_trace_token,[]},{error_handler,error_handler}]
```
2021-11-25 12:08:02 -03:00
JimMoen 18a9c0e177
fix:change default configuration of `max_topic_levels` #6294 2021-11-25 16:16:54 +08:00
JimMoen 289415b5aa fix(conf): change `max_topic_levels` default configuration 2021-11-25 09:52:12 +08:00
Shawn f5ac6fb714 chore(appup): bump emqx_bridge_mqtt to 4.3.3 2021-11-25 09:46:06 +08:00
Thales Macedo Garitezi 15b71c118b
Merge pull request #6291 from emqx/fix-update-appup-script
chore(appup): minor fixes to update_appup.escript

- Fixes clause error on `create_stub/1`.
- Small optimization: do not download the same file multiple times
  with `wget`.
- Fix: remove old file extension (`.app.src`) and preserve dirname 
  when creating stubs for apps.
2021-11-24 17:10:00 -03:00
Thales Macedo Garitezi 29f982385d
chore(appup): remove old file extension and preserve dirname for apps 2021-11-24 15:09:25 -03:00
Thales Macedo Garitezi c950566294
chore(appup): minor fixes to update_appup.escript
- Fixes clause error on `create_stub/1`.
- Small optimization: do not download the same file multiple times
  with `wget`.
2021-11-24 14:22:51 -03:00
Shawn 3b9bb1d66c fix(ecpool): update ecpool to 0.5.2 2021-11-24 18:57:23 +08:00
Shawn a54668e83b fix(mqtt_bridge): the mqtt bridge hangs with an unreachable IP 2021-11-24 17:11:04 +08:00
Zaiming Shi 39e52d583e Merge remote-tracking branch 'origin/main-v4.3' into main-v4.4 2021-11-23 23:18:43 +01:00
Zaiming (Stone) Shi 2e26b8dfed
Merge pull request #6246 from zmstone/upgrade-otp-to-23.2.7.2-emqx-3
build: upgrade to otp sed 23.2.7.2-emqx-3
2021-11-23 21:38:19 +01:00
Zaiming Shi c4b0008ebd build: delete arch suffix from alpine build-env image tag 2021-11-23 17:06:46 +01:00
zhanghongtong b87ed0666c ci: add new steps for push ecr image when release 2021-11-23 17:54:17 +08:00
zhanghongtong 5a3169b83a ci: add new steps for push ecr image when release 2021-11-23 17:36:27 +08:00
Zaiming (Stone) Shi afd55b31e8
build: define default builder docker image tag (#6245)
prior to this change, the OTP_VSN varaible was taken from
the docker host's OTP version which may differ from the
desired OTP version for the docker builder image.
2021-11-23 09:34:56 +08:00
Zaiming (Stone) Shi 2514f474b0
Sync v4.3 to v4.4 (#6262)
* fix(http): fix duplicate http headers

* chore(appup): add appup.src

* fix(appup): fix multiply defined module in appup

* chore(appup): fix wrong version

* chore(ekka): Bump version to 0.8.1.5

* fix(update_appup): Fix warnings, add support for external repos

* build: use find command's -delete option

* ci: do not sync master branch

* build: ensure openssl11

* build: copy only libcrypto and libtinfo

* fix(trace): handler_id now always return atom

Co-authored-by: zhouzb <zhouzb@emqx.io>
Co-authored-by: k32 <10274441+k32@users.noreply.github.com>
2021-11-23 09:33:20 +08:00
Zaiming (Stone) Shi b6064ce2c0
Merge pull request #6259 from zmstone/build-do-not-pack-glibc
build: copy only libcrypto and libtinfo
2021-11-22 17:20:20 +01:00
Zaiming (Stone) Shi 95e8671c7f
Merge pull request #6261 from zmstone/chore-trace-try-to-use-original-name
refactor(trace): hash non-printable or too long names
2021-11-22 17:16:48 +01:00
Zaiming Shi 0f52824872 refactor(trace): hash non-printable or too long names 2021-11-22 16:59:17 +01:00
Zaiming (Stone) Shi 1c64a9d95d
Merge pull request #6251 from zhongwencool/log-trace-api
feat: add support ip_address trace options
2021-11-22 16:19:53 +01:00
Zaiming Shi 122842a656 build: copy only libcrypto and libtinfo 2021-11-22 15:46:19 +01:00
zhongwencool 30fb9dd7ae fix: name must be printable unicode and len < 256 2021-11-22 22:20:02 +08:00
zhongwencool a91f975dc2
fix: make sure keepalive only 0~65535 (#6232) 2021-11-22 18:08:48 +08:00
zhongwencool d76275d17d feat: add support ip_address trace options 2021-11-22 15:20:24 +08:00
Turtle 62dc72c859 feat(sql_rule): test rule add User-Property information 2021-11-22 11:04:21 +08:00
lafirest 4767b41eb7
fix(emqx_st_statistics): fix initial value error (#6224)
* fix(emqx_st_statistics): fix initial value error
2021-11-21 18:55:43 +08:00
Zaiming Shi 1cb6cdbd76 build: upgrade to otp sed 23.2.7.2-emqx-3
There was a typo fix in ssl app for ecdsa_secp512r1_sha512
to ecdsa_secp521r1_sha512.

Hot-beam upgrade is supported when upgrading from OTP 23.2.7.2-emqx-2
just a 'ssl_cipher' module reload.
2021-11-20 23:28:12 +01:00
Zaiming (Stone) Shi efcdcc555f
Merge pull request #6239 from zmstone/build-ensure-openssl11
build: ensure openssl11
2021-11-20 17:12:07 +01:00
Zaiming (Stone) Shi 7de0891201
Merge pull request #6243 from emqx/ci-port-changes-from-master-branch
ci: port changes made in master branch
2021-11-20 17:11:42 +01:00
Zaiming Shi 099e2a8752 ci: port changes made in master branch 2021-11-19 23:03:06 +01:00
Zaiming (Stone) Shi ef41361753
Merge pull request #6233 from zmstone/ci-do-not-sync-master-branch
ci: do not sync master branch
2021-11-19 13:46:11 +01:00
Zaiming Shi 0b3037a571 build: ensure openssl11 2021-11-19 12:39:59 +01:00
Zaiming Shi f1aaed9276 ci: do not sync master branch 2021-11-19 08:46:53 +01:00
tigercl 35e32acf4a
Merge pull request #6195 from tigercl/fix/multi-http-headers
fix(http): fix duplicate http headers
2021-11-19 11:32:43 +08:00
Zaiming (Stone) Shi 747c609ec8
Merge pull request #6214 from zmstone/build-delete-potentially-broken-symlinks
build: ensure symlinks in _build dir are deleted after fetching deps
2021-11-18 13:35:00 +01:00
k32 df8fe88ac8
Merge pull request #6225 from k32/update-appup-ekka
fix(update_appup): Fix warnings, add support for external repos
2021-11-18 13:32:25 +01:00
Zaiming Shi ef36774189 build: use find command's -delete option 2021-11-18 13:20:53 +01:00
k32 113cfa6422 fix(update_appup): Fix warnings, add support for external repos 2021-11-18 12:31:02 +01:00
k32 b45296680d
Merge pull request #6221 from k32/ekka
chore(ekka): Bump version to 0.8.1.5
2021-11-18 12:28:31 +01:00
k32 5dc2e04e55 chore(ekka): Bump version to 0.8.1.5 2021-11-18 10:24:03 +01:00
zhanghongtong daeac6edf4 chore(release): update emqx release version 2021-11-18 15:26:36 +08:00
zhanghongtong d1cf526f34 ci: fix permission error when check sha256 2021-11-18 14:45:00 +08:00
lafirest 42333882c8
fix(emqx_st_statistics): fix unsafe rank range (#6207)
* fix(emqx_st_statistics): fix unsafe rank range
2021-11-18 14:41:59 +08:00
zhouzb b460172649 chore(appup): fix wrong version 2021-11-18 14:39:46 +08:00
zhanghongtong 093a93a7ec ci: fix not found package when check sha256 2021-11-18 14:27:37 +08:00
zhanghongtong eb0f4a543d ci: fix upload artifact error 2021-11-18 10:35:03 +08:00
Rory Z e5ffe8d7d5
Merge pull request #6216 from emqx/sync-v4.3-to-v4.4
Sync v4.3 to v4.4
2021-11-18 09:37:58 +08:00
Zaiming Shi c07f32f1ce Merge branch 'build-delete-potentially-broken-symlinks' into sync-v4.3-to-v4.4 2021-11-18 00:23:40 +01:00
Zaiming Shi cd4923d5b2 Merge remote-tracking branch 'origin/main-v4.3' into sync-v4.3-to-v4.4 2021-11-18 00:23:32 +01:00
Zaiming Shi 5db4607815 build: ensure symlinks in _build dir are deleted after fetching deps
In CI, the source code is downloaded with make deps-all
zipped and uploaded as an GitHub action artifact to be
downloaded in later steps to build packages

The symlinks are abs paths, meaning it might be broken
when unziped (inside docker containers)

This fix adds a `make clean` step after the deps-all target
and the `clean` target also removes rebar.lock and symlinks
2021-11-17 23:27:10 +01:00
William Yang a0fb78a38d
Merge pull request #6209 from qzhuyan/backport/william/4.3/node_dump-log-dir 2021-11-17 21:36:11 +01:00
Zaiming Shi 2a55a712d1 build: prepare for 4.4-alpha.1 release 2021-11-17 13:58:09 +01:00
William Yang 59656b3c3a fix: source emqx_vars in node_dump tool 2021-11-17 13:19:05 +01:00
William Yang 4f0d86dd57 fix(node_dump): locate log dir for package installation 2021-11-17 13:18:59 +01:00
Zaiming (Stone) Shi 14eea4647b
Merge pull request #6198 from Rory-Z/ci/cancel-otp-vsn-for-docker
ci: cancel otp vsn for docker image tag
2021-11-17 11:07:48 +01:00
Zaiming (Stone) Shi f30e21b4d6
Merge pull request #6188 from zmstone/feat-add-flexible-docker-build
feat(docker): add flexible docker build
2021-11-17 09:24:14 +01:00
Zaiming Shi 35164951e2 feat(docker): add flexible docker build
The defulat docker build (e.g. make emqx-docker) is based on alpine
image and it builds EMQ X from source code.
This is not flexible enough when we want to quickly run some tests
in a docker container.

The new docker build (e.g. make emqx-docker-testing) by default takes
the built zip package, and extract it in a very primitive base image
such as ubuntu:20.04 and centos:8
2021-11-17 08:31:35 +01:00
zhanghongtong 834240a760 ci: cancel otp vsn for docker image tag
add otp vsn for docker labels
2021-11-17 15:30:44 +08:00
zhouzb 3b25df9b47 fix(appup): fix multiply defined module in appup 2021-11-17 15:18:16 +08:00
zhanghongtong af5f93d81a build: show macos version 2021-11-17 14:43:03 +08:00
zhanghongtong 45965a3e71 ci: update emqx-ci-helper version 2021-11-17 14:43:03 +08:00
Zaiming (Stone) Shi 739703ea01
Merge pull request #6186 from zmstone/build-fix-rpm-release-scheme
build: Move otp version number to RPM's release part
2021-11-17 07:42:37 +01:00
zhouzb f948eb927d chore(appup): add appup.src 2021-11-17 14:31:37 +08:00
zhouzb 5e3fe6714e fix(http): fix duplicate http headers 2021-11-17 14:14:12 +08:00
Turtle ca1ece3db0 feat(publish-api): Publish api supports user-properties parameters 2021-11-17 11:17:10 +08:00
tigercl f335edaf6a
Merge pull request #6170 from tigercl/feat/mongo-migration
feat(migration): improve modules migration and add test cases
2021-11-17 09:42:09 +08:00
Zaiming Shi a070708e8d build: Move otp version number to RPM's release part 2021-11-16 19:14:15 +01:00
Zaiming (Stone) Shi 8b0478e663
Merge pull request #6184 from zmstone/chore-parameterise-docker-file
build: parameterise path to Dockerfile
2021-11-16 16:46:09 +01:00
Zaiming Shi ced2429011 fix: bump new feature lib-ce apps to 4.4 2021-11-16 16:08:36 +01:00
Zaiming Shi 4d9854012e build: parameterise path to Dockerfile 2021-11-16 16:08:36 +01:00
Rory Z addf0de4be
Merge pull request #6152 from Rory-Z/chore/rename-packages-name
Chore/rename packages name
2021-11-16 19:17:56 +08:00
Zaiming Shi db802ad04f chore: update toos-versions to pin 23.3.4.9-3 2021-11-16 11:35:13 +01:00
Zaiming Shi 49f164788b Merge remote-tracking branch 'rory/chore/rename-packages-name' into chore/rename-packages-name 2021-11-16 11:35:01 +01:00
zhanghongtong 284d122372 ci: pin otp version to 23.3.4.9-3 and builder version to 4.4-2 2021-11-16 18:33:21 +08:00
zhanghongtong 39c564c072 ci: rename windows packae 2021-11-16 17:56:33 +08:00
zhanghongtong 2251159c4f build: pin otp version to 23.3.4.9-3 and builder version to 4.4-2 2021-11-16 17:56:33 +08:00
Zaiming Shi 88efc6612d Merge remote-tracking branch 'origin/main-v4.4' into chore/rename-packages-name 2021-11-16 09:47:13 +01:00
Zaiming Shi 2dc63cffea fix(emqx_misc): call gen_tcp to decide if ipv6_probe is supported 2021-11-16 08:37:53 +01:00
Zaiming Shi e34055b6ef chore: pin OTP-23.2.7.2-emqx-3 2021-11-16 06:21:12 +01:00
zhanghongtong a178b6cc08 ci: fix make docker error for github action 2021-11-16 11:31:50 +08:00
Zaiming Shi 755dd11b07 build: always pull image before build 2021-11-16 01:28:42 +01:00
Zaiming Shi 3f1fc64a98 build: fix source rpm package name 2021-11-16 01:10:52 +01:00
Zaiming Shi c97c6aefc9 build: rename zip package and rpm deb packages 2021-11-16 00:59:28 +01:00
Zaiming Shi b68f01e7e7 build: do not print 'otp' prefix for otp version 2021-11-15 23:42:19 +01:00
Zaiming Shi d350281270 build: pin ghcr.io/emqx/emqx-builder/4.4-1:23.3.4.9-2-ubuntu20.04 2021-11-15 23:33:23 +01:00
Zaiming Shi f711f78c5c build: pin otp version 23.3.4.9-2 2021-11-15 23:17:21 +01:00
Zaiming Shi a6032d5435 build: update default otp versions 2021-11-15 23:04:31 +01:00
Zaiming (Stone) Shi c36ffc7ccb
Merge pull request #6174 from zmstone/main-v4.4-alpha.1
sync v4.3 to v4.4
2021-11-15 21:32:40 +01:00
Zaiming (Stone) Shi 684e51a865
Merge branch 'main-v4.4' into main-v4.4-alpha.1 2021-11-15 20:11:42 +01:00
Zaiming Shi ca1458d4d7 chore(emqx_rule_engine): bump app vsn to 4.4.0 2021-11-15 15:34:48 +01:00
k32 25f6633aaa
Merge pull request #6169 from k32/gen-rpc-ssl-4
feat(rpc): Bump gen_rpc version
2021-11-15 14:29:52 +01:00
k32 32086f97ec
Merge branch 'main-v4.4' into gen-rpc-ssl-4 2021-11-15 13:47:54 +01:00
Zaiming Shi d05e2ff0b1 Merge remote-tracking branch 'origin/main-v4.3' into main-v4.4-alpha.1 2021-11-15 13:41:51 +01:00
zhouzb 2f279b3607 test(migration): fix code scope 2021-11-15 18:14:51 +08:00
zhouzb fce93c5a17 feat(migration): improve modules migration and add test cases 2021-11-15 17:44:23 +08:00
zhongwencool 96d2615cc8
fix: return error code when trace log not foundd (#6168) 2021-11-15 17:41:22 +08:00
k32 61c68ddb35 feat(rpc): Bump gen_rpc version 2021-11-15 10:27:08 +01:00
Zaiming (Stone) Shi 7dc944a154
Merge pull request #6160 from zmstone/chore-check-version-before-build
build: ensure git tag matches release version
2021-11-15 09:07:37 +01:00
zhongwencool 23e2bd62c5
fix: there should not be multiple layers of directories when download trace zip file (#6165) 2021-11-15 15:53:49 +08:00
zhanghongtong cace53a02b build: update docker build 2021-11-15 14:33:23 +08:00
tigercl 8c119ea6d9
Merge pull request #6149 from Rory-Z/main-v4.4
chore(cluster): add new type for dns auto cluster
2021-11-15 13:48:19 +08:00
tigercl 276db400c2
Merge pull request #6150 from tigercl/feat/mongo-srv
feat(mongo srv): support srv for mongodb authentication
2021-11-15 11:25:17 +08:00
zhongwencool 6bd5fd218e
chore: limit/page to position/bytes (#6161) 2021-11-15 11:02:45 +08:00
lafirest 0357f7ad85
improve(emqx_st_statistics): optimize the parameters of on_publish_done (#6151)
* fix(emqx_st_statistics): optimize the parameters of on_publish_done
2021-11-15 11:00:04 +08:00
lafirest 87a2667e35
fix(emqx_retainer): fix timer message error (#6156)
* fix(emqx_retainer): fix timer message error
2021-11-15 10:57:59 +08:00
zhanghongtong f46084438b chore(cluster): add new type for dns auto cluster
Signed-off-by: zhanghongtong <rory-z@outlook.com>
2021-11-15 10:46:52 +08:00
zhanghongtong 7e91a47be8 ci: update workflows 2021-11-15 10:45:27 +08:00
zhanghongtong 454f609aa6 build: emqx package name scheme include otp version 2021-11-15 10:45:27 +08:00
zhouzb cb18538957 fix(mong srv): fix wrong configuration 2021-11-15 10:27:56 +08:00
zhouzb 25f504c90a feat(mongo srv): support srv for mongodb authentication 2021-11-15 10:26:38 +08:00
Shawn d305111929
Merge pull request #6076 from HJianBo/enhance-message-hook
feat(exhook): expose headers for on_messages_publish hook
2021-11-15 10:23:16 +08:00
JianBo He f7bdd6defe chore(lwm2m): fix bad appup.src 2021-11-15 09:50:32 +08:00
JianBo He 08cf0326b3 chore(exhook): bump version to 4.4.0 2021-11-15 09:50:32 +08:00
JianBo He 6b40048d29 chore: put the pool_size default value to avoid hot upgrade failure 2021-11-15 09:50:32 +08:00
JianBo He 2be33b33e3 chore: fix elvis warnings 2021-11-15 09:50:32 +08:00
JianBo He f194ae65d2 chore: update appup.src 2021-11-15 09:50:32 +08:00
JianBo He 8dfc8ed96b chore: fill message headers 2021-11-15 09:50:32 +08:00
JianBo He 7d06e48b4b chore: remove needless catch
Co-authored-by: Zaiming (Stone) Shi <zmstone@gmail.com>
2021-11-15 09:50:32 +08:00
JianBo He f2d99017a0 chore(exhook): update appup.src 2021-11-15 09:50:32 +08:00
JianBo He 641f36514f chore: fix elvis warnings 2021-11-15 09:50:32 +08:00
JianBo He 5922521e49 test(props): cover messages headers 2021-11-15 09:50:32 +08:00
JianBo He 44008b9a6d chore: fix compiling warnings 2021-11-15 09:50:32 +08:00
JianBo He c170d076e3 feat(exhook): expose process pool_size for grpc client 2021-11-15 09:50:32 +08:00
JianBo He b756e7d17a chore: upgrade grpc to 0.6.4 2021-11-15 09:50:32 +08:00
JianBo He 012c741592 chore(exhook): update appup.src 2021-11-15 09:50:32 +08:00
JianBo He ef2a5c1dc7 chore(exhook): fix diaylzer warnings 2021-11-15 09:50:32 +08:00
JianBo He 6fb3ff1f9f feat(exhook): expose headers for on_messages_publish hook 2021-11-15 09:50:32 +08:00
Zaiming Shi 0971567cff build: ensure git tag matches release version 2021-11-14 18:23:30 +01:00
lafirest ad4d3fc652
chore(emqx_retainer): refresh the timestamp when dispatch retained message (#6155)
1. refresh the timestamp when dispatch retained message
2. fix some elvis style error
2021-11-12 21:26:44 +08:00
zhongwencool 8bf6668e4c
Set keepalive via http api (#6143)
* feat: set keepalive over http api

* chore: elvis warning

* fix: bump retainer to 4.4.0
2021-11-12 20:11:59 +08:00
Zaiming (Stone) Shi 5f8d9db64b
Merge pull request #6142 from HJianBo/put-message-headers
Fill the message headers
2021-11-12 12:51:48 +01:00
JianBo He 54bb3ddaf2
Merge branch 'main-v4.3' into put-message-headers 2021-11-12 19:25:39 +08:00
Zaiming Shi d512c474be Merge tag 'v4.3.10' into main-v4.3 2021-11-12 11:52:54 +01:00
Zaiming (Stone) Shi 5412cf4c78
Merge pull request #6154 from lafirest/fix/revert_refresh_retained
Revert "fix(emqx_retainer): refresh the timestamp when dispatch retai…
2021-11-12 11:38:38 +01:00
firest e9ce8e7586 fix(emqx_retainer): revert the "refresh the timestamp when dispatch retained message"
This reverts commit 66d0c44e36.
2021-11-12 18:36:26 +08:00
zhongwencool f3de1bdb77
fix: make sure enable boolean() (#6147) 2021-11-12 15:48:30 +08:00
JianBo He d8f37be210 chore(lwm2m): fix bad appup.src 2021-11-12 15:38:49 +08:00
lafirest 66d0c44e36
fix(emqx_retainer): refresh the timestamp when dispatch retained message (#6148) 2021-11-12 14:51:02 +08:00
Shawn e7bbe98a7a fix(ekka): update ekka to 0.8.2 2021-11-12 14:48:58 +08:00
lafirest a4a7cac647
fix(eqmx_st_statistics): add ignore_before_create in config (#6140)
1. allows not to process the message before the session is created
to solve the problem caused by clean session = false
2. fix some elvis errors
2021-11-12 14:00:48 +08:00
tigercl 0003b05247
Merge pull request #6138 from emqx/chore/release
chore(release): update version to 4.3.10
2021-11-12 11:34:45 +08:00
JianBo He 8e4c2c88c3 chore: use PROTO_VER marco 2021-11-12 10:45:26 +08:00
JianBo He 439fb3a403 chore: fix elvis warnings 2021-11-12 10:43:21 +08:00
JianBo He 0c5cb1b9ac chore: update appup.src 2021-11-12 10:43:21 +08:00
JianBo He 4c29c3a5e5 chore: fill message headers 2021-11-12 10:43:21 +08:00
zhouzb 04a4462f1e chore(release): update version to 4.3.10 2021-11-12 10:36:27 +08:00
zhongwencool 7193cd4275
feat(trace): move common trace module to plugin_libs (#6127)
* feat(trace): move common mod to plugin_libs

* fix: elvis warning
2021-11-12 07:33:36 +08:00
lafirest 9d4f2916c2
refactor(emqx_st_statistics): optimize the code directory structure (#6128) 2021-11-11 23:49:54 +08:00
JianBo He edb2793180
Merge pull request #6126 from zmstone/test-fix-flaky-emqx-cm-test
Test fix flaky emqx cm test
2021-11-11 18:20:48 +08:00
Zaiming (Stone) Shi 1d20a8b720
Merge pull request #6116 from zmstone/4.3-chore-ehttpc-pin-0.1.12
chore(ehttpc): pin 0.1.12
2021-11-11 10:47:01 +01:00
Zaiming Shi 21898e1daf chore(emqx_dashboard): bump version after v4.3.10 release 2021-11-11 10:34:29 +01:00
Zaiming Shi 74b6b5214a test(emqx_cm_SUITE): add a gen_server call sync 2021-11-11 10:33:31 +01:00
zhongwencool 835ad52498
feat(trace): http api for trace (#6052)
* feat(trace): add http trace API

* feat: sub/unsub trace topic

* chore(trace): stream log use _page and _limit

* fix: elvis warning

* fix: mod_trace test failed

* fix: http api meta rename _page/_limit to _page/_limit

* fix: clientid string not working

* fix: add emqx_mod_trace to module

* fix(trace): fixed by review suggestions
2021-11-11 14:54:40 +08:00
JianBo He cc7b1aa93e
Merge pull request #6121 from HJianBo/ensure-start-listeners-before-plugin
Ensure starting listeners before plugins loading
2021-11-11 14:52:15 +08:00
JianBo He 86b8d88165 chore(emqx): update appup.src 2021-11-11 11:55:57 +08:00
JianBo He 8f07f26744 fix: ensure starting listeners before plugins 2021-11-11 11:37:54 +08:00
lafirest 1dd18aa07a
fix(emqx_st_statistics): change emqx_st_statistics implementation fro… (#6115)
* fix(emqx_st_statistics): change emqx_st_statistics implementation from plugin to module
2021-11-11 10:16:06 +08:00
Zaiming Shi 98136ff119 chore(ehttpc): pin 0.1.12 2021-11-10 14:41:37 +01:00
zhongwencool 06a1b37992
fix(test): flaky mqtt expiry test case. (#6111) 2021-11-10 15:41:31 +08:00
zhongwencool fa34d8353e
fix(test): flaky mqtt expiry test case. (#6112) 2021-11-10 15:40:46 +08:00
JianBo He a81140fd00
Merge pull request #6110 from HJianBo/force-clear-stomp-listener
fix(emqx_stomp): fix hot-upgrade stopping listener failed
2021-11-10 11:43:51 +08:00
Shawn 133609a040 fix(relup): configs for plugins are missing after relup 2021-11-10 11:10:23 +08:00
lafirest fae815b35c
Feat/slow topic api (#6101)
* feat(emqx_st_statistics): add api
2021-11-10 11:08:06 +08:00
Shawn 388c29344a fix(relup): configs for plugins are missing after relup 2021-11-10 10:25:02 +08:00
JianBo He fefadbcd17 fix(emqx_stomp): fix hot-upgrade stopping listener failed
When the upgrade is executed, all envs of plugins are cleared,
which causes the listener of stomp to stop failing.

This is only a temporary modification to ensure that the upgrade
can be executed successfully.

following fixes: https://github.com/emqx/emqx/pull/6105
2021-11-10 10:02:37 +08:00
lafirest f60f92b95d
Merge pull request #6074 from lafirest/improve/slow_topic
improve(emqx_st_statistics): optimize the implementation of topk
2021-11-09 13:46:44 +08:00
lafirest 354b0bc08e refactor(emqx_st_statistics): optimize the implementation of topk 2021-11-09 11:51:10 +08:00
Shawn 77c82cf189 fix(code_style): some elvis complaints 2021-11-09 10:30:29 +08:00
Shawn 2242bb9376 fix(rule): force max speed to 2 decimal digits of precision 2021-11-09 10:30:29 +08:00
Shawn 00ba4d32f3 fix(syntax): allow single quotes in the FROM clause 2021-11-09 09:31:58 +08:00
JianBo He 00ae24fd6a
Merge pull request #6097 from zmstone/chore-bump-pkg-vsn-to-4.4.0
chore: bump release version to 4.4.0
2021-11-09 09:22:12 +08:00
Zaiming Shi 9ea5c5e58d fix(emqx_mgmt): support v4.4 data export 2021-11-08 23:12:18 +01:00
Zaiming Shi fec83590a6 chore: bump release version to 4.4.0 2021-11-08 21:38:18 +01:00
Zaiming (Stone) Shi 70bc5f21e4
Merge pull request #6095 from zmstone/sync-v4.3-to-v4.4
Sync v4.3 to v4.4
2021-11-08 21:07:10 +01:00
Zaiming Shi 6222e1b0eb Merge remote-tracking branch 'origin/main-v4.3' into sync-v4.3-to-v4.4 2021-11-08 21:01:14 +01:00
Thales Macedo Garitezi 14474a2739
Merge pull request #6056 from emqx/feat-truly-connected-client-count
Track connected client count

In order to correctly display the number of _connected_ clients in our monitor dashboard, we need to track those connections that are actually connected to clients, not considering connections from persistent sessions that are disconnected. Today, the `connections.count` that is displayed in the dashboards considers those disconnected persistent sessions as well.

The new statistics can be found in the [`emqx_management`](https://github.com/emqx/emqx/tree/main-v4.4/apps/emqx_management) plugin, under `/api/v4/stats`, in the keys `live_connections.{max,count}`.
2021-11-08 15:46:49 -03:00
Thales Macedo Garitezi b9270ad719
feat(stats): track live / connected channel count for monitoring
In order to correctly display the number of connected clients in our
monitor dashboard, we need to track those connections that are
actually connected to clients, not considering connections from
persistent sessions that are disconnected.  Today, the
`connections.count` that is displayed in the dashboards considers
those disconnected persistent sessions as well.
2021-11-08 14:45:57 -03:00
Zaiming (Stone) Shi 6b06142562
Merge pull request #6077 from zmstone/sync-exproto-code
Sync exproto code from ee to ce
2021-11-08 16:43:01 +01:00
Zaiming Shi 83ecdb242f fix(appup): delete module load from app restart instruction group 2021-11-08 16:17:54 +01:00
Zaiming Shi ed171b8e60 chore: ensure version bump for dashboard app 2021-11-08 16:17:54 +01:00
Zaiming Shi 4f3790a6f5 style: fix code style for emqx_exproto 2021-11-08 16:17:54 +01:00
Zaiming Shi f1f2e51c99 fix: update appup 2021-11-08 16:17:54 +01:00
Zaiming Shi 412a68ac75 chore: bump version for emqx_proto 2021-11-08 16:17:54 +01:00
Zaiming Shi 325c5e5a97 chore: sync ce code added only to ee back to ce 2021-11-08 16:17:54 +01:00
Zaiming (Stone) Shi 1e47dbf14b
Merge pull request #6089 from k32/emqx-4-bump-version
chore(emqx): Update version and appup file
2021-11-08 16:17:16 +01:00
Zaiming (Stone) Shi e33c0a3b09
Merge pull request #6087 from emqx/patch-v4.3.9
fix(bin/emqx): ensure NAME is set
2021-11-08 16:13:07 +01:00
k32 7d07e8d948 chore(emqx): Update version and appup file 2021-11-08 14:24:37 +01:00
k32 4ebb65e5d5
Merge pull request #6065 from k32/remove-nosuspend
fix(emqx_connection): Introduce backpressure while sending data
2021-11-08 13:39:01 +01:00
Zaiming Shi f5a2421fdb fix(bin/emqx): ensure NAME is set 2021-11-08 13:27:38 +01:00
tigercl 26bc3ca0b4
Merge pull request #6063 from HJianBo/refactor-stomp-gw
fix(stomp): fix bad_return_value
2021-11-05 09:31:45 +08:00
Zaiming (Stone) Shi 5f4c6a23ab
Merge pull request #6069 from zmstone/v4-build-reenable-elvis-check
build: fix elvis check and ensure newline at EOF
2021-11-04 21:50:20 +01:00
Zaiming Shi 37edb03866 build: fix elvis check and ensure newline at EOF 2021-11-04 21:07:45 +01:00
k32 27afecb3ac fix(emqx_connection): Introduce backpressure while sending data
Fixes #5494
2021-11-04 15:25:34 +01:00
tigercl 4c4993fa25
Merge pull request #6062 from terry-xiaoyu/fix_mc_hungs
fix(ekka): add timeout to rpc:multicall/4
2021-11-04 21:59:09 +08:00
JianBo He ef9fe12825 fix(stomp): fix bad_return_value 2021-11-04 21:35:16 +08:00
Shawn a406c4f470 fix(ekka): add timeout to rpc:multicall/4 2021-11-04 16:30:46 +08:00
zhanghongtong d7aec58370 ci(relup): fix old vsn error
Signed-off-by: zhanghongtong <rory-z@outlook.com>
2021-11-04 11:54:06 +08:00
tigercl e79085c259
Merge pull request #6040 from HJianBo/refactor-stomp-gw
Refactor stomp gw
2021-11-04 10:22:21 +08:00
JianBo He f36abc281a chore(types): add comment for ver type 2021-11-04 09:11:02 +08:00
JianBo He 981f74d458 test(stomp): refine stomp test cases 2021-11-03 21:13:38 +08:00
JianBo He e4e8590a77 fix(stomp): backoff outgoung hear-beat timer interval 2021-11-03 20:37:07 +08:00
lafirest 4b2586fec4
Merge pull request #6048 from lafirest/feat/slow_topics
feat: add slow topics statistics plugin
2021-11-03 17:50:07 +08:00
lafirest f8acb31f89 feat: add slow topics statistics plugin 2021-11-03 17:20:07 +08:00
tigercl 8dfafc464e
Merge pull request #6049 from tigercl/chore/webhook-version
chore(version): skip the version occupied by enterprise
2021-11-03 16:09:46 +08:00
xiangfangyang-tech cee9b39b2d
Merge pull request #6050 from xiangfangyang-tech/improve_autotest_script
Improve autotest script
2021-11-03 16:00:53 +08:00
xiangfangyang-tech 9faab7cc9b chore(autotest): improve git_action script with emqx-fvt tag 2021-11-03 16:00:02 +08:00
xiangfangyang-tech 8fb9d27aa1
Merge pull request #1 from emqx/main-v4.3
Main v4.3
2021-11-03 15:34:08 +08:00
Zaiming Shi aa90177302 Merge remote-tracking branch 'origin/main-v4.3' into main-v4.4 2021-11-03 08:28:14 +01:00
zhouzb e5f30a4d28 chore(version): skip the version occupied by enterprise 2021-11-03 15:26:04 +08:00
JianBo He f7760232e4 fix(stomp): parse heartbeat EOL frame 2021-11-03 14:01:05 +08:00
JianBo He af7b5704ab fix(stomp): counting packets and messages 2021-11-03 14:01:05 +08:00
Zaiming (Stone) Shi 818bde1820
Merge pull request #6044 from zmstone/use-profile-name-as-default-docker-image-tag
Use profile name as default docker image tag
2021-11-03 06:21:34 +01:00
JianBo He 0a7f04caa3 fix(stomp): enrich sub-opts if sub-id/ack absent 2021-11-03 09:19:19 +08:00
Zaiming Shi 2fb8ffa8c2 test: add git credentials for enterprise tests 2021-11-02 23:12:30 +01:00
Zaiming Shi c6c9ba400e test: parameterise emqx image name in automated integration tests 2021-11-02 22:54:52 +01:00
Zaiming Shi 42695a2f9a chore: remove external contributors as maintainers
Big thank you to Raymond M Mouthaan and Huang Rui
2021-11-02 18:25:25 +01:00
Zaiming Shi 1d0c8a4eef fix: use of default profile name as default docker image name 2021-11-02 18:25:25 +01:00
JianBo He 14515e680e fix(stomp): fix stats_timer not working 2021-11-02 21:37:22 +08:00
Turtle f00e254bdf chore(release): update version to 4.3.9 2021-11-02 18:26:57 +08:00
JianBo He 2c4d3d1d24 chore(stomp): fix dialyzer warnings 2021-11-02 17:09:57 +08:00
JianBo He cc6ea6e4dd chore(stomp): remove needless properties 2021-11-02 13:56:31 +08:00
JianBo He fa2e97b1c5 chore(stomp): update appup.src 2021-11-02 13:48:03 +08:00
JianBo He 7734d6969c fix(stomp): support pub/sub operations 2021-11-02 13:31:27 +08:00
JianBo He d2924e82ab fix(stomp): fix kick/discard crash errors 2021-11-02 13:31:27 +08:00
JianBo He ed505ee120 refactor(stomp): compatible hooks system 2021-11-02 13:31:27 +08:00
JianBo He d2b6a95484 fix(stomp): fix anonymous not working 2021-11-02 13:31:27 +08:00
zhouzb 3d9054d25e fix(typo): fix typo in webhook resource 2021-11-02 13:28:46 +08:00
Shawn 0ab1b7c95d fix(mongo): update mongodb to 3.0.10 2021-11-02 09:23:07 +08:00
Zaiming (Stone) Shi 2a3d1fcb78
Merge pull request #6023 from xiangfangyang-tech/main-v4.3
chore(autotest): add mysql&pgsql&http test flow in git_action flow
2021-11-01 16:24:20 +01:00
Zaiming (Stone) Shi fa92e61440
Merge pull request #6031 from HJianBo/ignore-duplicated-sock-error
Ignore log duplicated sock error
2021-11-01 14:36:14 +01:00
JianBo He 763f567f7d chore(appup): update appup.src 2021-11-01 18:35:41 +08:00
JianBo He c9d39b4d35 chore(channel): remove redundant logs
Multiple sock_closed events may be generated,
so we need to allow sock_closed events to be reentrant
2021-11-01 18:17:38 +08:00
Zaiming (Stone) Shi 8526032200
Merge pull request #6012 from Rory-Z/chore/change-ci-helper-version
ci: change emqx-ci-helper version for build workflows
2021-11-01 10:18:49 +01:00
xiangfangyang-tech d28f913b94 chore(autotest): update redis ssl cert file 2021-11-01 16:34:37 +08:00
Zaiming (Stone) Shi 3175d59e7b
Merge pull request #6033 from zmstone/fix-find-missing-dyn-lib-before-boot
best-effort portable for zip packages
2021-11-01 08:08:26 +01:00
Zaiming Shi 9832a2ed00 build: show linux distro in BUILT_ON info 2021-10-31 17:21:47 +01:00
Zaiming Shi ecb6c1c59e build: copy dynamic libs for zip package 2021-10-31 17:21:26 +01:00
Zaiming (Stone) Shi 831f2eda0c
Merge pull request #6030 from zmstone/fix-force-kill-after-kick-or-discard-timeout
fix(session): force kill session for 'kick' and 'discard'
2021-10-31 09:48:04 +01:00
Zaiming Shi fb62487801 fix(emqx/appup): add emqx_cm to appup 2021-10-30 16:39:04 +02:00
Zaiming Shi 765a76fa80 fix(emqx_mgmt_cli): idempontent kick. now it always returns ok 2021-10-30 16:38:36 +02:00
Zaiming Shi 7f4809f61a fix(session): force kill session for 'kick' and 'discard'
Prior to this fix, 'kick' and 'discard' calls may timeout (or
fail for other reason), failures lead to only a log, then
continue to allow the new session to get registered.

As a result, in case a client is stuck, there is no way to
force it to step down, end up with multiple connections (sessions)
for the client ID in dashboard.

After this fix, the stale pids are notified to shutdown
via a gen_server:call, and forced with a exit(Pid, kill) for any
exception happend to the gen_server:call
2021-10-30 16:38:36 +02:00
k32 cfe3efed4a
Merge pull request #6024 from k32/4-4-fix-gen-rpc
fix(emqx_broker): Preserve message order across the cluster
2021-10-29 12:49:25 +02:00
k32 791caba2ed fix(broker): Fix out-of-order message delivery in a cluster
Fixes: #4658
2021-10-29 10:47:56 +02:00
xiangfangyang-tech 2ce592040e
Merge branch 'main-v4.3' into main-v4.3 2021-10-29 15:12:22 +08:00
xiangfangyang-tech afd6fe181c chore(autotest): add git action script for v4.3
chore(autotest): change git site of autemate script

chore(autotest): improve git action script with Stones advises

chore(autotest): improve trigger condition

chore(autotest): add mysql&pgsql&http test flow in git_action script
2021-10-29 15:00:42 +08:00
Zaiming (Stone) Shi b50f0b67d4
Merge pull request #6021 from zmstone/fix-boot-handle-vmarg-flags
fix: boot handle vmarg flags
2021-10-29 08:32:43 +02:00
Zaiming Shi cb3d2fd6c3 chore: refine -heart option document 2021-10-28 21:15:54 +02:00
Zaiming Shi 18fc82855b fix(bin/emqx): handle flags in vm.args
prior to this fix, the flags such as -heart in vm.args file were taken
as KEY="", VALUE="-heart"
as a result, the sed replacement replaces all lines with "-heart"
causing beam to crash at boot
2021-10-28 21:09:06 +02:00
Zaiming (Stone) Shi dd4e307753
Merge pull request #5995 from Spycsh/main-v4.3
chore: add cluster script for local machine
2021-10-28 14:20:07 +02:00
Spycsh ec30fb346a chore: add cluster script for local machine 2021-10-28 12:07:23 +08:00
zhanghongtong ba3e7841e5 ci: change emqx-ci-helper version for build workflows 2021-10-27 17:21:17 +08:00
Ilya Averyanov 49c7eae211
Merge pull request #5885 from savonarola/fix-acl-schema
fix(mnesia_acl): introduce optimized schema and migration process
2021-10-27 10:41:05 +03:00
Ilya Averyanov 6d48bbf34c fix(mnesia_acl): added acl migration test scripts 2021-10-27 09:58:21 +03:00
Ilya Averyanov ba319e1159 fix(mnesia_acl): upgrade snabbkaffe and use ?check_trace 2021-10-27 09:58:21 +03:00
Ilya Averyanov 43ac315444 fix(mnesia_acl): do not use matchspec terms in external APIs 2021-10-27 09:58:21 +03:00
Ilya Averyanov 8341a4d4a7 fix(mnesia_acl): introduce optimized schema and migration process 2021-10-27 09:58:12 +03:00
JianBo He b4c2643291 fix(api-clients): escape the searching string 2021-10-26 10:34:31 +08:00
xiangfangyang-tech 67b543f01e chore(autotest): improve trigger condition 2021-10-25 15:19:05 +08:00
xiangfangyang-tech 3e1abbddd2 chore(autotest): improve git action script with Stones advises 2021-10-25 15:19:05 +08:00
xiangfangyang-tech 48d932af83 chore(autotest): change git site of autemate script 2021-10-25 15:19:05 +08:00
xiangfangyang-tech a198158bfb chore(autotest): add git action script for v4.3 2021-10-25 15:19:05 +08:00
JianBo He 99453df637 fix(api-clients): escape the searching string 2021-10-25 09:40:56 +08:00
Zaiming (Stone) Shi fd34eb1f4e
Merge pull request #5970 from zmstone/fix-lwm2m-psk-ciphers
fix(lwm2m): add support for new cipher suites
2021-10-21 16:56:28 +02:00
Zaiming Shi 224cc0d5c7 fix(lwm2m): bump version in appup and add upgrade instructions 2021-10-21 14:31:59 +02:00
Zaiming Shi 4896c03881 fix(lwm2m): add support for new cipher suites
prior to this change, the schema does not allow newer
cipher suites, and the default ciperhs given in the conf file
is likely not supported by some clients (which only supports dtls v1.2)
2021-10-20 23:10:46 +02:00
Zaiming (Stone) Shi d2f4c55fd7
Merge pull request #5940 from savonarola/mqtt-http-api-validations-fix
fix(mgmt api): allow empty clientid in publish
2021-10-18 12:12:39 +02:00
Ilya Averyanov 3cae4437fa fix(mgmt api): allow empty clientid in publish 2021-10-18 12:10:21 +03:00
Zaiming (Stone) Shi 1984e5b68c
Merge pull request #5927 from zmstone/fix-relup-test-env-overrides
test(relup): fix env overrides
2021-10-16 06:38:52 +02:00
Zaiming (Stone) Shi 51d2fa1359
Merge pull request #5932 from zmstone/chore-scripts-skip-appup-file-app-vsn-check
chore: skip appup file in vsn check script
2021-10-15 12:59:18 +02:00
Zaiming Shi 08c2907d44 chore: skip appup file in vsn check script 2021-10-15 12:10:37 +02:00
Zaiming Shi 9038da0bd2 fix(ws_connection): check origin failure should return 403 not 500 2021-10-15 15:49:26 +08:00
Zaiming Shi 51bc9c83c3 fix: ignore unused var 2021-10-15 15:49:26 +08:00
Zaiming Shi f7d70d05ab chore: pin otp version for 4.3 2021-10-15 15:49:26 +08:00
k32 44d666f62b
Merge pull request #5923 from k32/update-appup-binary
feat(update_appup): Support binary releases (.zip)
2021-10-14 17:14:11 +02:00
Zaiming Shi d2649eea81 test(relup): fix env overrides
these are for 5.0
2021-10-14 15:53:44 +02:00
k32 14aaa4affe fix(update_appup): Fix dependency check 2021-10-14 14:18:09 +02:00
k32 cb5db8059b fix(update_appup): Use a different syntax for substitution 2021-10-14 13:47:04 +02:00
k32 703f52cec7 feat(update_appup): Support binary releases (.zip) 2021-10-13 17:25:29 +02:00
k32 4b098ce3af
Merge pull request #5907 from k32/mechanical-appup-update
chore(appup): Update appup scripts
2021-10-13 09:44:10 +02:00
k32 7e1f3c5882 revert(appup): Revert changes to management and dashboard 2021-10-13 09:19:49 +02:00
k32 b60e33ca41 fix(appup): Always run appup actions for management and dashboard 2021-10-12 19:19:26 +02:00
k32 4643415b0b chore(appup): Update appup scripts 2021-10-12 17:45:10 +02:00
Ilya Averyanov 6dca349435
Merge pull request #5878 from savonarola/fix-emqx-rpc
fix(emqx_rpc): removed unnecessary call wrapper
2021-10-12 11:18:30 +03:00
Ilya Averyanov 85723e4a35 fix(emqx_rpc): removed unnecessary call wrapper 2021-10-11 15:20:31 +03:00
zhanghongtong 75fac32c12 docs(docker): fix invalid links 2021-10-09 09:16:45 +08:00
k32 5ca28749ed
Merge pull request #5886 from k32/dev/bump-ekka-4.3
chore(ekka): Bump version
2021-10-08 13:15:02 +02:00
k32 b88674e876 chore(ekka): Bump version to 0.8.1.3
Fixes: #5367
2021-10-07 19:33:25 +02:00
Zaiming (Stone) Shi 58b5d3709d
Merge pull request #5882 from zmstone/fix-flaky-tests-cm-session-race-condition-4.3
chore: fix flaky test cm_SUITE open session racecondition
2021-10-07 17:26:24 +02:00
k32 230af7990d
Merge pull request #5868 from k32/appup-script-beams
feat(update_appup): Compare beam files
2021-10-07 17:00:02 +02:00
Zaiming Shi 7c1ce8bc70 chore: fix flaky test cm_SUITE open session racecondition 2021-10-07 11:53:53 +02:00
k32 668bcad4e0 fix(update_appup): Add forgotten CLI parameter 2021-10-07 09:59:32 +02:00
k32 20ee42be87 chore(update-appup): s/Pred/Prev/g 2021-10-06 23:45:52 +02:00
k32 29ad2c04da fix: Use application version instead of the release version 2021-10-06 23:45:52 +02:00
k32 ecf4d196eb feat(update_appup): Return error when missing appup.src 2021-10-06 23:45:52 +02:00
k32 89fbf5fea2 feat(update_appup): Add a warning message 2021-10-06 23:45:52 +02:00
k32 ca77749281 feat(update_appup): Add a full description of the algorithm 2021-10-06 23:45:52 +02:00
k32 6bee6279f8 feat(update_appup): Make the script more generic 2021-10-06 23:45:52 +02:00
k32 c50c72b18e fix(update_appup): Fix downgrade module loading 2021-10-06 23:45:52 +02:00
k32 f793883e35 refactor(update_appup): Minor code cleanup 2021-10-06 23:45:52 +02:00
k32 aca6367561 feat(update_appup): Create stubs 2021-10-06 23:45:52 +02:00
k32 4020db8fc1 feat(update_appup): Compare beam files 2021-10-06 23:45:52 +02:00
Zaiming (Stone) Shi c8dda45c55
Merge pull request #5879 from zmstone/chore-always-force-dashboard-vsn-bump
chore: force appup for emqx_dashboard app in each release
2021-10-06 22:55:58 +02:00
Zaiming Shi 1d9f5ea133 chore: force appup for emqx_dashboard app in each release 2021-10-06 22:19:05 +02:00
Zaiming (Stone) Shi 0120f8cf45
Merge pull request #5877 from zmstone/test-fix-flaky-test-case-in-broker-suite
test(emqx_broker): fix flaky tests
2021-10-06 15:22:52 +02:00
Zaiming Shi e012e77ce6 chore(ci): do not colorize JSON in jq output 2021-10-06 14:53:54 +02:00
Zaiming Shi e1a2dc9138 fix(ci): lux report verbose progress 2021-10-06 14:14:21 +02:00
Zaiming Shi 3fa442f4a4 test(emqx_broker): fix flaky tests 2021-10-05 20:55:36 +02:00
Zaiming (Stone) Shi feef02b639
Merge pull request #5874 from zmstone/fix-emqx-app-vsn
chore(emqx.app): bump emqx app to vsn 4.3.10
2021-10-05 14:34:20 +02:00
Ilya Averyanov da2c41702d
Merge pull request #5848 from savonarola/mqtt-http-api-validations
Add validations to management API
2021-10-05 14:26:38 +03:00
Zaiming Shi 7804b39e08 chore(emqx.app): bump emqx app to vsn 4.3.10
app 4.3.9 was release as a part of enterprise e4.3.4
but opensource v4.3.9 is not releasd yet, but we have
to bump app version to 4.3.10 to make appup work for the next
release (either opensource or enterprise).
2021-10-05 12:03:47 +02:00
Ilya Averyanov 44d16a26ab fix(mgmt api): validate clientid to avoid crashes and 500 HTTP errors 2021-10-05 12:09:03 +03:00
Ilya Averyanov 42339b2e35
Merge pull request #5869 from savonarola/refactor-relup-tests
chore(relup tests): refactored relup tests
2021-10-05 12:07:49 +03:00
Ilya Averyanov ebbf567fff chore(relup tests): refactored relup tests 2021-10-04 21:47:51 +03:00
Zaiming (Stone) Shi ec89781cc4
Merge pull request #5849 from savonarola/dashboard-login-security
fix(dashboard rest api): improve auth method security
2021-09-30 07:29:38 +02:00
Ilya Averyanov 40850d981d chore(CI): increased relup test timeout 2021-09-29 23:29:40 +03:00
Ilya Averyanov 188b889ed3 fix(dashboard rest api): improve auth method security
Make messages not differ when authenticating with invalid password and invalid username.

Closes: #5563
2021-09-29 19:33:45 +03:00
Zaiming (Stone) Shi 004160af56
Merge pull request #5826 from JimMoen/fix-frame
fix(frame): variable byte integer could be larger than 4 bytes.
2021-09-28 19:06:54 +02:00
JimMoen 2dba91d6d0 chore(test): variable integer test. 2021-09-28 15:15:05 +08:00
JimMoen 19031e21ec fix(frame): variable byte integer could be larger than 4 bytes. 2021-09-28 15:15:05 +08:00
Zaiming (Stone) Shi d59d3849e0
Merge pull request #5794 from zmstone/fix-flaky-test-bridge-worker
test(mqtt-bridge): increase timeout in snk event wait
2021-09-23 16:58:47 +02:00
Zaiming Shi 92a3d683cf test(mqtt-bridge): increase timeout in snk event wait 2021-09-23 13:34:27 +02:00
Zaiming (Stone) Shi 55d4dfde44
Merge pull request #5758 from m-yosefpor/main-v4.3
Backporting https://github.com/emqx/emqx/pull/5471 to main-v4.3
2021-09-16 21:36:50 +02:00
Mohammad Yosefpor 1ae3f8c204
fix: Running on Openshift clusters with restricted writable filesystem containers 2021-09-16 18:57:44 +04:30
Parham Alvani 9d993e1625
chore(auth-http): Disable Superuser Request by Defualt 2021-09-15 08:48:52 +08:00
Turtle 9f843d618d fix(rule): fix rpc timeout extend the RPC timeout interval 2021-09-15 08:19:22 +08:00
Zaiming (Stone) Shi 80421651d7
Merge pull request #5739 from zmstone/fix-pin-rebar_mix-plugin-version-0.4.0
chore(build): pin rebar_mix plugin version v0.4.0
2021-09-14 18:53:28 +02:00
Zaiming Shi d777ca7baf chore(build): pin rebar_mix plugin version v0.4.0 2021-09-14 16:31:38 +02:00
Turtle 3ad0678892 fix(rule): fix edit rule fail in the cluster 2021-09-14 14:54:24 +08:00
tigercl 4664b85968
Merge pull request #5696 from tigercl/fix/sni
fix(sni): fix bad type of sni
2021-09-09 17:04:17 +08:00
zhouzb ff2d73ad3b chore(upgrade): update upgrade script 2021-09-09 10:29:58 +08:00
zhouzb 1a694814e0 fix(sni): fix bad type of sni 2021-09-09 10:18:40 +08:00
zhanghongtong f92ff4494b fix(auth mnesia api): parsing the http body parameter does not require url decode 2021-09-09 09:42:03 +08:00
k32 1a291d5d97
Merge pull request #5666 from k32/pqueue
feat(mqueue): Interleave messages with different priorities
2021-09-07 16:14:24 +02:00
k32 9b097ac73f chore(mqueue): Remove forgotten debug message 2021-09-07 11:33:16 +02:00
k32 5fc1036cf7 chore(mqueue): Implement live upgrade 2021-09-06 23:15:14 +02:00
k32 ed61999fdf chore(emqx): Bump version 2021-09-06 22:22:48 +02:00
k32 4eacaa29bd feat(mqueue): Interleave messages with different priorities 2021-09-06 22:22:48 +02:00
zhanghongtong aaa7cd0a44 chore(release): update emqx release version 2021-09-06 10:55:02 +08:00
zhanghongtong eeb44086c8 fix(issue): 5565 2021-09-06 10:49:20 +08:00
zhouzb a27b75b98e chore(ehttpc): update tag for ehttpc 2021-09-06 10:41:24 +08:00
William Yang eb88a0b7b6
Merge pull request #5646 from qzhuyan/backport/william/issue/5254
Backport: fix(helm-chart): force headless svc ready while pod is not ready
2021-09-03 13:18:05 +02:00
William Yang e78967cfc3 fix(helm-chart): force headless svc ready while pod is not ready
fixs: #5254

The dist port behind headless svc should to be accessible during emqx
cluster boot.

Endpoints of headless SVC is not in 'ready' state that prevents nodes to talk to
each other, this issue only happens when K8s host node is restarted and
all emqx nodes are deployed on the same host.
2021-09-03 10:10:10 +02:00
Shawn fe343a0407 fix(ekka): kill the process if don't release lock 2021-09-03 10:50:12 +08:00
Turtle e532fff4df chore: Update dashboard version to 4.3.4 2021-08-27 16:28:01 +08:00
Turtle 7bcc67f95d fix(relup): get broker metrics 2021-08-27 16:28:01 +08:00
turtleDeng ea15aa3f9e
fix(data-import): fix import rule fail (#5512) 2021-08-18 09:55:04 +08:00
Turtle 6aa61ea78d fix(test): Increase the delay time 2021-08-13 17:29:51 +08:00
JianBo He a41e6604cf chore(exproto): update appup.src 2021-08-13 17:21:31 +08:00
JianBo He 473e600b53 fix(exproto): fix badarg is_process_alive/1 2021-08-13 17:21:31 +08:00
Turtle dffa81120c chore: emqx_rule_engine:load_providers has been called in emqx_dashboard.appup.src 2021-08-13 12:35:56 +08:00
Turtle f92cfa72d2 fix(webhook): fix the hot upgrade resulted in the loss of webhook messages 2021-08-13 11:05:55 +08:00
Shawn 1245020ec0 fix(force_shutdown): add some comments for the range of max_heap_size 2021-08-13 09:46:49 +08:00
Shawn 844133c7c5 fix(appup): always reload emqx_app 2021-08-13 09:46:49 +08:00
Shawn f9601804e5 chore(emqx): bump the emqx version to 4.3.8 2021-08-13 09:46:49 +08:00
Shawn d2d42ed33e fix(logger): change default value of log.max_depth to 100
The value 20 is too small to inspect some error messages, especially
when it contains stack traces.
2021-08-13 09:46:49 +08:00
Shawn 31a1942b61 fix(force_shutdown): cannot suicide if the process hangs up 2021-08-13 09:46:49 +08:00
Turtle 391eb55324 chore: format error log 2021-08-12 19:35:51 +08:00
Turtle a84b6b74bd chore(relup): update appup file 2021-08-12 19:35:51 +08:00
Turtle e0336e60da fix(webhook-action): fix webhook action path type error 2021-08-12 19:35:51 +08:00
Parham Alvani e5c4277109 fix: Correct Issues 2021-08-12 14:24:12 +08:00
Parham Alvani 45b9f682b2 feat: Expose Internal MQTT Service 2021-08-12 14:24:12 +08:00
William Yang 94712064b5 chore: bump to ekka 0.8.1.1 2021-08-12 11:04:14 +08:00
William Yang f3bd1f1c3a fix(helm-chart): make podManagementPolicy configurable 2021-08-12 11:04:14 +08:00
William Yang ced8693043 fix(helm-chart): start/stop pods in parallel 2021-08-12 11:04:14 +08:00
JianBo He 66f69e7693 feat(exhook): make request_failed_action working 2021-08-12 10:40:24 +08:00
JianBo He 7ec8dc21a6 chore(appup): update appup.src & app.src 2021-08-12 10:40:24 +08:00
JianBo He 1e4ca14476 fix(exhook): set trap_exit flag 2021-08-12 10:40:24 +08:00
JianBo He b3db4d0f7c refactor(exhook): move all manager code into mngr module 2021-08-12 10:40:24 +08:00
JianBo He 22f7b0b8e5 refactor(exhook): add mechanism to reload the failure server 2021-08-12 10:40:24 +08:00
JianBo He 60e830fef7 chore(ex): update grpc to 0.6.3 2021-08-10 17:59:36 +08:00
JianBo He f9a9d4a6f0 chore(exproto): update appup.src 2021-08-10 17:59:36 +08:00
JianBo He 5854bfab57 fix(exproto): retry the grpc request if the stream closed 2021-08-10 17:59:36 +08:00
zhanghongtong bf0036bf81 chore(cts): execute cts every six hours 2021-08-10 15:09:32 +08:00
zhanghongtong 554879c9d7 chore(relup tests): upload emqx package to workflow artifact
Signed-off-by: zhanghongtong <rory-z@outlook.com>
2021-08-09 13:27:00 +08:00
Shawn c09cb64db6
fix(lwm2m): write incorrect integer to device (#5425)
* fix(test): add testcase for write integer values

* fix(lwm2m): write incorrect integer to device

* fix(emqx_lwm2m): refactor the code for getting bits len of a signed int

* chore(emqx_lwm2m): bump version for emqx_lwm2m to 4.3.3
2021-08-06 17:05:25 +08:00
Rory Z ff96250b0b chore(tests): fix conunt error 2021-08-06 15:31:32 +08:00
Turtle 992e094ce9 fix(relup): fix badfun after relup 2021-08-05 14:19:53 +08:00
Turtle efa71d12fe chore(minirest): Upgrade minirest version to 0.3.7 2021-08-05 14:19:53 +08:00
Turtle de96349ddf chore: Specify emqtt-bench version 2021-08-05 14:19:53 +08:00
zhanghongtong 5b671e5c4f chore(CI): print log when deployment helm fail 2021-08-05 10:57:32 +08:00
zhanghongtong ed53b859d9 feat(helm): add externalIPs to chart 2021-08-05 10:57:32 +08:00
Turtle c45de03ac8 chore(version): update emqx version to 4.3.7
Signed-off-by: zhanghongtong <rory-z@outlook.com>
2021-08-05 10:00:03 +08:00
Turtle 87ee94b6f2 chore(version): update emqx version to 4.3.7 2021-08-04 18:44:47 +08:00
tigercl d4ce7f328d
Merge pull request #5395 from tigercl/chore/ehttpc
chore(ehttpc): update tag of ehttpc
2021-08-04 15:58:58 +08:00
Turtle ac0639f6b1 fix(actions): fix republish actions payload is null 2021-08-04 14:39:19 +08:00
Turtle ff4229bb93 fix(relup): fix check relup fail 2021-08-04 11:28:07 +08:00
Turtle 8fb9170df8 fix(rule-engine): fix rule status is not available after the emqx restart 2021-08-03 17:31:43 +08:00
zhouzb 46e58a50d0 chore(ehttpc): update tag of ehttpc 2021-08-03 17:27:35 +08:00
Turtle 35ae097038 fix(relup): upgrade emqx dashboard version 2021-08-03 12:17:35 +08:00
Turtle a7d67eb862 fix(script): fix emqx-ee get dashboard src fail 2021-08-03 12:17:35 +08:00
Zaiming (Stone) Shi 253fa9167c
Merge pull request #5368 from zmstone/fix-boot-log
fix(bin/emqx): fix boot log
2021-07-31 08:50:47 +02:00
Turtle 36fa9f99be test(relup): fix relup fail 2021-07-31 10:52:43 +08:00
zhouzb 65df4fd9ca fix(http pipelining): fix http pipelining for webhook resource 2021-07-30 15:22:18 +08:00
Turtle 49a00c3412 chore(relup): update appup 2021-07-30 14:39:37 +08:00
William Yang 2479c2a80b feat(ssl): mqtt bridge support ssl peer verification 2021-07-30 14:39:37 +08:00
zhanghongtong 07f58c0e9e chore(release): update emqx release version 2021-07-28 16:45:54 +08:00
tigercl 4618eb985a
Merge pull request #5334 from tigercl/chore/dashboard
chore(dashboard): update version for dashboard
2021-07-28 16:43:06 +08:00
zhouzb 2bd12a9a3d chore(dashboard): update version for dashboard 2021-07-28 14:31:24 +08:00
JianBo He d3d019cb89 chore(emqx): update appup.src 2021-07-28 13:54:13 +08:00
JianBo He 07c29e8c55 chore(acl): support ipaddr list 2021-07-28 13:54:13 +08:00
Zaiming Shi dd23ee6b15 fix(emqx_app): stop listeners in application prep_stop callback
Application:stop is call after the root supervisor is stopped,
in our case, prior to this fix, emqx_sup is stopped before
the listeners (hence the emqx_connection processes).

This causes shutdown to emit a lot of error logs
e.g. emqx_broker pool is down, but emqx_connection process is still
trying to call the pool
2021-07-28 08:52:04 +08:00
tigercl fee3462603
fix(http pipelining): support to switch http pipelining (#5279) 2021-07-27 17:32:56 +08:00
zhanghongtong a538979c3b chore(CI): upload emqx log in relup check 2021-07-27 14:57:46 +08:00
zhanghongtong 8809d72ee5 chore(CI): check relup rule action success 2021-07-27 11:31:44 +08:00
zhanghongtong a9a1c71eb4 chore(auth mnesia): hide the password in the api 2021-07-27 10:05:08 +08:00
Zaiming (Stone) Shi b851a7ea7c
Merge pull request #5301 from zmstone/add-notice
chore: Add NOTICE file
2021-07-23 11:44:26 +02:00
Zaiming Shi 7103324426 chore: Add NOTICE file 2021-07-23 10:55:03 +02:00
zhanghongtong f6138e8971 chore(relup tests): check rules matched
Signed-off-by: zhanghongtong <rory-z@outlook.com>
2021-07-22 15:28:04 +08:00
JianBo He b1d190fd3b fix(exproto): fix peername for udp socket 2021-07-21 17:33:21 +08:00
JianBo He fbaf2646f9 chore(exproto): update appup.src 2021-07-21 17:33:21 +08:00
JianBo He 31cbd66f61 fix(exproto): get peername after esockd_wait/1 2021-07-21 17:33:21 +08:00
JianBo He c878c73395
fix(exproto): fix bad socket type
* fix(exproto): fix bad socket type

* chore(exproto): update appup.src
2021-07-20 11:04:00 +08:00
zhanghongtong 5bc33b9b5b chore(CI): add time sleep for relup test 2021-07-16 20:18:33 +08:00
Turtle befd22282f chore(relup): fix check relup fail 2021-07-16 19:01:05 +08:00
Turtle 5edb5351b0 chore(relup): update emqx appup 2021-07-16 19:01:05 +08:00
zhanghongtong 2b1249ba9c fix(auth mnesia): fix add password error by api
Signed-off-by: zhanghongtong <rory-z@outlook.com>
2021-07-16 17:40:51 +08:00
Turtle 8b4b9a119b chore: bind_as_user set default value 2021-07-14 14:14:09 +08:00
JianBo He 46d0cb69dc chore(emqx): update appup.src 2021-07-13 17:53:34 +08:00
JianBo He 240a4b88a5 fix(cm): add a timeout to rpc_call function
An infinite wait will leave the client process waiting for
a return and cause the client to go to a dead state
2021-07-13 17:53:34 +08:00
JianBo He d3d636fc99 chore(mgmt): update appup.src 2021-07-13 17:53:26 +08:00
JianBo He 8cd1fa41b6 fix(mgmt): fix dump aborted by print function crash 2021-07-13 17:53:26 +08:00
zhanghongtong 750cb2d491 chore(CI): update emqx-ci-helper tag 2021-07-09 19:37:45 +08:00
Zaiming Shi 780e403262 fix(conf): change wss.external.max_connections from 16 to 102400 2021-07-09 09:30:57 +08:00
JianBo He 05b16c601b chore(presence): more fields for disconnected event 2021-07-07 18:36:09 +08:00
JianBo He 8935d28ed4 fix(exhook): catch the badarg error 2021-07-01 17:56:10 +08:00
zhanghongtong 0c66fcef00 chore(release): update emqx release version 2021-06-28 11:14:33 +08:00
tigercl 637cd5e804
Merge pull request #5105 from terry-xiaoyu/clean_emqx_shared_subscription2
fix(shared_sub): discard all unexpected msgs
2021-06-28 11:07:10 +08:00
Shawn 5fbf83e7f0 fix(shared_sub): discard all unexpected msgs 2021-06-28 09:37:34 +08:00
Shawn 513cd001ac chore(appup): update the appup for 4.3.5 2021-06-25 20:38:43 +08:00
Shawn 868cd6e57c fix(shared_sub): failed to clean the emqx_shared_subscription tab
A trick that fixes the issue that we demonitored the shared subscriber
too early if it not unsubscribed all of the topics.
2021-06-25 20:38:43 +08:00
zhanghongtong a8aabd5f74 revert: chore(CI): update events that trigger workflows
This reverts commit 002cbb6d8b
2021-06-25 20:13:35 +08:00
zhanghongtong 002cbb6d8b chore(CI): update events that trigger workflows 2021-06-24 17:44:30 +08:00
zhanghongtong e87838f272 docs(docker): fix env name error 2021-06-24 14:46:34 +08:00
zhanghongtong f18b9a92bc chore(CI): delete needless link when build packages 2021-06-23 21:38:30 +08:00
Zaiming Shi 49a78c8ef2 fix(script): exclude non-edge apps in relup dependency 2021-06-23 21:30:59 +08:00
zhanghongtong 8ad42cb827 chore(CI): add DIAGNOSTIC=1 when build windows 2021-06-23 20:27:08 +08:00
Zaiming Shi f17962e79a chore: add more info in error message 2021-06-23 19:40:12 +08:00
Turtle 98c4fff43f chore: fix inject deps notfound emqx_reloader 2021-06-23 19:03:06 +08:00
zhanghongtong bfc6c3aa42 chore(release): update emqx release version 2021-06-23 17:52:42 +08:00
Turtle 1a438125c7 chore(review): review 4.3.4 2021-06-23 17:51:17 +08:00
Turtle 2092bedb12 feat(lwm2m): fix check dialyzer fail 2021-06-23 17:08:44 +08:00
Turtle a6bd1c90d5 fix: Ignore repeatedly receiving connection packet in the wait_will_msg/wait_will_topic/connected state 2021-06-23 14:37:26 +08:00
JianBo He 3ddbdbc6c1
fix(emqx_cm): catch noproc exception from rpc_call (#5048) 2021-06-23 09:45:24 +08:00
zhanghongtong 2c0916ff05 chore(CI): upload rebar3.crashdump file when slim build failure 2021-06-23 09:44:58 +08:00
JianBo He 77a41ea88f
Fix coap uri format (#5059) 2021-06-23 08:50:20 +08:00
Zaiming Shi b92940af29 test(ci): add plugin list status check after relup new vsn install 2021-06-22 08:54:55 +08:00
Zaiming Shi bed45417dc chore(relup): add relup dependency injection 2021-06-22 08:54:55 +08:00
JianBo He 8110ef7a64 chore: upgrade lwm2m_coap to 1.1.4 2021-06-21 12:21:25 +08:00
Turtle ecec9bd2f6 feat(lwm2m): add emqx_lwm2m http API 2021-06-21 12:19:35 +08:00
Shawn 6724e59e7a fix(appup): relup for emqx_rule_registry failed 2021-06-21 12:18:39 +08:00
Shawn 5962c9c83c
feat(rules): remove stats update from rule_engine_registry (#5029) 2021-06-19 17:02:43 +08:00
Shawn c0367fb8dd
Delete resource failed when searching dependent rules (#4996) 2021-06-17 16:45:44 +08:00
tigercl 0ecaa80fb8
fix(query string): support query string in path (#4981) 2021-06-17 16:12:08 +08:00
JianBo He bdd9154001 fix(modules): fix start/stop exhook module failure 2021-06-16 14:40:57 +08:00
JianBo He bbed1b55e0 fix(ws): avoid funcation_clause for un-inited websocket 2021-06-15 11:27:47 +08:00
k32 074c0bd2cc fix(auth_ldap): Handle missing attributes
Fixes: #4953
2021-06-11 18:35:21 +08:00
Zaiming (Stone) Shi 69ef5cbdc3
Merge pull request #4979 from zmstone/chore-config-rpc-connections-default-to-one
chore(conf): change default number of gen_rpc connections to 1
2021-06-11 09:58:50 +02:00
JianBo He 42a6f2aba5
fix(mqttsn): fix proto_name to MQTT-SN instead of MQTT (#4961) 2021-06-11 11:08:24 +08:00
Shawn 0184a1b3e8
fix(minirest): encode response message failed (#4965) 2021-06-11 09:56:11 +08:00
JianBo He 86766ee7f1 fix(lwm2m): base64 decode for opaque value 2021-06-11 09:48:57 +08:00
Turtle 8eebdd5cdb chore: remove lager schema info 2021-06-11 09:48:25 +08:00
Zaiming Shi 1f57968c9b chore(conf): change default number of gen_rpc connections to 1 2021-06-10 19:59:10 +02:00
285 changed files with 12794 additions and 3183 deletions

14
.ci/acl_migration_test/build.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
set -xe
cd "$EMQX_PATH"
rm -rf _build _upgrade_base
mkdir _upgrade_base
pushd _upgrade_base
wget "https://s3-us-west-2.amazonaws.com/packages.emqx/emqx-ce/v${EMQX_BASE}/emqx-ubuntu20.04-${EMQX_BASE}-amd64.zip"
popd
make emqx-zip

View File

@ -0,0 +1,15 @@
#!/bin/bash
set -xe
mkdir -p "$TEST_PATH"
cd "$TEST_PATH"
cp ../"$EMQX_PATH"/_upgrade_base/*.zip ./
unzip ./*.zip
cp ../"$EMQX_PATH"/_packages/emqx/*.zip ./emqx/releases/
git clone --depth 1 https://github.com/terry-xiaoyu/one_more_emqx.git
./one_more_emqx/one_more_emqx.sh emqx2

17
.ci/acl_migration_test/suite.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
set -xe
export EMQX_PATH="$1"
export EMQX_BASE="$2"
export TEST_PATH="emqx_test"
./build.sh
VERSION=$("$EMQX_PATH"/pkg-vsn.sh)
export VERSION
./prepare.sh
./test.sh

121
.ci/acl_migration_test/test.sh Executable file
View File

@ -0,0 +1,121 @@
#!/bin/bash
set -e
EMQX_ENDPOINT="http://localhost:8081/api/v4/acl"
EMQX2_ENDPOINT="http://localhost:8917/api/v4/acl"
function run() {
emqx="$1"
shift
echo "[$emqx]" "$@"
pushd "$TEST_PATH/$emqx"
"$@"
popd
}
function post_rule() {
endpoint="$1"
rule="$2"
echo -n "->($endpoint) "
curl -s -u admin:public -X POST "$endpoint" -d "$rule"
echo
}
function verify_clientid_rule() {
endpoint="$1"
id="$2"
echo -n "<-($endpoint) "
curl -s -u admin:public "$endpoint/clientid/$id" | grep "$id" || (echo "verify rule for client $id failed" && return 1)
}
# Run nodes
run emqx ./bin/emqx start
run emqx2 ./bin/emqx start
run emqx ./bin/emqx_ctl plugins load emqx_auth_mnesia
run emqx2 ./bin/emqx_ctl plugins load emqx_auth_mnesia
run emqx2 ./bin/emqx_ctl cluster join 'emqx@127.0.0.1'
# Add ACL rule to unupgraded EMQX nodes
post_rule "$EMQX_ENDPOINT" '{"clientid": "CLIENT1_A","topic": "t", "action": "pub", "access": "allow"}'
post_rule "$EMQX2_ENDPOINT" '{"clientid": "CLIENT1_B","topic": "t", "action": "pub", "access": "allow"}'
# Upgrade emqx2 node
run emqx2 ./bin/emqx install "$VERSION"
sleep 60
# Verify upgrade blocked
run emqx2 ./bin/emqx eval 'emqx_acl_mnesia_migrator:is_old_table_migrated().' | grep false || (echo "emqx2 shouldn't have migrated" && exit 1)
# Verify old rules on both nodes
verify_clientid_rule "$EMQX_ENDPOINT" 'CLIENT1_A'
verify_clientid_rule "$EMQX2_ENDPOINT" 'CLIENT1_A'
verify_clientid_rule "$EMQX_ENDPOINT" 'CLIENT1_B'
verify_clientid_rule "$EMQX2_ENDPOINT" 'CLIENT1_B'
# Add ACL on OLD and NEW node, verify on all nodes
post_rule "$EMQX_ENDPOINT" '{"clientid": "CLIENT2_A","topic": "t", "action": "pub", "access": "allow"}'
post_rule "$EMQX2_ENDPOINT" '{"clientid": "CLIENT2_B","topic": "t", "action": "pub", "access": "allow"}'
verify_clientid_rule "$EMQX_ENDPOINT" 'CLIENT2_A'
verify_clientid_rule "$EMQX2_ENDPOINT" 'CLIENT2_A'
verify_clientid_rule "$EMQX_ENDPOINT" 'CLIENT2_B'
verify_clientid_rule "$EMQX2_ENDPOINT" 'CLIENT2_B'
# Upgrade emqx node
run emqx ./bin/emqx install "$VERSION"
# Wait for upgrade
sleep 60
# Verify if upgrade occured
run emqx ./bin/emqx eval 'emqx_acl_mnesia_migrator:is_old_table_migrated().' | grep true || (echo "emqx should have migrated" && exit 1)
run emqx2 ./bin/emqx eval 'emqx_acl_mnesia_migrator:is_old_table_migrated().' | grep true || (echo "emqx2 should have migrated" && exit 1)
# Verify rules are kept
verify_clientid_rule "$EMQX_ENDPOINT" 'CLIENT1_A'
verify_clientid_rule "$EMQX2_ENDPOINT" 'CLIENT1_A'
verify_clientid_rule "$EMQX_ENDPOINT" 'CLIENT1_B'
verify_clientid_rule "$EMQX2_ENDPOINT" 'CLIENT1_B'
verify_clientid_rule "$EMQX_ENDPOINT" 'CLIENT2_A'
verify_clientid_rule "$EMQX2_ENDPOINT" 'CLIENT2_A'
verify_clientid_rule "$EMQX_ENDPOINT" 'CLIENT2_B'
verify_clientid_rule "$EMQX2_ENDPOINT" 'CLIENT2_B'
# Add ACL on OLD and NEW node, verify on all nodes
post_rule "$EMQX_ENDPOINT" '{"clientid": "CLIENT3_A","topic": "t", "action": "pub", "access": "allow"}'
post_rule "$EMQX2_ENDPOINT" '{"clientid": "CLIENT3_B","topic": "t", "action": "pub", "access": "allow"}'
verify_clientid_rule "$EMQX_ENDPOINT" 'CLIENT3_A'
verify_clientid_rule "$EMQX2_ENDPOINT" 'CLIENT3_A'
verify_clientid_rule "$EMQX_ENDPOINT" 'CLIENT3_B'
verify_clientid_rule "$EMQX2_ENDPOINT" 'CLIENT3_B'
# Stop nodes
run emqx ./bin/emqx stop
run emqx2 ./bin/emqx stop
echo "Success!"

View File

@ -1,4 +1,4 @@
ARG BUILD_FROM=emqx/build-env:erl23.2.7.2-emqx-2-ubuntu20.04
ARG BUILD_FROM=ghcr.io/emqx/emqx-builder/4.4-2:23.3.4.9-3-ubuntu20.04
FROM ${BUILD_FROM}
ARG EMQX_NAME=emqx

View File

@ -88,6 +88,12 @@ emqx_test(){
;;
"rpm")
packagename=$(basename "${PACKAGE_PATH}/${EMQX_NAME}"-*.rpm)
if [[ "${ARCH}" == "amd64" && $(rpm -E '%{rhel}') == 7 ]] ; then
# EMQX OTP requires openssl11 to have TLS1.3 support
yum install -y openssl11
fi
rpm -ivh "${PACKAGE_PATH}/${packagename}"
if ! rpm -q emqx | grep -q emqx; then
echo "package install error"
@ -129,23 +135,6 @@ running_test(){
pytest -v /paho-mqtt-testing/interoperability/test_client/V5/test_connect.py::test_basic
# shellcheck disable=SC2009 # pgrep does not support Extended Regular Expressions
emqx stop || kill "$(ps -ef | grep -E '\-progname\s.+emqx\s' |awk '{print $2}')"
if [ "$(sed -n '/^ID=/p' /etc/os-release | sed -r 's/ID=(.*)/\1/g' | sed 's/"//g')" = ubuntu ] \
|| [ "$(sed -n '/^ID=/p' /etc/os-release | sed -r 's/ID=(.*)/\1/g' | sed 's/"//g')" = debian ] ;then
service emqx start || ( tail /var/log/emqx/emqx.log.1 && exit 1 )
IDLE_TIME=0
while ! emqx_ctl status | grep -E 'Node\s.*@.*\sis\sstarted'
do
if [ $IDLE_TIME -gt 10 ]
then
echo "emqx service error"
exit 1
fi
sleep 10
IDLE_TIME=$((IDLE_TIME+1))
done
service emqx stop
fi
}
relup_test(){

View File

@ -0,0 +1,99 @@
version: '3.9'
services:
haproxy:
container_name: haproxy
image: haproxy:2.3
depends_on:
- emqx1
- emqx2
volumes:
- ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
- ../../etc/certs:/usr/local/etc/haproxy/certs
ports:
- "18083:18083"
# - "1883:1883"
# - "8883:8883"
# - "8083:8083"
# - "5683:5683/udp"
# - "9999:9999"
# - "8084:8084"
networks:
- emqx_bridge
working_dir: /usr/local/etc/haproxy
command:
- bash
- -c
- |
cat /usr/local/etc/haproxy/certs/cert.pem /usr/local/etc/haproxy/certs/key.pem > /usr/local/etc/haproxy/certs/emqx.pem
haproxy -f /usr/local/etc/haproxy/haproxy.cfg
emqx1:
restart: always
container_name: node1.emqx.io
image: $TARGET:$EMQX_TAG
env_file:
- conf.cluster.env
volumes:
- etc:/opt/emqx/etc
environment:
- "EMQX_HOST=node1.emqx.io"
ports:
- "11881:18083"
# - "1883:1883"
command:
- /bin/sh
- -c
- |
sed -i "s 127.0.0.1 $$(ip route show |grep "link" |awk '{print $$1}') g" /opt/emqx/etc/acl.conf
sed -i '/emqx_telemetry/d' /opt/emqx/data/loaded_plugins
/opt/emqx/bin/emqx foreground
healthcheck:
test: ["CMD", "/opt/emqx/bin/emqx_ctl", "status"]
interval: 5s
timeout: 25s
retries: 5
networks:
emqx_bridge:
aliases:
- node1.emqx.io
emqx2:
restart: always
container_name: node2.emqx.io
image: $TARGET:$EMQX_TAG
env_file:
- conf.cluster.env
volumes:
- etc:/opt/emqx/etc
environment:
- "EMQX_HOST=node2.emqx.io"
ports:
- "11882:18083"
command:
- /bin/sh
- -c
- |
sed -i "s 127.0.0.1 $$(ip route show |grep "link" |awk '{print $$1}') g" /opt/emqx/etc/acl.conf
sed -i '/emqx_telemetry/d' /opt/emqx/data/loaded_plugins
/opt/emqx/bin/emqx foreground
healthcheck:
test: ["CMD", "/opt/emqx/bin/emqx", "ping"]
interval: 5s
timeout: 25s
retries: 5
networks:
emqx_bridge:
aliases:
- node2.emqx.io
volumes:
etc:
networks:
emqx_bridge:
driver: bridge
name: emqx_bridge
ipam:
driver: default
config:
- subnet: 172.100.239.0/24
gateway: 172.100.239.1

View File

@ -27,6 +27,7 @@ services:
haproxy -f /usr/local/etc/haproxy/haproxy.cfg
emqx1:
restart: always
container_name: node1.emqx.io
image: $TARGET:$EMQX_TAG
env_file:
@ -51,6 +52,7 @@ services:
- node1.emqx.io
emqx2:
restart: always
container_name: node2.emqx.io
image: $TARGET:$EMQX_TAG
env_file:

View File

@ -0,0 +1,10 @@
version: '3.9'
services:
web_server:
container_name: Tomcat
build:
context: ./http-service
image: web-server
networks:
- emqx_bridge

View File

@ -3,7 +3,7 @@ version: '3.9'
services:
erlang:
container_name: erlang
image: emqx/build-env:erl23.2.7.2-emqx-2-ubuntu20.04
image: ghcr.io/emqx/emqx-builder/4.4-2:23.3.4.9-3-ubuntu20.04
env_file:
- conf.env
environment:

View File

@ -0,0 +1,15 @@
FROM tomcat:10.0.5
RUN wget https://downloads.apache.org/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.zip \
&& unzip apache-maven-3.6.3-bin.zip \
&& mv apache-maven-3.6.3 /opt/apache-maven-3.6.3/ \
&& ln -s /opt/apache-maven-3.6.3/ /opt/maven
ENV M2_HOME=/opt/maven
ENV M2=$M2_HOME/bin
ENV PATH=$M2:$PATH
COPY ./web-server /code
WORKDIR /code
RUN mvn package -Dmaven.skip.test=true
RUN mv ./target/emqx-web-0.0.1.war /usr/local/tomcat/webapps/emqx-web.war
EXPOSE 8080
CMD ["/usr/local/tomcat/bin/catalina.sh","run"]

View File

@ -0,0 +1,65 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>emqx-web</groupId>
<artifactId>emqx-web</artifactId>
<version>0.0.1</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/reousrce</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,54 @@
package com.emqx.dao;
import java.io.IOException;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import com.emqx.util.EmqxDatabaseUtil;
public class AuthDAO {
public String getUserName(String userName) throws IOException, SQLException {
QueryRunner runner = new QueryRunner(EmqxDatabaseUtil.getDataSource());
String sql = "select password from http_user where username='"+userName+"'";
String password =runner.query(sql, new ScalarHandler<String>());
return password;
}
public String getClient(String clientid) throws IOException, SQLException {
QueryRunner runner = new QueryRunner(EmqxDatabaseUtil.getDataSource());
String sql = "select password from http_user where clientid='"+clientid+"'";
String password =runner.query(sql, new ScalarHandler<String>());
return password;
}
public String getUserAccess(String userName) throws IOException, SQLException {
QueryRunner runner = new QueryRunner(EmqxDatabaseUtil.getDataSource());
String sql = "select access from http_acl where username='"+userName+"'";
String access =runner.query(sql, new ScalarHandler<String>());
return access;
}
public String getUserTopic(String userName) throws IOException, SQLException {
QueryRunner runner = new QueryRunner(EmqxDatabaseUtil.getDataSource());
String sql = "select topic from http_acl where username='"+userName+"'";
String topic =runner.query(sql, new ScalarHandler<String>());
return topic;
}
public String getClientAccess(String clientid) throws IOException, SQLException {
QueryRunner runner = new QueryRunner(EmqxDatabaseUtil.getDataSource());
String sql = "select access from http_acl where clientid='"+clientid+"'";
String access =runner.query(sql, new ScalarHandler<String>());
return access;
}
public String getClientTopic(String clientid) throws IOException, SQLException {
QueryRunner runner = new QueryRunner(EmqxDatabaseUtil.getDataSource());
String sql = "select topic from http_acl where clientid='"+clientid+"'";
String topic =runner.query(sql, new ScalarHandler<String>());
return topic;
}
}

View File

@ -0,0 +1,45 @@
package com.emqx.dao;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Properties;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.apache.commons.dbutils.handlers.columns.StringColumnHandler;
public class DBUtilsTest {
public static void main(String args[]) throws FileNotFoundException, IOException, SQLException {
Properties property = new Properties();//流文件
property.load(DBUtilsTest.class.getClassLoader().getResourceAsStream("database.properties"));
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(property.getProperty("jdbc.driver"));
dataSource.setUrl(property.getProperty("jdbc.url"));
dataSource.setUsername(property.getProperty("jdbc.username"));
dataSource.setPassword(property.getProperty("jdbc.password"));
// 初始化连接数 if(initialSize!=null)
//dataSource.setInitialSize(Integer.parseInt(initialSize));
// 最小空闲连接 if(minIdle!=null)
//dataSource.setMinIdle(Integer.parseInt(minIdle));
// 最大空闲连接 if(maxIdle!=null)
//dataSource.setMaxIdle(Integer.parseInt(maxIdle));
QueryRunner runner = new QueryRunner(dataSource);
String sql="select username from mqtt_user where id=1";
String result = runner.query(sql, new ScalarHandler<String>());
System.out.println(result);
}
}

View File

@ -0,0 +1,103 @@
package com.emqx.servlet;
import java.io.IOException;
import java.sql.SQLException;
import com.emqx.dao.AuthDAO;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class AclServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String clientid = req.getParameter("clientid");
String username = req.getParameter("username");
String access = req.getParameter("access");
String topic = req.getParameter("topic");
//String password = req.getParameter("password");
//step0: password is not null, or not pass.
AuthDAO dao = new AuthDAO();
try {
//step1: check username access&topic
if(username != null) {
String access_1 = dao.getUserAccess(username);
String topic_1 = dao.getUserTopic(username);
if(access.equals(access_1)) {
if(topic.equals(topic_1)) {
resp.setStatus(200);
}
else {
if(clientid != null){
String access_2 = dao.getClientAccess(clientid);
String topic_2 = dao.getClientTopic(clientid);
if(access.equals(access_2)) {
if(topic.equals(topic_2)) {
resp.setStatus(200);
}
else {
resp.setStatus(400);
}
}else {
resp.setStatus(400);
}
}else {
resp.setStatus(400);
}
}
}else {//step2.1: username password is not match, then check clientid password
if(clientid != null){
String access_3 = dao.getClientAccess(clientid);
String topic_3 = dao.getClientTopic(clientid);
if(access.equals(access_3)) {
if(topic.equals(topic_3)) {
resp.setStatus(200);
}
else {
resp.setStatus(400);
}
}else {
resp.setStatus(400);
}
}else {
resp.setStatus(400);
}
}
}else {//step2.2: username is null, then check clientid password
if(clientid != null){
String access_4 = dao.getClientAccess(clientid);
String topic_4 = dao.getClientTopic(clientid);
if(access.equals(access_4)) {
if(topic.equals(topic_4)) {
resp.setStatus(200);
}
else {
resp.setStatus(400);
}
}else {
resp.setStatus(400);
}
}else {
resp.setStatus(400);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,72 @@
package com.emqx.servlet;
import java.io.IOException;
import java.sql.SQLException;
import com.emqx.dao.AuthDAO;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class AuthServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String clientid = req.getParameter("clientid");
String username =req.getParameter("username");
String password = req.getParameter("password");
//step0: password is not null, or not pass.
if(password == null) {
resp.setStatus(400);
return;
}
AuthDAO dao = new AuthDAO();
try {
//step1: check username password
if(username != null) {
String password_d = dao.getUserName(username);
if(password.equals(password_d)) {
resp.setStatus(200);
//200
}else {//step2.1: username password is not match, then check clientid password
if(clientid != null){
String password_c = dao.getClient(clientid);
if(password.equals(password_c)) {
resp.setStatus(200);
}else {
resp.setStatus(400);
}
}else {
resp.setStatus(400);
}
}
}else {//step2.2: username is null, then check clientid password
if(clientid != null){
String password_c = dao.getClient(clientid);
if(password.equals(password_c)) {
resp.setStatus(200);
}else {
resp.setStatus(400);
}
}else {
resp.setStatus(400);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,27 @@
package com.emqx.util;
import java.io.IOException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import com.emqx.dao.DBUtilsTest;
public class EmqxDatabaseUtil {
public static DataSource getDataSource() throws IOException {
Properties property = new Properties();// 流文件
property.load(EmqxDatabaseUtil.class.getClassLoader().getResourceAsStream("database.properties"));
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(property.getProperty("jdbc.driver"));
dataSource.setUrl(property.getProperty("jdbc.url"));
dataSource.setUsername(property.getProperty("jdbc.username"));
dataSource.setPassword(property.getProperty("jdbc.password"));
return dataSource;
}
}

View File

@ -0,0 +1,4 @@
jdbc.driver= com.mysql.jdbc.Driver
jdbc.url= jdbc:mysql://mysql_server:3306/mqtt
jdbc.username= root
jdbc.password= public

View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Class-Path:

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://JAVA.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>emqx-web</display-name>
<servlet>
<servlet-name>Auth</servlet-name>
<servlet-class>com.emqx.servlet.AuthServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>Acl</servlet-name>
<servlet-class>com.emqx.servlet.AclServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Auth</servlet-name>
<url-pattern>/auth</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Acl</servlet-name>
<url-pattern>/acl</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>love</title>
</head>
<body>
It's lucky, jiabanxiang.
</body>
</html>

View File

@ -1,7 +1,7 @@
{erl_opts, [debug_info]}.
{deps,
[
{minirest, {git, "https://github.com/emqx/minirest.git", {tag, "0.3.5"}}}
{minirest, {git, "https://github.com/emqx/minirest.git", {tag, "0.3.6"}}}
]}.
{shell, [

View File

@ -0,0 +1,44 @@
#!/bin/bash
USAGE="$0 profile vsn old_vsn package_path"
EXAMPLE="$0 emqx 4.3.8-b3bb6075 v4.3.2 /home/alice/relup_dubug/downloaded_packages"
if [[ $# -ne 4 ]]; then
echo "$USAGE"
echo "$EXAMPLE"
exit 1
fi
set -ex
PROFILE="$1"
VSN="$2"
OLD_VSN="$3"
PACKAGE_PATH="$4"
FROM_OTP_VSN="${5:-23.3.4.9-3}"
TO_OTP_VSN="${6:-23.3.4.9-3}"
TEMPDIR=$(mktemp -d)
trap '{ rm -rf -- "$TEMPDIR"; }' EXIT
git clone --branch=master "https://github.com/terry-xiaoyu/one_more_emqx.git" "$TEMPDIR/one_more_emqx"
cp -r "$PACKAGE_PATH" "$TEMPDIR/packages"
cp relup.lux "$TEMPDIR/"
cp -r http_server "$TEMPDIR/http_server"
exec docker run \
-v "$TEMPDIR:/relup_test" \
-w "/relup_test" \
-e REBAR_COLOR=none \
-it emqx/relup-test-env:erl23.2.7.2-emqx-3-ubuntu20.04 \
lux \
--progress verbose \
--case_timeout infinity \
--var PROFILE="$PROFILE" \
--var PACKAGE_PATH="/relup_test/packages" \
--var ONE_MORE_EMQX_PATH="/relup_test/one_more_emqx" \
--var VSN="$VSN" \
--var OLD_VSN="$OLD_VSN" \
--var FROM_OTP_VSN="$FROM_OTP_VSN" \
--var TO_OTP_VSN="$TO_OTP_VSN" \
relup.lux

View File

@ -1,15 +1,14 @@
[config var=PROFILE]
[config var=PACKAGE_PATH]
[config var=BENCH_PATH]
[config var=ONE_MORE_EMQX_PATH]
[config var=VSN]
[config var=OLD_VSNS]
[config var=OLD_VSN]
[config var=FROM_OTP_VSN]
[config var=TO_OTP_VSN]
[config shell_cmd=/bin/bash]
[config timeout=600000]
[loop old_vsn $OLD_VSNS]
[shell http_server]
!cd http_server
!rebar3 shell
@ -22,12 +21,11 @@
[shell emqx]
!cd $PACKAGE_PATH
!unzip -q -o $PROFILE-ubuntu20.04-$(echo $old_vsn | sed -r 's/[v|e]//g')-amd64.zip
!unzip -q -o $PROFILE-$(echo $OLD_VSN | sed -r 's/[v|e]//g')-otp${FROM_OTP_VSN}-ubuntu20.04-amd64.zip
?SH-PROMPT
!cd emqx
!sed -i 's|listener.wss.external[ \t]*=.*|listener.wss.external = 8085|g' etc/emqx.conf
!sed -i '/emqx_telemetry/d' data/loaded_plugins
!export EMQX_LOG__LEVEL=debug
!./bin/emqx start
?EMQ X .* is started successfully!
@ -40,10 +38,10 @@
?SH-PROMPT
!cd emqx2
!sed -i '/emqx_telemetry/d' data/loaded_plugins
!export EMQX_LOG__LEVEL=debug
!./bin/emqx start
?EMQ X (.*) is started successfully!
?EMQ X .* is started successfully!
?SH-PROMPT
!./bin/emqx_ctl cluster join emqx@127.0.0.1
@ -63,6 +61,8 @@
!./bin/emqx_ctl rules create 'SELECT * FROM "t/#"' '[{"name":"data_to_webserver", "params": {"$$resource": "resource:691c29ba"}}]'
?created
?SH-PROMPT
!sleep 5
?SH-PROMPT
[shell emqx]
!./bin/emqx_ctl resources list
@ -71,18 +71,18 @@
!./bin/emqx_ctl rules list
?691c29ba
?SH-PROMPT
!./bin/emqx_ctl broker metrics | grep "messages.publish"
???SH-PROMPT
[shell bench]
!cd $BENCH_PATH
!./emqtt_bench pub -c 10 -I 1000 -t t/%i -s 64 -L 300
!emqtt_bench pub -c 10 -I 1000 -t t/%i -s 64 -L 300
???sent
[shell emqx]
!echo "" > log/emqx.log.1
?SH-PROMPT
!cp -f ../$PROFILE-ubuntu20.04-$VSN-amd64.zip releases/
!cp -f ../$PROFILE-$VSN-otp${TO_OTP_VSN}-ubuntu20.04-amd64.zip releases/
!./bin/emqx install $VSN
?Made release permanent: "$VSN"
@ -99,11 +99,15 @@
"""
?SH-PROMPT
!./bin/emqx_ctl plugins list | grep --color=never emqx_management
?Plugin\(emqx_management.*active=true\)
?SH-PROMPT
[shell emqx2]
!echo "" > log/emqx.log.1
?SH-PROMPT
!cp -f ../$PROFILE-ubuntu20.04-$VSN-amd64.zip releases/
!cp -f ../$PROFILE-$VSN-otp${TO_OTP_VSN}-ubuntu20.04-amd64.zip releases/
!./bin/emqx install $VSN
?Made release permanent: "$VSN"
@ -120,9 +124,29 @@
"""
?SH-PROMPT
!./bin/emqx_ctl plugins list | grep --color=never emqx_management
?Plugin\(emqx_management.*active=true\)
?SH-PROMPT
[shell bench]
???publish complete
??SH-PROMPT:
!sleep 30
?SH-PROMPT
[shell emqx]
!./bin/emqx_ctl broker metrics | grep "messages.publish"
???SH-PROMPT
[shell bench]
!curl --user admin:public --silent --show-error http://localhost:8081/api/v4/rules | jq -M --raw-output ".data[0].metrics[] | select(.node==\"emqx@127.0.0.1\").matched"
?300
?SH-PROMPT
!curl --user admin:public --silent --show-error http://localhost:8081/api/v4/rules | jq -M --raw-output ".data[0].actions[0].metrics[] | select(.node==\"emqx@127.0.0.1\").success"
?300
?SH-PROMPT
!curl http://127.0.0.1:8080/counter
???{"data":300,"code":0}
?SH-PROMPT
@ -158,8 +182,6 @@
!halt(3).
?SH-PROMPT:
[endloop]
[cleanup]
!echo ==$$?==
?==0==

View File

@ -1,5 +1,9 @@
name: Cross build packages
concurrency:
group: build-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true
on:
schedule:
- cron: '0 */6 * * *'
@ -11,11 +15,12 @@ on:
jobs:
prepare:
runs-on: ubuntu-20.04
container: emqx/build-env:erl23.2.7.2-emqx-2-ubuntu20.04
# prepare source with any OTP version, no need for a matrix
container: ghcr.io/emqx/emqx-builder/4.4-2:23.3.4.9-3-ubuntu20.04
outputs:
profiles: ${{ steps.set_profile.outputs.profiles}}
old_vsns: ${{ steps.set_profile.outputs.old_vsns}}
profiles: ${{ steps.set_profile.outputs.profiles }}
old_vsns: ${{ steps.set_profile.outputs.old_vsns }}
steps:
- uses: actions/checkout@v2
@ -25,8 +30,8 @@ jobs:
- name: set profile
id: set_profile
shell: bash
working-directory: source
run: |
cd source
vsn="$(./pkg-vsn.sh)"
pre_vsn="$(echo $vsn | grep -oE '^[0-9]+.[0-9]')"
if make emqx-ee --dry-run > /dev/null 2>&1; then
@ -43,7 +48,7 @@ jobs:
run: |
make -C source deps-all
zip -ryq source.zip source/* source/.[^.]*
- name: get_all_deps
- name: get_all_deps_ee
if: endsWith(github.repository, 'enterprise')
run: |
echo "https://ci%40emqx.io:${{ secrets.CI_GIT_TOKEN }}@github.com" > $HOME/.git-credentials
@ -63,8 +68,11 @@ jobs:
if: endsWith(github.repository, 'emqx')
strategy:
fail-fast: false
matrix:
profile: ${{fromJSON(needs.prepare.outputs.profiles)}}
otp:
- 23.2
exclude:
- profile: emqx-edge
@ -76,25 +84,27 @@ jobs:
- name: unzip source code
run: Expand-Archive -Path source.zip -DestinationPath ./
- uses: ilammy/msvc-dev-cmd@v1
- uses: gleam-lang/setup-erlang@v1.1.0
- uses: gleam-lang/setup-erlang@v1.1.2
id: install_erlang
## gleam-lang/setup-erlang does not yet support the installation of otp24 on windows
with:
otp-version: 23.2
otp-version: ${{ matrix.otp }}
- name: build
env:
PYTHON: python
DIAGNOSTIC: 1
working-directory: source
run: |
$env:PATH = "${{ steps.install_erlang.outputs.erlpath }}\bin;$env:PATH"
$version = $( "${{ github.ref }}" -replace "^(.*)/(.*)/" )
if ($version -match "^v[0-9]+\.[0-9]+(\.[0-9]+)?") {
$regex = "[0-9]+\.[0-9]+(-alpha|-beta|-rc)?\.[0-9]+"
$pkg_name = "${{ matrix.profile }}-windows-$([regex]::matches($version, $regex).value).zip"
$pkg_name = "${{ matrix.profile }}-$([regex]::matches($version, $regex).value)-otp${{ matrix.otp }}-windows-amd64.zip"
}
else {
$pkg_name = "${{ matrix.profile }}-windows-$($version -replace '/').zip"
$pkg_name = "${{ matrix.profile }}-$($version -replace '/')-otp${{ matrix.otp }}-windows-amd64.zip"
}
cd source
## We do not build/release bcrypt for windows package
Remove-Item -Recurse -Force -Path _build/default/lib/bcrypt/
if (Test-Path rebar.lock) {
@ -111,8 +121,8 @@ jobs:
Get-FileHash -Path "_packages/${{ matrix.profile }}/$pkg_name" | Format-List | grep 'Hash' | awk '{print $3}' > _packages/${{ matrix.profile }}/$pkg_name.sha256
- name: run emqx
timeout-minutes: 1
working-directory: source
run: |
cd source
./_build/${{ matrix.profile }}/rel/emqx/bin/emqx start
Start-Sleep -s 5
./_build/${{ matrix.profile }}/rel/emqx/bin/emqx stop
@ -125,18 +135,19 @@ jobs:
path: source/_packages/${{ matrix.profile }}/.
mac:
runs-on: macos-10.15
needs: prepare
strategy:
fail-fast: false
matrix:
profile: ${{fromJSON(needs.prepare.outputs.profiles)}}
erl_otp:
- 23.2.7.2-emqx-2
- 23.3.4.9-3
macos:
- macos-11
- macos-10.15
exclude:
- profile: emqx-edge
runs-on: ${{ matrix.macos }}
steps:
- uses: actions/download-artifact@v2
with:
@ -154,7 +165,7 @@ jobs:
id: cache
with:
path: ~/.kerl
key: erl${{ matrix.erl_otp }}-macos10.15
key: erl${{ matrix.erl_otp }}-${{ matrix.macos }}
- name: build erlang
if: steps.cache.outputs.cache-hit != 'true'
timeout-minutes: 60
@ -166,16 +177,19 @@ jobs:
kerl build ${{ matrix.erl_otp }}
kerl install ${{ matrix.erl_otp }} $HOME/.kerl/${{ matrix.erl_otp }}
- name: build
working-directory: source
run: |
. $HOME/.kerl/${{ matrix.erl_otp }}/activate
make -C source ensure-rebar3
sudo cp source/rebar3 /usr/local/bin/rebar3
make -C source ${{ matrix.profile }}-zip
make ensure-rebar3
sudo cp rebar3 /usr/local/bin/rebar3
rm -rf _build/${{ matrix.profile }}/lib
make ${{ matrix.profile }}-zip
- name: test
working-directory: source
run: |
cd source
pkg_name=$(basename _packages/${{ matrix.profile }}/${{ matrix.profile }}-*.zip)
unzip -q _packages/${{ matrix.profile }}/$pkg_name
set -x
pkg_name=$(find _packages/${{ matrix.profile }} -mindepth 1 -maxdepth 1 -iname \*.zip)
unzip -q $pkg_name
gsed -i '/emqx_telemetry/d' ./emqx/data/loaded_plugins
./emqx/bin/emqx start || cat emqx/log/erlang.log.1
ready='no'
@ -194,11 +208,11 @@ jobs:
./emqx/bin/emqx_ctl status
./emqx/bin/emqx stop
rm -rf emqx
openssl dgst -sha256 ./_packages/${{ matrix.profile }}/$pkg_name | awk '{print $2}' > ./_packages/${{ matrix.profile }}/$pkg_name.sha256
openssl dgst -sha256 $pkg_name | awk '{print $2}' > $pkg_name.sha256
- uses: actions/upload-artifact@v1
if: startsWith(github.ref, 'refs/tags/')
with:
name: ${{ matrix.profile }}
name: ${{ matrix.profile }}-${{ matrix.otp }}
path: source/_packages/${{ matrix.profile }}/.
linux:
@ -207,8 +221,11 @@ jobs:
needs: prepare
strategy:
fail-fast: false
matrix:
profile: ${{fromJSON(needs.prepare.outputs.profiles)}}
otp:
- 23.3.4.9-3
arch:
- amd64
- arm64
@ -245,15 +262,11 @@ jobs:
shell: bash
steps:
- name: prepare docker
run: |
mkdir -p $HOME/.docker
echo '{ "experimental": "enabled" }' | tee $HOME/.docker/config.json
echo '{ "experimental": true, "storage-driver": "overlay2", "max-concurrent-downloads": 50, "max-concurrent-uploads": 50}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
docker info
docker buildx create --use --name mybuild
docker run --rm --privileged tonistiigi/binfmt --install all
- uses: docker/setup-buildx-action@v1
- uses: docker/setup-qemu-action@v1
with:
image: tonistiigi/binfmt:latest
platforms: all
- uses: actions/download-artifact@v2
with:
name: source
@ -262,10 +275,12 @@ jobs:
run: unzip -q source.zip
- name: downloads old emqx zip packages
env:
OTP_VSN: ${{ matrix.otp }}
PROFILE: ${{ matrix.profile }}
ARCH: ${{ matrix.arch }}
SYSTEM: ${{ matrix.os }}
OLD_VSNS: ${{ needs.prepare.outputs.old_vsns }}
working-directory: source
run: |
set -e -x -u
broker=$PROFILE
@ -276,76 +291,61 @@ jobs:
export ARCH="arm"
fi
mkdir -p source/_upgrade_base
cd source/_upgrade_base
mkdir -p _upgrade_base
cd _upgrade_base
old_vsns=($(echo $OLD_VSNS | tr ' ' ' '))
for tag in ${old_vsns[@]}; do
if [ ! -z "$(echo $(curl -I -m 10 -o /dev/null -s -w %{http_code} https://s3-us-west-2.amazonaws.com/packages.emqx/$broker/$tag/$PROFILE-$SYSTEM-${tag#[e|v]}-$ARCH.zip) | grep -oE "^[23]+")" ];then
wget --no-verbose https://s3-us-west-2.amazonaws.com/packages.emqx/$broker/$tag/$PROFILE-$SYSTEM-${tag#[e|v]}-$ARCH.zip
wget --no-verbose https://s3-us-west-2.amazonaws.com/packages.emqx/$broker/$tag/$PROFILE-$SYSTEM-${tag#[e|v]}-$ARCH.zip.sha256
echo "$(cat $PROFILE-$SYSTEM-${tag#[e|v]}-$ARCH.zip.sha256) $PROFILE-$SYSTEM-${tag#[e|v]}-$ARCH.zip" | sha256sum -c || exit 1
package_name="${PROFILE}-${tag#[e|v]}-otp${OTP_VSN}-${SYSTEM}-${ARCH}"
if [ ! -z "$(echo $(curl -I -m 10 -o /dev/null -s -w %{http_code} https://s3-us-west-2.amazonaws.com/packages.emqx/$broker/$tag/$package_name.zip) | grep -oE "^[23]+")" ]; then
wget --no-verbose https://s3-us-west-2.amazonaws.com/packages.emqx/$broker/$tag/$package_name.zip
wget --no-verbose https://s3-us-west-2.amazonaws.com/packages.emqx/$broker/$tag/$package_name.zip.sha256
echo "$(cat $package_name.zip.sha256) $package_name.zip" | sha256sum -c || exit 1
fi
done
- name: build emqx packages
env:
ERL_OTP: erl23.2.7.2-emqx-2
OTP: ${{ matrix.otp }}
PROFILE: ${{ matrix.profile }}
ARCH: ${{ matrix.arch }}
SYSTEM: ${{ matrix.os }}
working-directory: source
run: |
set -e -u
cd source
docker buildx build --no-cache \
--platform=linux/$ARCH \
-t cross_build_emqx_for_$SYSTEM \
-f .ci/build_packages/Dockerfile \
--build-arg BUILD_FROM=emqx/build-env:$ERL_OTP-$SYSTEM \
--build-arg EMQX_NAME=$PROFILE \
--output type=tar,dest=/tmp/cross-build-$PROFILE-for-$SYSTEM.tar .
mkdir -p /tmp/packages/$PROFILE
tar -xvf /tmp/cross-build-$PROFILE-for-$SYSTEM.tar --wildcards emqx/_packages/$PROFILE/*
mv emqx/_packages/$PROFILE/* /tmp/packages/$PROFILE/
rm -rf /tmp/cross-build-$PROFILE-for-$SYSTEM.tar
docker rm -f $(docker ps -a -q)
docker volume prune -f
docker run -i --rm \
-v $(pwd):/emqx \
--workdir /emqx \
--platform linux/$ARCH \
ghcr.io/emqx/emqx-builder/4.4-2:$OTP-$SYSTEM \
bash -euc "make $PROFILE-zip || cat rebar3.crashdump; \
make $PROFILE-pkg || cat rebar3.crashdump; \
EMQX_NAME=$PROFILE && .ci/build_packages/tests.sh"
- name: create sha256
working-directory: source
env:
PROFILE: ${{ matrix.profile}}
run: |
if [ -d /tmp/packages/$PROFILE ]; then
cd /tmp/packages/$PROFILE
if [ -d _packages/$PROFILE ]; then
cd _packages/$PROFILE
for var in $(ls emqx-* ); do
bash -c "echo $(sha256sum $var | awk '{print $1}') > $var.sha256"
sudo bash -c "echo $(sha256sum $var | awk '{print $1}') > $var.sha256"
done
cd -
fi
- uses: actions/upload-artifact@v1
if: startsWith(github.ref, 'refs/tags/')
with:
name: ${{ matrix.profile }}
path: /tmp/packages/${{ matrix.profile }}/.
name: ${{ matrix.profile }}-${{ matrix.otp }}
path: source/_packages/${{ matrix.profile }}/.
docker:
runs-on: ubuntu-20.04
needs: prepare
strategy:
fail-fast: false
matrix:
profile: ${{fromJSON(needs.prepare.outputs.profiles)}}
arch:
- [amd64, x86_64]
- [arm64v8, aarch64]
- [arm32v7, arm]
- [i386, i386]
- [s390x, s390x]
exclude:
- profile: emqx-ee
arch: [i386, i386]
- profile: emqx-ee
arch: [s390x, s390x]
otp:
- 23.3.4.9-3
steps:
- uses: actions/download-artifact@v2
@ -354,22 +354,57 @@ jobs:
path: .
- name: unzip source code
run: unzip -q source.zip
- name: build emqx docker image
env:
PROFILE: ${{ matrix.profile }}
ARCH: ${{ matrix.arch[0] }}
QEMU_ARCH: ${{ matrix.arch[1] }}
run: |
sudo docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
cd source
sudo TARGET=emqx/$PROFILE ARCH=$ARCH QEMU_ARCH=$QEMU_ARCH make docker
cd _packages/$PROFILE && for var in $(ls ${PROFILE}-docker-* ); do sudo bash -c "echo $(sha256sum $var | awk '{print $1}') > $var.sha256"; done && cd -
- uses: actions/upload-artifact@v1
if: startsWith(github.ref, 'refs/tags/')
- uses: docker/setup-buildx-action@v1
- uses: docker/setup-qemu-action@v1
with:
name: ${{ matrix.profile }}
path: source/_packages/${{ matrix.profile }}/.
image: tonistiigi/binfmt:latest
platforms: all
- uses: docker/metadata-action@v3
id: meta
with:
images: ${{ github.repository_owner }}/${{ matrix.profile }}
flavor: |
latest=${{ !github.event.release.prerelease }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=ref,event=tag
type=semver,pattern={{version}}
labels:
org.opencontainers.image.otp.version=${{ matrix.otp }}
- uses: docker/login-action@v1
if: github.event_name == 'release'
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- uses: docker/build-push-action@v2
with:
push: ${{ github.event_name == 'release' && !github.event.release.prerelease }}
pull: true
no-cache: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
BUILD_FROM=ghcr.io/emqx/emqx-builder/4.4-2:${{ matrix.otp }}-alpine3.14
RUN_FROM=alpine:3.14
EMQX_NAME=${{ matrix.profile }}
file: source/deploy/docker/Dockerfile
context: source
- uses: aws-actions/configure-aws-credentials@v1
if: github.event_name == 'release' && !github.event.release.prerelease && matrix.profile == 'emqx'
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_DEFAULT_REGION }}
- name: Push image to aws ecr
if: github.event_name == 'release' && !github.event.release.prerelease && matrix.profile == 'emqx'
run: |
version=${GITHUB_REF##*/}
docker pull emqx/emqx:${version#v}
docker tag emqx/emqx:${version#v} public.ecr.aws/emqx/emqx:${version#v}
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws
docker push public.ecr.aws/emqx/emqx:${version#v}
delete-artifact:
runs-on: ubuntu-20.04
@ -389,6 +424,8 @@ jobs:
strategy:
matrix:
profile: ${{fromJSON(needs.prepare.outputs.profiles)}}
otp:
- 23.3.4.9-3
steps:
- uses: actions/checkout@v2
@ -399,7 +436,7 @@ jobs:
echo 'EOF' >> $GITHUB_ENV
- uses: actions/download-artifact@v2
with:
name: ${{ matrix.profile }}
name: ${{ matrix.profile }}-${{ matrix.otp }}
path: ./_packages/${{ matrix.profile }}
- name: install dos2unix
run: sudo apt-get update && sudo apt install -y dos2unix
@ -458,23 +495,30 @@ jobs:
echo ${{ secrets.DOCKER_HUB_TOKEN }} |sudo docker login -u ${{ secrets.DOCKER_HUB_USER }} --password-stdin
sudo TARGET=emqx/${{ matrix.profile }} make docker-push
sudo TARGET=emqx/${{ matrix.profile }} make docker-manifest-list
- name: push docker image to aws ecr
if: github.event_name == 'release'
run: |
set -e -x -u
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws
docker tag emqx/emqx:${version#v} public.ecr.aws/emqx/emqx:${version#v}
docker push public.ecr.aws/emqx/emqx:${version#v}
- name: update repo.emqx.io
if: github.event_name == 'release' && endsWith(github.repository, 'enterprise') && matrix.profile == 'emqx-ee'
if: github.event_name == 'release' && matrix.profile == 'emqx-ee'
run: |
curl --silent --show-error \
-H "Authorization: token ${{ secrets.CI_GIT_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
-X POST \
-d "{\"ref\":\"v1.0.1\",\"inputs\":{\"version\": \"${{ env.version }}\", \"emqx_ee\": \"true\"}}" \
-d "{\"ref\":\"v1.0.4\",\"inputs\":{\"version\": \"${{ env.version }}\", \"emqx_ee\": \"true\"}}" \
"https://api.github.com/repos/emqx/emqx-ci-helper/actions/workflows/update_emqx_repos.yaml/dispatches"
- name: update repo.emqx.io
if: github.event_name == 'release' && endsWith(github.repository, 'emqx') && matrix.profile == 'emqx'
if: github.event_name == 'release' && matrix.profile == 'emqx'
run: |
curl --silent --show-error \
-H "Authorization: token ${{ secrets.CI_GIT_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
-X POST \
-d "{\"ref\":\"v1.0.1\",\"inputs\":{\"version\": \"${{ env.version }}\", \"emqx_ce\": \"true\"}}" \
-d "{\"ref\":\"v1.0.4\",\"inputs\":{\"version\": \"${{ env.version }}\", \"emqx_ce\": \"true\"}}" \
"https://api.github.com/repos/emqx/emqx-ci-helper/actions/workflows/update_emqx_repos.yaml/dispatches"
- name: update homebrew packages
if: github.event_name == 'release' && endsWith(github.repository, 'emqx') && matrix.profile == 'emqx'
@ -484,7 +528,7 @@ jobs:
-H "Authorization: token ${{ secrets.CI_GIT_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
-X POST \
-d "{\"ref\":\"v1.0.1\",\"inputs\":{\"version\": \"${{ env.version }}\"}}" \
-d "{\"ref\":\"v1.0.4\",\"inputs\":{\"version\": \"${{ env.version }}\"}}" \
"https://api.github.com/repos/emqx/emqx-ci-helper/actions/workflows/update_emqx_homebrew.yaml/dispatches"
fi
- uses: geekyeggo/delete-artifact@v1

View File

@ -1,5 +1,10 @@
name: Build slim packages
concurrency:
group: slim-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
tags:
@ -13,14 +18,15 @@ jobs:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
erl_otp:
- erl23.2.7.2-emqx-2
- 23.3.4.9-3
os:
- ubuntu20.04
- centos7
container: emqx/build-env:${{ matrix.erl_otp }}-${{ matrix.os }}
container: ghcr.io/emqx/emqx-builder/4.4-2:${{ matrix.erl_otp }}-${{ matrix.os }}
steps:
- uses: actions/checkout@v1
@ -38,7 +44,12 @@ jobs:
run: make ${EMQX_NAME}-zip
- name: build deb/rpm packages
run: make ${EMQX_NAME}-pkg
- name: pakcages test
- uses: actions/upload-artifact@v1
if: failure()
with:
name: rebar3.crashdump
path: ./rebar3.crashdump
- name: packages test
run: |
export CODE_PATH=$GITHUB_WORKSPACE
.ci/build_packages/tests.sh
@ -48,12 +59,17 @@ jobs:
path: _packages/**/*.zip
mac:
runs-on: macos-10.15
strategy:
fail-fast: false
matrix:
erl_otp:
- 23.2.7.2-emqx-2
- 23.3.4.9-3
macos:
- macos-11
- macos-10.15
runs-on: ${{ matrix.macos }}
steps:
- uses: actions/checkout@v1
@ -77,7 +93,7 @@ jobs:
id: cache
with:
path: ~/.kerl
key: erl${{ matrix.erl_otp }}-macos10.15
key: otp-${{ matrix.erl_otp }}-${{ matrix.macos }}
- name: build erlang
if: steps.cache.outputs.cache-hit != 'true'
timeout-minutes: 60
@ -94,10 +110,15 @@ jobs:
make ensure-rebar3
sudo cp rebar3 /usr/local/bin/rebar3
make ${EMQX_NAME}-zip
- uses: actions/upload-artifact@v1
if: failure()
with:
name: rebar3.crashdump
path: ./rebar3.crashdump
- name: test
run: |
pkg_name=$(basename _packages/${EMQX_NAME}/emqx-*.zip)
unzip -q _packages/${EMQX_NAME}/$pkg_name
pkg_name=$(find _packages/${EMQX_NAME} -mindepth 1 -maxdepth 1 -iname \*.zip)
unzip -q $pkg_name
gsed -i '/emqx_telemetry/d' ./emqx/data/loaded_plugins
./emqx/bin/emqx start || cat emqx/log/erlang.log.1
ready='no'

View File

@ -5,7 +5,7 @@ on: [pull_request]
jobs:
check_deps_integrity:
runs-on: ubuntu-20.04
container: emqx/build-env:erl23.2.7.2-emqx-2-ubuntu20.04
container: ghcr.io/emqx/emqx-builder/4.4-2:23.3.4.9-3-ubuntu20.04
steps:
- uses: actions/checkout@v2

View File

@ -1,4 +1,4 @@
name: Elvis Linter
name: Code style check
on: [pull_request]
@ -7,10 +7,18 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1000
- name: Set git token
if: endsWith(github.repository, 'enterprise')
run: |
echo "https://ci%40emqx.io:${{ secrets.CI_GIT_TOKEN }}@github.com" > $HOME/.git-credentials
git config --global credential.helper store
- run: |
./scripts/elvis-check.sh $GITHUB_BASE_REF
- name: Run elvis check
run: |
set -e
if [ -f EMQX_ENTERPRISE ]; then
./scripts/elvis-check.sh $GITHUB_BASE_REF emqx-enterprise
else
./scripts/elvis-check.sh $GITHUB_BASE_REF emqx
fi

View File

@ -3,7 +3,6 @@ name: Sync to enterprise
on:
push:
branches:
- master
- main-v*
jobs:
@ -23,11 +22,7 @@ jobs:
id: create_pull_request
run: |
set -euo pipefail
if [ "$GITHUB_REF" = "refs/heads/master" ]; then
EE_REF="refs/heads/enterprise"
else
EE_REF="${GITHUB_REF}-enterprise"
fi
R=$(curl --silent --show-error \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.CI_GIT_TOKEN }}" \

View File

@ -0,0 +1,22 @@
name: ACL fix & migration integration tests
on: workflow_dispatch
jobs:
test:
runs-on: ubuntu-20.04
container: ghcr.io/emqx/emqx-builder/4.4-2:23.3.4.9-3-ubuntu20.04
strategy:
fail-fast: true
env:
BASE_VERSION: "4.3.0"
steps:
- uses: actions/checkout@v2
with:
path: emqx
- name: Prepare scripts
run: |
cp ./emqx/.ci/acl_migration_test/*.sh ./
- name: Run tests
run: |
./suite.sh emqx "$BASE_VERSION"

View File

@ -0,0 +1,426 @@
name: Integration Test Suites
on:
push:
tags:
- "v4.*"
pull_request:
branches:
- "main-v4.*"
jobs:
build:
runs-on: ubuntu-latest
outputs:
imgname: ${{ steps.prepare.outputs.imgname}}
version: ${{ steps.prepare.outputs.version}}
steps:
- uses: actions/checkout@v2
- name: prepare
id: prepare
run: |
if [ -f EMQX_ENTERPRISE ]; then
echo "https://ci%40emqx.io:${{ secrets.CI_GIT_TOKEN }}@github.com" > $HOME/.git-credentials
git config --global credential.helper store
echo "${{ secrets.CI_GIT_TOKEN }}" >> scripts/git-token
make deps-emqx-ee
make clean
echo "::set-output name=imgname::emqx-ee"
echo "::set-output name=version::$(./pkg-vsn.sh)"
else
echo "::set-output name=imgname::emqx"
echo "::set-output name=version::$(./pkg-vsn.sh)"
fi
- name: build docker image
env:
OTP_VSN: 23.3.4.9-3
run: |
make ${{ steps.prepare.outputs.imgname }}-docker
docker save emqx/${{ steps.prepare.outputs.imgname }}:${{ steps.prepare.outputs.version }} -o image.tar.gz
- uses: actions/upload-artifact@v2
with:
name: image
path: image.tar.gz
webhook:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
webhook_type:
- webhook_data_bridge
needs: build
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@v2
with:
name: image
path: /tmp
- name: load docker image
run: |
docker load < /tmp/image.tar.gz
- name: docker compose up
timeout-minutes: 5
env:
TARGET: emqx/${{ needs.build.outputs.imgname }}
EMQX_TAG: ${{ needs.build.outputs.version }}
run: |
docker-compose \
-f .ci/docker-compose-file/docker-compose-emqx-cluster.yaml \
up -d --build
- uses: actions/checkout@v2
with:
repository: emqx/emqx-svt-web-server
ref: web-server-1.0
path: emqx-svt-web-server
- uses: actions/download-artifact@v2
- name: run webserver in docker
run: |
cd ./emqx-svt-web-server/svtserver
mvn clean package
cd target
docker run --name webserver --network emqx_bridge -d -v $(pwd)/svtserver-0.0.1.jar:/webserver/svtserver-0.0.1.jar --workdir /webserver openjdk:8-jdk bash \
-c "java -jar svtserver-0.0.1.jar"
- name: wait docker compose up
timeout-minutes: 5
run: |
while [ "$(docker inspect -f '{{ .State.Health.Status}}' node1.emqx.io)" != "healthy" ] || [ "$(docker inspect -f '{{ .State.Health.Status}}' node2.emqx.io)" != "healthy" ]; do
echo "['$(date -u +"%y-%m-%dt%h:%m:%sz")']:waiting emqx";
sleep 5;
done
docker ps -a
echo HAPROXY_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' haproxy) >> $GITHUB_ENV
echo WEB_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' webserver) >> $GITHUB_ENV
- uses: actions/checkout@v2
with:
repository: emqx/emqx-fvt
ref: v1.4.0
path: scripts
- uses: actions/setup-java@v1
with:
java-version: '8.0.282' # The JDK version to make available on the path.
java-package: jdk # (jre, jdk, or jdk+fx) - defaults to jdk
architecture: x64 # (x64 or x86) - defaults to x64
- name: install jmeter
timeout-minutes: 10
env:
JMETER_VERSION: 5.3
run: |
wget --no-verbose --no-check-certificate -O /tmp/apache-jmeter.tgz https://downloads.apache.org/jmeter/binaries/apache-jmeter-$JMETER_VERSION.tgz
cd /tmp && tar -xvf apache-jmeter.tgz
echo "jmeter.save.saveservice.output_format=xml" >> /tmp/apache-jmeter-$JMETER_VERSION/user.properties
echo "jmeter.save.saveservice.response_data.on_error=true" >> /tmp/apache-jmeter-$JMETER_VERSION/user.properties
wget --no-verbose -O /tmp/apache-jmeter-$JMETER_VERSION/lib/ext/mqtt-xmeter-2.0.2-jar-with-dependencies.jar https://raw.githubusercontent.com/xmeter-net/mqtt-jmeter/master/Download/v2.0.2/mqtt-xmeter-2.0.2-jar-with-dependencies.jar
ln -s /tmp/apache-jmeter-$JMETER_VERSION /opt/jmeter
- name: run jmeter
run: |
/opt/jmeter/bin/jmeter.sh \
-Jjmeter.save.saveservice.output_format=xml -n \
-t scripts/.ci/automate-test-suite/${{ matrix.webhook_type }}.jmx \
-Demqx_ip=$HAPROXY_IP \
-Dweb_ip=$WEB_IP \
-l jmeter_logs/webhook_${{ matrix.webhook_type }}.jtl \
-j jmeter_logs/logs/webhook_${{ matrix.webhook_type }}.log
- name: check logs
run: |
if cat jmeter_logs/webhook_${{ matrix.webhook_type }}.jtl | grep -e '<failure>true</failure>' > /dev/null 2>&1; then
echo "check logs filed"
exit 1
fi
- uses: actions/upload-artifact@v1
if: always()
with:
name: jmeter_logs
path: ./jmeter_logs
mysql:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
mysql_tag:
- 5.7
- 8
mysql_type:
- mysql_auth_acl
needs: build
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@v2
with:
name: image
path: /tmp
- name: load docker image
run: |
docker load < /tmp/image.tar.gz
- name: docker compose up
timeout-minutes: 5
env:
TARGET: emqx/${{ needs.build.outputs.imgname }}
EMQX_TAG: ${{ needs.build.outputs.version }}
MYSQL_TAG: ${{ matrix.mysql_tag }}
run: |
docker-compose \
-f .ci/docker-compose-file/docker-compose-emqx-cluster.yaml \
-f .ci/docker-compose-file/docker-compose-mysql-tls.yaml \
up -d --build
- name: wait docker compose up
timeout-minutes: 5
run: |
while [ "$(docker inspect -f '{{ .State.Health.Status}}' node1.emqx.io)" != "healthy" ] || [ "$(docker inspect -f '{{ .State.Health.Status}}' node2.emqx.io)" != "healthy" ]; do
echo "['$(date -u +"%y-%m-%dt%h:%m:%sz")']:waiting emqx";
sleep 5;
done
while [ $(docker ps -a --filter name=client --filter exited=0 | wc -l) \
!= $(docker ps -a --filter name=client | wc -l) ]; do
sleep 1
done
docker ps -a
echo HAPROXY_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' haproxy) >> $GITHUB_ENV
echo MYSQL_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mysql) >> $GITHUB_ENV
- uses: actions/checkout@v2
with:
repository: emqx/emqx-fvt
ref: v1.4.0
path: scripts
- uses: actions/setup-java@v1
with:
java-version: '8.0.282' # The JDK version to make available on the path.
java-package: jdk # (jre, jdk, or jdk+fx) - defaults to jdk
architecture: x64 # (x64 or x86) - defaults to x64
- name: install jmeter
timeout-minutes: 10
env:
JMETER_VERSION: 5.3
run: |
wget --no-verbose --no-check-certificate -O /tmp/apache-jmeter.tgz https://downloads.apache.org/jmeter/binaries/apache-jmeter-$JMETER_VERSION.tgz
cd /tmp && tar -xvf apache-jmeter.tgz
echo "jmeter.save.saveservice.output_format=xml" >> /tmp/apache-jmeter-$JMETER_VERSION/user.properties
echo "jmeter.save.saveservice.response_data.on_error=true" >> /tmp/apache-jmeter-$JMETER_VERSION/user.properties
wget --no-verbose -O /tmp/apache-jmeter-$JMETER_VERSION/lib/ext/mqtt-xmeter-2.0.2-jar-with-dependencies.jar https://raw.githubusercontent.com/xmeter-net/mqtt-jmeter/master/Download/v2.0.2/mqtt-xmeter-2.0.2-jar-with-dependencies.jar
ln -s /tmp/apache-jmeter-$JMETER_VERSION /opt/jmeter
- name: install jmeter plugin
run: |
wget --no-verbose -O "/opt/jmeter/lib/mysql-connector-java-8.0.16.jar" https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.16/mysql-connector-java-8.0.16.jar
- name: run jmeter
run: |
/opt/jmeter/bin/jmeter.sh \
-Jjmeter.save.saveservice.output_format=xml -n \
-t scripts/.ci/automate-test-suite/${{ matrix.mysql_type }}.jmx \
-Droute="apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data" \
-Dmysql_ip=$MYSQL_IP \
-Demqx_ip=$HAPROXY_IP \
-Ddbname="mqtt" \
-Dmysql_user="ssluser" \
-Ddb_user="root" \
-Dmysql_pwd="public" \
-Dconfig_path="/tmp/etc" \
-Ddocker_path=".ci/docker-compose-file" \
-l jmeter_logs/${{ matrix.mysql_type }}_${{ matrix.mysql_tag }}.jtl \
-j jmeter_logs/logs/${{ matrix.mysql_type }}_${{ matrix.mysql_tag }}.log
- name: check logs
run: |
if cat jmeter_logs/${{ matrix.mysql_type }}_${{ matrix.mysql_tag }}.jtl | grep -e '<failure>true</failure>' > /dev/null 2>&1; then
echo "check logs filed"
exit 1
fi
- uses: actions/upload-artifact@v1
if: always()
with:
name: jmeter_logs
path: ./jmeter_logs
postgresql:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
pgsql_type:
- pgsql_auth_acl
pgsql_tag:
- 9
- 10
- 11
- 12
- 13
needs: build
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@v2
with:
name: image
path: /tmp
- name: load docker image
run: |
docker load < /tmp/image.tar.gz
- name: docker compose up
timeout-minutes: 5
env:
TARGET: emqx/${{ needs.build.outputs.imgname }}
EMQX_TAG: ${{ needs.build.outputs.version }}
PGSQL_TAG: ${{ matrix.pgsql_tag }}
run: |
docker-compose \
-f .ci/docker-compose-file/docker-compose-emqx-broker-cluster.yaml \
-f .ci/docker-compose-file/docker-compose-pgsql-tls.yaml \
up -d --build
- name: wait docker compose up
timeout-minutes: 5
run: |
while [ "$(docker inspect -f '{{ .State.Health.Status}}' node1.emqx.io)" != "healthy" ] || [ "$(docker inspect -f '{{ .State.Health.Status}}' node2.emqx.io)" != "healthy" ]; do
echo "['$(date -u +"%y-%m-%dt%h:%m:%sz")']:waiting emqx";
sleep 5;
done
docker ps -a
echo HAPROXY_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' haproxy) >> $GITHUB_ENV
echo PGSQL_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' pgsql) >> $GITHUB_ENV
echo CONFIG_PATH=$(docker inspect -f '{{ range .Mounts }}{{ if eq .Name "docker-compose-file_etc" }}{{ .Source }}{{ end }}{{ end }}' node1.emqx.io) >> $GITHUB_ENV
- uses: actions/checkout@v2
with:
repository: emqx/emqx-fvt
ref: v1.4.0
path: scripts
- uses: actions/setup-java@v1
with:
java-version: '8.0.282' # The JDK version to make available on the path.
java-package: jdk # (jre, jdk, or jdk+fx) - defaults to jdk
architecture: x64 # (x64 or x86) - defaults to x64
- name: install jmeter
timeout-minutes: 10
env:
JMETER_VERSION: 5.3
run: |
wget --no-verbose --no-check-certificate -O /tmp/apache-jmeter.tgz https://downloads.apache.org/jmeter/binaries/apache-jmeter-$JMETER_VERSION.tgz
cd /tmp && tar -xvf apache-jmeter.tgz
echo "jmeter.save.saveservice.output_format=xml" >> /tmp/apache-jmeter-$JMETER_VERSION/user.properties
echo "jmeter.save.saveservice.response_data.on_error=true" >> /tmp/apache-jmeter-$JMETER_VERSION/user.properties
wget --no-verbose -O /tmp/apache-jmeter-$JMETER_VERSION/lib/ext/mqtt-xmeter-2.0.2-jar-with-dependencies.jar https://raw.githubusercontent.com/xmeter-net/mqtt-jmeter/master/Download/v2.0.2/mqtt-xmeter-2.0.2-jar-with-dependencies.jar
ln -s /tmp/apache-jmeter-$JMETER_VERSION /opt/jmeter
- name: install jmeter plugin
run: |
wget --no-verbose -O "/opt/jmeter/lib/postgresql-42.2.18.jar" https://repo1.maven.org/maven2/org/postgresql/postgresql/42.2.18/postgresql-42.2.18.jar
- name: run jmeter
run: |
sudo /opt/jmeter/bin/jmeter.sh \
-Jjmeter.save.saveservice.output_format=xml -n \
-t scripts/.ci/automate-test-suite/${{ matrix.pgsql_type }}.jmx \
-Droute="apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data" \
-Dca_name="ca.pem" \
-Dkey_name="client-key.pem" \
-Dcert_name="client-cert.pem" \
-Ddb_ip=$PGSQL_IP \
-Dpgsql_ip=$PGSQL_IP \
-Demqx_ip=$HAPROXY_IP \
-Dpgsql_user="root" \
-Dpgsql_pwd="public" \
-Ddbname="mqtt" \
-Dpgsql_db="mqtt" \
-Dport="5432" \
-Dconfig_path=$CONFIG_PATH \
-Ddocker_path=".ci/docker-compose-file" \
-l jmeter_logs/${{ matrix.pgsql_type }}_${{ matrix.pgsql_tag }}.jtl \
-j jmeter_logs/logs/${{ matrix.pgsql_type }}_${{ matrix.pgsql_tag }}.log
- name: check logs
run: |
if cat jmeter_logs/${{ matrix.pgsql_type }}_${{ matrix.pgsql_tag }}.jtl | grep -e '<failure>true</failure>' > /dev/null 2>&1; then
echo "check logs filed"
exit 1
fi
- uses: actions/upload-artifact@v1
if: always()
with:
name: jmeter_logs
path: ./jmeter_logs
http:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@v2
with:
name: image
path: /tmp
- name: load docker image
run: |
docker load < /tmp/image.tar.gz
- name: docker compose up
timeout-minutes: 5
env:
TARGET: emqx/${{ needs.build.outputs.imgname }}
EMQX_TAG: ${{ needs.build.outputs.version }}
MYSQL_TAG: 8
run: |
docker-compose \
-f .ci/docker-compose-file/docker-compose-emqx-broker-cluster.yaml \
-f .ci/docker-compose-file/docker-compose-mysql-tcp.yaml \
-f .ci/docker-compose-file/docker-compose-enterprise-tomcat-tcp.yaml \
up -d --build
- name: wait docker compose up
timeout-minutes: 5
run: |
while [ "$(docker inspect -f '{{ .State.Health.Status}}' node1.emqx.io)" != "healthy" ] || [ "$(docker inspect -f '{{ .State.Health.Status}}' node2.emqx.io)" != "healthy" ]; do
echo "['$(date -u +"%y-%m-%dt%h:%m:%sz")']:waiting emqx";
sleep 5;
done
docker ps -a
echo HAPROXY_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' haproxy) >> $GITHUB_ENV
echo HTTP_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' Tomcat) >> $GITHUB_ENV
echo MYSQL_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mysql) >> $GITHUB_ENV
echo CONFIG_PATH=$(docker inspect -f '{{ range .Mounts }}{{ if eq .Name "docker-compose-file_etc" }}{{ .Source }}{{ end }}{{ end }}' node1.emqx.io) >> $GITHUB_ENV
- uses: actions/checkout@v2
with:
repository: emqx/emqx-fvt
ref: v1.4.0
path: scripts
- uses: actions/setup-java@v1
with:
java-version: '8.0.282' # The JDK version to make available on the path.
java-package: jdk # (jre, jdk, or jdk+fx) - defaults to jdk
architecture: x64 # (x64 or x86) - defaults to x64
- name: install jmeter
timeout-minutes: 10
env:
JMETER_VERSION: 5.3
run: |
wget --no-verbose --no-check-certificate -O /tmp/apache-jmeter.tgz https://downloads.apache.org/jmeter/binaries/apache-jmeter-$JMETER_VERSION.tgz
cd /tmp && tar -xvf apache-jmeter.tgz
echo "jmeter.save.saveservice.output_format=xml" >> /tmp/apache-jmeter-$JMETER_VERSION/user.properties
echo "jmeter.save.saveservice.response_data.on_error=true" >> /tmp/apache-jmeter-$JMETER_VERSION/user.properties
wget --no-verbose -O /tmp/apache-jmeter-$JMETER_VERSION/lib/ext/mqtt-xmeter-2.0.2-jar-with-dependencies.jar https://raw.githubusercontent.com/xmeter-net/mqtt-jmeter/master/Download/v2.0.2/mqtt-xmeter-2.0.2-jar-with-dependencies.jar
ln -s /tmp/apache-jmeter-$JMETER_VERSION /opt/jmeter
- name: install jmeter plugin
run: |
wget --no-verbose -O "/opt/jmeter/lib/mysql-connector-java-8.0.16.jar" https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.16/mysql-connector-java-8.0.16.jar
- name: run jmeter
run: |
sudo /opt/jmeter/bin/jmeter.sh \
-Jjmeter.save.saveservice.output_format=xml -n \
-t scripts/.ci/automate-test-suite/http_auth_acl.jmx \
-Dmysql_ip=$MYSQL_IP \
-Demqx_ip=$HAPROXY_IP \
-Dweb_server_ip=$HTTP_IP \
-Dconfig_path=$CONFIG_PATH \
-Ddocker_path=".ci/docker-compose-file" \
-l jmeter_logs/http_auth_acl.jtl \
-j jmeter_logs/logs/http_auth_acl.log
- name: check logs
run: |
if cat jmeter_logs/http_auth_acl.jtl | grep -e '<failure>true</failure>' > /dev/null 2>&1; then
echo "check logs filed"
sudo cat /var/lib/docker/volumes/docker-compose-file_etc/_data/emqx.conf
exit 1
fi
- uses: actions/upload-artifact@v1
if: always()
with:
name: jmeter_logs
path: ./jmeter_logs

View File

@ -1,11 +1,12 @@
name: Compatibility Test Suite
on:
schedule:
- cron: '0 */6 * * *'
push:
tags:
- v*
- e*
pull_request:
jobs:
ldap:

View File

@ -13,10 +13,6 @@ jobs:
steps:
- uses: actions/checkout@v1
- uses: gleam-lang/setup-erlang@v1.1.2
id: install_erlang
with:
otp-version: 23.2
- name: prepare
run: |
if make emqx-ee --dry-run > /dev/null 2>&1; then
@ -24,14 +20,19 @@ jobs:
git config --global credential.helper store
echo "${{ secrets.CI_GIT_TOKEN }}" >> scripts/git-token
make deps-emqx-ee
make clean
echo "TARGET=emqx/emqx-ee" >> $GITHUB_ENV
echo "PROFILE=emqx-ee" >> $GITHUB_ENV
echo "EMQX_TAG=$(./pkg-vsn.sh)" >> $GITHUB_ENV
else
echo "TARGET=emqx/emqx" >> $GITHUB_ENV
echo "PROFILE=emqx" >> $GITHUB_ENV
echo "EMQX_TAG=$(./pkg-vsn.sh)" >> $GITHUB_ENV
fi
- name: make emqx image
run: make docker
env:
OTP_VSN: 23.3.4.9-3
run: make ${PROFILE}-docker
- name: run emqx
timeout-minutes: 5
run: |
@ -64,13 +65,15 @@ jobs:
helm_test:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
discovery:
- k8s
- dns
steps:
- uses: actions/checkout@v1
- uses: gleam-lang/setup-erlang@v1.1.2
id: install_erlang
with:
otp-version: 23.2
- name: prepare
run: |
if make emqx-ee --dry-run > /dev/null 2>&1; then
@ -78,12 +81,19 @@ jobs:
git config --global credential.helper store
echo "${{ secrets.CI_GIT_TOKEN }}" >> scripts/git-token
make deps-emqx-ee
make clean
echo "TARGET=emqx/emqx-ee" >> $GITHUB_ENV
echo "PROFILE=emqx-ee" >> $GITHUB_ENV
echo "EMQX_TAG=$(./pkg-vsn.sh)" >> $GITHUB_ENV
else
echo "TARGET=emqx/emqx" >> $GITHUB_ENV
echo "PROFILE=emqx" >> $GITHUB_ENV
echo "EMQX_TAG=$(./pkg-vsn.sh)" >> $GITHUB_ENV
fi
- name: make emqx image
run: make docker
env:
OTP_VSN: 23.3.4.9-3
run: make ${PROFILE}-docker
- name: install k3s
env:
KUBECONFIG: "/etc/rancher/k3s/k3s.yaml"
@ -100,18 +110,18 @@ jobs:
sudo chmod 700 get_helm.sh
sudo ./get_helm.sh
helm version
- name: run emqx on chart
env:
KUBECONFIG: "/etc/rancher/k3s/k3s.yaml"
timeout-minutes: 5
- name: setup emqx chart
run: |
version=$(./pkg-vsn.sh)
sudo docker save ${TARGET}:$version -o emqx.tar.gz
sudo docker save ${TARGET}:${EMQX_TAG} -o emqx.tar.gz
sudo k3s ctr image import emqx.tar.gz
sed -i -r "s/^appVersion: .*$/appVersion: \"${version}\"/g" deploy/charts/emqx/Chart.yaml
sed -i -r "s/^appVersion: .*$/appVersion: \"${EMQX_TAG}\"/g" deploy/charts/emqx/Chart.yaml
sed -i '/emqx_telemetry/d' deploy/charts/emqx/values.yaml
- name: run emqx on chart
if: matrix.discovery == 'k8s'
env:
KUBECONFIG: "/etc/rancher/k3s/k3s.yaml"
run: |
helm install emqx \
--set image.repository=${TARGET} \
--set image.pullPolicy=Never \
@ -121,7 +131,29 @@ jobs:
--set emqxConfig.EMQX_MQTT__MAX_TOPIC_ALIAS=10 \
deploy/charts/emqx \
--debug
- name: run emqx on chart
if: matrix.discovery == 'dns'
env:
KUBECONFIG: "/etc/rancher/k3s/k3s.yaml"
run: |
helm install emqx \
--set emqxConfig.EMQX_CLUSTER__DISCOVERY="dns" \
--set emqxConfig.EMQX_CLUSTER__DNS__NAME="emqx-headless.default.svc.cluster.local" \
--set emqxConfig.EMQX_CLUSTER__DNS__APP="emqx" \
--set emqxConfig.EMQX_CLUSTER__DNS__TYPE="srv" \
--set image.repository=${TARGET} \
--set image.pullPolicy=Never \
--set emqxAclConfig="" \
--set image.pullPolicy=Never \
--set emqxConfig.EMQX_ZONE__EXTERNAL__RETRY_INTERVAL=2s \
--set emqxConfig.EMQX_MQTT__MAX_TOPIC_ALIAS=10 \
deploy/charts/emqx \
--debug
- name: waiting emqx started
env:
KUBECONFIG: "/etc/rancher/k3s/k3s.yaml"
timeout-minutes: 5
run: |
while [ "$(kubectl get StatefulSet -l app.kubernetes.io/name=emqx -o jsonpath='{.items[0].status.replicas}')" \
!= "$(kubectl get StatefulSet -l app.kubernetes.io/name=emqx -o jsonpath='{.items[0].status.readyReplicas}')" ]; do
echo "==============================";
@ -130,11 +162,39 @@ jobs:
echo "waiting emqx started";
sleep 10;
done
- name: get pods log
- name: Check ${{ matrix.kind[0]}} cluster
env:
KUBECONFIG: "/etc/rancher/k3s/k3s.yaml"
timeout-minutes: 10
run: |
while
nodes="$(kubectl exec -i emqx-0 -- curl --silent --basic -u admin:public -X GET http://localhost:8081/api/v4/brokers | jq '.data|length')";
[ "$nodes" != "3" ];
do
echo "waiting emqx cluster scale"
sleep 1
done
- name: get emqx-0 pods log
if: failure()
env:
KUBECONFIG: "/etc/rancher/k3s/k3s.yaml"
run: kubectl describe pods emqx-0
run: |
kubectl describe pods emqx-0
kubectl logs emqx-0
- name: get emqx-1 pods log
if: failure()
env:
KUBECONFIG: "/etc/rancher/k3s/k3s.yaml"
run: |
kubectl describe pods emqx-1
kubectl logs emqx-1
- name: get emqx-2 pods log
if: failure()
env:
KUBECONFIG: "/etc/rancher/k3s/k3s.yaml"
run: |
kubectl describe pods emqx-2
kubectl logs emqx-2
- uses: actions/checkout@v2
with:
repository: emqx/paho.mqtt.testing
@ -162,76 +222,78 @@ jobs:
fi
exit $RESULT
relup_test:
relup_test_plan:
runs-on: ubuntu-20.04
container: emqx/build-env:erl23.2.7.2-emqx-2-ubuntu20.04
container: ghcr.io/emqx/emqx-builder/4.4-2:23.3.4.9-3-ubuntu20.04
outputs:
profile: ${{ steps.profile-and-versions.outputs.profile }}
vsn: ${{ steps.profile-and-versions.outputs.vsn }}
old_vsns: ${{ steps.profile-and-versions.outputs.old_vsns }}
broker: ${{ steps.profile-and-versions.outputs.broker }}
matrix: ${{ steps.generate-matrix.outputs.matrix }}
defaults:
run:
shell: bash
steps:
- uses: actions/setup-python@v2
with:
python-version: '3.8'
architecture: 'x64'
- uses: actions/checkout@v2
name: Checkout
with:
repository: emqx/paho.mqtt.testing
ref: develop-4.0
path: paho.mqtt.testing
- uses: actions/checkout@v2
with:
repository: terry-xiaoyu/one_more_emqx
ref: master
path: one_more_emqx
- uses: actions/checkout@v2
with:
repository: emqx/emqtt-bench
ref: master
path: emqtt-bench
- uses: actions/checkout@v2
with:
repository: hawk/lux
ref: lux-2.6
path: lux
- uses: actions/checkout@v2
with:
repository: ${{ github.repository }}
path: emqx
fetch-depth: 0
- name: prepare
- name: Get profile and version list
id: profile-and-versions
run: |
if make -C emqx emqx-ee --dry-run > /dev/null 2>&1; then
cd emqx
vsn="$(./pkg-vsn.sh)"
pre_vsn="$(echo $vsn | grep -oE '^[0-9]+.[0-9]')"
if make emqx-ee --dry-run > /dev/null 2>&1; then
profile="emqx-ee"
old_vsns="$(git tag -l "e$pre_vsn.[0-9]" | xargs echo -n | sed "s/e$vsn//")"
broker="emqx-ee"
else
profile="emqx"
old_vsns="$(git tag -l "v$pre_vsn.[0-9]" | xargs echo -n | sed "s/v$vsn//")"
broker="emqx-ce"
fi
echo "OLD_VSNS=$old_vsns" >> $GITHUB_ENV
echo "::set-output name=vsn::$vsn"
echo "::set-output name=profile::$profile"
echo "::set-output name=broker::$broker"
echo "::set-output name=old_vsns::$old_vsns"
- name: Generate matrix
id: generate-matrix
run: |
matrix=$(echo -n "$OLD_VSNS" | sed 's/ $//g' | jq -R -s -c 'split(" ")')
echo "::set-output name=matrix::$matrix"
relup_test_build:
needs: relup_test_plan
runs-on: ubuntu-20.04
container: ghcr.io/emqx/emqx-builder/4.4-2:23.3.4.9-3-ubuntu20.04
defaults:
run:
shell: bash
env:
OLD_VSNS: "${{ needs.relup_test_plan.outputs.old_vsns }}"
PROFILE: "${{ needs.relup_test_plan.outputs.profile }}"
BROKER: "${{ needs.relup_test_plan.outputs.broker }}"
steps:
- uses: actions/checkout@v2
name: Checkout
with:
path: emqx
- name: Prepare credentials
run: |
if [ "$PROFILE" = "emqx-ee" ]; then
echo "https://ci%40emqx.io:${{ secrets.CI_GIT_TOKEN }}@github.com" > $HOME/.git-credentials
git config --global credential.helper store
echo "${{ secrets.CI_GIT_TOKEN }}" >> emqx/scripts/git-token
echo "PROFILE=emqx-ee" >> $GITHUB_ENV
else
echo "PROFILE=emqx" >> $GITHUB_ENV
fi
- name: get version
run: |
set -e -x -u
cd emqx
if [ $PROFILE = "emqx" ];then
broker="emqx-ce"
edition='opensource'
else
broker="emqx-ee"
edition='enterprise'
fi
echo "BROKER=$broker" >> $GITHUB_ENV
vsn="$(./pkg-vsn.sh)"
echo "VSN=$vsn" >> $GITHUB_ENV
pre_vsn="$(echo $vsn | grep -oE '^[0-9]+.[0-9]')"
if [ $PROFILE = "emqx" ]; then
old_vsns="$(git tag -l "v$pre_vsn.[0-9]" | xargs echo -n | sed "s/v$vsn//")"
else
old_vsns="$(git tag -l "e$pre_vsn.[0-9]" | xargs echo -n | sed "s/e$vsn//")"
fi
echo "OLD_VSNS=$old_vsns" >> $GITHUB_ENV
- name: download emqx
- name: Download bases
run: |
set -e -x -u
mkdir -p emqx/_upgrade_base
@ -240,39 +302,74 @@ jobs:
for old_vsn in ${old_vsns[@]}; do
wget --no-verbose https://s3-us-west-2.amazonaws.com/packages.emqx/$BROKER/$old_vsn/$PROFILE-ubuntu20.04-${old_vsn#[e|v]}-amd64.zip
done
- name: build emqx
- name: Build emqx
run: make -C emqx ${PROFILE}-zip
- name: build emqtt-bench
run: make -C emqtt-bench
- name: build lux
run: |
set -e -u -x
cd lux
autoconf
./configure
make
make install
- name: run relup test
timeout-minutes: 20
- uses: actions/upload-artifact@v2
name: Upload built emqx and test scenario
with:
name: emqx_built
path: |
emqx/_packages/*/*.zip
emqx/.ci/fvt_tests
relup_test_run:
needs:
- relup_test_plan
- relup_test_build
runs-on: ubuntu-20.04
container: emqx/relup-test-env:erl23.2.7.2-emqx-2-ubuntu20.04
strategy:
fail-fast: false
matrix:
old_vsn: ${{ fromJson(needs.relup_test_plan.outputs.matrix) }}
env:
OLD_VSN: "${{ matrix.old_vsn }}"
PROFILE: "${{ needs.relup_test_plan.outputs.profile }}"
VSN: "${{ needs.relup_test_plan.outputs.vsn }}"
BROKER: "${{ needs.relup_test_plan.outputs.broker }}"
defaults:
run:
shell: bash
steps:
- uses: actions/download-artifact@v2
name: Dowload built emqx and test scenario
with:
name: emqx_built
path: emqx_built
- uses: actions/checkout@v2
name: Checkout one_more_emqx
with:
repository: terry-xiaoyu/one_more_emqx
ref: master
path: one_more_emqx
- name: Prepare packages
run: |
set -e -x -u
if [ -n "$OLD_VSNS" ]; then
mkdir -p packages
cp emqx/_packages/${PROFILE}/*.zip packages
cp emqx/_upgrade_base/*.zip packages
cp emqx_built/_packages/*/*.zip packages
cd packages
wget --no-verbose https://s3-us-west-2.amazonaws.com/packages.emqx/$BROKER/$OLD_VSN/$PROFILE-ubuntu20.04-${OLD_VSN#[e|v]}-amd64.zip
- name: Run relup test scenario
timeout-minutes: 5
run: |
lux \
--progress verbose \
--case_timeout infinity \
--var PROFILE=$PROFILE \
--var PACKAGE_PATH=$(pwd)/packages \
--var BENCH_PATH=$(pwd)/emqtt-bench \
--var ONE_MORE_EMQX_PATH=$(pwd)/one_more_emqx \
--var VSN="$VSN" \
--var OLD_VSNS="$OLD_VSNS" \
emqx/.ci/fvt_tests/relup.lux
fi
- uses: actions/upload-artifact@v1
--var OLD_VSN="$OLD_VSN" \
--var FROM_OTP_VSN="23.3.4.9-3" \
--var TO_OTP_VSN="23.3.4.9-3" \
emqx_built/.ci/fvt_tests/relup.lux
- uses: actions/upload-artifact@v2
name: Save debug data
if: failure()
with:
name: lux_logs
path: lux_logs
name: debug_data
path: |
packages/emqx/log/emqx.log.1
packages/emqx2/log/emqx.log.1
packages/*.zip
lux_logs

View File

@ -10,7 +10,7 @@ on:
jobs:
run_static_analysis:
runs-on: ubuntu-20.04
container: emqx/build-env:erl23.2.7.2-emqx-2-ubuntu20.04
container: ghcr.io/emqx/emqx-builder/4.4-2:23.3.4.9-3-ubuntu20.04
steps:
- uses: actions/checkout@v2
@ -27,7 +27,7 @@ jobs:
run_proper_test:
runs-on: ubuntu-20.04
container: emqx/build-env:erl23.2.7.2-emqx-2-ubuntu20.04
container: ghcr.io/emqx/emqx-builder/4.4-2:23.3.4.9-3-ubuntu20.04
steps:
- uses: actions/checkout@v2

View File

@ -1 +1 @@
erlang 24.0.1-emqx-1
erlang 23.3.4.9-3

View File

@ -3,9 +3,14 @@ REBAR_VERSION = 3.14.3-emqx-8
REBAR = $(CURDIR)/rebar3
BUILD = $(CURDIR)/build
SCRIPTS = $(CURDIR)/scripts
export EMQX_DEFAULT_BUILDER = ghcr.io/emqx/emqx-builder/4.4-2:23.3.4.9-3-alpine3.14
export EMQX_DEFAULT_RUNNER = alpine:3.14
export OTP_VSN ?= $(shell $(CURDIR)/scripts/get-otp-vsn.sh)
export PKG_VSN ?= $(shell $(CURDIR)/pkg-vsn.sh)
export EMQX_DESC ?= EMQ X
export EMQX_CE_DASHBOARD_VERSION ?= v4.3.1
export EMQX_CE_DASHBOARD_VERSION ?= v4.3.3
export DOCKERFILE := deploy/docker/Dockerfile
export DOCKERFILE_TESTING := deploy/docker/Dockerfile.testing
ifeq ($(OS),Windows_NT)
export REBAR_COLOR=none
endif
@ -85,16 +90,20 @@ $(REL_PROFILES:%=%): $(REBAR) get-dashboard
clean: $(PROFILES:%=clean-%)
$(PROFILES:%=clean-%):
@if [ -d _build/$(@:clean-%=%) ]; then \
rm rebar.lock \
rm -rf _build/$(@:clean-%=%)/rel; \
find _build/$(@:clean-%=%) -name '*.beam' -o -name '*.so' -o -name '*.app' -o -name '*.appup' -o -name '*.o' -o -name '*.d' -type f | xargs rm -f; \
find _build/$(@:clean-%=%) -type l -delete; \
fi
.PHONY: clean-all
clean-all:
@rm -f rebar.lock
@rm -rf _build
.PHONY: deps-all
deps-all: $(REBAR) $(PROFILES:%=deps-%)
@make clean # ensure clean at the end
## deps-<profile> is used in CI scripts to download deps and the
## share downloads between CI steps and/or copied into containers
@ -102,6 +111,7 @@ deps-all: $(REBAR) $(PROFILES:%=deps-%)
.PHONY: $(PROFILES:%=deps-%)
$(PROFILES:%=deps-%): $(REBAR) get-dashboard
@$(REBAR) as $(@:deps-%=%) get-deps
@rm -f rebar.lock
.PHONY: xref
xref: $(REBAR)
@ -144,11 +154,30 @@ $1: $1-rel
endef
$(foreach pt,$(PKG_PROFILES),$(eval $(call gen-pkg-target,$(pt))))
## docker target is to create docker instructions
.PHONY: $(REL_PROFILES:%=%-docker)
define gen-docker-target
$1-docker: $(COMMON_DEPS)
@$(BUILD) $1 docker
endef
ALL_ZIPS = $(REL_PROFILES)
$(foreach zt,$(ALL_ZIPS),$(eval $(call gen-docker-target,$(zt))))
## emqx-docker-testing
## emqx-ee-docker-testing
## is to directly copy a unzipped zip-package to a
## base image such as ubuntu20.04. Mostly for testing
.PHONY: $(REL_PROFILES:%=%-docker-testing)
define gen-docker-target-testing
$1-docker-testing: $(COMMON_DEPS)
@$(BUILD) $1 docker-testing
endef
ALL_ZIPS = $(REL_PROFILES)
$(foreach zt,$(ALL_ZIPS),$(eval $(call gen-docker-target-testing,$(zt))))
.PHONY: run
run: $(PROFILE) quickrun
.PHONY: quickrun
quickrun:
./_build/$(PROFILE)/rel/emqx/bin/emqx console
include docker.mk

5
NOTICE Normal file
View File

@ -0,0 +1,5 @@
EMQ X, a highly scalable, highly available distributed MQTT messaging broker for IoT.
Copyright (c) 2017-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
This product contains code developed at EMQ Technologies Co., Ltd.
Visit https://www.emqx.come to learn more.

View File

@ -42,18 +42,18 @@ auth.http.auth_req.params = clientid=%c,username=%u,password=%P
## Value: URL
##
## Examples: http://127.0.0.1:80/mqtt/superuser, https://[::1]:80/mqtt/superuser
auth.http.super_req.url = http://127.0.0.1:80/mqtt/superuser
# auth.http.super_req.url = http://127.0.0.1:80/mqtt/superuser
## HTTP Request Method for SuperUser Request
##
## Value: post | get
auth.http.super_req.method = post
# auth.http.super_req.method = post
## HTTP Request Headers for SuperUser Request, Content-Type header is configured by default.
## The possible values of the Content-Type header: application/x-www-form-urlencoded, application/json
##
## Examples: auth.http.super_req.headers.accept = */*
auth.http.super_req.headers.content-type = application/x-www-form-urlencoded
# auth.http.super_req.headers.content-type = application/x-www-form-urlencoded
## Parameters used to construct the request body or query string parameters
## When the request method is GET, these parameters will be converted into query string parameters
@ -70,7 +70,7 @@ auth.http.super_req.headers.content-type = application/x-www-form-urlencoded
## - %d: subject of client TLS cert
##
## Value: <K1>=<V1>,<K2>=<V2>,...
auth.http.super_req.params = clientid=%c,username=%u
# auth.http.super_req.params = clientid=%c,username=%u
## HTTP URL API path for ACL Request
## Comment out this config to disable ACL checks
@ -136,6 +136,11 @@ auth.http.connect_timeout = 5s
## Value: Number
auth.http.pool_size = 32
## Whether to enable HTTP Pipelining
##
## See: https://en.wikipedia.org/wiki/HTTP_pipelining
auth.http.enable_pipelining = true
##------------------------------------------------------------------------------
## SSL options

View File

@ -109,6 +109,11 @@ end}.
{datatype, integer}
]}.
{mapping, "auth.http.enable_pipelining", "emqx_auth_http.enable_pipelining", [
{default, true},
{datatype, {enum, [true, false]}}
]}.
{mapping, "auth.http.ssl.cacertfile", "emqx_auth_http.cacertfile", [
{datatype, string}
]}.

View File

@ -1,6 +1,6 @@
{application, emqx_auth_http,
[{description, "EMQ X Authentication/ACL with HTTP API"},
{vsn, "4.3.0"}, % strict semver, bump manually!
{vsn, "4.3.3"}, % strict semver, bump manually!
{modules, []},
{registered, [emqx_auth_http_sup]},
{applications, [kernel,stdlib,ehttpc]},

View File

@ -0,0 +1,16 @@
%% -*- mode: erlang -*-
{VSN,
[{"4.3.2",
[{apply,{application,stop,[emqx_auth_http]}},
{load_module,emqx_auth_http_app,brutal_purge,soft_purge,[]},
{load_module,emqx_auth_http_cli,brutal_purge,soft_purge,[]}]},
{<<"4.3.[0-1]">>,
[{restart_application,emqx_auth_http}]},
{<<".*">>,[]}],
[{"4.3.2",
[{apply,{application,stop,[emqx_auth_http]}},
{load_module,emqx_auth_http_app,brutal_purge,soft_purge,[]},
{load_module,emqx_auth_http_cli,brutal_purge,soft_purge,[]}]},
{<<"4.3.[0-1]">>,
[{restart_application,emqx_auth_http}]},
{<<".*">>,[]}]}.

View File

@ -50,14 +50,14 @@ translate_env(EnvName) ->
case application:get_env(?APP, EnvName) of
undefined -> ok;
{ok, Req} ->
{ok, EnablePipelining} = application:get_env(?APP, enable_pipelining),
{ok, PoolSize} = application:get_env(?APP, pool_size),
{ok, ConnectTimeout} = application:get_env(?APP, connect_timeout),
URL = proplists:get_value(url, Req),
{ok, #{host := Host,
path := Path0,
port := Port,
scheme := Scheme}} = emqx_http_lib:uri_parse(URL),
Path = path(Path0),
scheme := Scheme} = URIMap} = emqx_http_lib:uri_parse(URL),
Path = path(URIMap),
MoreOpts = case Scheme of
http ->
[{transport_opts, emqx_misc:ipv6_probe([])}];
@ -89,6 +89,7 @@ translate_env(EnvName) ->
end,
PoolOpts = [{host, Host},
{port, Port},
{enable_pipelining, EnablePipelining},
{pool_size, PoolSize},
{pool_type, random},
{connect_timeout, ConnectTimeout},
@ -149,10 +150,14 @@ ensure_content_type_header(Method, Headers)
when Method =:= post orelse Method =:= put ->
Headers;
ensure_content_type_header(_Method, Headers) ->
lists:keydelete("content-type", 1, Headers).
lists:keydelete(<<"content-type">>, 1, Headers).
path("") ->
path(#{path := "", 'query' := Query}) ->
"?" ++ Query;
path(#{path := Path, 'query' := Query}) ->
Path ++ "?" ++ Query;
path(#{path := ""}) ->
"/";
path(Path) ->
path(#{path := Path}) ->
Path.

View File

@ -29,16 +29,16 @@
request(PoolName, get, Path, Headers, Params, Timeout) ->
NewPath = Path ++ "?" ++ binary_to_list(cow_qs:qs(bin_kw(Params))),
reply(ehttpc:request(ehttpc_pool:pick_worker(PoolName), get, {NewPath, Headers}, Timeout));
reply(ehttpc:request(PoolName, get, {NewPath, Headers}, Timeout));
request(PoolName, post, Path, Headers, Params, Timeout) ->
Body = case proplists:get_value("content-type", Headers) of
Body = case proplists:get_value(<<"content-type">>, Headers) of
"application/x-www-form-urlencoded" ->
cow_qs:qs(bin_kw(Params));
"application/json" ->
emqx_json:encode(bin_kw(Params))
end,
reply(ehttpc:request(ehttpc_pool:pick_worker(PoolName), post, {Path, Headers, Body}, Timeout)).
reply(ehttpc:request(PoolName, post, {Path, Headers, Body}, Timeout)).
reply({ok, StatusCode, _Headers}) ->
{ok, StatusCode, <<>>};

View File

@ -27,10 +27,6 @@
, description/0
]).
-import(proplists, [get_value/2]).
-import(emqx_auth_ldap_cli, [search/4]).
-spec(register_metrics() -> ok).
register_metrics() ->
lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS).
@ -70,14 +66,14 @@ do_check_acl(#{username := Username}, PubSub, Topic, _NoMatchAction,
BaseDN = emqx_auth_ldap:replace_vars(CustomBaseDN, ReplaceRules),
case search(Pool, BaseDN, Filter, [Attribute, Attribute1]) of
case emqx_auth_ldap_cli:search(Pool, BaseDN, Filter, [Attribute, Attribute1]) of
{error, noSuchObject} ->
ok;
{ok, #eldap_search_result{entries = []}} ->
ok;
{ok, #eldap_search_result{entries = [Entry]}} ->
Topics = get_value(Attribute, Entry#eldap_entry.attributes)
++ get_value(Attribute1, Entry#eldap_entry.attributes),
Topics = proplists:get_value(Attribute, Entry#eldap_entry.attributes, [])
++ proplists:get_value(Attribute1, Entry#eldap_entry.attributes, []),
match(Topic, Topics);
Error ->
?LOG(error, "[LDAP] search error:~p", [Error]),
@ -95,4 +91,3 @@ match(Topic, [Filter | Topics]) ->
description() ->
"ACL with LDAP".

View File

@ -1,6 +1,6 @@
{application, emqx_auth_ldap,
[{description, "EMQ X Authentication/ACL with LDAP"},
{vsn, "4.3.0"}, % strict semver, bump manually!
{vsn, "4.3.2"}, % strict semver, bump manually!
{modules, []},
{registered, [emqx_auth_ldap_sup]},
{applications, [kernel,stdlib,eldap2,ecpool]},

View File

@ -0,0 +1,22 @@
%% -*-: erlang -*-
{VSN,
[ {"4.3.0",
[ {load_module, emqx_acl_ldap, brutal_purge, soft_purge, []}
, {load_module, emqx_auth_ldap_cli, brutal_purge, soft_purge, []}
]},
{"4.3.1",
[ {load_module, emqx_auth_ldap_cli, brutal_purge, soft_purge, []}
]},
{<<".*">>, []}
],
[
{"4.3.0",
[ {load_module, emqx_acl_ldap, brutal_purge, soft_purge, []}
, {load_module, emqx_auth_ldap_cli, brutal_purge, soft_purge, []}
]},
{"4.3.1",
[ {load_module, emqx_auth_ldap_cli, brutal_purge, soft_purge, []}
]},
{<<".*">>, []}
]
}.

View File

@ -76,8 +76,8 @@ connect(Opts) ->
search(Pool, Base, Filter) ->
ecpool:with_client(Pool,
fun(C) ->
case application:get_env(?APP, bind_as_user) of
{ok, true} ->
case application:get_env(?APP, bind_as_user, false) of
true ->
{ok, Opts} = application:get_env(?APP, ldap),
BindDn = get_value(bind_dn, Opts),
BindPassword = get_value(bind_password, Opts),
@ -91,7 +91,7 @@ search(Pool, Base, Filter) ->
catch
error:Reason -> {error, Reason}
end;
{ok, false} ->
false ->
eldap2:search(C, [{base, Base},
{filter, Filter},
{deref, eldap2:derefFindingBaseObj()}])
@ -101,8 +101,8 @@ search(Pool, Base, Filter) ->
search(Pool, Base, Filter, Attributes) ->
ecpool:with_client(Pool,
fun(C) ->
case application:get_env(?APP, bind_as_user) of
{ok, true} ->
case application:get_env(?APP, bind_as_user, false) of
true ->
{ok, Opts} = application:get_env(?APP, ldap),
BindDn = get_value(bind_dn, Opts),
BindPassword = get_value(bind_password, Opts),
@ -117,7 +117,7 @@ search(Pool, Base, Filter, Attributes) ->
catch
error:Reason -> {error, Reason}
end;
{ok, false} ->
false ->
eldap2:search(C, [{base, Base},
{filter, Filter},
{attributes, Attributes},

View File

@ -1,21 +1,47 @@
-define(APP, emqx_auth_mnesia).
-type(login():: {clientid, binary()}
-type(login() :: {clientid, binary()}
| {username, binary()}).
-type(acl_target() :: login() | all).
-type(acl_target_type() :: clientid | username | all).
-type(access():: allow | deny).
-type(action():: pub | sub).
-type(legacy_action():: action() | pubsub).
-type(created_at():: integer()).
-record(emqx_user, {
login :: login(),
password :: binary(),
created_at :: integer()
created_at :: created_at()
}).
-record(emqx_acl, {
filter:: {login() | all, emqx_topic:topic()},
action :: pub | sub | pubsub,
access :: allow | deny,
created_at :: integer()
-define(ACL_TABLE, emqx_acl).
-define(MIGRATION_MARK_KEY, emqx_acl2_migration_started).
-record(?ACL_TABLE, {
filter :: {acl_target(), emqx_topic:topic()} | ?MIGRATION_MARK_KEY,
action :: legacy_action(),
access :: access(),
created_at :: created_at()
}).
-define(MIGRATION_MARK_RECORD, #?ACL_TABLE{filter = ?MIGRATION_MARK_KEY, action = pub, access = deny, created_at = 0}).
-type(rule() :: {access(), action(), emqx_topic:topic(), created_at()}).
-define(ACL_TABLE2, emqx_acl2).
-record(?ACL_TABLE2, {
who :: acl_target(),
rules :: [ rule() ]
}).
-type(acl_record() :: {acl_target(), emqx_topic:topic(), action(), access(), created_at()}).
-record(auth_metrics, {
success = 'client.auth.success',
failure = 'client.auth.failure',

View File

@ -18,10 +18,6 @@
-include("emqx_auth_mnesia.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-define(TABLE, emqx_acl).
%% ACL Callbacks
-export([ init/0
, register_metrics/0
@ -30,12 +26,8 @@
]).
init() ->
ok = ekka_mnesia:create_table(emqx_acl, [
{type, bag},
{disc_copies, [node()]},
{attributes, record_info(fields, emqx_acl)},
{storage_properties, [{ets, [{read_concurrency, true}]}]}]),
ok = ekka_mnesia:copy_table(emqx_acl, disc_copies).
ok = emqx_acl_mnesia_db:create_table(),
ok = emqx_acl_mnesia_db:create_table2().
-spec(register_metrics() -> ok).
register_metrics() ->
@ -46,12 +38,12 @@ check_acl(ClientInfo = #{ clientid := Clientid }, PubSub, Topic, _NoMatchAction,
Acls = case Username of
undefined ->
emqx_acl_mnesia_cli:lookup_acl({clientid, Clientid}) ++
emqx_acl_mnesia_cli:lookup_acl(all);
emqx_acl_mnesia_db:lookup_acl({clientid, Clientid}) ++
emqx_acl_mnesia_db:lookup_acl(all);
_ ->
emqx_acl_mnesia_cli:lookup_acl({clientid, Clientid}) ++
emqx_acl_mnesia_cli:lookup_acl({username, Username}) ++
emqx_acl_mnesia_cli:lookup_acl(all)
emqx_acl_mnesia_db:lookup_acl({clientid, Clientid}) ++
emqx_acl_mnesia_db:lookup_acl({username, Username}) ++
emqx_acl_mnesia_db:lookup_acl(all)
end,
case match(ClientInfo, PubSub, Topic, Acls) of
@ -83,7 +75,6 @@ match(ClientInfo, PubSub, Topic, [ {_, ACLTopic, Action, Access, _} | Acls]) ->
match_topic(ClientInfo, Topic, ACLTopic) when is_binary(Topic) ->
emqx_topic:match(Topic, feed_var(ClientInfo, ACLTopic)).
match_actions(_, pubsub) -> true;
match_actions(subscribe, sub) -> true;
match_actions(publish, pub) -> true;
match_actions(_, _) -> false.

View File

@ -16,8 +16,6 @@
-module(emqx_acl_mnesia_api).
-include("emqx_auth_mnesia.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-import(proplists, [ get_value/2
@ -99,26 +97,22 @@
]).
list_clientid(_Bindings, Params) ->
MatchSpec = ets:fun2ms(
fun({emqx_acl, {{clientid, Clientid}, Topic}, Action, Access, CreatedAt}) -> {{clientid,Clientid}, Topic, Action,Access, CreatedAt} end),
return({ok, emqx_auth_mnesia_api:paginate(emqx_acl, MatchSpec, Params, fun emqx_acl_mnesia_cli:comparing/2, fun format/1)}).
Table = emqx_acl_mnesia_db:login_acl_table(clientid),
return({ok, emqx_auth_mnesia_api:paginate_qh(Table, count(Table), Params, fun emqx_acl_mnesia_db:comparing/2, fun format/1)}).
list_username(_Bindings, Params) ->
MatchSpec = ets:fun2ms(
fun({emqx_acl, {{username, Username}, Topic}, Action, Access, CreatedAt}) -> {{username, Username}, Topic, Action,Access, CreatedAt} end),
return({ok, emqx_auth_mnesia_api:paginate(emqx_acl, MatchSpec, Params, fun emqx_acl_mnesia_cli:comparing/2, fun format/1)}).
Table = emqx_acl_mnesia_db:login_acl_table(username),
return({ok, emqx_auth_mnesia_api:paginate_qh(Table, count(Table), Params, fun emqx_acl_mnesia_db:comparing/2, fun format/1)}).
list_all(_Bindings, Params) ->
MatchSpec = ets:fun2ms(
fun({emqx_acl, {all, Topic}, Action, Access, CreatedAt}) -> {all, Topic, Action,Access, CreatedAt}end
),
return({ok, emqx_auth_mnesia_api:paginate(emqx_acl, MatchSpec, Params, fun emqx_acl_mnesia_cli:comparing/2, fun format/1)}).
Table = emqx_acl_mnesia_db:login_acl_table(all),
return({ok, emqx_auth_mnesia_api:paginate_qh(Table, count(Table), Params, fun emqx_acl_mnesia_db:comparing/2, fun format/1)}).
lookup(#{clientid := Clientid}, _Params) ->
return({ok, format(emqx_acl_mnesia_cli:lookup_acl({clientid, urldecode(Clientid)}))});
return({ok, format(emqx_acl_mnesia_db:lookup_acl({clientid, urldecode(Clientid)}))});
lookup(#{username := Username}, _Params) ->
return({ok, format(emqx_acl_mnesia_cli:lookup_acl({username, urldecode(Username)}))}).
return({ok, format(emqx_acl_mnesia_db:lookup_acl({username, urldecode(Username)}))}).
add(_Bindings, Params) ->
[ P | _] = Params,
@ -144,15 +138,15 @@ do_add(Params) ->
Username = get_value(<<"username">>, Params, undefined),
Login = case {Clientid, Username} of
{undefined, undefined} -> all;
{_, undefined} -> {clientid, urldecode(Clientid)};
{undefined, _} -> {username, urldecode(Username)}
{_, undefined} -> {clientid, Clientid};
{undefined, _} -> {username, Username}
end,
Topic = urldecode(get_value(<<"topic">>, Params)),
Action = urldecode(get_value(<<"action">>, Params)),
Access = urldecode(get_value(<<"access">>, Params)),
Topic = get_value(<<"topic">>, Params),
Action = get_value(<<"action">>, Params),
Access = get_value(<<"access">>, Params),
Re = case validate([login, topic, action, access], [Login, Topic, Action, Access]) of
ok ->
emqx_acl_mnesia_cli:add_acl(Login, Topic, erlang:binary_to_atom(Action, utf8), erlang:binary_to_atom(Access, utf8));
emqx_acl_mnesia_db:add_acl(Login, Topic, erlang:binary_to_atom(Action, utf8), erlang:binary_to_atom(Access, utf8));
Err -> Err
end,
maps:merge(#{topic => Topic,
@ -165,15 +159,19 @@ do_add(Params) ->
end).
delete(#{clientid := Clientid, topic := Topic}, _) ->
return(emqx_acl_mnesia_cli:remove_acl({clientid, urldecode(Clientid)}, urldecode(Topic)));
return(emqx_acl_mnesia_db:remove_acl({clientid, urldecode(Clientid)}, urldecode(Topic)));
delete(#{username := Username, topic := Topic}, _) ->
return(emqx_acl_mnesia_cli:remove_acl({username, urldecode(Username)}, urldecode(Topic)));
return(emqx_acl_mnesia_db:remove_acl({username, urldecode(Username)}, urldecode(Topic)));
delete(#{topic := Topic}, _) ->
return(emqx_acl_mnesia_cli:remove_acl(all, urldecode(Topic))).
return(emqx_acl_mnesia_db:remove_acl(all, urldecode(Topic))).
%%------------------------------------------------------------------------------
%% Interval Funcs
%%------------------------------------------------------------------------------
count(QH) ->
qlc:fold(fun(_, Count) -> Count + 1 end, 0, QH).
format({{clientid, Clientid}, Topic, Action, Access, _CreatedAt}) ->
#{clientid => Clientid, topic => Topic, action => Action, access => Access};
format({{username, Username}, Topic, Action, Access, _CreatedAt}) ->

View File

@ -16,110 +16,28 @@
-module(emqx_acl_mnesia_cli).
-include("emqx_auth_mnesia.hrl").
-include_lib("emqx/include/logger.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-define(TABLE, emqx_acl).
%% Acl APIs
-export([ add_acl/4
, lookup_acl/1
, all_acls/0
, all_acls/1
, remove_acl/2
]).
-export([cli/1]).
-export([comparing/2]).
%%--------------------------------------------------------------------
%% Acl API
%%--------------------------------------------------------------------
%% @doc Add Acls
-spec(add_acl(login() | all, emqx_topic:topic(), pub | sub | pubsub, allow | deny) ->
ok | {error, any()}).
add_acl(Login, Topic, Action, Access) ->
Filter = {Login, Topic},
Acl = #?TABLE{
filter = Filter,
action = Action,
access = Access,
created_at = erlang:system_time(millisecond)
},
ret(mnesia:transaction(
fun() ->
OldRecords = mnesia:wread({?TABLE, Filter}),
case Action of
pubsub ->
update_permission(pub, Acl, OldRecords),
update_permission(sub, Acl, OldRecords);
_ ->
update_permission(Action, Acl, OldRecords)
end
end)).
%% @doc Lookup acl by login
-spec(lookup_acl(login() | all) -> list()).
lookup_acl(undefined) -> [];
lookup_acl(Login) ->
MatchSpec = ets:fun2ms(fun({?TABLE, {Filter, ACLTopic}, Action, Access, CreatedAt})
when Filter =:= Login ->
{Filter, ACLTopic, Action, Access, CreatedAt}
end),
lists:sort(fun comparing/2, ets:select(?TABLE, MatchSpec)).
%% @doc Remove acl
-spec(remove_acl(login() | all, emqx_topic:topic()) -> ok | {error, any()}).
remove_acl(Login, Topic) ->
ret(mnesia:transaction(fun mnesia:delete/1, [{?TABLE, {Login, Topic}}])).
%% @doc All logins
-spec(all_acls() -> list()).
all_acls() ->
all_acls(clientid) ++
all_acls(username) ++
all_acls(all).
all_acls(clientid) ->
MatchSpec = ets:fun2ms(
fun({?TABLE, {{clientid, Clientid}, Topic}, Action, Access, CreatedAt}) ->
{{clientid, Clientid}, Topic, Action, Access, CreatedAt}
end),
lists:sort(fun comparing/2, ets:select(?TABLE, MatchSpec));
all_acls(username) ->
MatchSpec = ets:fun2ms(
fun({?TABLE, {{username, Username}, Topic}, Action, Access, CreatedAt}) ->
{{username, Username}, Topic, Action, Access, CreatedAt}
end),
lists:sort(fun comparing/2, ets:select(?TABLE, MatchSpec));
all_acls(all) ->
MatchSpec = ets:fun2ms(
fun({?TABLE, {all, Topic}, Action, Access, CreatedAt}) ->
{all, Topic, Action, Access, CreatedAt}
end
),
lists:sort(fun comparing/2, ets:select(?TABLE, MatchSpec)).
%%--------------------------------------------------------------------
%% ACL Cli
%%--------------------------------------------------------------------
cli(["list"]) ->
[print_acl(Acl) || Acl <- all_acls()];
[print_acl(Acl) || Acl <- emqx_acl_mnesia_db:all_acls()];
cli(["list", "clientid"]) ->
[print_acl(Acl) || Acl <- all_acls(clientid)];
[print_acl(Acl) || Acl <- emqx_acl_mnesia_db:all_acls(clientid)];
cli(["list", "username"]) ->
[print_acl(Acl) || Acl <- all_acls(username)];
[print_acl(Acl) || Acl <- emqx_acl_mnesia_db:all_acls(username)];
cli(["list", "_all"]) ->
[print_acl(Acl) || Acl <- all_acls(all)];
[print_acl(Acl) || Acl <- emqx_acl_mnesia_db:all_acls(all)];
cli(["add", "clientid", Clientid, Topic, Action, Access]) ->
case validate(action, Action) andalso validate(access, Access) of
true ->
case add_acl(
case emqx_acl_mnesia_db:add_acl(
{clientid, iolist_to_binary(Clientid)},
iolist_to_binary(Topic),
list_to_existing_atom(Action),
@ -135,7 +53,7 @@ cli(["add", "clientid", Clientid, Topic, Action, Access]) ->
cli(["add", "username", Username, Topic, Action, Access]) ->
case validate(action, Action) andalso validate(access, Access) of
true ->
case add_acl(
case emqx_acl_mnesia_db:add_acl(
{username, iolist_to_binary(Username)},
iolist_to_binary(Topic),
list_to_existing_atom(Action),
@ -151,7 +69,7 @@ cli(["add", "username", Username, Topic, Action, Access]) ->
cli(["add", "_all", Topic, Action, Access]) ->
case validate(action, Action) andalso validate(access, Access) of
true ->
case add_acl(
case emqx_acl_mnesia_db:add_acl(
all,
iolist_to_binary(Topic),
list_to_existing_atom(Action),
@ -165,16 +83,16 @@ cli(["add", "_all", Topic, Action, Access]) ->
end;
cli(["show", "clientid", Clientid]) ->
[print_acl(Acl) || Acl <- lookup_acl({clientid, iolist_to_binary(Clientid)})];
[print_acl(Acl) || Acl <- emqx_acl_mnesia_db:lookup_acl({clientid, iolist_to_binary(Clientid)})];
cli(["show", "username", Username]) ->
[print_acl(Acl) || Acl <- lookup_acl({username, iolist_to_binary(Username)})];
[print_acl(Acl) || Acl <- emqx_acl_mnesia_db:lookup_acl({username, iolist_to_binary(Username)})];
cli(["del", "clientid", Clientid, Topic])->
cli(["delete", "clientid", Clientid, Topic]);
cli(["delete", "clientid", Clientid, Topic])->
case remove_acl({clientid, iolist_to_binary(Clientid)}, iolist_to_binary(Topic)) of
case emqx_acl_mnesia_db:remove_acl({clientid, iolist_to_binary(Clientid)}, iolist_to_binary(Topic)) of
ok -> emqx_ctl:print("ok~n");
{error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason])
end;
@ -183,7 +101,7 @@ cli(["del", "username", Username, Topic])->
cli(["delete", "username", Username, Topic]);
cli(["delete", "username", Username, Topic])->
case remove_acl({username, iolist_to_binary(Username)}, iolist_to_binary(Topic)) of
case emqx_acl_mnesia_db:remove_acl({username, iolist_to_binary(Username)}, iolist_to_binary(Topic)) of
ok -> emqx_ctl:print("ok~n");
{error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason])
end;
@ -192,7 +110,7 @@ cli(["del", "_all", Topic])->
cli(["delete", "_all", Topic]);
cli(["delete", "_all", Topic])->
case remove_acl(all, iolist_to_binary(Topic)) of
case emqx_acl_mnesia_db:remove_acl(all, iolist_to_binary(Topic)) of
ok -> emqx_ctl:print("ok~n");
{error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason])
end;
@ -215,13 +133,6 @@ cli(_) ->
%% Internal functions
%%--------------------------------------------------------------------
comparing({_, _, _, _, CreatedAt1},
{_, _, _, _, CreatedAt2}) ->
CreatedAt1 >= CreatedAt2.
ret({atomic, ok}) -> ok;
ret({aborted, Error}) -> {error, Error}.
validate(action, "pub") -> true;
validate(action, "sub") -> true;
validate(action, "pubsub") -> true;
@ -244,27 +155,3 @@ print_acl({all, Topic, Action, Access, _}) ->
"Acl($all topic = ~p action = ~p access = ~p)~n",
[Topic, Action, Access]
).
update_permission(Action, Acl0, OldRecords) ->
Acl = Acl0 #?TABLE{action = Action},
maybe_delete_shadowed_records(Action, OldRecords),
mnesia:write(Acl).
maybe_delete_shadowed_records(_, []) ->
ok;
maybe_delete_shadowed_records(Action1, [Rec = #emqx_acl{action = Action2} | Rest]) ->
if Action1 =:= Action2 ->
ok = mnesia:delete_object(Rec);
Action2 =:= pubsub ->
%% Perform migration from the old data format on the
%% fly. This is needed only for the enterprise version,
%% delete this branch on 5.0
mnesia:delete_object(Rec),
mnesia:write(Rec#?TABLE{action = other_action(Action1)});
true ->
ok
end,
maybe_delete_shadowed_records(Action1, Rest).
other_action(pub) -> sub;
other_action(sub) -> pub.

View File

@ -0,0 +1,339 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_acl_mnesia_db).
-include("emqx_auth_mnesia.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-include_lib("stdlib/include/qlc.hrl").
%% ACL APIs
-export([ create_table/0
, create_table2/0
]).
-export([ add_acl/4
, lookup_acl/1
, all_acls_export/0
, all_acls/0
, all_acls/1
, remove_acl/2
, merge_acl_records/3
, login_acl_table/1
, is_migration_started/0
]).
-export([comparing/2]).
%%--------------------------------------------------------------------
%% ACL API
%%--------------------------------------------------------------------
%% @doc Create table `emqx_acl` of old format rules
-spec(create_table() -> ok).
create_table() ->
ok = ekka_mnesia:create_table(?ACL_TABLE, [
{type, bag},
{disc_copies, [node()]},
{attributes, record_info(fields, ?ACL_TABLE)},
{storage_properties, [{ets, [{read_concurrency, true}]}]}]),
ok = ekka_mnesia:copy_table(?ACL_TABLE, disc_copies).
%% @doc Create table `emqx_acl2` of new format rules
-spec(create_table2() -> ok).
create_table2() ->
ok = ekka_mnesia:create_table(?ACL_TABLE2, [
{type, ordered_set},
{disc_copies, [node()]},
{attributes, record_info(fields, ?ACL_TABLE2)},
{storage_properties, [{ets, [{read_concurrency, true}]}]}]),
ok = ekka_mnesia:copy_table(?ACL_TABLE2, disc_copies).
%% @doc Add Acls
-spec(add_acl(acl_target(), emqx_topic:topic(), legacy_action(), access()) ->
ok | {error, any()}).
add_acl(Login, Topic, Action, Access) ->
ret(mnesia:transaction(fun() ->
case is_migration_started() of
true -> add_acl_new(Login, Topic, Action, Access);
false -> add_acl_old(Login, Topic, Action, Access)
end
end)).
%% @doc Lookup acl by login
-spec(lookup_acl(acl_target()) -> list(acl_record())).
lookup_acl(undefined) -> [];
lookup_acl(Login) ->
% After migration to ?ACL_TABLE2, ?ACL_TABLE never has any rules. This lookup should be removed later.
MatchSpec = ets:fun2ms(fun(#?ACL_TABLE{filter = {Filter, _}} = Rec)
when Filter =:= Login -> Rec
end),
OldRecs = ets:select(?ACL_TABLE, MatchSpec),
NewAcls = ets:lookup(?ACL_TABLE2, Login),
MergedAcl = merge_acl_records(Login, OldRecs, NewAcls),
lists:sort(fun comparing/2, acl_to_list(MergedAcl)).
%% @doc Remove ACL
-spec remove_acl(acl_target(), emqx_topic:topic()) -> ok | {error, any()}.
remove_acl(Login, Topic) ->
ret(mnesia:transaction(fun() ->
mnesia:delete({?ACL_TABLE, {Login, Topic}}),
case mnesia:wread({?ACL_TABLE2, Login}) of
[] -> ok;
[#?ACL_TABLE2{rules = Rules} = Acl] ->
case delete_topic_rules(Topic, Rules) of
[] -> mnesia:delete({?ACL_TABLE2, Login});
[_ | _] = RemainingRules ->
mnesia:write(Acl#?ACL_TABLE2{rules = RemainingRules})
end
end
end)).
%% @doc All ACL rules
-spec(all_acls() -> list(acl_record())).
all_acls() ->
all_acls(username) ++
all_acls(clientid) ++
all_acls(all).
%% @doc All ACL rules of specified type
-spec(all_acls(acl_target_type()) -> list(acl_record())).
all_acls(AclTargetType) ->
lists:sort(fun comparing/2, qlc:eval(login_acl_table(AclTargetType))).
%% @doc All ACL rules fetched transactionally
-spec(all_acls_export() -> list(acl_record())).
all_acls_export() ->
AclTargetTypes = [username, clientid, all],
MatchSpecNew = lists:flatmap(fun login_match_spec_new/1, AclTargetTypes),
MatchSpecOld = lists:flatmap(fun login_match_spec_old/1, AclTargetTypes),
{atomic, Records} = mnesia:transaction(
fun() ->
QH = acl_table(MatchSpecNew, MatchSpecOld, fun mnesia:table/2, fun lookup_mnesia/2),
qlc:eval(QH)
end),
Records.
%% @doc QLC table of logins matching spec
-spec(login_acl_table(acl_target_type()) -> qlc:query_handle()).
login_acl_table(AclTargetType) ->
MatchSpecNew = login_match_spec_new(AclTargetType),
MatchSpecOld = login_match_spec_old(AclTargetType),
acl_table(MatchSpecNew, MatchSpecOld, fun ets:table/2, fun lookup_ets/2).
%% @doc Combine old `emqx_acl` ACL records with a new `emqx_acl2` ACL record for a given login
-spec(merge_acl_records(acl_target(), [#?ACL_TABLE{}], [#?ACL_TABLE2{}]) -> #?ACL_TABLE2{}).
merge_acl_records(Login, OldRecs, Acls) ->
OldRules = old_recs_to_rules(OldRecs),
NewRules = case Acls of
[] -> [];
[#?ACL_TABLE2{rules = Rules}] -> Rules
end,
#?ACL_TABLE2{who = Login, rules = merge_rules(NewRules, OldRules)}.
%% @doc Checks if background migration of ACL rules from `emqx_acl` to `emqx_acl2` format started.
%% Should be run in transaction
-spec(is_migration_started() -> boolean()).
is_migration_started() ->
case mnesia:read({?ACL_TABLE, ?MIGRATION_MARK_KEY}) of
[?MIGRATION_MARK_RECORD | _] -> true;
[] -> false
end.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
add_acl_new(Login, Topic, Action, Access) ->
Rule = {Access, Action, Topic, erlang:system_time(millisecond)},
Rules = normalize_rule(Rule),
OldAcl = mnesia:wread({?ACL_TABLE2, Login}),
NewAcl = case OldAcl of
[#?ACL_TABLE2{rules = OldRules} = Acl] ->
Acl#?ACL_TABLE2{rules = merge_rules(Rules, OldRules)};
[] ->
#?ACL_TABLE2{who = Login, rules = Rules}
end,
mnesia:write(NewAcl).
add_acl_old(Login, Topic, Action, Access) ->
Filter = {Login, Topic},
Acl = #?ACL_TABLE{
filter = Filter,
action = Action,
access = Access,
created_at = erlang:system_time(millisecond)
},
OldRecords = mnesia:wread({?ACL_TABLE, Filter}),
case Action of
pubsub ->
update_permission(pub, Acl, OldRecords),
update_permission(sub, Acl, OldRecords);
_ ->
update_permission(Action, Acl, OldRecords)
end.
old_recs_to_rules(OldRecs) ->
lists:flatmap(fun old_rec_to_rules/1, OldRecs).
old_rec_to_rules(#?ACL_TABLE{filter = {_, Topic}, action = Action, access = Access, created_at = CreatedAt}) ->
normalize_rule({Access, Action, Topic, CreatedAt}).
normalize_rule({Access, pubsub, Topic, CreatedAt}) ->
[{Access, pub, Topic, CreatedAt}, {Access, sub, Topic, CreatedAt}];
normalize_rule({Access, Action, Topic, CreatedAt}) ->
[{Access, Action, Topic, CreatedAt}].
merge_rules([], OldRules) -> OldRules;
merge_rules([NewRule | RestNewRules], OldRules) ->
merge_rules(RestNewRules, merge_rule(NewRule, OldRules)).
merge_rule({_, Action, Topic, _ } = NewRule, OldRules) ->
[NewRule | lists:filter(
fun({_, OldAction, OldTopic, _}) ->
{Action, Topic} =/= {OldAction, OldTopic}
end, OldRules)].
acl_to_list(#?ACL_TABLE2{who = Login, rules = Rules}) ->
[{Login, Topic, Action, Access, CreatedAt} || {Access, Action, Topic, CreatedAt} <- Rules].
delete_topic_rules(Topic, Rules) ->
[Rule || {_, _, T, _} = Rule <- Rules, T =/= Topic].
comparing({_, _, _, _, CreatedAt} = Rec1,
{_, _, _, _, CreatedAt} = Rec2) ->
Rec1 >= Rec2;
comparing({_, _, _, _, CreatedAt1},
{_, _, _, _, CreatedAt2}) ->
CreatedAt1 >= CreatedAt2.
login_match_spec_old(all) ->
ets:fun2ms(fun(#?ACL_TABLE{filter = {all, _}} = Record) ->
Record
end);
login_match_spec_old(Type) when (Type =:= username) or (Type =:= clientid) ->
ets:fun2ms(fun(#?ACL_TABLE{filter = {{RecordType, _}, _}} = Record)
when RecordType =:= Type -> Record
end).
login_match_spec_new(all) ->
ets:fun2ms(fun(#?ACL_TABLE2{who = all} = Record) ->
Record
end);
login_match_spec_new(Type) when (Type =:= username) or (Type =:= clientid) ->
ets:fun2ms(fun(#?ACL_TABLE2{who = {RecordType, _}} = Record)
when RecordType =:= Type -> Record
end).
acl_table(MatchSpecNew, MatchSpecOld, TableFun, LookupFun) ->
TraverseFun =
fun() ->
CursorNew =
qlc:cursor(
TableFun(?ACL_TABLE2, [{traverse, {select, MatchSpecNew}}])),
CursorOld =
qlc:cursor(
TableFun(?ACL_TABLE, [{traverse, {select, MatchSpecOld}}])),
traverse_new(CursorNew, CursorOld, #{}, LookupFun)
end,
qlc:table(TraverseFun, []).
% These are traverse funs for qlc table created by `acl_table/4`.
% Traversing consumes memory: it collects logins present in `?ACL_TABLE` and
% at the same time having rules in `?ACL_TABLE2`.
% Such records appear if ACLs are inserted before migration started.
% After migration, number of such logins is zero, so traversing starts working in
% constant memory.
traverse_new(CursorNew, CursorOld, FoundKeys, LookupFun) ->
Acls = qlc:next_answers(CursorNew, 1),
case Acls of
[] ->
qlc:delete_cursor(CursorNew),
traverse_old(CursorOld, FoundKeys);
[#?ACL_TABLE2{who = Login, rules = Rules} = Acl] ->
Keys = lists:usort([{Login, Topic} || {_, _, Topic, _} <- Rules]),
OldRecs = lists:flatmap(fun(Key) -> LookupFun(?ACL_TABLE, Key) end, Keys),
MergedAcl = merge_acl_records(Login, OldRecs, [Acl]),
NewFoundKeys =
lists:foldl(fun(#?ACL_TABLE{filter = Key}, Found) -> maps:put(Key, true, Found) end,
FoundKeys,
OldRecs),
case acl_to_list(MergedAcl) of
[] ->
traverse_new(CursorNew, CursorOld, NewFoundKeys, LookupFun);
List ->
List ++ fun() -> traverse_new(CursorNew, CursorOld, NewFoundKeys, LookupFun) end
end
end.
traverse_old(CursorOld, FoundKeys) ->
OldAcls = qlc:next_answers(CursorOld),
case OldAcls of
[] ->
qlc:delete_cursor(CursorOld),
[];
_ ->
Records = [ {Login, Topic, Action, Access, CreatedAt}
|| #?ACL_TABLE{filter = {Login, Topic}, action = LegacyAction, access = Access, created_at = CreatedAt} <- OldAcls,
{_, Action, _, _} <- normalize_rule({Access, LegacyAction, Topic, CreatedAt}),
not maps:is_key({Login, Topic}, FoundKeys)
],
case Records of
[] -> traverse_old(CursorOld, FoundKeys);
List -> List ++ fun() -> traverse_old(CursorOld, FoundKeys) end
end
end.
lookup_mnesia(Tab, Key) ->
mnesia:read({Tab, Key}).
lookup_ets(Tab, Key) ->
ets:lookup(Tab, Key).
update_permission(Action, Acl0, OldRecords) ->
Acl = Acl0 #?ACL_TABLE{action = Action},
maybe_delete_shadowed_records(Action, OldRecords),
mnesia:write(Acl).
maybe_delete_shadowed_records(_, []) ->
ok;
maybe_delete_shadowed_records(Action1, [Rec = #emqx_acl{action = Action2} | Rest]) ->
if Action1 =:= Action2 ->
ok = mnesia:delete_object(Rec);
Action2 =:= pubsub ->
%% Perform migration from the old data format on the
%% fly. This is needed only for the enterprise version,
%% delete this branch on 5.0
mnesia:delete_object(Rec),
mnesia:write(Rec#?ACL_TABLE{action = other_action(Action1)});
true ->
ok
end,
maybe_delete_shadowed_records(Action1, Rest).
other_action(pub) -> sub;
other_action(sub) -> pub.
ret({atomic, ok}) -> ok;
ret({aborted, Error}) -> {error, Error}.

View File

@ -0,0 +1,215 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_acl_mnesia_migrator).
-include("emqx_auth_mnesia.hrl").
-include_lib("emqx/include/logger.hrl").
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
-behaviour(gen_statem).
-define(CHECK_ALL_NODES_INTERVAL, 60000).
-type(migration_delay_reason() :: old_nodes | bad_nodes).
-export([
callback_mode/0,
init/1
]).
-export([
waiting_all_nodes/3,
checking_old_table/3,
migrating/3
]).
-export([
start_link/0,
start_link/1,
start_supervised/0,
stop_supervised/0,
migrate_records/0,
is_migrating_on_node/1,
is_old_table_migrated/0
]).
%%--------------------------------------------------------------------
%% External interface
%%--------------------------------------------------------------------
start_link() ->
start_link(?MODULE).
start_link(Name) when is_atom(Name) ->
start_link(#{
name => Name
});
start_link(#{name := Name} = Opts) ->
gen_statem:start_link({local, Name}, ?MODULE, Opts, []).
start_supervised() ->
try
{ok, _} = supervisor:restart_child(emqx_auth_mnesia_sup, ?MODULE),
ok
catch
exit:{noproc, _} -> ok
end.
stop_supervised() ->
try
ok = supervisor:terminate_child(emqx_auth_mnesia_sup, ?MODULE),
ok = supervisor:delete_child(emqx_auth_mnesia_sup, ?MODULE)
catch
exit:{noproc, _} -> ok
end.
%%--------------------------------------------------------------------
%% gen_statem callbacks
%%--------------------------------------------------------------------
callback_mode() -> state_functions.
init(Opts) ->
ok = emqx_acl_mnesia_db:create_table(),
ok = emqx_acl_mnesia_db:create_table2(),
Name = maps:get(name, Opts, ?MODULE),
CheckNodesInterval = maps:get(check_nodes_interval, Opts, ?CHECK_ALL_NODES_INTERVAL),
GetNodes = maps:get(get_nodes, Opts, fun all_nodes/0),
Data =
#{name => Name,
check_nodes_interval => CheckNodesInterval,
get_nodes => GetNodes},
{ok, waiting_all_nodes, Data, [{state_timeout, 0, check_nodes}]}.
%%--------------------------------------------------------------------
%% state callbacks
%%--------------------------------------------------------------------
waiting_all_nodes(state_timeout, check_nodes, Data) ->
#{name := Name, check_nodes_interval := CheckNodesInterval, get_nodes := GetNodes} = Data,
case is_all_nodes_migrating(Name, GetNodes()) of
true ->
?tp(info, emqx_acl_mnesia_migrator_check_old_table, #{}),
{next_state, checking_old_table, Data, [{next_event, internal, check_old_table}]};
{false, Reason, Nodes} ->
?tp(info,
emqx_acl_mnesia_migrator_bad_nodes_delay,
#{delay => CheckNodesInterval,
reason => Reason,
name => Name,
nodes => Nodes}),
{keep_state_and_data, [{state_timeout, CheckNodesInterval, check_nodes}]}
end.
checking_old_table(internal, check_old_table, Data) ->
case is_old_table_migrated() of
true ->
?tp(info, emqx_acl_mnesia_migrator_finish, #{}),
{next_state, finished, Data, [{hibernate, true}]};
false ->
?tp(info, emqx_acl_mnesia_migrator_start_migration, #{}),
{next_state, migrating, Data, [{next_event, internal, start_migration}]}
end.
migrating(internal, start_migration, Data) ->
ok = migrate_records(),
{next_state, checking_old_table, Data, [{next_event, internal, check_old_table}]}.
%% @doc Returns `true` if migration is started in the local node, otherwise crash.
-spec(is_migrating_on_node(atom()) -> true).
is_migrating_on_node(Name) ->
true = is_pid(erlang:whereis(Name)).
%% @doc Run migration of records
-spec(migrate_records() -> ok).
migrate_records() ->
ok = add_migration_mark(),
Key = peek_record(),
do_migrate_records(Key).
%% @doc Run migration of records
-spec(is_all_nodes_migrating(atom(), list(node())) -> true | {false, migration_delay_reason(), list(node())}).
is_all_nodes_migrating(Name, Nodes) ->
case rpc:multicall(Nodes, ?MODULE, is_migrating_on_node, [Name]) of
{Results, []} ->
OldNodes = [ Node || {Node, Result} <- lists:zip(Nodes, Results), Result =/= true ],
case OldNodes of
[] -> true;
_ -> {false, old_nodes, OldNodes}
end;
{_, [_BadNode | _] = BadNodes} ->
{false, bad_nodes, BadNodes}
end.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
all_nodes() ->
ekka_mnesia:cluster_nodes(all).
is_old_table_migrated() ->
Result =
mnesia:transaction(fun() ->
case mnesia:first(?ACL_TABLE) of
?MIGRATION_MARK_KEY ->
case mnesia:next(?ACL_TABLE, ?MIGRATION_MARK_KEY) of
'$end_of_table' -> true;
_OtherKey -> false
end;
'$end_of_table' -> false;
_OtherKey -> false
end
end),
case Result of
{atomic, true} ->
true;
_ ->
false
end.
add_migration_mark() ->
{atomic, ok} = mnesia:transaction(fun() -> mnesia:write(?MIGRATION_MARK_RECORD) end),
ok.
peek_record() ->
Key = mnesia:dirty_first(?ACL_TABLE),
case Key of
?MIGRATION_MARK_KEY ->
mnesia:dirty_next(?ACL_TABLE, Key);
_ -> Key
end.
do_migrate_records('$end_of_table') -> ok;
do_migrate_records({_Login, _Topic} = Key) ->
?tp(emqx_acl_mnesia_migrator_record_selected, #{key => Key}),
_ = mnesia:transaction(fun migrate_one_record/1, [Key]),
do_migrate_records(peek_record()).
migrate_one_record({Login, _Topic} = Key) ->
case mnesia:wread({?ACL_TABLE, Key}) of
[] ->
?tp(emqx_acl_mnesia_migrator_record_missed, #{key => Key}),
record_missing;
OldRecs ->
Acls = mnesia:wread({?ACL_TABLE2, Login}),
UpdatedAcl = emqx_acl_mnesia_db:merge_acl_records(Login, OldRecs, Acls),
ok = mnesia:write(UpdatedAcl),
ok = mnesia:delete({?ACL_TABLE, Key}),
?tp(emqx_acl_mnesia_migrator_record_migrated, #{key => Key})
end.

View File

@ -1,6 +1,6 @@
{application, emqx_auth_mnesia,
[{description, "EMQ X Authentication with Mnesia"},
{vsn, "4.3.0"}, % strict semver, bump manually
{vsn, "4.3.4"}, % strict semver, bump manually
{modules, []},
{registered, []},
{applications, [kernel,stdlib,mnesia]},

View File

@ -0,0 +1,31 @@
%% -*- mode: erlang -*-
{VSN,
[
{<<"4.3.[0-3]">>, [
{add_module,emqx_acl_mnesia_db},
{add_module,emqx_acl_mnesia_migrator, [emqx_acl_mnesia_db]},
{update, emqx_auth_mnesia_sup, supervisor},
{apply, {emqx_acl_mnesia_migrator, start_supervised, []}},
{load_module,emqx_auth_mnesia_api, brutal_purge,soft_purge,[]},
{load_module,emqx_acl_mnesia, brutal_purge,soft_purge,[]},
{load_module,emqx_acl_mnesia_api, brutal_purge,soft_purge,[]},
{load_module,emqx_acl_mnesia_cli, brutal_purge,soft_purge,[]}
]},
{<<".*">>, [
]}
],
[
{<<"4.3.[0-3]">>, [
{apply, {emqx_acl_mnesia_migrator, stop_supervised, []}},
{update, emqx_auth_mnesia_sup, supervisor},
{load_module,emqx_acl_mnesia_cli, brutal_purge,soft_purge,[]},
{load_module,emqx_acl_mnesia_api, brutal_purge,soft_purge,[]},
{load_module,emqx_auth_mnesia_api, brutal_purge,soft_purge,[]},
{load_module,emqx_acl_mnesia, brutal_purge,soft_purge,[]},
{delete_module,emqx_acl_mnesia_migrator},
{delete_module,emqx_acl_mnesia_db}
]},
{<<".*">>, [
]}
]
}.

View File

@ -23,7 +23,7 @@
-import(proplists, [get_value/2]).
-import(minirest, [return/1]).
-export([paginate/5]).
-export([paginate_qh/5]).
-export([ list_clientid/2
, lookup_clientid/2
@ -133,15 +133,15 @@ add_clientid(_Bindings, Params) ->
end.
do_add_clientid([ Params | ParamsN ], ReList ) ->
Clientid = urldecode(get_value(<<"clientid">>, Params)),
Clientid = get_value(<<"clientid">>, Params),
do_add_clientid(ParamsN, [{Clientid, format_msg(do_add_clientid(Params))} | ReList]);
do_add_clientid([], ReList) ->
{ok, ReList}.
do_add_clientid(Params) ->
Clientid = urldecode(get_value(<<"clientid">>, Params)),
Password = urldecode(get_value(<<"password">>, Params)),
Clientid = get_value(<<"clientid">>, Params),
Password = get_value(<<"password">>, Params),
Login = {clientid, Clientid},
case validate([login, password], [Login, Password]) of
ok ->
@ -152,7 +152,7 @@ do_add_clientid(Params) ->
update_clientid(#{clientid := Clientid}, Params) ->
Password = get_value(<<"password">>, Params),
case validate([password], [Password]) of
ok -> return(emqx_auth_mnesia_cli:update_user({clientid, urldecode(Clientid)}, urldecode(Password)));
ok -> return(emqx_auth_mnesia_cli:update_user({clientid, urldecode(Clientid)}, Password));
Err -> return(Err)
end.
@ -182,15 +182,15 @@ add_username(_Bindings, Params) ->
end.
do_add_username([ Params | ParamsN ], ReList ) ->
Username = urldecode(get_value(<<"username">>, Params)),
Username = get_value(<<"username">>, Params),
do_add_username(ParamsN, [{Username, format_msg(do_add_username(Params))} | ReList]);
do_add_username([], ReList) ->
{ok, ReList}.
do_add_username(Params) ->
Username = urldecode(get_value(<<"username">>, Params)),
Password = urldecode(get_value(<<"password">>, Params)),
Username = get_value(<<"username">>, Params),
Password = get_value(<<"password">>, Params),
Login = {username, Username},
case validate([login, password], [Login, Password]) of
ok ->
@ -201,7 +201,7 @@ do_add_username(Params) ->
update_username(#{username := Username}, Params) ->
Password = get_value(<<"password">>, Params),
case validate([password], [Password]) of
ok -> return(emqx_auth_mnesia_cli:update_user({username, urldecode(Username)}, urldecode(Password)));
ok -> return(emqx_auth_mnesia_cli:update_user({username, urldecode(Username)}, Password));
Err -> return(Err)
end.
@ -212,9 +212,12 @@ delete_username(#{username := Username}, _) ->
%% Paging Query
%%------------------------------------------------------------------------------
paginate(Tables, MatchSpec, Params, ComparingFun, RowFun) ->
Qh = query_handle(Tables, MatchSpec),
Count = count(Tables, MatchSpec),
paginate(Table, MatchSpec, Params, ComparingFun, RowFun) ->
Qh = query_handle(Table, MatchSpec),
Count = count(Table, MatchSpec),
paginate_qh(Qh, Count, Params, ComparingFun, RowFun).
paginate_qh(Qh, Count, Params, ComparingFun, RowFun) ->
Page = page(Params),
Limit = limit(Params),
Cursor = qlc:cursor(Qh),
@ -231,24 +234,12 @@ paginate(Tables, MatchSpec, Params, ComparingFun, RowFun) ->
query_handle(Table, MatchSpec) when is_atom(Table) ->
Options = {traverse, {select, MatchSpec}},
qlc:q([R|| R <- ets:table(Table, Options)]);
query_handle([Table], MatchSpec) when is_atom(Table) ->
Options = {traverse, {select, MatchSpec}},
qlc:q([R|| R <- ets:table(Table, Options)]);
query_handle(Tables, MatchSpec) ->
Options = {traverse, {select, MatchSpec}},
qlc:append([qlc:q([E || E <- ets:table(T, Options)]) || T <- Tables]).
qlc:q([R || R <- ets:table(Table, Options)]).
count(Table, MatchSpec) when is_atom(Table) ->
[{MatchPattern, Where, _Re}] = MatchSpec,
NMatchSpec = [{MatchPattern, Where, [true]}],
ets:select_count(Table, NMatchSpec);
count([Table], MatchSpec) when is_atom(Table) ->
[{MatchPattern, Where, _Re}] = MatchSpec,
NMatchSpec = [{MatchPattern, Where, [true]}],
ets:select_count(Table, NMatchSpec);
count(Tables, MatchSpec) ->
lists:sum([count(T, MatchSpec) || T <- Tables]).
ets:select_count(Table, NMatchSpec).
page(Params) ->
binary_to_integer(proplists:get_value(<<"_page">>, Params, <<"1">>)).
@ -263,13 +254,11 @@ limit(Params) ->
%% Interval Funcs
%%------------------------------------------------------------------------------
format([{?TABLE, {clientid, ClientId}, Password, _InterTime}]) ->
#{clientid => ClientId,
password => Password};
format([{?TABLE, {clientid, ClientId}, _Password, _InterTime}]) ->
#{clientid => ClientId};
format([{?TABLE, {username, Username}, Password, _InterTime}]) ->
#{username => Username,
password => Password};
format([{?TABLE, {username, Username}, _Password, _InterTime}]) ->
#{username => Username};
format([]) ->
#{}.

View File

@ -33,4 +33,16 @@ start_link() ->
%%--------------------------------------------------------------------
init([]) ->
{ok, {{one_for_one, 10, 100}, []}}.
{ok, {{one_for_one, 10, 100}, [
child_spec(emqx_acl_mnesia_migrator, worker, [])
]}}.
child_spec(M, worker, Args) ->
#{id => M,
start => {M, start_link, Args},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [M]
}.

View File

@ -22,6 +22,7 @@
-include("emqx_auth_mnesia.hrl").
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
-import(emqx_ct_http, [ request_api/3
, request_api/5
@ -39,10 +40,15 @@ all() ->
emqx_ct:all(?MODULE).
groups() ->
[].
[{async_migration_tests, [sequence], [
t_old_and_new_acl_migration_by_migrator,
t_old_and_new_acl_migration_repeated_by_migrator,
t_migration_concurrency
]}].
init_per_suite(Config) ->
emqx_ct_helpers:start_apps([emqx_modules, emqx_management, emqx_auth_mnesia], fun set_special_configs/1),
supervisor:terminate_child(emqx_auth_mnesia_sup, emqx_acl_mnesia_migrator),
create_default_app(),
Config.
@ -50,14 +56,32 @@ end_per_suite(_Config) ->
delete_default_app(),
emqx_ct_helpers:stop_apps([emqx_modules, emqx_management, emqx_auth_mnesia]).
init_per_testcase(t_check_acl_as_clientid, Config) ->
init_per_testcase_clean(_, Config) ->
mnesia:clear_table(?ACL_TABLE),
mnesia:clear_table(?ACL_TABLE2),
Config.
init_per_testcase_emqx_hook(t_check_acl_as_clientid, Config) ->
emqx:hook('client.check_acl', fun emqx_acl_mnesia:check_acl/5, [#{key_as => clientid}]),
Config;
init_per_testcase(_, Config) ->
init_per_testcase_emqx_hook(_, Config) ->
emqx:hook('client.check_acl', fun emqx_acl_mnesia:check_acl/5, [#{key_as => username}]),
Config.
init_per_testcase_migration(t_management_before_migration, Config) ->
Config;
init_per_testcase_migration(_, Config) ->
emqx_acl_mnesia_migrator:migrate_records(),
Config.
init_per_testcase(Case, Config) ->
PerTestInitializers = [
fun init_per_testcase_clean/2,
fun init_per_testcase_migration/2,
fun init_per_testcase_emqx_hook/2
],
lists:foldl(fun(Init, Conf) -> Init(Case, Conf) end, Config, PerTestInitializers).
end_per_testcase(_, Config) ->
emqx:unhook('client.check_acl', fun emqx_acl_mnesia:check_acl/5),
Config.
@ -76,25 +100,34 @@ set_special_configs(_App) ->
%% Testcases
%%------------------------------------------------------------------------------
t_management(_Config) ->
clean_all_acls(),
?assertEqual("Acl with Mnesia", emqx_acl_mnesia:description()),
?assertEqual([], emqx_acl_mnesia_cli:all_acls()),
t_management_before_migration(_Config) ->
{atomic, IsStarted} = mnesia:transaction(fun emqx_acl_mnesia_db:is_migration_started/0),
?assertNot(IsStarted),
run_acl_tests().
ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/%c">>, sub, allow),
ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/+">>, pub, deny),
ok = emqx_acl_mnesia_cli:add_acl({username, <<"test_username">>}, <<"topic/%u">>, sub, deny),
ok = emqx_acl_mnesia_cli:add_acl({username, <<"test_username">>}, <<"topic/+">>, pub, allow),
ok = emqx_acl_mnesia_cli:add_acl(all, <<"#">>, pubsub, deny),
t_management_after_migration(_Config) ->
{atomic, IsStarted} = mnesia:transaction(fun emqx_acl_mnesia_db:is_migration_started/0),
?assert(IsStarted),
run_acl_tests().
run_acl_tests() ->
?assertEqual("Acl with Mnesia", emqx_acl_mnesia:description()),
?assertEqual([], emqx_acl_mnesia_db:all_acls()),
ok = emqx_acl_mnesia_db:add_acl({clientid, <<"test_clientid">>}, <<"topic/%c">>, sub, allow),
ok = emqx_acl_mnesia_db:add_acl({clientid, <<"test_clientid">>}, <<"topic/+">>, pub, deny),
ok = emqx_acl_mnesia_db:add_acl({username, <<"test_username">>}, <<"topic/%u">>, sub, deny),
ok = emqx_acl_mnesia_db:add_acl({username, <<"test_username">>}, <<"topic/+">>, pub, allow),
ok = emqx_acl_mnesia_db:add_acl(all, <<"#">>, pubsub, deny),
%% Sleeps below are needed to hide the race condition between
%% mnesia and ets dirty select in check_acl, that make this test
%% flaky
timer:sleep(100),
?assertEqual(2, length(emqx_acl_mnesia_cli:lookup_acl({clientid, <<"test_clientid">>}))),
?assertEqual(2, length(emqx_acl_mnesia_cli:lookup_acl({username, <<"test_username">>}))),
?assertEqual(2, length(emqx_acl_mnesia_cli:lookup_acl(all))),
?assertEqual(6, length(emqx_acl_mnesia_cli:all_acls())),
?assertEqual(2, length(emqx_acl_mnesia_db:lookup_acl({clientid, <<"test_clientid">>}))),
?assertEqual(2, length(emqx_acl_mnesia_db:lookup_acl({username, <<"test_username">>}))),
?assertEqual(2, length(emqx_acl_mnesia_db:lookup_acl(all))),
?assertEqual(6, length(emqx_acl_mnesia_db:all_acls())),
User1 = #{zone => external, clientid => <<"test_clientid">>},
User2 = #{zone => external, clientid => <<"no_exist">>, username => <<"test_username">>},
@ -110,30 +143,30 @@ t_management(_Config) ->
deny = emqx_access_control:check_acl(User3, publish, <<"topic/A/B">>),
%% Test merging of pubsub capability:
ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pubsub, deny),
ok = emqx_acl_mnesia_db:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pubsub, deny),
timer:sleep(100),
deny = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>),
deny = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>),
ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pub, allow),
ok = emqx_acl_mnesia_db:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pub, allow),
timer:sleep(100),
deny = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>),
allow = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>),
ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pubsub, allow),
ok = emqx_acl_mnesia_db:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pubsub, allow),
timer:sleep(100),
allow = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>),
allow = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>),
ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, sub, deny),
ok = emqx_acl_mnesia_db:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, sub, deny),
timer:sleep(100),
deny = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>),
allow = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>),
ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pub, deny),
ok = emqx_acl_mnesia_db:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pub, deny),
timer:sleep(100),
deny = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>),
deny = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>),
%% Test implicit migration of pubsub to pub and sub:
ok = emqx_acl_mnesia_cli:remove_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>),
ok = mnesia:dirty_write(#emqx_acl{
ok = emqx_acl_mnesia_db:remove_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>),
ok = mnesia:dirty_write(#?ACL_TABLE{
filter = {{clientid, <<"test_clientid">>}, <<"topic/mix">>},
action = pubsub,
access = allow,
@ -142,24 +175,130 @@ t_management(_Config) ->
timer:sleep(100),
allow = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>),
allow = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>),
ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pub, deny),
ok = emqx_acl_mnesia_db:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, pub, deny),
timer:sleep(100),
allow = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>),
deny = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>),
ok = emqx_acl_mnesia_cli:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, sub, deny),
ok = emqx_acl_mnesia_db:add_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>, sub, deny),
timer:sleep(100),
deny = emqx_access_control:check_acl(User1, subscribe, <<"topic/mix">>),
deny = emqx_access_control:check_acl(User1, publish, <<"topic/mix">>),
ok = emqx_acl_mnesia_cli:remove_acl({clientid, <<"test_clientid">>}, <<"topic/%c">>),
ok = emqx_acl_mnesia_cli:remove_acl({clientid, <<"test_clientid">>}, <<"topic/+">>),
ok = emqx_acl_mnesia_cli:remove_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>),
ok = emqx_acl_mnesia_cli:remove_acl({username, <<"test_username">>}, <<"topic/%u">>),
ok = emqx_acl_mnesia_cli:remove_acl({username, <<"test_username">>}, <<"topic/+">>),
ok = emqx_acl_mnesia_cli:remove_acl(all, <<"#">>),
ok = emqx_acl_mnesia_db:remove_acl({clientid, <<"test_clientid">>}, <<"topic/%c">>),
ok = emqx_acl_mnesia_db:remove_acl({clientid, <<"test_clientid">>}, <<"topic/+">>),
ok = emqx_acl_mnesia_db:remove_acl({clientid, <<"test_clientid">>}, <<"topic/mix">>),
ok = emqx_acl_mnesia_db:remove_acl({username, <<"test_username">>}, <<"topic/%u">>),
ok = emqx_acl_mnesia_db:remove_acl({username, <<"test_username">>}, <<"topic/+">>),
ok = emqx_acl_mnesia_db:remove_acl(all, <<"#">>),
timer:sleep(100),
?assertEqual([], emqx_acl_mnesia_cli:all_acls()).
?assertEqual([], emqx_acl_mnesia_db:all_acls()).
t_old_and_new_acl_combination(_Config) ->
create_conflicting_records(),
?assertEqual(combined_conflicting_records(), emqx_acl_mnesia_db:all_acls()),
?assertEqual(
lists:usort(combined_conflicting_records()),
lists:usort(emqx_acl_mnesia_db:all_acls_export())).
t_old_and_new_acl_migration(_Config) ->
create_conflicting_records(),
emqx_acl_mnesia_migrator:migrate_records(),
?assertEqual(combined_conflicting_records(), emqx_acl_mnesia_db:all_acls()),
?assertEqual(
lists:usort(combined_conflicting_records()),
lists:usort(emqx_acl_mnesia_db:all_acls_export())),
% check that old table is not popoulated anymore
ok = emqx_acl_mnesia_db:add_acl({clientid, <<"test_clientid">>}, <<"topic/%c">>, sub, allow),
?assert(emqx_acl_mnesia_migrator:is_old_table_migrated()).
t_migration_concurrency(_Config) ->
Key = {{clientid,<<"client6">>}, <<"t">>},
Record = #?ACL_TABLE{filter = Key, action = pubsub, access = deny, created_at = 0},
{atomic, ok} = mnesia:transaction(fun mnesia:write/1, [Record]),
LockWaitAndDelete =
fun() ->
[_Rec] = mnesia:wread({?ACL_TABLE, Key}),
{{Pid, Ref}, _} =
?wait_async_action(spawn_monitor(fun emqx_acl_mnesia_migrator:migrate_records/0),
#{?snk_kind := emqx_acl_mnesia_migrator_record_selected},
1000),
mnesia:delete({?ACL_TABLE, Key}),
{Pid, Ref}
end,
?check_trace(
begin
{atomic, {Pid, Ref}} = mnesia:transaction(LockWaitAndDelete),
receive {'DOWN', Ref, process, Pid, _} -> ok end
end,
fun(_, Trace) ->
?assertMatch([_], ?of_kind(emqx_acl_mnesia_migrator_record_missed, Trace))
end),
?assert(emqx_acl_mnesia_migrator:is_old_table_migrated()),
?assertEqual([], emqx_acl_mnesia_db:all_acls()).
t_old_and_new_acl_migration_by_migrator(_Config) ->
create_conflicting_records(),
meck:new(fake_nodes, [non_strict]),
meck:expect(fake_nodes, all, fun() -> [node(), 'somebadnode@127.0.0.1'] end),
?check_trace(
begin
% check all nodes every 30 ms
{ok, _} = emqx_acl_mnesia_migrator:start_link(#{
name => ct_migrator,
check_nodes_interval => 30,
get_nodes => fun fake_nodes:all/0
}),
timer:sleep(100)
end,
fun(_, Trace) ->
?assertEqual([], ?of_kind(emqx_acl_mnesia_migrator_start_migration, Trace))
end),
?check_trace(
begin
meck:expect(fake_nodes, all, fun() -> [node()] end),
timer:sleep(100)
end,
fun(_, Trace) ->
?assertMatch([_], ?of_kind(emqx_acl_mnesia_migrator_finish, Trace))
end),
meck:unload(fake_nodes),
?assertEqual(combined_conflicting_records(), emqx_acl_mnesia_db:all_acls()),
?assert(emqx_acl_mnesia_migrator:is_old_table_migrated()).
t_old_and_new_acl_migration_repeated_by_migrator(_Config) ->
create_conflicting_records(),
emqx_acl_mnesia_migrator:migrate_records(),
?check_trace(
begin
{ok, _} = emqx_acl_mnesia_migrator:start_link(ct_migrator),
timer:sleep(100)
end,
fun(_, Trace) ->
?assertEqual([], ?of_kind(emqx_acl_mnesia_migrator_start_migration, Trace)),
?assertMatch([_], ?of_kind(emqx_acl_mnesia_migrator_finish, Trace))
end).
t_start_stop_supervised(_Config) ->
?assertEqual(undefined, whereis(emqx_acl_mnesia_migrator)),
ok = emqx_acl_mnesia_migrator:start_supervised(),
?assert(is_pid(whereis(emqx_acl_mnesia_migrator))),
ok = emqx_acl_mnesia_migrator:stop_supervised(),
?assertEqual(undefined, whereis(emqx_acl_mnesia_migrator)).
t_acl_cli(_Config) ->
meck:new(emqx_ctl, [non_strict, passthrough]),
@ -168,8 +307,6 @@ t_acl_cli(_Config) ->
meck:expect(emqx_ctl, usage, fun(Usages) -> emqx_ctl:format_usage(Usages) end),
meck:expect(emqx_ctl, usage, fun(Cmd, Descr) -> emqx_ctl:format_usage(Cmd, Descr) end),
clean_all_acls(),
?assertEqual(0, length(emqx_acl_mnesia_cli:cli(["list"]))),
emqx_acl_mnesia_cli:cli(["add", "clientid", "test_clientid", "topic/A", "pub", "deny"]),
@ -202,8 +339,6 @@ t_acl_cli(_Config) ->
meck:unload(emqx_ctl).
t_rest_api(_Config) ->
clean_all_acls(),
Params1 = [#{<<"clientid">> => <<"test_clientid">>,
<<"topic">> => <<"topic/A">>,
<<"action">> => <<"pub">>,
@ -273,13 +408,24 @@ t_rest_api(_Config) ->
{ok, Res3} = request_http_rest_list(["$all"]),
?assertMatch([], get_http_data(Res3)).
%%------------------------------------------------------------------------------
%% Helpers
%%------------------------------------------------------------------------------
clean_all_acls() ->
[ mnesia:dirty_delete({emqx_acl, Login})
|| Login <- mnesia:dirty_all_keys(emqx_acl)].
create_conflicting_records() ->
Records = [
#?ACL_TABLE{filter = {{clientid,<<"client6">>}, <<"t">>}, action = pubsub, access = deny, created_at = 0},
#?ACL_TABLE{filter = {{clientid,<<"client5">>}, <<"t">>}, action = pubsub, access = deny, created_at = 1},
#?ACL_TABLE2{who = {clientid,<<"client5">>}, rules = [{allow, sub, <<"t">>, 2}]}
],
mnesia:transaction(fun() -> lists:foreach(fun mnesia:write/1, Records) end).
combined_conflicting_records() ->
% pubsub's are split, ACL_TABLE2 rules shadow ACL_TABLE rules
[
{{clientid,<<"client5">>},<<"t">>,sub,allow,2},
{{clientid,<<"client5">>},<<"t">>,pub,deny,1},
{{clientid,<<"client6">>},<<"t">>,sub,deny,0},
{{clientid,<<"client6">>},<<"t">>,pub,deny,0}
].
%%--------------------------------------------------------------------
%% HTTP Request

View File

@ -7,6 +7,12 @@
## Value: single | unknown | sharded | rs
auth.mongo.type = single
## Whether to use SRV and TXT records.
##
## Value: true | false
## Default: false
auth.mongo.srv_record = false
## The set name if type is rs.
##
## Value: String
@ -37,7 +43,6 @@ auth.mongo.pool = 8
## MongoDB AuthSource
##
## Value: String
## Default: mqtt
## auth.mongo.auth_source = admin
## MongoDB database

View File

@ -6,8 +6,12 @@
{datatype, {enum, [single, unknown, sharded, rs]}}
]}.
{mapping, "auth.mongo.srv_record", "emqx_auth_mongo.server", [
{default, false},
{datatype, {enum, [true, false]}}
]}.
{mapping, "auth.mongo.rs_set_name", "emqx_auth_mongo.server", [
{default, "mqtt"},
{datatype, string}
]}.
@ -41,7 +45,6 @@
]}.
{mapping, "auth.mongo.auth_source", "emqx_auth_mongo.server", [
{default, "mqtt"},
{datatype, string}
]}.
@ -101,9 +104,9 @@
]}.
{translation, "emqx_auth_mongo.server", fun(Conf) ->
H = cuttlefish:conf_get("auth.mongo.server", Conf),
Hosts = string:tokens(H, ","),
Type0 = cuttlefish:conf_get("auth.mongo.type", Conf),
SrvRecord = cuttlefish:conf_get("auth.mongo.srv_record", Conf, false),
Server = cuttlefish:conf_get("auth.mongo.server", Conf),
Type = cuttlefish:conf_get("auth.mongo.type", Conf),
Pool = cuttlefish:conf_get("auth.mongo.pool", Conf),
%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0
Login = cuttlefish:conf_get("auth.mongo.username", Conf,
@ -111,7 +114,10 @@
),
Passwd = cuttlefish:conf_get("auth.mongo.password", Conf),
DB = cuttlefish:conf_get("auth.mongo.database", Conf),
AuthSrc = cuttlefish:conf_get("auth.mongo.auth_source", Conf),
AuthSource = case cuttlefish:conf_get("auth.mongo.auth_source", Conf, undefined) of
undefined -> [];
AuthSource0 -> [{auth_source, list_to_binary(AuthSource0)}]
end,
R = cuttlefish:conf_get("auth.mongo.w_mode", Conf),
W = cuttlefish:conf_get("auth.mongo.r_mode", Conf),
Login0 = case Login =:= [] of
@ -156,8 +162,8 @@
false -> []
end,
WorkerOptions = [{database, list_to_binary(DB)}, {auth_source, list_to_binary(AuthSrc)}]
++ Login0 ++ Passwd0 ++ W0 ++ R0 ++ Ssl,
WorkerOptions = [{database, list_to_binary(DB)}]
++ Login0 ++ Passwd0 ++ W0 ++ R0 ++ Ssl ++ AuthSource,
Vars = cuttlefish_variable:fuzzy_matches(["auth", "mongo", "topology", "$name"], Conf),
Options = lists:map(fun({_, Name}) ->
@ -174,16 +180,17 @@
{list_to_atom(Name2), cuttlefish:conf_get("auth.mongo.topology."++Name, Conf)}
end, Vars),
Type = case Type0 =:= rs of
true -> {Type0, list_to_binary(cuttlefish:conf_get("auth.mongo.rs_set_name", Conf))};
false -> Type0
ReplicaSet = case cuttlefish:conf_get("auth.mongo.rs_set_name", Conf, undefined) of
undefined -> [];
ReplicaSet0 -> [{rs_set_name, list_to_binary(ReplicaSet0)}]
end,
[{type, Type},
{hosts, Hosts},
[{srv_record, SrvRecord},
{type, Type},
{server, Server},
{options, Options},
{worker_options, WorkerOptions},
{auto_reconnect, 1},
{pool_size, Pool}]
{pool_size, Pool}] ++ ReplicaSet
end}.
%% The mongodb operation timeout is specified by the value of `cursor_timeout` from application config,

View File

@ -1,6 +1,6 @@
{deps,
%% NOTE: mind poolboy version when updating mongodb-erlang version
[{mongodb, {git,"https://github.com/emqx/mongodb-erlang", {tag, "v3.0.7"}}},
[{mongodb, {git,"https://github.com/emqx/mongodb-erlang", {tag, "v3.0.10"}}},
%% mongodb-erlang uses a special fork https://github.com/comtihon/poolboy.git
%% (which has overflow_ttl feature added).
%% However, it references `{branch, "master}` (commit 9c06a9a on 2021-04-07).

View File

@ -1,6 +1,6 @@
{application, emqx_auth_mongo,
[{description, "EMQ X Authentication/ACL with MongoDB"},
{vsn, "4.3.0"}, % strict semver, bump manually!
{vsn, "4.4.0"}, % strict semver, bump manually!
{modules, []},
{registered, [emqx_auth_mongo_sup]},
{applications, [kernel,stdlib,mongodb,ecpool]},

View File

@ -28,7 +28,96 @@ start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
{ok, PoolEnv} = application:get_env(?APP, server),
PoolSpec = ecpool:pool_spec(?APP, ?APP, ?APP, PoolEnv),
{ok, Opts} = application:get_env(?APP, server),
NOpts = may_parse_srv_and_txt_records(Opts),
PoolSpec = ecpool:pool_spec(?APP, ?APP, ?APP, NOpts),
{ok, {{one_for_all, 10, 100}, [PoolSpec]}}.
may_parse_srv_and_txt_records(Opts) when is_list(Opts) ->
maps:to_list(may_parse_srv_and_txt_records(maps:from_list(Opts)));
may_parse_srv_and_txt_records(#{type := Type,
srv_record := false,
server := Server} = Opts) ->
Hosts = to_hosts(Server),
case Type =:= rs of
true ->
case maps:get(rs_set_name, Opts, undefined) of
undefined ->
error({missing_parameter, rs_set_name});
ReplicaSet ->
Opts#{type => {rs, ReplicaSet},
hosts => Hosts}
end;
false ->
Opts#{hosts => Hosts}
end;
may_parse_srv_and_txt_records(#{type := Type,
srv_record := true,
server := Server,
worker_options := WorkerOptions} = Opts) ->
Hosts = parse_srv_records(Server),
Opts0 = parse_txt_records(Type, Server),
NWorkerOptions = maps:to_list(maps:merge(maps:from_list(WorkerOptions), maps:with([auth_source], Opts0))),
NOpts = Opts#{hosts => Hosts, worker_options => NWorkerOptions},
case Type =:= rs of
true ->
case maps:get(rs_set_name, Opts0, maps:get(rs_set_name, NOpts, undefined)) of
undefined ->
error({missing_parameter, rs_set_name});
ReplicaSet ->
NOpts#{type => {Type, ReplicaSet}}
end;
false ->
NOpts
end.
to_hosts(Server) ->
[string:trim(H) || H <- string:tokens(Server, ",")].
parse_srv_records(Server) ->
case inet_res:lookup("_mongodb._tcp." ++ Server, in, srv) of
[] ->
error(service_not_found);
Services ->
[Host ++ ":" ++ integer_to_list(Port) || {_, _, Port, Host} <- Services]
end.
parse_txt_records(Type, Server) ->
case inet_res:lookup(Server, in, txt) of
[] ->
#{};
[[QueryString]] ->
case uri_string:dissect_query(QueryString) of
{error, _, _} ->
error({invalid_txt_record, invalid_query_string});
Options ->
Fields = case Type of
rs -> ["authSource", "replicaSet"];
_ -> ["authSource"]
end,
take_and_convert(Fields, Options)
end;
_ ->
error({invalid_txt_record, multiple_records})
end.
take_and_convert(Fields, Options) ->
take_and_convert(Fields, Options, #{}).
take_and_convert([], [_ | _], _Acc) ->
error({invalid_txt_record, invalid_option});
take_and_convert([], [], Acc) ->
Acc;
take_and_convert([Field | More], Options, Acc) ->
case lists:keytake(Field, 1, Options) of
{value, {"authSource", V}, NOptions} ->
take_and_convert(More, NOptions, Acc#{auth_source => list_to_binary(V)});
{value, {"replicaSet", V}, NOptions} ->
take_and_convert(More, NOptions, Acc#{rs_set_name => list_to_binary(V)});
{value, _, _} ->
error({invalid_txt_record, invalid_option});
false ->
take_and_convert(More, Options, Acc)
end.

View File

@ -1,5 +1,5 @@
{deps,
[{epgsql, {git, "https://github.com/epgsql/epgsql", {tag, "4.4.0"}}}
[{epgsql, {git, "https://github.com/epgsql/epgsql.git", {tag, "4.4.0"}}}
]}.
{erl_opts, [warn_unused_vars,

View File

@ -1,23 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID1zCCAb8CCQC/+qKgZd+m/DANBgkqhkiG9w0BAQsFADA1MRMwEQYDVQQKDApS
ZWRpcyBUZXN0MR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjAx
MDI5MDEzNDE2WhcNMjExMDI5MDEzNDE2WjAmMRMwEQYDVQQKDApSZWRpcyBUZXN0
MIID1zCCAb8CCQC/+qKgZd+m/jANBgkqhkiG9w0BAQsFADA1MRMwEQYDVQQKDApS
ZWRpcyBUZXN0MR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjEx
MTAxMDgwMDU1WhcNMzExMDMwMDgwMDU1WjAmMRMwEQYDVQQKDApSZWRpcyBUZXN0
MQ8wDQYDVQQDDAZTZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDSs3bQ9sYi2AhFuHU75Ryk1HHSgfzA6pQAJilmJdTy0s5vyiWe1HQJaWkMcS5V
GVzGMK+c+OBqtXtDDninL3betg1YPMjSCOjPMOTC1H9K7+effwf7Iwpnw9Zro8mb
TEmMslIYhhcDedzT9Owli4QAgbgTn4l1BYuKX9CLrrKFtnr21miKu3ydViy9q7T1
pib3eigvAyk7X2fadHFArGEttsXrD6cetPPkSF/1OLWNlqzUKXzhSyrBXzO44Kks
fwR/EpTiES9g4dNOL2wvKS/YE1fNKhiCENrNxTXQo1l0yOdm2+MeyOeHFzRuS0b/
+uGDFOPPi04KXeO6dQ5olBCPAgMBAAEwDQYJKoZIhvcNAQELBQADggIBADn0E2vG
iQWe8/I7VbBdPhPNupVNcLvew10eIHxY2g5vSruCSVRQTgk8itVMRmDQxbb7gdDW
jnCRbxykxbLjM9iCRljnOCsIcTi7qO7JRl8niV8dtEpPOs9lZxEdNXjIV1iZoWf3
arBbPQSyQZvTQHG6qbFnyCdMMyyXGGvEPGQDaBiKH+Ko1qeAbCi0zupChYvxmtZ8
hSTPlMFezDT9bKoNY0pkJSELfokEPU/Pn6Lz/NVbdzmCMjVa/xmF3s31g+DGhz95
4AyOnCr6o0aydPVVV3pB/BCezNXPUxpp53BG0w/K2f2DnKYCvGvJbqDAaJ8bG/J1
EFSOmwobdwVxJz3KNubmo1qJ6xOl/YT7yyqPRQRM1SY8nZW+YcoJSZjOe8wJVlob
d0bOwN1C3HQwomyMWes187bEQP6Y36HuEbR1fK8yIOzGsGDKRFAFwQwMgw2M91lr
EJIP5NRD3OZRuiYDiVfVhDZDaNahrAMZUcPCgeCAwc4YG6Gp2sDtdorOl4kIJYWE
BbBZ0Jplq9+g6ciu5ChjAW8iFl0Ae5U24MxPGXnrxiRF4WWxLeZMVLXLDvlPqReD
CHII5ifyvGEt5+RhqtZC/L+HimL+5wQgOlntqhUdLb6yWRz7YW37PFMnUXU3MXe9
uY7m73ZLluXiLojcZxU2+cx89u5FOJxrYtrj
+uGDFOPPi04KXeO6dQ5olBCPAgMBAAEwDQYJKoZIhvcNAQELBQADggIBALRSylnk
JJhEFRniuQ+H1kbfZlVOqnSqGkm38r8X76dnYRZfkFlxVzU2q5HPnSdiL9D3JrrH
P7wIA5zjr76zK7GPJjkRExRZy5sTLSpsGp7YIGAZ19J3KsDVHSOvPTl38c6L217a
YzPeQL5IrrW55URmA5PZFu3lsm9z7CNguw1wn2pCNNB+r/cRl4iELehZJT891CQe
nV9a1YfHY/DkDoMnmrKqmeYdvje8n1uSqTnIV/wNiASU36ztxxD8ZmwprxxbjLSs
aBjBvsR/eBHbWrz2W1dc5ppgGLuCkiEKmh6/IWX/ZQqyBCzZkmFNiTs8QiLtmoC4
2bXkPVSyq5wT7eisGbRdcY9vGDtoW/WZOmFVA4XEDVx8M9fb4speHwoHRuTfWsA0
6Y8P9XpYjG2tQoPpxrZaRshZ+SiHWPov7cAvY34szFePfTWR8gzbL6SgpDz30ceh
XIuTArOMQMhfWHn3NaOc6hlkRsoviNhc5IXR9VjIdaNJCamEoLVNWZsvHJCUiP10
yx+9/0a9vI6G+i8oKQ+eKJsfP8Ikoiolf7vU6M+/1kF+sSMxGjFwkMCxLgZB67+a
m9kw83sVfykWLQ3eRwhdBz0/JiiYtDbbtyqgs3kPhJs9SGZUhDc/7R0lTWf4zxoJ
l3y7pn/3nJvYrGX7uCBbWPUuqWeHVM9Ip6AZ
-----END CERTIFICATE-----

View File

@ -114,6 +114,17 @@ bridge.mqtt.aws.keyfile = {{ platform_etc_dir }}/certs/client-key.pem
## Value: String
bridge.mqtt.aws.ciphers = TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA
## SSL peer validation with verify_peer or verify_none
## More information at: http://erlang.org/doc/man/ssl.html
##
## Value: true | false
#bridge.mqtt.aws.verify = false
## SSL hostname to be used in TLS Server Name Indication extension
##
## Value: String | disable
#bridge.mqtt.aws.server_name_indication = disable
## Ciphers for TLS PSK.
## Note that 'bridge.${BridgeName}.ciphers' and 'bridge.${BridgeName}.psk_ciphers' cannot
## be configured at the same time.

View File

@ -75,6 +75,14 @@
{datatype, string}
]}.
{mapping, "bridge.mqtt.$name.verify", "emqx_bridge_mqtt.bridges", [
{datatype, {enum, [true, false]}}
]}.
{mapping, "bridge.mqtt.$name.server_name_indication", "emqx_bridge_mqtt.bridges", [
{datatype, string}
]}.
{mapping, "bridge.mqtt.$name.ciphers", "emqx_bridge_mqtt.bridges", [
{datatype, string}
]}.
@ -144,6 +152,8 @@
(ciphers) -> true;
(psk_ciphers) -> true;
(tls_versions) -> true;
(verify) -> true;
(server_name_indication) -> true;
(_Opt) -> false
end,
@ -153,6 +163,14 @@
[{ciphers, Split(Ciphers)}];
(psk_ciphers, Ciphers) ->
[{ciphers, MapPSKCiphers(Split(Ciphers))}, {user_lookup_fun, {fun emqx_psk:lookup/3, <<>>}}];
(verify, true) ->
[{verify, verify_peer}];
(verify, false) ->
[{verify, verify_none}];
(server_name_indication, "disabled") ->
[{server_name_indication, disabled}];
(server_name_indication, Hostname) ->
[{server_name_indication, Hostname}];
(Opt, Val) ->
[{Opt, Val}]
end,

View File

@ -1,6 +1,6 @@
{application, emqx_bridge_mqtt,
[{description, "EMQ X Bridge to MQTT Broker"},
{vsn, "4.3.1"}, % strict semver, bump manually!
{vsn, "4.3.3"}, % strict semver, bump manually!
{modules, []},
{registered, []},
{applications, [kernel,stdlib,replayq,emqtt]},

View File

@ -1,15 +1,23 @@
%% -*-: erlang -*-
{VSN,
{"4.3.3",
[
{<<"4.3.[1-2]">>, [
{load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []}
]},
{"4.3.0", [
{load_module, emqx_bridge_worker, brutal_purge, soft_purge, []}
{load_module, emqx_bridge_worker, brutal_purge, soft_purge, []},
{load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []}
]},
{<<".*">>, []}
],
[
{<<"4.3.[1-2]">>, [
{load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []}
]},
{"4.3.0", [
{load_module, emqx_bridge_worker, brutal_purge, soft_purge, []}
{load_module, emqx_bridge_worker, brutal_purge, soft_purge, []},
{load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []}
]},
{<<".*">>, []}
]

View File

@ -410,7 +410,24 @@ start_resource(ResId, PoolName, Options) ->
end.
test_resource_status(PoolName) ->
IsConnected = fun(Worker) ->
Parent = self(),
Pids = [spawn(fun() -> Parent ! {self(), get_worker_status(Worker)} end)
|| {_WorkerName, Worker} <- ecpool:workers(PoolName)],
try
Status = [
receive {Pid, R} -> R
after 1000 -> %% get_worker_status/1 should be a quick operation
throw({timeout, Pid})
end || Pid <- Pids],
lists:any(fun(St) -> St =:= true end, Status)
catch
throw:Reason ->
?LOG(error, "Get mqtt bridge status timeout: ~p", [Reason]),
lists:foreach(fun(Pid) -> exit(Pid, kill) end, Pids),
false
end.
get_worker_status(Worker) ->
case ecpool_worker:client(Worker) of
{ok, Bridge} ->
try emqx_bridge_worker:status(Bridge) of
@ -421,10 +438,7 @@ test_resource_status(PoolName) ->
end;
{error, _} ->
false
end
end,
Status = [IsConnected(Worker) || {_WorkerName, Worker} <- ecpool:workers(PoolName)],
lists:any(fun(St) -> St =:= true end, Status).
end.
-spec(on_get_resource_status(ResId::binary(), Params::map()) -> Status::map()).
on_get_resource_status(_ResId, #{<<"pool">> := PoolName}) ->

View File

@ -27,7 +27,7 @@
-define(wait(For, Timeout), emqx_ct_helpers:wait_for(?FUNCTION_NAME, ?LINE, fun() -> For end, Timeout)).
-define(SNK_WAIT(WHAT), ?assertMatch({ok, _}, ?block_until(#{?snk_kind := WHAT}, 2000, 1000))).
-define(SNK_WAIT(WHAT), ?assertMatch({ok, _}, ?block_until(#{?snk_kind := WHAT}, 5000, 5000))).
receive_messages(Count) ->
receive_messages(Count, []).

View File

@ -1,6 +1,6 @@
{application, emqx_coap,
[{description, "EMQ X CoAP Gateway"},
{vsn, "4.3.0"}, % strict semver, bump manually!
{vsn, "4.3.1"}, % strict semver, bump manually!
{modules, []},
{registered, []},
{applications, [kernel,stdlib,gen_coap]},

View File

@ -0,0 +1,9 @@
%% -*-: erlang -*-
{VSN,
[{"4.3.0",[
{load_module, emqx_coap_mqtt_adapter, brutal_purge, soft_purge, []}]},
{<<".*">>, []}],
[{"4.3.0",[
{load_module, emqx_coap_mqtt_adapter, brutal_purge, soft_purge, []}]},
{<<".*">>, []}]
}.

View File

@ -58,6 +58,8 @@
-define(SUBOPTS, #{rh => 0, rap => 0, nl => 0, qos => ?QOS_0, is_new => false}).
-define(PROTO_VER, 1).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
@ -139,7 +141,7 @@ handle_call({subscribe, Topic, CoapPid}, _From, State=#state{sub_topics = TopicL
NewTopics = proplists:delete(Topic, TopicList),
IsWild = emqx_topic:wildcard(Topic),
{reply, chann_subscribe(Topic, State), State#state{sub_topics =
[{Topic, {IsWild, CoapPid}}|NewTopics]}, hibernate};
[{Topic, {IsWild, CoapPid}} | NewTopics]}, hibernate};
handle_call({unsubscribe, Topic, _CoapPid}, _From, State=#state{sub_topics = TopicList}) ->
NewTopics = proplists:delete(Topic, TopicList),
@ -244,15 +246,26 @@ chann_publish(Topic, Payload, State = #state{clientid = ClientId}) ->
case emqx_access_control:check_acl(clientinfo(State), publish, Topic) of
allow ->
_ = emqx_broker:publish(
emqx_message:set_flag(retain, false,
emqx_message:make(ClientId, ?QOS_0, Topic, Payload))),
ok;
packet_to_message(Topic, Payload, State)), ok;
deny ->
?LOG(warning, "publish to ~p by clientid ~p failed due to acl check.",
[Topic, ClientId]),
{error, forbidden}
end.
packet_to_message(Topic, Payload,
#state{clientid = ClientId,
username = Username,
peername = {PeerHost, _}}) ->
Message = emqx_message:set_flag(
retain, false,
emqx_message:make(ClientId, ?QOS_0, Topic, Payload)
),
emqx_message:set_headers(
#{ proto_ver => ?PROTO_VER
, protocol => coap
, username => Username
, peerhost => PeerHost}, Message).
%%--------------------------------------------------------------------
%% Deliver
@ -270,7 +283,7 @@ do_deliver({Topic, Payload}, Subscribers) ->
deliver_to_coap(_TopicName, _Payload, []) ->
ok;
deliver_to_coap(TopicName, Payload, [{TopicFilter, {IsWild, CoapPid}}|T]) ->
deliver_to_coap(TopicName, Payload, [{TopicFilter, {IsWild, CoapPid}} | T]) ->
Matched = case IsWild of
true -> emqx_topic:match(TopicName, TopicFilter);
false -> TopicName =:= TopicFilter
@ -324,7 +337,7 @@ conninfo(#state{peername = Peername,
peercert => nossl, %% TODO: dtls
conn_mod => ?MODULE,
proto_name => <<"CoAP">>,
proto_ver => 1,
proto_ver => ?PROTO_VER,
clean_start => true,
clientid => ClientId,
username => undefined,
@ -384,4 +397,3 @@ clientinfo(#state{peername = {PeerHost, _},
mountpoint => undefined,
ws_cookie => undefined
}.

View File

@ -2,8 +2,36 @@
## EMQ X Hooks
##====================================================================
## The default value or action will be returned, while the request to
## the gRPC server failed or no available grpc server running.
##
## Default: deny
## Value: ignore | deny
#exhook.request_failed_action = deny
## The timeout to request grpc server
##
## Default: 5s
## Value: Duration
#exhook.request_timeout = 5s
## Whether to automatically reconnect (initialize) the gRPC server
##
## When gRPC is not available, exhook tries to request the gRPC service at
## that interval and reinitialize the list of mounted hooks.
##
## Default: false
## Value: false | Duration
#exhook.auto_reconnect = 60s
## The process pool size for gRPC client
##
## Default: Equals cpu cores
## Value: Integer
#exhook.pool_size = 16
##--------------------------------------------------------------------
## Server Address
## The Hook callback servers
## The gRPC server url
##

View File

@ -1,5 +1,35 @@
%%-*- mode: erlang -*-
{mapping, "exhook.request_failed_action", "emqx_exhook.request_failed_action", [
{default, "deny"},
{datatype, {enum, [ignore, deny]}}
]}.
{mapping, "exhook.request_timeout", "emqx_exhook.request_timeout", [
{default, "5s"},
{datatype, {duration, ms}}
]}.
{mapping, "exhook.auto_reconnect", "emqx_exhook.auto_reconnect", [
{default, "60s"},
{datatype, string}
]}.
{translation, "emqx_exhook.auto_reconnect", fun(Conf) ->
case cuttlefish:conf_get("exhook.auto_reconnect", Conf) of
"false" -> false;
Dur ->
case cuttlefish_duration:parse(Dur, ms) of
Ms when is_integer(Ms) -> Ms;
{error, Reason} -> error(Reason)
end
end
end}.
{mapping, "exhook.pool_size", "emqx_exhook.pool_size", [
{datatype, integer}
]}.
{mapping, "exhook.server.$name.url", "emqx_exhook.servers", [
{datatype, string}
]}.

View File

@ -358,6 +358,31 @@ message Message {
bytes payload = 6;
uint64 timestamp = 7;
// The key of header can be:
// - username:
// * Readonly
// * The username of sender client
// * Value type: utf8 string
// - protocol:
// * Readonly
// * The protocol name of sender client
// * Value type: string enum with "mqtt", "mqtt-sn", ...
// - peerhost:
// * Readonly
// * The peerhost of sender client
// * Value type: ip address string
// - allow_publish:
// * Writable
// * Whether to allow the message to be published by emqx
// * Value type: string enum with "true", "false", default is "true"
//
// Notes: All header may be missing, which means that the message does not
// carry these headers. We can guarantee that clients coming from MQTT,
// MQTT-SN, CoAP, LwM2M and other natively supported protocol clients will
// carry these headers, but there is no guarantee that messages published
// by other means will do, e.g. messages published by HTTP-API
map<string, string> headers = 8;
}
message Property {

View File

@ -5,7 +5,7 @@
]}.
{deps,
[{grpc, {git, "https://github.com/emqx/grpc-erl", {tag, "0.6.2"}}}
[{grpc, {git, "https://github.com/emqx/grpc-erl", {tag, "0.6.4"}}}
]}.
{grpc,

View File

@ -1,6 +1,6 @@
{application, emqx_exhook,
[{description, "EMQ X Extension for Hook"},
{vsn, "4.3.1"},
{vsn, "4.4.0"},
{modules, []},
{registered, []},
{mod, {emqx_exhook_app, []}},

View File

@ -1,15 +1,7 @@
%% -*-: erlang -*-
{VSN,
[
{"4.3.0", [
{load_module, emqx_exhook_pb, brutal_purge, soft_purge, []}
]},
{<<".*">>, []}
[{<<".*">>, []}
],
[
{"4.3.0", [
{load_module, emqx_exhook_pb, brutal_purge, soft_purge, []}
]},
{<<".*">>, []}
[{<<".*">>, []}
]
}.

View File

@ -21,10 +21,8 @@
-logger_header("[ExHook]").
%% Mgmt APIs
-export([ enable/2
-export([ enable/1
, disable/1
, disable_all/0
, list/0
]).
@ -36,101 +34,85 @@
%% Mgmt APIs
%%--------------------------------------------------------------------
%% XXX: Only return the running servers
-spec list() -> [emqx_exhook_server:server()].
list() ->
[server(Name) || Name <- running()].
-spec enable(atom()|string(), list()) -> ok | {error, term()}.
enable(Name, Opts) ->
case lists:member(Name, running()) of
true ->
{error, already_started};
_ ->
case emqx_exhook_server:load(Name, Opts) of
{ok, ServiceState} ->
save(Name, ServiceState);
{error, Reason} ->
?LOG(error, "Load server ~p failed: ~p", [Name, Reason]),
{error, Reason}
end
end.
-spec enable(atom()|string()) -> ok | {error, term()}.
enable(Name) ->
with_mngr(fun(Pid) -> emqx_exhook_mngr:enable(Pid, Name) end).
-spec disable(atom()|string()) -> ok | {error, term()}.
disable(Name) ->
case server(Name) of
undefined -> {error, not_running};
Service ->
ok = emqx_exhook_server:unload(Service),
unsave(Name)
with_mngr(fun(Pid) -> emqx_exhook_mngr:disable(Pid, Name) end).
-spec list() -> [atom() | string()].
list() ->
with_mngr(fun(Pid) -> emqx_exhook_mngr:list(Pid) end).
with_mngr(Fun) ->
case lists:keyfind(emqx_exhook_mngr, 1,
supervisor:which_children(emqx_exhook_sup)) of
{_, Pid, _, _} ->
Fun(Pid);
_ ->
{error, no_manager_svr}
end.
-spec disable_all() -> ok.
disable_all() ->
lists:foreach(fun disable/1, running()).
%%----------------------------------------------------------
%%--------------------------------------------------------------------
%% Dispatch APIs
%%----------------------------------------------------------
%%--------------------------------------------------------------------
-spec cast(atom(), map()) -> ok.
cast(Hookpoint, Req) ->
cast(Hookpoint, Req, running()).
cast(Hookpoint, Req, emqx_exhook_mngr:running()).
cast(_, _, []) ->
ok;
cast(Hookpoint, Req, [ServiceName|More]) ->
cast(Hookpoint, Req, [ServerName|More]) ->
%% XXX: Need a real asynchronous running
_ = emqx_exhook_server:call(Hookpoint, Req, server(ServiceName)),
_ = emqx_exhook_server:call(Hookpoint, Req,
emqx_exhook_mngr:server(ServerName)),
cast(Hookpoint, Req, More).
-spec call_fold(atom(), term(), function())
-> {ok, term()}
| {stop, term()}.
call_fold(Hookpoint, Req, AccFun) ->
call_fold(Hookpoint, Req, AccFun, running()).
FailedAction = emqx_exhook_mngr:get_request_failed_action(),
ServerNames = emqx_exhook_mngr:running(),
case ServerNames == [] andalso FailedAction == deny of
true ->
{stop, deny_action_result(Hookpoint, Req)};
_ ->
call_fold(Hookpoint, Req, FailedAction, AccFun, ServerNames)
end.
call_fold(_, Req, _, []) ->
call_fold(_, Req, _, _, []) ->
{ok, Req};
call_fold(Hookpoint, Req, AccFun, [ServiceName|More]) ->
case emqx_exhook_server:call(Hookpoint, Req, server(ServiceName)) of
call_fold(Hookpoint, Req, FailedAction, AccFun, [ServerName|More]) ->
Server = emqx_exhook_mngr:server(ServerName),
case emqx_exhook_server:call(Hookpoint, Req, Server) of
{ok, Resp} ->
case AccFun(Req, Resp) of
{stop, NReq} -> {stop, NReq};
{ok, NReq} -> call_fold(Hookpoint, NReq, AccFun, More);
_ -> call_fold(Hookpoint, Req, AccFun, More)
{stop, NReq} ->
{stop, NReq};
{ok, NReq} ->
call_fold(Hookpoint, NReq, FailedAction, AccFun, More);
_ ->
call_fold(Hookpoint, Req, FailedAction, AccFun, More)
end;
_ ->
call_fold(Hookpoint, Req, AccFun, More)
case FailedAction of
deny ->
{stop, deny_action_result(Hookpoint, Req)};
_ ->
call_fold(Hookpoint, Req, FailedAction, AccFun, More)
end
end.
%%----------------------------------------------------------
%% Storage
-compile({inline, [save/2]}).
save(Name, ServiceState) ->
Saved = persistent_term:get(?APP, []),
persistent_term:put(?APP, lists:reverse([Name | Saved])),
persistent_term:put({?APP, Name}, ServiceState).
-compile({inline, [unsave/1]}).
unsave(Name) ->
case persistent_term:get(?APP, []) of
[] ->
persistent_term:erase(?APP);
Saved ->
persistent_term:put(?APP, lists:delete(Name, Saved))
end,
persistent_term:erase({?APP, Name}),
ok.
-compile({inline, [running/0]}).
running() ->
persistent_term:get(?APP, []).
-compile({inline, [server/1]}).
server(Name) ->
case catch persistent_term:get({?APP, Name}) of
{'EXIT', {badarg,_}} -> undefined;
Service -> Service
end.
%% XXX: Hard-coded the deny response
deny_action_result('client.authenticate', _) ->
#{result => false};
deny_action_result('client.check_acl', _) ->
#{result => false};
deny_action_result('message.publish', Msg) ->
%% TODO: Not support to deny a message
%% maybe we can put the 'allow_publish' into message header
Msg.

View File

@ -22,73 +22,23 @@
-emqx_plugin(extension).
-define(CNTER, emqx_exhook_counter).
-export([ start/2
, stop/1
, prep_stop/1
]).
%% Internal export
-export([ load_server/2
, unload_server/1
, unload_exhooks/0
, init_hooks_cnter/0
]).
%%--------------------------------------------------------------------
%% Application callbacks
%%--------------------------------------------------------------------
start(_StartType, _StartArgs) ->
{ok, Sup} = emqx_exhook_sup:start_link(),
%% Init counter
init_hooks_cnter(),
%% Load all dirvers
load_all_servers(),
%% Register CLI
emqx_ctl:register_command(exhook, {emqx_exhook_cli, cli}, []),
{ok, Sup}.
prep_stop(State) ->
emqx_ctl:unregister_command(exhook),
_ = unload_exhooks(),
ok = unload_all_servers(),
State.
stop(_State) ->
ok.
%%--------------------------------------------------------------------
%% Internal funcs
%%--------------------------------------------------------------------
load_all_servers() ->
lists:foreach(fun({Name, Options}) ->
load_server(Name, Options)
end, application:get_env(?APP, servers, [])).
unload_all_servers() ->
emqx_exhook:disable_all().
load_server(Name, Options) ->
emqx_exhook:enable(Name, Options).
unload_server(Name) ->
emqx_exhook:disable(Name).
unload_exhooks() ->
[emqx:unhook(Name, {M, F}) ||
{Name, {M, F, _A}} <- ?ENABLED_HOOKS].
init_hooks_cnter() ->
try
_ = ets:new(?CNTER, [named_table, public]), ok
catch
exit:badarg:_ ->
ok
end.

View File

@ -22,25 +22,18 @@
cli(["server", "list"]) ->
if_enabled(fun() ->
Services = emqx_exhook:list(),
[emqx_ctl:print("HookServer(~s)~n",
[emqx_exhook_server:format(Service)]) || Service <- Services]
ServerNames = emqx_exhook:list(),
[emqx_ctl:print("Server(~s)~n", [format(Name)]) || Name <- ServerNames]
end);
cli(["server", "enable", Name0]) ->
cli(["server", "enable", Name]) ->
if_enabled(fun() ->
Name = list_to_atom(Name0),
case proplists:get_value(Name, application:get_env(?APP, servers, [])) of
undefined ->
emqx_ctl:print("not_found~n");
Opts ->
print(emqx_exhook:enable(Name, Opts))
end
print(emqx_exhook:enable(list_to_existing_atom(Name)))
end);
cli(["server", "disable", Name]) ->
if_enabled(fun() ->
print(emqx_exhook:disable(list_to_atom(Name)))
print(emqx_exhook:disable(list_to_existing_atom(Name)))
end);
cli(["server", "stats"]) ->
@ -65,7 +58,8 @@ print({error, Reason}) ->
if_enabled(Fun) ->
case lists:keymember(?APP, 1, application:which_applications()) of
true -> Fun();
true ->
Fun();
_ -> hint()
end.
@ -79,3 +73,11 @@ stats() ->
_ -> Acc
end
end, [], emqx_metrics:all())).
format(Name) ->
case emqx_exhook_mngr:server(Name) of
undefined ->
io_lib:format("name=~s, hooks=#{}, active=false", [Name]);
Server ->
emqx_exhook_server:format(Server)
end.

View File

@ -50,6 +50,7 @@
%% Utils
-export([ message/1
, headers/1
, stringfy/1
, merge_responsed_bool/2
, merge_responsed_message/2
@ -62,6 +63,8 @@
, call_fold/3
]).
-elvis([{elvis_style, god_modules, disable}]).
%%--------------------------------------------------------------------
%% Clients
%%--------------------------------------------------------------------
@ -258,17 +261,58 @@ clientinfo(ClientInfo =
cn => maybe(maps:get(cn, ClientInfo, undefined)),
dn => maybe(maps:get(dn, ClientInfo, undefined))}.
message(#message{id = Id, qos = Qos, from = From, topic = Topic, payload = Payload, timestamp = Ts}) ->
message(#message{id = Id, qos = Qos, from = From, topic = Topic,
payload = Payload, timestamp = Ts, headers = Headers}) ->
#{node => stringfy(node()),
id => emqx_guid:to_hexstr(Id),
qos => Qos,
from => stringfy(From),
topic => Topic,
payload => Payload,
timestamp => Ts}.
timestamp => Ts,
headers => headers(Headers)
}.
assign_to_message(#{qos := Qos, topic := Topic, payload := Payload}, Message) ->
Message#message{qos = Qos, topic = Topic, payload = Payload}.
headers(Headers) ->
Ls = [username, protocol, peerhost, allow_publish],
maps:fold(
fun
(_, undefined, Acc) ->
Acc; %% Ignore undefined value
(K, V, Acc) ->
case lists:member(K, Ls) of
true ->
Acc#{atom_to_binary(K) => bin(K, V)};
_ ->
Acc
end
end, #{}, Headers).
bin(K, V) when K == username;
K == protocol;
K == allow_publish ->
bin(V);
bin(peerhost, V) ->
bin(inet:ntoa(V)).
bin(V) when is_binary(V) -> V;
bin(V) when is_atom(V) -> atom_to_binary(V);
bin(V) when is_list(V) -> iolist_to_binary(V).
assign_to_message(InMessage = #{qos := Qos, topic := Topic,
payload := Payload}, Message) ->
NMsg = Message#message{qos = Qos, topic = Topic, payload = Payload},
enrich_header(maps:get(headers, InMessage, #{}), NMsg).
enrich_header(Headers, Message) ->
case maps:get(<<"allow_publish">>, Headers, undefined) of
<<"false">> ->
emqx_message:set_header(allow_publish, false, Message);
<<"true">> ->
emqx_message:set_header(allow_publish, true, Message);
_ ->
Message
end.
topicfilters(Tfs) when is_list(Tfs) ->
[#{name => Topic, qos => Qos} || {Topic, #{qos := Qos}} <- Tfs].
@ -299,11 +343,7 @@ merge_responsed_bool(_Req, #{type := 'IGNORE'}) ->
ignore;
merge_responsed_bool(Req, #{type := Type, value := {bool_result, NewBool}})
when is_boolean(NewBool) ->
NReq = Req#{result => NewBool},
case Type of
'CONTINUE' -> {ok, NReq};
'STOP_AND_RETURN' -> {stop, NReq}
end;
{ret(Type), Req#{result => NewBool}};
merge_responsed_bool(_Req, Resp) ->
?LOG(warning, "Unknown responsed value ~0p to merge to callback chain", [Resp]),
ignore.
@ -311,11 +351,10 @@ merge_responsed_bool(_Req, Resp) ->
merge_responsed_message(_Req, #{type := 'IGNORE'}) ->
ignore;
merge_responsed_message(Req, #{type := Type, value := {message, NMessage}}) ->
NReq = Req#{message => NMessage},
case Type of
'CONTINUE' -> {ok, NReq};
'STOP_AND_RETURN' -> {stop, NReq}
end;
{ret(Type), Req#{message => NMessage}};
merge_responsed_message(_Req, Resp) ->
?LOG(warning, "Unknown responsed value ~0p to merge to callback chain", [Resp]),
ignore.
ret('CONTINUE') -> ok;
ret('STOP_AND_RETURN') -> stop.

View File

@ -0,0 +1,324 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
%% @doc Manage the server status and reload strategy
-module(emqx_exhook_mngr).
-behaviour(gen_server).
-include("emqx_exhook.hrl").
-include_lib("emqx/include/logger.hrl").
%% APIs
-export([start_link/3]).
%% Mgmt API
-export([ enable/2
, disable/2
, list/1
]).
%% Helper funcs
-export([ running/0
, server/1
, put_request_failed_action/1
, get_request_failed_action/0
, put_pool_size/1
, get_pool_size/0
]).
%% gen_server callbacks
-export([ init/1
, handle_call/3
, handle_cast/2
, handle_info/2
, terminate/2
, code_change/3
]).
-record(state, {
%% Running servers
running :: map(), %% XXX: server order?
%% Wait to reload servers
waiting :: map(),
%% Marked stopped servers
stopped :: map(),
%% Auto reconnect timer interval
auto_reconnect :: false | non_neg_integer(),
%% Request options
request_options :: grpc_client:options(),
%% Timer references
trefs :: map()
}).
-type servers() :: [{Name :: atom(), server_options()}].
-type server_options() :: [ {scheme, http | https}
| {host, string()}
| {port, inet:port_number()}
].
-define(DEFAULT_TIMEOUT, 60000).
-define(CNTER, emqx_exhook_counter).
%%--------------------------------------------------------------------
%% APIs
%%--------------------------------------------------------------------
-spec start_link(servers(), false | non_neg_integer(), grpc_client:options())
->ignore
| {ok, pid()}
| {error, any()}.
start_link(Servers, AutoReconnect, ReqOpts) ->
gen_server:start_link(?MODULE, [Servers, AutoReconnect, ReqOpts], []).
-spec enable(pid(), atom() | string()) -> ok | {error, term()}.
enable(Pid, Name) ->
call(Pid, {load, Name}).
-spec disable(pid(), atom() | string()) -> ok | {error, term()}.
disable(Pid, Name) ->
call(Pid, {unload, Name}).
list(Pid) ->
call(Pid, list).
call(Pid, Req) ->
gen_server:call(Pid, Req, ?DEFAULT_TIMEOUT).
%%--------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
init([Servers, AutoReconnect, ReqOpts0]) ->
process_flag(trap_exit, true),
%% XXX: Due to the ExHook Module in the enterprise,
%% this process may start multiple times and they will share this table
try
_ = ets:new(?CNTER, [named_table, public]), ok
catch
error:badarg:_ ->
ok
end,
%% put the global option
put_request_failed_action(
maps:get(request_failed_action, ReqOpts0, deny)
),
put_pool_size(
maps:get(pool_size, ReqOpts0, erlang:system_info(schedulers))
),
%% Load the hook servers
ReqOpts = maps:without([request_failed_action], ReqOpts0),
{Waiting, Running} = load_all_servers(Servers, ReqOpts),
{ok, ensure_reload_timer(
#state{waiting = Waiting,
running = Running,
stopped = #{},
request_options = ReqOpts,
auto_reconnect = AutoReconnect,
trefs = #{}
}
)}.
%% @private
load_all_servers(Servers, ReqOpts) ->
load_all_servers(Servers, ReqOpts, #{}, #{}).
load_all_servers([], _Request, Waiting, Running) ->
{Waiting, Running};
load_all_servers([{Name, Options} | More], ReqOpts, Waiting, Running) ->
{NWaiting, NRunning} =
case emqx_exhook_server:load(Name, Options, ReqOpts) of
{ok, ServerState} ->
save(Name, ServerState),
{Waiting, Running#{Name => Options}};
{error, _} ->
{Waiting#{Name => Options}, Running}
end,
load_all_servers(More, ReqOpts, NWaiting, NRunning).
handle_call({load, Name}, _From, State) ->
{Result, NState} = do_load_server(Name, State),
{reply, Result, NState};
handle_call({unload, Name}, _From, State) ->
case do_unload_server(Name, State) of
{error, Reason} ->
{reply, {error, Reason}, State};
{ok, NState} ->
{reply, ok, NState}
end;
handle_call(list, _From, State = #state{
running = Running,
waiting = Waiting,
stopped = Stopped}) ->
ServerNames = maps:keys(Running)
++ maps:keys(Waiting)
++ maps:keys(Stopped),
{reply, ServerNames, State};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({timeout, _Ref, {reload, Name}}, State) ->
{Result, NState} = do_load_server(Name, State),
case Result of
ok ->
{noreply, NState};
{error, not_found} ->
{noreply, NState};
{error, Reason} ->
?LOG(warning, "Failed to reload exhook callback server \"~s\", "
"Reason: ~0p", [Name, Reason]),
{noreply, ensure_reload_timer(NState)}
end;
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, State = #state{running = Running}) ->
_ = maps:fold(fun(Name, _, AccIn) ->
{ok, NAccIn} = do_unload_server(Name, AccIn),
NAccIn
end, State, Running),
_ = unload_exhooks(),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%% Internal funcs
%%--------------------------------------------------------------------
unload_exhooks() ->
[emqx:unhook(Name, {M, F}) ||
{Name, {M, F, _A}} <- ?ENABLED_HOOKS].
do_load_server(Name, State0 = #state{
waiting = Waiting,
running = Running,
stopped = Stopped,
request_options = ReqOpts}) ->
State = clean_reload_timer(Name, State0),
case maps:get(Name, Running, undefined) of
undefined ->
case maps:get(Name, Stopped,
maps:get(Name, Waiting, undefined)) of
undefined ->
{{error, not_found}, State};
Options ->
case emqx_exhook_server:load(Name, Options, ReqOpts) of
{ok, ServerState} ->
save(Name, ServerState),
?LOG(info, "Load exhook callback server "
"\"~s\" successfully!", [Name]),
{ok, State#state{
running = maps:put(Name, Options, Running),
waiting = maps:remove(Name, Waiting),
stopped = maps:remove(Name, Stopped)
}
};
{error, Reason} ->
{{error, Reason}, State}
end
end;
_ ->
{{error, already_started}, State}
end.
do_unload_server(Name, State = #state{running = Running, stopped = Stopped}) ->
case maps:take(Name, Running) of
error -> {error, not_running};
{Options, NRunning} ->
ok = emqx_exhook_server:unload(server(Name)),
ok = unsave(Name),
{ok, State#state{running = NRunning,
stopped = maps:put(Name, Options, Stopped)
}}
end.
ensure_reload_timer(State = #state{auto_reconnect = false}) ->
State;
ensure_reload_timer(State = #state{waiting = Waiting,
trefs = TRefs,
auto_reconnect = Intv}) ->
NRefs = maps:fold(fun(Name, _, AccIn) ->
case maps:get(Name, AccIn, undefined) of
undefined ->
Ref = erlang:start_timer(Intv, self(), {reload, Name}),
AccIn#{Name => Ref};
_HasRef ->
AccIn
end
end, TRefs, Waiting),
State#state{trefs = NRefs}.
clean_reload_timer(Name, State = #state{trefs = TRefs}) ->
case maps:take(Name, TRefs) of
error -> State;
{TRef, NTRefs} ->
_ = erlang:cancel_timer(TRef),
State#state{trefs = NTRefs}
end.
%%--------------------------------------------------------------------
%% Server state persistent
put_request_failed_action(Val) ->
persistent_term:put({?APP, request_failed_action}, Val).
get_request_failed_action() ->
persistent_term:get({?APP, request_failed_action}).
put_pool_size(Val) ->
persistent_term:put({?APP, pool_size}, Val).
get_pool_size() ->
%% Avoid the scenario that the parameter is not set after
%% the hot upgrade completed.
persistent_term:get({?APP, pool_size}, erlang:system_info(schedulers)).
save(Name, ServerState) ->
Saved = persistent_term:get(?APP, []),
persistent_term:put(?APP, lists:reverse([Name | Saved])),
persistent_term:put({?APP, Name}, ServerState).
unsave(Name) ->
case persistent_term:get(?APP, []) of
[] ->
persistent_term:erase(?APP);
Saved ->
persistent_term:put(?APP, lists:delete(Name, Saved))
end,
persistent_term:erase({?APP, Name}),
ok.
running() ->
persistent_term:get(?APP, []).
server(Name) ->
case catch persistent_term:get({?APP, Name}) of
{'EXIT', {badarg,_}} -> undefined;
Service -> Service
end.

View File

@ -25,7 +25,7 @@
-define(PB_CLIENT_MOD, emqx_exhook_v_1_hook_provider_client).
%% Load/Unload
-export([ load/2
-export([ load/3
, unload/1
]).
@ -40,8 +40,8 @@
-record(server, {
%% Server name (equal to grpc client channel name)
name :: server_name(),
%% The server started options
options :: list(),
%% The function options
options :: map(),
%% gRPC channel pid
channel :: pid(),
%% Registered hook names and options
@ -77,12 +77,14 @@
-dialyzer({nowarn_function, [inc_metrics/2]}).
-elvis([{elvis_style, dont_repeat_yourself, disable}]).
%%--------------------------------------------------------------------
%% Load/Unload APIs
%%--------------------------------------------------------------------
-spec load(atom(), list()) -> {ok, server()} | {error, term()} .
load(Name0, Opts0) ->
-spec load(atom(), list(), map()) -> {ok, server()} | {error, term()} .
load(Name0, Opts0, ReqOpts) ->
Name = to_list(Name0),
{SvrAddr, ClientOpts} = channel_opts(Opts0),
case emqx_exhook_sup:start_grpc_client_channel(
@ -90,7 +92,7 @@ load(Name0, Opts0) ->
SvrAddr,
ClientOpts) of
{ok, _ChannPoolPid} ->
case do_init(Name) of
case do_init(Name, ReqOpts) of
{ok, HookSpecs} ->
%% Reigster metrics
Prefix = lists:flatten(
@ -99,7 +101,7 @@ load(Name0, Opts0) ->
%% Ensure hooks
ensure_hooks(HookSpecs),
{ok, #server{name = Name,
options = Opts0,
options = ReqOpts,
channel = _ChannPoolPid,
hookspec = HookSpecs,
prefix = Prefix }};
@ -122,31 +124,43 @@ channel_opts(Opts) ->
Scheme = proplists:get_value(scheme, Opts),
Host = proplists:get_value(host, Opts),
Port = proplists:get_value(port, Opts),
SvrAddr = lists:flatten(io_lib:format("~s://~s:~w", [Scheme, Host, Port])),
SvrAddr = format_http_uri(Scheme, Host, Port),
ClientOpts = case Scheme of
https ->
SslOpts = lists:keydelete(ssl, 1, proplists:get_value(ssl_options, Opts, [])),
SslOpts = lists:keydelete(
ssl,
1,
proplists:get_value(ssl_options, Opts, [])
),
#{gun_opts =>
#{transport => ssl,
transport_opts => SslOpts}};
_ -> #{}
end,
{SvrAddr, ClientOpts}.
NClientOpts = ClientOpts#{pool_size => emqx_exhook_mngr:get_pool_size()},
{SvrAddr, NClientOpts}.
format_http_uri(Scheme, Host0, Port) ->
Host = case is_tuple(Host0) of
true -> inet:ntoa(Host0);
_ -> Host0
end,
lists:flatten(io_lib:format("~s://~s:~w", [Scheme, Host, Port])).
-spec unload(server()) -> ok.
unload(#server{name = Name, hookspec = HookSpecs}) ->
_ = do_deinit(Name),
unload(#server{name = Name, options = ReqOpts, hookspec = HookSpecs}) ->
_ = do_deinit(Name, ReqOpts),
_ = may_unload_hooks(HookSpecs),
_ = emqx_exhook_sup:stop_grpc_client_channel(Name),
ok.
do_deinit(Name) ->
_ = do_call(Name, 'on_provider_unloaded', #{}),
do_deinit(Name, ReqOpts) ->
_ = do_call(Name, 'on_provider_unloaded', #{}, ReqOpts),
ok.
do_init(ChannName) ->
do_init(ChannName, ReqOpts) ->
Req = #{broker => maps:from_list(emqx_sys:info())},
case do_call(ChannName, 'on_provider_loaded', Req) of
case do_call(ChannName, 'on_provider_loaded', Req, ReqOpts) of
{ok, InitialResp} ->
try
{ok, resovle_hookspec(maps:get(hooks, InitialResp, []))}
@ -167,16 +181,18 @@ resovle_hookspec(HookSpecs) when is_list(HookSpecs) ->
case maps:get(name, HookSpec, undefined) of
undefined -> Acc;
Name0 ->
Name = try binary_to_existing_atom(Name0, utf8) catch T:R:_ -> {T,R} end,
case lists:member(Name, AvailableHooks) of
true ->
case lists:member(Name, MessageHooks) of
true ->
Acc#{Name => #{topics => maps:get(topics, HookSpec, [])}};
_ ->
Acc#{Name => #{}}
end;
_ -> error({unknown_hookpoint, Name})
Name = try
binary_to_existing_atom(Name0, utf8)
catch T:R -> {T,R}
end,
case {lists:member(Name, AvailableHooks),
lists:member(Name, MessageHooks)} of
{false, _} ->
error({unknown_hookpoint, Name});
{true, false} ->
Acc#{Name => #{}};
{true, true} ->
Acc#{Name => #{topics => maps:get(topics, HookSpec, [])}}
end
end
end, #{}, HookSpecs).
@ -212,7 +228,7 @@ may_unload_hooks(HookSpecs) ->
end, maps:keys(HookSpecs)).
format(#server{name = Name, hookspec = Hooks}) ->
io_lib:format("name=~p, hooks=~0p", [Name, Hooks]).
io_lib:format("name=~s, hooks=~0p, active=true", [Name, Hooks]).
%%--------------------------------------------------------------------
%% APIs
@ -225,7 +241,8 @@ name(#server{name = Name}) ->
-> ignore
| {ok, Resp :: term()}
| {error, term()}.
call(Hookpoint, Req, #server{name = ChannName, hookspec = Hooks, prefix = Prefix}) ->
call(Hookpoint, Req, #server{name = ChannName, options = ReqOpts,
hookspec = Hooks, prefix = Prefix}) ->
GrpcFunc = hk2func(Hookpoint),
case maps:get(Hookpoint, Hooks, undefined) of
undefined -> ignore;
@ -240,14 +257,14 @@ call(Hookpoint, Req, #server{name = ChannName, hookspec = Hooks, prefix = Prefix
false -> ignore;
_ ->
inc_metrics(Prefix, Hookpoint),
do_call(ChannName, GrpcFunc, Req)
do_call(ChannName, GrpcFunc, Req, ReqOpts)
end
end.
%% @private
inc_metrics(IncFun, Name) when is_function(IncFun) ->
%% BACKW: e4.2.0-e4.2.2
{env, [Prefix|_]} = erlang:fun_info(IncFun, env),
{env, [Prefix | _]} = erlang:fun_info(IncFun, env),
inc_metrics(Prefix, Name);
inc_metrics(Prefix, Name) when is_list(Prefix) ->
emqx_metrics:inc(list_to_atom(Prefix ++ atom_to_list(Name))).
@ -258,13 +275,13 @@ match_topic_filter(_, []) ->
match_topic_filter(TopicName, TopicFilter) ->
lists:any(fun(F) -> emqx_topic:match(TopicName, F) end, TopicFilter).
-spec do_call(string(), atom(), map()) -> {ok, map()} | {error, term()}.
do_call(ChannName, Fun, Req) ->
Options = #{channel => ChannName},
-spec do_call(string(), atom(), map(), map()) -> {ok, map()} | {error, term()}.
do_call(ChannName, Fun, Req, ReqOpts) ->
Options = ReqOpts#{channel => ChannName},
?LOG(debug, "Call ~0p:~0p(~0p, ~0p)", [?PB_CLIENT_MOD, Fun, Req, Options]),
case catch apply(?PB_CLIENT_MOD, Fun, [Req, Options]) of
{ok, Resp, _Metadata} ->
?LOG(debug, "Response {ok, ~0p, ~0p}", [Resp, _Metadata]),
{ok, Resp, Metadata} ->
?LOG(debug, "Response {ok, ~0p, ~0p}", [Resp, Metadata]),
{ok, Resp};
{error, {Code, Msg}, _Metadata} ->
?LOG(error, "CALL ~0p:~0p(~0p, ~0p) response errcode: ~0p, errmsg: ~0p",

View File

@ -26,6 +26,14 @@
, stop_grpc_client_channel/1
]).
-define(CHILD(Mod, Type, Args),
#{ id => Mod
, start => {Mod, start_link, Args}
, type => Type
, shutdown => 15000
}
).
%%--------------------------------------------------------------------
%% Supervisor APIs & Callbacks
%%--------------------------------------------------------------------
@ -34,7 +42,24 @@ start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
{ok, {{one_for_one, 10, 100}, []}}.
Mngr = ?CHILD(emqx_exhook_mngr, worker,
[servers(), auto_reconnect(), request_options()]),
{ok, {{one_for_one, 10, 100}, [Mngr]}}.
servers() ->
env(servers, []).
auto_reconnect() ->
env(auto_reconnect, 60000).
request_options() ->
#{timeout => env(request_timeout, 5000),
request_failed_action => env(request_failed_action, deny),
pool_size => env(pool_size, erlang:system_info(schedulers))
}.
env(Key, Def) ->
application:get_env(emqx_exhook, Key, Def).
%%--------------------------------------------------------------------
%% APIs
@ -43,7 +68,7 @@ init([]) ->
-spec start_grpc_client_channel(
string(),
uri_string:uri_string(),
grpc_client:options()) -> {ok, pid()} | {error, term()}.
grpc_client_sup:options()) -> {ok, pid()} | {error, term()}.
start_grpc_client_channel(Name, SvrAddr, Options) ->
grpc_client_sup:create_channel_pool(Name, SvrAddr, Options).

View File

@ -52,17 +52,32 @@ set_special_cfgs(emqx_exhook) ->
t_noserver_nohook(_) ->
emqx_exhook:disable(default),
?assertEqual([], ets:tab2list(emqx_hooks)),
Opts = proplists:get_value(
default,
application:get_env(emqx_exhook, servers, [])
),
ok = emqx_exhook:enable(default, Opts),
ok = emqx_exhook:enable(default),
?assertNotEqual([], ets:tab2list(emqx_hooks)).
t_access_failed_if_no_server_running(_) ->
emqx_exhook:disable(default),
ClientInfo = #{clientid => <<"user-id-1">>,
username => <<"usera">>,
peerhost => {127,0,0,1},
sockport => 1883,
protocol => mqtt,
mountpoint => undefined
},
?assertMatch({stop, #{auth_result := not_authorized}},
emqx_exhook_handler:on_client_authenticate(ClientInfo, #{auth_result => success})),
?assertMatch({stop, deny},
emqx_exhook_handler:on_client_check_acl(ClientInfo, publish, <<"t/1">>, allow)),
Message = emqx_message:make(<<"t/1">>, <<"abc">>),
?assertMatch({stop, Message},
emqx_exhook_handler:on_message_publish(Message)),
emqx_exhook:enable(default).
t_cli_list(_) ->
meck_print(),
?assertEqual( [[emqx_exhook_server:format(Svr) || Svr <- emqx_exhook:list()]]
?assertEqual( [[emqx_exhook_server:format(emqx_exhook_mngr:server(Name)) || Name <- emqx_exhook:list()]]
, emqx_exhook_cli:cli(["server", "list"])
),
unmeck_print().
@ -71,7 +86,7 @@ t_cli_enable_disable(_) ->
meck_print(),
?assertEqual([already_started], emqx_exhook_cli:cli(["server", "enable", "default"])),
?assertEqual(ok, emqx_exhook_cli:cli(["server", "disable", "default"])),
?assertEqual([], emqx_exhook_cli:cli(["server", "list"])),
?assertEqual([["name=default, hooks=#{}, active=false"]], emqx_exhook_cli:cli(["server", "list"])),
?assertEqual([not_running], emqx_exhook_cli:cli(["server", "disable", "default"])),
?assertEqual(ok, emqx_exhook_cli:cli(["server", "enable", "default"])),

View File

@ -299,21 +299,31 @@ on_message_publish(#{message := #{from := From} = Msg} = Req, Md) ->
%% some cases for testing
case From of
<<"baduser">> ->
NMsg = Msg#{qos => 0,
NMsg = deny(Msg#{qos => 0,
topic => <<"">>,
payload => <<"">>
},
}),
{ok, #{type => 'STOP_AND_RETURN',
value => {message, NMsg}}, Md};
<<"gooduser">> ->
NMsg = Msg#{topic => From,
payload => From},
NMsg = allow(Msg#{topic => From,
payload => From}),
{ok, #{type => 'STOP_AND_RETURN',
value => {message, NMsg}}, Md};
_ ->
{ok, #{type => 'IGNORE'}, Md}
end.
deny(Msg) ->
NHeader = maps:put(<<"allow_publish">>, <<"false">>,
maps:get(headers, Msg, #{})),
maps:put(headers, NHeader, Msg).
allow(Msg) ->
NHeader = maps:put(<<"allow_publish">>, <<"true">>,
maps:get(headers, Msg, #{})),
maps:put(headers, NHeader, Msg).
-spec on_message_delivered(emqx_exhook_pb:message_delivered_request(), grpc:metadata())
-> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()}
| {error, grpc_cowboy_h:error_response()}.

View File

@ -299,19 +299,24 @@ prop_message_publish() ->
_ ->
ExpectedOutMsg = case emqx_message:from(Msg) of
<<"baduser">> ->
MsgMap = emqx_message:to_map(Msg),
MsgMap = #{headers := Headers}
= emqx_message:to_map(Msg),
emqx_message:from_map(
MsgMap#{qos => 0,
topic => <<"">>,
payload => <<"">>
payload => <<"">>,
headers => maps:put(allow_publish, false, Headers)
});
<<"gooduser">> = From ->
MsgMap = emqx_message:to_map(Msg),
MsgMap = #{headers := Headers}
= emqx_message:to_map(Msg),
emqx_message:from_map(
MsgMap#{topic => From,
payload => From
payload => From,
headers => maps:put(allow_publish, true, Headers)
});
_ -> Msg
_ ->
Msg
end,
?assertEqual(ExpectedOutMsg, OutMsg),
@ -464,7 +469,9 @@ from_message(Msg) ->
from => stringfy(emqx_message:from(Msg)),
topic => emqx_message:topic(Msg),
payload => emqx_message:payload(Msg),
timestamp => emqx_message:timestamp(Msg)
timestamp => emqx_message:timestamp(Msg),
headers => emqx_exhook_handler:headers(
emqx_message:get_headers(Msg))
}.
%%--------------------------------------------------------------------

View File

@ -13,7 +13,7 @@
]}.
{deps,
[{grpc, {git, "https://github.com/emqx/grpc-erl", {tag, "0.6.2"}}}
[{grpc, {git, "https://github.com/emqx/grpc-erl", {tag, "0.6.4"}}}
]}.
{grpc,

View File

@ -1,6 +1,6 @@
{application, emqx_exproto,
[{description, "EMQ X Extension for Protocol"},
{vsn, "4.3.0"}, %% strict semver
{vsn, "4.3.5"}, %% 4.3.3 is used by ee
{modules, []},
{registered, []},
{mod, {emqx_exproto_app, []}},

View File

@ -0,0 +1,30 @@
%% -*- mode: erlang -*-
{VSN,
[{"4.3.4",
[{load_module,emqx_exproto_channel,brutal_purge,soft_purge,[]}]},
{"4.3.3",
[{load_module,emqx_exproto_conn,brutal_purge,soft_purge,[]},
{load_module,emqx_exproto_channel,brutal_purge,soft_purge,[]}]},
{"4.3.2",
[{load_module,emqx_exproto_conn,brutal_purge,soft_purge,[]},
{load_module,emqx_exproto_channel,brutal_purge,soft_purge,[]}]},
{<<"4.3.[0-1]">>,
[{load_module,emqx_exproto_gsvr,brutal_purge,soft_purge,[]},
{load_module,emqx_exproto_gcli,brutal_purge,soft_purge,[]},
{load_module,emqx_exproto_conn,brutal_purge,soft_purge,[]},
{load_module,emqx_exproto_channel,brutal_purge,soft_purge,[]}]},
{<<".*">>,[]}],
[{"4.3.4",
[{load_module,emqx_exproto_channel,brutal_purge,soft_purge,[]}]},
{"4.3.3",
[{load_module,emqx_exproto_conn,brutal_purge,soft_purge,[]},
{load_module,emqx_exproto_channel,brutal_purge,soft_purge,[]}]},
{"4.3.2",
[{load_module,emqx_exproto_conn,brutal_purge,soft_purge,[]},
{load_module,emqx_exproto_channel,brutal_purge,soft_purge,[]}]},
{<<"4.3.[0-1]">>,
[{load_module,emqx_exproto_gsvr,brutal_purge,soft_purge,[]},
{load_module,emqx_exproto_gcli,brutal_purge,soft_purge,[]},
{load_module,emqx_exproto_conn,brutal_purge,soft_purge,[]},
{load_module,emqx_exproto_channel,brutal_purge,soft_purge,[]}]},
{<<".*">>,[]}]}.

View File

@ -94,6 +94,9 @@
awaiting_rel_max
]).
-define(CHANMOCK(P), {exproto_anonymous_client, P}).
-define(CHAN_CONN_TAB, emqx_channel_conn).
%%--------------------------------------------------------------------
%% Info, Attrs and Caps
%%--------------------------------------------------------------------
@ -103,7 +106,7 @@
info(Channel) ->
maps:from_list(info(?INFO_KEYS, Channel)).
-spec(info(list(atom())|atom(), channel()) -> term()).
-spec(info(list(atom()) | atom(), channel()) -> term()).
info(Keys, Channel) when is_list(Keys) ->
[{Key, info(Key, Channel)} || Key <- Keys];
info(conninfo, #channel{conninfo = ConnInfo}) ->
@ -155,21 +158,36 @@ init(ConnInfo = #{socktype := Socktype,
conn_state = connecting,
timers = #{}
},
case emqx_hooks:run_fold('client.connect', [NConnInfo], #{}) of
{error, _Reason} ->
throw(nopermission);
_ ->
ConnMod = maps:get(conn_mod, NConnInfo),
true = ets:insert(?CHAN_CONN_TAB, {?CHANMOCK(self()), ConnMod}),
Req = #{conninfo =>
peercert(Peercert,
#{socktype => socktype(Socktype),
peername => address(Peername),
sockname => address(Sockname)})},
try_dispatch(on_socket_created, wrap(Req), Channel).
try_dispatch(on_socket_created, wrap(Req), Channel)
end.
%% @private
peercert(nossl, ConnInfo) ->
peercert(NoSsl, ConnInfo) when NoSsl == nossl;
NoSsl == undefined ->
ConnInfo;
peercert(Peercert, ConnInfo) ->
ConnInfo#{peercert =>
Fn = fun(_, V) -> V =/= undefined end,
Infos = maps:filter(Fn,
#{cn => esockd_peercert:common_name(Peercert),
dn => esockd_peercert:subject(Peercert)}}.
dn => esockd_peercert:subject(Peercert)}
),
case maps:size(Infos) of
0 ->
ConnInfo;
_ ->
ConnInfo#{peercert => Infos}
end.
%% @private
socktype(tcp) -> 'TCP';
@ -234,7 +252,7 @@ handle_timeout(_TRef, {keepalive, StatVal},
end;
handle_timeout(_TRef, force_close, Channel = #channel{closed_reason = Reason}) ->
{shutdown, {error, {force_close, Reason}}, Channel};
{shutdown, Reason, Channel};
handle_timeout(_TRef, Msg, Channel) ->
?WARN("Unexpected timeout: ~p", [Msg]),
@ -260,7 +278,7 @@ handle_call({auth, ClientInfo0, Password},
Channel = #channel{conninfo = ConnInfo,
clientinfo = ClientInfo}) ->
ClientInfo1 = enrich_clientinfo(ClientInfo0, ClientInfo),
NConnInfo = enrich_conninfo(ClientInfo1, ConnInfo),
NConnInfo = enrich_conninfo(ClientInfo0, ConnInfo),
Channel1 = Channel#channel{conninfo = NConnInfo,
clientinfo = ClientInfo1},
@ -274,6 +292,7 @@ handle_call({auth, ClientInfo0, Password},
emqx_metrics:inc('client.auth.anonymous'),
NClientInfo = maps:merge(ClientInfo1, AuthResult),
NChannel = Channel1#channel{clientinfo = NClientInfo},
clean_anonymous_clients(),
case emqx_cm:open_session(true, NClientInfo, NConnInfo) of
{ok, _Session} ->
?LOG(debug, "Client ~s (Username: '~s') authorized successfully!",
@ -321,17 +340,14 @@ handle_call({unsubscribe, TopicFilter},
handle_call({publish, Topic, Qos, Payload},
Channel = #channel{
conn_state = connected,
clientinfo = ClientInfo
= #{clientid := From,
mountpoint := Mountpoint}}) ->
clientinfo = ClientInfo}) ->
case is_acl_enabled(ClientInfo) andalso
emqx_access_control:check_acl(ClientInfo, publish, Topic) of
deny ->
{reply, {error, ?RESP_PERMISSION_DENY, <<"ACL deny">>}, Channel};
_ ->
Msg = emqx_message:make(From, Qos, Topic, Payload),
NMsg = emqx_mountpoint:mount(Mountpoint, Msg),
_ = emqx:publish(NMsg),
Msg = packet_to_message(Topic, Qos, Payload, Channel),
_ = emqx:publish(Msg),
{reply, ok, Channel}
end;
@ -364,13 +380,13 @@ handle_info({sock_closed, Reason},
case queue:len(Queue) =:= 0
andalso Inflight =:= undefined of
true ->
Channel1 = ensure_disconnected({sock_closed, Reason}, Channel),
{shutdown, {sock_closed, Reason}, Channel1};
Channel1 = ensure_disconnected(Reason, Channel),
{shutdown, Reason, Channel1};
_ ->
%% delayed close process for flushing all callback funcs to gRPC server
Channel1 = Channel#channel{closed_reason = {sock_closed, Reason}},
Channel1 = Channel#channel{closed_reason = Reason},
Channel2 = ensure_timer(force_timer, Channel1),
{ok, ensure_disconnected({sock_closed, Reason}, Channel2)}
{ok, ensure_disconnected(Reason, Channel2)}
end;
handle_info({hreply, on_socket_created, ok}, Channel) ->
@ -390,12 +406,34 @@ handle_info(Info, Channel) ->
-spec(terminate(any(), channel()) -> channel()).
terminate(Reason, Channel) ->
clean_anonymous_clients(),
Req = #{reason => stringfy(Reason)},
try_dispatch(on_socket_closed, wrap(Req), Channel).
is_anonymous(#{anonymous := true}) -> true;
is_anonymous(_AuthResult) -> false.
clean_anonymous_clients() ->
ets:delete(?CHAN_CONN_TAB, ?CHANMOCK(self())).
packet_to_message(Topic, Qos, Payload,
#channel{
conninfo = #{proto_ver := ProtoVer},
clientinfo = #{
protocol := Protocol,
clientid := ClientId,
username := Username,
peerhost := PeerHost,
mountpoint := Mountpoint}}) ->
Msg = emqx_message:make(
ClientId, Qos,
Topic, Payload, #{},
#{proto_ver => ProtoVer,
protocol => Protocol,
username => Username,
peerhost => PeerHost}),
emqx_mountpoint:mount(Mountpoint, Msg).
%%--------------------------------------------------------------------
%% Sub/UnSub
%%--------------------------------------------------------------------
@ -568,7 +606,6 @@ default_conninfo(ConnInfo) ->
ConnInfo#{clean_start => true,
clientid => undefined,
username => undefined,
conn_mod => undefined,
conn_props => #{},
connected => true,
connected_at => erlang:system_time(millisecond),

View File

@ -17,6 +17,7 @@
%% TCP/TLS/UDP/DTLS Connection
-module(emqx_exproto_conn).
-include_lib("esockd/include/esockd.hrl").
-include_lib("emqx/include/types.hrl").
-include_lib("emqx/include/logger.hrl").
@ -106,19 +107,15 @@ start_link(Socket = {udp, _SockPid, _Sock}, Peername, Options) ->
%% tcp/ssl/dtls
start_link(esockd_transport, Sock, Options) ->
Socket = {esockd_transport, Sock},
case esockd_transport:peername(Sock) of
{ok, Peername} ->
Args = [self(), Socket, Peername, Options],
{ok, proc_lib:spawn_link(?MODULE, init, Args)};
R = {error, _} -> R
end.
Args = [self(), Socket, undefined, Options],
{ok, proc_lib:spawn_link(?MODULE, init, Args)}.
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
%% @doc Get infos of the connection/channel.
-spec(info(pid()|state()) -> emqx_types:infos()).
-spec(info(pid() | state()) -> emqx_types:infos()).
info(CPid) when is_pid(CPid) ->
call(CPid, info);
info(State = #state{channel = Channel}) ->
@ -140,7 +137,7 @@ info(sockstate, #state{sockstate = SockSt}) ->
info(active_n, #state{active_n = ActiveN}) ->
ActiveN.
-spec(stats(pid()|state()) -> emqx_types:stats()).
-spec(stats(pid() | state()) -> emqx_types:stats()).
stats(CPid) when is_pid(CPid) ->
call(CPid, stats);
stats(#state{socket = Socket,
@ -170,6 +167,12 @@ stop(Pid) ->
%% Wrapped funcs
%%--------------------------------------------------------------------
esockd_peername({udp, _SockPid, _Sock}, Peername) ->
Peername;
esockd_peername({esockd_transport, Sock}, _Peername) ->
{ok, Peername} = esockd_transport:ensure_ok_or_exit(peername, [Sock]),
Peername.
esockd_wait(Socket = {udp, _SockPid, _Sock}) ->
{ok, Socket};
esockd_wait({esockd_transport, Sock}) ->
@ -195,7 +198,12 @@ esockd_ensure_ok_or_exit(Fun, {esockd_transport, Socket}) ->
esockd_type({udp, _, _}) ->
udp;
esockd_type({esockd_transport, Socket}) ->
esockd_transport:type(Socket).
case esockd_transport:type(Socket) of
proxy ->
esockd_transport:type(Socket#proxy_socket.socket);
Type ->
Type
end.
esockd_setopts({udp, _, _}, _) ->
ok;
@ -221,10 +229,15 @@ send(Data, #state{socket = {esockd_transport, Sock}}) ->
-define(DEFAULT_IDLE_TIMEOUT, 30000).
-define(DEFAULT_OOM_POLICY, #{max_heap_size => 4194304,message_queue_len => 32000}).
init(Parent, WrappedSock, Peername, Options) ->
init(Parent, WrappedSock, Peername0, Options) ->
case esockd_wait(WrappedSock) of
{ok, NWrappedSock} ->
run_loop(Parent, init_state(NWrappedSock, Peername, Options));
Peername = esockd_peername(NWrappedSock, Peername0),
try
run_loop(Parent, init_state(NWrappedSock, Peername, Options))
catch
throw : nopermission -> erlang:exit(normal)
end;
{error, Reason} ->
ok = esockd_close(WrappedSock),
exit_on_sock_error(Reason)
@ -328,7 +341,7 @@ cancel_stats_timer(State) -> State.
process_msg([], Parent, State) -> recvloop(Parent, State);
process_msg([Msg|More], Parent, State) ->
process_msg([Msg | More], Parent, State) ->
case catch handle_msg(Msg, State) of
ok ->
process_msg(More, Parent, State);
@ -404,7 +417,7 @@ handle_msg({Passive, _Sock}, State)
handle_msg(Deliver = {deliver, _Topic, _Msg},
State = #state{active_n = ActiveN}) ->
Delivers = [Deliver|emqx_misc:drain_deliver(ActiveN)],
Delivers = [Deliver | emqx_misc:drain_deliver(ActiveN)],
with_channel(handle_deliver, [Delivers], State);
%% Something sent
@ -592,9 +605,9 @@ handle_outgoing(IoData, State = #state{socket = Socket}) ->
handle_info(activate_socket, State = #state{sockstate = OldSst}) ->
case activate_socket(State) of
{ok, NState = #state{sockstate = NewSst}} ->
if OldSst =/= NewSst ->
{ok, {event, NewSst}, NState};
true -> {ok, NState}
case OldSst =/= NewSst of
true -> {ok, {event, NewSst}, NState};
false -> {ok, NState}
end;
{error, Reason} ->
handle_info({sock_error, Reason}, State)

View File

@ -89,15 +89,22 @@ handle_cast({rpc, Fun, Req, Options, From}, State = #state{streams = Streams}) -
{ok, Stream} ->
case catch grpc_client:send(Stream, Req) of
ok ->
?LOG(debug, "Send to ~p method successfully, request: ~0p", [Fun, Req]),
?LOG(debug, "Send to ~s method successfully, request: ~0p", [Fun, Req]),
reply(From, Fun, ok),
{noreply, State#state{streams = Streams#{Fun => Stream}}};
{'EXIT', {not_found, _Stk}} ->
%% Not found the stream, reopen it
?LOG(info, "Can not find the old stream ref for ~s; "
"re-try with a new stream!", [Fun]),
handle_cast({rpc, Fun, Req, Options, From},
State#state{streams = maps:remove(Fun, Streams)});
{'EXIT', {timeout, _Stk}} ->
?LOG(error, "Send to ~p method timeout, request: ~0p", [Fun, Req]),
?LOG(error, "Send to ~s method timeout, request: ~0p", [Fun, Req]),
reply(From, Fun, {error, timeout}),
{noreply, State#state{streams = Streams#{Fun => Stream}}};
{'EXIT', {Reason1, _Stk}} ->
?LOG(error, "Send to ~p method failure, request: ~0p, stacktrace: ~0p", [Fun, Req, _Stk]),
?LOG(error, "Send to ~s method failure, request: ~0p, reason: ~p, "
"stacktrace: ~0p", [Fun, Req, Reason1, _Stk]),
reply(From, Fun, {error, Reason1}),
{noreply, State#state{streams = Streams#{Fun => undefined}}}
end

View File

@ -123,12 +123,14 @@ call(ConnStr, Req) ->
{error, ?RESP_PARAMS_TYPE_ERROR,
<<"The conn type error">>};
Pid when is_pid(Pid) ->
case erlang:is_process_alive(Pid) of
true ->
emqx_exproto_conn:call(Pid, Req);
false ->
case catch emqx_exproto_conn:call(Pid, Req) of
{'EXIT',{noproc, _}} ->
{error, ?RESP_CONN_PROCESS_NOT_ALIVE,
<<"Connection process is not alive">>}
<<"Connection process is not alive">>};
{'EXIT',{timeout, _}} ->
{error, ?RESP_UNKNOWN, <<"Connection is not answered">>};
Result ->
Result
end
end.

View File

@ -146,4 +146,4 @@ lwm2m.dtls.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,E
## Note that 'lwm2m.dtls.ciphers' and 'lwm2m.dtls.psk_ciphers' cannot
## be configured at the same time.
## See 'https://tools.ietf.org/html/rfc4279#section-2'.
#lwm2m.dtls.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA
#lwm2m.dtls.psk_ciphers = RSA-PSK-AES256-GCM-SHA384,RSA-PSK-AES256-CBC-SHA384,RSA-PSK-AES128-GCM-SHA256,RSA-PSK-AES128-CBC-SHA256,RSA-PSK-AES256-CBC-SHA,RSA-PSK-AES128-CBC-SHA

View File

@ -185,7 +185,7 @@ end}.
OldCert = cuttlefish:conf_get("lwm2m.certfile", Conf, undefined),
%% Ciphers
SplitFun = fun(undefined) -> undefined; (S) -> string:tokens(S, ",") end,
SplitFun = fun(undefined) -> []; (S) -> string:tokens(S, ",") end,
Ciphers =
case cuttlefish:conf_get("lwm2m.dtls.ciphers", Conf, undefined) of
undefined ->
@ -198,16 +198,17 @@ end}.
undefined ->
[];
C2 ->
Psk = lists:map(fun("PSK-AES128-CBC-SHA") -> {psk, aes_128_cbc, sha};
("PSK-AES256-CBC-SHA") -> {psk, aes_256_cbc, sha};
("PSK-3DES-EDE-CBC-SHA") -> {psk, '3des_ede_cbc', sha};
("PSK-RC4-SHA") -> {psk, rc4_128, sha}
Psk = lists:map(fun("PSK-AES128-CBC-SHA") -> "RSA-PSK-AES128-CBC-SHA";
("PSK-AES256-CBC-SHA") -> "RSA-PSK-AES256-CBC-SHA";
("PSK-3DES-EDE-CBC-SHA") -> "RSA-PSK-3DES-EDE-CBC-SHA";
("PSK-RC4-SHA") -> "RSA-PSK-RC4-SHA";
(Suite) -> Suite
end, SplitFun(C2)),
[{ciphers, Psk}, {user_lookup_fun, {fun emqx_psk:lookup/3, <<>>}}]
end,
Ciphers /= []
andalso PskCiphers /= []
andalso cuttlefish:invalid("The 'lwm2m.dtls.ciphers' and 'lwm2m.dtls.psk_ciphers' cannot exist simultaneously."),
andalso cuttlefish:invalid("The 'lwm2m.dtls.ciphers' and 'lwm2m.dtls.psk_ciphers' cannot coexist"),
NCiphers = Ciphers ++ PskCiphers,

View File

@ -1,5 +1,5 @@
{deps,
[{lwm2m_coap, {git, "https://github.com/emqx/lwm2m-coap", {tag, "v1.1.2"}}}
[{lwm2m_coap, {git, "https://github.com/emqx/lwm2m-coap", {tag, "v1.1.5"}}}
]}.
{profiles,

Some files were not shown because too many files have changed in this diff Show More