Merge pull request #10425 from lafirest/feat/opentsdb
feat(opents): OpenTSDB integration
This commit is contained in:
commit
903b559e4f
|
@ -7,6 +7,7 @@ INFLUXDB_TAG=2.5.0
|
||||||
TDENGINE_TAG=3.0.2.4
|
TDENGINE_TAG=3.0.2.4
|
||||||
DYNAMO_TAG=1.21.0
|
DYNAMO_TAG=1.21.0
|
||||||
CASSANDRA_TAG=3.11.6
|
CASSANDRA_TAG=3.11.6
|
||||||
|
OPENTS_TAG=9aa7f88
|
||||||
|
|
||||||
MS_IMAGE_ADDR=mcr.microsoft.com/mssql/server
|
MS_IMAGE_ADDR=mcr.microsoft.com/mssql/server
|
||||||
SQLSERVER_TAG=2019-CU19-ubuntu-20.04
|
SQLSERVER_TAG=2019-CU19-ubuntu-20.04
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
opents_server:
|
||||||
|
container_name: opents
|
||||||
|
image: petergrace/opentsdb-docker:${OPENTS_TAG}
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- emqx_bridge
|
|
@ -26,6 +26,7 @@ services:
|
||||||
- 19876:9876
|
- 19876:9876
|
||||||
- 19042:9042
|
- 19042:9042
|
||||||
- 19142:9142
|
- 19142:9142
|
||||||
|
- 14242:4242
|
||||||
command:
|
command:
|
||||||
- "-host=0.0.0.0"
|
- "-host=0.0.0.0"
|
||||||
- "-config=/config/toxiproxy.json"
|
- "-config=/config/toxiproxy.json"
|
||||||
|
|
|
@ -101,5 +101,11 @@
|
||||||
"listen": "0.0.0.0:1433",
|
"listen": "0.0.0.0:1433",
|
||||||
"upstream": "sqlserver:1433",
|
"upstream": "sqlserver:1433",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "opents",
|
||||||
|
"listen": "0.0.0.0:4242",
|
||||||
|
"upstream": "opents:4242",
|
||||||
|
"enabled": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -168,6 +168,7 @@ jobs:
|
||||||
REDIS_TAG: "7.0"
|
REDIS_TAG: "7.0"
|
||||||
INFLUXDB_TAG: "2.5.0"
|
INFLUXDB_TAG: "2.5.0"
|
||||||
TDENGINE_TAG: "3.0.2.4"
|
TDENGINE_TAG: "3.0.2.4"
|
||||||
|
OPENTS_TAG: "9aa7f88"
|
||||||
PROFILE: ${{ matrix.profile }}
|
PROFILE: ${{ matrix.profile }}
|
||||||
CT_COVER_EXPORT_PREFIX: ${{ matrix.profile }}-${{ matrix.otp }}
|
CT_COVER_EXPORT_PREFIX: ${{ matrix.profile }}-${{ matrix.otp }}
|
||||||
run: ./scripts/ct/run.sh --ci --app ${{ matrix.app }}
|
run: ./scripts/ct/run.sh --ci --app ${{ matrix.app }}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
Business Source License 1.1
|
||||||
|
|
||||||
|
Licensor: Hangzhou EMQ Technologies Co., Ltd.
|
||||||
|
Licensed Work: EMQX Enterprise Edition
|
||||||
|
The Licensed Work is (c) 2023
|
||||||
|
Hangzhou EMQ Technologies Co., Ltd.
|
||||||
|
Additional Use Grant: Students and educators are granted right to copy,
|
||||||
|
modify, and create derivative work for research
|
||||||
|
or education.
|
||||||
|
Change Date: 2027-02-01
|
||||||
|
Change License: Apache License, Version 2.0
|
||||||
|
|
||||||
|
For information about alternative licensing arrangements for the Software,
|
||||||
|
please contact Licensor: https://www.emqx.com/en/contact
|
||||||
|
|
||||||
|
Notice
|
||||||
|
|
||||||
|
The Business Source License (this document, or the “License”) is not an Open
|
||||||
|
Source license. However, the Licensed Work will eventually be made available
|
||||||
|
under an Open Source License, as stated in this License.
|
||||||
|
|
||||||
|
License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
|
||||||
|
“Business Source License” is a trademark of MariaDB Corporation Ab.
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Business Source License 1.1
|
||||||
|
|
||||||
|
Terms
|
||||||
|
|
||||||
|
The Licensor hereby grants you the right to copy, modify, create derivative
|
||||||
|
works, redistribute, and make non-production use of the Licensed Work. The
|
||||||
|
Licensor may make an Additional Use Grant, above, permitting limited
|
||||||
|
production use.
|
||||||
|
|
||||||
|
Effective on the Change Date, or the fourth anniversary of the first publicly
|
||||||
|
available distribution of a specific version of the Licensed Work under this
|
||||||
|
License, whichever comes first, the Licensor hereby grants you rights under
|
||||||
|
the terms of the Change License, and the rights granted in the paragraph
|
||||||
|
above terminate.
|
||||||
|
|
||||||
|
If your use of the Licensed Work does not comply with the requirements
|
||||||
|
currently in effect as described in this License, you must purchase a
|
||||||
|
commercial license from the Licensor, its affiliated entities, or authorized
|
||||||
|
resellers, or you must refrain from using the Licensed Work.
|
||||||
|
|
||||||
|
All copies of the original and modified Licensed Work, and derivative works
|
||||||
|
of the Licensed Work, are subject to this License. This License applies
|
||||||
|
separately for each version of the Licensed Work and the Change Date may vary
|
||||||
|
for each version of the Licensed Work released by Licensor.
|
||||||
|
|
||||||
|
You must conspicuously display this License on each original or modified copy
|
||||||
|
of the Licensed Work. If you receive the Licensed Work in original or
|
||||||
|
modified form from a third party, the terms and conditions set forth in this
|
||||||
|
License apply to your use of that work.
|
||||||
|
|
||||||
|
Any use of the Licensed Work in violation of this License will automatically
|
||||||
|
terminate your rights under this License for the current and all other
|
||||||
|
versions of the Licensed Work.
|
||||||
|
|
||||||
|
This License does not grant you any right in any trademark or logo of
|
||||||
|
Licensor or its affiliates (provided that you may use a trademark or logo of
|
||||||
|
Licensor as expressly required by this License).
|
||||||
|
|
||||||
|
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
||||||
|
AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
||||||
|
TITLE.
|
||||||
|
|
||||||
|
MariaDB hereby grants you permission to use this License’s text to license
|
||||||
|
your works, and to refer to it using the trademark “Business Source License”,
|
||||||
|
as long as you comply with the Covenants of Licensor below.
|
||||||
|
|
||||||
|
Covenants of Licensor
|
||||||
|
|
||||||
|
In consideration of the right to use this License’s text and the “Business
|
||||||
|
Source License” name and trademark, Licensor covenants to MariaDB, and to all
|
||||||
|
other recipients of the licensed work to be provided by Licensor:
|
||||||
|
|
||||||
|
1. To specify as the Change License the GPL Version 2.0 or any later version,
|
||||||
|
or a license that is compatible with GPL Version 2.0 or a later version,
|
||||||
|
where “compatible” means that software provided under the Change License can
|
||||||
|
be included in a program with software provided under GPL Version 2.0 or a
|
||||||
|
later version. Licensor may specify additional Change Licenses without
|
||||||
|
limitation.
|
||||||
|
|
||||||
|
2. To either: (a) specify an additional grant of rights to use that does not
|
||||||
|
impose any additional restriction on the right granted in this License, as
|
||||||
|
the Additional Use Grant; or (b) insert the text “None”.
|
||||||
|
|
||||||
|
3. To specify a Change Date.
|
||||||
|
|
||||||
|
4. Not to modify this License in any other way.
|
|
@ -0,0 +1,36 @@
|
||||||
|
# EMQX OpenTSDB Bridge
|
||||||
|
|
||||||
|
[OpenTSDB](http://opentsdb.net) is a distributed, scalable Time Series Database (TSDB) written on top of HBase.
|
||||||
|
|
||||||
|
OpenTSDB was written to address a common need: store, index and serve metrics collected from computer systems (network gear, operating systems, applications) at a large scale, and make this data easily accessible and graphable.
|
||||||
|
|
||||||
|
OpenTSDB allows you to collect thousands of metrics from tens of thousands of hosts and applications, at a high rate (every few seconds).
|
||||||
|
|
||||||
|
OpenTSDB will never delete or downsample data and can easily store hundreds of billions of data points.
|
||||||
|
|
||||||
|
The application is used to connect EMQX and OpenTSDB. User can create a rule and easily ingest IoT data into OpenTSDB by leveraging the
|
||||||
|
[EMQX Rules](https://docs.emqx.com/en/enterprise/v5.0/data-integration/rules.html).
|
||||||
|
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
|
||||||
|
- Refer to [EMQX Rules](https://docs.emqx.com/en/enterprise/v5.0/data-integration/rules.html)
|
||||||
|
for the EMQX rules engine introduction.
|
||||||
|
|
||||||
|
|
||||||
|
# HTTP APIs
|
||||||
|
|
||||||
|
- Several APIs are provided for bridge management, which includes create bridge,
|
||||||
|
update bridge, get bridge, stop or restart bridge and list bridges etc.
|
||||||
|
|
||||||
|
Refer to [API Docs - Bridges](https://docs.emqx.com/en/enterprise/v5.0/admin/api-docs.html#tag/Bridges) for more detailed information.
|
||||||
|
|
||||||
|
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
Please see our [contributing.md](../../CONTRIBUTING.md).
|
||||||
|
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
EMQ Business Source License 1.1, refer to [LICENSE](BSL.txt).
|
|
@ -0,0 +1,2 @@
|
||||||
|
toxiproxy
|
||||||
|
opents
|
|
@ -0,0 +1,8 @@
|
||||||
|
{erl_opts, [debug_info]}.
|
||||||
|
|
||||||
|
{deps, [
|
||||||
|
{opentsdb, {git, "https://github.com/emqx/opentsdb-client-erl", {tag, "v0.5.1"}}},
|
||||||
|
{emqx_connector, {path, "../../apps/emqx_connector"}},
|
||||||
|
{emqx_resource, {path, "../../apps/emqx_resource"}},
|
||||||
|
{emqx_bridge, {path, "../../apps/emqx_bridge"}}
|
||||||
|
]}.
|
|
@ -0,0 +1,15 @@
|
||||||
|
{application, emqx_bridge_opents, [
|
||||||
|
{description, "EMQX Enterprise OpenTSDB Bridge"},
|
||||||
|
{vsn, "0.1.0"},
|
||||||
|
{registered, []},
|
||||||
|
{applications, [
|
||||||
|
kernel,
|
||||||
|
stdlib,
|
||||||
|
opentsdb
|
||||||
|
]},
|
||||||
|
{env, []},
|
||||||
|
{modules, []},
|
||||||
|
|
||||||
|
{licenses, ["BSL"]},
|
||||||
|
{links, []}
|
||||||
|
]}.
|
|
@ -0,0 +1,85 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
-module(emqx_bridge_opents).
|
||||||
|
|
||||||
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
|
-include_lib("emqx_resource/include/emqx_resource.hrl").
|
||||||
|
|
||||||
|
-import(hoconsc, [mk/2, enum/1, ref/2]).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
conn_bridge_examples/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
namespace/0,
|
||||||
|
roots/0,
|
||||||
|
fields/1,
|
||||||
|
desc/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
%% -------------------------------------------------------------------------------------------------
|
||||||
|
%% api
|
||||||
|
conn_bridge_examples(Method) ->
|
||||||
|
[
|
||||||
|
#{
|
||||||
|
<<"opents">> => #{
|
||||||
|
summary => <<"OpenTSDB Bridge">>,
|
||||||
|
value => values(Method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
].
|
||||||
|
|
||||||
|
values(_Method) ->
|
||||||
|
#{
|
||||||
|
enable => true,
|
||||||
|
type => opents,
|
||||||
|
name => <<"foo">>,
|
||||||
|
server => <<"http://127.0.0.1:4242">>,
|
||||||
|
pool_size => 8,
|
||||||
|
resource_opts => #{
|
||||||
|
worker_pool_size => 1,
|
||||||
|
health_check_interval => ?HEALTHCHECK_INTERVAL_RAW,
|
||||||
|
auto_restart_interval => ?AUTO_RESTART_INTERVAL_RAW,
|
||||||
|
batch_size => ?DEFAULT_BATCH_SIZE,
|
||||||
|
batch_time => ?DEFAULT_BATCH_TIME,
|
||||||
|
query_mode => async,
|
||||||
|
max_buffer_bytes => ?DEFAULT_BUFFER_BYTES
|
||||||
|
}
|
||||||
|
}.
|
||||||
|
|
||||||
|
%% -------------------------------------------------------------------------------------------------
|
||||||
|
%% Hocon Schema Definitions
|
||||||
|
namespace() -> "bridge_opents".
|
||||||
|
|
||||||
|
roots() -> [].
|
||||||
|
|
||||||
|
fields("config") ->
|
||||||
|
[
|
||||||
|
{enable, mk(boolean(), #{desc => ?DESC("config_enable"), default => true})}
|
||||||
|
] ++ emqx_resource_schema:fields("resource_opts") ++
|
||||||
|
emqx_bridge_opents_connector:fields(config);
|
||||||
|
fields("post") ->
|
||||||
|
[type_field(), name_field() | fields("config")];
|
||||||
|
fields("put") ->
|
||||||
|
fields("config");
|
||||||
|
fields("get") ->
|
||||||
|
emqx_bridge_schema:status_fields() ++ fields("post").
|
||||||
|
|
||||||
|
desc("config") ->
|
||||||
|
?DESC("desc_config");
|
||||||
|
desc(Method) when Method =:= "get"; Method =:= "put"; Method =:= "post" ->
|
||||||
|
["Configuration for OpenTSDB using `", string:to_upper(Method), "` method."];
|
||||||
|
desc(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
|
%% -------------------------------------------------------------------------------------------------
|
||||||
|
%% internal
|
||||||
|
|
||||||
|
type_field() ->
|
||||||
|
{type, mk(enum([opents]), #{required => true, desc => ?DESC("desc_type")})}.
|
||||||
|
|
||||||
|
name_field() ->
|
||||||
|
{name, mk(binary(), #{required => true, desc => ?DESC("desc_name")})}.
|
|
@ -0,0 +1,184 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_bridge_opents_connector).
|
||||||
|
|
||||||
|
-behaviour(emqx_resource).
|
||||||
|
|
||||||
|
-include_lib("emqx_resource/include/emqx_resource.hrl").
|
||||||
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
||||||
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
|
|
||||||
|
-export([roots/0, fields/1]).
|
||||||
|
|
||||||
|
%% `emqx_resource' API
|
||||||
|
-export([
|
||||||
|
callback_mode/0,
|
||||||
|
is_buffer_supported/0,
|
||||||
|
on_start/2,
|
||||||
|
on_stop/2,
|
||||||
|
on_query/3,
|
||||||
|
on_batch_query/3,
|
||||||
|
on_get_status/2
|
||||||
|
]).
|
||||||
|
|
||||||
|
-export([connect/1]).
|
||||||
|
|
||||||
|
-import(hoconsc, [mk/2, enum/1, ref/2]).
|
||||||
|
|
||||||
|
%%=====================================================================
|
||||||
|
%% Hocon schema
|
||||||
|
roots() ->
|
||||||
|
[{config, #{type => hoconsc:ref(?MODULE, config)}}].
|
||||||
|
|
||||||
|
fields(config) ->
|
||||||
|
[
|
||||||
|
{server, mk(binary(), #{required => true, desc => ?DESC("server")})},
|
||||||
|
{pool_size, fun emqx_connector_schema_lib:pool_size/1},
|
||||||
|
{summary, mk(boolean(), #{default => true, desc => ?DESC("summary")})},
|
||||||
|
{details, mk(boolean(), #{default => false, desc => ?DESC("details")})},
|
||||||
|
{auto_reconnect, fun emqx_connector_schema_lib:auto_reconnect/1}
|
||||||
|
].
|
||||||
|
|
||||||
|
%%========================================================================================
|
||||||
|
%% `emqx_resource' API
|
||||||
|
%%========================================================================================
|
||||||
|
|
||||||
|
callback_mode() -> always_sync.
|
||||||
|
|
||||||
|
is_buffer_supported() -> false.
|
||||||
|
|
||||||
|
on_start(
|
||||||
|
InstanceId,
|
||||||
|
#{
|
||||||
|
server := Server,
|
||||||
|
pool_size := PoolSize,
|
||||||
|
summary := Summary,
|
||||||
|
details := Details,
|
||||||
|
resource_opts := #{batch_size := BatchSize}
|
||||||
|
} = Config
|
||||||
|
) ->
|
||||||
|
?SLOG(info, #{
|
||||||
|
msg => "starting_opents_connector",
|
||||||
|
connector => InstanceId,
|
||||||
|
config => emqx_utils:redact(Config)
|
||||||
|
}),
|
||||||
|
|
||||||
|
Options = [
|
||||||
|
{server, to_str(Server)},
|
||||||
|
{summary, Summary},
|
||||||
|
{details, Details},
|
||||||
|
{max_batch_size, BatchSize},
|
||||||
|
{pool_size, PoolSize}
|
||||||
|
],
|
||||||
|
|
||||||
|
State = #{pool_name => InstanceId, server => Server},
|
||||||
|
case opentsdb_connectivity(Server) of
|
||||||
|
ok ->
|
||||||
|
case emqx_resource_pool:start(InstanceId, ?MODULE, Options) of
|
||||||
|
ok ->
|
||||||
|
{ok, State};
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end;
|
||||||
|
{error, Reason} = Error ->
|
||||||
|
?SLOG(error, #{msg => "Initiate resource failed", reason => Reason}),
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
on_stop(InstanceId, #{pool_name := PoolName} = _State) ->
|
||||||
|
?SLOG(info, #{
|
||||||
|
msg => "stopping_opents_connector",
|
||||||
|
connector => InstanceId
|
||||||
|
}),
|
||||||
|
emqx_resource_pool:stop(PoolName).
|
||||||
|
|
||||||
|
on_query(InstanceId, Request, State) ->
|
||||||
|
on_batch_query(InstanceId, [Request], State).
|
||||||
|
|
||||||
|
on_batch_query(
|
||||||
|
InstanceId,
|
||||||
|
BatchReq,
|
||||||
|
State
|
||||||
|
) ->
|
||||||
|
Datas = [format_opentsdb_msg(Msg) || {_Key, Msg} <- BatchReq],
|
||||||
|
do_query(InstanceId, Datas, State).
|
||||||
|
|
||||||
|
on_get_status(_InstanceId, #{server := Server}) ->
|
||||||
|
Result =
|
||||||
|
case opentsdb_connectivity(Server) of
|
||||||
|
ok ->
|
||||||
|
connected;
|
||||||
|
{error, Reason} ->
|
||||||
|
?SLOG(error, #{msg => "OpenTSDB lost connection", reason => Reason}),
|
||||||
|
connecting
|
||||||
|
end,
|
||||||
|
Result.
|
||||||
|
|
||||||
|
%%========================================================================================
|
||||||
|
%% Helper fns
|
||||||
|
%%========================================================================================
|
||||||
|
|
||||||
|
do_query(InstanceId, Query, #{pool_name := PoolName} = State) ->
|
||||||
|
?TRACE(
|
||||||
|
"QUERY",
|
||||||
|
"opents_connector_received",
|
||||||
|
#{connector => InstanceId, query => Query, state => State}
|
||||||
|
),
|
||||||
|
Result = ecpool:pick_and_do(PoolName, {opentsdb, put, [Query]}, no_handover),
|
||||||
|
|
||||||
|
case Result of
|
||||||
|
{error, Reason} ->
|
||||||
|
?tp(
|
||||||
|
opents_connector_query_return,
|
||||||
|
#{error => Reason}
|
||||||
|
),
|
||||||
|
?SLOG(error, #{
|
||||||
|
msg => "opents_connector_do_query_failed",
|
||||||
|
connector => InstanceId,
|
||||||
|
query => Query,
|
||||||
|
reason => Reason
|
||||||
|
}),
|
||||||
|
Result;
|
||||||
|
_ ->
|
||||||
|
?tp(
|
||||||
|
opents_connector_query_return,
|
||||||
|
#{result => Result}
|
||||||
|
),
|
||||||
|
Result
|
||||||
|
end.
|
||||||
|
|
||||||
|
connect(Opts) ->
|
||||||
|
opentsdb:start_link(Opts).
|
||||||
|
|
||||||
|
to_str(List) when is_list(List) ->
|
||||||
|
List;
|
||||||
|
to_str(Bin) when is_binary(Bin) ->
|
||||||
|
erlang:binary_to_list(Bin).
|
||||||
|
|
||||||
|
opentsdb_connectivity(Server) ->
|
||||||
|
SvrUrl =
|
||||||
|
case Server of
|
||||||
|
<<"http://", _/binary>> -> Server;
|
||||||
|
<<"https://", _/binary>> -> Server;
|
||||||
|
_ -> "http://" ++ Server
|
||||||
|
end,
|
||||||
|
emqx_plugin_libs_rule:http_connectivity(SvrUrl).
|
||||||
|
|
||||||
|
format_opentsdb_msg(Msg) ->
|
||||||
|
maps:with(
|
||||||
|
[
|
||||||
|
timestamp,
|
||||||
|
metric,
|
||||||
|
tags,
|
||||||
|
value,
|
||||||
|
<<"timestamp">>,
|
||||||
|
<<"metric">>,
|
||||||
|
<<"tags">>,
|
||||||
|
<<"value">>
|
||||||
|
],
|
||||||
|
Msg
|
||||||
|
).
|
|
@ -0,0 +1,363 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_bridge_opents_SUITE).
|
||||||
|
|
||||||
|
-compile(nowarn_export_all).
|
||||||
|
-compile(export_all).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-include_lib("common_test/include/ct.hrl").
|
||||||
|
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
||||||
|
|
||||||
|
% DB defaults
|
||||||
|
-define(BATCH_SIZE, 10).
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% CT boilerplate
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
all() ->
|
||||||
|
[
|
||||||
|
{group, with_batch},
|
||||||
|
{group, without_batch}
|
||||||
|
].
|
||||||
|
|
||||||
|
groups() ->
|
||||||
|
TCs = emqx_common_test_helpers:all(?MODULE),
|
||||||
|
[
|
||||||
|
{with_batch, TCs},
|
||||||
|
{without_batch, TCs}
|
||||||
|
].
|
||||||
|
|
||||||
|
init_per_group(with_batch, Config0) ->
|
||||||
|
Config = [{batch_size, ?BATCH_SIZE} | Config0],
|
||||||
|
common_init(Config);
|
||||||
|
init_per_group(without_batch, Config0) ->
|
||||||
|
Config = [{batch_size, 1} | Config0],
|
||||||
|
common_init(Config);
|
||||||
|
init_per_group(_Group, Config) ->
|
||||||
|
Config.
|
||||||
|
|
||||||
|
end_per_group(Group, Config) when Group =:= with_batch; Group =:= without_batch ->
|
||||||
|
ProxyHost = ?config(proxy_host, Config),
|
||||||
|
ProxyPort = ?config(proxy_port, Config),
|
||||||
|
emqx_common_test_helpers:reset_proxy(ProxyHost, ProxyPort),
|
||||||
|
ok;
|
||||||
|
end_per_group(_Group, _Config) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
init_per_suite(Config) ->
|
||||||
|
Config.
|
||||||
|
|
||||||
|
end_per_suite(_Config) ->
|
||||||
|
emqx_mgmt_api_test_util:end_suite(),
|
||||||
|
ok = emqx_common_test_helpers:stop_apps([emqx_bridge, emqx_conf]),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
init_per_testcase(_Testcase, Config) ->
|
||||||
|
delete_bridge(Config),
|
||||||
|
snabbkaffe:start_trace(),
|
||||||
|
Config.
|
||||||
|
|
||||||
|
end_per_testcase(_Testcase, Config) ->
|
||||||
|
ProxyHost = ?config(proxy_host, Config),
|
||||||
|
ProxyPort = ?config(proxy_port, Config),
|
||||||
|
emqx_common_test_helpers:reset_proxy(ProxyHost, ProxyPort),
|
||||||
|
ok = snabbkaffe:stop(),
|
||||||
|
delete_bridge(Config),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% Helper fns
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
common_init(ConfigT) ->
|
||||||
|
Host = os:getenv("OPENTS_HOST", "toxiproxy"),
|
||||||
|
Port = list_to_integer(os:getenv("OPENTS_PORT", "4242")),
|
||||||
|
|
||||||
|
Config0 = [
|
||||||
|
{opents_host, Host},
|
||||||
|
{opents_port, Port},
|
||||||
|
{proxy_name, "opents"}
|
||||||
|
| ConfigT
|
||||||
|
],
|
||||||
|
|
||||||
|
BridgeType = proplists:get_value(bridge_type, Config0, <<"opents">>),
|
||||||
|
case emqx_common_test_helpers:is_tcp_server_available(Host, Port) of
|
||||||
|
true ->
|
||||||
|
% Setup toxiproxy
|
||||||
|
ProxyHost = os:getenv("PROXY_HOST", "toxiproxy"),
|
||||||
|
ProxyPort = list_to_integer(os:getenv("PROXY_PORT", "8474")),
|
||||||
|
emqx_common_test_helpers:reset_proxy(ProxyHost, ProxyPort),
|
||||||
|
% Ensure EE bridge module is loaded
|
||||||
|
_ = application:load(emqx_ee_bridge),
|
||||||
|
_ = emqx_ee_bridge:module_info(),
|
||||||
|
ok = emqx_common_test_helpers:start_apps([emqx_conf, emqx_bridge]),
|
||||||
|
emqx_mgmt_api_test_util:init_suite(),
|
||||||
|
{Name, OpenTSConf} = opents_config(BridgeType, Config0),
|
||||||
|
Config =
|
||||||
|
[
|
||||||
|
{opents_config, OpenTSConf},
|
||||||
|
{opents_bridge_type, BridgeType},
|
||||||
|
{opents_name, Name},
|
||||||
|
{proxy_host, ProxyHost},
|
||||||
|
{proxy_port, ProxyPort}
|
||||||
|
| Config0
|
||||||
|
],
|
||||||
|
Config;
|
||||||
|
false ->
|
||||||
|
case os:getenv("IS_CI") of
|
||||||
|
"yes" ->
|
||||||
|
throw(no_opents);
|
||||||
|
_ ->
|
||||||
|
{skip, no_opents}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
opents_config(BridgeType, Config) ->
|
||||||
|
Port = integer_to_list(?config(opents_port, Config)),
|
||||||
|
Server = "http://" ++ ?config(opents_host, Config) ++ ":" ++ Port,
|
||||||
|
Name = atom_to_binary(?MODULE),
|
||||||
|
BatchSize = ?config(batch_size, Config),
|
||||||
|
ConfigString =
|
||||||
|
io_lib:format(
|
||||||
|
"bridges.~s.~s {\n"
|
||||||
|
" enable = true\n"
|
||||||
|
" server = ~p\n"
|
||||||
|
" resource_opts = {\n"
|
||||||
|
" request_timeout = 500ms\n"
|
||||||
|
" batch_size = ~b\n"
|
||||||
|
" query_mode = sync\n"
|
||||||
|
" }\n"
|
||||||
|
"}",
|
||||||
|
[
|
||||||
|
BridgeType,
|
||||||
|
Name,
|
||||||
|
Server,
|
||||||
|
BatchSize
|
||||||
|
]
|
||||||
|
),
|
||||||
|
{Name, parse_and_check(ConfigString, BridgeType, Name)}.
|
||||||
|
|
||||||
|
parse_and_check(ConfigString, BridgeType, Name) ->
|
||||||
|
{ok, RawConf} = hocon:binary(ConfigString, #{format => map}),
|
||||||
|
hocon_tconf:check_plain(emqx_bridge_schema, RawConf, #{required => false, atom_key => false}),
|
||||||
|
#{<<"bridges">> := #{BridgeType := #{Name := Config}}} = RawConf,
|
||||||
|
Config.
|
||||||
|
|
||||||
|
create_bridge(Config) ->
|
||||||
|
create_bridge(Config, _Overrides = #{}).
|
||||||
|
|
||||||
|
create_bridge(Config, Overrides) ->
|
||||||
|
BridgeType = ?config(opents_bridge_type, Config),
|
||||||
|
Name = ?config(opents_name, Config),
|
||||||
|
Config0 = ?config(opents_config, Config),
|
||||||
|
Config1 = emqx_utils_maps:deep_merge(Config0, Overrides),
|
||||||
|
emqx_bridge:create(BridgeType, Name, Config1).
|
||||||
|
|
||||||
|
delete_bridge(Config) ->
|
||||||
|
BridgeType = ?config(opents_bridge_type, Config),
|
||||||
|
Name = ?config(opents_name, Config),
|
||||||
|
emqx_bridge:remove(BridgeType, Name).
|
||||||
|
|
||||||
|
create_bridge_http(Params) ->
|
||||||
|
Path = emqx_mgmt_api_test_util:api_path(["bridges"]),
|
||||||
|
AuthHeader = emqx_mgmt_api_test_util:auth_header_(),
|
||||||
|
case emqx_mgmt_api_test_util:request_api(post, Path, "", AuthHeader, Params) of
|
||||||
|
{ok, Res} -> {ok, emqx_utils_json:decode(Res, [return_maps])};
|
||||||
|
Error -> Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
send_message(Config, Payload) ->
|
||||||
|
Name = ?config(opents_name, Config),
|
||||||
|
BridgeType = ?config(opents_bridge_type, Config),
|
||||||
|
BridgeID = emqx_bridge_resource:bridge_id(BridgeType, Name),
|
||||||
|
emqx_bridge:send_message(BridgeID, Payload).
|
||||||
|
|
||||||
|
query_resource(Config, Request) ->
|
||||||
|
query_resource(Config, Request, 1_000).
|
||||||
|
|
||||||
|
query_resource(Config, Request, Timeout) ->
|
||||||
|
Name = ?config(opents_name, Config),
|
||||||
|
BridgeType = ?config(opents_bridge_type, Config),
|
||||||
|
ResourceID = emqx_bridge_resource:resource_id(BridgeType, Name),
|
||||||
|
emqx_resource:query(ResourceID, Request, #{timeout => Timeout}).
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% Testcases
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
t_setup_via_config_and_publish(Config) ->
|
||||||
|
?assertMatch(
|
||||||
|
{ok, _},
|
||||||
|
create_bridge(Config)
|
||||||
|
),
|
||||||
|
SentData = make_data(),
|
||||||
|
?check_trace(
|
||||||
|
begin
|
||||||
|
{_, {ok, #{result := Result}}} =
|
||||||
|
?wait_async_action(
|
||||||
|
send_message(Config, SentData),
|
||||||
|
#{?snk_kind := buffer_worker_flush_ack},
|
||||||
|
2_000
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
{ok, 200, #{failed := 0, success := 1}}, Result
|
||||||
|
),
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
fun(Trace0) ->
|
||||||
|
Trace = ?of_kind(opents_connector_query_return, Trace0),
|
||||||
|
?assertMatch([#{result := {ok, 200, #{failed := 0, success := 1}}}], Trace),
|
||||||
|
ok
|
||||||
|
end
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_setup_via_http_api_and_publish(Config) ->
|
||||||
|
BridgeType = ?config(opents_bridge_type, Config),
|
||||||
|
Name = ?config(opents_name, Config),
|
||||||
|
OpentsConfig0 = ?config(opents_config, Config),
|
||||||
|
OpentsConfig = OpentsConfig0#{
|
||||||
|
<<"name">> => Name,
|
||||||
|
<<"type">> => BridgeType
|
||||||
|
},
|
||||||
|
?assertMatch(
|
||||||
|
{ok, _},
|
||||||
|
create_bridge_http(OpentsConfig)
|
||||||
|
),
|
||||||
|
SentData = make_data(),
|
||||||
|
?check_trace(
|
||||||
|
begin
|
||||||
|
Request = {send_message, SentData},
|
||||||
|
Res0 = query_resource(Config, Request, 2_500),
|
||||||
|
?assertMatch(
|
||||||
|
{ok, 200, #{failed := 0, success := 1}}, Res0
|
||||||
|
),
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
fun(Trace0) ->
|
||||||
|
Trace = ?of_kind(opents_connector_query_return, Trace0),
|
||||||
|
?assertMatch([#{result := {ok, 200, #{failed := 0, success := 1}}}], Trace),
|
||||||
|
ok
|
||||||
|
end
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_get_status(Config) ->
|
||||||
|
?assertMatch(
|
||||||
|
{ok, _},
|
||||||
|
create_bridge(Config)
|
||||||
|
),
|
||||||
|
|
||||||
|
Name = ?config(opents_name, Config),
|
||||||
|
BridgeType = ?config(opents_bridge_type, Config),
|
||||||
|
ResourceID = emqx_bridge_resource:resource_id(BridgeType, Name),
|
||||||
|
|
||||||
|
?assertEqual({ok, connected}, emqx_resource_manager:health_check(ResourceID)),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_create_disconnected(Config) ->
|
||||||
|
BridgeType = proplists:get_value(bridge_type, Config, <<"opents">>),
|
||||||
|
Config1 = lists:keyreplace(opents_port, 1, Config, {opents_port, 61234}),
|
||||||
|
{_Name, OpenTSConf} = opents_config(BridgeType, Config1),
|
||||||
|
|
||||||
|
Config2 = lists:keyreplace(opents_config, 1, Config1, {opents_config, OpenTSConf}),
|
||||||
|
?assertMatch({ok, _}, create_bridge(Config2)),
|
||||||
|
|
||||||
|
Name = ?config(opents_name, Config),
|
||||||
|
ResourceID = emqx_bridge_resource:resource_id(BridgeType, Name),
|
||||||
|
?assertEqual({ok, disconnected}, emqx_resource_manager:health_check(ResourceID)),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_write_failure(Config) ->
|
||||||
|
ProxyName = ?config(proxy_name, Config),
|
||||||
|
ProxyPort = ?config(proxy_port, Config),
|
||||||
|
ProxyHost = ?config(proxy_host, Config),
|
||||||
|
{ok, _} = create_bridge(Config),
|
||||||
|
SentData = make_data(),
|
||||||
|
emqx_common_test_helpers:with_failure(down, ProxyName, ProxyHost, ProxyPort, fun() ->
|
||||||
|
{_, {ok, #{result := Result}}} =
|
||||||
|
?wait_async_action(
|
||||||
|
send_message(Config, SentData),
|
||||||
|
#{?snk_kind := buffer_worker_flush_ack},
|
||||||
|
2_000
|
||||||
|
),
|
||||||
|
?assertMatch({error, _}, Result),
|
||||||
|
ok
|
||||||
|
end),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_write_timeout(Config) ->
|
||||||
|
ProxyName = ?config(proxy_name, Config),
|
||||||
|
ProxyPort = ?config(proxy_port, Config),
|
||||||
|
ProxyHost = ?config(proxy_host, Config),
|
||||||
|
{ok, _} = create_bridge(
|
||||||
|
Config,
|
||||||
|
#{
|
||||||
|
<<"resource_opts">> => #{
|
||||||
|
<<"request_timeout">> => 500,
|
||||||
|
<<"resume_interval">> => 100,
|
||||||
|
<<"health_check_interval">> => 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
SentData = make_data(),
|
||||||
|
emqx_common_test_helpers:with_failure(
|
||||||
|
timeout, ProxyName, ProxyHost, ProxyPort, fun() ->
|
||||||
|
?assertMatch(
|
||||||
|
{error, {resource_error, #{reason := timeout}}},
|
||||||
|
query_resource(Config, {send_message, SentData})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_missing_data(Config) ->
|
||||||
|
?assertMatch(
|
||||||
|
{ok, _},
|
||||||
|
create_bridge(Config)
|
||||||
|
),
|
||||||
|
{_, {ok, #{result := Result}}} =
|
||||||
|
?wait_async_action(
|
||||||
|
send_message(Config, #{}),
|
||||||
|
#{?snk_kind := buffer_worker_flush_ack},
|
||||||
|
2_000
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
{error, {400, #{failed := 1, success := 0}}},
|
||||||
|
Result
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
t_bad_data(Config) ->
|
||||||
|
?assertMatch(
|
||||||
|
{ok, _},
|
||||||
|
create_bridge(Config)
|
||||||
|
),
|
||||||
|
Data = maps:without([metric], make_data()),
|
||||||
|
{_, {ok, #{result := Result}}} =
|
||||||
|
?wait_async_action(
|
||||||
|
send_message(Config, Data),
|
||||||
|
#{?snk_kind := buffer_worker_flush_ack},
|
||||||
|
2_000
|
||||||
|
),
|
||||||
|
|
||||||
|
?assertMatch(
|
||||||
|
{error, {400, #{failed := 1, success := 0}}}, Result
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
make_data() ->
|
||||||
|
make_data(<<"cpu">>, 12).
|
||||||
|
|
||||||
|
make_data(Metric, Value) ->
|
||||||
|
#{
|
||||||
|
metric => Metric,
|
||||||
|
tags => #{
|
||||||
|
<<"host">> => <<"serverA">>
|
||||||
|
},
|
||||||
|
value => Value
|
||||||
|
}.
|
|
@ -0,0 +1 @@
|
||||||
|
Implement OpenTSDB data bridge.
|
|
@ -8,7 +8,8 @@
|
||||||
emqx_ee_connector,
|
emqx_ee_connector,
|
||||||
telemetry,
|
telemetry,
|
||||||
emqx_bridge_kafka,
|
emqx_bridge_kafka,
|
||||||
emqx_bridge_gcp_pubsub
|
emqx_bridge_gcp_pubsub,
|
||||||
|
emqx_bridge_opents
|
||||||
]},
|
]},
|
||||||
{env, []},
|
{env, []},
|
||||||
{modules, []},
|
{modules, []},
|
||||||
|
|
|
@ -35,7 +35,8 @@ api_schemas(Method) ->
|
||||||
ref(emqx_ee_bridge_clickhouse, Method),
|
ref(emqx_ee_bridge_clickhouse, Method),
|
||||||
ref(emqx_ee_bridge_dynamo, Method),
|
ref(emqx_ee_bridge_dynamo, Method),
|
||||||
ref(emqx_ee_bridge_rocketmq, Method),
|
ref(emqx_ee_bridge_rocketmq, Method),
|
||||||
ref(emqx_ee_bridge_sqlserver, Method)
|
ref(emqx_ee_bridge_sqlserver, Method),
|
||||||
|
ref(emqx_bridge_opents, Method)
|
||||||
].
|
].
|
||||||
|
|
||||||
schema_modules() ->
|
schema_modules() ->
|
||||||
|
@ -55,7 +56,8 @@ schema_modules() ->
|
||||||
emqx_ee_bridge_clickhouse,
|
emqx_ee_bridge_clickhouse,
|
||||||
emqx_ee_bridge_dynamo,
|
emqx_ee_bridge_dynamo,
|
||||||
emqx_ee_bridge_rocketmq,
|
emqx_ee_bridge_rocketmq,
|
||||||
emqx_ee_bridge_sqlserver
|
emqx_ee_bridge_sqlserver,
|
||||||
|
emqx_bridge_opents
|
||||||
].
|
].
|
||||||
|
|
||||||
examples(Method) ->
|
examples(Method) ->
|
||||||
|
@ -94,7 +96,8 @@ resource_type(tdengine) -> emqx_ee_connector_tdengine;
|
||||||
resource_type(clickhouse) -> emqx_ee_connector_clickhouse;
|
resource_type(clickhouse) -> emqx_ee_connector_clickhouse;
|
||||||
resource_type(dynamo) -> emqx_ee_connector_dynamo;
|
resource_type(dynamo) -> emqx_ee_connector_dynamo;
|
||||||
resource_type(rocketmq) -> emqx_ee_connector_rocketmq;
|
resource_type(rocketmq) -> emqx_ee_connector_rocketmq;
|
||||||
resource_type(sqlserver) -> emqx_ee_connector_sqlserver.
|
resource_type(sqlserver) -> emqx_ee_connector_sqlserver;
|
||||||
|
resource_type(opents) -> emqx_bridge_opents_connector.
|
||||||
|
|
||||||
fields(bridges) ->
|
fields(bridges) ->
|
||||||
[
|
[
|
||||||
|
@ -153,6 +156,14 @@ fields(bridges) ->
|
||||||
desc => <<"Cassandra Bridge Config">>,
|
desc => <<"Cassandra Bridge Config">>,
|
||||||
required => false
|
required => false
|
||||||
}
|
}
|
||||||
|
)},
|
||||||
|
{opents,
|
||||||
|
mk(
|
||||||
|
hoconsc:map(name, ref(emqx_bridge_opents, "config")),
|
||||||
|
#{
|
||||||
|
desc => <<"OpenTSDB Bridge Config">>,
|
||||||
|
required => false
|
||||||
|
}
|
||||||
)}
|
)}
|
||||||
] ++ kafka_structs() ++ mongodb_structs() ++ influxdb_structs() ++ redis_structs() ++
|
] ++ kafka_structs() ++ mongodb_structs() ++ influxdb_structs() ++ redis_structs() ++
|
||||||
pgsql_structs() ++ clickhouse_structs() ++ sqlserver_structs().
|
pgsql_structs() ++ clickhouse_structs() ++ sqlserver_structs().
|
||||||
|
|
5
mix.exs
5
mix.exs
|
@ -157,6 +157,7 @@ defmodule EMQXUmbrella.MixProject do
|
||||||
:emqx_bridge_kafka,
|
:emqx_bridge_kafka,
|
||||||
:emqx_bridge_gcp_pubsub,
|
:emqx_bridge_gcp_pubsub,
|
||||||
:emqx_bridge_cassandra,
|
:emqx_bridge_cassandra,
|
||||||
|
:emqx_bridge_opents,
|
||||||
:emqx_bridge_clickhouse,
|
:emqx_bridge_clickhouse,
|
||||||
:emqx_bridge_dynamo,
|
:emqx_bridge_dynamo,
|
||||||
:emqx_bridge_hstreamdb,
|
:emqx_bridge_hstreamdb,
|
||||||
|
@ -182,7 +183,8 @@ defmodule EMQXUmbrella.MixProject do
|
||||||
{:brod, github: "kafka4beam/brod", tag: "3.16.8"},
|
{:brod, github: "kafka4beam/brod", tag: "3.16.8"},
|
||||||
{:snappyer, "1.2.8", override: true},
|
{:snappyer, "1.2.8", override: true},
|
||||||
{:crc32cer, "0.1.8", override: true},
|
{:crc32cer, "0.1.8", override: true},
|
||||||
{:supervisor3, "1.1.12", override: true}
|
{:supervisor3, "1.1.12", override: true},
|
||||||
|
{:opentsdb, github: "emqx/opentsdb-client-erl", tag: "v0.5.1", override: true}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -360,6 +362,7 @@ defmodule EMQXUmbrella.MixProject do
|
||||||
emqx_bridge_kafka: :permanent,
|
emqx_bridge_kafka: :permanent,
|
||||||
emqx_bridge_gcp_pubsub: :permanent,
|
emqx_bridge_gcp_pubsub: :permanent,
|
||||||
emqx_bridge_cassandra: :permanent,
|
emqx_bridge_cassandra: :permanent,
|
||||||
|
emqx_bridge_opents: :permanent,
|
||||||
emqx_bridge_clickhouse: :permanent,
|
emqx_bridge_clickhouse: :permanent,
|
||||||
emqx_bridge_dynamo: :permanent,
|
emqx_bridge_dynamo: :permanent,
|
||||||
emqx_bridge_hstreamdb: :permanent,
|
emqx_bridge_hstreamdb: :permanent,
|
||||||
|
|
|
@ -81,6 +81,7 @@ is_enterprise(ee) -> true.
|
||||||
is_community_umbrella_app("apps/emqx_bridge_kafka") -> false;
|
is_community_umbrella_app("apps/emqx_bridge_kafka") -> false;
|
||||||
is_community_umbrella_app("apps/emqx_bridge_gcp_pubsub") -> false;
|
is_community_umbrella_app("apps/emqx_bridge_gcp_pubsub") -> false;
|
||||||
is_community_umbrella_app("apps/emqx_bridge_cassandra") -> false;
|
is_community_umbrella_app("apps/emqx_bridge_cassandra") -> false;
|
||||||
|
is_community_umbrella_app("apps/emqx_bridge_opents") -> false;
|
||||||
is_community_umbrella_app("apps/emqx_bridge_clickhouse") -> false;
|
is_community_umbrella_app("apps/emqx_bridge_clickhouse") -> false;
|
||||||
is_community_umbrella_app("apps/emqx_bridge_dynamo") -> false;
|
is_community_umbrella_app("apps/emqx_bridge_dynamo") -> false;
|
||||||
is_community_umbrella_app("apps/emqx_bridge_hstreamdb") -> false;
|
is_community_umbrella_app("apps/emqx_bridge_hstreamdb") -> false;
|
||||||
|
@ -455,6 +456,7 @@ relx_apps_per_edition(ee) ->
|
||||||
emqx_bridge_kafka,
|
emqx_bridge_kafka,
|
||||||
emqx_bridge_gcp_pubsub,
|
emqx_bridge_gcp_pubsub,
|
||||||
emqx_bridge_cassandra,
|
emqx_bridge_cassandra,
|
||||||
|
emqx_bridge_opents,
|
||||||
emqx_bridge_clickhouse,
|
emqx_bridge_clickhouse,
|
||||||
emqx_bridge_dynamo,
|
emqx_bridge_dynamo,
|
||||||
emqx_bridge_hstreamdb,
|
emqx_bridge_hstreamdb,
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
emqx_bridge_opents {
|
||||||
|
|
||||||
|
config_enable.desc:
|
||||||
|
"""Enable or disable this bridge"""
|
||||||
|
|
||||||
|
config_enable.label:
|
||||||
|
"Enable Or Disable Bridge"
|
||||||
|
|
||||||
|
desc_config.desc:
|
||||||
|
"""Configuration for an OpenTSDB bridge."""
|
||||||
|
|
||||||
|
desc_config.label:
|
||||||
|
"OpenTSDB Bridge Configuration"
|
||||||
|
|
||||||
|
desc_type.desc:
|
||||||
|
"""The Bridge Type"""
|
||||||
|
|
||||||
|
desc_type.label:
|
||||||
|
"Bridge Type"
|
||||||
|
|
||||||
|
desc_name.desc:
|
||||||
|
"""Bridge name."""
|
||||||
|
|
||||||
|
desc_name.label:
|
||||||
|
"Bridge Name"
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
emqx_bridge_opents_connector {
|
||||||
|
|
||||||
|
server.desc:
|
||||||
|
"""The URL of OpenTSDB endpoint."""
|
||||||
|
|
||||||
|
server.label:
|
||||||
|
"URL"
|
||||||
|
|
||||||
|
summary.desc:
|
||||||
|
"""Whether to return summary information."""
|
||||||
|
|
||||||
|
summary.label:
|
||||||
|
"Summary"
|
||||||
|
|
||||||
|
details.desc:
|
||||||
|
"""Whether to return detailed information."""
|
||||||
|
|
||||||
|
details.label:
|
||||||
|
"Details"
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
emqx_bridge_opents {
|
||||||
|
|
||||||
|
config_enable.desc:
|
||||||
|
"""启用/禁用桥接"""
|
||||||
|
|
||||||
|
config_enable.label:
|
||||||
|
"启用/禁用桥接"
|
||||||
|
|
||||||
|
desc_config.desc:
|
||||||
|
"""OpenTSDB 桥接配置"""
|
||||||
|
|
||||||
|
desc_config.label:
|
||||||
|
"OpenTSDB 桥接配置"
|
||||||
|
|
||||||
|
desc_type.desc:
|
||||||
|
"""Bridge 类型"""
|
||||||
|
|
||||||
|
desc_type.label:
|
||||||
|
"桥接类型"
|
||||||
|
|
||||||
|
desc_name.desc:
|
||||||
|
"""桥接名字"""
|
||||||
|
|
||||||
|
desc_name.label:
|
||||||
|
"桥接名字"
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
emqx_bridge_opents_connector {
|
||||||
|
|
||||||
|
server.desc:
|
||||||
|
"""服务器的地址。"""
|
||||||
|
|
||||||
|
server.label:
|
||||||
|
"服务器地址"
|
||||||
|
|
||||||
|
summary.desc:
|
||||||
|
"""是否返回摘要信息。"""
|
||||||
|
|
||||||
|
summary.label:
|
||||||
|
"摘要信息"
|
||||||
|
|
||||||
|
details.desc:
|
||||||
|
"""是否返回详细信息。"""
|
||||||
|
|
||||||
|
details.label:
|
||||||
|
"详细信息"
|
||||||
|
}
|
|
@ -188,6 +188,9 @@ for dep in ${CT_DEPS}; do
|
||||||
ODBC_REQUEST='yes'
|
ODBC_REQUEST='yes'
|
||||||
FILES+=( '.ci/docker-compose-file/docker-compose-sqlserver.yaml' )
|
FILES+=( '.ci/docker-compose-file/docker-compose-sqlserver.yaml' )
|
||||||
;;
|
;;
|
||||||
|
opents)
|
||||||
|
FILES+=( '.ci/docker-compose-file/docker-compose-opents.yaml' )
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "unknown_ct_dependency $dep"
|
echo "unknown_ct_dependency $dep"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
@ -274,3 +274,4 @@ clickhouse
|
||||||
FormatType
|
FormatType
|
||||||
RocketMQ
|
RocketMQ
|
||||||
Keyspace
|
Keyspace
|
||||||
|
OpenTSDB
|
||||||
|
|
Loading…
Reference in New Issue