Merge branch 'dev/v5.0' into resolve-master-dev/v5.0-conflict-release-version
This commit is contained in:
commit
5dab6985c1
|
@ -11,3 +11,4 @@ EMQX_AUTH__PGSQL__DATABASE=mqtt
|
|||
EMQX_AUTH__REDIS__SERVER=redis_server:6379
|
||||
EMQX_AUTH__REDIS__PASSWORD=public
|
||||
CUTTLEFISH_ENV_OVERRIDE_PREFIX=EMQX_
|
||||
HOCON_ENV_OVERRIDE_PREFIX=EMQX_
|
||||
|
|
|
@ -86,7 +86,7 @@ jobs:
|
|||
if: matrix.connect_type == 'tls'
|
||||
run: |
|
||||
cat <<-EOF >> "$GITHUB_ENV"
|
||||
EMQX_AUTH__MONGO__SSL=on
|
||||
EMQX_AUTH__MONGO__SSL__ENABLE=on
|
||||
EMQX_AUTH__MONGO__SSL__CACERTFILE=/emqx/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem
|
||||
EMQX_AUTH__MONGO__SSL__CERTFILE=/emqx/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem
|
||||
EMQX_AUTH__MONGO__SSL__KEYFILE=/emqx/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem
|
||||
|
@ -98,7 +98,7 @@ jobs:
|
|||
MONGO_TAG: ${{ matrix.mongo_tag }}
|
||||
if: matrix.connect_type == 'tcp'
|
||||
run: |
|
||||
echo EMQX_AUTH__MONGO__SSL=off >> "$GITHUB_ENV"
|
||||
echo EMQX_AUTH__MONGO__SSL__ENABLE=off >> "$GITHUB_ENV"
|
||||
- name: setup
|
||||
if: matrix.network_type == 'ipv4'
|
||||
run: |
|
||||
|
@ -160,10 +160,10 @@ jobs:
|
|||
if: matrix.connect_type == 'tls'
|
||||
run: |
|
||||
cat <<-EOF >> "$GITHUB_ENV"
|
||||
EMQX_AUTH__MYSQL__SSL__ENABLE=on
|
||||
EMQX_AUTH__MYSQL__USERNAME=ssluser
|
||||
EMQX_AUTH__MYSQL__PASSWORD=public
|
||||
EMQX_AUTH__MYSQL__DATABASE=mqtt
|
||||
EMQX_AUTH__MYSQL__SSL=on
|
||||
EMQX_AUTH__MYSQL__SSL__CACERTFILE=/emqx/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem
|
||||
EMQX_AUTH__MYSQL__SSL__CERTFILE=/emqx/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem
|
||||
EMQX_AUTH__MYSQL__SSL__KEYFILE=/emqx/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem
|
||||
|
@ -179,7 +179,7 @@ jobs:
|
|||
EMQX_AUTH__MYSQL__USERNAME=root
|
||||
EMQX_AUTH__MYSQL__PASSWORD=public
|
||||
EMQX_AUTH__MYSQL__DATABASE=mqtt
|
||||
EMQX_AUTH__MYSQL__SSL=off
|
||||
EMQX_AUTH__MYSQL__SSL__ENABLE=off
|
||||
EOF
|
||||
- name: setup
|
||||
if: matrix.network_type == 'ipv4'
|
||||
|
@ -239,7 +239,7 @@ jobs:
|
|||
if: matrix.connect_type == 'tls'
|
||||
run: |
|
||||
cat <<-EOF >> "$GITHUB_ENV"
|
||||
EMQX_AUTH__PGSQL__SSL=on
|
||||
EMQX_AUTH__PGSQL__SSL__ENABLE=on
|
||||
EMQX_AUTH__PGSQL__SSL__CACERTFILE=/emqx/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca.pem
|
||||
EMQX_AUTH__PGSQL__SSL__CERTFILE=/emqx/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-cert.pem
|
||||
EMQX_AUTH__PGSQL__SSL__KEYFILE=/emqx/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-key.pem
|
||||
|
@ -251,7 +251,7 @@ jobs:
|
|||
PGSQL_TAG: ${{ matrix.pgsql_tag }}
|
||||
if: matrix.connect_type == 'tcp'
|
||||
run: |
|
||||
echo EMQX_AUTH__PGSQL__SSL=off >> "$GITHUB_ENV"
|
||||
echo EMQX_AUTH__PGSQL__SSL__ENABLE=off >> "$GITHUB_ENV"
|
||||
- name: setup
|
||||
if: matrix.network_type == 'ipv4'
|
||||
run: |
|
||||
|
@ -318,7 +318,7 @@ jobs:
|
|||
if: matrix.connect_type == 'tls'
|
||||
run: |
|
||||
cat <<-EOF >> "$GITHUB_ENV"
|
||||
EMQX_AUTH__REDIS__SSL=on
|
||||
EMQX_AUTH__REDIS__SSL__ENABLE=on
|
||||
EMQX_AUTH__REDIS__SSL__CACERTFILE=/emqx/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt
|
||||
EMQX_AUTH__REDIS__SSL__CERTFILE=/emqx/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt
|
||||
EMQX_AUTH__REDIS__SSL__KEYFILE=/emqx/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key
|
||||
|
@ -330,7 +330,7 @@ jobs:
|
|||
REDIS_TAG: ${{ matrix.redis_tag }}
|
||||
if: matrix.connect_type == 'tcp'
|
||||
run: |
|
||||
echo EMQX_AUTH__REDIS__SSL=off >> "$GITHUB_ENV"
|
||||
echo EMQX_AUTH__REDIS__SSL__ENABLE=off >> "$GITHUB_ENV"
|
||||
- name: get server address
|
||||
run: |
|
||||
ipv4_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis)
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
## Value: URL
|
||||
##
|
||||
## Examples: http://127.0.0.1:80/mqtt/auth, https://[::1]:80/mqtt/auth
|
||||
auth.http.auth_req.url = http://127.0.0.1:80/mqtt/auth
|
||||
auth.http.auth_req.url = "http://127.0.0.1:80/mqtt/auth"
|
||||
|
||||
## HTTP Request Method for Auth Request
|
||||
##
|
||||
|
@ -18,7 +18,8 @@ auth.http.auth_req.method = post
|
|||
## The possible values of the Content-Type header: application/x-www-form-urlencoded, application/json
|
||||
##
|
||||
## Examples: auth.http.auth_req.headers.accept = */*
|
||||
auth.http.auth_req.headers.content_type = application/x-www-form-urlencoded
|
||||
|
||||
auth.http.auth_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
|
||||
|
@ -35,14 +36,14 @@ auth.http.auth_req.headers.content_type = application/x-www-form-urlencoded
|
|||
## - %d: subject of client TLS cert
|
||||
##
|
||||
## Value: <K1>=<V1>,<K2>=<V2>,...
|
||||
auth.http.auth_req.params = clientid=%c,username=%u,password=%P
|
||||
auth.http.auth_req.params = "clientid=%c,username=%u,password=%P"
|
||||
|
||||
## HTTP URL API path for SuperUser Request
|
||||
##
|
||||
## 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
|
||||
##
|
||||
|
@ -53,7 +54,7 @@ auth.http.super_req.method = post
|
|||
## 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 +71,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
|
||||
|
@ -78,7 +79,7 @@ auth.http.super_req.params = clientid=%c,username=%u
|
|||
## Value: URL
|
||||
##
|
||||
## Examples: http://127.0.0.1:80/mqtt/acl, https://[::1]:80/mqtt/acl
|
||||
auth.http.acl_req.url = http://127.0.0.1:80/mqtt/acl
|
||||
auth.http.acl_req.url = "http://127.0.0.1:80/mqtt/acl"
|
||||
|
||||
## HTTP Request Method for ACL Request
|
||||
##
|
||||
|
@ -89,7 +90,7 @@ auth.http.acl_req.method = post
|
|||
## The possible values of the Content-Type header: application/x-www-form-urlencoded, application/json
|
||||
##
|
||||
## Examples: auth.http.acl_req.headers.accept = */*
|
||||
auth.http.acl_req.headers.content-type = application/x-www-form-urlencoded
|
||||
auth.http.acl_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
|
||||
|
@ -108,7 +109,7 @@ auth.http.acl_req.headers.content-type = application/x-www-form-urlencoded
|
|||
## - %t: topic
|
||||
##
|
||||
## Value: <K1>=<V1>,<K2>=<V2>,...
|
||||
auth.http.acl_req.params = access=%A,username=%u,clientid=%c,ipaddr=%a,topic=%t,mountpoint=%m
|
||||
auth.http.acl_req.params = "access=%A,username=%u,clientid=%c,ipaddr=%a,topic=%t,mountpoint=%m"
|
||||
|
||||
## Time-out time for the request.
|
||||
##
|
||||
|
@ -143,17 +144,17 @@ auth.http.pool_size = 32
|
|||
## are used during server authentication and when building the client certificate chain.
|
||||
##
|
||||
## Value: File
|
||||
## auth.http.ssl.cacertfile = {{ platform_etc_dir }}/certs/ca.pem
|
||||
## auth.http.ssl.cacertfile = "{{ platform_etc_dir }}/certs/ca.pem"
|
||||
|
||||
## The path to a file containing the client's certificate.
|
||||
##
|
||||
## Value: File
|
||||
## auth.http.ssl.certfile = {{ platform_etc_dir }}/certs/client-cert.pem
|
||||
## auth.http.ssl.certfile = "{{ platform_etc_dir }}/certs/client-cert.pem"
|
||||
|
||||
## Path to a file containing the client's private PEM-encoded key.
|
||||
##
|
||||
## Value: File
|
||||
## auth.http.ssl.keyfile = {{ platform_etc_dir }}/certs/client-key.pem
|
||||
## auth.http.ssl.keyfile = "{{ platform_etc_dir }}/certs/client-key.pem"
|
||||
|
||||
## In mode verify_none the default behavior is to allow all x509-path
|
||||
## validation errors.
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
{profiles,
|
||||
[{test,
|
||||
[{deps,
|
||||
[{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}},
|
||||
[
|
||||
{emqtt, {git, "https://github.com/emqx/emqtt", {tag, "v1.2.2"}}}
|
||||
]}
|
||||
]}
|
||||
|
|
|
@ -10,13 +10,13 @@ auth.jwt.secret = emqxsecret
|
|||
## RSA or ECDSA public key file.
|
||||
##
|
||||
## Value: File
|
||||
#auth.jwt.pubkey = etc/certs/jwt_public_key.pem
|
||||
#auth.jwt.pubkey = "etc/certs/jwt_public_key.pem"
|
||||
|
||||
## The JWKs server address
|
||||
##
|
||||
## see: http://self-issued.info/docs/draft-ietf-jose-json-web-key.html
|
||||
##
|
||||
#auth.jwt.jwks = https://127.0.0.1:8080/jwks
|
||||
#auth.jwt.jwks.endpoint = "https://127.0.0.1:8080/jwks"
|
||||
|
||||
## The JWKs refresh interval
|
||||
##
|
||||
|
@ -32,7 +32,7 @@ auth.jwt.from = password
|
|||
## Enable to verify claims fields
|
||||
##
|
||||
## Value: on | off
|
||||
auth.jwt.verify_claims = off
|
||||
auth.jwt.verify_claims.enable = off
|
||||
|
||||
## The checklist of claims to validate
|
||||
##
|
||||
|
@ -46,4 +46,4 @@ auth.jwt.verify_claims = off
|
|||
##
|
||||
## For example, to verify that the username in the JWT payload is the same
|
||||
## as the client (MQTT protocol) username
|
||||
#auth.jwt.verify_claims.username = %u
|
||||
#auth.jwt.verify_claims.username = "%u"
|
|
@ -4,7 +4,7 @@
|
|||
{datatype, string}
|
||||
]}.
|
||||
|
||||
{mapping, "auth.jwt.jwks", "emqx_auth_jwt.jwks", [
|
||||
{mapping, "auth.jwt.jwks.endpoint", "emqx_auth_jwt.jwks", [
|
||||
{datatype, string}
|
||||
]}.
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
|||
{datatype, {enum, [raw, der]}}
|
||||
]}.
|
||||
|
||||
{mapping, "auth.jwt.verify_claims", "emqx_auth_jwt.verify_claims", [
|
||||
{mapping, "auth.jwt.verify_claims.enable", "emqx_auth_jwt.verify_claims", [
|
||||
{default, off},
|
||||
{datatype, flag}
|
||||
]}.
|
||||
|
@ -36,7 +36,7 @@
|
|||
]}.
|
||||
|
||||
{translation, "emqx_auth_jwt.verify_claims", fun(Conf) ->
|
||||
case cuttlefish:conf_get("auth.jwt.verify_claims", Conf) of
|
||||
case cuttlefish:conf_get("auth.jwt.verify_claims.enable", Conf) of
|
||||
false -> cuttlefish:unset();
|
||||
true ->
|
||||
lists:foldr(
|
||||
|
|
|
@ -20,6 +20,6 @@
|
|||
|
||||
{profiles,
|
||||
[{test,
|
||||
[{deps, [{emqx_ct_helpers, {git, "http://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}}]}
|
||||
[{deps, []}
|
||||
]}
|
||||
]}.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
## LDAP server list, seperated by ','.
|
||||
##
|
||||
## Value: String
|
||||
auth.ldap.servers = 127.0.0.1
|
||||
auth.ldap.servers = "127.0.0.1"
|
||||
|
||||
## LDAP server port.
|
||||
##
|
||||
|
@ -20,7 +20,7 @@ auth.ldap.pool = 8
|
|||
## LDAP Bind DN.
|
||||
##
|
||||
## Value: DN
|
||||
auth.ldap.bind_dn = cn=root,dc=emqx,dc=io
|
||||
auth.ldap.bind_dn = "cn=root,dc=emqx,dc=io"
|
||||
|
||||
## LDAP Bind Password.
|
||||
##
|
||||
|
@ -37,7 +37,7 @@ auth.ldap.timeout = 30s
|
|||
## Variables:
|
||||
##
|
||||
## Value: DN
|
||||
auth.ldap.device_dn = ou=device,dc=emqx,dc=io
|
||||
auth.ldap.device_dn = "ou=device,dc=emqx,dc=io"
|
||||
|
||||
## Specified ObjectClass
|
||||
##
|
||||
|
@ -63,14 +63,14 @@ auth.ldap.password.attributetype = userPassword
|
|||
## Whether to enable SSL.
|
||||
##
|
||||
## Value: true | false
|
||||
auth.ldap.ssl = false
|
||||
auth.ldap.ssl.enable = false
|
||||
|
||||
#auth.ldap.ssl.certfile = etc/certs/cert.pem
|
||||
#auth.ldap.ssl.certfile = "etc/certs/cert.pem"
|
||||
|
||||
#auth.ldap.ssl.keyfile = etc/certs/key.pem
|
||||
#auth.ldap.ssl.keyfile = "etc/certs/key.pem"
|
||||
|
||||
#auth.ldap.ssl.cacertfile = etc/certs/cacert.pem
|
||||
#auth.ldap.ssl.cacertfile = "etc/certs/cacert.pem"
|
||||
|
||||
#auth.ldap.ssl.verify = verify_peer
|
||||
#auth.ldap.ssl.verify = "verify_peer"
|
||||
|
||||
#auth.ldap.ssl.server_name_indication = your_server_name
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
{datatype, {duration, ms}}
|
||||
]}.
|
||||
|
||||
{mapping, "auth.ldap.ssl", "emqx_auth_ldap.ldap", [
|
||||
{mapping, "auth.ldap.ssl.enable", "emqx_auth_ldap.ldap", [
|
||||
{default, false},
|
||||
{datatype, {enum, [true, false]}}
|
||||
]}.
|
||||
|
@ -83,7 +83,7 @@
|
|||
{bind_password, BindPassword},
|
||||
{pool, Pool},
|
||||
{auto_reconnect, 2}],
|
||||
case cuttlefish:conf_get("auth.ldap.ssl", Conf) of
|
||||
case cuttlefish:conf_get("auth.ldap.ssl.enable", Conf) of
|
||||
true -> [{ssl, true}, {sslopts, Filter(SslOpts())}|Opts];
|
||||
false -> [{ssl, false}|Opts]
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
{profiles,
|
||||
[{test,
|
||||
[{deps, [{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}}]}
|
||||
[{deps, []}
|
||||
]}
|
||||
]}.
|
||||
|
||||
|
|
|
@ -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.4.0"}, % strict semver, bump manually!
|
||||
{modules, []},
|
||||
{registered, [emqx_auth_ldap_sup]},
|
||||
{applications, [kernel,stdlib,eldap2,ecpool]},
|
||||
|
|
|
@ -10,12 +10,12 @@ auth.mnesia.password_hash = sha256
|
|||
## Examples
|
||||
##auth.client.1.clientid = id
|
||||
##auth.client.1.password = passwd
|
||||
##auth.client.2.clientid = dev:devid
|
||||
##auth.client.2.clientid = "dev:devid"
|
||||
##auth.client.2.password = passwd2
|
||||
##auth.client.3.clientid = app:appid
|
||||
##auth.client.3.clientid = "app:appid"
|
||||
##auth.client.3.password = passwd3
|
||||
##auth.client.4.clientid = client~!@#$%^&*()_+
|
||||
##auth.client.4.password = passwd~!@#$%^&*()_+
|
||||
##auth.client.4.clientid = "client~!@#$%^&*()_+"
|
||||
##auth.client.4.password = "passwd~!@#$%^&*()_+"
|
||||
|
||||
##--------------------------------------------------------------------
|
||||
## Username Authentication
|
||||
|
@ -26,5 +26,5 @@ auth.mnesia.password_hash = sha256
|
|||
##auth.user.1.password = public
|
||||
##auth.user.2.username = feng@emqtt.io
|
||||
##auth.user.2.password = public
|
||||
##auth.user.3.username = name~!@#$%^&*()_+
|
||||
##auth.user.3.password = pwsswd~!@#$%^&*()_+
|
||||
##auth.user.3.username = "name~!@#$%^&*()_+"
|
||||
##auth.user.3.password = "pwsswd~!@#$%^&*()_+"
|
||||
|
|
|
@ -16,8 +16,8 @@ auth.mongo.type = single
|
|||
##
|
||||
## Value: String
|
||||
##
|
||||
## Examples: 127.0.0.1:27017,127.0.0.2:27017...
|
||||
auth.mongo.server = 127.0.0.1:27017
|
||||
## Examples: "127.0.0.1:27017,127.0.0.2:27017,..."
|
||||
auth.mongo.server = "127.0.0.1:27017"
|
||||
|
||||
## MongoDB pool size
|
||||
##
|
||||
|
@ -53,7 +53,7 @@ auth.mongo.database = mqtt
|
|||
## Whether to enable SSL connection.
|
||||
##
|
||||
## Value: on | off
|
||||
## auth.mongo.ssl = off
|
||||
## auth.mongo.ssl.enable = off
|
||||
|
||||
## SSL keyfile.
|
||||
##
|
||||
|
@ -117,17 +117,17 @@ auth.mongo.topology.max_overflow = 0
|
|||
auth.mongo.auth_query.password_hash = sha256
|
||||
|
||||
## sha256 with salt suffix
|
||||
## auth.mongo.auth_query.password_hash = sha256,salt
|
||||
## auth.mongo.auth_query.password_hash = "sha256,salt"
|
||||
|
||||
## sha256 with salt prefix
|
||||
## auth.mongo.auth_query.password_hash = salt,sha256
|
||||
## auth.mongo.auth_query.password_hash = "salt,sha256"
|
||||
|
||||
## bcrypt with salt prefix
|
||||
## auth.mongo.auth_query.password_hash = salt,bcrypt
|
||||
## auth.mongo.auth_query.password_hash = "salt,bcrypt"
|
||||
|
||||
## pbkdf2 with macfun iterations dklen
|
||||
## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512
|
||||
## auth.mongo.auth_query.password_hash = pbkdf2,sha256,1000,20
|
||||
## auth.mongo.auth_query.password_hash = "pbkdf2,sha256,1000,20"
|
||||
|
||||
## Authentication query.
|
||||
auth.mongo.auth_query.collection = mqtt_user
|
||||
|
@ -146,15 +146,15 @@ auth.mongo.auth_query.password_field = password
|
|||
## - %d: subject of client TLS cert
|
||||
##
|
||||
## auth.mongo.auth_query.selector = {Field}={Placeholder}
|
||||
auth.mongo.auth_query.selector = username=%u
|
||||
auth.mongo.auth_query.selector = "username=%u"
|
||||
|
||||
## -------------------------------------------------
|
||||
## Super User Query
|
||||
## -------------------------------------------------
|
||||
auth.mongo.super_query.collection = mqtt_user
|
||||
auth.mongo.super_query.super_field = is_superuser
|
||||
#auth.mongo.super_query.selector = username=%u, clientid=%c
|
||||
auth.mongo.super_query.selector = username=%u
|
||||
#auth.mongo.super_query.selector.1 = username=%u, clientid=%c
|
||||
auth.mongo.super_query.selector = "username=%u"
|
||||
|
||||
## ACL Selector.
|
||||
##
|
||||
|
@ -165,8 +165,8 @@ auth.mongo.super_query.selector = username=%u
|
|||
##
|
||||
## With following 2 selectors configured:
|
||||
##
|
||||
## auth.mongo.acl_query.selector.1 = username=%u
|
||||
## auth.mongo.acl_query.selector.2 = username=$all
|
||||
## auth.mongo.acl_query.selector.1 = "username=%u"
|
||||
## auth.mongo.acl_query.selector.2 = "username=$all"
|
||||
##
|
||||
## And if a client connected using username 'ilyas',
|
||||
## then the following mongo command will be used to
|
||||
|
@ -180,8 +180,8 @@ auth.mongo.super_query.selector = username=%u
|
|||
##
|
||||
## Examples:
|
||||
##
|
||||
## auth.mongo.acl_query.selector.1 = username=%u,clientid=%c
|
||||
## auth.mongo.acl_query.selector.2 = username=$all
|
||||
## auth.mongo.acl_query.selector.3 = clientid=$all
|
||||
## auth.mongo.acl_query.selector.1 = "username=%u,clientid=%c"
|
||||
## auth.mongo.acl_query.selector.2 = "username=$all"
|
||||
## auth.mongo.acl_query.selector.3 = "clientid=$all"
|
||||
auth.mongo.acl_query.collection = mqtt_acl
|
||||
auth.mongo.acl_query.selector = username=%u
|
||||
auth.mongo.acl_query.selector = "username=%u"
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
{datatype, string}
|
||||
]}.
|
||||
|
||||
{mapping, "auth.mongo.ssl", "emqx_auth_mongo.server", [
|
||||
{mapping, "auth.mongo.ssl.enable", "emqx_auth_mongo.server", [
|
||||
{default, off},
|
||||
{datatype, {enum, [on, off, true, false]}} %% FIXME: ture/false is compatible with 4.0-4.2 version format, plan to delete in 5.0
|
||||
]}.
|
||||
|
@ -130,8 +130,6 @@
|
|||
true -> [];
|
||||
false -> [{r_mode, R}]
|
||||
end,
|
||||
|
||||
|
||||
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
|
||||
SslOpts = fun(Prefix) ->
|
||||
Verify = case cuttlefish:conf_get(Prefix ++ ".verify", Conf, false) of
|
||||
|
@ -149,8 +147,14 @@
|
|||
end,
|
||||
|
||||
%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0
|
||||
Ssl = case cuttlefish:conf_get("auth.mongo.ssl", Conf) of
|
||||
on -> [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl")}];
|
||||
GenSsl = case cuttlefish:conf_get("auth.mongo.ssl.cacertfile", Conf, undefined) of
|
||||
undefined -> [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl_opts")}];
|
||||
_ -> [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl")}]
|
||||
end,
|
||||
|
||||
%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0
|
||||
Ssl = case cuttlefish:conf_get("auth.mongo.ssl.enable", Conf) of
|
||||
on -> GenSsl;
|
||||
off -> [];
|
||||
true -> [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl_opts")}];
|
||||
false -> []
|
||||
|
|
|
@ -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]},
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
## Value: Port | IP:Port
|
||||
##
|
||||
## Examples: 3306, 127.0.0.1:3306, localhost:3306
|
||||
auth.mysql.server = 127.0.0.1:3306
|
||||
auth.mysql.server = "127.0.0.1:3306"
|
||||
|
||||
## MySQL pool size.
|
||||
##
|
||||
|
@ -50,7 +50,7 @@ auth.mysql.database = mqtt
|
|||
## - %C: common name of client TLS cert
|
||||
## - %d: subject of client TLS cert
|
||||
##
|
||||
auth.mysql.auth_query = select password from mqtt_user where username = '%u' limit 1
|
||||
auth.mysql.auth_query = "select password from mqtt_user where username = '%u' limit 1"
|
||||
## auth.mysql.auth_query = select password_hash as password from mqtt_user where username = '%u' limit 1
|
||||
|
||||
## Password hash.
|
||||
|
@ -59,17 +59,17 @@ auth.mysql.auth_query = select password from mqtt_user where username = '%u' lim
|
|||
auth.mysql.password_hash = sha256
|
||||
|
||||
## sha256 with salt prefix
|
||||
## auth.mysql.password_hash = salt,sha256
|
||||
## auth.mysql.password_hash = "salt,sha256"
|
||||
|
||||
## bcrypt with salt only prefix
|
||||
## auth.mysql.password_hash = salt,bcrypt
|
||||
## auth.mysql.password_hash = "salt,bcrypt"
|
||||
|
||||
## sha256 with salt suffix
|
||||
## auth.mysql.password_hash = sha256,salt
|
||||
## auth.mysql.password_hash = "sha256,salt"
|
||||
|
||||
## pbkdf2 with macfun iterations dklen
|
||||
## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512
|
||||
## auth.mysql.password_hash = pbkdf2,sha256,1000,20
|
||||
## auth.mysql.password_hash = "pbkdf2,sha256,1000,20"
|
||||
|
||||
## Superuser query.
|
||||
##
|
||||
|
@ -81,7 +81,7 @@ auth.mysql.password_hash = sha256
|
|||
## - %C: common name of client TLS cert
|
||||
## - %d: subject of client TLS cert
|
||||
##
|
||||
auth.mysql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1
|
||||
auth.mysql.super_query = "select is_superuser from mqtt_user where username = '%u' limit 1"
|
||||
|
||||
## ACL query.
|
||||
##
|
||||
|
@ -93,12 +93,12 @@ auth.mysql.super_query = select is_superuser from mqtt_user where username = '%u
|
|||
## - %c: clientid
|
||||
##
|
||||
## Note: You can add the 'ORDER BY' statement to control the rules match order
|
||||
auth.mysql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'
|
||||
auth.mysql.acl_query = "select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'"
|
||||
|
||||
## Mysql ssl configuration.
|
||||
##
|
||||
## Value: on | off
|
||||
#auth.mysql.ssl = off
|
||||
## auth.mysql.ssl.enable = off
|
||||
|
||||
## CA certificate.
|
||||
##
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
{datatype, string}
|
||||
]}.
|
||||
|
||||
{mapping, "auth.mysql.ssl", "emqx_auth_mysql.server", [
|
||||
{mapping, "auth.mysql.ssl.enable", "emqx_auth_mysql.server", [
|
||||
{default, off},
|
||||
{datatype, flag}
|
||||
]}.
|
||||
|
@ -94,7 +94,7 @@
|
|||
{keep_alive, true}],
|
||||
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
|
||||
Options1 =
|
||||
case cuttlefish:conf_get("auth.mysql.ssl", Conf) of
|
||||
case cuttlefish:conf_get("auth.mysql.ssl.enable", Conf) of
|
||||
true ->
|
||||
%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0
|
||||
CA = cuttlefish:conf_get(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_auth_mysql,
|
||||
[{description, "EMQ X Authentication/ACL with MySQL"},
|
||||
{vsn, "4.3.0"}, % strict semver, bump manually!
|
||||
{vsn, "4.4.0"}, % strict semver, bump manually!
|
||||
{modules, []},
|
||||
{registered, [emqx_auth_mysql_sup]},
|
||||
{applications, [kernel,stdlib,mysql,ecpool]},
|
||||
|
|
|
@ -49,7 +49,7 @@ auth.pgsql.encoding = utf8
|
|||
## Whether to enable SSL connection.
|
||||
##
|
||||
## Value: true | false
|
||||
auth.pgsql.ssl = false
|
||||
auth.pgsql.ssl.enable = false
|
||||
|
||||
## SSL keyfile.
|
||||
##
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
##
|
||||
## Value: Port | IP:Port
|
||||
##
|
||||
## Examples: 5432, 127.0.0.1:5432, localhost:5432
|
||||
auth.pgsql.server = 127.0.0.1:5432
|
||||
## Examples: 5432, "127.0.0.1:5432", "localhost:5432"
|
||||
auth.pgsql.server = "127.0.0.1:5432"
|
||||
|
||||
## PostgreSQL pool size.
|
||||
##
|
||||
|
@ -37,7 +37,7 @@ auth.pgsql.encoding = utf8
|
|||
## Whether to enable SSL connection.
|
||||
##
|
||||
## Value: on | off
|
||||
auth.pgsql.ssl = off
|
||||
auth.pgsql.ssl.enable = off
|
||||
|
||||
## TLS version.
|
||||
##
|
||||
|
@ -87,7 +87,7 @@ auth.pgsql.ssl = off
|
|||
## - %C: common name of client TLS cert
|
||||
## - %d: subject of client TLS cert
|
||||
##
|
||||
auth.pgsql.auth_query = select password from mqtt_user where username = '%u' limit 1
|
||||
auth.pgsql.auth_query = "select password from mqtt_user where username = '%u' limit 1"
|
||||
|
||||
## Password hash.
|
||||
##
|
||||
|
@ -95,17 +95,17 @@ auth.pgsql.auth_query = select password from mqtt_user where username = '%u' lim
|
|||
auth.pgsql.password_hash = sha256
|
||||
|
||||
## sha256 with salt prefix
|
||||
## auth.pgsql.password_hash = salt,sha256
|
||||
## auth.pgsql.password_hash = "salt,sha256"
|
||||
|
||||
## sha256 with salt suffix
|
||||
## auth.pgsql.password_hash = sha256,salt
|
||||
## auth.pgsql.password_hash = "sha256,salt"
|
||||
|
||||
## bcrypt with salt prefix
|
||||
## auth.pgsql.password_hash = salt,bcrypt
|
||||
## auth.pgsql.password_hash = "salt,bcrypt"
|
||||
|
||||
## pbkdf2 with macfun iterations dklen
|
||||
## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512
|
||||
## auth.pgsql.password_hash = pbkdf2,sha256,1000,20
|
||||
## auth.pgsql.password_hash = "pbkdf2,sha256,1000,20"
|
||||
|
||||
## Superuser query.
|
||||
##
|
||||
|
@ -117,7 +117,7 @@ auth.pgsql.password_hash = sha256
|
|||
## - %C: common name of client TLS cert
|
||||
## - %d: subject of client TLS cert
|
||||
##
|
||||
auth.pgsql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1
|
||||
auth.pgsql.super_query = "select is_superuser from mqtt_user where username = '%u' limit 1"
|
||||
|
||||
## ACL query. Comment this query, the ACL will be disabled.
|
||||
##
|
||||
|
@ -129,4 +129,4 @@ auth.pgsql.super_query = select is_superuser from mqtt_user where username = '%u
|
|||
## - %c: clientid
|
||||
##
|
||||
## Note: You can add the 'ORDER BY' statement to control the rules match order
|
||||
auth.pgsql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'
|
||||
auth.pgsql.acl_query = "select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'"
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
{datatype, atom}
|
||||
]}.
|
||||
|
||||
{mapping, "auth.pgsql.ssl", "emqx_auth_pgsql.server", [
|
||||
{mapping, "auth.pgsql.ssl.enable", "emqx_auth_pgsql.server", [
|
||||
{default, off},
|
||||
{datatype, {enum, [on, off, true, false]}} %% FIXME: true/fasle is compatible with 4.0-4.2 version format, plan to delete in 5.0
|
||||
]}.
|
||||
|
@ -116,8 +116,14 @@
|
|||
end,
|
||||
|
||||
%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0
|
||||
Ssl = case cuttlefish:conf_get("auth.pgsql.ssl", Conf) of
|
||||
on -> [{ssl, true}, {ssl_opts, SslOpts("auth.pgsql.ssl")}];
|
||||
GenSsl = case cuttlefish:conf_get("auth.pgsql.ssl.cacertfile", Conf, undefined) of
|
||||
undefined -> [{ssl, true}, {ssl_opts, SslOpts("auth.pgsql.ssl_opts")}];
|
||||
_ -> [{ssl, true}, {ssl_opts, SslOpts("auth.pgsql.ssl")}]
|
||||
end,
|
||||
|
||||
%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0
|
||||
Ssl = case cuttlefish:conf_get("auth.pgsql.ssl.enable", Conf) of
|
||||
on -> GenSsl;
|
||||
off -> [];
|
||||
true -> [{ssl, true}, {ssl_opts, SslOpts("auth.pgsql.ssl_opts")}];
|
||||
false -> []
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_auth_pgsql,
|
||||
[{description, "EMQ X Authentication/ACL with PostgreSQL"},
|
||||
{vsn, "4.3.0"}, % strict semver, bump manually!
|
||||
{vsn, "4.4.0"}, % strict semver, bump manually!
|
||||
{modules, []},
|
||||
{registered, [emqx_auth_pgsql_sup]},
|
||||
{applications, [kernel,stdlib,epgsql,ecpool]},
|
||||
|
|
|
@ -12,9 +12,9 @@ auth.redis.type = single
|
|||
## Value: Port | IP:Port
|
||||
##
|
||||
## Single Redis Server: 127.0.0.1:6379, localhost:6379
|
||||
## Redis Sentinel: 127.0.0.1:26379,127.0.0.2:26379,127.0.0.3:26379
|
||||
## Redis Cluster: 127.0.0.1:6379,127.0.0.2:6379,127.0.0.3:6379
|
||||
auth.redis.server = 127.0.0.1:6379
|
||||
## Redis Sentinel: "127.0.0.1:26379,127.0.0.2:26379,127.0.0.3:26379"
|
||||
## Redis Cluster: "127.0.0.1:6379,127.0.0.2:6379,127.0.0.3:6379"
|
||||
auth.redis.server = "127.0.0.1:6379"
|
||||
|
||||
## Redis sentinel cluster name.
|
||||
##
|
||||
|
@ -52,10 +52,10 @@ auth.redis.database = 0
|
|||
## - %d: subject of client TLS cert
|
||||
##
|
||||
## Examples:
|
||||
## - HGET mqtt_user:%u password
|
||||
## - HMGET mqtt_user:%u password
|
||||
## - HMGET mqtt_user:%u password salt
|
||||
auth.redis.auth_cmd = HMGET mqtt_user:%u password
|
||||
## - "HGET mqtt_user:%u password"
|
||||
## - "HMGET mqtt_user:%u password"
|
||||
## - "HMGET mqtt_user:%u password salt"
|
||||
auth.redis.auth_cmd = "HMGET mqtt_user:%u password"
|
||||
|
||||
## Password hash.
|
||||
##
|
||||
|
@ -63,17 +63,17 @@ auth.redis.auth_cmd = HMGET mqtt_user:%u password
|
|||
auth.redis.password_hash = plain
|
||||
|
||||
## sha256 with salt prefix
|
||||
## auth.redis.password_hash = salt,sha256
|
||||
## auth.redis.password_hash = "salt,sha256"
|
||||
|
||||
## sha256 with salt suffix
|
||||
## auth.redis.password_hash = sha256,salt
|
||||
## auth.redis.password_hash = "sha256,salt"
|
||||
|
||||
## bcrypt with salt prefix
|
||||
## auth.redis.password_hash = salt,bcrypt
|
||||
## auth.redis.password_hash = "salt,bcrypt"
|
||||
|
||||
## pbkdf2 with macfun iterations dklen
|
||||
## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512
|
||||
## auth.redis.password_hash = pbkdf2,sha256,1000,20
|
||||
## auth.redis.password_hash = "pbkdf2,sha256,1000,20"
|
||||
|
||||
## Superuser query command.
|
||||
##
|
||||
|
@ -84,7 +84,7 @@ auth.redis.password_hash = plain
|
|||
## - %c: clientid
|
||||
## - %C: common name of client TLS cert
|
||||
## - %d: subject of client TLS cert
|
||||
auth.redis.super_cmd = HGET mqtt_user:%u is_superuser
|
||||
auth.redis.super_cmd = "HGET mqtt_user:%u is_superuser"
|
||||
|
||||
## ACL query command.
|
||||
##
|
||||
|
@ -93,12 +93,12 @@ auth.redis.super_cmd = HGET mqtt_user:%u is_superuser
|
|||
## Variables:
|
||||
## - %u: username
|
||||
## - %c: clientid
|
||||
auth.redis.acl_cmd = HGETALL mqtt_acl:%u
|
||||
auth.redis.acl_cmd = "HGETALL mqtt_acl:%u"
|
||||
|
||||
## Redis ssl configuration.
|
||||
##
|
||||
## Value: on | off
|
||||
#auth.redis.ssl = off
|
||||
# auth.redis.ssl.enable = off
|
||||
|
||||
## CA certificate.
|
||||
##
|
||||
|
@ -108,12 +108,12 @@ auth.redis.acl_cmd = HGETALL mqtt_acl:%u
|
|||
## Client ssl certificate.
|
||||
##
|
||||
## Value: File
|
||||
#auth.redis.ssl.certfile = path/to/your/certfile
|
||||
# auth.redis.ssl.certfile = path/to/your/certfile
|
||||
|
||||
## Client ssl keyfile.
|
||||
##
|
||||
## Value: File
|
||||
#auth.redis.ssl.keyfile = path/to/your/keyfile
|
||||
# auth.redis.ssl.keyfile = path/to/your/keyfile
|
||||
|
||||
## In mode verify_none the default behavior is to allow all x509-path
|
||||
## validation errors.
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
hidden
|
||||
]}.
|
||||
|
||||
{mapping, "auth.redis.ssl", "emqx_auth_redis.options", [
|
||||
{mapping, "auth.redis.ssl.enable", "emqx_auth_redis.options", [
|
||||
{default, off},
|
||||
{datatype, flag}
|
||||
]}.
|
||||
|
@ -75,7 +75,7 @@
|
|||
]}.
|
||||
|
||||
{translation, "emqx_auth_redis.options", fun(Conf) ->
|
||||
Ssl = cuttlefish:conf_get("auth.redis.ssl", Conf, false),
|
||||
Ssl = cuttlefish:conf_get("auth.redis.ssl.enable", Conf, false),
|
||||
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
|
||||
case Ssl of
|
||||
true ->
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_auth_redis,
|
||||
[{description, "EMQ X Authentication/ACL with Redis"},
|
||||
{vsn, "4.3.0"}, % strict semver, bump manually!
|
||||
{vsn, "4.4.0"}, % strict semver, bump manually!
|
||||
{modules, []},
|
||||
{registered, [emqx_auth_redis_sup]},
|
||||
{applications, [kernel,stdlib,eredis,eredis_cluster,ecpool]},
|
||||
|
|
|
@ -53,13 +53,13 @@ The following is the basic configuration of RPC bridging. A simplest RPC bridgin
|
|||
|
||||
```
|
||||
## Bridge Address: Use node name (nodename@host) for rpc bridging, and host:port for mqtt connection
|
||||
bridge.mqtt.emqx2.address = emqx2@192.168.1.2
|
||||
bridge.mqtt.emqx2.address = "emqx2@192.168.1.2"
|
||||
|
||||
## Forwarding topics of the message
|
||||
bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/#
|
||||
bridge.mqtt.emqx2.forwards = "sensor1/#,sensor2/#"
|
||||
|
||||
## bridged mountpoint
|
||||
bridge.mqtt.emqx2.mountpoint = bridge/emqx2/${node}/
|
||||
bridge.mqtt.emqx2.mountpoint = "bridge/emqx2/${node}/"
|
||||
```
|
||||
|
||||
If the messages received by the local node emqx1 matches the topic `sersor1/#` or `sensor2/#`, these messages will be forwarded to the `sensor1/#` or `sensor2/#` topic of the remote node emqx2.
|
||||
|
@ -82,66 +82,66 @@ EMQ X MQTT bridging principle: Create an MQTT client on the EMQ X broker, and co
|
|||
|
||||
```
|
||||
## Bridge Address: Use node name for rpc bridging, use host:port for mqtt connection
|
||||
bridge.mqtt.emqx2.address = 192.168.1.2:1883
|
||||
bridge.mqtt.emqx2.address = "192.168.1.2:1883"
|
||||
|
||||
## Bridged Protocol Version
|
||||
## Enumeration value: mqttv3 | mqttv4 | mqttv5
|
||||
bridge.mqtt.emqx2.proto_ver = mqttv4
|
||||
bridge.mqtt.emqx2.proto_ver = "mqttv4"
|
||||
|
||||
## mqtt client's clientid
|
||||
bridge.mqtt.emqx2.clientid = bridge_emq
|
||||
bridge.mqtt.emqx2.clientid = "bridge_emq"
|
||||
|
||||
## mqtt client's clean_start field
|
||||
## Note: Some MQTT Brokers need to set the clean_start value as `true`
|
||||
bridge.mqtt.emqx2.clean_start = true
|
||||
|
||||
## mqtt client's username field
|
||||
bridge.mqtt.emqx2.username = user
|
||||
bridge.mqtt.emqx2.username = "user"
|
||||
|
||||
## mqtt client's password field
|
||||
bridge.mqtt.emqx2.password = passwd
|
||||
bridge.mqtt.emqx2.password = "passwd"
|
||||
|
||||
## Whether the mqtt client uses ssl to connect to a remote serve or not
|
||||
bridge.mqtt.emqx2.ssl = off
|
||||
|
||||
## CA Certificate of Client SSL Connection (PEM format)
|
||||
bridge.mqtt.emqx2.cacertfile = etc/certs/cacert.pem
|
||||
bridge.mqtt.emqx2.cacertfile = "etc/certs/cacert.pem"
|
||||
|
||||
## SSL certificate of Client SSL connection
|
||||
bridge.mqtt.emqx2.certfile = etc/certs/client-cert.pem
|
||||
bridge.mqtt.emqx2.certfile = "etc/certs/client-cert.pem"
|
||||
|
||||
## Key file of Client SSL connection
|
||||
bridge.mqtt.emqx2.keyfile = etc/certs/client-key.pem
|
||||
bridge.mqtt.emqx2.keyfile = "etc/certs/client-key.pem"
|
||||
|
||||
## SSL encryption
|
||||
bridge.mqtt.emqx2.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384
|
||||
bridge.mqtt.emqx2.ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384"
|
||||
|
||||
## TTLS PSK password
|
||||
## Note 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot be configured at the same time
|
||||
##
|
||||
## See 'https://tools.ietf.org/html/rfc4279#section-2'.
|
||||
## bridge.mqtt.emqx2.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA
|
||||
## bridge.mqtt.emqx2.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
|
||||
|
||||
## Client's heartbeat interval
|
||||
bridge.mqtt.emqx2.keepalive = 60s
|
||||
|
||||
## Supported TLS version
|
||||
bridge.mqtt.emqx2.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
|
||||
bridge.mqtt.emqx2.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
|
||||
|
||||
## Forwarding topics of the message
|
||||
bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/#
|
||||
bridge.mqtt.emqx2.forwards = "sensor1/#,sensor2/#"
|
||||
|
||||
## Bridged mountpoint
|
||||
bridge.mqtt.emqx2.mountpoint = bridge/emqx2/${node}/
|
||||
bridge.mqtt.emqx2.mountpoint = "bridge/emqx2/${node}/"
|
||||
|
||||
## Subscription topic for bridging
|
||||
bridge.mqtt.emqx2.subscription.1.topic = cmd/topic1
|
||||
bridge.mqtt.emqx2.subscription.1.topic = "cmd/topic1"
|
||||
|
||||
## Subscription qos for bridging
|
||||
bridge.mqtt.emqx2.subscription.1.qos = 1
|
||||
|
||||
## Subscription topic for bridging
|
||||
bridge.mqtt.emqx2.subscription.2.topic = cmd/topic2
|
||||
bridge.mqtt.emqx2.subscription.2.topic = "cmd/topic2"
|
||||
|
||||
## Subscription qos for bridging
|
||||
bridge.mqtt.emqx2.subscription.2.qos = 1
|
||||
|
|
|
@ -39,7 +39,7 @@ In EMQ X, bridge is configured by modifying ``etc/emqx.conf``. EMQ X distinguish
|
|||
.. code-block::
|
||||
|
||||
## Bridge address: node name for local bridge, host:port for remote.
|
||||
bridge.mqtt.aws.address = 127.0.0.1:1883
|
||||
bridge.mqtt.aws.address = "127.0.0.1:1883"
|
||||
|
||||
This configuration declares a bridge named ``aws`` and specifies that it is bridged to the MQTT broker of 127.0.0.1:1883 by MQTT mode.
|
||||
|
||||
|
@ -69,13 +69,13 @@ The following is the basic configuration of RPC bridging. A simplest RPC bridgin
|
|||
.. code-block::
|
||||
|
||||
## Bridge Address: Use node name (nodename@host) for rpc bridging, and host:port for mqtt connection
|
||||
bridge.mqtt.emqx2.address = emqx2@192.168.1.2
|
||||
bridge.mqtt.emqx2.address = "emqx2@192.168.1.2"
|
||||
|
||||
## Forwarding topics of the message
|
||||
bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/#
|
||||
bridge.mqtt.emqx2.forwards = "sensor1/#,sensor2/#"
|
||||
|
||||
## bridged mountpoint
|
||||
bridge.mqtt.emqx2.mountpoint = bridge/emqx2/${node}/
|
||||
bridge.mqtt.emqx2.mountpoint = "bridge/emqx2/${node}/"
|
||||
|
||||
If the messages received by the local node emqx1 matches the topic ``sersor1/#`` or ``sensor2/#``\ , these messages will be forwarded to the ``sensor1/#`` or ``sensor2/#`` topic of the remote node emqx2.
|
||||
|
||||
|
@ -86,10 +86,10 @@ If the messages received by the local node emqx1 matches the topic ``sersor1/#``
|
|||
Limitations of RPC bridging:
|
||||
|
||||
|
||||
#.
|
||||
#.
|
||||
The RPC bridge of emqx can only forward local messages to the remote node, and cannot synchronize the messages of the remote node to the local node;
|
||||
|
||||
#.
|
||||
#.
|
||||
RPC bridge can only bridge two EMQ X broker together and cannot bridge EMQ X broker to other MQTT brokers.
|
||||
|
||||
EMQ X MQTT Bridge Configuration
|
||||
|
@ -102,66 +102,66 @@ EMQ X MQTT bridging principle: Create an MQTT client on the EMQ X broker, and co
|
|||
.. code-block::
|
||||
|
||||
## Bridge Address: Use node name for rpc bridging, use host:port for mqtt connection
|
||||
bridge.mqtt.emqx2.address = 192.168.1.2:1883
|
||||
bridge.mqtt.emqx2.address = "192.168.1.2:1883"
|
||||
|
||||
## Bridged Protocol Version
|
||||
## Enumeration value: mqttv3 | mqttv4 | mqttv5
|
||||
bridge.mqtt.emqx2.proto_ver = mqttv4
|
||||
bridge.mqtt.emqx2.proto_ver = "mqttv4"
|
||||
|
||||
## mqtt client's clientid
|
||||
bridge.mqtt.emqx2.clientid = bridge_emq
|
||||
bridge.mqtt.emqx2.clientid = "bridge_emq"
|
||||
|
||||
## mqtt client's clean_start field
|
||||
## Note: Some MQTT Brokers need to set the clean_start value as `true`
|
||||
bridge.mqtt.emqx2.clean_start = true
|
||||
|
||||
## mqtt client's username field
|
||||
bridge.mqtt.emqx2.username = user
|
||||
bridge.mqtt.emqx2.username = "user"
|
||||
|
||||
## mqtt client's password field
|
||||
bridge.mqtt.emqx2.password = passwd
|
||||
bridge.mqtt.emqx2.password = "passwd"
|
||||
|
||||
## Whether the mqtt client uses ssl to connect to a remote serve or not
|
||||
bridge.mqtt.emqx2.ssl = off
|
||||
|
||||
## CA Certificate of Client SSL Connection (PEM format)
|
||||
bridge.mqtt.emqx2.cacertfile = etc/certs/cacert.pem
|
||||
bridge.mqtt.emqx2.cacertfile = "etc/certs/cacert.pem"
|
||||
|
||||
## SSL certificate of Client SSL connection
|
||||
bridge.mqtt.emqx2.certfile = etc/certs/client-cert.pem
|
||||
bridge.mqtt.emqx2.certfile = "etc/certs/client-cert.pem"
|
||||
|
||||
## Key file of Client SSL connection
|
||||
bridge.mqtt.emqx2.keyfile = etc/certs/client-key.pem
|
||||
bridge.mqtt.emqx2.keyfile = "etc/certs/client-key.pem"
|
||||
|
||||
## TTLS PSK password
|
||||
## Note 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot be configured at the same time
|
||||
##
|
||||
## See 'https://tools.ietf.org/html/rfc4279#section-2'.
|
||||
## bridge.mqtt.emqx2.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA
|
||||
## bridge.mqtt.emqx2.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
|
||||
|
||||
## Client's heartbeat interval
|
||||
bridge.mqtt.emqx2.keepalive = 60s
|
||||
|
||||
## Supported TLS version
|
||||
bridge.mqtt.emqx2.tls_versions = tlsv1.2
|
||||
bridge.mqtt.emqx2.tls_versions = "tlsv1.2"
|
||||
|
||||
## SSL encryption
|
||||
bridge.mqtt.emqx2.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384
|
||||
bridge.mqtt.emqx2.ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384"
|
||||
|
||||
## Forwarding topics of the message
|
||||
bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/#
|
||||
bridge.mqtt.emqx2.forwards = "sensor1/#,sensor2/#"
|
||||
|
||||
## Bridged mountpoint
|
||||
bridge.mqtt.emqx2.mountpoint = bridge/emqx2/${node}/
|
||||
bridge.mqtt.emqx2.mountpoint = "bridge/emqx2/${node}/"
|
||||
|
||||
## Subscription topic for bridging
|
||||
bridge.mqtt.emqx2.subscription.1.topic = cmd/topic1
|
||||
bridge.mqtt.emqx2.subscription.1.topic = "cmd/topic1"
|
||||
|
||||
## Subscription qos for bridging
|
||||
bridge.mqtt.emqx2.subscription.1.qos = 1
|
||||
|
||||
## Subscription topic for bridging
|
||||
bridge.mqtt.emqx2.subscription.2.topic = cmd/topic2
|
||||
bridge.mqtt.emqx2.subscription.2.topic = "cmd/topic2"
|
||||
|
||||
## Subscription qos for bridging
|
||||
bridge.mqtt.emqx2.subscription.2.qos = 1
|
||||
|
@ -190,7 +190,7 @@ The bridge of EMQ X has a message caching mechanism. The caching mechanism is ap
|
|||
bridge.mqtt.emqx2.queue.batch_bytes_limit = 1000MB
|
||||
|
||||
## The path for placing replayq queue. If it is not specified, then replayq will run in `mem-only` mode and messages will not be cached on disk.
|
||||
bridge.mqtt.emqx2.queue.replayq_dir = data/emqx_emqx2_bridge/
|
||||
bridge.mqtt.emqx2.queue.replayq_dir = "data/emqx_emqx2_bridge/"
|
||||
|
||||
## Replayq data segment size
|
||||
bridge.mqtt.emqx2.queue.replayq_seg_bytes = 10MB
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
## Bridge address: node name for local bridge, host:port for remote.
|
||||
##
|
||||
## Value: String
|
||||
## Example: emqx@127.0.0.1, 127.0.0.1:1883
|
||||
bridge.mqtt.aws.address = 127.0.0.1:1883
|
||||
## Example: emqx@127.0.0.1, "127.0.0.1:1883"
|
||||
bridge.mqtt.aws.address = "127.0.0.1:1883"
|
||||
|
||||
## Protocol version of the bridge.
|
||||
##
|
||||
|
@ -65,18 +65,18 @@ bridge.mqtt.aws.password = passwd
|
|||
## Topics that need to be forward to AWS IoTHUB
|
||||
##
|
||||
## Value: String
|
||||
## Example: topic1/#,topic2/#
|
||||
bridge.mqtt.aws.forwards = topic1/#,topic2/#
|
||||
## Example: "topic1/#,topic2/#"
|
||||
bridge.mqtt.aws.forwards = "topic1/#,topic2/#"
|
||||
|
||||
## Forward messages to the mountpoint of an AWS IoTHUB
|
||||
##
|
||||
## Value: String
|
||||
bridge.mqtt.aws.forward_mountpoint = bridge/aws/${node}/
|
||||
bridge.mqtt.aws.forward_mountpoint = "bridge/aws/${node}/"
|
||||
|
||||
## Need to subscribe to AWS topics
|
||||
##
|
||||
## Value: String
|
||||
## bridge.mqtt.aws.subscription.1.topic = cmd/topic1
|
||||
## bridge.mqtt.aws.subscription.1.topic = "cmd/topic1"
|
||||
|
||||
## Need to subscribe to AWS topics QoS.
|
||||
##
|
||||
|
@ -86,7 +86,7 @@ bridge.mqtt.aws.forward_mountpoint = bridge/aws/${node}/
|
|||
## A mountpoint that receives messages from AWS IoTHUB
|
||||
##
|
||||
## Value: String
|
||||
## bridge.mqtt.aws.receive_mountpoint = receive/aws/
|
||||
## bridge.mqtt.aws.receive_mountpoint = "receive/aws/"
|
||||
|
||||
|
||||
## Bribge to remote server via SSL.
|
||||
|
@ -97,28 +97,28 @@ bridge.mqtt.aws.ssl = off
|
|||
## PEM-encoded CA certificates of the bridge.
|
||||
##
|
||||
## Value: File
|
||||
bridge.mqtt.aws.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem
|
||||
bridge.mqtt.aws.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
||||
|
||||
## Client SSL Certfile of the bridge.
|
||||
##
|
||||
## Value: File
|
||||
bridge.mqtt.aws.certfile = {{ platform_etc_dir }}/certs/client-cert.pem
|
||||
bridge.mqtt.aws.certfile = "{{ platform_etc_dir }}/certs/client-cert.pem"
|
||||
|
||||
## Client SSL Keyfile of the bridge.
|
||||
##
|
||||
## Value: File
|
||||
bridge.mqtt.aws.keyfile = {{ platform_etc_dir }}/certs/client-key.pem
|
||||
bridge.mqtt.aws.keyfile = "{{ platform_etc_dir }}/certs/client-key.pem"
|
||||
|
||||
## SSL Ciphers used by the bridge.
|
||||
##
|
||||
## 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
|
||||
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"
|
||||
|
||||
## Ciphers for TLS PSK.
|
||||
## Note that 'bridge.${BridgeName}.ciphers' and 'bridge.${BridgeName}.psk_ciphers' cannot
|
||||
## be configured at the same time.
|
||||
## See 'https://tools.ietf.org/html/rfc4279#section-2'.
|
||||
#bridge.mqtt.aws.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA
|
||||
#bridge.mqtt.aws.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
|
||||
|
||||
## Ping interval of a down bridge.
|
||||
##
|
||||
|
@ -130,7 +130,7 @@ bridge.mqtt.aws.keepalive = 60s
|
|||
##
|
||||
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
|
||||
## Value: String
|
||||
bridge.mqtt.aws.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
|
||||
bridge.mqtt.aws.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
|
||||
|
||||
## Bridge reconnect time.
|
||||
##
|
||||
|
@ -160,7 +160,7 @@ bridge.mqtt.aws.max_inflight_size = 32
|
|||
## replayq works in a mem-only manner.
|
||||
##
|
||||
## Value: String
|
||||
bridge.mqtt.aws.queue.replayq_dir = {{ platform_data_dir }}/replayq/emqx_aws_bridge/
|
||||
bridge.mqtt.aws.queue.replayq_dir = "{{ platform_data_dir }}/replayq/emqx_aws_bridge/"
|
||||
|
||||
## Replayq segment size
|
||||
##
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
|
||||
## The IP and UDP port that CoAP bind with.
|
||||
##
|
||||
## Default: 0.0.0.0:5683
|
||||
## Default: "0.0.0.0:5683"
|
||||
##
|
||||
## Examples:
|
||||
## coap.bind.udp.x = 0.0.0.0:5683 | :::5683 | 127.0.0.1:5683 | ::1:5683
|
||||
## coap.bind.udp.x = "0.0.0.0:5683" | ":::5683" | "127.0.0.1:5683" | "::1:5683"
|
||||
##
|
||||
coap.bind.udp.1 = 0.0.0.0:5683
|
||||
##coap.bind.udp.2 = 0.0.0.0:6683
|
||||
coap.bind.udp.1 = "0.0.0.0:5683"
|
||||
##coap.bind.udp.2 = "0.0.0.0:6683"
|
||||
|
||||
## Whether to enable statistics for CoAP clients.
|
||||
##
|
||||
|
@ -23,13 +23,13 @@ coap.enable_stats = off
|
|||
|
||||
## The DTLS port that CoAP is listening on.
|
||||
##
|
||||
## Default: 0.0.0.0:5684
|
||||
## Default: "0.0.0.0:5684"
|
||||
##
|
||||
## Examples:
|
||||
## coap.bind.dtls.x = 0.0.0.0:5684 | :::5684 | 127.0.0.1:5684 | ::1:5684
|
||||
## coap.bind.dtls.x = "0.0.0.0:5684" | ":::5684" | "127.0.0.1:5684" | "::1:5684"
|
||||
##
|
||||
coap.bind.dtls.1 = 0.0.0.0:5684
|
||||
##coap.bind.dtls.2 = 0.0.0.0:6684
|
||||
coap.bind.dtls.1 = "0.0.0.0:5684"
|
||||
##coap.bind.dtls.2 = "0.0.0.0:6684"
|
||||
|
||||
## A server only does x509-path validation in mode verify_peer,
|
||||
## as it then sends a certificate request to the client (this
|
||||
|
@ -43,17 +43,17 @@ coap.bind.dtls.1 = 0.0.0.0:5684
|
|||
## Private key file for DTLS
|
||||
##
|
||||
## Value: File
|
||||
coap.dtls.keyfile = {{ platform_etc_dir }}/certs/key.pem
|
||||
coap.dtls.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
|
||||
|
||||
## Server certificate for DTLS.
|
||||
##
|
||||
## Value: File
|
||||
coap.dtls.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
||||
coap.dtls.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
|
||||
|
||||
## PEM-encoded CA certificates for DTLS
|
||||
##
|
||||
## Value: File
|
||||
## coap.dtls.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem
|
||||
## coap.dtls.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
||||
|
||||
## Used together with {verify, verify_peer} by an SSL server. If set to true,
|
||||
## the server fails if the client does not have a certificate to send, that is,
|
||||
|
@ -79,4 +79,4 @@ coap.dtls.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
|||
## Most of it was copied from Mozilla’s Server Side TLS article
|
||||
##
|
||||
## Value: Ciphers
|
||||
coap.dtls.ciphers = 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
|
||||
coap.dtls.ciphers = "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"
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
## The gRPC server url
|
||||
##
|
||||
## exhook.server.$name.url = url()
|
||||
exhook.server.default.url = http://127.0.0.1:9000
|
||||
exhook.server.default.url = "http://127.0.0.1:9000"
|
||||
|
||||
#exhook.server.default.ssl.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem
|
||||
#exhook.server.default.ssl.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
||||
#exhook.server.default.ssl.keyfile = {{ platform_etc_dir }}/certs/key.pem
|
||||
#exhook.server.default.ssl.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
||||
#exhook.server.default.ssl.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
|
||||
#exhook.server.default.ssl.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
{profiles,
|
||||
[{test,
|
||||
[{deps,
|
||||
[{emqx_ct_helper, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "v1.3.1"}}}
|
||||
]}
|
||||
[]}
|
||||
]}
|
||||
]}.
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
exproto.server.http.port = 9100
|
||||
|
||||
exproto.server.https.port = 9101
|
||||
exproto.server.https.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem
|
||||
exproto.server.https.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
||||
exproto.server.https.keyfile = {{ platform_etc_dir }}/certs/key.pem
|
||||
exproto.server.https.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
||||
exproto.server.https.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
|
||||
exproto.server.https.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
|
||||
|
||||
##--------------------------------------------------------------------
|
||||
## Listeners
|
||||
|
@ -20,12 +20,12 @@ exproto.server.https.keyfile = {{ platform_etc_dir }}/certs/key.pem
|
|||
##
|
||||
## Value: <tcp|ssl|udp|dtls>://<ip>:<port>
|
||||
##
|
||||
## Examples: tcp://0.0.0.0:7993 | ssl://127.0.0.1:7994
|
||||
exproto.listener.protoname = tcp://0.0.0.0:7993
|
||||
## Examples: "tcp://0.0.0.0:7993" | "ssl://127.0.0.1:7994"
|
||||
exproto.listener.protoname.endpoint = "tcp://0.0.0.0:7993"
|
||||
|
||||
## The ConnectionHandler server address
|
||||
##
|
||||
exproto.listener.protoname.connection_handler_url = http://127.0.0.1:9001
|
||||
exproto.listener.protoname.connection_handler_url = "http://127.0.0.1:9001"
|
||||
|
||||
#exproto.listener.protoname.connection_handler_certfile =
|
||||
#exproto.listener.protoname.connection_handler_cacertfile =
|
||||
|
@ -62,8 +62,8 @@ exproto.listener.protoname.idle_timeout = 30s
|
|||
##
|
||||
## Value: ACL Rule
|
||||
##
|
||||
## Example: allow 192.168.0.0/24
|
||||
exproto.listener.protoname.access.1 = allow all
|
||||
## Example: "allow 192.168.0.0/24"
|
||||
exproto.listener.protoname.access.1 = "allow all"
|
||||
|
||||
## Enable the Proxy Protocol V1/2 if the EMQ X cluster is deployed
|
||||
## behind HAProxy or Nginx.
|
||||
|
@ -146,27 +146,27 @@ exproto.listener.protoname.reuseaddr = true
|
|||
## See: http://erlang.org/doc/man/ssl.html
|
||||
##
|
||||
## Value: String, seperated by ','
|
||||
#exproto.listener.protoname.tls_versions = tlsv1.2,tlsv1.1,tlsv1
|
||||
#exproto.listener.protoname.tls_versions = "tlsv1.2,tlsv1.1,tlsv1"
|
||||
|
||||
## Path to the file containing the user's private PEM-encoded key.
|
||||
##
|
||||
## See: http://erlang.org/doc/man/ssl.html
|
||||
##
|
||||
## Value: File
|
||||
#exproto.listener.protoname.keyfile = {{ platform_etc_dir }}/certs/key.pem
|
||||
#exproto.listener.protoname.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
|
||||
|
||||
## Path to a file containing the user certificate.
|
||||
##
|
||||
## See: http://erlang.org/doc/man/ssl.html
|
||||
##
|
||||
## Value: File
|
||||
#exproto.listener.protoname.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
||||
#exproto.listener.protoname.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
|
||||
|
||||
## Path to the file containing PEM-encoded CA certificates. The CA certificates
|
||||
## are used during server authentication and when building the client certificate chain.
|
||||
##
|
||||
## Value: File
|
||||
#exproto.listener.protoname.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem
|
||||
#exproto.listener.protoname.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
||||
|
||||
## The Ephemeral Diffie-Helman key exchange is a very effective way of
|
||||
## ensuring Forward Secrecy by exchanging a set of keys that never hit
|
||||
|
@ -183,7 +183,7 @@ exproto.listener.protoname.reuseaddr = true
|
|||
## openssl dhparam -out dh-params.pem 2048
|
||||
##
|
||||
## Value: File
|
||||
#exproto.listener.protoname.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem
|
||||
#exproto.listener.protoname.dhfile = "{{ platform_etc_dir }}/certs/dh-params.pem"
|
||||
|
||||
## A server only does x509-path validation in mode verify_peer,
|
||||
## as it then sends a certificate request to the client (this
|
||||
|
@ -218,13 +218,13 @@ exproto.listener.protoname.reuseaddr = true
|
|||
## Most of it was copied from Mozilla’s Server Side TLS article
|
||||
##
|
||||
## Value: Ciphers
|
||||
#exproto.listener.protoname.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
|
||||
#exproto.listener.protoname.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"
|
||||
|
||||
## Ciphers for TLS PSK.
|
||||
## Note that 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot
|
||||
## be configured at the same time.
|
||||
## See 'https://tools.ietf.org/html/rfc4279#section-2'.
|
||||
#exproto.listener.protoname.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA
|
||||
#exproto.listener.protoname.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
|
||||
|
||||
## SSL parameter renegotiation is a feature that allows a client and a server
|
||||
## to renegotiate the parameters of the SSL connection on the fly.
|
||||
|
|
|
@ -44,7 +44,7 @@ end}.
|
|||
%%--------------------------------------------------------------------
|
||||
%% Listeners
|
||||
|
||||
{mapping, "exproto.listener.$proto", "emqx_exproto.listeners", [
|
||||
{mapping, "exproto.listener.$proto.endpoint", "emqx_exproto.listeners", [
|
||||
{datatype, string}
|
||||
]}.
|
||||
|
||||
|
@ -340,7 +340,7 @@ end}.
|
|||
Listeners = fun(Proto) ->
|
||||
Prefix = string:join(["exproto","listener", Proto], "."),
|
||||
Opts = HandlerOpts(Prefix) ++ ConnOpts(Prefix) ++ LisOpts(Prefix),
|
||||
case cuttlefish:conf_get(Prefix, Conf, undefined) of
|
||||
case cuttlefish:conf_get(Prefix ++ ".endpoint", Conf, undefined) of
|
||||
undefined -> [];
|
||||
ListenOn0 ->
|
||||
case ParseListenOn(ListenOn0) of
|
||||
|
@ -359,6 +359,6 @@ end}.
|
|||
end
|
||||
end
|
||||
end,
|
||||
lists:flatten([Listeners(Proto) || {[_, "listener", Proto], ListenOn}
|
||||
lists:flatten([Listeners(Proto) || {[_, "listener", Proto, "endpoint"], ListenOn}
|
||||
<- cuttlefish_variable:filter_by_prefix("exproto.listener", Conf)])
|
||||
end}.
|
||||
|
|
|
@ -46,7 +46,6 @@
|
|||
{profiles,
|
||||
[{test,
|
||||
[{deps,
|
||||
[{emqx_ct_helper, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "v1.3.0"}}}
|
||||
]}
|
||||
[]}
|
||||
]}
|
||||
]}.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_exproto,
|
||||
[{description, "EMQ X Extension for Protocol"},
|
||||
{vsn, "4.3.0"}, %% strict semver
|
||||
{vsn, "4.4.0"}, %% strict semver
|
||||
{modules, []},
|
||||
{registered, []},
|
||||
{mod, {emqx_exproto_app, []}},
|
||||
|
|
|
@ -25,25 +25,25 @@ lwm2m.lifetime_max = 86400s
|
|||
# Placeholders supported:
|
||||
# '%e': Endpoint Name
|
||||
# '%a': IP Address
|
||||
lwm2m.mountpoint = lwm2m/%e/
|
||||
lwm2m.mountpoint = "lwm2m/%e/"
|
||||
|
||||
# The topic subscribed by the lwm2m client after it is connected
|
||||
# Placeholders supported:
|
||||
# '%e': Endpoint Name
|
||||
# '%a': IP Address
|
||||
lwm2m.topics.command = dn/#
|
||||
lwm2m.topics.command = "dn/#"
|
||||
|
||||
# The topic to which the lwm2m client's response is published
|
||||
lwm2m.topics.response = up/resp
|
||||
lwm2m.topics.response = "up/resp"
|
||||
|
||||
# The topic to which the lwm2m client's notify message is published
|
||||
lwm2m.topics.notify = up/notify
|
||||
lwm2m.topics.notify = "up/notify"
|
||||
|
||||
# The topic to which the lwm2m client's register message is published
|
||||
lwm2m.topics.register = up/resp
|
||||
lwm2m.topics.register = "up/resp"
|
||||
|
||||
# The topic to which the lwm2m client's update message is published
|
||||
lwm2m.topics.update = up/resp
|
||||
lwm2m.topics.update = "up/resp"
|
||||
|
||||
# When publish the update message.
|
||||
#
|
||||
|
@ -55,18 +55,18 @@ lwm2m.topics.update = up/resp
|
|||
#lwm2m.update_msg_publish_condition = contains_object_list
|
||||
|
||||
# Dir where the object definition files can be found
|
||||
lwm2m.xml_dir = {{ platform_etc_dir }}/lwm2m_xml
|
||||
lwm2m.xml_dir = "{{ platform_etc_dir }}/lwm2m_xml"
|
||||
|
||||
##--------------------------------------------------------------------
|
||||
## UDP Listener options
|
||||
|
||||
## The IP and port of the LwM2M Gateway
|
||||
##
|
||||
## Default: 0.0.0.0:5683
|
||||
## Default: "0.0.0.0:5683"
|
||||
## Examples:
|
||||
## lwm2m.bind.udp.x = 0.0.0.0:5683 | :::5683 | 127.0.0.1:5683 | ::1:5683
|
||||
lwm2m.bind.udp.1 = 0.0.0.0:5683
|
||||
#lwm2m.bind.udp.2 = 0.0.0.0:6683
|
||||
## lwm2m.bind.udp.x = "0.0.0.0:5683" | ":::5683" | "127.0.0.1:5683" | "::1:5683"
|
||||
lwm2m.bind.udp.1 = "0.0.0.0:5683"
|
||||
#lwm2m.bind.udp.2 = "0.0.0.0:6683"
|
||||
|
||||
## Socket options, used for performance tuning
|
||||
##
|
||||
|
@ -83,13 +83,13 @@ lwm2m.opts.read_packets = 20
|
|||
|
||||
## The DTLS port that LwM2M is listening on.
|
||||
##
|
||||
## Default: 0.0.0.0:5684
|
||||
## Default: "0.0.0.0:5684"
|
||||
##
|
||||
## Examples:
|
||||
## lwm2m.bind.dtls.x = 0.0.0.0:5684 | :::5684 | 127.0.0.1:5684 | ::1:5684
|
||||
## lwm2m.bind.dtls.x = "0.0.0.0:5684" | ":::5684" | "127.0.0.1:5684" | "::1:5684"
|
||||
##
|
||||
lwm2m.bind.dtls.1 = 0.0.0.0:5684
|
||||
#lwm2m.bind.dtls.2 = 0.0.0.0:6684
|
||||
lwm2m.bind.dtls.1 = "0.0.0.0:5684"
|
||||
#lwm2m.bind.dtls.2 = "0.0.0.0:6684"
|
||||
|
||||
## A server only does x509-path validation in mode verify_peer,
|
||||
## as it then sends a certificate request to the client (this
|
||||
|
@ -103,17 +103,17 @@ lwm2m.bind.dtls.1 = 0.0.0.0:5684
|
|||
## Private key file for DTLS
|
||||
##
|
||||
## Value: File
|
||||
lwm2m.dtls.keyfile = {{ platform_etc_dir }}/certs/key.pem
|
||||
lwm2m.dtls.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
|
||||
|
||||
## Server certificate for DTLS.
|
||||
##
|
||||
## Value: File
|
||||
lwm2m.dtls.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
||||
lwm2m.dtls.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
|
||||
|
||||
## PEM-encoded CA certificates for DTLS
|
||||
##
|
||||
## Value: File
|
||||
#lwm2m.dtls.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem
|
||||
#lwm2m.dtls.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
||||
|
||||
## Used together with {verify, verify_peer} by an SSL server. If set to true,
|
||||
## the server fails if the client does not have a certificate to send, that is,
|
||||
|
@ -139,11 +139,11 @@ lwm2m.dtls.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
|||
## Most of it was copied from Mozilla’s Server Side TLS article
|
||||
##
|
||||
## Value: Ciphers
|
||||
lwm2m.dtls.ciphers = 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
|
||||
lwm2m.dtls.ciphers = "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"
|
||||
|
||||
## Ciphers for TLS PSK.
|
||||
##
|
||||
## 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 = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
{profiles,
|
||||
[{test,
|
||||
[{deps, [{er_coap_client, {git, "https://github.com/emqx/er_coap_client", {tag, "v1.0"}}},
|
||||
{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}},
|
||||
{emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.0"}}}
|
||||
]}
|
||||
]}
|
||||
|
|
|
@ -23,7 +23,7 @@ management.default_application.secret = public
|
|||
##--------------------------------------------------------------------
|
||||
## HTTP Listener
|
||||
|
||||
management.listener.http = 8081
|
||||
management.listener.http.port = 8081
|
||||
management.listener.http.acceptors = 2
|
||||
management.listener.http.max_clients = 512
|
||||
management.listener.http.backlog = 512
|
||||
|
@ -35,19 +35,19 @@ management.listener.http.ipv6_v6only = false
|
|||
##--------------------------------------------------------------------
|
||||
## HTTPS Listener
|
||||
|
||||
## management.listener.https = 8081
|
||||
## management.listener.https.port = 8081
|
||||
## management.listener.https.acceptors = 2
|
||||
## management.listener.https.max_clients = 512
|
||||
## management.listener.https.backlog = 512
|
||||
## management.listener.https.send_timeout = 15s
|
||||
## management.listener.https.send_timeout_close = on
|
||||
## management.listener.https.certfile = etc/certs/cert.pem
|
||||
## management.listener.https.keyfile = etc/certs/key.pem
|
||||
## management.listener.https.cacertfile = etc/certs/cacert.pem
|
||||
## management.listener.https.certfile = "etc/certs/cert.pem"
|
||||
## management.listener.https.keyfile = "etc/certs/key.pem"
|
||||
## management.listener.https.cacertfile = "etc/certs/cacert.pem"
|
||||
## management.listener.https.verify = verify_peer
|
||||
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
|
||||
## management.listener.https.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
|
||||
## management.listener.https.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
|
||||
## management.listener.https.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
|
||||
## management.listener.https.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"
|
||||
## management.listener.https.fail_if_no_peer_cert = true
|
||||
## management.listener.https.inet6 = false
|
||||
## management.listener.https.ipv6_v6only = false
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
{datatype, string}
|
||||
]}.
|
||||
|
||||
{mapping, "management.listener.http", "emqx_management.listeners", [
|
||||
{mapping, "management.listener.http.port", "emqx_management.listeners", [
|
||||
{datatype, [integer, ip]}
|
||||
]}.
|
||||
|
||||
|
@ -85,7 +85,7 @@
|
|||
{datatype, {enum, [true, false]}}
|
||||
]}.
|
||||
|
||||
{mapping, "management.listener.https", "emqx_management.listeners", [
|
||||
{mapping, "management.listener.https.port", "emqx_management.listeners", [
|
||||
{datatype, [integer, ip]}
|
||||
]}.
|
||||
|
||||
|
@ -225,7 +225,7 @@ end}.
|
|||
lists:foldl(
|
||||
fun(Proto, Acc) ->
|
||||
Prefix = "management.listener." ++ atom_to_list(Proto),
|
||||
case cuttlefish:conf_get(Prefix, Conf, undefined) of
|
||||
case cuttlefish:conf_get(Prefix ++ ".port", Conf, undefined) of
|
||||
undefined -> Acc;
|
||||
Port ->
|
||||
[{Proto, Port, TcpOpts(Prefix) ++ Opts(Prefix)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_management,
|
||||
[{description, "EMQ X Management API and CLI"},
|
||||
{vsn, "4.3.3"}, % strict semver, bump manually!
|
||||
{vsn, "4.4.0"}, % strict semver, bump manually!
|
||||
{modules, []},
|
||||
{registered, [emqx_management_sup]},
|
||||
{applications, [kernel,stdlib,minirest]},
|
||||
|
|
|
@ -181,4 +181,4 @@ remove_resources() ->
|
|||
lists:foreach(fun(#resource{id = Id}) ->
|
||||
emqx_rule_engine:delete_resource(Id)
|
||||
end, emqx_rule_registry:get_resources()),
|
||||
timer:sleep(500).
|
||||
timer:sleep(500).
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
## The Prometheus Push Gateway URL address
|
||||
##
|
||||
## Note: You can comment out this line to disable it
|
||||
prometheus.push.gateway.server = http://127.0.0.1:9091
|
||||
prometheus.push.gateway.server = "http://127.0.0.1:9091"
|
||||
|
||||
## The metrics data push interval (millisecond)
|
||||
##
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
psk.file.path = {{ platform_etc_dir }}/psk.txt
|
||||
psk.file.delimiter = :
|
||||
psk.file.path = "{{ platform_etc_dir }}/psk.txt"
|
||||
psk.file.delimiter = ":"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_recon,
|
||||
[{description, "EMQ X Recon Plugin"},
|
||||
{vsn, "4.3.0"}, % strict semver, bump manually!
|
||||
{vsn, "4.4.0"}, % strict semver, bump manually!
|
||||
{modules, []},
|
||||
{registered, []},
|
||||
{applications, [kernel,stdlib,recon]},
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
REBAR := rebar3
|
||||
|
||||
.PHONY: all
|
||||
all: es
|
||||
|
||||
.PHONY: compile
|
||||
compile:
|
||||
$(REBAR) compile
|
||||
|
||||
.PHONY: clean
|
||||
clean: distclean
|
||||
|
||||
.PHONY: distclean
|
||||
distclean:
|
||||
@rm -rf _build erl_crash.dump rebar3.crashdump
|
||||
|
||||
.PHONY: xref
|
||||
xref:
|
||||
$(REBAR) xref
|
||||
|
||||
.PHONY: eunit
|
||||
eunit: compile
|
||||
$(REBAR) eunit -v -c
|
||||
$(REBAR) cover
|
||||
|
||||
.PHONY: ct
|
||||
ct: compile
|
||||
$(REBAR) as test ct -v
|
||||
|
||||
cover:
|
||||
$(REBAR) cover
|
||||
|
||||
.PHONY: dialyzer
|
||||
dialyzer:
|
||||
$(REBAR) dialyzer
|
||||
|
||||
.PHONY: es
|
||||
es: compile
|
||||
$(REBAR) escriptize
|
||||
|
||||
.PHONY: elvis
|
||||
elvis:
|
||||
./scripts/elvis-check.sh
|
|
@ -0,0 +1,53 @@
|
|||
# emqx_resource
|
||||
|
||||
The `emqx_resource` is an application that manages configuration specs and runtime states
|
||||
for components that need to be configured and manipulated from the emqx-dashboard.
|
||||
|
||||
It is intended to be used by resources, actions, acl, auth, backend_logics and more.
|
||||
|
||||
It reads the configuration spec from *.spec (in HOCON format) and provide APIs for
|
||||
creating, updating and destroying resource instances among all nodes in the cluster.
|
||||
|
||||
It handles the problem like storing the configs and runtime states for both resource
|
||||
and resource instances, and how porting them between different emqx_resource versions.
|
||||
|
||||
It may maintain the config and data in JSON or HOCON files in data/ dir.
|
||||
|
||||
After restarting the emqx_resource, it re-creates all the resource instances.
|
||||
|
||||
There can be foreign references between resource instances via resource-id.
|
||||
So they may find each other via this Id.
|
||||
|
||||
## Try it out
|
||||
|
||||
$ ./demo.sh
|
||||
Eshell V11.1.8 (abort with ^G)
|
||||
1> == the demo log tracer <<"log_tracer_clientid_shawn">> started.
|
||||
config: #{<<"config">> =>
|
||||
#{<<"bulk">> => <<"10KB">>,<<"cache_log_dir">> => <<"/tmp">>,
|
||||
<<"condition">> => #{<<"clientid">> => <<"abc">>},
|
||||
<<"level">> => <<"debug">>},
|
||||
<<"id">> => <<"log_tracer_clientid_shawn">>,
|
||||
<<"resource_type">> => <<"log_tracer">>}
|
||||
1> emqx_resource_instance:health_check(<<"log_tracer_clientid_shawn">>).
|
||||
== the demo log tracer <<"log_tracer_clientid_shawn">> is working well
|
||||
state: #{health_checked => 1,logger_handler_id => abc}
|
||||
ok
|
||||
|
||||
2> emqx_resource_instance:health_check(<<"log_tracer_clientid_shawn">>).
|
||||
== the demo log tracer <<"log_tracer_clientid_shawn">> is working well
|
||||
state: #{health_checked => 2,logger_handler_id => abc}
|
||||
ok
|
||||
|
||||
3> emqx_resource_instance:query(<<"log_tracer_clientid_shawn">>, get_log).
|
||||
== the demo log tracer <<"log_tracer_clientid_shawn">> received request: get_log
|
||||
state: #{health_checked => 2,logger_handler_id => abc}
|
||||
"this is a demo log messages..."
|
||||
|
||||
4> emqx_resource_instance:remove(<<"log_tracer_clientid_shawn">>).
|
||||
== the demo log tracer <<"log_tracer_clientid_shawn">> stopped.
|
||||
state: #{health_checked => 0,logger_handler_id => abc}
|
||||
ok
|
||||
|
||||
5> emqx_resource_instance:query(<<"log_tracer_clientid_shawn">>, get_log).
|
||||
** exception error: {get_instance,{<<"log_tracer_clientid_shawn">>,not_found}}
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
rebar3 compile
|
||||
|
||||
erl -sname abc -pa _build/default/lib/*/ebin _build/default/lib/emqx_resource/examples -s demo
|
|
@ -0,0 +1,14 @@
|
|||
[{elvis, [{config, [
|
||||
|
||||
#{dirs => ["src"],
|
||||
filter => "*.erl",
|
||||
%ignore => [],
|
||||
ruleset => erl_files,
|
||||
rules => [{elvis_style, operator_spaces, #{
|
||||
rules => [{right, ","},
|
||||
{right, "|"},
|
||||
{left, "|"},
|
||||
{right, "||"},
|
||||
{left, "||"}]}},
|
||||
{elvis_style, god_modules, #{limit => 100}}]}
|
||||
]}]}].
|
|
@ -0,0 +1,3 @@
|
|||
##--------------------------------------------------------------------
|
||||
## EMQ X Resource Plugin
|
||||
##--------------------------------------------------------------------
|
|
@ -0,0 +1,13 @@
|
|||
-module(demo).
|
||||
|
||||
-export([start/0]).
|
||||
|
||||
start() ->
|
||||
code:load_file(log_tracer),
|
||||
code:load_file(log_tracer_schema),
|
||||
{ok, _} = application:ensure_all_started(minirest),
|
||||
{ok, _} = application:ensure_all_started(emqx_resource),
|
||||
emqx_resource:load_instances("./_build/default/lib/emqx_resource/examples"),
|
||||
Handlers = [{"/", minirest:handler(#{modules => [log_tracer]})}],
|
||||
Dispatch = [{"/[...]", minirest, Handlers}],
|
||||
minirest:start_http(?MODULE, #{socket_opts => [inet, {port, 9900}]}, Dispatch).
|
|
@ -0,0 +1,147 @@
|
|||
---
|
||||
theme: gaia
|
||||
color: #000
|
||||
colorSecondary: #333
|
||||
backgroundColor: #fff
|
||||
backgroundImage: url('https://marp.app/assets/hero-background.jpg')
|
||||
paginate: true
|
||||
marp: true
|
||||
---
|
||||
|
||||
<!-- _class: lead -->
|
||||
|
||||
# EMQX Resource
|
||||
|
||||
---
|
||||
|
||||
## What is it for
|
||||
|
||||
The [emqx_resource](https://github.com/terry-xiaoyu/emqx_resource) for managing configurations and runtime states for dashboard components .
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
<!-- _class: lead -->
|
||||
|
||||
# The Demo
|
||||
|
||||
The little log tracer
|
||||
|
||||
---
|
||||
|
||||
- The hocon schema file (log_tracer_schema.erl):
|
||||
|
||||
https://github.com/terry-xiaoyu/emqx_resource/blob/main/examples/log_tracer_schema.erl
|
||||
|
||||
- The callback file (log_tracer.erl):
|
||||
|
||||
https://github.com/terry-xiaoyu/emqx_resource/blob/main/examples/log_tracer.erl
|
||||
|
||||
---
|
||||
|
||||
Start the demo log tracer
|
||||
|
||||
```
|
||||
./demo.sh
|
||||
```
|
||||
|
||||
Load instance from config files (auto loaded)
|
||||
|
||||
```
|
||||
## This will load all of the "*.conf" file under that directory:
|
||||
|
||||
emqx_resource:load_instances("./_build/default/lib/emqx_resource/examples").
|
||||
```
|
||||
|
||||
The config file is validated against the schema (`*_schema.erl`) before loaded.
|
||||
|
||||
---
|
||||
|
||||
# List Types and Instances
|
||||
|
||||
- To list all the available resource types:
|
||||
|
||||
```
|
||||
emqx_resource:list_types().
|
||||
emqx_resource:list_instances().
|
||||
```
|
||||
|
||||
- And there's `*_verbose` versions for these `list_*` APIs:
|
||||
|
||||
```
|
||||
emqx_resource:list_types_verbose().
|
||||
emqx_resource:list_instances_verbose().
|
||||
```
|
||||
|
||||
---
|
||||
# Instance management
|
||||
|
||||
- To get a resource types and instances:
|
||||
|
||||
```
|
||||
emqx_resource:get_type(log_tracer).
|
||||
emqx_resource:get_instance("log_tracer_clientid_shawn").
|
||||
```
|
||||
|
||||
- To create a resource instances:
|
||||
|
||||
```
|
||||
emqx_resource:create("log_tracer2", log_tracer,
|
||||
#{bulk => <<"1KB">>,cache_log_dir => <<"/tmp">>,
|
||||
cache_logs_in => <<"memory">>,chars_limit => 1024,
|
||||
condition => #{<<"app">> => <<"emqx">>},
|
||||
enable_cache => true,level => debug}).
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
- To update a resource:
|
||||
|
||||
```
|
||||
emqx_resource:update("log_tracer2", log_tracer, #{bulk => <<"100KB">>}, []).
|
||||
```
|
||||
|
||||
- To delete a resource:
|
||||
|
||||
```
|
||||
emqx_resource:remove("log_tracer2").
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: lead -->
|
||||
|
||||
# HTTP APIs Demo
|
||||
|
||||
---
|
||||
|
||||
# Get a log tracer
|
||||
|
||||
To list current log tracers:
|
||||
|
||||
```
|
||||
curl -s -XGET 'http://localhost:9900/log_tracer' | jq .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Update or Create
|
||||
|
||||
To update an existing log tracer or create a new one:
|
||||
|
||||
```
|
||||
INST='{
|
||||
"resource_type": "log_tracer",
|
||||
"config": {
|
||||
"condition": {
|
||||
"app": "emqx"
|
||||
},
|
||||
"level": "debug",
|
||||
"cache_log_dir": "/tmp",
|
||||
"bulk": "10KB",
|
||||
"chars_limit": 1024
|
||||
}
|
||||
}'
|
||||
curl -sv -XPUT 'http://localhost:9900/log_tracer/log_tracer2' -d $INST | jq .
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"id": "log_tracer_clientid_shawn"
|
||||
"resource_type": "log_tracer"
|
||||
"config": {
|
||||
"condition": {"app": "emqx"}
|
||||
"level": "debug"
|
||||
"cache_log_dir": "/tmp"
|
||||
"bulk": "10KB"
|
||||
"chars_limit": 1024
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
-module(log_tracer).
|
||||
|
||||
-include_lib("emqx_resource/include/emqx_resource_behaviour.hrl").
|
||||
|
||||
-emqx_resource_api_path("/log_tracer").
|
||||
|
||||
%% callbacks of behaviour emqx_resource
|
||||
-export([ on_start/2
|
||||
, on_stop/2
|
||||
, on_query/4
|
||||
, on_health_check/2
|
||||
, on_api_reply_format/1
|
||||
, on_config_merge/3
|
||||
]).
|
||||
|
||||
%% callbacks for emqx_resource config schema
|
||||
-export([fields/1]).
|
||||
|
||||
fields(ConfPath) ->
|
||||
log_tracer_schema:fields(ConfPath).
|
||||
|
||||
on_start(InstId, Config) ->
|
||||
io:format("== the demo log tracer ~p started.~nconfig: ~p~n", [InstId, Config]),
|
||||
{ok, #{logger_handler_id => abc, health_checked => 0}}.
|
||||
|
||||
on_stop(InstId, State) ->
|
||||
io:format("== the demo log tracer ~p stopped.~nstate: ~p~n", [InstId, State]),
|
||||
ok.
|
||||
|
||||
on_query(InstId, Request, AfterQuery, State) ->
|
||||
io:format("== the demo log tracer ~p received request: ~p~nstate: ~p~n",
|
||||
[InstId, Request, State]),
|
||||
emqx_resource:query_success(AfterQuery),
|
||||
"this is a demo log messages...".
|
||||
|
||||
on_health_check(InstId, State = #{health_checked := Checked}) ->
|
||||
NState = State#{health_checked => Checked + 1},
|
||||
io:format("== the demo log tracer ~p is working well~nstate: ~p~n", [InstId, NState]),
|
||||
{ok, NState}.
|
||||
|
||||
on_api_reply_format(#{id := Id, status := Status, state := #{health_checked := NChecked}}) ->
|
||||
#{id => Id, status => Status, checked_count => NChecked}.
|
||||
|
||||
on_config_merge(OldConfig, NewConfig, _Params) ->
|
||||
maps:merge(OldConfig, NewConfig).
|
|
@ -0,0 +1,45 @@
|
|||
-module(log_tracer_schema).
|
||||
|
||||
-include_lib("typerefl/include/types.hrl").
|
||||
|
||||
-export([fields/1]).
|
||||
|
||||
-reflect_type([t_level/0, t_cache_logs_in/0]).
|
||||
|
||||
-type t_level() :: debug | info | notice | warning | error | critical | alert | emergency.
|
||||
|
||||
-type t_cache_logs_in() :: memory | file.
|
||||
|
||||
fields("config") ->
|
||||
[ {condition, fun condition/1}
|
||||
, {level, fun level/1}
|
||||
, {enable_cache, fun enable_cache/1}
|
||||
, {cache_logs_in, fun cache_logs_in/1}
|
||||
, {cache_log_dir, fun cache_log_dir/1}
|
||||
, {bulk, fun bulk/1}
|
||||
];
|
||||
fields(_) -> [].
|
||||
|
||||
condition(mapping) -> "config.condition";
|
||||
condition(type) -> map();
|
||||
condition(_) -> undefined.
|
||||
|
||||
level(mapping) -> "config.level";
|
||||
level(type) -> t_level();
|
||||
level(_) -> undefined.
|
||||
|
||||
enable_cache(mapping) -> "config.enable_cache";
|
||||
enable_cache(type) -> boolean();
|
||||
enable_cache(_) -> undefined.
|
||||
|
||||
cache_logs_in(mapping) -> "config.cache_logs_in";
|
||||
cache_logs_in(type) -> t_cache_logs_in();
|
||||
cache_logs_in(_) -> undefined.
|
||||
|
||||
cache_log_dir(mapping) -> "config.cache_log_dir";
|
||||
cache_log_dir(type) -> typerefl:regexp_string("^(.*)$");
|
||||
cache_log_dir(_) -> undefined.
|
||||
|
||||
bulk(mapping) -> "config.bulk";
|
||||
bulk(type) -> typerefl:regexp_string("^[. 0-9]+(B|KB|MB|GB)$");
|
||||
bulk(_) -> undefined.
|
|
@ -0,0 +1,34 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% 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.
|
||||
%%--------------------------------------------------------------------
|
||||
-type resource_type() :: module().
|
||||
-type instance_id() :: binary().
|
||||
-type resource_config() :: term().
|
||||
-type resource_spec() :: map().
|
||||
-type resource_state() :: term().
|
||||
-type resource_data() :: #{
|
||||
id => instance_id(),
|
||||
mod => module(),
|
||||
config => resource_config(),
|
||||
state => resource_state(),
|
||||
status => started | stopped
|
||||
}.
|
||||
|
||||
-type after_query() :: {OnSuccess :: after_query_fun(), OnFailed :: after_query_fun()} |
|
||||
undefined.
|
||||
|
||||
%% the `after_query_fun()` is mainly for callbacks that increment counters or do some fallback
|
||||
%% actions upon query failure
|
||||
-type after_query_fun() :: {fun((...) -> ok), Args :: [term()]}.
|
|
@ -0,0 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% 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.
|
||||
%%--------------------------------------------------------------------
|
||||
-include_lib("emqx_resource/include/emqx_resource.hrl").
|
||||
-behaviour(emqx_resource).
|
||||
-compile({parse_transform, emqx_resource_transform}).
|
|
@ -0,0 +1,53 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% 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.
|
||||
%%--------------------------------------------------------------------
|
||||
-define(CLUSTER_CALL(Func, Args), ?CLUSTER_CALL(Func, Args, ok)).
|
||||
|
||||
-define(CLUSTER_CALL(Func, Args, ResParttern),
|
||||
%% ekka_mnesia:running_nodes()
|
||||
fun() ->
|
||||
case LocalResult = erlang:apply(?MODULE, Func, Args) of
|
||||
ResParttern ->
|
||||
case rpc:multicall(nodes(), ?MODULE, Func, Args, 5000) of
|
||||
{ResL, []} ->
|
||||
Filter = fun
|
||||
(ResParttern) -> false;
|
||||
({badrpc, {'EXIT', {undef, [{?MODULE, Func0, _, []}]}}})
|
||||
when Func0 =:= Func -> false;
|
||||
(_) -> true
|
||||
end,
|
||||
case lists:filter(Filter, ResL) of
|
||||
[] -> LocalResult;
|
||||
ErrL -> {error, ErrL}
|
||||
end;
|
||||
{ResL, BadNodes} ->
|
||||
{error, {failed_on_nodes, BadNodes, ResL}}
|
||||
end;
|
||||
ErrorResult ->
|
||||
{error, ErrorResult}
|
||||
end
|
||||
end()).
|
||||
|
||||
-define(SAFE_CALL(_EXP_),
|
||||
?SAFE_CALL(_EXP_, _ = do_nothing)).
|
||||
|
||||
-define(SAFE_CALL(_EXP_, _EXP_ON_FAIL_),
|
||||
fun() ->
|
||||
try (_EXP_)
|
||||
catch _EXCLASS_:_EXCPTION_:_ST_ ->
|
||||
_EXP_ON_FAIL_,
|
||||
{error, {_EXCLASS_, _EXCPTION_, _ST_}}
|
||||
end
|
||||
end()).
|
|
@ -0,0 +1,2 @@
|
|||
%%-*- mode: erlang -*-
|
||||
%% emqx-resource config mapping
|
|
@ -0,0 +1,17 @@
|
|||
{erl_opts, [ debug_info
|
||||
, nowarn_unused_import
|
||||
%, {d, 'RESOURCE_DEBUG'}
|
||||
]}.
|
||||
|
||||
{erl_first_files, ["src/emqx_resource_transform.erl"]}.
|
||||
|
||||
{extra_src_dirs, ["examples"]}.
|
||||
|
||||
%% try to override the dialyzer 'race_conditions' defined in the top-level dir,
|
||||
%% but it doesn't work
|
||||
{dialyzer, [{warnings, [unmatched_returns, error_handling]}
|
||||
]}.
|
||||
|
||||
{deps, [ {jsx, {git, "https://github.com/talentdeficit/jsx", {tag, "v3.1.0"}}}
|
||||
]}.
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ELVIS_VERSION='1.0.0-emqx-2'
|
||||
|
||||
elvis_version="${2:-$ELVIS_VERSION}"
|
||||
|
||||
echo "elvis -v: $elvis_version"
|
||||
|
||||
if [ ! -f ./elvis ] || [ "$(./elvis -v | grep -oE '[1-9]+\.[0-9]+\.[0-9]+\-emqx-[0-9]+')" != "$elvis_version" ]; then
|
||||
curl -fLO "https://github.com/emqx/elvis/releases/download/$elvis_version/elvis"
|
||||
chmod +x ./elvis
|
||||
fi
|
||||
|
||||
./elvis rock --config elvis.config
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{application, emqx_resource,
|
||||
[{description, "An OTP application"},
|
||||
{vsn, "0.1.0"},
|
||||
{registered, []},
|
||||
{mod, {emqx_resource_app, []}},
|
||||
{applications,
|
||||
[kernel,
|
||||
stdlib,
|
||||
gproc,
|
||||
hocon,
|
||||
jsx
|
||||
]},
|
||||
{env,[]},
|
||||
{modules, []},
|
||||
|
||||
{licenses, ["Apache 2.0"]},
|
||||
{links, []}
|
||||
]}.
|
|
@ -0,0 +1,310 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% 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_resource).
|
||||
|
||||
-include("emqx_resource.hrl").
|
||||
-include("emqx_resource_utils.hrl").
|
||||
|
||||
%% APIs for resource types
|
||||
|
||||
-export([ get_type/1
|
||||
, list_types/0
|
||||
, list_types_verbose/0
|
||||
]).
|
||||
|
||||
-export([ discover_resource_mods/0
|
||||
, is_resource_mod/1
|
||||
, call_instance/2
|
||||
]).
|
||||
|
||||
-export([ query_success/1
|
||||
, query_failed/1
|
||||
]).
|
||||
|
||||
%% APIs for instances
|
||||
|
||||
-export([ parse_config/2
|
||||
, resource_type_from_str/1
|
||||
]).
|
||||
|
||||
%% Sync resource instances and files
|
||||
%% provisional solution: rpc:multical to all the nodes for creating/updating/removing
|
||||
%% todo: replicate operations
|
||||
-export([ create/3 %% store the config and start the instance
|
||||
, create_dry_run/3 %% run start/2, health_check/2 and stop/1 sequentially
|
||||
, update/4 %% update the config, stop the old instance and start the new one
|
||||
%% it will create a new resource when the id does not exist
|
||||
, remove/1 %% remove the config and stop the instance
|
||||
]).
|
||||
|
||||
%% Calls to the callback module with current resource state
|
||||
%% They also save the state after the call finished (except query/2,3).
|
||||
-export([ restart/1 %% restart the instance.
|
||||
, health_check/1 %% verify if the resource is working normally
|
||||
, stop/1 %% stop the instance
|
||||
, query/2 %% query the instance
|
||||
, query/3 %% query the instance with after_query()
|
||||
]).
|
||||
|
||||
%% Direct calls to the callback module
|
||||
-export([ call_start/3 %% start the instance
|
||||
, call_health_check/3 %% verify if the resource is working normally
|
||||
, call_stop/3 %% stop the instance
|
||||
, call_config_merge/4 %% merge the config when updating
|
||||
, call_jsonify/2
|
||||
, call_api_reply_format/2
|
||||
]).
|
||||
|
||||
-export([ list_instances/0 %% list all the instances, id only.
|
||||
, list_instances_verbose/0 %% list all the instances
|
||||
, get_instance/1 %% return the data of the instance
|
||||
, get_instance_by_type/1 %% return all the instances of the same resource type
|
||||
, load_instances_from_dir/1 %% load instances from a directory
|
||||
, load_instance_from_file/1 %% load an instance from a config file
|
||||
, load_instance_from_config/1 %% load an instance from a map or json-string config
|
||||
% , dependents/1
|
||||
% , inc_counter/2 %% increment the counter of the instance
|
||||
% , inc_counter/3 %% increment the counter by a given integer
|
||||
]).
|
||||
|
||||
-define(EXT, "*.spec").
|
||||
|
||||
-optional_callbacks([ on_query/4
|
||||
, on_health_check/2
|
||||
, on_config_merge/3
|
||||
, on_jsonify/1
|
||||
, on_api_reply_format/1
|
||||
]).
|
||||
|
||||
-callback on_api_reply_format(resource_data()) -> jsx:json_term().
|
||||
|
||||
-callback on_config_merge(resource_config(), resource_config(), term()) -> resource_config().
|
||||
|
||||
-callback on_jsonify(resource_config()) -> jsx:json_term().
|
||||
|
||||
%% when calling emqx_resource:start/1
|
||||
-callback on_start(instance_id(), resource_config()) ->
|
||||
{ok, resource_state()} | {error, Reason :: term()}.
|
||||
|
||||
%% when calling emqx_resource:stop/1
|
||||
-callback on_stop(instance_id(), resource_state()) -> term().
|
||||
|
||||
%% when calling emqx_resource:query/3
|
||||
-callback on_query(instance_id(), Request :: term(), after_query(), resource_state()) -> term().
|
||||
|
||||
%% when calling emqx_resource:health_check/2
|
||||
-callback on_health_check(instance_id(), resource_state()) ->
|
||||
{ok, resource_state()} | {error, Reason:: term(), resource_state()}.
|
||||
|
||||
%% load specs and return the loaded resources this time.
|
||||
-spec list_types_verbose() -> [resource_spec()].
|
||||
list_types_verbose() ->
|
||||
[get_spec(Mod) || Mod <- list_types()].
|
||||
|
||||
-spec list_types() -> [module()].
|
||||
list_types() ->
|
||||
discover_resource_mods().
|
||||
|
||||
-spec get_type(module()) -> {ok, resource_spec()} | {error, not_found}.
|
||||
get_type(Mod) ->
|
||||
case is_resource_mod(Mod) of
|
||||
true -> {ok, get_spec(Mod)};
|
||||
false -> {error, not_found}
|
||||
end.
|
||||
|
||||
-spec get_spec(module()) -> resource_spec().
|
||||
get_spec(Mod) ->
|
||||
maps:put(<<"resource_type">>, Mod, Mod:emqx_resource_schema()).
|
||||
|
||||
-spec discover_resource_mods() -> [module()].
|
||||
discover_resource_mods() ->
|
||||
[Mod || {Mod, _} <- code:all_loaded(), is_resource_mod(Mod)].
|
||||
|
||||
-spec is_resource_mod(module()) -> boolean().
|
||||
is_resource_mod(Mod) ->
|
||||
erlang:function_exported(Mod, emqx_resource_schema, 0).
|
||||
|
||||
-spec query_success(after_query()) -> ok.
|
||||
query_success(undefined) -> ok;
|
||||
query_success({{OnSucc, Args}, _}) ->
|
||||
safe_apply(OnSucc, Args).
|
||||
|
||||
-spec query_failed(after_query()) -> ok.
|
||||
query_failed(undefined) -> ok;
|
||||
query_failed({_, {OnFailed, Args}}) ->
|
||||
safe_apply(OnFailed, Args).
|
||||
|
||||
%% =================================================================================
|
||||
%% APIs for resource instances
|
||||
%% =================================================================================
|
||||
-spec create(instance_id(), resource_type(), resource_config()) ->
|
||||
{ok, resource_data()} | {error, Reason :: term()}.
|
||||
create(InstId, ResourceType, Config) ->
|
||||
?CLUSTER_CALL(call_instance, [InstId, {create, InstId, ResourceType, Config}], {ok, _}).
|
||||
|
||||
-spec create_dry_run(instance_id(), resource_type(), resource_config()) ->
|
||||
ok | {error, Reason :: term()}.
|
||||
create_dry_run(InstId, ResourceType, Config) ->
|
||||
?CLUSTER_CALL(call_instance, [InstId, {create_dry_run, InstId, ResourceType, Config}]).
|
||||
|
||||
-spec update(instance_id(), resource_type(), resource_config(), term()) ->
|
||||
{ok, resource_data()} | {error, Reason :: term()}.
|
||||
update(InstId, ResourceType, Config, Params) ->
|
||||
?CLUSTER_CALL(call_instance, [InstId, {update, InstId, ResourceType, Config, Params}], {ok, _}).
|
||||
|
||||
-spec remove(instance_id()) -> ok | {error, Reason :: term()}.
|
||||
remove(InstId) ->
|
||||
?CLUSTER_CALL(call_instance, [InstId, {remove, InstId}]).
|
||||
|
||||
-spec query(instance_id(), Request :: term()) -> Result :: term().
|
||||
query(InstId, Request) ->
|
||||
query(InstId, Request, undefined).
|
||||
|
||||
%% same to above, also defines what to do when the Module:on_query success or failed
|
||||
%% it is the duty of the Moudle to apply the `after_query()` functions.
|
||||
-spec query(instance_id(), Request :: term(), after_query()) -> Result :: term().
|
||||
query(InstId, Request, AfterQuery) ->
|
||||
case get_instance(InstId) of
|
||||
{ok, #{mod := Mod, state := ResourceState}} ->
|
||||
%% the resource state is readonly to Moudle:on_query/4
|
||||
%% and the `after_query()` functions should be thread safe
|
||||
Mod:on_query(InstId, Request, AfterQuery, ResourceState);
|
||||
{error, Reason} ->
|
||||
error({get_instance, {InstId, Reason}})
|
||||
end.
|
||||
|
||||
-spec restart(instance_id()) -> ok | {error, Reason :: term()}.
|
||||
restart(InstId) ->
|
||||
call_instance(InstId, {restart, InstId}).
|
||||
|
||||
-spec stop(instance_id()) -> ok | {error, Reason :: term()}.
|
||||
stop(InstId) ->
|
||||
call_instance(InstId, {stop, InstId}).
|
||||
|
||||
-spec health_check(instance_id()) -> ok | {error, Reason :: term()}.
|
||||
health_check(InstId) ->
|
||||
call_instance(InstId, {health_check, InstId}).
|
||||
|
||||
-spec get_instance(instance_id()) -> {ok, resource_data()} | {error, Reason :: term()}.
|
||||
get_instance(InstId) ->
|
||||
emqx_resource_instance:lookup(InstId).
|
||||
|
||||
-spec list_instances() -> [instance_id()].
|
||||
list_instances() ->
|
||||
[Id || #{id := Id} <- list_instances_verbose()].
|
||||
|
||||
-spec list_instances_verbose() -> [resource_data()].
|
||||
list_instances_verbose() ->
|
||||
emqx_resource_instance:list_all().
|
||||
|
||||
-spec get_instance_by_type(module()) -> [resource_data()].
|
||||
get_instance_by_type(ResourceType) ->
|
||||
emqx_resource_instance:lookup_by_type(ResourceType).
|
||||
|
||||
-spec load_instances_from_dir(Dir :: string()) -> ok.
|
||||
load_instances_from_dir(Dir) ->
|
||||
emqx_resource_instance:load_dir(Dir).
|
||||
|
||||
-spec load_instance_from_file(File :: string()) -> ok.
|
||||
load_instance_from_file(File) ->
|
||||
emqx_resource_instance:load_file(File).
|
||||
|
||||
-spec load_instance_from_config(binary() | map()) -> {ok, resource_data()} | {error, term()}.
|
||||
load_instance_from_config(Config) ->
|
||||
emqx_resource_instance:load_config(Config).
|
||||
|
||||
-spec call_start(instance_id(), module(), resource_config()) ->
|
||||
{ok, resource_state()} | {error, Reason :: term()}.
|
||||
call_start(InstId, Mod, Config) ->
|
||||
?SAFE_CALL(Mod:on_start(InstId, Config)).
|
||||
|
||||
-spec call_health_check(instance_id(), module(), resource_state()) ->
|
||||
{ok, resource_state()} | {error, Reason:: term(), resource_state()}.
|
||||
call_health_check(InstId, Mod, ResourceState) ->
|
||||
?SAFE_CALL(Mod:on_health_check(InstId, ResourceState)).
|
||||
|
||||
-spec call_stop(instance_id(), module(), resource_state()) -> term().
|
||||
call_stop(InstId, Mod, ResourceState) ->
|
||||
?SAFE_CALL(Mod:on_stop(InstId, ResourceState)).
|
||||
|
||||
-spec call_config_merge(module(), resource_config(), resource_config(), term()) ->
|
||||
resource_config().
|
||||
call_config_merge(Mod, OldConfig, NewConfig, Params) ->
|
||||
case erlang:function_exported(Mod, on_jsonify, 1) of
|
||||
true ->
|
||||
?SAFE_CALL(Mod:on_config_merge(OldConfig, NewConfig, Params));
|
||||
false when is_map(OldConfig), is_map(NewConfig) ->
|
||||
maps:merge(OldConfig, NewConfig);
|
||||
false ->
|
||||
NewConfig
|
||||
end.
|
||||
|
||||
-spec call_jsonify(module(), resource_config()) -> jsx:json_term().
|
||||
call_jsonify(Mod, Config) ->
|
||||
case erlang:function_exported(Mod, on_jsonify, 1) of
|
||||
false -> Config;
|
||||
true -> ?SAFE_CALL(Mod:on_jsonify(Config))
|
||||
end.
|
||||
|
||||
-spec call_api_reply_format(module(), resource_data()) -> jsx:json_term().
|
||||
call_api_reply_format(Mod, Data) ->
|
||||
case erlang:function_exported(Mod, on_api_reply_format, 1) of
|
||||
false -> emqx_resource_api:default_api_reply_format(Data);
|
||||
true -> ?SAFE_CALL(Mod:on_api_reply_format(Data))
|
||||
end.
|
||||
|
||||
-spec parse_config(resource_type(), binary() | term()) ->
|
||||
{ok, resource_config()} | {error, term()}.
|
||||
parse_config(ResourceType, RawConfig) when is_binary(RawConfig) ->
|
||||
case hocon:binary(RawConfig, #{format => richmap}) of
|
||||
{ok, MapConfig} ->
|
||||
do_parse_config(ResourceType, MapConfig);
|
||||
Error -> Error
|
||||
end;
|
||||
parse_config(ResourceType, RawConfigTerm) ->
|
||||
parse_config(ResourceType, jsx:encode(#{config => RawConfigTerm})).
|
||||
|
||||
-spec do_parse_config(resource_type(), map()) -> {ok, resource_config()} | {error, term()}.
|
||||
do_parse_config(ResourceType, MapConfig) ->
|
||||
case ?SAFE_CALL(hocon_schema:generate(ResourceType, MapConfig)) of
|
||||
{error, Reason} -> {error, Reason};
|
||||
Config ->
|
||||
InstConf = maps:from_list(proplists:get_value(config, Config)),
|
||||
{ok, InstConf}
|
||||
end.
|
||||
|
||||
%% =================================================================================
|
||||
|
||||
-spec resource_type_from_str(string()) -> {ok, resource_type()} | {error, term()}.
|
||||
resource_type_from_str(ResourceType) ->
|
||||
try Mod = list_to_existing_atom(str(ResourceType)),
|
||||
case emqx_resource:is_resource_mod(Mod) of
|
||||
true -> {ok, Mod};
|
||||
false -> {error, {invalid_resource, Mod}}
|
||||
end
|
||||
catch error:badarg ->
|
||||
{error, {resource_not_found, ResourceType}}
|
||||
end.
|
||||
|
||||
call_instance(InstId, Query) ->
|
||||
emqx_resource_instance:hash_call(InstId, Query).
|
||||
|
||||
safe_apply(Func, Args) ->
|
||||
?SAFE_CALL(erlang:apply(Func, Args)).
|
||||
|
||||
str(S) when is_binary(S) -> binary_to_list(S);
|
||||
str(S) when is_list(S) -> S.
|
|
@ -0,0 +1,78 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% 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_resource_api).
|
||||
|
||||
-export([ get_all/3
|
||||
, get/3
|
||||
, put/3
|
||||
, delete/3
|
||||
]).
|
||||
|
||||
-export([default_api_reply_format/1]).
|
||||
|
||||
get_all(Mod, _Binding, _Params) ->
|
||||
{200, #{code => 0, data =>
|
||||
[format_data(Mod, Data) || Data <- emqx_resource:list_instances_verbose()]}}.
|
||||
|
||||
get(Mod, #{id := Id}, _Params) ->
|
||||
case emqx_resource:get_instance(stringnify(Id)) of
|
||||
{ok, Data} ->
|
||||
{200, #{code => 0, data => format_data(Mod, Data)}};
|
||||
{error, not_found} ->
|
||||
{404, #{code => 102, message => {resource_instance_not_found, stringnify(Id)}}}
|
||||
end.
|
||||
|
||||
put(Mod, #{id := Id}, Params) ->
|
||||
ConfigParams = proplists:get_value(<<"config">>, Params),
|
||||
ResourceTypeStr = proplists:get_value(<<"resource_type">>, Params, #{}),
|
||||
case emqx_resource:resource_type_from_str(ResourceTypeStr) of
|
||||
{ok, ResourceType} ->
|
||||
do_put(Mod, stringnify(Id), ConfigParams, ResourceType, Params);
|
||||
{error, Reason} ->
|
||||
{404, #{code => 102, message => stringnify(Reason)}}
|
||||
end.
|
||||
|
||||
do_put(Mod, Id, ConfigParams, ResourceType, Params) ->
|
||||
case emqx_resource:parse_config(ResourceType, ConfigParams) of
|
||||
{ok, Config} ->
|
||||
case emqx_resource:update(Id, ResourceType, Config, Params) of
|
||||
{ok, Data} ->
|
||||
{200, #{code => 0, data => format_data(Mod, Data)}};
|
||||
{error, Reason} ->
|
||||
{500, #{code => 102, message => stringnify(Reason)}}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
{400, #{code => 108, message => stringnify(Reason)}}
|
||||
end.
|
||||
|
||||
delete(_Mod, #{id := Id}, _Params) ->
|
||||
case emqx_resource:remove(stringnify(Id)) of
|
||||
ok -> {200, #{code => 0, data => #{}}};
|
||||
{error, Reason} ->
|
||||
{500, #{code => 102, message => stringnify(Reason)}}
|
||||
end.
|
||||
|
||||
format_data(Mod, Data) ->
|
||||
emqx_resource:call_api_reply_format(Mod, Data).
|
||||
|
||||
default_api_reply_format(#{id := Id, mod := Mod, status := Status, config := Config}) ->
|
||||
#{node => node(), id => Id, status => Status, resource_type => Mod,
|
||||
config => emqx_resource:call_jsonify(Mod, Config)}.
|
||||
|
||||
stringnify(Bin) when is_binary(Bin) -> Bin;
|
||||
stringnify(Str) when is_list(Str) -> list_to_binary(Str);
|
||||
stringnify(Reason) ->
|
||||
iolist_to_binary(io_lib:format("~p", [Reason])).
|
|
@ -0,0 +1,33 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% 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_resource_app).
|
||||
|
||||
-behaviour(application).
|
||||
|
||||
-include("emqx_resource.hrl").
|
||||
|
||||
-emqx_plugin(?MODULE).
|
||||
|
||||
-export([start/2, stop/1]).
|
||||
|
||||
start(_StartType, _StartArgs) ->
|
||||
emqx_resource_sup:start_link().
|
||||
|
||||
stop(_State) ->
|
||||
ok.
|
||||
|
||||
%% internal functions
|
|
@ -0,0 +1,315 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% 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_resource_instance).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-include("emqx_resource.hrl").
|
||||
-include("emqx_resource_utils.hrl").
|
||||
|
||||
-export([start_link/2]).
|
||||
|
||||
%% load resource instances from *.conf files
|
||||
-export([ load_dir/1
|
||||
, load_file/1
|
||||
, load_config/1
|
||||
, lookup/1
|
||||
, list_all/0
|
||||
, lookup_by_type/1
|
||||
, create_local/3
|
||||
]).
|
||||
|
||||
-export([ hash_call/2
|
||||
, hash_call/3
|
||||
]).
|
||||
|
||||
%% gen_server Callbacks
|
||||
-export([ init/1
|
||||
, handle_call/3
|
||||
, handle_cast/2
|
||||
, handle_info/2
|
||||
, terminate/2
|
||||
, code_change/3
|
||||
]).
|
||||
|
||||
-record(state, {worker_pool, worker_id}).
|
||||
|
||||
-type state() :: #state{}.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Start the registry
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
start_link(Pool, Id) ->
|
||||
gen_server:start_link({local, proc_name(?MODULE, Id)},
|
||||
?MODULE, {Pool, Id}, []).
|
||||
|
||||
%% call the worker by the hash of resource-instance-id, to make sure we always handle
|
||||
%% operations on the same instance in the same worker.
|
||||
hash_call(InstId, Request) ->
|
||||
hash_call(InstId, Request, infinity).
|
||||
|
||||
hash_call(InstId, Request, Timeout) ->
|
||||
gen_server:call(pick(InstId), Request, Timeout).
|
||||
|
||||
-spec lookup(instance_id()) -> {ok, resource_data()} | {error, Reason :: term()}.
|
||||
lookup(InstId) ->
|
||||
case ets:lookup(emqx_resource_instance, InstId) of
|
||||
[] -> {error, not_found};
|
||||
[{_, Data}] -> {ok, Data#{id => InstId}}
|
||||
end.
|
||||
|
||||
force_lookup(InstId) ->
|
||||
{ok, Data} = lookup(InstId),
|
||||
Data.
|
||||
|
||||
-spec list_all() -> [resource_data()].
|
||||
list_all() ->
|
||||
[Data#{id => Id} || {Id, Data} <- ets:tab2list(emqx_resource_instance)].
|
||||
|
||||
-spec lookup_by_type(module()) -> [resource_data()].
|
||||
lookup_by_type(ResourceType) ->
|
||||
[Data || #{mod := Mod} = Data <- list_all()
|
||||
, Mod =:= ResourceType].
|
||||
|
||||
-spec load_dir(Dir :: string()) -> ok.
|
||||
load_dir(Dir) ->
|
||||
lists:foreach(fun load_file/1, filelib:wildcard(filename:join([Dir, "*.conf"]))).
|
||||
|
||||
load_file(File) ->
|
||||
case ?SAFE_CALL(hocon_token:read(File)) of
|
||||
{error, Reason} ->
|
||||
logger:error("load resource from ~p failed: ~p", [File, Reason]);
|
||||
RawConfig ->
|
||||
case load_config(RawConfig) of
|
||||
{ok, Data} ->
|
||||
logger:debug("loaded resource instance from file: ~p, data: ~p",
|
||||
[File, Data]);
|
||||
{error, Reason} ->
|
||||
logger:error("load resource from ~p failed: ~p", [File, Reason])
|
||||
end
|
||||
end.
|
||||
|
||||
-spec load_config(binary() | map()) -> {ok, resource_data()} | {error, term()}.
|
||||
load_config(RawConfig) when is_binary(RawConfig) ->
|
||||
case hocon:binary(RawConfig, #{format => map}) of
|
||||
{ok, ConfigTerm} -> load_config(ConfigTerm);
|
||||
Error -> Error
|
||||
end;
|
||||
|
||||
load_config(#{<<"id">> := Id, <<"resource_type">> := ResourceTypeStr} = Config) ->
|
||||
MapConfig = maps:get(<<"config">>, Config, #{}),
|
||||
case emqx_resource:resource_type_from_str(ResourceTypeStr) of
|
||||
{ok, ResourceType} -> parse_and_load_config(Id, ResourceType, MapConfig);
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
parse_and_load_config(InstId, ResourceType, MapConfig) ->
|
||||
case emqx_resource:parse_config(ResourceType, MapConfig) of
|
||||
{ok, InstConf} -> create_local(InstId, ResourceType, InstConf);
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
create_local(InstId, ResourceType, InstConf) ->
|
||||
case hash_call(InstId, {create, InstId, ResourceType, InstConf}, 15000) of
|
||||
{ok, Data} -> {ok, Data};
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
save_config_to_disk(InstId, ResourceType, Config) ->
|
||||
%% TODO: send an event to the config handler, and the hander (single process)
|
||||
%% will dump configs for all instances (from an ETS table) to a file.
|
||||
file:write_file(filename:join([emqx_data_dir(), binary_to_list(InstId) ++ ".conf"]),
|
||||
jsx:encode(#{id => InstId, resource_type => ResourceType,
|
||||
config => emqx_resource:call_jsonify(ResourceType, Config)})).
|
||||
|
||||
emqx_data_dir() ->
|
||||
"data".
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% gen_server callbacks
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
-spec init({atom(), integer()}) ->
|
||||
{ok, State :: state()} | {ok, State :: state(), timeout() | hibernate | {continue, term()}} |
|
||||
{stop, Reason :: term()} | ignore.
|
||||
init({Pool, Id}) ->
|
||||
true = gproc_pool:connect_worker(Pool, {Pool, Id}),
|
||||
{ok, #state{worker_pool = Pool, worker_id = Id}}.
|
||||
|
||||
handle_call({create, InstId, ResourceType, Config}, _From, State) ->
|
||||
{reply, do_create(InstId, ResourceType, Config), State};
|
||||
|
||||
handle_call({create_dry_run, InstId, ResourceType, Config}, _From, State) ->
|
||||
{reply, do_create_dry_run(InstId, ResourceType, Config), State};
|
||||
|
||||
handle_call({update, InstId, ResourceType, Config, Params}, _From, State) ->
|
||||
{reply, do_update(InstId, ResourceType, Config, Params), State};
|
||||
|
||||
handle_call({remove, InstId}, _From, State) ->
|
||||
{reply, do_remove(InstId), State};
|
||||
|
||||
handle_call({restart, InstId}, _From, State) ->
|
||||
{reply, do_restart(InstId), State};
|
||||
|
||||
handle_call({stop, InstId}, _From, State) ->
|
||||
{reply, do_stop(InstId), State};
|
||||
|
||||
handle_call({health_check, InstId}, _From, State) ->
|
||||
{reply, do_health_check(InstId), State};
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
logger:error("Received unexpected call: ~p", [Req]),
|
||||
{reply, ignored, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{worker_pool = Pool, worker_id = Id}) ->
|
||||
gproc_pool:disconnect_worker(Pool, {Pool, Id}).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
%% suppress the race condition check, as these functions are protected in gproc workers
|
||||
-dialyzer({nowarn_function, [do_update/4, do_create/3, do_restart/1, do_stop/1, do_health_check/1]}).
|
||||
do_update(InstId, ResourceType, NewConfig, Params) ->
|
||||
case lookup(InstId) of
|
||||
{ok, #{mod := ResourceType, state := ResourceState, config := OldConfig}} ->
|
||||
Config = emqx_resource:call_config_merge(ResourceType, OldConfig,
|
||||
NewConfig, Params),
|
||||
case do_create_dry_run(InstId, ResourceType, Config) of
|
||||
ok ->
|
||||
do_remove(ResourceType, InstId, ResourceState),
|
||||
do_create(InstId, ResourceType, Config);
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
{ok, #{mod := Mod}} when Mod =/= ResourceType ->
|
||||
{error, updating_to_incorrect_resource_type};
|
||||
{error, not_found} ->
|
||||
do_create(InstId, ResourceType, NewConfig)
|
||||
end.
|
||||
|
||||
do_create(InstId, ResourceType, Config) ->
|
||||
case lookup(InstId) of
|
||||
{ok, _} -> {error, already_created};
|
||||
_ ->
|
||||
case emqx_resource:call_start(InstId, ResourceType, Config) of
|
||||
{ok, ResourceState} ->
|
||||
ets:insert(emqx_resource_instance, {InstId,
|
||||
#{mod => ResourceType, config => Config,
|
||||
state => ResourceState, status => stopped}}),
|
||||
_ = do_health_check(InstId),
|
||||
case save_config_to_disk(InstId, ResourceType, Config) of
|
||||
ok -> {ok, force_lookup(InstId)};
|
||||
{error, Reason} ->
|
||||
logger:error("save config for ~p resource ~p to disk failed: ~p",
|
||||
[ResourceType, InstId, Reason]),
|
||||
{error, Reason}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
logger:error("start ~s resource ~s failed: ~p", [ResourceType, InstId, Reason]),
|
||||
{error, Reason}
|
||||
end
|
||||
end.
|
||||
|
||||
do_create_dry_run(InstId, ResourceType, Config) ->
|
||||
case emqx_resource:call_start(InstId, ResourceType, Config) of
|
||||
{ok, ResourceState0} ->
|
||||
Return = case emqx_resource:call_health_check(InstId, ResourceType, ResourceState0) of
|
||||
{ok, ResourceState1} -> ok;
|
||||
{error, Reason, ResourceState1} ->
|
||||
{error, Reason}
|
||||
end,
|
||||
_ = emqx_resource:call_stop(InstId, ResourceType, ResourceState1),
|
||||
Return;
|
||||
{error, Reason} ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
do_remove(InstId) ->
|
||||
case lookup(InstId) of
|
||||
{ok, #{mod := Mod, state := ResourceState}} ->
|
||||
do_remove(Mod, InstId, ResourceState);
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
do_remove(Mod, InstId, ResourceState) ->
|
||||
_ = emqx_resource:call_stop(InstId, Mod, ResourceState),
|
||||
ets:delete(emqx_resource_instance, InstId),
|
||||
ok.
|
||||
|
||||
do_restart(InstId) ->
|
||||
case lookup(InstId) of
|
||||
{ok, #{mod := Mod, state := ResourceState, config := Config} = Data} ->
|
||||
_ = emqx_resource:call_stop(InstId, Mod, ResourceState),
|
||||
case emqx_resource:call_start(InstId, Mod, Config) of
|
||||
{ok, ResourceState} ->
|
||||
ets:insert(emqx_resource_instance,
|
||||
{InstId, Data#{state => ResourceState, status => started}}),
|
||||
ok;
|
||||
{error, Reason} ->
|
||||
ets:insert(emqx_resource_instance, {InstId, Data#{status => stopped}}),
|
||||
{error, Reason}
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
do_stop(InstId) ->
|
||||
case lookup(InstId) of
|
||||
{ok, #{mod := Mod, state := ResourceState} = Data} ->
|
||||
_ = emqx_resource:call_stop(InstId, Mod, ResourceState),
|
||||
ets:insert(emqx_resource_instance, {InstId, Data#{status => stopped}}),
|
||||
ok;
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
do_health_check(InstId) ->
|
||||
case lookup(InstId) of
|
||||
{ok, #{mod := Mod, state := ResourceState0} = Data} ->
|
||||
case emqx_resource:call_health_check(InstId, Mod, ResourceState0) of
|
||||
{ok, ResourceState1} ->
|
||||
ets:insert(emqx_resource_instance,
|
||||
{InstId, Data#{status => started, state => ResourceState1}}),
|
||||
ok;
|
||||
{error, Reason, ResourceState1} ->
|
||||
logger:error("health check for ~p failed: ~p", [InstId, Reason]),
|
||||
ets:insert(emqx_resource_instance,
|
||||
{InstId, Data#{status => stopped, state => ResourceState1}}),
|
||||
{error, Reason}
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% internal functions
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
proc_name(Mod, Id) ->
|
||||
list_to_atom(lists:concat([Mod, "_", Id])).
|
||||
|
||||
pick(InstId) ->
|
||||
gproc_pool:pick_worker(emqx_resource_instance, InstId).
|
|
@ -0,0 +1,58 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% 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_resource_sup).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([init/1]).
|
||||
|
||||
-define(RESOURCE_INST_MOD, emqx_resource_instance).
|
||||
-define(POOL_SIZE, 64). %% set a very large pool size in case all the workers busy
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
TabOpts = [named_table, set, public, {read_concurrency, true}],
|
||||
_ = ets:new(emqx_resource_instance, TabOpts),
|
||||
|
||||
SupFlags = #{strategy => one_for_one, intensity => 10, period => 10},
|
||||
Pool = ?RESOURCE_INST_MOD,
|
||||
Mod = ?RESOURCE_INST_MOD,
|
||||
ensure_pool(Pool, hash, [{size, ?POOL_SIZE}]),
|
||||
{ok, {SupFlags, [
|
||||
begin
|
||||
ensure_pool_worker(Pool, {Pool, Idx}, Idx),
|
||||
#{id => {Mod, Idx},
|
||||
start => {Mod, start_link, [Pool, Idx]},
|
||||
restart => transient,
|
||||
shutdown => 5000, type => worker, modules => [Mod]}
|
||||
end || Idx <- lists:seq(1, ?POOL_SIZE)]}}.
|
||||
|
||||
%% internal functions
|
||||
ensure_pool(Pool, Type, Opts) ->
|
||||
try gproc_pool:new(Pool, Type, Opts)
|
||||
catch
|
||||
error:exists -> ok
|
||||
end.
|
||||
|
||||
ensure_pool_worker(Pool, Name, Slot) ->
|
||||
try gproc_pool:add_worker(Pool, Name, Slot)
|
||||
catch
|
||||
error:exists -> ok
|
||||
end.
|
|
@ -0,0 +1,114 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% 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_resource_transform).
|
||||
|
||||
-include_lib("syntax_tools/include/merl.hrl").
|
||||
|
||||
-export([parse_transform/2]).
|
||||
|
||||
parse_transform(Forms, _Opts) ->
|
||||
Mod = hd([M || {attribute, _, module, M} <- Forms]),
|
||||
AST = trans(Mod, proplists:delete(eof, Forms)),
|
||||
debug_print(Mod, AST),
|
||||
AST.
|
||||
|
||||
-ifdef(RESOURCE_DEBUG).
|
||||
|
||||
debug_print(Mod, Ts) ->
|
||||
{ok, Io} = file:open("./" ++ atom_to_list(Mod) ++ ".trans.erl", [write]),
|
||||
_ = do_debug_print(Io, Ts),
|
||||
file:close(Io).
|
||||
|
||||
do_debug_print(Io, Ts) when is_list(Ts) ->
|
||||
lists:foreach(fun(T) -> do_debug_print(Io, T) end, Ts);
|
||||
do_debug_print(Io, T) ->
|
||||
io:put_chars(Io, erl_prettypr:format(merl:tree(T))),
|
||||
io:nl(Io).
|
||||
-else.
|
||||
debug_print(_Mod, _AST) ->
|
||||
ok.
|
||||
-endif.
|
||||
|
||||
trans(Mod, Forms) ->
|
||||
forms(Mod, Forms) ++ [erl_syntax:revert(erl_syntax:eof_marker())].
|
||||
|
||||
forms(Mod, [F0 | Fs0]) ->
|
||||
case form(Mod, F0) of
|
||||
{CurrForm, AppendedForms} ->
|
||||
CurrForm ++ forms(Mod, Fs0) ++ AppendedForms;
|
||||
{AHeadForms, CurrForm, AppendedForms} ->
|
||||
AHeadForms ++ CurrForm ++ forms(Mod, Fs0) ++ AppendedForms
|
||||
end;
|
||||
forms(_, []) -> [].
|
||||
|
||||
form(Mod, Form) ->
|
||||
case Form of
|
||||
?Q("-emqx_resource_api_path('@Path').") ->
|
||||
{fix_spec_attrs() ++ fix_api_attrs(Mod, erl_syntax:concrete(Path))
|
||||
++ fix_api_exports(),
|
||||
[],
|
||||
fix_spec_funcs(Mod) ++ fix_api_funcs(Mod)};
|
||||
_ ->
|
||||
%io:format("---other form: ~p~n", [Form]),
|
||||
{[], [Form], []}
|
||||
end.
|
||||
|
||||
fix_spec_attrs() ->
|
||||
[ ?Q("-export([emqx_resource_schema/0]).")
|
||||
, ?Q("-export([structs/0]).")
|
||||
, ?Q("-behaviour(hocon_schema).")
|
||||
].
|
||||
fix_spec_funcs(_Mod) ->
|
||||
[ (?Q("emqx_resource_schema() -> <<\"demo_swagger_schema\">>."))
|
||||
, ?Q("structs() -> [\"config\"].")
|
||||
].
|
||||
|
||||
fix_api_attrs(Mod, Path) ->
|
||||
BaseName = atom_to_list(Mod),
|
||||
[erl_syntax:revert(
|
||||
erl_syntax:attribute(?Q("rest_api"), [
|
||||
erl_syntax:abstract(#{
|
||||
name => list_to_atom(Act ++ "_" ++ BaseName),
|
||||
method => Method,
|
||||
path => mk_path(Path, WithId),
|
||||
func => Func,
|
||||
descr => Act ++ " the " ++ BaseName})]))
|
||||
|| {Act, Method, WithId, Func} <- [
|
||||
{"list", 'GET', noid, api_get_all},
|
||||
{"get", 'GET', id, api_get},
|
||||
{"update", 'PUT', id, api_put},
|
||||
{"delete", 'DELETE', id, api_delete}]].
|
||||
|
||||
fix_api_exports() ->
|
||||
[?Q("-export([api_get_all/2, api_get/2, api_put/2, api_delete/2]).")].
|
||||
|
||||
fix_api_funcs(Mod) ->
|
||||
[erl_syntax:revert(?Q(
|
||||
"api_get_all(Binding, Params) ->
|
||||
emqx_resource_api:get_all('@Mod@', Binding, Params).")),
|
||||
erl_syntax:revert(?Q(
|
||||
"api_get(Binding, Params) ->
|
||||
emqx_resource_api:get('@Mod@', Binding, Params).")),
|
||||
erl_syntax:revert(?Q(
|
||||
"api_put(Binding, Params) ->
|
||||
emqx_resource_api:put('@Mod@', Binding, Params).")),
|
||||
erl_syntax:revert(?Q(
|
||||
"api_delete(Binding, Params) ->
|
||||
emqx_resource_api:delete('@Mod@', Binding, Params)."))
|
||||
].
|
||||
|
||||
mk_path(Path, id) -> string:trim(Path, trailing, "/") ++ "/:bin:id";
|
||||
mk_path(Path, noid) -> Path.
|
|
@ -0,0 +1,16 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% 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_resource_uitils).
|
|
@ -0,0 +1,63 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% 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_resource_validator).
|
||||
|
||||
-export([ min/2
|
||||
, max/2
|
||||
, equals/2
|
||||
, enum/1
|
||||
, required/1
|
||||
]).
|
||||
|
||||
max(Type, Max) ->
|
||||
limit(Type, '=<', Max).
|
||||
|
||||
min(Type, Min) ->
|
||||
limit(Type, '>=', Min).
|
||||
|
||||
equals(Type, Expected) ->
|
||||
limit(Type, '==', Expected).
|
||||
|
||||
enum(Items) ->
|
||||
fun(Value) ->
|
||||
return(lists:member(Value, Items),
|
||||
err_limit({enum, {is_member_of, Items}, {got, Value}}))
|
||||
end.
|
||||
|
||||
required(ErrMsg) ->
|
||||
fun(undefined) -> {error, ErrMsg};
|
||||
(_) -> ok
|
||||
end.
|
||||
|
||||
limit(Type, Op, Expected) ->
|
||||
L = len(Type),
|
||||
fun(Value) ->
|
||||
Got = L(Value),
|
||||
return(erlang:Op(Got, Expected),
|
||||
err_limit({Type, {Op, Expected}, {got, Got}}))
|
||||
end.
|
||||
|
||||
len(array) -> fun erlang:length/1;
|
||||
len(string) -> fun string:length/1;
|
||||
len(_Type) -> fun(Val) -> Val end.
|
||||
|
||||
err_limit({Type, {Op, Expected}, {got, Got}}) ->
|
||||
io_lib:format("Expect the ~s value ~s ~p but got: ~p", [Type, Op, Expected, Got]).
|
||||
|
||||
return(true, _) -> ok;
|
||||
return(false, Error) ->
|
||||
{error, Error}.
|
|
@ -37,5 +37,5 @@ retainer.max_payload_size = 1MB
|
|||
## - 30m: 30 minutes
|
||||
## - 20s: 20 seconds
|
||||
##
|
||||
## Defaut: 0
|
||||
## Default: 0
|
||||
retainer.expiry_interval = 0
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
{profiles,
|
||||
[{test,
|
||||
[{deps,
|
||||
[{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}},
|
||||
[
|
||||
{emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.3"}}}]}
|
||||
]}
|
||||
]}.
|
||||
|
|
|
@ -32,7 +32,7 @@ rule_engine.ignore_sys_message = on
|
|||
##
|
||||
## QoS-Level: qos0/qos1/qos2
|
||||
|
||||
#rule_engine.events.client_connected = on, qos1
|
||||
#rule_engine.events.client_connected = "on, qos1"
|
||||
rule_engine.events.client_connected = off
|
||||
rule_engine.events.client_disconnected = off
|
||||
rule_engine.events.session_subscribed = off
|
||||
|
|
|
@ -2554,23 +2554,15 @@ start_apps() ->
|
|||
[start_apps(App, SchemaFile, ConfigFile) ||
|
||||
{App, SchemaFile, ConfigFile}
|
||||
<- [{emqx, deps_path(emqx, "priv/emqx.schema"),
|
||||
deps_path(emqx, "etc/emqx.conf")},
|
||||
deps_path(emqx, "etc/emqx.conf.rendered")},
|
||||
{emqx_rule_engine, local_path("priv/emqx_rule_engine.schema"),
|
||||
local_path("etc/emqx_rule_engine.conf")}]].
|
||||
|
||||
start_apps(App, SchemaFile, ConfigFile) ->
|
||||
read_schema_configs(App, SchemaFile, ConfigFile),
|
||||
emqx_ct_helpers:read_schema_configs(SchemaFile, ConfigFile),
|
||||
set_special_configs(App),
|
||||
{ok, _} = application:ensure_all_started(App).
|
||||
|
||||
read_schema_configs(App, SchemaFile, ConfigFile) ->
|
||||
ct:pal("Read configs - SchemaFile: ~p, ConfigFile: ~p", [SchemaFile, ConfigFile]),
|
||||
Schema = cuttlefish_schema:files([SchemaFile]),
|
||||
Conf = conf_parse:file(ConfigFile),
|
||||
NewConfig = cuttlefish_generator:map(Schema, Conf),
|
||||
Vals = proplists:get_value(App, NewConfig, []),
|
||||
[application:set_env(App, Par, Value) || {Par, Value} <- Vals].
|
||||
|
||||
deps_path(App, RelativePath) ->
|
||||
%% Note: not lib_dir because etc dir is not sym-link-ed to _build dir
|
||||
%% but priv dir is
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
##
|
||||
## Value: IP:Port | Port
|
||||
##
|
||||
## Examples: 1884, 127.0.0.1:1884, ::1:1884
|
||||
## Examples: 1884, "127.0.0.1:1884", "::1:1884"
|
||||
mqtt.sn.port = 1884
|
||||
|
||||
## The duration that emqx-sn broadcast ADVERTISE message through.
|
||||
|
@ -37,8 +37,8 @@ mqtt.sn.idle_timeout = 30s
|
|||
## The pre-defined topic name corresponding to the pre-defined topic id of N.
|
||||
## Note that the pre-defined topic id of 0 is reserved.
|
||||
mqtt.sn.predefined.topic.0 = reserved
|
||||
mqtt.sn.predefined.topic.1 = /predefined/topic/name/hello
|
||||
mqtt.sn.predefined.topic.2 = /predefined/topic/name/nice
|
||||
mqtt.sn.predefined.topic.1 = "/predefined/topic/name/hello"
|
||||
mqtt.sn.predefined.topic.2 = "/predefined/topic/name/nice"
|
||||
|
||||
## Default username for MQTT-SN. This parameter is optional. If specified,
|
||||
## emq-sn will connect EMQ core with this username. It is useful if any auth
|
||||
|
|
|
@ -1,23 +1,19 @@
|
|||
%%-*- mode: erlang -*-
|
||||
%% emqx_sn config mapping
|
||||
{mapping, "mqtt.sn.port", "emqx_sn.port", [
|
||||
{default, "1884"},
|
||||
{datatype, string}
|
||||
{default, 1884},
|
||||
{datatype, [integer, ip]}
|
||||
]}.
|
||||
|
||||
{translation, "emqx_sn.port", fun(Conf) ->
|
||||
case re:split(cuttlefish:conf_get("mqtt.sn.port", Conf, ""), ":", [{return, list}]) of
|
||||
[Port] ->
|
||||
{{0,0,0,0}, list_to_integer(Port)};
|
||||
Tokens ->
|
||||
Port = lists:last(Tokens),
|
||||
IP = case inet:parse_address(lists:flatten(lists:join(":", Tokens -- [Port]))) of
|
||||
{error, Reason} ->
|
||||
throw({invalid_ip_address, Reason});
|
||||
{ok, X} -> X
|
||||
end,
|
||||
Port1 = list_to_integer(Port),
|
||||
{IP, Port1}
|
||||
case cuttlefish:conf_get("mqtt.sn.port", Conf, undefined) of
|
||||
Port when is_integer(Port) ->
|
||||
{{0,0,0,0}, Port};
|
||||
{Ip, Port} ->
|
||||
case inet:parse_address(Ip) of
|
||||
{ok ,R} -> {R, Port};
|
||||
_ -> {Ip, Port}
|
||||
end
|
||||
end
|
||||
end}.
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
{plugins, [rebar3_proper]}.
|
||||
|
||||
{deps,
|
||||
[{esockd, {git, "https://github.com/emqx/esockd", {tag, "5.7.4"}}},
|
||||
{cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v3.0.0"}}}
|
||||
[{esockd, {git, "https://github.com/emqx/esockd", {tag, "5.7.4"}}}
|
||||
]}.
|
||||
|
||||
{edoc_opts, [{preprocess, true}]}.
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
## The Port that stomp listener will bind.
|
||||
##
|
||||
## Value: Port
|
||||
stomp.listener = 61613
|
||||
stomp.listener.port = 61613
|
||||
|
||||
## The acceptor pool for stomp listener.
|
||||
##
|
||||
|
@ -28,22 +28,22 @@ stomp.listener.max_connections = 512
|
|||
## Path to the file containing the user's private PEM-encoded key.
|
||||
##
|
||||
## Value: File
|
||||
## stomp.listener.keyfile = etc/certs/key.pem
|
||||
## stomp.listener.keyfile = "etc/certs/key.pem"
|
||||
|
||||
## Path to a file containing the user certificate.
|
||||
##
|
||||
## Value: File
|
||||
## stomp.listener.certfile = etc/certs/cert.pem
|
||||
## stomp.listener.certfile = "etc/certs/cert.pem"
|
||||
|
||||
## Path to the file containing PEM-encoded CA certificates.
|
||||
##
|
||||
## Value: File
|
||||
## stomp.listener.cacertfile = etc/certs/cacert.pem
|
||||
## stomp.listener.cacertfile = "etc/certs/cacert.pem"
|
||||
|
||||
## See: 'listener.ssl.<name>.dhfile' in emq.conf
|
||||
##
|
||||
## Value: File
|
||||
## stomp.listener.dhfile = etc/certs/dh-params.pem
|
||||
## stomp.listener.dhfile = "etc/certs/dh-params.pem"
|
||||
|
||||
## See: 'listener.ssl.<name>.verify' in emq.conf
|
||||
##
|
||||
|
@ -59,7 +59,7 @@ stomp.listener.max_connections = 512
|
|||
##
|
||||
## Value: String, seperated by ','
|
||||
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
|
||||
## stomp.listener.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
|
||||
## stomp.listener.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
|
||||
|
||||
## SSL Handshake timeout.
|
||||
##
|
||||
|
@ -69,7 +69,7 @@ stomp.listener.max_connections = 512
|
|||
## See: 'listener.ssl.<name>.ciphers' in emq.conf
|
||||
##
|
||||
## Value: Ciphers
|
||||
## stomp.listener.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
|
||||
## stomp.listener.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"
|
||||
|
||||
## See: 'listener.ssl.<name>.secure_renegotiate' in emq.conf
|
||||
##
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%%-*- mode: erlang -*-
|
||||
%% emqx_stomp config mapping
|
||||
|
||||
{mapping, "stomp.listener", "emqx_stomp.listener", [
|
||||
{mapping, "stomp.listener.port", "emqx_stomp.listener", [
|
||||
{default, 61613},
|
||||
{datatype, [integer, ip]}
|
||||
]}.
|
||||
|
@ -72,7 +72,7 @@
|
|||
]}.
|
||||
|
||||
{translation, "emqx_stomp.listener", fun(Conf) ->
|
||||
Port = cuttlefish:conf_get("stomp.listener", Conf),
|
||||
Port = cuttlefish:conf_get("stomp.listener.port", Conf),
|
||||
Acceptors = cuttlefish:conf_get("stomp.listener.acceptors", Conf),
|
||||
MaxConnections = cuttlefish:conf_get("stomp.listener.max_connections", Conf),
|
||||
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_stomp,
|
||||
[{description, "EMQ X Stomp Protocol Plugin"},
|
||||
{vsn, "4.3.0"}, % strict semver, bump manually!
|
||||
{vsn, "4.4.0"}, % strict semver, bump manually!
|
||||
{modules, []},
|
||||
{registered, [emqx_stomp_sup]},
|
||||
{applications, [kernel,stdlib]},
|
||||
|
|
|
@ -5,16 +5,16 @@
|
|||
## Webhook URL
|
||||
##
|
||||
## Value: String
|
||||
web.hook.url = http://127.0.0.1:80
|
||||
web.hook.url = "http://127.0.0.1:80"
|
||||
|
||||
## HTTP Headers
|
||||
##
|
||||
## Example:
|
||||
## 1. web.hook.headers.content-type = application/json
|
||||
## 2. web.hook.headers.accept = *
|
||||
## 1. web.hook.headers.content-type = "application/json"
|
||||
## 2. web.hook.headers.accept = "*"
|
||||
##
|
||||
## Value: String
|
||||
web.hook.headers.content-type = application/json
|
||||
web.hook.headers.content-type = "application/json"
|
||||
|
||||
## The encoding format of the payload field in the HTTP body
|
||||
## The payload field only appears in the on_message_publish and on_message_delivered actions
|
||||
|
@ -63,15 +63,15 @@ web.hook.pool_size = 32
|
|||
##
|
||||
## Format:
|
||||
## web.hook.rule.<HookName>.<No> = <Spec>
|
||||
#web.hook.rule.client.connect.1 = {"action": "on_client_connect"}
|
||||
#web.hook.rule.client.connack.1 = {"action": "on_client_connack"}
|
||||
#web.hook.rule.client.connected.1 = {"action": "on_client_connected"}
|
||||
#web.hook.rule.client.disconnected.1 = {"action": "on_client_disconnected"}
|
||||
#web.hook.rule.client.subscribe.1 = {"action": "on_client_subscribe"}
|
||||
#web.hook.rule.client.unsubscribe.1 = {"action": "on_client_unsubscribe"}
|
||||
#web.hook.rule.session.subscribed.1 = {"action": "on_session_subscribed"}
|
||||
#web.hook.rule.session.unsubscribed.1 = {"action": "on_session_unsubscribed"}
|
||||
#web.hook.rule.session.terminated.1 = {"action": "on_session_terminated"}
|
||||
#web.hook.rule.message.publish.1 = {"action": "on_message_publish"}
|
||||
#web.hook.rule.message.delivered.1 = {"action": "on_message_delivered"}
|
||||
#web.hook.rule.message.acked.1 = {"action": "on_message_acked"}
|
||||
#web.hook.rule.client.connect.1 = "{"action": "on_client_connect"}"
|
||||
#web.hook.rule.client.connack.1 = "{"action": "on_client_connack"}"
|
||||
#web.hook.rule.client.connected.1 = "{"action": "on_client_connected"}"
|
||||
#web.hook.rule.client.disconnected.1 = "{"action": "on_client_disconnected"}"
|
||||
#web.hook.rule.client.subscribe.1 = "{"action": "on_client_subscribe"}"
|
||||
#web.hook.rule.client.unsubscribe.1 = "{"action": "on_client_unsubscribe"}"
|
||||
#web.hook.rule.session.subscribed.1 = "{"action": "on_session_subscribed"}"
|
||||
#web.hook.rule.session.unsubscribed.1 = "{"action": "on_session_unsubscribed"}"
|
||||
#web.hook.rule.session.terminated.1 = "{"action": "on_session_terminated"}"
|
||||
#web.hook.rule.message.publish.1 = "{"action": "on_message_publish"}"
|
||||
#web.hook.rule.message.delivered.1 = "{"action": "on_message_delivered"}"
|
||||
#web.hook.rule.message.acked.1 = ""{"action": "on_message_acked"}"
|
||||
|
|
|
@ -15,4 +15,4 @@
|
|||
warnings_as_errors, deprecated_functions]}.
|
||||
{cover_enabled, true}.
|
||||
{cover_opts, [verbose]}.
|
||||
{cover_export_enabled, true}.
|
||||
{cover_export_enabled, true}.
|
94
bin/emqx
94
bin/emqx
|
@ -20,8 +20,8 @@ mkdir -p "$RUNNER_LOG_DIR"
|
|||
# Make sure data directory exists
|
||||
mkdir -p "$RUNNER_DATA_DIR"
|
||||
|
||||
# cuttlefish try to read environment variables starting with "EMQX_"
|
||||
export CUTTLEFISH_ENV_OVERRIDE_PREFIX='EMQX_'
|
||||
# hocon try to read environment variables starting with "EMQX_"
|
||||
export HOCON_ENV_OVERRIDE_PREFIX='EMQX_'
|
||||
|
||||
relx_usage() {
|
||||
command="$1"
|
||||
|
@ -123,9 +123,6 @@ fi
|
|||
# Echo to stderr on errors
|
||||
echoerr() { echo "$@" 1>&2; }
|
||||
|
||||
# By default, use cuttlefish to generate app.config and vm.args
|
||||
CUTTLEFISH="${USE_CUTTLEFISH:-yes}"
|
||||
|
||||
SED_REPLACE="sed -i "
|
||||
case $(sed --help 2>&1) in
|
||||
*GNU*) SED_REPLACE="sed -i ";;
|
||||
|
@ -202,53 +199,45 @@ generate_config() {
|
|||
## changing the config 'log.rotation.size'
|
||||
rm -rf "${RUNNER_LOG_DIR}"/*.siz
|
||||
|
||||
if [ "$CUTTLEFISH" != "yes" ]; then
|
||||
# Note: we have added a parameter '-vm_args' to this. It
|
||||
# appears redundant but it is not! the erlang vm allows us to
|
||||
# access all arguments to the erl command EXCEPT '-args_file',
|
||||
# so in order to get access to this file location from within
|
||||
# the vm, we need to pass it in twice.
|
||||
CONFIG_ARGS=" -config $RUNNER_ETC_DIR/app.config -args_file $RUNNER_ETC_DIR/vm.args -vm_args $RUNNER_ETC_DIR/vm.args "
|
||||
else
|
||||
EMQX_LICENSE_CONF_OPTION=""
|
||||
if [ "${EMQX_LICENSE_CONF:-}" != "" ]; then
|
||||
EMQX_LICENSE_CONF_OPTION="-i ${EMQX_LICENSE_CONF}"
|
||||
fi
|
||||
## todo: include license conf option to hocon escript
|
||||
## EMQX_LICENSE_CONF_OPTION=""
|
||||
## if [ "${EMQX_LICENSE_CONF:-}" != "" ]; then
|
||||
## EMQX_LICENSE_CONF_OPTION="-i ${EMQX_LICENSE_CONF}"
|
||||
## fi
|
||||
|
||||
set +e
|
||||
# shellcheck disable=SC2086
|
||||
CUTTLEFISH_OUTPUT="$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/cuttlefish -v -i "$REL_DIR"/emqx.schema $EMQX_LICENSE_CONF_OPTION -c "$RUNNER_ETC_DIR"/emqx.conf -d "$RUNNER_DATA_DIR"/configs generate)"
|
||||
# shellcheck disable=SC2181
|
||||
RESULT=$?
|
||||
set -e
|
||||
if [ $RESULT -gt 0 ]; then
|
||||
echo "$CUTTLEFISH_OUTPUT"
|
||||
exit $RESULT
|
||||
fi
|
||||
# print override from environment variables (EMQX_*)
|
||||
echo "$CUTTLEFISH_OUTPUT" | sed -e '$d'
|
||||
CONFIG_ARGS=$(echo "$CUTTLEFISH_OUTPUT" | tail -n 1)
|
||||
|
||||
## Merge cuttlefish generated *.args into the vm.args
|
||||
CUTTLE_GEN_ARG_FILE=$(echo "$CONFIG_ARGS" | sed -n 's/^.*\(vm_args[[:space:]]\)//p' | awk '{print $1}')
|
||||
TMP_ARG_FILE="$RUNNER_DATA_DIR/configs/vm.args.tmp"
|
||||
cp "$RUNNER_ETC_DIR/vm.args" "$TMP_ARG_FILE"
|
||||
echo "" >> "$TMP_ARG_FILE"
|
||||
echo "-pa ${REL_DIR}/consolidated" >> "$TMP_ARG_FILE"
|
||||
sed '/^#/d' "$CUTTLE_GEN_ARG_FILE" | sed '/^$/d' | while IFS='' read -r ARG_LINE || [ -n "$ARG_LINE" ]; do
|
||||
ARG_KEY=$(echo "$ARG_LINE" | awk '{$NF="";print}')
|
||||
ARG_VALUE=$(echo "$ARG_LINE" | awk '{print $NF}')
|
||||
TMP_ARG_VALUE=$(grep "^$ARG_KEY" "$TMP_ARG_FILE" | awk '{print $NF}')
|
||||
if [ "$ARG_VALUE" != "$TMP_ARG_VALUE" ] ; then
|
||||
if [ -n "$TMP_ARG_VALUE" ]; then
|
||||
sh -c "$SED_REPLACE 's/^$ARG_KEY.*$/$ARG_LINE/' $TMP_ARG_FILE"
|
||||
else
|
||||
echo "$ARG_LINE" >> "$TMP_ARG_FILE"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
mv -f "$TMP_ARG_FILE" "$CUTTLE_GEN_ARG_FILE"
|
||||
set +e
|
||||
# shellcheck disable=SC2086
|
||||
HOCON_OUTPUT="$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/hocon -s emqx_schema -c "$RUNNER_ETC_DIR"/emqx.conf -d "$RUNNER_DATA_DIR"/configs generate)"
|
||||
# shellcheck disable=SC2181
|
||||
RESULT=$?
|
||||
set -e
|
||||
if [ $RESULT -gt 0 ]; then
|
||||
echo "$HOCON_OUTPUT"
|
||||
exit $RESULT
|
||||
fi
|
||||
# print override from environment variables (EMQX_*)
|
||||
echo "$HOCON_OUTPUT" | sed -e '$d'
|
||||
CONFIG_ARGS=$(echo "$HOCON_OUTPUT" | tail -n 1)
|
||||
|
||||
## Merge hocon generated *.args into the vm.args
|
||||
HOCON_GEN_ARG_FILE=$(echo "$CONFIG_ARGS" | sed -n 's/^.*\(vm_args[[:space:]]\)//p' | awk '{print $1}')
|
||||
TMP_ARG_FILE="$RUNNER_DATA_DIR/configs/vm.args.tmp"
|
||||
cp "$RUNNER_ETC_DIR/vm.args" "$TMP_ARG_FILE"
|
||||
echo "" >> "$TMP_ARG_FILE"
|
||||
echo "-pa ${REL_DIR}/consolidated" >> "$TMP_ARG_FILE"
|
||||
sed '/^#/d' "$HOCON_GEN_ARG_FILE" | sed '/^$/d' | while IFS='' read -r ARG_LINE || [ -n "$ARG_LINE" ]; do
|
||||
ARG_KEY=$(echo "$ARG_LINE" | awk '{$NF="";print}')
|
||||
ARG_VALUE=$(echo "$ARG_LINE" | awk '{print $NF}')
|
||||
TMP_ARG_VALUE=$(grep "^$ARG_KEY" "$TMP_ARG_FILE" | awk '{print $NF}')
|
||||
if [ "$ARG_VALUE" != "$TMP_ARG_VALUE" ] ; then
|
||||
if [ -n "$TMP_ARG_VALUE" ]; then
|
||||
sh -c "$SED_REPLACE 's/^$ARG_KEY.*$/$ARG_LINE/' $TMP_ARG_FILE"
|
||||
else
|
||||
echo "$ARG_LINE" >> "$TMP_ARG_FILE"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
mv -f "$TMP_ARG_FILE" "$HOCON_GEN_ARG_FILE"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
if ! relx_nodetool chkconfig $CONFIG_ARGS; then
|
||||
|
@ -303,7 +292,8 @@ if [ -z "$NAME_ARG" ]; then
|
|||
NODENAME="$(grep -E '^-name' "$LATEST_VM_ARGS" | awk '{print $2}')"
|
||||
else
|
||||
# for boot commands, inspect emqx.conf for node name
|
||||
NODENAME=$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/cuttlefish -i "$REL_DIR"/emqx.schema -c "$RUNNER_ETC_DIR"/emqx.conf get node.name)
|
||||
# todo: use get command from hocon escript
|
||||
NODENAME="$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/hocon -s emqx_schema -c "$RUNNER_ETC_DIR"/emqx.conf get node.name | tr -d \")"
|
||||
fi
|
||||
fi
|
||||
if [ -z "$NODENAME" ]; then
|
||||
|
@ -329,7 +319,7 @@ PIPE_DIR="${PIPE_DIR:-/$RUNNER_DATA_DIR/${WHOAMI}_erl_pipes/$NAME/}"
|
|||
COOKIE="${EMQX_NODE_COOKIE:-}"
|
||||
if [ -z "$COOKIE" ]; then
|
||||
if [ "$IS_BOOT_COMMAND" = 'yes' ]; then
|
||||
COOKIE=$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/cuttlefish -i "$REL_DIR"/emqx.schema -c "$RUNNER_ETC_DIR"/emqx.conf get node.cookie)
|
||||
COOKIE="$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/hocon -s emqx_schema -c "$RUNNER_ETC_DIR"/emqx.conf get node.cookie | tr -d \")"
|
||||
else
|
||||
# shellcheck disable=SC2012,SC2086
|
||||
LATEST_VM_ARGS="$(ls -t $RUNNER_DATA_DIR/configs/vm.*.args | head -1)"
|
||||
|
|
|
@ -5,4 +5,5 @@
|
|||
{emqx_retainer, {{enable_plugin_emqx_retainer}}}.
|
||||
{emqx_telemetry, {{enable_plugin_emqx_telemetry}}}.
|
||||
{emqx_rule_engine, {{enable_plugin_emqx_rule_engine}}}.
|
||||
{emqx_resource, {{enable_plugin_emqx_resource}}}.
|
||||
{emqx_bridge_mqtt, {{enable_plugin_emqx_bridge_mqtt}}}.
|
||||
|
|
188
etc/emqx.conf
188
etc/emqx.conf
|
@ -56,7 +56,7 @@ cluster.autoclean = 5m
|
|||
## Node list of the cluster.
|
||||
##
|
||||
## Value: String
|
||||
## cluster.static.seeds = emqx1@127.0.0.1,emqx2@127.0.0.1
|
||||
## cluster.static.seeds = "emqx1@127.0.0.1,emqx2@127.0.0.1"
|
||||
|
||||
##--------------------------------------------------------------------
|
||||
## Cluster using IP Multicast.
|
||||
|
@ -64,19 +64,19 @@ cluster.autoclean = 5m
|
|||
## IP Multicast Address.
|
||||
##
|
||||
## Value: IP Address
|
||||
## cluster.mcast.addr = 239.192.0.1
|
||||
## cluster.mcast.addr = "239.192.0.1"
|
||||
|
||||
## Multicast Ports.
|
||||
##
|
||||
## Value: Port List
|
||||
## cluster.mcast.ports = 4369,4370
|
||||
## cluster.mcast.ports = "4369,4370"
|
||||
|
||||
## Multicast Iface.
|
||||
##
|
||||
## Value: Iface Address
|
||||
##
|
||||
## Default: 0.0.0.0
|
||||
## cluster.mcast.iface = 0.0.0.0
|
||||
## Default: "0.0.0.0"
|
||||
## cluster.mcast.iface = "0.0.0.0"
|
||||
|
||||
## Multicast Ttl.
|
||||
##
|
||||
|
@ -107,7 +107,14 @@ cluster.autoclean = 5m
|
|||
## Etcd server list, seperated by ','.
|
||||
##
|
||||
## Value: String
|
||||
## cluster.etcd.server = http://127.0.0.1:2379
|
||||
## cluster.etcd.server = "http://127.0.0.1:2379"
|
||||
|
||||
## Etcd api version
|
||||
##
|
||||
## Value: Enum
|
||||
## - v2
|
||||
## - v3
|
||||
## cluster.etcd.version = v3
|
||||
|
||||
## The prefix helps build nodes path in etcd. Each node in the cluster
|
||||
## will create a path in etcd: v2/keys/<prefix>/<cluster.name>/<node.name>
|
||||
|
@ -125,18 +132,18 @@ cluster.autoclean = 5m
|
|||
## Path to a file containing the client's private PEM-encoded key.
|
||||
##
|
||||
## Value: File
|
||||
## cluster.etcd.ssl.keyfile = {{ platform_etc_dir }}/certs/client-key.pem
|
||||
## cluster.etcd.ssl.keyfile = "{{ platform_etc_dir }}/certs/client-key.pem"
|
||||
|
||||
## The path to a file containing the client's certificate.
|
||||
##
|
||||
## Value: File
|
||||
## cluster.etcd.ssl.certfile = {{ platform_etc_dir }}/certs/client.pem
|
||||
## cluster.etcd.ssl.certfile = "{{ platform_etc_dir }}/certs/client.pem"
|
||||
|
||||
## Path to the file containing PEM-encoded CA certificates. The CA certificates
|
||||
## are used during server authentication and when building the client certificate chain.
|
||||
##
|
||||
## Value: File
|
||||
## cluster.etcd.ssl.cacertfile = {{ platform_etc_dir }}/certs/ca.pem
|
||||
## cluster.etcd.ssl.cacertfile = "{{ platform_etc_dir }}/certs/ca.pem"
|
||||
|
||||
##--------------------------------------------------------------------
|
||||
## Cluster using Kubernetes
|
||||
|
@ -144,7 +151,7 @@ cluster.autoclean = 5m
|
|||
## Kubernetes API server list, seperated by ','.
|
||||
##
|
||||
## Value: String
|
||||
## cluster.k8s.apiserver = http://10.110.111.204:8080
|
||||
## cluster.k8s.apiserver = "http://10.110.111.204:8080"
|
||||
|
||||
## The service name helps lookup EMQ nodes in the cluster.
|
||||
##
|
||||
|
@ -184,17 +191,17 @@ cluster.autoclean = 5m
|
|||
## Value: <name>@<host>
|
||||
##
|
||||
## Default: emqx@127.0.0.1
|
||||
node.name = emqx@127.0.0.1
|
||||
node.name = "emqx@127.0.0.1"
|
||||
|
||||
## Cookie for distributed node communication.
|
||||
##
|
||||
## Value: String
|
||||
node.cookie = emqxsecretcookie
|
||||
node.cookie = "emqxsecretcookie"
|
||||
|
||||
## Data dir for the node
|
||||
##
|
||||
## Value: Folder
|
||||
node.data_dir = {{ platform_data_dir }}
|
||||
node.data_dir = "{{ platform_data_dir }}"
|
||||
|
||||
## Heartbeat monitoring of an Erlang runtime system. Comment the line to disable
|
||||
## heartbeat, or set the value as 'on'
|
||||
|
@ -271,14 +278,14 @@ node.global_gc_interval = 15m
|
|||
## Crash dump log file.
|
||||
##
|
||||
## Value: Log file
|
||||
node.crash_dump = {{ platform_log_dir }}/crash.dump
|
||||
node.crash_dump = "{{ platform_log_dir }}/crash.dump"
|
||||
|
||||
## Specify SSL Options in the file if using SSL for Erlang Distribution.
|
||||
##
|
||||
## Value: File
|
||||
##
|
||||
## vm.args: -ssl_dist_optfile <File>
|
||||
## node.ssl_dist_optfile = {{ platform_etc_dir }}/ssl_dist.conf
|
||||
## node.ssl_dist_optfile = "{{ platform_etc_dir }}/ssl_dist.conf"
|
||||
|
||||
## Sets the net_kernel tick time. TickTime is specified in seconds.
|
||||
## Notice that all communicating nodes are to have the same TickTime
|
||||
|
@ -419,10 +426,17 @@ log.to = file
|
|||
## Default: warning
|
||||
log.level = warning
|
||||
|
||||
## Timezone offset to display in logs
|
||||
## Value:
|
||||
## - "system" use system zone
|
||||
## - "utc" for Universal Coordinated Time (UTC)
|
||||
## - "+hh:mm" or "-hh:mm" for a specified offset
|
||||
log.time_offset = system
|
||||
|
||||
## The dir for log files.
|
||||
##
|
||||
## Value: Folder
|
||||
log.dir = {{ platform_log_dir }}
|
||||
log.dir = "{{ platform_log_dir }}"
|
||||
|
||||
## The log filename for logs of level specified in "log.level".
|
||||
##
|
||||
|
@ -460,7 +474,7 @@ log.file = emqx.log
|
|||
##
|
||||
## Value: on | off
|
||||
## Default: on
|
||||
log.rotation = on
|
||||
log.rotation.enable = on
|
||||
|
||||
## Maximum size of each log file.
|
||||
##
|
||||
|
@ -579,7 +593,7 @@ log.rotation.count = 5
|
|||
## Value: MaxBurstCount,TimeWindow
|
||||
## Default: disabled
|
||||
##
|
||||
#log.burst_limit = 20000, 1s
|
||||
#log.burst_limit = "20000, 1s"
|
||||
|
||||
## CONFIG_SECTION_END=logger ===================================================
|
||||
|
||||
|
@ -591,42 +605,42 @@ log.rotation.count = 5
|
|||
## Notice: Disable the option in production deployment!
|
||||
##
|
||||
## Value: true | false
|
||||
allow_anonymous = true
|
||||
acl.allow_anonymous = true
|
||||
|
||||
## Allow or deny if no ACL rules matched.
|
||||
##
|
||||
## Value: allow | deny
|
||||
acl_nomatch = allow
|
||||
acl.acl_nomatch = allow
|
||||
|
||||
## Default ACL File.
|
||||
##
|
||||
## Value: File Name
|
||||
acl_file = {{ platform_etc_dir }}/acl.conf
|
||||
acl.acl_file = "{{ platform_etc_dir }}/acl.conf"
|
||||
|
||||
## Whether to enable ACL cache.
|
||||
##
|
||||
## If enabled, ACLs roles for each client will be cached in the memory
|
||||
##
|
||||
## Value: on | off
|
||||
enable_acl_cache = on
|
||||
acl.enable_acl_cache = on
|
||||
|
||||
## The maximum count of ACL entries can be cached for a client.
|
||||
##
|
||||
## Value: Integer greater than 0
|
||||
## Default: 32
|
||||
acl_cache_max_size = 32
|
||||
acl.acl_cache_max_size = 32
|
||||
|
||||
## The time after which an ACL cache entry will be deleted
|
||||
##
|
||||
## Value: Duration
|
||||
## Default: 1 minute
|
||||
acl_cache_ttl = 1m
|
||||
acl.acl_cache_ttl = 1m
|
||||
|
||||
## The action when acl check reject current operation
|
||||
##
|
||||
## Value: ignore | disconnect
|
||||
## Default: ignore
|
||||
acl_deny_action = ignore
|
||||
acl.acl_deny_action = ignore
|
||||
|
||||
## Specify the global flapping detect policy.
|
||||
## The value is a string composed of flapping threshold, duration and banned interval.
|
||||
|
@ -635,7 +649,7 @@ acl_deny_action = ignore
|
|||
## 3. banned interval: the banned interval if a flapping is detected.
|
||||
##
|
||||
## Value: Integer,Duration,Duration
|
||||
flapping_detect_policy = 30, 1m, 5m
|
||||
acl.flapping_detect_policy = "30, 1m, 5m"
|
||||
|
||||
##--------------------------------------------------------------------
|
||||
## MQTT Protocol
|
||||
|
@ -732,7 +746,7 @@ zone.external.acl_deny_action = ignore
|
|||
## messages | bytes passed through.
|
||||
##
|
||||
## Numbers delimited by `|'. Zero or negative is to disable.
|
||||
zone.external.force_gc_policy = 16000|16MB
|
||||
zone.external.force_gc_policy = "16000|16MB"
|
||||
|
||||
## Max message queue length and total heap size to force shutdown
|
||||
## connection/session process.
|
||||
|
@ -742,9 +756,9 @@ zone.external.force_gc_policy = 16000|16MB
|
|||
## Numbers delimited by `|'. Zero or negative is to disable.
|
||||
##
|
||||
## Default:
|
||||
## - 10000|64MB on ARCH_64 system
|
||||
## - 1000|32MB on ARCH_32 sytem
|
||||
#zone.external.force_shutdown_policy = 10000|64MB
|
||||
## - "10000|64MB" on ARCH_64 system
|
||||
## - "1000|32MB" on ARCH_32 sytem
|
||||
#zone.external.force_shutdown_policy = "10000|64MB"
|
||||
|
||||
## Maximum MQTT packet size allowed.
|
||||
##
|
||||
|
@ -850,7 +864,7 @@ zone.external.max_mqueue_len = 1000
|
|||
## are treated equal
|
||||
##
|
||||
## Priority number [1-255]
|
||||
## Example: topic/1=10,topic/2=8
|
||||
## Example: "topic/1=10,topic/2=8"
|
||||
## NOTE: comma and equal signs are not allowed for priority topic names
|
||||
## NOTE: messages for topics not in the priority table are treated as
|
||||
## either highest or lowest priority depending on the configured
|
||||
|
@ -877,13 +891,13 @@ zone.external.enable_flapping_detect = off
|
|||
##
|
||||
## Value: Number,Duration
|
||||
## Example: 100 messages per 10 seconds.
|
||||
#zone.external.rate_limit.conn_messages_in = 100,10s
|
||||
#zone.external.rate_limit.conn_messages_in = "100,10s"
|
||||
|
||||
## Bytes limit for a external MQTT connections.
|
||||
##
|
||||
## Value: Number,Duration
|
||||
## Example: 100KB incoming per 10 seconds.
|
||||
#zone.external.rate_limit.conn_bytes_in = 100KB,10s
|
||||
#zone.external.rate_limit.conn_bytes_in = "100KB,10s"
|
||||
|
||||
## Whether to alarm the congested connections.
|
||||
##
|
||||
|
@ -914,16 +928,16 @@ zone.external.enable_flapping_detect = off
|
|||
##
|
||||
## Value: Number, Duration
|
||||
##
|
||||
## Example: 100 messaegs per 1s
|
||||
#zone.external.quota.conn_messages_routing = 100,1s
|
||||
## Example: 100 messages per 1s
|
||||
#zone.external.quota.conn_messages_routing = "100,1s"
|
||||
|
||||
## Messages quota for the all of external MQTT connections.
|
||||
## This value consumed by the number of recipient on a message.
|
||||
##
|
||||
## Value: Number, Duration
|
||||
##
|
||||
## Example: 200000 messaegs per 1s
|
||||
#zone.external.quota.overall_messages_routing = 200000,1s
|
||||
## Example: 200000 messages per 1s
|
||||
#zone.external.quota.overall_messages_routing = "200000,1s"
|
||||
|
||||
## All the topics will be prefixed with the mountpoint path if this option is enabled.
|
||||
##
|
||||
|
@ -932,7 +946,7 @@ zone.external.enable_flapping_detect = off
|
|||
## - %u: username
|
||||
##
|
||||
## Value: String
|
||||
## zone.external.mountpoint = devicebound/
|
||||
## zone.external.mountpoint = "devicebound/"
|
||||
|
||||
## Whether use username replace client id
|
||||
##
|
||||
|
@ -977,7 +991,7 @@ zone.internal.enable_acl = off
|
|||
zone.internal.acl_deny_action = ignore
|
||||
|
||||
## See zone.$name.force_gc_policy
|
||||
## zone.internal.force_gc_policy = 128000|128MB
|
||||
## zone.internal.force_gc_policy = "128000|128MB"
|
||||
|
||||
## See zone.$name.wildcard_subscription.
|
||||
##
|
||||
|
@ -1022,8 +1036,8 @@ zone.internal.enable_flapping_detect = off
|
|||
## See zone.$name.force_shutdown_policy
|
||||
##
|
||||
## Default:
|
||||
## - 10000|64MB on ARCH_64 system
|
||||
## - 1000|32MB on ARCH_32 sytem
|
||||
## - "10000|64MB" on ARCH_64 system
|
||||
## - "1000|32MB" on ARCH_32 sytem
|
||||
#zone.internal.force_shutdown_policy = 10000|64MB
|
||||
|
||||
## All the topics will be prefixed with the mountpoint path if this option is enabled.
|
||||
|
@ -1033,7 +1047,7 @@ zone.internal.enable_flapping_detect = off
|
|||
## - %u: username
|
||||
##
|
||||
## Value: String
|
||||
## zone.internal.mountpoint = cloudbound/
|
||||
## zone.internal.mountpoint = "cloudbound/"
|
||||
|
||||
## Whether to ignore loop delivery of messages.(for mqtt v3.1.1)
|
||||
##
|
||||
|
@ -1067,8 +1081,8 @@ zone.internal.bypass_auth_plugins = true
|
|||
##
|
||||
## Value: IP:Port | Port
|
||||
##
|
||||
## Examples: 1883, 127.0.0.1:1883, ::1:1883
|
||||
listener.tcp.external = 0.0.0.0:1883
|
||||
## Examples: 1883, "127.0.0.1:1883", "::1:1883"
|
||||
listener.tcp.external.endpoint = "0.0.0.0:1883"
|
||||
|
||||
## The acceptor pool for external MQTT/TCP listener.
|
||||
##
|
||||
|
@ -1103,8 +1117,8 @@ listener.tcp.external.zone = external
|
|||
##
|
||||
## Value: ACL Rule
|
||||
##
|
||||
## Example: allow 192.168.0.0/24
|
||||
listener.tcp.external.access.1 = allow all
|
||||
## Example: "allow 192.168.0.0/24"
|
||||
listener.tcp.external.access.1 = "allow all"
|
||||
|
||||
## Enable the Proxy Protocol V1/2 if the EMQ X cluster is deployed
|
||||
## behind HAProxy or Nginx.
|
||||
|
@ -1207,8 +1221,8 @@ listener.tcp.external.reuseaddr = true
|
|||
##
|
||||
## Value: IP:Port, Port
|
||||
##
|
||||
## Examples: 11883, 127.0.0.1:11883, ::1:11883
|
||||
listener.tcp.internal = 127.0.0.1:11883
|
||||
## Examples: 11883, "127.0.0.1:11883", "::1:11883"
|
||||
listener.tcp.internal.endpoint = "127.0.0.1:11883"
|
||||
|
||||
## The acceptor pool for internal MQTT/TCP listener.
|
||||
##
|
||||
|
@ -1304,8 +1318,8 @@ listener.tcp.internal.reuseaddr = true
|
|||
##
|
||||
## Value: IP:Port | Port
|
||||
##
|
||||
## Examples: 8883, 127.0.0.1:8883, ::1:8883
|
||||
listener.ssl.external = 8883
|
||||
## Examples: 8883, "127.0.0.1:8883", "::1:8883"
|
||||
listener.ssl.external.endpoint = 8883
|
||||
|
||||
## The acceptor pool for external MQTT/SSL listener.
|
||||
##
|
||||
|
@ -1337,7 +1351,7 @@ listener.ssl.external.zone = external
|
|||
## See: listener.tcp.$name.access
|
||||
##
|
||||
## Value: ACL Rule
|
||||
listener.ssl.external.access.1 = allow all
|
||||
listener.ssl.external.access.1 = "allow all"
|
||||
|
||||
## Enable the Proxy Protocol V1/2 if the EMQ cluster is deployed behind
|
||||
## HAProxy or Nginx.
|
||||
|
@ -1360,7 +1374,7 @@ listener.ssl.external.access.1 = allow all
|
|||
##
|
||||
## Value: String, seperated by ','
|
||||
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
|
||||
## listener.ssl.external.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
|
||||
## listener.ssl.external.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
|
||||
|
||||
## TLS Handshake timeout.
|
||||
##
|
||||
|
@ -1384,20 +1398,20 @@ listener.ssl.external.handshake_timeout = 15s
|
|||
## See: http://erlang.org/doc/man/ssl.html
|
||||
##
|
||||
## Value: File
|
||||
listener.ssl.external.keyfile = {{ platform_etc_dir }}/certs/key.pem
|
||||
listener.ssl.external.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
|
||||
|
||||
## Path to a file containing the user certificate.
|
||||
##
|
||||
## See: http://erlang.org/doc/man/ssl.html
|
||||
##
|
||||
## Value: File
|
||||
listener.ssl.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
||||
listener.ssl.external.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
|
||||
|
||||
## Path to the file containing PEM-encoded CA certificates. The CA certificates
|
||||
## are used during server authentication and when building the client certificate chain.
|
||||
##
|
||||
## Value: File
|
||||
## listener.ssl.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem
|
||||
## listener.ssl.external.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
||||
|
||||
## The Ephemeral Diffie-Helman key exchange is a very effective way of
|
||||
## ensuring Forward Secrecy by exchanging a set of keys that never hit
|
||||
|
@ -1414,7 +1428,7 @@ listener.ssl.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
|||
## openssl dhparam -out dh-params.pem 2048
|
||||
##
|
||||
## Value: File
|
||||
## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem
|
||||
## listener.ssl.external.dhfile = "{{ platform_etc_dir }}/certs/dh-params.pem"
|
||||
|
||||
## A server only does x509-path validation in mode verify_peer,
|
||||
## as it then sends a certificate request to the client (this
|
||||
|
@ -1449,14 +1463,13 @@ listener.ssl.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
|||
## Most of it was copied from Mozilla’s Server Side TLS article
|
||||
##
|
||||
## Value: Ciphers
|
||||
listener.ssl.external.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
|
||||
|
||||
listener.ssl.external.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"
|
||||
|
||||
## Ciphers for TLS PSK.
|
||||
## Note that 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot
|
||||
## be configured at the same time.
|
||||
## See 'https://tools.ietf.org/html/rfc4279#section-2'.
|
||||
#listener.ssl.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA
|
||||
#listener.ssl.external.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
|
||||
|
||||
## SSL parameter renegotiation is a feature that allows a client and a server
|
||||
## to renegotiate the parameters of the SSL connection on the fly.
|
||||
|
@ -1566,13 +1579,13 @@ listener.ssl.external.reuseaddr = true
|
|||
##
|
||||
## Value: IP:Port | Port
|
||||
##
|
||||
## Examples: 8083, 127.0.0.1:8083, ::1:8083
|
||||
listener.ws.external = 8083
|
||||
## Examples: 8083, "127.0.0.1:8083", "::1:8083"
|
||||
listener.ws.external.endpoint = 8083
|
||||
|
||||
## The path of WebSocket MQTT endpoint
|
||||
##
|
||||
## Value: URL Path
|
||||
listener.ws.external.mqtt_path = /mqtt
|
||||
listener.ws.external.mqtt_path = "/mqtt"
|
||||
|
||||
## The acceptor pool for external MQTT/WebSocket listener.
|
||||
##
|
||||
|
@ -1604,7 +1617,7 @@ listener.ws.external.zone = external
|
|||
## See: listener.ws.$name.access
|
||||
##
|
||||
## Value: ACL Rule
|
||||
listener.ws.external.access.1 = allow all
|
||||
listener.ws.external.access.1 = "allow all"
|
||||
|
||||
## If set to true, the server fails if the client does not have a Sec-WebSocket-Protocol to send.
|
||||
## Set to false for WeChat MiniApp.
|
||||
|
@ -1615,7 +1628,7 @@ listener.ws.external.access.1 = allow all
|
|||
## Supported subprotocols
|
||||
##
|
||||
## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5
|
||||
## listener.ws.external.supported_subprotocols = mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5
|
||||
## listener.ws.external.supported_subprotocols = "mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5"
|
||||
|
||||
## Specify which HTTP header for real source IP if the EMQ X cluster is
|
||||
## deployed behind NGINX or HAProxy.
|
||||
|
@ -1823,7 +1836,7 @@ listener.ws.external.allow_origin_absence = true
|
|||
## Comma separated list of allowed origin in header for websocket connection
|
||||
##
|
||||
## Value: http://url eg. local http dashboard url - http://localhost:18083, http://127.0.0.1:18083
|
||||
listener.ws.external.check_origins = http://localhost:18083, http://127.0.0.1:18083
|
||||
listener.ws.external.check_origins = "http://localhost:18083, http://127.0.0.1:18083"
|
||||
|
||||
##--------------------------------------------------------------------
|
||||
## External WebSocket/SSL listener for MQTT Protocol
|
||||
|
@ -1833,13 +1846,13 @@ listener.ws.external.check_origins = http://localhost:18083, http://127.0.0.1:18
|
|||
##
|
||||
## Value: IP:Port | Port
|
||||
##
|
||||
## Examples: 8084, 127.0.0.1:8084, ::1:8084
|
||||
listener.wss.external = 8084
|
||||
## Examples: 8084, "127.0.0.1:8084", "::1:8084"
|
||||
listener.wss.external.endpoint = 8084
|
||||
|
||||
## The path of WebSocket MQTT endpoint
|
||||
##
|
||||
## Value: URL Path
|
||||
listener.wss.external.mqtt_path = /mqtt
|
||||
listener.wss.external.mqtt_path = "/mqtt"
|
||||
|
||||
## The acceptor pool for external MQTT/WebSocket/SSL listener.
|
||||
##
|
||||
|
@ -1873,7 +1886,7 @@ listener.wss.external.zone = external
|
|||
## See: listener.tcp.$name.access.<no>
|
||||
##
|
||||
## Value: ACL Rule
|
||||
listener.wss.external.access.1 = allow all
|
||||
listener.wss.external.access.1 = "allow all"
|
||||
|
||||
## If set to true, the server fails if the client does not have a Sec-WebSocket-Protocol to send.
|
||||
## Set to false for WeChat MiniApp.
|
||||
|
@ -1884,7 +1897,7 @@ listener.wss.external.access.1 = allow all
|
|||
## Supported subprotocols
|
||||
##
|
||||
## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5
|
||||
## listener.wss.external.supported_subprotocols = mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5
|
||||
## listener.wss.external.supported_subprotocols = "mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5"
|
||||
|
||||
## Specify which HTTP header for real source IP if the EMQ X cluster is
|
||||
## deployed behind NGINX or HAProxy.
|
||||
|
@ -1918,28 +1931,28 @@ listener.wss.external.access.1 = allow all
|
|||
##
|
||||
## Value: String, seperated by ','
|
||||
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
|
||||
## listener.wss.external.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
|
||||
## listener.wss.external.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
|
||||
|
||||
## Path to the file containing the user's private PEM-encoded key.
|
||||
##
|
||||
## See: listener.ssl.$name.keyfile
|
||||
##
|
||||
## Value: File
|
||||
listener.wss.external.keyfile = {{ platform_etc_dir }}/certs/key.pem
|
||||
listener.wss.external.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
|
||||
|
||||
## Path to a file containing the user certificate.
|
||||
##
|
||||
## See: listener.ssl.$name.certfile
|
||||
##
|
||||
## Value: File
|
||||
listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
||||
listener.wss.external.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
|
||||
|
||||
## Path to the file containing PEM-encoded CA certificates.
|
||||
##
|
||||
## See: listener.ssl.$name.cacert
|
||||
##
|
||||
## Value: File
|
||||
## listener.wss.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem
|
||||
## listener.wss.external.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
||||
|
||||
## Maximum number of non-self-issued intermediate certificates that
|
||||
## can follow the peer certificate in a valid certification path.
|
||||
|
@ -1960,7 +1973,7 @@ listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
|||
## See: listener.ssl.$name.dhfile
|
||||
##
|
||||
## Value: File
|
||||
## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem
|
||||
## listener.ssl.external.dhfile = "{{ platform_etc_dir }}/certs/dh-params.pem"
|
||||
|
||||
## See: listener.ssl.$name.verify
|
||||
##
|
||||
|
@ -1975,13 +1988,13 @@ listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
|||
## See: listener.ssl.$name.ciphers
|
||||
##
|
||||
## Value: Ciphers
|
||||
listener.wss.external.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
|
||||
listener.wss.external.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"
|
||||
|
||||
## Ciphers for TLS PSK.
|
||||
## Note that 'listener.wss.external.ciphers' and 'listener.wss.external.psk_ciphers' cannot
|
||||
## be configured at the same time.
|
||||
## See 'https://tools.ietf.org/html/rfc4279#section-2'.
|
||||
## listener.wss.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA
|
||||
## listener.wss.external.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
|
||||
|
||||
## See: listener.ssl.$name.secure_renegotiate
|
||||
##
|
||||
|
@ -2140,7 +2153,7 @@ listener.wss.external.allow_origin_absence = true
|
|||
## Comma separated list of allowed origin in header for secure websocket connection
|
||||
##
|
||||
## Value: http://url eg. https://localhost:8084, https://127.0.0.1:8084
|
||||
listener.wss.external.check_origins = https://localhost:8084, https://127.0.0.1:8084
|
||||
listener.wss.external.check_origins = "https://localhost:8084, https://127.0.0.1:8084"
|
||||
|
||||
## CONFIG_SECTION_END=listeners ================================================
|
||||
|
||||
|
@ -2149,7 +2162,7 @@ listener.wss.external.check_origins = https://localhost:8084, https://127.0.0.1:
|
|||
## The file to store loaded module names.
|
||||
##
|
||||
## Value: File
|
||||
modules.loaded_file = {{ platform_data_dir }}/loaded_modules
|
||||
module.loaded_file = "{{ platform_data_dir }}/loaded_modules"
|
||||
|
||||
##--------------------------------------------------------------------
|
||||
## Presence Module
|
||||
|
@ -2165,7 +2178,7 @@ module.presence.qos = 1
|
|||
## Subscribe the Topics automatically when client connected.
|
||||
##
|
||||
## Value: String
|
||||
## module.subscription.1.topic = connected/%c/%u
|
||||
## module.subscription.1.topic = "connected/%c/%u"
|
||||
|
||||
## Qos of the proxy subscription.
|
||||
##
|
||||
|
@ -2198,8 +2211,8 @@ module.presence.qos = 1
|
|||
## Rewrite Module
|
||||
|
||||
## {rewrite, Topic, Re, Dest}
|
||||
## module.rewrite.pub.rule.1 = x/# ^x/y/(.+)$ z/y/$1
|
||||
## module.rewrite.sub.rule.1 = y/+/z/# ^y/(.+)/z/(.+)$ y/z/$2
|
||||
## module.rewrite.pub_rule.1 = "x/# ^x/y/(.+)$ z/y/$1"
|
||||
## module.rewrite.sub_rule.1 = "y/+/z/# ^y/(.+)/z/(.+)$ y/z/$2"
|
||||
|
||||
## CONFIG_SECTION_END=modules ==================================================
|
||||
|
||||
|
@ -2210,17 +2223,17 @@ module.presence.qos = 1
|
|||
## The etc dir for plugins' config.
|
||||
##
|
||||
## Value: Folder
|
||||
plugins.etc_dir = {{ platform_etc_dir }}/plugins/
|
||||
plugins.etc_dir = "{{ platform_etc_dir }}/plugins/"
|
||||
|
||||
## The file to store loaded plugin names.
|
||||
##
|
||||
## Value: File
|
||||
plugins.loaded_file = {{ platform_data_dir }}/loaded_plugins
|
||||
plugins.loaded_file = "{{ platform_data_dir }}/loaded_plugins"
|
||||
|
||||
## The directory of extension plugins.
|
||||
##
|
||||
## Value: File
|
||||
plugins.expand_plugins_dir = {{ platform_plugins_dir }}/
|
||||
plugins.expand_plugins_dir = "{{ platform_plugins_dir }}/"
|
||||
|
||||
##--------------------------------------------------------------------
|
||||
## Broker
|
||||
|
@ -2334,7 +2347,6 @@ sysmon.long_gc = 0
|
|||
## Examples:
|
||||
## - 2h: 2 hours
|
||||
## - 30m: 30 minutes
|
||||
## - 0.1s: 0.1 seconds
|
||||
## - 100ms: 100 milliseconds
|
||||
##
|
||||
## Default: 0ms
|
||||
|
@ -2426,8 +2438,8 @@ vm_mon.process_low_watermark = 60%
|
|||
## - log
|
||||
## - publish
|
||||
##
|
||||
## Default: log,publish
|
||||
alarm.actions = log,publish
|
||||
## Default: "log,publish"
|
||||
alarm.actions = "log,publish"
|
||||
|
||||
## The maximum number of deactivated alarms
|
||||
##
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
-ifndef(EMQX_ENTERPRISE).
|
||||
|
||||
-define(EMQX_RELEASE, {opensource, "4.3.3"}).
|
||||
-define(EMQX_RELEASE, {opensource, "5.0-pre"}).
|
||||
|
||||
-else.
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ dashboard.default_user.password = public
|
|||
## Value: Port
|
||||
##
|
||||
## Examples: 18083
|
||||
dashboard.listener.http = 18083
|
||||
dashboard.listener.http.port = 18083
|
||||
|
||||
## The acceptor pool for external Dashboard HTTP listener.
|
||||
##
|
||||
|
@ -50,7 +50,7 @@ dashboard.listener.http.ipv6_v6only = false
|
|||
## Value: Port
|
||||
##
|
||||
## Examples: 18084
|
||||
## dashboard.listener.https = 18084
|
||||
## dashboard.listener.https.port = 18084
|
||||
|
||||
## The acceptor pool for external Dashboard HTTPS listener.
|
||||
##
|
||||
|
@ -75,22 +75,22 @@ dashboard.listener.http.ipv6_v6only = false
|
|||
## Path to the file containing the user's private PEM-encoded key.
|
||||
##
|
||||
## Value: File
|
||||
## dashboard.listener.https.keyfile = etc/certs/key.pem
|
||||
## dashboard.listener.https.keyfile = "etc/certs/key.pem"
|
||||
|
||||
## Path to a file containing the user certificate.
|
||||
##
|
||||
## Value: File
|
||||
## dashboard.listener.https.certfile = etc/certs/cert.pem
|
||||
## dashboard.listener.https.certfile = "etc/certs/cert.pem"
|
||||
|
||||
## Path to the file containing PEM-encoded CA certificates.
|
||||
##
|
||||
## Value: File
|
||||
## dashboard.listener.https.cacertfile = etc/certs/cacert.pem
|
||||
## dashboard.listener.https.cacertfile = "etc/certs/cacert.pem"
|
||||
|
||||
## See: 'listener.ssl.<name>.dhfile' in emq.conf
|
||||
##
|
||||
## Value: File
|
||||
## dashboard.listener.https.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem
|
||||
## dashboard.listener.https.dhfile = "{{ platform_etc_dir }}/certs/dh-params.pem"
|
||||
|
||||
## See: 'listener.ssl.<name>.verify' in emq.conf
|
||||
##
|
||||
|
@ -106,12 +106,12 @@ dashboard.listener.http.ipv6_v6only = false
|
|||
##
|
||||
## Value: String, seperated by ','
|
||||
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
|
||||
## dashboard.listener.https.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
|
||||
## dashboard.listener.https.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
|
||||
|
||||
## See: 'listener.ssl.<name>.ciphers' in emq.conf
|
||||
##
|
||||
## Value: Ciphers
|
||||
## dashboard.listener.https.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
|
||||
## dashboard.listener.https.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"
|
||||
|
||||
## See: 'listener.ssl.<name>.secure_renegotiate' in emq.conf
|
||||
##
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
{override_env, "ADMIN_PASSWORD"}
|
||||
]}.
|
||||
|
||||
{mapping, "dashboard.listener.http", "emqx_dashboard.listeners", [
|
||||
{mapping, "dashboard.listener.http.port", "emqx_dashboard.listeners", [
|
||||
{datatype, integer}
|
||||
]}.
|
||||
|
||||
|
@ -38,7 +38,7 @@
|
|||
{datatype, {enum, [true, false]}}
|
||||
]}.
|
||||
|
||||
{mapping, "dashboard.listener.https", "emqx_dashboard.listeners", [
|
||||
{mapping, "dashboard.listener.https.port", "emqx_dashboard.listeners", [
|
||||
{datatype, integer}
|
||||
]}.
|
||||
|
||||
|
@ -139,7 +139,7 @@
|
|||
lists:map(
|
||||
fun(Proto) ->
|
||||
Prefix = "dashboard.listener." ++ atom_to_list(Proto),
|
||||
case cuttlefish:conf_get(Prefix, Conf, undefined) of
|
||||
case cuttlefish:conf_get(Prefix ++ ".port", Conf, undefined) of
|
||||
undefined -> [];
|
||||
Port ->
|
||||
[{Proto, Port, case Proto of
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_dashboard,
|
||||
[{description, "EMQ X Web Dashboard"},
|
||||
{vsn, "4.3.0"}, % strict semver, bump manually!
|
||||
{vsn, "4.4.0"}, % strict semver, bump manually!
|
||||
{modules, []},
|
||||
{registered, [emqx_dashboard_sup]},
|
||||
{applications, [kernel,stdlib,mnesia,minirest]},
|
||||
|
|
|
@ -13,8 +13,8 @@ telemetry.enabled = true
|
|||
##
|
||||
## Value: String
|
||||
##
|
||||
## Default: https://telemetry.emqx.io/api/telemetry
|
||||
telemetry.url = https://telemetry.emqx.io/api/telemetry
|
||||
## Default: "https://telemetry.emqx.io/api/telemetry"
|
||||
telemetry.url = "https://telemetry.emqx.io/api/telemetry"
|
||||
|
||||
## Interval for reporting telemetry data
|
||||
##
|
||||
|
@ -25,4 +25,4 @@ telemetry.url = https://telemetry.emqx.io/api/telemetry
|
|||
## -s: second
|
||||
##
|
||||
## Default: 7d
|
||||
telemetry.report_interval = 7d
|
||||
telemetry.report_interval = 7d
|
||||
|
|
|
@ -103,6 +103,10 @@
|
|||
{datatype, string}
|
||||
]}.
|
||||
|
||||
{mapping, "cluster.etcd.version", "ekka.cluster_discovery", [
|
||||
{datatype, {enum, [v2, v3]}}
|
||||
]}.
|
||||
|
||||
{mapping, "cluster.etcd.prefix", "ekka.cluster_discovery", [
|
||||
{datatype, string}
|
||||
]}.
|
||||
|
@ -180,6 +184,7 @@
|
|||
end, Options)
|
||||
end,
|
||||
[{server, string:tokens(cuttlefish:conf_get("cluster.etcd.server", Conf), ",")},
|
||||
{version, cuttlefish:conf_get("cluster.etcd.version", Conf, v3)},
|
||||
{prefix, cuttlefish:conf_get("cluster.etcd.prefix", Conf, "emqcl")},
|
||||
{node_ttl, cuttlefish:conf_get("cluster.etcd.node_ttl", Conf, 60)},
|
||||
{ssl_options, SslOpts(Conf)}];
|
||||
|
@ -467,6 +472,15 @@ end}.
|
|||
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, all]}}
|
||||
]}.
|
||||
|
||||
%% @doc Timezone offset to display in logs,
|
||||
%% "system" use system time zone
|
||||
%% "utc" for Universal Coordinated Time (UTC)
|
||||
%% "+hh:mm" or "-hh:mm" for a specified offset
|
||||
{mapping, "log.time_offset", "kernel.logger", [
|
||||
{default, "system"},
|
||||
{datatype, string}
|
||||
]}.
|
||||
|
||||
{mapping, "log.primary_log_level", "kernel.logger_level", [
|
||||
{default, warning},
|
||||
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, all]}}
|
||||
|
@ -512,7 +526,7 @@ end}.
|
|||
{datatype, {enum, [true, false]}}
|
||||
]}.
|
||||
|
||||
{mapping, "log.rotation", "kernel.logger", [
|
||||
{mapping, "log.rotation.enable", "kernel.logger", [
|
||||
{default, on},
|
||||
{datatype, flag}
|
||||
]}.
|
||||
|
@ -600,7 +614,27 @@ end}.
|
|||
{translation, "kernel.logger", fun(Conf) ->
|
||||
LogTo = cuttlefish:conf_get("log.to", Conf),
|
||||
LogLevel = cuttlefish:conf_get("log.level", Conf),
|
||||
LogType = case cuttlefish:conf_get("log.rotation", Conf) of
|
||||
LogTimeoffset =
|
||||
case cuttlefish:conf_get("log.time_offset", Conf) of
|
||||
"system" -> "";
|
||||
"utc" -> "0";
|
||||
[S, H1, H2, $:, M1, M2] = HHMM ->
|
||||
(S =:= $+ orelse S =:= $-) andalso
|
||||
try
|
||||
begin
|
||||
H = list_to_integer([H1, H2]),
|
||||
M = list_to_integer([M1, M2]),
|
||||
H >=0 andalso H =< 14 andalso
|
||||
M >= 0 andalso M =< 59
|
||||
end
|
||||
catch
|
||||
_ : _ ->
|
||||
error({"invalid_log_time_offset", HHMM})
|
||||
end andalso HHMM;
|
||||
Other ->
|
||||
error({"invalid_log_time_offset", Other})
|
||||
end,
|
||||
LogType = case cuttlefish:conf_get("log.rotation.enable", Conf) of
|
||||
true -> wrap;
|
||||
false -> halt
|
||||
end,
|
||||
|
@ -1229,7 +1263,7 @@ end}.
|
|||
%%--------------------------------------------------------------------
|
||||
%% TCP Listeners
|
||||
|
||||
{mapping, "listener.tcp.$name", "emqx.listeners", [
|
||||
{mapping, "listener.tcp.$name.endpoint", "emqx.listeners", [
|
||||
{datatype, [integer, ip]}
|
||||
]}.
|
||||
|
||||
|
@ -1336,7 +1370,7 @@ end}.
|
|||
%%--------------------------------------------------------------------
|
||||
%% SSL Listeners
|
||||
|
||||
{mapping, "listener.ssl.$name", "emqx.listeners", [
|
||||
{mapping, "listener.ssl.$name.endpoint", "emqx.listeners", [
|
||||
{datatype, [integer, ip]}
|
||||
]}.
|
||||
|
||||
|
@ -1504,7 +1538,7 @@ end}.
|
|||
%%--------------------------------------------------------------------
|
||||
%% MQTT/WebSocket Listeners
|
||||
|
||||
{mapping, "listener.ws.$name", "emqx.listeners", [
|
||||
{mapping, "listener.ws.$name.endpoint", "emqx.listeners", [
|
||||
{datatype, [integer, ip]}
|
||||
]}.
|
||||
|
||||
|
@ -1698,7 +1732,7 @@ end}.
|
|||
%%--------------------------------------------------------------------
|
||||
%% MQTT/WebSocket/SSL Listeners
|
||||
|
||||
{mapping, "listener.wss.$name", "emqx.listeners", [
|
||||
{mapping, "listener.wss.$name.endpoint", "emqx.listeners", [
|
||||
{datatype, [integer, ip]}
|
||||
]}.
|
||||
|
||||
|
@ -1954,7 +1988,6 @@ end}.
|
|||
]}.
|
||||
|
||||
{translation, "emqx.listeners", fun(Conf) ->
|
||||
|
||||
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
|
||||
|
||||
Atom = fun(undefined) -> undefined; (S) -> list_to_atom(S) end,
|
||||
|
@ -2103,7 +2136,7 @@ end}.
|
|||
|
||||
TcpListeners = fun(Type, Name) ->
|
||||
Prefix = string:join(["listener", Type, Name], "."),
|
||||
ListenOnN = case cuttlefish:conf_get(Prefix, Conf, undefined) of
|
||||
ListenOnN = case cuttlefish:conf_get(Prefix ++ ".endpoint", Conf, undefined) of
|
||||
undefined -> [];
|
||||
ListenOn -> Listen_fix(ListenOn)
|
||||
end,
|
||||
|
@ -2119,7 +2152,7 @@ end}.
|
|||
end,
|
||||
SslListeners = fun(Type, Name) ->
|
||||
Prefix = string:join(["listener", Type, Name], "."),
|
||||
case cuttlefish:conf_get(Prefix, Conf, undefined) of
|
||||
case cuttlefish:conf_get(Prefix ++ ".endpoint", Conf, undefined) of
|
||||
undefined ->
|
||||
[];
|
||||
ListenOn ->
|
||||
|
@ -2135,12 +2168,11 @@ end}.
|
|||
]
|
||||
end
|
||||
end,
|
||||
|
||||
lists:flatten([TcpListeners(Type, Name) || {["listener", Type, Name], ListenOn}
|
||||
lists:flatten([TcpListeners(Type, Name) || {["listener", Type, Name, "endpoint"], ListenOn}
|
||||
<- cuttlefish_variable:filter_by_prefix("listener.tcp", Conf)
|
||||
++ cuttlefish_variable:filter_by_prefix("listener.ws", Conf)]
|
||||
++
|
||||
[SslListeners(Type, Name) || {["listener", Type, Name], ListenOn}
|
||||
[SslListeners(Type, Name) || {["listener", Type, Name, "endpoint"], ListenOn}
|
||||
<- cuttlefish_variable:filter_by_prefix("listener.ssl", Conf)
|
||||
++ cuttlefish_variable:filter_by_prefix("listener.wss", Conf)])
|
||||
end}.
|
||||
|
|
|
@ -43,9 +43,9 @@
|
|||
, {jiffy, {git, "https://github.com/emqx/jiffy", {tag, "1.0.5"}}}
|
||||
, {cowboy, {git, "https://github.com/emqx/cowboy", {tag, "2.8.2"}}}
|
||||
, {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.8.0"}}}
|
||||
, {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.8.1"}}}
|
||||
, {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.9.0"}}}
|
||||
, {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.5.1"}}}
|
||||
, {cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v3.3.5"}}}
|
||||
, {cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v4.0.1"}}} % TODO: delete when all apps moved to hocon
|
||||
, {minirest, {git, "https://github.com/emqx/minirest", {tag, "0.3.5"}}}
|
||||
, {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "0.5.1"}}}
|
||||
, {replayq, {git, "https://github.com/emqx/replayq", {tag, "0.3.2"}}}
|
||||
|
@ -56,6 +56,7 @@
|
|||
, {observer_cli, "1.6.1"} % NOTE: depends on recon 2.5.1
|
||||
, {getopt, "1.0.1"}
|
||||
, {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}}
|
||||
, {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.4.0"}}}
|
||||
]}.
|
||||
|
||||
{xref_ignores,
|
||||
|
|
|
@ -106,7 +106,7 @@ test_plugins() ->
|
|||
|
||||
test_deps() ->
|
||||
[ {bbmustache, "1.10.0"}
|
||||
, {emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.3.9"}}}
|
||||
, {emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {branch, "hocon"}}}
|
||||
, meck
|
||||
].
|
||||
|
||||
|
@ -188,6 +188,7 @@ overlay_vars_rel(RelType) ->
|
|||
end,
|
||||
[ {enable_plugin_emqx_rule_engine, RelType =:= cloud}
|
||||
, {enable_plugin_emqx_bridge_mqtt, RelType =:= edge}
|
||||
, {enable_plugin_emqx_resource, true}
|
||||
, {enable_plugin_emqx_modules, false} %% modules is not a plugin in ce
|
||||
, {enable_plugin_emqx_recon, true}
|
||||
, {enable_plugin_emqx_retainer, true}
|
||||
|
@ -281,6 +282,7 @@ relx_plugin_apps(ReleaseType) ->
|
|||
, emqx_auth_mnesia
|
||||
, emqx_web_hook
|
||||
, emqx_recon
|
||||
, emqx_resource
|
||||
, emqx_rule_engine
|
||||
, emqx_sasl
|
||||
]
|
||||
|
@ -334,9 +336,8 @@ relx_overlay(ReleaseType) ->
|
|||
, {template, "bin/emqx_ctl.cmd", "bin/emqx_ctl.cmd"}
|
||||
, {copy, "bin/nodetool", "bin/nodetool"}
|
||||
, {copy, "bin/nodetool", "bin/nodetool-{{release_version}}"}
|
||||
, {copy, "_build/default/lib/cuttlefish/cuttlefish", "bin/cuttlefish"}
|
||||
, {copy, "_build/default/lib/cuttlefish/cuttlefish", "bin/cuttlefish-{{release_version}}"}
|
||||
, {copy, "priv/emqx.schema", "releases/{{release_version}}/"}
|
||||
, {copy, "_build/default/lib/hocon/hocon", "bin/hocon"}
|
||||
, {copy, "_build/default/lib/hocon/hocon", "bin/hocon-{{release_version}}"}
|
||||
] ++ case is_enterprise() of
|
||||
true -> ee_etc_overlay(ReleaseType);
|
||||
false -> etc_overlay(ReleaseType)
|
||||
|
|
|
@ -6,22 +6,45 @@ latest_release=$(git describe --tags "$(git rev-list --tags --max-count=1 --remo
|
|||
|
||||
bad_app_count=0
|
||||
|
||||
while read -r app; do
|
||||
if [ "$app" != "emqx" ]; then
|
||||
app_path="$app"
|
||||
get_vsn() {
|
||||
commit="$1"
|
||||
app_src_file="$2"
|
||||
if [ "$commit" = 'HEAD' ]; then
|
||||
if [ -f "$app_src_file" ]; then
|
||||
grep vsn "$app_src_file" | grep -oE '"[0-9]+.[0-9]+.[0-9]+"' | tr -d '"' || true
|
||||
fi
|
||||
else
|
||||
app_path="."
|
||||
git show "$commit":"$app_src_file" 2>/dev/null | grep vsn | grep -oE '"[0-9]+.[0-9]+.[0-9]+"' | tr -d '"' || true
|
||||
fi
|
||||
src_file="$app_path/src/$(basename "$app").app.src"
|
||||
old_app_version="$(git show "$latest_release":"$src_file" | grep vsn | grep -oE '"[0-9]+.[0-9]+.[0-9]+"' | tr -d '"')"
|
||||
now_app_version=$(grep -E 'vsn' "$src_file" | grep -oE '"[0-9]+\.[0-9]+\.[0-9]+"' | tr -d '"')
|
||||
if [ "$old_app_version" = "$now_app_version" ]; then
|
||||
changed="$(git diff --name-only "$latest_release"...HEAD \
|
||||
}
|
||||
|
||||
while read -r app_path; do
|
||||
app=$(basename "$app_path")
|
||||
src_file="$app_path/src/$app.app.src"
|
||||
old_app_version="$(get_vsn "$latest_release" "$src_file")"
|
||||
## TODO: delete it after new version is released with emqx app in apps dir
|
||||
if [ "$app" = 'emqx' ] && [ "$old_app_version" = '' ]; then
|
||||
old_app_version="$(get_vsn "$latest_release" 'src/emqx.app.src')"
|
||||
fi
|
||||
now_app_version="$(get_vsn 'HEAD' "$src_file")"
|
||||
## TODO: delete it after new version is released with emqx app in apps dir
|
||||
if [ "$app" = 'emqx' ] && [ "$now_app_version" = '' ]; then
|
||||
now_app_version="$(get_vsn 'HEAD' 'src/emqx.app.src')"
|
||||
fi
|
||||
if [ -z "$now_app_version" ]; then
|
||||
echo "failed_to_get_new_app_vsn for $app"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${old_app_version:-}" ]; then
|
||||
echo "skiped checking new app ${app}"
|
||||
elif [ "$old_app_version" = "$now_app_version" ]; then
|
||||
lines="$(git diff --name-only "$latest_release"...HEAD \
|
||||
-- "$app_path/src" \
|
||||
-- "$app_path/priv" \
|
||||
-- "$app_path/c_src" | wc -l)"
|
||||
if [ "$changed" -gt 0 ]; then
|
||||
echo "$src_file needs a vsn bump"
|
||||
-- "$app_path/c_src")"
|
||||
if [ "$lines" != '' ]; then
|
||||
echo "$src_file needs a vsn bump (old=$old_app_version)"
|
||||
echo "changed: $lines"
|
||||
bad_app_count=$(( bad_app_count + 1))
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -32,6 +32,9 @@
|
|||
-export([format/2]).
|
||||
|
||||
-ifdef(TEST).
|
||||
-include_lib("proper/include/proper.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-export([report_cb_1/1, report_cb_2/2, report_cb_crash/2]).
|
||||
-endif.
|
||||
|
||||
|
@ -220,8 +223,6 @@ json_key(Term) ->
|
|||
end.
|
||||
|
||||
-ifdef(TEST).
|
||||
-include_lib("proper/include/proper.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
no_crash_test_() ->
|
||||
Opts = [{numtests, 1000}, {to_file, user}],
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -65,16 +65,17 @@ mustache_vars() ->
|
|||
].
|
||||
|
||||
generate_config() ->
|
||||
Schema = cuttlefish_schema:files([local_path(["priv", "emqx.schema"])]),
|
||||
ConfFile = render_config_file(),
|
||||
Conf = conf_parse:file(ConfFile),
|
||||
cuttlefish_generator:map(Schema, Conf).
|
||||
{ok, Conf} = hocon:load(ConfFile, #{format => richmap}),
|
||||
hocon_schema:generate(emqx_schema, Conf).
|
||||
|
||||
set_app_env({App, Lists}) ->
|
||||
lists:foreach(fun({acl_file, _Var}) ->
|
||||
application:set_env(App, acl_file, local_path(["etc", "acl.conf"]));
|
||||
({plugins_loaded_file, _Var}) ->
|
||||
application:set_env(App, plugins_loaded_file, local_path(["test", "emqx_SUITE_data","loaded_plugins"]));
|
||||
application:set_env(App,
|
||||
plugins_loaded_file,
|
||||
local_path(["test", "emqx_SUITE_data","loaded_plugins"]));
|
||||
({Par, Var}) ->
|
||||
application:set_env(App, Par, Var)
|
||||
end, Lists).
|
||||
|
@ -91,4 +92,4 @@ get_base_dir(Module) ->
|
|||
|
||||
get_base_dir() ->
|
||||
get_base_dir(?MODULE).
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue